From 45121b8647a5ec2f0a26bb2868928a578f1b36bc Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Mon, 22 Sep 2025 17:45:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0me=E9=A2=91=E9=81=93=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/items/ChannelCardItem.java | 63 +++++++++++++++ .../bridge/InterfaceWirelessLinkBridge.java | 8 ++ .../com/extendedae_plus/init/ModItems.java | 7 ++ .../extendedae_plus/init/UpgradeCards.java | 12 ++- .../InterfaceLogicChannelCardMixin.java | 74 ++++++++++++++++++ .../InterfaceLogicChannelLinkBridge.java | 8 ++ .../helpers/InterfaceLogicTickerMixin.java | 30 +++++++ .../endpoint/InterfaceNodeEndpointImpl.java | 49 ++++++++++++ .../assets/extendedae_plus/lang/en_us.json | 7 +- .../assets/extendedae_plus/lang/zh_cn.json | 7 +- .../models/item/channel_card.json | 6 ++ .../textures/item/channel_card.png | Bin 0 -> 455 bytes .../resources/extendedae_plus.mixins.json | 4 +- 13 files changed, 271 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java create mode 100644 src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java create mode 100644 src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java create mode 100644 src/main/resources/assets/extendedae_plus/models/item/channel_card.json create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/channel_card.png diff --git a/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java new file mode 100644 index 0000000..1d68551 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java @@ -0,0 +1,63 @@ +package com.extendedae_plus.ae.items; + +import appeng.items.materials.UpgradeCardItem; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResultHolder; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * 频道卡(MVP):仅存储一个 long 类型的频道号到 NBT:"channel"。 + * 继承 AE2 的 UpgradeCardItem 以复用升级卡判定与提示框架。 + */ +public class ChannelCardItem extends UpgradeCardItem { + public static final String TAG_CHANNEL = "channel"; + + public ChannelCardItem(Item.Properties properties) { + super(properties); + } + + public static void setChannel(ItemStack stack, long channel) { + CompoundTag tag = stack.getOrCreateTag(); + tag.putLong(TAG_CHANNEL, channel); + } + + public static long getChannel(ItemStack stack) { + CompoundTag tag = stack.getTag(); + return tag != null && tag.contains(TAG_CHANNEL) ? tag.getLong(TAG_CHANNEL) : 0L; + } + + @Override + public void appendHoverText(ItemStack stack, @Nullable Level level, List lines, TooltipFlag flag) { + super.appendHoverText(stack, level, lines, flag); + long ch = getChannel(stack); + if (ch == 0L) { + lines.add(Component.translatable("item.extendedae_plus.channel_card.channel.unset")); + } else { + lines.add(Component.translatable("item.extendedae_plus.channel_card.channel", ch)); + } + } + + @Override + public InteractionResultHolder use(Level level, Player player, InteractionHand hand) { + ItemStack stack = player.getItemInHand(hand); + if (!level.isClientSide) { + long ch = getChannel(stack); + boolean dec = player.isShiftKeyDown(); + long next = dec ? Math.max(0L, ch - 1L) : ch + 1L; + if (next != ch) { + setChannel(stack, next); + player.displayClientMessage(Component.translatable("item.extendedae_plus.channel_card.set", next), true); + } + } + return InteractionResultHolder.sidedSuccess(stack, level.isClientSide); + } +} diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java new file mode 100644 index 0000000..097b091 --- /dev/null +++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java @@ -0,0 +1,8 @@ +package com.extendedae_plus.bridge; + +/** + * 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。 + */ +public interface InterfaceWirelessLinkBridge { + void extendedae_plus$updateWirelessLink(); +} diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 0f7b430..79b4209 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -5,6 +5,7 @@ import appeng.api.parts.PartModels; import appeng.items.parts.PartModelsHelper; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; +import com.extendedae_plus.ae.items.ChannelCardItem; import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem; import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; import net.minecraft.world.item.BlockItem; @@ -76,6 +77,12 @@ public final class ModItems { "infinity_biginteger_cell", InfinityBigIntegerCellItem::new ); + // 频道卡(作为 AE 升级卡使用) + public static final RegistryObject CHANNEL_CARD = ITEMS.register( + "channel_card", + () -> new ChannelCardItem(new Item.Properties()) + ); + /** * 为 PartItem 注册 AE2 部件模型。 diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java index ddd73d4..05dec0d 100644 --- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -1,10 +1,15 @@ package com.extendedae_plus.init; import appeng.api.upgrades.Upgrades; +import appeng.core.definitions.AEBlocks; import appeng.core.definitions.AEItems; +import appeng.core.definitions.AEParts; +import appeng.core.localization.GuiText; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; - +/** + * + */ public class UpgradeCards { public UpgradeCards(final FMLCommonSetupEvent event) { event.enqueueWork(() -> { @@ -12,6 +17,11 @@ public class UpgradeCards { Upgrades.add(AEItems.ENERGY_CARD, ModItems.ENTITY_TICKER_PART_ITEM.get(), 8, "group.entity_ticker.name"); // 使用单一的 UpgradeCard Item 作为注册键,总共允许安装 4 张(不同等级由 ItemStack NBT 区分) Upgrades.add(ModItems.ENTITY_SPEED_CARD.get(), ModItems.ENTITY_TICKER_PART_ITEM.get(), 4, "group.entity_ticker.name"); + + // 新增:频道卡仅允许安装在 ME 接口(方块与部件)上,每台最多 1 张 + String interfaceGroup = GuiText.Interface.getTranslationKey(); + Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.INTERFACE, 1, interfaceGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.INTERFACE, 1, interfaceGroup); }); } } \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java new file mode 100644 index 0000000..659b120 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java @@ -0,0 +1,74 @@ +package com.extendedae_plus.mixin.ae2.helpers; + +import appeng.api.upgrades.IUpgradeInventory; +import appeng.helpers.InterfaceLogic; +import appeng.helpers.InterfaceLogicHost; +import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.wireless.IWirelessEndpoint; +import com.extendedae_plus.wireless.WirelessSlaveLink; +import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(InterfaceLogic.class) +public abstract class InterfaceLogicChannelCardMixin implements InterfaceWirelessLinkBridge { + + @Shadow(remap = false) public abstract IUpgradeInventory getUpgrades(); + + @Shadow(remap = false) public abstract appeng.api.networking.IGridNode getActionableNode(); + + @Shadow(remap = false) protected InterfaceLogicHost host; + + @Unique + private WirelessSlaveLink extendedae_plus$link; + + @Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false) + private void extendedae_plus$onUpgradesChangedTail(CallbackInfo ci) { + handleChannelCardChange(); + } + + @Inject(method = "readFromNBT", at = @At("TAIL"), remap = false) + private void extendedae_plus$afterReadNBT(net.minecraft.nbt.CompoundTag tag, CallbackInfo ci) { + // 重载后根据卡状态恢复连接 + handleChannelCardChange(); + } + + @Inject(method = "clearContent", at = @At("HEAD"), remap = false) + private void extendedae_plus$onClearContent(CallbackInfo ci) { + if (extendedae_plus$link != null) { + extendedae_plus$link.onUnloadOrRemove(); + } + } + + @Unique + private void handleChannelCardChange() { + var inv = getUpgrades(); + long channel = 0L; + for (ItemStack stack : inv) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + channel = ChannelCardItem.getChannel(stack); + break; + } + } + if (extendedae_plus$link == null) { + IWirelessEndpoint endpoint = new InterfaceNodeEndpointImpl(host, this::getActionableNode); + extendedae_plus$link = new WirelessSlaveLink(endpoint); + } + extendedae_plus$link.setFrequency(channel); + } + + @Override + public void extendedae_plus$updateWirelessLink() { + if (extendedae_plus$link != null) { + extendedae_plus$link.updateStatus(); + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java new file mode 100644 index 0000000..498d023 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java @@ -0,0 +1,8 @@ +package com.extendedae_plus.bridge; + +/** + * 旧名兼容:已迁移到非 mixin 包,避免 Mixin 处理器禁止直接引用。 + */ +public interface InterfaceLogicChannelLinkBridge { + void extendedae_plus$updateWirelessLink(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java new file mode 100644 index 0000000..77f9cbc --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java @@ -0,0 +1,30 @@ +package com.extendedae_plus.mixin.ae2.helpers; + +import appeng.helpers.InterfaceLogic; +import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +/** + * 注入到 InterfaceLogic.Ticker 的每tick回调,驱动无线链接状态更新。 + */ +@Mixin(targets = "appeng.helpers.InterfaceLogic$Ticker") +public abstract class InterfaceLogicTickerMixin { + + // Mixin 访问内部类的外部引用字段(javac 生成名 this$0) + @Shadow(remap = false) + @Final + private InterfaceLogic this$0; + + @Inject(method = "tickingRequest", at = @At("TAIL"), remap = false) + private void extendedae_plus$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall, + CallbackInfoReturnable cir) { + if (this$0 instanceof InterfaceWirelessLinkBridge bridge) { + bridge.extendedae_plus$updateWirelessLink(); + } + } +} diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java new file mode 100644 index 0000000..6736f4c --- /dev/null +++ b/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java @@ -0,0 +1,49 @@ +package com.extendedae_plus.wireless.endpoint; + +import appeng.api.networking.IGridNode; +import appeng.helpers.InterfaceLogicHost; +import com.extendedae_plus.wireless.IWirelessEndpoint; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; + +import java.util.Objects; +import java.util.function.Supplier; + +/** + * IWirelessEndpoint 实现:基于 InterfaceLogicHost 与节点提供者。 + */ +public class InterfaceNodeEndpointImpl implements IWirelessEndpoint { + private final InterfaceLogicHost host; + private final Supplier nodeSupplier; + + public InterfaceNodeEndpointImpl(InterfaceLogicHost host, Supplier nodeSupplier) { + this.host = Objects.requireNonNull(host); + this.nodeSupplier = Objects.requireNonNull(nodeSupplier); + } + + @Override + public ServerLevel getServerLevel() { + var be = host.getBlockEntity(); + if (be == null) return null; + Level lvl = be.getLevel(); + return (lvl instanceof ServerLevel) ? (ServerLevel) lvl : null; + } + + @Override + public BlockPos getBlockPos() { + var be = host.getBlockEntity(); + return be != null ? be.getBlockPos() : BlockPos.ZERO; + } + + @Override + public IGridNode getGridNode() { + return nodeSupplier.get(); + } + + @Override + public boolean isEndpointRemoved() { + var be = host.getBlockEntity(); + return be == null || be.isRemoved(); + } +} diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json index 320a792..ef955fa 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -87,5 +87,10 @@ "config.extendedae_plus.option.entityTickerBlackList": "Entity Ticker Blacklist", "config.extendedae_plus.option.entityTickerMultipliers": "Entity Ticker Extra Consumption Multipliers", "config.extendedae_plus.option.craftingPauseThreshold": "AE synthesis calculation pause check threshold", - "block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core" + "block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core", + + "item.extendedae_plus.channel_card": "Channel Card", + "item.extendedae_plus.channel_card.channel": "Frequency: %s", + "item.extendedae_plus.channel_card.channel.unset": "Frequency: Unset", + "item.extendedae_plus.channel_card.set": "Frequency set to: %s" } \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index d7996ab..210532c 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -92,5 +92,10 @@ "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_success": "样板已上传到装配矩阵", "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略", "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_no_matrix": "未在当前网络中找到已成型的装配矩阵", - "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "装配矩阵的样板仓已满或无法插入" + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "装配矩阵的样板仓已满或无法插入", + + "item.extendedae_plus.channel_card": "频道卡", + "item.extendedae_plus.channel_card.channel": "频率:%s", + "item.extendedae_plus.channel_card.channel.unset": "频率:未设置", + "item.extendedae_plus.channel_card.set": "已设置频率:%s" } \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/models/item/channel_card.json b/src/main/resources/assets/extendedae_plus/models/item/channel_card.json new file mode 100644 index 0000000..8699938 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/channel_card.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "extendedae_plus:item/channel_card" + } +} diff --git a/src/main/resources/assets/extendedae_plus/textures/item/channel_card.png b/src/main/resources/assets/extendedae_plus/textures/item/channel_card.png new file mode 100644 index 0000000000000000000000000000000000000000..2fe67ab91aa171c096bdf1c628d56947b2663360 GIT binary patch literal 455 zcmV;&0XY7NP)oOTmiB@DmIl35%|JuY>F2;O!3E$3d;}_*)Yk`AHwmg5 xM&r^95{Cy$a#9jFGr=+qx+ZJ^h^~e#J^)VZn#5&}mr?)#002ovPDHLkV1gDyzjXir literal 0 HcmV?d00001 diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index aaad6df..a856a94 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -67,7 +67,9 @@ "extendedae.common.TileExPatternProviderMixin", "extendedae.container.ContainerExPatternProviderMixin", "extendedae.container.ContainerExPatternTerminalMixin", - "extendedae.container.ContainerWirelessExPatternTerminalMixin" + "extendedae.container.ContainerWirelessExPatternTerminalMixin", + "ae2.helpers.InterfaceLogicChannelCardMixin", + "ae2.helpers.InterfaceLogicTickerMixin" ], "injectors": { "defaultRequire": 1