Skip to content

Conditions

One grammar, used everywhere a condition can appear: fragment and choice *requires:, block *if/*elseif, conditional navigation inside an *if block, and inline {if cond} text.

TermMeaning
INT >= 60Stat comparison. Operators: >= <= > < =
age >= 17Age comparison
has_petVariable is truthy (true, or a positive integer)
!has_petVariable is falsy - false, 0, negative, or never set
has_pet = trueThe same as has_pet, spelled the way *set: spells it
has_pet = falseThe same as !has_pet, never-set included
dog_debt >= 2Integer variable comparison
random(20) + VIT >= 70Dice check - a fresh 1..20 roll plus the stat, against the threshold
VIT + WIL >= 120Summed comparison - stats, int variables, and age can be added
played:some_idThe fragment with that id has been played this life
pronouns = she_herThe character’s pronouns, one of he_him, she_her, they_them

All values are integers; > 40 is exactly >= 41.

Comparison is a single =. There is no assignment inside a condition to tell it apart from - writing happens in *set: - so == is a build error that names the fix rather than a second spelling of the same operator.

has_pet = false and !has_pet are one condition: a variable that was never set satisfies both. Whether it was never set or set and undone is a question the Life panel answers, not a condition.

random(N) contributes a fresh roll of 1..N every time the condition is evaluated. The natural home is an *if block - the attempt happens when the body runs:

> Climb the wall
*if random(20) + VIT >= 70
You make it up, somehow.
*else
You slip on the second hold.
*effects: VIT-1
*endif

Three rules keep rolls predictable:

  • Not allowed in inline {if} - inline text re-renders while the player reads, and the roll would flicker. The build rejects it and points you to the pattern below.
  • Each evaluation rolls fresh. An *elseif chain rolls separate dice per branch; a *requires: with a roll re-rolls each time its scene is entered.
  • To roll once and reuse the result, assign it and branch on the variable:
*set: fate = random(6)
*if fate >= 5
A very good day.
*elseif fate >= 3
An ordinary day.
*else
One of those days.
*endif

pronouns is read off the character rather than out of the variable store, so no fragment declares it and none can set it. It compares with = and != only, like any other set of names with no order. It is not a gender: the game knows which words to write and nothing else, and a character’s gender, if a fragment needs one, is an ordinary shared variable that some scene establishes.

&& is AND, || is OR; && binds tighter, and parentheses group however you like:

requires: CHA >= 70 || INT >= 70
*if VIT >= 30 && !injured || has_medkit
*if (CHA >= 70 || INT >= 70) && WIL >= 40
*if ((random(6) >= 4 || lucky_charm) && VIT >= 30) || played:big_break

Without parentheses, VIT >= 30 && !injured || has_medkit reads as (VIT >= 30 && !injured) || has_medkit. With them, you decide. Unbalanced or misplaced parentheses are build errors; random(N)’s own parentheses never interfere with grouping.

Any term the parser doesn’t recognize is a build error - a typo like VIT => 70 or HPA >= 60 can never silently turn a gate into “always true.”

Conditions evaluated mid-fragment see the effective state: the character’s base stats plus every effect and assignment accumulated so far in this fragment. If a choice applies *effects: HAP+4 and an *if HAP >= 52 follows it, the +4 already counts. Stats commit permanently only when the fragment ends.