All checks were successful
release / build-release (push) Successful in 1m37s
Resor med byten: en bana kan ha next som pekar på en delbana (kind leg, syns inte på bankartan). När man klivit av rätt tåg fortsätter samma omgång på nästa station med klocka, plånbok, sällskap och räknare; par-tiden adderas, dimma och folk är nya, en Byte!-banner visas och HUD:en visar resmålet via bytesstationen. Tidsgräns och missade tåg gäller hela resan. level.ValidateChains kräver sammanhängande kedjor. Nya resor: 007 Akihabara -> Ueno -> Asakusa och finalen 008 Tokyo (två våningar) -> Nippori -> Narita flygplats. Slutscen när sista huvudbanan klaras första gången: planet lyfter, resan summeras, eftertexter; Progress.StoryCleared sparas; flaggan -ending. tools/levelcheck ritar banorna och rapporterar problem. HUD-nivån kortas när mätarna tar plats; poängraden radbryts. Skärmdumpar och docs uppdaterade. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
163 lines
5.6 KiB
Go
163 lines
5.6 KiB
Go
package game
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
"github.com/hajimehoshi/ebiten/v2/vector"
|
|
|
|
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/sim"
|
|
)
|
|
|
|
// ResultScene visas när banan är avgjord (rätt eller fel tåg).
|
|
type ResultScene struct {
|
|
from *LevelScene
|
|
}
|
|
|
|
// NewResultScene bygger resultatet för banan som just spelades.
|
|
func NewResultScene(from *LevelScene) *ResultScene { return &ResultScene{from: from} }
|
|
|
|
func (r *ResultScene) Update(g *Game) error {
|
|
switch {
|
|
case wordsJustPressed():
|
|
g.SetScene(NewWordsScene(g.Assets, r))
|
|
case inpututil_R():
|
|
s, err := g.startLevel(r.from.id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
g.SetScene(s)
|
|
case confirmJustPressed() || backJustPressed():
|
|
if r.from.outcome.StoryCleared {
|
|
g.SetScene(NewEndingScene(g)) // storyn klar: hemresan
|
|
return nil
|
|
}
|
|
g.SetScene(NewMapScene(g))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *ResultScene) Draw(g *Game, screen *ebiten.Image) {
|
|
r.from.Draw(g, screen) // banan i bakgrunden
|
|
run := r.from.run
|
|
a := g.Assets
|
|
vector.DrawFilledRect(screen, 20, 30, ScreenW-40, 144, colPanel, false)
|
|
|
|
accent, headline := colBad, a.T("level.lost")
|
|
if run.Outcome == sim.Won {
|
|
accent, headline = colGood, a.T("level.cleared")
|
|
}
|
|
vector.DrawFilledRect(screen, 20, 30, ScreenW-40, 3, accent, false)
|
|
a.DrawBig(screen, headline, 28, 36, accent)
|
|
if run.Outcome == sim.Won {
|
|
stars := strings.Repeat("★", run.Stars) + strings.Repeat("☆", 3-run.Stars)
|
|
a.DrawBig(screen, stars, ScreenW-28-a.Width(stars)-8, 36, colWarn)
|
|
}
|
|
|
|
y := 54
|
|
if run.Boarded == nil && run.Outcome == sim.Lost {
|
|
a.Draw(screen, a.T(run.LostReason), 28, y, colText)
|
|
y += LineH
|
|
}
|
|
if tr := run.Boarded; tr != nil {
|
|
a.Draw(screen, fmt.Sprintf("%s%s %s %s%s", tr.Track, a.T("hud.track_suffix_ja"), tr.Line.Get("ja"),
|
|
tr.Destination.Get("ja"), a.T("hud.for_ja")), 28, y, colKnown)
|
|
y += LineH
|
|
if run.Outcome == sim.Lost {
|
|
reason := a.T(run.LostReason)
|
|
if detail, ok := map[string]string{
|
|
sim.LostWrongTrain: "result.wrong_detail",
|
|
sim.LostWrongDirection: "result.wrong_direction_detail",
|
|
sim.LostTooFar: "result.too_far_detail",
|
|
}[run.LostReason]; ok {
|
|
reason += " " + fmt.Sprintf(a.T(detail), tr.Destination.Get("romaji"), run.Level.Destination.Get("romaji"))
|
|
}
|
|
for _, l := range a.wrapLine(plainLine(reason, colText), ScreenW-56) {
|
|
a.drawLines(screen, []line{l}, 28, y)
|
|
y += LineH
|
|
}
|
|
}
|
|
}
|
|
y += 4
|
|
a.Draw(screen, fmt.Sprintf("%s %.1fs (%s %.0fs) %s %d %s %.0f %%", a.T("hud.time"), run.Time, a.T("result.par"), run.ParSec(),
|
|
a.T("hud.steps"), run.Steps, a.T("result.explored"), 100*run.Fog.Explored(run.Level)), 28, y, colText)
|
|
y += LineH
|
|
a.Draw(screen, fmt.Sprintf("%s %d %s %d¥ %s %d¥ %s %d", a.T("result.gates"), run.GatePasses, a.T("hud.card"), run.Wallet.Card,
|
|
a.T("result.spent"), run.Wallet.Spent, a.T("result.missed_short"), run.MissedTrains), 28, y, colText)
|
|
y += LineH
|
|
if run.Outcome == sim.Won {
|
|
sc := run.Score
|
|
// Poängen som en uträkning; radbryts till högst två rader.
|
|
parts := []string{
|
|
fmt.Sprintf("%s %d", strings.ToLower(a.T("hud.time")), sc.Time), fmt.Sprintf("%s %d", a.T("result.money"), sc.Money),
|
|
fmt.Sprintf("%s %d", a.T("result.meters"), sc.Meters), fmt.Sprintf("%s %d", a.T("result.map"), sc.Explore),
|
|
fmt.Sprintf("%s %d", a.T("result.car"), sc.Car),
|
|
}
|
|
if sc.Bonus > 0 {
|
|
parts = append(parts, fmt.Sprintf("%s %d", a.T("result.bonus"), sc.Bonus))
|
|
}
|
|
total := fmt.Sprintf("%s %d = %s", a.T("result.score"), sc.Total, strings.Join(parts, " + "))
|
|
if sc.Penalty > 0 {
|
|
total += fmt.Sprintf(" - %s %d", a.T("result.penalty"), sc.Penalty)
|
|
}
|
|
for i, l := range a.wrapLine(plainLine(total, colText), ScreenW-56) {
|
|
if i > 1 {
|
|
break
|
|
}
|
|
a.drawLines(screen, []line{l}, 28, y)
|
|
y += LineH
|
|
}
|
|
msg := fmt.Sprintf("+%d %s", run.PointsEarned, a.T("result.lang_points"))
|
|
if len(run.LearnedDest) > 0 {
|
|
msg += " " + a.T("hud.learned") + ": " + strings.Join(run.LearnedDest, " ") + " (" + a.T("result.destination") + ")"
|
|
}
|
|
a.Draw(screen, msg, 28, y, colKnown)
|
|
y += LineH
|
|
}
|
|
if o := r.from.outcome; run.Outcome == sim.Won {
|
|
prog := fmt.Sprintf("+%d %s", o.XP, a.T("map.xp"))
|
|
if o.LevelUps > 0 {
|
|
prog += fmt.Sprintf(" %s %d (+%d %s)", a.T("result.level_up"), g.Progress.Level(), o.LevelUps+o.NewPoints, a.T("map.skill_points"))
|
|
} else if o.NewPoints > 0 {
|
|
prog += fmt.Sprintf(" +%d %s", o.NewPoints, a.T("map.skill_points"))
|
|
}
|
|
if len(o.NewItems) > 0 {
|
|
var names []string
|
|
for _, id := range o.NewItems {
|
|
names = append(names, g.Catalog.Item(id).Name.Get(UILang))
|
|
}
|
|
prog += " " + a.T("result.new_items") + ": " + strings.Join(names, ", ")
|
|
}
|
|
if len(o.Unlocked) > 0 {
|
|
var names []string
|
|
for _, id := range o.Unlocked {
|
|
if l := g.LevelByID(id); l != nil {
|
|
names = append(names, l.Name.Get(UILang))
|
|
}
|
|
}
|
|
prog += " " + a.T("result.unlocked") + ": " + strings.Join(names, ", ")
|
|
}
|
|
for i, l := range a.wrapLine(plainLine(prog, colGood), ScreenW-56) {
|
|
if i > 1 {
|
|
break
|
|
}
|
|
a.drawLines(screen, []line{l}, 28, y)
|
|
y += LineH
|
|
}
|
|
}
|
|
if len(run.NewDebuffs) > 0 {
|
|
var parts []string
|
|
for _, d := range run.NewDebuffs {
|
|
parts = append(parts, fmt.Sprintf("%s (%d)", a.T("debuff."+string(d.Kind)), d.LevelsLeft))
|
|
}
|
|
a.Draw(screen, a.T("result.new_debuffs")+": "+strings.Join(parts, ", "), 28, y, colBad)
|
|
y += LineH
|
|
}
|
|
if y <= 149 {
|
|
a.Draw(screen, fmt.Sprintf("%s Lv%d %d %s %s %d (%d %s)", a.T("hud.japanese"), run.Know.Level(), run.Know.Count(),
|
|
a.T("hud.words"), a.T("words.points"), run.Know.Points, run.Know.Helps, a.T("result.helps")), 28, y, colDim)
|
|
}
|
|
a.Draw(screen, a.T("result.hint"), 28, 161, colDim)
|
|
}
|