储罐升级器共用机制(ItemTankChanger)

本页说明 13 个"储罐升级"物品共享的全部行为。每个物品是 ItemTankChanger 的一个独立实例,差异只来自 TankChangerType 枚举的 4 个字段(源等级、目标等级、内部名、配方)。

三个类

类 文件 角色
ItemGeneric item/ItemGeneric.java 抽象基类;只做未本地化名 item.irontank:<name>、创造标签、图标注册,并声明抽象方法 addRecipe()
ItemTankChanger item/ItemTankChanger.java 13 个升级器物品的同一个类,覆写 addRecipe() 与 onItemUseFirst()
TankChangerType reference/TankChangerType.java 枚举:源等级 / 目标等级 / 内部名 / 配方

ModItems(init/ModItems.java:11-26)创建 13 个 new ItemTankChanger(TankChangerType.X),注意声明类型是 ItemGeneric;init()(:29-41)用 TankChangerType.X.name 注册,即物品注册名 = 枚举的 name 字段(不是 name())。

构造器(ItemTankChanger:23-30)只设两件事:未本地化名 type.name、最大堆叠 1。没有耐久、没有附魔相关覆写、没有创造标签覆写(ItemGeneric:18 已设为 MainTab)。

升级是怎么发生的(ItemTankChanger:112-144)

触发点是 onItemUseFirst(右键方块、在方块放置之前触发):

TileEntity worldTile = world.getTileEntity(X, Y, Z);
TileTank curTankTile;
if (worldTile instanceof TileIronTank) {                       // 本 mod 的储罐
    curTankTile = (TileTank) worldTile;
    if (!type.canUpgrade(((TileIronTank) curTankTile).type)) return false;   // 等级不匹配 → 无事发生
} else if (worldTile instanceof TileTank) {                     // BuildCraft 原版储罐
    curTankTile = (TileTank) worldTile;
    if (!type.canUpgrade(TankType.GLASS)) return false;          // 只有 2 个玻璃升级器能处理
} else {
    return false;                                                // 不是储罐 → 无事发生
}

TileIronTank newIronTankTile = new TileIronTank(type.target);
newIronTankTile.tank.setFluid(curTankTile.tank.getFluid());      // 搬走流体

world.setBlock(X, Y, Z, Block.getBlockFromName(Reference.MODID + ":" + type.target.name));
world.setTileEntity(X, Y, Z, newIronTankTile);
stack.stackSize = 0;                                             // 消耗 1 个
return true;

判定规则(canUpgrade,TankChangerType:65-67)是严格相等,不是"等级更低":

public boolean canUpgrade(TankType from) { return from == this.source; }

所以每个升级器只认一个源等级,对其他等级右键完全无效、也不消耗。

行为要点

问题 答案 依据
需要先倒空储罐吗? 不需要。代码没有检查储罐是否为空,直接 setFluid(curTankTile.tank.getFluid()) 搬运 ItemTankChanger:136
会丢流体吗? 不会。13 条升级路径的源容量全部 ≤ 目标容量(最大的一档 DIAMONDOBSIDIAN 是 64→64 相等),没有降容路径 TankChangerType:25-37 + TankType 容量字段
客户端会发生什么? world.isRemote 时直接 return false,客户端完全不预测;只有服务端真正替换方块与 TileEntity,再由同步包把结果发给客户端 ItemTankChanger:115-117
物品会消失吗? 会,stack.stackSize = 0 消耗 1 个,且不返还 ItemTankChanger:141
失败时消耗吗? 不会,等级不匹配或目标不是储罐时都 return false,物品与储罐都不变 ItemTankChanger:123-132
目标的旧数据会保留吗? 不会。world.setBlock + setTileEntity 是整体替换,旧 TileEntity 的其他 NBT 一律丢弃(TileIronTank 除 type 外无自有字段,所以实际无损失) ItemTankChanger:138-139
能降级吗? 不能。没有任何一条 TankChangerType 的目标容量小于源容量 TankChangerType:25-37
能对着空气用吗? 无意义。onItemUseFirst 只在点到方块时触发,点空气/方块时都是 return false ItemTankChanger:113
能升级别的 mod 的储罐吗? 不能。非 TileIronTank 且非 BuildCraft TileTank 的 TileEntity 一律 return false ItemTankChanger:131-132

Block.getBlockFromName 的隐含约束

目标方块用字符串查找:Block.getBlockFromName("irontank:" + type.target.name)(:138)。13 个 target 全部是 ModBlocks 已注册的 10 个方块之一(GLASS 从不作为 target),所以查找必然成功;一旦某个 target 拼写错误或方块未注册,getBlockFromName 返回 null 并在 setBlock 时崩溃(该后果属 Forge API 行为推断)。

合成配方机制(ItemTankChanger:33-110)

配方是 TankChangerType 构造器 varargs 里的 3 个字符串(不是 9 字符单串),直接作为 ShapedOreRecipe 的行数组。

addRecipe() 会双重循环 type.source.materials × type.target.materials,把 'x' 映射到源材料、'y' 映射到目标材料(:42-45)。但:

  • 13 条配方串里一次都没有出现 x 或 y(只用数字、等级字母和 z),
  • 每个 TankType.materials 列表也只有 1 个元素,

所以这个双重循环实际只跑 1 次,materials 字段对配方不起作用。等级差异完全靠配方串里硬编码的字母表达。

13 条配方的结构完全统一:

[ A  A  A ]   A = 目标等级的双层板, 两侧是目标等级的 screw
[ z  M  z ]   M = 源等级的材料板,  z = paneGlass
[ r  x  v ]   r = craftingToolHardHammer,  v = craftingToolScrewdriver
  • 中间行 = 源等级的材料板,顶行 = 目标等级,底行 = 硬锤 + 螺丝刀(ItemTankChanger:105-106 中 r/v 没有被重复定义,不存在 共用机制 里方块配方 'r' 的歧义)。
  • 2 个玻璃升级器的中间行是 'j' = plateGlass,用来标记源是 BuildCraft 原版储罐。
  • ⚠️ 命名不一致:TankType.GLASS.materials 写的是 "blockGlass"(玻璃方块),而 GLASSIRON / GLASSCOPPER 配方实际用的是 plateGlass(玻璃板)。两处不一致(但由于 materials 是死字段,不影响合成结果)。

ItemTankChanger 的字符表与 BlockExtendedTank 的不同,容易看混:

字母 ItemTankChanger(升级器) BlockExtendedTank(储罐)
q plateCopper BuildCraftFactory.tankBlock
p plateSteel (未定义)
f plateAluminium (未定义)
l plateStainlessSteel (未定义)
k plateTitanium (未定义)
d plateDiamond plateDiamond
r craftingToolHardHammer 定义两次(见 共用机制)
x / y 源 / 目标材料(配方串里未使用) paneGlass
z paneGlass (未定义)

注册流程

ModRecipies.init()(init/ModRecipies.java:5-32)逐个手写调用 13 个 ModItems.*.addRecipe() 与 10 个 ModBlocks.*.addRecipe(),没有循环也没有自动扫描。新增枚举值必须手动在此加一行,否则方块存在但无合成配方。

相关条目

  • 13 个升级器物品:见分类页物品列表
  • 储罐共用机制 - 10 个储罐方块的 BlockExtendedTank / TileIronTank / ItemBlockExtendedTank 行为