BranchScene.h header

#include <ew/core/BranchScene.h>

Namespace ew::core

BranchEffect struct

struct ew::core::BranchEffect

One thing a node does to the story's state when the reader passes it.

This is the half of the variable system that WRITES. A Condition reads state; with nothing to put anything into it, every condition would test the value its declaration started at for ever, and branching logic would be decoration.

Members

ew::core::foundation::VariableId ew::core::branching::BranchEffect::variableId

The variable it writes.

By id, never by name, for the same reason a Condition tests by id: renaming gold to coins must not silently break what writes to it. A null id is a defect the validator reports rather than something applied to nothing.

BranchEffectOperation ew::core::branching::BranchEffect::operation =

What it does to that variable.

VariableValue ew::core::branching::BranchEffect::value

The value for BranchEffectOperation::Set, or the amount for BranchEffectOperation::Add. Unused by every other operation.

ew::core::foundation::VariableId ew::core::branching::BranchEffect::sourceVariableId

The variable READ by BranchEffectOperation::SetFromVariable; null for every other operation.

A second id rather than a VariableValue alternative holding one: a value is what a variable currently contains, and "the contents of `currentTown`" is not a value – it is an instruction to go and look, which is exactly what distinguishes this operation from Set.

bool operator==(const BranchEffect &, const BranchEffect &)=default

Effects compare equal when all four fields match.

BranchNode struct

struct ew::core::BranchNode

One node in a branching scene.

Carries its OWN content rather than pointing at another object, which is the reason this is not built on ew::core::canvas: a canvas card is a reference to a note or entity that already exists, while a branch node's text – the line spoken, the choice offered – exists nowhere else.

Members

ew::core::foundation::BranchNodeId ew::core::branching::BranchNode::id

Stable identity. Links reference this.

BranchNodeType ew::core::branching::BranchNode::type =

What the node does.

QString ew::core::branching::BranchNode::text

The prose shown to a reader (Line, Hub), or the author's label for the fork (Choice, Condition, Jump). Never empty in a finished scene, and the validator says so.

ew::core::foundation::ContentId ew::core::branching::BranchNode::speakerId

The codex entity saying this line; null when nobody in particular does (narration, or a node that is not spoken).

By id, never by name. An author renaming a character must not silently orphan every line they speak – the same rule Variable references follow, for the same reason. A speaker pointing at an entity that has been deleted is reported as a dangling reference rather than quietly becoming narration.

Localisation carries it as translator context (a line reads differently depending on who says it), voice extraction derives the cast list from it, and the graph shows it – three things that were each maintained by hand beside a script that already knew the answer.

ew::core::foundation::ContentId ew::core::branching::BranchNode::listenerId

The codex entity being spoken TO, when it matters; null otherwise.

Separate from the speaker because a translator, a director and a camera all need both, and because a line addressed to one of three people present is a different line.

QString ew::core::branching::BranchNode::speakerDisplayName

What the reader sees the speaker called here, when it differs from the entity's name – "the hooded figure" before a reveal, "Father" from one character and "the King" from another.

The entity is still the speaker: casting, the cast list and every reference keep working while the reader is told something else. Empty means the entity's own name.

QString ew::core::branching::BranchNode::delivery

How the line is delivered – "weary", "shouted", "under her breath".

Free text rather than a fixed set: no vocabulary of emotions survives contact with a real script, and a closed list would send authors back to writing it in the line itself. It reaches the voice line list, where a director reads it.

QString ew::core::branching::BranchNode::notes

The author's own note about this node; never shown to a reader.

Direction the line needs and the prose cannot carry – "VO: weary, he has said this before", "check this against the treaty date". It travels with the node, is searchable, and is not a comment: a comment is a conversation ABOUT the line and gets resolved, a note is part of writing it. Variable::description is the same idea on a variable, and the editor asks for both with the same words.

ew::core::foundation::VariableId ew::core::branching::BranchNode::variableId

For a Condition, the variable it tests; null otherwise.

By id, never by name – an author renaming gold to coins must not silently break the branch that tests it. K2 recorded the same rule for the same reason.

BranchComparison ew::core::branching::BranchNode::comparison =

For a Condition, how its variable is compared against comparand.

VariableValue ew::core::branching::BranchNode::comparand

For a Condition, the value its variable is compared against.

A whole value rather than a string, so gold >= 50 compares two NUMBERS rather than two spellings of a number – the same reason VariableValue is a closed set of three alternatives instead of text everyone re-parses.

QString ew::core::branching::BranchNode::conditionExpression

For a Condition, the expression it tests – gold >= 50 AND NOT hasKey – instead of the single variable/comparison/comparand above.

When this is non-empty it is what the condition means, and the three fields above are ignored. They are kept, not replaced: every scene ever saved holds them, a fork on one variable is still what most conditions are, and the editor writes whichever form the author chose. Written in the language ew::core::expression::evaluate reads, over the story's variables (see ew::app::expression::VariableBindings for how they are named).

ew::core::foundation::BranchNodeId ew::core::branching::BranchNode::jumpTarget

For a Jump, the node it sends the reader to; null otherwise.

ew::core::foundation::ContentId ew::core::branching::BranchNode::jumpSceneId

For a Jump that leaves this scene, the scene jumpTarget is in; null for a jump within the same scene.

Null means "here", and that is deliberate: every jump ever authored is a local one, and a field that had to be filled in to mean what it already meant would rewrite every scene file in every project on first save. It also keeps the common case – most jumps are local – free of ceremony.

The target node must be one the other scene declares in BranchScene::entryPointIds. Jumping into the middle of a scene nobody opened up is how a story acquires a path its author never sanctioned, and the validator says so.

std::vector<BranchEffect> ew::core::branching::BranchNode::effects

What passing this node does to the story's state, in order.

Applied when the reader ARRIVES, before this node's outgoing links are chosen – so a Condition that also carries an effect tests the state it arrived with, and the node after it sees the change. Ordered, so two effects on one variable compose the way they are written.

They live on the NODE rather than on a Choice's option link. An effect per option would save a node in some scenes; it would also be a second placement to validate, serialise, edit and explain, and the node behind an option already exists – it is where the consequence prose goes ("You hand over the coins").

double ew::core::branching::BranchNode::x = 0.0

Where the node sits on the canvas. Presentation only – the story does not depend on it, and a scene with every node at the origin still plays correctly (the editor can lay it out).

double ew::core::branching::BranchNode::y = 0.0

The node's y position.

BranchScene class

class ew::core::BranchScene

A branching scene: typed nodes and the directed links between them.

Links are DIRECTED and that is the whole point. A reader goes from one node to the next; a link from A to B is not a link from B to A, and a model that could not tell them apart could not express the thing being authored.

Members

ew::core::branching::BranchScene::BranchScene(ew::core::foundation::ContentId id, QString title)

Creates an empty scene with identity id and title.

const QString & ew::core::branching::BranchScene::title() const

The scene's title.

void ew::core::branching::BranchScene::setTitle(QString title)

Sets the title.

ew::core::foundation::BranchNodeId ew::core::branching::BranchScene::startNodeId() const

The node the reader starts at; null when the scene has no entry point yet.

void ew::core::branching::BranchScene::setStartNodeId(ew::core::foundation::BranchNodeId id)

Sets the starting node.

const std::vector< BranchNode > & ew::core::branching::BranchScene::nodes() const

The nodes, in insertion order.

void ew::core::branching::BranchScene::setNodes(std::vector< BranchNode > nodes)

Replaces every node.

const BranchNode * ew::core::branching::BranchScene::findNode(ew::core::foundation::BranchNodeId id) const

The node with id, or null when the scene has none.

std::vector< BranchLink > ew::core::branching::BranchScene::linksFrom(ew::core::foundation::BranchNodeId id) const

The links leaving id, in order – a Choice's options, in the order the reader sees them.

const std::vector< ew::core::foundation::BranchNodeId > & ew::core::branching::BranchScene::entryPointIds() const

The nodes another scene's Jump may enter this one at, in declaration order.

The author's sanctioned ways in. A jump from another scene may land only here – not in the middle of a conversation, where a reader would arrive without the state the surrounding nodes assume. startNodeId is always an entry in effect and does not need declaring.

They are also what makes the Unreachable finding survive cross-scene jumps. Reachability is walked forwards from the start node; a node entered only from elsewhere has nothing leading to it inside its own scene, so without a declaration it would be reported as orphaned prose. Declaring it says "this is reached from outside" once, rather than every validator having to scan every scene in the project for a jump that might land here.

void ew::core::branching::BranchScene::setEntryPointIds(std::vector< ew::core::foundation::BranchNodeId > ids)

Replaces the declared entry points.

bool ew::core::branching::BranchScene::isEntryPoint(ew::core::foundation::BranchNodeId id) const

Whether id may be entered from another scene – the start node, or a declared entry point.

Enumerations

enum class BranchComparison { Equals, NotEquals, LessThan, AtLeast }

How a Condition compares its variable against a value.

enum class BranchEffectOperation { Set, SetFromVariable, Add, Toggle, Clear }

What an effect does to its variable when the reader passes the node carrying it.

enum class BranchLinkGuard { Always, WhenTrue, WhenFalse }

When a link is taken.

A Condition's two outcomes are named EXPLICITLY rather than taken from link order. Order would work until somebody reordered the links – and then the story would quietly invert, sending the reader down the false branch whenever the condition held.

enum class BranchNodeType { Line, Choice, Condition, Jump, Hub }

What a node in a branching scene does when the reader reaches it.

enum class UnavailableOption { Hidden, ShownDisabled }

What a reader sees when an option's condition does not hold.

Functions

std::optional< BranchComparison > ew::core::branching::branchComparisonFromToken(QStringView token)

Parses a comparison from its serialization token; std::nullopt if unrecognized or absent.

std::optional< BranchEffectOperation > ew::core::branching::branchEffectOperationFromToken(QStringView token)

Parses an effect operation from its serialization token; std::nullopt if unrecognized or absent.

std::optional< BranchLinkGuard > ew::core::branching::branchLinkGuardFromToken(QStringView token)

Parses a link guard from its serialization token; std::nullopt if unrecognized or absent.

std::optional< BranchNodeType > ew::core::branching::branchNodeTypeFromToken(QStringView token)

Parses a node type from its serialization token; std::nullopt if unrecognized or absent.

QString ew::core::branching::toToken(BranchNodeType type)

Returns the stable serialization token for type.

QString ew::core::branching::toToken(BranchComparison comparison)

Returns the stable serialization token for comparison.

QString ew::core::branching::toToken(UnavailableOption option)

Returns the stable serialization token for option.

QString ew::core::branching::toToken(BranchLinkGuard guard)

Returns the stable serialization token for guard.

QString ew::core::branching::toToken(BranchEffectOperation operation)

Returns the stable serialization token for operation.

std::optional< UnavailableOption > ew::core::branching::unavailableOptionFromToken(QStringView token)

Parses an unavailable-option behaviour from its token; std::nullopt if unrecognized or absent.