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

@@ -19,7 +19,9 @@ import (
// Stegen skiljs med ';':
//
// wait N vänta N bildrutor
// hold N håll Enter nere N bildrutor (sedelinmatning)
// waittrain vänta tills tåget vid perrongen spelaren står på är inne
// waitstop vänta tills tågresan står vid resmålet
// press KEY tryck enter | esc | r | h | l | up | down en bildruta
// move DIR N gå N rutor åt up | down | left | right (stannar exakt där)
// shot PATH spara aktuell bild som PNG (intern upplösning × shotScale)
@@ -51,8 +53,9 @@ type script struct {
// virtual är den simulerade inputen som input.go läser före riktig input.
var virtual struct {
held sim.Dir
pressed map[string]bool
held sim.Dir
pressed map[string]bool
holdEnter bool
}
func parseScript(src string) (*script, error) {
@@ -64,9 +67,9 @@ func parseScript(src string) (*script, error) {
}
st := scriptStep{cmd: f[0]}
switch f[0] {
case "wait":
case "wait", "hold":
if len(f) != 2 {
return nil, fmt.Errorf("script: 'wait N'")
return nil, fmt.Errorf("script: '%s N'", f[0])
}
st.n, _ = strconv.Atoi(f[1])
case "press":
@@ -88,7 +91,7 @@ func parseScript(src string) (*script, error) {
return nil, fmt.Errorf("script: 'shot PATH'")
}
st.arg = f[1]
case "quit", "waittrain":
case "quit", "waittrain", "waitstop":
default:
return nil, fmt.Errorf("script: okänt kommando %q", f[0])
}
@@ -118,13 +121,17 @@ type playerTiler interface {
}
// trainWaiter implementeras av scener där man kan vänta på ett tåg.
type trainWaiter interface{ TrainHere() bool }
type trainWaiter interface {
TrainHere() bool
RideAtDest() bool
}
// tick körs i början av varje Update. frame är senast ritade bild.
// Returnerar ebiten.Termination när skriptet vill avsluta.
func (s *script) tick(g *Game, frame *ebiten.Image) error {
virtual.held = sim.None
virtual.pressed = map[string]bool{}
virtual.holdEnter = false
if s.i >= len(s.steps) {
return nil
}
@@ -135,6 +142,12 @@ func (s *script) tick(g *Game, frame *ebiten.Image) error {
if s.counter >= st.n {
s.advance()
}
case "hold":
virtual.holdEnter = true
s.counter++
if s.counter >= st.n {
s.advance()
}
case "press":
virtual.pressed[st.arg] = true
s.advance()
@@ -174,6 +187,10 @@ func (s *script) tick(g *Game, frame *ebiten.Image) error {
if tw, ok := g.scene.(trainWaiter); !ok || tw.TrainHere() {
s.advance()
}
case "waitstop":
if tw, ok := g.scene.(trainWaiter); !ok || tw.RideAtDest() {
s.advance()
}
case "shot":
if err := saveShot(frame, st.arg); err != nil {
return err