M8: progression, items, bankarta och sparfil
All checks were successful
release / build-release (push) Successful in 1m46s
All checks were successful
release / build-release (push) Successful in 1m46s
- internal/progress: katalog (skills.json 12 noder, items.json 25 items) med validering; XP/nivåer (150·n(n+1)/2) och skillpoäng; skillträd med krav och kostnad; items per plats med buffar, genvägar (JR Pass, kartor, powerbank) och färgton; banresultat och upplåsning (huvudbanor i ordning, sidospår efter föräldern); 33 %-regeln som test - internal/save: versionerad JSON med kunskap, plånbok, debuffar, progression och pågående bana + seed; atomisk skrivning; migrering; ~/.config/metro-puzzle-jp/save.json resp. %AppData% - sim: RunOptions får Extra/FogRadius/Landmarks/FreeGates/RevealAll/ InfiniteBattery; luck påverkar trängselzonen; Fog.RevealAll och extra landmärken - game: bankarta i spelbrädesstil ersätter menyn (⟳ återupptar med sparad seed), skillträd (T), garderob (I), butik i kiosken (B), XP/nivå/items/ upplåsningar på resultatet, spelaren tonas av skins; autospar vid banstart, efter banan och vid köp; flaggor -save/-nosave - level: kind/order/parent/rewards/shop; ny sidobana 002b Kabukicho som belönar skinnet Kimono; 001 belönar Salaryman-skinnet; butikssortiment på 002/003 - tools/shots: demo-sparfil (build/, aldrig den riktiga), bilder av karta, skills, garderob och butik; alla körningar med -nosave - docs/README/wiki uppdaterade Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -954,3 +954,50 @@ func TestNearTrackAndPhases(t *testing.T) {
|
||||
t.Fatal("tre rutor bort är utom räckhåll")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunOptionsFromProgression(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
extra := DefaultStats()
|
||||
extra[StatSpeed] = 1.2
|
||||
r := NewRun(l, RunOptions{Seed: rng.FromInt(1), Extra: extra, FogRadius: 2, Landmarks: true, FreeGates: true, RevealAll: true, InfiniteBattery: true})
|
||||
r.Party.nextEventAt = 1e9
|
||||
r.Wallet.Card, r.Wallet.Phone, r.Wallet.Battery = 0, true, 50
|
||||
r.Step(frame, Right, false)
|
||||
if math.Abs(r.Player.Speed-DefaultSpeed*1.2*PhoneSpeedFactor) > 1e-9 {
|
||||
t.Fatalf("extra fart: %.2f", r.Player.Speed)
|
||||
}
|
||||
if r.Wallet.Battery != 50 {
|
||||
t.Fatal("powerbank: batteriet laddar inte ur")
|
||||
}
|
||||
if r.Fog.Radius != FogRadius+2 || !r.Fog.ExtraLandmarks || r.Fog.Explored(l) != 1 {
|
||||
t.Fatalf("dimma: radius=%d landmarks=%v explored=%.2f", r.Fog.Radius, r.Fog.ExtraLandmarks, r.Fog.Explored(l))
|
||||
}
|
||||
gate := l.Find(level.Gate)[0]
|
||||
if !r.CanEnter(gate) {
|
||||
t.Fatal("JR Pass: spärren släpper igenom utan saldo")
|
||||
}
|
||||
for r.Player.Moving() {
|
||||
r.Step(frame, None, false)
|
||||
}
|
||||
path := l.Path(r.Player.Tile, gate, r.CanEnter)
|
||||
for i, p := range path {
|
||||
if l.At(p.X, p.Y) == level.Gate {
|
||||
path = path[:i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
walk(t, r, path)
|
||||
if r.Wallet.Spent != 0 || r.GatePasses != 1 {
|
||||
t.Fatalf("gratis spärr: spent=%d passes=%d", r.Wallet.Spent, r.GatePasses)
|
||||
}
|
||||
// Utan extras: standard.
|
||||
plain := NewRun(l, RunOptions{Seed: rng.FromInt(1)})
|
||||
if plain.Stats != DefaultStats() || plain.Fog.Radius != FogRadius || plain.Fog.ExtraLandmarks {
|
||||
t.Fatal("standardvärden utan progression")
|
||||
}
|
||||
// Lokalkännedom: kiosken syns på minikartan utan att vara sedd.
|
||||
k := l.Find(level.Kiosk)[0]
|
||||
if plain.Fog.Visible(l, k) || !r.Fog.Visible(l, k) {
|
||||
t.Fatal("landmärken från skillen")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user