Engine API
The Engine class runs the game. It receives a content registry, which is the loaded collection of game definitions, and tracks the changing GameState: the current location, flags, variables, inventory, quests, and other progress. Each player action returns a snapshot containing the data the renderer needs for the current game screen.
Constructor
Section titled “Constructor”new Engine(registry: ContentRegistry, state?: GameState)| Parameter | Type | Description |
|---|---|---|
registry |
ContentRegistry |
All game content (locations, characters, dialogues, etc.) |
state |
GameState |
Existing game state, usually from a save |
Omit state when starting a new game with newGame().
Methods
Section titled “Methods”newGame
Section titled “newGame”newGame(config: GameConfig): SnapshotStart a new game. Initializes state from config, sets up character and item locations from the registry, and checks for triggered dialogues and interludes at the starting location.
const engine = new Engine(registry);const snapshot = engine.newGame(config);setPlayerProfile
Section titled “setPlayerProfile”setPlayerProfile(profile: PlayerProfileInput): SnapshotComplete a player-entered profile when content/game.yaml sets
playerCreatesProfile: true. name is required after trimming; title and
biography are optional. A custom renderer may also supply portrait.
const snapshot = engine.setPlayerProfile({ name: 'Aria', title: 'Warden', biography: 'A ranger from the northern road.',});The built-in React renderer calls this method through
actions.setPlayerProfile.
loadGame
Section titled “loadGame”loadGame(saveData: SaveData): SnapshotRestore game state from save data and return a snapshot.
const snapshot = engine.loadGame(saveData);saveGame
Section titled “saveGame”saveGame(): SaveDataCapture the current game state as serializable save data.
const saveData = engine.saveGame();localStorage.setItem('save', JSON.stringify(saveData));Returns:
interface SaveData { version: string; // "1.0" timestamp: string; // ISO 8601 state: GameState; // Complete state}selectChoice
Section titled “selectChoice”selectChoice(choiceId: string): SnapshotProcess a player’s dialogue choice. Applies the choice’s effects, advances to the next dialogue node, and applies that node’s effects.
The dialogue ends when no next node exists. Outside dialogue, the method returns the current snapshot unchanged.
const snapshot = engine.selectChoice('choice_buy_drink');continueDialogue
Section titled “continueDialogue”continueDialogue(): SnapshotAdvance past a text-only dialogue node (a node with text but no choices). If the current node has a next node, advances to it. If there is no next node, the dialogue ends.
The method leaves the snapshot unchanged outside dialogue or when the current node has choices.
const snapshot = engine.continueDialogue();talkTo
Section titled “talkTo”talkTo(characterId: string): SnapshotStart a conversation with a character. Looks up the character’s dialogue field, finds the start node, and begins the dialogue.
const snapshot = engine.talkTo('bartender');travelTo
Section titled “travelTo”travelTo(locationId: string): SnapshotTravel to a location on the current map. The current map is the map that contains the player’s current location. A successful travel advances time, moves party members to the destination, ends any active dialogue, clears a dialogue music override so the destination’s music can resume, and checks for triggered dialogues and interludes at the destination.
Travel time is round(distance / scale) in hours, using the straight-line distance between the two markers, with a minimum of 1 hour.
The snapshot remains unchanged when the map is disabled (mapEnabled: false), the current location has no map, the destination is on a different map, or scale is zero, negative, or not a number.
const snapshot = engine.travelTo('market');writeNote
Section titled “writeNote”writeNote(title: string, text: string): SnapshotAdd a player note to the journal. The engine gives each note a unique ID.
const snapshot = engine.writeNote('Clue', 'The bartender mentioned a coin...');deleteNote
Section titled “deleteNote”deleteNote(noteId: string): SnapshotRemove a player note from the journal.
const snapshot = engine.deleteNote(note.id);setLocale
Section titled “setLocale”setLocale(locale: string): SnapshotChange the active language. The next snapshot will have all @key references resolved against the new locale.
const snapshot = engine.setLocale('es');trackQuest
Section titled “trackQuest”trackQuest(questId: string): SnapshotFollow an active quest. An unknown, unstarted, or completed quest is ignored.
clearTrackedQuest
Section titled “clearTrackedQuest”clearTrackedQuest(): SnapshotStop following the current quest.
getSnapshot
Section titled “getSnapshot”getSnapshot(): SnapshotGet the current snapshot without making any changes. Useful for initial rendering.
const snapshot = engine.getSnapshot();getState
Section titled “getState”getState(): GameStateGet a complete copy of current game progress, including flags, variables, inventory, quests, and character state. Editing the returned value does not change the running game.
const state = engine.getState();console.log(state.flags);getRegistry
Section titled “getRegistry”getRegistry(): ContentRegistryGet a complete copy of the content loaded by the engine. Editing the returned value does not change the content used by the running game.
const content = engine.getRegistry();console.log(content.dialogues);dismissInterlude
Section titled “dismissInterlude”dismissInterlude(): SnapshotClear the current pending interlude after the renderer has shown it.
const snapshot = engine.dismissInterlude();Debug and editor methods
Section titled “Debug and editor methods”These methods support playtest tools, state inspectors, and other development-only workflows.
setTrace
Section titled “setTrace”setTrace(sink: TraceSink | null): voidSend engine decisions to a debug tool while it runs. A trace sink can receive
node, condition, effect, transition, hidden-choice, and error events. Pass
null to stop tracing.
applyDebugEffect
Section titled “applyDebugEffect”applyDebugEffect(effect: Effect): SnapshotApply one effect to the current play session and return the updated snapshot. The effect uses the same processing as an effect written in game content.
teleport
Section titled “teleport”teleport(locationId: string): SnapshotJump to any location for testing. Party members move with the player. The jump does not add travel time or run location triggers.
startDialogueAt
Section titled “startDialogueAt”startDialogueAt(dialogueId: string, nodeId: string): SnapshotStart a dialogue at a chosen node for testing. The node’s effects run normally, and a silent node advances normally. If the dialogue or node is missing, the game remains at its current state.
explainChoices
Section titled “explainChoices”explainChoices(): ChoiceVisibility[]Report whether each choice on the current dialogue node is visible. A hidden choice includes its first failed condition and the state values that condition read. Returns an empty array when no dialogue is active.
Data Flow
Section titled “Data Flow”Player action methods follow the same pattern:
- Validate inputs
- Update internal state
- Build and return a snapshot
- Clear transient state for action-produced snapshots
Transient state such as notifications, pending sounds, pending video, and pending interludes appears in the snapshot returned by the action that produced it. getSnapshot() is a read-only snapshot and does not consume transient state. Use explicit renderer actions such as dismissInterlude() when presentation state needs to be cleared.
Triggered Dialogues
Section titled “Triggered Dialogues”After newGame() and travelTo(), the engine checks for dialogues with a triggerLocation matching the current location. The first dialogue whose conditions pass begins automatically. One triggered dialogue can begin per location change.
The engine also checks triggered interludes after newGame() and travelTo(). If an interlude’s triggerLocation and triggerConditions match, the snapshot includes it as pendingInterlude.
resolveText
Section titled “resolveText”resolveText( text: string, localeData: LocaleData, variables?: Record<string, number | string>, characters?: Record< string, { name: string; title: string; biography: string; stats: Record<string, number | string>; } >): string| Parameter | Type | Description |
|---|---|---|
text |
string |
A @key or plain text |
localeData |
LocaleData |
Locale dictionary for the current language |
variables |
Record<string, number | string> |
Values for {variable} placeholders |
characters |
Character values by ID | Values for {id.name}, {id.title}, {id.biography}, and {id.stats.key} |
Text starting with @ is looked up in localeData, and a missing key returns the @key itself. Text without @ is returned as written. Placeholders are filled in afterwards from variables and characters, and any placeholder without a matching value is left as written.
import { resolveText } from '@doodle-engine/core';
resolveText('@bluff.rolled', localeData, { bluffRoll: 17 });// "You spin the tale with 17 on your roll, and Marcus listens carefully."The engine calls this while building a snapshot, so a renderer receives finished text.
Text Formatting
Section titled “Text Formatting”parseRichText
Section titled “parseRichText”parseRichText(text: string): RichTextSegment[]Convert resolved dialogue or choice text into ordered segments with bold,
italic, and color properties for a custom renderer to apply.
import { parseRichText } from '@doodle-engine/core';
const segments = parseRichText('Take the cE5C453[*key*].');// [// { text: 'Take the ' },// { text: 'key', bold: true, color: '#E5C453' },// { text: '.' },// ]Format Dialogue Text for the complete formatting syntax.