# Mission scripting with blocks Missions of editor campaigns are scripted with **blocks** (like Blockly): coloured puzzle pieces put together in the block editor (see [CAMPAIGN_EDITOR.md](CAMPAIGN_EDITOR.md)). There is no code: the game runs the blocks directly, inside its fixed simulation step, so a co-op match runs them identically on every computer. Code: `src/blocks.rs` (blocks, values, checking), `src/mscript.rs` (run time, triggers, zones), `src/campaign_doc.rs` (`MissionScripts`, `TriggerDoc`, `TriggerKind`). ## Scripts of a mission | Script | When it runs | |---|---| | Start | once, when the mission begins | | Victory check | every second – use *If ... Win the mission* | | Defeat check | every second – use *If ... Lose the mission* | | Clock | every second | | Triggers | whenever their condition is met (see below) | The normal goals of the mission still apply; scripts are for anything more complicated. Any script may call *Win* or *Lose*. ## Triggers Each trigger has a **name**, a **kind** with its condition, **once** (fires only the first time; switched on again by a *Switch trigger* block it may fire once more) and **active** (switched on at the start). Conditions are checked ten times a second. | Kind | Condition | The "trigger unit" | |---|---|---| | Zone entered | a unit of a side/kind comes into a zone | that unit | | Zone left | a unit of a side/kind leaves a zone | that unit | | Line crossed | a unit crosses a line zone (from one side to the other, within its ends) | that unit | | Zone claimed | the player claims more tiles in a zone | – | | Dug in the zone | fewer solid tiles in a zone (dug, caved in) | – | | Units in the zone | the count of a side/kind in a zone becomes `< <= = != >= >` n | – | | Timer | after n seconds, or every n seconds (*repeat*) | – | | A unit falls | a unit of a side/kind dies (or the unit with this name, see *Spawn*) | the fallen unit (its place) | | A tower falls | a tower of the dungeon / the knights / anyone is destroyed | – | | A value is reached | gold, mana, creatures, heroes or a variable becomes `op n` | – | | Room built | a room of a kind (or any) grows | – | | Spell cast | a spell (or any) is cast | – | | Creature slapped | the player slaps one of his creatures | – | | Research done | the library finishes a discovery | – | "Becomes" means the trigger fires when the condition turns true, not every check while it stays true. Sides: player, heroes, Mossgrim, Lady Nightwell, rival 3, anyone. Kinds: any, or one of the 22 creatures and heroes. ## Blocks | Category | Block | Fields | |---|---|---| | Speak | Message | text – `{name}` shows the value of variable `name` | | | Objective | text (shown as the mission's objective) | | | Crookfinger says | text (with a sound) | | Units | Spawn | count, kind, level, side, where (the core, the trigger unit, a zone), optional name | | | Hero wave | count (sent at once) | | | Remove | side, zone (removes those units) | | World | Change ground | zone → floor, earth, rock, water, lava, gold (the core, portals, towers and the gate stay) | | | Reveal | zone (lifts the fog of war) | | | Show | zone (moves the camera there) | | | Gold + / Mana + | amount (negative takes) | | Logic | If / else | condition; blocks inside | | | Repeat | times (0–100); blocks inside | | | Repeat until | condition, checked **before** each run (already true: the inside never runs); blocks inside | | | Repeat, then check: until | condition, checked **after** each run (the inside runs at least once); blocks inside | | | Wait | seconds – the script pauses here and goes on with the next block later (also inside loops and "if") | | | Switch trigger | trigger, on/off | | Variables | Set | variable, value | | | Change | variable, value to add | | Mission | Win the mission / Lose the mission | – | ### Values Every number field holds a **value**; the switch in front of it changes the value's kind: - numbers: *Number*, *Variable*, *Gold*, *Mana*, *Seconds played*, *Count* (creatures of a side/kind), *Count* in a zone, *Random* (whole number between a and b, deterministic), *Calculation* (`+ - * /`, division by 0 gives 0), - conditions (for *If*): *Comparison* (`< <= = != >= >`), *and*, *or*, *not*, *Yes/no*, *Trigger unit is a* (kind). Variables are numbers the mission remembers (saved with the game); a variable is known once some script of the mission sets it. ### Checking The editor checks while you build and marks problems red: empty texts, unknown zones, triggers or creature kinds, variables never set, a condition where a number belongs (or the other way round). A trigger whose zone is missing is marked in the trigger list. Problems are also listed in the campaign file check (`CampaignDoc::validate`). ## At run time (`src/mscript.rs`) - Every run of a script (the start, each second's checks, each fired trigger) is a **thread**: the lists of blocks it is in and where. *Wait* parks a thread until its time; threads are saved with the game, so a waiting script goes on after loading. - The victory check, defeat check and clock start every second, but not while their previous run is still waiting. A trigger that fires again while its last run waits starts a second run. - Loops without a *Wait* run up to 2000 blocks per check (ten checks a second) and then go on at the next check – a script can never hang the game. - Blocks act at once, in order: a *Count* after a *Spawn* already sees the new units. Variables, the random state, fired triggers, zone contents and waiting threads are part of `ScriptState`. - Scripts always run from the first player's side; messages reach every player of a match. - Test play shows the variables and the trigger log at the top right, and the zones on the map. ## The demo *Open the demo* in the campaign editor: mission 1 ("The Trial of Triggers") uses all 14 trigger kinds, all special scripts and every block (including both kinds of *until* loops and *Wait*) on a painted map with five zones (rectangle, circle, line, tiles); mission 2 is a random map behind a branch. Play it with *Test play* and watch the debug view.