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 0000000..9fe911f Binary files /dev/null and b/src/main/resources/assets/grafting/textures/gui/slots.png differ 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 0000000..ba60fca Binary files /dev/null and b/src/main/resources/assets/grafting/textures/gui/slots.xcf differ