Appearance
Math, Loops & Expressions
Arithmetic, comparison, loop control, and expression evaluation in passage logic.
Reference: Loops, Arrays, Numbers & Strings and Conditionals.
Arithmetic
Anywhere a value is expected (inside (set:), conditions, or another macro's arguments), you can do math:
ana
(set: _dmg to ($player.str * 2) + 5)
(set: _half to (floor: $player.gold / 2))
(if: $turn % 2 is 0)[An even turn.]Operators: + - * / % and unary minus. Precedence, tightest first: unary minus → * / % → + - → comparisons (is, >, …) → not → and → or. Parenthesize when in doubt. / is floating-point (7 / 2 → 3.5); wrap in (floor:), (ceil:), or (round:) for whole numbers. + also concatenates when either operand is a string: "HP: " + $player.hp.
and / or short-circuit (the right side is skipped when the left already decides the result), so (if: $has_key and (check-lock:)) is safe even when the right side is expensive.
Picking a value instead of branching
When you want to choose a value rather than emit a block of content, (cond:) is shorter than a chain of (if:)/(elseif:)/(else:). It takes condition/result pairs and returns the first result whose condition is true, with an optional trailing default:
ana
(set: $status to (cond: $cash >= 1000, "loaded", $cash >= 500, "stable", "broke"))
Your (cond: $wonTheRace, "gasps of triumph", "wheezes of defeat") drown out all other noise.Polymorphic helpers
Several utility macros accept either a single array or a series of inline values, so you don't need a different macro depending on where your data lives: (sort:), (shuffle:), (pick:), (sum:), and (avg:). (length:) likewise spans strings (character count) and arrays (entry count), and (join:) concatenates strings or merges arrays. The longer arr-* / str-* names still work as aliases.
Loop control
(each: _x in collection)[...] repeats its body once per element. Inside the body:
(break:)stops the loop immediately.(continue:)skips to the next element.
Both target the innermost loop and work inside (if:) branches, which is handy for "find the first match" scans:
ana
(each: _id in (ids: $inv))[
(if: (get: $item, _id, "type") is "weapon")[
(set: $world.drawn to _id)
(break:)
]
]Defining your own macros
When you find yourself repeating the same little cluster of macros (a styled name plate, a health bar, a "can the player afford this?" check), give it a name with (macro:). You compose existing macros once and call the result everywhere. Define them in GameInit so they exist before any passage runs.
A content macro emits whatever its body renders. Parameters are bare names, available in the body as _temps:
ana
(macro: "healthbar", current, max)[
(meter: _current, _max)
]
(healthbar: $player.hp, 100)
(healthbar: $rival.hp, 100)A value macro computes something you use elsewhere; mark it by adding a trailing return to the definition. The macro then hands back the value of its last expression (it doesn't draw anything), so you can use it inside (set:) or (if:). The last line must be an expression-producing macro call, and that value is what comes back:
ana
(macro: "can-afford", cost, return)[
(cond: $player.gold >= _cost, true, false)
]
(if: (can-afford: 50))[The merchant smiles. "A fine choice."]Parameters can have defaults (a literal right after the parameter), redefining your own macro replaces it, and you can't shadow a built-in. When a macro needs logic the language genuinely can't express, like shuffling a deck or running a canvas minigame, reach for JavaScript instead; see Extending with JavaScript. The full rules and more examples live in the (macro:) reference.