Expressions
An expression is a small piece of code that returns a value: true/false,
a number, or a string. Writers use them to decide which choices show up,
which scenes the player can reach, when an event fires, and how text reads.
This page is the orientation; for every operator and function the runtime
supports, see Expression operators and
Expression functions.
Where you write them
Anywhere the inspector shows a syntax-highlighted field: node and scene Visibility, event Conditions, scene Resolved when, and a handful of others. The editor validates as you type, autocompletes function and variable names, and underlines unknown identifiers in red.
The simplest expressions
The smallest expression is just a literal value or a variable:
true // a literal true
42 // a number
"hello" // a string
var_player_name // the current value of a variable
Variable names are bare identifiers: no quotes, no $, no dollar-prefix.
The editor’s autocomplete suggests them as you type.
Comparing values
To turn a number or string into a yes/no answer, compare it. The result is
true or false, which is what visibility and condition fields expect.
var_hp > 0 // alive?
var_score >= 100 // hit the threshold?
var_player_name == "Alice" // string compare (case-insensitive)
var_attempts != 0 // tried at least once?
All six comparisons (==, !=, <, <=, >, >=) work on numbers, and
== / != work on strings.
Combining with &&, ||, !
Most real visibility gates chain a few conditions together:
var_hp > 0 && var_has_key // both must hold
var_helmet || var_shield // either is enough
!Played(node_warning_intro) // logical negation
(var_hp > 0 && var_mp > 5) || var_is_god // grouped with parens
&& is “and”, || is “or”, ! is “not”. The editor uses these symbols
when it generates expressions on your behalf (for example, the gates a
Dependency line writes), so they’re the form you’ll see most often in your projects. The
keyword spellings and / or / not work too if you prefer reading them
aloud. Parentheses group sub-expressions and override the default
precedence: comparison binds tighter than &&, which binds tighter than
||.
Referring to entities by id
When you pass a scene, node, tag, skill, or item to a function, you reference it by its id (the short, human-readable label you set in the editor), not by the long internal uuid:
Visits(scene_kitchen) // correct
Visits("scene_kitchen") // wrong: ids are bare, not quoted
Visits(d3f9-…-a8c1) // wrong: uuids are storage details
Each function expects a specific entity kind for each of its arguments, and its reference page calls that out. Renaming an entity’s id afterwards keeps existing expressions working as long as you rename through the editor; it rewrites references everywhere at once.
Calling functions
Most of the expression power comes from functions. The two you’ll reach for most often are play-history checks:
Played(node_meet_alice) // did this node play?
Visits(scene_kitchen) >= 2 // have they entered this scene twice?
There are around fifty in total, covering math, strings, time, lists, and sequence slots. Skim Expression functions to get a feel for the categories; you don’t need to memorize them.
Scene-scoped play history
This is the rule that catches new writers most often. Played(X),
Selected(X), and Seen(X) track state per visit of the scene
containing X. Every time a scene reactivates, it starts with an empty
visit-state; what happened in the previous visit doesn’t count.
For whole-run queries (“has the player ever encountered this content?”)
use the Ever variants:
PlayedEver(X)instead ofPlayed(X)SelectedEver(X)instead ofSelected(X)SeenEver(X)instead ofSeen(X)
Rule of thumb: if you’re asking “did this happen in this conversation
with this character?” use the per-visit form. If you’re asking “has the
player ever encountered this content at all?” use Ever.
Boolean coercion
When a non-boolean value lands somewhere a boolean is expected (a
visibility expression, an If(...) condition, an operand of &&), the
runtime coerces it:
0,0.0, empty string,"false"(case-insensitive) →false- Anything else (non-zero numbers, non-empty strings) →
true
This means var_hp on its own works as a visibility gate (true when
non-zero), the same as var_hp != 0. The Bool(x) function exposes the
rule explicitly when you want to be clear about the coercion.
See also
- Error messages: Expressions: panel messages about undeclared identifiers and
Played/Selectedscope faults.