Wand Crafting Handler(魔杖合成)

基本信息

属性 值
类型 NEI Recipe / Usage Handler
源码类 com.gtnewhorizons.aspectrecipeindex.nei.arcaneworkbench.WandRecipeHandler
基类 ShapedArcaneRecipeHandler
Overlay ID thaumcraft.wands
界面名 aspectrecipeindex.wand_crafting.title = “Wand Crafting”
IMC 关联物品 Thaumcraft:WandCasting(handler 高度 144);催化剂 Thaumcraft:blockTable:15
双分支 GTNH_WAND_RECIPES = Loader.isModLoaded("gtnhtcwands")

功能:展示魔杖与法杖的合成配方。这是本模组唯一不依赖 Thaumcraft 配方注册表而是自己生成配方的 handler。

常量

常量 值 说明
SCEPTRE "SCEPTRE" 法杖研究键
ROD_WOOD "ROD_wood" 木杖柄研究键——lang 覆写为 tc.research_name.ROD_wood = “Basic Thaumaturgy”
CAP_IRON "CAP_iron" 铁杖帽研究键——lang 覆写为 tc.research_name.CAP_iron = “Basic Thaumaturgy”
GTNH_WAND_RECIPES Loader.isModLoaded("gtnhtcwands") static final,类加载时求值一次
VALID_RESEARCH WandRecipeHandler::validResearch 谓词:研究键是否合法
VISIBLE_RESEARCH WandRecipeHandler::show 谓词:研究键是否合法且已对玩家可见

ROD_wood 与 CAP_iron 被 validResearch 特判为恒定合法(否则 Thaumcraft 没有这两个 ResearchItem),同时用 lang 覆写给它们补上可读名称"基础神秘学"。

配方生成的两条路径

路径 A:无 GTNH-TC-Wands

forEachRodCap(BiConsumer, Predicate) 遍历 WandRod.rods.values() × WandCap.caps.values(),双层用 researchCheck 过滤,然后:

  1. generateRecipes(rod, cap, researchCheck) — 先建普通魔杖配方 addRecipe(wand, rod, cap, false);若 researchCheck.test(SCEPTRE) 通过,再 generateScepterRecipe(wand.copy(), rod, cap)。
  2. generateScepterRecipe 调 makeScepter(wand) 后 addRecipe(..., true)。

makeScepter(ItemStack):写 NBT 标签 sceptre(值为 1 的 NBTTagByte),并把 metadata 设为 原值 * 3 / 2。

两种配对的材料布局(3×3,null 表示空格):

配方 布局
魔杖 第 1 行:空、空、杖帽;第 2 行:杖柄、空、杖帽;第 3 行:全部空
法杖 第 1 行:空、杖帽、itemResource metadata 15;第 2 行:杖柄、杖帽、杖帽;第 3 行:全部空

ConfigItems.itemResource 的 metadata 15 即 Thaumcraft 的"奥术核心"类资源。

路径 B:有 GTNH-TC-Wands

改用 GTNH-TC-Wands 的 API:外层遍历 TCWandAPI.getWandWrappers(),内层遍历 TCWandAPI.getCaps(),每对取 wand.getRecipe(cap) 直接拿它生成的 ShapedArcaneRecipe。杖柄与杖帽两侧都要 validResearch / show 通过。

用途查询因此拆成 5 个独立方法(见下)。

vis 成本计算

getWandVisCost(ItemStack) 是 static 公共方法:

  1. 非 ItemWandCasting → 返回空 AspectList。
  2. cost = rod.getCraftCost() * cap.getCraftCost()。
  3. 若是法杖 → cost = cost * 3 / 2(源码注释 *= 1.5)。
  4. 若杖柄研究为 ROD_wood 且杖帽研究为 CAP_iron 且不是法杖 → 直接返回空(源码注释 “Stick + iron cap wand (not scepter) costs no vis”)。
  5. 否则对 Aspect.getPrimalAspects() 里每一个原始元素都 add(aspect, cost)。

第 5 步是刻意为之:奥法工作台消耗的是"所有原始元素各多少点"的总和,而不是某几个具体元素。

createWand(rod, cap) 造出的魔杖物品,metadata 被设为 getWandVisCost(stack).getAmount(Aspect.AIR)——源码注释说明这是"折扣前的合成成本",即 metadata 直接编码成本值。

可见性判定

shouldShowWandRecipe(ItemStack):

showLockedRecipes
  || (杖柄研究已完成 && 杖帽研究已完成 && (SCEPTRE 已完成 || 该魔杖不是法杖))

三个研究键都要分别判定,因此魔杖配方可能显示出来但研究控件列出多个未完成项。

show(String research) = validResearch(research) && Util.shouldShowRecipe(research)。

用途查询

loadUsageRecipes(ItemStack) 在路径 B 下转给 loadGTNHUsageRecipes(component),依次跑 5 个方法:

方法 匹配条件
usagesForGTNHPrimalCharms itemResource metadata 15 且 show(SCEPTRE) → 遍历所有 SceptreWrapper × 全部 caps
usagesForGTNHRods TCWandAPI.getWrapperForRod(component, false) 找到杖柄 → 遍历全部 caps 生成魔杖配方;法杖版杖柄另生成一批
usagesForGTNHCaps TCWandAPI.getWrapperForCap(component) 找到杖帽 → 遍历全部 wand wrappers
usagesForGTNHScrewsAndConductors 遍历 GTTier.values(),用 OreDictionary 查 screw + 材料名,或 OreDictionary.itemMatches(component, tier.getConductor(), true) 匹配导体;只处理第一个命中的 tier 后 return
usagesForGTNHVis 输入是 ItemAspect 且该元素 isPrimal() → wand × cap 全交叉(视作 vis 供给)

路径 A 下 loadUsageRecipes 的 3 条分支:

  1. itemResource metadata 15 且 show(SCEPTRE) → 全部法杖配方。
  2. 输入是 ItemAspect 且该元素 isPrimal() → 全部魔杖 + 法杖配方。
  3. 其他 → 用 OreDictionary.itemMatches 分别试杖柄与杖帽,命中任一即生成全交叉。

这解释了 README 里"搜索 Gold Caps 的用途会显示它与每种杖柄的组合"。

加载路径(产物方向)

loadCraftingRecipes(ItemStack):只处理 instanceof ItemWandCasting。取出 wand.getRod(result) / wand.getCap(result) / wand.isSceptre(result),两侧研究都 validResearch 才继续;可见性用 shouldShowWandRecipe。路径 B 下用 wrapper 取配方,路径 A 下用 ArcaneWandCachedRecipe。

前置研究

缓存类 追加的研究键
ArcaneWandCachedRecipe 法杖时先加 SCEPTRE,再加杖帽研究、杖柄研究
GTNHWandCachedRecipe wandWrapper instanceof SceptreWrapper 时加 SCEPTRE,再加杖帽研究、wandWrapper.getResearchName()

addResearch 内部对空名与查不到 ResearchItem 的情况会跳过(addResearch 判空串,addResearch → new ResearchInfo(...) 允许 null researchItem 并显示 “N/A”)。

相关条目