Candy Logo Copyright © 2025-2026 Candy Arts

Hook Functions
Hook functions are functions Candy Dialogue Engine calls at specific steps of Dialogue processing, where your own code runs. They ship empty: write code in the ones you need, and leave the others as they are. There are two kinds:
  • Global hook functions are called by every Engine. Each is a script in Candy_DE/Scripts/User_Data/Functions, listed in the Wizard's Hook Functions tool, with the hook_arguments table: click a name to open its script. Choose the tool in the drop-down at the top of the Wizard's panel.
  • UI Element hook functions are written in the script of a UI Element or media player, and only called for that one.
Arguments Every hook function takes the same three dictionaries and arrays as arguments:
func x_dialogue_end(candy_args: Dictionary, user_args: Array, custom_data: Array): var engine = candy_args["caller"] print("A Dialogue ended in ", engine.name)

  • candy_args: Candy Dialogue Engine's own values, by name. "caller" is always the Engine. The other keys depend on the hook: see the tables below.
  • user_args: your own values, which you list in the hook_arguments table (see below).
  • custom_data: the Custom Data of the Line the hook is called for: the numbered values every Line can carry in Candy Dialogue Creator. It contains one Array of values per level, such as [[line values]] or [[line values], [variation values]], and is empty for a hook that isn't called for a Line. See UI, under Hook Functions.

A hook function can use await: the Engine waits for it to return before it carries on. A hook that runs long therefore delays the Dialogue. Five hooks are the exception, because nothing can wait for them: x_flag_changed(), x_dialogue_suspended(), x_dialogue_resumed(), x_write_progress() and x_playback_finished(). The Engine calls them and carries on at once. Global Hook Functions These are called at the main steps of a Dialogue:
FunctionCalledcandy_args
x_dialogue_start()When a Dialogue starts, right before its first Line runs. change_game_state() has run and the Engine is shown. Not called by a Jump, which carries on the same Dialogue."caller", "conversation", "block", "line": where the Dialogue starts, as passed to start_dialogue().
x_new_line()When a new Line starts, before its Command runs. Not called for Line Marks and Comments, nor for the Commands run inside another one (by an If, a choice...)."caller", "line_data": the whole Line, as written in the Dialogue.
x_command_start()Right after, before each Command of the Line runs. Not called for Spoken Lines, which have the three hooks below."caller", "line_data", "command": the name of the Command that starts, as the Line writes it, "command_value": that Command's keys.
x_new_speech_start()When a Spoken Line starts, before anything is decided: the Line may still be skipped."caller", "line_data"
x_new_speech_display()When a Spoken Line is about to be displayed: its Variation is picked and its speaker is known. Not called for a Line that is skipped (see Dispositions and Variations)."caller", "line_data", "speaker": the Actor speaking (a Role is replaced with its Actor), "variation": the name of the chosen Variation, "text": its text, with text variables and word substitutions applied. An LLM reply only arrives later.
x_speech_advance()When a Spoken Line is advanced, by the player or by auto-advance, once the UI Element that displayed it has finished with the Line. Not called for a Line that is skipped, nor when the Dialogue is stopped while the Line waits."caller", "line_data", "speech": the CandySpeech object of the Line, as it was displayed. It contains the speaker (speaker_ref), the final text (text), the Variation (chosen_variation), the Dialogue Mode (style) and the Line's settings (settings).
x_choice_selected()When a choice is selected, by the player or by a timer, before it switches category and before its Finish transition runs (see Choice List). Not called for an inactive choice."caller", "menu": the menu node, "category" and "choice": their names, "button": the button node, "choice_data": the choice's entry in the Choice List's data.
x_jump(), x_bridge()When a Jump or a Bridge command runs, once its target is found, before the Dialogue leaves the current Block."caller", "line_data", "conversation", "block", "line": where the Command is, "new_conversation", "new_block", "new_line": its destination.
x_return()When a Return command runs, before the Dialogue leaves the current Block. Not called when a bridged Block reaches its last Line and returns on its own."caller", "line_data", "conversation", "block", "line", "levels": the Command's Levels, as a number.
x_end()When an End command runs, before the Dialogue ends. A Dialogue can end in other ways, and x_dialogue_end() is called for all of them."caller", "line_data", "conversation", "block", "line"
x_flag_changed()When a Flag command changes a flag (see Flags)."caller", "flag", "value"
x_dialogue_suspended()When a Dialogue is suspended, once the Engine has paused everything it runs itself (see Suspending Dialogues)."caller"
x_dialogue_resumed()When a suspended Dialogue is resumed, right before its Lines carry on."caller", "ending": true when the suspension is only lifted because the Dialogue is ending or being interrupted.
x_dialogue_end()When a Dialogue ends, at the end of the cleanup (see Ending Dialogues)."caller"

x_jump(), x_bridge(), x_return() and x_end() are also called for the transitions a choice menu or an If runs. Such a transition isn't a Line of the Dialogue. "line_data" is then the Line the Engine built for it, and "line" is the index of the Line holding it (the Choice_List or the If). A choice menu's transition has no Custom Data, so custom_data is [[]]. Each of these has a job of its own, explained on its own page:
FunctionJob
change_game_state()Switches your game to its dialogue state when a Dialogue starts, and back when it ends. See Game State.
tts()Generates the voice of a line with text-to-speech. See TTS.
llm_query()Asks an LLM for a line's text. See LLM.
_on_lexicon_clicked()Decides what happens when a Lexicon keyword is clicked. See Lexicon.
_on_lexicon_hovered()Decides what happens when the mouse enters or leaves a clickable Lexicon keyword. See Lexicon.
Don't delete or rename these scripts. Candy Dialogue Engine checks at startup that every one of them exists, and reports the ones that are missing. A hook you don't use costs nothing: leave it empty. The scripts of this folder are 'safe' files: updates never overwrite them. UI Element Hook Functions These are written in the script attached to a UI Element or a media player, so each one can behave in its own way: one dialogue box can play an animation when its text starts, and another one not.
FunctionCalled
x_write_begun()When the UI Element starts writing a line's text.
x_write_progress()While the typewriter writes, each time characters appear (see Speech Display).
x_write_finished()When the text is fully written.
x_speech_advance()When the line is advanced, by the player or by auto-advance.
x_playback_starting()When a media player, a voice player, a background layer or a bust is about to play something.
x_playback_started()Right after it started playing.
x_playback_finished()When a media player's playback is over, because the media reached its end or was stopped. Only media players have it (see Media Players).
x_initialization_finished()When a choice menu is built and ready.
x_menu_closing()When a selected choice is about to close a choice menu, while the menu is still shown (see Menu UI).
They take the same three arguments, and candy_args also contains "node": the UI Element or media player the hook belongs to. Each UI Element's page lists its hooks and what they receive: see UI. x_speech_advance() exists as both kinds. The UI Element's is called first, as soon as the line is advanced. The global one is called next, once the UI Element has finished with the line: its voice is stopped, and the line is added to the dialogue history. Both receive the same user_args, since they share one entry of the hook_arguments table. These scripts are 'unsafe' files. Every update replaces them, after the Wizard has warned you, and you then add your hook functions to the new versions again. To avoid that, attach a copy of the script, under a new name, to your own scenes. Your copy is 'safe' (see Customizing UI Elements, on the UI page). Adding Your Own Arguments To pass more values to a hook function, list them in the hook_arguments table, in Hook_Arguments.gd (User_Data/Data_Tables). Each key is a hook's name, and its value the Array the hook receives as user_args:
var hook_arguments = { "x_new_line": [ "my literal value", # user_args[0] "¢globals.player_hp", # user_args[1] ], }
  • An entry is passed as written, unless it starts with the CandyRef symbol (default ¢): it's then evaluated each time the hook is called, so the hook always receives the current value.
  • A hook with no entry receives an empty Array.
  • UI Element hook functions read the same table, by the hook's name.
Candy Dialogue Engine's own calls therefore never need editing to pass a hook what it requires. How the Functions Are Found Every script of User_Data/Functions that extends CandyFunction is loaded when the game starts, sub-folders included. Each function it declares is then known by its own name: x_new_line() is found because a script declares a function of that name, whatever the file is called.
  • A script can declare several functions, which groups related ones in one file.
  • Several scripts can declare the same hook function: all of them are called, in the order of their file names. The code of separate features can therefore stay in separate files. For a hook that returns a value (tts(), llm_query()), only the first one's value counts, and a warning is printed.
  • The scripts are nodes, children of the candy_de autoload: get_tree(), get_node(), timers and signals all work in them.
Functions of Your Own The same folder can contain functions that aren't hooks: code of your own, stored beside the rest. Add a script that extends CandyFunction, and declare your function in it with whatever arguments it needs:
extends CandyFunction func give_reward(amount: int) -> void: globals.gold += amount
Such functions aren't methods of candy_de, so they're called by name:
await candy_de.run_user_function("give_reward", [50])
candy_de.has_user_function("give_reward") returns whether a function exists. Four functions are meant to be called by your game, and are real methods of candy_de: open_instance_history() and open_general_history() (see History Display), clear_busts_ood() (see Visual Novels) and clear_bg_layers_ood() (see Backgrounds).