Event Scripts
Three Commands stop the Dialogue and wait for something to happen: Input waits for its Input Menu, Choice_List for its choice menu, and Await for a signal. What they do when it happens is decided by event scripts, which you can extend with scripts of your own.
| Command | Folder | The script's file name is |
| Input | Scripts/Input_Events | The event the Input Menu sends. |
| Choice_List | Scripts/Choice_Events | The event the choice menu sends. |
| Await | Scripts/Await_Events | The signal waited for. |
The folders are inside Candy_DE. Names aren't case-sensitive, and nothing needs registering: see How Drop-In Scripts Work, on the Code Modification page.
The scripts you add to these folders are 'safe': updates never modify them. The built-in event scripts are 'unsafe': updates overwrite the changes you make to them (see Updates).
The Custom command works the same way, with scripts in Scripts/Custom_Events: see Custom Commands. So does the CS_Move command, with one script for each movement in Scripts/Move_Events: see CS Move.
Return Values
Every event script has one function, handle(), which runs your code and then returns what the Command does next:
| Returns | Effect |
"Wait" | The Command continues to wait: the menu stays open, or the signal is waited for again. |
"Continue" | The Command is over, and the Dialogue carries on with the next Line. |
"Return" | The Command is over, and the Dialogue returns from the current Block, as a Return command does. |
"END" | The Dialogue ends. |
handle() can use await: the Command waits for it to return. It can also run Commands with engine.commands(), which takes a Command's name, its data and the context.
Returning from several Blocks
"Return" returns from one Bridge. To return from several, run the Return command itself, and return what it returns:
return await engine.commands("Return", {"Levels": "2"}, ctx)
Input Events
An Input Menu signals its Input command by emitting the Engine's input_received signal, with an event and the data that accompanies it (see Input Menus):
caller.input_received.emit(depth, "hint", "keypad")
The script named after the event handles it. Four events are built in:
| Script | Event |
| Finish.gd | Leaves the Input command, and retains the Input Menu. |
| Close.gd | Leaves the Input command, and deletes the Input Menu. |
| Run_Lines.gd | Runs the Lines it's sent, then continues to wait. |
| Suspend.gd | Sent by the Engine itself when the Dialogue is suspended: holds the Input Menu until it resumes. |
To add an event, add a script that extends CandyInputEvent. This Hint.gd script handles the event sent above, and leaves the menu open:
extends CandyInputEvent
func handle(_engine: CandyEngine, _input_ui: Node, _depth: int, parameters: Variant, _ctx: CandyContext) -> String:
Hints.show_hint(str(parameters))
return "Wait"
| Argument | Value |
| engine | The Engine running the Input command. |
| input_ui | The Input Menu that is open. |
| depth | The nesting depth of the Input command. |
| parameters | The data the Input Menu sent with the event. |
| ctx | The Line holding the Input command, a CandyContext: conversation, block, line_index, line_data. |
An event that leaves the Input command decides what becomes of the Input Menu: hide it, delete it with queue_free(), or leave it on screen.
An event with no script ends the Input command, with a warning, so that a faulty Input Menu can't block the Dialogue.
Choice Events
A choice menu signals its Choice_List command by emitting the Engine's choice_list_event signal. The built-in choice button emits it when it's pressed:
caller.choice_list_event.emit(parent_menu.nesting_depth, "Choice", category_name, choice_name, self)
The signal carries the menu's nesting depth, the event, then three values at the event's disposal: a category, a choice, and the node that sent it.
Three events are built in:
| Script | Event |
| Choice.gd | Sent by a choice button: the player picked a choice. Runs the choice's transition. |
| Timer.gd | Sent by the menu, with the timer's name as the choice: a timer ran out. Runs the timer's transition, and loops it. |
| Suspend.gd | Sent by the Engine itself when the Dialogue is suspended: holds the menu until it resumes. |
To add an event, add a script that extends CandyChoiceEvent, and send the event from your own menu, category or button scene. For example, a button can report that the mouse hovers it:
caller.choice_list_event.emit(parent_menu.nesting_depth, "Preview", category_name, choice_name, self)
This Preview.gd script then displays a preview of the hovered choice, and leaves the menu open:
extends CandyChoiceEvent
func handle(_engine: CandyEngine, _menu: Node, _depth: int, _category: Variant, choice: Variant, _sender: Object, _ctx: CandyContext) -> String:
Preview_Panel.show_preview(str(choice))
return "Wait"
| Argument | Value |
| engine | The Engine running the Choice_List command. |
| menu | The choice menu that is open. |
| depth | The nesting depth of the Choice_List command. |
| category, choice, sender | The last three values of the signal, as the menu sent them. |
| ctx | The Line holding the Choice_List command. |
Leaving the menu
An event that returns anything other than "Wait" must close the menu first. The Choice_List command doesn't do it:
await CandyChoices.get_shared().close_menu(engine, self, depth, menu)
return "Continue"
This restores the mouse mode, clears what the Engine recorded for this menu, and displays the menu underneath again, if any. With await, the event also waits for that menu's reactivate(), which may play an animation.
close_menu() deletes the menu at once. To play the menu's closing animation first, call play_closing() before it. It runs the menu's x_menu_closing() hook function and waits for it (see Menu UI):
if not await CandyChoices.get_shared().play_closing(engine, self, depth, menu, {"finish": "my_event"}):
return "END"
await CandyChoices.get_shared().close_menu(engine, self, depth, menu)
return "Continue"
The last argument is added to the hook's candy_args. play_closing() returns false when the Dialogue was stopped while the animation played: the event then returns "END".
An event with no script is ignored, with a warning, and the menu stays open.
The built-in events
Choice.gd and Timer.gd carry most of a choice menu's behaviour. They're 'unsafe' files: if you edit them, the Wizard warns you at the next update, then overwrites your changes. Rather than editing them, add an event of your own, or write your code in the x_choice_selected() hook function or the hook functions of the menu.
Await Events
By default, the Dialogue carries on as soon as the signal an Await command waits for is emitted. A script named after the signal runs your code at that moment, and decides what happens next.
The script extends CandyAwaitEvent. This Door_Opened.gd script handles a door_opened signal, and the Dialogue waits until the vault door opens:
extends CandyAwaitEvent
func handle(_engine: CandyEngine, _emitter: Object, arguments: Array, _ctx: CandyContext) -> String:
if arguments.is_empty() or arguments[0] != "vault":
return "Wait"
return "Continue"
| Argument | Value |
| engine | The Engine running the Await command. |
| emitter | The object that emitted the signal. |
| arguments | The signal's arguments, as an Array. Empty for a signal with none. |
| ctx | The Line holding the Await command. |
- The script is named after the signal alone, whatever object emits it: the pressed signals of two different buttons are both handled by Pressed.gd. Read emitter to distinguish them.
- When handle() runs, the Await line's Variable already contains the signal's arguments.
"Wait" waits for the signal to be emitted again, then runs handle() again.
No script ships in this folder. See Await.