feat: 添加内存卡对实体加速器、实体加速卡的复制粘贴支持

This commit is contained in:
C-H716 2025-09-28 20:18:30 +08:00
parent d61a418731
commit 869ab98af4
8 changed files with 287 additions and 20 deletions

View File

@ -0,0 +1,41 @@
package com.extendedae_plus.ae.api.config;
import appeng.api.config.Setting;
import appeng.api.config.YesNo;
import com.google.common.base.Preconditions;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
public final class Settings {
private static final Map<String, Setting<?>> SETTINGS = new HashMap<>();
public static final Setting<YesNo> ACCELERATE = register("accelerate", YesNo.NO, YesNo.YES);
private Settings() {
}
private synchronized static <T extends Enum<T>> Setting<T> register(String name, Class<T> enumClass) {
Preconditions.checkState(!SETTINGS.containsKey(name));
var setting = new Setting<>(name, enumClass);
SETTINGS.put(name, setting);
return setting;
}
@SafeVarargs
private synchronized static <T extends Enum<T>> Setting<T> register(String name, T firstOption, T... moreOptions) {
Preconditions.checkState(!SETTINGS.containsKey(name));
var setting = new Setting<T>(name, firstOption.getDeclaringClass(), EnumSet.of(firstOption, moreOptions));
SETTINGS.put(name, setting);
return setting;
}
public static Setting<?> getOrThrow(String name) {
var setting = SETTINGS.get(name);
if (setting == null) {
throw new IllegalArgumentException("Unknown setting '" + name + "'");
}
return setting;
}
}

View File

@ -13,6 +13,7 @@ import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils;
import com.extendedae_plus.util.entitySpeed.PowerUtils;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.registries.ForgeRegistries;
@ -93,7 +94,7 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu<EntitySpeedTickerPart
* @param slot 发生变化的槽位
*/
@Override
public void onSlotChange(net.minecraft.world.inventory.Slot slot) {
public void onSlotChange(Slot slot) {
super.onSlotChange(slot);
if (isClientSide()) {
updateCardCounts();

View File

@ -2,6 +2,7 @@ package com.extendedae_plus.ae.parts;
import appeng.api.config.Actionable;
import appeng.api.config.PowerMultiplier;
import appeng.api.config.YesNo;
import appeng.api.networking.GridFlags;
import appeng.api.networking.IGridNode;
import appeng.api.networking.energy.IEnergyService;
@ -62,7 +63,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
public static final PartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel"));
public EntitySpeedTickerMenu menu; // 当前打开的菜单实例
private boolean accelerateEnabled = true; // 是否启用加速
private boolean networkEnergySufficient = true; // 网络能量是否充足
/**
@ -75,14 +75,16 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
.setFlags(GridFlags.REQUIRE_CHANNEL)
.setIdlePowerUsage(1)
.addService(IGridTickable.class, this);
// 注册可记忆的配置YES/NO
this.getConfigManager().registerSetting(
com.extendedae_plus.ae.api.config.Settings.ACCELERATE,
YesNo.YES
);
}
public boolean getAccelerateEnabled() {
return this.accelerateEnabled;
}
public boolean isNetworkEnergySufficient() {
return this.networkEnergySufficient;
return this.getConfigManager().getSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE) == YesNo.YES;
}
/**
@ -90,12 +92,17 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka
* @param enabled 是否启用加速
*/
public void setAccelerateEnabled(boolean enabled) {
this.accelerateEnabled = enabled;
this.getConfigManager().putSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE, enabled ? YesNo.YES : YesNo.NO);
// 是否启用加速
if (menu != null) {
menu.setAccelerateEnabled(enabled);
}
}
public boolean isNetworkEnergySufficient() {
return this.networkEnergySufficient;
}
/**
* 更新网络能量充足状态并通知菜单
* @param sufficient 是否能量充足

View File

@ -23,8 +23,8 @@ import java.util.Map;
* 实体加速器界面显示加速状态卡数量能耗和倍率信息
*/
public class EntitySpeedTickerScreen<C extends EntitySpeedTickerMenu> extends UpgradeableScreen<C> {
private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态
private final SettingToggleButton<YesNo> eap$entitySpeedTickerToggle; // 加速开关按钮
private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态
private final SettingToggleButton<YesNo> eap$entitySpeedTickerToggle; // 加速开关按钮
/**
* 构造函数初始化界面和控件

View File

@ -6,14 +6,8 @@ import appeng.core.definitions.AEItems;
import appeng.core.definitions.AEParts;
import appeng.core.localization.GuiText;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER_PART;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE_PART;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE_PART;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_IMPORT_BUS;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_EXPORT_BUS;
import static com.glodblock.github.extendedae.common.EPPItemAndBlock.*;
/**
*

View File

@ -0,0 +1,223 @@
package com.extendedae_plus.mixin.ae2.items;
import appeng.api.inventories.InternalInventory;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.core.localization.PlayerMessages;
import appeng.items.tools.MemoryCardItem;
import appeng.items.tools.NetworkToolItem;
import appeng.util.inv.PlayerInternalInventory;
import com.extendedae_plus.ae.parts.EntitySpeedTickerPart;
import com.extendedae_plus.init.ModItems;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.ArrayList;
@Mixin(value = MemoryCardItem.class, remap = false)
public class MemoryCardItemMixin {
/**
* 写入 Memory Card 时保留实体加速卡的完整 NBT 数据
*/
@Inject(method = "storeUpgrades", at = @At("HEAD"), cancellable = true)
private static void storeUpgradesCustom(IUpgradeableObject upgradeableObject, CompoundTag output, CallbackInfo ci) {
try {
CompoundTag desiredUpgradesTag = new CompoundTag();
ListTag entitySpeedCards = new ListTag();
InternalInventory upgrades = upgradeableObject.getUpgrades();
for (int i = 0; i < upgrades.size(); i++) {
ItemStack upgradeStack = upgrades.getStackInSlot(i);
if (upgradeStack.isEmpty()) continue;
ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(upgradeStack.getItem());
String key = itemId.toString();
if (upgradeStack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) {
CompoundTag stackTag = new CompoundTag();
stackTag.putInt("Slot", i);
upgradeStack.save(stackTag);
entitySpeedCards.add(stackTag);
} else {
desiredUpgradesTag.putInt(key, desiredUpgradesTag.getInt(key) + upgradeStack.getCount());
}
}
if (!entitySpeedCards.isEmpty()) {
desiredUpgradesTag.put("entity_speed_cards", entitySpeedCards);
}
output.put("upgrades", desiredUpgradesTag);
ci.cancel();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Memory Card 恢复升级时从玩家背包或网络工具提取实体加速卡
*/
@Inject(method = "restoreUpgrades", at = @At("HEAD"), cancellable = true)
private static void restoreUpgradesCustom(Player player, CompoundTag input, IUpgradeableObject upgradeableObject, CallbackInfoReturnable<Boolean> cir) {
try {
if (!input.contains("upgrades")) {
cir.setReturnValue(false);
return;
}
CompoundTag desiredUpgradesTag = input.getCompound("upgrades");
InternalInventory upgrades = upgradeableObject.getUpgrades();
// 收集背包和网络工具作为升级卡来源
var upgradeSources = new ArrayList<InternalInventory>();
upgradeSources.add(new PlayerInternalInventory(player.getInventory()));
var networkTool = NetworkToolItem.findNetworkToolInv(player);
if (networkTool != null) {
upgradeSources.add(networkTool.getInventory());
}
// 清空所有槽位中的 EntitySpeedCardItem
for (int i = 0; i < upgrades.size(); i++) {
ItemStack stack = upgrades.getStackInSlot(i);
if (!stack.isEmpty() && stack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) {
ItemStack removed = upgrades.extractItem(i, stack.getCount(), false);
for (var source : upgradeSources) {
if (!removed.isEmpty()) {
removed = source.addItems(removed);
}
}
if (!removed.isEmpty()) {
player.drop(removed, false);
}
}
}
// 恢复 EntitySpeedCardItem
if (desiredUpgradesTag.contains("entity_speed_cards", Tag.TAG_LIST)) {
ListTag entitySpeedCards = desiredUpgradesTag.getList("entity_speed_cards", Tag.TAG_COMPOUND);
for (int i = 0; i < entitySpeedCards.size(); i++) {
CompoundTag stackTag = entitySpeedCards.getCompound(i);
ItemStack desiredStack = ItemStack.of(stackTag);
int slot = stackTag.contains("Slot") ? stackTag.getInt("Slot") : i;
if (player.getAbilities().instabuild) {
// 创造模式直接生成
if (slot >= 0 && slot < upgrades.size()) {
upgrades.setItemDirect(slot, desiredStack);
} else {
upgrades.addItems(desiredStack);
}
} else {
// 非创造模式从背包或网络工具提取
int missingAmount = desiredStack.getCount();
ItemStack extracted = ItemStack.EMPTY;
for (var source : upgradeSources) {
ItemStack potential = new ItemStack(desiredStack.getItem(), missingAmount);
if (desiredStack.hasTag()) {
potential.setTag(desiredStack.getTag().copy());
}
ItemStack cards = source.removeItems(missingAmount, potential, null);
if (!cards.isEmpty()) {
ItemStack overflow = upgrades.addItems(cards);
if (!overflow.isEmpty()) {
player.getInventory().placeItemBackInInventory(overflow);
}
missingAmount -= cards.getCount();
extracted = cards;
}
if (missingAmount <= 0) break;
}
if (missingAmount > 0 && !player.level().isClientSide()) {
player.displayClientMessage(
PlayerMessages.MissingUpgrades.text(desiredStack.getItem().getDescription(), missingAmount),
true
);
} else if (!extracted.isEmpty()) {
if (slot >= 0 && slot < upgrades.size()) {
upgrades.setItemDirect(slot, extracted);
} else {
upgrades.addItems(extracted);
}
}
}
}
}
// 恢复其他升级卡 AE2 原逻辑
for (String key : desiredUpgradesTag.getAllKeys()) {
ResourceLocation id;
try {
id = new ResourceLocation(key);
} catch (Exception ex) {
continue;
}
var item = BuiltInRegistries.ITEM.getOptional(id).orElse(null);
if (item == null || item.equals(ModItems.ENTITY_SPEED_CARD.get())) continue;
int desiredCount = desiredUpgradesTag.getInt(key);
if (desiredCount > 0) {
if (player.getAbilities().instabuild) {
// 创造模式直接生成
ItemStack stack = new ItemStack(item, desiredCount);
upgrades.addItems(stack);
} else {
// 非创造模式从背包或网络工具提取
int missingAmount = desiredCount;
ItemStack potential = new ItemStack(item, missingAmount);
ItemStack overflow = upgrades.addItems(potential, true);
if (!overflow.isEmpty()) {
missingAmount -= overflow.getCount();
}
for (var source : upgradeSources) {
ItemStack cards = source.removeItems(missingAmount, potential, null);
if (!cards.isEmpty()) {
overflow = upgrades.addItems(cards);
if (!overflow.isEmpty()) {
player.getInventory().placeItemBackInInventory(overflow);
}
missingAmount -= cards.getCount();
}
if (missingAmount <= 0) break;
}
if (missingAmount > 0 && !player.level().isClientSide()) {
player.displayClientMessage(
PlayerMessages.MissingUpgrades.text(item.getDescription(), missingAmount),
true
);
}
}
}
}
// 标记保存并通知升级变化
if (upgradeableObject instanceof EntitySpeedTickerPart speedTickerPart) {
BlockEntity be = speedTickerPart.getBlockEntity();
if (be != null) {
be.setChanged();
}
speedTickerPart.upgradesChanged();
}
cir.setReturnValue(true);
} catch (Exception e) {
e.printStackTrace();
cir.setReturnValue(false);
}
}
}

View File

@ -1,4 +1,4 @@
package com.extendedae_plus.mixin.ae2;
package com.extendedae_plus.mixin.ae2.items;
import appeng.api.parts.IPartHost;
import appeng.api.parts.SelectedPart;

View File

@ -9,7 +9,6 @@
"accessor.ScreenAccessor",
"advancedae.client.gui.AdvPatternProviderScreenMixin",
"advancedae.client.gui.SmallAdvPatternProviderScreenMixin",
"ae2.QuartzCuttingKnifeItemMixin",
"ae2.accessor.AEBaseScreenAccessor",
"ae2.accessor.MEStorageScreenAccessor",
"ae2.accessor.PatternAccessTermScreenAccessor",
@ -21,6 +20,7 @@
"ae2.client.gui.PatternProviderScreenMixin",
"ae2.client.gui.SlotGridLayoutMixin",
"ae2.compat.PatternProviderScreenCompatMixin",
"ae2.items.QuartzCuttingKnifeItemMixin",
"ae2.menu.CraftConfirmMenuGoBackMixin",
"extendedae.accessor.GuiExPatternTerminalAccessor",
"extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor",
@ -62,6 +62,7 @@
"ae2.helpers.PatternProviderLogicAdvancedMixin",
"ae2.helpers.PatternProviderLogicDoublingMixin",
"ae2.helpers.patternprovider.PatternProviderLogicTickerMixin",
"ae2.items.MemoryCardItemMixin",
"ae2.menu.ContainerPatternEncodingTermMenuMixin",
"ae2.menu.MEStorageMenuMixin",
"ae2.menu.PatternEncodingTermMenuMixin",