v0.4.9d — three playtest bugs: a train that looked gone, an unload that ignored the pick, a hang that wasn't one
Patched directly onto 0.4.9a rather than the in-progress 0.5.0 line. A switching train vanished from the board the moment it left the Office. `selectedTrain` in `officeSvg` (board-svg.ts), which gates whether a card draws a train's crew badge, was only ever assigned inside the `cell.adTracks !== null` branch — true for the Office card alone. A train standing anywhere else, which is everywhere it stands while actually being switched, drew no badge at all. Game state was never affected; confirmed against the reported save with the engine directly. Fix hoists the assignment out of the guard so it runs for any card with a train on it. Unloading always took the westmost car, whichever one was picked. `laborer.beginUnload` carried the player's chosen `carIndex`, and `check()` validated that specific car, but the `unloadBegan` event it produced carried only the car's type — the reducer that performs the swap re-derived the target with `industryTrack.cars.findIndex(c => c.loaded)`, which always answers the first loaded car in track order regardless of what was requested. Fix adds `carIndex` to the event and uses it directly. A legal decision could render with zero buttons, which looked exactly like a hang. The "where does this Extra start" decision is titled by `trainCardTitle`, beginning "Making up Extra X22…" — the same prefix `renderActions()` stripped from the action list on the assumption it only ever belonged to the separate yard-chip car-placement panel. With no tray yet being filled, that panel is null, so nothing rendered the Extra's decision either: a legal, correctly-computed option with no button anywhere on the page. Replaying the reported save found no engine deadlock at any step — the engine always had a move. Fix matches the exact title of the one group the yard-chip panel covers instead of a title-prefix regex. New tests for all three: a train parked on an ordinary facility card must draw its crew badge; three differently-typed loaded cars, unloading index 2, must leave indices 0 and 1 alone; the existing softlock regression test now mirrors the real render filter instead of only the raw menu, plus a deterministic test for the exact reported scenario. 590 tests, 0 failures.
This commit is contained in:
+18
-2
@@ -1663,7 +1663,13 @@ function execute(s: GameState, player: PlayerIndex, i: Intent): GameEvent[] {
|
||||
case 'laborer.beginUnload': {
|
||||
const f = facilityAt(s, player, i.at)!;
|
||||
return [
|
||||
{ type: 'unloadBegan', player, at: i.at, carType: f.industryTrack.cars[i.carIndex]!.type },
|
||||
{
|
||||
type: 'unloadBegan',
|
||||
player,
|
||||
at: i.at,
|
||||
carType: f.industryTrack.cars[i.carIndex]!.type,
|
||||
carIndex: i.carIndex,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -2206,7 +2212,17 @@ export function reduce(s: GameState, e: GameEvent): void {
|
||||
|
||||
case 'unloadBegan': {
|
||||
const f = facilityAt(s, e.player, e.at)!;
|
||||
const ci = f.industryTrack.cars.findIndex((c) => c.loaded);
|
||||
/**
|
||||
* THE CAR THE PLAYER PICKED, not merely "a loaded one".
|
||||
*
|
||||
* REPORTED: with several loaded cars spotted on one industry track, unloading always took the
|
||||
* WESTMOST car regardless of which one was chosen. This used to re-derive the target with
|
||||
* `industryTrack.cars.findIndex(c => c.loaded)`, which returns the same answer — the first
|
||||
* loaded car in track order — no matter which `carIndex` the intent actually named; `check`
|
||||
* had already validated that specific car, and the choice was thrown away between there and
|
||||
* here. `e.carIndex` is exactly what `laborer.beginUnload`'s reducer read off the intent.
|
||||
*/
|
||||
const ci = e.carIndex;
|
||||
if (ci >= 0) {
|
||||
/**
|
||||
* §9.3 — the replacement empty comes out of the Division Yard. Conjuring it here is what
|
||||
|
||||
Reference in New Issue
Block a user