Files
ngzz-mc/internal/block/registry.go
NianGao Dev 5da460c9a2 feat(ui): 小地图(左上角地表色图+玩家标记)与物品栏界面(E 键/Q 丢弃)
- 小地图(UI还原.md §3.5):96×96 地表色图(方块注册表→图集中心色,
  植被染色后采样),每 1.5s 重建,玩家标记烘焙进地图图(白点+黑描边);
  覆盖层 quad 批处理末尾颜色异常,标记改走烘焙规避
- 物品栏(UI还原.md §3.3):E 键开关屏幕状态 5,inventory.png 176×166
  窗口 + 27 主物品区 (7,83) + 热键栏行 (7,141),点击与手持栈交换/合并,
  Esc 关闭手持栈自动回收
- Q 丢弃选中槽 1 个物品(掉落在视线前方,UI与输入.md §5)
- cjkfont 多行渲染(设置界面按键教程 4 行列表)
- 动物攻击测试、原版掉落测试(草→泥土/动物掉肉)
2026-08-16 20:35:21 +08:00

168 lines
5.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Package block 方块注册表:加载 blocks.json,提供方块定义与状态查询(设计.md §6.1)。
//
// 设计要点:
// - 运行时状态 = 数字 ID(uint16)+ meta(uint8),打包为 State(uint32);
// - ID 一旦发布不可变(ID 稳定性铁律,热更新.md §4.4);
// - isSolid / isOpaque / light 等判定为「单一来源」,光照、物理、渲染共用。
package block
import (
"encoding/json"
"fmt"
"os"
)
// State 运行时方块状态:ID(低 16 位)+ meta(高 8 位)。
type State uint32
// Air 空气(ID 0)。
const Air State = 0
// NewState 打包状态。
func NewState(id uint16, meta uint8) State { return State(uint32(id) | uint32(meta)<<16) }
// ID 返回方块 ID。
func (s State) ID() uint16 { return uint16(s & 0xFFFF) }
// Meta 返回附加数据(朝向/含水/阶段等)。
func (s State) Meta() uint8 { return uint8(s >> 16) }
// Def 方块定义(blocks.json 条目,设计.md §2.3)。
type Def struct {
ID uint16 `json:"id"`
Name string `json:"-"`
Hardness float64 `json:"hardness"`
Resistance float64 `json:"resistance"`
Light uint8 `json:"light"`
Transparent bool `json:"transparent"`
Solid bool `json:"solid"`
Cutout bool `json:"cutout"`
Translucent bool `json:"translucent"`
Fluid bool `json:"fluid"`
RequiredTier int `json:"required_tier"`
Drops string `json:"drops"`
NoDrop bool `json:"no_drop"` // 破坏不掉落(如树叶)
Textures map[string]string `json:"textures"`
Tint string `json:"tint"` // 植被染色表:grass/foliage(渲染.md §4.1)
}
// Registry 方块注册表。
type Registry struct {
byID []Def
byName map[string]uint16
}
// Load 解析 blocks.json 构建注册表(按 id 索引;ID 0 保留给空气,其余不要求连续但必须有唯一)。
func Load(path string) (*Registry, error) {
b, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("block.Load %s: %w", path, err)
}
var doc struct {
Blocks map[string]Def `json:"blocks"`
}
if err := json.Unmarshal(b, &doc); err != nil {
return nil, fmt.Errorf("block.Load %s: %w", path, err)
}
r := &Registry{byName: make(map[string]uint16, len(doc.Blocks))}
maxID := uint16(0)
for name, d := range doc.Blocks {
if d.ID == 0 {
return nil, fmt.Errorf("block.Load: 方块 %q 使用保留 ID 0(空气)", name)
}
if d.ID > maxID {
maxID = d.ID
}
if _, dup := r.byName[name]; dup {
return nil, fmt.Errorf("block.Load: 方块重名 %q", name)
}
r.byName[name] = d.ID
}
// 按 ID 建立索引(下标 0 = 空气零值)
r.byID = make([]Def, int(maxID)+1)
for name, d := range doc.Blocks {
d.Name = name
if r.byID[d.ID].ID != 0 {
return nil, fmt.Errorf("block.Load: ID %d 被重复使用", d.ID)
}
r.byID[d.ID] = d
}
return r, nil
}
// Get 按 ID 取定义;越界返回零值(视为空气)。
func (r *Registry) Get(id uint16) Def {
if int(id) < len(r.byID) {
return r.byID[id]
}
return Def{}
}
// All 返回全部方块定义(下标即 ID,含空槽 0——小地图配色表等遍历用)。
func (r *Registry) All() []Def { return r.byID }
// ID 按名称取 ID。
func (r *Registry) ID(name string) (uint16, bool) {
id, ok := r.byName[name]
return id, ok
}
// Name 按 ID 取名称。
func (r *Registry) Name(id uint16) string { return r.Get(id).Name }
// IsSolid 实体可站立/碰撞判定(场景/物理与碰撞.md §2)。
func (r *Registry) IsSolid(s State) bool { return s != Air && r.Get(s.ID()).Solid }
// IsOpaque 完全遮光判定(场景/光照.md §7 分类表)。
func (r *Registry) IsOpaque(s State) bool {
if s == Air {
return false
}
d := r.Get(s.ID())
return !d.Transparent
}
// Light 光源亮度(0–15,场景/光照.md §4)。
func (r *Registry) Light(s State) uint8 { return r.Get(s.ID()).Light }
// IsCutout alpha test 类型(树叶/玻璃,渲染.md §6)。
func (r *Registry) IsCutout(s State) bool { return r.Get(s.ID()).Cutout }
// IsTranslucent 半透明类型(水/彩色玻璃,需要排序 pass)。
func (r *Registry) IsTranslucent(s State) bool { return r.Get(s.ID()).Translucent }
// IsFluid 液体判定(水/岩浆)。
func (r *Registry) IsFluid(s State) bool { return r.Get(s.ID()).Fluid }
// Texture 取方块某个面的纹理键(如 "up"/"side"/"all")。
func (r *Registry) Texture(s State, face string) string {
d := r.Get(s.ID())
if t, ok := d.Textures[face]; ok {
return t
}
if t, ok := d.Textures["all"]; ok {
return t
}
return "block/" + d.Name
}
// TintTextureKeys 返回「染色表名 → 涉及纹理键」:图集构建后按染色表给这些
// 纹理乘色(渲染.md §4.1 植被染色)。无染色标记的方块不出现。
func (r *Registry) TintTextureKeys() map[string][]string {
out := make(map[string][]string)
for _, d := range r.byID {
if d.ID == 0 || d.Tint == "" {
continue
}
seen := make(map[string]struct{})
for _, k := range d.Textures {
if _, dup := seen[k]; dup {
continue
}
seen[k] = struct{}{}
out[d.Tint] = append(out[d.Tint], k)
}
}
return out
}