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:
+40
-16
@@ -9,6 +9,7 @@
|
||||
import type {
|
||||
CarType,
|
||||
Direction,
|
||||
Hand,
|
||||
MainlineKind,
|
||||
FreightKind,
|
||||
GameLength,
|
||||
@@ -127,11 +128,6 @@ export type OfficeArea = {
|
||||
heldAtLimits: TrayId[];
|
||||
/** Dispatch bonuses spent this Day, by enhancement key — each is once a Day. */
|
||||
dispatchUsedToday: string[];
|
||||
/**
|
||||
* The player's personal track supply (26 pieces), keyed `geometry:hand`. Track is NOT in the
|
||||
* Home Office deck — it is laid from here.
|
||||
*/
|
||||
trackSupply: Map<string, number>;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -192,10 +188,24 @@ export function spaceOn(card: TrackCard): number {
|
||||
|
||||
export function isOperationalRail(card: TrackCard): boolean {
|
||||
if (!card.baseOperationalRail) return false;
|
||||
if (card.facility && card.facility.menAtWork.some((slot) => slot !== null)) return false;
|
||||
if (isLockedByWork(card)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* §9.3 — "While ANY loads are in the MEN | AT | WORK track, the industry's track is locked down for
|
||||
* safety reasons. It loses its status as Operational Rail. No cars can be picked up or dropped off,
|
||||
* and **no trains may occupy or move on it**."
|
||||
*
|
||||
* That last clause is stronger than losing Operational Rail status. A turnout is not Operational
|
||||
* Rail either, and a train runs straight through one — so "cannot stop here" and "cannot pass
|
||||
* through here" are different properties, and only a locked industry has both. Kept separate from
|
||||
* `isOperationalRail` for exactly that reason.
|
||||
*/
|
||||
export function isLockedByWork(card: TrackCard): boolean {
|
||||
return !!card.facility && card.facility.menAtWork.some((slot) => slot !== null);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Trains
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -223,7 +233,16 @@ export type CrewTray = {
|
||||
* the four-car limit (§A.4) is a limit on cars, not on the locomotive hauling them.
|
||||
*/
|
||||
engineAt: number;
|
||||
/** ORDERED, left-to-right. Max 4 including any caboose (§A.4). */
|
||||
/**
|
||||
* ORDERED NOSE FIRST: index 0 is the end nearest the front of the train, the last index is the
|
||||
* tail. Max 4 including any caboose (§A.4); the engine is not one of them.
|
||||
*
|
||||
* Both diagrams in Appendix A read this way. A train drawn `[<ENG][yellow][brown][blue][red]`
|
||||
* drops "red as he starts, then brown and blue", ending with only yellow — cars come off the TAIL,
|
||||
* which is the end of the array. And a train that picks cars up on its nose gets them "in the
|
||||
* order that they were in, pushing them into the Facility" — ahead of the engine, which is why
|
||||
* `engineAt` moves when it happens.
|
||||
*/
|
||||
consist: RollingStock[];
|
||||
direction: Direction;
|
||||
/**
|
||||
@@ -298,7 +317,8 @@ export type CardKind =
|
||||
| { kind: 'office'; tier: OfficeTier }
|
||||
| { kind: 'freightFacility'; facility: FreightKind }
|
||||
| { kind: 'modifier'; modifier: ModifierKind }
|
||||
| { kind: 'track'; geometry: TrackGeometry }
|
||||
/** Handedness is printed on the card: it is the diagonal the 45° leg lies on. */
|
||||
| { kind: 'track'; geometry: TrackGeometry; hand: Hand }
|
||||
/**
|
||||
* The four categories recovered from the design. They are in the deck so the composition is
|
||||
* right; their BEHAVIOUR is not implemented yet (implications.md §10 is still open), so
|
||||
@@ -315,8 +335,18 @@ export type Card = { id: CardId; kind: CardKind };
|
||||
export type Decks = {
|
||||
/** Face down. Order is SECRET — never projected to any client. */
|
||||
homeOffice: CardId[];
|
||||
/** Three face-up market slots fed from the deck (§2.6). */
|
||||
departments: (CardId | null)[];
|
||||
/**
|
||||
* THE THREE DEPARTMENT DECKS — shared by every player, and stacks rather than single slots.
|
||||
*
|
||||
* §6.2: a discard goes "face up ON TOP of one of the three Department slots", and a draw takes
|
||||
* "the TOP face-up card". Both phrases only mean anything if a Department is a pile, and modelling
|
||||
* them as one-card slots silently DESTROYED whatever was underneath — discarding onto an occupied
|
||||
* slot overwrote it, leaking cards out of a closed deck.
|
||||
*
|
||||
* Last element is the top of the pile: the card that is face up, the only one that may be drawn,
|
||||
* and the one a discarding player is choosing to offer or to bury.
|
||||
*/
|
||||
departments: CardId[][];
|
||||
/** Face up, so players can audit discards (§2.6). */
|
||||
salvageYard: CardId[];
|
||||
/** Private to the owner. Max 3, or 4 with a Red Flag. */
|
||||
@@ -412,11 +442,6 @@ export type TurnState = {
|
||||
option: 'switch' | 'draw' | 'freightAgent' | null;
|
||||
movesRemaining: number;
|
||||
drawnThisTurn: boolean;
|
||||
/**
|
||||
* Track was a CARD before it became a per-player supply, so it was naturally limited to one play
|
||||
* a turn. The supply has 26 pieces and nothing else bounds it, so keep that limit explicitly.
|
||||
*/
|
||||
laidThisTurn: boolean;
|
||||
freightAgentUsed: boolean;
|
||||
/** Set when the actor finishes; the phase driver then moves to the next player. */
|
||||
done: boolean;
|
||||
@@ -427,7 +452,6 @@ export function freshTurn(moves: number): TurnState {
|
||||
option: null,
|
||||
movesRemaining: moves,
|
||||
drawnThisTurn: false,
|
||||
laidThisTurn: false,
|
||||
freightAgentUsed: false,
|
||||
done: false,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user