Interpolation
Hard-coding values into lines breaks the moment anything changes. {...} lets you drop any expression directly into text - variables, math, function calls, all of it substituted before the event reaches your game.
Aria: You have {$gold} gold and {$potions} potions.
The expression is evaluated when the line is produced. By the time DialogueEvent::Line arrives in your game code, the text is already resolved:
DialogueEvent::Line { text, .. } => {
// text = "You have 42 gold and 3 potions."
}
What can go inside {…}?
Anything that’s a valid expression.
Aria: Half of your gold is {$gold / 2}.
Aria: You are {plural($lives, "life", "lives")} from a new record.
Aria: The roll was {dice(6, 2)}.
Aria: {select($mood, "happy:Lovely|sad:Sigh|other:Hm")}.
Strings, numbers, booleans - all formatted with sensible defaults:
- Numbers: integers as
42, floats as3.14(no trailing zeros). - Booleans:
true/false. - Strings: inserted verbatim.
For finer formatting, do it in an expression or a custom function:
Aria: You have {round($hp)} HP.
Aria: Gold: {string($gold)}.
Escaping
If you need a literal { or } in your text, double them:
Aria: She said "{{one}}" - I don't know what it meant.
That line arrives as: She said "{one}" - I don't know what it meant.
Tip: Curly braces outside of interpolation are rare in natural prose, so you’ll almost never need this. But when you do (code, JSON, serialised data),
{{and}}have your back.
Interpolation in options and commands
{…} works anywhere text flows:
-> Give the Barkeep {$gold} gold.
Barkeep: Much obliged.
<<play_sound "coin_{$coin_type}">>
The option text substitutes before the Options event, so your menu shows real numbers. The command argument substitutes before the Command event, so your host code sees coin_gold, not coin_{$coin_type}.
A worked example
title: LevelUp
---
<<declare $lvl = 1>>
<<declare $xp = 0>>
<<declare $to_next = 100>>
<<set $xp = $xp + 40>>
<<if $xp >= $to_next>>
<<set $lvl = $lvl + 1>>
<<set $xp = $xp - $to_next>>
Narrator: Level up! You are now level {$lvl}.
Narrator: {$to_next - $xp} XP until level {$lvl + 1}.
<<else>>
Narrator: {$xp}/{$to_next} XP - keep going.
<<endif>>
===
$lvl, arithmetic expressions, and a full sentence composed of three interpolations. Readable, fast, and it sits squarely in the territory you’d expect a scripting language to cover.
Localisation-aware interpolation
When a line carries a #line:id tag and a LineProvider returns a translation, {…} expressions in the translation are evaluated against the current variable storage. Translators can reorder or reshape interpolations for their language:
# English source
You found {$n} {plural($n, "gem", "gems")}.
# German translation (stored in the provider)
Du hast {$n} {plural($n, "Edelstein", "Edelsteine")} gefunden.
Same variables, same expressions, grammatically correct output.
Markup and interpolation together
{expr} and [markup] syntax both live in the same text and are processed in one pass. Byte offsets in the returned MarkupSpans are always relative to the final, expression-substituted string:
[b]{$name}[/b] found the key.
If $name is "Alice", the text arrives as "Alice found the key." and the span is { name: "b", start: 0, length: 5 }. The span points at the right characters regardless of how long the expression result turns out to be.
See Markup for the full tag syntax and span reference.
Next: Markup