Skip to content

Formulas

A formula is an expression like add(strength, intelligence) or years_since(birth_date) that produces a value from this entity’s other fields. The system computes the result whenever the entity is shown.

Formulas are the most flexible kind of auto-calculated field. You can do maths, string formatting, date arithmetic, and conditional logic — all from a small set of built-in functions.

Formulas live in the Formula field of the auto-calculate behaviour editor. The editor shows you a function picker on the right; click any function to insert it at the cursor.

The shape is always the same: a function name, an opening parenthesis, arguments separated by commas, and a closing parenthesis.

add(strength, intelligence)
multiply(base_value, 2)
years_since(birth_date)

You can nest functions inside each other:

divide(add(a, b), 2) // average of a and b
round(multiply(price, 1.07)) // price plus 7%, rounded

To reference a field on this entity, just type its identifier — strength, birth_date, etc. (No braces, no quotes.)

For text values, wrap them in quotes:

concat(first_name, " ", last_name)

For numbers, type them as-is: 2, 0.5, -7.

The editor checks your formula as you type. You’ll see one of three things under the formula box:

  • Nothing — looks fine.
  • “Checking syntax…” — the editor is asking the server.
  • A red error message — describes what’s wrong (e.g. “Unknown function: bogus” or “Unbalanced parentheses”). Save is blocked until you fix it.

The check runs about half a second after you stop typing, so quick edits don’t spam the server.

A few formulas you’ll reach for again and again:

GoalFormula
Sum of two statsadd(strength, intelligence)
Differencesubtract(max_health, current_health)
Percentagemultiply(divide(current, max), 100)
Round a resultround(divide(total, count))
Pick one of two valuesif(is_active, bonus, 0)
First non-emptycoalesce(nickname, first_name, "Unknown")
Years between datesyears_since(birth_date)
Days between datesdays_between(start_date, end_date)

Functions are grouped by what they do. Click into the editor’s function picker to insert any of them with placeholder arguments.

FunctionWhat it doesExample
add(a, b, ...)Add two or more numbers.add(strength, intelligence)
subtract(a, b)First minus second.subtract(max_hp, current_hp)
multiply(a, b, ...)Multiply two or more numbers.multiply(price, quantity)
divide(a, b)First divided by second.divide(total_xp, level)
power(base, exponent)Raise a number to a power.power(level, 2)
sqrt(x)Square root.sqrt(damage)
abs(x)Absolute value.abs(temperature_delta)
FunctionWhat it doesExample
round(x)Round to nearest whole number.round(average)
floor(x)Round down.floor(divide(xp, 1000))
ceil(x)Round up.ceil(required_materials)
mod(a, b)Remainder after division.mod(day_number, 7)
FunctionWhat it doesExample
min(a, b, ...)Smallest of the inputs.min(health, stamina, mana)
max(a, b, ...)Largest of the inputs.max(base_dmg, modified_dmg)
sum(a, b, ...)Add all the inputs.sum(str, dex, con, int, wis, cha)
avg(a, b, ...)Average of the inputs.avg(test1, test2, test3)
count(a, b, ...)How many of the inputs aren’t empty.count(skill1, skill2, skill3)
FunctionWhat it doesExample
concat(a, b, ...)Stick strings together.concat(first_name, " ", last_name)
upper(s)Make uppercase.upper(name)
lower(s)Make lowercase.lower(email)
trim(s)Remove whitespace from start and end.trim(user_input)
length(s)Number of characters.length(description)
substring(s, start, end)Take a slice from index start up to index end.substring(code, 0, 3)
replace(s, old, new)Replace text in a string.replace(text, "old", "new")
FunctionWhat it doesExample
years_since(date)Years from the date to now.years_since(birth_date)
days_between(a, b)Days between two dates.days_between(start, end)
date_add_days(date, n)Add days to a date.date_add_days(start, 30)
date_add_months(date, n)Add months to a date.date_add_months(founded, 6)
date_format(date, fmt)Format the date as a string.date_format(birth_date, "%Y-%m-%d")
FunctionWhat it doesExample
if(condition, then, else)Pick one of two values based on a condition.if(is_active, "Active", "Inactive")
coalesce(a, b, ...)First value that isn’t empty.coalesce(nickname, first_name, "Unknown")
is_null(x)True when the value is empty.is_null(optional_field)
is_not_null(x)True when the value isn’t empty.is_not_null(required_field)

A handful of complete formulas, with a note on what each one is for.

You have a birth_date field, and you want an age field that updates as the in-world calendar advances:

years_since(birth_date)

The result is a whole number. Set the age field to read-only.

Characters might or might not have a title. You want display_name to read “Lord Tyrell Mormont” when there is one, “Tyrell Mormont” when there isn’t:

concat(coalesce(title, ""), if(is_null(title), "", " "), first_name, " ", last_name)

A bit hairy. The cleaner answer is usually a Template instead — formulas are great for maths, less elegant for variable text-stitching.

Max HP minus damage taken, with a floor at zero:

max(0, subtract(max_hp, damage_taken))

When a Yes/No field is enough to decide the value:

if(is_active, "Active", "Inactive")

if takes a true/false value as its first argument and returns one of two results. For a Yes/No field, use the field directly. For “is this empty”, use is_null or is_not_null.

D&D-style:

add(8, proficiency_bonus, ability_modifier)

A few things the syntax checker won’t catch:

Field renamed or deleted. If the formula references strength and you later rename the field to str, the formula stops resolving. The result becomes empty until you update the formula. The Behaviour editor flags this when you open it.

Numeric field is empty. A formula like add(strength, intelligence) returns nothing if either field is empty. Use coalesce(strength, 0) to default to zero.

Division by zero or empty. divide(total, count) returns nothing when count is zero or empty. Wrap so the divide only runs with a usable value: if(coalesce(count, 0), divide(total, count), 0). The coalesce defaults count to 0 when it’s empty, and if treats 0 as false — so the divide only runs when count is non-zero.

Reference to itself. A formula that uses its own field (age = add(age, 1)) creates a cycle. The system rejects the save with an error.

For the full list of “I saved it but it doesn’t work” cases, see Common Pitfalls.