While both the sculptural and QBN models define their own terms of how sections of a story might become available (constraints and qualities, respectively), neither explain how to express these.
Working with the story format SugarCube in Twine 2, the library TinyQBN by Grams (2019) solves this issue through using the tags of passages. Based on the requirements of not containing spaces, Grams (2019) introduced a shorthand for writing qualities using hyphens between the operators (what is being compared) and the operation (how to compare values): operator-operation-operator.
This format created an easy way to include prerequisites in Twine using tags in passages, but comes with some issues:
Starting with version 1.4, SimpleQBN began to use the MongoDB Query Language for expressions. As of version 1.4.2, SimpleQBN started using the Quis NPM package. As of version 1.5.0, SimpleQBN exclusively uses Quis syntax for all expressions, removing MongoDB query language support to reduce dependencies and code size.
Quis expressions support:
==, !=, >, >=, <, <=&& (AND), || (OR), ! (NOT)($health > 50 && $level >= 5) || $emergency == true$variableName for simple valuesExamples:
$health > 50$user_role == "admin" && $user_active == true($score >= 80 || $bonus_points > 10) && $attempts <= 3For full Quis syntax documentation, see the Quis documentation.
The Expression class wraps a single Quis expression string and evaluates it against a State.
| Method / Property | Description |
|---|---|
Expression(expression) |
Constructor — accepts a Quis expression string. |
expression |
Read-only getter — returns the internal expression string. |
change(s) |
Replaces the internal expression with a new string. |
check(state) |
Evaluates the expression against a State; returns boolean. |
import Expression from 'simple-qbn/Expression';
import State from 'simple-qbn/State';
const expr = new Expression('$health > 50');
const s = new State();
s.set('health', 80);
expr.check(s); // true
expr.expression; // '$health > 50'
expr.change('$health < 30');
expr.check(s); // false
A QualitySet is an ordered collection of Expression objects. A quality set is valid when all of its expressions are valid (logical AND).
| Method | Description |
|---|---|
QualitySet() |
Constructor — creates an empty set. |
add(expression) |
Adds a Quis expression string (no duplicates). |
has(s) |
Returns true if the expression string is already in the set. |
remove(s) |
Removes an expression string from the set. |
check(state) |
Returns true if every expression is valid against the given State. |
size() |
Returns the number of expressions in the set. |
import QualitySet from 'simple-qbn/QualitySet';
import State from 'simple-qbn/State';
const qs = new QualitySet();
qs.add('$level >= 5');
qs.add('$hasKey == true');
const s = new State();
s.set('level', 10);
s.set('hasKey', true);
qs.check(s); // true
qs.size(); // 2
qs.has('$level >= 5'); // true
qs.remove('$hasKey == true');
qs.size(); // 1
The reactive architecture provides ReactiveExpression and ReactiveQualitySet, which automatically re-evaluate when the bound ReactiveState changes.
| Method / Property | Description |
|---|---|
ReactiveExpression(expression, state?) |
Constructor — Quis string and optional ReactiveState. |
expressionString |
Read-only getter — the internal expression string. |
result |
Getter — current evaluation result (requires bound state). |
check(state) |
Evaluates against a given ReactiveState. |
change(newExpression) |
Replaces the expression and notifies listeners. |
subscribe(listener) |
Subscribes to result changes; returns unsubscribe function. |
bindToState(state) |
Binds to a ReactiveState. |
unbind() |
Unbinds from the current state. |
dispose() |
Cleans up all subscriptions and listeners. |
listenerCount() |
Number of active listeners. |
clearListeners() |
Removes all listeners. |
import ReactiveExpression from 'simple-qbn/reactive/Expression';
import ReactiveState from 'simple-qbn/reactive/State';
const state = new ReactiveState();
state.set('score', 10);
const expr = new ReactiveExpression('$score > 50', state);
expr.result; // false
expr.subscribe((result) => {
console.log('Expression is now:', result);
});
state.set('score', 75); // logs: "Expression is now: true"
expr.dispose(); // clean up when done
| Method / Property | Description |
|---|---|
ReactiveQualitySet(state?) |
Constructor — optional ReactiveState. |
result |
Getter — true if all expressions pass (requires bound state). |
add(expression) |
Adds a Quis expression string. |
has(s) |
Returns true if expression exists in the set. |
remove(s) |
Removes an expression and disposes it. |
check(state) |
Evaluates all expressions against a given ReactiveState. |
size() |
Number of expressions in the set. |
subscribe(listener) |
Subscribes to result changes; returns unsubscribe function. |
bindToState(state) |
Binds to a ReactiveState. |
unbind() |
Unbinds from the current state. |
dispose() |
Cleans up all internal expressions, subscriptions, and listeners. |
listenerCount() |
Number of active listeners. |
clearListeners() |
Removes all listeners. |
Note: MongoDB query language support was removed in version 1.5.0. This section is preserved for historical reference only. Current versions use only Quis syntax.
In versions 1.4.0 through 1.4.x, SimpleQBN supported MongoDB query language through the Mingo library.
For example, an Expression to test if the State value score was greater than 15 would be written as the following:
{score: {$gt: 15}}
The use of the MongoDB query language also introduces much more complex queries as well. To test if the State value score was greater than 15, State value act1 was true, and State value reputation was less than 3.5, it would be the following:
{score: {$gt: 15}, act1: true, reputation: {$lt: 3.5}}
Grams, J. (2019). Tiny-QBN. GitHub. Retrieved from https://github.com/JoshuaGrams/tiny-qbn