Handling Choices
Dialogue nodes can present the player with multiple choices, typed input fields, and conditionally visible options. This guide covers how to read, display, and respond to every kind of player interaction in a StoryFlow dialogue.
Choice Options
When a dialogue node has options, they are delivered through the StoryFlowDialogueState that your UI receives on every dialogue update. Each option is represented by a StoryFlowDialogueOption object with two properties:
- id (
String) - A unique identifier for this option, used when callingselect_option - text (
String) - The display text for the option, already fully resolved with variable interpolation
You read options from the options array on StoryFlowDialogueState:
# Inside your dialogue UI script
func _on_dialogue_updated(state: StoryFlowDialogueState) -> void:
# Clear previous option buttons
for child in options_container.get_children():
child.queue_free()
# Create a button for each available option
for option in state.options:
var button := Button.new()
button.text = option.text
button.pressed.connect(_on_option_pressed.bind(option.id))
options_container.add_child(button) Pre-Filtered Options
The options array only contains options the player should see right now. Hidden options (those that fail their visibility condition or have already been used as once-only) are automatically excluded. Your UI simply iterates the array without any additional filtering.
Selecting an Option
When the player clicks a choice, call select_option on the StoryFlowComponent with the option's id:
# Call select_option on the StoryFlowComponent
storyflow_component.select_option(option_id)
When select_option is called, the runtime follows the dialogue node's output edge that matches the selected option. Internally, the source handle format is source-{nodeId}-{optionId}, but you never need to construct this yourself - just pass the id from StoryFlowDialogueOption.
Here is a complete example wiring a button click to option selection:
extends Control
@onready var storyflow: StoryFlowComponent = %StoryFlowComponent
@onready var options_container: VBoxContainer = %OptionsContainer
func _ready() -> void:
storyflow.dialogue_updated.connect(_on_dialogue_updated)
func _on_dialogue_updated(state: StoryFlowDialogueState) -> void:
# Build option buttons
for child in options_container.get_children():
child.queue_free()
for option in state.options:
var button := Button.new()
button.text = option.text
button.pressed.connect(_on_option_pressed.bind(option.id))
options_container.add_child(button)
func _on_option_pressed(option_id: String) -> void:
# Tell the runtime the player picked this option
storyflow.select_option(option_id)
# The component will emit dialogue_updated with the next
# dialogue state, or dialogue_ended if the story is over. Variable Re-rendering
If an option's edge leads to a Set* node (e.g., setBool, setInt) that has no outgoing edge, the runtime automatically returns to the current dialogue and re-renders it with the updated variable values. This enables live variable interpolation - for example, a toggle option that updates displayed text without leaving the dialogue.
Advancing Narrative-Only Dialogues
Not every dialogue presents choices. Narrative-only dialogues display text (and optionally a character, image, or audio) but have no selectable options. You can detect this state and show a "Continue" or "Next" button instead.
A dialogue is narrative-only when:
StoryFlowDialogueState.can_advanceistrueStoryFlowDialogueState.optionsis empty
func _on_dialogue_updated(state: StoryFlowDialogueState) -> void:
# Update speaker name, dialogue text, portrait, etc.
update_dialogue_display(state)
if state.options.is_empty() and state.can_advance:
# No options - show a "Continue" button
continue_button.visible = true
options_container.visible = false
else:
# Has options - show the option buttons
continue_button.visible = false
options_container.visible = true
populate_options(state.options)
func _on_continue_pressed() -> void:
# Advance uses the dialogue node's header output edge
storyflow.advance_dialogue() Once-Only Options
Options can be marked as once-only in the StoryFlow Editor. After a player selects a once-only option, it is permanently hidden from future displays of that dialogue node. This is useful for:
- One-time dialogue branches ("Ask about the artifact" disappears after asking)
- Unlockable conversations that are consumed on use
- First-encounter dialogue that should not repeat
- Exhaustible question lists where the player works through available topics
The runtime tracks used once-only options via the StoryFlowRuntime autoload. Each entry is keyed as nodeId-optionId. The manager provides mark_option_used() and is_option_used() for tracking.
# Access once-only option state through the manager
var manager = get_node("/root/StoryFlowRuntime")
# Check if a specific once-only option has been used
var key := "%s-%s" % [node_id, option_id]
var already_used: bool = manager.is_option_used(key)
# To reset a specific once-only option (e.g., for New Game+)
var used_options: Dictionary = manager.get_used_once_only_options()
used_options.erase(key)
# To reset all once-only tracking
manager.get_used_once_only_options().clear() Once-Only Persistence
Once-only option tracking persists across dialogue sessions and survives save/load cycles. When you call save_to_slot() on the StoryFlowRuntime autoload, the used once-only options dictionary is included in the serialized state. This means a player who saves after exhausting a dialogue option will not see it again when they load that save. If you need to reset once-only tracking (for example, when starting a new game), call reset_all_state() on the manager before beginning the new session.
Input Options
Beyond simple clickable choices, the StoryFlow Editor supports typed input fields on dialogue nodes - including string, integer, float, boolean, and enum inputs. These allow players to enter values directly rather than choosing from predefined options.
Not Yet Available in Godot
Typed input options are a feature of the StoryFlow Editor's built-in runtime. Support for input options in the Godot plugin is planned for a future release. Currently, only standard button-style choice options are supported via select_option(). If your StoryFlow project uses input options, you will need to implement custom UI handling and map user input to variable changes using the component's set_bool_variable(), set_int_variable(), set_string_variable(), and other typed setter functions.
Conditional Visibility
Options in the StoryFlow Editor can have visibility conditions connected to them. These are boolean node chains (using Get, And, Or, Not, and comparison nodes) that determine whether an option should appear at runtime.
The runtime evaluates these conditions automatically every time the dialogue is rendered. Options that fail their visibility check are excluded from the options array before it reaches your UI.
# You do NOT need to check visibility yourself.
# The options array is already filtered:
for option in state.options:
# Every option here has passed its visibility check.
# Hidden options are simply not in the array.
create_option_button(option)
# Example scenario:
# - "Buy Sword (50 gold)" only visible when player has >= 50 gold
# - "Enter VIP Room" only visible when has_vip_pass is true
# - "Ask about the map" hidden after it's been selected (once-only)
# All of this is handled before options reaches your UI. Because visibility conditions are re-evaluated on every render, they respond to variable changes in real time. If a Set* node updates a variable that a visibility condition depends on and then returns to the dialogue, the option list will reflect the new state immediately.
Next Steps
Now that you understand how to handle choices, learn how to work with Variables to create dynamic conditions and persistent state, or explore Characters to display speaker portraits and names alongside your dialogue options.