the Roster Pass: every train at the Office visible
Builds "The Roster Pass" / "Two Trains, One Card" (station display for multiple trains at one Office). The Office is the one square where more than one train may legally stand at once (one per A/D track), and CellView.train had room for exactly one — a second train at a busy Station was counted in the old A/D pips and never drawn. CellView.train -> CellView.trains: TrainView[], seat-filtered and collecting every match rather than the first (fixes a latent cross-district leak in the process: trainOnCard never checked seat). The A/D pips are replaced with one roster chip per A/D track, always, free or occupied; clicking a chip sets selectedCrew, wiring the board and the action panel to the same value. standingWest moves from CrewTray to TrackCard: two trays sharing one Office card need one shared split, not one each, and there is no such thing as "west of one particular A/D track". No save migration — Save replays through the engine — and a stale value on an emptied card is inert because nothing reads a split with no train standing there. The Division map's Office cell is now sized by A/D capacity rather than occupancy, so it holds still as trains arrive and leave; chips lay into fixed slots instead of centre-spreading onto the Limits cards either side. 584 tests, 0 failures. Verified end-to-end against the built app and a direct render of a 4-train Terminal (screenshotted). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SAt2YCXgd5qCjBcF2x34aK
This commit is contained in:
+17
-6
@@ -110,8 +110,8 @@ const esc = (s: string): string =>
|
||||
*/
|
||||
function piecePreview(links: string[], label: string): string {
|
||||
const cell = {
|
||||
row: 0, col: 0, kind: 'trk', label, running: false, enhancements: [],
|
||||
tray: null, cars: [], facility: null, links, what: '',
|
||||
row: 0, col: 0, kind: 'trk', label, running: false, enhancements: [], enhancementsWhat: [],
|
||||
trains: [], adTracks: null, cars: [], standingWest: 0, facility: null, links, what: '',
|
||||
};
|
||||
return officeSvg([cell] as never, 99).replace(
|
||||
/<text class="bs-(coord|rowlab)"[^>]*>[^<]*<\/text>/g,
|
||||
@@ -301,7 +301,14 @@ function render(): void {
|
||||
: 'ATTACH TO THIS CARD';
|
||||
return [{ row: cell.row, col: cell.col, label }];
|
||||
});
|
||||
grid.innerHTML = officeSvg(f.cells, f.runningRow, ghostCoords, legalCaps, f.limits);
|
||||
grid.innerHTML = officeSvg(f.cells, f.runningRow, ghostCoords, legalCaps, f.limits, selectedCrew);
|
||||
|
||||
// Wire the roster chips: clicking one sets `selectedCrew`, the same value the "Which train are
|
||||
// you switching?" picker writes, so the board and the action panel drive one value either way.
|
||||
for (const g of Array.from(grid.querySelectorAll('g[data-crew]'))) {
|
||||
const trayId = (g as HTMLElement).dataset['crew'];
|
||||
if (trayId) (g as unknown as HTMLElement).onclick = () => { selectedCrew = trayId; render(); };
|
||||
}
|
||||
|
||||
// Highlighting rides on top of the drawing: outline the legal squares and make them clickable.
|
||||
for (const [key, list] of spotsAt) {
|
||||
@@ -337,8 +344,10 @@ function render(): void {
|
||||
*/
|
||||
const crew = pickedCrew(f);
|
||||
if (crew && !forPlay) {
|
||||
const here = grid.querySelector(`g[data-cell="${crew.from.row},${crew.from.col}"]`);
|
||||
if (here) here.classList.add('bs-from');
|
||||
// Marked on the crew strip, not the whole card: the Office is the one square a second train may
|
||||
// share, and outlining the card would claim it belongs to both.
|
||||
const strip = grid.querySelector(`g[data-cell="${crew.from.row},${crew.from.col}"] .bs-crew`);
|
||||
if (strip) strip.classList.add('bs-from');
|
||||
for (const c of crew.to) {
|
||||
const g = grid.querySelector(`g[data-cell="${c.row},${c.col}"]`);
|
||||
if (g) g.classList.add('bs-focus');
|
||||
@@ -629,7 +638,9 @@ function renderDistrict(f: Frame): void {
|
||||
else sec.classList.add('folded');
|
||||
|
||||
const cars = f.cells.reduce((n, c) => n + c.cars.length, 0);
|
||||
const crew = f.cells.filter((c) => c.tray).length;
|
||||
// Trains, not cards-with-a-crew: the Office is the one card that may hold more than one, and a
|
||||
// card-count silently read "1 crew on the board" with two trains standing at a busy Station.
|
||||
const crew = f.cells.reduce((n, c) => n + c.trains.length, 0);
|
||||
$('districtsummary').textContent =
|
||||
`${f.cells.length} cards · ${f.facilities.length} facilities · ${cars} cars standing` +
|
||||
(crew > 0 ? ` · ${crew} crew on the board` : '');
|
||||
|
||||
Reference in New Issue
Block a user