Skip to content

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.

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().

newGame(config: GameConfig): Snapshot

Start 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(profile: PlayerProfileInput): Snapshot

Complete 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(saveData: SaveData): Snapshot

Restore game state from save data and return a snapshot.

const snapshot = engine.loadGame(saveData);
saveGame(): SaveData

Capture 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(choiceId: string): Snapshot

Process 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(): Snapshot

Advance 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(characterId: string): Snapshot

Start 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(locationId: string): Snapshot

Travel 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(title: string, text: string): Snapshot

Add 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(noteId: string): Snapshot

Remove a player note from the journal.

const snapshot = engine.deleteNote(note.id);
setLocale(locale: string): Snapshot

Change the active language. The next snapshot will have all @key references resolved against the new locale.

const snapshot = engine.setLocale('es');
trackQuest(questId: string): Snapshot

Follow an active quest. An unknown, unstarted, or completed quest is ignored.

clearTrackedQuest(): Snapshot

Stop following the current quest.

getSnapshot(): Snapshot

Get the current snapshot without making any changes. Useful for initial rendering.

const snapshot = engine.getSnapshot();
getState(): GameState

Get 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(): ContentRegistry

Get 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(): Snapshot

Clear the current pending interlude after the renderer has shown it.

const snapshot = engine.dismissInterlude();

These methods support playtest tools, state inspectors, and other development-only workflows.

setTrace(sink: TraceSink | null): void

Send 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(effect: Effect): Snapshot

Apply 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(locationId: string): Snapshot

Jump to any location for testing. Party members move with the player. The jump does not add travel time or run location triggers.

startDialogueAt(dialogueId: string, nodeId: string): Snapshot

Start 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(): 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.

Player action methods follow the same pattern:

  1. Validate inputs
  2. Update internal state
  3. Build and return a snapshot
  4. 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.

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(
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.

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.