Files
orzo/.agents/skills/dialogic/SKILL.md

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 advance
  • REVEALING_TEXT — currently revealing text
  • ANIMATING — an animation is playing
  • AWAITING_CHOICE — awaiting choice selection
  • WAITING — 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 extend DialogicPortrait.
  • 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 extend DialogicLayoutLayer.
  • 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 dictionary
  • dialogic/layout/default_style — default style name
  • dialogic/layout/end_behaviour — 0=free, 1=hide, 2=keep layout on end
  • dialogic/text/letter_speed — default letter reveal time
  • dialogic/text/autocolor_names — auto-color character names in text
  • dialogic/text/split_at_new_lines — treat newlines as [n]
  • dialogic/text/autopauses — auto-insert pauses after punctuation
  • dialogic/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 .dch filename 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.