Iron Chest 方块(IronChest:BlockIronChest)

基本信息

属性 值
注册名 IronChest:BlockIronChest(GameRegistry.registerBlock(ironChestBlock, ItemIronChest.class, "BlockIronChest"))
源码类 cpw.mods.ironchest.BlockIronChest(extends BlockContainer)
材质 Material.iron
硬度 3.0F(构造函数里唯一的数值设置:setHardness(3.0F))
爆炸抗性 无 setResistance() 调用;改由 getExplosionResistance() 覆写按 TileEntity 类型动态返回,见下
碰撞箱 setBlockBounds(0.0625F, 0F, 0.0625F, 0.9375F, 0.875F, 0.9375F)
创造栏 CreativeTabs.tabDecorations
渲染类型 getRenderType() = 22(方块模型,物品形态另走 ItemIronChest)
变体数 1 个注册方块 + 11 个 metadata 变体(不是 11 个方块)

[!IMPORTANT] 整个 mod 只注册了 1 个方块。11 种箱子是同一个 BlockIronChest 的 metadata 变体,metadata 值就是 IronChestType 枚举的 ordinal()。

metadata 如何选择箱子类型

调用链只有一层,createTileEntity 直接把 metadata 丢给枚举工厂:

// BlockIronChest.java:84
public TileEntity createTileEntity(World world, int metadata) {
    return IronChestType.makeEntity(metadata);
}
// IronChestType.java:146
public static TileEntityIronChest makeEntity(int metadata) {
    int chesttype = validateMeta(metadata);          // 越界 / size<=0 一律回落到 0
    if (chesttype == metadata) {
        return VALUES[chesttype].clazz.newInstance(); // 用枚举里登记的 TileEntity 类反射实例化
    }
    return null;
}

validateMeta(int i)(IronChestType.java:286):

if (i < VALUES.length && VALUES[i].size > 0) return i;
else return 0;   // 非法 metadata 一律当作 IRON

因此合法 metadata 恰好是 ordinal() ∈ [0, 10];第 12 个常量 WOOD(ordinal() == 11)的 size == 0,被判为非法,getIcon/getSubBlocks 都跳过它,isValidForCreativeMode() 也返回 false。WOOD 是哨兵常量,不存在对应箱子——它只是给"原版木箱子"这个升级来源一个占位身份(见 Wood to Iron Chest Upgrade)。

metadata 分配表

metadata ordinal() 常量 显示名 容量
0 IRON Iron Chest 54
1 GOLD Gold Chest 81
2 DIAMOND Diamond Chest 108
3 COPPER Copper Chest 45
4 STEEL Steel Chest 72
5 CRYSTAL Crystal Chest 108
6 OBSIDIAN Obsidian Chest 108
7 DIRTCHEST9000 Dirt Chest 9000 1
8 NETHERITE Netherite Chest 135
9 DARKSTEEL Dark Steel Chest 135
10 SILVER Silver Chest 72
11 WOOD (无,不注册) 0

数值属性(逐变体)

属性 值 来源
硬度 全部 11 个变体都是 3.0F 构造函数只调一次 setHardness(3.0F),没有任何按 metadata 分化的硬度
爆炸抗性 抗爆型(OBSIDIAN、NETHERITE)返回 10000.0F;其余沿用 Material.iron 的原版值 getExplosionResistance() 覆写
亮度 无(全仓库没有 setLightLevel) —
脚步/破坏音效 未覆写,走 Material.iron 默认 —

爆炸抗性的判定是按 TileEntity 而非按 metadata:

// BlockIronChest.java:235
public float getExplosionResistance(Entity e, World world, int x, int y, int z,
        double ex, double ey, double ez) {
    TileEntity te = world.getTileEntity(x, y, z);
    if (te instanceof TileEntityIronChest) {
        if (((TileEntityIronChest) te).getType().isExplosionResistant()) {
            return 10000f;
        }
    }
    return super.getExplosionResistance(e, world, x, y, z, ex, ey, ez);
}

isExplosionResistant() = resistance >= 1,resistance 是 IronChestType 构造函数的最后一个参数(不是 metadata,也不是爆炸抗性数值本身,只是个 0/1 标志)。全枚举只有 OBSIDIAN(resistance = 1)和 NETHERITE(resistance = 1)为 1,其余全为 0。ironchest:blocks/obsidian_*.tooltip 里那句红色 §4Blast Proof 只有 Obsidian 箱子带。

交互

触发 行为 源码
右键方块 打开箱子 GUI:player.openGui(IronChest.instance, te.getType().ordinal(), world, x, y, z),GUI ID 即 ordinal() BlockIronChest.java:108
右键(上方被实心方块压住) 直接 return true,不开 GUI(world.isSideSolid(x, y+1, z, DOWN)) BlockIronChest.java:116
右键(上方有坐下的猫) return true 但不开 GUI(受配置 ocelotsBlockChests 控制) BlockIronChest.java:124
客户端(world.isRemote) return true,GUI 由 ClientProxy.getClientGuiElement 侧构建 BlockIronChest.java:120
破坏 breakBlock 先 removeAdornments()(泥土箱会删掉说明书)再把全部内容物 dropContent(0, ...) 散落 BlockIronChest.java:171
创造模式物品栏 getSubBlocks 遍历 VALUES,跳过 !isValidForCreativeMode() 与 !isEnabled() 的类型 BlockIronChest.java:222
掉落 getDrops 恒为 1 个 new ItemStack(this, 1, metadata),再调 adornItemDrop(只有泥土箱会打 NBT 标记) BlockIronChest.java:99
红石比较器 hasComparatorInputOverride() = true + Container.calcRedstoneFromInventory(te),读物品栏填充率 BlockIronChest.java:248
旋转(Forge wrench) getValidRotations 只允许 UP/DOWN,rotateBlock 调 TileEntityIronChest.rotateAround(axis) BlockIronChest.java:272

摆放与朝向

onBlockPlacedBy(BlockIronChest.java:141)按玩家 yaw 取 facing = floor(yaw*4/360 + 0.5) & 3,再映射成箱子 metadata 风格的朝向值:

玩家朝向 facing 写入 teic.setFacing()
0 2
1 5
2 3
3 4

随后调 teic.wasPlaced(entityliving, itemStack)(泥土箱在此插入说明书),并 world.markBlockForUpdate。 朝向值本身存在 TileEntity 的 NBT 字段 facing 里(不是方块 metadata;方块 metadata 始终是箱子类型 ordinal())。

掉落物内容散落算法

dropContent(int newSize, IInventory chest, ...)(BlockIronChest.java:186)从 newSize 开始逐格取出:

  • 每格物品按 random.nextInt(21) + 10(10~30 个)随机拆分成多个 EntityItem 抛出,抛物线初速 0.05F 随机 + 0.2F 向上。
  • 物品的 NBT 会被 copy() 后写回掉落物实体。
  • 正常破坏时 newSize = 0,全部内容散落;箱子升级时由 TileEntityIronChest.applyUpgradeItem 传入 newSize = 新箱子容量,即超出新容量的部分散落在地上,且 Y 坐标 +1(避免卡在原箱子方块内)。

渲染

事项 值
方块图标 getIcon(int side, int meta) → IronChestType.VALUES[meta].getIcon(side);makeIcons 为每个类型注册 3 张 ironchest:<name>_{top,front,side},按 sideMapping = {0,0,2,1,2,2,2} 映射 6 个方块朝向
STEEL 特例 图标注册时 name().equalsIgnoreCase("steel") 被特判成 ironchest:silver_%s(钢箱子复用银箱子贴图)
TileEntity 渲染器 TileEntityIronChestRenderer,模型贴图取 textures/model/<modelTexture>(即 getModelTexture());ModelChest 盖子角度来自 prevLidAngle/lidAngle
物品栏渲染 IronChestRenderHelper extends TileEntityRendererChestHelper,为每个 ordinal() 预建一个空 TE,让物品形态用同一套渲染器画
透明内容渲染 仅 CRYSTAL(isTransparent())在 TRANSPARENT_RENDER_INSIDE 开启且距离 < TRANSPARENT_RENDER_DISTANCE 时,把 topStacks 里 8 个最多堆叠的物品悬浮旋转显示(关光照,RenderItem 动画关闭)。若 topStacks[1] == null 则放大到 0.85 并整体下移

猫与箱子

mod 通过 OcelotsSitOnChestsHandler(LivingUpdateEvent,仅在 OCELOTS_SITONCHESTS 开启时注册)把原版 EntityAIOcelotSit(priority == 6)替换为 IronChestAIOcelotSit(ocelot, 1.33D),并覆写 func_151486_a(isSittableBlock,由 META-INF/IronChest_at.cfg 映射)使任何 IronChest:BlockIronChest 元数据都可坐,且只在 getNumUsingPlayers() == 0(没人开着 GUI)时允许。

相关条目