Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rhai Reference

Conditions, scripts, and sequences all share one engine, so beyond what’s listed here they are plain Rhai: arithmetic, strings, if, let, loops, functions.

The variable store

It’s a simple map that is available everywhere. See Variables. You can access it and modify it in the following ways:

SyntaxWhat it does
vars["Name"]Reads a variable. Unknown names are an error, so typos surface as warnings.
vars["Name"] = valueWrites a variable, creating it if needed. Holds bools, numbers, and text.
vars.has("Name")Whether the variable exists.

Numbers are floats on the script side, but Rhai mixes integers and floats freely: vars["Gold"] >= 10 works.

Built-in cues

Available in sequences, you can schedule cues instead of running on the spot.

CallWhat it schedules
wait(seconds)A simple wait to make a step in your cutscene to take as long as you need.
emit("message")Fires a named message. Other cues can start .after it.
ConstantValue
line_endThe estimated reading time of the line being presented, from SequencerSettings.

An entry with no sequence plays the default one, wait(line_end) unless you change it in SequencerSettings.

Timing methods

There are some built-in methods you can use for timing:

MethodEffect
.at(seconds)Starts the cue that many seconds into the sequence.
.after("message")Holds the cue until that message fires.
.emits("message")Fires a message when the cue finishes.
.required()Still runs when the line is skipped, for cleanup and state that must land.

An example:

shake_camera(0.5);
play_anim("draw").at(1.5).emits("drawn");
play_sound("gasp").after("drawn");
reset_camera().required();
wait(line_end)

Your game’s functions

You register your own Bevy systems so dialogue runners can call them by name. There are two ways to register one, because there are two different things a call can mean:

RegistrationIt’s a…The call in RhaiWhat that means
app.add_dialogue_system("has_item", system)functionhas_item("sword")Runs right away and gives back an answer.
app.add_sequencer_command("play_anim", system)stage directionplay_anim("draw").at(1.5)Doesn’t run yet when a runner reads the sequence. It’s staged for later, with a .at/.after/.emits/.required on when.

Both kinds are callable everywhere: conditions, scripts, and sequences. That’s why a sequence can mix them, if has_item("lute") { play_anim() }: checking has_item happens immediately, while play_anim() gets scheduled to play out.

Both take up to four arguments of bool, i64, f64, f32, String, or Dynamic.

Where each field runs

Entry fieldModeRunsGood for
conditionExpression only — one expression returning a boolWhen the runner checks whether the entry can be reachedGating branches: vars["Gold"] >= 10 && has_item("sword")
scriptFull RhaiOnce per presentation; never on resumeChanging the game: vars["Gold"] -= 10; guard_bribed()
sequenceFull Rhai, calls schedule cuesEvery presentation, including resumeShowing the game: play_anim("bow"); wait(line_end)

Broken logic never blocks dialogue: a condition that fails to compile or errors at runtime passes with a warning, a failing script is reported and skipped.