Questlight Studio
Docs / Story, dialog and quests

15. Story, dialog and quests

In short: One small language tells the whole story. Conditions ask a question ("does the party carry the key? is the flag set?") and effects change the world ("give an item, start a battle, recruit a character"). Dialogs, quests, puzzles, scenes and encounters all use them, and they all read and write one saved story state of flags and variables. Branching dialogs, quests with several endings, parallel scenarios and cinematics are data you assemble in editors, with no code.

15.1 Basics

15.1.1 The story state

The story state is one saved record of what has happened. Its keys are Gameplay Tags under five roots:

RootHoldsMissing value
Narrative.Flag.*true or falsefalse
Narrative.Var.*an integer (counters, choices, affinity)0
Narrative.Puzzle.*a puzzle solvednot solved
Narrative.Area.*an area, door or shortcut unlockedlocked
Narrative.Event.*a scene or optional event seennot seen

Examples: Narrative.Flag.Chapter1.MetMira, Narrative.Var.Chapter1.MiraTrust, Narrative.Puzzle.Mansion.ClockTower.

Keys are tags so that you pick them from a list (no typos), organize them by chapter, and rename them with a redirect without breaking data. Declare new keys in a tag file under the plugin's or your project's tags (Project Settings → GameplayTags). Validation rejects a key under the wrong root, for example a flag used as a variable. A new game starts from the values in Project Settings → RPG Saga Engine → Narrative (starting flags, variables and unlocked areas).

Never delete a tag that a save may contain: rename it with a redirect.

15.1.2 Conditions and effects

Conditions are questions. A condition set has a mode (All or Any) and a list of conditions. An empty set passes. The common ones:

ConditionTrue when
Has ItemThe shared inventory holds at least that many
Flag IsA flag has the wanted value
Variable CompareA variable compares true (==, !=, <, <=, >, >=; missing = 0)
Member In Party / Member Not KOA character is in the party (and standing)
Puzzle Solved / Area Unlocked / Event SeenThe matching state key is set
Instigator IsWho or what triggered this (the player, a certain actor)
Has MoneyThe party has that much money
Quest State / Quest Objective Complete / Ending IsA quest or ending condition
Time Of Day Is Between / Phase Of Day Is / Weather Is / Day Number / Season IsThe clock and weather
Place Discovered / Area VisitedTravel state
NPC Is At ActivityA living NPC is doing something
Has Card / Beat Card OpponentThe card game
Dialog Node Visited / Dialog Option Chosen / Dialog SeenDialog history (dialogs with history on)
All Of / Any Of / NotCombine other conditions
Custom (Blueprint)Your own class

A condition that lacks the state it needs (for example no party) does not pass.

Effects change the world. Every effect has an optional Only If gate (itself a condition set), so you can write small branches without a separate asset. The common ones:

GroupEffects
StateSet Flag, Modify Variable (Set or Add), Mark Puzzle Solved, Unlock Area, Mark Event Seen
Items and moneyGive Item, Take Item, Give Money, Take Money, Open Shop, Rest At Inn
PartyAdd Member, Remove Member (Permanent or Parallel Scenario), Restore Party, Set Level, Give Experience, Learn Ability, Forget Ability, Teach Ability, Award Growth Points, Open Teacher
Story flowStart Battle, Start Dialog, Play Scene, Start Quest, Set Quest Stage, Complete Quest Objective, Complete Quest, Fail Quest, Add Ending Score, Request Point Of No Return
WorldSet Time, Advance Time, Set Weather, Lock Weather, Set Clock, Set NPC Schedule, Discover Place, Travel To, Open World Map, Change Encounter Rate, Remove Encounter Rate, Alert Enemies, Report Crime, Set Data Layer State
PuzzlesOpen Code Lock, Use Item On Object, Apply Examine
PresentationPlay Music, Stop Music, Restore Music, Play Sound, Show Tutorial
RulesSet Rules Profile
CustomCustom (Blueprint)

An effect only broadcasts a real change: setting a flag to the value it already has sends no event.

15.1.3 An Interaction asset

URSEInteractionData (RSE Interaction Data) is the standard shape for examinable objects, doors, simple NPCs and puzzle pieces: Conditions → Effects on success, Failed Effects otherwise (for example "It is locked."), and with One Shot, Completed Effects after the first success ("The chest is empty."). The world actor only points at the asset. See Exploration.

15.1.4 Your first branching dialog

  1. In the Content Browser, right click a folder → RPG Saga → Dialog, fill the short form, press Create.
  2. Double-click the dialog to open the visual dialog editor.
  3. Right click the graph → Add Line, connect it from the green Dialog Start, and type the speaker and text.
  4. Add a Choice node: one output pin per option. Give each option text, and optional conditions and effects.
  5. Add an Effect node on one branch (for example Set Flag Narrative.Flag.Chapter1.MetMira).
  6. Press Play from Here in the toolbar. It starts the dialog in Play In Editor at the selected node.
  7. To start it in the world, place an RSE NPC and set its dialog (see Exploration), or use the Start Dialog effect from anywhere.

Dialog node types:

NodeDoesOutputs
LineA speaker says something (text, portrait, expression, media, voice)Next
ChoiceThe player picks from options (conditions, once-only, unavailable modes, hints)One per option
BranchTests conditionsOne per "If", then Else
EffectRuns an effect listNext
CueCamera, sequence, emote, expression, look-at, sound, screen effect, wait, or a generic cueNext
JumpGo to a node, or to another dialog (a call that returns)Go to, After return
EndEnds the dialognone

Text markup inside a line: <color=Danger>...</color> named colors (the look is in the UI style, never in the text), styles (emphasis, shout, whisper, shake, wave), {p=0.4} for a pause, {speed=0.5}...{/speed} for speed, and {cue=N} to fire the line's cue at that character.

Presentation is a tag on the dialog (Dialog Start → Presentation): Box (default), Cinematic (a title card and letterbox), Bark (a short line over a head, no choices) and Battle (in-battle banter, cannot start battles).

15.1.5 Your first quest

  1. Right click → RPG Saga → Quest. Double-click to open the visual quest editor.
  2. Select Quest Start to set the category (Main, Side, Card, Event, or your own Quest.Category.* tag), the description, the giver, and Available When.
  3. Add Stages (blue). Each has a journal entry and objectives. An objective completes when its Complete When conditions pass, when its Progress Variable reaches Required Progress, or explicitly (the Complete Quest Objective effect, usually from a dialog).
  4. Add Outcomes: green for success, red for failure, each with a final journal entry and effects (the rewards).
  5. Wire transitions: each stage output goes to another stage or to an outcome. With no transitions a stage continues to the next in the list, and after the last stage the quest ends with the default success outcome.
  6. Start it with the Start Quest effect, or tick Auto Start (optionally limited to certain maps).

15.2 How it works

Content (dialog, quest, interaction, scene, encounter) holds a condition set and an effect list
  -> URSENarrativeSubsystem (game instance) owns FRSENarrativeState and runs them
  -> conditions read a read-only FRSEConditionContext (story state + party + inventory + money + time)
  -> effects write through an FRSEEffectContext and raise events
  -> RSECore effects that need the world (battle, dialog, scene, screen) raise a request;
     the RSE presentation layer carries it out

Everything story-related lives in RSECore, which knows no actors, UI or input. The dialog UI, scene player, NPCs and triggers in RSE react to requests such as Start Dialog, Play Scene and Start Battle. This keeps the story testable without a world and makes it part of the save.

Quest states are Not Started, Active, Completed, Failed, Missed and Abandoned. Failed means the player failed inside the quest. Missed means the story moved on before the quest was finished: a failure rule marked Missed, or the point of no return (see below). Only Side quests can be abandoned (Can Abandon).

Endings. An Ending Set (URSEEndingSetData, linked from Project Settings → Quests → Ending Set) defines endings, key choices and a hidden score. The Add Ending Score effect raises the score from anywhere. Request Point Of No Return asks the player to confirm that the story cannot go back, and quests marked to fail there become Missed. Ending Is tests the result.

15.3 Key classes and assets

Class / assetModuleRole
FRSENarrativeStateRSECoreThe saved flags, variables, solved puzzles, unlocked areas, seen events and one-shot interactions
URSENarrativeSubsystemRSECoreGame-instance subsystem: owns the state, creates contexts, runs interactions, raises events
URSENarrativeSettingsRSECoreProject Settings → Narrative: the state of a new game
URSECondition, FRSEConditionSetRSECoreOne question, and a list of them (All or Any)
URSEWorldEffect, FRSEEffectListRSECoreOne change, and an ordered list of them
URSEInteractionDataRSECoreData asset: conditions, effects, failed effects, one-shot
URSEDialogDataRSECoreData asset: a dialog graph of instanced nodes (URSEDialogNode_Line, _Choice, _Branch, _Effect, _Cue, _Jump, _End)
URSESpeakerDataRSECoreData asset: a speaker (name, portraits, expressions)
URSEDialogSubsystem, URSEDialogRunnerRSECoreRuns a dialog; headless
URSEDialogSettingsRSECoreText speed, auto-advance, co-op choice rules
URSEQuestDataRSECoreData asset: stages, objectives, transitions, outcomes, failure rules
URSEQuestSubsystem, URSEQuestSettingsRSECoreQuest state and commands; Project Settings → Quests
URSEEndingSetDataRSECoreData asset: endings, key choices, score
URSESceneDataRSECoreData asset: a Sequencer scene with bindings, dialog sync, end effects, music
URSEOptionalSceneData, ARSEOptionalSceneTriggerRSECore, RSEAn optional scene and the actor that offers it
URSEScenePlayerSubsystemRSEPlays scenes: letterbox, skip, input lock, HUD hide, save block
ARSESceneTriggerRSEVolume that plays a scene when the player walks in
ARSENPC, URSESpeakerComponent, URSENPCTalkComponentRSENPCs and the component that registers a speaker for dialogs and scenes
URSECinematicLibraryRSEEditorEditor library behind Cinematic from Screenplay

15.4 Settings

WhereWhat
Project Settings → RPG Saga Engine → NarrativeThe starting flags, variables and unlocked areas of a new game
… → DialogCharacters per second (40 default), text speed steps, auto-advance times, fast-forward, co-op choice deciders and vote rules
… → Dialog UIPlayer speaker tags, portraits, boxes (see User interface)
… → QuestsEnding Set, load quests from the Asset Manager, auto-track started quests, evaluation passes
… → CinematicsOutput folder, timing, shots, camera, staging, animation of generated cutscenes
… → ScenesThe scene player's defaults (letterbox, blend out)

See Appendix B for the full list.

15.5 Blueprint usage

The story library URSEBlueprintLibrary_Narrative has static nodes that need no object reference.

15.5.1 Read and write the story state

  • Is Flag Set (Flag), Set Flag (Flag, Value), Get Variable, Set Variable, Add To Variable (Delta), Is Puzzle Solved, Set Puzzle Solved, Is Area Unlocked, Set Area Unlocked, Is Event Seen, Set Event Seen.

Example, gating a door in a level Blueprint:

  1. Event (the player overlaps a trigger) → Is Flag Set (Flag = Narrative.Flag.Chapter1.MetMira).
  2. Branch: True → open the door. False → Start Dialog (Dialog = DA_Dlg_DoorLocked, Initiator = a speaker tag).

15.5.2 Run conditions and effects from a Blueprint

  • Check Conditions (a Condition Set variable you expose) and Run Effects (an Effect List variable). Both structs show their instanced editor in the Details panel, so designers fill them in without code.
  • Run Interaction and Can Run Interaction (an Interaction asset).
  • Request Battle (Encounter), Request Scene (Scene, Event, Mark Seen), Request Screen.

15.5.3 Dialogs and quests

  • Start Dialog (Dialog, Initiator, Start Node), Is Dialog Active, Abort Dialog.
  • Start Quest (Quest, Ignore Availability), Complete Quest Objective (Quest, Objective), Get Quest Status, Is Quest Active.
  • Subsystem getters: Get Narrative Subsystem, Get Dialog Subsystem, Get Quest Subsystem. The quest subsystem also offers Set Stage, Complete Quest, Fail Quest, Abandon Quest, Set Tracked Quest, Get Current Stage, Get Quests, Get Objective Views, Get Journal, Evaluate Ending, Get Ending Score, Request Point Of No Return and Confirm Point Of No Return.

15.5.4 Write a custom condition or effect

Create a Blueprint with the parent Custom (Blueprint) condition or effect. The library's Context... and Effect... nodes read and change the story from inside it: Context Is Flag Set, Context Get Variable, Context Get Item Count, Context Is Party Member, Context Get Level, Context Get Money, Effect Set Flag, Effect Modify Variable, Effect Give Item, Effect Take Item, Effect Add Money, Effect Add Member, Effect Remove Member, Effect Run Effects, Effect Request Battle.

See Appendix A for every node.

15.6 C++ usage

A custom condition and a custom effect:

UCLASS(meta = (DisplayName = "Moon Is Full"))
class UMyCondition_MoonIsFull : public URSECondition
{
	GENERATED_BODY()

public:
	virtual bool Evaluate(const FRSEConditionContext& Context) const override
	{
		return Context.Narrative && Context.Narrative->GetVariable(MyTags::Var_MoonPhase) == 4;
	}
};

UCLASS(meta = (DisplayName = "Ring The Bell"))
class UMyEffect_RingBell : public URSEWorldEffect
{
	GENERATED_BODY()

public:
	virtual void Execute(FRSEEffectContext& Context) const override
	{
		URSEBlueprintLibrary_Narrative::EffectSetFlag(Context, MyTags::Flag_BellRung, true);
	}
};

Both show up in the instanced picker of every condition set and effect list. They run headless, so unit-test them without a world.

Drive the story from code:

if (URSENarrativeSubsystem* Story = URSENarrativeSubsystem::GetNarrativeSubsystem(this)) // or GameInstance->GetSubsystem
{
	const bool bMet = Story->GetFlag(MyTags::Flag_MetMira);
	Story->RunInteraction(MyInteraction);
}

Extension points: subclasses of URSECondition and URSEWorldEffect; dialog node classes (your own node type appears in the editor's Add Node menu); URSEDialogCue for your own cue; URSEDialogControlPolicy for co-op rules of who may advance or choose.

15.7 Advanced

15.7.1 Parallel scenarios

The plugin has no special "scenario" class: a parallel scenario is a pattern of the primitives above, in the style of a classic multi-party story.

  1. A story beat splits the party. Run Remove Member with Departure = Parallel Scenario for each character leaving. Their equipment stays on them (a Permanent departure returns it to the inventory; guests always keep theirs).
  2. Run Add Member for the characters of the other group, and Set Flag Narrative.Flag.Act2.GroupB so the rest of the content can ask which group you are playing.
  3. Play the segment in its own level or area, with its own quests, dialogs and encounters gated by that flag.
  4. At the convergence, Add Member the first group back. A returning character keeps what it wore.

Because the story state is one record, a decision made in either segment (a flag, a variable, an ending score) is visible to the other.

15.7.2 Optional events

Optional scenes, where the player may watch what other characters are doing elsewhere, have their own pieces.

  1. Create an Optional Scene Data asset (URSEOptionalSceneData): a Display Name (the title shown to the player), an Event (a Narrative.Event.* tag that records it was seen), Conditions (when it is available), the Dialog that plays as the scene, Effects run when it is watched, and Missed When Skipped.
  2. Place an Optional Scene Trigger (ARSEOptionalSceneTrigger, an interactable actor with a large radius) in the level and set its Scene.
  3. While the player stands in range and the scene is available (conditions pass, the event is not yet seen), a discreet prompt offers it: "Optional scene: {Title}". Interacting watches it: the event is marked seen, the effects run and the dialog plays. Walking away ignores it: it stays available, or is missed for good when Missed When Skipped is on.
  4. Use the Event Seen condition to branch later content on whether the player saw it.

A scene can also be a full Sequencer scene (below) offered by an RSE Scene Trigger volume, or started from any effect list with Play Scene. A scene already seen is not queued again.

15.7.3 Scenes with Sequencer

A Scene Data (URSESceneData) plays a Level Sequence. Its parts:

  • Bindings: tagged Sequencer bindings played by real actors. Source is the Player, a dialog Speaker or an Actor Tag. Mode is Replace (the real actor plays it) or Stand In (the sequence's own stand-in plays and the real actor hides, lending its look). A binding can have Conditions and a Fallback (placeholder or hide).
  • Dialog Sync: Cues (a sequence plays to each dialog cue and waits for its dialog) or Line Sections (the dialog drives the sequence: each line has a section that plays once, then its hold loops until the player continues).
  • End Effects (run once when the scene ends or is skipped), Skippable, Hide Player, Keep State At End, Blend Out Seconds, and Music with fade and restore.

While a scene plays: letterbox, skip with confirmation, input locked, HUD hidden, saving blocked, then the camera blends back. Scenes queue: one at a time. Start one with RSE.Scene.Play <scene>, Skip with RSE.Scene.Skip.

15.7.4 Cinematic from Screenplay

Making a conversation into a staged cutscene by hand takes hours. Cinematic from Screenplay turns a dialog (or a pasted screenplay) into a Level Sequence plus scene data in one step. It places the speakers, sets up cine cameras with a shot grammar, times every line and writes LS_Cine_<Dialog> and DA_Scene_Cine_<Dialog>. The result is a first pass you can polish in Sequencer.

  1. Open the level where the scene happens.
  2. Right click a Dialog → RPG Saga: Cinematic from Screenplay... (or RPG Saga Engine → Advanced tools).
  3. Leave Staging and Anchor on Auto, or select the NPC or marker the scene happens around.
  4. Press Dry Run and read the report (cast, shots, cameras, warnings). Nothing changes.
  5. Press Generate, then Open Sequence. Tick Place Scene Trigger (or use the Play Scene effect) and press Play.

The tool follows a shot grammar: an establishing wide, an over-the-shoulder shot for each line, close-ups for emotional lines, reaction shots, two-shots for quick back-and-forth and the 180 degree rule so screen directions never flip. Timing comes from the voice, or the text length divided by a reading speed. Speakers talk, gesture and turn toward each other using the character's animation set.

Stage directions go at the start of a line in a screenplay: [shot:close], [camera:over:Mira], [pause:1.5], [move:Mira:to:Door], [anim:Mira:AM_Bow]. They are stored on the line and never shown in game.

Running it again updates only what the tool made: it lives in a Sequencer folder named RSE Generated, and tracks you add on its bindings are kept. Command line and console:

UnrealEditor-Cmd <Project>.uproject -run=RSECinematic -Map=/Game/Maps/L_Town -Dialog=<path> [-DryRun] [-Report=<file>]

In the open editor, RSE.Cinematic.Generate Dialog=... does the same. Settings are in Project Settings → Cinematics.

15.7.5 Dialogs: the details

  • History. Turn on Track History on a dialog to save visited nodes and chosen options. Once-only options, "already chosen" markers and the history conditions need it.
  • Portraits and media. Lines pick a speaker, an expression (Dialog.Expression.* tags: Happy, Angry, Sad, Surprised and your own) and optional media. See Character visuals and animation.
  • Co-op. Choices are decided by the Default rules (story choices versus system prompts), the speaker, the host, or a vote of every player. Set per choice, with defaults in Project Settings → Dialog → Co-op.
  • Localization. Every player-facing text is an FText with a deterministic key (Dlg.<AssetName> namespace, the node id as key), so translations survive edits. See Localization.
  • Import and export. The toolbar has Import Script and Export Script, the screenplay format of the content pipeline (=== DIALOG <Id> ...). Import shows the changes first.
  • Validation. Validate and the Issues panel check every dialog; RSE.Dialog.Explore <dialog> walks every path and reports dead ends.

15.7.6 Quests: the details

  • Failure rules (Fail When): conditions that fail the quest, optionally with Missed set, with their own outcome.
  • Objective flow: Parallel (all at once) or Sequential. Optional objectives never block the stage. Hidden objectives appear in the journal only when completed. An objective completed stays completed even if the condition becomes false later (for example the item was handed over).
  • Auto-track. Started quests become the tracked quest unless Auto Track Started Quests is off.
  • Quest-giver markers use the Quest.Giver.* and Quest.Region.* tags (NPC markers and map markers).
  • Console. RSE.Quests.List, Start, Complete, Fail, Track, Abandon, Ending, Dump, Trace; RSE.Narrative.Dump, Set and Reset read and change the story state. RSE.Dialog.Start <dialog>, RSE.Dialog.Dump.

15.7.7 Networking

The story state, dialogs, quests and scenes run on the host in co-op. Dialog advance and choice are commands that the host validates (FRSECmd_DialogAdvance, FRSECmd_DialogChoose and so on), so who may advance or choose is a rule, not a hack. See Co-op and online.

15.7.8 Performance notes

Conditions and effects are cheap synchronous reads and writes. Quest evaluation re-runs when story state changes, up to Max Evaluation Passes (64) per change so that cascading transitions settle. Dialogs and scenes load their soft references when they start.

15.8 Troubleshooting

SymptomCauseFix
A flag never changesThe effect has an Only If gate that fails, or the tag is under the wrong rootRun RSE.Narrative.Dump, check the gate and the root
A tag is "invalid" after a loadThe tag was deleted from the tag fileRestore it, or rename with a redirect
A dialog option is missingIts conditions fail, or its Unavailable Mode hides itCheck the option; use the Show disabled with hint mode if you want it greyed
A once-only option repeatsThe dialog does not Track HistoryTurn it on
A quest never advancesIts objective has no condition and no counter (explicit only), or no transition passesComplete it with the effect, or add a condition; check RSE.Quests.Dump
A quest says MissedA failure rule marked Missed passed, or the point of no return was passedCheck Fail When and Fails At Point Of No Return
A scene plays twiceIt has no Seen EventSet a Narrative.Event.* tag
Nothing happens on Play SceneThe scene has no Sequence, or the scene queue is busyCheck Scene Data validation and RSE.Scene.Dump
Generated camera looks through a wallThe tool does not avoid objectsMove the anchor, or edit the camera in Sequencer
A character stands still while speakingIts animation set has no Talk animationAdd one to the animation set
"The Dialogs content type is disabled"Pasted screenplays import through Content ImportEnable the Dialogs type in Project Settings → Content Import

15.9 See also