储罐共用机制(BlockExtendedTank / TileIronTank)

本页说明 10 个储罐方块共享的全部行为。所有差异只来自 TankType 枚举的 5 个数值字段(容量、材料、配方、抗爆、硬度),本 mod 没有为任何等级覆写交互逻辑。

三个类

类 文件 角色
BlockExtendedTank block/BlockExtendedTank.java 10 个方块的同一个类,10 个独立实例
TileIronTank tile/TileIronTank.java 10 个等级共用同一个 TileEntity 类,靠 NBT 里的 type 区分
ItemBlockExtendedTank item/ItemBlockExtendedTank.java 10 个方块的方块物品,只有覆写了 tooltip

ModBlocks(init/ModBlocks.java:11-20)创建 10 个 new BlockExtendedTank(TankType.X) 实例,init()(:23-32)逐一 GameRegistry.registerBlock(..., ItemBlockExtendedTank.class, ironTank.type.name)。所以方块注册名 = TankType.name,物品名与方块同名(irontank:ironTank 同时是方块和方块物品的注册名)。

继承自 BuildCraft

public class BlockExtendedTank extends buildcraft.factory.BlockTank { ... }   // BlockExtendedTank.java:23
public class TileIronTank extends buildcraft.factory.TileTank { ... }       // TileIronTank.java:10
  • 硬依赖声明在 Reference.DEPENDENCIES = required-after:BuildCraft|Factory@[7.0.0,)(reference/Reference.java:11)。
  • 本 mod 没有自绘 GUI,也没有自己实现任何流体填充/抽取逻辑。TileTank 的 tank 字段、右键开关 GUI、漏斗/管道填充、活塞推出行为全部继承自 BuildCraft Transport 的 TileTank。
  • 本 mod 只做两件事:把 tank 容量改掉、把 type 存进 NBT。

容量计算

TileIronTank.setCapacityFromType()(:36-39):

int capacity = FluidContainerRegistry.BUCKET_VOLUME * type.capacity;
this.tank.setCapacity(capacity);
  • FluidContainerRegistry.BUCKET_VOLUME = 1000 mB(Forge 1.7.10 常量)。
  • 所以 TankType.capacity 字段的单位是桶,tank 实际容量是 mB。IRON 的 32 → 32000 mB。
  • 10 个储罐的 tooltip 文案(en_US.lang)写的是 Stores up to 32 Buckets,与 TankType.capacity 数值完全一致,可交叉验证单位换算无误。

NBT 存储(TileIronTank:24-34)

方向 键 值
写 type type.ordinal()(枚举下标,整数)
读 type TankType.values()[data.getInteger("type")]

⚠️ 顺序依赖:readFromNBT 用 NBT 里的整数直接当 TankType.values() 的下标。TankType 当前下标顺序为 COPPER(0) SILVER(1) IRON(2) GOLD(3) DIAMOND(4) OBSIDIAN(5) GLASS(6) EMERALD(7) STAINLESSSTEEL(8) TITANIUM(9) TUNGSTENSTEEL(10) —— 与枚举声明顺序完全一致(注意 TankType.java:19-35 就是这个顺序)。在枚举中间插入新常量会让所有旧存档的储罐等级错位并可能抛 ArrayIndexOutOfBoundsException。新增等级只能追加到末尾。

读档后 readFromNBT 会再调一次 setCapacityFromType()(:28),所以容量永远由 type 决定,与 NBT 里的数值无关。构造器也调一次(:21),无参构造器默认 TankType.IRON(:14-16)。

硬度与抗爆(BlockExtendedTank:32-39)

this.setResistance(type.resistance);   // TankType 的第 5 个构造参数
this.setHardness(type.hardness);       // TankType 的第 6 个构造参数

TankType 的 Javadoc(:58-66)确认 resistance = blast resistance(爆炸抗性)、hardness = hardness(硬度),构造函数顺序也是 (capacity, name, materials, recipes, resistance, hardness)(:68-69)。

⚠️ 注意各等级硬度普遍高于抗爆,与原版金属块(铁 5.0/6.0)的惯例相反;黑曜石储罐是唯一抗爆远高于硬度的等级(6000000 / 50)。

方块外观与竖向堆叠(BlockExtendedTank:45-58)

public IIcon getIconAbsolute(IBlockAccess access, int i, int j, int k, int side, int metadata) {
    if (side >= 2 && access.getBlock(i, j - 1, k) instanceof BlockTank) {
        return textureStackedSide;
    } else {
        return super.getIconAbsolute(side, metadata);
    }
}
  • textureStackedSide 注册自 irontank:<name>/side_stacked(:48),即每个等级一张专门的"堆叠侧板"贴图。
  • side >= 2 指方块 4 个水平面(面序号 0=下 1=上 2~5=四侧),不包括顶面和底面。
  • 判定条件是正下方那个方块是不是任意 BlockTank —— 包括 BuildCraft 原版储罐和其他等级的储罐,不限同等级。所以竖向叠放的储罐会自动接上侧板贴图,形成连续的罐柱外观。
  • 本类没有覆写 onBlockActivated / onNeighborChanged 等交互方法,GUI 与流体行为全在 TileTank 里。

未本地化名(BlockExtendedTank:140-150)

return String.format("tile.%s%s", Reference.MODID.toLowerCase() + ":", getUnwrappedUnlocalizedName(...));

即 tile.irontank:ironTank。zh_CN.lang 提供 10 条 tile.irontank:*Tank.name(铁储罐 / 金储罐 / 钻石储罐 / 铝储罐 / 黑曜石储罐 / 铜储罐 / 钢储罐 / 不锈钢储罐 / 钛储罐 / 钨钢储罐)。

方块物品与 tooltip(ItemBlockExtendedTank:20-29)

ItemBlockExtendedTank 只覆写了一个方法 addInformation:取 tile.<name>.tooltip 本地化文本,按字面量 \n 分割成多行加进 tooltip。

  • tooltip 键不带 irontank: 命名空间前缀(是 tile.ironTank.tooltip,不是 tile.irontank.ironTank.tooltip)。
  • en_US.lang 有全部 10 条 tooltip,文案形如 Stores up to 32 Buckets of one Liquid + \n§4Not portable, use a dolly(2 行);黑曜石储罐是唯一 3 行的,额外带 (Blast Proof)。
  • ⚠️ zh_CN.lang 完全没有 tooltip 条目,只有 10 条方块名、13 条物品名和 1 条 itemGroup.irontank。所以中文环境下按住 Shift 看不到容量和"需搬运器"的提示。
  • 方块物品的其余行为全部继承 ItemBlock(正常放置、正常掉落),本类没有覆写放置或破坏逻辑。

合成配方机制(BlockExtendedTank:60-138)

配方来自 TankType.recipes 里的一串 9 个字符,被切成 3 行 3 列:

String[] recipeSplit = { recipe.substring(0,3), recipe.substring(3,6), recipe.substring(6,9) };

10 个配方的结构完全统一:

[ N  N  N ]   N = 该等级的 screw<材料>
[ x  q  x ]   x = paneGlass,  q = BuildCraftFactory.tankBlock
[ r  P  v ]   P = 该等级的双层板,  r/v = 工具
  • 核心是 BuildCraft 的储罐方块(q = BuildCraftFactory.tankBlock):本 mod 是在 BC 储罐外套壳改容量,不是从零实现储罐。
  • r = craftingToolHardHammer(硬锤)、v = craftingToolScrewdriver(螺丝刀),两者都必须消耗,套装成本很高。
  • 每个等级只有一条配方,TankType.recipes 是 List 但实际都只放 1 条。

⚠️ 'r' 在 addRecipe() 里被定义了两次::86-87 是 'r', "plateSteel",:132-133 是 'r', "craftingToolHardHammer"。ShapedOreRecipe 构造器按顺序把 (字符, 物品) 对写进映射表,后写入者覆盖先写入者,所以实际生效的是硬锤(此为推断,依赖 Forge ShapedOreRecipe 的实现,该实现不在本仓库内)。本页及各等级页的配方图按"生效值 = 硬锤"绘制。

⚠️ 'm' 槽(矿石字典材料)在 10 条配方里一次都没出现:addRecipe() 计算了 targetMaterial = MaterialHelper.translateOreName(material) 并把 'm' 映射给它,但 10 条配方串全部不含 m。所以 TankType.materials 字段对方块配方实际不起作用,等级差异完全靠配方串里硬编码的字母表达。materials 列表对升级器配方同样不起作用(见 储罐升级器共用机制)。

MaterialHelper.translateOreName(utility/MaterialHelper.java:7-12)只特判字符串 "obsidian" → Blocks.obsidian,而 TankType 里用的是 "plateDenseObsidian",因此这个特例在本 mod 中从未命中。

已知问题 / 设计缺陷

  1. TileEntity 注册 ID 字符串与实际包名不符:IronTank.java:50-53 用 @Mod.EventHandler 注册 GameRegistry.registerTileEntity(TileIronTank.class, Reference.TILE_IRON_TANK),而 Reference.TILE_IRON_TANK = "com.indemnity83.irontank.block.TileIronTank"(Reference.java:10)—— block 包,但类实际在 tile 包(com.indemnity83.irontank.tile.TileIronTank)。字符串 ID 与类真实全名不匹配(运行时后果未验证,本仓库无 Forge 源码)。

  2. onRemap 是空实现:IronTank.java:55-58 的 FMLMissingMappingsEvent 处理器只打一行日志 "Missing Mapping Event Fired!",没有任何重映射逻辑。该 GTNH fork 若改过方块/物品名,旧存档的 irontank:* 数据不会被迁移。

  3. @Mod.EventHandler 标在 load(FMLInitializationEvent) 上:方法名叫 load 但参数是初始化事件,与标准命名的 init 并列(IronTank.java:40-53),两个事件都是 FMLInitializationEvent,行为等价但命名有误导性。

  4. TankType.GLASS 是死枚举值:capacity=0、name=""、配方为空串,ModBlocks 没有为它注册方块,zh_CN.lang 也没有名字。它只作为"把 BuildCraft 原版储罐当作玻璃罐来升级"的中间标记使用。

相关条目