M11: oändligt läge och New Game+
All checks were successful
release / build-release (push) Successful in 1m34s

internal/endless lottar mål ur en seed: en av storyns nätkopplade
stationer (aldrig samma två gånger i rad) med ett resmål som minst ett
tåg går rakt till men inte alla; banan kopieras, bedöms om mot nätet,
sällskapet växer var tredje mål och tidsgränsen krymper. O på bankartan
(eller -endless) startar en omgång; klarade mål ger poäng och XP, Enter
ger nästa mål, förlust eller Esc avslutar och omgången hamnar i
sparfilens poänglista (fem bästa). G två gånger startar New Game+: items,
banresultat, pengar och debuffar nollställs, skills, XP och japanskan
behålls, sällskapet är en person större per varv. Docs, wiki, tester och
skärmdumpar uppdaterade.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 23:20:12 +02:00
parent 46bd330dfc
commit 36a45563ec
22 changed files with 556 additions and 19 deletions

View File

@@ -17,11 +17,12 @@ import (
// linje från flygplatsen, sidobanorna som avstickare ovanför. Låsta banor
// är grå, klarade visar stjärnor.
type MapScene struct {
mains []*level.Level
sides map[string][]*level.Level // parent -> sidobanor
sel int // vald huvudbana
side int // 0 = huvudbanan, 1.. = sidobana nr
msg string
confirmNG bool // G tryckt en gång: nästa G startar New Game+
mains []*level.Level
sides map[string][]*level.Level // parent -> sidobanor
sel int // vald huvudbana
side int // 0 = huvudbanan, 1.. = sidobana nr
msg string
}
type mapNode struct {
@@ -105,6 +106,26 @@ func (m *MapScene) Update(g *Game) error {
g.Save()
case modeJustPressed():
g.toggleMode()
case endlessJustPressed() && g.Progress.StoryCleared:
s, err := g.startEndless(g.seed())
if err != nil {
m.msg = err.Error()
return nil
}
g.SetScene(s)
return nil
case ngPlusJustPressed() && g.Progress.StoryCleared:
if !m.confirmNG {
m.confirmNG = true
m.msg = g.Assets.T("map.ngplus_confirm")
return nil
}
g.newGamePlus()
g.SetScene(NewMapScene(g))
return nil
}
if m.confirmNG && (confirmJustPressed() || miniMoveXYAny()) {
m.confirmNG, m.msg = false, "" // något annat tryckt: ingen NG+
}
if n := len(m.mains); n > 0 {
mx, my := miniMoveXY()
@@ -207,7 +228,9 @@ func (m *MapScene) Draw(g *Game, screen *ebiten.Image) {
if !p.Unlocked(g.Levels, cur.ID) {
a.Draw(screen, a.T("map.locked"), 150, 30, colBad)
} else if m.msg != "" {
a.Draw(screen, m.msg, 150, 30, colBad)
for i, l := range a.wrapLine(plainLine(m.msg, colBad), ScreenW-158) {
a.drawLines(screen, []line{l}, 150, 30+i*LineH)
}
}
}
onoff := a.T("menu.off")
@@ -218,12 +241,25 @@ func (m *MapScene) Draw(g *Game, screen *ebiten.Image) {
a.T("hud.cash"), g.Wallet.Cash)
a.Draw(screen, foot, 8, 30, colDim)
a.Draw(screen, fmt.Sprintf(a.T("menu.phone"), onoff)+" "+fmt.Sprintf(a.T("menu.mode"), a.T("mode."+g.Mode.String())), 8, 42, colDim)
y := 54
if p.StoryCleared { // efter storyn: oändligt läge och New Game+
line := a.T("map.endless")
if b := g.State.BestEndless(); b != nil {
line += fmt.Sprintf(" (%s)", fmt.Sprintf(a.T("map.endless_best"), b.Score, b.Goals))
}
line += " " + a.T("map.ngplus")
if p.NGPlus > 0 {
line += fmt.Sprintf(" (%s)", fmt.Sprintf(a.T("map.ngplus_level"), p.NGPlus))
}
a.Draw(screen, line, 8, y, colKnown)
y += LineH
}
if len(g.Profile.Debuffs) > 0 {
var parts []string
for _, d := range g.Profile.Debuffs {
parts = append(parts, fmt.Sprintf("%s (%d)", a.T("debuff."+string(d.Kind)), d.LevelsLeft))
}
a.Draw(screen, a.T("hud.debuffs")+": "+strings.Join(parts, ", "), 8, 54, colBad)
a.Draw(screen, a.T("hud.debuffs")+": "+strings.Join(parts, ", "), 8, y, colBad)
}
a.Draw(screen, a.T("map.hint"), 8, ScreenH-24, colDim)
a.Draw(screen, fmt.Sprintf("%s (%s)", buildinfo.Version, buildinfo.Mode), 8, ScreenH-12, colDim)