21 KiB
name, description
| name | description |
|---|---|
| dialogic | Dialogic 2 addon for Godot 4 — a dialog system with timelines, events, characters, subsystems, styles, layouts, variables, save/load, and text effects. Use when working with dialog trees, visual novel scenes, character portraits, branching conversations, or any Dialogic-related code in this project. |
Dialogic 2 Skill
Dialogic is a Godot 4 addon (addons/dialogic/) providing a full dialog and visual novel system. It is an autoload (Dialogic) with a modular subsystem/event architecture.
Official docs: https://docs.dialogic.pro/
Architecture Overview
DialogicGameHandler (autoload "Dialogic")
├── Subsystems (modular, extendable)
│ ├── Animations — tweening/transition helpers
│ ├── Audio — sound effects, music, channels
│ ├── Backgrounds — background images/scenes with transitions
│ ├── Choices — branching choice menus
│ ├── Expressions — expression/variable evaluation
│ ├── Glossary — hover-to-inspect word definitions
│ ├── History — conversation log
│ ├── Inputs — keyboard/mouse input handling
│ ├── Jump — label navigation & jump/return logic
│ ├── PortraitContainers — layout slots for portraits
│ ├── Portraits — character portrait display & animation
│ ├── Save — built-in save/load system
│ ├── Settings — runtime settings
│ ├── Styles — UI layout/style management
│ ├── Text — text display with BBCode effects
│ ├── TextInput — player text input prompts
│ ├── VAR — variable storage & manipulation
│ └── Voice — voice/audio per character line
Core Files
| File | Purpose |
|---|---|
addons/dialogic/Core/DialogicGameHandler.gd |
Autoload: manages state, timeline execution, subsystems |
addons/dialogic/Core/DialogicUtil.gd |
Static helpers (filesystem, variables, conversions, suggestions) |
addons/dialogic/Core/DialogicResourceUtil.gd |
Resource directory management, caches, lookups |
addons/dialogic/Core/Dialogic_Subsystem.gd |
Base class for all subsystems |
addons/dialogic/Resources/event.gd |
Base class for all events |
addons/dialogic/Resources/timeline.gd |
Timeline resource |
addons/dialogic/Resources/character.gd |
Character resource |
addons/dialogic/Resources/dialogic_style.gd |
Style resource |
addons/dialogic/Resources/dialogic_layout_base.gd |
Layout resource |
addons/dialogic/Resources/dialogic_layout_layer.gd |
Layout layer resource |
addons/dialogic/Resources/dialogic_style_layer.gd |
Style layer resource |
DialogicGameHandler API (Dialogic autoload)
Access via Dialogic (global autoload name) or DialogicUtil.autoload().
States (enum States)
IDLE— awaiting input to advanceREVEALING_TEXT— currently revealing textANIMATING— an animation is playingAWAITING_CHOICE— awaiting choice selectionWAITING— awaiting something (e.g., timer)
current_state property; state_changed(new_state) signal.
Pausing
Dialogic.paused = true # pauses all subsystems (not the scene tree)
Dialogic.paused = false # resumes
Signals: dialogic_paused, dialogic_resumed
Starting/Ending Timelines
# Start a timeline AND load a layout scene (use for first dialog)
var layout_node = Dialogic.start(timeline, label_or_idx)
# timeline: DialogicTimeline resource OR string path OR identifier name
# label_or_idx: optional label (string) or event index (int) to jump to
# Start without loading layout (use when layout already exists)
Dialogic.start_timeline(timeline, label_or_idx)
# Preload (prepare without playing)
var preloaded = Dialogic.preload_timeline(timeline_resource)
# End the current timeline
Dialogic.end_timeline(skip_ending:=false)
# Check if timeline exists
if Dialogic.timeline_exists(timeline): ...
# Only start dialog if none is active
if Dialogic.current_timeline == null:
Dialogic.start("my_timeline")
Signals: timeline_started, timeline_ended, event_handled(resource)
Timeline Properties
Dialogic.current_timeline # DialogicTimeline resource
Dialogic.current_timeline_events # Array of event resources
Dialogic.current_event_idx # int
Dialogic.current_state_info # Dictionary of state info
Clear States (enum ClearFlags)
Dialogic.clear(Dialogic.ClearFlags.FULL_CLEAR) # clear everything
Dialogic.clear(Dialogic.ClearFlags.KEEP_VARIABLES) # keep variables
Dialogic.clear(Dialogic.ClearFlags.TIMELINE_INFO_ONLY) # only clear timeline/index
Save/Load Full State
var state = Dialogic.get_full_state() # returns Dictionary
Dialogic.load_full_state(state) # restores full dialog state
Event Handling
Dialogic.handle_next_event() # advance to next event
Dialogic.handle_event(event_index) # jump to specific event index
Subsystem Access
All subsystems are accessible as properties:
Dialogic.Animations
Dialogic.Audio
Dialogic.Backgrounds
Dialogic.Choices
Dialogic.Expressions
Dialogic.History
Dialogic.Inputs
Dialogic.Jump
Dialogic.PortraitContainers
Dialogic.Portraits
Dialogic.Save
Dialogic.Settings
Dialogic.Styles
Dialogic.Text
Dialogic.TextInput
Dialogic.VAR
Dialogic.Voice
Dialogic.Glossary
Check existence: Dialogic.has_subsystem("SubsystemName")
Timeline Events
Events are scripts in addons/dialogic/Modules/<Module>/event_<name>.gd. They extend DialogicEvent (base class in Resources/event.gd).
Event Types
| Module | Event | Purpose |
|---|---|---|
| Text | event_text |
Display dialog text with character portrait |
| Character | event_character |
Join/leave/update character portraits |
| Choice | event_choice |
Present branching choices |
| Condition | event_condition |
Branch based on variable conditions |
| Audio | event_audio |
Play sound effects / music on channels |
| Background | event_background |
Change background with transition |
| Jump | event_jump |
Jump to a label |
| Jump | event_label |
Define a label target |
| Jump | event_return |
Return from a jump |
| Signal | event_signal |
Emit custom signals |
| Variable | event_variable |
Set/modify variables |
| Wait | event_wait |
Wait for a duration |
| WaitInput | event_wait_input |
Wait for player input |
| Clear | event_clear |
Clear subsystems |
| Comment | event_comment |
Editor-only comments |
| End | event_end |
End the timeline |
| Save | event_save |
Trigger save |
| Settings | event_setting |
Change runtime settings |
| Style | event_style |
Switch style/layout |
| Glossary | event_glossary |
Manage glossary |
| History | event_history |
Manage history |
| TextInput | event_text_input |
Prompt player for text |
| Voice | event_voice |
Play voice clip |
| Call | event_call |
Call a method on an autoload |
| Core | event_end_branch |
Internal branch-ending marker |
Timeline Text Format
Timelines are saved in text format (.dtl files). They use TAB indentation to know what events belong to a choice or condition block. Only changes in indentation matter — be consistent within one block.
Custom-syntax events (bare text, no brackets):
# Comments (lines starting with #)
# Comment Event — editor-only, ignored at runtime
# Text Event
CharacterName: Dialog text here.
CharacterName (portrait): Dialog text with portrait change.
NoName: Text said by no one in particular.
Long text ending with \ will include the next line too.\
This line is part of the same text event.
# Character Event
join CharacterName (portrait) position [animation="Bounce In"]
leave CharacterName [animation="Bounce Out" length="0.3"]
update CharacterName (portrait) position [animation="Tada" wait="true"]
# Condition Event (indentation-based blocks)
if {coins} == 0:
# indented events run when true
elif {health} <= 10:
# another branch
else:
# fallback
# Choice Event (each option starts with -)
- Yes, let's go!
# indented events fire when chosen
- No, not now. | [if {relationship} > 23]
# conditional choice
- Maybe | [if {charisma} > 10] [else="disable" alt_text="Maybe [too insecure]"]
# Set Variable Event
set {MyVariable} += 10
set {flag} = true
# Label Event
label LabelIdentifier
label LabelIdentifier (Display Name)
# Jump Event
jump LabelIdentifier
jump TimelineName/LabelIdentifier # jump to label in another timeline
jump TimelineName/ # jump to beginning of another timeline
# Return Event
return
# Do/Call Event
do AutoloadName.method("argument")
Shortcode events (wrapped in [...]):
[background path="res://icon.png" fade="1.0"]
[audio path="res://music.ogg" channel="music" fade="1.0"]
[signal arg="activate_something"]
[end_timeline]
[wait time=1.5]
[clear time=0.5]
[style name="MyStyle"]
[save slot="auto"]
Shortcode parameter values must be in double quotes. Order doesn't matter.
Characters
Characters are .dch resources managed via DialogicResourceUtil:
var char = DialogicResourceUtil.get_character_resource("CharacterName")
var all_chars = DialogicResourceUtil.get_character_directory()
Character Properties
- Display Name: Shown on name labels. Can contain
{variables}. - Nicknames: Used for autocolor names feature.
- Default Portrait: Used when no portrait is specified (Character join,
[portrait]text effect). Does NOT affect Text events without a portrait. - Portraits: Dictionary of portrait name → scene settings. A portrait is a scene (
.tscn) instanced by Dialogic. The default portrait scene uses an image. Custom portraits extendDialogicPortrait. - Style: Optional custom style used when this character speaks.
- Sound Moods: Random typing sounds per portrait.
- Prefix/Suffix: Text segments wrapped around each text section (e.g., quotation marks).
- Scale, Offset, Mirror: Per-character and per-portrait transform settings.
Character lookup
Characters are found by identifier string in the dch_directory. The directory maps identifiers to file paths. By default, the identifier is derived from the filename (e.g., Miko.dch → "Miko"). The lookup is case-sensitive — ensure timeline text uses the exact same case as the .dch filename.
If a character is not found at runtime, Dialogic creates a temporary character with no portraits. Portraits won't display because change_speaker() skips characters with empty portraits.
Variables
Variables are stored in ProjectSettings under dialogic/variables as a nested Dictionary. Access/manage via Dialogic.VAR subsystem.
Important: Variables must be pre-declared in the Dialogic Variables editor (or project.godot) before they can be set by timeline set commands. The Variable event reads the current value first and fails if the variable doesn't exist.
DialogicUtil.get_default_variables() # returns the full variable dict
DialogicUtil.list_variables(dict, path) # flatten to array of paths
DialogicUtil.get_variable_type(path, dict) # get VarTypes enum value
# VarTypes enum: ANY, STRING, FLOAT, INT, BOOL
Variable references in expressions
Dialogic variables are referenced with curly braces in expressions (conditions, set values, choice conditions):
if {coins} == 0:
set {coins} += 10
- Option | [if {player_has_key}]
Bare names like coins are NOT resolved as variables — they're treated as autoload properties by Godot's Expression class.
Text Effects (inline BBCode)
Text effects are tags within TextEvent text that trigger actions when reached during text reveal:
| Effect | Syntax | Purpose |
|---|---|---|
| Speed | [speed=x] / [speed] |
Set speed multiplier (1 = normal, 0 = instant). Resets per event. |
| Letter Speed | [lspeed=x] / [lspeed=x!] |
Set letter reveal time in seconds. ! makes it absolute (ignores multiplier). |
| Pause | [pause=x] / [pause=x!] |
Pause reveal for x seconds. ! makes it absolute. |
| Portrait | [portrait=name] |
Switch speaker's portrait mid-text. |
| Signal | [signal=arg] |
Emit Dialogic.text_signal with argument. |
| Mood | [mood=name] |
Change typing sound mood. |
| Extra Data | [extra_data=value] |
Set extra data on speaker's portrait. |
| New Event | [n] / [n+] |
Split text into manual-advance sections. + appends to previous without clearing. |
| Input | [input] |
Await any input (can be skipped). |
| Auto-Advance | [aa] / [aa=x] / [aa=x?] |
Enable auto-advance. x sets time. ? sets time without enabling. |
| No Skip | [ns] / [ns=x] |
Disable text skipping for this event. x sets auto-advance time. |
Important: Text effects are NOT paired — there is no [/speed] closing tag. Effects change behavior from that point until the next text event resets them. Writing [/speed] will appear as visible text.
Text Modifiers (preprocessors)
Text modifiers run on text before display:
| Modifier | Syntax | Purpose |
|---|---|---|
| Line Break | [br] |
Insert line break (useful in choices or with "New Lines as New events") |
| Random Selection | <Option1/Option2/Option3> |
Random variation for repeated text |
| Conditional Text | [if {cond} Text if true./Text if false.] |
Inline conditional text |
| Autopauses | (automatic) | Insert [pause] after configured punctuation characters |
Styles & Layouts
Styles define the visual presentation. Layouts define the scene structure.
- Dialogic Nodes: UI nodes (DialogText, NameLabel, PortraitContainer, ChoiceButton, etc.) that display things. They register themselves in groups.
- Layout Scenes: Scenes containing Dialogic Nodes and custom scripts. Base layout extends
DialogicLayoutBase, layers extendDialogicLayoutLayer. - Styles: Resources combining one base layout + list of layer layouts with overridable settings. Can inherit from other styles.
Dialogic.Styles.load_style("StyleName") # load/switch to a style
Dialogic.Styles.has_active_layout_node() # check if layout is present
Dialogic.Styles.get_layout_node() # get the layout scene node
Dialogic.Styles.change_style("StyleName") # change style, preserving state
Signals & Signal Events
Signal Event
Emits Dialogic.signal_event with an argument:
Dialogic.signal_event.connect(_on_dialogic_signal)
func _on_dialogic_signal(argument: String):
if argument == "activate_something":
print("Activated!")
Text Signal (via [signal=arg] text effect)
Emits Dialogic.text_signal:
Dialogic.text_signal.connect(_on_dialogic_text_signal)
Timeline Start/End
Dialogic.timeline_started.connect(_on_timeline_started)
Dialogic.timeline_ended.connect(_on_timeline_ended)
Key Subsystem Signals
Dialogic.Text.about_to_show_text(info: Dictionary)
Dialogic.Text.text_finished(info: Dictionary)
Dialogic.Text.speaker_updated(character: DialogicCharacter)
Dialogic.Text.textbox_visibility_changed(visible: bool)
Dialogic.Portraits.character_joined(info: Dictionary)
Dialogic.Portraits.character_left(info: Dictionary)
Dialogic.Portraits.character_portrait_changed(info: Dictionary)
Dialogic.VAR.variable_changed(info: Dictionary)
Dialogic.VAR.variable_was_set(info: Dictionary) # only from set variable events
Saving & Loading
# Simple save/load (takes screenshots too)
Dialogic.Save.save("slot_name")
Dialogic.Save.load("slot_name")
# Save without screenshot
Dialogic.Save.save("slot_name", false, Dialogic.Save.ThumbnailMode.NONE)
# Save with extra info (date, custom data)
var extra = {"text": Dialogic.current_state_info["text"], "date": Time.get_datetime_string_from_system(false, true)}
Dialogic.Save.save("slot_name", false, Dialogic.Save.ThumbnailMode.STORE_ONLY, extra)
# Check if slot exists
if Dialogic.Save.has_slot("slot_name"):
var info = Dialogic.Save.get_slot_info("slot_name")
Savegames store Dialogic's timeline state and variables. Extra info is stored per-slot but not loaded into Dialogic state.
Audio Channels
Audio plays on named channels. Default channels defined in project settings:
DialogicUtil.get_audio_channel_defaults() # returns Dictionary
DialogicUtil.get_audio_channel_suggestions("search")
DialogicUtil.validate_audio_channel_name("name")
Channel names: "" (default), "music" (looping by default), plus custom.
Resource Management (DialogicResourceUtil)
# Directories (map identifier -> path)
DialogicResourceUtil.get_directory('dtl') # timelines
DialogicResourceUtil.get_directory('dch') # characters
DialogicResourceUtil.update_directory(extension) # rescan
# Lookups
DialogicResourceUtil.get_timeline_resource("name")
DialogicResourceUtil.get_character_resource("name")
DialogicResourceUtil.get_unique_identifier_by_path("res://...")
DialogicResourceUtil.get_resource_path_from_identifier("name", "dtl")
# Label cache (editor only)
DialogicResourceUtil.get_label_cache()
DialogicResourceUtil.update_label_cache()
# Event cache
DialogicResourceUtil.get_event_cache() # all event types
DialogicResourceUtil.update_event_cache()
Utility Helpers (DialogicUtil)
# Variables
DialogicUtil.VarTypes # ANY, STRING, FLOAT, INT, BOOL
DialogicUtil.get_default_variables()
DialogicUtil.list_variables(dict, path, type)
DialogicUtil.get_variable_value_type(value)
DialogicUtil.get_variable_type(path, dict)
DialogicUtil.logical_convert(value) # auto-convert string to int/float/bool
# Suggestions (for editor fields)
DialogicUtil.get_character_suggestions(search, current, allow_none, allow_all)
DialogicUtil.get_portrait_suggestions(search, character, allow_empty)
DialogicUtil.get_autoload_suggestions(filter)
DialogicUtil.get_autoload_method_suggestions(filter, autoload_name)
DialogicUtil.get_autoload_property_suggestions(filter, autoload_name)
DialogicUtil.get_audio_bus_suggestions(filter)
DialogicUtil.get_audio_channel_suggestions(search)
# Timer
DialogicUtil.is_physics_timer()
DialogicUtil.update_timer_process_callback(timer)
Project Settings
Key settings (under dialogic/):
dialogic/variables— default variable dictionarydialogic/layout/default_style— default style namedialogic/layout/end_behaviour— 0=free, 1=hide, 2=keep layout on enddialogic/text/letter_speed— default letter reveal timedialogic/text/autocolor_names— auto-color character names in textdialogic/text/split_at_new_lines— treat newlines as[n]dialogic/text/autopauses— auto-insert pauses after punctuationdialogic/extensions_folder— custom modules path (default:res://addons/dialogic_additions/)dialogic/directories/*_directory— resource directories (dch, dtl, style)dialogic/audio/channel_defaults— default audio channels
Working with Dialogic in This Project
Common Patterns
Starting dialog (guard against double-start):
func _input(event):
if player_is_in_area and Input.is_action_pressed("start_interaction"):
if Dialogic.current_timeline == null:
Dialogic.start("my_timeline")
Reacting to dialog signals:
Dialogic.signal_event.connect(_on_dialogic_signal)
Dialogic.timeline_ended.connect(_on_dialog_finished)
func _on_dialog_finished():
Dialogic.timeline_ended.disconnect(_on_dialog_finished)
# game logic here
Setting variables from code:
Dialogic.VAR.set_variable("player_name", "Miko")
Dialogic.VAR.set_variable("coins", 42)
Creating timelines in code:
# From strings (follow text syntax)
var events: Array = """
Jowan (Surprised): Wow this is interesting!
- Yes
set {MyAutoload.excitement} += 20
- No
set {MyAutoload.excitement} -= 10
""".split('\n')
var timeline = DialogicTimeline.new()
timeline.events = events
Dialogic.start(timeline)
Hiding/showing textbox:
if Dialogic.Text.is_textbox_visible():
Dialogic.Text.hide_textbox()
else:
Dialogic.Text.show_textbox()
Getting current speaker:
var speaker: DialogicCharacter = Dialogic.Text.get_current_speaker()
var portrait_info := Dialogic.Portraits.get_character_info(speaker)
Troubleshooting
- Portraits not showing: Characters must join the scene with a Character Event (Join mode) before their portraits appear. Ensure
.dchfilename case matches timeline text exactly. - Wrong text displayed: If translation is enabled, CSV rows take priority. Disable translation during development or update CSVs.
- Lag on dialog start: Preload styles with
style.prepare()or preload an empty timeline during your loading screen. - Input conflicts: If your interaction key matches the dialogic input action, guard with
if Dialogic.current_timeline == null:before starting new dialog.