adding classification and division yard updates, train collisions, engine and car updates, turnout improvements.
This commit is contained in:
+46
-1
@@ -179,6 +179,19 @@ export type Frame = {
|
||||
departments: string[];
|
||||
/** What each face-up Department card does. */
|
||||
departmentsWhat: string[];
|
||||
/**
|
||||
* The two yards, by car type.
|
||||
*
|
||||
* Rolling stock is finite and the Classification Yard only returns to service when the Division
|
||||
* Yard is BARE, so watching the Division Yard run down is now real information — and the game
|
||||
* showed neither yard at all.
|
||||
*/
|
||||
yards: {
|
||||
division: { type: string; loaded: number; empty: number }[];
|
||||
classification: { type: string; loaded: number; empty: number }[];
|
||||
divisionTotal: number;
|
||||
classificationTotal: number;
|
||||
};
|
||||
/** 12 slots; the train number due out at each Stage, or null. */
|
||||
timetable: (number | null)[];
|
||||
blocked: Impediment[];
|
||||
@@ -598,6 +611,12 @@ export function snapshot(
|
||||
deck: s.decks.homeOffice.length,
|
||||
departments: s.decks.departments.map((id) => (id ? cardName(s, id) : '—')),
|
||||
departmentsWhat: s.decks.departments.map((id) => (id ? cardDescription(s, id) : '')),
|
||||
yards: {
|
||||
division: countStock(s.yards.divisionYard),
|
||||
classification: countStock(s.yards.classificationYard),
|
||||
divisionTotal: s.yards.divisionYard.length,
|
||||
classificationTotal: s.yards.classificationYard.length,
|
||||
},
|
||||
timetable: [...s.timetable],
|
||||
decision,
|
||||
wasted,
|
||||
@@ -889,12 +908,38 @@ const MODIFIER_NAMES: Record<string, string> = {
|
||||
};
|
||||
|
||||
/** A train on the board, with what it is carrying — otherwise a run looks identical empty or full. */
|
||||
/** Cars in a yard, grouped by type and split loaded / empty, in a stable order. */
|
||||
function countStock(
|
||||
stock: readonly { type: string; loaded: boolean }[],
|
||||
): { type: string; loaded: number; empty: number }[] {
|
||||
const order = ['coach', 'boxcar', 'reefer', 'hopper', 'tank', 'caboose'];
|
||||
const by = new Map<string, { type: string; loaded: number; empty: number }>();
|
||||
for (const c of stock) {
|
||||
const row = by.get(c.type) ?? { type: c.type, loaded: 0, empty: 0 };
|
||||
if (c.loaded) row.loaded += 1;
|
||||
else row.empty += 1;
|
||||
by.set(c.type, row);
|
||||
}
|
||||
return [...by.values()].sort((a, b) => order.indexOf(a.type) - order.indexOf(b.type));
|
||||
}
|
||||
|
||||
function trainChip(s: GameState, id: string): TrainChip {
|
||||
const t = s.trays.get(id);
|
||||
if (!t) return { label: id, consist: [] };
|
||||
/**
|
||||
* The engine is drawn IN the consist, at the position it occupies.
|
||||
*
|
||||
* A Crew Tray is an engine plus its Rolling Stock, and the engine may be pulling, pushing, or in
|
||||
* the middle doing both — which is a thing a player has to be able to see, since it decides which
|
||||
* end cars couple onto (§A.3) and which way the train can shove. It was recorded as a boolean
|
||||
* that nothing read, so every consist was drawn as an anonymous row of cars.
|
||||
*/
|
||||
const cars = t.consist.map(carLabel);
|
||||
const at = Math.max(0, Math.min(cars.length, t.engineAt));
|
||||
cars.splice(at, 0, 'ENG');
|
||||
return {
|
||||
label: t.trainNumber === null ? 'crew' : `T${t.trainIsExtra ? 'X' : ''}${t.trainNumber}`,
|
||||
consist: t.consist.map(carLabel),
|
||||
consist: cars,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user