Text Events
A Text Event runs Commands while a Spoken Line is being written, at a precise point of its text: when the typewriter reaches a word, the Event can slow down or pause the writing, play a sound, flash the screen, change a variable, call a function, change the speaker's portrait, move a character...
"Text": "Wait‡boom‡... what was that?"
When the typewriter reaches ‡boom‡, it runs the Event named "boom", waits for it to finish, then writes the rest of the line. The marker itself is never displayed.
Markers
A marker is an Event's name between two symbols, text_event_start_symbol and text_event_end_symbol in Candy_Properties.gd (‡ by default, see Symbols).
- A marker can be anywhere in the text: at its start (the Event runs before the first letter is displayed), between two words, or at its end (once the last letter is written, before the end-of-line string is displayed).
- Each Variation places its own markers, where its own words are: a French translation puts them where the French sentence needs them.
- To display the symbol as a literal, escape it by doubling it: ‡‡ is displayed as ‡.
- A marker naming no Event, for the Line's Variation, is reported in the output, and runs nothing.
Markers are read once text variables and Word Substitutions are resolved, so a variable's value can contain markers of its own: if the value contains ‡boom‡, the Event "boom" runs where the value is displayed. A doubled symbol in a value is displayed once, as in written text. The template Input Menu doubles the symbols in what the player types, so a player can't run Events (see Input Menus). Markers are removed from every other use of the Line's text: the text read by TTS (a _TTS Variation's markers run nothing), the Dialogue History, and the text the hook functions receive.
Where Events Are Defined
Events come from two places:
- The Text Events dictionary: Events every Line can use, defined once, like Lexicon keywords.
- The Line's own Events: Events defined in the Spoken Line itself, in its Text_Events key. In Candy Dialogue Creator, they are written in the Text Event Editor widget.
For each marker, the Engine searches the Line's own Events first, then the dictionary. A Line can therefore use the dictionary's Events, and replace any of them with its own.
Text Events Dictionary
Text_Events.gd (in User_Data/Data_Tables) is a 'safe' file: updates never overwrite it. Events are defined in its text_events dictionary:
var text_events = {
"*": {
"boom": {
"Lines": [
{"Media_Play": {"Players": {"Sound": {"File": "Explosion"}}}},
{"Write_Pause": {"Wait_Time": "0.6"}},
],
"Run_On_Skip": "1",
},
},
}
It ships empty, with an example in its comments.
Variation Keys
Events are sorted by Variation, exactly like the Lexicon's keywords:
- "*" applies to every Variation.
- A key ending with * (e.g. "Default*") applies to every Variation whose name starts with it.
- A key without * (e.g. "Default") only applies to the Variation with that exact name.
When an Event's name is defined under several keys that apply, the most specific one is used: an exact name first, then the longest key ending with *, then "*". A translation can therefore define its own version of an Event, while the other Variations use the shared one.
A Line's own Text_Events are sorted the same way, each Variation key holding its Events under an Events key:
"Text_Events": {
"*": { "Events": { "boom": { "Lines": [...], "Run_On_Skip": "1" } } },
"French": { "Events": { "boom": { ... } } }
}
The Line's Events always take precedence over the dictionary's, whatever their Variation keys: a Line's "*" Event is used rather than the dictionary's "Default" one.
Event Keys
| Lines | The Commands the Event runs, in order, written as Lines are in a Dialogue file. The writing waits for them to finish. Only the Commands allowed in Text Events can run (see below). |
| Run_On_Skip | "1" (default): when the writing is skipped, run the Event without its waits. "0": don't run it at all (see Skipping below). |
| Pause_Voice | "1": the Line's voice pauses until the Event is over. "0": the voice continues. "null" or left out: the Engine's setting decides (see Voice below). |
| Resync_Voice | "1": in a Line whose writing follows its voice, the writing speed is recalculated after the Event, so the text still ends with the voice. "0": it isn't. "null" or left out: the Engine's setting decides (see Voice below). |
Text Event Commands
Three Commands exist for Text Events. They only run inside an Event: elsewhere, they are reported and skipped.
- Write_Speed: changes the writing speed of the rest of the Line.
- Write_Pause: pauses the writing for a while.
- Line_Portrait: changes, pauses, resumes or stops the Line's portrait, or jumps to one of its frames.
Allowed Commands
An Event can only run the Commands listed in text_event_commands, in Candy_Properties.gd. Any other Command is reported in the output, and skipped. Names are case-insensitive, and a name ending with * covers every Command starting with it ("Media_*").
By default, the list contains Set, Flag, Call, Emit, Await, Name, Role, Disposition, Export, Import, the Condition Commands (If, Elif, Else, For, While), Wait, Custom, Mouse, UI_Z, every Media, Effect, Background, Visual Novel and Cutscene Command, and the three Text Event Commands. Comment and LM are always allowed.
Left out on purpose: Spoken_Line, Choice_List, Input, Player_Advance, the Transition Commands, the Engine Commands and UI_Clear. They would display another line, wait for the player, or clear the Line being written. With End added to the list, an Event can end the Dialogue. Return can't run in an Event.
An Event's Lines don't call the x_new_line() and x_command_start() hooks, and don't count down a Media_Pause's Lines: they are part of the Spoken Line.
Skipping
When the player skips the writing (the skip input, or a speed input whose speed is 0), or the Line is written instantly (a writing speed of 0), the Events the writing hasn't reached yet all run at once, in order, without their waits:
- Wait, Await, Write_Pause and every _Wait Command are left out, inside Condition Commands too.
- Every Wait_Loops and Wait_Time is set to 0, so media starts playing without the Event waiting for it.
The variables, portraits and media therefore reach the state they would have reached, without the player waiting. An Event whose Run_On_Skip is "0" doesn't run at all when skipped.
A Command that waits in some other way still waits: CS_Move, or a Call of a function that awaits.
Voice
While an Event runs, the Line's voice continues playing, unless the Event pauses it. Two settings decide this, at three levels, each level overriding the previous one:
- Candy_Properties.gd: text_events_pause_voice (false by default) and text_events_resync_voice (true by default).
- The Engine, under the Text Events category: text_events_pause_voice and text_events_resync_voice, used while their enable_ switch is on.
- The Event's own Pause_Voice and Resync_Voice keys.
Pause_Voice pauses the voice while the Event runs, and resumes it where it was: useful when a Write_Pause should be a silence too.
Resync_Voice only applies in a Line whose writing follows its voice (sync_write_speed_voice, see Typewriter Effect). A pause leaves the text behind the voice; with Resync_Voice, the writing speeds up afterwards, so the text still ends with the voice. It doesn't apply while a Write_Speed is in effect.
Dialogue Modes
Text Events work in every Dialogue Mode that writes text: Box, Subtitles, Chat, Bubbles, VN Bubbles and Barks. A marker's position is counted in the characters the player sees: BBCode tags aren't counted, nor is the speaker's name written before the text.
Voice Mode writes nothing: a Line's Events all run once its voice starts, as when the writing is skipped.
A UI Element of your own, copied from one of Candy Dialogue Engine's, requires two calls in its display() function to support Text Events: compare it with the UI Element it was copied from.
Other Cases
LLM: when an LLM rewrites a Line, its reply replaces the whole text, markers included. The Line's Events then run once the reply is written.
Suspension: suspending the Dialogue pauses an Event's waits and the writing alike.
Interruption: interrupting or ending the Dialogue stops the writing and its Events at once: the Events the writing hadn't reached don't run.