diff --git a/src/engine/types.ts b/src/engine/types.ts new file mode 100644 index 0000000..ad20157 --- /dev/null +++ b/src/engine/types.ts @@ -0,0 +1,84 @@ +// 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: '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 type Theme = 'amber' | 'ansi' + +export interface ItemInstance { + id: ItemId + /** Per-instance state: lit/unlit, broken/whole, etc. */ + state: Record +} + +export type EncounterPhase = string // phase names are encounter-specific + +export interface TranscriptLine { + kind: 'narration' | 'player' | 'system' + 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> + /** Story-wide flags (e.g. 'gateOpened', 'mirrorTarnished'). */ + flags: Record + resolveLevel: ResolveLevel + /** Active encounter phase by encounter id, or null if no encounter is mid-flight. */ + encounterState: Record + /** 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[] + theme: Theme + /** 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'] diff --git a/src/world/types.ts b/src/world/types.ts new file mode 100644 index 0000000..a1449b5 --- /dev/null +++ b/src/world/types.ts @@ -0,0 +1,90 @@ +// World data type definitions. World modules export plain data conforming to +// these shapes; the engine reads but never mutates them. + +import type { RoomId, ItemId, EncounterId, Direction, Verb, EncounterPhase } from '../engine/types' + +export interface RoomDescriptions { + /** Shown the first time the player enters this room. */ + firstVisit: string + /** Shown on subsequent entries. */ + revisit: string + /** Shown when the player types `look` (richer detail). */ + examined: string +} + +export interface Room { + id: RoomId + title: string // e.g. "[ Foyer ]" + descriptions: RoomDescriptions + /** Direction → destination room id. Locked exits are listed in `lockedExits`. */ + exits: Partial> + /** Direction → unlock condition (item id or flag name). */ + lockedExits?: Partial> + /** Items that start in this room. Items the player drops are tracked in roomState. */ + items: ItemId[] + /** Encounter that triggers when this room is entered, or null. */ + encounter?: EncounterId + /** Optional "safe" flag: entering a safe room regenerates one resolve step. */ + safe?: boolean +} + +export interface Item { + id: ItemId + /** Canonical name and any aliases for the parser. */ + names: string[] + /** Short description shown in inventory. */ + short: string + /** Long description shown when examined. */ + long: string + /** Initial per-instance state (e.g. `{ lit: false }`). */ + initialState: Record + /** True if the player can pick it up. */ + takeable: boolean +} + +export interface EncounterPhaseDef { + /** Description shown each turn while in this phase. */ + description: string + transitions: EncounterTransition[] +} + +export interface EncounterTransition { + verb: Verb + /** Required target noun id, or '*' for any target, or undefined for verb-only. */ + target?: ItemId | EncounterId | '*' + /** Required item id in inventory (and optional state predicate). */ + requires?: { item: ItemId; state?: Record } + /** Phase to transition to, or 'resolved' / 'failed'. */ + to: EncounterPhase | 'resolved' | 'failed' + /** Narration on this transition. */ + narration: string + /** Resolve cost for the player on this transition (0–2). */ + resolveCost?: 0 | 1 | 2 +} + +export interface EncounterDef { + id: EncounterId + startsIn: RoomId + initialPhase: EncounterPhase + phases: Record + /** Effects on resolution (set flags, unlock exits). */ + onResolved?: { setFlags?: Record; unlockExits?: { room: RoomId; direction: Direction }[] } + /** What happens at 'failed' (e.g. retreat to previous safe room). */ + onFailed?: { narration: string; retreatTo: RoomId } + /** Default narration for wrong-verb attempts not matching any transition. */ + defaultWrongVerbNarration?: string +} + +export interface World { + startingRoom: RoomId + startingInventory: ItemId[] + rooms: Record + items: Record + encounters: Record + /** Story flag definitions and the endings they unlock. */ + endings: { + true: { whenFlags: Record; narration: string } + wrong: { whenFlags: Record; narration: string } + bad: { whenFlags: Record; narration: string } + } +}