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 变得可损坏,修复链会断。
相关条目
- Length Wire - 制作与修复的原料
- Signal Terminal - 第 1 步右键目标
- Signal Bus - 第 2 步右键目标
- Signal Bus 网络 -
registerTerminal的实现