移动占位方块(BlockMovingRow)

ForgeRelocation 内部方块。结构开始移动时,它会临时占据每一行最前端的那一格,从而让整行方块在动画期间表现为"前移一格"。

方块名 relocation.blockmovingrow,由 mrtjp.relocation.blocks.BlockMovingRow 定义。mcmod.info 里的描述只有一句:“Move blocks from one place to another”。

基本属性

继承自 MrTJPCore 的 InstancedBlock(带 TileEntity 的方块基类),后者在构造时执行 GameRegistry.registerBlock(this, getItemBlockClass, name),因此本方块会自动注册出一个 ItemBlockCore。

属性 值 来源
方块名 relocation.blockmovingrow blocks.scala:22
基类 mrtjp.core.block.InstancedBlock blocks.scala:22
材质 Material.iron blocks.scala:22
硬度 -1f(setHardness(-1f)) blocks.scala:23
脚步声 Block.soundTypeGravel blocks.scala:24
创造栏 setCreativeTab(null)——不进创造模式物品栏 blocks.scala:25
TileEntity addSingleTile(classOf[TileMovingRow]) blocks.scala:26
抗爆 setResistance 无(全仓库该方块未调用) —
光照等级 无 —

因为 setCreativeTab(null),这个方块无法被玩家以任何方式获得——它只由代码在移动开始时放置、移动结束时清除。

放置与清除

TileMovingRow.setBlockForRow(w, r) 对每一行调用:

w.setBlock(r.pos.x, r.pos.y, r.pos.z, RelocationMod.blockMovingRow, 0, 3)

meta 恒为 0;更新标记 3 即二进制 11(发送方块更新 + 抑制邻居更新)。整个移动过程不靠 metadata 表达方向——方向信息在 BlockRow.moveDir 与网络描述符里,不在方块状态里。

TileMovingRow.update() 是它的自愈逻辑:

override def update() {
  if (!MovementManager2.isMoving(world, x, y, z)) world.setBlockToAir(x, y, z)
}

若世界状态与占位方块不同步(例如结构已被移除),该方块在下一次更新时把自己变成空气。

碰撞箱 = 前方方块的箱子

TileMovingRow 覆写 getBlockBounds 与 getCollisionBounds(后者直接返回前者),从运动中的结构里按坐标反查自己所属的 BlockRow,再算出动态碰撞箱:

override def getBlockBounds = {
  val s = MovementManager2.getEnclosedStructure(world, x, y, z)
  if (s != null) {
    val r = s.rows.find(_.contains(x, y, z)).get
    TileMovingRow.getBoxFor(world, r, s.progress)
  } else Cuboid6.full
}

getBoxFor 的逻辑(blocks.scala:34-47):取 p = r.pos.copy.offset(r.moveDir ^ 1)(占位方块身后那一格,即真正被推走的方块)。

  • 若 p 处也是 blockMovingRow(相邻两行首尾相接的极端情况),返回满立方 Cuboid6.full.copy()
  • 否则取该方块的 getCollisionBoundingBoxFromPool,减去 r.pos 的偏移,加上 BlockCoord.sideOffsets(r.moveDir) * progress——碰撞箱因此跟随动画进度一起平移

这就是玩家在移动过程中能撞上"正在移动的方块"的原因:碰撞箱是按 progress 连续插值的,而方块本体还停在原位。

推动实体

pushEntities(r, progress) 由 BlockRow.pushEntities 每 tick 调用:

val box = Cuboid6.full.copy
  .add(new Vector3(r.preMoveBlocks.head))
  .add(new Vector3(BlockCoord.sideOffsets(r.moveDir)).multiply(progress))
  .toAABB

val dp = (if (progress >= 1.0) progress + 0.1 else progress) - prevProg
val d = new Vector3(BlockCoord.sideOffsets(r.moveDir)) * dp
world.getEntitiesWithinAABBExcludingEntity(null, box) match {
  case list: JList[_] =>
    for (e <- list.asInstanceOf[JList[Entity]])
      e.moveEntity(d.x, d.y max 0, d.z)
  case _ =>
}
prevProg = progress

三点值得注意:

  1. 差分推动:不直接用绝对 progress,而是算出本 tick 的增量 dp 再乘方向向量,配合 prevProg 字段。
  2. 垂直方向不向下推:d.y max 0——实体永远不会被压进地板。向下移动的结构不会把站在上面的实体拖下去。
  3. 末帧补偿:progress >= 1.0 时用 progress + 0.1,多推 0.1 格,确保实体被推出方块原本占据的格位,而不是卡在边界上。

语言文件

显示名是 Moving Block,但语言键与文件都在 MCFrames 那边:

tile.relocation.blockmovingrow.name=Moving Block

它位于 src/main/resources/assets/mcframes/lang/en_US.lang——ForgeRelocation 自己没有 assets/relocation/ 目录,也没有 zh_CN.lang。见 配置文件 一节对资源文件的完整清单。

相关条目