睡袋方块(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 入参,只要被调用就会:

  1. ExtendedPlayer.get(player).setWakeUpFlag(true) → 通知服务端 tick 刷新该玩家的重生点
  2. 按朝向偏移找到另一半,若另一半也是睡袋,则尝试把睡袋收回背包:
    • 槽位选择:优先当前手持栏 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 图集精灵

相关条目