睡袋方块(Sleeping Bag Block)
基本信息
| 属性 | 值 |
|---|---|
| 类 | ru.npo6ka.sleepingbag.blocks.BlockSleepingBag(继承 net.minecraft.block.BlockBed) |
| modid | sleepingbag |
| 注册名 | sleepingbag:sleepingBagBlock(GameRegistry.registerBlock(sleepingBagBlock, "sleepingBagBlock")) |
| 本地化键 | tile.sleepingBagBlock.name(由 setBlockName("sleepingBagBlock") 推导) |
| 纹理根名 | sleepingbag:sleeping_bag(setBlockTextureName) |
| TileEntity | 无(源码中没有任何 TileEntity 类) |
| 硬度 | 0.2(ItemsRegister.init() 中显式 setHardness(0.2f)) |
| 爆炸抗性 | 本 mod 未赋值,沿用原版 BlockBed 构造函数的默认值(推断:源码里看不到具体数值) |
| 亮度 | 0(未调用 setLightLevel) |
| 碰撞箱 | 高 0.25:setBlockBounds(0.0f, 0.0f, 0.0f, 1.0f, 0.25f, 1.0f) |
| metadata | 仅用低 2 bit 作朝向(0~3)+ 第 4 bit(+8)作「头部」标记,没有染色变体 |
| 创造标签 | 物品侧为 CreativeTabs.tabDecorations |
方块本身不可独立获得:
getItemDropped在头部返回Item.getItemById(0)(空气),只有脚部才掉一个睡袋物品。
纹理(客户端)
registerBlockIcons 注册 6 张 16×16 贴图,按「脚部 / 头部」两套各 3 张:
| 用途 | 资源路径 |
|---|---|
| 顶面 | assets/sleepingbag/textures/blocks/sleeping_bag_feet_top.png / sleeping_bag_head_top.png |
| 端面 | assets/sleepingbag/textures/blocks/sleeping_bag_feet_end.png / sleeping_bag_head_end.png |
| 侧面 | assets/sleepingbag/textures/blocks/sleeping_bag_feet_side.png / sleeping_bag_head_side.png |
getIcon(side, meta) 把 side == 0(底面)重映射为 1(顶面),再用 Direction.bedDirection[dir][side] 决定取 iconTop / iconSide / iconEnd。
摆放要求
- 必须成对放置,占 2 个格子;朝向由物品的放置逻辑决定(见睡袋物品页)
- 下方一格必须有实心顶面(
World.doesBlockHaveSolidTopSurface),否则放置失败 - 高度只有 0.25 格(比原版床的 0.5625 更扁),可以贴着墙根、地毯、机器顶面放
交互
| 触发 | 行为 |
|---|---|
| 右键(脚部) | 自动重定向到头部方块坐标再处理 |
| 右键 · 下界 / 不可重生维度 | 拆掉两半并以威力 5.0 爆炸(world.newExplosion(null, ..., 5.0f, true, true),着火 + 破坏方块) |
| 右键 · 已被他人占用 | 提示 tile.bed.occupied |
| 右键 · 白天 | 提示 tile.bed.noSleep(由 canPlayerSleep 发出) |
| 右键 · 附近有敌对生物 | 提示 tile.bed.notSafe |
| 右键 · 成功入睡 | 置 occupied 标记,并触发「个人重生点」刷新 |
| 破坏(脚部) | 掉落 1 个睡袋物品 |
| 破坏(头部) | 无掉落(返回空气) |
isBed(world, x, y, z, EntityLivingBase) 返回 this == ItemsRegister.sleepingBagBlock,即只有本方块被识别为床(覆写了原版对所有 BlockBed 的判定)。
重生点接管(核心机制)
getBedSpawnPosition 被覆写为:
服务端:返回 ExtendedPlayer.get(player).getLastCoord() // 该玩家自己放置睡袋时站的位置
客户端:返回 null
也就是说睡袋放置成功后,MobSpawner / 判怪点判定会把该玩家的出生点算到睡袋旁边,这是 GTNH 服务器战点(spawn point)不被睡袋链条污染的关键。
拆袋回收
setBedOccupied 覆写了原版同名方法,并且完全忽略 occupied 入参,只要被调用就会:
ExtendedPlayer.get(player).setWakeUpFlag(true)→ 通知服务端 tick 刷新该玩家的重生点- 按朝向偏移找到另一半,若另一半也是睡袋,则尝试把睡袋收回背包:
- 槽位选择:优先当前手持栏
player.inventory.currentItem;该槽非空时从索引 9 扫到 34 找第一个空格 - 创造模式:直接拆掉两半,不给物品
- 找到空槽:拆掉两半并放入 1 个
new ItemStack(sleepingBagItem) - 背包全满:退回原版
removedByPlayer(world, player, x, y, z, false)走正常破坏掉落流程
- 槽位选择:优先当前手持栏
客户端渲染
ClientProxy.init() 创建 BlockRenderer(ISimpleBlockRenderingHandler)并通过 RenderingRegistry.registerBlockHandler 注册,随后 setRenderId(blockRenderer.getRenderId()) 写回方块;服务端上 renderType 保持构造函数给的 0。
BlockRenderer.renderWorldBlock 是手写的等距(isometric)床渲染,与原版不同之处:
| 面 | 高度 | 亮度系数 |
|---|---|---|
| 底面 | y | 0.5 |
| 顶面 | y + 0.25 | 1.0 |
| 南北侧(z-1 / z+1) | y ~ y+1 | 0.8 |
| 东西侧(x-1 / x+1) | y ~ y+1 | 0.6 |
- 每个面用内部
TextureSquare类按dir做 UV 旋转(rotateRight/rotateLeft/rotateDouble/invertHorizontal) - 朝向面自身被跳过(
k1判定),不渲染床头挡板 - ⚠️ 侧面上沿画到
y + 1.0,而碰撞箱只有 0.25 高——渲染高度与碰撞高度不一致,这是源码里的既定行为 renderInventoryBlock是空方法,shouldRender3DInInventory返回false,即物品栏里不渲染方块模型getIconSafe在贴图为 null 时回落到missingno图集精灵
相关条目
- 睡袋(Sleeping Bag) - 放置成对睡袋的唯一途径
- 个人重生点与睡眠状态机 -
ExtendedPlayer+EventHandler的数据与 tick 流程