NPC orders
How does an Argon transport know to fly to a station, dock, sell its cargo, and return for more? Itβs not driven by MD missions β those are for missions. NPC ship behaviour runs through aiscript orders: declarative XML files in aiscripts/order.*.xml that define what a ship does and what it reacts to.
This overview explains how 81 vanilla order files compose into the runtime behaviour the player sees.
For the runtime data side (cancel orders, query state) see Order. For the language schema see Order definition.
Vanilla scope
Section titled βVanilla scopeβaiscripts/βββ order.attack.xmlβββ order.assist.xmlβββ order.build.deploy.xmlβββ order.collect.ship.xmlβββ order.flee.xmlβββ order.mining.routine.xmlβββ order.patrol.xmlβββ order.protect.position.xmlβββ order.wait.xmlβββ ... 72 moreβββ interrupt.attacked.xmlβββ interrupt.scanned.xmlβββ ... more interruptsβββ lib.fleet.organize.defences.xmlβββ lib.find.sectors.inrange.xmlβββ ... lib helpersβββ orders.base.xml β shared lifecycle code- 81 order files (
order.X.xml) - 15 interrupt files (
interrupt.X.xml) β see Interrupt - Many lib files β shared utilities (
lib.X.xml) - orders.base.xml (394 lines) β base lifecycle code referenced by all orders
The lifecycle pattern
Section titled βThe lifecycle patternβ create_order (MD or engine) β ββββββββββββββββββββββββββββββββββββ β Order queue β β [active] β [next] β ... β [end] β ββββββββββββββββββββββββββββββββββββ β Active order's <actions> running β ββββββββββββββββββββββββββββββββββββ β Interrupts evaluated each tick β β (per-order subscription list) β ββββββββββββββββββββββββββββββββββββ β ββββββββββββββββββββββββββββββββββββ β - Order completes naturally β β β next order activates β β - Order interrupted β β β handler runs, order may end β β - Order cancelled β β β cleanup, next runs β ββββββββββββββββββββββββββββββββββββ β Queue empty β fallback to .defaultorder (or idle if no default)orders.base.xml β shared lifecycle
Section titled βorders.base.xml β shared lifecycleβMost orders share lifecycle code via orders.base.xml. It handles:
- Setup at order start β register the order with the entity, claim resources, etc.
- Cleanup at order end β release resources, reset state
- Player vs NPC dispatch β different UI / voice for player ships
- Order parameter normalisation β defaults, type coercion
- Skipwait logic β when a new override order arrives, base detects via
this.$skipwait
Custom orders include base via:
<include_actions ref="orders.base"/>Without this, your custom order is missing standard X4 niceties.
Order anatomy (the schema)
Section titled βOrder anatomy (the schema)βEvery aiscript order file has:
<aiscript>βββ <order id="X" .../>β βββ <params> ...β βββ <requires> ...βββ <actions> β main executionβββ <on_abort> β cancellation cleanupβββ <on_finish> β natural completionβββ <attention min="...">β attention-level branchingβββ <interrupts> β reactive handlers βββ <handler ref="AttackHandler"/> βββ <handler ref="..."/></aiscript>See Order definition for the schema details.
Default order vs queued orders
Section titled βDefault order vs queued ordersβA ship has:
.defaultorderβ the fallback that fires when queue is empty.ordersβ queue of explicitly scheduled orders.orderβ currently active (front of queue, or default if queue empty)
Typical patterns:
| Pattern | What runs |
|---|---|
Queue is empty, default = 'Wait' | Ship waits in place |
Queue is empty, default = 'Patrol' | Ship patrols the sector |
Queue is empty, default = 'ProtectPosition' | Ship guards a position |
Queue has 'Attack' queued | Ship attacks first; queue advances |
The vanilla canonical pattern for βset this shipβs default behaviourβ:
<cancel_all_orders object="$ship"/><create_order id="'ProtectPosition'" object="$ship" default="true"> <param name="destination" value="[$sector, $position]"/></create_order>cancel_all_orders clears queue + default; create_order default="true" sets the new default. See Order β Cancel and swap orders.
Interrupt integration
Section titled βInterrupt integrationβEach order subscribes to interrupts in its <interrupts> block:
<interrupts> <handler ref="AttackHandler"/> <handler ref="MissileLockHandler"/> <handler ref="ScannedHandler"/> <handler ref="InspectedHandler"/></interrupts>When any of these events fires during the order, the handler runs and may:
- Modify order state (set
$flag) - Force the order to end (set
this.$skipwait) - Issue a new override order
- Just log without acting (
consume="false")
See Interrupt for the handler reference.
Attention integration
Section titled βAttention integrationβAiscript orders use <attention> blocks to skip expensive logic when the player isnβt watching:
<actions> <wait min="30s" max="120s"/> <!-- runs at all attentions --></actions>
<attention min="insector"> <actions> <!-- detailed movements only when player in sector --> <move_to ... /> </actions></attention>See Attention.
Player vs NPC paths
Section titled βPlayer vs NPC pathsβMost order files split player-ship vs NPC-ship handling:
<do_if value="this.isplayerowned"> <!-- player UI prompts, voice lines, camera framing --></do_if><do_else> <!-- silent NPC execution --></do_else>Player paths are heavier β UI feedback, voice lines, sometimes auto-engage prompts. NPC paths just run the math. This is partially why orders.base.xml is 394 lines.
MD β Aiscript coupling
Section titled βMD β Aiscript couplingβMD missions / cues create orders, but the actual behaviour runs in aiscript:
MD cue Aiscript orderββββββββββ βββββββββββββ<create_order order.protect.position.xml id="'ProtectPosition'" reads $destination object="$ship"> runs <actions> block <param subscribes to interrupts name="destination" value="..."/></create_order>Mission cues observe outcomes via signal_objects emitted from the aiscriptβs <on_finish> block:
<!-- inside aiscript --><on_finish> <signal_objects object="this.$mission_cue" param="'order_done'"/></on_finish><!-- MD cue listens --><cue name="WatchOrderDone" instantiate="true"> <conditions> <event_object_signalled object="this.$mission_cue" param="'order_done'"/> </conditions> ...</cue>This is the canonical mission β ship-behaviour bridge.
Why this matters for modders
Section titled βWhy this matters for moddersβCustom orders
Section titled βCustom ordersβAdding a new order requires:
- New
aiscripts/order.X.xmlfile (see Order definition) - Test load β engine errors at load if XML invalid
- Reference order id from MD with
<create_order id="'YourId'"/>
Custom interrupt handlers
Section titled βCustom interrupt handlersβAdding new interrupt handlers (see Interrupt) lets you create reusable reaction logic that multiple orders can import.
Vanilla file lookup
Section titled βVanilla file lookupβTo understand βwhat does the AI do when Xβ, grep aiscripts/order.X.xml. The 81 files cover most NPC behaviour; the lib files cover shared utilities.
Performance
Section titled βPerformanceβOrders run continuously while active. Interrupts evaluate each tick. Mods that add expensive logic to a popular interrupt (or a popular order) impact CPU for every NPC ship that uses it.
Cross-references
Section titled βCross-referencesβ- Order (game) β runtime data + queue management
- Order definition (lang) β XML schema
- Interrupt (lang) β handler library
- Attention (lang) β fidelity branching
- Ship β what executes orders
Related architectural overviews
Section titled βRelated architectural overviewsβ- Patrol coordination β how patrol orders integrate with galaxy combat bus
- Boarding β boarding operations use specialised orders
- Fleet reconstitution β replacing destroyed ships uses build orders