6.1 KiB
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). Players never see code – internally the blocks are translated into Rhai, compiled once per mission and run inside the game's fixed simulation step, so a co-op match runs them identically on every computer.
Code: src/blocks.rs (blocks, values, checking, translation), 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 | |
| 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)
- The mission's program is generated by
mscript::program– one Rhai function per script and trigger (s_start,s_victory,s_defeat,s_tick,t_0,t_1, ...) – and compiled once. - Scripts read the game (
m.gold(),m.count(side, kind),m.in_zone(zone, side, kind), ...) and collect actions (m.message,m.spawn_unit,m.terrain,m.win, ...), which are applied after the call. Variables, the random state, fired triggers and zone contents are part ofScriptStateand saved with the game. - Scripts always run from the first player's side; messages reach every player of a match.
- A script error stops nothing else: it is shown once as a message and written to the test log.
- Test play shows the variables and the trigger log at the top right, and the zones on the map.
Rhai API (internal)
gold() mana() time() get(name) set(name, v) count(side, kind) in_zone(zone, side, kind) random(a, b) div(a, b) times(n) event_is(kind) message(t) objective(t) hint(t) spawn_unit(side, kind, count, level, place, zone, name) wave(n) add_gold(n) add_mana(n) win() lose() reveal(zone) camera(zone) terrain(zone, kind) kill(zone, side) trigger(name, on) – numbers are
floats; spawn is a reserved word in Rhai, hence spawn_unit. The Rhai dependency keeps the
no_time feature (WASM).
The demo
Open the demo in the campaign editor: mission 1 ("The Trial of Triggers") uses all 14 trigger kinds, all special scripts and nearly every block 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.