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
+44 -2
View File
@@ -464,9 +464,36 @@ export function officeSvg(
const spots = cell.facility ? Math.max(1, cell.facility.trackCap) : cell.cars.length;
for (let i = 0; i < Math.min(spots, 4); i++) {
const filled = i < cell.cars.length;
out += `<rect class="bs-slot${filled ? ' bs-occ' : ''}" x="${8 + i * 30}" y="${H - 26}" width="26" height="15" rx="2"/>`;
/**
* WHICH CAR IS STANDING HERE, not just that one is.
*
* The switching game is entirely about getting the RIGHT car to the right industry, so "a car
* is here" is not enough — a Grocer's Warehouse wants a boxcar or a reefer and nothing else
* will do. This printed the first three characters of the label, which for "loaded hopper" and
* "loaded boxcar" alike is `loa`: every car on the board looked the same.
*
* Type by colour and by three letters, loaded by a filled slot and empty by an outlined one —
* the same distinction the printed game makes with coloured tokens.
*/
const car = cell.cars[i] ?? '';
const type = /caboose/.test(car)
? 'cab'
: /hopper/.test(car)
? 'hop'
: /boxcar/.test(car)
? 'box'
: /reefer/.test(car)
? 'rfr'
: /tank/.test(car)
? 'tnk'
: /coach/.test(car)
? 'cch'
: '';
const loaded = /^loaded/.test(car);
const cls = filled ? ` bs-occ bs-car-${type}${loaded ? ' bs-loaded' : ''}` : '';
out += `<rect class="bs-slot${cls}" x="${8 + i * 30}" y="${H - 26}" width="26" height="15" rx="2"/>`;
if (filled) {
out += `<text class="bs-carlab" x="${21 + i * 30}" y="${H - 15}" text-anchor="middle">${esc((cell.cars[i] ?? '').slice(0, 3))}</text>`;
out += `<text class="bs-carlab" x="${21 + i * 30}" y="${H - 15}" text-anchor="middle">${esc(type)}</text>`;
}
}
@@ -535,6 +562,21 @@ export const BOARD_CSS = `
.bs-region{stroke:#4a5361;stroke-width:1.2;stroke-dasharray:3 3}
.bs-slot{fill:none;stroke:#5f6b7a;stroke-width:1.1;stroke-dasharray:3 2}
.bs-slot.bs-occ{fill:rgba(90,169,230,.20);stroke:#5aa9e6;stroke-dasharray:none}
/* CAR TYPE BY COLOUR, LOAD STATE BY FILL. An industry takes one commodity and nothing else, so the
type has to be readable at a glance; and a loaded car and an empty one of the same type are
opposite halves of the same job. Empty rides as an outline, loaded as a solid face. */
.bs-slot.bs-car-box{stroke:#c08a4a}
.bs-slot.bs-car-hop{stroke:#8d97a5}
.bs-slot.bs-car-tnk{stroke:#7a6ab0}
.bs-slot.bs-car-rfr{stroke:#6fb5a0}
.bs-slot.bs-car-cch{stroke:#5aa9e6}
.bs-slot.bs-car-cab{stroke:#c05a5a}
.bs-slot.bs-car-box.bs-loaded{fill:rgba(192,138,74,.42)}
.bs-slot.bs-car-hop.bs-loaded{fill:rgba(141,151,165,.42)}
.bs-slot.bs-car-tnk.bs-loaded{fill:rgba(122,106,176,.42)}
.bs-slot.bs-car-rfr.bs-loaded{fill:rgba(111,181,160,.42)}
.bs-slot.bs-car-cch.bs-loaded{fill:rgba(90,169,230,.42)}
.bs-slot.bs-car-cab{fill:rgba(192,90,90,.42)}
.bs-train rect{fill:#2f6b3d;stroke:#8fd6a0;stroke-width:1.2}
.bs-crew rect{fill:#8a6d1f;stroke:#e0c060;stroke-width:1.2}
.bs-tlab{fill:#eaf6ec;font:600 11px ui-monospace,monospace}