Files
halfstreet/src/engine/types.ts
T
ejlewis 46f851bc3a feat(parser): return ambiguous variant when noun matches multiple aliases
Replaces the previous behavior of returning unknown-noun. The dispatcher
will use this in the next commit to prompt the player to disambiguate.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-09 13:53:38 -05:00

84 lines
3.1 KiB
TypeScript

// Engine type definitions. No runtime code — these shapes are the contract
// between the world data, the engine, and the UI.
export type RoomId = string
export type ItemId = string
export type EncounterId = string
export type Direction = 'n' | 's' | 'e' | 'w' | 'u' | 'd'
export type Verb =
| 'go' | 'look' | 'examine' | 'take' | 'drop' | 'use' | 'open' | 'close'
| 'read' | 'light' | 'extinguish' | 'attack' | 'inventory' | 'wait'
| 'hold' | 'push' | 'pull'
export type MetaVerb = 'restart' | 'undo' | 'hint' | 'save' | 'quit' | 'theme'
export interface NounRef {
/** Canonical noun (matches an ItemId, EncounterId, or a directional/world noun). */
canonical: string
/** The raw token(s) the player typed, for narration. */
raw: string
}
export type ParsedCommand =
| { kind: 'verb-only'; verb: Verb | 'look' | 'inventory' | 'wait' }
| { kind: 'verb-target'; verb: Verb; target: NounRef }
| { kind: 'verb-target-prep'; verb: Verb; target: NounRef; preposition: string; indirect: NounRef }
| { kind: 'ambiguous'; verb: Verb; rawNoun: string; candidates: string[] }
| { kind: 'go'; direction: Direction }
| { kind: 'meta'; verb: MetaVerb }
| { kind: 'disambiguation'; chosen: string }
| { kind: 'unknown'; raw: string; reason: 'unknown-verb' | 'unknown-noun' | 'malformed' }
export type ResolveLevel = 'steady' | 'shaken' | 'reeling' | 'returning'
export interface ItemInstance {
id: ItemId
/** Per-instance state: lit/unlit, broken/whole, etc. */
state: Record<string, string | boolean | number | string[]>
}
export type EncounterPhase = string // phase names are encounter-specific
export interface TranscriptLine {
kind: 'narration' | 'player' | 'system' | 'ending'
text: string
}
export interface PendingDisambiguation {
verb: Verb
candidates: string[] // canonical noun ids the player must choose between
prompt: string
}
export interface GameState {
schemaVersion: number
location: RoomId
inventory: ItemInstance[]
/** Per-room state: visited, items dropped, descriptive flags. */
roomState: Record<RoomId, Record<string, string | boolean | number | string[]>>
/** Story-wide flags (e.g. 'gateOpened', 'mirrorTarnished'). */
flags: Record<string, string | boolean | number | string[]>
resolveLevel: ResolveLevel
/** Active encounter phase by encounter id, or null if no encounter is mid-flight. */
encounterState: Record<EncounterId, EncounterPhase>
/** Last referenced noun, for pronoun resolution. */
lastNoun: NounRef | null
/** Pending multi-word disambiguation, set when the parser cannot decide. */
pendingDisambiguation: PendingDisambiguation | null
/** Capped at 200 entries; older entries are dropped on append. */
transcript: TranscriptLine[]
/** Set true when the player has reached an ending. UI shows ending screen. */
endedWith: 'true' | 'wrong' | 'bad' | null
}
export interface DispatchResult {
state: GameState
/** Lines to append to the transcript (already added to state.transcript). */
appended: TranscriptLine[]
}
export const SCHEMA_VERSION = 1
export const TRANSCRIPT_CAP = 200
export const RESOLVE_LEVELS: ResolveLevel[] = ['steady', 'shaken', 'reeling', 'returning']