铁箱子运输矿车(共用机制基类)
基本信息
| 属性 | 值 |
|---|---|
| 类 | 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")
对每个类:
- 用无参构造(传入
nullWorld)实例化一次,取出type(); EntityRegistry.registerModEntity(clazz, "minecart_chest_" + type.name().toLowerCase().replace("teel","ilver"), type.ordinal(), instance, 80, 3, true);- 存入
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)
- 先发布可取消的 Forge
MinecartInteractEvent(this, player),被其它 mod 取消则直接返回true。 - 检查手持物是否为 Iron Chests 的
ItemChestChanger:changer.getType().canUpgrade(type())(Iron Chests 实现为from == this.source);- 服务端:把当前实体完整 NBT 写入临时
NBTTagCompound,setDead()删除旧矿车,反射构造目标等级矿车并readFromNBT(nbt)后spawnEntityInWorld—— 物品栏、显示方块、自定义名全部保留; - 生存模式扣除 1 个升级器。
- 服务端
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。