Skip to main content

Scripting Syntax

The idea behind Drafft is that it should be engine- and language-agnostic. You are free to use whatever language you prefer for your game logic and runtime. However, certain features — such as dialogue flow, narration, and commands — require identifying the purpose of each line.

For this reason, the Drafft scripting syntax (UAF) exists.

UAF is intentionally minimal. It is designed to add structure and meaning to plain text without turning it into a full programming language. If you do not need these features, the syntax can be safely ignored.


Scripting Rules

  1. Scripts are read top to bottom, one line at a time.
  2. Each line has a single, clear purpose (dialogue, comment, command, or control flow).
  3. Control flow statements (@if, @else, @endif) never produce output on their own.
  4. Lines inside inactive control blocks are skipped entirely.
  5. State changes persist forward for the remainder of the script.

Evaluation Contexts

Drafft scripts can be interpreted in different contexts depending on how they are used.

The scripting syntax always describes structure and intent.
Whether that intent is evaluated or executed depends on the context.


Export Context

During export:

  • No game state is assumed
  • Control flow is preserved, not evaluated
  • Conditions are not resolved
  • Commands are emitted as descriptive instructions

This ensures scripts remain engine-agnostic, portable, and safe to transform.


Simulation Context (Dialogue Simulator)

The Dialogue Simulator evaluates scripts using a temporary, simulated state.

In this context:

  • Conditions are evaluated
  • Control flow is executed
  • Variable assignments affect simulated state
  • Commands may be visualized or logged, but not executed

This allows authors to preview dialogue flow and branching logic without running the game.


Runtime Context

At runtime:

  • The game engine owns the state
  • Conditions are evaluated using live game data
  • Commands are executed according to engine logic

Context Summary

ContextState ExistsConditions EvaluatedCommands Executed
ExportNoNoNo
Dialogue SimulatorYesYesSimulated
Game RuntimeYesYesYes

tip

Drafft defines structure and intent.
Evaluation depends on context.


Comments

// Comment

Comments are lines that are commonly ignored in the target engine. Drafft will include comments in the screenplay.

Example

// INT. SUBURBAN HOME - KITCHEN - NIGHT
// FILBERT (9), wiry, lost in his own imaginary world. Dressed as a Knight. A toy sword in his other hand.

Actor Line (with Speech Tag)

::Actor:: Actor Line [expression]
[#speechTag]::Actor:: Actor Line [expression]

Actor lines are the core building block of Drafft scripting. They represent a line of dialogue spoken by an actor and carry semantic meaning beyond simple text.

An actor line is still valid, readable text on its own, but the syntax allows Drafft to identify who is speaking, what is being said, and how that line should be interpreted or exported.


Purpose

Actor lines serve multiple roles:

  • Identify actors to build and maintain the actor database
  • Identify spoken dialogue to generate voice-over references
  • Include dialogue lines in the generated screenplay
  • Attach expressions or annotations to dialogue without breaking readability
  • Allow remapping to engine-specific formats during export

Structure

Actor Identifier

::Actor::
  • Actor is treated as an identifier, not executable code
  • The name is not validated or interpreted by the scripting system
  • The same actor name can appear across multiple scripts

Dialogue Text

The dialogue text is the spoken content of the line and may include inline expressions.

::Aries:: I have ${v.player.totalCoins} coins.

Speech Tags

Speech tags are optional identifiers used to uniquely reference a specific line of dialogue.

[#speechTag_abcd]::Actor:: Actor Line

In most cases, speech tags are automatically generated by Drafft and do not need to be written manually.


Purpose of Speech Tags

Speech tags are primarily intended for:

  • Voice-over pipelines
  • Audio asset linking
  • Localization workflows
  • Stable references across exports and revisions

They allow dialogue lines to be tracked independently of their position in a script or their literal text.


Automatic Generation

When desired, Drafft automatically generates a speech tag for actor lines that do not already have one.

Generated tags are based on:

  • The document alias
  • A short, random identifier

This ensures:

  • Tags are stable within a document
  • Collisions are extremely unlikely
  • Tags remain human-readable and diff-friendly

Example of an auto-generated tag:

[#intro_a9F3]::Aries:: It's quieter than I expected.

Manual Tags (Optional)

While speech tags are usually auto-generated, they may also be written manually when explicit control is required.

[#line001_abcd]::Travis:: You talkin' to me?

Manual tags must follow the same format and uniqueness rules as generated tags.


Format Rules

Speech tags must:

  • Be wrapped in square brackets
  • Start with #
  • Contain only alphanumeric characters, underscores, or dots
  • Have a minimum length of 6 characters (excluding #)

Valid examples:

[#intro_4aZ9]
[#scene01.line02]
[#npcGreeting]

Invalid examples:

[#hi]          // too short
[#hello-world] // invalid character
[# hello ] // spaces not allowed

Execution Behavior

Speech tags:

  • Do not affect control flow
  • Do not affect execution
  • Are ignored at runtime
  • Are preserved during export

They exist purely as metadata.


Audio Tag Extraction

During export or processing, the speech tag identifier can be extracted without the surrounding brackets for use in audio or localization systems.

For example:

[#library_8F2a]::Kenji:: Wow, it’s peaceful in here.

May produce:

library_8F2a

as an audio or localization key.


Design Rationale

Speech tags are designed to be:

  • Mostly invisible to writers
  • Automatically managed by the tool
  • Stable across script edits
  • Friendly to external pipelines

This keeps scripts readable while still supporting production workflows.


Expression Annotation

An (Optional) expression annotation can be added at the end of the line to provide contextual information, such as tone, emotion, or delivery.

::Actor:: Actor Line [expression]

Expression annotations:

  • Are treated as metadata
  • Do not affect control flow or execution
  • Are included in screenplay and export outputs

Examples

::Tyler:: The first rule of Fight Club is: You do not talk about Fight Club. [serious]
[#line001_abcd]::Travis:: You talkin' to me?
[#greeting_soft]::Aries:: It's nice to see you again. [warm]

Export Behavior

Actor lines can be transformed during export to match the syntax or conventions of the target engine or pipeline.

For example:

  • Renaming actors
  • Converting speech tags to engine IDs
  • Stripping or mapping expression annotations

See Export Mappings for more details.


Design Notes

Actor lines are intentionally:

  • Human-readable
  • Diff-friendly
  • Independent of any specific engine or runtime

This allows Drafft scripts to function both as working data and as long-term narrative documentation.


Commands

Commands represent engine-defined actions expressed declaratively inside a Drafft script.

Drafft does not execute commands. Instead, commands are preserved, identified, and exported so they can be interpreted by the target game engine, runtime, or pipeline.


Syntax

<CommandName(arg1, arg2)>
  • CommandName is an identifier
  • Arguments are passed as plain values or expressions
  • Drafft does not validate command names or arguments

Examples

<PlayBackgroundMusic("calm_library")>
<Pause(2)>
<ShowCharacter("Aries", "smiling")>

Special Cases

Fade commands <FadeOut(?params)> and <FadeIn(?params)> also get some special treatment in the screenplay output.


Execution Semantics

From Drafft’s perspective:

  • Commands are descriptive, not executable
  • They are emitted in order during export
  • They are included in simulation only if their surrounding control flow is active
  • Their meaning is defined entirely by the target engine or toolchain

Drafft treats commands as structured intent, not behavior.


Export Mappings

Commands may be transformed during export to match the conventions or APIs of the target engine.

For example:

  • Renaming commands
  • Rewriting arguments
  • Converting commands into function calls, events, or data structures
  • Removing unsupported commands

See Export Mappings for details.


Design Rationale

Commands are intentionally:

  • Engine-agnostic
  • Non-executable within Drafft
  • Easy to identify and transform
  • Safe to ignore if unsupported

This allows Drafft scripts to remain portable, readable, and adaptable across different engines and workflows without embedding engine logic into the scripting language itself.


Control Flow Statements

Control flow statements define conditional structure inside a script.

They describe intent and structure, not execution. Whether conditions are evaluated depends on the evaluation context (export, simulation, or runtime).


Expressions and Resolution Model

All expressions reference values from the evaluation context.

Values are always accessed using the same structural pattern:

<collection>.<_sid>.<property>

The _sid is a human-readable, author-defined identifier used exclusively for scripting and expression resolution.

_sid: forestTrial
title: "The Forest Trial"
difficulty: Easy

Uniqueness and Validation

\_sid must be unique within its collection

  • Conflicts are reported by Drafft during validation
  • Scripts referencing ambiguous or missing \_sid values cannot be simulated or exported
  • Drafft provides tooling assistance to help identify and resolve conflicts early.

Common roots include:

  • defaults (or d) — authored default values
  • variables (or v) — variables properties
  • quest (or q) — quest properties and state (available in future versions)
  • items (or i) — item properties (available in future versions)
  • actors (or a) — actor properties (available in future versions)

Examples:

variables.player.courage
quest.forestTrial.courage

Examples below will be available in future versions.

items.healthPotion.count
actors.Aries.mood
v.settings.speechVolume
i.sword.damage
a.Aries.mood

This consistency keeps scripts predictable, diff-friendly, and engine-agnostic.


@if

@if <expression>

Defines a conditional block. The enclosed lines belong to the branch that should be taken if the expression evaluates to true.

Example:

@if variables.player.courage > 5
::Aries:: You seem more confident.
@else
::Aries:: Still working on your courage?
@endif

Evaluation Context

  • Export context The condition is preserved as-is. No evaluation occurs.
  • Simulation context The expression is evaluated using simulated state to preview dialogue flow.
  • Runtime context The expression is evaluated by the game engine if required

Multi-Way Branching

For 3+ options, use nested @if blocks:

@if variables.player.courage > 7
::Aries:: You are fearless now.
@else
@if variables.player.courage > 3
::Aries:: You are getting braver.
@else
::Aries:: You still seem unsure.
@endif
@endif

@else

@else

Defines a fallback branch that applies if no previous condition matched.

  • Optional
  • May appear only once per @if block

@endif

@endif

Ends a conditional block.

Every @if must have a matching @endif.

Script Player Feedback

In the Script Player:

  • @if, @else, and @endif lines are visible
  • The meta box shows condition results (TRUE/FALSE)
  • Inactive lines are dimmed and show which condition is blocking them

State Assignment

@set

@set <target> = <expression>

Defines an assignment that may modify runtime state.

The assignment target must follow the standard resolution pattern:

<collection>.<_sid>.<property>

Example:

@set variables.player.courage = variables.player.courage + 1
@set items.sword.condition = broken
@set defaults.quest.forestTrial.started = true

Evaluation Context

  • Export context The assignment is preserved but not executed.
  • Simulation context The assignment updates simulated state.
  • Runtime context The assignment updates game state as defined by the engine.

Rules

  • One assignment per line
  • Left-hand side must be a writable reference
  • Right-hand side must be a valid expression
  • No side effects outside of the assignment itself

Compound Assignment Operators

You can use compound assignment operators for brevity when performing arithmetic operations:

OperatorMeaningExample
+=Add and assign@set variables.player.hp += 5 (same as = variables.player.hp + 5)
-=Subtract and assign@set variables.player.hp -= 3
*=Multiply and assign@set variables.player.level *= 2
/=Divide and assign@set variables.player.mp /= 2
^=Exponentiate and assign@set variables.player.power ^= 2

Benefits for dialogue writers:

  • Avoid repeating long variable paths
  • Reduce opportunities for typos
  • Make intent clearer at a glance

Example comparison:

// Verbose form (still supported)
@set variables.player.quest.forestTrial.jobsDone = variables.player.quest.forestTrial.jobsDone + 1

// Compact form (equivalent)
@set variables.player.quest.forestTrial.jobsDone += 1

Inline Interpolation (${})

Inline interpolation allows you to embed values from the evaluation context directly into text.

It is commonly used inside actor lines, narration, and other plain-text content.

${ expression }

Expressions inside ${} follow the same resolution rules as control flow and assignments.

tip

Currently variables collection is supported for interpolation, but support for quest, items, and actors is planned for future versions.


Purpose

Inline interpolation is designed to:

  • Insert dynamic values into dialogue and narration
  • Reference variables and document properties
  • Keep scripts readable and close to natural language

It does not execute logic and does not mutate state.


Examples

::Aries:: I feel like my courage is at ${variables.player.courage} today.
::Narrator:: You have ${variables.player.gold} coins left.
::Narrator:: Quest completed: ${quest.libraryVisit.title}

Supported Expressions

Inline interpolation supports simple, read-only expressions:

  • Property access

    ${variables.player.courage}
    ${quest.libraryVisit.courage}

The exact capabilities depend on the target engine or simulation environment.


What ${} Is Not

Inline interpolation is intentionally limited.

It does not support:

  • Control flow (if, else, loops)
  • Assignments or state mutation
  • Function calls
  • Access to engine APIs

All logic and decision-making must be expressed using @ directives or handled by the target engine.


Relationship to Control Flow

Inline interpolation does not affect whether a line is included or excluded.

Control flow is handled exclusively by @if, @else, and @endif.

@if quest.libraryVisit.courage > 2
::Aries:: I feel confident (${quest.libraryVisit.courage}).
@endif

In this example:

  • @if determines whether the line exists
  • ${} only affects what is displayed

Data Resolution: State vs Defaults

By design, all property lookups resolve to runtime state by default.

${variables.player.courage}

To explicitly reference the authored default value, use the defaults root:

${defaults.variables.player.courage}

Resolution Summary

ExpressionResolves To
variables.x.yRuntime variable state
defaults.variables.x.yAuthored variable default
quest.x.yRuntime quest state (not available yet)
defaults.quest.x.yAuthored quest default (not available yet)

Notes

  • Whitespace inside ${} is ignored
  • Unresolved expressions may be:
    • left untouched
    • replaced with a fallback value
    • handled by the target engine (implementation-specific)

Mental Model

If it starts with @, it changes or decides something. If it’s inside ${}, it only displays something.

This separation is intentional and central to Drafft’s scripting philosophy.


Inline JSON

Since v1.0.13 it is possible to inline standard JSON in both speech and commands lines. %json variable will contain parsed content.

Examples:

::Reporter:: Now let's take a look at the weather for today.
<CutTo(Location1)> {"weather":"rainy"}
::FieldReporter:: Not looking very good here. {"props":["umbrella","raincoat", "microphone"]}
<CutTo(Studio)>
::Reporter:: What's the traffic on your side Mike?
<CutTo(Location2)> {"traffic": {"cars": "low", "trucks":"none"}}
::Mike:: Low traffic in this area.

JSON can be transformed at export time as well, see Export Mappings


Common Misconceptions

A few clarifications that help avoid common sources of confusion:

  • “My @if didn’t run during export.” This is expected. Control flow directives are not evaluated during export. They describe intent and are preserved for the target engine or simulation environment.
  • “Commands execute when the script is loaded.” Drafft never executes commands. Commands are descriptive markers whose execution is entirely up to the game engine or tools consuming the exported data.
  • “The Dialogue Simulator defines how scripts behave in-game.” The Dialogue Simulator is a preview and debugging tool. It evaluates scripts using its own evaluation context, but the game engine is always the final authority.
  • “Drafft replaces game logic.” Drafft describes narrative structure, flow, and intent. Game logic, rules, and systems live in the engine.