[修正] 奥术配方研究 key 修复与奥术合成终端充能完善

- 配方: 1k/4k/16k/64k 存储组件、无线傀儡背包、源质发信器
  研究 key 改为实际注册节点 (ESSENTIASTORAGE / GOLEMWIFIBACKPACK / ESSENTIATERMINAL)
- 配方: 扩散/凝聚核心材料 quicksilver → quicksilver_drop
- 配方: 知识记录仪/蒸馏编码台/源质终端 显示器三变体合并为
  thaumicenergistics:monitors tag (9 份重复配方 → 3 份)
- 终端: 新增法杖自动充能 (内存卡链接 Vis Interface P2P 频率,
  IGridTickable 每 tick 抽取, 速率与原版充能中继一致 5 cV/tick)
- 装配器: 删除无调用方且会静默失败的 findMatchingArcaneRecipe;
  vis 折扣魔法值统一为 BASE_VIS_DISCOUNT 常量
- 清理: 删除 Feature 空壳层/ThEFeatureBase/AEPartGridBlock 等死代码
  (-362 行), 删除空 registerScanEvent, 硬编码节点数, lang 死键
- 编译: IDEA 全项目通过 (problems: [])
This commit is contained in:
1820982382
2026-08-09 05:58:22 +08:00
parent 2d93c9d256
commit 0e85bfc303
21 changed files with 139 additions and 389 deletions
@@ -1,6 +1,7 @@
package thaumicenergistics;
import appeng.api.features.GridLinkables;
import appeng.api.features.P2PTunnelAttunement;
import appeng.api.parts.PartModels;
import appeng.api.stacks.AEKeyType;
import appeng.api.stacks.AEKeyTypesInternal;
@@ -19,7 +20,6 @@ import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import thaumicenergistics.common.container.*;
import thaumicenergistics.common.features.FeatureRegistry;
import thaumicenergistics.common.features.RecipeRegistration;
import thaumicenergistics.common.integration.appeng.AEssentiaKeyType;
import thaumicenergistics.common.integration.appeng.ThEAppliedEnergistics;
@@ -107,12 +107,11 @@ public final class ThaumicEnergistics {
event.enqueueWork(() -> {
RecipeRegistration.registerAll();
ThEThaumcraft.init();
P2PTunnelAttunement.registerAttunementTag(ModItems.VIS_INTERFACE.get());
GridLinkables.register(ModItems.GOLEM_WIFI_BACKPACK.get(), ItemGolemWirelessBackpack.LINKABLE_HANDLER);
GridLinkables.register(ModItems.WIRELESS_ESSENTIA_TERMINAL.get(),
appeng.items.tools.powered.WirelessTerminalItem.LINKABLE_HANDLER);
});
FeatureRegistry.registerAll();
}
private void registerUpgrades(FMLCommonSetupEvent event) {
@@ -1,21 +0,0 @@
package thaumicenergistics.common.features;
/**
* 功能:奥术自动合成。
* 启用奥术装配器及相关合成样板。
* 自 1.7.10 FeatureAutocrafting_Arcane 移植(约 7k 行)。
*/
public class FeatureAutocraftingArcane extends ThEFeatureBase {
public FeatureAutocraftingArcane() { super("arcane_crafting_terminal"); }
@Override
protected void doRegister() {
// Arcane Assembler 方块经 ModBlocks 注册
// TODO: Register arcane crafting pattern encoder, crafting CPU integration
}
@Override
protected void doPostInit() {
// TODO: Register arcane crafting recipes, research entries
}
}
@@ -1,20 +0,0 @@
package thaumicenergistics.common.features;
/**
* 源质存储元件功能。
* 注册 cell 物品、元件工作台与存储组件物品。
* 自 1.7.10 FeatureCells 移植(约 12k 行)。
*/
public class FeatureCells extends ThEFeatureBase {
public FeatureCells() { super("cells"); }
@Override
protected void doRegister() {
// 物品经 init 包的 ModItems 注册——本功能校验其存在
}
@Override
protected void doPostInit() {
// TODO: Register cell recipes, research entries, cell workbench functionality
}
}
@@ -1,35 +0,0 @@
package thaumicenergistics.common.features;
import thaumicenergistics.ThaumicEnergistics;
import thaumicenergistics.init.ModItems;
/**
* 功能:源质 IO 总线。
* 启用源质输入/输出/存储总线Part部件
* Part注册已移至模组主类ThaumicEnergistics构造方法中,
* 通过AE2官方PartHelper.registerPart() API完成,
* 本Feature负责功能开关、配方注册、研究数据等跨模组集成内容。
*/
public class FeatureEssentiaIOBuses extends ThEFeatureBase {
public FeatureEssentiaIOBuses() { super("io_buses"); }
@Override
protected void doRegister() {
// ===== Part核心注册已在ThaumicEnergistics构造方法中完成 =====
// 原因:AE2 PartHelper.registerPart()需要DeferredItem注册完成后立即调用,
// Feature.register()的调用时机同样满足,但为了集中管理Part注册入口,统一在主类中处理。
// ===== 以下登记本Feature涉及的物品,供跨模组引用追踪 =====
ThaumicEnergistics.LOG.info(" -> EssentiaImportBus PartItem: {} (ready)",
ModItems.ESSENTIA_IMPORT_BUS.getId());
ThaumicEnergistics.LOG.info(" -> EssentiaExportBus PartItem: {} (pending)",
ModItems.ESSENTIA_EXPORT_BUS.getId());
ThaumicEnergistics.LOG.info(" -> EssentiaStorageBus PartItem: {} (pending)",
ModItems.ESSENTIA_STORAGE_BUS.getId());
}
@Override
protected void doPostInit() {
// 后置初始化:注册神秘时代研究条目、AE2合成配方等
}
}
@@ -1,15 +0,0 @@
package thaumicenergistics.common.features;
/**
* 源质监控功能。
* 启用源质水平检测器、存储监控器与转换监控器部件。
* 自 1.7.10 FeatureEssentiaMonitoring 移植(约 6k 行)。
*/
public class FeatureEssentiaMonitoring extends ThEFeatureBase {
public FeatureEssentiaMonitoring() { super("monitoring"); }
@Override
protected void doRegister() {
// TODO: Register monitoring-related parts when AE2 part API is integrated
}
}
@@ -1,7 +0,0 @@
package thaumicenergistics.common.features;
/** 功能:源质供应器。从 AE 网络核心抽取源质。 */
public class FeatureEssentiaProvider extends ThEFeatureBase {
public FeatureEssentiaProvider() { super("essentia_provider"); }
@Override protected void doRegister() {}
}
@@ -1,21 +0,0 @@
package thaumicenergistics.common.features;
/**
* 功能:源质谐振仓。
* 启用基于源质的能量生产。
* 自 1.7.10 FeatureEssentiaVibrationChamber 移植(约 4k 行)。
*/
public class FeatureEssentiaVibrationChamber extends ThEFeatureBase {
public FeatureEssentiaVibrationChamber() { super("vibration_chamber"); }
@Override
protected void doRegister() {
// EVC 方块经 ModBlocks 注册
// TODO: Register fuel values for each essentia type
}
@Override
protected void doPostInit() {
// TODO: Register EVC recipe, research entry
}
}
@@ -1,11 +0,0 @@
package thaumicenergistics.common.features;
/** 功能:无线傀儡背包。启用傀儡访问 AE 网络。 */
public class FeatureGolemBackpack extends ThEFeatureBase {
public FeatureGolemBackpack() { super("golem_backpack"); }
@Override
protected void doRegister() {
// GridLinkables 注册在 commonSetup 阶段完成(需要物品已绑定)
}
}
@@ -1,16 +0,0 @@
package thaumicenergistics.common.features;
/**
* 功能:注魔供应器。
* 为 TC 注魔自动供应源质。
* 自 1.7.10 FeatureInfusionProvider 移植(约 4k 行)。
*/
public class FeatureInfusionProvider extends ThEFeatureBase {
public FeatureInfusionProvider() { super("infusion_provider"); }
@Override
protected void doRegister() {
// Infusion Provider 方块经 ModBlocks 注册
// TODO: Register infusion detection and essentia delivery logic
}
}
@@ -1,17 +0,0 @@
package thaumicenergistics.common.features;
import thaumicenergistics.ThaumicEnergistics;
public final class FeatureRegistry {
private FeatureRegistry() {}
private static final ThEFeatureBase[] FEATURES = {
new FeatureCells(), new FeatureEssentiaIOBuses(), new FeatureEssentiaMonitoring(),
new FeatureEssentiaVibrationChamber(), new FeatureInfusionProvider(),
new FeatureAutocraftingArcane(), new FeatureVisRelayInterface(),
new FeatureGolemBackpack(), new FeatureEssentiaProvider()
};
public static void registerAll() {
for (var f : FEATURES) { ThaumicEnergistics.LOG.info("Feature [{}] {}", f.getName(), f.isEnabled() ? "enabled":"disabled"); f.register(); }
}
public static void postInitAll() { for (var f : FEATURES) f.postInit(); }
}
@@ -1,17 +0,0 @@
package thaumicenergistics.common.features;
import appeng.api.features.P2PTunnelAttunement;
import thaumicenergistics.init.ModItems;
public class FeatureVisRelayInterface extends ThEFeatureBase {
public FeatureVisRelayInterface() { super("vis_interface"); }
@Override
protected void doRegister() {
P2PTunnelAttunement.registerAttunementTag(ModItems.VIS_INTERFACE.get());
}
@Override
protected void doPostInit() {
}
}
@@ -54,6 +54,8 @@ public final class RecipeRegistration {
public static CrucibleRecipe CERTUS_DUPE_RECIPE;
private static final TagKey<Item> WISP_ESSENCES = tag("thaumicenergistics", "wisp_essences");
/** AE2 三种显示器(普通/暗/半暗)任一可用。 */
private static final TagKey<Item> MONITORS = tag("thaumicenergistics", "monitors");
@SuppressWarnings("SameParameterValue")
private static TagKey<Item> tag(String ns, String path) {
@@ -80,7 +82,7 @@ public final class RecipeRegistration {
}
private static void registerShapedArcane() {
STORAGE_COMPONENT_1K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE1K", stack(ModItems.STORAGE_COMPONENT_1K),
STORAGE_COMPONENT_1K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE", stack(ModItems.STORAGE_COMPONENT_1K),
new AspectList().add(Aspect.FIRE, 3).add(Aspect.ORDER, 1),
"EQ ",
"QPQ",
@@ -89,7 +91,7 @@ public final class RecipeRegistration {
'Q', ae("certus_quartz_crystal"),
'P', ae("logic_processor"));
STORAGE_COMPONENT_4K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE4K", stack(ModItems.STORAGE_COMPONENT_4K),
STORAGE_COMPONENT_4K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE", stack(ModItems.STORAGE_COMPONENT_4K),
new AspectList().add(Aspect.FIRE, 3).add(Aspect.ORDER, 2),
"EPE",
"1G1",
@@ -99,7 +101,7 @@ public final class RecipeRegistration {
'1', stack(ModItems.STORAGE_COMPONENT_1K),
'G', ae("quartz_glass"));
STORAGE_COMPONENT_16K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE16K", stack(ModItems.STORAGE_COMPONENT_16K),
STORAGE_COMPONENT_16K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE", stack(ModItems.STORAGE_COMPONENT_16K),
new AspectList().add(Aspect.FIRE, 3).add(Aspect.ORDER, 4),
"SPE",
"4G4",
@@ -110,7 +112,7 @@ public final class RecipeRegistration {
'G', ae("quartz_glass"),
'E', WISP_ESSENCES);
STORAGE_COMPONENT_64K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE64K", stack(ModItems.STORAGE_COMPONENT_64K),
STORAGE_COMPONENT_64K = ThaumcraftApi.addArcaneCraftingRecipe("ESSENTIASTORAGE", stack(ModItems.STORAGE_COMPONENT_64K),
new AspectList().add(Aspect.FIRE, 3).add(Aspect.ORDER, 8),
"SPS",
"6G6",
@@ -140,7 +142,7 @@ public final class RecipeRegistration {
'F', tc("fire_shard"),
'W', ae("certus_quartz_wrench"));
GOLEM_WIFI_BACKPACK_RECIPE = ThaumcraftApi.addArcaneCraftingRecipe("GOLEMWIFI", stack(ModItems.GOLEM_WIFI_BACKPACK),
GOLEM_WIFI_BACKPACK_RECIPE = ThaumcraftApi.addArcaneCraftingRecipe("GOLEMWIFIBACKPACK", stack(ModItems.GOLEM_WIFI_BACKPACK),
new AspectList().add(Aspect.AIR, 6).add(Aspect.FIRE, 4).add(Aspect.ORDER, 3),
"tIt",
"nRn",
@@ -189,58 +191,17 @@ public final class RecipeRegistration {
" T ",
"IPI",
'I', new ItemStack(Items.IRON_INGOT, 4),
'L', ae("monitor"),
'L', MONITORS,
'T', tc("thaumonomicon"),
'P', ae("logic_processor"));
ThaumcraftApi.addArcaneCraftingRecipe("KNOWLEDGEINSCRIBER", stack(ModBlocks.KNOWLEDGE_INSCRIBER),
new AspectList().add(Aspect.WATER, 5).add(Aspect.EARTH, 5).add(Aspect.FIRE, 5).add(Aspect.AIR, 5).add(Aspect.ORDER, 5).add(Aspect.ENTROPY, 5),
"ILI",
" T ",
"IPI",
'I', new ItemStack(Items.IRON_INGOT, 4),
'L', ae("dark_monitor"),
'T', tc("thaumonomicon"),
'P', ae("logic_processor"));
ThaumcraftApi.addArcaneCraftingRecipe("KNOWLEDGEINSCRIBER", stack(ModBlocks.KNOWLEDGE_INSCRIBER),
new AspectList().add(Aspect.WATER, 5).add(Aspect.EARTH, 5).add(Aspect.FIRE, 5).add(Aspect.AIR, 5).add(Aspect.ORDER, 5).add(Aspect.ENTROPY, 5),
"ILI",
" T ",
"IPI",
'I', new ItemStack(Items.IRON_INGOT, 4),
'L', ae("semi_dark_monitor"),
'T', tc("thaumonomicon"),
'P', ae("logic_processor"));
DISTILLATION_ENCODER_BLOCK = ThaumcraftApi.addArcaneCraftingRecipe("DISTILLATIONENCODER", stack(ModBlocks.DISTILLATION_ENCODER),
new AspectList().add(Aspect.ORDER, 5).add(Aspect.ENTROPY, 5).add(Aspect.FIRE, 3),
"ILI",
" G ",
"IPI",
'I', new ItemStack(Items.IRON_INGOT, 4),
'L', ae("monitor"),
'G', tc("thaumometer"),
'P', ae("engineering_processor"));
ThaumcraftApi.addArcaneCraftingRecipe("DISTILLATIONENCODER", stack(ModBlocks.DISTILLATION_ENCODER),
new AspectList().add(Aspect.ORDER, 5).add(Aspect.ENTROPY, 5).add(Aspect.FIRE, 3),
"ILI",
" G ",
"IPI",
'I', new ItemStack(Items.IRON_INGOT, 4),
'L', ae("dark_monitor"),
'G', tc("thaumometer"),
'P', ae("engineering_processor"));
ThaumcraftApi.addArcaneCraftingRecipe("DISTILLATIONENCODER", stack(ModBlocks.DISTILLATION_ENCODER),
new AspectList().add(Aspect.ORDER, 5).add(Aspect.ENTROPY, 5).add(Aspect.FIRE, 3),
"ILI",
" G ",
"IPI",
'I', new ItemStack(Items.IRON_INGOT, 4),
'L', ae("semi_dark_monitor"),
'L', MONITORS,
'G', tc("thaumometer"),
'P', ae("engineering_processor"));
}
@@ -250,23 +211,23 @@ public final class RecipeRegistration {
// 1. 扩散核心
DIFFUSION_CORE = ThaumcraftApi.addShapelessArcaneCraftingRecipe("DIGISENTIA", stack(ModItems.DIFFUSION_CORE),
new AspectList().add(Aspect.WATER, 2).add(Aspect.ENTROPY, 2),
tc("quicksilver"),
tc("quicksilver"),
tc("quicksilver"),
tc("quicksilver_drop"),
tc("quicksilver_drop"),
tc("quicksilver_drop"),
tc("entropy_shard"),
ae("annihilation_core"));
// 2. 凝聚核心
COALESCENCE_CORE = ThaumcraftApi.addShapelessArcaneCraftingRecipe("DIGISENTIA", stack(ModItems.COALESCENCE_CORE),
new AspectList().add(Aspect.WATER, 2).add(Aspect.ORDER, 2),
tc("quicksilver"),
tc("quicksilver"),
tc("quicksilver"),
tc("quicksilver_drop"),
tc("quicksilver_drop"),
tc("quicksilver_drop"),
tc("order_shard"),
ae("formation_core"));
// 3. 原质等级发射器
ESSENTIA_LEVEL_EMITTER = ThaumcraftApi.addShapelessArcaneCraftingRecipe("ESSENTIAMONITORING", new ItemStack(ModItems.ESSENTIA_LEVEL_EMITTER.get(), 1),
ESSENTIA_LEVEL_EMITTER = ThaumcraftApi.addShapelessArcaneCraftingRecipe("ESSENTIATERMINAL", new ItemStack(ModItems.ESSENTIA_LEVEL_EMITTER.get(), 1),
new AspectList().add(Aspect.FIRE, 4),
ae("calculation_processor"),
new ItemStack(Items.REDSTONE_TORCH),
@@ -275,26 +236,7 @@ public final class RecipeRegistration {
// 4. 原质终端
ESSENTIA_TERMINAL = ThaumcraftApi.addShapelessArcaneCraftingRecipe("ESSENTIATERMINAL", stack(ModItems.ESSENTIA_TERMINAL_ITEM),
new AspectList().add(Aspect.WATER, 5).add(Aspect.ORDER, 2).add(Aspect.FIRE, 1),
//'ae2:semi_dark_monitor' 'ae2:monitor' 'ae2:dark_monitor'三者任一
ae("monitor"),
stack(ModItems.DIFFUSION_CORE),
stack(ModItems.COALESCENCE_CORE),
ae("logic_processor"),
tc("vis_filter"));
ThaumcraftApi.addShapelessArcaneCraftingRecipe("ESSENTIATERMINAL", stack(ModItems.ESSENTIA_TERMINAL_ITEM),
new AspectList().add(Aspect.WATER, 5).add(Aspect.ORDER, 2).add(Aspect.FIRE, 1),
//'ae2:semi_dark_monitor' 'ae2:monitor' 'ae2:dark_monitor'三者任一
ae("dark_monitor"),
stack(ModItems.DIFFUSION_CORE),
stack(ModItems.COALESCENCE_CORE),
ae("logic_processor"),
tc("vis_filter"));
ThaumcraftApi.addShapelessArcaneCraftingRecipe("ESSENTIATERMINAL", stack(ModItems.ESSENTIA_TERMINAL_ITEM),
new AspectList().add(Aspect.WATER, 5).add(Aspect.ORDER, 2).add(Aspect.FIRE, 1),
//'ae2:semi_dark_monitor' 'ae2:monitor' 'ae2:dark_monitor'三者任一
ae("semi_dark_monitor"),
MONITORS,
stack(ModItems.DIFFUSION_CORE),
stack(ModItems.COALESCENCE_CORE),
ae("logic_processor"),
@@ -1,43 +0,0 @@
package thaumicenergistics.common.features;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import thaumicenergistics.ThaumicEnergistics;
import thaumicenergistics.config.ThEConfig;
import java.util.function.Supplier;
/**
* 所有功能模块的基类。每个功能可独立启停。
* 自 1.7.10 ThEFeatureBase 移植。
*/
public abstract class ThEFeatureBase {
private final String name;
protected boolean enabled;
protected ThEFeatureBase(String name) {
this.name = name;
this.enabled = ThEConfig.isFeatureEnabled(name);
}
public final String getName() { return name; }
public final boolean isEnabled() { return enabled; }
/** 模组构造期间调用,用于注册物品与方块。 */
public void register() {
if (!enabled) return;
ThaumicEnergistics.LOG.info("Feature [{}] loading...", name);
doRegister();
}
/** 所有功能注册完成后调用,用于跨功能设置。 */
public void postInit() {
if (!enabled) return;
doPostInit();
}
/** 覆写以注册本功能特有的方块/物品。 */
protected abstract void doRegister();
/** 覆写以进行注册后设置(配方、研究等)。 */
protected void doPostInit() {}
}
@@ -1,22 +0,0 @@
package thaumicenergistics.common.grid;
/**
* 表示连接到 AE2 网格的部件。
* 对应 1.7.10 AEPartGridBlock——为 1.21.1 简化。
* TODO: 通过 AE2 19.x API 完整连接 IGridNode。
*/
public class AEPartGridBlock {
private final Object part; // 占位 PartBase 或 IPartItem
public AEPartGridBlock(Object part) { this.part = part; }
public double getIdlePowerUsage() {
// TODO: Delegate to part
return 1.0;
}
public Object getGridNode() {
// TODO: AE2 IGridNode
return null;
}
}
@@ -4,7 +4,6 @@ import java.util.HashMap;
import java.util.Map;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import thaumcraft.api.ThaumcraftApi;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.AspectList;
import thaumcraft.api.events.ResearchCompletedEvent;
@@ -60,8 +59,6 @@ public final class ThEThaumcraft {
ResourceLocation.fromNamespaceAndPath(MODID, "textures/research/tab_icon.png"),
ResourceLocation.fromNamespaceAndPath(MODID, "textures/research/research_background.png"));
registerScanEvent();
// ⚠ 必须先在当前 tab 注册伪前置节点(替代 TC 原版研究),
// 否则 setParents 跨分类引用时线画不出来。
registerPseudoParents();
@@ -69,45 +66,25 @@ public final class ThEThaumcraft {
// 注册事件监听:当 TC 原版研究解锁时,自动 complete 对应伪前置节点
registerPseudoSync();
// 严格按照 1.7.10 FeatureRegistry 构造函数中的注册顺序
// 1. FeatureThaumicEnergistics → BASEENERGISTICS (0,0)
// 注册顺序沿袭 1.7.10 移植(决定研究 tab 内节点坐标布局),勿随意调整。
registerBaseEnergistics();
// 2. FeatureAutocrafting_Arcane → ARCANE_ASSEMBLER + KNOWLEDGE_INSCRIBER
registerArcaneAssembler();
registerKnowledgeInscriber();
// 3. FeatureCells → ESSENTIASTORAGE (0,2) 向左延伸 1k/4k/16k/64k
registerEssentiaStorage();
// 4. FeatureACT → ARCANECRAFTINGTERMINAL (2,-1)
registerArcaneCraftingTerminal();
// 5. FeatureVisRelayInterface → VISINTERFACE (4,-1)
registerVisInterface();
// 6. FeatureEssentiaIOBuses → ESSENTIABUSES (-2,-2)
registerEssentiaBuses();
// 7. FeatureInfusionProvider → INFUSIONPROVIDER (-5,-2)
registerInfusionProvider();
// 8. FeatureEssentiaProvider → ESSENTIAPROVIDER (-2,-4)
registerEssentiaProvider();
// 9. FeatureEssentiaMonitoring → ESSENTIATERMINAL (-1,-4)
registerEssentiaTerminal();
// 10. FeatureConversionCores → DIGISENTIA (0,-2)
registerDigisentia();
// 12. FeatureWrenchFocus → FOCUS_AEWRENCH (-3,-7)
registerFocusAEWrench();
// 13. FeatureQuartzDupe → CERTUS_DUPE (-5,-5)
registerCertusDupe();
// 14. FeatureEssentiaVibrationChamber → VIBRATIONCHAMBER (1,-4)
registerEssentiaVibrationChamber();
// 15. FeatureAutocrafting_Essentia → DISTILLATIONENCODER (-4,-4)
registerDistillationEncoder();
// 16. FeatureGolemBackpack → GOLEMWIFIBACKPACK (0,-6)
registerGolemWifiBackpack();
LOG.info("ThaumicEnergistics research registered ({} nodes).", 13);
}
private static void registerScanEvent() {
ThaumcraftApi.registerScanEventhandler((scanner, level, player) ->
java.util.Optional.empty());
LOG.info("ThaumicEnergistics research registered.");
}
// ==================== 伪前置节点(跨分类引用 TC 原版研究) ====================
@@ -5,24 +5,38 @@ import java.util.List;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.Vec3;
import appeng.api.implementations.items.IMemoryCard;
import appeng.api.implementations.items.MemoryCardMessages;
import appeng.api.ids.AEComponents;
import appeng.api.inventories.InternalInventory;
import appeng.api.networking.IGrid;
import appeng.api.networking.IGridNode;
import appeng.api.networking.ticking.IGridTickable;
import appeng.api.networking.ticking.TickRateModulation;
import appeng.api.networking.ticking.TickingRequest;
import appeng.api.parts.IPartItem;
import appeng.api.parts.IPartModel;
import appeng.core.AppEng;
import appeng.items.parts.PartModels;
import appeng.me.service.P2PService;
import appeng.menu.locator.MenuLocators;
import appeng.menu.MenuOpener;
import appeng.parts.PartModel;
import appeng.parts.p2p.P2PTunnelPart;
import appeng.parts.reporting.AbstractTerminalPart;
import appeng.util.inv.AppEngInternalInventory;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.common.items.TCFunctionalItems;
import thaumicenergistics.ThaumicEnergistics;
import thaumicenergistics.common.integration.tc.TCReflection;
import thaumicenergistics.init.ModMenuTypes;
public class ArcaneCraftingTerminalPart extends AbstractTerminalPart {
public class ArcaneCraftingTerminalPart extends AbstractTerminalPart implements IGridTickable {
public static final ResourceLocation INV_CRAFTING = AppEng.makeId("arcane_crafting_terminal_crafting");
public static final ResourceLocation INV_WAND = ThaumicEnergistics.id("arcane_crafting_terminal_wand");
@@ -42,12 +56,22 @@ public class ArcaneCraftingTerminalPart extends AbstractTerminalPart {
public static final int WAND_SLOT_INDEX = 0;
/** 充能间隔:每 tick 抽取一次。与 TC 能量节点池子每 tick 重置的频率、原版充能中继一致;无需放大。 */
private static final int VIS_REPLENISH_INTERVAL = 1;
/** 每 tick 每要素最大充能 cV,与原版奥术工作台充能中继(MagicWorkbenchCharger)一致(5 cV/tick = 1 vis/s)。 */
private static final int MAX_CVIS_PER_TICK = 5;
private static final String NBT_KEY_VIS_SOURCE_FREQ = "visSourceFreq";
/** 内存卡设定的 vis 源 P2P 频率;0 = 未设置(不自动充能)。 */
private short visSourceFreq = 0;
private final AppEngInternalInventory craftingGrid = new AppEngInternalInventory(this, 9);
private final AppEngInternalInventory wandInv = new AppEngInternalInventory(this, 1);
public ArcaneCraftingTerminalPart(IPartItem<?> partItem) {
super(partItem);
this.getMainNode().setIdlePowerUsage(0.5);
this.getMainNode().addService(IGridTickable.class, this);
}
@Override
@@ -63,6 +87,88 @@ public class ArcaneCraftingTerminalPart extends AbstractTerminalPart {
return true;
}
/**
* 内存卡右键终端:读取 Vis Interface 的 P2P 频率,设为法杖充能的 vis 源。
* 复刻奥术装配器 {@code TileArcaneAssembler.onMemoryCardActivate} 的行为:
* 卡上带 P2P 频率(从 vis_interface shift+右键复制来的)→ 设为 vis 源;
* 空白卡 → 清空;其它 → 交给 AE2 标准内存卡逻辑。
*/
@Override
public boolean onUseItemOn(ItemStack heldItem, Player player, InteractionHand hand, Vec3 pos) {
if (heldItem.getItem() instanceof IMemoryCard memoryCard) {
Short freq = heldItem.get(AEComponents.EXPORTED_P2P_FREQUENCY);
if (freq != null) {
this.visSourceFreq = freq;
getHost().markForSave();
memoryCard.notifyUser(player, MemoryCardMessages.SETTINGS_LOADED);
return true;
} else if (heldItem.get(AEComponents.EXPORTED_SETTINGS_SOURCE) == null) {
// 空白卡 → 清空 vis 源
if (this.visSourceFreq != 0) {
this.visSourceFreq = 0;
getHost().markForSave();
}
memoryCard.notifyUser(player, MemoryCardMessages.SETTINGS_CLEARED);
return true;
}
}
return super.onUseItemOn(heldItem, player, hand, pos);
}
// ===== 法杖自动充能(内存卡链接 Vis Interface 作为 vis 源) =====
@Override
public TickingRequest getTickingRequest(IGridNode node) {
return new TickingRequest(VIS_REPLENISH_INTERVAL, VIS_REPLENISH_INTERVAL, false);
}
@Override
public TickRateModulation tickingRequest(IGridNode node, int ticksSinceLastCall) {
if (visSourceFreq == 0) {
return TickRateModulation.SAME;
}
ItemStack wandStack = wandInv.getStackInSlot(0);
if (wandStack.isEmpty() || !(wandStack.getItem() instanceof TCFunctionalItems.WandCastingItem wand)) {
return TickRateModulation.SAME;
}
IGrid grid = node.getGrid();
if (grid == null) {
return TickRateModulation.SAME;
}
P2PService p2p = P2PService.get(grid);
if (p2p == null) {
return TickRateModulation.SAME;
}
P2PTunnelPart<?> input = p2p.getInput(visSourceFreq);
if (!(input instanceof VisInterfacePart visSource)) {
return TickRateModulation.SAME;
}
boolean changed = false;
int maxVis = wand.getMaxVis(wandStack);
for (Aspect primal : Aspect.getPrimalAspects()) {
int needed = maxVis - wand.getVis(wandStack, primal);
if (needed <= 0) {
continue;
}
ResourceLocation id = TCReflection.getAspectId(primal);
if (id == null) {
continue;
}
// 每 tick 申请真实量(与池子每 tick 重置对齐),充入真实量,vis 守恒
int got = visSource.consumeVis(id, Math.min(MAX_CVIS_PER_TICK, needed));
if (got > 0) {
wand.addRealVis(wandStack, primal, got, true);
changed = true;
}
}
if (changed) {
// 法杖 vis 存储在 ItemStack NBT,手动标记保存
getHost().markForSave();
}
return TickRateModulation.SAME;
}
@Override
public MenuType<?> getMenuType(Player player) {
return ModMenuTypes.ARCANE_CRAFTING_TERMINAL.get();
@@ -95,6 +201,7 @@ public class ArcaneCraftingTerminalPart extends AbstractTerminalPart {
super.readFromNBT(data, registries);
this.craftingGrid.readFromNBT(data, "craftingGrid", registries);
this.wandInv.readFromNBT(data, "wandInv", registries);
this.visSourceFreq = data.contains(NBT_KEY_VIS_SOURCE_FREQ) ? data.getShort(NBT_KEY_VIS_SOURCE_FREQ) : 0;
}
@Override
@@ -102,6 +209,9 @@ public class ArcaneCraftingTerminalPart extends AbstractTerminalPart {
super.writeToNBT(data, registries);
this.craftingGrid.writeToNBT(data, "craftingGrid", registries);
this.wandInv.writeToNBT(data, "wandInv", registries);
if (visSourceFreq != 0) {
data.putShort(NBT_KEY_VIS_SOURCE_FREQ, visSourceFreq);
}
}
@Override
@@ -16,7 +16,6 @@ import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.Container;
import net.minecraft.world.SimpleContainer;
import appeng.api.config.Actionable;
@@ -47,7 +46,6 @@ import appeng.api.util.AECableType;
import thaumcraft.api.IVisDiscountGear;
import thaumcraft.api.IWarpingGear;
import thaumcraft.api.ThaumcraftApi;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.AspectList;
import thaumcraft.api.crafting.IArcaneRecipe;
@@ -68,6 +66,8 @@ public class TileArcaneAssembler extends ThETileBase
Aspect.EARTH, Aspect.ORDER, Aspect.ENTROPY
};
/** 基础 vis 折扣(0.8 = 80% 成本)。calculateVisDiscounts 从该值起再减护甲折扣;无护甲时即此值。 */
private static final float BASE_VIS_DISCOUNT = 0.8F;
private static final int CVIS_MULTIPLIER = 10;
private static final int MAX_STORED_CVIS = 187 * CVIS_MULTIPLIER;
private static final int BASE_TICKS_PER_CRAFT = 20;
@@ -152,7 +152,7 @@ public class TileArcaneAssembler extends ThETileBase
actionSource = IActionSource.ofMachine(this);
for (Aspect primal : PRIMALS) {
visDiscount.put(primal, 0.8F);
visDiscount.put(primal, BASE_VIS_DISCOUNT);
}
}
@@ -392,7 +392,7 @@ public class TileArcaneAssembler extends ThETileBase
private int getRequiredCvisForAspect(Aspect aspect) {
if (currentRecipeAspects == null) return 0;
int baseCost = currentRecipeAspects.getAmount(aspect) * CVIS_MULTIPLIER;
float discount = visDiscount.getOrDefault(aspect, 1.0F);
float discount = visDiscount.getOrDefault(aspect, BASE_VIS_DISCOUNT);
return Math.max(0, Math.round(baseCost * discount));
}
@@ -402,7 +402,7 @@ public class TileArcaneAssembler extends ThETileBase
private void calculateVisDiscounts() {
for (Aspect primal : PRIMALS) {
float discount = 0.8F;
float discount = BASE_VIS_DISCOUNT;
for (int i = 0; i < 4; i++) {
ItemStack armor = internalInventory.getItem(ARMOR_SLOT_START + i);
@@ -536,21 +536,6 @@ public class TileArcaneAssembler extends ThETileBase
return !isCrafting;
}
@Nullable
public IArcaneRecipe findMatchingArcaneRecipe(Container craftingGrid) {
if (level == null) return null;
for (Object recipeObj : ThaumcraftApi.getCraftingRecipes()) {
if (recipeObj instanceof IArcaneRecipe recipe) {
try {
if (recipe.matches(craftingGrid, level, null)) {
return recipe;
}
} catch (Exception ignored) {}
}
}
return null;
}
public boolean isCrafting() { return isCrafting; }
public int getSpeedUpgrades() { return speedUpgrades; }
public int getCraftTickCounter() { return craftTickCounter; }
@@ -561,7 +546,7 @@ public class TileArcaneAssembler extends ThETileBase
}
public AspectList getStoredVis() { return storedVis; }
public SimpleContainer getInternalInventory() { return internalInventory; }
public float getVisDiscountForAspect(Aspect aspect) { return visDiscount.getOrDefault(aspect, 1.0F); }
public float getVisDiscountForAspect(Aspect aspect) { return visDiscount.getOrDefault(aspect, BASE_VIS_DISCOUNT); }
public float getWarpPowerMultiplier() { return warpPowerMultiplier; }
public void setSpeedUpgrades(int count) {
@@ -1,10 +1,7 @@
package thaumicenergistics.config;
import java.util.Set;
public final class ThEConfig {
private ThEConfig() {}
public static final double EVC_POWER_PER_TICK = 5.0;
public static final int CELL_1K = 1024, CELL_4K = 4096, CELL_16K = 16384, CELL_64K = 65536;
public static final int CELL_MAX_TYPES = 12;
private static final Set<String> ENABLED = Set.of("cells","io_buses","monitoring","arcane_crafting_terminal","essentia_provider","infusion_provider","vibration_chamber","vis_interface","golem_backpack");
public static boolean isFeatureEnabled(String n) { return ENABLED.contains(n); }
}