修复了一些bug
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,6 @@
|
||||
//go:build gl
|
||||
|
||||
// 实体可视映射:实体类型 → 贴图键/身高/动物模型(渲染.md §5.3)。
|
||||
// 动物用方块组合模型(身体+头+四腿)近似原版外观;僵尸/村民用交叉广告牌;
|
||||
// 掉落物用物品图标小广告牌。
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -11,8 +9,45 @@ import (
|
||||
"mc/internal/render"
|
||||
)
|
||||
|
||||
// entitySkinKeys 实体皮肤候选(temperate 优先,回退短名)。
|
||||
var entitySkinKeys = []string{
|
||||
"entity/player/wide/steve",
|
||||
"entity/steve",
|
||||
"entity/zombie/zombie",
|
||||
"entity/villager/villager",
|
||||
"entity/cow/cow_temperate",
|
||||
"entity/cow/cow",
|
||||
"entity/pig/pig_temperate",
|
||||
"entity/pig/pig",
|
||||
"entity/sheep/sheep_temperate",
|
||||
"entity/sheep/sheep",
|
||||
"entity/sheep/sheep_fur",
|
||||
"entity/sheep/sheep_wool",
|
||||
"entity/chicken/chicken_temperate",
|
||||
"entity/chicken/chicken",
|
||||
}
|
||||
|
||||
// requiredEntitySkinGroups 必选皮肤候选组:组内任一键命中即可;全缺才品红占位。
|
||||
var requiredEntitySkinGroups = [][]string{
|
||||
{"entity/zombie/zombie"},
|
||||
{"entity/villager/villager"},
|
||||
{"entity/cow/cow_temperate", "entity/cow/cow"},
|
||||
{"entity/pig/pig_temperate", "entity/pig/pig"},
|
||||
{"entity/sheep/sheep_temperate", "entity/sheep/sheep"},
|
||||
{"entity/chicken/chicken_temperate", "entity/chicken/chicken"},
|
||||
}
|
||||
|
||||
func firstUV(r *render.Renderer, keys ...string) ([4]float32, bool) {
|
||||
for _, k := range keys {
|
||||
if uv, ok := r.EntityUV(k); ok {
|
||||
return uv, true
|
||||
}
|
||||
}
|
||||
return [4]float32{}, false
|
||||
}
|
||||
|
||||
// entityVisual 返回实体的贴图键与身高(米)。
|
||||
// 动物额外返回外观变体(0 牛 / 1 猪 / 2 羊 / 3 鸡),供方块模型选择。
|
||||
// 动物额外返回外观变体(0 牛 / 1 猪 / 2 羊 / 3 鸡)。
|
||||
func entityVisual(e *entity.Entity, items *item.Registry) (key string, height float32, variant int) {
|
||||
switch e.Kind {
|
||||
case entity.KindZombie:
|
||||
@@ -20,7 +55,7 @@ func entityVisual(e *entity.Entity, items *item.Registry) (key string, height fl
|
||||
case entity.KindVillager:
|
||||
return "entity/villager/villager", 1.95, -1
|
||||
case entity.KindAnimal:
|
||||
switch e.ID % 4 {
|
||||
switch entity.AnimalSpecies(e) {
|
||||
case 0:
|
||||
return "entity/cow/cow_temperate", 1.4, 0
|
||||
case 1:
|
||||
@@ -45,82 +80,15 @@ func entityVisual(e *entity.Entity, items *item.Registry) (key string, height fl
|
||||
}
|
||||
}
|
||||
|
||||
// rect 构造图集 tile 内归一化子矩形。
|
||||
func rect(u0, v0, u1, v1 float32) [4]float32 { return [4]float32{u0, v0, u1, v1} }
|
||||
|
||||
// boxPart 构造模型部件:六面统一用 side 矩形,前/后取中间条带,顶取上条带,底取下条带。
|
||||
func boxPart(cx, cy, cz, w, h, d float32, side [4]float32) render.AnimalPart {
|
||||
u0, v0, u1, v1 := side[0], side[1], side[2], side[3]
|
||||
uw, vh := u1-u0, v1-v0
|
||||
front := rect(u0+uw*0.35, v0, u0+uw*0.65, v1) // 前/后面:中间三分之一
|
||||
top := rect(u0, v0, u1, v0+vh*0.3)
|
||||
bottom := rect(u0, v1-vh*0.3, u1, v1)
|
||||
var uv [6][4]float32
|
||||
// 顺序同 cubeFaces:顶/底/东/西/南(前)/北(后)
|
||||
uv[0], uv[1] = top, bottom
|
||||
uv[2], uv[3] = side, side
|
||||
uv[4], uv[5] = front, front
|
||||
return render.AnimalPart{CX: cx, CY: cy, CZ: cz, W: w, H: h, D: d, UV: uv}
|
||||
}
|
||||
|
||||
// animalModel 返回动物方块模型(variant:0 牛 / 1 猪 / 2 羊 / 3 鸡)。
|
||||
// 贴图子矩形按原版皮肤布局:头部在纹理左侧、身体在右侧、腿在下部。
|
||||
// 图集 tile 内纹理被等比拉伸成 64×64(64×32 原图纵向拉长一倍,矩形已按拉伸后比例折算)。
|
||||
func animalModel(variant int) render.AnimalModel {
|
||||
func animalSkinKeys(variant int) []string {
|
||||
switch variant {
|
||||
case 0: // 牛:纹理 64×64,内容在上半(v 0–0.5)
|
||||
head := rect(0.02, 0.02, 0.48, 0.48)
|
||||
body := rect(0.52, 0.02, 0.98, 0.48)
|
||||
legs := rect(0.55, 0.30, 0.98, 0.50)
|
||||
var m render.AnimalModel
|
||||
m.Parts = append(m.Parts,
|
||||
boxPart(0, 0.72, 0, 0.92, 0.62, 1.28, body),
|
||||
boxPart(0, 1.26, 0.60, 0.56, 0.50, 0.56, head),
|
||||
boxPart(-0.34, 0.24, 0.50, 0.22, 0.48, 0.22, legs),
|
||||
boxPart(0.34, 0.24, 0.50, 0.22, 0.48, 0.22, legs),
|
||||
boxPart(-0.34, 0.24, -0.50, 0.22, 0.48, 0.22, legs),
|
||||
boxPart(0.34, 0.24, -0.50, 0.22, 0.48, 0.22, legs),
|
||||
)
|
||||
return m
|
||||
case 1: // 猪:纹理 64×64,内容在上半
|
||||
head := rect(0.14, 0.03, 0.48, 0.45)
|
||||
body := rect(0.52, 0.03, 0.98, 0.45)
|
||||
legs := rect(0.60, 0.30, 0.98, 0.50)
|
||||
var m render.AnimalModel
|
||||
m.Parts = append(m.Parts,
|
||||
boxPart(0, 0.50, 0, 0.76, 0.44, 1.02, body),
|
||||
boxPart(0, 0.86, 0.50, 0.50, 0.42, 0.50, head),
|
||||
boxPart(-0.28, 0.15, 0.42, 0.18, 0.30, 0.18, legs),
|
||||
boxPart(0.28, 0.15, 0.42, 0.18, 0.30, 0.18, legs),
|
||||
boxPart(-0.28, 0.15, -0.42, 0.18, 0.30, 0.18, legs),
|
||||
boxPart(0.28, 0.15, -0.42, 0.18, 0.30, 0.18, legs),
|
||||
)
|
||||
return m
|
||||
case 2: // 羊:纹理 64×32(tile 内 v 拉长一倍)
|
||||
head := rect(0.02, 0.02, 0.26, 0.40)
|
||||
body := rect(0.50, 0.15, 1.00, 0.72)
|
||||
legs := rect(0.62, 0.50, 0.95, 0.85)
|
||||
var m render.AnimalModel
|
||||
m.Parts = append(m.Parts,
|
||||
boxPart(0, 0.72, 0, 0.92, 0.60, 1.26, body),
|
||||
boxPart(0, 1.20, 0.60, 0.50, 0.46, 0.50, head),
|
||||
boxPart(-0.33, 0.21, 0.50, 0.20, 0.42, 0.20, legs),
|
||||
boxPart(0.33, 0.21, 0.50, 0.20, 0.42, 0.20, legs),
|
||||
boxPart(-0.33, 0.21, -0.50, 0.20, 0.42, 0.20, legs),
|
||||
boxPart(0.33, 0.21, -0.50, 0.20, 0.42, 0.20, legs),
|
||||
)
|
||||
return m
|
||||
default: // 鸡:纹理 64×32
|
||||
head := rect(0.02, 0.05, 0.28, 0.55)
|
||||
body := rect(0.28, 0.10, 0.70, 0.75)
|
||||
legs := rect(0.30, 0.55, 0.60, 0.80)
|
||||
var m render.AnimalModel
|
||||
m.Parts = append(m.Parts,
|
||||
boxPart(0, 0.36, 0, 0.44, 0.40, 0.52, body),
|
||||
boxPart(0, 0.66, 0.24, 0.32, 0.30, 0.32, head),
|
||||
boxPart(-0.14, 0.10, 0.18, 0.09, 0.20, 0.09, legs),
|
||||
boxPart(0.14, 0.10, 0.18, 0.09, 0.20, 0.09, legs),
|
||||
)
|
||||
return m
|
||||
case 0:
|
||||
return []string{"entity/cow/cow_temperate", "entity/cow/cow"}
|
||||
case 1:
|
||||
return []string{"entity/pig/pig_temperate", "entity/pig/pig"}
|
||||
case 2:
|
||||
return []string{"entity/sheep/sheep_temperate", "entity/sheep/sheep"}
|
||||
default:
|
||||
return []string{"entity/chicken/chicken_temperate", "entity/chicken/chicken"}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ type inputState struct {
|
||||
breakDown bool
|
||||
breakHeld bool
|
||||
placeDown bool
|
||||
placeHeld bool
|
||||
selectSlot int // -1 无
|
||||
toggleInv bool // E 打开/关闭物品栏
|
||||
dropItem bool // Q 丢弃选中槽 1 个物品
|
||||
@@ -39,7 +40,13 @@ type inputState struct {
|
||||
cursorX, cursorY float64
|
||||
clickX, clickY float32
|
||||
clicked bool
|
||||
clickedRight bool
|
||||
escPressed bool
|
||||
toggleFS bool // F11 / Alt+Enter(渲染线程消费,渲染.md §5.4)
|
||||
|
||||
textMode bool
|
||||
chars []rune
|
||||
backspace bool
|
||||
}
|
||||
|
||||
// newInputState 创建输入状态并注册回调。
|
||||
@@ -49,6 +56,7 @@ func newInputState(win *glfw.Window) *inputState {
|
||||
win.SetMouseButtonCallback(in.mouseButtonCallback)
|
||||
win.SetCursorPosCallback(in.cursorCallback)
|
||||
win.SetScrollCallback(in.scrollCallback)
|
||||
win.SetCharCallback(in.charCallback)
|
||||
return in
|
||||
}
|
||||
|
||||
@@ -63,7 +71,7 @@ func (in *inputState) SetSensitivity(s float64) {
|
||||
func (in *inputState) Sensitivity() float64 { return in.sens }
|
||||
|
||||
// keyCallback 键盘回调(动作映射:WASD/空格/Shift/Ctrl/Esc)。
|
||||
func (in *inputState) keyCallback(_ *glfw.Window, key glfw.Key, _ int, action glfw.Action, _ glfw.ModifierKey) {
|
||||
func (in *inputState) keyCallback(_ *glfw.Window, key glfw.Key, _ int, action glfw.Action, mods glfw.ModifierKey) {
|
||||
down := action != glfw.Release
|
||||
switch key {
|
||||
case glfw.KeyW:
|
||||
@@ -84,6 +92,10 @@ func (in *inputState) keyCallback(_ *glfw.Window, key glfw.Key, _ int, action gl
|
||||
if action == glfw.Press {
|
||||
in.escPressed = true // 暂停/继续由屏幕状态机处理
|
||||
}
|
||||
case glfw.KeyBackspace:
|
||||
if action != glfw.Release && in.textMode {
|
||||
in.backspace = true
|
||||
}
|
||||
case glfw.Key1, glfw.Key2, glfw.Key3, glfw.Key4, glfw.Key5, glfw.Key6, glfw.Key7, glfw.Key8, glfw.Key9:
|
||||
if action == glfw.Press {
|
||||
in.selectSlot = int(key) - int(glfw.Key1)
|
||||
@@ -96,9 +108,24 @@ func (in *inputState) keyCallback(_ *glfw.Window, key glfw.Key, _ int, action gl
|
||||
if action == glfw.Press {
|
||||
in.dropItem = true // 丢弃物品(UI与输入.md §5)
|
||||
}
|
||||
case glfw.KeyF11:
|
||||
if action == glfw.Press {
|
||||
in.toggleFS = true
|
||||
}
|
||||
case glfw.KeyEnter, glfw.KeyKPEnter:
|
||||
if action == glfw.Press && mods&glfw.ModAlt != 0 {
|
||||
in.toggleFS = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (in *inputState) charCallback(_ *glfw.Window, char rune) {
|
||||
if !in.textMode {
|
||||
return
|
||||
}
|
||||
in.chars = append(in.chars, char)
|
||||
}
|
||||
|
||||
// mouseButtonCallback 鼠标按键:左键破坏(按住累积)、右键放置(UI与输入.md §6 长按)。
|
||||
func (in *inputState) mouseButtonCallback(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, _ glfw.ModifierKey) {
|
||||
switch button {
|
||||
@@ -113,7 +140,11 @@ func (in *inputState) mouseButtonCallback(_ *glfw.Window, button glfw.MouseButto
|
||||
case glfw.MouseButtonRight:
|
||||
if action == glfw.Press {
|
||||
in.placeDown = true
|
||||
in.clickedRight = true
|
||||
in.clickX = float32(in.cursorX)
|
||||
in.clickY = float32(in.cursorY)
|
||||
}
|
||||
in.placeHeld = action != glfw.Release
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,10 +215,15 @@ func (in *inputState) apply(s *game.Session) {
|
||||
wish[1] -= r[2]
|
||||
}
|
||||
speed := 4.3
|
||||
if in.sprint {
|
||||
if p.InWater {
|
||||
speed = 2.2
|
||||
if in.sprint && in.forward {
|
||||
speed = 5.4
|
||||
}
|
||||
} else if in.sprint {
|
||||
speed *= 1.3
|
||||
}
|
||||
if in.sneak {
|
||||
if in.sneak && !p.InWater {
|
||||
speed *= 0.3
|
||||
}
|
||||
l := math.Sqrt(wish[0]*wish[0] + wish[1]*wish[1])
|
||||
@@ -199,17 +235,25 @@ func (in *inputState) apply(s *game.Session) {
|
||||
p.Vel[2] = 0
|
||||
}
|
||||
|
||||
// 跳跃
|
||||
if in.jump && p.OnGround {
|
||||
p.Jump()
|
||||
// 跳跃 / 游泳(物理与碰撞.md §4:水中空格上浮,不要求落地)
|
||||
if in.jump {
|
||||
if p.InWater {
|
||||
p.SwimUp()
|
||||
} else if p.OnGround {
|
||||
p.Jump()
|
||||
}
|
||||
}
|
||||
if in.sneak && p.InWater {
|
||||
p.SwimDown()
|
||||
}
|
||||
p.SetPose(s.World, in.sneak && !p.InWater, in.sprint && in.forward && p.InWater)
|
||||
|
||||
// 热键栏切换
|
||||
// 热键栏切换(UI与输入.md §6:上滚槽位−1,下滚+1,对齐原版 Java)
|
||||
switch in.selectSlot {
|
||||
case -2:
|
||||
s.Inv.Selected = (s.Inv.Selected + 1) % 9
|
||||
case -3:
|
||||
s.Inv.Selected = (s.Inv.Selected + 8) % 9
|
||||
case -3:
|
||||
s.Inv.Selected = (s.Inv.Selected + 1) % 9
|
||||
default:
|
||||
if in.selectSlot >= 0 && in.selectSlot < 9 {
|
||||
s.Inv.Selected = in.selectSlot
|
||||
@@ -217,20 +261,28 @@ func (in *inputState) apply(s *game.Session) {
|
||||
}
|
||||
in.selectSlot = -1
|
||||
|
||||
// 左键:优先攻击实体(动物体系.md §5),未命中再破坏方块(按住累积)
|
||||
// 左键:按下边沿攻击一次;按住连续破坏(方块交互与动画.md §3)
|
||||
if in.breakDown {
|
||||
if !s.Attack() {
|
||||
if _, ok := s.Cast(5); !ok {
|
||||
s.SwingMiss()
|
||||
}
|
||||
s.BeginBreak()
|
||||
}
|
||||
in.breakDown = false
|
||||
}
|
||||
if !in.breakHeld && s.BreakProgress() != 0 {
|
||||
} else if in.breakHeld {
|
||||
if !s.Breaking() {
|
||||
s.BeginBreak()
|
||||
} else {
|
||||
s.ContinueBreak()
|
||||
}
|
||||
} else if s.Breaking() || s.BreakProgress() != 0 {
|
||||
s.EndBreak()
|
||||
}
|
||||
|
||||
// 放置(右键;命中工作台则打开合成界面,工作台与合成表.md §5)
|
||||
// 放置(右键;命中工作台/熔炉/箱子则打开界面)
|
||||
if in.placeDown {
|
||||
if !s.TryOpenCrafting() {
|
||||
if !s.TryOpenCrafting() && !s.TryOpenFurnace() && !s.TryOpenChest() && !s.TryFlickLever() && !s.TryUseEntity() && !s.TryEat() {
|
||||
s.PlaceSelected()
|
||||
}
|
||||
in.placeDown = false
|
||||
|
||||
@@ -92,7 +92,7 @@ func buildScreens(r *ui.Renderer, am *assets.Manager, lang *assets.Lang) (*ui.Ti
|
||||
// 按钮(原版 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}
|
||||
return ui.Button{X: (427 - 200) / 2, Y: y, W: 200, H: 20, Tex: tex, LabelW: lw, LabelH: lh, Action: action}
|
||||
}
|
||||
title := &ui.TitleScreen{
|
||||
TitleTex: titleTex, TitleW: tw, TitleH: th,
|
||||
@@ -109,10 +109,10 @@ func buildScreens(r *ui.Renderer, am *assets.Manager, lang *assets.Lang) (*ui.Ti
|
||||
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", "设置…", "options", 438),
|
||||
mkBtn("menu.quit", "退出游戏", "quit", 462),
|
||||
mkBtn("menu.singleplayer", "单人游戏", "select", 108),
|
||||
mkBtn("menu.multiplayer", "多人游戏", "multi", 132),
|
||||
mkBtn("menu.options", "设置…", "options", 156),
|
||||
mkBtn("menu.quit", "退出游戏", "quit", 180),
|
||||
}
|
||||
|
||||
pauseTitle, ptw, pth := tr("menu.game", "游戏菜单", 32, color.RGBA{255, 255, 255, 255})
|
||||
@@ -121,13 +121,116 @@ func buildScreens(r *ui.Renderer, am *assets.Manager, lang *assets.Lang) (*ui.Ti
|
||||
BtnTex: btnTex, BtnHover: btnHover,
|
||||
}
|
||||
pause.Buttons = []ui.Button{
|
||||
mkBtn("gui.resume", "继续游戏", "resume", 300),
|
||||
mkBtn("options.title", "设置…", "options", 324),
|
||||
mkBtn("menu.quit", "保存并退出", "quit", 348),
|
||||
mkBtn("gui.resume", "继续游戏", "resume", 72),
|
||||
mkBtn("options.title", "设置…", "options", 94),
|
||||
mkBtn("menu.save", "保存存档", "save", 116),
|
||||
mkBtn("menu.saveAndQuit", "保存并退出", "saveQuit", 138),
|
||||
mkBtn("menu.quitWorld", "退出游戏", "quitWorld", 160),
|
||||
}
|
||||
return title, pause
|
||||
}
|
||||
|
||||
func buildDeathScreen(r *ui.Renderer, lang *assets.Lang, btnTex, btnHover uint32) *ui.DeathScreen {
|
||||
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)
|
||||
}
|
||||
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: 113.5, Y: y, W: 200, H: 20, Tex: tex, LabelW: lw, LabelH: lh, Action: action}
|
||||
}
|
||||
title, tw, th := tr("deathScreen.title", "你死了!", 32, color.RGBA{255, 80, 80, 255})
|
||||
d := &ui.DeathScreen{
|
||||
PauseScreen: ui.PauseScreen{
|
||||
TitleTex: title, TitleW: tw, TitleH: th,
|
||||
BtnTex: btnTex, BtnHover: btnHover,
|
||||
Buttons: []ui.Button{
|
||||
mkBtn("deathScreen.respawn", "重生", "respawn", 100),
|
||||
mkBtn("deathScreen.titleScreen", "返回标题", "title", 124),
|
||||
},
|
||||
},
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func buildSelectWorldScreen(r *ui.Renderer, lang *assets.Lang, btnTex, btnHover uint32) *ui.SelectWorldScreen {
|
||||
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)
|
||||
}
|
||||
white := color.RGBA{255, 255, 255, 255}
|
||||
mk := func(key, fallback, action string, x, y float32) ui.Button {
|
||||
tex, lw, lh := tr(key, fallback, 16, white)
|
||||
return ui.Button{X: x, Y: y, W: 148, H: 20, Tex: tex, LabelW: lw, LabelH: lh, Action: action}
|
||||
}
|
||||
s := &ui.SelectWorldScreen{BtnTex: btnTex, BtnHover: btnHover, Selected: -1}
|
||||
s.TitleTex, s.TitleW, s.TitleH = tr("selectWorld.title", "选择世界", 32, white)
|
||||
s.EmptyTex, s.EmptyW, s.EmptyH = tr("selectWorld.empty", "还没有世界", 18, white)
|
||||
s.ConfirmQTex, s.ConfirmQW, s.ConfirmQH = tr("selectWorld.deleteQuestion", "确定要删除这个世界吗?", 18, white)
|
||||
s.ConfirmTex, s.ConfirmW, s.ConfirmH = tr("selectWorld.deleteWarning", "将永远消失!(很长一段时间!)", 16, color.RGBA{255, 85, 85, 255})
|
||||
s.Buttons = []ui.Button{
|
||||
mk("selectWorld.select", "进入选中的世界", "play", 63, 186),
|
||||
mk("selectWorld.create", "创建新的世界", "create", 215, 186),
|
||||
mk("selectWorld.delete", "删除", "delete", 63, 210),
|
||||
mk("gui.cancel", "取消", "cancel", 215, 210),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func buildCreateWorldScreen(r *ui.Renderer, lang *assets.Lang, btnTex, btnHover uint32) *ui.CreateWorldScreen {
|
||||
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)
|
||||
}
|
||||
white := color.RGBA{255, 255, 255, 255}
|
||||
mk := func(key, fallback, action string, x, y float32) ui.Button {
|
||||
tex, lw, lh := tr(key, fallback, 16, white)
|
||||
return ui.Button{X: x, Y: y, W: 148, H: 20, Tex: tex, LabelW: lw, LabelH: lh, Action: action}
|
||||
}
|
||||
s := &ui.CreateWorldScreen{BtnTex: btnTex, BtnHover: btnHover}
|
||||
s.TitleTex, s.TitleW, s.TitleH = tr("createWorld.title", "创建新的世界", 32, white)
|
||||
s.NameLblTex, s.NameLblW, s.NameLblH = tr("createWorld.worldName", "世界名称", 16, white)
|
||||
s.SeedLblTex, s.SeedLblW, s.SeedLblH = tr("createWorld.seed", "世界种子", 16, white)
|
||||
s.Buttons = []ui.Button{
|
||||
mk("createWorld.create", "创建新的世界", "create", 63, 186),
|
||||
mk("gui.cancel", "取消", "cancel", 215, 186),
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// buildOptionsScreen 构建设置界面(灵敏度/音量滑条、种子显示、按键教程列表)。
|
||||
func buildOptionsScreen(r *ui.Renderer, lang *assets.Lang, btnTex, btnHover uint32, seed int64) *ui.OptionsScreen {
|
||||
text := func(s string, size float64, c color.RGBA) (uint32, float32, float32) {
|
||||
@@ -155,8 +258,8 @@ func buildOptionsScreen(r *ui.Renderer, lang *assets.Lang, btnTex, btnHover uint
|
||||
"按键教程:\n"+
|
||||
"W/A/S/D 移动 空格 跳跃 Shift 潜行 Ctrl 冲刺\n"+
|
||||
"鼠标左键 攻击/破坏 鼠标右键 放置/使用\n"+
|
||||
"E 打开背包 Q 丢弃物品 1-9/滚轮 切换快捷栏\n"+
|
||||
"Esc 暂停/返回",
|
||||
"E 打开背包 Q 丢弃物品 1-9/滚轮 切换快捷栏(上滚左、下滚右)\n"+
|
||||
"F11 / Alt+Enter 全屏 Esc 暂停/返回",
|
||||
16, white)
|
||||
s.DoneLabelTex, s.DoneLabelW, s.DoneLabelH = tr("options.done", "完成", 16, white)
|
||||
return s
|
||||
|
||||
Reference in New Issue
Block a user