//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 → 中文标签退化不显示)。 func loadCJK() *cjkfont.Renderer { for _, p := range cjkPaths { if _, err := os.Stat(p); err == nil { if r, err := cjkfont.Load(p, 24); err == nil { return r } } } return nil } // buildScreens 构建主菜单与暂停菜单(原版布局与控件)。 func buildScreens(r *ui.Renderer, am *assets.Manager, lang *assets.Lang) (*ui.TitleScreen, *ui.PauseScreen) { cjk := loadCJK() // 原版按钮控件(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) { 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. 请勿分发!", } 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) }