Skip to main content
Flow control turns effect lists into sequences and decisions: pause between lines, branch on conditions, loop, and pick random outcomes.

delay

Pauses the remaining lines of the current effect list, then resumes. This is the backbone of telegraphed attacks. Alias: wait The duration is positional: delay 0.5s, delay 20.
A delay inside a named effect pauses that effect only, not whatever called it.

run

Runs a named effect. Its lines inherit this line’s targets unless they set their own @. Aliases: effect, skill The effect id is positional:

if

Runs one body when a condition holds, optionally another when it doesn’t. Alias: branch

loop

Runs a body a fixed number of times.

while

Re-checks a condition on an interval and runs the body while it holds. It checks first, then runs.

Bodies: named effects or inline blocks

A body (then / else / effect) is either a named effect id, resolved mob-local first exactly like run, or an inline block {stmt;stmt;...}:
  • Statements are full effect lines, mechanic plus optional @targeter, separated by ;.
  • Blocks nest: a statement can itself be an if/loop/while with its own block.
  • A block may span multiple lines, and continuation lines may start with - for readability. The loader joins them before YAML parsing, so the raw multi-line form doesn’t need to be valid YAML on its own.
Rules for blocks:
  • No #triggers inside a block. The trigger belongs to the outer line.
  • No comments inside a multi-line block.
  • A malformed or empty block fails the whole line at load time, logged and skipped.
  • Prefer a named effect over a block when the body should be reusable or run-able. Blocks are for short one-off branches.

randomskill

Runs one randomly chosen named effect. Great for varied attack patterns. Alias: randomeffect

randommessage

Sends one randomly chosen message. Alias: randommsg

Conditions

if and while share a small condition language over variables. A condition is one or more comparisons joined by && and ||, where && binds tighter:
  • Operators: >= <= > < == !=.
  • Truthy check: a lone operand with no comparison is true when the value is set and non-zero, non-empty and not "false". Example: if{cond=var.enraged;then=...}.
  • Operands: numbers; variable references without angle brackets (var.x, target.var.x, global.var.x, world.var.x, skill.var.x); the keywords random (alias chance, a fresh 0-1 roll per evaluation, so random < 0.3 is a 30% chance), health and maxhealth (the casting mob’s), and time (world ticks). Anything else is a string literal, bare word, no quotes needed.
  • Both sides numeric means numeric comparison. Otherwise case-insensitive string equality, == and != only; ordered operators on strings are always false.
  • Unset variables count as 0 in numeric comparisons and "" in string ones.
  • target.var.x reads from the line’s first target.
  • A malformed condition fails the whole line at load time, logged and skipped, like any other parse error.

Built-in state conditions

Beyond variables and keywords, these named conditions check the state of the line’s first target: They combine with everything else:
The same conditions gate dialog nodes and choices, where the subject is the talking player instead. See Dialog Conditions.

Place conditions

These ask about a place rather than an entity: the target’s position, or the position the line is running at. They are what natural spawn rules are almost entirely made of, and they work in ordinary if{} lines too. Each of the list-taking ones takes a comma-separated list meaning “any of these”, compared case-insensitively.
A bare name matches any namespace; a qualified one matches only that namespace. isBiome{desert} catches minecraft:desert and a world generator’s own desert alike, while isBiome{iris:frozen_wastes} names exactly one — which is what lets a custom-biome plugin integrate at all. The same applies to isBlock and isBlockBelow.
Written bare with no argument, the list-taking conditions are invalid rather than always-true. A gate that silently passes is the failure mode this is trying to avoid.

Place operands

Alongside them, these are numbers you compare directly, exactly like health or random:
time is the world’s game time (ticks since the world was created, always increasing). For time of day, use daytime or isTime{night}.

Behavior notes

  • A branch or loop body runs with the caller’s context: same targets (unless its lines set their own @targeter) and the same skill. variable scope, so loop counters via var{...} work across iterations.
  • A delay inside a looped effect pauses that iteration only, not the loop schedule.
  • Spaced loop and while stop early if the mob dies or despawns. while’s max is a hard safety cap against infinite loops.

The counter pattern