156 lines
4.7 KiB
Go
156 lines
4.7 KiB
Go
//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)
|
||
}
|