Files
metro-puzzle-jp/internal/game/scene_title.go
claude 5114115976
All checks were successful
release / build-release (push) Successful in 1m37s
Texterna ryms på skärmen på svenska och engelska
Björn såg att bankartans tangentrad klipptes på engelska. Nu mäts alla
enradstexter med den riktiga fonten i båda språken mot sitt utrymme
(game.TestSingleLineTextsFit) och HUD-raden för alla banor
(TestLevelNamesFitHUD). Åtgärder: kortare etiketter ("Mål"/"To"),
bankartans tangentrad på två rader (versionen står på titelskärmen),
HUD:en stryker japansk hint, "via" och valfria plånboksdelar som inte
får plats och kortar med "..", fler rutor radbryts, kortare bannamn,
kortare minispelstexter. Skärmdumpar på engelska (map-en, sign-en) via en
engelsk demosparfil. Alla skärmdumpar omtagna.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-25 18:10:26 +01:00

212 lines
5.4 KiB
Go

package game
import (
"fmt"
"image/color"
"math"
"github.com/hajimehoshi/ebiten/v2"
"github.com/hajimehoshi/ebiten/v2/vector"
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/buildinfo"
)
// TitleScene är titelskärmen: premissen i text och menyn Fortsätt/Starta,
// Tutorial, Inställningar, Avsluta (Enter två gånger).
type TitleScene struct {
sel int
t float64
confirm bool // Avsluta valt en gång
}
// NewTitleScene öppnar titelskärmen.
func NewTitleScene() *TitleScene { return &TitleScene{} }
const titleItems = 4
func (s *TitleScene) Update(g *Game) error {
s.t += dt
if mv := menuMove(); mv != 0 {
s.sel = (s.sel + mv + titleItems) % titleItems
s.confirm = false
}
if backJustPressed() {
s.confirm = false
}
if !confirmJustPressed() {
return nil
}
g.Audio.Play(SoundConfirm, 0.6)
switch s.sel {
case 0:
g.SetScene(NewMapScene(g))
case 1:
g.SetScene(NewTutorialScene(s))
case 2:
g.SetScene(NewSettingsScene(s))
case 3:
if s.confirm {
return ebiten.Termination
}
s.confirm = true
}
return nil
}
func (s *TitleScene) Draw(g *Game, screen *ebiten.Image) {
a := g.Assets
screen.Fill(color.RGBA{24, 20, 37, 255})
// Räls längs nederkanten med ett tåg som rullar förbi.
railY := ScreenH - 28
for x := 0; x < ScreenW; x += TileSize {
var op ebiten.DrawImageOptions
op.GeoM.Translate(float64(x), float64(railY))
screen.DrawImage(a.Rails, &op)
}
tx := math.Mod(s.t*40, float64(ScreenW+6*TileSize)) - 3*TileSize
for i := 0; i < 3; i++ {
var op ebiten.DrawImageOptions
op.GeoM.Translate(tx+float64(i*TileSize), float64(railY-TileSize+4))
screen.DrawImage(a.Train, &op)
}
a.DrawBig(screen, a.T("game.title"), 16, 8, colKnown)
ver := fmt.Sprintf("%s (%s)", buildinfo.Version, buildinfo.Mode)
a.Draw(screen, ver, ScreenW-8-a.Width(ver), 10, colDim)
y := 30
for _, l := range a.wrapLine(plainLine(a.T("title.premise"), colText), ScreenW-32) {
a.drawLines(screen, []line{l}, 16, y)
y += LineH
}
start := a.T("menu.continue")
if g.Progress.TotalStars() == 0 && len(g.Progress.Levels) == 0 {
start = a.T("menu.start")
}
items := [titleItems]string{start, a.T("title.tutorial"), a.T("menu.settings"), a.T("menu.quit")}
y += 6
for i, it := range items {
col := colText
if i == s.sel {
vector.DrawFilledRect(screen, 12, float32(y-2), 130, LineH, colBar, false)
col = colKnown
a.Draw(screen, "▶", 14, y, colKnown)
}
a.Draw(screen, it, 26, y, col)
y += LineH
}
if s.confirm {
a.Draw(screen, a.T("title.quit_confirm"), 16, y+2, colWarn)
}
a.Draw(screen, a.T("title.hint"), 8, ScreenH-12, colDim)
}
// LangScene visas första gången spelet startar: välj språk.
type LangScene struct{ sel int }
var langChoices = []string{"sv", "en"}
func (s *LangScene) Update(g *Game) error {
if mv := menuMove(); mv != 0 {
s.sel = (s.sel + mv + len(langChoices)) % len(langChoices)
}
if confirmJustPressed() {
g.State.Settings.Lang = langChoices[s.sel]
g.applySettings()
g.Save()
g.Audio.Play(SoundConfirm, 0.6)
g.SetScene(NewTitleScene())
}
return nil
}
func (s *LangScene) Draw(g *Game, screen *ebiten.Image) {
a := g.Assets
screen.Fill(color.RGBA{24, 20, 37, 255})
a.DrawBig(screen, a.T("game.title"), 16, 10, colKnown)
a.Draw(screen, "Välj språk / Choose language", 16, 50, colText)
y := 72
for i, l := range langChoices {
col := colText
if i == s.sel {
vector.DrawFilledRect(screen, 12, float32(y-2), 130, LineH+2, colBar, false)
col = colKnown
a.Draw(screen, "▶", 14, y, colKnown)
}
a.Draw(screen, a.T("lang."+l), 26, y, col)
y += LineH + 1
}
a.Draw(screen, "Pilar + Enter / Arrows + Enter", 16, y+8, colDim)
}
// PauseScene är pausmenyn i en bana (Esc): Fortsätt, Lämna banan,
// Inställningar, Avsluta spelet (Enter två gånger). Esc igen fortsätter.
type PauseScene struct {
from *LevelScene
sel int
confirm bool
}
const pauseItems = 4
// NewPauseScene pausar banan.
func NewPauseScene(from *LevelScene) *PauseScene { return &PauseScene{from: from} }
func (s *PauseScene) Update(g *Game) error {
if backJustPressed() {
g.SetScene(s.from)
return nil
}
if mv := menuMove(); mv != 0 {
s.sel = (s.sel + mv + pauseItems) % pauseItems
s.confirm = false
}
if !confirmJustPressed() {
return nil
}
g.Audio.Play(SoundConfirm, 0.6)
switch s.sel {
case 0:
g.SetScene(s.from)
case 1:
if s.from.endless {
g.endEndless()
}
g.SetScene(NewMapScene(g))
case 2:
g.SetScene(NewSettingsScene(s))
case 3:
if s.confirm {
return ebiten.Termination
}
s.confirm = true
}
return nil
}
func (s *PauseScene) Draw(g *Game, screen *ebiten.Image) {
s.from.Draw(g, screen)
a := g.Assets
vector.DrawFilledRect(screen, 0, 0, ScreenW, ScreenH, color.RGBA{0, 0, 0, 120}, false)
vector.DrawFilledRect(screen, 40, 40, ScreenW-80, 104, colPanel, false)
vector.DrawFilledRect(screen, 40, 40, ScreenW-80, 2, colWarn, false)
a.DrawBig(screen, a.T("pause.title"), 48, 46, colText)
items := [pauseItems]string{a.T("pause.resume"), a.T("pause.leave"), a.T("menu.settings"), a.T("pause.quit")}
y := 68
for i, it := range items {
col := colText
if i == s.sel {
vector.DrawFilledRect(screen, 44, float32(y-2), ScreenW-88, LineH+2, colBar, false)
col = colKnown
}
a.Draw(screen, it, 50, y, col)
y += LineH + 2
}
msg := a.T("pause.hint")
if s.confirm {
msg = a.T("pause.quit_confirm")
}
for _, l := range a.wrapLine(plainLine(msg, colDim), ScreenW-96) {
a.drawLines(screen, []line{l}, 48, y+2)
y += LineH
}
}