From c1ed6aacc27e80f93cdf38f9a77525e7ac891990 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 20 Aug 2026 17:26:09 +0000 Subject: [PATCH 1/3] Add part offset support to crafting interfaces, Closes #138, Closes #152 Crafting interfaces already accepted Part Offset enhancements, but there was no way to configure the offset, and the offset was not taken into account when interacting with the targeted machine. * Let PartTypeCraftingBase extend PartTypeConfigurable, so that crafting interfaces expose the part offsets gui. * Add a part offsets button to the crafting interface gui and to the crafting interface settings gui. The latter is needed for the attuned crafting interface, which shows the settings gui as its main gui. * Re-target crafting interfaces when their target changes, so that the offset is picked up without having to save the part settings first. Recipes are reloaded and re-registered in the crafting network for the new target, which makes recipe validation, recipe attuning, and ingredient insertion happen at the offset position. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NHfm2FRdBFfrNNRxJSCPDX --- .../ContainerScreenPartInterfaceCrafting.java | 7 + ...erScreenPartInterfaceCraftingSettings.java | 24 +++ .../core/part/PartTypeCraftingBase.java | 16 +- .../part/PartTypeInterfaceCraftingBase.java | 37 +++++ .../GameTestHelpersIntegratedCrafting.java | 21 +++ .../gametest/GameTestsPartOffsets.java | 139 ++++++++++++++++++ ...ontainerPartInterfaceCraftingSettings.java | 11 ++ 7 files changed, 245 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsPartOffsets.java diff --git a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java index 57ff0a58a..e0126c161 100644 --- a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java +++ b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java @@ -13,6 +13,7 @@ import org.cyclops.cyclopscore.helper.GuiHelpers; import org.cyclops.integratedcrafting.Reference; import org.cyclops.integratedcrafting.inventory.container.ContainerPartInterfaceCrafting; +import org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipart; import org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipartAspects; import java.util.Collections; @@ -35,6 +36,12 @@ public void init() { Component.translatable("gui.integrateddynamics.part_settings"), createServerPressable(ContainerMultipartAspects.BUTTON_SETTINGS, b -> {}), true, Images.CONFIG_BOARD, -2, -3)); + if (getMenu().getPartType().supportsOffsets()) { + addRenderableWidget(new ButtonImage(this.leftPos + 138, this.topPos + 4, 15, 15, + Component.translatable("gui.integrateddynamics.part_offsets"), + createServerPressable(ContainerMultipart.BUTTON_OFFSETS, b -> {}), true, + org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_MIDDLE_OFFSET, -2, -3)); + } } @Override diff --git a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java index f0380fd59..32c56fab9 100644 --- a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java +++ b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java @@ -12,14 +12,17 @@ import net.minecraft.world.entity.player.Inventory; import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent; import org.cyclops.cyclopscore.client.gui.component.button.ButtonCheckbox; +import org.cyclops.cyclopscore.client.gui.component.button.ButtonImage; import org.cyclops.cyclopscore.client.gui.component.input.IInputListener; import org.cyclops.cyclopscore.client.gui.component.input.WidgetArrowedListField; import org.cyclops.cyclopscore.client.gui.component.input.WidgetNumberField; +import org.cyclops.cyclopscore.client.gui.image.IImage; import org.cyclops.cyclopscore.helper.Helpers; import org.cyclops.cyclopscore.helper.L10NHelpers; import org.cyclops.cyclopscore.helper.ValueNotifierHelpers; import org.cyclops.integratedcrafting.Reference; import org.cyclops.integratedcrafting.inventory.container.ContainerPartInterfaceCraftingSettings; +import org.cyclops.integrateddynamics.client.gui.image.Images; import org.cyclops.integrateddynamics.core.client.gui.WidgetTextFieldDropdown; import org.cyclops.integrateddynamics.core.client.gui.container.ContainerScreenPartSettings; import org.lwjgl.glfw.GLFW; @@ -95,6 +98,17 @@ protected void onSave() { public void init() { super.init(); + if (getMenu().getPartType().supportsOffsets()) { + addRenderableWidget(new ButtonImage(this.leftPos - 20, this.topPos, 18, 18, + Component.translatable("gui.integrateddynamics.part_offsets"), + createServerPressable(ContainerPartInterfaceCraftingSettings.BUTTON_OFFSETS, (button) -> {}), + new IImage[]{ + Images.BUTTON_BACKGROUND_INACTIVE, + Images.BUTTON_MIDDLE_OFFSET + }, + false, 0, 0)); + } + ingredientComponentSideSelector = new WidgetArrowedListField>(font, leftPos + 106, topPos + 9, 68, 15, true, Component.translatable("gui.integratedcrafting.partsettings.ingredient"), @@ -202,6 +216,16 @@ protected void renderBg(GuiGraphics guiGraphics, float partialTicks, int mouseX, checkboxFieldBlockingMode.render(guiGraphics, mouseX, mouseY, partialTicks); } + @Override + protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { + super.renderLabels(guiGraphics, mouseX, mouseY); + + if (getMenu().getPartType().supportsOffsets() && isHovering(-20, 0, 18, 18, mouseX, mouseY)) { + drawTooltip(Lists.newArrayList(Component.translatable("gui.integrateddynamics.part_offsets")), + guiGraphics.pose(), mouseX - leftPos, mouseY - topPos); + } + } + @Override protected int getBaseYSize() { return 256; diff --git a/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeCraftingBase.java b/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeCraftingBase.java index 9cec80dc1..860c5a664 100644 --- a/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeCraftingBase.java +++ b/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeCraftingBase.java @@ -1,8 +1,6 @@ package org.cyclops.integratedcrafting.core.part; -import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.MenuProvider; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; @@ -10,7 +8,6 @@ import net.minecraft.world.inventory.AbstractContainerMenu; import org.apache.commons.lang3.tuple.Triple; import org.cyclops.cyclopscore.init.ModBase; -import org.cyclops.cyclopscore.network.PacketCodec; import org.cyclops.integratedcrafting.IntegratedCrafting; import org.cyclops.integrateddynamics.api.part.IPartContainer; import org.cyclops.integrateddynamics.api.part.IPartState; @@ -21,15 +18,20 @@ import org.cyclops.integrateddynamics.core.helper.PartHelpers; import org.cyclops.integrateddynamics.core.inventory.container.ContainerPartSettings; import org.cyclops.integrateddynamics.core.part.PartTypeBase; +import org.cyclops.integrateddynamics.core.part.PartTypeConfigurable; import javax.annotation.Nullable; import java.util.Optional; /** * Base part for a crafting part. + * + * This extends {@link PartTypeConfigurable} so that crafting parts + * expose the part settings and part offsets guis. + * * @author rubensworks */ -public abstract class PartTypeCraftingBase

, S extends IPartState

> extends PartTypeBase { +public abstract class PartTypeCraftingBase

, S extends IPartState

> extends PartTypeConfigurable { public PartTypeCraftingBase(String name) { super(name, new PartRenderPosition(0.1875F, 0.1875F, 0.625F, 0.625F)); @@ -64,10 +66,4 @@ public boolean shouldTriggerClientSideContainerClosingOnOpen() { }); } - @Override - public void writeExtraGuiDataSettings(RegistryFriendlyByteBuf packetBuffer, PartPos pos, ServerPlayer player) { - PacketCodec.write(packetBuffer, pos); - packetBuffer.writeUtf(this.getUniqueName().toString()); - } - } diff --git a/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingBase.java b/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingBase.java index 0d38b5f36..cdad4ea3c 100644 --- a/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingBase.java +++ b/src/main/java/org/cyclops/integratedcrafting/core/part/PartTypeInterfaceCraftingBase.java @@ -92,6 +92,36 @@ protected void addTargetToNetwork(INetwork network, PartTarget pos, S state, boo }); } + /** + * Update the target of the given part state, and make the crafting network aware of it. + * + * Contrary to {@link #removeTargetFromNetwork(INetwork, PartPos, S)} followed by + * {@link #addTargetToNetwork(INetwork, PartTarget, S, boolean)}, + * this retains the network and channel of the part, + * as only the targeted position changes. + * + * @param network The network. + * @param newTarget The new target. + * @param state The part state. + */ + protected void retarget(INetwork network, PartTarget newTarget, S state) { + ICraftingNetwork craftingNetwork = state.getCraftingNetwork(); + + // Unregister the recipes for the old target from the crafting network. + // This must happen before the recipes are reloaded, as the old recipes are needed for a proper removal. + if (craftingNetwork != null) { + craftingNetwork.removeCraftingInterface(state.getChannelCrafting(), state); + } + + // Update the target, and reload all recipes based on this new target. + state.setTarget(newTarget); + state.setNetworks(network, craftingNetwork, NetworkHelpers.getPartNetworkChecked(network), state.getChannel(), + ValueDeseralizationContext.of(newTarget.getCenter().getPos().getLevel(true)), false); + + // Re-register to the crafting network, so that the recipes for the new target are picked up. + state.setShouldAddToCraftingNetwork(true); + } + protected void removeTargetFromNetwork(INetwork network, PartPos pos, S state) { ICraftingNetwork craftingNetwork = state.getCraftingNetwork(); if (craftingNetwork != null) { @@ -139,6 +169,13 @@ public void update(INetwork network, IPartNetwork partNetwork, PartTarget target // This can occur when the part chunk is being reloaded. if (state.getCraftingNetwork() == null) { addTargetToNetwork(network, target, state, false); + } else { + // Detect changes to our target, which can occur when the target offset is changed. + // The target is recalculated here, as offset variables may have changed it during this update. + PartTarget currentTarget = getTarget(target.getCenter(), state); + if (!currentTarget.equals(state.getTarget())) { + retarget(network, currentTarget, state); + } } int channelCrafting = state.getChannelCrafting(); diff --git a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java index dc2fc7e28..fbd605bcd 100644 --- a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java +++ b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestHelpersIntegratedCrafting.java @@ -4,6 +4,7 @@ import com.google.common.collect.Maps; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.Vec3i; import net.minecraft.gametest.framework.GameTestAssertException; import net.minecraft.gametest.framework.GameTestHelper; import net.minecraft.resources.ResourceLocation; @@ -34,6 +35,8 @@ import org.cyclops.integrateddynamics.RegistryEntries; import org.cyclops.integrateddynamics.api.evaluate.variable.IValue; import org.cyclops.integrateddynamics.api.evaluate.variable.IValueType; +import org.cyclops.integrateddynamics.api.part.IPartState; +import org.cyclops.integrateddynamics.api.part.IPartType; import org.cyclops.integrateddynamics.api.part.PartPos; import org.cyclops.integrateddynamics.api.part.PartTarget; import org.cyclops.integrateddynamics.api.part.aspect.IAspectWrite; @@ -241,6 +244,24 @@ public static , V extends IValue> void setCraftingInterf ((PartTypeInterfaceCrafting.State) partStateHolder.getState()).getCraftingJobHandler().setBlockingJobsMode(blocking); } + /** + * Make the part at the given position target another position via an offset. + * + * This also increases the max offset of the part, + * just like applying part offset enhancement items would do. + * + * @param partPos The (center) position of the part. + * @param offset The target offset. + */ + public static void setPartOffset(PartPos partPos, Vec3i offset) { + PartHelpers.PartStateHolder partStateHolder = PartHelpers.getPart(partPos); + IPartState partState = partStateHolder.getState(); + partState.setMaxOffset(Math.max(Math.abs(offset.getX()), Math.max(Math.abs(offset.getY()), Math.abs(offset.getZ())))); + if (!((IPartType) partStateHolder.getPart()).setTargetOffset(partState, partPos, offset)) { + throw new GameTestAssertException("Could not set target offset " + offset + " on the part at " + partPos); + } + } + public static , V extends IValue> void setCraftingInterfaceUpdateInterval(PartPos writerPos, int updateInterval) { PartHelpers.PartStateHolder partStateHolder = PartHelpers.getPart(writerPos); partStateHolder.getState().setUpdateInterval(updateInterval); diff --git a/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsPartOffsets.java b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsPartOffsets.java new file mode 100644 index 000000000..282807687 --- /dev/null +++ b/src/main/java/org/cyclops/integratedcrafting/gametest/GameTestsPartOffsets.java @@ -0,0 +1,139 @@ +package org.cyclops.integratedcrafting.gametest; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; +import net.minecraft.gametest.framework.GameTest; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.crafting.RecipeType; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.ChestBlockEntity; +import net.neoforged.neoforge.gametest.GameTestHolder; +import net.neoforged.neoforge.gametest.PrefixGameTestTemplate; +import org.apache.commons.lang3.tuple.Triple; +import org.cyclops.integratedcrafting.Reference; +import org.cyclops.integratedcrafting.part.PartTypeInterfaceCrafting; +import org.cyclops.integratedcrafting.part.PartTypeInterfaceCraftingAttuned; + +import static org.cyclops.integratedcrafting.gametest.GameTestHelpersIntegratedCrafting.createBasicNetwork; +import static org.cyclops.integratedcrafting.gametest.GameTestHelpersIntegratedCrafting.enableRecipeInWriter; +import static org.cyclops.integratedcrafting.gametest.GameTestHelpersIntegratedCrafting.setPartOffset; + +/** + * Game tests for crafting interfaces that target machines via a part offset. + * + * @author rubensworks + */ +@GameTestHolder(Reference.MOD_ID) +@PrefixGameTestTemplate(false) +public class GameTestsPartOffsets { + + public static final String TEMPLATE_EMPTY = "empty10"; + public static final int TIMEOUT = 2000; + public static final BlockPos POS = BlockPos.ZERO.offset(2, 0, 2); + + /** + * The crafting interface points at an empty block, and reaches the crafting table via an offset. + */ + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testItemsCraftChestOffset(GameTestHelper helper) { + GameTestHelpersIntegratedCrafting.INetworkPositions positions = createBasicNetwork(helper, POS); + + // Move the crafting table one block away from the crafting interface, + // and make the crafting interface target it via an offset. + helper.setBlock(POS.west(), Blocks.AIR); + helper.setBlock(POS.west().north(), Blocks.CRAFTING_TABLE); + setPartOffset(positions.interfaces().get(0), new Vec3i(0, 0, -1)); + + // Insert items in interface chest + ChestBlockEntity chestIn = helper.getBlockEntity(POS.east()); + chestIn.setItem(0, new ItemStack(Items.OAK_PLANKS, 64)); + + // Add chest recipe to crafting interface + positions.interfaceRecipeAdders().get(0).accept(Triple.of(0, RecipeType.CRAFTING, ResourceLocation.fromNamespaceAndPath("minecraft", "chest"))); + + // Enable crafting aspect in crafting writer + enableRecipeInWriter(helper, positions.writer(), new ItemStack(Items.CHEST)); + + helper.succeedWhen(() -> { + // Check crafting interface state + helper.assertTrue(positions.interfaceStates().get(0).isRecipeSlotValid(0), "Recipe in crafting interface is not valid"); + + // Check if items have been crafted + helper.assertValueEqual(chestIn.getItem(0).getItem(), Items.OAK_PLANKS, "Slot 0 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(0).getCount(), 56, "Slot 0 amount is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getItem(), Items.CHEST, "Slot 1 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getCount(), 1, "Slot 1 amount is incorrect"); + }); + } + + /** + * The crafting interface points at a furnace, but must ignore it because an offset makes it target a crafting table. + */ + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testItemsCraftChestOffsetIgnoresAdjacentMachine(GameTestHelper helper) { + GameTestHelpersIntegratedCrafting.INetworkPositions positions = createBasicNetwork(helper, POS); + + // Place a furnace directly in front of the crafting interface, + // and make the crafting interface target a crafting table via an offset. + helper.setBlock(POS.west(), Blocks.FURNACE); + helper.setBlock(POS.west().north(), Blocks.CRAFTING_TABLE); + setPartOffset(positions.interfaces().get(0), new Vec3i(0, 0, -1)); + + // Insert items in interface chest + ChestBlockEntity chestIn = helper.getBlockEntity(POS.east()); + chestIn.setItem(0, new ItemStack(Items.OAK_PLANKS, 64)); + + // Add chest recipe to crafting interface + positions.interfaceRecipeAdders().get(0).accept(Triple.of(0, RecipeType.CRAFTING, ResourceLocation.fromNamespaceAndPath("minecraft", "chest"))); + + // Enable crafting aspect in crafting writer + enableRecipeInWriter(helper, positions.writer(), new ItemStack(Items.CHEST)); + + helper.succeedWhen(() -> { + // Check crafting interface state + helper.assertTrue(positions.interfaceStates().get(0).isRecipeSlotValid(0), "Recipe in crafting interface is not valid"); + + // Check if items have been crafted + helper.assertValueEqual(chestIn.getItem(0).getItem(), Items.OAK_PLANKS, "Slot 0 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(0).getCount(), 56, "Slot 0 amount is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getItem(), Items.CHEST, "Slot 1 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getCount(), 1, "Slot 1 amount is incorrect"); + }); + } + + /** + * The attuned crafting interface must read the recipes of the machine it targets via an offset. + */ + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testItemsCraftAttunedPlanksOffset(GameTestHelper helper) { + GameTestHelpersIntegratedCrafting.INetworkPositions positions = createBasicNetwork(helper, POS, true); + + // Move the crafting table one block away from the crafting interface, + // and make the crafting interface target it via an offset. + helper.setBlock(POS.west(), Blocks.AIR); + helper.setBlock(POS.west().north(), Blocks.CRAFTING_TABLE); + setPartOffset(positions.interfaces().get(0), new Vec3i(0, 0, -1)); + + // Insert items in interface chest + ChestBlockEntity chestIn = helper.getBlockEntity(POS.east()); + chestIn.setItem(0, new ItemStack(Items.OAK_LOG, 64)); + + // Enable crafting aspect in crafting writer + enableRecipeInWriter(helper, positions.writer(), new ItemStack(Items.OAK_PLANKS)); + + helper.succeedWhen(() -> { + // Check crafting interface state + helper.assertTrue(positions.interfaceStates().get(0).hasValidTarget(), "Crafting interface has no valid target"); + + // Check if items have been crafted + helper.assertValueEqual(chestIn.getItem(0).getItem(), Items.OAK_LOG, "Slot 0 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(0).getCount(), 63, "Slot 0 amount is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getItem(), Items.OAK_PLANKS, "Slot 1 item is incorrect"); + helper.assertValueEqual(chestIn.getItem(1).getCount(), 4, "Slot 1 amount is incorrect"); + }); + } + +} diff --git a/src/main/java/org/cyclops/integratedcrafting/inventory/container/ContainerPartInterfaceCraftingSettings.java b/src/main/java/org/cyclops/integratedcrafting/inventory/container/ContainerPartInterfaceCraftingSettings.java index 170f5b2a9..adcb10320 100644 --- a/src/main/java/org/cyclops/integratedcrafting/inventory/container/ContainerPartInterfaceCraftingSettings.java +++ b/src/main/java/org/cyclops/integratedcrafting/inventory/container/ContainerPartInterfaceCraftingSettings.java @@ -5,6 +5,7 @@ import net.minecraft.core.Direction; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; @@ -28,6 +29,8 @@ */ public class ContainerPartInterfaceCraftingSettings extends ContainerPartSettings { + public static final String BUTTON_OFFSETS = "button_offsets"; + private final int lastChannelInterfaceCraftingValueId; private final Map, Integer> targetSideOverrideValueIds; private final int lastDisableCraftingCheckValueId; @@ -48,6 +51,14 @@ public ContainerPartInterfaceCraftingSettings(int id, Inventory playerInventory, } lastDisableCraftingCheckValueId = getNextValueId(); lastBlockingModeValueId = getNextValueId(); + + // Expose the offsets gui from within the settings gui, + // as some crafting interfaces (such as the attuned one) show the settings gui as their main gui. + putButtonAction(ContainerPartInterfaceCraftingSettings.BUTTON_OFFSETS, (s, containerExtended) -> { + if (!player.level().isClientSide()) { + PartHelpers.openContainerPartOffsets((ServerPlayer) player, getTarget().getCenter(), getPartType()); + } + }); } @Override From 4ba08d22fa962136f484306fc3c201b41c67c91f Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 23 Aug 2026 14:16:50 +0000 Subject: [PATCH 2/3] Drop the part offsets button from the crafting interface gui Addresses review feedback on #215. The crafting interface settings gui already exposes the part offsets gui, and that settings gui is also the main gui of the attuned crafting interface. Keeping a second button in the crafting interface gui would give players two different places to reach the same screen. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01NHfm2FRdBFfrNNRxJSCPDX --- .../client/gui/ContainerScreenPartInterfaceCrafting.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java index e0126c161..57ff0a58a 100644 --- a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java +++ b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCrafting.java @@ -13,7 +13,6 @@ import org.cyclops.cyclopscore.helper.GuiHelpers; import org.cyclops.integratedcrafting.Reference; import org.cyclops.integratedcrafting.inventory.container.ContainerPartInterfaceCrafting; -import org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipart; import org.cyclops.integrateddynamics.core.inventory.container.ContainerMultipartAspects; import java.util.Collections; @@ -36,12 +35,6 @@ public void init() { Component.translatable("gui.integrateddynamics.part_settings"), createServerPressable(ContainerMultipartAspects.BUTTON_SETTINGS, b -> {}), true, Images.CONFIG_BOARD, -2, -3)); - if (getMenu().getPartType().supportsOffsets()) { - addRenderableWidget(new ButtonImage(this.leftPos + 138, this.topPos + 4, 15, 15, - Component.translatable("gui.integrateddynamics.part_offsets"), - createServerPressable(ContainerMultipart.BUTTON_OFFSETS, b -> {}), true, - org.cyclops.integrateddynamics.client.gui.image.Images.BUTTON_MIDDLE_OFFSET, -2, -3)); - } } @Override From 6f858903936f5bc0398962380cd8924fa21a022a Mon Sep 17 00:00:00 2001 From: Ruben Taelman Date: Sun, 23 Aug 2026 19:41:45 +0200 Subject: [PATCH 3/3] Move offset button lower --- .../gui/ContainerScreenPartInterfaceCraftingSettings.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java index 32c56fab9..2189c2dcc 100644 --- a/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java +++ b/src/main/java/org/cyclops/integratedcrafting/client/gui/ContainerScreenPartInterfaceCraftingSettings.java @@ -99,7 +99,7 @@ public void init() { super.init(); if (getMenu().getPartType().supportsOffsets()) { - addRenderableWidget(new ButtonImage(this.leftPos - 20, this.topPos, 18, 18, + addRenderableWidget(new ButtonImage(this.leftPos - 20, this.topPos + 10, 18, 18, Component.translatable("gui.integrateddynamics.part_offsets"), createServerPressable(ContainerPartInterfaceCraftingSettings.BUTTON_OFFSETS, (button) -> {}), new IImage[]{ @@ -220,7 +220,7 @@ protected void renderBg(GuiGraphics guiGraphics, float partialTicks, int mouseX, protected void renderLabels(GuiGraphics guiGraphics, int mouseX, int mouseY) { super.renderLabels(guiGraphics, mouseX, mouseY); - if (getMenu().getPartType().supportsOffsets() && isHovering(-20, 0, 18, 18, mouseX, mouseY)) { + if (getMenu().getPartType().supportsOffsets() && isHovering(-20, 0 + 10, 18, 18, mouseX, mouseY)) { drawTooltip(Lists.newArrayList(Component.translatable("gui.integrateddynamics.part_offsets")), guiGraphics.pose(), mouseX - leftPos, mouseY - topPos); }