- 植被染色(渲染.md §4.1):blocks.json 增 tint 标记,图集构建后按 colormap/grass|foliage 中心色乘色——原版材质包草顶/树叶为灰度图,不染色全灰 - 破坏裂纹(方块交互与动画.md §4):destroy_stage_0..9 裂纹盒叠加破坏目标方块 - 第一人称手持方块(渲染.md §5.2):选中槽放置方块六面 UV 视图空间立方体 - 实体广告牌(渲染.md §5.3):僵尸/村民/动物按贴图渲染,动物按 ID 轮换外观 - HUD 换原版精灵:crosshair/hotbar/hotbar_selection/heart/food(UI还原.md §3.4) - 主菜单标题/按钮标签按请求字号光栅化(修复 24px 固定字号);版本角标 CJK - 恢复背面剔除(相机修复后 '脚下黑块' 误诊解除);ESC 光标请求原子化防竞态 - dev 测试参数:-give/-crackstage/-spawntest/-esctest
146 lines
4.8 KiB
Go
146 lines
4.8 KiB
Go
//go:build gl
|
||
|
||
// 界面屏幕:主菜单与暂停菜单(UI还原.md §2:布局/控件/版本角标复刻原版)。
|
||
package ui
|
||
|
||
// Button 一个按钮:区域 + 悬停高亮 + 标签纹理。
|
||
type Button struct {
|
||
X, Y, W, H float32
|
||
Tex uint32 // 标签纹理(预光栅化中文)
|
||
LabelW float32
|
||
LabelH float32
|
||
Action string
|
||
}
|
||
|
||
// TitleScreen 主菜单(UI还原.md §2:土质背景 + 标题 + 原版按钮 + 版本/版权角标)。
|
||
type TitleScreen struct {
|
||
TitleTex uint32
|
||
TitleW float32
|
||
TitleH float32
|
||
|
||
SplashTex uint32 // 黄色 splash 文本(原版风格)
|
||
SplashW float32
|
||
SplashH float32
|
||
|
||
BgTex uint32 // 土质背景(16px dirt 平铺预渲染)
|
||
BtnTex uint32 // 原版按钮控件(widget/button.png 200×20)
|
||
BtnHover uint32 // 悬停高亮(button_highlighted.png)
|
||
Buttons []Button
|
||
|
||
Version string // 左下角版本(原版角标)
|
||
Copyright string // 右下角版权
|
||
VersionTex uint32 // 版本/版权 CJK 光栅纹理(中文字形)
|
||
VersionW float32
|
||
VersionH float32
|
||
CopyrightTex uint32
|
||
CopyrightW float32
|
||
CopyrightH float32
|
||
}
|
||
|
||
// HitTest 命中检测(按钮区域)。
|
||
func (s *TitleScreen) HitTest(mx, my float32) string {
|
||
for _, b := range s.Buttons {
|
||
if mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H {
|
||
return b.Action
|
||
}
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// Draw 绘制主菜单(原版布局:背景 → 标题+splash → 按钮列 → 版本/版权角标)。
|
||
func (s *TitleScreen) Draw(r *Renderer, mx, my float32) {
|
||
w, h := float32(r.w), float32(r.h)
|
||
// 土质背景(UI还原.md §2 背景)
|
||
if s.BgTex != 0 {
|
||
r.pushQuad(quad{x: 0, y: 0, w: w, h: h, u0: 0, v0: 0, u1: 1, v1: 1,
|
||
r: 1, g: 1, b: 1, a: 1, tex: s.BgTex})
|
||
} else {
|
||
r.DrawRect(0, 0, w, h, [4]float32{0.08, 0.1, 0.12, 1})
|
||
}
|
||
// 标题(居中偏上,原版 logo 位置)
|
||
if s.TitleTex != 0 {
|
||
r.pushQuad(quad{x: w/2 - s.TitleW/2, y: h * 0.15, w: s.TitleW, h: s.TitleH,
|
||
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.TitleTex})
|
||
}
|
||
// splash(标题右下,原版黄色倾斜文本——MVP 水平放置)
|
||
if s.SplashTex != 0 {
|
||
r.pushQuad(quad{x: w/2 + s.TitleW/2 - s.SplashW + 20, y: h*0.15 + s.TitleH, w: s.SplashW, h: s.SplashH,
|
||
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.SplashTex})
|
||
}
|
||
// 按钮列(原版:居中偏下,200×20 控件 + 标签)
|
||
for _, b := range s.Buttons {
|
||
hover := mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H
|
||
tex := s.BtnTex
|
||
if hover && s.BtnHover != 0 {
|
||
tex = s.BtnHover
|
||
}
|
||
if tex != 0 {
|
||
r.pushQuad(quad{x: b.X, y: b.Y, w: b.W, h: b.H, u0: 0, v0: 0, u1: 1, v1: 1,
|
||
r: 1, g: 1, b: 1, a: 1, tex: tex})
|
||
}
|
||
if b.Tex != 0 {
|
||
// 标签居中
|
||
r.pushQuad(quad{x: b.X + (b.W-b.LabelW)/2, y: b.Y + (b.H-b.LabelH)/2, w: b.LabelW, h: b.LabelH,
|
||
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: b.Tex})
|
||
}
|
||
}
|
||
// 版本角标(左下)与版权(右下,原版样式;中文用预光栅纹理)
|
||
if s.VersionTex != 0 {
|
||
r.pushQuad(quad{x: 4, y: h - s.VersionH - 4, w: s.VersionW, h: s.VersionH,
|
||
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.VersionTex})
|
||
} else if s.Version != "" {
|
||
r.DrawText(8, h-12, s.Version, 1, [4]float32{1, 1, 1, 1})
|
||
}
|
||
if s.CopyrightTex != 0 {
|
||
r.pushQuad(quad{x: w - s.CopyrightW - 4, y: h - s.CopyrightH - 4, w: s.CopyrightW, h: s.CopyrightH,
|
||
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.CopyrightTex})
|
||
} else if s.Copyright != "" {
|
||
r.DrawText(w-r.TextWidth(s.Copyright, 1)-8, h-12, s.Copyright, 1, [4]float32{1, 1, 1, 1})
|
||
}
|
||
}
|
||
|
||
// PauseScreen 暂停菜单(UI还原.md §2:半透明遮罩 + 原版按钮)。
|
||
type PauseScreen struct {
|
||
TitleTex uint32
|
||
TitleW float32
|
||
TitleH float32
|
||
BtnTex uint32
|
||
BtnHover uint32
|
||
Buttons []Button
|
||
}
|
||
|
||
// HitTest 命中检测。
|
||
func (s *PauseScreen) HitTest(mx, my float32) string {
|
||
for _, b := range s.Buttons {
|
||
if mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H {
|
||
return b.Action
|
||
}
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// Draw 绘制暂停菜单。
|
||
func (s *PauseScreen) Draw(r *Renderer, mx, my float32) {
|
||
w, h := float32(r.w), float32(r.h)
|
||
r.DrawRect(0, 0, w, h, [4]float32{0, 0, 0, 0.6})
|
||
if s.TitleTex != 0 {
|
||
r.pushQuad(quad{x: w/2 - s.TitleW/2, y: h * 0.15, w: s.TitleW, h: s.TitleH,
|
||
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: s.TitleTex})
|
||
}
|
||
for _, b := range s.Buttons {
|
||
hover := mx >= b.X && mx <= b.X+b.W && my >= b.Y && my <= b.Y+b.H
|
||
tex := s.BtnTex
|
||
if hover && s.BtnHover != 0 {
|
||
tex = s.BtnHover
|
||
}
|
||
if tex != 0 {
|
||
r.pushQuad(quad{x: b.X, y: b.Y, w: b.W, h: b.H, u0: 0, v0: 0, u1: 1, v1: 1,
|
||
r: 1, g: 1, b: 1, a: 1, tex: tex})
|
||
}
|
||
if b.Tex != 0 {
|
||
r.pushQuad(quad{x: b.X + (b.W-b.LabelW)/2, y: b.Y + (b.H-b.LabelH)/2, w: b.LabelW, h: b.LabelH,
|
||
u0: 0, v0: 0, u1: 1, v1: 1, r: 1, g: 1, b: 1, a: 1, tex: b.Tex})
|
||
}
|
||
}
|
||
}
|