diff --git a/src/engine/dispatcher.ts b/src/engine/dispatcher.ts index 91e047c..20cef42 100644 --- a/src/engine/dispatcher.ts +++ b/src/engine/dispatcher.ts @@ -29,7 +29,6 @@ export function initialStateFor(world: World): GameState { lastNoun: null, pendingDisambiguation: null, transcript: opening, - theme: 'amber', endedWith: null, } } @@ -115,10 +114,10 @@ function narrate(state: GameState, lines: TranscriptLine[]): DispatchResult { function handleMeta(state: GameState, verb: 'restart' | 'undo' | 'hint' | 'save' | 'quit' | 'theme'): DispatchResult { if (verb === 'save') return narrate(state, [{ kind: 'system', text: '(your progress is saved automatically)' }]) - if (verb === 'theme') { - const newTheme = state.theme === 'amber' ? 'ansi' : 'amber' - return narrate({ ...state, theme: newTheme }, [{ kind: 'system', text: `Theme: ${newTheme}.` }]) - } + // 'theme' is a UI preference: the terminal intercepts it before dispatch and + // dispatches a 'halfstreet-toggle-theme' DOM event. The engine no-ops here so + // typing the verb still produces transcript output if the UI ever misses it. + if (verb === 'theme') return narrate(state, [{ kind: 'system', text: '(theme)' }]) // restart / undo / hint / quit are handled by the UI layer (state mutations // require coordination with the save layer and route navigation). The // engine acknowledges them with a no-op narration; the UI intercepts before diff --git a/src/engine/save.test.ts b/src/engine/save.test.ts index 43a8aae..7ac5886 100644 --- a/src/engine/save.test.ts +++ b/src/engine/save.test.ts @@ -14,7 +14,6 @@ const baseState = (overrides: Partial = {}): GameState => ({ lastNoun: null, pendingDisambiguation: null, transcript: [], - theme: 'amber', endedWith: null, ...overrides, }) diff --git a/src/engine/save.ts b/src/engine/save.ts index 3c48523..bbfede1 100644 --- a/src/engine/save.ts +++ b/src/engine/save.ts @@ -48,6 +48,9 @@ export function loadState(): GameState | null { return null } + // Older saves may carry fields no longer in GameState (e.g. `theme` before + // it became a UI-only preference). TypeScript ignores extra fields at runtime; + // bump SCHEMA_VERSION only when the meaning of an existing field changes. return parsed as GameState } diff --git a/src/engine/types.ts b/src/engine/types.ts index b28349f..e68419d 100644 --- a/src/engine/types.ts +++ b/src/engine/types.ts @@ -30,7 +30,6 @@ export type ParsedCommand = | { kind: 'unknown'; raw: string; reason: 'unknown-verb' | 'unknown-noun' | 'malformed' } export type ResolveLevel = 'steady' | 'shaken' | 'reeling' | 'returning' -export type Theme = 'amber' | 'ansi' export interface ItemInstance { id: ItemId @@ -68,7 +67,6 @@ export interface GameState { pendingDisambiguation: PendingDisambiguation | null /** Capped at 200 entries; older entries are dropped on append. */ transcript: TranscriptLine[] - theme: Theme /** Set true when the player has reached an ending. UI shows ending screen. */ endedWith: 'true' | 'wrong' | 'bad' | null }