diff --git a/src/main/java/appeng/client/gui/AEBaseScreen.java b/src/main/java/appeng/client/gui/AEBaseScreen.java index 6e73565cc..6cc789905 100644 --- a/src/main/java/appeng/client/gui/AEBaseScreen.java +++ b/src/main/java/appeng/client/gui/AEBaseScreen.java @@ -168,14 +168,14 @@ public abstract class AEBaseScreen extends HandledScr protected void drawGuiSlot(MatrixStack matrices, CustomSlotWidget slot, int mouseX, int mouseY, float partialTicks) { if (slot.isSlotEnabled()) { - final int left = slot.xPos(); - final int top = slot.yPos(); - final int right = left + slot.getWidth(); - final int bottom = top + slot.getHeight(); + final int left = slot.getTooltipAreaX(); + final int top = slot.getTooltipAreaY(); + final int right = left + slot.getTooltipAreaWidth(); + final int bottom = top + slot.getTooltipAreaHeight(); slot.drawContent(getClient(), mouseX, mouseY, partialTicks); - if (this.isPointWithinBounds(left, top, slot.getWidth(), slot.getHeight(), mouseX, mouseY) + if (this.isPointWithinBounds(left, top, slot.getTooltipAreaWidth(), slot.getTooltipAreaHeight(), mouseX, mouseY) && slot.canClick(getPlayer())) { RenderSystem.colorMask(true, true, true, false); this.fillGradient(matrices, left, top, right, bottom, -2130706433, -2130706433); @@ -185,27 +185,28 @@ public abstract class AEBaseScreen extends HandledScr } private void drawTooltip(MatrixStack matrices, ITooltip tooltip, int mouseX, int mouseY) { - final int x = tooltip.xPos(); - int y = tooltip.yPos(); + final int x = tooltip.getTooltipAreaX(); + int y = tooltip.getTooltipAreaY(); - if (x < mouseX && x + tooltip.getWidth() > mouseX && tooltip.isVisible()) { - if (y < mouseY && y + tooltip.getHeight() > mouseY) { + if (x < mouseX && x + tooltip.getTooltipAreaWidth() > mouseX && tooltip.isTooltipAreaVisible()) { + if (y < mouseY && y + tooltip.getTooltipAreaHeight() > mouseY) { if (y < 15) { y = 15; } - final Text msg = tooltip.getMessage(); - if (msg != null && !msg.getString().isEmpty()) { - this.drawTooltip(matrices, x + 11, y + 4, msg); - } + final Text msg = tooltip.getTooltipMessage(); + this.drawTooltip(matrices, x + 11, y + 4, msg); } } } protected void drawTooltip(MatrixStack matrices, int x, int y, Text message) { - String[] lines = message.getString().split("\n"); // FIXME FABRIC - List textLines = Arrays.stream(lines).map(LiteralText::new).collect(Collectors.toList()); - this.drawTooltip(matrices, x, y, textLines); + String tooltipText = message.getString(); + + if (!tooltipText.isEmpty()) { + String[] lines = tooltipText.split("\n"); // FIXME FABRIC + List textLines = Arrays.stream(lines).map(LiteralText::new).collect(Collectors.toList()); + this.drawTooltip(matrices, x, y, textLines);} } // FIXME FABRIC: move out to json (?) @@ -290,8 +291,8 @@ public abstract class AEBaseScreen extends HandledScr } for (CustomSlotWidget slot : this.guiSlots) { - if (this.isPointWithinBounds(slot.xPos(), slot.yPos(), slot.getWidth(), slot.getHeight(), xCoord, yCoord) - && slot.canClick(getPlayer())) { + if (this.isPointWithinBounds(slot.getTooltipAreaX(), slot.getTooltipAreaY(), slot.getTooltipAreaWidth(), + slot.getTooltipAreaHeight(), xCoord, yCoord) && slot.canClick(getPlayer())) { slot.slotClicked(getPlayer().inventory.getCursorStack(), btn); } } diff --git a/src/main/java/appeng/client/gui/widgets/ActionButton.java b/src/main/java/appeng/client/gui/widgets/ActionButton.java index 80e08d114..733b39170 100644 --- a/src/main/java/appeng/client/gui/widgets/ActionButton.java +++ b/src/main/java/appeng/client/gui/widgets/ActionButton.java @@ -27,7 +27,7 @@ import net.minecraft.text.Text; import appeng.api.config.ActionItems; import appeng.core.localization.ButtonToolTips; -public class ActionButton extends IconButton implements ITooltip { +public class ActionButton extends IconButton { private static final Pattern PATTERN_NEW_LINE = Pattern.compile("\\n", Pattern.LITERAL); private final int iconIndex; diff --git a/src/main/java/appeng/client/gui/widgets/CustomSlotWidget.java b/src/main/java/appeng/client/gui/widgets/CustomSlotWidget.java index 0b90b2e50..26ca1ec8a 100644 --- a/src/main/java/appeng/client/gui/widgets/CustomSlotWidget.java +++ b/src/main/java/appeng/client/gui/widgets/CustomSlotWidget.java @@ -6,6 +6,7 @@ import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.text.LiteralText; import net.minecraft.text.Text; public abstract class CustomSlotWidget extends DrawableHelper implements ITooltip { @@ -37,32 +38,32 @@ public abstract class CustomSlotWidget extends DrawableHelper implements IToolti } @Override - public Text getMessage() { - return null; + public Text getTooltipMessage() { + return LiteralText.EMPTY; } @Override - public int xPos() { + public int getTooltipAreaX() { return this.x; } @Override - public int yPos() { + public int getTooltipAreaY() { return this.y; } @Override - public int getWidth() { + public int getTooltipAreaWidth() { return 16; } @Override - public int getHeight() { + public int getTooltipAreaHeight() { return 16; } @Override - public boolean isVisible() { + public boolean isTooltipAreaVisible() { return false; } diff --git a/src/main/java/appeng/client/gui/widgets/ITooltip.java b/src/main/java/appeng/client/gui/widgets/ITooltip.java index a4e1f6ce0..9d0072389 100644 --- a/src/main/java/appeng/client/gui/widgets/ITooltip.java +++ b/src/main/java/appeng/client/gui/widgets/ITooltip.java @@ -20,48 +20,53 @@ package appeng.client.gui.widgets; import net.minecraft.text.Text; +import javax.annotation.Nonnull; + /** * AEBaseGui controlled Tooltip Interface. */ public interface ITooltip { /** - * returns the tooltip message. + * Returns the tooltip message. + * + * Should use {@link net.minecraft.text.LiteralText#EMPTY} for no tooltip * * @return tooltip message */ - Text getMessage(); + @Nonnull + Text getTooltipMessage(); /** * x Location for the object that triggers the tooltip. * * @return xPosition */ - int xPos(); + int getTooltipAreaX(); /** * y Location for the object that triggers the tooltip. * * @return yPosition */ - int yPos(); + int getTooltipAreaY(); /** * Width of the object that triggers the tooltip. * * @return width */ - int getWidth(); + int getTooltipAreaWidth(); /** * Height for the object that triggers the tooltip. * * @return height */ - int getHeight(); + int getTooltipAreaHeight(); /** * @return true if button being drawn */ - boolean isVisible(); + boolean isTooltipAreaVisible(); } diff --git a/src/main/java/appeng/client/gui/widgets/IconButton.java b/src/main/java/appeng/client/gui/widgets/IconButton.java index 5434ee8e1..9d9fa02bd 100644 --- a/src/main/java/appeng/client/gui/widgets/IconButton.java +++ b/src/main/java/appeng/client/gui/widgets/IconButton.java @@ -25,6 +25,7 @@ import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.texture.TextureManager; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; import net.minecraft.util.Identifier; public abstract class IconButton extends ButtonWidget implements ITooltip { @@ -95,27 +96,32 @@ public abstract class IconButton extends ButtonWidget implements ITooltip { protected abstract int getIconIndex(); @Override - public int xPos() { + public Text getTooltipMessage() { + return getMessage(); + } + + @Override + public int getTooltipAreaX() { return this.x; } @Override - public int yPos() { + public int getTooltipAreaY() { return this.y; } @Override - public int getWidth() { + public int getTooltipAreaWidth() { return this.halfSize ? 8 : 16; } @Override - public int getHeight() { + public int getTooltipAreaHeight() { return this.halfSize ? 8 : 16; } @Override - public boolean isVisible() { + public boolean isTooltipAreaVisible() { return this.visible; } diff --git a/src/main/java/appeng/client/gui/widgets/ProgressBar.java b/src/main/java/appeng/client/gui/widgets/ProgressBar.java index fa2001368..56b22096f 100644 --- a/src/main/java/appeng/client/gui/widgets/ProgressBar.java +++ b/src/main/java/appeng/client/gui/widgets/ProgressBar.java @@ -77,7 +77,7 @@ public class ProgressBar extends AbstractButtonWidget implements ITooltip { } @Override - public Text getMessage() { + public Text getTooltipMessage() { if (this.fullMsg != null) { return this.fullMsg; } @@ -88,27 +88,27 @@ public class ProgressBar extends AbstractButtonWidget implements ITooltip { } @Override - public int xPos() { + public int getTooltipAreaX() { return this.x - 2; } @Override - public int yPos() { + public int getTooltipAreaY() { return this.y - 2; } @Override - public int getWidth() { + public int getTooltipAreaWidth() { return this.width + 4; } @Override - public int getHeight() { + public int getTooltipAreaHeight() { return this.height + 4; } @Override - public boolean isVisible() { + public boolean isTooltipAreaVisible() { return true; } diff --git a/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java b/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java index 2c4063d2e..bc788d22a 100644 --- a/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java +++ b/src/main/java/appeng/client/gui/widgets/SettingToggleButton.java @@ -279,7 +279,7 @@ public class SettingToggleButton> extends IconButton { } @Override - public Text getMessage() { + public Text getTooltipMessage() { Text displayName = null; Text displayValue = null; diff --git a/src/main/java/appeng/client/gui/widgets/TabButton.java b/src/main/java/appeng/client/gui/widgets/TabButton.java index 4bbf5f8c9..adc907cf4 100644 --- a/src/main/java/appeng/client/gui/widgets/TabButton.java +++ b/src/main/java/appeng/client/gui/widgets/TabButton.java @@ -93,27 +93,32 @@ public class TabButton extends ButtonWidget implements ITooltip { } @Override - public int xPos() { + public ITextComponent getTooltipMessage() { + return getMessage(); + } + + @Override + public int getTooltipAreaX() { return this.x; } @Override - public int yPos() { + public int getTooltipAreaY() { return this.y; } @Override - public int getWidth() { + public int getTooltipAreaWidth() { return 22; } @Override - public int getHeight() { + public int getTooltipAreaHeight() { return 22; } @Override - public boolean isVisible() { + public boolean isTooltipAreaVisible() { return this.visible; } diff --git a/src/main/java/appeng/client/gui/widgets/ToggleButton.java b/src/main/java/appeng/client/gui/widgets/ToggleButton.java index 9958210fd..8e42e89ab 100644 --- a/src/main/java/appeng/client/gui/widgets/ToggleButton.java +++ b/src/main/java/appeng/client/gui/widgets/ToggleButton.java @@ -74,7 +74,7 @@ public class ToggleButton extends ButtonWidget implements ITooltip { } @Override - public Text getMessage() { + public Text getTooltipMessage() { if (this.displayName != null) { String name = this.displayName.getString(); String value = this.displayHint.getString(); @@ -96,27 +96,27 @@ public class ToggleButton extends ButtonWidget implements ITooltip { } @Override - public int xPos() { + public int getTooltipAreaX() { return this.x; } @Override - public int yPos() { + public int getTooltipAreaY() { return this.y; } @Override - public int getWidth() { + public int getTooltipAreaWidth() { return 16; } @Override - public int getHeight() { + public int getTooltipAreaHeight() { return 16; } @Override - public boolean isVisible() { + public boolean isTooltipAreaVisible() { return this.visible; } } diff --git a/src/main/java/appeng/container/implementations/FormationPlaneContainer.java b/src/main/java/appeng/container/implementations/FormationPlaneContainer.java index 1bb894908..cad591712 100644 --- a/src/main/java/appeng/container/implementations/FormationPlaneContainer.java +++ b/src/main/java/appeng/container/implementations/FormationPlaneContainer.java @@ -53,7 +53,7 @@ public class FormationPlaneContainer extends UpgradeableContainer { return helper.open(player, locator); } - @GuiSync(6) + @GuiSync(7) public YesNo placeMode; public FormationPlaneContainer(int id, final PlayerInventory ip, final FormationPlanePart te) { diff --git a/src/main/java/appeng/facade/FacadePart.java b/src/main/java/appeng/facade/FacadePart.java index e010a513d..e59bcc9b0 100644 --- a/src/main/java/appeng/facade/FacadePart.java +++ b/src/main/java/appeng/facade/FacadePart.java @@ -49,7 +49,7 @@ public class FacadePart implements IFacadePart { @Override public void getBoxes(final IPartCollisionHelper ch, boolean livingEntity) { - if (livingEntity) { + if (livingEntity || !ch.isBBCollision()) { // prevent weird snag behavior ch.addBox(0.0, 0.0, 14, 16.0, 16.0, 16.0); } else { diff --git a/src/main/java/appeng/fluids/client/gui/widgets/FluidSlotWidget.java b/src/main/java/appeng/fluids/client/gui/widgets/FluidSlotWidget.java index f4aa42892..52fb6627c 100644 --- a/src/main/java/appeng/fluids/client/gui/widgets/FluidSlotWidget.java +++ b/src/main/java/appeng/fluids/client/gui/widgets/FluidSlotWidget.java @@ -7,6 +7,7 @@ import java.util.Set; import net.minecraft.client.MinecraftClient; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; +import net.minecraft.text.LiteralText; import net.minecraft.text.Text; import alexiil.mc.lib.attributes.fluid.FluidAttributes; @@ -35,7 +36,12 @@ public class FluidSlotWidget extends CustomSlotWidget { public void drawContent(final MinecraftClient mc, final int mouseX, final int mouseY, final float partialTicks) { final IAEFluidStack fs = this.getFluidStack(); if (fs != null) { - fs.getFluidStack().renderGuiRect(xPos(), yPos(), xPos() + getWidth(), yPos() + getHeight()); + // The tooltip area coincides with the area of the slot + int x = getTooltipAreaX(); + int y = getTooltipAreaY(); + int width = getTooltipAreaX(); + int height = getTooltipAreaY(); + fs.getFluidStack().renderGuiRect(x, y, x + width, y + height); } } @@ -61,16 +67,16 @@ public class FluidSlotWidget extends CustomSlotWidget { } @Override - public Text getMessage() { + public Text getTooltipMessage() { final IAEFluidStack fluid = this.getFluidStack(); if (fluid != null) { return fluid.getFluidStack().getName(); } - return null; + return LiteralText.EMPTY; } @Override - public boolean isVisible() { + public boolean isTooltipAreaVisible() { return true; } diff --git a/src/main/java/appeng/fluids/client/gui/widgets/FluidTankWidget.java b/src/main/java/appeng/fluids/client/gui/widgets/FluidTankWidget.java index 5b0798d0d..9f1d72f40 100644 --- a/src/main/java/appeng/fluids/client/gui/widgets/FluidTankWidget.java +++ b/src/main/java/appeng/fluids/client/gui/widgets/FluidTankWidget.java @@ -83,7 +83,7 @@ public class FluidTankWidget extends AbstractButtonWidget implements ITooltip { } @Override - public Text getMessage() { + public Text getTooltipMessage() { final IAEFluidStack fluid = this.tank.getFluidInSlot(this.slot); if (fluid != null && fluid.getStackSize() > 0) { Text desc = fluid.getFluidStack().getName(); @@ -95,27 +95,27 @@ public class FluidTankWidget extends AbstractButtonWidget implements ITooltip { } @Override - public int xPos() { + public int getTooltipAreaX() { return this.x - 2; } @Override - public int yPos() { + public int getTooltipAreaY() { return this.y - 2; } @Override - public int getWidth() { + public int getTooltipAreaWidth() { return this.width + 4; } @Override - public int getHeight() { + public int getTooltipAreaHeight() { return this.height + 4; } @Override - public boolean isVisible() { + public boolean isTooltipAreaVisible() { return true; } diff --git a/src/main/java/appeng/fluids/client/gui/widgets/OptionalFluidSlotWidget.java b/src/main/java/appeng/fluids/client/gui/widgets/OptionalFluidSlotWidget.java index 8535367d4..471130d8c 100644 --- a/src/main/java/appeng/fluids/client/gui/widgets/OptionalFluidSlotWidget.java +++ b/src/main/java/appeng/fluids/client/gui/widgets/OptionalFluidSlotWidget.java @@ -50,8 +50,8 @@ public class OptionalFluidSlotWidget extends FluidSlotWidget { } int oldZOffset = getZOffset(); setZOffset(currentZIndex); - drawTexture(matrices, guileft + this.xPos() - 1, guitop + this.yPos() - 1, this.srcX - 1, this.srcY - 1, - this.getWidth() + 2, this.getHeight() + 2); + drawTexture(matrices, guileft + this.getTooltipAreaX() - 1, guitop + this.getTooltipAreaY() - 1, this.srcX - 1, this.srcY - 1, + this.getTooltipAreaWidth() + 2, this.getTooltipAreaHeight() + 2); setZOffset(oldZOffset); } } diff --git a/src/main/java/appeng/fluids/parts/FluidAnnihilationPlanePart.java b/src/main/java/appeng/fluids/parts/FluidAnnihilationPlanePart.java index 09ef1890a..b4e09ec2a 100644 --- a/src/main/java/appeng/fluids/parts/FluidAnnihilationPlanePart.java +++ b/src/main/java/appeng/fluids/parts/FluidAnnihilationPlanePart.java @@ -38,15 +38,12 @@ import appeng.api.networking.storage.IStorageGrid; import appeng.api.networking.ticking.IGridTickable; import appeng.api.networking.ticking.TickRateModulation; import appeng.api.networking.ticking.TickingRequest; -import appeng.api.parts.IPart; import appeng.api.parts.IPartCollisionHelper; -import appeng.api.parts.IPartHost; import appeng.api.parts.IPartModel; import appeng.api.storage.IMEInventory; import appeng.api.storage.channels.IFluidStorageChannel; import appeng.api.storage.data.IAEFluidStack; import appeng.api.util.AECableType; -import appeng.api.util.AEPartLocation; import appeng.core.Api; import appeng.core.AppEng; import appeng.core.settings.TickRates; @@ -56,6 +53,7 @@ import appeng.items.parts.PartModels; import appeng.me.GridAccessException; import appeng.me.helpers.MachineSource; import appeng.parts.BasicStatePart; +import appeng.parts.automation.PlaneConnectionHelper; import appeng.parts.automation.PlaneConnections; import appeng.parts.automation.PlaneModels; import appeng.util.Platform; @@ -75,115 +73,27 @@ public class FluidAnnihilationPlanePart extends BasicStatePart implements IGridT private final IActionSource mySrc = new MachineSource(this); + private final PlaneConnectionHelper connectionHelper = new PlaneConnectionHelper(this); + public FluidAnnihilationPlanePart(final ItemStack is) { super(is); } @Override public void getBoxes(final IPartCollisionHelper bch) { - int minX = 1; - int minY = 1; - int maxX = 15; - int maxY = 15; - - final IPartHost host = this.getHost(); - if (host != null) { - final BlockEntity te = host.getTile(); - - final BlockPos pos = te.getPos(); - - final Direction e = bch.getWorldX(); - final Direction u = bch.getWorldY(); - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(e.getOpposite())), this.getSide())) { - minX = 0; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(e)), this.getSide())) { - maxX = 16; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(u.getOpposite())), this.getSide())) { - minY = 0; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(e)), this.getSide())) { - maxY = 16; - } - } - - bch.addBox(5, 5, 14, 11, 11, 15); - bch.addBox(minX, minY, 15, maxX, maxY, 16); + connectionHelper.getBoxes(bch); } public PlaneConnections getConnections() { - - final Direction facingRight, facingUp; - AEPartLocation location = this.getSide(); - switch (location) { - case UP: - facingRight = Direction.EAST; - facingUp = Direction.NORTH; - break; - case DOWN: - facingRight = Direction.WEST; - facingUp = Direction.NORTH; - break; - case NORTH: - facingRight = Direction.WEST; - facingUp = Direction.UP; - break; - case SOUTH: - facingRight = Direction.EAST; - facingUp = Direction.UP; - break; - case WEST: - facingRight = Direction.SOUTH; - facingUp = Direction.UP; - break; - case EAST: - facingRight = Direction.NORTH; - facingUp = Direction.UP; - break; - default: - case INTERNAL: - return PlaneConnections.of(false, false, false, false); - } - - boolean left = false, right = false, down = false, up = false; - - final IPartHost host = this.getHost(); - if (host != null) { - final BlockEntity te = host.getTile(); - - final BlockPos pos = te.getPos(); - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingRight.getOpposite())), - this.getSide())) { - left = true; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingRight)), this.getSide())) { - right = true; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingUp.getOpposite())), - this.getSide())) { - down = true; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingUp)), this.getSide())) { - up = true; - } - } - - return PlaneConnections.of(up, right, down, left); + return connectionHelper.getConnections(); } @Override public void onneighborUpdate(BlockView w, BlockPos pos, BlockPos neighbor) { if (pos.offset(this.getSide().getFacing()).equals(neighbor)) { this.refresh(); + } else { + connectionHelper.updateConnections(); } } @@ -192,14 +102,6 @@ public class FluidAnnihilationPlanePart extends BasicStatePart implements IGridT return 1; } - private boolean isAnnihilationPlane(final BlockEntity blockTileEntity, final AEPartLocation side) { - if (blockTileEntity instanceof IPartHost) { - final IPart p = ((IPartHost) blockTileEntity).getPart(side); - return p != null && p.getClass() == this.getClass(); - } - return false; - } - private void refresh() { try { this.getProxy().getTick().alertDevice(this.getProxy().getNode()); diff --git a/src/main/java/appeng/parts/automation/AbstractFormationPlanePart.java b/src/main/java/appeng/parts/automation/AbstractFormationPlanePart.java index ef7e1ecf4..e2af8a42c 100644 --- a/src/main/java/appeng/parts/automation/AbstractFormationPlanePart.java +++ b/src/main/java/appeng/parts/automation/AbstractFormationPlanePart.java @@ -11,9 +11,7 @@ import net.minecraft.world.BlockView; import appeng.api.config.Actionable; import appeng.api.config.Settings; import appeng.api.networking.security.IActionSource; -import appeng.api.parts.IPart; import appeng.api.parts.IPartCollisionHelper; -import appeng.api.parts.IPartHost; import appeng.api.storage.IMEInventory; import appeng.api.storage.cells.ICellContainer; import appeng.api.storage.cells.ICellInventory; @@ -30,6 +28,7 @@ public abstract class AbstractFormationPlanePart> extends private boolean wasActive = false; private int priority = 0; protected boolean blocked = false; + private final PlaneConnectionHelper connectionHelper = new PlaneConnectionHelper(this); public AbstractFormationPlanePart(ItemStack is) { super(is); @@ -64,103 +63,11 @@ public abstract class AbstractFormationPlanePart> extends @Override public void getBoxes(final IPartCollisionHelper bch) { - int minX = 1; - int minY = 1; - int maxX = 15; - int maxY = 15; - - final IPartHost host = this.getHost(); - if (host != null) { - final BlockEntity te = host.getTile(); - - final BlockPos pos = te.getPos(); - - final Direction e = bch.getWorldX(); - final Direction u = bch.getWorldY(); - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(e.getOpposite())), this.getSide())) { - minX = 0; - } - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(e)), this.getSide())) { - maxX = 16; - } - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(u.getOpposite())), this.getSide())) { - minY = 0; - } - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(u)), this.getSide())) { - maxY = 16; - } - } - - bch.addBox(5, 5, 14, 11, 11, 15); - bch.addBox(minX, minY, 15, maxX, maxY, 16); + connectionHelper.getBoxes(bch); } public PlaneConnections getConnections() { - - final Direction facingRight, facingUp; - AEPartLocation location = this.getSide(); - switch (location) { - case UP: - facingRight = Direction.EAST; - facingUp = Direction.NORTH; - break; - case DOWN: - facingRight = Direction.WEST; - facingUp = Direction.NORTH; - break; - case NORTH: - facingRight = Direction.WEST; - facingUp = Direction.UP; - break; - case SOUTH: - facingRight = Direction.EAST; - facingUp = Direction.UP; - break; - case WEST: - facingRight = Direction.SOUTH; - facingUp = Direction.UP; - break; - case EAST: - facingRight = Direction.NORTH; - facingUp = Direction.UP; - break; - default: - case INTERNAL: - return PlaneConnections.of(false, false, false, false); - } - - boolean left = false, right = false, down = false, up = false; - - final IPartHost host = this.getHost(); - if (host != null) { - final BlockEntity te = host.getTile(); - - final BlockPos pos = te.getPos(); - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(facingRight.getOpposite())), - this.getSide())) { - left = true; - } - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(facingRight)), this.getSide())) { - right = true; - } - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(facingUp.getOpposite())), - this.getSide())) { - down = true; - } - - if (this.isTransitionPlane(te.getWorld().getBlockEntity(pos.offset(facingUp)), this.getSide())) { - up = true; - } - } - - return PlaneConnections.of(up, right, down, left); + return connectionHelper.getConnections(); } @Override @@ -172,6 +79,8 @@ public abstract class AbstractFormationPlanePart> extends final BlockPos tePos = te.getPos().offset(side.getFacing()); this.blocked = isBlocking(w, tePos); + } else { + connectionHelper.updateConnections(); } } @@ -184,14 +93,6 @@ public abstract class AbstractFormationPlanePart> extends return 1; } - protected boolean isTransitionPlane(final BlockEntity blockTileEntity, final AEPartLocation side) { - if (blockTileEntity instanceof IPartHost) { - final IPart p = ((IPartHost) blockTileEntity).getPart(side); - return p != null && this.getClass() == p.getClass(); - } - return false; - } - @Override public T extractItems(final T request, final Actionable mode, final IActionSource src) { return null; diff --git a/src/main/java/appeng/parts/automation/AnnihilationPlanePart.java b/src/main/java/appeng/parts/automation/AnnihilationPlanePart.java index 3ad352501..bb4841428 100644 --- a/src/main/java/appeng/parts/automation/AnnihilationPlanePart.java +++ b/src/main/java/appeng/parts/automation/AnnihilationPlanePart.java @@ -60,9 +60,7 @@ import appeng.api.networking.storage.IStorageGrid; import appeng.api.networking.ticking.IGridTickable; import appeng.api.networking.ticking.TickRateModulation; import appeng.api.networking.ticking.TickingRequest; -import appeng.api.parts.IPart; import appeng.api.parts.IPartCollisionHelper; -import appeng.api.parts.IPartHost; import appeng.api.parts.IPartModel; import appeng.api.storage.channels.IItemStorageChannel; import appeng.api.storage.data.IAEItemStack; @@ -98,53 +96,31 @@ public class AnnihilationPlanePart extends BasicStatePart implements IGridTickab private boolean isAccepting = true; private boolean breaking = false; + private final PlaneConnectionHelper connectionHelper = new PlaneConnectionHelper(this); + public AnnihilationPlanePart(final ItemStack is) { super(is); } @Override - public TickRateModulation call(final World world) throws Exception { + public TickRateModulation call(final World world) { this.breaking = false; return this.breakBlock(true); } @Override public void getBoxes(final IPartCollisionHelper bch) { - int minX = 1; - int minY = 1; - int maxX = 15; - int maxY = 15; - final IPartHost host = this.getHost(); - if (host != null) { - final BlockEntity te = host.getTile(); - - final BlockPos pos = te.getPos(); - - final Direction e = bch.getWorldX(); - final Direction u = bch.getWorldY(); - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(e.getOpposite())), this.getSide())) { - minX = 0; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(e)), this.getSide())) { - maxX = 16; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(u.getOpposite())), this.getSide())) { - minY = 0; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(e)), this.getSide())) { - maxY = 16; - } + // For collision, we're using a simplified bounding box + if (bch.isBBCollision()) { + // The smaller collision hitbox here is needed to allow for the entity collision + // event + bch.addBox(0, 0, 14, 16, 16, 15.5); + return; } - bch.addBox(5, 5, 14, 11, 11, 15); - // The smaller collision hitbox here is needed to allow for the entity collision - // event - bch.addBox(minX, minY, 15, maxX, maxY, bch.isBBCollision() ? 15 : 16); + connectionHelper.getBoxes(bch); + } /** @@ -152,73 +128,15 @@ public class AnnihilationPlanePart extends BasicStatePart implements IGridTickab * visually. */ public PlaneConnections getConnections() { - - final Direction facingRight, facingUp; - AEPartLocation location = this.getSide(); - switch (location) { - case UP: - facingRight = Direction.EAST; - facingUp = Direction.NORTH; - break; - case DOWN: - facingRight = Direction.WEST; - facingUp = Direction.NORTH; - break; - case NORTH: - facingRight = Direction.WEST; - facingUp = Direction.UP; - break; - case SOUTH: - facingRight = Direction.EAST; - facingUp = Direction.UP; - break; - case WEST: - facingRight = Direction.SOUTH; - facingUp = Direction.UP; - break; - case EAST: - facingRight = Direction.NORTH; - facingUp = Direction.UP; - break; - default: - case INTERNAL: - return PlaneConnections.of(false, false, false, false); - } - - boolean left = false, right = false, down = false, up = false; - - final IPartHost host = this.getHost(); - if (host != null) { - final BlockEntity te = host.getTile(); - - final BlockPos pos = te.getPos(); - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingRight.getOpposite())), - this.getSide())) { - left = true; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingRight)), this.getSide())) { - right = true; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingUp.getOpposite())), - this.getSide())) { - down = true; - } - - if (this.isAnnihilationPlane(te.getWorld().getBlockEntity(pos.offset(facingUp)), this.getSide())) { - up = true; - } - } - - return PlaneConnections.of(up, right, down, left); + return connectionHelper.getConnections(); } @Override public void onneighborUpdate(BlockView w, BlockPos pos, BlockPos neighbor) { if (pos.offset(this.getSide().getFacing()).equals(neighbor)) { this.refresh(); + } else { + connectionHelper.updateConnections(); } } @@ -363,14 +281,6 @@ public class AnnihilationPlanePart extends BasicStatePart implements IGridTickab return changed; } - protected boolean isAnnihilationPlane(final BlockEntity blockTileEntity, final AEPartLocation side) { - if (blockTileEntity instanceof IPartHost) { - final IPart p = ((IPartHost) blockTileEntity).getPart(side); - return p != null && p.getClass() == this.getClass(); - } - return false; - } - @Override @MENetworkEventSubscribe public void chanRender(final MENetworkChannelsChanged c) { diff --git a/src/main/java/appeng/parts/automation/FormationPlanePart.java b/src/main/java/appeng/parts/automation/FormationPlanePart.java index 72380e121..52b354a47 100644 --- a/src/main/java/appeng/parts/automation/FormationPlanePart.java +++ b/src/main/java/appeng/parts/automation/FormationPlanePart.java @@ -21,11 +21,13 @@ package appeng.parts.automation; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Random; import javax.annotation.Nullable; import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.*; @@ -49,7 +51,6 @@ import appeng.api.networking.events.MENetworkChannelsChanged; import appeng.api.networking.events.MENetworkEventSubscribe; import appeng.api.networking.events.MENetworkPowerStatusChange; import appeng.api.networking.security.IActionSource; -import appeng.api.parts.IPartItem; import appeng.api.parts.IPartModel; import appeng.api.storage.IMEInventoryHandler; import appeng.api.storage.IStorageChannel; @@ -76,6 +77,7 @@ import appeng.util.prioritylist.PrecisePriorityList; public class FormationPlanePart extends AbstractFormationPlanePart { private static final PlaneModels MODELS = new PlaneModels("part/formation_plane", "part/formation_plane_on"); + private static final Random RANDOM_OFFSET = new Random(); @PartModels public static List getModels() { @@ -214,12 +216,12 @@ public class FormationPlanePart extends AbstractFormationPlanePart final BlockPos placePos = te.getPos().offset(side.getFacing()); if (w.getBlockState(placePos).getMaterial().isReplaceable()) { - if (placeBlock == YesNo.YES && (i instanceof BlockItem || i instanceof FireworkChargeItem - || i instanceof FireworkItem || i instanceof IPartItem)) { + if (placeBlock == YesNo.YES) { final PlayerEntity player = FakePlayer.getOrCreate(serverWorld); Platform.configurePlayer(player, side, this.getTile()); - Hand hand = player.getActiveHand(); - player.setStackInHand(hand, is); + // Seems to work without... + // Hand hand = player.getActiveHand(); + // player.setStackInHand(hand, is); maxStorage = is.getCount(); worked = true; @@ -227,83 +229,33 @@ public class FormationPlanePart extends AbstractFormationPlanePart // The side the plane is attached to will be considered the look direction // in terms of placing an item Direction lookDirection = side.getFacing(); + PlaneDirectionalPlaceContext context = new PlaneDirectionalPlaceContext(w, player, placePos, + lookDirection, is, lookDirection.getOpposite()); - // FIXME No idea what any of this is _supposed_ to do, comment badly needed - if (i instanceof WallStandingBlockItem) { - boolean Worked = false; + i.onItemUse(context); + maxStorage -= is.getCount(); - // Up or Down, Attempt 1?? - if (side.xOffset == 0 && side.zOffset == 0) { - Worked = i - .useOnBlock(new AutomaticItemPlacementContext(w, placePos.offset(side.getFacing()), - lookDirection, is, side.getFacing())) == ActionResult.SUCCESS; - } - - // Up or Down, Attempt 2?? - if (!Worked && side.xOffset == 0 && side.zOffset == 0) { - Worked = i.useOnBlock(new AutomaticItemPlacementContext(w, - placePos.offset(side.getFacing().getOpposite()), lookDirection, is, - side.getFacing().getOpposite())) == ActionResult.SUCCESS; - } - - // Horizontal, attempt 1?? - if (!Worked && side.yOffset == 0) { - Worked = i.useOnBlock(new AutomaticItemPlacementContext(w, placePos.offset(Direction.DOWN), - lookDirection, is, Direction.DOWN)) == ActionResult.SUCCESS; - } - - if (!Worked) { - i.useOnBlock(new AutomaticItemPlacementContext(w, placePos, lookDirection, is, - lookDirection.getOpposite())); - } - - maxStorage -= is.getCount(); - } else { - i.useOnBlock(new AutomaticItemPlacementContext(w, placePos, lookDirection, is, - lookDirection.getOpposite())); - maxStorage -= is.getCount(); - } } else { maxStorage = 1; } - // Safe keeping - player.setStackInHand(hand, ItemStack.EMPTY); + // Seems to work without... Safe keeping + // player.setStackInHand(hand, ItemStack.EMPTY); } else { - worked = true; - final int sum = this.countEntitesAround(w, placePos); + // Disable spawning once there is a certain amount of entities in an area. if (sum < AEConfig.instance().getFormationPlaneEntityLimit()) { + worked = true; + if (type == Actionable.MODULATE) { is.setCount((int) maxStorage); - final double x = (side.xOffset != 0 ? 0 : .7 * (Platform.getRandomFloat() - .5)) + side.xOffset - + .5 + te.getPos().getX(); - final double y = (side.yOffset != 0 ? 0 : .7 * (Platform.getRandomFloat() - .5)) + side.yOffset - + .5 + te.getPos().getY(); - final double z = (side.zOffset != 0 ? 0 : .7 * (Platform.getRandomFloat() - .5)) + side.zOffset - + .5 + te.getPos().getZ(); - - final ItemEntity ei = new ItemEntity(w, x, y, z, is.copy()); - ei.setVelocity(side.xOffset * 0.2, side.yOffset * 0.2, side.zOffset * 0.2); - - Entity result; - if (is.getItem() instanceof AECustomEntityItem) { - result = ((AECustomEntityItem) is.getItem()).replaceItemEntity(serverWorld, ei, is); - if (result != ei) { - ei.remove(); - } - } else { - result = ei; - } - - if (!w.spawnEntity(result)) { - result.remove(); + if (!spawnItemEntity(serverWorld, te, side, is)) { + // revert in case something prevents spawning. worked = false; } + } - } else { - worked = false; } } } @@ -322,6 +274,56 @@ public class FormationPlanePart extends AbstractFormationPlanePart return input; } + private static boolean spawnItemEntity(ServerWorld w, BlockEntity te, AEPartLocation side, ItemStack is) { + // the item offset based on the entity height plus some offset + final double itemOffset = .55 + EntityType.ITEM.getHeight(); + + // The center of the block the plane is located in + final double centerX = te.getPos().getX() + .5; + final double centerY = te.getPos().getY() + .5; + final double centerZ = te.getPos().getZ() + .5; + + // When spawning downwards, we have to take the item height of 0.25 into account + // Otherwise it will get stuck and be spit out in a random direction as + // minecraft spawns it at its feet position and not center + final double additionalYOffset = side.yOffset == -1 ? -.3 : 0; + + // Calculate the offsets to spawn it into the adjacent block, taking the sign + // into account. + // Spawn it 0.8 blocks away from the center pos when facing in this direction + // Every other direction will select a position in a .5 block area around the + // block center. + final double offsetX = (side.xOffset == 0) ? ((RANDOM_OFFSET.nextFloat() / 2) - .25) + : (side.xOffset * itemOffset); + final double offsetY = (side.yOffset == 0) ? ((RANDOM_OFFSET.nextFloat() / 2) - .25) + : ((side.yOffset * itemOffset) + additionalYOffset); + final double offsetZ = (side.zOffset == 0) ? ((RANDOM_OFFSET.nextFloat() / 2) - .25) + : (side.zOffset * itemOffset); + + final double absoluteX = centerX + offsetX; + final double absoluteY = centerY + offsetY; + final double absoluteZ = centerZ + offsetZ; + + final ItemEntity ei = new ItemEntity(w, absoluteX, absoluteY, absoluteZ, is.copy()); + ei.setVelocity(side.xOffset * .1, side.yOffset * 0.1, side.zOffset * 0.1); + + Entity result; + if (is.getItem() instanceof AECustomEntityItem) { + result = ((AECustomEntityItem) is.getItem()).replaceItemEntity(w, ei, is); + if (result != ei) { + ei.remove(); + } + } else { + result = ei; + } + + if (!w.spawnEntity(result)) { + result.remove(); + return false; + } + return true; + } + @Override public IStorageChannel getChannel() { return Api.instance().storage().getStorageChannel(IItemStorageChannel.class); @@ -354,11 +356,76 @@ public class FormationPlanePart extends AbstractFormationPlanePart return list.size(); } - private class ForcedItemUseContext extends ItemUsageContext { - protected ForcedItemUseContext(World worldIn, @Nullable PlayerEntity player, Hand handIn, ItemStack heldItem, - BlockHitResult rayTraceResultIn) { - super(worldIn, player, handIn, heldItem, rayTraceResultIn); + /** + * A custom {@link DirectionalPlaceContext} which also accepts a player needed + * various blocks like seeds. + *

+ * Also removed {@link DirectionalPlaceContext#replacingClickedOnBlock} as this + * can cause a {@link StackOverflowError} for certain replaceable blocks. + */ + private static class PlaneDirectionalPlaceContext extends BlockItemUseContext { + private final Direction lookDirection; + + public PlaneDirectionalPlaceContext(World world, PlayerEntity player, BlockPos pos, Direction lookDirection, + ItemStack itemStack, Direction facing) { + super(world, player, Hand.MAIN_HAND, itemStack, + new BlockRayTraceResult(Vector3d.copyCenteredHorizontally(pos), facing, pos, false)); + this.lookDirection = lookDirection; + } + + @Override + public BlockPos getPos() { + return this.rayTraceResult.getPos(); + } + + @Override + public boolean canPlace() { + return this.world.getBlockState(this.rayTraceResult.getPos()).isReplaceable(this); + } + + @Override + public Direction getNearestLookingDirection() { + return Direction.DOWN; + } + + @Override + public Direction[] getNearestLookingDirections() { + switch (this.lookDirection) { + case DOWN: + default: + return new Direction[] { Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, + Direction.WEST, Direction.UP }; + case UP: + return new Direction[] { Direction.DOWN, Direction.UP, Direction.NORTH, Direction.EAST, + Direction.SOUTH, Direction.WEST }; + case NORTH: + return new Direction[] { Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.WEST, + Direction.UP, Direction.SOUTH }; + case SOUTH: + return new Direction[] { Direction.DOWN, Direction.SOUTH, Direction.EAST, Direction.WEST, + Direction.UP, Direction.NORTH }; + case WEST: + return new Direction[] { Direction.DOWN, Direction.WEST, Direction.SOUTH, Direction.UP, + Direction.NORTH, Direction.EAST }; + case EAST: + return new Direction[] { Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.UP, + Direction.NORTH, Direction.WEST }; + } + } + + @Override + public Direction getPlacementHorizontalFacing() { + return this.lookDirection.getAxis() == Axis.Y ? Direction.NORTH : this.lookDirection; + } + + @Override + public boolean func_225518_g_() { + return false; + } + + @Override + public float getPlacementYaw() { + return (float) (this.lookDirection.getHorizontalIndex() * 90); } } - } diff --git a/src/main/java/appeng/parts/automation/IdentityAnnihilationPlanePart.java b/src/main/java/appeng/parts/automation/IdentityAnnihilationPlanePart.java index 1768319ea..d235d3129 100644 --- a/src/main/java/appeng/parts/automation/IdentityAnnihilationPlanePart.java +++ b/src/main/java/appeng/parts/automation/IdentityAnnihilationPlanePart.java @@ -54,15 +54,6 @@ public class IdentityAnnihilationPlanePart extends AnnihilationPlanePart { super(is); } - @Override - protected boolean isAnnihilationPlane(final BlockEntity blockTileEntity, final AEPartLocation side) { - if (blockTileEntity instanceof IPartHost) { - final IPart p = ((IPartHost) blockTileEntity).getPart(side); - return p != null && p.getClass() == this.getClass(); - } - return false; - } - @Override protected float calculateEnergyUsage(final ServerWorld w, final BlockPos pos, final List items) { final float requiredEnergy = super.calculateEnergyUsage(w, pos, items); diff --git a/src/main/java/appeng/parts/automation/PlaneConnectionHelper.java b/src/main/java/appeng/parts/automation/PlaneConnectionHelper.java new file mode 100644 index 000000000..31d7ee5a8 --- /dev/null +++ b/src/main/java/appeng/parts/automation/PlaneConnectionHelper.java @@ -0,0 +1,160 @@ +package appeng.parts.automation; + +import javax.annotation.Nullable; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import appeng.api.parts.IPart; +import appeng.api.parts.IPartCollisionHelper; +import appeng.api.parts.IPartHost; +import appeng.api.util.AEPartLocation; +import appeng.parts.AEBasePart; + +/** + * Helps plane parts (annihilation, formation) with determining and checking for + * connections to adjacent plane parts of the same type to form a visually + * larger plane. + */ +public final class PlaneConnectionHelper { + + private final AEBasePart part; + + public PlaneConnectionHelper(AEBasePart part) { + this.part = part; + } + + /** + * Gets on which sides this part has adjacent planes that it visually connects + * to + */ + public PlaneConnections getConnections() { + TileEntity hostTileEntity = getHostTileEntity(); + AEPartLocation side = part.getSide(); + + final Direction facingRight, facingUp; + switch (side) { + case UP: + facingRight = Direction.EAST; + facingUp = Direction.NORTH; + break; + case DOWN: + facingRight = Direction.WEST; + facingUp = Direction.NORTH; + break; + case NORTH: + facingRight = Direction.WEST; + facingUp = Direction.UP; + break; + case SOUTH: + facingRight = Direction.EAST; + facingUp = Direction.UP; + break; + case WEST: + facingRight = Direction.SOUTH; + facingUp = Direction.UP; + break; + case EAST: + facingRight = Direction.NORTH; + facingUp = Direction.UP; + break; + default: + case INTERNAL: + return PlaneConnections.of(false, false, false, false); + } + + boolean left = false, right = false, down = false, up = false; + + if (hostTileEntity != null) { + World world = hostTileEntity.getWorld(); + BlockPos pos = hostTileEntity.getPos(); + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(facingRight.getOpposite())))) { + left = true; + } + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(facingRight)))) { + right = true; + } + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(facingUp.getOpposite())))) { + down = true; + } + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(facingUp)))) { + up = true; + } + } + + return PlaneConnections.of(up, right, down, left); + } + + /** + * Get the bounding boxes of this plane parts components. + */ + public void getBoxes(IPartCollisionHelper bch) { + int minX = 1; + int minY = 1; + int maxX = 15; + int maxY = 15; + + TileEntity hostTile = getHostTileEntity(); + if (hostTile != null) { + World world = hostTile.getWorld(); + + final BlockPos pos = hostTile.getPos(); + + final Direction e = bch.getWorldX(); + final Direction u = bch.getWorldY(); + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(e.getOpposite())))) { + minX = 0; + } + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(e)))) { + maxX = 16; + } + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(u.getOpposite())))) { + minY = 0; + } + + if (isCompatiblePlaneAdjacent(world.getTileEntity(pos.offset(u)))) { + maxY = 16; + } + } + + bch.addBox(5, 5, 14, 11, 11, 15); + bch.addBox(minX, minY, 15, maxX, maxY, 16); + } + + /** + * Call this when an adjacent block has changed since the connections need to be + * recalculated. + */ + public void updateConnections() { + TileEntity hostTile = getHostTileEntity(); + if (hostTile != null) { + hostTile.requestModelDataUpdate(); + } + } + + private boolean isCompatiblePlaneAdjacent(@Nullable TileEntity adjacentTileEntity) { + if (adjacentTileEntity instanceof IPartHost) { + final IPart p = ((IPartHost) adjacentTileEntity).getPart(part.getSide()); + return p != null && p.getClass() == part.getClass(); + } + return false; + } + + private TileEntity getHostTileEntity() { + IPartHost host = part.getHost(); + if (host != null) { + return host.getTile(); + } + return null; + } + +} diff --git a/src/main/java/appeng/parts/p2p/FluidP2PTunnelPart.java b/src/main/java/appeng/parts/p2p/FluidP2PTunnelPart.java index 8f21f9f05..ff530ac21 100644 --- a/src/main/java/appeng/parts/p2p/FluidP2PTunnelPart.java +++ b/src/main/java/appeng/parts/p2p/FluidP2PTunnelPart.java @@ -18,32 +18,32 @@ package appeng.parts.p2p; -import java.util.*; +import java.util.List; + +import javax.annotation.Nonnull; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockView; - -import alexiil.mc.lib.attributes.AttributeList; -import alexiil.mc.lib.attributes.Simulation; -import alexiil.mc.lib.attributes.fluid.FluidAttributes; -import alexiil.mc.lib.attributes.fluid.FluidInsertable; -import alexiil.mc.lib.attributes.fluid.FluidVolumeUtil; -import alexiil.mc.lib.attributes.fluid.amount.FluidAmount; -import alexiil.mc.lib.attributes.fluid.volume.FluidKey; -import alexiil.mc.lib.attributes.fluid.volume.FluidVolume; +import net.minecraft.world.IBlockReader; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fluids.capability.CapabilityFluidHandler; +import net.minecraftforge.fluids.capability.IFluidHandler; +import appeng.api.config.PowerUnits; import appeng.api.parts.IPartModel; import appeng.items.parts.PartModels; import appeng.me.GridAccessException; -public class FluidP2PTunnelPart extends P2PTunnelPart implements FluidInsertable { +public class FluidP2PTunnelPart extends P2PTunnelPart { private static final P2PModels MODELS = new P2PModels("part/p2p/p2p_tunnel_fluids"); + private static final IFluidHandler NULL_FLUID_HANDLER = new NullFluidHandler(); - private static final ThreadLocal> DEPTH = new ThreadLocal<>();; - - private FluidInsertable cachedTank; + private final IFluidHandler inputHandler = new InputFluidHandler(); + private final IFluidHandler outputHandler = new OutputFluidHandler(); public FluidP2PTunnelPart(final ItemStack is) { super(is); @@ -60,13 +60,10 @@ public class FluidP2PTunnelPart extends P2PTunnelPart implem @Override public void onTunnelNetworkChange() { - this.cachedTank = null; } @Override - public void onneighborUpdate(BlockView w, BlockPos pos, BlockPos neighbor) { - this.cachedTank = null; - + public void onNeighborChanged(IBlockReader w, BlockPos pos, BlockPos neighbor) { if (this.isOutput()) { final FluidP2PTunnelPart in = this.getInput(); if (in != null) { @@ -75,9 +72,17 @@ public class FluidP2PTunnelPart extends P2PTunnelPart implem } } + @SuppressWarnings("unchecked") @Override - public void addAllAttributes(AttributeList to) { - to.offer(this); + public LazyOptional getCapability(Capability capabilityClass) { + if (capabilityClass == CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY) { + if (this.isOutput()) { + return (LazyOptional) LazyOptional.of(() -> this.outputHandler); + } + return (LazyOptional) LazyOptional.of(() -> this.inputHandler); + } + + return super.getCapability(capabilityClass); } @Override @@ -85,122 +90,173 @@ public class FluidP2PTunnelPart extends P2PTunnelPart implem return MODELS.getModel(this.isPowered(), this.isActive()); } - @Override - public FluidVolume attemptInsertion(FluidVolume fluid, Simulation simulation) { + private IFluidHandler getAttachedFluidHandler() { + LazyOptional fluidHandler = LazyOptional.empty(); + if (this.isActive()) { + final TileEntity self = this.getTile(); + final TileEntity te = self.getWorld().getTileEntity(self.getPos().offset(this.getSide().getFacing())); - final Deque stack = this.getDepth(); - - for (final FluidP2PTunnelPart t : stack) { - if (t == this) { - return fluid; + if (te != null) { + fluidHandler = te.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY, + this.getSide().getOpposite().getFacing()); } } - - stack.push(this); - - final List list = this.getOutputs(fluid.getFluidKey()); - FluidAmount requestTotal = FluidAmount.ZERO; - - Iterator i = list.iterator(); - - while (i.hasNext()) { - final FluidP2PTunnelPart l = i.next(); - final FluidInsertable tank = l.getTarget(); - FluidAmount inserted; - if (tank != null) { - inserted = fluid.amount().sub(tank.attemptInsertion(fluid.copy(), Simulation.SIMULATE).amount()); - } else { - inserted = FluidAmount.ZERO; - } - - if (inserted.isZero()) { - i.remove(); - } else { - requestTotal = requestTotal.add(inserted); - } - } - - if (requestTotal.isZero()) { - if (stack.pop() != this) { - throw new IllegalStateException("Invalid Recursion detected."); - } - - return fluid; - } - - if (simulation != Simulation.ACTION) { - if (stack.pop() != this) { - throw new IllegalStateException("Invalid Recursion detected."); - } - - return fluid.copy().split(fluid.amount().min(requestTotal)); - } - - FluidAmount remaining = fluid.amount(); - - i = list.iterator(); - - while (i.hasNext() && !remaining.isZero()) { - final FluidP2PTunnelPart l = i.next(); - - FluidVolume insert = fluid.withAmount(fluid.amount().div(list.size())); - if (insert.amount().isGreaterThan(remaining)) { - insert = insert.withAmount(remaining); - } - - FluidInsertable tank = l.getTarget(); - if (tank != null) { - remaining = remaining - .sub(insert.amount().sub(tank.attemptInsertion(insert, Simulation.ACTION).amount())); - } - } - - if (stack.pop() != this) { - throw new IllegalStateException("Invalid Recursion detected."); - } - - return remaining.isZero() ? FluidVolumeUtil.EMPTY : fluid.withAmount(remaining); + return fluidHandler.orElse(NULL_FLUID_HANDLER); } - private Deque getDepth() { - Deque s = DEPTH.get(); + private class InputFluidHandler implements IFluidHandler { - if (s == null) { - DEPTH.set(s = new ArrayDeque<>()); + @Override + public int getTanks() { + return 1; } - return s; - } + @Override + @Nonnull + public FluidStack getFluidInTank(int tank) { + return FluidStack.EMPTY; + } - private List getOutputs(final FluidKey input) { - final List outs = new ArrayList<>(); + @Override + public int getTankCapacity(int tank) { + return Integer.MAX_VALUE; + } - try { - for (final FluidP2PTunnelPart l : this.getOutputs()) { - final FluidInsertable handler = l.getTarget(); + @Override + public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { + return true; + } - if (handler != null) { - outs.add(l); + @Override + public int fill(FluidStack resource, FluidAction action) { + int total = 0; + + try { + final int outputTunnels = FluidP2PTunnelPart.this.getOutputs().size(); + final int amount = resource.getAmount(); + + if (outputTunnels == 0 || amount == 0) { + return 0; } + + final int amountPerOutput = Math.max(1, amount / outputTunnels); + int overflow = amountPerOutput == 0 ? amount : amount % amountPerOutput; + + for (FluidP2PTunnelPart target : FluidP2PTunnelPart.this.getOutputs()) { + final IFluidHandler output = target.getAttachedFluidHandler(); + final int toSend = amountPerOutput + overflow; + final FluidStack fillWithFluidStack = resource.copy(); + fillWithFluidStack.setAmount(toSend); + + final int received = output.fill(fillWithFluidStack, action); + + overflow = toSend - received; + total += received; + } + + if (action == FluidAction.EXECUTE) { + FluidP2PTunnelPart.this.queueTunnelDrain(PowerUnits.RF, total); + } + } catch (GridAccessException ignored) { } - } catch (final GridAccessException e) { - // :P + + return total; + } + + @Override + @Nonnull + public FluidStack drain(FluidStack resource, FluidAction action) { + return FluidStack.EMPTY; + } + + @Override + @Nonnull + public FluidStack drain(int maxDrain, FluidAction action) { + return FluidStack.EMPTY; } - return outs; } - private FluidInsertable getTarget() { - if (!this.getProxy().isActive()) { - return null; + private class OutputFluidHandler implements IFluidHandler { + + @Override + public int getTanks() { + return FluidP2PTunnelPart.this.getAttachedFluidHandler().getTanks(); } - if (this.cachedTank != null) { - return this.cachedTank; + @Override + @Nonnull + public FluidStack getFluidInTank(int tank) { + return FluidP2PTunnelPart.this.getAttachedFluidHandler().getFluidInTank(tank); } - return this.cachedTank = FluidAttributes.INSERTABLE.getFirstOrNullFromNeighbour(this.getTile(), - this.getSide().getFacing()); + @Override + public int getTankCapacity(int tank) { + return FluidP2PTunnelPart.this.getAttachedFluidHandler().getTankCapacity(tank); + } + + @Override + public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { + return FluidP2PTunnelPart.this.getAttachedFluidHandler().isFluidValid(tank, stack); + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + return 0; + } + + @Override + @Nonnull + public FluidStack drain(FluidStack resource, FluidAction action) { + return FluidP2PTunnelPart.this.getAttachedFluidHandler().drain(resource, action); + } + + @Override + @Nonnull + public FluidStack drain(int maxDrain, FluidAction action) { + return FluidP2PTunnelPart.this.getAttachedFluidHandler().drain(maxDrain, action); + } + } + + private static class NullFluidHandler implements IFluidHandler { + + @Override + public int getTanks() { + return 0; + } + + @Override + @Nonnull + public FluidStack getFluidInTank(int tank) { + return FluidStack.EMPTY; + } + + @Override + public int getTankCapacity(int tank) { + return 0; + } + + @Override + public boolean isFluidValid(int tank, @Nonnull FluidStack stack) { + return false; + } + + @Override + public int fill(FluidStack resource, FluidAction action) { + return 0; + } + + @Override + @Nonnull + public FluidStack drain(FluidStack resource, FluidAction action) { + return FluidStack.EMPTY; + } + + @Override + @Nonnull + public FluidStack drain(int maxDrain, FluidAction action) { + return FluidStack.EMPTY; + } } }