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:
@@ -84,6 +84,7 @@ type Run struct {
|
||||
spentStart int // plånbokens Spent vid banstart
|
||||
Score ScoreBreakdown
|
||||
NewDebuffs []Debuff // debuffar banan gav (appliceras på profilen efteråt)
|
||||
opts RunOptions
|
||||
}
|
||||
|
||||
// ScoreBreakdown är poängen för en vunnen bana.
|
||||
@@ -163,6 +164,14 @@ type RunOptions struct {
|
||||
Wallet *Wallet // nil = startkassa
|
||||
Debuffs []Debuff // aktiva debuffar från profilen
|
||||
Seed rng.Seed // omgångens seed (sparas med spelet)
|
||||
|
||||
// Från skills och items (progression):
|
||||
Extra Stats // multipliceras in i statsen (nollvärde = inga)
|
||||
FogRadius int // extra siktsteg
|
||||
Landmarks bool // toaletter/kiosker/automater alltid på minikartan
|
||||
FreeGates bool // spärrarna kostar inget och kräver inget
|
||||
RevealAll bool // hela banan känd från start
|
||||
InfiniteBattery bool // telefonen laddar inte ur
|
||||
}
|
||||
|
||||
// DefaultSpeed är spelarens grundfart i rutor per sekund.
|
||||
@@ -215,11 +224,21 @@ func NewRun(l *level.Level, o RunOptions) *Run {
|
||||
Party: NewParty(l.Party, l.SpawnPoint(), o.Seed.Derive("party")),
|
||||
baseSpeed: o.Speed,
|
||||
NoticeAt: -1,
|
||||
Fog: NewFog(l, FogRadius),
|
||||
Stats: CombinedStats(o.Debuffs),
|
||||
spentStart: o.Wallet.Spent,
|
||||
opts: o,
|
||||
}
|
||||
if o.Extra != (Stats{}) {
|
||||
for i := range r.Stats {
|
||||
r.Stats[i] *= o.Extra[i]
|
||||
}
|
||||
}
|
||||
r.Fog = NewFog(l, FogRadius+o.FogRadius)
|
||||
r.Fog.ExtraLandmarks = o.Landmarks
|
||||
r.Party.Rates = Rates{Thirst: r.Stats[StatThirst], Toilet: r.Stats[StatToilet]}
|
||||
if o.RevealAll {
|
||||
r.Fog.RevealAll(l)
|
||||
}
|
||||
r.Fog.Reveal(l, l.SpawnPoint())
|
||||
if r.parSec <= 0 {
|
||||
n := len(l.Path(l.SpawnPoint(), l.CorrectPlatform(), l.CanEnter))
|
||||
@@ -259,6 +278,9 @@ func (r *Run) CanEnter(p level.Point) bool {
|
||||
}
|
||||
|
||||
func (r *Run) canPassGate() bool {
|
||||
if r.opts.FreeGates {
|
||||
return true // JR Pass: genväg genom alla spärrar
|
||||
}
|
||||
if r.Level.TicketRequired {
|
||||
if r.Wallet.Ticket == "" {
|
||||
return false
|
||||
@@ -306,7 +328,9 @@ func (r *Run) Step(dt float64, want Dir, board bool) {
|
||||
return // minispel pågår: använd StepMini
|
||||
}
|
||||
r.Time += dt
|
||||
r.Wallet.Drain(dt)
|
||||
if !r.opts.InfiniteBattery {
|
||||
r.Wallet.Drain(dt)
|
||||
}
|
||||
before := r.Player.Tile
|
||||
wasGate := r.inGate()
|
||||
r.Player.Speed = r.baseSpeed * r.Party.SpeedFactor() * r.Stats[StatSpeed]
|
||||
@@ -332,7 +356,7 @@ func (r *Run) Step(dt float64, want Dir, board bool) {
|
||||
r.checkLimits()
|
||||
if r.Player.Tile != before && r.inGate() && !wasGate {
|
||||
r.GatePasses++
|
||||
if !r.Level.TicketRequired {
|
||||
if !r.Level.TicketRequired && !r.opts.FreeGates {
|
||||
r.Wallet.PayCard(r.Level.FareYen)
|
||||
if r.Level.IndividualCards {
|
||||
for _, m := range r.Party.Followers() {
|
||||
@@ -427,7 +451,7 @@ func (r *Run) tryBoard(tr *level.Train) {
|
||||
}
|
||||
r.PendingTrain = tr
|
||||
if tr.Crowded {
|
||||
r.Mini, r.MiniKind = minigame.NewCrowd(r.miniSeed(), CrowdTries), MiniCrowd
|
||||
r.Mini, r.MiniKind = minigame.NewCrowd(r.miniSeed(), CrowdTries, r.Stats[StatLuck]), MiniCrowd
|
||||
return
|
||||
}
|
||||
r.startRide()
|
||||
@@ -449,7 +473,9 @@ func (r *Run) StepMini(dt float64, in minigame.Input) {
|
||||
return
|
||||
}
|
||||
r.Time += dt
|
||||
r.Wallet.Drain(dt)
|
||||
if !r.opts.InfiniteBattery {
|
||||
r.Wallet.Drain(dt)
|
||||
}
|
||||
r.Party.Advance(dt, r.Time)
|
||||
r.Mini.Update(in, dt)
|
||||
if !r.Mini.Done() {
|
||||
|
||||
Reference in New Issue
Block a user