From f588ac5de8b109cec31df0fc38106ee4bc010ec5 Mon Sep 17 00:00:00 2001 From: Dakedres Date: Fri, 18 Apr 2025 23:35:48 -0600 Subject: [PATCH] Reimplement stack distribution and new WIP placement order preview --- .../block/entity/OrderedContainer.java | 77 ++++++- .../block/entity/WorkbenchBlockEntity.java | 192 ++++++++++++++---- .../grafting/font/slot_order_overlay.json | 22 ++ .../assets/grafting/font/workbench_gui.json | 2 +- .../assets/grafting/textures/gui/slots.png | Bin 0 -> 1353 bytes .../assets/grafting/textures/gui/slots.xcf | Bin 0 -> 87049 bytes 6 files changed, 255 insertions(+), 38 deletions(-) create mode 100644 src/main/resources/assets/grafting/font/slot_order_overlay.json create mode 100644 src/main/resources/assets/grafting/textures/gui/slots.png create mode 100644 src/main/resources/assets/grafting/textures/gui/slots.xcf diff --git a/src/main/java/net/sys42/dakedres/grafting/block/entity/OrderedContainer.java b/src/main/java/net/sys42/dakedres/grafting/block/entity/OrderedContainer.java index a96d3d3..129a577 100644 --- a/src/main/java/net/sys42/dakedres/grafting/block/entity/OrderedContainer.java +++ b/src/main/java/net/sys42/dakedres/grafting/block/entity/OrderedContainer.java @@ -153,15 +153,19 @@ public abstract class OrderedContainer extends BlockEntity implements Inventory markDirty(); } - public void pushStack(ItemStack stack) { + public ItemStack pushStack(ItemStack stack) { if(stack.isEmpty()) { - return; + return stack; } if(hasRoom()) { inventory.add(stack); + distributeStack(stack); markDirty(); + return ItemStack.EMPTY; } + + return stack; } public ItemStack pullStack() { @@ -212,4 +216,73 @@ public abstract class OrderedContainer extends BlockEntity implements Inventory public ItemStack topStack() { return this.inventory.getLast(); } + +// public ItemStack topStackAlike(ItemStack comparisonStack) { +// for(ItemStack stack : inventory) { +// if(ItemStack.areItemsAndComponentsEqual(stack, comparisonStack)) { +// return stack; +// } +// } +// +// return ItemStack.EMPTY; +// } + + public List similarStacks(ItemStack comparisonStack) { + var out = new ArrayList(); + + for(int i = 0; i < filledSlots(); i++) { + ItemStack stack = getStack(i); + + if(ItemStack.areItemsAndComponentsEqual(stack, comparisonStack)) { + out.add(stack); + } + } + + return out; + } + + public void distributeStack(ItemStack target) { + distributeStack(target, 0); + } + + public void distributeStack(ItemStack target, int amount) { + List candidates = similarStacks(target); + + if(candidates.isEmpty()) { + return; + } + + int total = amount; + int maxTotal = 0; + + for(ItemStack candidate : candidates) { + total += candidate.getCount(); + maxTotal += candidate.getMaxCount(); + candidate.setCount(0); + } + + int remainder = 0; + if(total > maxTotal) { + remainder = total - maxTotal; + total = maxTotal; + } + + int share = total / candidates.size(); + + for(ItemStack candidate : candidates) { + candidate.setCount(share); + total -= share; + } + + int i = 0; + while(total > 0) { + candidates.get(i % candidates.size()).increment(1); + i++; + total--; + } + + if(amount != 0) { + target.decrement(amount); + } + } } diff --git a/src/main/java/net/sys42/dakedres/grafting/block/entity/WorkbenchBlockEntity.java b/src/main/java/net/sys42/dakedres/grafting/block/entity/WorkbenchBlockEntity.java index 20a1fa4..522ad8a 100644 --- a/src/main/java/net/sys42/dakedres/grafting/block/entity/WorkbenchBlockEntity.java +++ b/src/main/java/net/sys42/dakedres/grafting/block/entity/WorkbenchBlockEntity.java @@ -9,7 +9,6 @@ import net.minecraft.block.BlockState; import net.minecraft.block.CrafterBlock; import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.player.PlayerInventory; import net.minecraft.inventory.CraftingResultInventory; import net.minecraft.inventory.Inventory; import net.minecraft.inventory.RecipeInputInventory; @@ -18,7 +17,6 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.recipe.RecipeMatcher; import net.minecraft.recipe.input.CraftingRecipeInput; import net.minecraft.registry.RegistryWrapper; -import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandlerType; import net.minecraft.screen.slot.CraftingResultSlot; import net.minecraft.screen.slot.Slot; @@ -247,15 +245,23 @@ public class WorkbenchBlockEntity extends OrderedContainer { } public class Gui extends SimpleGui { + private static final Identifier GUI_FONT_ID = Identifier.of(Grafting.MOD_ID, "workbench_gui"); + + private static final Identifier SLOT_ORDER_OVERLAY_FONT_ID = Identifier.of(Grafting.MOD_ID, "slot_order_overlay"); + private static final char[][] SLOT_ORDER_OVERLAY_FONT_MAP = { + { '0', '1', '2', '3', '4', '5', '6', '7' }, + { '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }, + { 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n' } + }; + private static final int BUTTON_COLUMN = 1; private static final int GRID_START_COLUMN = BUTTON_COLUMN + 1; private static final int RESULT_COLUMN = GRID_START_COLUMN + GRID_SIZE + 2; private static final int BASE_HEIGHT = 3; - private static final Identifier GUI_FONT_ID = Identifier.of(Grafting.MOD_ID, "workbench_gui"); - private final CraftingResultInventory resultInventory = new CraftingResultInventory(); - private boolean attachedInventory; + private AttachedInventory attachedInventory; + private ItemStack knownCursorStack = ItemStack.EMPTY; public Gui(ServerPlayerEntity player) { this(player, Optional.empty()); } @@ -268,13 +274,10 @@ public class WorkbenchBlockEntity extends OrderedContainer { player, false ); + attachedInventory.ifPresent(inventory -> this.attachedInventory = inventory); guis.add(this); - setTitle( - Text.empty() - .append(Text.literal(attachedInventory.isEmpty() ? "-0." : "-1.").setStyle(Style.EMPTY.withFont(GUI_FONT_ID).withColor(Formatting.WHITE))) - .append(Text.translatable("container.crafting")) - ); + updateTitle(); setSlot(BUTTON_COLUMN, new RotateGridButton(Icons.ROTATE_CLOCKWISE,1)); setSlot(BUTTON_COLUMN + 9, new RotateGridButton(Icons.ROTATE_COUNTER_CLOCKWISE, -1)); @@ -297,24 +300,109 @@ public class WorkbenchBlockEntity extends OrderedContainer { updateResult(); } - @Override - public void close() { - guis.remove(this); - super.close(); + public int getUnextendedSize() { + return BASE_HEIGHT * 9; } protected void updateResult() { resultInventory.setStack(0, getResult(player)); } + @Override + public void close() { + guis.remove(this); + super.close(); + } + + @Override + public boolean onAnyClick(int index, ClickType type, SlotActionType action) { + ItemStack cursorStack = screenHandler.getCursorStack(); + + Slot slot = getSlotRedirectOrPlayer(index); + if(type.equals(ClickType.MOUSE_LEFT) && slot != null) { + cursorStack = slot.getStack(); + } + + if(cursorStack != knownCursorStack) { + knownCursorStack = cursorStack; + updateTitle(); + } + + return super.onAnyClick(index, type, action); + } + + private void updateTitle() { + var title = Text.empty(); + + title.append( + Text.literal(attachedInventory != null ? "-1." : "-0.") + .setStyle(Style.EMPTY.withFont(GUI_FONT_ID).withColor(Formatting.WHITE)) + ); + + title.append( + Text.literal(getSlotOrderLiteral()) + .setStyle(Style.EMPTY.withFont(SLOT_ORDER_OVERLAY_FONT_ID).withColor(Formatting.WHITE)) + ); + + title.append(Text.translatable("container.crafting")); + setTitle(title); + } + + private String getSlotOrderLiteral() { + StringBuilder out = new StringBuilder(); + out.append(" ".repeat(GRID_START_COLUMN)); + + if(knownCursorStack.isEmpty()) { + return out.toString(); + } + + var placements = PlacementUtils.rotate( + PlacementUtils.getPlacements(knownCursorStack, (ServerWorld) world), + craftingOrientation + ); + + for(int i = 0; i < 3; i++) { + boolean newCol = true; + for(int j = 0; j < 3; j++) { + int order = placements[j * 3 + i] - filledSlots() - 1; + + if(order < 0) { + continue; + } + + if(newCol) { + newCol = false; + out.append('.'); + } else { + out.append('-'); + } + + out.append(SLOT_ORDER_OVERLAY_FONT_MAP[j][order]); + } + + if(newCol) { + out.append(' '); + } + } + + out.append("___"); + + Grafting.LOGGER.info("Test: {}", out.toString()); + + return out.toString(); + } + + /* + * See: CraftingScreenHandler#quickMove + */ @Override public ItemStack quickMove(int index) { ItemStack returnStack = ItemStack.EMPTY; Slot slot = this.getSlotRedirectOrPlayer(index); + boolean isResultSlot = slot instanceof CraftingResultSlot; if(slot != null && slot.hasStack() && !(slot instanceof VirtualSlot)) { ItemStack operatingStack; - if(slot instanceof GridSlot) { operatingStack = topStack(); } else { @@ -322,12 +410,24 @@ public class WorkbenchBlockEntity extends OrderedContainer { } returnStack = operatingStack.copy(); - if(index < this.getVirtualSize()) { + if(isResultSlot) { + operatingStack.getItem().onCraftByPlayer(operatingStack, world, player); + if(!this.insertItem(operatingStack, this.getVirtualSize(), this.getVirtualSize() + 36, true)) { return ItemStack.EMPTY; } - } else if (!this.insertItem(operatingStack, 0, this.getVirtualSize(), false)) { - return ItemStack.EMPTY; + + slot.onQuickTransfer(operatingStack, returnStack); + } else if(index < getUnextendedSize()) { + if(!this.insertItem(operatingStack, this.getUnextendedSize(), this.getVirtualSize() + 36, false)) { + return ItemStack.EMPTY; + } + } else { + if(similarStacks(operatingStack).isEmpty() && hasRoom()) { + pushStack(operatingStack.copyAndEmpty()); + } else { + distributeStack(operatingStack, operatingStack.getCount()); + } } if (operatingStack.isEmpty()) { @@ -335,6 +435,15 @@ public class WorkbenchBlockEntity extends OrderedContainer { } else { slot.markDirty(); } + + if(returnStack.getCount() == operatingStack.getCount()) { + return ItemStack.EMPTY; + } + + slot.onTakeItem(player, returnStack); + if(isResultSlot) { + player.dropItem(returnStack, false); + } } else if (slot instanceof VirtualSlot) { return slot.getStack(); } @@ -349,23 +458,36 @@ public class WorkbenchBlockEntity extends OrderedContainer { super(WorkbenchBlockEntity.this.recipeInventory, index, index, 0); } -// @Override -// public ItemStack insertStack(ItemStack stack, int count) { -// -// if(stack.isEmpty()) { -// return stack; -// } -// -// int allowedCount = Math.min(count, stack.getCount()); -// if( -// (!hasItemsLike(stack) || count == 1) && -// WorkbenchBlockEntity.this.hasRoom() -// ) { -// pushStack(stack.split(allowedCount)); -// } -// -// return stack; -// } + @Override + public ItemStack insertStack(ItemStack stack, int count) { + if(stack.isEmpty()) { + return stack; + } + + int allowedCount = Math.min(count, stack.getCount()); + if( + (getStack().isEmpty() || similarStacks(stack).isEmpty()) && + hasRoom() + ) { // Right click + pushStack(stack.split(allowedCount)); + } else { + distributeStack(stack, allowedCount); + if(stack.isEmpty()) { + return ItemStack.EMPTY; + } + } + + return stack; + } + + @Override + public ItemStack takeStack(int amount) { + var o = super.takeStack(amount); + if(!getStack().isEmpty()) { + distributeStack(getStack()); + } + return o; + } } public class CraftingResultGridSlot extends CraftingResultSlot { diff --git a/src/main/resources/assets/grafting/font/slot_order_overlay.json b/src/main/resources/assets/grafting/font/slot_order_overlay.json new file mode 100644 index 0000000..cfd5765 --- /dev/null +++ b/src/main/resources/assets/grafting/font/slot_order_overlay.json @@ -0,0 +1,22 @@ +{ + "providers": [ + { + "type": "space", + "advances": { + ".": -1, + "-": -19, + "_": -18, + " ": 18 + } + }, + { + "type": "bitmap", + "file": "grafting:gui/slots.png", + "ascent": -4, + "height": 64, + "chars": [ + "0123456789abcdefghijklmn" + ] + } + ] +} diff --git a/src/main/resources/assets/grafting/font/workbench_gui.json b/src/main/resources/assets/grafting/font/workbench_gui.json index d6ae828..81f9865 100644 --- a/src/main/resources/assets/grafting/font/workbench_gui.json +++ b/src/main/resources/assets/grafting/font/workbench_gui.json @@ -3,7 +3,7 @@ { "type": "space", "advances": { - ".": -134, + ".": -169, "-": -8 } }, diff --git a/src/main/resources/assets/grafting/textures/gui/slots.png b/src/main/resources/assets/grafting/textures/gui/slots.png new file mode 100644 index 0000000000000000000000000000000000000000..9fe911ff3bc1706a3e3072efc4b98c81f17a0da6 GIT binary patch literal 1353 zcmeAS@N?(olHy`uVBq!ia0y~yVB7#?J8-Z8$?DZg?LdLnOlRi+PiJR^f};Gi%$!sP z291fe6Ky>XJIEZ3x8I^9D%jm&k+Om>NnEsWO{j)bYk>BaR|!Qg+b=9p^eikCiQ(p} z(+QJ}nNwO?Aj)!(U!eS;=S5eqKk-2sf{zat+TXideP=K8pRY#2XA@)sOiz2L3p<%@ zS89n9bdk3*Y%uJc`D=~FvImy?-aUN$I<_aha{lk)FB+AT8SNS^&XpWiic_C=Z%fI! z-$w+)CW$;MepJ}3=ke!6>CLC+=Um(W+88ILTq-^yn6&W5u_EcslNJj33%ZMjF0QG+ zmfHGREPZQC>e=c~;%%RAeGK$~4GlNv8e7hw?>#ly;^R z2n z3eK@h^BS6+6SjEV%$9rqF6;f<`^IPg)iE?@7p4ojv)I-@#+PlUuc!8t=Atf3i7S5?{E% ze9xOh-_va5t}k4?cI{gErBed=i}y_rx>h}-IL2${7lY+mOKV;xo?9MtYE8T4?4?uI z+n%Y6_uBa`@!ajbd-ujOCNQ$CUcH*#@R)%NU;Uc3Yk6&urdUn8%V{!Kr(y}P^n+VeYe zZeG0_`novp+`+SFeL?h-Z{N0kwKPwE{O;X45M5GVfB)B<*~T?BH957{N@J^=8d~EL zV)fQfxm5bO{l?QzUYfPp&q{Xg3G&*U<~RMc_R^9IXI>UnE}f!wc8=ckaIeh8GcSWw z_0F;c>$;Qwv45r|O1|a70X=DUqO#=p?u!zJ1Od+r;o&b5zT!alo&z;UUulQMZ)8Gp5+VD+ zE@MRZ77wyZl?Ih>U~fa}yVTw|~CpR_{GCBWCX8#oK%D#hO)tf>&%t z`HFiBtJ=BtoZvnazGh$2S5a+gTe~DWM4fkh4(D literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/grafting/textures/gui/slots.xcf b/src/main/resources/assets/grafting/textures/gui/slots.xcf new file mode 100644 index 0000000000000000000000000000000000000000..ba60fca6f7c35d115c0d646aa470a716d471daca GIT binary patch literal 87049 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