增加me频道卡

This commit is contained in:
GaLicn 2025-09-22 17:45:18 +08:00
parent 39f89183e7
commit 45121b8647
13 changed files with 271 additions and 4 deletions

View File

@ -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<Component> 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<ItemStack> 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);
}
}

View File

@ -0,0 +1,8 @@
package com.extendedae_plus.bridge;
/**
* mixin 包下的桥接接口 mixin 进行 instanceof 检测和回调
*/
public interface InterfaceWirelessLinkBridge {
void extendedae_plus$updateWirelessLink();
}

View File

@ -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<ChannelCardItem> CHANNEL_CARD = ITEMS.register(
"channel_card",
() -> new ChannelCardItem(new Item.Properties())
);
/**
* PartItem 注册 AE2 部件模型

View File

@ -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);
});
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,8 @@
package com.extendedae_plus.bridge;
/**
* 旧名兼容已迁移到非 mixin 避免 Mixin 处理器禁止直接引用
*/
public interface InterfaceLogicChannelLinkBridge {
void extendedae_plus$updateWirelessLink();
}

View File

@ -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<appeng.api.networking.ticking.TickRateModulation> cir) {
if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
bridge.extendedae_plus$updateWirelessLink();
}
}
}

View File

@ -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<IGridNode> nodeSupplier;
public InterfaceNodeEndpointImpl(InterfaceLogicHost host, Supplier<IGridNode> 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();
}
}

View File

@ -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"
}

View File

@ -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"
}

View File

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "extendedae_plus:item/channel_card"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

View File

@ -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