-
Notifications
You must be signed in to change notification settings - Fork 0
Missions AI and Scripting
Missions are data-driven .DTE files interpreted by an in-engine trigger/event VM. The
complete opcode / command / AI tables are on the
.DTE Scripting Reference page; this page covers the file format,
trigger system, lifecycle, and how the two reverse-engineering sources reconcile. All [verified]
unless noted.
This documentation fuses our static decompilation (addresses, struct layouts, the VM, per-command implementations) with the black-box research of Captain Foster / "Starlancer ME" (https://starlancerme.blogspot.com/, @CaptainFoster) — the numeric opcode/command/AI/ship tables and observed in-game semantics. Wherever the two overlap, they agree.
The HOG-stored file is RefPack / EA-"QFS" compressed (signature 10 FB, 3-byte big-endian
uncompressed size — the HOG itself is EA's BIGF format). FUN_0045A300 obtains the bytes (a loose
missions\%s.dte if present, else the HOG resource — a straight read, 0x1A EOF byte on loose
reads) and the RefPack stream decompresses into a fixed image (the ≤ 0xFA000 buffer is sized
for it). FUN_00451D90 then resolves 27 sections in a fixed order via FUN_00452A20 — each an
8-byte descriptor { u16 count, relocation-flag bits @ 24-27, u32 offset } read from offset 0 of
the decompressed image and fixed up to a live pointer (0xFFFF = empty section).
Path format ..\missions\%s.dte (FUN_0044F3D0 is only the mission-select menu).
Verified section roles/strides:
| Section | Pointer | Stride |
|---|---|---|
| ship / flight-group array |
DAT_0052951C (count DAT_00529504) |
0x4C |
| globals / variables | DAT_005294F8 |
0x0C (u16 name-idx, u32 value) |
| objectives / operands | DAT_005267CC |
0x14 |
| trigger records | DAT_005294E0 |
0x30 |
| script bytecode base | DAT_00525F88 |
— |
| per-ship trigger index | DAT_005267C0 |
0x08 (+1 count, +2 u16 first-index) |
| per-bytecode-byte flag map | DAT_005294D8 |
1 |
[resolved] Decompressed, the 27×8 directory validates cleanly, and our section offsets match
Starlancer ME's black-box anchors to the byte — ships 0x30FF7, FG/objectives 0x3A7F7,
triggers 0x3BBF7, script 0x47BF7, target list 0x57BF7. The earlier "our copies differ /
offsets past EOF / inline name strings at 0xB6" puzzle was only the compression: our extracted
blob is the packed form, the blog worked from the expanded one — the two efforts now agree
completely. tools/dte_parse.py decodes all 44 missions (decode / sweep): a clean-room RefPack
decompressor + the directory + ship/objective/trigger records + a script disassembler.
Campaign‑level view: how these missions are ordered, numbered, and branched (the progression rule, the in‑game↔
.dtenumbering, the variant/unused files) is in Campaign Mission Flow.
FUN_0045B330 holds the name array, indexed by value: 33 scriptable conditions, 0x00–0x20
(TT_SHOTAT=0 … TT_BEING_CHASED=0x20) — identical to Starlancer ME's Triggers page. (The
condition-descriptor table DAT_0052952C has 35 slots; the 2 past 0x20 are internal.) Full list
on the Scripting Reference.
| Offset | Field |
|---|---|
+0x00 |
subject ship / flight-group ref |
+0x01 |
repeat mode (0 one-shot, 2 repeat-N via +0x19) |
+0x02 |
u16 linked action index (0xFFFF = none) |
+0x14 |
enabled flag |
+0x15 |
TT_* condition type |
+0x16 |
action id → spawns the script thread (FUN_0045B8D0) |
+0x1C… |
operand array (stride 4) |
Dispatch: event → look up subject's trigger list via DAT_005267C0 → FUN_0045CEA0 tests
type + operands → fire-list DAT_0052ABE0 (stride 0x30, cap 1000) → action script. Proximity
(5/6) is polled in FUN_0045AF60.
A single bytecode stream (base DAT_00525F88) run by the interpreter FUN_0045C980:
fetch one opcode byte → index the 256-entry handler table DAT_004F6350 → advance IP → call
handler, until one returns 0. Script blocks are u16-length-prefixed; the per-byte flag array
DAT_005294D8 marks yield points. Two registers of meaning share the stream — commands
(0x21 <index> → the catalogue DAT_004F3AD0, ~90 of 96 with verified implementation address +
parameter count) and micro-ops (compare / immediate / global read-write, the if/else
machinery). This resolves the previous open question of how the action opcodes were
enumerated: see the Scripting Reference for the full tables.
Outcome grade DAT_0052A428 selects from five tiers, 0–4: Failure, Partial Failure,
Partial Success, Success, Success + Bonus. A mission defaults to "Failure" and is
promoted by the script's win/lose flag (opcode 0x40 writes on the kill condition; 0x27 reads
at mission end).
| Addr | Role |
|---|---|
0x0045A4E0 / 0x0045A530 / 0x0045A570
|
mission init / destroy / process (per-frame). Guards DAT_005373E8 (active), DAT_005373E4 (fire count). |
0x00486830 |
objectives ("ERROR: No mission Objectives def…"); SetObjective states 0/1/2. |
0x0048E140 |
dumps live mission state to c:\mission.txt (syncs, globals, ships). |
0x004924B0 |
mission-exit poll; manages outcome grade DAT_0052A428. |
0x00401000 |
multiplayer AI sync gate — per-ship bitmask entity + 0x710 + idx; proceeds only when all players are synced. Driven by MultiplayerScriptSync. |
0x0040D210 |
capital-ship / turret / ion-cannon AI (ship types 0x44/0x48/0xA5). |
The numeric trigger/command/AI/ship/pilot tables and observed runtime semantics are the work of
Captain Foster / Starlancer ME (https://starlancerme.blogspot.com/,
@CaptainFoster, email withheld), who also
maintains a Python "Mission Ship Editor". This page pairs that with our static decompilation.