Skip to main content

Overview

Options allow players to make choices in dialogue, creating interactive conversations. Each option represents a possible response the player can choose, and each option can lead to different dialogue branches.

Adding Options

Options are attached to text entries using add_option():
var entry = add_text_entry("What would you like to know?")

# Add options to the entry
var option_1 = entry.add_option("Tell me about yourself.")
var option_2 = entry.add_option("Where am I?")
var option_3 = entry.add_option("Goodbye.")
Each option returns an option ID (starting from 0) that you use to configure where that option leads.

Setting Option Gotos

Every option needs a goto that specifies which dialogue entry to jump to when that option is chosen:
var entry = add_text_entry("What would you like to know?")

# Add options
var option_1 = entry.add_option("Tell me about yourself.")
var option_2 = entry.add_option("Where am I?")

# Create target entries for each option
var about_entry = add_text_entry("I'm a humble merchant.")
var location_entry = add_text_entry("You're in the village square.")

# Connect options to their targets
entry.set_option_goto_id(option_1, about_entry.get_id())
entry.set_option_goto_id(option_2, location_entry.get_id())
Options without valid gotos will cause the dialogue to cancel when chosen. Always set gotos for your options.

Choosing Options

Your UI code is responsible for presenting options to the player and calling choose_option() when the player makes a choice:
# When dialogue_continued is emitted with an entry that has options:
dialogue_engine.dialogue_continued.connect(func(entry: DialogueEntry) -> void:
    if entry.has_options():
        # Present options to the player
        for i in entry.get_option_count():
            var option_text = entry.get_option_text(i)
            print("%d. %s" % [i + 1, option_text])
        
        # When player selects option (e.g., option 0):
        entry.choose_option(0)
        
        # Then advance to process the choice
        dialogue_engine.advance()
)

Options with Branches

Options commonly lead to different branches that later merge back together:
enum {
    DEFAULT_TOPIC = 0,
    GO_BACK_TO_SLEEP = 1,
    KEEP_WORKING = 2,
}

func _setup() -> void:
    var entry = add_text_entry("The storm rages outside. I should...")
    
    # Option 1: Sleep branch
    var option_id_1 = entry.add_option("Go back to sleep.")
    var sleep_entry = add_text_entry("Sleep is for the strong.", GO_BACK_TO_SLEEP)
    entry.set_option_goto_id(option_id_1, sleep_entry.get_id())
    
    # Option 2: Work branch
    var option_id_2 = entry.add_option("Get back to work.")
    var work_entry = add_text_entry("Let's get back to work.", KEEP_WORKING)
    entry.set_option_goto_id(option_id_2, work_entry.get_id())
    
    # Merge branches back to main path
    var merge_point = add_text_entry("Some time passes...")
    sleep_entry.set_goto_id(merge_point.get_id())
    work_entry.set_goto_id(merge_point.get_id())
    
    add_text_entry("<Press 'Space' or 'Enter' to quit>")

Managing Options

DialogueEntry provides several methods for working with options:

Checking for Options

var entry = dialogue_engine.get_current_entry()

# Check if entry has any options
if entry.has_options():
    print("This entry has player choices")

# Get the number of options
var count = entry.get_option_count()
print("Number of options: ", count)

# Check if entry has no options
if entry.is_options_empty():
    print("No choices available")

Getting Option Data

var entry = dialogue_engine.get_current_entry()

# Get option text
for i in entry.get_option_count():
    var text = entry.get_option_text(i)
    print("Option %d: %s" % [i, text])

# Get option goto
var goto_id = entry.get_option_goto_id(0)
var goto_entry = entry.get_option_goto_entry(0)
if goto_entry:
    print("Option 0 leads to: ", goto_entry.get_text())

Modifying Options

var entry = add_text_entry("What would you like?")
var option_id = entry.add_option("Original text")

# Change option text
entry.set_option_text(option_id, "Updated text")

# Remove a specific option
entry.remove_option_at(option_id)

# Clear all options
entry.clear_options()

Checking Chosen Option

# Check if an option was chosen
if entry.has_chosen_option():
    var chosen = entry.get_chosen_option()
    print("Player chose option: ", chosen)

# Clear the chosen option
entry.remove_chosen_option()

Complete Example

Here’s a full example from the demos showing a branching options dialogue:
extends DialogueEngine

enum {
    DEFAULT_TOPIC = 0,
    GO_BACK_TO_SLEEP = 1,
    KEEP_WORKING = 2,
}

func _setup() -> void:
    var entry = add_text_entry("The storm rages right outside the window. I should...")
    
    var option_id_1 = entry.add_option("Go back to sleep.")
    var option_id_1_entry = add_text_entry("That's right, sleep is for the strong 💪.", GO_BACK_TO_SLEEP)
    entry.set_option_goto_id(option_id_1, option_id_1_entry.get_id())
    
    var option_id_2 = entry.add_option("Get back to work.")
    var option_id_2_entry = add_text_entry("That's right, let's get back to work 🫡", KEEP_WORKING)
    entry.set_option_goto_id(option_id_2, option_id_2_entry.get_id())
    
    # Join branches into the default topic
    var default_topic = add_text_entry("Some time passes...")
    option_id_1_entry.set_goto_id(default_topic.get_id())
    option_id_2_entry.set_goto_id(default_topic.get_id())
    
    add_text_entry("<Press 'Space' or 'Enter' to quit>")

Options vs Conditions

Both options and conditions create branching dialogue, but they serve different purposes:

Options

  • Player makes the choice
  • Creates interactive dialogue
  • Requires UI to present choices
  • Pauses dialogue flow until choice is made

Conditions

  • Game logic makes the choice
  • Creates dynamic responses
  • Processes automatically
  • No pause in dialogue flow
# Options: Player decides
var entry = add_text_entry("What will you do?")
entry.add_option("Fight") # Player chooses
entry.add_option("Flee")

# Conditions: Game decides
var condition = add_conditional_entry(func() -> bool:
    return player_is_brave # Game checks state
)

Option Validation

The engine validates options when they’re chosen:
# Invalid option ID
entry.choose_option(99) # Warns and cancels dialogue

# Missing goto
var option_id = entry.add_option("Test")
# If set_option_goto_id() is never called, choosing this option will cancel dialogue

# Invalid goto
entry.set_option_goto_id(option_id, 9999) # Non-existent entry ID
# Choosing this option will cancel dialogue
Invalid options trigger the dialogue_canceled signal, allowing you to handle errors gracefully in your UI.

Dynamic Options

You can create options dynamically based on game state:
var entry = add_text_entry("What would you like to buy?")

# Add options based on available items
for item in shop_inventory:
    if player.gold >= item.price:
        var option_id = entry.add_option("Buy %s (%d gold)" % [item.name, item.price])
        var response = add_text_entry("You bought %s." % item.name)
        entry.set_option_goto_id(option_id, response.get_id())

Common Patterns

Hub Menu

Create a dialogue menu that returns to itself:
func _setup() -> void:
    var hub = add_text_entry("What would you like to know?")
    
    # Add multiple topics
    var topics = [
        ["Tell me about the town.", "This is a peaceful village..."],
        ["Tell me about the quest.", "A dragon threatens the kingdom..."],
        ["Tell me about yourself.", "I'm just a simple merchant..."],
    ]
    
    for topic in topics:
        var option_id = hub.add_option(topic[0])
        var response = add_text_entry(topic[1])
        entry.set_option_goto_id(option_id, response.get_id())
        # Return to hub after each topic
        response.set_goto_id(hub.get_id())
    
    # Add exit option
    var exit_id = hub.add_option("Goodbye.")
    var exit_entry = add_text_entry("Farewell!")
    hub.set_option_goto_id(exit_id, exit_entry.get_id())

Conditional Options

Show different options based on game state:
var entry = add_text_entry("How can I help you?")

if player.has_item("letter"):
    var option_id = entry.add_option("I have a letter for you.")
    var response = add_text_entry("Ah, thank you!")
    entry.set_option_goto_id(option_id, response.get_id())

if player.quest_active:
    var option_id = entry.add_option("About that quest...")
    var response = add_text_entry("How's it going?")
    entry.set_option_goto_id(option_id, response.get_id())

# Always available option
var goodbye_id = entry.add_option("Never mind.")
var goodbye = add_text_entry("Alright then.")
entry.set_option_goto_id(goodbye_id, goodbye.get_id())

Timed Options

Implement time-limited choices (requires custom UI code):
var timer = Timer.new()
var auto_choice_made = false

dialogue_engine.dialogue_continued.connect(func(entry: DialogueEntry) -> void:
    if entry.has_options() and not auto_choice_made:
        # Show options to player
        display_options(entry)
        
        # Start timer
        timer.start(5.0) # 5 second limit
        await timer.timeout
        
        # If player hasn't chosen, auto-select
        if not entry.has_chosen_option():
            entry.choose_option(0) # Default to first option
            dialogue_engine.advance()
            auto_choice_made = true
)

Best Practices

  1. Always set gotos: Every option needs a valid goto
  2. Provide clear text: Make option text clear and concise
  3. Limit choices: 2-4 options is usually ideal for readability
  4. Merge branches: Bring divergent paths back together when appropriate
  5. Test all paths: Make sure every option leads somewhere valid
Options and conditions are mutually exclusive on the same entry. Setting a condition on an entry with options will cause the options to be ignored.

UI Implementation Example

Here’s a simple example of how to implement option display in your UI:
extends Control

var dialogue_engine: DialogueEngine
var option_buttons: Array[Button] = []

func _ready() -> void:
    dialogue_engine = YourDialogueEngine.new()
    dialogue_engine.dialogue_continued.connect(_on_dialogue_continued)

func _on_dialogue_continued(entry: DialogueEntry) -> void:
    # Display the dialogue text
    $DialogueLabel.text = entry.get_text()
    
    # Clear previous options
    for button in option_buttons:
        button.queue_free()
    option_buttons.clear()
    
    # Create buttons for each option
    if entry.has_options():
        for i in entry.get_option_count():
            var button = Button.new()
            button.text = entry.get_option_text(i)
            button.pressed.connect(func() -> void:
                entry.choose_option(i)
                dialogue_engine.advance()
            )
            $OptionsContainer.add_child(button)
            option_buttons.append(button)
    else:
        # No options, advance on click
        await get_tree().create_timer(0.1).timeout
        if Input.is_action_just_pressed("ui_accept"):
            dialogue_engine.advance()

Next Steps

Metadata

Add custom data to dialogue entries

Branching

Learn more about branch IDs and gotos

Build docs developers (and LLMs) love