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>
59 lines
1.5 KiB
Go
59 lines
1.5 KiB
Go
package minigame
|
|
|
|
import "gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/rng"
|
|
|
|
// Crowd är "få plats på det fulla tåget": en markör pendlar över en stapel,
|
|
// tryck Enter när den är i den gröna zonen. Två försök, sedan går tåget.
|
|
type Crowd struct {
|
|
base
|
|
Pos float64 // markörens läge 0..1
|
|
dir float64
|
|
Speed float64 // enheter per sekund
|
|
ZoneLo float64
|
|
ZoneHi float64
|
|
Attempts int
|
|
MaxTries int
|
|
}
|
|
|
|
const crowdZoneWidth = 0.22
|
|
|
|
// NewCrowd seedar zonens läge och markörens fart. luck (1 = normalt) gör
|
|
// zonen bredare eller smalare, inom rimliga gränser.
|
|
func NewCrowd(seed rng.Seed, tries int, luck float64) *Crowd {
|
|
r := newRand(seed, "crowd")
|
|
width := min(0.6, max(0.1, crowdZoneWidth*max(0.5, min(2, luck))))
|
|
lo := r.between(0.1, 0.9-width)
|
|
return &Crowd{dir: 1, Speed: r.between(0.9, 1.5), ZoneLo: lo, ZoneHi: lo + width, MaxTries: max(1, tries)}
|
|
}
|
|
|
|
// InZone rapporterar om markören är i zonen just nu.
|
|
func (g *Crowd) InZone() bool { return g.Pos >= g.ZoneLo && g.Pos <= g.ZoneHi }
|
|
|
|
// Update pendlar markören och tar emot försök.
|
|
func (g *Crowd) Update(in Input, dt float64) {
|
|
if g.done {
|
|
return
|
|
}
|
|
if in.Cancel {
|
|
g.done = true
|
|
return
|
|
}
|
|
g.Pos += g.dir * g.Speed * dt
|
|
for g.Pos < 0 || g.Pos > 1 {
|
|
if g.Pos < 0 {
|
|
g.Pos = -g.Pos
|
|
} else {
|
|
g.Pos = 2 - g.Pos
|
|
}
|
|
g.dir = -g.dir
|
|
}
|
|
if in.Confirm {
|
|
g.Attempts++
|
|
if g.InZone() {
|
|
g.done, g.success = true, true
|
|
} else if g.Attempts >= g.MaxTries {
|
|
g.done = true
|
|
}
|
|
}
|
|
}
|