Spool of Wire

基本信息

属性 值
注册名 TMechworks:spoolwire
类 SpoolOfWire(继承 Item)
堆叠上限 1(this.maxStackSize = 1)
最大耐久 256(setMaxDamage(256))
subtype setHasSubtypes(true),仅 damage == 0 一种(getSubItems 只产出 meta 0)
贴图 tmechworks:logic/spoolwire
容器物品 getContainerItem 返回 meta = 256 的满耐久新线轴(items/SpoolOfWire.java:38-40)
发光效果 hasEffect 在 NBT 含 spoolWireData 时为 true(附魔光效)
NBT 结构 spoolWireData → targetDim / targetX / targetY / targetZ(4 个 int)
作用 在 Signal Terminal 与 Signal Bus 之间建立连接

核心机制:两段式右键

onItemUse(items/SpoolOfWire.java:76-135)实现两步操作:

第 1 步 —— 在 Signal Terminal 上右键:清空旧的 spoolWireData,写入当前坐标

if (te instanceof SignalTerminalLogic) {
    if (data.hasKey("spoolWireData")) data.removeTag("spoolWireData");
    spoolData = new NBTTagCompound();
    spoolData.setInteger("targetDim", world.provider.dimensionId);
    spoolData.setInteger("targetX", x);
    spoolData.setInteger("targetY", y);
    spoolData.setInteger("targetZ", z);
    data.setTag("spoolWireData", spoolData);
    return true;
}

第 2 步 —— 在 Signal Bus 上右键:按曼哈顿距离扣耐久并注册终端

if (te instanceof SignalBusLogic) {
    if (data.hasKey("spoolWireData")) {
        spoolData = data.getCompoundTag("spoolWireData");
        int calc = Math.abs(targetX - x) + Math.abs(targetY - y) + Math.abs(targetZ - z);
        if ((itemstack.getMaxDamage() - itemstack.getItemDamage()) < calc) return false;   // 耐久不足
        if (targetDim == world.provider.dimensionId && calc < 16) {                          // 同维度且距离 < 16
            boolean registered = ((SignalBusLogic) te).registerTerminal(world, targetX, targetY, targetZ, true);
            data.removeTag("spoolWireData");
            if (registered) itemstack.damageItem(calc, player);                              // 成功才扣耐久
            return true;
        }
    }
    return false;
}

距离成本 = 曼哈顿距离(|Δx| + |Δy| + |Δz|),所以上/下方向比水平方向便宜 1/格。满耐久 256 意味着单根线最多支持 15 格(受 calc < 16 硬上限约束,实际上永远用不满 256)。

数值

数值 值
注册名 TMechworks:spoolwire
堆叠上限 1
最大耐久 256
subtype 仅 meta 0
距离成本 曼哈顿距离 `
距离硬上限 calc < 16(即最大 15 格)
跨维度支持 记录 targetDim,但注册要求 targetDim == 当前维度 → 当前版本不支持真正跨维度
容器物品 meta 256(满耐久)新线轴
NBT 键 spoolWireData(含 targetDim/targetX/targetY/targetZ)
发光 有 NBT 时 hasEffect = true
tooltip tooltip.spoolofwire(meta 0 时)+ tooltip.spoolofwire.connecting(含目标 x/y/z/dim)
配方产出 meta 256 - 8(即 8/256 已用)

配方

common/MechRecipes.java:138-156,2 条:

制作(:138-147):

w w w
w r w
w w w
符号 材料 数量
w Length Wire 8
r stoneRod 1

产出 spoolwire meta 256 - 8(已消耗 8 点耐久)。

修复(:149-151):自定义无序配方 SpoolRepairRecipe(common/SpoolRepairRecipe.java)—— 1 个未损坏的 Length Wire 换 1 点耐久:

// matches: 网格内只能有 1 个 spool + N 个 damage==0 的 wire
// getCraftingResult:
newSpool.setItemDamage(newSpool.getItemDamage() - countWire);   // 减去 wire 数量 = 修复

因此满耐久 256 的线轴需要 256 个 Length Wire 完全修复(= 85 个铝黄铜锭),耐久系统成本合理。RecipeSorter.register("TMechworks:spoolrepair", SpoolRepairRecipe.class, Category.SHAPELESS, "after:forge:shapelessore")(:152-156)把它注册为无序配方。

交互

触发 行为
右键 Signal Terminal 记录目标坐标到 NBT,物品开始发光
右键 Signal Bus(同维度、距离 < 16、耐久足够) 扣曼哈顿距离的耐久、注册终端、清空 NBT
右键 Signal Bus(条件不满足) return false,NBT 保留、耐久不扣
右键其他方块 return false
客户端 onItemUse 首行 if (world.isRemote) return false; —— 纯服务端操作
悬停 显示「正在连接 x, y, z, dim」

源码缺陷

  • targetDim 被写入但从不用于跨维度(:104 与 :108):写入 spoolData.setInteger("targetDim", ...),但注册条件是 targetDim == world.provider.dimensionId —— 必须在同一维度。targetDim 字段在当前实现中除了 tooltip 显示外毫无作用,是未完成的跨维度功能的残留。
  • 距离硬上限 16 使 256 耐久严重浪费(:108):calc < 16 意味着单根线最多消耗 15 点耐久,剩余 241 点永远无法通过这条路径花掉。玩家修线(1 个 Length Wire → 满耐久)比拉长距离更划算,设计上不平衡。
  • return true 无条件吞掉失败(:110):注册失败(registered == false)时既不扣耐久也不清 NBT(NBT 只在进入 if 分支时才 removeTag),但仍返回 true 阻止原版方块放置。玩家看不到任何反馈。
  • getContainerItem 返回未注册的物品(:38-40):返回 new ItemStack(this, 1, this.getMaxDamage()) = meta 256。ItemStack 的 damage 是 int 但渲染/客户端按 0–32767 处理;meta 256 的 Spool 不在 getSubItems 产出的 subtype 范围内(只产出 meta 0),因此该容器物品在部分 UI 下显示异常。
  • data.removeTag 后仍 return true 但未 markDirty:NBT 变更后物品栈未标记 dirty,理论上存在同步延迟(1.7.10 背包 NBT 每 tick 同步,实际影响小)。
  • addInformation 无 @SideOnly 但用了 StatCollector:SpoolOfWire.addInformation 有 @SideOnly(Side.CLIENT)(:43),正确;但 getUnlocalizedName 被覆写为 return "item." + unlocalizedName(:72)使 MechContent 中的 .setUnlocalizedName("spoolwire")(:56)完全失效,属冗余调用。
  • getRecipeSize() 返回 9(common/SpoolRepairRecipe.java:71-74,带 // TODO Auto-generated method stub 注释):IRecipe.getRecipeSize() 对有序配方表示网格尺寸,对无序配方应返回 1。返回 9 会让 CraftingManager 在 NEI/合成界面把它当作 3×3 有序配方展示,而实际逻辑是 1+1 无序。RecipeSorter 已声明 Category.SHAPELESS 但 getRecipeSize 未同步,属真实缺陷。
  • matches 的提前 return 在循环体内(:39-41):if (countWire > 0 && foundSpool) return true; 位于 for 内部、if (tmpStack instanceof ItemStack) 块之后。只要先遇到 spool 再遇到 wire 就立即返回,不检查后续格子。由于同一次 matches 已经在上方对每个格子做了「否则 return false」,实际结果正确,但逻辑依赖遍历顺序且极易在改动时破坏。
  • tmpStack instanceof ItemStack 恒为真或假(:27):非 null 的 ItemStack 必然 instanceof ItemStack,null 则为 false。这是无意义的类型检查。
  • spool 配方的 meta 256 被忽略(:28):注册时传入 new ItemStack(spoolWire, 1, 256),但 matches 只比 tmpStack.getItem() == spool.getItem(),不检查 damage → 任意耐久的线轴都能修复(这是期望行为,但 spool 字段的 damage 部分纯属摆设)。
  • SpoolRepairRecipe 要求 wire 的 damage 必须为 0(:33):tmpStack.getItemDamage() == wire.getItemDamage(),而注册的是 new ItemStack(lengthWire, 1)(damage 0)。Length Wire 本身不可损坏,故该条件恒成立;若未来 Length Wire 变得可损坏,修复链会断。

相关条目