Skip to main content

State & Simulation Lifecycle

Drafft separates authored data from simulated state.

This distinction is essential to understanding how scripts behave, how the Dialogue Simulator works, and how to debug narrative flow without coupling logic to export or runtime.

This page describes how state exists, changes, and resets during authoring.


Core Concepts

Defaults (Authored State)

Defaults are the values you author in Drafft documents:

  • Variables
  • Quest properties
  • Actor properties
  • Items, flags, and other design data

Defaults represent authorial intent.

They are:

  • Stable
  • Version-controlled
  • Exported as part of the project
  • Never mutated by scripts

Live Scenario (Simulated State)

The Live Scenario represents the current simulated world state while authoring.

It is derived from defaults and then modified by script execution or manual data entry.

tip

Think of the Live Scenario as: “What is true right now, based on what I’ve simulated so far?”


State Lifecycle

1. Initialization

When a project or editor session starts:

Live Scenario = Defaults

At this point, no simulation has occurred.


2. Script Execution

When a script is simulated:

@set variables.aries.mood = "angry"
  • The script runs against the Live Scenario
  • Mutations apply immediately
  • Subsequent scripts see the updated state

Example:

::Narrator:: Aries looks ${variables.aries.mood}.

After simulation, this resolves to:

Aries looks angry.

3. Continuity Across Scripts

The Live Scenario persists across scripts.

If Script A sets:

@set variables.aries.mood = "angry"

Then Script B, authored later, will observe:

${variables.aries.mood} → "angry"

This matches natural author expectations during narrative development.


4. Resetting State

At any point, the Live Scenario can be reset . This clears all simulated mutations and restores the authored baseline.


Write to Live Scenario Toggle

By default, simulation writes to the Live Scenario.

However, Drafft provides a toggle:

Write to Live Scenario

Enabled (default)

  • Script mutations persist
  • State carries across simulations
  • Ideal for narrative authoring and iteration

Disabled (ephemeral simulation)

  • Scripts run against a temporary copy of state
  • All changes are discarded after execution
  • Live Scenario remains unchanged

This mode is useful for:

  • Testing edge cases
  • Previewing alternative branches
  • Verifying conditional logic without affecting ongoing work

Divergence Detection

The Live Scenario compares itself against the currently loaded scenario to detect divergence. This comparison is snapshot-based.

When Does Divergence Occur?

Divergence is only detected when:

  1. A scenario is loaded into the Live Scenario
  2. You modify values (manually via the Variables Inspector or via script execution)
  3. The modified values differ from the loaded scenario's snapshot
warning

Divergence only exists when a scenario is loaded. If no scenario is active, there is no divergence to track.

Visual Indicator

When divergence is detected:

  • A divergence indicator (an asterisk) appears in the UI
  • The "Update Source Scenario" button becomes enabled
  • The "Discard Changes" button becomes enabled

Divergence vs Changes

  • Divergence: Live state differs from a loaded scenario's snapshot
  • No divergence + scenario loaded: Live matches the loaded scenario exactly (clean state)
  • Changes with no scenario loaded: Not tracked as divergence; considered a new scenario if saved

Scenarios as Diffs

While the Live Scenario is ephemeral during authoring, you can save and restore snapshots of it using scenarios.

important

Scenarios are diffs, not complete snapshots.

A scenario stores only the values that differ from defaults, not the entire state. This is similar to a patch file.

Why Diffs?

  • Smaller files: Only overridden values are stored
  • Clearer intent: You see exactly which defaults were modified
  • Better version control: Diffs are easier to review and merge

Example

If your defaults contain:

{
"player": {
"level": 1,
"hp": 100,
"status": "healthy"
}
}

And during authoring you change only status to "sad", the saved scenario will contain:

{
"player": {
"status": "sad"
}
}

When you load this scenario, it overwrites only the status field while preserving level: 1 and hp: 100 from defaults.

Current Scope

Scenarios currently save Variable state only. Support for additional collections (Actors, Quests, Items, etc.) is planned for future releases.

Use Cases

  • "Chapter 2 – After Library"
  • "Boss Fight Setup"
  • Bug reproduction states
  • QA handoff
  • Testing alternative narrative branches

Scenarios:

  • Do not affect defaults
  • Can be loaded and restored into the Live Scenario
  • Are optional, not required for normal authoring

Live Scenario Buttons

The Live Scenario page provides several buttons for managing scenarios. Here's what each does:

Save as New Scenario

Button: 💾 Save as New Scenario

Serializes the current Live Scenario state and creates a new scenario document.

What it saves:

  • Compares live state against defaults
  • Stores only the values that differ
  • Creates a new scenario document with a user-provided name

When to use:

  • After reaching a meaningful narrative checkpoint
  • Before testing alternative branches
  • To create reproducible QA states
tip

You can also open the Live Scenario page by right clicking on the current scenario in the status bar.


Update Source Scenario

Button: 🔄 Update Source Scenario (enabled only when divergence is detected)

Serializes current live divergence and updates the source scenario document in the editor.

What it does:

  1. Compares current live state against the loaded scenario's snapshot
  2. Serializes only the differences (the new diff)
  3. Opens the scenario document in the editor tab (or focuses existing tab)
  4. Updates the editor buffer with the new content
  5. Waits for you to explicitly save (preserves review/approval workflow)

Multi-user safety:

  • Checks if scenario is locked by another user
  • Prevents automatic overwrites
  • User remains in editor before committing changes

When to use:

  • You've made changes during scripting that should become the new baseline
  • You want to refine an existing scenario incrementally

Discard Changes

Button: 🔄 Discard Changes (enabled only when divergence is detected)

Reloads the original loaded scenario back into the Live Scenario, discarding all modifications.

What it does:

  1. Clears all live state
  2. Reapplies the loaded scenario's snapshot
  3. Removes the divergence indicator

When to use:

  • You made experimental changes and want to return to the scenario baseline
  • You want to reset to a known state without reloading from disk

Reset to Defaults

Button: 🔄 Reset to Defaults

Clears all live state and unloads any active scenario, returning to the pure authored defaults.

What it does:

  1. Clears all live variable values
  2. Clears the loaded scenario snapshot
  3. Unsets the active scenario
  4. Removes all divergence tracking

Differs from "Discard Changes" because:

  • It removes the scenario context entirely
  • You start fresh from authored defaults, not a scenario snapshot
  • Useful for starting a new simulation from baseline

When to use:

  • Starting a new test or simulation
  • Clearing all runtime state after a session
  • Testing default-only behavior without scenario modifications

State Resolution in Scripts

All state access follows the same resolution rule:

<collection>.<_sid>.<property>

Examples:

variables.player.courage
variables.player.inventory[0].name

// other collections will be implemented in future updates, but the same resolution applies:
actors.Aries.mood
quest.forestTrial.completed

Resolution Order (Three-Tier Fallback)

When you access a value, Drafft resolves it in this order:

  1. Live Scenario ← Used first if available
  2. Defaults ← Used if no live value exists
  3. Undefined ← If neither exists
// If live has modified this value:
${variables.player.mood} → resolves to live value

// If live doesn't have this, falls back to defaults:
${variables.player.level} → resolves to default value

// If neither exists:
${variables.nonexistent} → undefined

Accessing Defaults Explicitly

To access authored values and bypass any live modifications, use the defaults root:

${defaults.variables.player.courage} → always uses authored default

This is useful for:

  • Debugging state divergence
  • Checking what the original value was
  • Exporting context-free content

Context-Aware Resolution

Dependending on context, resolution behavior changes:

ContextResolutionUse Case
AuthoringLive → DefaultsScript simulation, testing
ExportDefaults onlyPure narrative content
RuntimeEngine-defined (script-bound)Your target platform

This separation ensures your exports remain pure and unaffected by simulation state.


What Scripts Can and Cannot Do

Scripts Can

  • Read Live Scenario state
  • Mutate Live Scenario via @set
  • Drive conditional structure during simulation
  • Be replayed deterministically

Scripts Cannot

  • Mutate defaults
  • Persist changes automatically
  • Execute engine logic
  • Implicitly affect export output

Relationship to Export and Runtime

ContextBehavior
ExportState is not evaluated or mutated
SimulationUses Live Scenario
RuntimeEngine defines state behavior

The Dialogue Simulator (aka Script Player) uses the same rules described on this page.


Why This Model Exists

This design ensures:

  • Predictable authoring
  • Clear separation of intent vs outcome
  • Debuggable narrative flow
  • Engine-agnostic exports
  • No hidden side effects

Summary

  • Defaults define what should exist
  • Live Scenario defines what currently exists
  • Scripts mutate Live Scenario
  • Live Scenario can be reset or saved
  • Export remains pure and context-free