Fixed division end of track labels and other minor bugs. Trains now show their information. Play cards now required to get to 3 or less, no other restrictions. auto hide now more prominent. it and sounds now are in replay.

This commit is contained in:
Jesse
2026-08-04 13:43:28 -04:00
parent c3c5c59a45
commit 4bba991f41
11 changed files with 466 additions and 161 deletions
+24 -9
View File
@@ -7,7 +7,7 @@
import { BOARD_CSS, divisionSvg, officeSvg } from '../sim/board-svg.ts';
import { TOOLTIP_CSS, installTooltips } from './tooltip.ts';
import { play } from './sound.ts';
import { playCue } from './sound.ts';
import type { Game } from './game.ts';
import {
actionMenu,
@@ -292,7 +292,7 @@ function render(): void {
// Drain whatever the last batch of events earned. Cleared either way, so turning sound on does
// not then play a backlog of everything that happened while it was off.
const cues = game.cues.splice(0, game.cues.length);
if (soundOn) for (const c of cues) play(c);
if (soundOn) for (const c of cues) playCue(c);
save();
}
@@ -309,10 +309,17 @@ function renderDistrict(f: ReturnType<typeof view>): void {
`${f.cells.length} cards · ${f.facilities.length} facilities · ${cars} cars standing` +
(crew > 0 ? ` · ${crew} crew on the board` : '');
// Say what pressing it DOES, not what the panel is currently doing. "auto · folded" reads as a
// status line and was missed entirely; "always show" is an instruction.
const btn = $('districttoggle');
btn.textContent = districtMode === 'auto' ? (open ? 'auto · open' : 'auto · folded') : districtMode;
btn.textContent =
districtMode === 'auto'
? (open ? 'auto-hide: on — click to keep open' : 'auto-hide: on — click to show')
: districtMode === 'open'
? 'always showing — click for auto-hide'
: 'always hidden — click for auto-hide';
btn.onclick = () => {
// auto -> the opposite of what it is doing now -> back to auto.
// auto -> pin it to the opposite of what auto is doing -> back to auto.
districtMode = districtMode === 'auto' ? (open ? 'closed' : 'open') : 'auto';
render();
};
@@ -382,10 +389,7 @@ function renderActions(menu: ReturnType<typeof actionMenu>): void {
// §6.2 — a drawn card has to be played or discarded before the turn can end. Keyed on
// the INTENT, not the label: matching button text would break the moment the wording
// changed, and would have caught `switch.end` too.
const blocked = menu.options[a.index]?.type === 'draw.end' && game.mustPlayCard;
return blocked
? `<button class="act blocked" disabled data-tip="You have drawn a card. Play it or discard it to a Department slot before ending your turn — §6.2 holds you to three cards.">${esc(a.label)} — play or discard first</button>`
: actionButton(a.label, a.index);
return actionButton(a.label, a.index);
})
.join('') +
`</div>`,
@@ -417,6 +421,17 @@ function renderActions(menu: ReturnType<typeof actionMenu>): void {
}
html += `</div>`;
}
if (
game.state.clock.phase === 'localOps' &&
game.state.turn.option === 'draw' &&
!menu.options.some((i) => i.type === 'draw.end')
) {
const hand = (game.state.decks.hands.get(0) ?? []).length;
html +=
`<div class="grp"><button class="act blocked" disabled data-tip="§6.2 — you may not end a turn holding more than three cards (four with a Red Flag). Play one onto the board, or discard one face-up to a Department slot.">` +
`End Local Operations — play or discard down to three first (holding ${hand})</button></div>`;
}
el.innerHTML = html;
for (const b of Array.from(el.querySelectorAll('button.act'))) {
@@ -502,7 +517,7 @@ if (soundBtn) {
paint();
// Confirm the change audibly — the one press where a sound is unambiguously wanted, and it
// doubles as the user gesture the browser needs before any audio may start.
if (soundOn) play('stage');
if (soundOn) playCue('stage');
};
paint();
}