Signal Terminal

基本信息

属性 值
注册名 TMechworks:signalTerminal
方块类 SignalTerminal(Block + ITileEntityProvider)
TileEntity SignalTerminalLogic(TileEntity,实现 ISignalTransceiver)
材质 Material.circuits
硬度 / 抗性 0.1 / 1(setHardness(0.1F) + setResistance(1))
步声 soundTypeMetal
碰撞箱 getCollisionBoundingBoxFromPool 返回 null —— 本身不阻挡,实际碰撞来自 13 个 hitbox
库存 / GUI 无
通道 6 面 × 16 通道,共 96 条逻辑连接
通道贴图 16 种染料色玻璃
所属总线 必须是某个 Signal Bus 的成员
放置条件 6 面中至少一面贴实心方块(canPlaceBlockAt)
掉落 getDrops 恒返回空;breakBlock 改掉已注册的终端与线材

核心机制:6 面 × 16 通道的选择器

这是本方块的关键设计,也是 byte[16] 信号数组的由来。

  • SignalTerminalLogic.sideChannel 是 byte[6] —— 每个面绑定一个通道号 0–15(:27)。
  • connectedSides 是 byte[6] —— 每个面当前的信号强度(:25)。
  • 总线用 byte[16] 搬运 16 个通道的强度。终端从总线取 signals[sideChannel[i]] 赋给 connectedSides[i](processSignalUpdate,:177-225)。

因此一个终端的 6 个面可以各自绑定到 16 条通道中的任意一条,实现「一路输入、六路分线,且每路监听不同信号」。

通道切换

SignalTerminal.onBlockActivated(blocks/SignalTerminal.java:441-476):

int side = sideBoxMapping[boxHit];
if (par5EntityPlayer.isSneaking()) ((SignalTerminalLogic) te).prevChannel(side);
else                                          ((SignalTerminalLogic) te).nextChannel(side);
  • 普通右键该面的通道块 → nextChannel(side)(+1,16 回绕到 0)
  • 潜行右键 → prevChannel(side)(−1,0 回绕到 15)

两个方法都在 worldObj.isRemote 时直接返回(仅服务端改状态),并置 doUpdate = true 触发重算。

13 个 hitbox

HITBOXES = 13,sideBoxMapping = {-1, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5}。索引 0 是无效(boxHit == 0 时 return false),索引 1–12 按「每面 2 个」分配:parts[2i-1] = 面板(plate),parts[2i] = 通道(channel)。closestClicked(player, 3.0F, ...) 用射线在 13 个 AABB 中取最近命中。

getRenderType() 返回 SignalTerminalRender.renderID(TESR),isOpaqueCube() / renderAsNormalBlock() 均返回 false。

16 种通道色

channelTextureNames = white, orange, magenta, lightblue, yellow, lime, pink, gray, lightgray, cyan, purple, blue, brown, green, red, black —— 即 1.7.10 的 16 种染色玻璃,贴图路径 tmechworks:glass/stainedglass_<name>。玩家从贴图即可读出每面绑定的通道号。

数值

数值 值
材质 Material.circuits
硬度 / 抗性 0.1 / 1
步声 soundTypeMetal
面数 6
每面通道数 16(0–15)
sideChannel byte[6],NBT 键 sideChannel(getByteArray)
connectedSides byte[6],NBT 键 connectedSides(getByteArray)
通道贴图数 16(16 染色玻璃)
hitbox 数 13(索引 0 无效,1–12 = 6 面 × 2)
hitbox 映射 {-1, 0,0, 1,1, 2,2, 3,3, 4,4, 5,5}
射线检测距离 3.0F
面板 AABB plate 0.25–0.75 宽,low 0.0–0.15 / high 0.85–1.0
通道 AABB channel 0.375–0.625
中心块 AABB(无 TE 时) 0.375–0.625 全轴
通道切换 普通右键 next(+1 mod 16)/ 潜行右键 prev(−1 mod 16)
弱电 / 强电 6 面均输出
放置 NBT 键 connectedSide(onBlockPlacedBy 读入后清空)
掉落 getDrops 恒空;breakBlock 掉 getDroppedTerminals() 个终端 + getDroppedWire() 个 Length Wire

交互

触发 行为
放置 需至少一面贴实心方块;读物品 NBT 的 connectedSide 预设朝向
右键面板外的通道块 切换该面通道(+1)
潜行 + 右键 切换该面通道(−1)
右键索引 0 的 hitbox return false(无效果)
Spool of Wire 右键 记录本方块坐标到 Spool 的 spoolWireData
总线信号变化 receiveSignalUpdate → processSignalUpdate 逐面更新 + notifyBlocksOfNeighborChange
破坏 notifyBreak() 注销总线归属;掉已注册终端与线材(motionY +0.2 高斯散射)

配方

common/MechRecipes.java:119-128,1 条:

b
g
b
符号 材料 数量
b TMechworks:signalBus 2
g minecraft:glass(OreDictionary.WILDCARD_VALUE) 1

源码缺陷

  • getDrops 恒返回空 ArrayList(:492-494):正常破坏不掉落自身物品。breakBlock 只掉「已注册的终端 + 线材」。这意味着未接入总线的终端被拆后物品直接消失,且该设计对「刚放就想拆回」的玩家极不友好。
  • readFromNBT 的数组长度无防御(:84-90):sideChannel / connectedSides 用 getByteArray 读回,只对 sideChannel 检查了 length != 6 并重置;connectedSides 未做同样检查,若 NBT 数据损坏或来自旧版本会得到非 6 长度数组,后续 connectedSides[i] 越界。
  • processSignalUpdate 的 oSide 作用域(:195-215):int oSide; 声明在 case 0: 块内却跨 case 赋值,依赖「无花括号 case」的作用域规则,可读性差。
  • onBlockActivated 有重复的 isRemote 判断(:445 与 :447):外层已判 if (!par1World.isRemote),内层又判一次,冗余。
  • onBlockPlacedBy 清空物品 NBT 的方式粗暴(:485):itemStack.stackTagCompound = null 直接丢弃整个 NBT,若玩家在物品上还有其他自定义数据会一并丢失。
  • canPlaceBlockAt / canBlockStay 逻辑完全重复(:152-159 与 :195-202),6 个 isSideSolid 调用逐字相同,可提取为共用方法。
  • breakBlock 用 new Random()(:501)而非 world.rand/itemRand,每次破坏产生相同的散射序列(因为未 seed 变化来自时间戳),掉落物散射不可控。
  • getChannelIcon(int channel) 有边界检查但 getChannelIcons() 之外无索引保护:SignalBusLogic 侧的 localHighSignals[16] 若 sideChannel 被外部写坏会越界。

相关条目