diff --git a/src/generated/resources/assets/ae2/lang/en_us.json b/src/generated/resources/assets/ae2/lang/en_us.json index 939f5812cae..9397f882fa1 100644 --- a/src/generated/resources/assets/ae2/lang/en_us.json +++ b/src/generated/resources/assets/ae2/lang/en_us.json @@ -971,6 +971,7 @@ "key.ae2.category": "Applied Energistics 2", "key.ae2.guide": "Open Guide for Items", "key.ae2.mouse_wheel_item_modifier": "Modifier for Mouse-Wheel Items", + "key.ae2.part_placement_opposite": "Place Parts on Opposite Side", "key.ae2.portable_fluid_cell": "Open Portable Fluid Cell", "key.ae2.portable_item_cell": "Open Portable Item Cell", "key.ae2.wireless_terminal": "Open Wireless Terminal", diff --git a/src/main/java/appeng/core/AppEngBase.java b/src/main/java/appeng/core/AppEngBase.java index 1b1a8c14eac..66dc989c7d6 100644 --- a/src/main/java/appeng/core/AppEngBase.java +++ b/src/main/java/appeng/core/AppEngBase.java @@ -57,6 +57,7 @@ import appeng.api.parts.CableRenderMode; import appeng.api.stacks.AEKeyType; import appeng.api.stacks.AEKeyTypesInternal; +import appeng.core.definitions.AEAttachmentTypes; import appeng.core.definitions.AEBlockEntities; import appeng.core.definitions.AEBlocks; import appeng.core.definitions.AEEntities; @@ -132,6 +133,7 @@ public AppEngBase(IEventBus modEventBus, ModContainer container) { AERecipeTypes.DR.register(modEventBus); AERecipeSerializers.DR.register(modEventBus); InitStructures.register(modEventBus); + AEAttachmentTypes.register(modEventBus); modEventBus.addListener(this::registerRegistries); modEventBus.addListener(MainCreativeTab::initExternal); diff --git a/src/main/java/appeng/core/AppEngClient.java b/src/main/java/appeng/core/AppEngClient.java index 27ee1510052..ee7370f9e9d 100644 --- a/src/main/java/appeng/core/AppEngClient.java +++ b/src/main/java/appeng/core/AppEngClient.java @@ -102,11 +102,13 @@ import appeng.client.render.tesr.InscriberTESR; import appeng.client.render.tesr.SkyChestTESR; import appeng.client.render.tesr.SkyStoneTankBlockEntityRenderer; +import appeng.core.definitions.AEAttachmentTypes; import appeng.core.definitions.AEBlockEntities; import appeng.core.definitions.AEBlocks; import appeng.core.definitions.AEEntities; import appeng.core.network.ServerboundPacket; import appeng.core.network.serverbound.MouseWheelPacket; +import appeng.core.network.serverbound.UpdateHoldingCtrlPacket; import appeng.entity.TinyTNTPrimedRenderer; import appeng.helpers.IMouseWheelItem; import appeng.hooks.BlockAttackHook; @@ -147,6 +149,10 @@ public class AppEngClient extends AppEngBase { "key.ae2.mouse_wheel_item_modifier", KeyConflictContext.IN_GAME, InputConstants.Type.KEYSYM, InputConstants.KEY_LSHIFT, "key.ae2.category"); + private static final KeyMapping PART_PLACEMENT_OPPOSITE = new KeyMapping( + "key.ae2.part_placement_opposite", KeyConflictContext.IN_GAME, InputConstants.Type.KEYSYM, + InputConstants.KEY_LCONTROL, "key.ae2.category"); + private final Guide guide; public AppEngClient(IEventBus modEventBus, ModContainer container) { @@ -253,6 +259,7 @@ public void registerHotkey(String id) { private void registerHotkeys(RegisterKeyMappingsEvent e) { e.register(OpenGuideHotkey.getHotkey()); e.register(MOUSE_WHEEL_ITEM_MODIFIER); + e.register(PART_PLACEMENT_OPPOSITE); Hotkeys.finalizeRegistration(e::register); } @@ -293,6 +300,7 @@ private void clientSetup(FMLClientSetupEvent event) { }); NeoForge.EVENT_BUS.addListener(this::wheelEvent); + NeoForge.EVENT_BUS.addListener(this::ctrlEvent); NeoForge.EVENT_BUS.register(OverlayManager.getInstance()); } @@ -359,6 +367,21 @@ private void wheelEvent(final InputEvent.MouseScrollingEvent me) { } } + private void ctrlEvent(InputEvent.Key event) { + if (event.getKey() == PART_PLACEMENT_OPPOSITE.getKey().getValue()) { + var player = Minecraft.getInstance().player; + + if (player != null) { + var isDown = event.getAction() == InputConstants.PRESS || event.getAction() == InputConstants.REPEAT; + var previousIsDown = player.getData(AEAttachmentTypes.HOLDING_CTRL); + if (previousIsDown != isDown) { + player.setData(AEAttachmentTypes.HOLDING_CTRL, isDown); + PacketDistributor.sendToServer(new UpdateHoldingCtrlPacket(isDown)); + } + } + } + } + public boolean shouldAddParticles(RandomSource r) { return switch (Minecraft.getInstance().options.particles().get()) { case ALL -> true; diff --git a/src/main/java/appeng/core/definitions/AEAttachmentTypes.java b/src/main/java/appeng/core/definitions/AEAttachmentTypes.java new file mode 100644 index 00000000000..734ece33c1e --- /dev/null +++ b/src/main/java/appeng/core/definitions/AEAttachmentTypes.java @@ -0,0 +1,22 @@ +package appeng.core.definitions; + +import java.util.function.Supplier; + +import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.attachment.AttachmentType; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.NeoForgeRegistries; + +import appeng.core.AppEng; + +public final class AEAttachmentTypes { + private static final DeferredRegister> ATTACHMENT_TYPES = DeferredRegister + .create(NeoForgeRegistries.ATTACHMENT_TYPES, AppEng.MOD_ID); + + public static final Supplier> HOLDING_CTRL = ATTACHMENT_TYPES.register("ctrl", + () -> AttachmentType.builder(() -> false).build()); + + public static void register(IEventBus modEventBus) { + ATTACHMENT_TYPES.register(modEventBus); + } +} diff --git a/src/main/java/appeng/core/network/InitNetwork.java b/src/main/java/appeng/core/network/InitNetwork.java index 2fa7d98fb35..ffc28ef700f 100644 --- a/src/main/java/appeng/core/network/InitNetwork.java +++ b/src/main/java/appeng/core/network/InitNetwork.java @@ -39,6 +39,7 @@ import appeng.core.network.serverbound.SelectKeyTypePacket; import appeng.core.network.serverbound.SwapSlotsPacket; import appeng.core.network.serverbound.SwitchGuisPacket; +import appeng.core.network.serverbound.UpdateHoldingCtrlPacket; public class InitNetwork { public static void init(RegisterPayloadHandlersEvent event) { @@ -78,6 +79,7 @@ public static void init(RegisterPayloadHandlersEvent event) { serverbound(registrar, SelectKeyTypePacket.TYPE, SelectKeyTypePacket.STREAM_CODEC); serverbound(registrar, SwapSlotsPacket.TYPE, SwapSlotsPacket.STREAM_CODEC); serverbound(registrar, SwitchGuisPacket.TYPE, SwitchGuisPacket.STREAM_CODEC); + serverbound(registrar, UpdateHoldingCtrlPacket.TYPE, UpdateHoldingCtrlPacket.STREAM_CODEC); // Bidirectional bidirectional(registrar, ConfigValuePacket.TYPE, ConfigValuePacket.STREAM_CODEC); diff --git a/src/main/java/appeng/core/network/serverbound/UpdateHoldingCtrlPacket.java b/src/main/java/appeng/core/network/serverbound/UpdateHoldingCtrlPacket.java new file mode 100644 index 00000000000..7a2e1a68c8e --- /dev/null +++ b/src/main/java/appeng/core/network/serverbound/UpdateHoldingCtrlPacket.java @@ -0,0 +1,38 @@ +package appeng.core.network.serverbound; + +import org.jetbrains.annotations.NotNull; + +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.server.level.ServerPlayer; + +import appeng.core.definitions.AEAttachmentTypes; +import appeng.core.network.CustomAppEngPayload; +import appeng.core.network.ServerboundPacket; + +public record UpdateHoldingCtrlPacket(boolean keyDown) implements ServerboundPacket { + public static final StreamCodec STREAM_CODEC = StreamCodec + .ofMember(UpdateHoldingCtrlPacket::write, UpdateHoldingCtrlPacket::decode); + + public static final Type TYPE = CustomAppEngPayload.createType("toggle_ctrl_down"); + + @NotNull + @Override + public Type type() { + return TYPE; + } + + public static UpdateHoldingCtrlPacket decode(RegistryFriendlyByteBuf buf) { + var keyDown = buf.readBoolean(); + return new UpdateHoldingCtrlPacket(keyDown); + } + + public void write(RegistryFriendlyByteBuf data) { + data.writeBoolean(keyDown); + } + + @Override + public void handleOnServer(ServerPlayer player) { + player.setData(AEAttachmentTypes.HOLDING_CTRL, keyDown); + } +} diff --git a/src/main/java/appeng/datagen/providers/localization/LocalizationProvider.java b/src/main/java/appeng/datagen/providers/localization/LocalizationProvider.java index e87505bc9f3..32e99a04edb 100644 --- a/src/main/java/appeng/datagen/providers/localization/LocalizationProvider.java +++ b/src/main/java/appeng/datagen/providers/localization/LocalizationProvider.java @@ -129,6 +129,7 @@ private void generateLocalizations() { add("key.ae2.wireless_terminal", "Open Wireless Terminal"); add("key.ae2.guide", "Open Guide for Items"); add("key.ae2.mouse_wheel_item_modifier", "Modifier for Mouse-Wheel Items"); + add("key.ae2.part_placement_opposite", "Place Parts on Opposite Side"); add("key.toggle_focus.desc", "Toggle search box focus"); add("stat.ae2.items_extracted", "Items extracted from ME Storage"); add("stat.ae2.items_inserted", "Items added to ME Storage"); diff --git a/src/main/java/appeng/parts/PartPlacement.java b/src/main/java/appeng/parts/PartPlacement.java index 2ed2738b46c..973d8e52efd 100644 --- a/src/main/java/appeng/parts/PartPlacement.java +++ b/src/main/java/appeng/parts/PartPlacement.java @@ -21,12 +21,12 @@ import appeng.api.parts.IPartItem; import appeng.api.parts.PartHelper; import appeng.core.AELog; +import appeng.core.definitions.AEAttachmentTypes; import appeng.parts.networking.CablePart; import appeng.util.Platform; import appeng.util.SettingsFrom; public class PartPlacement { - public static InteractionResult place(UseOnContext context) { var player = context.getPlayer(); @@ -133,7 +133,11 @@ public static Placement getPartPlacement(@Nullable Player player, // If a cable segment was clicked, try replacing that cable segment by the part var replaceCablePlacement = tryReplaceCableSegment(level, partStack, pos, clickLocation); if (replaceCablePlacement != null) { - return replaceCablePlacement; + side = replaceCablePlacement; + } + + if (player != null) { + side = player.getData(AEAttachmentTypes.HOLDING_CTRL) ? side.getOpposite() : side; } if (canPlacePartOnBlock(player, level, partStack, pos, side)) { @@ -153,7 +157,7 @@ public static Placement getPartPlacement(@Nullable Player player, } @Nullable - private static Placement tryReplaceCableSegment(Level level, ItemStack partStack, BlockPos pos, + private static Direction tryReplaceCableSegment(Level level, ItemStack partStack, BlockPos pos, Vec3 clickLocation) { // Check if there exists a host with a cable in its center var host = PartHelper.getPartHost(level, pos); @@ -183,7 +187,7 @@ private static Placement tryReplaceCableSegment(Level level, ItemStack partStack } if (host.canAddPart(partStack, hitSide)) { - return new Placement(pos, hitSide); + return hitSide; } else { return null; }