血魔法仪式:匠魂之咒

基本信息

属性 值
注册入口 BMCompat.bloody(),postInit 且 Loader.isModLoaded("AWWayofTime") && Config.BM(SuperTic.java:54)
仪式 ID "SuperTiC_Modifier"(BMCompat.java:12)
仪式实现类 com.Zoko061602.SuperTic.compat.RitualTinkerer extends RitualEffect
显示名 Spell of the diligent Tinkerer / 匠魂之咒(BMCompat.java:16 + lang ritual.SuperTiC_Modifier.name)
LP 消耗 Config.BM_LP,默认 100000,范围 1 ~ 80000000(Config.java:80-86)
刷新消耗 getCostPerRefresh() 返回 0(RitualTinkerer.java:27-30)

注册代码

Rituals.registerRitual("SuperTiC_Modifier", 1, Config.BM_LP, new RitualTinkerer(), "Spell of the diligent Tinkerer");

(BMCompat.java:11-16)整体包在 try { ... } catch (Exception e) { e.printStackTrace(); } 里 (BMCompat.java:10、17-19)——注册失败只打堆栈,游戏继续。

效果:升级槽 +1(与附魔配方同目标)

成功时对 NBT 的操作与附魔配方完全一致(RitualTinkerer.java:127-131, 对照 InfusionModifierRecipe.java:31-37):

nbt.setBoolean("STicBM", true);
int mod = nbt.getCompoundTag("InfiTool").getInteger("Modifiers") + 1;
nbt.getCompoundTag("InfiTool").setInteger("Modifiers", mod);

标记是 STicBM(不是 STicTC),因此同一件工具可以先附魔再仪式,或反之—— 两条路径的"只能一次"标记彼此独立,各用各的。lang 文本 “every tool can only gain one extra slot this way”(zh_CN.lang 的 STiC 行) 描述的是附魔路径,血魔法路径 lang 里没有对应说明。

执行流程

performEffect 用 ritualStone.getVar1() 做两阶段状态机 (RitualTinkerer.java:33-142)。

阶段 1:var1 == 0 — 等待工具投入

在仪式石正上方 y+1 ~ y+2 的 1×1×1 空间内搜 EntityItem (AxisAlignedBB.getBoundingBox(x, y + 1.0D, z, x + 1, y + 2, z + 1), RitualTinkerer.java:41-43)。对每个掉落物:

情况 行为 源码
是 ToolCore 且 NBT 无 STicBM setVar1(1);在 y+1 处放一道闪电;setCooldown(399);item.setDead()(吞掉原物品);break RitualTinkerer.java:49-56
是 ToolCore 但 NBT 已有 STicBM 向 ±6 格立方内((x±6, y±6, z±6))所有 EntityPlayer 广播 “This tool seems to resist the energy”;setActive(false) 并 return RitualTinkerer.java:57-65
非 ToolCore(任何掉落物) 有 1/10 概率放一次 SpellHelper.sendIndexedParticleToAllAround(color 1,20 帧) RitualTinkerer.java:69-81

⚠️ stack.getTagCompound() 在 ToolCore 分支里直接赋给 nbt 后立刻调 nbt.getBoolean("STicBM")(RitualTinkerer.java:50-51)。若工具没有 NBT (getTagCompound() 返回 null),这里会 NPE。而阶段 2 同样依赖 nbt 非 null。 见 已知源码问题。

阶段 2:var1 != 0 — 倒计时与天劫

每次 performEffect 把 cooldown 减 1(RitualTinkerer.java:85)。期间:

  • 1/30 概率触发一次落雷,位置从 8 个固定偏移中随机选 1 (world.rand.nextInt(8),RitualTinkerer.java:87-121): (x+4,y,z)、(x-4,y,z)、(x+0,y,z+4)、(x-0,y,z-4)、 (x+2,y+2,z+2)、(x+2,y+2,z-2)、(x-2,y+2,z+2)、(x-2,y+2,z-2)
  • cooldown <= 0 时结算:写 NBT → 在 (x+0.5, y+1, z+0.5) 生成新 EntityItem → setVar1(0) → setActive(false)(RitualTinkerer.java:125-139)

从激活到出结果共 400 次 performEffect:激活时 setCooldown(399)(RitualTinkerer.java:54), 之后每次调用减 1,减到 <= 0 需 399 次,即激活后第 400 次调用结算。 源码没有把这 399 写成常量,也没说明该值与 Blood Magic 主体的 tick 频率关系。

⚠️ 结算处 if (spawnedItem != null) 的判空在 spawnedItem.setTagCompound(nbt) 之后 (RitualTinkerer.java:127、129),顺序无实际意义——若 stack 为 null 早在 RitualTinkerer.java:127 就炸了。

仪式组件布局

getRitualComponentList 返回 26 个 RitualComponent(RitualTinkerer.java:144-182), 构造参数为 (itemMeta, x, y, z),其中 x / z 用 ±1 表示方位、y 为层高:

组 组件数 布局 行号
内圈四角 4 (1,-1,1,2)、(1,-1,-1,3)、(-1,-1,1,3)、(-1,-1,-1,2) RitualTinkerer.java:146-149
外圈四角(低) 4 (±2,0,±2,0) RitualTinkerer.java:151-154
外圈四角(高) 4 (±2,1,±2,{2或3}) RitualTinkerer.java:156-159
北向尖刺 3 (3,-1,0,1)、(4,-1,0,5)、(4,±1,-1,4) RitualTinkerer.java:161-163
南向尖刺 3 (-3,-1,0,1)、(-4,-1,0,5)、(-4,±1,-1,4) RitualTinkerer.java:165-167
东向尖刺 4 (0,-1,3,1)、(0,-1,4,5)、(±1,-1,4,4) RitualTinkerer.java:169-172
西向尖刺 4 (0,-1,-3,1)、(0,-1,-4,5)、(±1,-1,-4,4) RitualTinkerer.java:174-177

四向尖刺各 3~4 个,东/西比北/南多一个,四组不对称。itemMeta 列(第一个参数) 在所有 26 个组件里只有 1、2、3、4 四个值,且与方向无关—— 即整个布局不使用 Blood Magic 原版的 0(空气)位置区分,完全靠 x/z 偏移表达形状。 源码里没有任何注释或常量名说明 2 / 3 / 4 对应什么材料。

已知源码问题

  • NPE:ToolCore 无 NBT 时崩(RitualTinkerer.java:50-51)。stack.getTagCompound() 返回 null 时直接 nbt.getBoolean("STicBM")。匠魂工具正常都带 InfiTool NBT, 但被其它方式损坏 NBT 的工具会命中。对照组:PotionEventHandler.java:39-41 对同一情况做了 tags == null 的显式判空,两个文件风格不一致。
  • performEffect 的粒子分支对非工具物品也生效:world.rand.nextInt(10) == 0 的 特效在 if (stack instanceof ToolCore) 块之外(RitualTinkerer.java:69), 所以往仪式石里丢任何东西(骨头、泥土)都会有 1/10 概率放特效。源码无注释说明是否有意。
  • 英文提示语硬编码:“This tool seems to resist the energy” (RitualTinkerer.java:63)用 ChatComponentText 直接构造,不走 ChatComponentTranslation, 因此没有 zh_CN 翻译。而仪式名本身有 lang(ritual.SuperTiC_Modifier.name)。 中文玩家会看到英文提示。
  • 阶段 2 不再检查工具是否还在:阶段 1 已 item.setDead(),阶段 2 只用缓存的 stack 字段(RitualTinkerer.java:127)。若中途该 RitualEffect 实例被复用 (Blood Magic 的仪式对象生命周期由其本体管理),stack / nbt 字段 (RitualTinkerer.java:23-25)会残留上一次的工具 NBT。
  • LP 与冷却硬编码的 399 无来源说明:RitualTinkerer.java:54 的 399 是魔法数, 与 Config.BM_LP 的可配置风格不一致。