Drawbridge

基本信息

属性 值
注册名 TMechworks:redstoneMachine
metadata 0
方块类 RedstoneMachine(InventoryBlock)
TileEntity DrawbridgeLogic(InventoryLogic 2 槽,实现 IFacingLogic / IActiveLogic / IDrawbridgeLogicBase)
材质 Material.iron
硬度 12(setHardness(12))
步声 soundTypeMetal
爆炸抗性 未调用 setter —— Block 默认 10.0
亮度 动态:getLightValue 读取槽 1 中的方块亮度(见下)
最大伸展 15 格(maxExtension = 15,byte)
GUI ID 0(CommonProxy.drawbridgeID)
假玩家 FakePlayerLogic(GameProfile 名 Player.Drawbridge)
放置音效 tile.piston.out,音量 0.25F
TE 槽数 2(槽 0 = 待放置方块,槽 1 = 「伪装」方块,只影响渲染与光照)

功能

BuildCraft 式伸缩桥。红石激活时沿面朝方向逐格放置 inventory[0] 中的方块;取消激活时逐格收回。放置由 FakePlayerLogic 执行,因此方块的 onBlockPlaced 看到的是一个合法玩家。

updateEntity 逻辑(blocks/logic/DrawbridgeLogic.java:239+):

working && !isInvalid() && !worldObj.isRemote
  ticks++; if (ticks == 5) { ticks = 0;
    if (active) {          // 伸出
      if (inventory[0] != null && stackSize > 0 && extension < maxExtension) {
        extension++;
        按 direction 偏移得到 (xPos,yPos,zPos)
        越界检查 → extension-- 并 working=false
        目标格必须 为空气 或 canPlaceBlockAt
        placeBlockAt(bufferStack, fakePlayer, world, ..., bufferStack.getItemDamage(), placeBlock)
        playSoundEffect("tile.piston.out", 0.25F, ...)
      }
    } else { ... }          // 收回
  }

朝向(direction,6 值)

放置时由 setDirection(yaw, pitch, player) 决定:俯视(pitch > 45)→ 1(下);仰视(pitch < -45)→ 0(上);否则按 yaw 映射为 2(北)/ 5(东)/ 3(南)/ 4(西)。

动态亮度(RedstoneMachine.getLightValue)

metadata == 0 或 2 时,读取槽 1 的方块并返回其 getLightValue();槽 1 为空则回落到 super。这是唯一让 Drawbridge 变成光源的途径。

实际放置规则

updateEntity 的判定链(blocks/logic/DrawbridgeLogic.java:396-400):

if (bufferStack != null && validBlock(block) && validMetadata(block, meta)   // 恒 true
        && validDrawbridge(xPos, yPos, zPos) && !hasInventory(xPos, yPos, zPos))

validMetadata 是恒 return true 的桩(:476-483,真实逻辑被注释在 javadoc 块里),因此 TMechworksRegistry 的 137 条 drawbridgeState 白名单运行时从未被读取。详见 Drawbridge 放置规则。

生效的过滤只有 isItemValidForSlot(:580-596):必须是 ItemBlock、不在配置黑名单中;槽 1 额外要求 isOpaqueCube() && renderAsNormalBlock() 且堆叠为 1。

伪装渲染

getIcon(IBlockAccess, x, y, z, side):若槽 1 中的方块 renderAsNormalBlock(),直接返回该方块的贴图 —— 即可把 Drawbridge 伪装成任意普通方块。colorMultiplier 同样透传槽 1 的染色值。

数值

数值 值
metadata 0
最大伸展 15 格
伸/缩频率 每 5 tick 1 格(0.25 s/格)
TE 槽数 2
槽 0 待放置方块(消耗源)
槽 1 伪装方块(只影响 getIcon / getLightValue / colorMultiplier)
越界上界 yPos >= 256 或 |x/z| >= 30000000 → 回退
放置音效 tile.piston.out 0.25F
GUI ID 0
硬度 12
假玩家名 Player.Drawbridge
贴图数 getTextureNames() 共 17 张(meta 0-3 各自取用)
贴图域声明 getTextureDomain() 返回 "tinker",但 registerBlockIcons 实际用 "tmechworks:machines/"(见缺陷)

交互

触发 行为
红石脉冲 setActive(true) → working = true → 每 5 tick 伸 1 格直到槽 0 空或达 15
红石撤除 setActive(false) → 每 5 tick 缩 1 格直到 extension = 0
右键 打开 GUI(ID 0),4 个方向按钮设置 placementDirection
槽 1 变化 setInventorySlotContents / decrStackSize 对 slot 1 调 markBlockForUpdate
断电/区块卸载 invalidate() 置 fakePlayer = null(下次用时惰性重建)

配方

common/MechRecipes.java:17-48,2 条(有/无 pattern 变体),图案相同:

a c a
# d #
# r #
符号 材料
# ingotBronze × 4
a ingotAluminumBrass × 2
c blankPattern meta 1 或 meta 2(两条配方各一)
r minecraft:redstone × 1
d minecraft:dispenser × 1

TCon 手册另有 1 条 MantleClientRegistry.registerManualLargeRecipe("drawbridge", ...)(common/MechContent.java:158-169),图案为 3×3 大合成:redstoneMachine + 4×ingotAluminumBrass + blankPattern meta 1 + 4×ingotBronze + dispenser + redstone。

源码缺陷

  • getTextureDomain() 与 registerBlockIcons() 不一致(blocks/RedstoneMachine.java:145-156):前者返回 "tinker",后者硬编码 "tmechworks:machines/"。前者是 Mantle InventoryBlock 抽象方法的遗留实现,实际无效但会误导读者以为贴图来自 TCon。
  • setDirection(int side) 是空方法(:100)—— Mantle IFacingLogic 的接口方法未实现,只有 setDirection(yaw, pitch, player) 生效。
  • placeBlockAt 前的 if (Block.getBlockFromItem(inventory[0].getItem()) == null) return; 会卡死(:290):extension++ 已在前面执行,若此时返回则不回退 extension,导致 extension 虚增一格且不放置任何方块。
  • interchangableBlockMapping 的反向放置存在缺陷:redstone_ore / redstone_torch / unpowered_repeater / powered_repeater / unpowered_comparator / powered_comparator 都只注册了单向映射(registry.put(Blocks.redstone_ore, Blocks.lit_redstone_ore) 而无反向),而 dirt/grass 反而是双向(:222-223)。放置时只查 blockToItemMapping,缺失方向的块无法通过 Drawbridge 放置。
  • drawbridgeState 中 Blocks.tripwire_hook 被 put 了两次(lib/TMechworksRegistry.java:191-192),第二次覆盖第一次,HashMap 无害但属冗余。
  • canDropInventorySlot(int) 恒 false(:102-104),Mantle 销毁流程因此不返还槽内容物。
  • bufferStack 是字段而非局部变量(:52),跨 tick 残留,加载旧 NBT 时不会被清空。
  • FakePlayerLogic 用 new GameProfile(null, "Player.Drawbridge"):UUID 为 null,靠 initFakePlayer() 惰性重建规避了区块加载时序问题(源码注释明确说明 validate() 里创建会造成 TileEntity 重复)。
  • getLightValue 每次调用最多读 3 次槽 1(getStackInSlot(1) 被调用两次),无缓存。

相关条目