Files
dungeoncraft/docs/MISSION_SCRIPTING.md
T
2026-11-05 17:16:27 +01:00

6.1 KiB
Raw Blame History

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 of ScriptState and 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.