diff --git a/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java b/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java index 9548240..77eef56 100644 --- a/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java +++ b/src/main/java/de/ellpeck/prettypipes/packets/PacketButton.java @@ -7,6 +7,7 @@ import de.ellpeck.prettypipes.misc.ItemFilter.IFilteredContainer; import de.ellpeck.prettypipes.pipe.PipeBlockEntity; import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer; import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleContainer; +import de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleItem; import de.ellpeck.prettypipes.pipe.modules.modifier.FilterModifierModuleContainer; import de.ellpeck.prettypipes.pipe.modules.modifier.FilterModifierModuleItem; import de.ellpeck.prettypipes.pipe.modules.stacksize.StackSizeModuleItem; @@ -33,6 +34,7 @@ import java.util.ArrayList; import java.util.List; import static de.ellpeck.prettypipes.misc.DirectionSelector.IDirectionContainer; +import static de.ellpeck.prettypipes.pipe.modules.craft.CraftingModuleItem.*; public record PacketButton(BlockPos pos, int result, List data) implements CustomPacketPayload { @@ -95,15 +97,15 @@ public record PacketButton(BlockPos pos, int result, List data) impleme if (player.containerMenu instanceof IFilteredContainer filtered) filtered.getFilter().onButtonPacket(filtered, data.getFirst()); }), - ENSURE_ITEM_ORDER_BUTTON((pos, data, player) -> { + INSERTION_TYPE_BUTTON((pos, data, player) -> { if (player.containerMenu instanceof CraftingModuleContainer container) { - container.ensureItemOrder = !container.ensureItemOrder; + container.insertionType = InsertionType.values()[(container.insertionType.ordinal() + 1) % InsertionType.values().length]; container.modified = true; } }), - INSERT_SINGLES_BUTTON((pos, data, player) -> { + INSERT_UNSTACKED_BUTTON((pos, data, player) -> { if (player.containerMenu instanceof CraftingModuleContainer container) { - container.insertSingles = !container.insertSingles; + container.insertUnstacked = !container.insertUnstacked; container.modified = true; } }), diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java index 1aa1552..3f847e2 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleContainer.java @@ -6,18 +6,15 @@ import de.ellpeck.prettypipes.pipe.containers.AbstractPipeContainer; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.items.ItemStackHandler; -import net.neoforged.neoforge.items.SlotItemHandler; -import org.jetbrains.annotations.NotNull; public class CraftingModuleContainer extends AbstractPipeContainer { public ItemStackHandler input; public ItemStackHandler output; - public boolean ensureItemOrder; - public boolean insertSingles; public boolean emitRedstone; + public CraftingModuleItem.InsertionType insertionType; + public boolean insertUnstacked; public boolean modified; public CraftingModuleContainer(MenuType type, int id, Player player, BlockPos pos, int moduleIndex) { @@ -27,9 +24,9 @@ public class CraftingModuleContainer extends AbstractPipeContainer { @@ -28,26 +30,27 @@ public class CraftingModuleGui extends AbstractPipeGui @Override protected void init() { super.init(); - var cacheText = (Supplier) () -> "info." + PrettyPipes.ID + ".ensure_item_order_" + (this.menu.ensureItemOrder ? "on" : "off"); - this.addRenderableWidget(Button.builder(Component.translatable(cacheText.get()), button -> { - PacketButton.sendAndExecute(this.menu.tile.getBlockPos(), PacketButton.ButtonResult.ENSURE_ITEM_ORDER_BUTTON, List.of()); - button.setMessage(Component.translatable(cacheText.get())); - }).bounds(this.leftPos + this.imageWidth - 7 - 20, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( - Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".ensure_item_order.description").withStyle(ChatFormatting.GRAY))).build()); - - var singleText = (Supplier) () -> "info." + PrettyPipes.ID + ".insert_singles_" + (this.menu.insertSingles ? "on" : "off"); - this.addRenderableWidget(Button.builder(Component.translatable(singleText.get()), button -> { - PacketButton.sendAndExecute(this.menu.tile.getBlockPos(), PacketButton.ButtonResult.INSERT_SINGLES_BUTTON, List.of()); - button.setMessage(Component.translatable(singleText.get())); - }).bounds(this.leftPos + this.imageWidth - 7 - 20 - 22, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( - Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".insert_singles.description").withStyle(ChatFormatting.GRAY))).build()); var redstoneText = (Supplier) () -> "info." + PrettyPipes.ID + ".emit_redstone_" + (this.menu.emitRedstone ? "on" : "off"); this.addRenderableWidget(Button.builder(Component.translatable(redstoneText.get()), button -> { PacketButton.sendAndExecute(this.menu.tile.getBlockPos(), PacketButton.ButtonResult.EMIT_REDSTONE_BUTTON, List.of()); button.setMessage(Component.translatable(redstoneText.get())); - }).bounds(this.leftPos + 7, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( + }).bounds(this.leftPos + this.imageWidth - 7 - 20, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".emit_redstone.description").withStyle(ChatFormatting.GRAY))).build()); + var unstackedText = (Supplier) () -> "info." + PrettyPipes.ID + ".insert_unstacked_" + (this.menu.insertUnstacked ? "on" : "off"); + this.addRenderableWidget(Button.builder(Component.translatable(unstackedText.get()), button -> { + PacketButton.sendAndExecute(this.menu.tile.getBlockPos(), PacketButton.ButtonResult.INSERT_UNSTACKED_BUTTON, List.of()); + button.setMessage(Component.translatable(unstackedText.get())); + }).bounds(this.leftPos + this.imageWidth - 7 - 20 - 22, this.topPos + 17 + 32 + 18 * 2 + 2, 20, 20).tooltip( + Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".insert_unstacked.description").withStyle(ChatFormatting.GRAY))).build()); + + var insertionTypeText = (Supplier) () -> Component.translatable(this.menu.insertionType.translationKey()); + var insertionTypeTooltip = (Supplier) () -> Tooltip.create(Component.translatable(this.menu.insertionType.translationKey() + ".description").withStyle(ChatFormatting.GRAY)); + this.addRenderableWidget(Button.builder(insertionTypeText.get(), button -> { + PacketButton.sendAndExecute(this.menu.tile.getBlockPos(), PacketButton.ButtonResult.INSERTION_TYPE_BUTTON, List.of()); + button.setMessage(insertionTypeText.get()); + button.setTooltip(insertionTypeTooltip.get()); + }).bounds(this.leftPos + 7, this.topPos + 17 + 32 + 18 * 2 + 2, 42, 20).tooltip(insertionTypeTooltip.get()).build()); } } diff --git a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java index 0cfb78c..a50b78b 100644 --- a/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java +++ b/src/main/java/de/ellpeck/prettypipes/pipe/modules/craft/CraftingModuleItem.java @@ -3,6 +3,7 @@ package de.ellpeck.prettypipes.pipe.modules.craft; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.codecs.RecordCodecBuilder; +import de.ellpeck.prettypipes.PrettyPipes; import de.ellpeck.prettypipes.Registry; import de.ellpeck.prettypipes.Utility; import de.ellpeck.prettypipes.items.IModule; @@ -36,7 +37,7 @@ public class CraftingModuleItem extends ModuleItem { private final int speed; public CraftingModuleItem(String name, ModuleTier tier) { - super(name, new Properties().component(Contents.TYPE, new Contents(new ItemStackHandler(tier.forTier(1, 4, 9)), new ItemStackHandler(tier.forTier(1, 2, 4)), false, false, false))); + super(name, new Properties().component(Contents.TYPE, new Contents(new ItemStackHandler(tier.forTier(1, 4, 9)), new ItemStackHandler(tier.forTier(1, 2, 4)), false, InsertionType.ALL, false))); this.speed = tier.forTier(20, 10, 5); } @@ -87,7 +88,7 @@ public class CraftingModuleItem extends ModuleItem { var dest = tile.getAvailableDestination(Direction.values(), toRequest, true, true); if (dest != null) { // if we're ensuring the correct item order and the item is already on the way, don't do anything yet - if (!module.get(Contents.TYPE).ensureItemOrder || craft.travelingIngredients.isEmpty()) { + if (module.get(Contents.TYPE).insertionType != InsertionType.PER_ITEM || craft.travelingIngredients.isEmpty()) { var equalityTypes = ItemFilter.getEqualityTypes(tile); var requested = ingredient.map(l -> { // we can ignore the return value here since we're using a lock, so we know that the item is already waiting for us there @@ -217,15 +218,15 @@ public class CraftingModuleItem extends ModuleItem { var leftOfRequest = stack.getCount(); var allCrafts = new ArrayList(); - // if we're ensuring item order, all items for a single recipe should be sent in order first before starting on the next one! - for (var c = contents.ensureItemOrder ? toCraft : 1; c > 0; c--) { + // if we're inserting individual items or per craft, we need to split up the request into multiple crafts + for (var c = contents.insertionType != InsertionType.ALL ? toCraft : 1; c > 0; c--) { var toRequest = new ArrayList>(); for (var i = 0; i < contents.input.getSlots(); i++) { var in = contents.input.getStackInSlot(i); if (in.isEmpty()) continue; var request = in.copy(); - if (!contents.ensureItemOrder) + if (contents.insertionType == InsertionType.ALL) request.setCount(in.getCount() * toCraft); var ret = network.requestLocksAndStartCrafting(tile.getBlockPos(), items, unavailableConsumer, request, CraftingModuleItem.addDependency(dependencyChain, module), equalityTypes); for (var lock : ret.getLeft()) @@ -243,7 +244,7 @@ public class CraftingModuleItem extends ModuleItem { allCrafts.add(dep); } } - var crafted = contents.ensureItemOrder ? resultAmount : resultAmount * toCraft; + var crafted = contents.insertionType != InsertionType.ALL ? resultAmount : resultAmount * toCraft; // items we started craft dependencies for are ones that will be sent to us (so we're waiting for them immediately!) var activeCraft = new ActiveCraft(tile.getBlockPos(), slot, toRequest, new ArrayList<>(), destPipe, stack.copyWithCount(Math.min(crafted, leftOfRequest))); tile.getActiveCrafts().add(activeCraft); @@ -272,7 +273,7 @@ public class CraftingModuleItem extends ModuleItem { if (traveling.isEmpty()) craft.travelingIngredients.remove(traveling); - if (contents.insertSingles) { + if (contents.insertUnstacked) { var handler = tile.getItemHandler(direction); if (handler != null) { while (!stack.isEmpty()) { @@ -316,17 +317,28 @@ public class CraftingModuleItem extends ModuleItem { return deps; } - public record Contents(ItemStackHandler input, ItemStackHandler output, boolean ensureItemOrder, boolean insertSingles, boolean emitRedstone) { + public record Contents(ItemStackHandler input, ItemStackHandler output, boolean emitRedstone, InsertionType insertionType, boolean insertUnstacked) { public static final Codec CODEC = RecordCodecBuilder.create(i -> i.group( Utility.ITEM_STACK_HANDLER_CODEC.fieldOf("input").forGetter(d -> d.input), Utility.ITEM_STACK_HANDLER_CODEC.fieldOf("output").forGetter(d -> d.output), - Codec.BOOL.optionalFieldOf("ensure_item_order", false).forGetter(d -> d.ensureItemOrder), - Codec.BOOL.optionalFieldOf("insert_singles", false).forGetter(d -> d.insertSingles), - Codec.BOOL.optionalFieldOf("emit_redstone", false).forGetter(d -> d.emitRedstone) + Codec.BOOL.optionalFieldOf("emit_redstone", false).forGetter(d -> d.emitRedstone), + Codec.STRING.xmap(InsertionType::valueOf, InsertionType::name).optionalFieldOf("insertion_type", InsertionType.ALL).forGetter(d -> d.insertionType), + Codec.BOOL.optionalFieldOf("insert_unstacked", false).forGetter(d -> d.insertUnstacked) ).apply(i, Contents::new)); public static final DataComponentType TYPE = DataComponentType.builder().persistent(Contents.CODEC).cacheEncoding().build(); } + public enum InsertionType { + + ALL, + PER_ITEM, + PER_CRAFT; + + public String translationKey() { + return "info." + PrettyPipes.ID + ".insertion_type." + this.name().toLowerCase(Locale.ROOT); + } + } + } diff --git a/src/main/resources/assets/prettypipes/lang/en_us.json b/src/main/resources/assets/prettypipes/lang/en_us.json index 894bfd8..c1349c7 100644 --- a/src/main/resources/assets/prettypipes/lang/en_us.json +++ b/src/main/resources/assets/prettypipes/lang/en_us.json @@ -66,15 +66,18 @@ "info.prettypipes.blacklist": "\u00A74D", "info.prettypipes.whitelist.description": "Items in filter slots are allowed", "info.prettypipes.blacklist.description": "Items in filter slots are disallowed", - "info.prettypipes.insert_singles_on": "\u00A72S", - "info.prettypipes.insert_singles_off": "\u00A74\u00A7mS", - "info.prettypipes.insert_singles.description": "Whether items should be inserted one at a time, rather than as a stack\n\u00A7oRecommended for use with the Crafter", - "info.prettypipes.ensure_item_order_on": "\u00A72O", - "info.prettypipes.ensure_item_order_off": "\u00A74\u00A7mO", - "info.prettypipes.ensure_item_order.description": "Whether the module should wait for items to be inserted in the order they appear in the input slots\n\u00A7oRecommended for use with the Crafter", + "info.prettypipes.insert_unstacked_on": "\u00A72O", + "info.prettypipes.insert_unstacked_off": "\u00A74\u00A7mO", + "info.prettypipes.insert_unstacked.description": "Whether items should be inserted one at a time, rather than as a stack, causing them to be spread out in some containers\n\u00A7oRecommended for use with the Crafter", "info.prettypipes.emit_redstone_on": "\u00A72R", "info.prettypipes.emit_redstone_off": "\u00A74\u00A7mR", - "info.prettypipes.emit_redstone.description": "Whether a redstone signal should be emitted when all crafting items have arrived\nIf the module waits for items to be inserted in order, a redstone signal is emitted for each set of items required for a single craft\n\u00A7oRecommended for use with the Crafter", + "info.prettypipes.emit_redstone.description": "Whether a redstone signal should be emitted when all crafting items have arrived\nIf the module is set to insert items individually or per craft, a redstone signal is emitted for each set of items required for a single crafting operation\n\u00A7oRecommended for use with the Crafter", + "info.prettypipes.insertion_type.all": "All", + "info.prettypipes.insertion_type.all.description": "Causes the module to request all items required for the entire crafting request at once, even if the request consists of multiple crafting operations", + "info.prettypipes.insertion_type.per_item": "Singles", + "info.prettypipes.insertion_type.per_item.description": "Causes the module to request items individually so that they will be inserted in the order they appear in the input slots\n\u00A7oRecommended for use with the Crafter", + "info.prettypipes.insertion_type.per_craft": "Crafts", + "info.prettypipes.insertion_type.per_craft.description": "Causes the module to request all items for a single crafting operation at once", "info.prettypipes.shift": "Hold Shift for info", "info.prettypipes.populate": "P", "info.prettypipes.populate.description": "Populate filter slots with items from adjacent inventories",