Expressions

Every numeric and string parameter is an expression rather than a value, resolved at execution time, per target.

- damage{amount=<caster.level> * 2.5 + <random.1to4>} @target
- message{msg="<target.name> is at <target.hp.percent>%"} @nearestPlayer{r=10}

Constants are just expressions that ignore their input, so a mechanic never branches on whether its parameter happened to be literal.

Resolution order

The order is pinned, and implemented literally:

  1. Every <...> placeholder is substituted to its string value.
  2. In numeric contexts only, the result is parsed as an infix expression.

Substituting first is what lets <mob.var.formula> contain arithmetic. Parsing first would not, because the variable's contents would arrive after the parser had already run.

String parameters get substitution only, never arithmetic.

Arithmetic

+ - * / %, unary minus, parentheses, doubles throughout, whitespace insignificant.

Deliberately small and deliberately not configurable: one pinned behaviour means one thing to implement and one thing to document. Adding an operator later would be a breaking change to every config that used that character for something else.

Namespaces

NamespaceHolds
casterthe entity casting
targetthe current target
triggerwhatever tripped the trigger
originthe origin location
skill.var.*variables scoped to one execution
mob.var.*variables on the casting mob
caster.var.*, target.var.*variables on that specific entity
global.var.*server-wide variables
random.*randomness
math.*constants and functions
papi.*PlaceholderAPI, when it is installed

On an entity

name, display, uuid, type, id, level, phase, faction, hp, maxhp, hp.percent, hp.missing, armor, air, food, exp, distance, threat.

health is an alias of hp throughout, so <target.health.percent> and <target.hp.percent> are the same.

On a location

x, y, z for exact coordinates and bx, by, bz for block coordinates, plus yaw, pitch, world and biome.

random

WrittenGives
<random.4to9>an integer from 4 to 9 inclusive
<random.10>an integer from 0 to 10 inclusive
<random.float>a double from 0 to 1; double is an alias
<random.bool>true or false
<random.sign>1 or -1
<random.angle>a double from 0 to 360

<random.4to9> is the unambiguous spelling and the only one accepted outside drop amounts, because 4-9 is also a perfectly good subtraction. Treating it as a range everywhere would silently change the meaning of every expression containing a minus sign.

math

<math.pi>, <math.e>, <math.tau> and <math.random>.

Variables

Four scopes, written by set_variable and variable_math and read back through expressions.

- set_variable{name=charges;value=3;scope=mob}
- damage{amount=<mob.var.charges> * 4} @target
scopeLives forRead as
skill (default)one execution<skill.var.name>
mobthe life of the mob<mob.var.name>
targetthe life of that entity<target.var.name>
globalserver-wide<global.var.name>

name takes n, var and key as aliases, and value takes v and amount.

MiniMessage survives

A bracketed token whose first path segment is not a registered namespace is left exactly as written:

display: "<gradient:#e8d9a0:#c9a227>Example Champion</gradient>"

Nobody registered gradient, so Bestiary does not touch it and MiniMessage renders it later. This is why a display name can use the full tag set without escaping anything, and why adding a namespace is a considered decision rather than a free one.

Where they work

Everywhere a parameter is read: mechanic, targeter and condition arguments, bossbar titles, messages. A bossbar title resolves live:

bossbar:
  title: "<gold>Example Champion <gray>- <white><caster.hp.percent>%"

Testing one

/bestiary cast <skill> traces every step, including what each expression resolved to for each target. When a number is wrong, that trace is the fastest way to see whether the expression or the targeter is at fault.

Test with the same caster and target kind the real trigger supplies. A player command can resolve attributes that a location-only trigger cannot. <papi.*> values only resolve when PlaceholderAPI is installed; /bestiary platform confirms whether that optional hook was detected.