Sign-Post
路牌方块(注册名 signposts:signPost)。放置后高 2 格,下半格存储传送目标坐标,上半格只负责视觉。配合 魔法路牌标记 刻写铭文后,会作为 signPost 类型的全局标记出现在 Antique Atlas 中,点击即可传送。
基本信息
| 属性 | 值 |
|---|---|
| 注册名 | signposts:signPost |
| 内部名 | signPost(GameRegistry.registerBlock(signPostBlock, "signPost")) |
| 材质 | Material.plants |
| TileEntity | SignPostTileEntity(注册名 signPostTileEntity) |
| 硬度 | 2.0 |
| 爆炸抗性 | 6.0 |
| 挖掘等级 | 2,工具 axe |
| 脚步声 | Block.soundTypeWood |
| 碰撞箱 | 0.4, 0, 0.4 ~ 0.6, 1, 0.6 |
| 亮度 | 0(未调用 setLightLevel) |
| 方块容器 | isBlockContainer = true |
| 物品形态 | 无(见下方「方块形态」) |
合成配方
矿石字典配方(ShapedOreRecipe):
.WS W = 任意 plankWood(任意木材木板)
.WS S = 原版告示牌
.W.
即 2 任意木板 + 1 告示牌竖排,产出 1 个路牌。plankWood 走 ore dict,因此所有原版木板变种都可用。
metadata / 方块状态
只有 5 个 metadata 值,语义分为两类:
| meta | 含义 | isBottom |
由谁设置 |
|---|---|---|---|
| 0 | 下半格,面朝南(realY -= 0.13,旋转 180°) |
true | onBlockPlacedBy |
| 1 | 下半格,面朝东(realX -= 0.13,旋转 90°) |
true | onBlockPlacedBy |
| 2 | 下半格,面朝北(realY += 0.13,旋转 0°) |
true | onBlockPlacedBy |
| 3 | 下半格,面朝西(realX += 0.13,旋转 270°) |
true | onBlockPlacedBy |
| 4 | 上半格(自动生成),无渲染 | false | onBlockPlaced |
朝向换算在 onBlockPlacedBy 中由放置者 yaw 计算:rotation = floor(placer.rotationYaw * 4.0F / 360.0F + 0.5) & 3,再经 switch 映射 0→meta 2、1→meta 3、2→meta 0、3→meta 1,最后 setBlockMetadataWithNotify(..., 2)。
摆放要求
canPlaceBlockAt:当前位置与y+1位置都必须是isReplaceable,因此只能贴地或贴已有可替换方块。onBlockPlaced:放置下半格时自动在y+1写一个 meta 4 的同种方块(flags = 2,触发客户端更新),所以任何时候都是 2 格高。onBlockPlaced开头if (meta >= 4) return 4;—— meta 4(即上半格)被再次放置时直接返回 4,不会再往上叠第三格,形成高度上限 2。
交互
全部逻辑包在 world.isRemote 内(纯客户端),onBlockActivated 恒返回 false:
| 手持 / 状态 | 行为 |
|---|---|
手持 魔法路牌标记 且 markerId == 0 |
打开铭文输入界面(proxy.openSignPostLabelGui),跳转坐标固定为「玩家当前 X / floor(posY - 1) / 玩家当前 Z」 |
手持魔法路牌标记且 markerId != 0 |
什么也不做(return false) |
| 手持 Antique Atlas | 若 markerId != 0,打开地图界面,支付处理器键为 signposts |
快捷栏(槽位 8→1)存在 Atlas 且 markerId != 0 |
同上;注意循环为 for (int j = 8; j > 0; j--),跳过槽位 0 |
| 上述条件都不满足 | 无反应 |
破坏行为(breakBlock)
if (meta == 4) {
world.setBlockToAir(x, y - 1, z); // 拆上半格 → 连带拆掉下半格
} else {
SignPostsMod.removeJumpTarget(x, y, z, world); // 删除全局标记 + 映射
world.setBlockToAir(x, y + 1, z); // 拆下半格 → 连带拆掉上半格
}
world.removeTileEntity(x, y, z);
所以拆任一半都会连带拆掉另一半;拆下半格时才会清除 Atlas 标记。
方块形态
源码中没有 ItemBlock、getDrops、LootTable 或 dropItem 的任何实现(已 grep 确认),GameRegistry.registerBlock 也没有传 ItemBlock。因此:
- 路牌没有物品形态,不能放进背包或物品栏;
- 破坏时不掉落任何东西;
- 无法被
/give或创意模式物品栏取用,只能靠ShapedOreRecipe合成后直接放置。
NBT 字段(SignPostTileEntity)
| 字段 | 类型 | 写入条件 | 含义 |
|---|---|---|---|
isBottom |
boolean | 总是 | 是否为下半格(= meta != 4) |
jumpX |
double | 仅 isBottom == true |
传送目标 X(玩家坐标,浮点) |
jumpY |
double | 仅 isBottom == true |
传送目标 Y |
jumpZ |
double | 仅 isBottom == true |
传送目标 Z |
markerId |
int | 仅 isBottom == true |
对应的 Antique Atlas 全局标记 ID,0 表示未刻写 |
写入位置:setAsJumpLocation(x, y, z, markerId) 先通过 getBottom() 定位到下半格写入四个字段,再 worldObj.markBlockForUpdate(...)。上半格的 getJumpX/Y/Z()、getMarkerId() 全部委托给 getBottom(),后者在 isBottom == false 时取 worldObj.getTileEntity(xCoord, yCoord - 1, zCoord)。
同步走原版 S35PacketUpdateTileEntity(getDescriptionPacket 用 action id 1 + 完整 NBT,onDataPacket 反过来 readFromNBT)。
⚠️
public String text字段是唯一不参与 NBT 的数据:源码里没有任何data.setString("text", ...),因此它纯客户端、不随存档与区块同步,也不会在标记被改名后更新。
贴图与渲染
- 方块贴图
signposts:sign_post_top,getIcon对侧面(side > 1)返回Blocks.log.getIcon(side, 0),即侧面看起来像原版木头、顶底面用本模组贴图。 - 文字渲染由
SignPostRenderer extends TileEntitySignRenderer负责,getRenderType()返回 0(普通模型)。 - 渲染时
meta >= 4直接return(上半格不渲染文字),下半格按 meta 做0.13的水平/垂直偏移并旋转 0/90/180/270°。 - 缩放常量
scale = 0.6666667F,用ModelSign并隐藏signStick,绑定原版textures/entity/sign.png。 - 文字取自 Atlas 标记而非 NBT:当
signTe.text == null且markerId != 0时,用AntiqueAtlasMod.globalMarkersData.getData().getMarkerByID(id)取marker.getLocalizedLabel()赋给signTe.text;只有text != null的分支才真正调用FontRenderer.drawString。因此新刻写的路牌要等下一次渲染才显示文字。
相关条目
- 魔法路牌标记 - 刻写传送目标的消耗品,决定
markerId是否被写入 - 铭文输入界面 - 客户端弹出界面,限制 15 字符并发送创建消息
- Antique Atlas 集成 -
signPost标记类型与MarkerToTileMap存档数据 - 传送目标流程 - 刻写、标记、点击传送的完整链路
- 饥饿值支付 - 传送时扣除饱食度的默认实现