Skip to content

Story

Feather’s story system covers the three things most narrative games need: branching dialogues, quests with staged objectives, and cinematics. All of it is project data, authored in the Story window or through story.* commands, and saved with the project.

Conditions and actions are short Rhai snippets that use the same functions as world scripts. A dialogue choice can check the player’s inventory, and a quest stage can complete when a script value reaches a goal.

  • Dialogues: a node graph of lines and choices, with speakers, conditions, actions, voice-over, speaker animations, auto-advance and per-line camera framing.
  • Quests: ordered stages with objectives, progress toward a goal, completion and failure conditions, on-screen tracking, world markers and a journal.
  • Cinematics: a camera path with timed events — subtitles, animations, moves, sounds, fades and more — with letterbox, skipping and a blend back to gameplay.
  • Story progress — dialogue choices seen, quest status and stage — is kept in saves.
  1. Open Window → Story and select the Dialogues tab.
  2. Create a dialogue. Add nodes to the graph and drag between them to link a line to the next one or to a choice’s target.
  3. For each node, set the speaker and the text. Add choices for the player, each with optional conditions and actions.
  4. Use the preview to step through the conversation in the editor.
  5. Start the dialogue from a script, for example when the player interacts with a character.
// A world script: talk to the blacksmith (object 18).
fn on_interact(id) {
if id == "18" {
start_dialogue("blacksmith_intro", 18);
}
}
Field Description
speaker npc (the character the dialogue was started with), player, an object name or any display name; empty is a narrator line
text The line. {key} inserts script state and {npc} the partner’s name. A node with no text is a silent logic step that runs its action and follows the first choice whose condition passes
condition Rhai condition; when false the node is skipped and next follows
action Rhai code run when the node is entered
next The node after this one when it has no choices; empty ends the dialogue
choices Up to 12 choices
voice A sound played with the line
animation A clip the speaker plays
camera Free, Speaker, OverShoulder or Wide; unset uses the dialogue’s camera
auto_advance Seconds before advancing on its own; 0 waits for the player
Choice field Description
text What the player picks
next Node to go to; empty ends the dialogue
condition Rhai condition; when false the choice is hidden
show_disabled, hint Show a failing choice greyed out with a hint, such as “Needs 50 gold”
action Rhai code run when picked
once Offer the choice only until it has been picked once

The dialogue’s camera defaults to OverShoulder. Other options are Speaker (close on the speaker’s face), Wide (both characters side by side) and Free (leave the camera where it is).

  1. In Window → Story, select the Quests tab and create a quest.
  2. Give it a title, description and category, such as “Main” or “Side”, and a reward shown in the journal.
  3. Add stages in order. For each stage, write a short objective and set how it completes.
  4. Start the quest with auto_start, from a dialogue action, or from a script.
Quest field Description
category Journal group
start The first stage; empty uses the first in the list
auto_start A Rhai condition that starts the quest when it becomes true
on_complete, on_fail Rhai code run when the quest ends
reward Text shown in the journal
hidden Internal quests never show in the journal or tracker
Stage field Description
objective Short text for the tracker; {key} inserts script state
details Longer journal entry
complete_when Rhai condition that completes the stage
fail_when Rhai condition that fails the whole quest
progress, goal A Rhai number shown as progress toward the goal
on_enter, on_complete Rhai code run when the stage starts and ends
next The next stage; empty completes the quest
target Object name the quest marker points at

Quest conditions are checked every quest_check_interval seconds.

A cinematic is a timed sequence: camera keys plus events. Events can show a subtitle, play an animation, move, place or show an object, play a sound, fade, shake the camera, set the time of day, start a dialogue or run Rhai code. A cinematic can follow an anchor object, show a letterbox, hide the HUD, be skippable and blend back to the gameplay camera.

Cinematics are edited on the Cinematics tab, and as tracks in the Sequencer. See Sequencer.

Field Description Range Default
text_speed Characters revealed per second; 0 shows lines at once 0–1000 45
dialogue_camera Frame the speakers during dialogue On/off On
pause_clock_in_dialogue Stop the game clock while a dialogue is open On/off On
quest_tracker Show active objectives on screen On/off On
quest_markers Draw a marker over the current objective’s target On/off On
quest_check_interval Seconds between quest condition checks 0.05–10 s 0.5 s
journal_key Opens the quest journal Key name J
skip_key Skips a skippable cinematic Key name Space
advance_keys Advance dialogue Up to 8 keys Space, Enter, E
letterbox Letterbox bar height as a fraction of the screen 0–0.3 0.11
subtitles Show subtitles for cinematic lines On/off On
Function Description
start_dialogue(id[, npc]) Starts a dialogue, optionally with an object as the npc speaker
end_dialogue(), dialogue_active() End the open dialogue, or check for one
dialogue_seen(dialogue, node) Whether the player has reached a node
start_quest(id), set_quest_stage(id, stage) Start a quest or jump to a stage
complete_quest(id), fail_quest(id) End a quest
quest_status(id) inactive, active, completed or failed
quest_stage(id), quest_active(id) The current stage, or whether the quest is active
play_cinematic(id), stop_cinematic(), cinematic_playing() Cinematics
play_sequence(id) Plays a Sequencer sequence
open_journal(), close_journal() The quest journal
Hook Runs when
on_dialogue_end(id) A dialogue finishes
on_quest(id, status, stage) A quest changes status or stage; status is started, stage, completed or failed
on_cinematic_end(id) A cinematic finishes
// A world script: play the ending cinematic when the main quest completes.
fn on_quest(id, status, stage) {
if id == "main" && status == "completed" {
play_cinematic("ending");
}
}
  • Up to 512 dialogues with 400 nodes each, and 12 choices per node.
  • Up to 256 quests with 64 stages each.
  • Up to 128 cinematics with 512 events each.
  • Text up to 4,096 characters and Rhai snippets up to 8,192 characters.
Terminal window
# A two-line dialogue with a choice
feather-cli --connect .feather-session.json command story.set --json '{
"kind":"dialogue",
"value":{"id":"blacksmith_intro","title":"Blacksmith","nodes":[
{"id":"hello","speaker":"npc","text":"Need a blade sharpened?","choices":[
{"text":"Yes, please.","next":"thanks"},
{"text":"Not today.","next":""}]},
{"id":"thanks","speaker":"npc","text":"Come back at dusk."}]}}'
# Show lines instantly and hide quest markers
feather-cli --connect .feather-session.json command story.settings \
--json '{"text_speed":0,"quest_markers":false}'

story.set replaces the whole item, so read it with story.get first when you change one part. story.get with no arguments summarises every dialogue, quest and cinematic plus the settings.