v0.4.9 — X,Y coordinates, no-switching can still clear the mainline, Q3 corrected, a clearance bug, three sounds, and the box art

A playtest review of seed 58228926 (day 6), plus one long-standing display complaint and the
first real audio beyond a placeholder.

- Coordinate labels read X,Y everywhere shown to a player, not the internal Y,X storage order.
  Display-only.
- "No switching" now means may not add or drop cars, not "never touch it" — these trains can
  still be moved onto Secondary Track to clear the mainline.
- Q3 corrected: Expedite governs WHERE a train may be left standing, not WHEN it leaves. The
  forced same-Stage departure is gone; a new fault costs 1 Revenue if an expedited train is left
  off the station when a Mainline Phase begins. Resolves "3/4 Express prints a rule it can never
  use" as a side effect.
- evaluateClearance now checks every occupant on a Mainline card before offering a judgment
  call, instead of returning on whichever it found first — found while explaining a playtest
  report, fixed with a regression test.
- Three new synthesised sounds: arrive, depart, crash.
- The splash page shows the box art.

Full detail, measurements and reasoning in CHANGELOG.md.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FvU99NEakShRMg3nN3fHAZ
This commit is contained in:
Jesse
2026-08-19 23:51:35 -04:00
co-authored by Claude Sonnet 5
parent 37b1e5b969
commit 859817d173
25 changed files with 1048 additions and 739 deletions
+10 -3
View File
@@ -124,6 +124,13 @@ export type GameEvent =
| { type: 'trainHeld'; trainNumber: number; reason: string }
/** A train whose card pays for standing still (X18 Circus) collected on it. */
| { type: 'trainStoodStill'; trainNumber: number; where: string }
/**
* Q3 — A STATION MASTER FAULT. An expedited train is not to be held anywhere but the station: if
* it is still off the Office square — parked on Secondary Track, say, to clear a switching move —
* when a Mainline Phase begins, that is a failure to keep it ready to highball, and it costs
* Revenue. `where` names the square it was found on. Always followed by a `revenueChanged`.
*/
| { type: 'expediteFault'; player: PlayerIndex; trainNumber: number; where: string }
/**
* `why` is the RULE that let it go now, not a restatement of the move.
*
@@ -134,9 +141,9 @@ export type GameEvent =
*/
| { type: 'trainHighballed'; trainNumber: number; from: string; to: string; why: string }
/**
* `expedited` because it changes what the player must do RIGHT NOW. Q3 — an expedited train
* departs the same Stage it arrives, so there is no Local Operations turn in which to work it;
* every other train stands until the next Mainline Phase and can be switched in between.
* `expedited` flags a train that must not be left standing off the station (Q3) — it may be
* switched normally like any other arrival, but it has to be back on the Office square before the
* next Mainline Phase begins, or `expediteFault` fires.
*/
| { type: 'trainArrived'; trainNumber: number; consist: RollingStock[]; office: string; expedited: boolean }
| { type: 'trainDiverted'; trainNumber: number; to: string; reason: string }