From 7ed516e30ba224b4b8e3fad9c836c22ca00bfcdb Mon Sep 17 00:00:00 2001 From: Jason Mitchell Date: Fri, 20 Jan 2023 00:30:50 -0800 Subject: MTE Inventory updates (#1496) * MTE Inventory updates * Separate Input/Output inventory * Use a LinkedHashMap to ensure inventory orders are deterministic * Input/Output work on either Input/Output inventories * MTE Inventory * Add GT_Packet_MultiTileEntity * More dyanmic packet with packetFeatures * Add IMTE_HasModes for MultiBlockPart * Help with MTE Inventory (#1613) * convert inventory to use ItemStackHandler * Update MUI * inventories * move Iteminventory to its own method Co-authored-by: miozune * Update MUI * Update MUI * Add IMultiBlockPart * Mte fluid inventory (#1639) * first work on fluid inventory * make gui work with numbers not dividable by 4 * use math.min * add outputfluids saving * actually working * Update MUI Co-authored-by: miozune * Ticking Covers! * Parts now register covers with the controller * Controllers now tick covers on parts * Break cover ticking out into `tickCoverAtSide` Fix some inventory methods on MultiBlockController * Filter on tickable covers * Improve GUIs for MTEs (#1650) * working controller GUI * locked inventory selection work * input and output locking of inventories Co-authored-by: miozune * spotless * CoverInfo refactor (#1654) * Add `CoverInfo` and deprecate the old fields to hold cover information * Disable MTE registration * Fix NPE - Return EMPTY_INFO for SIDE_UNKNOWN Temporarily add back old NBT saving in case of a revert so covers aren't lost. * Actually save the old NBT data, instead of empty Co-authored-by: BlueWeabo <76872108+BlueWeabo@users.noreply.github.com> Co-authored-by: miozune --- .../multiblock/base/MultiBlockController.java | 452 +++++++++++++++++---- .../multiblock/base/MultiBlockPart.java | 274 +++++++++++-- 2 files changed, 619 insertions(+), 107 deletions(-) (limited to 'src/main/java/gregtech/api/multitileentity/multiblock') diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockController.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockController.java index bb01f0b4fa..49f2adcd40 100644 --- a/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockController.java +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockController.java @@ -1,6 +1,7 @@ package gregtech.api.multitileentity.multiblock.base; import static gregtech.GT_Mod.GT_FML_LOGGER; +import static gregtech.api.enums.GT_Values.ALL_VALID_SIDES; import static gregtech.api.enums.GT_Values.NBT; import com.gtnewhorizon.structurelib.StructureLibAPI; @@ -13,12 +14,26 @@ import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; import com.gtnewhorizon.structurelib.structure.IStructureDefinition; import com.gtnewhorizon.structurelib.structure.IStructureElement; import com.gtnewhorizon.structurelib.util.Vec3Impl; +import com.gtnewhorizons.modularui.api.ModularUITextures; +import com.gtnewhorizons.modularui.api.drawable.ItemDrawable; +import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; +import com.gtnewhorizons.modularui.api.forge.ListItemHandler; +import com.gtnewhorizons.modularui.api.screen.*; +import com.gtnewhorizons.modularui.api.widget.Widget; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget; +import com.gtnewhorizons.modularui.common.widget.MultiChildWidget; +import com.gtnewhorizons.modularui.common.widget.Scrollable; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; +import com.gtnewhorizons.modularui.common.widget.TabButton; +import com.gtnewhorizons.modularui.common.widget.TabContainer; import cpw.mods.fml.common.network.NetworkRegistry; import gnu.trove.list.TIntList; import gnu.trove.list.array.TIntArrayList; import gregtech.api.enums.GT_Values; import gregtech.api.enums.OrePrefixes; import gregtech.api.enums.TextureSet; +import gregtech.api.gui.modularui.GT_UITextures; import gregtech.api.interfaces.IDescribable; import gregtech.api.interfaces.tileentity.IMachineProgress; import gregtech.api.multitileentity.MultiTileEntityContainer; @@ -26,14 +41,19 @@ import gregtech.api.multitileentity.MultiTileEntityRegistry; import gregtech.api.multitileentity.interfaces.IMultiBlockController; import gregtech.api.multitileentity.interfaces.IMultiBlockFluidHandler; import gregtech.api.multitileentity.interfaces.IMultiBlockInventory; +import gregtech.api.multitileentity.interfaces.IMultiBlockPart; import gregtech.api.multitileentity.interfaces.IMultiTileEntity; import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_AddToolTips; import gregtech.api.multitileentity.machine.MultiTileBasicMachine; import gregtech.api.objects.GT_ItemStack; import gregtech.api.util.GT_Multiblock_Tooltip_Builder; import gregtech.api.util.GT_Utility; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -65,9 +85,8 @@ public abstract class MultiBlockController> ex protected BuildState buildState = new BuildState(); - // The 0th slot is the default inventory of the MultiBlock; any other has been added by an Inventory Extender of - // sorts - protected List multiBlockInventory = new ArrayList<>(); + protected Map multiBlockInputInventory = new LinkedHashMap<>(); + protected Map multiBlockOutputInventory = new LinkedHashMap<>(); private int mMaxProgresstime = 0, mProgresstime = 0; private boolean mStructureOkay = false, mStructureChanged = false; @@ -75,6 +94,16 @@ public abstract class MultiBlockController> ex private ExtendedFacing mExtendedFacing = ExtendedFacing.DEFAULT; private IAlignmentLimits mLimits = getInitialAlignmentLimits(); + // A list of sides + // Each side has a list of parts that have a cover that need to be ticked + protected List>> registeredCoveredParts = Arrays.asList( + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>(), + new LinkedList<>()); + /** Registry ID of the required casing */ public abstract short getCasingRegistryID(); /** Meta ID of the required casing */ @@ -125,7 +154,8 @@ public abstract class MultiBlockController> ex // Multiblock inventories are a collection of inventories. The first inventory is the default internal // inventory, // and the others are added by inventory extending blocks. - if (mInventory != null) multiBlockInventory.add(mInventory); + if (mInputInventory != null) multiBlockInputInventory.put("controller", mInputInventory); + if (mOutputInventory != null) multiBlockOutputInventory.put("controller", mOutputInventory); mStructureOkay = aNBT.getBoolean(NBT.STRUCTURE_OK); mExtendedFacing = ExtendedFacing.of( @@ -159,12 +189,6 @@ public abstract class MultiBlockController> ex protected GT_Multiblock_Tooltip_Builder getTooltip() { return createTooltip(); - // final int tooltipId = getToolTipID(); - // final GT_Multiblock_Tooltip_Builder tt = tooltip.get(tooltipId); - // if (tt == null) { - // return tooltip.computeIfAbsent(tooltipId, k -> createTooltip()); - // } - // return tt; } @Override @@ -277,8 +301,8 @@ public abstract class MultiBlockController> ex return super.onWrenchRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); if (aPlayer.isSneaking()) { // we won't be allowing horizontal flips, as it can be perfectly emulated by rotating twice and flipping - // horizontally - // allowing an extra round of flip make it hard to draw meaningful flip markers in GT_Proxy#drawGrid + // horizontally allowing an extra round of flip make it hard to draw meaningful flip markers in + // GT_Proxy#drawGrid toolSetFlip(getFlip().isHorizontallyFlipped() ? Flip.NONE : Flip.HORIZONTAL); } else { toolSetRotation(null); @@ -286,6 +310,27 @@ public abstract class MultiBlockController> ex return true; } + @Override + public void registerCoveredPartOnSide(final int aSide, IMultiBlockPart part) { + if (aSide < 0 || aSide >= 6) return; + + final LinkedList> registeredCovers = registeredCoveredParts.get(aSide); + // TODO: Make sure that we're not already registered on this side + registeredCovers.add(new WeakReference<>(part)); + } + + @Override + public void unregisterCoveredPartOnSide(final int aSide, IMultiBlockPart aPart) { + if (aSide < 0 || aSide >= 6) return; + + final LinkedList> coveredParts = registeredCoveredParts.get(aSide); + final Iterator> it = coveredParts.iterator(); + while (it.hasNext()) { + final IMultiBlockPart part = (it.next()).get(); + if (part == null || part == aPart) it.remove(); + } + } + @Override public void onFirstTick(boolean aIsServerSide) { super.onFirstTick(aIsServerSide); @@ -293,6 +338,32 @@ public abstract class MultiBlockController> ex else StructureLibAPI.queryAlignment(this); } + private boolean tickCovers() { + for (byte side : ALL_VALID_SIDES) { + // TODO: Tick controller covers, if any + final LinkedList> coveredParts = this.registeredCoveredParts.get(side); + final Iterator> it = coveredParts.iterator(); + while (it.hasNext()) { + final IMultiBlockPart part = (it.next()).get(); + if (part == null) { + it.remove(); + continue; + } + if (!part.tickCoverAtSide(side, mTickTimer)) it.remove(); + } + } + + return true; + } + + @Override + public void onTick(long aTimer, boolean isServerSide) { + // Tick all covers! + if (!tickCovers()) { + return; + } + } + @Override public void onPostTick(long aTick, boolean aIsServerSide) { if (aIsServerSide) { @@ -489,7 +560,7 @@ public abstract class MultiBlockController> ex mIcons = new IIcon[6]; Arrays.fill(mIcons, TextureSet.SET_NONE.mTextures[OrePrefixes.block.mTextureIndex].getIcon()); // Arrays.fill(mIcons, getTexture(aCasing); - // for (int i = 0; i < 6; i++) { + // for (byte i : ALL_VALID_SIDES) { // mIcons[i] = aCasing.getIcon(i, aMeta); // } } @@ -598,6 +669,13 @@ public abstract class MultiBlockController> ex return rInfo; } + @Override + public IFluidTank[] getFluidTanksForGUI(MultiBlockPart aPart) { + if (aPart.modeSelected(MultiBlockPart.FLUID_IN)) return mTanksInput; + if (aPart.modeSelected(MultiBlockPart.FLUID_OUT)) return mTanksOutput; + return GT_Values.emptyFluidTank; + } + /** * Energy - MultiBlock related Energy behavior */ @@ -699,10 +777,18 @@ public abstract class MultiBlockController> ex /** * Item - MultiBlock related Item behaviour. */ + protected boolean registerInventory(String invName, IItemHandlerModifiable inventory) { + if (multiBlockInputInventory.containsKey(invName)) return false; + multiBlockInputInventory.put(invName, inventory); + return true; + } + @Override public boolean hasInventoryBeenModified(MultiBlockPart aPart) { - // TODO: MultiInventory - Figure this out based on locked & the part - return hasInventoryBeenModified(); + if (aPart.modeSelected(MultiBlockPart.ITEM_IN)) return hasInventoryBeenModified(); + else if (aPart.modeSelected(MultiBlockPart.ITEM_OUT)) return hasOutputInventoryBeenModified(); + + return false; } @Override @@ -710,6 +796,20 @@ public abstract class MultiBlockController> ex return false; } + @Override + public IItemHandlerModifiable getInventoryForGUI(MultiBlockPart aPart) { + final Map multiBlockInventory = getMultiBlockInventory(aPart); + if (multiBlockInventory == null) return null; + + final String lockedInventory = aPart.getLockedInventory(); + if (lockedInventory == null) { + return new ListItemHandler(multiBlockInventory.values()); + } else { + final IItemHandlerModifiable inv = multiBlockInventory.get(lockedInventory); + return inv != null ? inv : null; + } + } + @Override public boolean addStackToSlot(MultiBlockPart aPart, int aIndex, ItemStack aStack) { return false; @@ -720,15 +820,25 @@ public abstract class MultiBlockController> ex return false; } - protected Pair getInventory(int lockedInventory, int aSlot) { - if (lockedInventory != -1) return new ImmutablePair<>(multiBlockInventory.get(lockedInventory), aSlot); + protected Map getMultiBlockInventory(MultiBlockPart aPart) { + if (aPart.modeSelected(MultiBlockPart.ITEM_IN)) return multiBlockInputInventory; + else if (aPart.modeSelected(MultiBlockPart.ITEM_OUT)) return multiBlockOutputInventory; + return null; + } + + protected Pair getInventory(MultiBlockPart aPart, int aSlot) { + final Map multiBlockInventory = getMultiBlockInventory(aPart); + if (multiBlockInventory == null) return null; + + final String invName = aPart.getLockedInventory(); + if (invName != null && !invName.isEmpty()) return new ImmutablePair<>(multiBlockInventory.get(invName), aSlot); int start = 0; - for (ItemStack[] inv : multiBlockInventory) { - if (aSlot > start && aSlot < start + inv.length) { + for (IItemHandlerModifiable inv : multiBlockInventory.values()) { + if (aSlot >= start && aSlot < start + inv.getSlots()) { return new ImmutablePair<>(inv, aSlot - start); } - start += inv.length; + start += inv.getSlots(); } return null; } @@ -736,16 +846,22 @@ public abstract class MultiBlockController> ex @Override public int[] getAccessibleSlotsFromSide(MultiBlockPart aPart, byte aSide) { final TIntList tList = new TIntArrayList(); - final int lockedInventory = aPart.getLockedInventory(); + final Map multiBlockInventory = getMultiBlockInventory(aPart); + if (multiBlockInventory == null) return tList.toArray(); + + final String lockedInventory = aPart.getLockedInventory(); + // Item in --> input inv + // Item out --> output inv int start = 0; - if (lockedInventory == -1) { - for (ItemStack[] inv : multiBlockInventory) { - for (int i = start; i < inv.length + start; i++) tList.add(i); - start += inv.length; + if (lockedInventory == null) { + for (IItemHandlerModifiable inv : multiBlockInventory.values()) { + for (int i = start; i < inv.getSlots() + start; i++) tList.add(i); + start += inv.getSlots(); } } else { - final int len = multiBlockInventory.get(lockedInventory).length; + final IItemHandlerModifiable inv = multiBlockInventory.get(lockedInventory); + final int len = inv != null ? inv.getSlots() : 0; for (int i = 0; i < len; i++) tList.add(i); } return tList.toArray(); @@ -753,64 +869,59 @@ public abstract class MultiBlockController> ex @Override public boolean canInsertItem(MultiBlockPart aPart, int aSlot, ItemStack aStack, byte aSide) { - final int lockedInventory = aPart.getLockedInventory(), tSlot; - final ItemStack[] inv; - if (lockedInventory == -1) { - final Pair tInv = getInventory(lockedInventory, aSlot); - if (tInv == null) return false; - inv = tInv.getLeft(); - tSlot = tInv.getRight(); - } else { - inv = multiBlockInventory.get(lockedInventory); - tSlot = aSlot; - } - return inv[tSlot] == null + final Pair tInv = getInventory(aPart, aSlot); + if (tInv == null) return false; + + final int tSlot = tInv.getRight(); + final IItemHandlerModifiable inv = tInv.getLeft(); + ; + + return inv.getStackInSlot(tSlot) == null || GT_Utility.areStacksEqual( - aStack, inv[tSlot]); // && allowPutStack(getBaseMetaTileEntity(), aIndex, (byte) aSide, aStack) + aStack, + inv.getStackInSlot( + tSlot)); // && allowPutStack(getBaseMetaTileEntity(), aIndex, (byte) aSide, aStack) } @Override public boolean canExtractItem(MultiBlockPart aPart, int aSlot, ItemStack aStack, byte aSide) { - final int lockedInventory = aPart.getLockedInventory(), tSlot; - final ItemStack[] inv; - if (lockedInventory == -1) { - final Pair tInv = getInventory(lockedInventory, aSlot); - if (tInv == null) return false; - inv = tInv.getLeft(); - tSlot = tInv.getRight(); - } else { - inv = multiBlockInventory.get(lockedInventory); - tSlot = aSlot; - } - return inv[tSlot] != null; // && allowPullStack(getBaseMetaTileEntity(), aIndex, (byte) aSide, aStack); + final Pair tInv = getInventory(aPart, aSlot); + if (tInv == null) return false; + + final int tSlot = tInv.getRight(); + final IItemHandlerModifiable inv = tInv.getLeft(); + ; + + return inv.getStackInSlot(tSlot) + != null; // && allowPullStack(getBaseMetaTileEntity(), aIndex, (byte) aSide, aStack); } @Override public int getSizeInventory(MultiBlockPart aPart) { - final int lockedInventory = aPart.getLockedInventory(); - if (lockedInventory == -1) { + final Map multiBlockInventory = getMultiBlockInventory(aPart); + if (multiBlockInventory == null) return 0; + + final String lockedInventory = aPart.getLockedInventory(); + if (lockedInventory == null) { int len = 0; - for (ItemStack[] inv : multiBlockInventory) len += inv.length; + for (IItemHandlerModifiable inv : multiBlockInventory.values()) len += inv.getSlots(); return len; } else { - return multiBlockInventory.get(lockedInventory).length; + final IItemHandlerModifiable inv = multiBlockInventory.get(lockedInventory); + return inv != null ? inv.getSlots() : 0; } } @Override public ItemStack getStackInSlot(MultiBlockPart aPart, int aSlot) { - final int lockedInventory = aPart.getLockedInventory(), tSlot; - final ItemStack[] inv; - if (lockedInventory == -1) { - final Pair tInv = getInventory(lockedInventory, aSlot); - if (tInv == null) return null; - inv = tInv.getLeft(); - tSlot = tInv.getRight(); - } else { - inv = multiBlockInventory.get(lockedInventory); - tSlot = aSlot; - } - return inv[tSlot]; + final Pair tInv = getInventory(aPart, aSlot); + if (tInv == null) return null; + + final int tSlot = tInv.getRight(); + final IItemHandlerModifiable inv = tInv.getLeft(); + if (inv == null) return null; + + return inv.getStackInSlot(tSlot); } @Override @@ -819,10 +930,10 @@ public abstract class MultiBlockController> ex ItemStack rStack = GT_Utility.copyOrNull(tStack); if (tStack != null) { if (tStack.stackSize <= aDecrement) { - setInventorySlotContents(aSlot, null); + setInventorySlotContents(aPart, aSlot, null); } else { rStack = tStack.splitStack(aDecrement); - if (tStack.stackSize == 0) setInventorySlotContents(aSlot, null); + if (tStack.stackSize == 0) setInventorySlotContents(aPart, aSlot, null); } } return rStack; @@ -830,30 +941,52 @@ public abstract class MultiBlockController> ex @Override public ItemStack getStackInSlotOnClosing(MultiBlockPart aPart, int aSlot) { - final Pair tInv = getInventory(aPart.getLockedInventory(), aSlot); + final Pair tInv = getInventory(aPart, aSlot); if (tInv == null) return null; - final ItemStack[] inv = tInv.getLeft(); + final IItemHandlerModifiable inv = tInv.getLeft(); final int tSlot = tInv.getRight(); - final ItemStack rStack = inv[tSlot]; - inv[tSlot] = null; + final ItemStack rStack = inv.getStackInSlot(tSlot); + inv.setStackInSlot(tSlot, null); return rStack; } @Override public void setInventorySlotContents(MultiBlockPart aPart, int aSlot, ItemStack aStack) { - final Pair tInv = getInventory(aPart.getLockedInventory(), aSlot); + final Pair tInv = getInventory(aPart, aSlot); if (tInv == null) return; - final ItemStack[] inv = tInv.getLeft(); + final IItemHandlerModifiable inv = tInv.getLeft(); final int tSlot = tInv.getRight(); - inv[tSlot] = aStack; + inv.setStackInSlot(tSlot, aStack); + } + + @Override + public List getInventoryNames(MultiBlockPart aPart) { + final List inventoryNames = new ArrayList<>(); + inventoryNames.add("all"); + inventoryNames.addAll(getMultiBlockInventory(aPart).keySet()); + return inventoryNames; } @Override public String getInventoryName(MultiBlockPart aPart) { - return getInventoryName(); // TODO: MultiInventory: Include part Name? + final StringBuilder str = new StringBuilder(); + str.append(getInventoryName()); + if (aPart.modeSelected(MultiBlockPart.ITEM_IN)) { + str.append(" Input"); + } else if (aPart.modeSelected(MultiBlockPart.ITEM_OUT)) { + str.append(" Output"); + } else { + str.append(" Unknown"); + } + final String lockedInventory = aPart.getLockedInventory(); + if (lockedInventory != null && !lockedInventory.equals("")) { + str.append(" [Locked: ").append(lockedInventory).append("]"); + } + + return str.toString(); } @Override @@ -868,9 +1001,9 @@ public abstract class MultiBlockController> ex @Override public void markDirty(MultiBlockPart aPart) { - // TODO: MultiInventory - Consider the part? markDirty(); - markInventoryBeenModified(); + if (aPart.modeSelected(MultiBlockPart.ITEM_OUT)) markOutputInventoryBeenModified(); + else markInventoryBeenModified(); } @Override @@ -894,4 +1027,165 @@ public abstract class MultiBlockController> ex public boolean isItemValidForSlot(MultiBlockPart aPart, int aSlot, ItemStack aStack) { return isItemValidForSlot(aSlot, aStack); } + + /** + * GUI Work - Multiblock GUI related methods + */ + @Override + public boolean useModularUI() { + return true; + } + + @Override + public ModularWindow createWindow(UIBuildContext buildContext) { + System.out.println("MultiBlockController::createWindow"); + return super.createWindow(buildContext); + } + + @Override + public boolean hasGui(byte aSide) { + return true; + } + + @Override + protected void addTitleTextStyle(ModularWindow.Builder builder, String title) { + // leave empty + } + + @Override + public void addGregTechLogo(ModularWindow.Builder builder) { + builder.widget(new DrawableWidget() + .setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(148, 60)); + } + + @Override + public void addUIWidgets(ModularWindow.Builder builder, UIBuildContext buildContext) { + builder.widget(new TabContainer() + .setButtonSize(20, 24) + .addTabButton(new TabButton(0) + .setBackground( + false, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0, 1f, 0.5f)) + .setBackground( + true, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0.5f, 1f, 1f)) + .addTooltip(getLocalName()) + .setPos(0, -20)) + .addTabButton(new TabButton(1) + .setBackground( + false, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0, 1f, 0.5f)) + .setBackground( + true, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0.5f, 1f, 1f)) + .setPos(20, -20)) + .addTabButton(new TabButton(2) + .setBackground( + false, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0, 1f, 0.5f)) + .setBackground( + true, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0.5f, 1f, 1f)) + .setPos(40, -20)) + .addTabButton(new TabButton(3) + .setBackground( + false, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0, 1f, 0.5f)) + .setBackground( + true, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0.5f, 1f, 1f)) + .setPos(60, -20)) + .addTabButton(new TabButton(4) + .setBackground( + false, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0, 1f, 0.5f)) + .setBackground( + true, ModularUITextures.VANILLA_TAB_TOP_START.getSubArea(0, 0.5f, 1f, 1f)) + .setPos(80, -20)) + .addPage(new MultiChildWidget() + .addChild(new DrawableWidget() + .setDrawable(GT_UITextures.PICTURE_SCREEN_BLACK) + .setPos(7, 4) + .setSize(160, 75))) + .addPage(new MultiChildWidget().addChild(getItemInventoryInputGUI())) + .addPage(new MultiChildWidget().addChild(getItemInventoryOutputGUI())) + .addPage(new MultiChildWidget().addChild(getFluidInventoryInputGUI())) + .addPage(new MultiChildWidget().addChild(getFluidInventoryOutputGUI()))) + .widget(new ItemDrawable(getStackForm(1)) + .asWidget() + .setSize(16, 16) + .setPos(2, -16)) + .widget(new DrawableWidget() + .setDrawable(GT_UITextures.PICTURE_ITEM_IN) + .setSize(16, 16) + .setPos(22, -16)) + .widget(new DrawableWidget() + .setDrawable(GT_UITextures.PICTURE_ITEM_OUT) + .setSize(16, 16) + .setPos(42, -16)) + .widget(new DrawableWidget() + .setDrawable(GT_UITextures.PICTURE_FLUID_IN) + .setSize(16, 16) + .setPos(62, -16)) + .widget(new DrawableWidget() + .setDrawable(GT_UITextures.PICTURE_FLUID_OUT) + .setSize(16, 16) + .setPos(82, -16)); + } + + protected Widget getItemInventoryInputGUI() { + final IItemHandlerModifiable inv = getInventoriesForInput(); + final Scrollable scrollable = new Scrollable().setVerticalScroll(); + for (int rows = 0; rows * 4 < Math.min(inv.getSlots(), 128); rows++) { + final int columnsToMake = Math.min(Math.min(inv.getSlots(), 128) - rows * 4, 4); + for (int column = 0; column < columnsToMake; column++) { + scrollable.widget(new SlotWidget(inv, rows * 4 + column) + .setPos(column * 18, rows * 18) + .setSize(18, 18)); + } + } + return scrollable.setSize(18 * 4 + 4, 18 * 4).setPos(52, 7); + } + + protected Widget getItemInventoryOutputGUI() { + final IItemHandlerModifiable inv = getInventoriesForOutput(); + final Scrollable scrollable = new Scrollable().setVerticalScroll(); + for (int rows = 0; rows * 4 < Math.min(inv.getSlots(), 128); rows++) { + final int columnsToMake = Math.min(Math.min(inv.getSlots(), 128) - rows * 4, 4); + for (int column = 0; column < columnsToMake; column++) { + scrollable.widget(new SlotWidget(inv, rows * 4 + column) + .setPos(column * 18, rows * 18) + .setSize(18, 18)); + } + } + return scrollable.setSize(18 * 4 + 4, 18 * 4).setPos(52, 7); + } + + protected IItemHandlerModifiable getInventoriesForInput() { + return new ListItemHandler(multiBlockInputInventory.values()); + } + + protected IItemHandlerModifiable getInventoriesForOutput() { + return new ListItemHandler(multiBlockOutputInventory.values()); + } + + protected Widget getFluidInventoryInputGUI() { + final IFluidTank[] tanks = mTanksInput; + final Scrollable scrollable = new Scrollable().setVerticalScroll(); + for (int rows = 0; rows * 4 < tanks.length; rows++) { + final int columnsToMake = Math.min(tanks.length - rows * 4, 4); + for (int column = 0; column < columnsToMake; column++) { + final FluidSlotWidget fluidSlot = new FluidSlotWidget(tanks[rows * 4 + column]); + scrollable.widget(fluidSlot.setPos(column * 18, rows * 18).setSize(18, 18)); + } + } + return scrollable.setSize(18 * 4 + 4, 18 * 4).setPos(52, 7); + } + + protected Widget getFluidInventoryOutputGUI() { + final IFluidTank[] tanks = mTanksOutput; + final Scrollable scrollable = new Scrollable().setVerticalScroll(); + for (int rows = 0; rows * 4 < tanks.length; rows++) { + final int columnsToMake = Math.min(tanks.length - rows * 4, 4); + for (int column = 0; column < columnsToMake; column++) { + final FluidSlotWidget fluidSlot = new FluidSlotWidget(tanks[rows * 4 + column]); + fluidSlot.setInteraction(true, false); + scrollable.widget(fluidSlot.setPos(column * 18, rows * 18).setSize(18, 18)); + } + } + return scrollable.setSize(18 * 4 + 4, 18 * 4).setPos(52, 7); + } } diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java index 628992a931..36fbf35961 100644 --- a/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java @@ -15,6 +15,15 @@ import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_IN; import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; import static org.apache.commons.lang3.ObjectUtils.firstNonNull; +import com.gtnewhorizons.modularui.api.forge.IItemHandlerModifiable; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; +import com.gtnewhorizons.modularui.api.screen.ModularWindow.Builder; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; +import com.gtnewhorizons.modularui.common.widget.DropDownWidget; +import com.gtnewhorizons.modularui.common.widget.FluidSlotWidget; +import com.gtnewhorizons.modularui.common.widget.Scrollable; +import com.gtnewhorizons.modularui.common.widget.SlotWidget; import gregtech.api.enums.GT_Values; import gregtech.api.enums.Textures; import gregtech.api.interfaces.IIconContainer; @@ -22,11 +31,12 @@ import gregtech.api.interfaces.ITexture; import gregtech.api.multitileentity.MultiTileEntityRegistry; import gregtech.api.multitileentity.base.BaseNontickableMultiTileEntity; import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.interfaces.IMultiBlockPart; import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_BreakBlock; +import gregtech.api.multitileentity.interfaces.IMultiTileEntity.IMTE_HasModes; import gregtech.api.render.TextureFactory; -import gregtech.api.util.GT_CoverBehaviorBase; import gregtech.api.util.GT_Utility; -import gregtech.api.util.ISerializableObject; +import gregtech.common.covers.CoverInfo; import java.math.RoundingMode; import java.util.ArrayList; import java.util.Arrays; @@ -44,8 +54,10 @@ import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.fluids.Fluid; import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidTankInfo; +import net.minecraftforge.fluids.IFluidTank; -public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IMTE_BreakBlock { +public class MultiBlockPart extends BaseNontickableMultiTileEntity + implements IMultiBlockPart, IMTE_BreakBlock, IMTE_HasModes { public static final int NOTHING = 0, ENERGY_IN = B[0], ENERGY_OUT = B[1], @@ -63,6 +75,9 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM protected int mAllowedModes = NOTHING; // BITMASK - Modes allowed for this part protected byte mMode = 0; // Mode selected for this part + protected String mLockedInventory = GT_Values.E; + protected int mLockedInventoryIndex = 0; + /** * What Part Tier is this part? All Basic Casings are Tier 1, and will allow: * Energy, Item, Fluid input/output. Some of the more advanced modes can be set to require a higher tier part. @@ -71,14 +86,15 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM return 1; } - public int getLockedInventory() { - return -1; + public String getLockedInventory() { + return mLockedInventory.equals("") ? null : mLockedInventory; } public void setTarget(IMultiBlockController aTarget, int aAllowedModes) { mTarget = aTarget; mTargetPos = (mTarget == null ? null : mTarget.getCoords()); mAllowedModes = aAllowedModes; + if (mTarget != null) registerCovers(mTarget); } @Override @@ -106,6 +122,8 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM final TileEntity te = worldObj.getTileEntity(mTargetPos.posX, mTargetPos.posY, mTargetPos.posZ); if (te instanceof IMultiBlockController) { mTarget = (IMultiBlockController) te; + // Register our covers with the controller + registerCovers(mTarget); } else { mTargetPos = null; } @@ -116,6 +134,46 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM } else return mTarget; } + public void registerCovers(IMultiBlockController controller) { + for (byte i : ALL_VALID_SIDES) { + final CoverInfo coverInfo = getCoverInfoAtSide(i); + if (coverInfo.isValid() && coverInfo.getTickRate() > 0) { + controller.registerCoveredPartOnSide(i, this); + } + } + } + + @Override + public void setCoverItemAtSide(byte aSide, ItemStack aCover) { + super.setCoverItemAtSide(aSide, aCover); + // TODO: Filter on tickable covers + final IMultiBlockController tTarget = getTarget(true); + if (tTarget != null) { + final CoverInfo coverInfo = getCoverInfoAtSide(aSide); + if (coverInfo.isValid() && coverInfo.getTickRate() > 0) { + tTarget.registerCoveredPartOnSide(aSide, this); + } + } + } + + public void unregisterCovers(IMultiBlockController controller) { + for (byte i : ALL_VALID_SIDES) { + if (getCoverInfoAtSide(i).isValid()) { + controller.unregisterCoveredPartOnSide(i, this); + } + } + } + + @Override + public boolean dropCover(byte aSide, byte aDroppedSide, boolean aForced) { + final boolean res = super.dropCover(aSide, aDroppedSide, aForced); + final IMultiBlockController tTarget = getTarget(true); + if (tTarget != null) { + tTarget.unregisterCoveredPartOnSide(aSide, this); + } + return res; + } + @Override public void readMultiTileNBT(NBTTagCompound aNBT) { if (aNBT.hasKey(NBT.ALLOWED_MODES)) mAllowedModes = aNBT.getInteger(NBT.ALLOWED_MODES); @@ -124,6 +182,12 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM mTargetPos = new ChunkCoordinates( aNBT.getInteger(NBT.TARGET_X), aNBT.getShort(NBT.TARGET_Y), aNBT.getInteger(NBT.TARGET_Z)); } + if (aNBT.hasKey(NBT.LOCKED_INVENTORY)) { + mLockedInventory = aNBT.getString(NBT.LOCKED_INVENTORY); + } + if (aNBT.hasKey(NBT.LOCKED_INVENTORY_INDEX)) { + mLockedInventoryIndex = aNBT.getInteger(NBT.LOCKED_INVENTORY_INDEX); + } } @Override @@ -136,12 +200,49 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM aNBT.setShort(NBT.TARGET_Y, (short) mTargetPos.posY); aNBT.setInteger(NBT.TARGET_Z, mTargetPos.posZ); } + if (mLockedInventory != null) { + aNBT.setString(NBT.LOCKED_INVENTORY, mLockedInventory); + } + if (mLockedInventoryIndex != 0) { + aNBT.setInteger(NBT.LOCKED_INVENTORY_INDEX, mLockedInventoryIndex); + } + } + + @Override + public void setTargetPos(ChunkCoordinates aTargetPos) { + mTargetPos = aTargetPos; + } + + @Override + public ChunkCoordinates getTargetPos() { + return mTargetPos; + } + + @Override + public void setMode(byte aMode) { + mMode = aMode; + } + + @Override + public byte getMode() { + return mMode; + } + + @Override + public int getAllowedModes() { + return mAllowedModes; + } + + @Override + public void setAllowedModes(int aAllowedModes) { + mAllowedModes = aAllowedModes; } /** * True if `aMode` is one of the allowed modes */ public boolean hasMode(int aMode) { + // This is not sent to the client return (mAllowedModes & aMode) != 0; } @@ -158,7 +259,10 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM @Override public boolean breakBlock() { final IMultiBlockController tTarget = getTarget(false); - if (tTarget != null) tTarget.onStructureChange(); + if (tTarget != null) { + unregisterCovers(tTarget); + tTarget.onStructureChange(); + } return false; } @@ -175,16 +279,6 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM } } - @Override - public byte getTextureData() { - return mMode; - } - - @Override - public void setTextureData(byte aData) { - mMode = aData; - } - @Override public void loadTextureNBT(NBTTagCompound aNBT) { // Loading the registry @@ -365,13 +459,10 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM final IMultiBlockController controller = getTarget(true); if (controller == null) return GT_Values.emptyFluidTankInfo; - final GT_CoverBehaviorBase tCover = getCoverBehaviorAtSideNew(aSide); - final int coverId = getCoverIDAtSide(aSide); - final ISerializableObject complexCoverData = getComplexCoverDataAtSide(aSide); + final CoverInfo coverInfo = getCoverInfoAtSide(aSide); - if ((controller.isLiquidInput(aSide) && tCover.letsFluidIn(aSide, coverId, complexCoverData, null, controller)) - || (controller.isLiquidOutput(aSide) - && tCover.letsFluidOut(aSide, coverId, complexCoverData, null, controller))) + if ((controller.isLiquidInput(aSide) && coverInfo.letsFluidIn(null, controller)) + || (controller.isLiquidOutput(aSide) && coverInfo.letsFluidOut(null, controller))) return controller.getTankInfo(this, aDirection); return GT_Values.emptyFluidTankInfo; @@ -441,7 +532,7 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM @Override public boolean decreaseStoredEnergyUnits(long aEnergy, boolean aIgnoreTooLittleEnergy) { - if (!modeSelected(ENERGY_IN)) return false; + if (!modeSelected(ENERGY_OUT)) return false; final IMultiBlockController controller = getTarget(true); return controller != null && hasMode(ENERGY_OUT) @@ -536,14 +627,14 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM @Override public boolean addStackToSlot(int aIndex, ItemStack aStack) { - if (!modeSelected(ITEM_IN)) return false; + if (!modeSelected(ITEM_IN, ITEM_OUT)) return false; final IMultiBlockController controller = getTarget(true); return (controller != null && controller.addStackToSlot(this, aIndex, aStack)); } @Override public boolean addStackToSlot(int aIndex, ItemStack aStack, int aAmount) { - if (!modeSelected(ITEM_IN)) return false; + if (!modeSelected(ITEM_IN, ITEM_OUT)) return false; final IMultiBlockController controller = getTarget(true); return (controller != null && controller.addStackToSlot(this, aIndex, aStack, aAmount)); } @@ -558,7 +649,7 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM @Override public boolean canInsertItem(int aSlot, ItemStack aStack, int aSide) { - if (!modeSelected(ITEM_IN) + if (!modeSelected(ITEM_IN, ITEM_OUT) || (mFacing != SIDE_UNKNOWN && (mFacing != aSide || !coverLetsItemsIn((byte) aSide, aSlot)))) return false; final IMultiBlockController controller = getTarget(true); @@ -567,7 +658,7 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM @Override public boolean canExtractItem(int aSlot, ItemStack aStack, int aSide) { - if (!modeSelected(ITEM_OUT) + if (!modeSelected(ITEM_IN, ITEM_OUT) || (mFacing != SIDE_UNKNOWN && (mFacing != aSide || !coverLetsItemsOut((byte) aSide, aSlot)))) return false; final IMultiBlockController controller = getTarget(true); @@ -590,7 +681,7 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM @Override public ItemStack decrStackSize(int aSlot, int aDecrement) { - if (!modeSelected(ITEM_OUT)) return null; + if (!modeSelected(ITEM_IN, ITEM_OUT)) return null; final IMultiBlockController controller = getTarget(true); return controller != null ? controller.decrStackSize(this, aSlot, aDecrement) : null; } @@ -628,4 +719,131 @@ public class MultiBlockPart extends BaseNontickableMultiTileEntity implements IM // End Inventory + // === Modular UI === + @Override + public boolean useModularUI() { + return true; + } + + @Override + public String getLocalName() { + if (modeSelected(ITEM_IN)) return "Input Inventory"; + if (modeSelected(ITEM_OUT)) return "Output Inventory"; + if (modeSelected(FLUID_IN)) return "Fluid Input Hatch"; + if (modeSelected(FLUID_OUT)) return "Fluid Output Hatch"; + + return "Unknown"; + } + + @Override + public boolean hasGui(byte aSide) { + // UIs only for specific mode(s) + if (modeSelected(ITEM_IN, ITEM_OUT, FLUID_IN, FLUID_OUT)) return true; + + return false; + } + + protected void addItemInventory(Builder builder, UIBuildContext buildContext) { + final IMultiBlockController controller = getTarget(false); + if (controller == null) { + return; + } + final IItemHandlerModifiable inv = controller.getInventoryForGUI(this); + final Scrollable scrollable = new Scrollable().setVerticalScroll(); + for (int rows = 0; rows * 4 < Math.min(inv.getSlots(), 128); rows++) { + int columnsToMake = Math.min(Math.min(inv.getSlots(), 128) - rows * 4, 4); + for (int column = 0; column < columnsToMake; column++) { + scrollable.widget(new SlotWidget(inv, rows * 4 + column) + .setPos(column * 18, rows * 18) + .setSize(18, 18)); + } + } + builder.widget(scrollable.setSize(18 * 4 + 4, 18 * 4).setPos(52, 18)); + DropDownWidget dropDown = new DropDownWidget(); + builder.widget(dropDown.addDropDownItemsSimple( + controller.getInventoryNames(this), + (buttonWidget, index, label, setSelected) -> buttonWidget.setOnClick((clickData, widget) -> { + if (getNameOfInventoryFromIndex(controller, index).equals("all")) { + mLockedInventory = GT_Values.E; + mLockedInventoryIndex = 0; + } else { + mLockedInventory = getNameOfInventoryFromIndex(controller, index); + mLockedInventoryIndex = index; + } + setSelected.run(); + }), + true) + .setSelected(mLockedInventoryIndex) + .setExpandedMaxHeight(60) + .setDirection(DropDownWidget.Direction.DOWN) + .setPos(53, 5) + .setSize(70, 11)); + } + + protected String getNameOfInventoryFromIndex(final IMultiBlockController controller, int index) { + final List invNames = controller.getInventoryNames(this); + if (index > invNames.size()) { + return invNames.get(0); + } + return invNames.get(index); + } + + protected void addFluidInventory(Builder builder, UIBuildContext buildContext) { + final IMultiBlockController controller = getTarget(false); + if (controller == null) { + return; + } + final IFluidTank[] tanks = controller.getFluidTanksForGUI(this); + final Scrollable scrollable = new Scrollable().setVerticalScroll(); + for (int rows = 0; rows * 4 < tanks.length; rows++) { + int columnsToMake = Math.min(tanks.length - rows * 4, 4); + for (int column = 0; column < columnsToMake; column++) { + FluidSlotWidget fluidSlot = new FluidSlotWidget(tanks[rows * 4 + column]); + if (modeSelected(FLUID_OUT)) { + fluidSlot.setInteraction(true, false); + } + scrollable.widget(fluidSlot.setPos(column * 18, rows * 18).setSize(18, 18)); + } + } + builder.widget(scrollable.setSize(18 * 4 + 4, 18 * 4).setPos(52, 7)); + } + + @Override + public void addUIWidgets(Builder builder, UIBuildContext buildContext) { + if (modeSelected(ITEM_IN, ITEM_OUT)) { + addItemInventory(builder, buildContext); + } + if (modeSelected(FLUID_IN, FLUID_OUT)) { + addFluidInventory(builder, buildContext); + } + } + + @Override + public ModularWindow createWindow(UIBuildContext buildContext) { + if (isServerSide()) { + issueClientUpdate(); + } + System.out.println("MultiBlockPart::createWindow"); + return super.createWindow(buildContext); + } + + @Override + protected int getGUIHeight() { + if (modeSelected(ITEM_IN, ITEM_OUT)) { + return super.getGUIHeight() + 11; + } + return super.getGUIHeight(); + } + + @Override + public void addGregTechLogo(Builder builder) { + if (modeSelected(ITEM_IN, ITEM_OUT)) { + builder.widget(new DrawableWidget() + .setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 74)); + } else { + super.addGregTechLogo(builder); + } + } } -- cgit