Show the whole Division mainline. district now has option for auto hiding on phases where not needed. scaffold for multi-player geometry showing division. regions on mainline cards implemented. bug fix on laying rail cards. optimized replay (carried links forward). Add sounds, first pass

This commit is contained in:
Jesse
2026-08-04 11:26:18 -04:00
parent ec792dfc15
commit c3c5c59a45
16 changed files with 1249 additions and 129 deletions
+18 -1
View File
@@ -79,6 +79,14 @@ export type Game = {
* against the current rules.
*/
mustPlayCard: boolean;
/**
* Sounds the last batch of events earned, for the page to play and clear.
*
* The model names WHAT happened — a Stage ended, a Day turned, a train was built — and the view
* decides what that sounds like. Detection lives here because this is where events are seen; it
* would be guesswork from a rendered frame.
*/
cues: string[];
};
/** How each intent kind is introduced in the action list, in the order they should appear. */
@@ -114,7 +122,7 @@ export const SOLO_PLAYER = 'Solitaire';
export function newGame(seed: number, config: GameConfig = SOLO_CONFIG): Game {
const state = createGame({ id: `web-${seed}`, seed, config, playerNames: [SOLO_PLAYER] });
const game: Game = { state, seed, history: [], log: [], mustPlayCard: false };
const game: Game = { state, seed, history: [], log: [], mustPlayCard: false, cues: [] };
// A history that opens mid-Stage reads as though something was missed. Say what the game IS
// first, then let the clock take over.
game.log.push({ text: 'Game Begins', tone: 'start' });
@@ -336,6 +344,15 @@ function record(game: Game, events: GameEvent[], actor: PlayerIndex | null = nul
const mine = who !== null && 'player' in e;
const text = mine ? `Player ${who} ${n.text.charAt(0).toLowerCase()}${n.text.slice(1)}` : n.text;
game.log.push({ text, tone: mine ? 'act' : n.tone });
if (e.type === 'trainMadeUp') game.cues.push('train');
if (e.type === 'stageBegan') {
// A Stage BEGINNING is the previous one ending — except the first, which is the game opening
// and has nothing behind it. A Day boundary gets the bell only: sounding both there would
// collide, and the bell is the bigger event.
if (e.day > 1 && e.stage === 1) game.cues.push('day');
else if (e.day > 1 || e.stage > 1) game.cues.push('stage');
}
}
// Keep the log bounded; the full history lives in `history` and can be replayed.
if (game.log.length > 400) game.log.splice(0, game.log.length - 400);