Skip to content

Missions AI and Scripting

LordBlacksun edited this page Sep 9, 2026 · 1 revision

Missions, AI & Scripting (.DTE)

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.

.DTE file container

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↔.dte numbering, the variant/unused files) is in Campaign Mission Flow.

Trigger conditions — the TT_* set [verified]

FUN_0045B330 holds the name array, indexed by value: 33 scriptable conditions, 0x000x20 (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.

Trigger record (0x30 bytes) [verified]

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_005267C0FUN_0045CEA0 tests type + operands → fire-list DAT_0052ABE0 (stride 0x30, cap 1000) → action script. Proximity (5/6) is polled in FUN_0045AF60.

Scripting VM [verified]

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.

Outcomes [verified]

Outcome grade DAT_0052A428 selects from five tiers, 04: 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).

Lifecycle & AI [verified]

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).

Credit

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.

Clone this wiki locally