Files
ngzz-mc/internal/ui/inventory.go
2026-09-19 14:21:08 +08:00

156 lines
4.7 KiB
Go
Raw Permalink 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.
//go:build gl
// 物品栏界面(UI还原.md §3.3、物品与背包.md §3):
// inventory.png 176×166 + 27 主区 (7,83) + 热键栏 (7,141) + 盔甲 4 (7,8)
// + 副手 (77,8) + 2×2 (98,8) + 输出 (125,24) + 玩家模型 (34,9)–(69,80)。
package ui
import (
"fmt"
"mc/internal/item"
)
// InventoryScreen 物品栏界面。
type InventoryScreen struct {
itemIcons
bgTex uint32
winW, winH float32
tipTex uint32
tipW, tipH float32
OpenT float32
}
// NewInventoryScreen 创建物品栏界面。
func NewInventoryScreen(bgTex uint32, items *item.Registry) *InventoryScreen {
s := &InventoryScreen{bgTex: bgTex, winW: 176, winH: 166, OpenT: 1}
s.items = items
s.itemUV = map[string][4]float32{}
s.blockUV = map[string][4]float32{}
return s
}
// SetItemAtlas 设置 items 图集。
func (s *InventoryScreen) SetItemAtlas(tex uint32, uv map[string][4]float32) {
s.setItemAtlas(tex, uv)
}
// SetBlockAtlas 设置 blocks 图集。
func (s *InventoryScreen) SetBlockAtlas(tex uint32, uv map[string][4]float32) {
s.setBlockAtlas(tex, uv)
}
// SetTooltip 设置悬停名称纹理(tick 光栅化 CJK,渲染线程上传)。
func (s *InventoryScreen) SetTooltip(tex uint32, w, h float32) {
s.tipTex = tex
s.tipW, s.tipH = w, h
}
// HitTest 命中:main0–26 / hot0–8 / armor0–3 / offhand / craft0–3 / result。
func (s *InventoryScreen) HitTest(mx, my float32, screenW, screenH, scale float32) string {
bx, by := screenW/2-s.winW/2*scale, screenH/2-s.winH/2*scale
for i := 0; i < 4; i++ {
if hitSlot(mx, my, bx, by, 7, 8+float32(i)*18, scale) {
return "armor" + fmt.Sprintf("%d", i)
}
}
if hitSlot(mx, my, bx, by, 77, 8, scale) {
return "offhand"
}
for i := 0; i < 4; i++ {
px := 98 + float32(i%2)*18
py := 8 + float32(i/2)*18
if hitSlot(mx, my, bx, by, px, py, scale) {
return "craft" + fmt.Sprintf("%d", i)
}
}
if hitSlot(mx, my, bx, by, 125, 24, scale) {
return "result"
}
for i := 0; i < 27; i++ {
px := 7 + float32(i%9)*18
py := 83 + float32(i/9)*18
if hitSlot(mx, my, bx, by, px, py, scale) {
return "main" + fmt.Sprintf("%d", i)
}
}
for i := 0; i < 9; i++ {
if hitSlot(mx, my, bx, by, 7+float32(i)*18, 141, scale) {
return "hot" + fmt.Sprintf("%d", i)
}
}
return ""
}
// Draw 绘制物品栏(slots 含盔甲/副手;craft 为 2×2;res 为输出)。
func (s *InventoryScreen) Draw(r *Renderer, slots []item.Stack, craft [4]item.Stack, res item.Stack, hasRes bool, held item.Stack, mx, my, scale float32) {
w, h := float32(r.w), float32(r.h)
r.DrawRect(0, 0, w, h, [4]float32{0, 0, 0, 0.35})
sc := scale * (0.85 + 0.15*s.OpenT)
bx, by := w/2-s.winW/2*sc, h/2-s.winH/2*sc
if s.bgTex != 0 {
r.pushQuad(quad{x: bx, y: by, w: s.winW * sc, h: s.winH * sc,
u0: 0, v0: 0, u1: 176.0 / 256.0, v1: 166.0 / 256.0,
r: 1, g: 1, b: 1, a: 1, tex: s.bgTex})
}
s.drawPlayerModel(r, bx, by, sc)
// 盔甲 4
for i := 0; i < 4; i++ {
idx := 36 + i
st := item.Stack{}
if idx < len(slots) {
st = slots[idx]
}
s.drawSlot(r, st, bx+(7+1)*sc, by+(8+float32(i)*18+1)*sc, sc)
}
if 40 < len(slots) {
s.drawSlot(r, slots[40], bx+(77+1)*sc, by+(8+1)*sc, sc)
}
for i := 0; i < 4; i++ {
px := 98 + float32(i%2)*18
py := 8 + float32(i/2)*18
s.drawSlot(r, craft[i], bx+(px+1)*sc, by+(py+1)*sc, sc)
}
if hasRes {
s.drawSlot(r, res, bx+(125+1)*sc, by+(24+1)*sc, sc)
}
for i := 0; i < 27; i++ {
idx := 9 + i
st := item.Stack{}
if idx < len(slots) {
st = slots[idx]
}
px := 7 + float32(i%9)*18
py := 83 + float32(i/9)*18
s.drawSlot(r, st, bx+(px+1)*sc, by+(py+1)*sc, sc)
}
for i := 0; i < 9 && i < len(slots); i++ {
s.drawSlot(r, slots[i], bx+(7+float32(i)*18+1)*sc, by+(141+1)*sc, sc)
}
if !held.Empty() {
s.drawSlot(r, held, mx-8*scale, my-8*scale, scale)
}
if s.tipTex != 0 && s.tipW > 0 {
tx, ty := mx+12, my+12
drawTooltipBG(r, tx, ty, s.tipW, s.tipH)
r.pushQuad(quad{x: tx, y: ty, w: s.tipW, h: s.tipH,
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.tipTex})
}
}
func (s *InventoryScreen) drawPlayerModel(r *Renderer, bx, by, scale float32) {
// 简化纸片人,区域 (34,9)–(69,80)
ox, oy := bx+34*scale, by+9*scale
skin := [4]float32{0.85, 0.68, 0.52, 1}
shirt := [4]float32{0.25, 0.35, 0.75, 1}
pants := [4]float32{0.22, 0.22, 0.55, 1}
// 头 8×8、身 8×12、臂 4×12、腿 4×12(逻辑像素)
r.DrawRect(ox+10*scale, oy+2*scale, 8*scale, 8*scale, skin)
r.DrawRect(ox+10*scale, oy+10*scale, 8*scale, 12*scale, shirt)
r.DrawRect(ox+6*scale, oy+10*scale, 4*scale, 12*scale, shirt)
r.DrawRect(ox+18*scale, oy+10*scale, 4*scale, 12*scale, shirt)
r.DrawRect(ox+10*scale, oy+22*scale, 4*scale, 12*scale, pants)
r.DrawRect(ox+14*scale, oy+22*scale, 4*scale, 12*scale, pants)
}