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:
Jesse
2026-08-05 08:28:22 -04:00
parent 2d348e1ecf
commit 9c76b1a5e2
28 changed files with 1927 additions and 394 deletions
+13 -8
View File
@@ -18,7 +18,7 @@ import {
OFFICE_PROFILES,
MAINLINE_PROFILES,
mainlineProfile,
TRACK_SUPPLY,
TRACK_CARDS,
ROLLING_STOCK_SUPPLY,
STAGES_PER_DAY,
TIMETABLED_TRAINS,
@@ -90,7 +90,11 @@ export function buildDeck(mode: GameConfig['mode'] = 'competitive'): Card[] {
for (let i = 0; i < c.copies; i++) push({ kind: 'action', key: c.key });
}
}
// Track is NOT in the deck any more — it is a per-player supply (content.ts TRACK_SUPPLY).
// Track is IN the deck, 104 cards of it — the single largest category, and the reason building a
// district costs you the industry or train you did not draw instead.
for (const t of TRACK_CARDS) {
for (let i = 0; i < t.copiesInDeck; i++) push({ kind: 'track', geometry: t.geometry, hand: t.hand });
}
return cards;
}
@@ -152,7 +156,6 @@ function buildOfficeArea(owner: PlayerIndex): OfficeArea {
adOccupancy: [],
heldAtLimits: [],
dispatchUsedToday: [],
trackSupply: new Map(TRACK_SUPPLY.map((t) => [`${t.geometry}:${t.hand}`, t.perPlayer])),
};
}
@@ -254,11 +257,13 @@ export function createGame(opts: SetupOptions): GameState {
hands.set(p, shuffled.slice(cursor, cursor + 3));
cursor += 3;
}
const departments: (CardId | null)[] = [
shuffled[cursor++] ?? null,
shuffled[cursor++] ?? null,
shuffled[cursor++] ?? null,
];
// §4.6-4.7 — three cards turned face up beside the deck. Each is the bottom of a Department pile
// that grows as players discard onto it.
const departments: CardId[][] = [[], [], []];
for (const pile of departments) {
const card = shuffled[cursor++];
if (card) pile.push(card);
}
const homeOffice = shuffled.slice(cursor);
const redFlags = new Map<PlayerIndex, boolean>();