This page lists every node in the trigger language. It is generated from the same catalog the validator uses, so it cannot fall out of step with the game. The JSON Schema for whole scripts is at /schemas/v1/trigger-script.json.
Every node is a JSON object with a k naming its kind. Unknown properties are errors. Wherever a field takes an expression, you can write a bare number, string or boolean instead: 5 means {"k":"int","v":5}, 1.5 means {"k":"real","v":1.5}, "hi" means {"k":"str","v":"hi"}. A string is always text; to read a variable, use {"k":"var","name":"score"}. Wherever a field takes text, a bare string is shorthand for a one-part template.
Every example below passes validateTriggerScript when placed into this script (events go in events, expressions in conditions, actions in actions):
{
"version": 1,
"vars": [
{
"name": "score",
"type": "int"
},
{
"name": "squad",
"type": "unitGroup"
},
{
"name": "code",
"type": "string"
}
],
"regions": [
{
"id": "arena",
"x": 0,
"y": 0,
"w": 8,
"h": 8
}
],
"functions": [
{
"name": "double",
"params": [
{
"name": "n",
"type": "int"
}
],
"returns": "int",
"actions": [
{
"k": "return",
"value": {
"k": "mul",
"a": {
"k": "var",
"name": "n"
},
"b": {
"k": "int",
"v": 2
}
}
}
]
}
],
"hashtables": [
{
"name": "scores",
"valueType": "int"
}
],
"triggers": [
{
"id": "main",
"events": [
{
"k": "mapInit"
}
],
"actions": []
}
]
}Events
What makes a trigger fire. A trigger fires when any one of its events happens.
Map events
mapInit
Map initialisation. Fires once on the first tick, after every global initialiser.
Editor: the map starts
No fields.
{"k":"mapInit"}Time events
periodic
Every N ticks. Fires when (tick − offset) is a multiple of everyTicks, from tick offsetTicks on.
Editor: every ‹Interval (ticks)› ticks, phase-shifted by ‹Phase offset›
| Field | Type | Required | Description |
|---|---|---|---|
everyTicks | int (≥ 1) | yes | Period in ticks (20 = 1 second). |
offsetTicks | int | no | Phase offset in ticks. |
{"k":"periodic","everyTicks":1}atTick
At tick. Fires once on an exact tick.
Editor: at tick ‹Tick›
| Field | Type | Required | Description |
|---|---|---|---|
tick | int (≥ 0) | yes | Tick number. |
{"k":"atTick","tick":1}timerExpires
Timer expires. Fires when a named timer reaches zero.
Editor: timer ‹Timer› expires
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
{"k":"timerExpires","timer":"wave_timer"}Region events
unitEntersRegion
Unit enters region. Fires for each unit whose tile moved into the region this tick. Units that start inside do not count.
Editor: a unit enters ‹Region› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
region | regionId | yes | Region id. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitEntersRegion","region":"arena"}unitLeavesRegion
Unit leaves region. Fires for each unit that left the region this tick — including by dying or being removed (matched against its last snapshot).
Editor: a unit leaves ‹Region› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
region | regionId | yes | Region id. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitLeavesRegion","region":"arena"}Unit events
unitDies
Unit dies. Fires when a unit or building dies. The filter is matched against the unit as it was at death (aliveOnly is ignored); event unit = the dead unit, other unit = killer.
Editor: a unit dies matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitDies"}unitTrained
Unit trained. Fires when production completes a unit.
Editor: a unit finishes training matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitTrained"}buildingComplete
Building completes. Fires when construction finishes.
Editor: a building completes matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"buildingComplete"}unitAttacked
Unit attacked. Fires when a unit takes an attack; event value = damage, other unit = attacker.
Editor: a unit is attacked matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitAttacked"}heroLevels
Hero levels up. Fires when a hero gains a level; event value = new level.
Editor: a hero gains a level matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"heroLevels"}unitDamaging
Unit is about to take damage. Fires between armour and the hp write, for EVERY source of damage in the game — melee, projectiles, spells, burns, thorns, lava, a collapsing bridge. Unit = the victim, other unit = the dealer (0 for the environment), event damage = the amount about to land. This is the one event whose amount you can change: use "set event damage" for a custom armour system, a shield that absorbs part of a hit, or a damage-type table. Triggers fire in authored order and each sees the previous one's result. "wait" is not allowed in a trigger with this event, because the hit is only half resolved. Needs map format 3.
Editor: a unit is about to take damage — ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitDamaging"}unitDamaged
Unit has taken damage. Fires immediately after the hp is written, for every source of damage. Event damage = the amount that actually landed. Use it to react — a bloodlust stack, a retaliation, "the boss enrages below half". The victim of a killing blow is still alive with 0 hp here, so read hp rather than alive. "set event damage" does nothing on this event; there is nothing left to change. Needs map format 3.
Editor: a unit has taken damage — ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitDamaged"}Item events
itemPickedUp
Item picked up. Fires when a hero picks up an item. NOTE: buying does not fire it — watch itemPurchased as well if you mean "the hero now holds one".
Editor: an item is picked up — ‹Item›
| Field | Type | Required | Description |
|---|---|---|---|
itemId | itemId | no | Only this item id; absent = any. |
{"k":"itemPickedUp"}itemUsed
Item used. Fires when a hero consumes or activates an item — a potion, a scroll, a town portal, an active. Event unit = the hero, text = the item id. Needs map format 3.
Editor: a hero uses an item (‹Item›)
| Field | Type | Required | Description |
|---|---|---|---|
itemId | itemId | no | Only this item id; absent = any. |
{"k":"itemUsed"}itemPurchased
Item purchased. Fires when a hero buys an item from a shop. Gambling-den rolls do NOT fire it. Event unit = the hero, text = the item id. Needs map format 3.
Editor: a hero buys an item (‹Item›)
| Field | Type | Required | Description |
|---|---|---|---|
itemId | itemId | no | Only this item id; absent = any. |
{"k":"itemPurchased"}itemSold
Item sold. Fires when a hero sells an item. Event value = 1 if sold for XP, 0 if sold for gold. Needs map format 3.
Editor: a hero sells an item (‹Item›)
| Field | Type | Required | Description |
|---|---|---|---|
itemId | itemId | no | Only this item id; absent = any. |
{"k":"itemSold"}itemDropped
Item dropped. Fires when a hero drops an item, from the backpack or off the paper doll. A hero DEATH drop fires it too, once per item. Needs map format 3.
Editor: a hero drops an item (‹Item›)
| Field | Type | Required | Description |
|---|---|---|---|
itemId | itemId | no | Only this item id; absent = any. |
{"k":"itemDropped"}Combat events
abilityCastStart
Cast starts. Fires once when a cast is committed, after every cost and range check has passed. Event unit = caster, other unit = target unit (0 if none), value = wind-up length in ticks, eventSpellId = the spell, eventTargetPoint = the target tile. An ordinary RTS or AI cast has NO wind-up (only a possessed hero or a spell with rtsCastBar does), so start, effect and finished all land on the same tick. Boss telegraphs use a separate engine and fire none of these. Needs map format 3.
Editor: a unit starts casting ‹Spell id›
| Field | Type | Required | Description |
|---|---|---|---|
spellId | string | no | Only this spell id; absent = any. |
{"k":"abilityCastStart"}abilityCastEffect
Cast resolves. Fires when the spell's effect actually resolves — the one to hang a consequence on. Event unit = caster, other unit = target unit, player = the caster's seat, eventSpellId = the spell, eventTargetPoint = the target tile. Needs map format 3.
Editor: a cast resolves (‹Spell id›)
| Field | Type | Required | Description |
|---|---|---|---|
spellId | string | no | Only this spell id; absent = any. |
{"k":"abilityCastEffect"}abilityCastFinished
Cast finishes. Fires once when a cast ends normally. Exactly-once per cast for both instant and cast-bar spells. Needs map format 3.
Editor: a cast finishes (‹Spell id›)
| Field | Type | Required | Description |
|---|---|---|---|
spellId | string | no | Only this spell id; absent = any. |
{"k":"abilityCastFinished"}abilityCastCancelled
Cast cancelled. Fires when a cast in progress is interrupted. Event text = the reason: move, death, damage, invalid or superseded. A cast REFUSED before it started (no mana, on cooldown, out of range, silenced, no line of sight) fires NOTHING, not even this. Sleep also strips a cast without firing it. Needs map format 3.
Editor: a cast is cancelled (‹Spell id›)
| Field | Type | Required | Description |
|---|---|---|---|
spellId | string | no | Only this spell id; absent = any. |
{"k":"abilityCastCancelled"}projectileHits
Projectile hits. Fires when a projectile strikes its target or a bomb detonates. Event unit = target, other unit = shooter, value = damage.
Editor: a projectile hits matching ‹Tag›
| Field | Type | Required | Description |
|---|---|---|---|
tag | string | no | Match the shooter's authored tag, the spell id, the shooter's unit type or the projectile type. |
{"k":"projectileHits"}Orders events
orderIssued
Order issued. Fires once when a unit or selection is ordered. COVERS every player order (right-click, drag, minimap, HUD, hotkey, queued waypoints), every trigger order action, and orders arriving by lockstep, replay, cinematic, possession or a mode's bot. DOES NOT cover the heuristic AI or the LLM commander, which write movement directly instead of issuing commands — so on an AI seat this is near-silent and must not be used to police AI behaviour. Event unit = the LOWEST entity id ordered, other unit = target unit (0 if none), value = how many units the order addressed, text = the order kind, eventTargetPoint = the target tile ((-1,-1) for stop/hold). ONE signal per order, not one per unit. Needs map format 3.
Editor: a unit is ordered to ‹Order› by player ‹From seat›
| Field | Type | Required | Description |
|---|---|---|---|
order | 'move' | 'attack_move' | 'attack' | 'stop' | 'hold' | 'patrol' | 'follow' | no | Only this order kind; absent = any. |
player | playerNum | no | Only orders from this seat; absent = any. |
{"k":"orderIssued"}Player events
playerEliminated
Player eliminated. Fires when a seat is eliminated.
Editor: a player is eliminated — ‹Player›
| Field | Type | Required | Description |
|---|---|---|---|
player | playerNum | no | Only this seat; absent = any. |
{"k":"playerEliminated"}playerLeaves
Player leaves. Fires on the tick a seat's human leaves the match, under any leaver policy. NOT the same as elimination and neither implies the other: a leaver under abandon keeps a living base, and an eliminated player usually stays to watch. Event player = the seat that left. Needs map format 3.
Editor: a player leaves — ‹Player›
| Field | Type | Required | Description |
|---|---|---|---|
player | playerNum | no | Only this seat; absent = any. |
{"k":"playerLeaves"}upgradeComplete
Upgrade completes. Fires when research completes; event text = upgrade id.
Editor: an upgrade completes — ‹Upgrade›
| Field | Type | Required | Description |
|---|---|---|---|
upgradeId | upgradeId | no | Only this upgrade; absent = any. |
{"k":"upgradeComplete"}playerChats
Player chats. Fires when a seated player sends an all-chat line (team chat and whispers never fire it); event text = the line.
Editor: player ‹Player› sends chat matching "‹Pattern›" (‹Match mode›)
| Field | Type | Required | Description |
|---|---|---|---|
pattern | string | no | Text to match; absent = any message. |
matchMode | 'exact' | 'prefix' | 'contains' | no | How pattern matches (default exact). |
player | playerNum | no | Only this seat. |
{"k":"playerChats","pattern":"-reset","matchMode":"prefix"}playerKeyPress
Player presses key. Fires when a player presses or releases a key (through lockstep); event text = key, value = 1 down / 0 up.
Editor: player ‹Player› presses ‹Key› (‹Key state›)
| Field | Type | Required | Description |
|---|---|---|---|
key | keyName | no | Key name, e.g. 'w', 'Space', 'ArrowUp'. |
state | 'down' | 'up' | no | down or up (absent = both). |
player | playerNum | no | Only this seat. |
{"k":"playerKeyPress","key":"Space","state":"down"}Variable events
variableChanges
Variable changes. Fires (in the cascade phase) when a GLOBAL variable is written with a different value.
Editor: variable ‹Variable› changes
| Field | Type | Required | Description |
|---|---|---|---|
name | varName | yes | Global variable name. |
{"k":"variableChanges","name":"score"}UI events
dialogButton
Dialog button clicked. Fires when a player clicks a trigger dialog button (arrives through lockstep); event value = button index.
Editor: a dialog button is clicked on ‹Dialog›, button ‹Button index›
| Field | Type | Required | Description |
|---|---|---|---|
dialogId | dialogId | no | Only this dialog. |
buttonIndex | int (≥ 0) | no | Only this 0-based button. |
{"k":"dialogButton"}uiFrameClicked
UI frame clicked. Fires when a player clicks one of your own HUD frames (arrives through lockstep, like a dialog button). Event player = the seat that clicked, text = the frame id. A click only counts when that seat can really see that frame right now — a frame that was destroyed, hidden, or shown to somebody else fires nothing, however the click arrived. Needs map format 3.
Editor: a UI frame is clicked: ‹Frame›
| Field | Type | Required | Description |
|---|---|---|---|
frameId | frameId | no | Only this frame; absent = any. |
{"k":"uiFrameClicked"}Custom events
custom
Custom event. Fires when raiseEvent raises this name (same tick, capped cascade).
Editor: custom event ‹Event name› is raised
| Field | Type | Required | Description |
|---|---|---|---|
name | eventName | yes | Event name. |
{"k":"custom","name":"boss_down"}Cutscene events
cutsceneFinished
Cutscene finished. Fires when a cutscene ends. Event value: 0 played out, 1 skipped by every human viewer, 2 replaced by another scene, 3 stopped by a trigger.
Editor: cutscene ‹Scene› finishes
| Field | Type | Required | Description |
|---|---|---|---|
sceneId | sceneId | no | Only this scene. |
{"k":"cutsceneFinished"}Conditions and expressions
Values. A trigger condition is any expression; the trigger runs only when all of its conditions are true.
Literal expressions
int
Integer — returns int. An integer literal. Non-integers are truncated.
Editor: ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
v | number | yes | The value. |
{"k":"int","v":1}real
Real number — returns real. A real-number literal.
Editor: ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
v | number | yes | The value. |
{"k":"real","v":1}bool
Boolean — returns bool. true or false.
Editor: ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
v | bool | yes | The value. |
{"k":"bool","v":true}str
Text — returns string. A string literal.
Editor: "‹Value›"
| Field | Type | Required | Description |
|---|---|---|---|
v | string | yes | The value. |
{"k":"str","v":"text"}Geometry expressions
point
Point — returns point. A tile coordinate built from two numbers.
Editor: (‹Tile X›, ‹Tile Y›)
| Field | Type | Required | Description |
|---|---|---|---|
x | expr (number) | yes | Tile X. |
y | expr (number) | yes | Tile Y. |
{"k":"point","x":1,"y":1}distance
Distance — returns real. Euclidean tile distance; either side may be a unit or a point.
Editor: distance from ‹From› to ‹To›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"distance","a":1,"b":1}pointX
Point X — returns number. X of a point.
Editor: X of ‹Point›
| Field | Type | Required | Description |
|---|---|---|---|
p | expr (point) | yes | The point. |
{"k":"pointX","p":{"k":"regionCenter","region":"arena"}}pointY
Point Y — returns number. Y of a point.
Editor: Y of ‹Point›
| Field | Type | Required | Description |
|---|---|---|---|
p | expr (point) | yes | The point. |
{"k":"pointY","p":{"k":"regionCenter","region":"arena"}}regionCenter
Region centre — returns point. Integer centre tile of a region.
Editor: centre of ‹Region›
| Field | Type | Required | Description |
|---|---|---|---|
region | regionId | yes | Region id. |
{"k":"regionCenter","region":"arena"}randomPointInRegion
Random point in region — returns point. Random tile inside the region (always two PRNG draws).
Editor: a random point in ‹Region›
| Field | Type | Required | Description |
|---|---|---|---|
region | regionId | yes | Region id. |
{"k":"randomPointInRegion","region":"arena"}pointInRegion
Point in region — returns bool. True when the point (or unit) lies inside the region.
Editor: ‹Point› is inside ‹Region›
| Field | Type | Required | Description |
|---|---|---|---|
p | expr (point) | yes | Point or unit. |
region | regionId | yes | Region id. |
{"k":"pointInRegion","p":{"k":"regionCenter","region":"arena"},"region":"arena"}offsetPoint
Offset point — returns point. A point moved by (dx, dy) tiles.
Editor: ‹Point› offset by (‹Tiles X›, ‹Tiles Y›)
| Field | Type | Required | Description |
|---|---|---|---|
p | expr (point) | yes | Point or unit. |
dx | expr (number) | yes | Tiles X. |
dy | expr (number) | yes | Tiles Y. |
{"k":"offsetPoint","p":{"k":"regionCenter","region":"arena"},"dx":1,"dy":1}polarOffset
Point at an angle — returns point. A point (or unit) moved distance tiles along angle. With angleBetween this is the whole of "spawn behind the hero", "ring of totems", "fire at this bearing". Needs map format 3.
Editor: ‹From› moved ‹Tiles› at ‹Direction›
| Field | Type | Required | Description |
|---|---|---|---|
p | expr (point) | yes | Point or unit to start from. |
angle | expr (number) | yes | Direction in RADIANS. Multiply by 57.29577951308232 for the degrees createUnits.facing wants. |
distance | expr (number) | yes | Distance in tiles; negative goes the other way. |
{"k":"polarOffset","p":{"k":"regionCenter","region":"arena"},"angle":1,"distance":1}angleBetween
Bearing from A to B — returns real. Direction from A to B in radians, -pi..pi. Either side may be a unit or a point. Due west is +pi, not -pi, so compare bearings with a tolerance rather than for equality. Needs map format 3.
Editor: bearing from ‹From› to ‹To›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"angleBetween","a":1,"b":1}Unit expressions
nullUnit
No unit — returns unit. Entity id 0. Every property of it reads as a zero value.
Editor: no unit
No fields.
{"k":"nullUnit"}unitProp
Unit property — returns any. Read a property of a unit. A unit that died THIS tick answers from its death snapshot; otherwise a missing unit gives the zero value.
Editor: ‹Property› of ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
prop | 'hp' | 'maxHp' | 'hpPercent' | 'mana' | 'maxMana' | 'x' | 'y' | 'level' | 'xp' | 'kills' | 'owner' | 'alive' | 'isHero' | 'isBuilding' | 'isFlying' | 'typeId' | yes | Property to read. |
{"k":"unitProp","unit":{"k":"eventUnit"},"prop":"hp"}unitAboard
Unit aboard transport — returns bool. Whether the original unit is held in the original transport cargo. False for missing units or hulls. Needs map format 3.
Editor: ‹Unit› is aboard ‹Transport›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
transport | expr (unit) | yes | Transport to check. |
{"k":"unitAboard","unit":{"k":"eventUnit"},"transport":{"k":"eventUnit"}}unitPoint
Unit position — returns point. Tile position of a unit.
Editor: position of ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
{"k":"unitPoint","unit":{"k":"eventUnit"}}entityByAid
Unit by authored id — returns unit. The placed entity carrying this authored id, or no unit.
Editor: the unit with authored id ‹Authored id›
| Field | Type | Required | Description |
|---|---|---|---|
aid | aid | yes | Authored id from the entity inspector. |
{"k":"entityByAid","aid":"north_gate"}unitHasBuff
Unit has buff — returns bool. True while this exact buff id is on the unit and has not run out. A buff whose remaining time has reached zero already answers false, one tick before the engine sweeps it up — so "re-apply when it runs out" does not double-cast. Matches the whole id, never a prefix. Needs map format 3.
Editor: ‹Unit› has buff ‹Buff id›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
buffId | string | yes | Buff id to look for — the same id add buff used. |
{"k":"unitHasBuff","unit":{"k":"eventUnit"},"buffId":"text"}Variable expressions
var
Variable — returns any. Read a variable: a trigger/function local, a function parameter, a loop variable or a global (resolved in that order).
Editor: ‹Variable›[‹Index›]
| Field | Type | Required | Description |
|---|---|---|---|
name | varName | yes | Variable name. |
index | expr (int) | no | Element index for an array variable. Out-of-range reads yield the zero value. |
{"k":"var","name":"score"}Arithmetic expressions
add
A + B — returns number. Sum. If either side is a string the two are concatenated.
Editor: ‹A› + ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"add","a":1,"b":1}sub
A − B — returns number. Difference.
Editor: ‹A› − ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
{"k":"sub","a":1,"b":1}mul
A × B — returns number. Product.
Editor: ‹A› × ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
{"k":"mul","a":1,"b":1}div
A ÷ B — returns number. Quotient. Division by zero yields 0 — never Infinity or NaN.
Editor: ‹A› ÷ ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
{"k":"div","a":1,"b":1}mod
A mod B — returns number. Remainder (sign of A). Modulo by zero yields 0.
Editor: ‹A› mod ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
{"k":"mod","a":1,"b":1}neg
Negate — returns number. Arithmetic negation.
Editor: −‹A›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"neg","a":1}abs
Absolute value — returns number. Absolute value.
Editor: |‹A›|
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"abs","a":1}min
Minimum — returns number. The smaller of A and B.
Editor: min(‹A›, ‹B›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
{"k":"min","a":1,"b":1}max
Maximum — returns number. The larger of A and B.
Editor: max(‹A›, ‹B›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
{"k":"max","a":1,"b":1}clamp
Clamp — returns number. A limited to [lo, hi]; reversed bounds are swapped.
Editor: clamp(‹A›, ‹Minimum›, ‹Maximum›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Value to clamp. |
lo | expr (number) | yes | Lower bound. |
hi | expr (number) | yes | Upper bound. |
{"k":"clamp","a":{"k":"var","name":"score"},"lo":0,"hi":100}trunc
Truncate — returns int. Real to integer toward zero (Math.trunc) — the one real→int rule in the language.
Editor: trunc(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (real) | yes | Left operand. |
{"k":"trunc","a":1.5}floor
Floor — returns int. Largest integer at or below A. Needs map format 3.
Editor: floor(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"floor","a":1}ceil
Ceiling — returns int. Smallest integer at or above A. Needs map format 3.
Editor: ceil(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"ceil","a":1}round
Round — returns int. Nearest integer, halves going toward +Infinity — so round(2.5) is 3 and round(-2.5) is -2. Needs map format 3.
Editor: round(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"round","a":1}sqrt
Square root — returns real. Square root. A negative A yields 0, never NaN. Needs map format 3.
Editor: √‹A›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"sqrt","a":1}pow
A to the power B — returns real. A raised to B. A non-finite result yields 0. Needs map format 3.
Editor: ‹A› ^ ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
{"k":"pow","a":1,"b":1}sign
Sign — returns int. -1 for a negative A, 1 for a positive one, 0 for zero. Needs map format 3.
Editor: sign(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"sign","a":1}lerp
Interpolate — returns real. A blended toward B by T: A + (B - A) x T. T is NOT clamped, so 2 overshoots and -1 extrapolates backwards. Needs map format 3.
Editor: blend ‹A› → ‹B› by ‹Blend factor›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
b | expr (number) | yes | Right operand. |
t | expr (number) | yes | Blend factor; 0 gives A, 1 gives B. |
{"k":"lerp","a":1,"b":1,"t":1}Maths expressions
sin
Sine — returns real. Sine of an angle in radians. Needs map format 3.
Editor: sin(‹Angle›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Angle in RADIANS. Multiply by 57.29577951308232 for the degrees createUnits.facing wants. |
{"k":"sin","a":1}cos
Cosine — returns real. Cosine of an angle in radians. Needs map format 3.
Editor: cos(‹Angle›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Angle in RADIANS. Multiply by 57.29577951308232 for the degrees createUnits.facing wants. |
{"k":"cos","a":1}tan
Tangent — returns real. Tangent of an angle in radians. Needs map format 3.
Editor: tan(‹Angle›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Angle in RADIANS. Multiply by 57.29577951308232 for the degrees createUnits.facing wants. |
{"k":"tan","a":1}atan2
Angle of a vector — returns real. Direction of the vector (X, Y) in radians, -pi..pi. Y comes first, as it does in every atan2. Due west is +pi, not -pi. Needs map format 3.
Editor: angle of (‹Horizontal component›, ‹Vertical component›)
| Field | Type | Required | Description |
|---|---|---|---|
y | expr (number) | yes | Vertical component. |
x | expr (number) | yes | Horizontal component. |
{"k":"atan2","y":1,"x":1}asin
Arc sine — returns real. Angle in radians whose sine is A. Outside -1..1 yields 0, never NaN. Needs map format 3.
Editor: asin(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"asin","a":1}acos
Arc cosine — returns real. Angle in radians whose cosine is A. Outside -1..1 yields 0, never NaN. Needs map format 3.
Editor: acos(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"acos","a":1}exp
e to the power A — returns real. e raised to A. A non-finite result yields 0. Needs map format 3.
Editor: e^‹A›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"exp","a":1}log
Natural logarithm — returns real. Natural log of A. A at or below zero yields 0, never -Infinity and never NaN. Needs map format 3.
Editor: ln(‹A›)
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (number) | yes | Left operand. |
{"k":"log","a":1}Comparison expressions
eq
A = B — returns bool. Equality. Strings compare by code unit; groups element-wise; points by x then y.
Editor: ‹A› = ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"eq","a":1,"b":1}ne
A ≠ B — returns bool. Inequality.
Editor: ‹A› ≠ ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"ne","a":1,"b":1}lt
A < B — returns bool. Less than.
Editor: ‹A› < ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"lt","a":1,"b":1}le
A ≤ B — returns bool. Less than or equal.
Editor: ‹A› ≤ ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"le","a":1,"b":1}gt
A > B — returns bool. Greater than.
Editor: ‹A› > ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"gt","a":1,"b":1}ge
A ≥ B — returns bool. Greater than or equal.
Editor: ‹A› ≥ ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
b | expr (any) | yes | Right operand. |
{"k":"ge","a":1,"b":1}Logic expressions
and
A and B — returns bool. Logical AND. Short-circuits: B is not evaluated when A is false.
Editor: ‹A› and ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (bool) | yes | Left operand. |
b | expr (bool) | yes | Right operand. |
{"k":"and","a":true,"b":true}or
A or B — returns bool. Logical OR. Short-circuits: B is not evaluated when A is true.
Editor: ‹A› or ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (bool) | yes | Left operand. |
b | expr (bool) | yes | Right operand. |
{"k":"or","a":true,"b":true}not
Not A — returns bool. Logical negation.
Editor: not ‹A›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (bool) | yes | Left operand. |
{"k":"not","a":true}Time expressions
tick
Game tick — returns int. The current simulation tick (20 per second) — the only clock in the language.
Editor: the game tick
No fields.
{"k":"tick"}rand
Random integer — returns int. Inclusive random integer from the synchronised game PRNG. Always consumes exactly one draw.
Editor: random ‹Minimum› to ‹Maximum›
| Field | Type | Required | Description |
|---|---|---|---|
min | expr (int) | yes | Lower bound (inclusive). |
max | expr (int) | yes | Upper bound (inclusive). |
{"k":"rand","min":1,"max":6}randReal
Random real — returns real. Random real in [min, max) from the synchronised game PRNG. Always consumes exactly one draw, even when min equals max, because a conditional draw would advance the stream on one client and not another. Needs map format 3.
Editor: random real ‹Minimum› to ‹Maximum (exclusive)›
| Field | Type | Required | Description |
|---|---|---|---|
min | expr (number) | yes | Lower bound (inclusive). |
max | expr (number) | yes | Upper bound (exclusive). |
{"k":"randReal","min":1,"max":1}timerRemaining
Timer ticks left — returns int. Ticks until the timer expires (0 if not running).
Editor: ticks left on ‹Timer›
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
{"k":"timerRemaining","timer":"wave_timer"}timerRunning
Timer is running — returns bool. True while the timer runs and is not paused.
Editor: ‹Timer› is running
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
{"k":"timerRunning","timer":"wave_timer"}Player expressions
playerResource
Player's resource — returns int. Current stock of one resource.
Editor: ‹Player›'s ‹Resource›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
resource | 'gold' | 'lumber' | 'stone' | 'oil' | yes | Resource kind. |
{"k":"playerResource","player":0,"resource":"gold"}playerPop
Player's population — returns int. Population in use.
Editor: ‹Player›'s population
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"playerPop","player":0}playerMaxPop
Player's population cap — returns int. Population cap.
Editor: ‹Player›'s population cap
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"playerMaxPop","player":0}playerAlive
Player is alive — returns bool. True while the seat has not been eliminated or defeated.
Editor: ‹Player› is still alive
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"playerAlive","player":0}playerTeam
Player's team — returns int. Team id of the seat, or -1.
Editor: ‹Player›'s team
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"playerTeam","player":0}playerHasUpgrade
Player has upgrade — returns bool. True once the seat has completed the research.
Editor: ‹Player› has researched ‹Upgrade›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
upgradeId | upgradeId | yes | Upgrade id. |
{"k":"playerHasUpgrade","player":0,"upgradeId":"dwarf_forged_weapons_1"}isAllied
Players are allied — returns bool. True when seats A and B are on the same team.
Editor: ‹A› is allied with ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (player) | yes | Left operand. |
b | expr (player) | yes | Right operand. |
{"k":"isAllied","a":0,"b":0}isEnemy
Players are enemies — returns bool. True when seats A and B are not allied.
Editor: ‹A› is an enemy of ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (player) | yes | Left operand. |
b | expr (player) | yes | Right operand. |
{"k":"isEnemy","a":0,"b":0}playerName
Player's name — returns string. Display name of a seat. Today this is always "Player 1", "Player 2"… — the lobby name is a per-client string and a trigger variable is hashed, so it is deliberately not read here. Needs map format 3.
Editor: ‹Player›'s name
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"playerName","player":0}playerIsHuman
A human is at this seat — returns bool. True when a human is present: a human slot whose player has not left. An AI seat is false, and so is a seat whose human has gone. Needs map format 3.
Editor: ‹Player› is a human player
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"playerIsHuman","player":0}playerCount
Number of players — returns int. How many seats match. The creep and neutral seats are never counted, so "alive" on a two-player map with creeps answers 2. Needs map format 3.
Editor: number of ‹Which seats› players
| Field | Type | Required | Description |
|---|---|---|---|
of | 'seated' | 'alive' | 'human' | 'ai' | yes | Which seats to count. |
{"k":"playerCount","of":"seated"}isPointVisibleTo
Seat can see tile — returns bool. True when THAT SEAT's own units or completed buildings reveal the tile right now. It is the simulation's own vision test, not the fog you are looking at, so it answers the same on every client and in a replay. Allied vision is NOT folded in: ask about each ally, or use "players are allied". A seat that is not in the lobby owns nothing and answers false; the creep seat answers like any other. It does not know about night, possession or a submerged submarine's halved sight — it is the daylight baseline radius plus the sightRadius buff. It SCANS every revealer in the world, so it costs about what "units of player" costs; do not put it inside a big loop. Needs map format 3.
Editor: ‹Player› can see ‹Tile›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
at | expr (point) | yes | Tile to test. |
{"k":"isPointVisibleTo","player":0,"at":{"k":"regionCenter","region":"arena"}}Group expressions
unitsInRegion
Units in region — returns unitGroup. Sorted group of units whose tile is inside the region.
Editor: units in ‹Region› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
region | regionId | yes | Region id. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitsInRegion","region":"arena"}unitsWithTag
Units with tag — returns unitGroup. Sorted group of placed entities sharing an authored tag.
Editor: units tagged ‹Tag› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
tag | tag | yes | Authored tag. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitsWithTag","tag":"wave"}unitsOfPlayer
Units of player — returns unitGroup. Sorted group of units owned by the seat.
Editor: units owned by ‹Player› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitsOfPlayer","player":0}unitsOfType
Units of type — returns unitGroup. Sorted group of units or buildings of one type id.
Editor: units of type ‹Unit type› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
unitTypeId | unitTypeId | yes | Unit or building type id. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitsOfType","unitTypeId":"dwarf_ironguard"}groupCount
Group size — returns int. Number of units in a group.
Editor: number of units in ‹Unit group›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | The group. |
{"k":"groupCount","group":{"k":"unitsInRegion","region":"arena"}}groupAt
Unit at index — returns unit. 0-based element of the sorted group; out of range gives no unit.
Editor: unit ‹Index› of ‹Unit group›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | The group. |
index | expr (int) | yes | 0-based index. |
{"k":"groupAt","group":{"k":"unitsInRegion","region":"arena"},"index":1}groupRandom
Random unit of group — returns unit. A random member (one PRNG draw, even for an empty group).
Editor: a random unit in ‹Unit group›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | The group. |
{"k":"groupRandom","group":{"k":"unitsInRegion","region":"arena"}}groupUnion
Group union — returns unitGroup. Members of A or B.
Editor: ‹A› together with ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (unitGroup) | yes | Left operand. |
b | expr (unitGroup) | yes | Right operand. |
{"k":"groupUnion","a":{"k":"unitsInRegion","region":"arena"},"b":{"k":"unitsInRegion","region":"arena"}}groupDiff
Group difference — returns unitGroup. Members of A not in B.
Editor: ‹A› except ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (unitGroup) | yes | Left operand. |
b | expr (unitGroup) | yes | Right operand. |
{"k":"groupDiff","a":{"k":"unitsInRegion","region":"arena"},"b":{"k":"unitsInRegion","region":"arena"}}groupHas
Group contains — returns bool. True when the unit is in the group.
Editor: ‹Unit group› contains ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | The group. |
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
{"k":"groupHas","group":{"k":"unitsInRegion","region":"arena"},"unit":{"k":"eventUnit"}}groupEmpty
Empty group — returns unitGroup. A group with no units.
Editor: an empty group
No fields.
{"k":"groupEmpty"}unitsInRadius
Units within a radius — returns unitGroup. Sorted group of units within radius tiles of a point or unit. Measured on squared distance, which is exact on an integer grid; a radius below 0 matches nothing. Needs map format 3.
Editor: units within ‹Radius in tiles› of ‹Centre› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
at | expr (point) | yes | Centre — a point, or a unit whose tile is used. |
radius | expr (number) | yes | Radius in tiles, inclusive. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"unitsInRadius","at":{"k":"regionCenter","region":"arena"},"radius":1}closestUnit
Closest unit — returns unit. The nearest matching unit to a point or unit, or no unit. A tie goes to the lowest entity id, so the answer never depends on iteration order. Needs map format 3.
Editor: closest unit to ‹From› within ‹Maximum distance› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
at | expr (point) | yes | Where to measure from — a point, or a unit whose tile is used. |
filter | filter | no | Optional unit filter; every set field is ANDed. aliveOnly defaults to true. |
maxRadius | expr (number) | no | Ignore anything further than this many tiles; absent = search the whole map. |
{"k":"closestUnit","at":{"k":"regionCenter","region":"arena"}}groupSortBy
Group in order — returns unitGroup. The group reordered. Ties always break on the LOWEST entity id, in both directions — so 'desc' reverses the keys but is not exactly the reverse of 'asc' when two units share a key, and "the weakest" and "the strongest" pick the same one of a tied pair. The result is NOT id-sorted, so storing it in a unitGroup variable loses the order (variables are canonicalised on write) — use it inline, with forEachUnit or groupAt. Needs map format 3.
Editor: ‹Unit group› ordered by ‹Order by› ‹Direction› from ‹Measure from›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | The group. |
by | 'hp' | 'maxHp' | 'hpPercent' | 'mana' | 'maxMana' | 'level' | 'xp' | 'kills' | 'x' | 'y' | 'distance' | 'id' | yes | What to order on. |
order | 'asc' | 'desc' | no | 'asc' (default) or 'desc'. |
from | expr (point) | no | Reference point for by: 'distance'; absent measures from tile (0, 0). |
{"k":"groupSortBy","group":{"k":"unitsInRegion","region":"arena"},"by":"hp"}groupPickN
N random units of group — returns unitGroup. At most N random members, sorted. Consumes exactly min(N, group size) PRNG draws — both numbers are hashed sim state, so every client draws the same count. Needs map format 3.
Editor: ‹How many› random units of ‹Unit group›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | The group. |
n | expr (int) | yes | How many to pick. 0 or less gives an empty group. |
{"k":"groupPickN","group":{"k":"unitsInRegion","region":"arena"},"n":1}groupFilter
Group matching a filter — returns unitGroup. Members of the group that match the filter. The one way to narrow a group you already hold without rebuilding it from the world. Needs map format 3.
Editor: ‹Unit group› matching ‹Filter›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | The group. |
filter | filter | yes | Unit filter; every set field is ANDed. aliveOnly defaults to true. |
{"k":"groupFilter","group":{"k":"unitsInRegion","region":"arena"},"filter":{}}Item expressions
heroHasItem
Unit has item — returns bool. True when the unit holds this item in its BACKPACK or has it EQUIPPED. Both halves, because "does he have the crown" does not mean "in his bag but not on his head". Needs map format 3.
Editor: ‹Unit› has ‹Item›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
itemId | itemId | yes | Item id to look for. |
{"k":"heroHasItem","unit":{"k":"eventUnit"},"itemId":"healing_potion"}heroItemCount
Unit item count — returns int. How many of this item the unit holds: backpack stack counts summed, plus one for each equipped copy. Needs map format 3.
Editor: count of ‹Item› on ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
itemId | itemId | yes | Item id to count. |
{"k":"heroItemCount","unit":{"k":"eventUnit"},"itemId":"healing_potion"}Event expressions
eventUnit
Triggering unit — returns unit. The unit the event is about (entered, died, trained, was attacked…). No unit when the event has none.
Editor: the triggering unit
No fields.
{"k":"eventUnit"}eventOtherUnit
Other unit — returns unit. The killer for unitDies, the attacker for unitAttacked, the shooter for projectileHits.
Editor: the other unit
No fields.
{"k":"eventOtherUnit"}eventPlayer
Triggering player — returns player. The seat the event is about, or -1.
Editor: the triggering player
No fields.
{"k":"eventPlayer"}eventValue
Event value — returns number. Numeric payload: dialog button index, hero level, damage, raised value.
Editor: the event value
No fields.
{"k":"eventValue"}eventString
Event text — returns string. String payload: upgrade id, item id, chat text, custom event name.
Editor: the event text
No fields.
{"k":"eventString"}eventDamage
Event damage — returns real. The damage amount as it stands right now. Inside "unit is about to take damage" that is after every earlier trigger's "set event damage", so armour triggers compose; inside "unit has taken damage" it is the amount that landed. 0 on any other event. Needs map format 3.
Editor: the event damage
No fields.
{"k":"eventDamage"}eventDamageType
Event damage element — returns string. The element the damage site tracks — physical, fire, and so on — or empty where it tracks none (a lava tile, a collapsing bridge). A different axis from the damage kind: a projectile can be fire and a spell can be physical. Needs map format 3.
Editor: the event damage element
No fields.
{"k":"eventDamageType"}eventDamageKind
Event damage kind — returns string. Which door the damage came through: melee, projectile, spell, burn, thorns, cleave, and the rest. This is the axis a damage-type table or a "spell damage only" shield switches on. Empty on a non-damage event. Needs map format 3.
Editor: the event damage kind
No fields.
{"k":"eventDamageKind"}eventSpellId
Event spell — returns string. The spell id of an abilityCast* event; '' on every other event. A separate node from eventString, which on a cancelled cast carries the cancel REASON. Needs map format 3.
Editor: event spell
No fields.
{"k":"eventSpellId"}eventTargetPoint
Event target point — returns point. The target TILE of an abilityCast* or orderIssued event. (-1, -1) when the event had no ground target — a unit-targeted cast, or a stop / hold order. Tile 0,0 is a legal map corner, so 0 could not be the sentinel. Needs map format 3.
Editor: event target point
No fields.
{"k":"eventTargetPoint"}UI expressions
counter
Counter value — returns number. Current value of an on-screen counter.
Editor: counter ‹Counter›
| Field | Type | Required | Description |
|---|---|---|---|
id | counterId | yes | Counter id. |
{"k":"counter","id":"lives"}Trigger expressions
triggerFireCount
Trigger fire count — returns int. How many times a trigger has fired.
Editor: times ‹Trigger› has fired
| Field | Type | Required | Description |
|---|---|---|---|
triggerId | triggerId | yes | Trigger id. |
{"k":"triggerFireCount","triggerId":"main"}Text expressions
concat
Concatenate — returns string. A joined with B as strings.
Editor: ‹A› joined with ‹B›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (string) | yes | Left operand. |
b | expr (string) | yes | Right operand. |
{"k":"concat","a":"text","b":"text"}toStr
To text — returns string. Value as text. Non-integers print with decimals places (default 2).
Editor: ‹Value› as text to ‹Decimal places› dp
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (any) | yes | Left operand. |
decimals | int (≥ 0, ≤ 6) | no | Fixed decimal places, 0..6. |
{"k":"toStr","a":1}subStr
Substring — returns string. 0-based slice; out-of-range start gives empty text.
Editor: substring of ‹String› from ‹Start index› length ‹Length›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | Source text. |
start | expr (int) | yes | Start index. |
length | expr (int) | no | Maximum length; absent = to the end. |
{"k":"subStr","text":"Hello world","start":0,"length":5}strLen
Text length — returns int. Length in UTF-16 code units.
Editor: length of ‹String›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | The text. |
{"k":"strLen","text":"text"}strFind
Find text — returns int. 0-based index of the first occurrence, or -1. An empty search text yields -1. Needs map format 3.
Editor: index of ‹Look for› in ‹String› from ‹From index›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | Text to search. |
find | expr (string) | yes | Text to look for. |
start | expr (int) | no | Index to start from; negative counts as 0. |
{"k":"strFind","text":"text","find":"text"}strReplace
Replace text — returns string. Every occurrence replaced. An empty search text returns the text unchanged rather than inserting between every character. Needs map format 3.
Editor: ‹String› with ‹Replace› replaced by ‹With›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | Source text. |
find | expr (string) | yes | Text to replace. |
to | expr (string) | yes | Replacement. |
{"k":"strReplace","text":"text","find":"text","to":"text"}strSplitPart
Split and take — returns string. Field index of the text split on sep, or empty text when the index is out of range. An empty separator treats the whole text as field 0. Needs map format 3.
Editor: part ‹Part (0-based)› of ‹String› split on ‹Separator›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | Source text. |
sep | expr (string) | yes | Separator. |
index | expr (int) | yes | 0-based field index. |
{"k":"strSplitPart","text":"text","sep":"text","index":1}strSplitCount
Split count — returns int. How many fields strSplitPart can reach: separator count plus one, and 0 for empty text. Needs map format 3.
Editor: parts of ‹String› split on ‹Separator›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | Source text. |
sep | expr (string) | yes | Separator. |
{"k":"strSplitCount","text":"text","sep":"text"}strUpper
Upper case — returns string. ASCII a-z raised to A-Z. Every other code unit is left alone, ON PURPOSE: Unicode case mapping depends on which Unicode revision the browser shipped, and these strings are hashed. Needs map format 3.
Editor: upper case of ‹String›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | The text. |
{"k":"strUpper","text":"text"}strLower
Lower case — returns string. ASCII A-Z lowered to a-z. Every other code unit is left alone — see strUpper. Needs map format 3.
Editor: lower case of ‹String›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | The text. |
{"k":"strLower","text":"text"}strTrimAscii
Trim spaces — returns string. Strip leading and trailing ASCII whitespace (space, tab, newline, carriage return, vertical tab, form feed). Non-breaking spaces and the byte-order mark are NOT stripped, because the Unicode whitespace set has grown between browser releases. Needs map format 3.
Editor: trimmed ‹String›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | The text. |
{"k":"strTrimAscii","text":"text"}strToInt
Text to integer — returns int. Parse text as a number and truncate toward zero. Anything unparseable yields 0. Surrounding ASCII whitespace is ignored. Needs map format 3.
Editor: ‹String› as an integer
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | The text. |
{"k":"strToInt","text":"text"}strToReal
Text to real — returns real. Parse text as a real number. Anything unparseable, infinite or NaN yields 0. Needs map format 3.
Editor: ‹String› as a real number
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | The text. |
{"k":"strToReal","text":"text"}formatTicks
Ticks as a clock — returns string. Ticks rendered as a clock. 20 ticks is one second; negatives clamp to 0. Minutes are not capped at 59, so an hour of "mm:ss" is "60:00". Needs map format 3.
Editor: ‹Ticks› as a clock (‹Layout›)
| Field | Type | Required | Description |
|---|---|---|---|
ticks | expr (int) | yes | Tick count. |
format | 'mm:ss' | 'm:ss' | 'h:mm:ss' | 'ss.t' | no | Layout (default mm:ss). |
{"k":"formatTicks","ticks":1}hashString
Hash text — returns int. Deterministic 32-bit FNV-1a hash.
Editor: hash of ‹String›
| Field | Type | Required | Description |
|---|---|---|---|
text | expr (string) | yes | The text. |
{"k":"hashString","text":"text"}Physics expressions
unitVelocity
Unit velocity — returns real. Kinematic velocity component or speed (tiles/tick); 0 without kinematics.
Editor: ‹Axis› velocity of ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
axis | 'x' | 'y' | 'speed' | yes | Component to read. |
{"k":"unitVelocity","unit":{"k":"eventUnit"},"axis":"x"}Save codes expressions
verifySaveCode
Read save code — returns any. Validate a save code for this map. With field, returns that field (or 0); without, returns 1 for a valid code and 0 otherwise.
Editor: save code ‹Code› field ‹Field› (valid?)
| Field | Type | Required | Description |
|---|---|---|---|
code | expr (string) | yes | The code text. |
field | string | no | Field to extract; playerId returns the issuing seat. |
{"k":"verifySaveCode","code":{"k":"var","name":"code"}}Function expressions
call
Call function — returns any. Call a user-defined function that declares a return type and yield its return value. Arguments are evaluated left to right and coerced to the parameter types. Recursion past the depth cap yields the zero value.
Editor: ‹Function›(‹Arguments›)
| Field | Type | Required | Description |
|---|---|---|---|
fn | functionName | yes | Function name. |
args | exprList | no | Arguments, one per parameter, in order. |
{"k":"call","fn":"double","args":[2]}Hashtable expressions
hashtableGet
Hashtable value — returns any. Value stored under (key1, key2), or the zero value of the table type. Keys are integers or strings.
Editor: ‹Hashtable›[‹Parent key›][‹Child key›]
| Field | Type | Required | Description |
|---|---|---|---|
table | hashtableName | yes | Hashtable name. |
key1 | expr (any) | yes | Parent key (int or string). |
key2 | expr (any) | yes | Child key (int or string). |
{"k":"hashtableGet","table":"scores","key1":1,"key2":1}hashtableHas
Hashtable has key — returns bool. True when a value is stored under (key1, key2).
Editor: ‹Hashtable› has [‹Parent key›][‹Child key›]
| Field | Type | Required | Description |
|---|---|---|---|
table | hashtableName | yes | Hashtable name. |
key1 | expr (any) | yes | Parent key. |
key2 | expr (any) | yes | Child key. |
{"k":"hashtableHas","table":"scores","key1":1,"key2":1}hashtableCount
Hashtable size — returns int. Number of entries in the table, or under one parent key when key1 is set.
Editor: number of entries in ‹Hashtable› under ‹Parent key›
| Field | Type | Required | Description |
|---|---|---|---|
table | hashtableName | yes | Hashtable name. |
key1 | expr (any) | no | Parent key to count children of. |
{"k":"hashtableCount","table":"scores"}Actions
What a trigger does, run top to bottom.
Control flow actions
if
If / then / else. Run then when cond is true, else else.
Editor: if ‹Condition›
| Field | Type | Required | Description |
|---|---|---|---|
cond | expr (bool) | yes | Condition. |
then | actions | yes | Actions when true. |
else | actions | no | Actions when false. |
{"k":"if","cond":true,"then":[]}while
While. Repeat the body while cond holds, at most maxIterations (hard cap 10000) times per run.
Editor: while ‹Condition›
| Field | Type | Required | Description |
|---|---|---|---|
cond | expr (bool) | yes | Loop condition, checked before each iteration. |
body | actions | yes | Loop body. |
maxIterations | int (≥ 1, ≤ 10000) | no | Iteration cap (≤ 10000). |
{"k":"while","cond":true,"body":[]}forEachUnit
For each unit. Run the body once per unit of a group (snapshotted and sorted before the first iteration).
Editor: for each unit ‹Loop variable› in ‹Unit group›
| Field | Type | Required | Description |
|---|---|---|---|
group | expr (unitGroup) | yes | Group to iterate. |
varName | loopVar | yes | Loop variable receiving each unit id; an undeclared name becomes a local. |
body | actions | yes | Loop body. |
{"k":"forEachUnit","group":{"k":"unitsInRegion","region":"arena"},"varName":"u","body":[]}forEachInt
For each integer. Run the body for each integer from..to inclusive (at most 10000).
Editor: for ‹Loop variable› from ‹From› to ‹To›
| Field | Type | Required | Description |
|---|---|---|---|
varName | loopVar | yes | Loop variable; an undeclared name becomes a local. |
from | expr (int) | yes | First value. |
to | expr (int) | yes | Last value (inclusive). |
body | actions | yes | Loop body. |
{"k":"forEachInt","varName":"i","from":1,"to":1,"body":[]}wait
Wait. Suspend this trigger for N ticks (min 1) and resume at the next action. Locals and loop state survive, including across save/load. Not allowed inside functions.
Editor: wait ‹Ticks› ticks
| Field | Type | Required | Description |
|---|---|---|---|
ticks | expr (int) | yes | Ticks to wait. |
{"k":"wait","ticks":1}raiseEvent
Raise custom event. Fire the custom event of this name later in the same tick.
Editor: raise custom event ‹Event name› with value ‹Value› and text ‹Text›
| Field | Type | Required | Description |
|---|---|---|---|
name | eventName | yes | Event name. |
value | expr (number) | no | Event value payload. |
text | expr (string) | no | Event text payload (default: the name). |
{"k":"raiseEvent","name":"boss_down"}comment
Comment. Does nothing; documents the script.
Editor: // ‹Comment›
| Field | Type | Required | Description |
|---|---|---|---|
text | string | yes | Comment text. |
{"k":"comment","text":"text"}Variable actions
setVar
Set variable. Write a variable (local, parameter, loop or global). Writing a global with a new value raises variableChanges.
Editor: set ‹Variable›[‹Index›] to ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
name | varName | yes | Variable name. The variable must already exist — an assignment does NOT declare one. |
index | expr (int) | no | Element index for an array. |
value | expr (any) | yes | New value (coerced to the variable type). |
{"k":"setVar","name":"score","value":1}addVar
Add to variable. Add a number to a numeric variable.
Editor: add ‹Amount› to ‹Variable›[‹Index›]
| Field | Type | Required | Description |
|---|---|---|---|
name | varName | yes | Variable name. The variable must already exist — this does NOT declare one. |
index | expr (int) | no | Element index for an array. |
delta | expr (number) | yes | Amount to add. |
{"k":"addVar","name":"score","delta":1}Unit actions
setEventDamage
Set event damage. Replace the damage this hit is about to do. Only works in a trigger with a "unit is about to take damage" (unitDamaging) event — the validator refuses it anywhere else, because anywhere else it would silently do nothing. A result that is not a finite number leaves the damage UNCHANGED rather than zeroing it, so a broken formula reads as "my armour does nothing" instead of as accidental invulnerability; otherwise it is clamped to 0..1000000000. Needs map format 3.
Editor: set the event damage to ‹Damage›
| Field | Type | Required | Description |
|---|---|---|---|
amount | expr (number) | yes | The new damage amount. Read the old one with "event damage". |
{"k":"setEventDamage","amount":1}addBuff
Add buff. Put a stat buff on a unit. The same buff id REFRESHES rather than stacks: duration, source and stat are overwritten, and the modifier is overwritten only when the new one is at least as strong — the engine's own rule, not a second one. A duration of -1 is permanent. NOTE: trigger buffs are hashed and lockstep-safe but are NOT saved — like every spell-applied buff, they are dropped by save/load. Needs map format 3.
Editor: buff ‹Unit›: ‹Stat› by ‹Amount› for ‹Duration in ticks› ticks as ‹Buff id›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
buffId | string | yes | Your id for this buff. Re-using it refreshes the existing one; remove buff and unit has buff take the same id. |
stat | 'damage' | 'damagePct' | 'siegeDamage' | 'armor' | 'attackSpeed' | 'lifesteal' | 'damageMirror' | 'thorns' | 'critChance' | 'dodgeChance' | 'blockChance' | 'blockValue' | 'parryChance' | 'hitBonus' | 'blind' | 'speed' | 'speedPct' | 'maxHpPct' | 'hpRegen' | 'manaRegen' | 'poison' | 'spellPower' | 'sightRadius' | 'productionSpeed' | 'gatherRatePct' | yes | What it modifies. Only these are read by the engine — any other name would be stored and ignored. |
modifier | expr (number) | yes | How much to add. Negative debuffs. A non-finite value makes the action do nothing. |
durationTicks | expr (int) | yes | How long, in ticks (20 = 1 second). -1 is permanent; 0 or less otherwise does nothing. |
{"k":"addBuff","unit":{"k":"eventUnit"},"buffId":"text","stat":"damage","modifier":1,"durationTicks":1}removeBuff
Remove buff. Take one buff off a unit, matching the WHOLE id. Aura buffs are left alone: AuraSystem re-stamps those every tick, so removing one would come straight back and read as a broken action. Needs map format 3.
Editor: remove buff ‹Buff id› from ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
buffId | string | yes | Buff id to remove. |
{"k":"removeBuff","unit":{"k":"eventUnit"},"buffId":"text"}addAbility
Add ability. Give a unit a spell it can cast. A unit with no caster slot gets one. Granting a spell it already has does nothing, so this is safe to call every tick. Needs map format 3.
Editor: give ‹Unit› the ability ‹Spell›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
spellId | spellId | yes | Spell id to grant. |
{"k":"addAbility","unit":{"k":"eventUnit"},"spellId":"berserk"}removeAbility
Remove ability. Take a spell away from a unit. Removing one it does not have does nothing. This does not silence: a cast already in flight resolves. Needs map format 3.
Editor: take the ability ‹Spell› from ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
spellId | spellId | yes | Spell id to remove. |
{"k":"removeAbility","unit":{"k":"eventUnit"},"spellId":"berserk"}Trigger actions
enableTrigger
Enable/disable trigger. Turn a trigger on or off.
Editor: set trigger ‹Trigger› enabled to ‹Enabled›
| Field | Type | Required | Description |
|---|---|---|---|
triggerId | triggerId | yes | Trigger id. |
on | bool | yes | Enabled. |
{"k":"enableTrigger","triggerId":"main","on":true}runTrigger
Run trigger. Run another trigger's actions inline in this event context.
Editor: run trigger ‹Trigger› (checking its conditions: ‹Check conditions›)
| Field | Type | Required | Description |
|---|---|---|---|
triggerId | triggerId | yes | Trigger id. |
checkConditions | bool | no | Evaluate its conditions first (and count it as fired). |
{"k":"runTrigger","triggerId":"main"}stopTrigger
Stop trigger. Abandon the rest of this trigger. Not allowed inside functions (use return).
Editor: stop running this trigger
No fields.
{"k":"stopTrigger"}Function actions
callFunction
Call function. Call a user-defined function as a statement, optionally storing its return value.
Editor: call ‹Function›(‹Arguments›) and store the result in ‹Store result in›[‹Index›]
| Field | Type | Required | Description |
|---|---|---|---|
fn | functionName | yes | Function name. |
args | exprList | no | Arguments, one per parameter, in order. |
intoVar | varName | no | Variable receiving the return value (function must declare returns). It must ALREADY exist — declared in vars, or a local, parameter or loop variable in scope. Unlike a loop variable, this does NOT declare one, and an undeclared name is an error. |
intoIndex | expr (int) | no | Element index when intoVar is an array. |
{"k":"callFunction","fn":"double","args":[2]}return
Return. Leave the current function, yielding value. At trigger level (no value allowed) it stops the trigger.
Editor: return ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
value | expr (any) | no | Return value (functions with a return type). |
{"k":"return"}Hashtable actions
hashtableSet
Hashtable set. Store a value under (key1, key2). New keys past the table cap are dropped.
Editor: set ‹Hashtable›[‹Parent key›][‹Child key›] to ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
table | hashtableName | yes | Hashtable name. |
key1 | expr (any) | yes | Parent key (int or string). |
key2 | expr (any) | yes | Child key (int or string). |
value | expr (any) | yes | Value (coerced to the table type). |
{"k":"hashtableSet","table":"scores","key1":1,"key2":1,"value":1}hashtableRemove
Hashtable remove. Remove the value under (key1, key2).
Editor: remove ‹Hashtable›[‹Parent key›][‹Child key›]
| Field | Type | Required | Description |
|---|---|---|---|
table | hashtableName | yes | Hashtable name. |
key1 | expr (any) | yes | Parent key. |
key2 | expr (any) | yes | Child key. |
{"k":"hashtableRemove","table":"scores","key1":1,"key2":1}hashtableClear
Hashtable clear. Remove every entry, or only those under key1.
Editor: clear ‹Hashtable› under ‹Parent key›
| Field | Type | Required | Description |
|---|---|---|---|
table | hashtableName | yes | Hashtable name. |
key1 | expr (any) | no | Parent key to flush; absent = whole table. |
{"k":"hashtableClear","table":"scores"}Units actions
createUnits
Create units. Spawn N units (max 500) for a player at a point.
Editor: create ‹Count› × ‹Unit type› for ‹Player› at ‹At point› facing ‹Facing (degrees)›° and store them in ‹Store in›
| Field | Type | Required | Description |
|---|---|---|---|
unitTypeId | unitTypeId | yes | Unit type id (base game or a defOverrides clone). |
count | expr (int) | yes | How many. |
player | expr (player) | yes | Seat id (0-based) of the player. |
at | expr (point) | yes | Spawn tile. |
facing | expr (int) | no | Facing in degrees. |
intoVar | varName | no | unitGroup variable receiving the created ids. It must ALREADY exist — declared in vars, or a local, parameter or loop variable in scope. Unlike a loop variable, this does NOT declare one, and an undeclared name is an error. |
{"k":"createUnits","unitTypeId":"dwarf_ironguard","count":1,"player":0,"at":{"k":"regionCenter","region":"arena"}}removeUnit
Remove unit. Remove a unit outright — no death, corpse or bounty.
Editor: remove ‹Unit› from the game
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
{"k":"removeUnit","unit":{"k":"eventUnit"}}killUnit
Kill unit. Kill a unit through the normal death path.
Editor: kill ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
{"k":"killUnit","unit":{"k":"eventUnit"}}setUnitHp
Set unit HP. Set HP (clamped to 0..max).
Editor: set HP of ‹Unit› to ‹Value› (as a percentage: ‹Value is a percentage›)
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
value | expr (number) | yes | HP or percentage. |
percent | bool | no | Treat value as a percentage of max HP. |
{"k":"setUnitHp","unit":{"k":"eventUnit"},"value":1}setUnitMana
Set unit mana. Set mana (clamped).
Editor: set mana of ‹Unit› to ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
value | expr (number) | yes | Mana. |
{"k":"setUnitMana","unit":{"k":"eventUnit"},"value":1}setUnitOwner
Change owner. Give a unit to another seat.
Editor: give ‹Unit› to ‹Player›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"setUnitOwner","unit":{"k":"eventUnit"},"player":0}setUnitInvulnerable
Set invulnerable. Toggle invulnerability.
Editor: set ‹Unit› invulnerable to ‹Invulnerable›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
on | bool | yes | Invulnerable. |
{"k":"setUnitInvulnerable","unit":{"k":"eventUnit"},"on":true}setUnitProp
Set unit stat. Write one live combat or movement stat. It does NOT cover hp, mana, owner or invulnerability - those have their own actions, and a second way to write them would drift. maxHp and maxMana move the CURRENT value by the same amount, so a buffed unit is not left at 40/900; lowering a max clamps the current down. Values clamp at 0, and attackSpeed / moveSpeed at a small positive floor (0 attacks per second is a division, 0 speed is a unit that never finishes a path). sightRadius writes the fog revealer and does nothing on a unit that has none, because giving fog vision to something that had none is a bigger decision than setting a number. Needs map format 3.
Editor: set ‹Stat› of ‹Unit› to ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
prop | 'damage' | 'range' | 'attackSpeed' | 'maxHp' | 'armor' | 'magicResist' | 'moveSpeed' | 'maxMana' | 'manaRegen' | 'sightRadius' | yes | Stat to write. attackSpeed is ATTACKS PER SECOND, not a cooldown in seconds - there is no attackCooldown field on a unit. |
value | expr (number) | yes | New value. |
{"k":"setUnitProp","unit":{"k":"eventUnit"},"prop":"damage","value":1}faceUnit
Face unit toward point. Turn a living stationary unit toward a tile using its ordinary turn rate. Does not move it, cancel orders or turn structures. Missing, dead, moving or coincident targets do nothing. Needs map format 3.
Editor: turn ‹Unit› toward ‹Toward point›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
toward | expr (point) | yes | Tile to face. Evaluated only for an eligible stationary unit. |
{"k":"faceUnit","unit":{"k":"eventUnit"},"toward":{"k":"regionCenter","region":"arena"}}moveUnit
Move unit instantly. Teleport a unit to a tile.
Editor: move ‹Unit› to ‹To point›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
to | expr (point) | yes | Destination tile. |
{"k":"moveUnit","unit":{"k":"eventUnit"},"to":{"k":"regionCenter","region":"arena"}}playAnimation
Play animation. Request a finite saved cinematic pose on a stationary living unit. Movement, death and scene completion release it; missing model clips do not perform.
Editor: play animation ‹Animation name› on ‹Unit› (loop: ‹Loop›) for ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
animation | string | yes | Canonical clip slot: idle, attack, gather, build, special_1/2/3, sleep or sitting. cast aliases special_1. There is no invented talk or living death clip. |
loop | bool | no | Whether to loop the animation. |
durationTicks | int (≥ 1) | no | Finite pose window (default 20 ticks, capped at 1200). Loops retain normal clip speed; one-shots fit this window. |
{"k":"playAnimation","unit":{"k":"eventUnit"},"animation":"text"}Hero actions
heroAddXp
Give hero XP. Grant XP to ONE hero and resolve every level it crosses, inside this action. A reviving hero is skipped. Safe to call from a heroLevels trigger: trigger signals are drained at the top of the next tick, so the level-up has already finished and there is no mid-level-up state to reason about. Needs map format 3.
Editor: give ‹XP› XP to ‹Unit›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
amount | expr (int) | yes | XP to grant. Zero or negative does nothing. |
{"k":"heroAddXp","unit":{"k":"eventUnit"},"amount":1}heroSetLevel
Set hero level. Raise a hero TO a level. It grants the XP rather than writing the level, so the hero gets every point of growth the levels are worth and is indistinguishable from one that earned them. LOWERING IS A NO-OP, not an approximation: the engine records the level but not the deltas used to reach it. The target is clamped to the match's level cap first, so asking for a million costs the same as asking for the cap. Needs map format 3.
Editor: raise ‹Unit› to level ‹Level›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
level | expr (int) | yes | Level to reach. Clamped to the match level cap; a level at or below the current one does nothing. |
{"k":"heroSetLevel","unit":{"k":"eventUnit"},"level":1}heroRevive
Revive hero. Finish a hero's revival now: clear the countdown, restore health and mana to full, and optionally move it. IT CANNOT RESURRECT A HERO WHOSE ENTITY IS GONE - a dead hero is destroyed and its progress banked, and respawning one is not exposed to triggers. Use it on a hero that still exists: one counting down a revive, or one left at zero HP. On a hero already up and whole it does nothing. Needs map format 3.
Editor: revive ‹Unit› at ‹Tile›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
at | expr (point) | no | Tile to put the hero on. Absent leaves it where it is. |
{"k":"heroRevive","unit":{"k":"eventUnit"}}Item actions
itemCreate
Create item on ground. Put an item on the ground. The tile is a request: the engine walks an expanding ring for a free walkable tile, and an item asked for on deep water floats there instead of being dragged ashore. It hands back NO handle, so a map cannot refer to the item it made. An unknown item id creates nothing. Needs map format 3.
Editor: create ‹Item› at ‹Tile›
| Field | Type | Required | Description |
|---|---|---|---|
itemId | itemId | yes | Item id to create. |
at | expr (point) | yes | Tile to create it on. |
{"k":"itemCreate","itemId":"healing_potion","at":{"k":"regionCenter","region":"arena"}}itemGive
Give item to unit. Put an item in a unit's BACKPACK. It does not auto-equip - equipping applies stat effects and set bonuses, which is a different request. Consumables stack to the item's cap. When the bag is full the remainder lands on the ground at the unit's feet rather than vanishing. Needs map format 3.
Editor: give ‹Item› to ‹Unit› x‹Count›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
itemId | itemId | yes | Item id to give. |
count | expr (int) | no | How many (default 1). |
{"k":"itemGive","unit":{"k":"eventUnit"},"itemId":"healing_potion"}itemRemove
Remove item from unit. Take an item out of a unit's BACKPACK. Equipped gear is not touched: pulling gear off has to reverse its stat effects and recompute the set bonus. Removes across stacks, lowest slot first. Asking for more than the unit holds removes what it holds. Needs map format 3.
Editor: remove ‹Item› from ‹Unit› x‹Count›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
itemId | itemId | yes | Item id to remove. |
count | expr (int) | no | How many (default 1). |
{"k":"itemRemove","unit":{"k":"eventUnit"},"itemId":"healing_potion"}Group actions
addUnitToGroup
Add unit to group. Add a unit to a unitGroup variable.
Editor: add ‹Unit› to ‹Group variable›
| Field | Type | Required | Description |
|---|---|---|---|
varName | varName | yes | An already-declared unitGroup variable; this does not create one. |
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
{"k":"addUnitToGroup","varName":"squad","unit":{"k":"eventUnit"}}removeUnitFromGroup
Remove unit from group. Remove a unit from a unitGroup variable.
Editor: remove ‹Unit› from ‹Group variable›
| Field | Type | Required | Description |
|---|---|---|---|
varName | varName | yes | An already-declared unitGroup variable; this does not create one. |
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
{"k":"removeUnitFromGroup","varName":"squad","unit":{"k":"eventUnit"}}clearGroup
Clear group. Empty a unitGroup variable.
Editor: clear ‹Group variable›
| Field | Type | Required | Description |
|---|---|---|---|
varName | varName | yes | An already-declared unitGroup variable; this does not create one. |
{"k":"clearGroup","varName":"squad"}Orders actions
boardTransport
Board transport. Issue ordinary boarding inside the tick. The unit walks into reach before the transport system boards it; owner, capacity and terrain rules still apply. Needs map format 3.
Editor: board ‹Units› onto ‹Transport›
| Field | Type | Required | Description |
|---|---|---|---|
units | expr (unitGroup) | yes | Original units to board. |
transport | expr (unit) | yes | Original transport. |
{"k":"boardTransport","units":{"k":"unitsInRegion","region":"arena"},"transport":{"k":"eventUnit"}}order
Order units. Issue an order to a group, applied inside the tick (never through lockstep).
Editor: order ‹Units› to ‹Order› at ‹Target point› targeting ‹Target unit› (queued: ‹Queue behind current order›)
| Field | Type | Required | Description |
|---|---|---|---|
units | expr (unitGroup) | yes | Units to order (a single unit also works). |
order | 'move' | 'attack_move' | 'attack' | 'stop' | 'hold' | 'patrol' | 'follow' | yes | Order kind. |
to | expr (point) | no | Target tile for move / attack_move / patrol. |
target | expr (unit) | no | Target unit for attack / follow. |
queue | bool | no | Queue after current orders. |
{"k":"order","units":{"k":"unitsInRegion","region":"arena"},"order":"attack_move","to":{"k":"regionCenter","region":"arena"}}Economy actions
setResource
Set resource. Set a player's resource stock (rounded, ≥ 0).
Editor: set ‹Player›'s ‹Resource› to ‹Value›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
resource | 'gold' | 'lumber' | 'stone' | 'oil' | yes | Resource kind. |
value | expr (int) | yes | New amount. |
{"k":"setResource","player":0,"resource":"gold","value":1}addResource
Add resource. Add to a player's resource stock (rounded, ≥ 0).
Editor: give ‹Player› ‹Amount› ‹Resource›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
resource | 'gold' | 'lumber' | 'stone' | 'oil' | yes | Resource kind. |
delta | expr (int) | yes | Amount to add (negative subtracts). |
{"k":"addResource","player":0,"resource":"gold","delta":1}setAlliance
Set alliance. Ally B to A's team, or put B back on its own team.
Editor: set ‹A› and ‹B› allied to ‹Allied›
| Field | Type | Required | Description |
|---|---|---|---|
a | expr (player) | yes | Left operand. |
b | expr (player) | yes | Right operand. |
allied | bool | yes | Allied. |
{"k":"setAlliance","a":0,"b":0,"allied":true}setBuildableRegion
Restrict building. Limit where a seat may build: a list of regions, [] for nowhere, or null to restore the default.
Editor: restrict building for ‹Player› to ‹Regions›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
regions | regionId[] | null | yes | Region ids, or null. |
{"k":"setBuildableRegion","player":0,"regions":["arena"]}setTechAllowed
Allow or forbid a research. Allow or forbid ONE upgrade for ONE seat, mid-match. Forbidding it greys the button out AND refuses the order at the lockstep door, so a forged command spends nothing — the rule is one shared predicate, not a check in the menu. It does NOT remove a research the seat has already completed, and it does not stop one already in the queue; it stops the next order. The lobby's own disabled-research list still applies on top: this can only ever take a research away, never hand back one the match rules removed. Allowing an upgrade that was never forbidden does nothing. Needs map format 3.
Editor: set ‹Upgrade› allowed for ‹Player› to ‹Allowed›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
upgradeId | upgradeId | yes | Upgrade id. |
allowed | bool | yes | True allows it (the default state), false forbids it. |
{"k":"setTechAllowed","player":0,"upgradeId":"dwarf_forged_weapons_1","allowed":true}setPopCap
Set population cap. Set a seat's population cap outright, or LEAVE value OUT to clear it and hand the seat back to the cap its buildings provide. It is a SEPARATE number from the building total, not a write to it: farms and town halls keep adding and removing their own capacity underneath, so destroying a farm never eats into an authored cap and clearing the cap restores exactly the right number with no recount. Clamped to 0..5000, and 0 is a real cap meaning "this seat trains nothing". A value that is not a finite number CLEARS the cap rather than writing it, so a broken formula reads as "my trigger did nothing" instead of as an unlimited army. Needs map format 3.
Editor: set ‹Player›'s population cap to ‹Cap›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
value | expr (int) | no | The cap. ABSENT clears the authored cap and returns the seat to its building-derived one. |
{"k":"setPopCap","player":0}revealArea
Reveal an area. Lift the fog on a disc of map for ONE seat, for a while. It works the way the Reveal spell works — it drops an invisible watcher that sees for you — so the ground stays EXPLORED afterwards (dimmed, remembered) even though the live vision goes away when the timer runs out, and anything standing inside it can be targeted while it lasts. Its teammates see through it too, exactly as they see through your scout. A radius of 0..64 tiles and a duration of 1..12000 ticks (10 minutes); leave the duration out for 100 ticks (5 seconds). There is no permanent reveal and no way to take vision AWAY — for a whole-map reveal, use the map's fog rule instead of a trigger. Each call costs one of the per-tick spawn budget, so a loop revealing a thousand tiles stops at the budget rather than freezing the match. Needs map format 3.
Editor: reveal ‹Radius (tiles)› tiles around ‹Centre tile› to ‹Player› for ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
at | expr (point) | yes | Centre tile. |
radius | expr (int) | yes | Radius in tiles (0..64). |
durationTicks | int (≥ 1, ≤ 12000) | no | How long the reveal lasts, in ticks (default 100 = 5 seconds, max 12000). |
{"k":"revealArea","player":0,"at":{"k":"regionCenter","region":"arena"},"radius":1}Outcome actions
victory
Victory. Declare a seat victorious.
Editor: declare victory for ‹Player›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"victory","player":0}defeat
Defeat. Declare a seat defeated.
Editor: declare defeat for ‹Player›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
{"k":"defeat","player":0}setObjective
Set a campaign objective. Complete, fail or advance ONE campaign mission objective. This is the campaign's own objective list — the one that decides whether a mission is won — and NOT set quest, which writes the trigger layer's quest log and works in every mode. Outside a campaign mission it does nothing at all, which the map validator reports. Needs map format 3.
Editor: mark campaign objective ‹Objective id› ‹Set to› by ‹Progress amount›
| Field | Type | Required | Description |
|---|---|---|---|
objectiveId | string | yes | The objective's id, as the mission defines it. |
state | 'completed' | 'failed' | 'progress' | yes | What to do to it. |
amount | expr (number) | no | Progress amount, for progress only. Absent counts as 1. Reaching the objective's target completes it. |
{"k":"setObjective","objectiveId":"text","state":"completed"}Timers actions
startTimer
Start timer. Start (or restart) a named timer.
Editor: start timer ‹Timer› for ‹Ticks› ticks (repeating: ‹Repeating›)
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
ticks | expr (int) | yes | Duration in ticks (≥ 1). |
periodic | bool | no | Restart on expiry. |
{"k":"startTimer","timer":"wave_timer","ticks":1}pauseTimer
Pause timer. Pause a timer.
Editor: pause timer ‹Timer›
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
{"k":"pauseTimer","timer":"wave_timer"}resumeTimer
Resume timer. Resume a paused timer.
Editor: resume timer ‹Timer›
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
{"k":"resumeTimer","timer":"wave_timer"}stopTimer
Stop timer. Stop and delete a timer.
Editor: stop timer ‹Timer›
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
{"k":"stopTimer","timer":"wave_timer"}timerWindowShow
Show a timer window. Put a named timer's countdown on screen with a title beside it. The window holds only the title and the audience — the clock itself is the timer, so the two can never drift apart. Showing a window for a timer nothing ever starts is a blank countdown, which the map validator reports. Needs map format 3.
Editor: show timer ‹Timer› as "‹Title›" to ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name, as start timer named it. |
title | text | yes | Title template shown beside the countdown. |
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"timerWindowShow","timer":"wave_timer","title":"Hello","players":null}timerWindowHide
Hide a timer window. Take a timer window off screen. The timer keeps running — this is presentation only, and stop timer is what stops the clock. Needs map format 3.
Editor: hide the timer window for ‹Timer›
| Field | Type | Required | Description |
|---|---|---|---|
timer | timerId | yes | Timer name. |
{"k":"timerWindowHide","timer":"wave_timer"}UI actions
showText
Show text. Show a text banner, resolved in the sim.
Editor: show ‹Text› to ‹Shown to› for ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
text | text | yes | Text template (a bare string is accepted). |
durationTicks | int (≥ 1) | no | Display time in ticks (default 100). |
{"k":"showText","players":null,"text":"Hello"}clearText
Clear text. Clear banners for these seats (null = all).
Editor: clear on-screen text for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"clearText","players":null}setCounter
Set counter. Create or update an on-screen counter.
Editor: set counter ‹Counter id› — ‹Label› — to ‹Value› for ‹Shown to› at position ‹Sort position›
| Field | Type | Required | Description |
|---|---|---|---|
id | counterId | yes | Counter id. |
label | text | yes | Label template. |
value | expr (number) | yes | Value. |
players | number[] | null | no | Seats that see this, or null/absent for everyone. |
order | int | no | Sort position. |
{"k":"setCounter","id":"lives","label":"Hello","value":1}removeCounter
Remove counter. Remove a counter.
Editor: remove counter ‹Counter id›
| Field | Type | Required | Description |
|---|---|---|---|
id | counterId | yes | Counter id. |
{"k":"removeCounter","id":"lives"}setLeaderboardTitle
Leaderboard title. Set the leaderboard title ("" hides it).
Editor: set the leaderboard title to ‹Title› for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
title | text | yes | Title template. |
players | number[] | null | no | Seats that see this, or null/absent for everyone. |
{"k":"setLeaderboardTitle","title":"Hello"}setLeaderboardRow
Leaderboard row. Create or update a leaderboard row.
Editor: set leaderboard row ‹Row id› — ‹Label› — to ‹Value› at position ‹Sort position›
| Field | Type | Required | Description |
|---|---|---|---|
rowId | rowId | yes | Row id. |
label | text | yes | Label template. |
value | expr (number) | yes | Value. |
order | int | no | Sort position. |
{"k":"setLeaderboardRow","rowId":"p1","label":"Hello","value":1}removeLeaderboardRow
Remove leaderboard row. Remove a leaderboard row.
Editor: remove leaderboard row ‹Row id›
| Field | Type | Required | Description |
|---|---|---|---|
rowId | rowId | yes | Row id. |
{"k":"removeLeaderboardRow","rowId":"p1"}clearLeaderboard
Clear leaderboard. Remove every row and the title.
Editor: clear the leaderboard
No fields.
{"k":"clearLeaderboard"}showDialog
Show dialog. Open a modal dialog; clicks fire dialogButton.
Editor: show dialog ‹Dialog id› titled ‹Title›, saying ‹Body›, with buttons ‹Buttons›, to ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
dialogId | dialogId | yes | Dialog id. |
title | text | yes | Title template. |
body | text | yes | Body template. |
buttons | textList | yes | Button labels (at least one). |
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"showDialog","dialogId":"menu","title":"Choose","body":"Pick a reward.","buttons":["Gold","Lumber"],"players":null}hideDialog
Hide dialog. Close a dialog.
Editor: hide dialog ‹Dialog id›
| Field | Type | Required | Description |
|---|---|---|---|
dialogId | dialogId | yes | Dialog id. |
{"k":"hideDialog","dialogId":"menu"}ping
Ping minimap. Ping a tile on the minimap.
Editor: ping ‹At point› for ‹Shown to› in ‹Colour› for ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
at | expr (point) | yes | Tile to ping. |
colorHex | colorHex | no | Colour (default #ffcc00). |
durationTicks | int (≥ 1) | no | Duration in ticks (default 60). |
{"k":"ping","players":null,"at":{"k":"regionCenter","region":"arena"}}playSound
Play sound. Play a sound effect (base game or map custom sound).
Editor: play sound ‹Sound› for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
soundId | soundId | yes | Sound id: any shipped sound effect, or a custom sound this map registers. The bare name only — no directory and no extension, so dwarf_ack_1, not voice/dwarf_ack_1.mp3. |
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"playSound","soundId":"explosion","players":null}createUIFrame
Create UI frame. Create or replace a declarative HUD frame.
Editor: create UI frame ‹Frame ID› at ‹Anchor› (‹X offset (px)›, ‹Y offset (px)›) size ‹Width (px)›×‹Height (px)› titled ‹Title› saying ‹Text› progress ‹Progress 0..1› in ‹Progress colour› styled ‹Style› for ‹Shown to› visible ‹Visible› clickable ‹Clickable›
| Field | Type | Required | Description |
|---|---|---|---|
frameId | frameId | yes | Frame id. |
anchor | 'top_left' | 'top_center' | 'top_right' | 'center_left' | 'center' | 'center_right' | 'bottom_left' | 'bottom_center' | 'bottom_right' | yes | Screen anchor. |
x | expr (number) | yes | X offset from the anchor (px). |
y | expr (number) | yes | Y offset from the anchor (px). |
width | expr (number) | yes | Width (px). |
height | expr (number) | yes | Height (px). |
title | text | no | Title template. |
text | text | no | Body template. |
progress | expr (real) | no | Progress bar fill 0..1. |
progressColor | colorHex | no | Progress bar colour. |
style | uiStyle | no | Visual style overrides. |
players | number[] | null | no | Seats that see this, or null/absent for everyone. |
visible | bool | no | Visible (default true). |
clickable | bool | no | Let this panel be CLICKED. Off by default, and a panel that leaves it off is invisible to the mouse — clicks, drags and wheel zoom pass straight through it to the map, exactly as every panel has always behaved. Turn it on for a panel that is a button, and a click on it fires uiFrameClicked for the seat that clicked. A clickable panel still sits UNDER the game's own HUD, so it can never swallow a click meant for a HUD control; and a click only counts if the panel is visible and shown to that seat, which the engine checks on every client. |
{"k":"createUIFrame","frameId":"hud","anchor":"top_left","x":1,"y":1,"width":1,"height":1}updateUIFrame
Update UI frame. Update fields of an existing frame.
Editor: update UI frame ‹Frame ID› title ‹Title› text ‹Text› progress ‹Progress 0..1› colour ‹Progress colour› visible ‹Visible› clickable ‹Clickable›
| Field | Type | Required | Description |
|---|---|---|---|
frameId | frameId | yes | Frame id. |
title | text | no | Title template. |
text | text | no | Body template. |
progress | expr (real) | no | Progress 0..1. |
progressColor | colorHex | no | Progress bar colour. |
visible | bool | no | Visible. |
clickable | bool | no | Arm or disarm this panel as a button. Let this panel be CLICKED. Off by default, and a panel that leaves it off is invisible to the mouse — clicks, drags and wheel zoom pass straight through it to the map, exactly as every panel has always behaved. Turn it on for a panel that is a button, and a click on it fires uiFrameClicked for the seat that clicked. A clickable panel still sits UNDER the game's own HUD, so it can never swallow a click meant for a HUD control; and a click only counts if the panel is visible and shown to that seat, which the engine checks on every client. |
{"k":"updateUIFrame","frameId":"hud"}destroyUIFrame
Destroy UI frame. Remove a frame.
Editor: destroy UI frame ‹Frame ID›
| Field | Type | Required | Description |
|---|---|---|---|
frameId | frameId | yes | Frame id. |
{"k":"destroyUIFrame","frameId":"hud"}clearUIFrames
Clear UI frames. Remove every frame.
Editor: clear all UI frames for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | no | Seats that see this, or null/absent for everyone. |
{"k":"clearUIFrames"}setQuest
Set quest. Create or update a quest-log entry.
Editor: set quest ‹Quest ID› "‹Title›" to ‹State›: ‹Description› icon ‹Icon› at position ‹Sort position›
| Field | Type | Required | Description |
|---|---|---|---|
questId | questId | yes | Quest id. |
title | text | yes | Title template. |
description | text | yes | Description template. |
state | 'active' | 'completed' | 'failed' | yes | Quest state. |
icon | string | no | Icon key. |
order | int | no | Sort position. |
{"k":"setQuest","questId":"main_quest","title":"Defend the gate","description":"Survive ten waves.","state":"active"}clearQuests
Clear quests. Remove every quest.
Editor: clear all quests
No fields.
{"k":"clearQuests"}playSoundAt
Play sound at a place. A sound effect at a tile: panned and quietened by distance, and HIDDEN BY FOG — a listener who cannot see the place does not hear it, because a sound is information. Use play sound instead for a UI cue that should always play at full volume. WARNING: the FIRST play of a sound the client has not loaded yet is SILENT — it starts the download and returns, and plays from the next request on. Only about a quarter of the sound library is preloaded, so a once-only dramatic cue may play nothing at all. Play it somewhere harmless earlier in the map, or use a sound the match has already made. Needs map format 3.
Editor: play sound ‹Sound› at ‹Where› for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
soundId | soundId | yes | Sound to play (base game or map custom sound). |
at | expr (point) | yes | Tile to play it at — a point, or a unit whose tile is used. |
players | number[] | null | yes | Seats that see this, or null for everyone. |
reachTiles | expr (number) | no | Widen the fog test by this many tiles, for something whose noise carries further than its centre. Default 0. |
{"k":"playSoundAt","soundId":"explosion","at":{"k":"regionCenter","region":"arena"},"players":null}playMusic
Play music. Start a soundtrack for these seats, replacing whatever is playing and stopping the automatic battle shuffler. Music keys are a different list from sound ids — the picker offers the shipped soundtrack. A track the client has not loaded is fetched and then played, so it starts a moment late the first time rather than not at all. Needs map format 3.
Editor: play music ‹Track› for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
musicId | musicId | yes | Soundtrack key. |
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"playMusic","musicId":"dwarf_battle_anvils","players":null}stopMusic
Stop music. Silence the soundtrack for these seats. KNOWN LIMITATION: after a play music this is exactly what it says. On its own, with the automatic battle shuffler still running, the music stops now and the shuffler starts its next track when the current one would have ended. Needs map format 3.
Editor: stop music for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"stopMusic","players":null}floatingText
Floating text. A word that floats up off a tile and fades — the same thing the game draws for "+10 gold" and "Level 3!". HIDDEN BY FOG: a viewer who cannot see the tile does not see the word, because a word floating over an enemy hero would report where the enemy hero is. Use show text for a banner that must always be read. Needs map format 3.
Editor: float ‹Text› off ‹Where› for ‹Shown to› in ‹Colour›
| Field | Type | Required | Description |
|---|---|---|---|
at | expr (point) | yes | Tile it floats off — a point, or a unit whose tile is used. |
text | text | yes | Text template (a bare string is accepted). |
players | number[] | null | yes | Seats that see this, or null for everyone. |
colorHex | colorHex | no | Colour (default #ffffff). |
{"k":"floatingText","at":{"k":"regionCenter","region":"arena"},"text":"Hello","players":null}playEffect
Play a visual effect. A spell's visual effect at a tile, with NONE of its gameplay: no damage, no mana, no cooldown, no caster. Effects are named by SPELL ID because that is the vocabulary the engine's own effect pipeline reads, so every shipped spell's look is available and a typo is an error rather than a silent nothing. Hidden by fog, like every other effect in the game. Needs map format 3.
Editor: play the ‹Effect (a spell)› effect at ‹Where› for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
spellId | spellId | yes | Spell whose visual to play. Its gameplay is NOT run — only its look. |
at | expr (point) | yes | Tile to play it at — a point, or a unit whose tile is used. |
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"playEffect","spellId":"berserk","at":{"k":"regionCenter","region":"arena"},"players":null}multiboardSet
Create or replace a multiboard. A titled grid of text — the multi-column scoreboard the leaderboard cannot be. REPLACES rather than merges: calling this again on the same board id resets its size, title, audience and every cell, so use set multiboard cell to change one number. Rows and columns past the engine grid cap are clamped, and boards past the board cap are refused. Needs map format 3.
Editor: multiboard ‹Board id› "‹Title›", ‹Rows› x ‹Columns›, for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
boardId | string | yes | Your id for this board. Re-using it replaces the board; the cell and remove actions take the same id. |
title | text | yes | Title template. Empty text hides the header. |
rows | expr (int) | yes | Rows the grid declares. |
cols | expr (int) | yes | Columns the grid declares. |
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"multiboardSet","boardId":"text","title":"Hello","rows":1,"cols":1,"players":null}multiboardCell
Set a multiboard cell. Write one cell. Rows and columns are 0-BASED. Does nothing when the board does not exist or the cell falls outside the grid it declared — both of which the map validator reports, because at runtime an unwritten cell and a cell written to a board that is not there look identical. Needs map format 3.
Editor: multiboard ‹Board id› cell ‹Row (0-based)›,‹Column (0-based)› = ‹Cell text›
| Field | Type | Required | Description |
|---|---|---|---|
boardId | string | yes | Board id, as create or replace a multiboard named it. |
row | expr (int) | yes | 0-based row. |
col | expr (int) | yes | 0-based column. |
text | text | yes | Cell text template. |
{"k":"multiboardCell","boardId":"text","row":1,"col":1,"text":"Hello"}multiboardRemove
Remove a multiboard. Take the board off screen and forget every cell in it. Removing one that is not there does nothing. Needs map format 3.
Editor: remove multiboard ‹Board id›
| Field | Type | Required | Description |
|---|---|---|---|
boardId | string | yes | Board id. |
{"k":"multiboardRemove","boardId":"text"}Cutscene actions
playCutscene
Play cutscene. Start a map scene for these seats; the sim records only the start.
Editor: play cutscene ‹Scene› for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
sceneId | sceneId | yes | Scene id. |
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"playCutscene","sceneId":"intro","players":null}stopCutscene
Stop cutscene. End running cutscenes for these seats.
Editor: stop the cutscene playing for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"stopCutscene","players":null}fadeScreen
Fade screen. Fade screen in or out with color and duration.
Editor: fade screen ‹Fade type› for ‹Shown to› in ‹Colour› over ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
type | 'in' | 'out' | yes | Fade type: in or out. |
colorHex | colorHex | no | Fade colour (default #000000). |
durationTicks | int (≥ 1) | no | Transition duration in ticks (default 20 = 1 sec). |
{"k":"fadeScreen","players":null,"type":"in"}Camera actions
cameraPan
Pan camera. Pan the camera to a point over a duration.
Editor: pan camera for ‹Shown to› to ‹Point› over ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
to | expr (point) | yes | Target tile. |
durationTicks | int (≥ 0) | no | Pan duration in ticks. |
{"k":"cameraPan","players":null,"to":{"k":"regionCenter","region":"arena"}}cameraSetZoom
Set camera zoom. Change the camera zoom.
Editor: set camera zoom for ‹Shown to› to ‹Zoom› over ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
zoom | expr (real) | yes | Zoom factor. |
durationTicks | int (≥ 0) | no | Transition in ticks. |
{"k":"cameraSetZoom","players":null,"zoom":1.5}cameraLock
Lock camera. Lock the camera to a unit or a point.
Editor: lock camera for ‹Shown to› to ‹Follow unit› at ‹Hold point›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
unit | expr (unit) | no | Unit to follow. |
at | expr (point) | no | Point to hold. |
{"k":"cameraLock","players":null,"unit":{"k":"eventUnit"}}cameraUnlock
Unlock camera. Release a camera lock.
Editor: unlock camera for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"cameraUnlock","players":null}cameraSetBounds
Camera bounds. Confine the camera to a region, or null to clear.
Editor: confine camera for ‹Shown to› to ‹Region›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
region | regionId | null | yes | Region id, or null. |
{"k":"cameraSetBounds","players":null,"region":"arena"}cameraShake
Shake camera. Shake the camera.
Editor: shake camera for ‹Shown to› at ‹Intensity (0–1)› for ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
intensity | expr (real) | yes | Shake intensity. |
durationTicks | int (≥ 0) | no | Duration in ticks. |
{"k":"cameraShake","players":null,"intensity":1.5}cameraReset
Reset camera. Clear locks, bounds and zoom overrides.
Editor: reset camera for ‹Shown to›
| Field | Type | Required | Description |
|---|---|---|---|
players | number[] | null | yes | Seats that see this, or null for everyone. |
{"k":"cameraReset","players":null}Physics actions
applyImpulse
Apply impulse. Give a unit a kinematic velocity.
Editor: apply velocity (‹Velocity X›, ‹Velocity Y›) to ‹Unit› with friction ‹Friction› bounce ‹Bounce› for ‹Duration (ticks)› ticks
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
vx | expr (real) | yes | Velocity X (tiles/tick). |
vy | expr (real) | yes | Velocity Y (tiles/tick). |
friction | expr (real) | no | Per-tick velocity multiplier. |
bounce | expr (real) | no | Restitution on collision. |
ticks | expr (int) | no | Maximum duration in ticks. |
{"k":"applyImpulse","unit":{"k":"eventUnit"},"vx":1.5,"vy":1.5}setUnitKinematics
Set kinematics. Tune kinematic parameters of a unit.
Editor: set kinematics of ‹Unit› friction ‹Friction› bounce ‹Bounce› stop below ‹Minimum speed›
| Field | Type | Required | Description |
|---|---|---|---|
unit | expr (unit) | yes | The unit (entity id). A dead or missing unit is harmless: reads give zero values, writes do nothing. |
friction | expr (real) | no | Per-tick velocity multiplier. |
bounce | expr (real) | no | Restitution on collision. |
minSpeedCutoff | expr (real) | no | Speed below which motion stops. |
{"k":"setUnitKinematics","unit":{"k":"eventUnit"}}Save codes actions
generateSaveCode
Generate save code. Encode named values into a tamper-evident code bound to this map, stored in a string variable.
Editor: generate save code for ‹Player› with ‹Data› into ‹Target variable›
| Field | Type | Required | Description |
|---|---|---|---|
player | expr (player) | yes | Seat id (0-based) of the player. |
toVar | varName | yes | An already-declared string variable to receive the code; this does not create one. |
data | exprRecord | yes | Field name → value expression. |
{"k":"generateSaveCode","player":0,"toVar":"code","data":{"level":3}}world actions
setTimeOfDay
Set Time of Day. Sets the Day/Night cycle time of day (0..1) and progression speed.
Editor: set time of day to ‹Time of day (0..1)› at speed ‹Speed multiplier›
| Field | Type | Required | Description |
|---|---|---|---|
progress | expr (any) | yes | Time of day from 0 (dawn) to 1 (next dawn). |
speed | expr (any) | no | Speed multiplier for day/night progression (0 = pause). |
{"k":"setTimeOfDay","progress":1}Chain actions
travelToZone
Travel to zone. Send the match to another ZONE: a different map, played as part of the same chain, carrying whatever the chain's carry-over list nominates (trigger variables, quests, each seat's resources, hero progress). The zone must be declared in the map's chain table. SINGLE-PLAYER ONLY — a zone hop replaces the map, and multiplayer hashes the terrain every turn, so the engine refuses the hop in a multiplayer match rather than letting it half-work. The zone must also declare respawn: true: a zone is REBUILT from its map file every time you enter it, so enemies come back and a building you destroyed is whole again. respawn: false ("remember this zone as I left it") is refused, because per-zone persistence is not built yet and quietly regenerating the zone instead would make the map lie to you. Nothing happens inside the action itself — it asks for the hop, the rest of the trigger still runs, and the match is replaced a moment later. Two gateways firing on the same tick: the first one wins. Needs map format 3.
Editor: travel to zone ‹Zone id› arriving at ‹Arrive at region›
| Field | Type | Required | Description |
|---|---|---|---|
zone | string | yes | Zone id to travel to, as declared in the map's chain table. |
entryRegion | string | no | Region in the destination to arrive at. Overrides the zone's own default; leave empty to use it. |
{"k":"travelToZone","zone":"text"}Script structure
TriggerScript
| Field | Type | Required | Description |
|---|---|---|---|
version | int (≥ 1, ≤ 1) | yes | Always 1. |
vars | varDecls | yes | Global variables, initialised in order on map init. |
regions | regionDefs | yes | Named tile rectangles (RegionDef[]). |
triggers | triggerDefs | yes | Triggers (TriggerDef[]), evaluated in array order. |
saveCodeSalt | string | no | Binds save codes to this map; defaults to the map name. |
functions | functionDefs | no | User-defined functions (FunctionDef[]). |
hashtables | hashtableDecls | no | Declared hashtables (HashtableDecl[]). |
chain | chainSpec | no | Zone chain: the maps this one can travel to, and the state that travels with the player. |
VarDecl
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Identifier: letters, digits and _, not starting with a digit or __. |
type | varType | yes | Value type. |
array | bool | no | Fixed-length array. |
arraySize | int (≥ 1) | no | Array length (globals ≤ 8192, locals ≤ 1024). |
init | expr (any) | no | Initial value (globals: at map init; locals: at each execution start). |
RegionDef
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Region id. |
x | int | yes | Top-left tile X. |
y | int | yes | Top-left tile Y. |
w | int (≥ 1) | yes | Width in tiles (≥ 1). |
h | int (≥ 1) | yes | Height in tiles (≥ 1). |
name | string | no | Editor label. |
TriggerDef
| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Stable trigger id. |
name | string | no | Editor label. |
enabled | bool | no | Initially enabled (default true). |
runOnce | bool | no | Fire at most once. |
events | events | yes | Events (any one fires the trigger). |
conditions | exprList | no | Conditions, all of which must hold. They see globals only. |
locals | varDecls | no | Per-execution local variables; survive wait and save/load. |
actions | actions | yes | Actions, run top to bottom. |
FunctionDef
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Function identifier. |
description | string | no | Documentation. |
params | params | no | Typed parameters (≤ 16, scalars only). |
returns | varType | no | Return type; absent = returns nothing. |
locals | varDecls | no | Per-call locals. |
actions | actions | yes | Body. May not contain wait or stopTrigger. |
FunctionParam
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Parameter identifier. |
type | varType | yes | Parameter type. |
description | string | no | Documentation. |
HashtableDecl
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Hashtable identifier. |
valueType | varType | yes | Type every stored value is coerced to. |
maxEntries | int (≥ 1, ≤ 16384) | no | Entry cap (≤ 16384). |
description | string | no | Documentation. |
UnitFilter
| Field | Type | Required | Description |
|---|---|---|---|
players | intList | no | Owner must be one of these seats. |
ofPlayer | expr (player) | no | Owner must equal this seat. |
alliedTo | expr (player) | no | Owner must be allied to this seat. |
enemyOf | expr (player) | no | Owner must not be allied to this seat. |
unitTypeIds | unitTypeIdList | no | Unit/building type must be one of these. |
tags | stringList | no | Authored tag must be one of these. |
aliveOnly | bool | no | Only living units (default true). |
heroesOnly | bool | no | Only heroes. |
buildingsOnly | bool | no | Only buildings. |
excludeBuildings | bool | no | No buildings. |
flyingOnly | bool | no | Only flyers. |
excludeFlying | bool | no | No flyers. |
excluding | expr (unitGroup) | no | Drop these units. |
UIFrameStyle
| Field | Type | Required | Description |
|---|---|---|---|
backgroundColor | colorHex | no | Hex colour; anything else renders the default. |
borderColor | colorHex | no | Hex colour; anything else renders the default. |
textColor | colorHex | no | Hex colour; anything else renders the default. |
fontSize | number | no | Font size (px). |
borderRadius | number | no | Corner radius (px). |
padding | number | no | Padding (px). |