Dialogue
Dialogue files and their runner, Yarn Spinner import, and the DialogueBox node.
Files: dialogue.odin, dialogue_box.odin, yarn.odin.
File overviews
Section titled “File overviews”dialogue.odin
Section titled “dialogue.odin”Dialogue: conversations as data, played by a runner and shown by a DialogueBox node (dialogue_box.odin).
A dialogue (an asset of kind “Dialogue”, a .dialogue.xml file; Yarn Spinner’s .yarn files
convert into one: yarn.odin) is a set of named nodes, each a list of steps:
| Step | Does |
|---|---|
| Line | someone says something: speaker, text ({$gold} puts a variable in) |
| Options | the player chooses: each option’s text and the node it goes next to |
| Jump | goes on in another node |
| Set | sets a variable: var="$gold" value="$gold - 5" |
| Command | tells the game something (<<shake 2>>): the box’s onCommand event; wait 1 pauses |
| Stop | ends the conversation |
Every step (and option) can have a condition (if="$gold >= 5 and not visited(\"Shop\")"): it
runs (or shows) only when true. A node that runs out of steps ends the conversation.
<dialogue formatVersion="13" start="Start"> <node name="Start" x="0" y="0"> <line speaker="Guide" text="Welcome! You have {$gold} gold."/> <options> <option text="Buy a sword" next="Buy" if="$gold >= 5"/> <option text="Goodbye" next="End"/> </options> </node> <node name="Buy" x="240" y="0"> <set var="$gold" value="$gold - 5"/> <command text="give sword"/> <line speaker="Guide" text="Here you go."/> </node></dialogue>Expressions are Yarn’s: numbers, “strings”, true / false, $variables (unset: 0), + - * / %,
== != < <= > >= (also is eq neq lt lte gt gte), and or not (also && || !), parentheses,
and the functions visited("Node"), visited_count("Node"), random(), random_range(a, b),
dice(sides).
A DialogueRunner plays one: Dialogue_Start, then Dialogue_Continue after each line and
Dialogue_Choose at options; its state says what to show. Variables (DialogueVariables) are a game’s
(Game.dialogueVariables: every DialogueBox shares them, so $met_guide set in one conversation
is true in the next) or a runner’s own; Dialogue_SetVariable / GetVariable, or
DialogueVariables_Set / Get from scripts.
dialogue_box.odin
Section titled “dialogue_box.odin”DialogueBox nodes show conversations (dialogue.odin): a box at the bottom of the screen with the speaker’s name, the line typed out a letter at a time, and the options to choose from.
- Playing:
dialogue(a.dialogue.xmlasset) fromstartNode(its own start when “”), when the scene starts (autostart) or when a script callsDialogueBox_Start. Variables are the game’s (Game.dialogueVariables), shared by every box and kept between conversations. - The player: the game’s
ui_acceptaction (or Space / Enter / a click) shows the whole line while it types, then goes on; at options,ui_up/ui_down(or the arrows) pick andui_acceptchooses, or a click on one. - Commands (
<<shake 2>>):onCommand(data: the command’s text,text := data.(string)), then it goes on, unless a listener callsDialogueBox_Hold(thenDialogueBox_Continuewhen the game is done).wait 1.5waits that long by itself. - Events:
onLine(data: the line’s text),onCommand,onFinished(the conversation ended). It hides itself when a conversation ends (hideWhenDone).
It draws in screen space at its position (a UI node: put it on a screen layer, or under no
camera), settings.size across, its text wrapped inside.
yarn.odin
Section titled “yarn.odin”Yarn Spinner files (.yarn, yarnspinner.dev, the language version 2) converted into a dialogue
(dialogue.odin), which the engine plays and the editor edits:
- each node (
title:andposition:headers, a body between---and===) becomes a node; Speaker: textlines become lines (#tagsdropped,{$x}kept),-> optiongroups become options: each option’s indented body becomes a node of its own (Title.1,Title.2…), and what follows the group a continuation node (Title.after1) the option bodies jump to;<<if>>/<<elseif>>/<<else>>/<<endif>>become conditions on the steps inside them,<<if ...>>after a line or an option that one’s own condition;<<jump X>>,<<set $x to expr>>,<<stop>>become jump, set and stop steps;<<declare $x = expr>>a set on the node’s first visit; any other command (<<wait 1>>,<<shake 2>>) a command step.
Not converted (reported): <<once>>, line groups (=>), smart variables, enums, functions
defined by the game other than the built-in ones (they evaluate as unknown), node groups.
yarnSpeaker reads Name: text; a colon later in the text isn’t a speaker.
DialogueStepKind
Section titled “DialogueStepKind”DialogueStepKind :: enum { Line, Options, Jump, Set, Command, Stop,}What a step does
enum. Source: dialogue.odin:61
DialogueOption
Section titled “DialogueOption”DialogueOption :: struct { text: string, // The node it goes to ("": the conversation ends) target: string, // Shown only when true ("": always) condition: string,}A choice of an Options step
struct. Source: dialogue.odin:71
DialogueStep
Section titled “DialogueStep”DialogueStep :: struct { kind: DialogueStepKind, // Runs only when true ("": always) condition: string, // Line: who says it ("": nobody: narration) and what speaker, text: string, // Options: the choices options: [dynamic]DialogueOption, // Jump: the node to go on in target: string, // Set: the variable ("$gold") and the expression it's set to variable, expression: string, // Command: its text ("shake 2") command: string,}One step of a node (the fields its kind uses)
struct. Source: dialogue.odin:80
DialogueNode
Section titled “DialogueNode”DialogueNode :: struct { name: string, // Where editors draw it in the graph position: [2]f32, steps: [dynamic]DialogueStep,}A node: its steps, run in order
struct. Source: dialogue.odin:97
DialogueData
Section titled “DialogueData”DialogueData :: struct { // The node conversations start in ("": the first) start: string, nodes: [dynamic]DialogueNode,}A dialogue (ParseDialogue; free with DialogueData_Destroy)
struct. Source: dialogue.odin:105
DialogueValue
Section titled “DialogueValue”DialogueValue :: union { f64, bool, string,}A value of an expression or variable
union. Source: dialogue.odin:112
DialogueState
Section titled “DialogueState”DialogueState :: enum { // Not started, or finished Idle, // A line shows: `Dialogue_Continue` after it Line, // Options show: `Dialogue_Choose` Options, // A command came: the game does it, then `Dialogue_Continue` Command, // It ended (a Stop, a node out of steps, an option to nowhere) Finished,}What a runner is waiting for
enum. Source: dialogue.odin:119
DialogueShownOption
Section titled “DialogueShownOption”DialogueShownOption :: struct { text: string, // Its index in the step (Dialogue_Choose takes the shown index) index: int,}An option as shown (its condition held)
struct. Source: dialogue.odin:133
DialogueVariables
Section titled “DialogueVariables”DialogueVariables :: struct { values: map[string]DialogueValue, visits: map[string]int, allocator: mem.Allocator,}Dialogue variables (“$gold”) and how often each node was entered (by name)
struct. Source: dialogue.odin:140
DialogueRunner
Section titled “DialogueRunner”DialogueRunner :: struct { data: ^DialogueData, state: DialogueState, // Where it is: a node's index and the next step in it node, step: int, // What shows now (its strings live until the next Continue or Choose) speaker, text: string, options: [dynamic]DialogueShownOption, // The command waiting (State Command) command: string, // Its variables and node visits: shared (a game's, `Game.dialogueVariables`, which every // DialogueBox uses), or nil for its own (`own`) shared: ^DialogueVariables, own: DialogueVariables, // The Options step showing (for Choose) optionsStep: int, allocator: mem.Allocator,}Plays a dialogue
struct. Source: dialogue.odin:147
DialogueBoxSettings
Section titled “DialogueBoxSettings”DialogueBoxSettings :: struct { // The box's size (`boxWidth`, `boxHeight`) size: [2]f32, // Letters typed a second (`typeSpeed`; 0: the whole line at once) typeSpeed: f32, // Text size, and the font ("" for the default; `fontSize`, `font`) fontSize: f32, font: string, // Space inside the box's edge (`boxPadding`) padding: f32, // Colors: the box, the text, the speaker's name, the option picked (`boxColor`, `textColor`, // `nameColor`, `pickColor`) background: sdl.Color, textColor: sdl.Color, nameColor: sdl.Color, pickColor: sdl.Color, // Starts its dialogue when the scene starts (`autostart`) autostart: bool, // Hides when a conversation ends, shows when one starts (`hideWhenDone`) hideWhenDone: bool,}A DialogueBox’s look and behaviour (scene files’ attributes)
struct. Source: dialogue_box.odin:31
DialogueBox
Section titled “DialogueBox”DialogueBox :: struct { using node: Node, settings: DialogueBoxSettings, // The dialogue asset and the node it starts in (node arena) dialogue: string, startNode: string, runner: DialogueRunner, // Letters of the line shown so far, and the time since the line began typed: f32, // The option highlighted pick: int, // A command's wait (seconds left), and a listener holding it waiting: f64, held: bool, started: bool, // A line began: it says so on onLine onLine: EventSubject `event:"A line shows (its speaker and text are the box's runner's)" data:"text := data.(string)"`, onCommand: EventSubject `event:"A command ran (<<shake 2>>): do it; DialogueBox_Hold to make it wait" data:"text := data.(string)"`, onFinished: EventSubject `event:"The conversation ended"`,}Shows a conversation
struct. Source: dialogue_box.odin:70
Procedures
Section titled “Procedures”ParseDialogue
Section titled “ParseDialogue”ParseDialogue :: proc(text: string, allocator := context.allocator) -> (DialogueData, ProjectError)Reads a dialogue file.
Inputs
- text: The file’s contents
- allocator: For the dialogue’s strings and lists
Returns
- The dialogue (free with
DialogueData_Destroy), or an error
procedure. Source: dialogue.odin:176
FormatDialogue
Section titled “FormatDialogue”FormatDialogue :: proc(data: DialogueData, allocator := context.allocator) -> stringWrites a dialogue file.
Returns
- The text (in
allocator)
procedure. Source: dialogue.odin:242
DialogueData_Clone
Section titled “DialogueData_Clone”DialogueData_Clone :: proc(data: DialogueData, allocator := context.allocator) -> DialogueDataA copy of a dialogue (in allocator)
procedure. Source: dialogue.odin:296
DialogueStep_Clone
Section titled “DialogueStep_Clone”DialogueStep_Clone :: proc(s: DialogueStep, allocator := context.allocator) -> DialogueStepA copy of a step (in allocator)
procedure. Source: dialogue.odin:307
DialogueStep_Destroy
Section titled “DialogueStep_Destroy”DialogueStep_Destroy :: proc(s: ^DialogueStep, allocator := context.allocator)Frees a step’s strings
procedure. Source: dialogue.odin:323
DialogueData_Destroy
Section titled “DialogueData_Destroy”DialogueData_Destroy :: proc(data: ^DialogueData, allocator := context.allocator)Frees what ParseDialogue or DialogueData_Clone made
procedure. Source: dialogue.odin:341
DialogueData_Equal
Section titled “DialogueData_Equal”DialogueData_Equal :: proc(a, b: DialogueData) -> boolWhether two dialogues are the same
procedure. Source: dialogue.odin:353
Dialogue_FindNode
Section titled “Dialogue_FindNode”Dialogue_FindNode :: proc(data: ^DialogueData, name: string) -> intA node’s index by name (-1 when there’s none)
procedure. Source: dialogue.odin:373
Dialogue_Start
Section titled “Dialogue_Start”Dialogue_Start :: proc(runner: ^DialogueRunner, data: ^DialogueData, node := "") -> boolStarts a conversation: in node (the dialogue’s start node when “”), running until the first
line, options, command or its end.
Inputs
- runner: The runner (its variables kept)
- data: The dialogue (kept by pointer: keep it alive while it plays)
- node: The node to start in
Returns
- false when there’s no such node (the runner is Finished)
procedure. Source: dialogue.odin:391
Dialogue_Continue
Section titled “Dialogue_Continue”Dialogue_Continue :: proc(runner: ^DialogueRunner) -> boolGoes on after a line or a command, until the next line, options, command or the end.
Returns
- false when the conversation is over (the runner is Finished)
procedure. Source: dialogue.odin:410
Dialogue_Choose
Section titled “Dialogue_Choose”Dialogue_Choose :: proc(runner: ^DialogueRunner, shown: int) -> boolChooses one of the options showing (its index among runner.options) and goes on in its node.
Returns
- false when it ended there (an option to nowhere), or there are no options showing
procedure. Source: dialogue.odin:421
Dialogue_Stop
Section titled “Dialogue_Stop”Dialogue_Stop :: proc(runner: ^DialogueRunner)Ends a conversation now
procedure. Source: dialogue.odin:435
Dialogue_SetVariable
Section titled “Dialogue_SetVariable”Dialogue_SetVariable :: proc(runner: ^DialogueRunner, name: string, value: DialogueValue)Sets a runner’s dialogue variable (“$gold”; a string is copied)
procedure. Source: dialogue.odin:441
Dialogue_GetVariable
Section titled “Dialogue_GetVariable”Dialogue_GetVariable :: proc(runner: ^DialogueRunner, name: string) -> DialogueValueA runner’s dialogue variable (nil when unset)
procedure. Source: dialogue.odin:446
DialogueVariables_Set
Section titled “DialogueVariables_Set”DialogueVariables_Set :: proc(vars: ^DialogueVariables, name: string, value: DialogueValue)Sets a dialogue variable (“$gold”; a string is copied).
Example
// Given: game: ^e.Gamee.DialogueVariables_Set(&game.dialogueVariables, "$gold", f64(12))procedure. Source: dialogue.odin:457
DialogueVariables_Get
Section titled “DialogueVariables_Get”DialogueVariables_Get :: proc(vars: ^DialogueVariables, name: string) -> DialogueValueA dialogue variable (nil when unset)
procedure. Source: dialogue.odin:472
DialogueVariables_Destroy
Section titled “DialogueVariables_Destroy”DialogueVariables_Destroy :: proc(vars: ^DialogueVariables)Frees dialogue variables
procedure. Source: dialogue.odin:477
DialogueRunner_Destroy
Section titled “DialogueRunner_Destroy”DialogueRunner_Destroy :: proc(runner: ^DialogueRunner)Frees a runner’s variables and lists (not its dialogue)
procedure. Source: dialogue.odin:489
Dialogue_Evaluate
Section titled “Dialogue_Evaluate”Dialogue_Evaluate :: proc(runner: ^DialogueRunner, expression: string) -> (DialogueValue, bool)Evaluates an expression with a runner’s variables (nil runner: no variables).
Returns
- The value, and false (with value nil) when it doesn’t parse
procedure. Source: dialogue.odin:505
DialogueValue_Truthy
Section titled “DialogueValue_Truthy”DialogueValue_Truthy :: proc(v: DialogueValue) -> boolWhether a value counts as true (nonzero numbers, non-empty strings)
procedure. Source: dialogue.odin:514
DialogueValue_Text
Section titled “DialogueValue_Text”DialogueValue_Text :: proc(v: DialogueValue) -> stringA value as text (“5”, “true”, “hello”; temp allocated)
procedure. Source: dialogue.odin:524
Dialogue_Interpolate
Section titled “Dialogue_Interpolate”Dialogue_Interpolate :: proc(runner: ^DialogueRunner, text: string) -> stringA line’s text with {expression} parts replaced by their values (“You have {$gold} gold”).
Returns
- The text (temp allocated)
procedure. Source: dialogue.odin:541
CreateDialogueBox
Section titled “CreateDialogueBox”CreateDialogueBox :: proc(scene: ^Scene, dialogue: string, startNode := "", settings := DEFAULT_DIALOGUE_BOX_SETTINGS) -> ^DialogueBoxMakes a DialogueBox for a dialogue asset (path relative to the project)
procedure. Source: dialogue_box.odin:92
DialogueBox_Start
Section titled “DialogueBox_Start”DialogueBox_Start :: proc(box: ^DialogueBox, node := "") -> boolStarts a conversation in the box (and shows it).
Inputs
- box: The box
- node: The dialogue node to start in (“”: the box’s
startNode, else the dialogue’s start)
Returns
- false when the dialogue or the node isn’t there (logged)
procedure. Source: dialogue_box.odin:126
DialogueBox_Hold
Section titled “DialogueBox_Hold”DialogueBox_Hold :: proc(box: ^DialogueBox)Makes a command wait: call from an onCommand listener, then DialogueBox_Continue
procedure. Source: dialogue_box.odin:143
DialogueBox_Continue
Section titled “DialogueBox_Continue”DialogueBox_Continue :: proc(box: ^DialogueBox)Goes on after a held command (or skips the line showing)
procedure. Source: dialogue_box.odin:148
DialogueBox_IsPlaying
Section titled “DialogueBox_IsPlaying”DialogueBox_IsPlaying :: proc(box: ^DialogueBox) -> boolWhether a conversation is playing in the box
procedure. Source: dialogue_box.odin:157
Game_LoadDialogue
Section titled “Game_LoadDialogue”Game_LoadDialogue :: proc(game: ^Game, path: string) -> (^DialogueData, bool)A dialogue, read once per game (Game.dialogues): the file relative to the project folder.
Returns
- The dialogue (the game’s; nil when it can’t be read, logged), and whether it was read
procedure. Source: dialogue_box.odin:168
Yarn_Convert
Section titled “Yarn_Convert”Yarn_Convert :: proc(text: string, allocator := context.allocator) -> (data: DialogueData, warnings: []string, ok: bool)Converts a Yarn file into a dialogue.
Inputs
- text: The .yarn file’s contents
- allocator: For the dialogue
Returns
- The dialogue (free with
DialogueData_Destroy), what wasn’t converted (temp allocated), and false when it has no nodes
procedure. Source: yarn.odin:38
Constants and variables
Section titled “Constants and variables”DIALOGUE_EXTENSION
Section titled “DIALOGUE_EXTENSION”DIALOGUE_EXTENSION :: ".dialogue.xml"A dialogue file’s extension
constant. Source: dialogue.odin:58
DEFAULT_DIALOGUE_BOX_SETTINGS
Section titled “DEFAULT_DIALOGUE_BOX_SETTINGS”DEFAULT_DIALOGUE_BOX_SETTINGS :: DialogueBoxSettings{ size = {600, 140}, typeSpeed = 40, fontSize = 20, padding = 14, background = {16, 18, 28, 230}, textColor = {240, 240, 240, 255}, nameColor = {255, 210, 110, 255}, pickColor = {120, 200, 255, 255}, autostart = true, hideWhenDone = true,}A dialogue box’s settings when none are given (CreateDialogueBox, scene files): 600 x 140,
40 characters a second, size 20 text on a dark panel; it starts
when the scene does and hides when the conversation ends
constant. Source: dialogue_box.odin:56
