Skip to content

Commit

Permalink
added a setting in the crafter to emit a redstone signal when all ite…
Browse files Browse the repository at this point in the history
…ms have arrived
  • Loading branch information
Ellpeck committed Nov 30, 2024
1 parent cee38be commit c72d1cc
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ public boolean shouldTriggerClientSideContainerClosingOnOpen() {
container.modified = true;
}
}),
EMIT_REDSTONE_BUTTON((pos, data, player) -> {
if (player.containerMenu instanceof CraftingModuleContainer container) {
container.emitRedstone = !container.emitRedstone;
container.modified = true;
}
}),
STACK_SIZE_MODULE_BUTTON((pos, data, player) -> {
var container = (AbstractPipeContainer<?>) player.containerMenu;
var moduleData = container.moduleStack.getOrDefault(StackSizeModuleItem.Data.TYPE, StackSizeModuleItem.Data.DEFAULT);
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/de/ellpeck/prettypipes/pipe/PipeBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,12 @@ public int getAnalogOutputSignal(BlockState state, Level world, BlockPos pos) {
return Math.min(15, pipe.getItems().size());
}

@Override
protected int getSignal(BlockState blockState, BlockGetter blockAccess, BlockPos pos, Direction side) {
var pipe = Utility.getBlockEntity(PipeBlockEntity.class, blockAccess, pos);
return pipe.redstoneTicks > 0 ? 15 : 0;
}

@org.jetbrains.annotations.Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
Expand Down
18 changes: 17 additions & 1 deletion src/main/java/de/ellpeck/prettypipes/pipe/PipeBlockEntity.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,18 @@ protected void onContentsChanged(int slot) {
PipeBlockEntity.this.setChanged();
}
};

public PressurizerBlockEntity pressurizer;
public BlockState cover;
public int moduleDropCheck;
public int redstoneTicks;

private final Lazy<Integer> workRandomizer = Lazy.of(() -> this.level.random.nextInt(200));

private List<IPipeItem> itemCache;
private List<ActiveCraft> activeCraftCache;
private int lastItemAmount;
private int priority;
private final Lazy<Integer> workRandomizer = Lazy.of(() -> this.level.random.nextInt(200));

public PipeBlockEntity(BlockPos pos, BlockState state) {
super(Registry.pipeBlockEntity, pos, state);
Expand All @@ -99,13 +103,15 @@ public void saveAdditional(CompoundTag compound, HolderLookup.Provider provider)
compound.putInt("module_drop_check", this.moduleDropCheck);
if (this.cover != null)
compound.put("cover", NbtUtils.writeBlockState(this.cover));
compound.putInt("priority", this.priority);
}

@Override
public void loadAdditional(CompoundTag compound, HolderLookup.Provider provider) {
this.modules.deserializeNBT(provider, compound.getCompound("modules"));
this.moduleDropCheck = compound.getInt("module_drop_check");
this.cover = compound.contains("cover") ? NbtUtils.readBlockState(this.level != null ? this.level.holderLookup(Registries.BLOCK) : BuiltInRegistries.BLOCK.asLookup(), compound.getCompound("cover")) : null;
this.priority = compound.getInt("priority");
super.loadAdditional(compound, provider);
}

Expand Down Expand Up @@ -404,6 +410,10 @@ public ItemStack store(ItemStack stack, Direction direction) {
return stack;
}

public void setRedstoneTicks(int ticks) {
this.redstoneTicks = ticks;
}

@Override
public Component getDisplayName() {
return Component.translatable("container." + PrettyPipes.ID + ".pipe");
Expand Down Expand Up @@ -433,6 +443,12 @@ public static void tick(Level level, BlockPos pos, BlockState state, PipeBlockEn
if (pipe.pressurizer != null && pipe.pressurizer.isRemoved())
pipe.pressurizer = null;

if (pipe.redstoneTicks > 0) {
pipe.redstoneTicks--;
if (pipe.redstoneTicks <= 0)
level.updateNeighborsAt(pos, state.getBlock());
}

if (!pipe.level.isAreaLoaded(pipe.worldPosition, 1))
return;
var profiler = pipe.level.getProfiler();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class CraftingModuleContainer extends AbstractPipeContainer<CraftingModul
public ItemStackHandler output;
public boolean ensureItemOrder;
public boolean insertSingles;
public boolean emitRedstone;
public boolean modified;

public CraftingModuleContainer(MenuType<?> type, int id, Player player, BlockPos pos, int moduleIndex) {
Expand All @@ -28,6 +29,7 @@ protected void addSlots() {
var contents = this.moduleStack.get(CraftingModuleItem.Contents.TYPE);
this.ensureItemOrder = contents.ensureItemOrder();
this.insertSingles = contents.insertSingles();
this.emitRedstone = contents.emitRedstone();

this.input = Utility.copy(contents.input());
for (var i = 0; i < this.input.getSlots(); i++) {
Expand Down Expand Up @@ -57,7 +59,7 @@ public void setChanged() {
public void removed(Player playerIn) {
super.removed(playerIn);
if (this.modified) {
this.moduleStack.set(CraftingModuleItem.Contents.TYPE, new CraftingModuleItem.Contents(this.input, this.output, this.ensureItemOrder, this.insertSingles));
this.moduleStack.set(CraftingModuleItem.Contents.TYPE, new CraftingModuleItem.Contents(this.input, this.output, this.ensureItemOrder, this.insertSingles, this.emitRedstone));
this.tile.setChanged();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ protected void init() {
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<String>) () -> "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(
Tooltip.create(Component.translatable("info." + PrettyPipes.ID + ".emit_redstone.description").withStyle(ChatFormatting.GRAY))).build());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,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)));
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)));
this.speed = tier.forTier(20, 10, 5);
}

Expand Down Expand Up @@ -172,6 +172,7 @@ public Pair<ItemStack, Collection<ActiveCraft>> craft(ItemStack module, PipeBloc
if (craftableAmount <= 0)
return Pair.of(stack, List.of());
var slot = tile.getModuleSlot(module);
var contents = module.get(Contents.TYPE);

var network = PipeNetwork.get(tile.getLevel());
var items = network.getOrderedNetworkItems(tile.getBlockPos());
Expand All @@ -183,11 +184,10 @@ public Pair<ItemStack, Collection<ActiveCraft>> craft(ItemStack module, PipeBloc
var craftableCrafts = Mth.ceil(craftableAmount / (float) resultAmount);
var toCraft = Math.min(craftableCrafts, requiredCrafts);

var locks = new ArrayList<NetworkLock>();
var crafts = new ArrayList<ActiveCraft>();
var contents = module.get(Contents.TYPE);
var allCrafts = new ArrayList<ActiveCraft>();
// 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--) {
var locks = new ArrayList<NetworkLock>();
for (var i = 0; i < contents.input.getSlots(); i++) {
var in = contents.input.getStackInSlot(i);
if (in.isEmpty())
Expand All @@ -196,29 +196,27 @@ public Pair<ItemStack, Collection<ActiveCraft>> craft(ItemStack module, PipeBloc
if (!contents.ensureItemOrder)
copy.setCount(in.getCount() * toCraft);
var ret = network.requestLocksAndStartCrafting(tile.getBlockPos(), items, unavailableConsumer, copy, CraftingModuleItem.addDependency(dependencyChain, module), equalityTypes);
// set crafting dependencies as in progress immediately so that, when canceling, they don't leave behind half-crafted inbetween dependencies
// TODO to be more optimal, we should really do this when setting the main craft as in progress, but that would require storing references to all of the dependencies
ret.getRight().forEach(a -> a.inProgress = true);
locks.addAll(ret.getLeft());
crafts.addAll(ret.getRight());
allCrafts.addAll(ret.getRight());
}
var result = stack.copyWithCount(contents.ensureItemOrder ? resultAmount : resultAmount * toCraft);
var activeCraft = new ActiveCraft(tile.getBlockPos(), slot, locks, destPipe, result);
tile.getActiveCrafts().add(activeCraft);
allCrafts.add(activeCraft);
}
// set crafting dependencies as in progress immediately so that, when canceling, they don't leave behind half-crafted inbetween dependencies
// TODO to be more optimal, we should really do this when setting the main craft as in progress, but that would require storing references to all of the dependencies
crafts.forEach(c -> c.inProgress = true);

var remain = stack.copy();
remain.shrink(resultAmount * toCraft);
var result = stack.copy();
result.shrink(remain.getCount());

var activeCraft = new ActiveCraft(tile.getBlockPos(), slot, locks, destPipe, result);
tile.getActiveCrafts().add(activeCraft);
crafts.add(activeCraft);

return Pair.of(remain, crafts);
return Pair.of(remain, allCrafts);
}

@Override
public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack, Direction direction) {
var slot = tile.getModuleSlot(module);
var contents = module.get(Contents.TYPE);
var equalityTypes = ItemFilter.getEqualityTypes(tile);
var crafts = tile.getActiveCrafts();
var craft = crafts.stream()
Expand All @@ -227,7 +225,7 @@ public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack,
if (craft != null) {
craft.travelingIngredients.remove(craft.getTravelingIngredient(stack, equalityTypes));

if (module.get(Contents.TYPE).insertSingles) {
if (contents.insertSingles) {
var handler = tile.getItemHandler(direction);
if (handler != null) {
while (!stack.isEmpty()) {
Expand All @@ -239,9 +237,16 @@ public ItemStack store(ItemStack module, PipeBlockEntity tile, ItemStack stack,
}
}

// if we canceled the request and all input items are delivered (ie the machine actually got what it expected), remove it from the queue
if (craft.canceled && craft.travelingIngredients.size() <= 0 && craft.ingredientsToRequest.size() <= 0)
crafts.remove(craft);
if (craft.travelingIngredients.size() <= 0 && craft.ingredientsToRequest.size() <= 0) {
if (contents.emitRedstone) {
tile.redstoneTicks = 5;
tile.getLevel().updateNeighborsAt(tile.getBlockPos(), tile.getBlockState().getBlock());
}

// if we canceled the request and all input items are delivered (ie the machine actually got what it expected), remove it from the queue
if (craft.canceled)
crafts.remove(craft);
}
}
return stack;
}
Expand All @@ -264,13 +269,14 @@ private static Stack<ItemStack> addDependency(Stack<ItemStack> deps, ItemStack m
return deps;
}

public record Contents(ItemStackHandler input, ItemStackHandler output, boolean ensureItemOrder, boolean insertSingles) {
public record Contents(ItemStackHandler input, ItemStackHandler output, boolean ensureItemOrder, boolean insertSingles, boolean emitRedstone) {

public static final Codec<Contents> 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("insert_singles", false).forGetter(d -> d.insertSingles),
Codec.BOOL.optionalFieldOf("emit_redstone", false).forGetter(d -> d.emitRedstone)
).apply(i, Contents::new));
public static final DataComponentType<Contents> TYPE = DataComponentType.<Contents>builder().persistent(Contents.CODEC).cacheEncoding().build();

Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/assets/prettypipes/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@
"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.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.shift": "Hold Shift for info",
"info.prettypipes.populate": "P",
"info.prettypipes.populate.description": "Populate filter slots with items from adjacent inventories",
Expand Down

0 comments on commit c72d1cc

Please sign in to comment.