Cue
A Cue is the central unit of MD (Mission Director) script. Every MD script consists of cues; everything that happens in MD β missions, events, NPC behaviour, story content β runs inside a cueβs <actions> block. Cues have state (waiting / active / complete / cancelled), can have child cues, and can be instantiated (template + N runtime instances).
If you come from OOP, think of a cue as a function with persistent state, plus a built-in event listener (the <conditions> block decides when to run).
Cue XML structure
Section titled βCue XML structureβA cue is defined in MD XML:
<cue name="MyCue" instantiate="true" version="1"> <conditions> <!-- When to trigger --> <event_object_destroyed/> </conditions>
<delay min="2s" max="5s"/>
<actions> <!-- What to do when triggered --> <write_to_logbook text="'Cue fired'"/> </actions>
<cues> <cue name="ChildCue"> <!-- Child cue, scoped to parent --> </cue> </cues></cue>The XML attributes are static; the runtime accessors are listed below.
Cue attributes (XML-side)
Section titled βCue attributes (XML-side)β| Attribute | Purpose |
|---|---|
name="X" | Cue identifier within its parent / script |
instantiate="true/false" | Template-or-instance toggle (see Instantiation) |
namespace="X" | Override the namespace for child variables |
version="N" | Cue version (for save migration) |
ref="path.to.library" | Cue is a library reference |
library="true" | Cue acts as a library template |
Runtime properties
Section titled βRuntime propertiesβFrom vanilla scriptproperties.xml:2194.
Identity
Section titled βIdentityβ| Property | Type | Description |
|---|---|---|
.exists | bool | Cue exists |
.name | string | Cue name (unqualified) |
.version | int | Version (from XML) |
Lifecycle state
Section titled βLifecycle stateβ| Property | Type | Description |
|---|---|---|
.state | cuestate | Current state (active, complete, etc.) |
.time | time | Time of cue activation / last action block |
Hierarchy
Section titled βHierarchyβ| Property | Type | Description |
|---|---|---|
.parent | cue | Parent cue (null for root) |
.static | cue | The instantiating cue (null if this is not an instance) |
.staticbase | cue | Static base used as instantiation template |
.namespace | cue | Namespace cue (where variables live) |
.library | cue | Base library cue if this is a library reference |
.isinstance | bool | Is this a runtime instance (vs template) |
Mission / objective
Section titled βMission / objectiveβ| Property | Type | Description |
|---|---|---|
.hasmissionoffer | bool | Has a mission offer |
.hasmission | bool | Has an active mission |
.hasguidance | bool | Guidance arc is active |
.offerlocations | list | Mission-offer component slots |
.canactivatesubmission.{cue} | bool | A submission can be activated |
.missiontype | missiontype | Type of mission |
.missionendtime | time | Mission end time (null if open-ended) |
.objective | objective | Current objective |
.objectiveendtime | time | Objective end time |
| Property | Type | Description |
|---|---|---|
.actor | nonplayer | Last associated actor |
.actors | list | All associated actors |
Variables
Section titled βVariablesβ| Property | Type | Description |
|---|---|---|
.$<variablename> | various | Value of a cue variable |
$variables are how MD stores per-cue state. They persist across action blocks and survive save/load.
Instantiation
Section titled βInstantiationβA cue with instantiate="true" is a template. Each time the conditions match, a new instance is created β .isinstance=true, .static points back to the template. Instance variables ($variables) are independent per-instance.
A cue with instantiate="false" (or default) is a singleton β runs at most once. Subsequent condition firings are ignored.
The vanilla canonical βlistener cueβ:
<cue name="WatchDestroys" instantiate="true"> <conditions> <event_object_destroyed group="$WatchedGroup"/> </conditions> <actions> <!-- Fires once per destruction --> </actions></cue>instantiate="true" is required if you want the cue to react more than once.
Cue lifecycle
Section titled βCue lifecycleβ βββββββββββββββββββ β Not yet β β activated β β XML defined but conditions never met ββββββββββ¬ββββββββββ β (conditions match) βββββββββββββββββββ β active β β running <actions> ββββββββββ¬ββββββββββ β (actions complete OR delay/timer) βββββββββββββββββββ β waiting β β waiting for sub-conditions OR delay ββββββββββ¬ββββββββββ β (next event / signal_cue / reset_cue) βββββββββββββββββββ β complete / β β cancelled β β terminal βββββββββββββββββββThe cuestate enum values cover all transitions. Use .state to read.
Common patterns
Section titled βCommon patternsββBridge + Workerβ race-avoidance pattern
Section titled ββBridge + Workerβ race-avoidance patternβFor listeners that must do entity writes (add_inventory, destroy_object), vanilla uses a split:
<!-- Bridge β instantiated, signals work --><cue name="Bridge" instantiate="true"> <conditions> <event_object_destroyed group="$X"/> <check_value value="not $busy"/> </conditions> <actions> <set_value name="$busy" exact="true"/> <signal_cue cue="Worker" param="event.object"/> </actions></cue>
<!-- Worker β NOT instantiated, does writes --><cue name="Worker" instantiate="false"> <conditions> <event_cue_signalled cue="Bridge"/> </conditions> <actions> <!-- entity writes here --> <add_inventory ware="..." entity="..."/>
<reset_cue cue="this"/> </actions></cue>
<!-- ReleaseBusy β clears the lock --><cue name="ReleaseBusy" delay="2s"> <actions> <set_value name="$busy" exact="false"/> </actions></cue>Pattern reason: instantiate="true" cues silently drop entity-write actions (see Common gotchas). The bridge runs first (allowed to instantiate), signals the worker, worker does the real work in a non-instantiated context.
Library reference
Section titled βLibrary referenceβ<cue name="UseLib" ref="md.LIB_Generic.TransferShipOwnership"> <param name="Ship" value="$capturedShip"/> <param name="NewOwner" value="faction.player"/></cue>The ref= attribute makes the cue a library reference. The actions execute as if inlined.
Namespace
Section titled βNamespaceβ<cue name="OuterCue" namespace="this"> <actions> <set_value name="this.$shared" exact="42"/> </actions>
<cues> <cue name="Child"> <actions> <!-- Can read this.$shared from outer cue --> <write_to_logbook text="this.$shared"/> </actions> </cue> </cues></cue>namespace="this" makes the cue itself the namespace for this.$var lookups by its children.
Common gotchas
Section titled βCommon gotchasβ- β
instantiate="true"silently drops entity writes (add_inventory,destroy_object). This is the most-bitten MD gotcha. Use the Bridge + Worker pattern above (mlog_bmb/mlog_frsmod canonical fix). - β
signal_cue param="X"silently drops the param (the queued form). Onlysignal_cue_instantlypropagatesparam=. Queued signal_cue deliversevent.param == null. Vanilla never uses the queued + param combo. - β
event_cue_signalled cue="X"attribute means βX RECEIVED a signalβ, not βX SENTβ. Confusing β use no-attr form for canonical InitβWorker pattern, else the listener silently never fires. - β Non-instantiated workers fire ONCE then stay completed. Subsequent signals β βno corresponding listenersβ warning. Append
<reset_cue cue="this"/>to actions to repeatedly fire. - β
<library>must be INSIDE<cues>as a sibling of<cue>, not between<mdscript>and<cues>. Top-level libraries βrun_actions ref=silently returns empty. - β
<return/>only works in libraries. In regular cue actions you get βScript node βreturnβ is not allowed in this contextβ. Usedo_elsewrap +reset_cuefor early-exit. - β
event_object_destroyed group="$X"watcher MUST be NESTED inside the group-creating cue. Top-level group filters silently never fire. Vanillasetup.xml:41/591pattern. - β
event_X group=$Xrequires the group to be set up in a no-conditions cue. Conditions-having setup cues fire too late; engine errors at time 0.00. - β Table keys must be
$-prefixedstrings.$tbl.{'key'}silently fails; must be$tbl.{'$key'}or$tbl.$key. - β Save migration via
version=. Older saves rerun cues with newversion=automatically. Use this for breaking changes.
Examples
Section titled βExamplesβExample 1: Watch a list of ships and react to destruction
Section titled βExample 1: Watch a list of ships and react to destructionβ<cue name="SetupAndWatch" instantiate="false"> <conditions> <event_cue_completed cue="md.GameStart"/> </conditions> <actions> <create_group groupname="global.$Watched"/> <find_ship_by_true_owner groupname="global.$Watched" space="player.galaxy" faction="faction.argon" multiple="true"/> </actions>
<cues> <cue name="OnDestroy" instantiate="true"> <conditions> <event_object_destroyed group="global.$Watched"/> </conditions> <actions> <write_to_logbook text="'Argon ship lost: ' + event.object.knownname"/> </actions> </cue> </cues></cue>Example 2: Heartbeat cue with reset
Section titled βExample 2: Heartbeat cue with resetβ<cue name="Heartbeat" instantiate="false"> <delay exact="60s"/> <actions> <!-- Do periodic work --> <write_to_logbook text="'Tick'"/>
<reset_cue cue="this"/> </actions></cue>Without reset_cue, fires once at 60s and stays complete. With it, fires every 60s forever.
Example 3: Mission cue with offer
Section titled βExample 3: Mission cue with offerβ<cue name="RescueMission" instantiate="true"> <conditions> <event_player_arrived sector="$TargetSector"/> </conditions> <actions> <set_value name="$missionid" exact="'rescue_001'"/> <create_offer cue="this" faction="faction.argon"/> </actions></cue>Mission cues set .hasmissionoffer=true; the player sees the offer in UI; accepting transitions to .hasmission=true.
Architectural context
Section titled βArchitectural contextβ- MD script engine: Architectural overview MD framework β cue evaluation, condition matching, action dispatch.
- Save/load lifecycle: Architectural overview Cue persistence β how
version=drives migration,instantiate=instances survive saves. - Listener race conditions: Architectural overview Listener race patterns β Bridge+Worker, ReleaseBusy, signal_cue idioms.
- Mission framework: Architectural overview Mission cues β how cues with
.hasmissionintegrate with the offer/accept/complete UX.
Related
Section titled βRelatedβ- Library β cue with
purpose="run_actions"for shared logic. - Action β what goes inside
<actions>. - Condition β what goes inside
<conditions>. - Expression β
value="..."expression syntax. - MD Framework overview β broader context.
- Cross-tree: Order β game-side βship is doing Xβ backed by aiscript, not cue.