Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Options

A node that just prints lines is a monologue. A node with options is a conversation.

title: Crossroads
---
Aria: Which way do you go?
-> The forest path.
    Aria: You hear wolves. You press on anyway.
-> The mountain pass.
    Aria: Cold, but clear. You can see for miles.
===

Every line starting with -> is an option. When the runner hits a set of options, it pauses and emits a DialogueEvent::Options(…). Your game shows the choices, the player picks one, you call runner.select_option(index), and execution continues into that option’s body.

Indentation matters

The body of an option is everything indented beneath it:

-> The forest path.
    Aria: You hear wolves.     <- runs only if this option is chosen
    Aria: You keep walking.    <- also inside the forest branch
-> The mountain pass.
    Aria: Cold, but clear.     <- runs only if the mountain option is chosen

Use spaces or tabs - just be consistent inside a block. After the options end, execution continues with whatever comes next in the node:

-> Yes.
    Aria: Good.
-> No.
    Aria: Fine.
Aria: Either way, we leave at dawn.   <- runs after either branch

Guards

Options can have conditions - so the “buy a sword” choice only shows up if you can afford it.

Merchant: What'll it be?
-> A fine sword (10g) <<if $gold >= 10>>
    Merchant: Sharp as a winter morning.
-> A loaf of bread (1g) <<if $gold >= 1>>
    Merchant: Still warm.
-> Nothing for now.
    Merchant: Come back soon.

The <<if>> after the option text is a guard. When the guard evaluates to false, the option is still presented - but marked available: false in the event:

DialogueEvent::Options(opts) => {
    for opt in &opts {
        if opt.available {
            println!("  [ ] {}", opt.text);
        } else {
            println!("  [x] {} (locked)", opt.text);
        }
    }
}

Whether you render locked options greyed-out, hidden, or with a tooltip is your call. Bubbles gives you the available field and steps aside.

Tip: If the player tries to select_option on an unavailable entry, Bubbles returns a runtime error. Filter or disable those choices in your UI.

Options that don’t jump

You don’t have to <<jump>> out of an option. If the body just runs and ends, execution falls through to whatever comes after the options block:

Aria: Ready?
-> Yes.
-> Actually, wait.
    Aria: Take a breath.
Aria: Alright, let's go.   <- always runs, after either branch

Both options land on the final line. That’s a handy pattern for “gate” choices where you want a beat before continuing.

A shopping example

Let’s make something practical - a little shop with a running gold total. This uses variables (next chapter!) but the shape is all options.

title: Shop
---
<<declare $gold = 20>>
Merchant: Welcome. What would you like?
-> Sword, 15 gold <<if $gold >= 15>>
    <<set $gold = $gold - 15>>
    Merchant: A fine blade.
-> Torch, 3 gold <<if $gold >= 3>>
    <<set $gold = $gold - 3>>
    Merchant: Mind the sparks.
-> Just browsing.
    Merchant: Suit yourself.
Merchant: Anything else? You have {$gold} gold left.
===

Three options, each guarded by gold, each doing its own thing. The final line runs regardless - and uses {$gold} interpolation to show the updated balance.


Try it: examples/snippets/options.bub: an insult sword-fight duel showing guarded options and an <<elseif>> skill chain.

cargo run -p bubbles-tui -- examples/snippets/options.bub

Next: Tags and Metadata