docs(mystery): spec for engine prereqs (verbs, disambiguation, ending UI) #1
@@ -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
|
||||
|
||||
@@ -14,7 +14,6 @@ const baseState = (overrides: Partial<GameState> = {}): GameState => ({
|
||||
lastNoun: null,
|
||||
pendingDisambiguation: null,
|
||||
transcript: [],
|
||||
theme: 'amber',
|
||||
endedWith: null,
|
||||
...overrides,
|
||||
})
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user