Hyperflex
Developer's Guide — Story (Classic 2), 2026 Update
Gates • visits • hits • scoped variables • safe expression evaluation1. Purpose
Hyperflex is the safe expression system used by Story (Classic 2) to decide whether page options are visible, active, hidden, or automatic. It evaluates visits, hit counts, page-family counts, saga-state variables, and qualified book/page references.
/pzlm-scripts/Story (Classic 2)/js/HyperflexCompiler.js/pzlm-scripts/Story (Classic 2)/js/ClassicStory.js/pzlm-assets/Story (Classic)/json/pages/<Book>/*.jsonThe compiler is deterministic and whitelist-parsed. It does not use eval() or Function().
2. Page JSON Format
{
"book": "Puzzlum's Palace",
"pageNum": 51,
"title": "Balcony (1)",
"illustration": "Story (Classic)/csv/sprites/YARD.csv",
"illustrations": [
"Story (Classic)/csv/sprites/BASE.csv",
"Story (Classic)/csv/sprites/OVERLAY.csv"
],
"desc": ["There is a courtyard below the", "balcony."],
"logic": [
["J", "Pond (1)", "+:Puzzlum's Palace (1)", "Jump"],
["T", "Balcony (2)", "'-:Balcony (2)' && '@hasKey' === 1", "Talk to man"],
["B", "@hasKey =: 1", "+:Cellar (1)", "Pick up brass key"]
]
}illustration is the single-image form. illustrations is the ordered overlay form and renders first-to-last.
3. Logic Tuples
Each logic row is:
[ asciiKey, destinationTitle, prereqExpression, caption ]
| Index | Name | Meaning |
|---|---|---|
| 0 | asciiKey | Required. Empty string fails. A single literal space is valid for Space. |
| 1 | destinationTitle | Page title or special variable assignment destination. |
| 2 | prereqExpression | Required gate. Empty expression fails closed. |
| 3 | caption | Displayed text. Blank caption creates a hidden/key-only binding if the gate passes. |
4. Runtime Pipeline
- Normalize page JSON.
- Record current book/page visit and hit counts.
- Compile each logic row’s prerequisite expression.
- Evaluate against the current scoped story context.
- Bind hidden options when the gate passes and caption is blank.
- Render visible options when the gate passes and caption is non-blank.
Template-document features such as includes, loops, regex, and variable interpolation are pass-based and should resolve structure before final variable substitution.
includes → loops → conditionals → regex → variables / @@ → fixpoint cap
5. Expressions
Simple Gates
+:Balcony (2) // true if visited -:Balcony (2) // true if not visited
Compound Gates
'+:Balcony (2)' && '-:Tower (1)' "Dungeon Hall" >= 4 "@score" >= 10 && "@hasKey" === 1 "Puzzlum's Palace::@hasKey" === 1
Simple unquoted +: and -: gates may be auto-wrapped by ClassicStory. Compound expressions should quote page and variable tokens.
6. Identifiers
| Form | Meaning | Returns |
|---|---|---|
"+:Page Title" | Visited check | Boolean |
"-:Page Title" | Unvisited check | Boolean |
"Page Title (3)" | Exact page hit count | Number |
"Page Title" | Base hit count across numbered variants | Number |
"@score" | Current-book saga variable | Number/string |
"Book::Page Title" | Book-qualified page lookup | Boolean/count |
"Book::@score" | Book-qualified variable lookup | Number/string |
7. Operators
! + - * / % + - > >= < <= == != === !== && ||
run(ctx) returns boolean gate truth. value(ctx) returns the raw evaluated value.
8. Variables and Scopes
Hyperflex now documents the full tier model for template contexts and Story contexts.
| Tier | Purpose |
|---|---|
local | Loop/temporary context. Highest priority. |
page | Current page context. |
book | Current book context. |
global | Shared story/runtime context. |
ink, nen, get | Engine/site/request contexts when supplied. |
local → page → book → global → vars
Bare names resolve through that order. Explicit names like page.title or book.hasKey resolve directly.
9. Indirect Variables with @@
Template expansion may use @@ to resolve a variable whose name is stored inside another variable.
= title
{{ page::title }} = Miser's House
{{ @@ink::field }} → Miser's House@@ belongs to the template-variable pass, after loops and conditionals have established the active local scope.
10. Regex Template Pass
Hyperflex template documents may pair pattern and replace variables for deterministic text transforms.
= /\bcat\b/i
= dog
{{ regex::ink::pattern::ink::replace::"The cat sat" }}
→ The dog sat- Pattern and replacement are evaluated as a pair.
- Invalid patterns fail safely and should be traceable.
- Regex transforms must not execute code.
11. Debug Trace
Trace mode is read-only diagnostic output. It should report pass name, matched directive/expression, before/after summary, and skip/failure reason without changing render behavior.
12. Compiler API
const hf = new HyperflexCompiler({ allowSingleQuotes: true });
const compiled = hf.compile("'@hasKey' === 1 && '+:Cellar (1)'");
compiled.ast; // parsed AST
compiled.value(ctx); // raw value
compiled.run(ctx); // boolean gateconst ctx = {
isVisited(name) { return seenTitles.has(qualifyPage(name)); },
getHits(name) { return exactHits.get(qualifyPage(name)) ?? 0; },
getHitsBase(base) { return visits.get(qualifyBase(base)) ?? 0; },
getVar(name) { return vars.get(qualifyVar(name)) ?? 0; },
local: {}, page: {}, book: {}, global: {}, vars: new Map()
};13. Recipes
Visit gate
"+:Doorway (1)"
One-time option
"-:Treasure Taken (1)"
Require region exploration
"Dungeon Hall" >= 4
Require a saga variable
"@hasKey" === 1
Cross-book state
"Puzzlum's Palace::@hasKey" === 1
14. Pitfalls
| Problem | Result | Fix |
|---|---|---|
| Empty key | Gate fails and option is not bound. | Use a real key or literal Space. |
| Empty prereq | Fails closed. | Use a valid always-true prerequisite. |
| Unquoted compound page names | Parse error. | Quote page tokens inside compound expressions. |
| Blank caption | Hidden/key-only binding. | Use visible caption unless intentionally hidden. |
| Wrong book qualification | Lookup misses. | Use Book::Page or current-book local titles consistently. |