M6: plånbok, biljetter, kortet i telefonen och seedade minispel
All checks were successful
release / build-release (push) Successful in 1m41s

- sim.Wallet: IC-kort, kontanter, pappersbiljett, telefon med batteri,
  spenderat; följer med mellan banor (Game.Wallet)
- internal/minigame: gemensamt Game-gränssnitt (Update/Done/Success), all
  slump från del-seeds: TopUp (hitta チャージ, belopp, mata sedlar),
  Ticket (taxetavla med resmål på japanska), Crowd (tidsfönster, två
  försök), Ride (stationerna passerar, kliv av vid resmålet)
- sim.Run: spärren kräver biljett på biljettbanor annars användbart kort;
  telefonen ger ×1,1 fart men 15 % seedad omskanning (frysning 1,5 s),
  dött batteri gör kortet oanvändbart; automaten startar påfyllning eller
  biljettköp; tåget kontrollerar biljetten; fulla tåg kräver trängsel;
  resan avgör vinst/förlust (fel station, åkte förbi, fel tåg); StepMini
  låter klockan och sällskapet ticka medan rörelsen pausar
- level: ticketRequired, rideStops, Train.Crowded; 002 är biljettbana med
  fullt tåg och fyra stopp; ordboken har stationer (kind) + sju nya namn
- game: vyer för minispelen, plånbok i HUD, P i menyn = kort i telefonen,
  förlustorsaker och spenderat på resultatet, flaggor -phone/-card,
  skriptkommandon hold/waitstop; inforuta/minikarta döljs under minispel
- plan: M7b (Björn) – tågvind, ankomstindikator nära rälsen,
  in-/utkörningsanimation för tåg och bussar – i spelbeskrivning, roadmap,
  testplan
- docs/README/wiki med tre nya bilder (automat, biljettavla, tågresa)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-24 09:50:12 +02:00
parent 3f56020777
commit 9fc7e94074
45 changed files with 1779 additions and 166 deletions

View File

@@ -0,0 +1,56 @@
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.
func NewCrowd(seed rng.Seed, tries int) *Crowd {
r := newRand(seed, "crowd")
lo := r.between(0.1, 0.9-crowdZoneWidth)
return &Crowd{dir: 1, Speed: r.between(0.9, 1.5), ZoneLo: lo, ZoneHi: lo + crowdZoneWidth, 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
}
}
}