Files
ngzz-mc/internal/ui/hud.go

108 lines
3.2 KiB
Go
Raw Normal View History

//go:build gl
// HUD 抬头显示:准星、热键栏、生命/饥饿、破坏进度(UI还原.md §2、§3.4 规格)。
package ui
import (
"fmt"
"mc/internal/game"
"mc/internal/item"
)
// HUD 游戏内 HUD。
type HUD struct {
sess *game.Session
items *item.Registry
// 物品图标:items 图集纹理与 UV
itemTex uint32
itemUV map[string][4]float32
// 逻辑分辨率与缩放(UI还原.md §3.1:427×240 基准 × 整数缩放)
baseW, baseH float32
scale float32
}
// NewHUD 创建 HUD。
func NewHUD(sess *game.Session, items *item.Registry, baseW, baseH, scale float32) *HUD {
return &HUD{sess: sess, items: items, itemUV: map[string][4]float32{}, baseW: baseW, baseH: baseH, scale: scale}
}
// SetItemAtlas 设置物品图标纹理与 UV 表(items 图集)。
func (h *HUD) SetItemAtlas(tex uint32, uv map[string][4]float32) {
h.itemTex = tex
h.itemUV = uv
}
// Draw 每帧绘制 HUD(调用方已 Begin)。
func (h *HUD) Draw(r *Renderer) {
w, hgt := float32(r.w), float32(r.h)
s := h.scale
white := [4]float32{1, 1, 1, 1}
// 准星(UI还原.md §3.4:屏幕正中 15×15 十字)
cx, cy := w/2, hgt/2
r.DrawRect(cx-1*s, cy-8*s, 2*s, 16*s, white)
r.DrawRect(cx-8*s, cy-1*s, 16*s, 2*s, white)
// 热键栏(UI还原.md §3.4:182×22,底部居中)
barW, barH := 182*s, 22*s
bx, by := cx-barW/2, hgt-barH
r.DrawRect(bx, by, barW, barH, [4]float32{0, 0, 0, 0.5})
for i := 0; i < 9; i++ {
sx := bx + float32(i)*20*s + 1*s
sy := by + 3*s
if i == h.sess.Inv.Selected {
r.DrawRect(sx-2*s, sy-2*s, 20*s, 20*s, [4]float32{1, 1, 1, 0.9}) // 选中高亮框
}
h.drawItemIcon(r, h.sess.Inv.Slots[i], sx, sy)
}
// 生命:10 心(每心 9×9,屏幕左下,UI还原.md §3.4)
hearts := int(20 / 2)
hp := h.sess.PlayerHealth()
hx, hy := w/2-91*s, hgt-30*s
for i := 0; i < hearts; i++ {
color := [4]float32{0.2, 0.2, 0.2, 0.6} // 空
if i < hp/2 {
color = [4]float32{1, 0.1, 0.1, 1} // 满
}
r.DrawRect(hx+float32(i%10)*8*s, hy+float32(i/10)*9*s, 7*s, 7*s, color)
}
// 饥饿:10 鸡腿(右侧偏移)
for i := 0; i < 10; i++ {
r.DrawRect(w/2-91*s+100*s+float32(i%10)*8*s, hy+float32(i/10)*9*s, 7*s, 7*s, [4]float32{0.6, 0.4, 0.15, 1})
}
// 破坏进度(方块交互与动画.md §4:HUD 中央进度)
if prog := h.sess.BreakProgress(); prog > 0 {
r.DrawRect(cx-40*s, cy+20*s, 80*s, 4*s, [4]float32{0, 0, 0, 0.7})
r.DrawRect(cx-40*s, cy+20*s, 80*s*float32(prog), 4*s, [4]float32{1, 1, 1, 0.9})
}
// 坐标调试(后置移除)
r.DrawText(w/2, hgt-40*s, fmt.Sprintf("XYZ %.1f / %.1f / %.1f", h.sess.Player.Pos[0], h.sess.Player.Pos[1], h.sess.Player.Pos[2]), 1*s, white)
}
// drawItemIcon 在槽位绘制物品图标 + 数量。
func (h *HUD) drawItemIcon(r *Renderer, stack item.Stack, x, y float32) {
s := h.scale
if stack.Empty() {
return
}
def, ok := h.items.Get(stack.Name)
if !ok {
return
}
if uv, found := h.itemUV[def.Texture]; found && h.itemTex != 0 {
r.pushQuad(quad{x: x + 2*s, y: y + 2*s, w: 16 * s, h: 16 * s,
u0: uv[0], v0: uv[1], u1: uv[2], v1: uv[3],
r: 1, g: 1, b: 1, a: 1, tex: h.itemTex})
}
if stack.Count > 1 {
r.DrawText(x+10*s, y+8*s, fmt.Sprintf("%d", stack.Count), 1*s, [4]float32{1, 1, 1, 1})
}
}