铁箱子运输矿车(共用机制基类)

基本信息

属性 值
类 ganymedes01.ironchestminecarts.minecarts.EntityMinecartIronChestAbstract
修饰 abstract,@SuppressWarnings("all")
父类 net.minecraft.entity.item.EntityMinecartChest
实现接口 mods.railcraft.api.carts.IItemTransfer(@Optional.Interface(iface=..., modid="Railcraft"))
模组 ironchestminecarts
直接子类 8 个,每个只覆写 type()

本类不注册任何实体,是 8 个矿车实体共享的全部逻辑所在。等级/容量等差异全部由子类的 type() 提供,基类自身行为对 8 个变体完全一致。

实体注册(反射扫描)

基类的 static 初始化块用 Guava ClassPath 扫描自身包名下的 types 子包,把每个 EntityMinecart*Chest 类自动注册:

ClassPath.from(...).getTopLevelClasses("ganymedes01.ironchestminecarts.minecarts.types")

对每个类:

  1. 用无参构造(传入 null World)实例化一次,取出 type();
  2. EntityRegistry.registerModEntity(clazz, "minecart_chest_" + type.name().toLowerCase().replace("teel","ilver"), type.ordinal(), instance, 80, 3, true);
  3. 存入 static Map<IronChestType, Class<...>> map。
注册参数 值
实体 ID 名 minecart_chest_<lowercase>,钢为 minecart_chest_silver
modId(mod 内部 id) type().ordinal()(0=铁, 1=金, 2=钻石, 3=铜, 4=钢, 5=水晶, 6=黑曜石, 7=泥土)
追踪范围 80
更新频率 3
发送速度更新 true

加变体的方式:在 minecarts/types/ 下新增一个带 World 和 World,double,double,double 两个构造器的子类并实现 type(),注册与实体 ID 分配会自动完成。扫描只注册本模组的 8 个类,不检查 IronChestType.isEnabled(),所以 Iron Chests 配置中关闭某个等级也不会影响矿车的存在。

物品栏

private ItemStack[] inventory = new ItemStack[getSizeInventory()];
public int getSizeInventory() { return type().getRowCount() * type().getRowLength(); }
  • 物品栏是基类自建的数组,不基于 TileEntity/Dropper。
  • 槽位数由 IronChestType 的 size / rowLength 决定(见分类页变体表)。
  • 覆写了 getStackInSlot / decrStackSize / getStackInSlotOnClosing / setInventorySlotContents 五个标准 IInventory 方法。
  • isItemValidForSlot(slot, stack) 直接委托 type().acceptsStack(stack):只有泥土箱子矿车拒绝非泥土物品。
  • setInventorySlotContents 会把超出 getInventoryStackLimit() 的堆叠截断(该上限本类未覆写,沿用 IInventory 默认 64,属推断)。
  • getCartItem() 返回 IronChestMinecarts.carts.get(type()) 对应的物品;查不到时回退到原版 Items.minecart。

生成

方式 行为
物品放置 ItemMinecartIronChest.onItemUse:目标方块 BlockRailBase.func_150051_a 为真时,在方块中心 (x+0.5, y+0.5, z+0.5) 通过 makeMinecart(world,...) 反射生成,物品数量 -1
箱子升级器 右键矿车且升级器 canUpgrade(type()) 时,整车 NBT 迁移到目标等级的新矿车
自然生成 无。本模组不注册任何 WorldGenerator、刷怪蛋或自定义生成逻辑

若物品有自定义名(hasDisplayName),生成的矿车会继承该名字(setMinecartName)。

交互

右键(interactFirst)

  1. 先发布可取消的 Forge MinecartInteractEvent(this, player),被其它 mod 取消则直接返回 true。
  2. 检查手持物是否为 Iron Chests 的 ItemChestChanger:
    • changer.getType().canUpgrade(type())(Iron Chests 实现为 from == this.source);
    • 服务端:把当前实体完整 NBT 写入临时 NBTTagCompound,setDead() 删除旧矿车,反射构造目标等级矿车并 readFromNBT(nbt) 后 spawnEntityInWorld —— 物品栏、显示方块、自定义名全部保留;
    • 生存模式扣除 1 个升级器。
  3. 服务端 openGui(IronChestMinecarts.instance, hashCode(), worldObj, 0, 0, 0) 打开箱子界面。

⚠️ 目标等级必须是本模组已注册的 8 个之一。若目标为 SILVER / NETHERITE / DARKSTEEL,map.get(newType) 返回 null,setDead() 已经执行但新矿车无法构造,NPE 被 catch (Exception e) { e.printStackTrace(); } 吞掉 —— 矿车连同车内物品一起消失。完整受影响列表见分类页"已知问题"。

该路径也没有调用 IronChestType.allowUpgradeFrom,因此 Iron Chests 的 blocklistUpgrades 配置对矿车无效。

GUI 路由

openGui 传入的 GUI ID 是矿车自身的 hashCode()(不是实体 ID)。GuiHandler 收到 ID 后用 world.getEntityByID(ID) 反查:

方法 返回
getServerGuiElement new ContainerIronChest(player.inventory, minecart, minecart.type(), 0, 0)
getClientGuiElement 反射取 GUIChest 的私有构造器 (GUIChest.GUI, IInventory, IInventory),界面枚举按 minecart.type().ordinal() 取值

两个 GUI 类都来自 Iron Chests,本模组只做转发。

破坏与掉落(killMinecart)

完全覆写原版实现,依次掉落:

顺序 掉落物 说明
1 1 个原版 Items.minecart 若矿车有自定义名,setStackDisplayName 保留
2 1 个对应等级的铁箱子 new ItemStack(func_145817_o(), 1, type().ordinal())
3 车内每个非空堆叠 各自 copy() 成 EntityItem,抛物线散落(motionX/Z = N(0, 0.05),motionY = N(0, 0.25))

setDead() 也被覆写为直接 isDead = true。注意 getCartItem()(返回本模组矿车物品)与这里的原版 Items.minecart 是两套掉落路径。

NBT

键 何时写入 说明
Items 总是 复合标签列表,每项含 Slot(byte)与完整 ItemStack
CustomDisplayTile hasDisplayTile() 为真 标记需要恢复显示方块
DisplayTile 同上 显示方块 id;方块材质为空气时写 0
DisplayData 同上 getDisplayTileData(),本类覆写为 type().ordinal()
DisplayOffset 同上 getDisplayTileOffset()
CustomName 名字非空 字符串,供 setMinecartName 恢复

readEntityFromNBT 会按 getSizeInventory() 重新分配数组(升到更高等级后槽位数增加也能正确读入),并对 Slot 做 & 255 还原与越界检查。铁箱子方块本身由 func_145817_o() 恒定返回 IronChest.ironChestBlock,因此矿车内显示的箱子外观由 DisplayData(即等级)决定。

Railcraft 集成

全部标 @Optional.Method(modid = "Railcraft"),Railcraft 未安装时由 FML 剥离。

方法 行为
offerItem(Object, ItemStack) 从槽位 0 起找第一个空位放入;否则对相同物品堆叠,堆到 min(maxStackSize, getInventoryStackLimit()) 后把溢出部分作为返回值交还
requestItem(Object) 返回第一个非空槽位的 1 个物品
requestItem(Object, ItemStack) 返回第一个与请求物品相同的槽位的 1 个物品
requestItem(Object, IStackFilter) 返回第一个匹配 IStackFilter.matches 的槽位的 1 个物品
getCommandSenderName() 用 StatCollector 翻译 entity.<id>.name.railcraft

Railcraft 的装料器(loader)因此可以直接向箱子矿车塞物品,卸载器(unloader)可以取出,不必手动开 GUI。

相关条目