v0.4.4 — New Game dialog for the opening hand and the three revenue rates, Yard renamed Interchange, and two movement bugs behind a mirrored consist

This commit is contained in:
Jesse
2026-08-14 22:39:50 -04:00
parent d1314066bd
commit 6655e20ea8
29 changed files with 5450 additions and 3902 deletions
+11 -3
View File
@@ -17,10 +17,12 @@
import type { Intent } from '../engine/intents.ts';
import type { Frame } from '../sim/view.ts';
import type { GameConfig, PlayerIndex } from '../engine/state.ts';
import type { PlayerIndex } from '../engine/state.ts';
import type { HouseRuleOverrides } from '../engine/content.ts';
import type { Game, Menu, Save } from './game.ts';
import {
actionMenu,
configWith,
currentActor,
fromSave,
handPlayable,
@@ -112,9 +114,15 @@ export type LocalSession = Session & {
*
* `Game` is mutated in place by `submit`, so the wrapper keeps a mutable reference rather than
* copying — `undo` and `restore` replace the whole game, which is why `game` is a getter.
*
* It takes `rules` rather than a whole `GameConfig` because DEALING is the only thing on the far
* side of this that the page is allowed to decide. A config carries the mode, the victory condition
* and the optional rules — table settings a lobby owns — and handing main.ts a `GameConfig` to build
* meant importing the engine's own defaults into the page, which is the boundary `session.test.ts`
* guards. The seed and the house rules are the two things a player picks when they press New game.
*/
export function createLocalSession(seed: number, config?: GameConfig): LocalSession {
let game: Game = config ? newGame(seed, config) : newGame(seed);
export function createLocalSession(seed: number, rules?: HouseRuleOverrides): LocalSession {
let game: Game = rules ? newGame(seed, configWith(rules)) : newGame(seed);
const listeners = new Set<() => void>();
const changed = (): void => {
for (const fn of [...listeners]) fn();