From 88bddb9b940f5b53bbc3c7252f4a28584038b2d7 Mon Sep 17 00:00:00 2001 From: Darkere Date: Sun, 21 Nov 2021 20:03:24 +0100 Subject: [PATCH] add more slots to the processing pattern grid and combine fluids and items(#2535) * WIP more slots in the patterngrid * implement more slots for processing pattern * simplified some calculations, some comments * bugfixes * fix accidental formatting changes * port to 1.16 and some cleanup * more cleanup * make code readable * Combine fluid and item view in pattern grid * changelog --- CHANGELOG.md | 2 + .../apiimpl/autocrafting/AllowedTagList.java | 4 +- .../autocrafting/CraftingPatternFactory.java | 3 +- .../apiimpl/network/node/GridNetworkNode.java | 35 +++--- .../container/GridContainer.java | 83 +++++++++++-- .../slot/filter/FluidFilterSlot.java | 5 + .../refinedstorage/item/PatternItem.java | 3 +- .../grid/GridProcessingTransferMessage.java | 35 ++---- .../screen/grid/GridScreen.java | 113 +++++++++++++++++- .../screen/widget/ScrollbarWidget.java | 13 +- .../widget/sidebutton/TypeSideButton.java | 4 +- .../resources/META-INF/accesstransformer.cfg | 1 + .../textures/gui/pattern_grid_processing.png | Bin 2818 -> 2816 bytes .../assets/refinedstorage/textures/icons.png | Bin 5716 -> 4806 bytes 14 files changed, 237 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d1ad9417e..3f5b2eb89 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ # Refined Storage Changelog ### 1.9.17 +- Added more slots to the Pattern Grid (Darkere) +- Combined Fluid and Item View in the Pattern Grid (Darkere) ### 1.9.16 diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/AllowedTagList.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/AllowedTagList.java index 6bc7f1732..2fab37882 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/AllowedTagList.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/AllowedTagList.java @@ -22,8 +22,8 @@ public class AllowedTagList { @Nullable private final Runnable listener; - public AllowedTagList(@Nullable Runnable listener) { - for (int i = 0; i < 9; ++i) { + public AllowedTagList(@Nullable Runnable listener, int size) { + for (int i = 0; i < size; ++i) { allowedItemTags.add(new HashSet<>()); allowedFluidTags.add(new HashSet<>()); } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternFactory.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternFactory.java index 6655f7b52..305801d54 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternFactory.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/autocrafting/CraftingPatternFactory.java @@ -2,6 +2,7 @@ package com.refinedmods.refinedstorage.apiimpl.autocrafting; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPattern; import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternContainer; +import com.refinedmods.refinedstorage.apiimpl.network.node.GridNetworkNode; import com.refinedmods.refinedstorage.item.PatternItem; import net.minecraft.fluid.Fluid; import net.minecraft.inventory.CraftingInventory; @@ -39,7 +40,7 @@ public class CraftingPatternFactory { try { if (processing) { - for (int i = 0; i < 9; ++i) { + for (int i = 0; i < GridNetworkNode.PROCESSING_MATRIX_SIZE; ++i) { fillProcessingInputs(i, stack, inputs, outputs, allowedTagList); fillProcessingFluidInputs(i, stack, fluidInputs, fluidOutputs, allowedTagList); } diff --git a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/GridNetworkNode.java b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/GridNetworkNode.java index 098e2df48..2340e634c 100644 --- a/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/GridNetworkNode.java +++ b/src/main/java/com/refinedmods/refinedstorage/apiimpl/network/node/GridNetworkNode.java @@ -79,7 +79,9 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I private static final String NBT_PROCESSING_MATRIX_FLUIDS = "ProcessingMatrixFluids"; private static final String NBT_ALLOWED_TAGS = "AllowedTags"; - private final AllowedTagList allowedTagList = new AllowedTagList(this::updateAllowedTags); + public static final int PROCESSING_MATRIX_SIZE = 81; + + private final AllowedTagList allowedTagList = new AllowedTagList(this::updateAllowedTags, PROCESSING_MATRIX_SIZE); private final Container craftingContainer = new Container(ContainerType.CRAFTING, 0) { @Override @@ -97,17 +99,17 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I private ICraftingRecipe currentRecipe; private final CraftingInventory matrix = new CraftingInventory(craftingContainer, 3, 3); private final CraftResultInventory result = new CraftResultInventory(); - private final BaseItemHandler processingMatrix = new BaseItemHandler(9 * 2) + private final BaseItemHandler processingMatrix = new BaseItemHandler(PROCESSING_MATRIX_SIZE * 2) .addListener(new NetworkNodeInventoryListener(this)) .addListener((handler, slot, reading) -> { - if (!reading && slot < 9) { + if (!reading && slot < PROCESSING_MATRIX_SIZE) { allowedTagList.clearItemTags(slot); } }); - private final FluidInventory processingMatrixFluids = new FluidInventory(9 * 2, FluidAttributes.BUCKET_VOLUME * 64) + private final FluidInventory processingMatrixFluids = new FluidInventory(PROCESSING_MATRIX_SIZE * 2, FluidAttributes.BUCKET_VOLUME * 64) .addListener(new NetworkNodeFluidInventoryListener(this)) .addListener((handler, slot, reading) -> { - if (!reading && slot < 9) { + if (!reading && slot < PROCESSING_MATRIX_SIZE) { allowedTagList.clearFluidTags(slot); } }); @@ -146,14 +148,11 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I boolean processing = PatternItem.isProcessing(pattern); if (processing) { - for (int i = 0; i < 9; ++i) { + for (int i = 0; i < PROCESSING_MATRIX_SIZE; ++i) { processingMatrix.setStackInSlot(i, PatternItem.getInputSlot(pattern, i)); processingMatrixFluids.setFluid(i, PatternItem.getFluidInputSlot(pattern, i)); - } - - for (int i = 0; i < 9; ++i) { - processingMatrix.setStackInSlot(9 + i, PatternItem.getOutputSlot(pattern, i)); - processingMatrixFluids.setFluid(9 + i, PatternItem.getFluidOutputSlot(pattern, i)); + processingMatrix.setStackInSlot(PROCESSING_MATRIX_SIZE + i, PatternItem.getOutputSlot(pattern, i)); + processingMatrixFluids.setFluid(PROCESSING_MATRIX_SIZE + i, PatternItem.getFluidOutputSlot(pattern, i)); } AllowedTagList allowedTagsFromPattern = PatternItem.getAllowedTags(pattern); @@ -498,10 +497,10 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I } if (processingPattern) { - for (int i = 0; i < 18; ++i) { + for (int i = 0; i < processingMatrix.getSlots(); ++i) { if (!processingMatrix.getStackInSlot(i).isEmpty()) { - if (i >= 9) { - PatternItem.setOutputSlot(pattern, i - 9, processingMatrix.getStackInSlot(i)); + if (i >= PROCESSING_MATRIX_SIZE) { + PatternItem.setOutputSlot(pattern, i - PROCESSING_MATRIX_SIZE, processingMatrix.getStackInSlot(i)); } else { PatternItem.setInputSlot(pattern, i, processingMatrix.getStackInSlot(i)); } @@ -509,8 +508,8 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I FluidStack fluid = processingMatrixFluids.getFluid(i); if (!fluid.isEmpty()) { - if (i >= 9) { - PatternItem.setFluidOutputSlot(pattern, i - 9, fluid); + if (i >= PROCESSING_MATRIX_SIZE) { + PatternItem.setFluidOutputSlot(pattern, i - PROCESSING_MATRIX_SIZE, fluid); } else { PatternItem.setFluidInputSlot(pattern, i, fluid); } @@ -543,7 +542,7 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I int inputsFilled = 0; int outputsFilled = 0; - for (int i = 0; i < 9; ++i) { + for (int i = 0; i < PROCESSING_MATRIX_SIZE; ++i) { if (!processingMatrix.getStackInSlot(i).isEmpty()) { inputsFilled++; } @@ -553,7 +552,7 @@ public class GridNetworkNode extends NetworkNode implements INetworkAwareGrid, I } } - for (int i = 9; i < 18; ++i) { + for (int i = PROCESSING_MATRIX_SIZE; i < processingMatrix.getSlots(); ++i) { if (!processingMatrix.getStackInSlot(i).isEmpty()) { outputsFilled++; } diff --git a/src/main/java/com/refinedmods/refinedstorage/container/GridContainer.java b/src/main/java/com/refinedmods/refinedstorage/container/GridContainer.java index c613c0c4d..70a2ed899 100644 --- a/src/main/java/com/refinedmods/refinedstorage/container/GridContainer.java +++ b/src/main/java/com/refinedmods/refinedstorage/container/GridContainer.java @@ -29,6 +29,8 @@ import net.minecraft.network.play.server.SSetSlotPacket; import net.minecraftforge.items.SlotItemHandler; import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; public class GridContainer extends BaseContainer implements ICraftingGridListener { private final IGrid grid; @@ -38,6 +40,9 @@ public class GridContainer extends BaseContainer implements ICraftingGridListene private ResultCraftingGridSlot craftingResultSlot; private LegacyBaseSlot patternResultSlot; + private List itemPatternSlots = new ArrayList<>(); + private List fluidPatternSlots = new ArrayList<>(); + private int patternScrollOffset; public GridContainer(IGrid grid, @Nullable BaseTile gridTile, PlayerEntity player, int windowId) { super(RSContainers.GRID, gridTile, player, windowId); @@ -162,6 +167,8 @@ public class GridContainer extends BaseContainer implements ICraftingGridListene } private void addPatternSlots() { + itemPatternSlots.clear(); + fluidPatternSlots.clear(); int headerAndSlots = screenInfoProvider.getTopHeight() + (screenInfoProvider.getVisibleRows() * 18); addSlot(new SlotItemHandler(((GridNetworkNode) grid).getPatterns(), 0, 172, headerAndSlots + 4)); @@ -174,25 +181,28 @@ public class GridContainer extends BaseContainer implements ICraftingGridListene int x = ox; int y = headerAndSlots + 4; - for (int i = 0; i < 9 * 2; ++i) { + for (int i = 0; i < GridNetworkNode.PROCESSING_MATRIX_SIZE * 2; ++i) { int itemFilterSlotConfig = FilterSlot.FILTER_ALLOW_SIZE; - if (i < 9) { + if (i < GridNetworkNode.PROCESSING_MATRIX_SIZE) { itemFilterSlotConfig |= FilterSlot.FILTER_ALLOW_ALTERNATIVES; } int fluidFilterSlotConfig = FluidFilterSlot.FILTER_ALLOW_SIZE; - if (i < 9) { + if (i < GridNetworkNode.PROCESSING_MATRIX_SIZE) { fluidFilterSlotConfig |= FluidFilterSlot.FILTER_ALLOW_ALTERNATIVES; } - addSlot(new FilterSlot(((GridNetworkNode) grid).getProcessingMatrix(), i, x, y, itemFilterSlotConfig).setEnableHandler(() -> ((GridNetworkNode) grid).isProcessingPattern() && ((GridNetworkNode) grid).getType() == IType.ITEMS)); - addSlot(new FluidFilterSlot(((GridNetworkNode) grid).getProcessingMatrixFluids(), i, x, y, fluidFilterSlotConfig).setEnableHandler(() -> ((GridNetworkNode) grid).isProcessingPattern() && ((GridNetworkNode) grid).getType() == IType.FLUIDS)); + int finalI = i; + itemPatternSlots.add(addSlot(new FilterSlot(((GridNetworkNode) grid).getProcessingMatrix(), i, x, y, itemFilterSlotConfig) + .setEnableHandler(() -> getSlotEnabled(finalI, true)))); + fluidPatternSlots.add(addSlot(new FluidFilterSlot(((GridNetworkNode) grid).getProcessingMatrixFluids(), i, x, y, fluidFilterSlotConfig) + .setEnableHandler(() -> getSlotEnabled(finalI, false)))); x += 18; if ((i + 1) % 3 == 0) { - if (i == 8) { - ox = 98; + if (i == GridNetworkNode.PROCESSING_MATRIX_SIZE - 1) { + ox = 93; x = ox; y = headerAndSlots + 4; } else { @@ -221,6 +231,42 @@ public class GridContainer extends BaseContainer implements ICraftingGridListene addSlot(patternResultSlot); } + private boolean getSlotEnabled(int i, boolean item) { + if (!((GridNetworkNode) grid).isProcessingPattern() || !isVisible(i)) { + return false; + } + + if (item) { + if (itemPatternSlots.get(i).getHasStack()) { + return true; + } + + if (((FluidFilterSlot) fluidPatternSlots.get(i)).hasStack()) { + return false; + } + + return ((GridNetworkNode) grid).getType() == IType.ITEMS; + } else { + if (((FluidFilterSlot) fluidPatternSlots.get(i)).hasStack()) { + return true; + } + + if (itemPatternSlots.get(i).getHasStack()) { + return false; + } + + return ((GridNetworkNode) grid).getType() == IType.FLUIDS; + } + } + + private boolean isVisible(int slotNumber) { + return (slotNumber >= patternScrollOffset * 3 + && slotNumber < patternScrollOffset * 3 + 9) + + || (slotNumber >= patternScrollOffset * 3 + GridNetworkNode.PROCESSING_MATRIX_SIZE + && slotNumber < patternScrollOffset * 3 + GridNetworkNode.PROCESSING_MATRIX_SIZE + 9); + } + public IGrid getGrid() { return grid; } @@ -293,4 +339,25 @@ public class GridContainer extends BaseContainer implements ICraftingGridListene protected int getDisabledSlotNumber() { return grid.getSlotId(); } -} + + public void updatePatternSlotPositions(int newOffset) { + patternScrollOffset = newOffset; + int yPosition = screenInfoProvider.getTopHeight() + (screenInfoProvider.getVisibleRows() * 18) + 4; + int originalYPosition = yPosition; + + for (int i = 0; i < itemPatternSlots.size(); i++) { + + if (i == GridNetworkNode.PROCESSING_MATRIX_SIZE) { // reset when reaching output slots + yPosition = originalYPosition; + } + + if (isVisible(i)) { + itemPatternSlots.get(i).yPos = yPosition; + fluidPatternSlots.get(i).yPos = yPosition; + if ((i + 1) % 3 == 0) { + yPosition += 18; + } + } + } + } +} \ No newline at end of file diff --git a/src/main/java/com/refinedmods/refinedstorage/container/slot/filter/FluidFilterSlot.java b/src/main/java/com/refinedmods/refinedstorage/container/slot/filter/FluidFilterSlot.java index f6e1f8edd..673c4d5ee 100644 --- a/src/main/java/com/refinedmods/refinedstorage/container/slot/filter/FluidFilterSlot.java +++ b/src/main/java/com/refinedmods/refinedstorage/container/slot/filter/FluidFilterSlot.java @@ -52,4 +52,9 @@ public class FluidFilterSlot extends BaseSlot { public FluidInventory getFluidInventory() { return fluidInventory; } + + // not overriding getHasStack as we do the tooltips ourselves + public boolean hasStack() { + return !fluidInventory.getFluid(getSlotIndex()).isEmpty(); + } } diff --git a/src/main/java/com/refinedmods/refinedstorage/item/PatternItem.java b/src/main/java/com/refinedmods/refinedstorage/item/PatternItem.java index 1e7522bd0..80c4aff0d 100644 --- a/src/main/java/com/refinedmods/refinedstorage/item/PatternItem.java +++ b/src/main/java/com/refinedmods/refinedstorage/item/PatternItem.java @@ -8,6 +8,7 @@ import com.refinedmods.refinedstorage.api.autocrafting.ICraftingPatternProvider; import com.refinedmods.refinedstorage.apiimpl.autocrafting.AllowedTagList; import com.refinedmods.refinedstorage.apiimpl.autocrafting.CraftingPattern; import com.refinedmods.refinedstorage.apiimpl.autocrafting.CraftingPatternFactory; +import com.refinedmods.refinedstorage.apiimpl.network.node.GridNetworkNode; import com.refinedmods.refinedstorage.render.Styles; import com.refinedmods.refinedstorage.render.tesr.PatternItemStackTileRenderer; import com.refinedmods.refinedstorage.util.ItemStackKey; @@ -273,7 +274,7 @@ public class PatternItem extends Item implements ICraftingPatternProvider { return null; } - AllowedTagList allowedTagList = new AllowedTagList(null); + AllowedTagList allowedTagList = new AllowedTagList(null, GridNetworkNode.PROCESSING_MATRIX_SIZE); allowedTagList.readFromNbt(pattern.getTag().getCompound(NBT_ALLOWED_TAGS)); diff --git a/src/main/java/com/refinedmods/refinedstorage/network/grid/GridProcessingTransferMessage.java b/src/main/java/com/refinedmods/refinedstorage/network/grid/GridProcessingTransferMessage.java index 9d8d422ce..751064f9d 100644 --- a/src/main/java/com/refinedmods/refinedstorage/network/grid/GridProcessingTransferMessage.java +++ b/src/main/java/com/refinedmods/refinedstorage/network/grid/GridProcessingTransferMessage.java @@ -108,11 +108,9 @@ public class GridProcessingTransferMessage { clearInputsAndOutputs(handler); clearInputsAndOutputs(handlerFluid); - setInputs(handler, message.inputs); - setOutputs(handler, message.outputs); + setInputs(handler, message.inputs, handlerFluid, message.fluidInputs); + setOutputs(handler, message.outputs, handlerFluid, message.fluidOutputs); - setFluidInputs(handlerFluid, message.fluidInputs); - setFluidOutputs(handlerFluid, message.fluidOutputs); ((GridNetworkNode) grid).setProcessingPattern(true); ((GridNetworkNode) grid).markDirty(); @@ -125,26 +123,26 @@ public class GridProcessingTransferMessage { } private static void clearInputsAndOutputs(BaseItemHandler handler) { - for (int i = 0; i < 9 * 2; ++i) { + for (int i = 0; i < handler.getSlots(); ++i) { handler.setStackInSlot(i, ItemStack.EMPTY); } } private static void clearInputsAndOutputs(FluidInventory handler) { - for (int i = 0; i < 9 * 2; ++i) { + for (int i = 0; i < handler.getSlots(); ++i) { handler.setFluid(i, FluidStack.EMPTY); } } - private static void setInputs(BaseItemHandler handler, Collection stacks) { - setSlots(handler, stacks, 0, 9); + private static void setInputs(BaseItemHandler handler, Collection stacks, FluidInventory fluidHandler, Collection fluidStacks) { + setSlots(handler, stacks, fluidHandler, fluidStacks, 0, handler.getSlots() / 2); } - private static void setOutputs(BaseItemHandler handler, Collection stacks) { - setSlots(handler, stacks, 9, 18); + private static void setOutputs(BaseItemHandler handler, Collection stacks, FluidInventory fluidHandler, Collection fluidStacks) { + setSlots(handler, stacks, fluidHandler, fluidStacks, handler.getSlots() / 2, handler.getSlots()); } - private static void setSlots(BaseItemHandler handler, Collection stacks, int begin, int end) { + private static void setSlots(BaseItemHandler handler, Collection stacks, FluidInventory fluidHandler, Collection fluidStacks, int begin, int end) { for (ItemStack stack : stacks) { handler.setStackInSlot(begin, stack); @@ -154,20 +152,9 @@ public class GridProcessingTransferMessage { break; } } - } + for (FluidStack stack : fluidStacks) { - private static void setFluidInputs(FluidInventory inventory, Collection stacks) { - setFluidSlots(inventory, stacks, 0, 9); - } - - private static void setFluidOutputs(FluidInventory inventory, Collection stacks) { - setFluidSlots(inventory, stacks, 9, 18); - } - - private static void setFluidSlots(FluidInventory inventory, Collection stacks, int begin, int end) { - for (FluidStack stack : stacks) { - - inventory.setFluid(begin, stack.copy()); + fluidHandler.setFluid(begin, stack.copy()); begin++; diff --git a/src/main/java/com/refinedmods/refinedstorage/screen/grid/GridScreen.java b/src/main/java/com/refinedmods/refinedstorage/screen/grid/GridScreen.java index 0a9970d1d..5cb49ffff 100644 --- a/src/main/java/com/refinedmods/refinedstorage/screen/grid/GridScreen.java +++ b/src/main/java/com/refinedmods/refinedstorage/screen/grid/GridScreen.java @@ -25,6 +25,7 @@ import com.refinedmods.refinedstorage.screen.widget.SearchWidget; import com.refinedmods.refinedstorage.screen.widget.TabListWidget; import com.refinedmods.refinedstorage.screen.widget.sidebutton.*; import com.refinedmods.refinedstorage.tile.NetworkNodeTile; +import com.refinedmods.refinedstorage.tile.config.IType; import com.refinedmods.refinedstorage.tile.data.TileDataManager; import com.refinedmods.refinedstorage.tile.grid.GridTile; import com.refinedmods.refinedstorage.tile.grid.portable.IPortableGrid; @@ -38,6 +39,7 @@ import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.SoundEvents; import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; import net.minecraft.util.text.TranslationTextComponent; import org.lwjgl.glfw.GLFW; import yalter.mousetweaks.api.MouseTweaksDisableWheelTweak; @@ -54,8 +56,10 @@ public class GridScreen extends BaseScreen implements IScreenInfo private SearchWidget searchField; private CheckboxWidget exactPattern; private CheckboxWidget processingPattern; + private CheckboxWidget fluidCheckBox; private ScrollbarWidget scrollbar; + private ScrollbarWidget patternScrollbar; private final IGrid grid; private final TabListWidget tabs; @@ -63,7 +67,12 @@ public class GridScreen extends BaseScreen implements IScreenInfo private boolean wasConnected; private boolean doSort; + private final int patternScrollOffsetAbsoluteMax = GridNetworkNode.PROCESSING_MATRIX_SIZE / 3 - 3; + private int slotNumber; + private int patternScrollOffset; + private int patternScrollOffsetMax; + private boolean updatePatternOffset; public GridScreen(GridContainer container, IGrid grid, PlayerInventory inventory, ITextComponent title) { super(container, 227, 0, inventory, title); @@ -83,6 +92,12 @@ public class GridScreen extends BaseScreen implements IScreenInfo grid.onTabPageChanged(page); } }); + + if (grid instanceof GridNetworkNode && grid.getGridType() == GridType.PATTERN) { + GridNetworkNode node = (GridNetworkNode) grid; + node.getProcessingMatrix().addListener((handler, slot, reading) -> updatePatternOffset = true); + node.getProcessingMatrixFluids().addListener((handler, slot, reading) -> updatePatternOffset = true); + } } @Override @@ -135,12 +150,24 @@ public class GridScreen extends BaseScreen implements IScreenInfo addSideButton(new GridSizeSideButton(this, grid::getSize, grid::onSizeChanged)); if (grid.getGridType() == GridType.PATTERN) { + patternScrollbar = new ScrollbarWidget(this, 160, getTopHeight() + getVisibleRows() * 18 + 4, 6, 18 * 3 - 2, true); + + patternScrollbar.setMaxOffset(patternScrollOffsetAbsoluteMax); + patternScrollbar.setOffset(patternScrollOffset); // keep offset when changing between fluid and item view + + container.updatePatternSlotPositions(patternScrollOffset); + patternScrollbar.addListener((oldOffset, newOffset) -> { + patternScrollOffset = newOffset; + container.updatePatternSlotPositions(newOffset); + }); + processingPattern = addCheckBox(x + 7, y + getTopHeight() + (getVisibleRows() * 18) + 60, new TranslationTextComponent("misc.refinedstorage.processing"), GridTile.PROCESSING_PATTERN.getValue(), btn -> { // Rebuild the inventory slots before the slot change packet arrives. GridTile.PROCESSING_PATTERN.setValue(false, processingPattern.isChecked()); ((GridNetworkNode) grid).clearMatrix(); // The server does this but let's do it earlier so the client doesn't notice. this.container.initSlots(); + patternScrollOffset = 0; // reset offset when switching between crafting and processing TileDataManager.setParameter(GridTile.PROCESSING_PATTERN, processingPattern.isChecked()); }); @@ -152,9 +179,14 @@ public class GridScreen extends BaseScreen implements IScreenInfo GridTile.EXACT_PATTERN.getValue(), btn -> TileDataManager.setParameter(GridTile.EXACT_PATTERN, exactPattern.isChecked()) ); - } + patternScrollbar.setEnabled(false); + } else { + patternScrollbar.setEnabled(true); - addSideButton(new TypeSideButton(this, GridTile.PROCESSING_TYPE)); + fluidCheckBox = addCheckBox(processingPattern.x + processingPattern.getWidth() + 5, y + getTopHeight() + (getVisibleRows() * 18) + 60, new StringTextComponent("Fluids"), ((GridNetworkNode) grid).getType() == IType.FLUIDS, button -> { + TileDataManager.setParameter(GridTile.PROCESSING_TYPE, GridTile.PROCESSING_TYPE.getValue() == IType.ITEMS ? IType.FLUIDS : IType.ITEMS); + }); + } } updateScrollbar(); @@ -180,6 +212,11 @@ public class GridScreen extends BaseScreen implements IScreenInfo view.sort(); } + if (updatePatternOffset) { + updatePatternOffsetMax(); + updatePatternOffset = false; + } + if (isKeyDown(RSKeyBindings.CLEAR_GRID_CRAFTING_MATRIX)) { RS.NETWORK_HANDLER.sendToServer(new GridClearMessage()); } @@ -342,6 +379,10 @@ public class GridScreen extends BaseScreen implements IScreenInfo } blit(matrixStack, x + 172, y + getTopHeight() + (getVisibleRows() * 18) + 22, 240, ty * 16, 16, 16); + if (processingPattern.isChecked()) { + updatePatternScrollbar(); + patternScrollbar.render(matrixStack); + } } tabs.drawForeground(matrixStack, x, y - tabs.getHeight(), mouseX, mouseY, true); @@ -444,7 +485,9 @@ public class GridScreen extends BaseScreen implements IScreenInfo if (scrollbar.mouseClicked(mouseX, mouseY, clickedButton)) { return true; } - + if (grid.getGridType() == GridType.PATTERN && patternScrollbar.mouseClicked(mouseX, mouseY, clickedButton)) { + return true; + } if (RS.CLIENT_CONFIG.getGrid().getPreventSortingWhileShiftIsDown()) { doSort = !isOverSlotArea(mouseX - guiLeft, mouseY - guiTop) && !isOverCraftingOutputArea(mouseX - guiLeft, mouseY - guiTop); } @@ -520,11 +563,17 @@ public class GridScreen extends BaseScreen implements IScreenInfo public void mouseMoved(double mx, double my) { scrollbar.mouseMoved(mx, my); + if (grid.getGridType() == GridType.PATTERN) { + patternScrollbar.mouseMoved(mx, my); + } super.mouseMoved(mx, my); } @Override public boolean mouseReleased(double mx, double my, int button) { + if (grid.getGridType() == GridType.PATTERN && patternScrollbar.mouseReleased(mx, my, button)) { + return true; + } return scrollbar.mouseReleased(mx, my, button) || super.mouseReleased(mx, my, button); } @@ -534,7 +583,6 @@ public class GridScreen extends BaseScreen implements IScreenInfo if (RS.CLIENT_CONFIG.getGrid().getPreventSortingWhileShiftIsDown()) { doSort = !isOverSlotArea(x - guiLeft, y - guiTop) && !isOverCraftingOutputArea(x - guiLeft, y - guiTop); } - if (grid.getGridType() != GridType.FLUID) { if (isOverInventory(x - guiLeft, y - guiTop) && hoveredSlot != null && hoveredSlot.getHasStack()) { RS.NETWORK_HANDLER.sendToServer(new GridItemInventoryScrollMessage(hoveredSlot.getSlotIndex(), hasShiftDown(), delta > 0)); @@ -544,15 +592,23 @@ public class GridScreen extends BaseScreen implements IScreenInfo } return super.mouseScrolled(x, y, delta); - } else { - return this.scrollbar.mouseScrolled(x, y, delta) || super.mouseScrolled(x, y, delta); } + + if (grid.getGridType() == GridType.PATTERN && isOverPatternArea(x - guiLeft, y - guiTop) && patternScrollbar.mouseScrolled(x, y, delta)) { + return true; + } + + return this.scrollbar.mouseScrolled(x, y, delta) || super.mouseScrolled(x, y, delta); } private boolean isOverInventory(double x, double y) { return RenderUtils.inBounds(8, getYPlayerInventory(), 9 * 18 - 2, 4 * 18 + 2, x, y); } + private boolean isOverPatternArea(double x, double y) { + return RenderUtils.inBounds(8, getTopHeight() + getVisibleRows() * 18, 152, 54, x, y); + } + @Override public boolean charTyped(char unknown1, int unknown2) { if (searchField.charTyped(unknown1, unknown2)) { @@ -602,6 +658,51 @@ public class GridScreen extends BaseScreen implements IScreenInfo } } + //check processing matrix and update maximum offset depending on contents + private void updatePatternOffsetMax() { + int filledInputSlots = 0; + int filledOutputSlots = 0; + int lastFilledInputSlot = 0; + int lastFilledOutputSlot = 0; + + for (int i = 0; i < GridNetworkNode.PROCESSING_MATRIX_SIZE * 2; i++) { + if (!isMatrixSlotEmpty(i)) { + if (i > GridNetworkNode.PROCESSING_MATRIX_SIZE - 1) { + filledOutputSlots++; + lastFilledOutputSlot = i - GridNetworkNode.PROCESSING_MATRIX_SIZE - 1; + } else { + filledInputSlots++; + lastFilledInputSlot = i; + } + } + } + + int maxFilledSlots = Math.max(filledInputSlots, filledOutputSlots); + int maxLastFilledSlot = Math.max(lastFilledInputSlot, lastFilledOutputSlot); + + // offset calculation to show next row if 4 of 9 slots are filled. + int filledSlotOffset = Math.floorDiv(maxFilledSlots - 1, 3); + // offset to show the last item in the matrix + int lastSlotOffset = Math.floorDiv(maxLastFilledSlot, 3) - 2; + + patternScrollOffsetMax = Math.max(filledSlotOffset, lastSlotOffset); + } + + private boolean isMatrixSlotEmpty(int slotNumber) { + return ((GridNetworkNode) grid).getProcessingMatrix().getStackInSlot(slotNumber).isEmpty() + && ((GridNetworkNode) grid).getProcessingMatrixFluids().getFluid(slotNumber).isEmpty(); + } + + public void updatePatternScrollbar() { + patternScrollbar.setEnabled(processingPattern.isChecked() && patternScrollOffsetMax > 0); + int oldOffset = patternScrollbar.getOffset(); + patternScrollbar.setMaxOffset(Math.min(patternScrollOffsetMax, patternScrollOffsetAbsoluteMax)); + + if (oldOffset != patternScrollbar.getOffset()) { + container.updatePatternSlotPositions(patternScrollbar.getOffset()); + } + } + public void updateScrollbar() { scrollbar.setEnabled(getRows() > getVisibleRows()); scrollbar.setMaxOffset(getRows() - getVisibleRows()); diff --git a/src/main/java/com/refinedmods/refinedstorage/screen/widget/ScrollbarWidget.java b/src/main/java/com/refinedmods/refinedstorage/screen/widget/ScrollbarWidget.java index 795b42dda..fc93d235c 100644 --- a/src/main/java/com/refinedmods/refinedstorage/screen/widget/ScrollbarWidget.java +++ b/src/main/java/com/refinedmods/refinedstorage/screen/widget/ScrollbarWidget.java @@ -25,6 +25,7 @@ public class ScrollbarWidget implements IGuiEventListener { private int maxOffset; private boolean clicked = false; + private boolean small = false; private final List listeners = new LinkedList<>(); @@ -38,6 +39,11 @@ public class ScrollbarWidget implements IGuiEventListener { this.height = height; } + public ScrollbarWidget(BaseScreen screen, int x, int y, int width, int height, boolean small) { + this(screen, x, y, width, height); + this.small = small; + } + public void addListener(ScrollbarWidgetListener listener) { listeners.add(listener); } @@ -62,7 +68,12 @@ public class ScrollbarWidget implements IGuiEventListener { RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F); screen.bindTexture(RS.ID, "icons.png"); - screen.blit(matrixStack, screen.getGuiLeft() + x, screen.getGuiTop() + y + (int) Math.min(height - SCROLLER_HEIGHT, (float) offset / (float) maxOffset * (float) (height - SCROLLER_HEIGHT)), isEnabled() ? 232 : 244, 0, 12, 15); + if (small) { + screen.blit(matrixStack, screen.getGuiLeft() + x, screen.getGuiTop() + y + (int) Math.min(height - SCROLLER_HEIGHT, (float) offset / (float) maxOffset * (float) (height - SCROLLER_HEIGHT)), isEnabled() ? 218 : 225, 0, 7, SCROLLER_HEIGHT); + } else { + screen.blit(matrixStack, screen.getGuiLeft() + x, screen.getGuiTop() + y + (int) Math.min(height - SCROLLER_HEIGHT, (float) offset / (float) maxOffset * (float) (height - SCROLLER_HEIGHT)), isEnabled() ? 232 : 244, 0, 12, SCROLLER_HEIGHT); + } + } @Override diff --git a/src/main/java/com/refinedmods/refinedstorage/screen/widget/sidebutton/TypeSideButton.java b/src/main/java/com/refinedmods/refinedstorage/screen/widget/sidebutton/TypeSideButton.java index 1319f0ded..c41e41357 100644 --- a/src/main/java/com/refinedmods/refinedstorage/screen/widget/sidebutton/TypeSideButton.java +++ b/src/main/java/com/refinedmods/refinedstorage/screen/widget/sidebutton/TypeSideButton.java @@ -2,8 +2,6 @@ package com.refinedmods.refinedstorage.screen.widget.sidebutton; import com.mojang.blaze3d.matrix.MatrixStack; import com.refinedmods.refinedstorage.screen.BaseScreen; -import com.refinedmods.refinedstorage.tile.config.IType; -import com.refinedmods.refinedstorage.tile.data.TileDataManager; import com.refinedmods.refinedstorage.tile.data.TileDataParameter; import net.minecraft.client.resources.I18n; import net.minecraft.util.text.TextFormatting; @@ -29,6 +27,6 @@ public class TypeSideButton extends SideButton { @Override public void onPress() { - TileDataManager.setParameter(type, type.getValue() == IType.ITEMS ? IType.FLUIDS : IType.ITEMS); + } } diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 1c5b0eb7e..64cad406f 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -2,3 +2,4 @@ protected net.minecraft.inventory.container.Container field_75153_a # inventoryI protected net.minecraft.inventory.container.Container field_75149_d # listeners protected net.minecraft.client.gui.widget.TextFieldWidget field_146212_n # canLoseFocus public net.minecraft.client.gui.widget.button.CheckboxButton field_212943_a # checked +public-f net.minecraft.inventory.container.Slot field_75221_f # yPos \ No newline at end of file diff --git a/src/main/resources/assets/refinedstorage/textures/gui/pattern_grid_processing.png b/src/main/resources/assets/refinedstorage/textures/gui/pattern_grid_processing.png index e897e8244beeca379c0d3fbce4cdb95213359891..bb89bd16fe06a28018e2aa3591b47321318e5187 100644 GIT binary patch delta 1713 zcmaJOP1bCqxr#qXq)L>r@AsjRFqwVEyJyz?X)W)mmPDo zpa=IlYdZBZW0e!+o?CQflFO}EUsR|Xb054s&ED&cDk=-L)jpBsIg2!uR6v7PJ06&Q z;8kM3w|_~~m^^Xmi+(>#by87c2NZqK^#jm2B6?ggttc=roh1pZjr51#l2j5?6W*iV zJa5tVCWu#EoNM{PfP(+ry!hrma*FztZf%CR{Ul}_#z^np?V#Y(e%T`S1Rh{+snxP= z@M$$kYt4h8)S@Da*o$vO{7iSU`eVwIzi2-*O`guMk1haeO7_adO|p!JKAU6hW|H|) zjoWZZ{*WZ?;Y`y*MmQ@xJh-!+O2A|GB_*&0Z$CsUVQg_7fqr}k|9z3LyZMWMeI;g_iRDzZ^-7o;h9_p!TCBKyz zG3N8|wV7oNJCHu@$1gvsD4`cr=!TmlwnO^GZhG1U{G&A6g+Q9E70qgPlHX1hzm8w{ zWai#RUq#Ot;pRXwFv5h~taH|#dVgo+65{6Cz`NT#Q(OS`?w;ew7t}5`uklEt)v1@StMd1y|)wu2*+@c=Hccz1~B%U^ETPb+k~$rX>

{OC2Pbxojkw!J)U{^h z)SzSH4Jk?}7(l_W&eryz_0zh?Juj?fguQJGNY4?L1BqWQa~m7%Z&Z3YWaSyFsFL6u zPv634vTt_CMkUBWxuQ>@MylzSRvFC~A^;%4!}&Sa*R{zx@EoDEGY!BZh){h>?Pmxi zumMoFt~yzRb#~Y&HUWdO!pWFV8p}YkuR@_pj@}7XGwPx(KlWT670)T79nUFlT0hEVMY>HGP@G)ze8CLTU7G6j4 zycN0-T68SknX}g8(VC4Z_dZZJUVgSYHx)N?QleZ@*A@hhkvF~%|{ z-)vwzY|lba6s=cR7LV=F+&}>!*+g*FnQY<;&Q%-Mb#o)WCxw3mTws=8$-QS~K>6ZW zYjRA!wA{kP@4*pB#C%8eaY`;!fk!McUvsseDN7W#^st89SIAp7(!K#ug2rqRlvN(? z*WycCW~r`HsKKl5@y+_31aCXM%V*wYCeleNg8fZPTO?kFv@(&z=<(*~C-;MQ{w`l> zw$^&!!XaEH=WYOMnQGZX=$p3xf&gr711H!xXg4sy3pJUX@VHh@=?1Sl$ZTzSmDP1@?H#= z@*)~i>$9f(){>ql-Jx6J0}h&-A;}(5WOH%G@fdOXXo*Qo>QR9KH&`20FbzjEUst70d8!-ksu&4AUK>$4G-eNo2Vw#jiys@@Hqfb{vjg* zLqYA~*|1oVaqRHI^ zC&?T$EJF9Tc1(yr`etB$YB?y>h;4ej#px|VIa%A(K%i~k@1Q2qQ1X2CRq)sE8s;e~5SJ5a(gDzUJ4;xkry7?R}#Wcx4-0OXGU~ z;?`1gkqRsS7!-aH2T?H_{OZog?mE7?f=cpOkrdmrWB7|e{KB=1O!0?YUYa+xuA7&K z`%!pjsA6?Wu9`}+5Ae)TuZWEZOds-Rdv{1XjkcwpMB}s>DiqxNyKuvYqc;nWSM3o))5LMr%d-xj3QBIU zu*BYGVp;hbA#%Uox2-J-WkJ)0aqDNNcobaRli;dOF)g-Y=w(o3URTXb%xeN3dz*a& zZyPV^V*1%MtZ3=Kj9O^|pxo#OQ|@{d&*@YQXd%+d2u>-Zwd1N{TIJPM+i&E12`=de z<@;;X-rX-_J?e^2^R`Fb@Q@dQKcrqN@BQP`?i6EC6$MmH?46DCnKzm*=wpUwVE}?f zCMwo1b}CLZ{^0Lb<=vcCWDDxt{iUouO!zSv_=q9WCJ;0iG6xx;|Q9 zMy7NX0_R$^+NZ>KgT(QO^|Flr%UhtH%j$ZbY zJ<>$*UW>t;mPp-%ZeElE%*T+n4VI$LN*1XJx`jZF(&$<=Tx}5wJj+yI#6Zn;1%)Tb z)#J-4t+DRTiuiSu{PsIF7A~~!t>0ZcMMn31#8#8+g>2-bi(*eml zXLPoh24LhyR4Lsivr0ItMz>Sk^_g0V0#ShUXK#bLpS+X=h>V4-R=|j1DxoNq19kXe z*mpL7nX{0EtZyqgTN_Erq(|p0r_CHtQZT~^dew4~y1lMvkxErGtg*Nxna*l+qiuP1VP~KiK}1M zDh%`rCTXAg#RvZd1t6Gz6ECGNtIBBV;&=mNwFiyI$K1thx;SiLw@=dD?{7X;S{&p5 z)h!8lGd2)8$I^bTteCUBxR*>|tUknee1f?|us>|0v2T0v+<*E2S+=^ob6b}hDEGYD zU-nsNVXHw54ef)rd@gYl((_im+@>S}rX0)nqy2cbS7*RZTkiqP$hEEIni&sqloc}{ Zkn|42l+YZ<Qt?OgTrk3@IvE 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 literal 5716 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