Files
ngzz-mc/cmd/client/screens_gl.go

153 lines
4.8 KiB
Go
Raw Normal View History

//go:build gl
// 界面屏幕构建:原版复刻(UI还原.md §2)——土质背景、原版按钮控件、
// 中文标题「年糕历险记」、黄色 splash、版本/版权角标。
package main
import (
"image"
"image/color"
"image/draw"
"image/png"
"os"
"path/filepath"
"mc/internal/assets"
"mc/internal/cjkfont"
"mc/internal/ui"
)
// cjkPaths 系统 CJK 字体候选。
var cjkPaths = []string{
filepath.Join(os.Getenv("WINDIR"), "Fonts", "msyh.ttc"),
filepath.Join(os.Getenv("WINDIR"), "Fonts", "msyh.ttf"),
}
// loadCJK 加载系统 CJK 字体(失败返回 nil → 中文标签退化不显示)。
// 按字号缓存:不同字号(标题 96 / splash 24 / 按钮 18)需要各自的光栅化实例。
var cjkCache = map[float64]*cjkfont.Renderer{}
func loadCJK(size float64) *cjkfont.Renderer {
if r, ok := cjkCache[size]; ok {
return r
}
for _, p := range cjkPaths {
if _, err := os.Stat(p); err == nil {
if r, err := cjkfont.Load(p, size); err == nil {
cjkCache[size] = r
return r
}
}
}
return nil
}
// buildScreens 构建主菜单与暂停菜单(原版布局与控件)。
func buildScreens(r *ui.Renderer, am *assets.Manager, lang *assets.Lang) (*ui.TitleScreen, *ui.PauseScreen) {
// 原版按钮控件(widget/button.png 200×20 与悬停高亮)
loadPng := func(key string) (uint32, bool) {
p, ok := am.ResolveTexture(key)
if !ok {
return 0, false
}
f, err := os.Open(p)
if err != nil {
return 0, false
}
img, err := png.Decode(f)
_ = f.Close()
if err != nil {
return 0, false
}
return r.UploadImage(img), true
}
btnTex, _ := loadPng("gui/sprites/widget/button")
btnHover, _ := loadPng("gui/sprites/widget/button_highlighted")
// 土质背景:16px dirt 平铺 1280×720(原版菜单背景质感)
bgTex := tileBackground(r, am)
// 中文文本光栅化(按请求字号取缓存渲染器)
text := func(s string, size float64, c color.RGBA) (uint32, float32, float32) {
cjk := loadCJK(size)
if cjk == nil || s == "" {
return 0, 0, 0
}
img := cjk.Draw(s, c)
return r.UploadImage(img), float32(img.Bounds().Dx()), float32(img.Bounds().Dy())
}
tr := func(key, fallback string, size float64, c color.RGBA) (uint32, float32, float32) {
s := fallback
if lang != nil && lang.Get(key) != key {
s = lang.Get(key)
}
return text(s, size, c)
}
// 标题与 splash(原版:大标题 + 黄色 splash 于其右下)
titleTex, tw, th := tr("menu.title", "年糕历险记", 96, color.RGBA{255, 255, 255, 255})
splashTex, sw, sh := tr("menu.splash", "牛气冲天!", 24, color.RGBA{255, 255, 85, 255})
// 按钮(原版 200×20、间距 24、居中,UI还原.md §2 布局)
mkBtn := func(key, fallback, action string, y float32) ui.Button {
tex, lw, lh := tr(key, fallback, 18, color.RGBA{255, 255, 255, 255})
return ui.Button{X: 1280/2 - 100, Y: y, W: 200, H: 20, Tex: tex, LabelW: lw, LabelH: lh, Action: action}
}
title := &ui.TitleScreen{
TitleTex: titleTex, TitleW: tw, TitleH: th,
SplashTex: splashTex, SplashW: sw, SplashH: sh,
BgTex: bgTex, BtnTex: btnTex, BtnHover: btnHover,
Version: "年糕历险记 v0.1.0",
Copyright: "Copyright NianGao Studio. 请勿分发!",
}
// 版本/版权角标:中文走 CJK 光栅(ASCII 位图字体缺中文字形)
if vTex, vw, vh := tr("menu.version", "年糕历险记 v0.1.0", 12, color.RGBA{220, 220, 220, 255}); vTex != 0 {
title.VersionTex, title.VersionW, title.VersionH = vTex, vw, vh
}
if cTex, cw, ch := text("Copyright NianGao Studio. 请勿分发!", 12, color.RGBA{220, 220, 220, 255}); cTex != 0 {
title.CopyrightTex, title.CopyrightW, title.CopyrightH = cTex, cw, ch
}
title.Buttons = []ui.Button{
mkBtn("menu.singleplayer", "单人游戏", "play", 390),
mkBtn("menu.multiplayer", "多人游戏", "multi", 414),
mkBtn("menu.options", "设置…", "", 438),
mkBtn("menu.quit", "退出游戏", "quit", 462),
}
pauseTitle, ptw, pth := tr("menu.game", "游戏菜单", 32, color.RGBA{255, 255, 255, 255})
pause := &ui.PauseScreen{
TitleTex: pauseTitle, TitleW: ptw, TitleH: pth,
BtnTex: btnTex, BtnHover: btnHover,
}
pause.Buttons = []ui.Button{
mkBtn("gui.resume", "继续游戏", "resume", 300),
mkBtn("menu.quit", "保存并退出", "quit", 324),
}
return title, pause
}
// tileBackground 把 dirt 纹理平铺成全屏背景(像素风土墙)。
func tileBackground(r *ui.Renderer, am *assets.Manager) uint32 {
p, ok := am.ResolveTexture("block/dirt")
if !ok {
return 0
}
f, err := os.Open(p)
if err != nil {
return 0
}
tile, err := png.Decode(f)
_ = f.Close()
if err != nil {
return 0
}
const w, h = 1280, 720
bg := image.NewRGBA(image.Rect(0, 0, w, h))
for y := 0; y < h; y += 16 {
for x := 0; x < w; x += 16 {
draw.Draw(bg, image.Rect(x, y, x+16, y+16), tile, image.Point{}, draw.Src)
}
}
return r.UploadImage(bg)
}