Track becomes a deck card, Departments become decks, and trains must be made up to leave, industry restrictions enforced, update display and add a new game button
This commit is contained in:
@@ -341,6 +341,35 @@ function mainlinePhase(s: GameState, events: GameEvent[]): AdvanceResult {
|
||||
|
||||
type MoveOutcome = 'moved' | 'held' | 'needsClearance' | 'expedited';
|
||||
|
||||
/**
|
||||
* §8.2 — why this train is not fit to run, or null if it is.
|
||||
*
|
||||
* "The engine in front, cars behind, and if there is a caboose, the caboose at the rear."
|
||||
*
|
||||
* DIRECTION-FREE, deliberately. The engine may be at either end of the tray — pulling or pushing —
|
||||
* and which of those counts as "in front" depends on which way the train is pointed, which the tray
|
||||
* does not reliably record for a crew that has been shunting round a siding. What a train may NOT be
|
||||
* is broken-backed: the engine buried among its own cars, with some ahead of it and some behind. The
|
||||
* caboose then has to ride at the far end from the engine, which is the rear whichever way it runs.
|
||||
*
|
||||
* This is reachable purely through switching. A train arrives made up, and only comes apart because
|
||||
* the player took cars onto the nose or picked up a cut in a run-around.
|
||||
*/
|
||||
function badlyMadeUp(tray: CrewTray): string | null {
|
||||
const n = tray.consist.length;
|
||||
if (n === 0) return null;
|
||||
const pulling = tray.engineAt === 0;
|
||||
const pushing = tray.engineAt === n;
|
||||
if (!pulling && !pushing) {
|
||||
return `not made up — the engine is buried in the train, ${tray.engineAt} car(s) ahead of it`;
|
||||
}
|
||||
const caboose = tray.consist.findIndex((c) => c.type === 'caboose');
|
||||
if (caboose === -1) return null;
|
||||
// The rear is the end away from the engine.
|
||||
const rear = pulling ? n - 1 : 0;
|
||||
return caboose === rear ? null : 'not made up — the caboose must be at the rear of the train';
|
||||
}
|
||||
|
||||
/** Puts a train onto a Mainline card with its crossing time already computed. */
|
||||
function enterMainline(
|
||||
s: GameState,
|
||||
@@ -449,6 +478,26 @@ function moveTrain(
|
||||
return 'held';
|
||||
}
|
||||
|
||||
/**
|
||||
* §8.2 — A TRAIN MUST BE MADE UP BEFORE IT MAY LEAVE.
|
||||
*
|
||||
* The engine leads, the cars follow, and a caboose rides at the rear. A train that has been
|
||||
* shunting can easily be in none of those states: taking cars on the nose puts them AHEAD of the
|
||||
* engine ("pushing them into the Facility"), and cars picked up in a run-around land wherever the
|
||||
* approach put them. Nothing checked, so a crew could shove a cut into a siding and then highball
|
||||
* onto the Mainline engine-last with the caboose in the middle.
|
||||
*
|
||||
* The remedy is in the player's hands and is the reason both exist: run around the train, or
|
||||
* spend a Move in a Small Yard, which re-makes it (`consistSorted` puts the engine back on the
|
||||
* nose). Held rather than rejected — a train that cannot leave stays where it is, which is what
|
||||
* makes an A/D track fill up and eventually bite.
|
||||
*/
|
||||
const badOrder = badlyMadeUp(tray);
|
||||
if (badOrder !== null) {
|
||||
events.push({ type: 'trainHeld', trainNumber: tray.trainNumber ?? 0, reason: badOrder });
|
||||
return 'held';
|
||||
}
|
||||
|
||||
const officeIndex = nodeIndexOfOffice(s, owner);
|
||||
const target = officeIndex + dir;
|
||||
const node = s.division.nodes[target];
|
||||
|
||||
Reference in New Issue
Block a user