Preface: “Descent’s models were x86 assembler”

“Fun fact: Descent didn’t use 3D file formats per se. Instead, 3D models were compiled as x86 assembler doing draw commands.”@praeclarum, 2025

It’s a great observation, and there’s a real kernel of truth to it — though the full story is even more interesting. The tweet doesn’t specify which Descent it’s referring to, and the answer changes quite a bit depending on which generation you look at.

What Descent 1 & 2 actually did (1994-1996). The original Descent stored 3D models in POF (Parallax Object Format) files as a stream of custom bytecode opcodes — not native x86 machine code, but something close in spirit. Each model was essentially a small program written in a 9-opcode domain-specific language:

OpcodeValueAction
OP_EOF0End of model data
OP_DEFPOINTS1Define vertex list (rotate into view space)
OP_FLATPOLY2Draw a flat-shaded polygon
OP_TMAPPOLY3Draw a texture-mapped polygon
OP_SORTNORM4BSP-style front/back sort by normal facing
OP_RODBM5Draw a cylindrical rod bitmap
OP_SUBCALL6Call into a subobject (with offset + animation angles)
OP_DEFP_START7Define points with explicit starting index
OP_GLOW8Set glow value for next polygon

This bytecode was interpreted at runtime by a hand-written x86 assembly dispatcher (3D/INTERP.ASM in the Descent 1 source). The ebp register served as the program counter, a next macro read the 16-bit opcode and jumped through a dispatch table, and each handler advanced ebp past its operand data before calling next again. For morphing models (robot death animations), Descent even hot-patched the dispatch table at runtime to swap in alternative handlers — polymorphism at the assembly level.

So the models really were bytecode programs, not data files — @praeclarum is onto something genuinely cool there. The nuance is that the bytecode wasn’t native x86 machine code, but a custom interpreted format. It’s easy to see how the two get conflated, especially since “compiled sprites” — a genuine 90s technique (used in Allegro, Jazz Jackrabbit, and others) where 2D sprite pixels were literally emitted as x86 MOV instructions — were a real and well-documented thing from the same era.

What Descent 3 did (1999). Descent 3, whose source code this document covers, completely abandoned the bytecode interpreter model. The vestigial ID_IDTA (“Interpreter Data”) chunk ID is still defined at polymodel.cpp:632:

#define ID_IDTA 'ATDI'           // Interpreter data

But no code ever reads it. Descent 3 replaced the interpreter with what the codebase calls “new style” models — conventional vertex/face arrays stored in OOF/POF chunks and rendered through a standard algorithmic pipeline:

RenderPolygonModel()
  └─ RenderSubmodel()          // per-subobject transform + lighting
       └─ RenderSubmodelFace() // iterate faces, setup UVs, call GPU
            └─ rend_DrawPolygon3D() // submit to OpenGL

The ASSERT(pm->new_style == 1) guard at polymodel.cpp:1276 enforces this — legacy interpreter-format models cannot even be loaded into the Descent 3 renderer.

In summary: the tweet points at something genuinely fascinating about Descent’s heritage. The fine print is that the models were custom bytecode rather than native x86, and that Descent 3 moved on to traditional geometry data structures — so the technique was really a Descent 1 & 2 era thing. But the core insight — that these models were closer to programs than to data files — is spot on, and it’s a wonderful example of 90s engine ingenuity.


1. Executive Summary

1.1 Document Purpose and Scope

This document provides a comprehensive architectural reference for the three foundational runtime subsystems of the Descent3 game engine:

SubsystemTypeEraStatus
D3XRegister-based bytecode VM1997-98Legacy (ISA preserved, interpreter lost in open-source release)
OSIRISNative DLL/SO module systemDec 1998+Production (active scripting system)
RendererOpenGL abstraction layer1997-presentActive (modernized to GLSL #version 150 core)

1.2 Historical Context: D3X to OSIRIS Evolution

The D3X bytecode VM was the original scripting system designed by Samir (Parallax Software) in mid-1997. It provided a custom register machine with 52 opcodes, vector-float registers, and a dual-stack execution model. Scripts were compiled to .d3x bytecode files tagged "D3X5".

In December 1998, Jeff replaced D3X with OSIRIS (Object-oriented Scripting for Interactive Routines, Ingame Scenarios). OSIRIS moved from interpreted bytecode to compiled native DLLs/SOs, eliminating interpretation overhead entirely. The event model concepts from D3X — events, object references (ME, IT), and script types (object/trigger/level) — were carried forward into OSIRIS.

The Dallas visual scripting tool generated C++ source code that compiled into OSIRIS DLL modules, using a library of 300+ predefined action (a*) and query (q*) functions.

1.3 System Relationship Overview


2. D3X Bytecode Virtual Machine (Legacy)

Status: The D3X ISA is fully preserved in lib/d3x_op.h. The interpreter implementation was not included in the open-source release. This section documents the architecture as designed.

2.1 Instruction Set Architecture

2.1.1 Register File

D3X uses a register-based architecture with 32 total registers divided into two banks:

BankRegistersCountPurpose
VF (Vector-Float)VF0-VF7 (+ 8 extended)16General-purpose: integers, floats, and 3D vectors
AD (Address)AD0, AD12Indirect addressing; AD0 typically accesses local stack variables
(Reserved)14Padding to MAX_D3X_REGS = 32
Register Map (32 slots):
┌──────────────────────────────┬──────┬──────┬────────────────┐
│ VF0-VF7 + VF8-VF15 (16 reg)  │ AD0  │ AD1  │ Reserved (14)  │
│       VFREG_START = 0        │  16  │  17  │    18 .. 31    │
│       General Purpose        │ Addr │ Addr │    (unused)    │
└──────────────────────────────┴──────┴──────┴────────────────┘

2.1.2 Opcode Catalog

D3X defines 52 opcodes across 7 categories:

Complete Opcode Table:

ValueMnemonicEncodingDescription
0BREAKDebug breakpoint
1LOAD_ABSrirx ← mem[ABSOLUTE]
2LOAD_ADIaiirx ← mem[ADx + IMM]
3LOAD_IMMrirx ← IMM (immediate int or float)
4LOAD_PARMrirx ← parameter[IMM]
5STORE_ABSrimem[ABSOLUTE] ← rx
6STORE_ADIaiimem[ADx + IMM] ← rx
7STORE_PARMriparameter[IMM] ← rx
10ADDrrrd ← rd + rs
11SUBrrrd ← rd - rs
12MULrrrd ← rd * rs
13DIVrrrd ← rd / rs
14MODrrrd ← rd % rs
15ANDrrrd ← rd && rs (logical)
16ORrrrd ← rd || rs (logical)
17NEGrrrd ← -rd
18NOTrrrd ← !rd
19ABSrrrd ← abs(rd)
20EQUrrrd ← (rd == rs)
21NEQrrrd ← (rd != rs)
22LTrrrd ← (rd < rs)
23LTErrrd ← (rd <= rs)
24GTrrrd ← (rd > rs)
25GTErrrd ← (rd >= rs)
26BORrrrd ← rd | rs (bitwise)
27BANDrrrd ← rd & rs (bitwise)
28VEXrrrd ← vs.x (extract X from vector)
29VEYrrrd ← vs.y (extract Y from vector)
30VEZrrrd ← vs.z (extract Z from vector)
31XEVrrvd.x ← rs (set X of vector)
32YEVrrvd.y ← rs (set Y of vector)
33ZEVrrvd.z ← rs (set Z of vector)
34ADDIrird ← rd + IMM
35BANDIrird ← rd & IMM
36BORIrird ← rd | IMM
40EPUSHrrPush register to execution buffer
41EPOPrrPop from execution buffer to register
42PCALLraCall predefined external function
43CALLraCall script subroutine at ABSOLUTE address
44CPUSHrrPush register to call stack
45CPOPrrPop from call stack to register
46TOCSPrrTransfer ADx to call stack pointer
47FROMCSPrrTransfer call stack pointer to ADx
48RETReturn from CALL (pops address from call stack)
49DEFERriDefer execution: 0=end, 1=default handler
50JUMP_ABSraUnconditional jump to ABSOLUTE address
51JUMP_NCONDraJump to ABSOLUTE if register == 0

2.1.3 Instruction Encoding

Each instruction is encoded as a tD3XInstruction struct with a 1-byte opcode followed by a union of 4 operand formats:

FormatFieldsSizeUsed by
rid (reg), imm (int|float)6 bytesLOAD_IMM, LOAD_ABS, STORE_ABS, LOAD_PARM, STORE_PARM, ADDI, BANDI, BORI, DEFER
aiid (dest reg), a (addr reg), imm (uint16 offset)5 bytesLOAD_ADI, STORE_ADI
raabs (uint16 address), r (reg, 0xFF=unused)4 bytesJUMP_ABS, JUMP_NCOND, CALL, PCALL
rrd (dest), s (src)3 bytesAll arithmetic, comparison, vector, stack ops

2.2 Program Structure

2.2.1 File Format

D3X script files are identified by the tag "D3X5". Each file contains:

  • A header with the D3X tag
  • A program map (tD3XPMap entries) indexing named scripts
  • The bytecode instruction stream

2.2.2 Program Map Entry (tD3XPMap)

struct tD3XPMap {
    char name[32];     // Script name (MAX_D3XID_NAME)
    uint16_t ip;       // Entry point: instruction pointer
    uint16_t mem;      // Memory required by this script
    uint16_t type;     // Script binding type
    uint16_t parms;    // Number of parameters
};

Script Types:

ValueConstantDescription
0REF_OBJTYPEBound to a specific game object (robot, door, powerup, etc.); receives events when that object is interacted with
1REF_TRIGTYPEBound to a trigger volume; fires when a player or object enters the designated area in the level
2REF_LEVELTYPELevel-wide script that handles global events (level start/end, goals, cinematics) not tied to any single object
3REF_GAMEMODEGame mode script that controls multiplayer rule logic (scoring, team assignment, win conditions) for a specific mode

Parameter Types:

ValueConstantDescription
0PARMTYPE_NUMBERInteger or float
1PARMTYPE_VECTOR3D vector (x, y, z)
2PARMTYPE_REFObject reference handle
3PARMTYPE_STRREFString reference

2.3 Execution Model

2.3.1 Dual-Stack Architecture

D3X uses two independent stacks:

StackPush/PopPurpose
Execution BufferEPUSH/EPOPPassing arguments to external function calls (PCALL)
Call StackCPUSH/CPOPReturn addresses, local variables, subroutine frames

The TOCSP and FROMCSP instructions transfer the address register (ADx) to/from the call stack pointer, enabling frame pointer manipulation for local variable access.

2.3.2 Event Arguments

When a script is invoked, the call stack is pre-populated with standard arguments at fixed offsets:

OffsetConstantDescription
0SCRARG_EVENTEvent type being handled (e.g., collision, damage, timer — identifies why this script was invoked)
1SCRARG_MEHandle of “ME” — the object this script is attached to (the robot, door, or powerup that owns this script)
2SCRARG_ITTYPEType code of “IT” — the other object involved in the interaction (player, weapon, another robot, etc.)
3SCRARG_ITHandle of “IT” — the interacting object (e.g., the player who collided with ME, or the weapon that damaged ME)
4-11EVTARG_*8 event-specific argument slots (carry extra data like damage amount, room number, or timer ID depending on event type)
12+SCRSTACK_STARTStart of script-defined local variables (script’s own working memory, allocated per the mem field in the program map)

2.3.3 Control Flow

  • CALL/RET: Standard subroutine mechanism. CALL pushes the return address onto the call stack; RET pops it.
  • PCALL: Calls a predefined engine function by index. Arguments are passed via the execution buffer (EPUSH before PCALL, results EPOP after).
  • DEFER: Terminates script execution. DEFER_END (0) stops completely; DEFER_DEFAULT (1) defers to the default event handler.
  • JUMP_NCOND: Conditional branch — jumps to the absolute address if the specified register contains zero.

3. OSIRIS Module System (Modern Scripting)

3.1 Architecture Overview

OSIRIS replaced D3X with compiled native code modules. Scripts are written in C++, compiled to platform-specific shared libraries (.dll/.so/.dylib), and loaded at runtime.

Key source files:

3.1.1 Module Loading

The module struct in module/module.h provides cross-platform dynamic loading:

mod_LoadModule(module*, filename, flags)  → Load DLL/SO
mod_GetSymbol(module*, name, parmbytes)   → Resolve function pointer
mod_FreeModule(module*)                   → Unload

Loading Flags:

FlagValueDescription
MODF_LAZYResolve function pointers on demand — only look up each symbol when it is first called, not at load time
MODF_NOWResolve all function pointers immediately at load time; fails fast if any expected symbol is missing
MODF_GLOBALMake this module’s symbols visible to other modules, allowing cross-module function calls

3.1.2 Module Slot Management

OSIRIS manages up to 64 simultaneously loaded modules (MAX_LOADED_MODULES). Each slot tracks:

  • Reference count (multiple objects may share a module)
  • Module flags: OSIMF_INUSE, OSIMF_LEVEL, OSIMF_DLLELSEWHERE, OSIMF_INTEMPDIR, OSIMF_NOUNLOAD
  • Three dedicated slots: Level module, Game module, Mission module

3.1.3 Module Types

TypeLoaderLifecycle
LevelOsiris_LoadLevelModule()Loaded when a level begins, unloaded when it ends; contains scripts for that level’s rooms, triggers, puzzles, and cinematics
GameOsiris_LoadGameModule()Persists across the entire game session; provides shared logic like the default object behaviors and the GuideBot AI companion
MissionOsiris_LoadMissionModule()Custom per-mission scripts for add-on content; allows third-party missions to define unique gameplay mechanics and event responses

3.2 Module Interface Contract

Every OSIRIS module must export 9 functions:

ExportSignaturePurpose
InitializeDLL(tOSIRISModuleInit*) → charCalled once at load: receives the engine’s 256-slot function pointer table so the script can call back into the engine
ShutdownDLL() → voidCalled when the module is unloaded; frees any resources the script allocated during its lifetime
GetGOScriptID(name, is_door) → intGiven a game object’s name (e.g., “TubeWindA”), returns the script ID that should handle its events, or -1 if unrecognized
CreateInstance(id) → void*Allocates per-object memory for a script instance (e.g., tracking an individual robot’s custom state like patrol waypoints)
DestroyInstance(id, ptr) → voidFrees the per-object instance memory when the object is removed from the game world
CallInstanceEvent(id, ptr, event, data) → int16_tMain event handler: receives gameplay events (collision, damage, timer, etc.) and returns flags controlling chain behavior
GetTriggerScriptID(name) → intGiven a trigger volume’s name, returns the script ID that should handle enter/exit events for that trigger
GetCOScriptList(scripts, params) → intEnumerates all custom object scripts this module provides, so the engine knows which objects this module can handle
SaveRestoreState(file, saving) → intSerializes or deserializes the module’s global state to/from a save-game file, preserving progress across save/load cycles

3.2.1 Initialization: tOSIRISModuleInit

struct tOSIRISModuleInit {
    int32_t *fp[256];          // MAX_MODULEFUNCS function pointers
    char **string_table;       // Localized strings
    int32_t string_count;
    int32_t module_identifier;
    bool module_is_static;     // Don't unload when refcount=0
    char *script_identifier;   // Unique ID for OMMS
    uint32_t game_checksum;    // Struct compatibility check
};

The game_checksum is computed by multiplying struct sizes (sizeof(object), sizeof(player), etc.) with prime numbers. Modules compare this against their compile-time value to detect ABI mismatches.

3.3 Event System

3.3.1 Event Taxonomy

OSIRIS defines 35+ event types in osiris_common.h:

CategoryEventsWhen These Fire
LifecycleEVT_CREATED (0x104), EVT_DESTROY (0x105)When an object is first spawned into the world, or when it is about to be removed (killed, exploded, or cleaned up)
Per-FrameEVT_INTERVAL (0x100), EVT_AI_FRAME (0x101)Every game frame — used for continuous behaviors like monitoring conditions, updating custom animations, or polling object state
InteractionEVT_DAMAGED (0x102), EVT_COLLIDE (0x103), EVT_USE (0x107)When an object takes damage from a weapon, physically collides with another object, or is activated/used by the player (e.g., a switch)
AI NotificationsEVT_AI_NOTIFY (0x110), EVT_AI_INIT (0x111), plus 11 child events: EVT_AIN_OBJKILLED, EVT_AIN_SEEPLAYER, EVT_AIN_GOALCOMPLETE, EVT_AIN_MELEE_HIT, EVT_AIN_MOVIE_START, etc.When AI-controlled robots detect players, complete navigation goals, land melee attacks, have their targets killed, or enter cinematics
TimerEVT_TIMER (0x106), EVT_TIMERCANCEL (0x11A)When a script-created countdown expires (for delayed actions like timed doors or staged explosions), or when a timer is cancelled early
LevelEVT_CHANGESEG (0x115), EVT_LEVELSTART, EVT_LEVELENDWhen an object moves from one room to another, or at level load/unload — used for room-based triggers and level initialization/teardown
PersistenceEVT_SAVESTATE (0x117), EVT_RESTORESTATE (0x118), EVT_MEMRESTORE (0x119)During save/load game — scripts serialize their custom state (puzzle progress, counters, flags) so gameplay resumes correctly after load
DoorsEVT_DOOR_ACTIVATE (0x125), EVT_DOOR_CLOSE (0x126)When a door begins opening (activated by player, key, or script) or finishes closing — used for locked-door puzzles and ambush triggers
GoalsEVT_LEVEL_GOAL_COMPLETE (0x128), EVT_ALL_LEVEL_GOALS_COMPLETE (0x129), EVT_LEVEL_GOAL_ITEM_COMPLETE (0x12A)When the player completes mission objectives — used to trigger cinematics, unlock new areas, or advance the mission narrative
PlayerEVT_PLAYER_MOVIE_START/END (0x12B/C), EVT_PLAYER_RESPAWN (0x12D), EVT_PLAYER_DIES (0x12E)When a cinematic sequence begins/ends, when a player respawns after death (multiplayer), or when a player is killed
MatcenEVT_MATCEN_CREATE (0x124)When a matcen (material center, i.e., enemy spawning facility) produces a new robot or object into the world
MiscEVT_CHILD_DIED (0x127)When an object that was spawned by or attached to this object is destroyed — lets parents react to children being killed

Each event carries its data through the tOSIRISEventInfo union, which contains a type-specific struct plus the common me_handle and extra_info fields.

3.3.2 Event Chain Dispatch

Return flags:

FlagValueMeaning
CONTINUE_CHAIN0x0100Pass the event to the next script in the chain (custom → level → mission → default); if omitted, no further scripts see this event
CONTINUE_DEFAULT0x0001Allow the engine’s built-in behavior to run (e.g., apply damage, play death animation); if omitted, the script fully overrides the default action

3.3.3 Event Masking

Events can be selectively enabled/disabled by category:

#define OEM_OBJECTS  0x01  // Object events
#define OEM_TRIGGERS 0x02  // Trigger events
#define OEM_LEVELS   0x04  // Level events

Osiris_EnableEvents(OEM_OBJECTS | OEM_TRIGGERS);
Osiris_DisableEvents(OEM_LEVELS);

Create events can be suppressed during game loading/demo playback via Osiris_DisableCreateEvents().

3.4 Dallas Action/Query System

The Dallas visual scripting tool generates C++ code that calls predefined engine functions. These are declared in scripts/DallasFuncs.h and organized by category:

Action Functions (a* prefix)

CategoryDescriptionKey Functions
Portal/RoomControl room-level environment effects: forcefields at doorways, fog/wind atmosphere, hazard damage, wall textures, lightingaPortalRenderSet, aPortalBreakGlass, aRoomSetFog, aRoomChangeFog, aRoomSetWind, aRoomChangeWind, aRoomSetDamage, aRoomSetFaceTexture, aRoomSetLightingStrobe/Flicker/Pulse, aRoomSetFuelcen, aRoomFogSetState
ObjectsManipulate individual game objects: set health/energy, apply damage, kill or hide objects, play animations, toggle visibility (ghosting makes objects intangible), apply visual effects like sparking or mesh deformationaObjSetShields, aObjSetEnergy, aObjApplyDamage, aObjKill, aObjDestroy, aObjPlayAnim, aObjGhostSet, aObjHide, aObjMakeInvuln, aObjDeform, aObjSpark, aObjSetVelocity, aObjGravityEnable, aObjSetLightingDist/Color, aObjSetMovementType, aObjFireWeapon, aObjSaveHandle
AIControl robot behavior: set awareness state, field of vision, targets, team allegiance, navigation goals (go to room, follow path, pick up object), and movement speedaAISetState, aAISetFOV, aAISetTarget, aAISetTeam, aAISetMode, aAISetMaxSpeed, aAIFlags, aAIGoalGotoRoom, aAIGoalGotoObject, aAIGoalFollowPath/Simple, aAIGoalPickUpObject, aAIClearGoal, aAIGoalSetCircleDistance
SoundPlay audio effects: 2D UI sounds, positional 3D sounds attached to objects, streaming music/voiceover, volume controlaSoundPlay2D, aSoundPlay2DObj, aSoundPlayObject, aSoundPlaySteaming/Obj, aSoundVolumeObj, aSoundStopObj
WeatherToggle environmental weather effects visible to the player: rain, snow, and lightning storms with configurable bolt effectsaRainTurnOn/Off, aSnowTurnOn/Off, aLightningTurnOn/Off, aLightningCreate, aLightningCreateGunpoints
CinematicsTrigger in-game cutscenes: camera paths, text overlays, letterbox framing, and multi-track sequences with timed camera cutsaCinematicSimple, aCinematicIntro, aCinematicStop, aComplexCinematicStart/End/Text/Track/CameraOnPath/CameraAtPoint/ScreenMode
DoorsControl door state: lock/unlock (requiring keys), force open/close, set position along its travel, or stop mid-movementaDoorLockUnlock, aDoorActivate, aDoorSetPos, aDoorStop
InventoryAdd or remove items from a player’s inventory (powerups, keys, quest items)aAddObjectToInventory/Named, aRemoveObjectFromInventory
TimersSchedule delayed actions: create countdowns on objects or the level itself, cancel pending timers, display timer on HUDaSetObjectTimer, aSetLevelTimer, aCancelTimer, aTimerShow
Level FlowControl mission progression: end the level (success or failure), trigger the exit flythrough sequence, fade to white on exitaEndLevel, aFailLevel, aStartEndlevelSequence/Path, aFadeWhiteAndEndlevel
PlayerModify player capabilities: disable/enable movement controls, grant keys, toggle invisibility (cloaking), strip all weapons and energy back to the basic laseraTogglePlayerObjControl/AllControls, aObjectPlayerGiveKey, aCloakObject, aUnCloakObject, aCloakAllPlayers, aStripWeaponsEnergy
HUD/MessagesDisplay text to the player: on-screen messages, colored alerts, persistent game messages, and waypoint markers (HUD arrows guiding the player to objectives)aShowHUDMessage/Obj, aShowColoredHUDMessage/Obj, aAddGameMessage, aSetWaypoint
GoalsManage mission objectives: mark goals as completed/failed, enable or disable goals dynamically, set display priority and completion textaGoalCompleted, aGoalFailed, aGoalEnableDisable, aGoalSetPriority, aGoalSetCompletionMessage
MiscMiscellaneous: set script-local flags/variables for tracking puzzle state, change background music region, enable custom death sequences, physically attach objects together, emit continuous particle streams (spew), apply screen shake, control matcen (enemy spawner) production rates, toggle available shipsaUserFlagSet, aUserVarSet/Inc/Dec/Add/Sub, aMusicSetRegion/All, aSetScriptedDeath, aAttachObject/Existing, aUnAttachObject, aTurnOnSpew/Off, aMiscViewerShake, aMiscShakeArea, aMatcenSetState/EnableState/Values, aTriggerSetState, aEnableShip/DisableShip

Query Functions (q* prefix)

CategoryDescriptionKey Functions
Object StateCheck properties of any game object: whether it exists/is alive, its health/energy, current room, distance to other objects, line-of-sight visibility, and what type of object it is (player, robot, powerup, etc.)qObjExists, qObjIsPlayer, qObjIsPlayerWeapon, qObjIsPlayerOrPlayerWeapon, qObjIsType, qObjShields, qObjEnergy, qObjShieldsOriginal, qObjRoom, qObjOnTerrain, qObjType, qObjParent, qObjAnimFrame, qObjGetDistance, qObjGetLightingDist, qObjDamage, qObjCanSeeObj/Advanced, qObjCanSeePlayer/Advanced
User VariablesRead back script-local state: retrieve the current value of numbered variables or boolean flags used to track puzzle/level progressqUserVarValue, qUserVarValueInt, qUserFlag
Door/PortalCheck door and portal status: whether a door is locked, can be opened, its current position along its travel, or whether a portal’s forcefield is activeqDoorLocked, qDoorOpenable, qDoorGetPos, qPortalIsOn
RoomQuery room environment state: whether fog is active, whether any player is currently inside, and the room’s hazard damage rateqRoomFogOn, qRoomHasPlayer, qRoomGetDamage
AIQuery robot AI state: proximity to current target, whether the robot is aware of threats, what it’s currently targeting, and its speed limitqAICloseToTarget, qAIIsObjectAware, qAIGetTarget, qAIQueryMaxSpeed
InventoryCheck whether a specific item is in a player’s inventory (used for key-gate puzzles and item-dependent progression)qHasObjectInInventory
GoalsCheck mission objective status: whether all primary goals are done, whether a specific goal is enabled, completed, or failedqGoalPrimariesComplete, qGoalEnabled, qGoalCompleted, qGoalFailed
MathArithmetic helpers for Dallas visual scripts (which lack native math operators): add, subtract, multiply, convert types, compute percentagesqMathAddFloat, qMathSubFloat, qMathMulFloat, qMathIntToFloat, qMathAddPercent, qMathSubPercent, qMathAddInt, qMathSubInt, qMathPercentage
MiscGeneral-purpose queries: random number generation, find nearest player, get frame delta time, check difficulty setting, count objects of a type, check ship availability, read trigger state, check if player is cloaked (invisible)qRandomChance, qRandomValue, qPlayerClosest, qFrametime, qGetDifficulty, qObjCountTypeID, qIsShipEnabled, qTriggerGetState, qVirusInfected, qNegativeLight, qObjectCloakTime, qObjectPosition

User State: Scripts maintain up to 25 user variables (MAX_USER_VARS) via the user_var variant type (float or int32), plus boolean flags and 50 spew handles (MAX_SPEW_HANDLES), and 20 saved object handles.

3.5 Memory Management

OSIRIS provides three tiers of memory management:

3.5.1 Auto-Save Memory

Scripts allocate memory via Osiris_AllocateMemory(tOSIRISMEMCHUNK*). The engine automatically saves this memory to disk on game save and fires EVT_MEMRESTORE with the restored pointer on load.

struct tOSIRISMEMCHUNK {
    tOSIRISSCRIPTID my_id;  // Script identity (type + handle)
    uint16_t id;             // Chunk identifier
    int32_t size;            // Allocation size
};

3.5.2 OMMS (Osiris Mission Memory System)

For cross-script shared state, OMMS provides reference-counted global memory:

Osiris_InitOMMS()                    -- Initialize at mission start
OMMS_Malloc(size, unique_id)         -- Allocate shared block
OMMS_Attach(handle) / OMMS_Detach() -- Increment/decrement refcount
OMMS_Find(unique_id)                -- Look up by unique ID
OMMS_Free(handle)                   -- Mark for deletion

Memory is only freed when both OMMS_Free has been called AND the reference count reaches zero.

3.5.3 Script State Persistence

Scripts handle EVT_SAVESTATE and EVT_RESTORESTATE to manually serialize custom data through CFILE read/write functions (CFReadInt, CFWriteString, etc.).

3.6 Timer System

struct tOSIRISTIMER { // https://github.com/DescentDevelopers/Descent3/blob/main/scripts/osiris_common.h#L1174
    uint16_t flags;                     // OTF_REPEATER, OTF_TRIGGER, OTF_LEVEL, OTF_CANCELONDEAD
    int32_t id;                         // User-defined ID (passed back in EVT_TIMER)
    int32_t repeat_count;               // -1 for infinite repeat
    union {
        int32_t object_handle;          // Recipient of EVT_TIMER
        int32_t trigger_number;         // If OTF_TRIGGER set
    };
    int32_t object_handle_detonator;    // If OTF_CANCELONDEAD: auto-cancel if this object dies
    float timer_interval;               // Seconds between signals
};

Osiris_ProcessTimers() is called every frame, scanning active timers and firing EVT_TIMER when intervals elapse. EVT_TIMERCANCEL is sent when a timer is cancelled (either explicitly or by detonator object death).

3.7 Multiplayer Safety Layer

Network-synchronized operations go through the MSafe system, which ensures all clients execute the same game state changes.

Architecture: MSafe_CallFunction(type, msafe_struct*) dispatches one of 100+ operation types defined by MSAFE_* constants. The msafe_struct is a large universal parameter block containing fields for rooms, objects, weather, sound, doors, triggers, inventory, and more.

Categories (from osiris_common.h):

BaseCategoryOperations
0RoomChange wall textures, set wind direction/speed, configure fog color/density, set pulsing/strobing/flickering lights, apply environmental hazard damage, toggle portal forcefields, enable refueling (shield/energy restore), break glass
20ObjectSet health (shields) and energy, change light emission color, scale movement/recharge/weapon speeds, apply damage, grant weapons, emit particle streams (spew), make intangible (ghost/unghost), reposition, set velocity, toggle invisibility (cloak), fire weapons, add spark effects, ignite on fire
100WeatherToggle rain, snow, and lightning storm effects; create individual lightning bolts between points
110SoundPlay non-positional (2D) audio, play positional audio attached to objects, play streaming audio (voiceover/music), stop sounds, adjust volume
130Matcen(reserved for matcen/enemy spawner operations — not yet implemented)
150MiscDisplay HUD messages, set waypoint navigation markers, trigger level end, show popup camera views, post game messages, change background music region, enable/disable selectable ships, update mission goals, rename the GuideBot companion, manage timers, control HUD item visibility
170DoorLock or unlock doors, force doors open, set door position along travel, stop door movement, query whether a door can be opened
190TriggerEnable or disable trigger volumes (the invisible regions that fire events when entered)
200InventoryAdd items to player inventory by type or ID, check for specific items, count held items, remove items, query inventory capacity, add by direct object handle
210CountermeasureManage player countermeasures (deployable decoys that distract homing missiles): add, count, check availability, remove, query capacity
250WeaponCheck whether a player has a specific weapon, grant new weapons to a player

3.8 Netgame DLL System

Multiplayer game modes (Anarchy, CTF, Coop, Entropy, Hoard, MonsterBall, RoboAnarchy, Team Anarchy) are implemented as separate DLLs using the same OSIRIS module interface. The engine passes a game_api structure containing:

  • Data pointers to engine arrays (Objects, Rooms, Players)
  • ~450 function pointers for engine callbacks
  • ~50 variable pointers
  • The standard tOSIRISModuleInit for OSIRIS integration

4. Renderer Abstraction Layer

4.1 Architecture Overview

The renderer provides a hardware-agnostic API through ~50 rend_* functions declared in lib/renderer.h. The current implementation targets OpenGL 3.2 core profile via renderer/HardwareOpenGL.cpp.

Backend enumeration (renderer_type):

enum renderer_type {
    RENDERER_OPENGL   = 2,   // Active
    RENDERER_DIRECT3D = 3,   // Legacy (in legacy/renderer/)
    RENDERER_GLIDE    = 4,   // Unused
    RENDERER_NONE     = 5,   // Stub/dedicated server
};

Key source files:

FilePurpose
lib/renderer.hPublic API: all rend_* function declarations
renderer/HardwareInternal.hInternal types: PosColorUVVertex, PosColorUV2Vertex, color_array, tex_array
renderer/HardwareOpenGL.cppOpenGL backend: init, texture cache, GPU render functions
renderer/HardwareBaseGPU.cppShared GPU logic: state machine, DeterminePointColor, rend_DrawPolygon2D/3D
renderer/HardwareDraw.cpp3D library: g3_DrawPoly, g3_DrawLine, g3_DrawBitmap
renderer/ShaderProgram.hModern GLSL infrastructure: ShaderProgram<V>, OrphaningVertexBuffer
renderer/shaders/vertex.glslVertex shader: MVP transform
renderer/shaders/fragment.glslFragment shader: dual-texture, fog, gamma

4.2 State Machine

The renderer maintains global state via the rendering_state struct:

struct rendering_state {
    int8_t initted;
    int8_t cur_bilinear_state;     // Texture filtering on/off
    int8_t cur_zbuffer_state;      // Z-buffer on/off
    texture_type cur_texture_type; // TT_FLAT through TT_PERSPECTIVE_SPECIAL
    color_model cur_color_model;   // CM_MONO or CM_RGB
    light_state cur_light_state;   // LS_NONE/GOURAUD/PHONG/FLAT_GOURAUD
    int8_t cur_alpha_type;         // AT_ALWAYS through AT_LIGHTMAP_BLEND_SATURATE
    wrap_type cur_wrap_type;       // WT_WRAP, WT_CLAMP, WT_WRAP_V
    int cur_alpha;                 // Constant alpha value (0-255)
    ddgr_color cur_color;          // Flat color for LS_FLAT_GOURAUD
    int8_t cur_texture_quality;    // 0=none, 1=linear, 2=perspective
    int clip_x1, clip_x2, clip_y1, clip_y2;  // Viewport clip region
    int screen_width, screen_height;
};

Alpha blending modes (15 total, defined in lib/renderer.h):

ValueConstantFormulaVisual Effect
0AT_ALWAYSAlpha = 1.0 (fully opaque)No transparency; surface is completely solid and opaque
1AT_CONSTANTAlpha = cur_alpha / 255Uniform transparency across the entire surface, like tinted glass; used for fading UI and movie overlays
2AT_TEXTUREAlpha from texture alpha channelPer-pixel transparency from the texture image; creates detailed silhouettes with smooth edges
3AT_CONSTANT_TEXTUREtexture_alpha * constantTexture transparency modulated by a global fade; used for fading decals, HUD gauges, scorch marks
4AT_VERTEXAlpha from per-vertex p3_aSmooth transparency gradients across a polygon; used for smoke trails fading from opaque to transparent
5AT_CONSTANT_VERTEXvertex_alpha * constantVertex gradients with overall fade control; used for HUD elements with per-corner fading
6AT_TEXTURE_VERTEXtexture_alpha * vertex_alphaTextured transparency with per-vertex gradients; for complex particle shapes that fade at edges
7AT_CONSTANT_TEXTURE_VERTEXtexture * constant * vertexMaximum flexibility: texture detail + vertex gradients + overall fade; for cinematic particle effects
8AT_LIGHTMAP_BLENDdest * src (multiplicative)Darkens surfaces by multiplying colors; applies baked lighting and shadow maps to walls and terrain
9AT_SATURATE_TEXTURESaturate to whiteAdditive glow that brightens toward white; used for explosions, weapon fire, energy auras, light halos
12AT_SATURATE_VERTEXSaturate with vertex alphaAdditive glow with smooth vertex gradients; for expanding blast rings that fade at their edges
13AT_SATURATE_CONSTANT_VERTEXConstant * vertex saturationAdditive glow with vertex gradients and overall brightness control; for colored light rings
14AT_SATURATE_TEXTURE_VERTEXTexture * vertex saturationAdditive glow preserving texture detail with vertex fading; for textured blast waves and energy effects
32AT_SPECULARSpecular blendingShiny reflective highlights on glossy materials (polished metal, plastic); bright spots where light hits
33AT_LIGHTMAP_BLEND_SATURATEAdditive lightmap blendBrightens surfaces additively; used for specular highlight maps and intense light reflections on walls

4.3 Rendering Pipeline

Frame lifecycle:

  1. rend_StartFrame(x1, y1, x2, y2, clear_flags) — Set viewport, optionally clear Z-buffer
  2. Game issues draw calls (g3_DrawPoly, rend_DrawScaledBitmap, etc.)
  3. rend_EndFrame() — Finalize frame
  4. rend_Flip() — Swap front/back buffers (SDL3 SDL_GL_SwapWindow)

Color determination (DeterminePointColor):

alpha = gpu_Alpha_multiplier * gpu_Alpha_factor   // see HardwareBaseGPU.cpp:63
if (ATF_VERTEX flag set): alpha *= pnt->p3_a

if (LS_FLAT_GOURAUD):     color = cur_color (flat)
else if (LS_NONE):        color = white (1,1,1)
else if (CM_MONO):        color = (p3_l, p3_l, p3_l)  // monochromatic intensity
else:                     color = (p3_r, p3_g, p3_b)   // per-vertex RGB

4.4 Shader System

4.4.1 GLSL Shaders (OpenGL 3.2 Core, #version 150)

Vertex Shader (renderer/shaders/vertex.glsl):

in vec3 in_pos;
in vec4 in_color;
in vec2 in_uv0;
in vec2 in_uv1;

uniform mat4 u_modelview;
uniform mat4 u_projection;

void main() {
    vertex_modelview_pos = u_modelview * vec4(in_pos, 1);
    gl_Position = u_projection * vertex_modelview_pos;
    // Pass through: vertex_color, vertex_uv0, vertex_uv1
}

Fragment Shader (renderer/shaders/fragment.glsl):

uniform sampler2D u_texture0, u_texture1;
uniform int u_texture_enable;      // Bitfield: bit 0 = tex0, bit 1 = tex1
uniform bool u_fog_enable;
uniform vec4 u_fog_color;
uniform float u_fog_start, u_fog_end;
uniform float u_gamma;

void main() {
    // Dual-texture multiply (branchless disable via max with inverted enable)
    out_color = vertex_color
        * max(texture(u_texture0, vertex_uv0), vec4(float(!bool(u_texture_enable & 1))))
        * max(texture(u_texture1, vertex_uv1), vec4(float(!bool(u_texture_enable & 2))));

    // Distance-based fog
    float fog_factor = clamp((fog_end - length(modelview_pos)) / (fog_end - fog_start), 0, 1);
    out_color = out_color * fog_factor + (1 - fog_factor) * u_fog_color;

    // Gamma correction
    out_color.rgb = pow(out_color.rgb, vec3(1.0 / u_gamma));
}

Key design: Texture enable/disable is branchless — disabled textures sample vec4(1) via max() with an inverted enable flag, avoiding GPU branching overhead.

4.4.2 ShaderProgram<V> Template

template <typename V>
struct ShaderProgram {
    ShaderProgram(vertex_src, fragment_src, attribs);
    void Use() / Unuse();                    // Bind/unbind program
    size_t addVertexData(begin, end);        // Stream vertices to VBO
    void setUniformMat4f(name, matrix);      // Set 4x4 matrix uniform
    void setUniform1i/1f/4fv(name, value);   // Set scalar/vector uniforms
private:
    OrphaningVertexBuffer<V> vbo_;           // Streaming vertex buffer
    unordered_map<string, GLint> uniform_cache_;  // Cached uniform locations
};

4.5 Vertex Buffer Management

4.5.1 Vertex Formats (defined in HardwareInternal.h)

struct PosColorUVVertex {        // Single-texture polygon
    vector pos;                  // 3D position (12 bytes)
    color_array color;           // RGBA float (16 bytes)
    tex_array uv;                // UV + r,w (16 bytes)
};                               // Total: 44 bytes/vertex

struct PosColorUV2Vertex {       // Multi-texture polygon (base + lightmap)
    vector pos;                  // 3D position
    color_array color;           // RGBA float
    tex_array uv0;               // Primary texture coords
    tex_array uv1;               // Lightmap texture coords
};                               // Total: 60 bytes/vertex

4.5.2 OrphaningVertexBuffer

The OrphaningVertexBuffer<V> implements the buffer orphaning streaming pattern:

Ring Buffer (kVertexCount = 64K vertices = 1 << 16):   // see ShaderProgram.h:140
┌──────────────────────────────────────────────────────┐
│ ████████████ used │ ← next write ─── free space ───→ │
└──────────────────────────────────────────────────────┘
                     ↑ nextVertex_

When nextVertex_ + newCount >= 64K:
  → glBufferData(GL_ARRAY_BUFFER, size, nullptr, GL_STREAM_DRAW)  // Orphan
  → nextVertex_ = 0  // Wrap to start
  • GL_STREAM_DRAW usage hint for frequently-updated data
  • glMapBufferRange with GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT for zero-copy streaming
  • Buffer orphaning avoids GPU stalls by discarding the old buffer when it fills up

4.6 Texture Cache System

Tracking arrays (defined in HardwareOpenGL.cpp):

Lightmap UV scaling: Lightmap texture coordinates are scaled by width / square_res and height / square_res to account for power-of-two padding.

4.7 Platform Integration

  • SDL3: Window creation, GL context management, input handling, buffer swap
  • OpenGL 3.2 Core Profile: SDL_GL_CONTEXT_MAJOR_VERSION = 3, SDL_GL_CONTEXT_MINOR_VERSION = 2
  • FBO Resolution Scaling: Renders to an offscreen FBO at the configured resolution, then blits to the window at display resolution
  • Dynamic GL Loading: dyna_gl.h wraps all GL function pointers (loaded via SDL3’s SDL_GL_GetProcAddress), enabling runtime GL library selection via -gllibrary command-line flag

5. Cross-Cutting: Memory Architecture

SubsystemMemory ModelAllocationPersistence
D3XStack-basedtD3XPMap.mem per-scriptN/A (legacy)
OSIRIS Auto-SaveEngine-managed heapOsiris_AllocateMemoryAuto-saved/restored with game saves
OSIRIS OMMSRef-counted shared heapOMMS_MallocPer-mission lifetime
OSIRIS Script InstanceScript-allocatedCreateInstance returns void*Script manages via SaveRestoreState
Renderer Texture CacheGPU-sideGL texture objects via remap tablesLifetime of texture/lightmap data
Renderer VBOGPU-side ring bufferOrphaningVertexBuffer 64K verticesPer-frame streaming (ephemeral)
Module LoadingOS DLL/SO allocatormod_LoadModuleUntil mod_FreeModule

6. Cross-Cutting: Performance Characteristics

6.1 Renderer Performance Tracking

tRendererStats:

struct tRendererStats {
    int poly_count;        // Polygons drawn this frame
    int vert_count;        // Vertices processed this frame
    int texture_uploads;   // Textures uploaded to GPU this frame
};

Internal counters (not exposed via API):

6.2 Optimization Strategies

StrategyImplementationBenefit
Redundant state avoidanceCheck cur_* before setting GL stateSkips expensive GPU driver calls when the state is already set correctly (e.g., don’t re-enable what’s enabled)
Texture dirty trackingBF_CHANGED/BF_BRAND_NEW flagsOnly re-uploads texture image data to the GPU when it has actually been modified, saving bus bandwidth
Last-bound cacheOpenGL_last_bound[2] per slotTracks which texture is already active on each texture unit; skips the bind call if it’s already the right one
Buffer orphaningglBufferData(nullptr) on overflowWhen the vertex ring buffer fills up, discards it and starts fresh instead of waiting for the GPU to finish
Unsynchronized mappingGL_MAP_UNSYNCHRONIZED_BITLets the CPU write new vertices into the buffer without waiting for the GPU to finish reading old ones
Branchless shadermax(sample, inverted_enable)Disables a texture by forcing its sample to white (multiply identity) instead of using an if/else branch on GPU
Alpha multiplier cachegpu_Alpha_multiplier precomputedPre-calculates the combined alpha factor once when the blend mode changes, instead of recomputing it per vertex

6.3 OSIRIS vs D3X Performance

OSIRIS scripts execute as native code — no interpretation overhead. The per-event cost is:

  • One function pointer indirection per script in the chain (CallInstanceEvent)
  • Up to 4 chain links per event (custom → level → mission → default)
  • Osiris_ProcessTimers() scans all active timers linearly each frame

D3X (when it was active) had per-instruction interpretation overhead from the decode-dispatch loop, making OSIRIS significantly faster for complex scripts.


7. Cross-Cutting: Debugging & Diagnostics

7.1 OSIRIS Debug Capabilities

FeatureControlPurpose
Debug messagesShow_osiris_debug flagToggle verbose event logging
Object trackingOSIRISDEBUG compile flagtRefObj linked list per module tracks all bound objects
Object dumpOsiris_DumpLoadedObjects()Write all bound objects to file
Event maskingOsiris_EnableEvents/DisableEventsSelectively suppress event categories
Create suppressionOsiris_DisableCreateEvents()Suppress object creation events during game load/demo playback

7.2 Renderer Diagnostics

FeatureLocationPurpose
Frame statisticsrend_GetStatistics()Poly count, vert count, texture uploads
State change countsOpenGL_sets_this_frame[10]Track redundant state changes
GL info loggingopengl_GetInformation()Log vendor, renderer, version strings
Error reportingrend_GetErrorMessage()/SetErrorMessage()Last renderer error string

7.3 Checksum Validation

Osiris_CreateGameChecksum() computes a checksum from critical struct sizes multiplied by prime numbers. This detects ABI mismatches between the engine and script DLLs at load time, preventing crashes from struct layout changes.


8. Appendices

8.1 Complete D3X Opcode Reference (lib/d3x_op.h)

ValueMnemonicFormatOperands
0BREAK(none)
1LOAD_ABSrid, imm (absolute address)
2LOAD_ADIaiid, a (addr reg), imm (offset)
3LOAD_IMMrid, imm (int or float literal)
4LOAD_PARMrid, imm (parameter index)
5STORE_ABSrid (source reg), imm (absolute address)
6STORE_ADIaiid (source), a (addr reg), imm (offset)
7STORE_PARMrid (source), imm (parameter index)
10ADDrrd (dest += src), s
11SUBrrd, s
12MULrrd, s
13DIVrrd, s
14MODrrd, s
15ANDrrd, s (logical)
16ORrrd, s (logical)
17NEGrrd (negate)
18NOTrrd (logical not)
19ABSrrd (absolute value)
20EQUrrd, s
21NEQrrd, s
22LTrrd, s
23LTErrd, s
24GTrrd, s
25GTErrd, s
26BORrrd, s (bitwise or)
27BANDrrd, s (bitwise and)
28VEXrrd (float), s (vector) — extract X
29VEYrrd, s — extract Y
30VEZrrd, s — extract Z
31XEVrrd (vector), s (float) — set X
32YEVrrd, s — set Y
33ZEVrrd, s — set Z
34ADDIrid, imm (add immediate)
35BANDIrid, imm (bitwise and immediate)
36BORIrid, imm (bitwise or immediate)
40EPUSHrrd (push to exec buffer)
41EPOPrrd (pop from exec buffer)
42PCALLraabs (function index)
43CALLraabs (script address)
44CPUSHrrd (push to call stack)
45CPOPrrd (pop from call stack)
46TOCSPrrd (ADx → call stack pointer)
47FROMCSPrrd (call stack pointer → ADx)
48RETReturn from CALL
49DEFERriimm: 0=end, 1=default handler
50JUMP_ABSraabs (target address)
51JUMP_NCONDraabs (target), r (condition reg, jump if 0)

8.2 OSIRIS Event Type Reference (scripts/osiris_common.h)

ValueEventData StructTrigger Description
0x100EVT_INTERVALtOSIRISEVTINTERVAL (frame_time, game_time)Every game frame; for continuous monitoring, custom animations, or periodic state checks
0x101EVT_AI_FRAMEtOSIRISIEVTAIFRAMEEvery frame for AI objects; used for custom robot behavior that runs alongside the AI
0x102EVT_DAMAGEDtOSIRISEVTDAMAGED (damage, it_handle, weapon_handle, damage_type)When this object takes damage; carries the amount, source weapon, and damage type
0x103EVT_COLLIDEtOSIRISEVTCOLLIDE (it_handle)When this object physically touches another object (player hits wall, robot bumps player)
0x104EVT_CREATEDtOSIRISEVTCREATEDWhen this object is first spawned into the game world (level load or runtime creation)
0x105EVT_DESTROYtOSIRISEVTDESTROY (is_dying)When this object is about to be removed; is_dying distinguishes death from cleanup
0x106EVT_TIMERtOSIRISEVTTIMER (id, game_time)When a script-created countdown expires; id identifies which timer fired
0x107EVT_USEtOSIRISEVTUSE (it_handle)When a player activates/uses this object (pressing a switch, picking up an item)
0x110EVT_AI_NOTIFYtOSIRISEVTAINOTIFY (notify_type, it_handle, goal_num, goal_uid)General AI notification; notify_type specifies which sub-event (see 0x11B-0x123 below)
0x111EVT_AI_INITtOSIRISEVTAIINITWhen AI is first initialized on this object; for setting up initial goals and behaviors
0x115EVT_CHANGESEGtOSIRISEVTCHANGESEG (room_num)When this object moves from one room to another; carries the new room number
0x117EVT_SAVESTATEtOSIRISEVTSAVESTATE (fileptr)During game save; script writes its custom state (puzzle progress, flags) to the file
0x118EVT_RESTORESTATEtOSIRISEVTRESTORESTATE (fileptr)During game load; script reads back its saved state to resume where the player left off
0x119EVT_MEMRESTOREtOSIRISEVTMEMRESTORE (id, memory_ptr)After game load; engine provides the restored auto-save memory pointer for the script
0x11AEVT_TIMERCANCELtOSIRISEVTTIMERCANCEL (handle, detonated)When a timer is cancelled; detonated indicates if it was auto-cancelled by object death
0x11BEVT_AIN_OBJKILLEDtOSIRISEVTAINOTIFYAI notification: the robot’s current target or an observed object was killed
0x11CEVT_AIN_SEEPLAYERtOSIRISEVTAINOTIFYAI notification: the robot has spotted a player within its field of vision
0x11DEVT_AIN_WHITOBJECTtOSIRISEVTAINOTIFYAI notification: the robot has collided with another object while navigating
0x11EEVT_AIN_GOALCOMPLETEtOSIRISEVTAINOTIFYAI notification: the robot has reached its navigation destination or completed its AI goal
0x11FEVT_AIN_GOALFAILtOSIRISEVTAINOTIFYAI notification: the robot’s current navigation goal could not be completed (path blocked)
0x120EVT_AIN_MELEE_HITtOSIRISEVTAINOTIFYAI notification: the robot’s melee (close-range) attack successfully connected with a target
0x121EVT_AIN_MELEE_ATTACK_FRAMEtOSIRISEVTAINOTIFYAI notification: the robot’s animation has reached the melee strike frame (for timing FX)
0x122EVT_AIN_MOVIE_STARTtOSIRISEVTAINOTIFYAI notification: an in-game cinematic has begun; robot may need to pause or play a role
0x123EVT_AIN_MOVIE_ENDtOSIRISEVTAINOTIFYAI notification: the cinematic has ended; robot can resume normal behavior
0x124EVT_MATCEN_CREATEtOSIRISEVTMATCENCREATE (it_handle, id)A matcen (enemy spawning facility) has produced a new object; carries the spawned object
0x125EVT_DOOR_ACTIVATEtOSIRISEVTDOORACTIVATEA door has been activated (started opening), by player, key, or script command
0x126EVT_DOOR_CLOSEtOSIRISEVTDOORCLOSEA door has finished closing; used to trigger ambushes or lock-behind-you sequences
0x127EVT_CHILD_DIEDtOSIRISEVTCHILDDIED (it_handle)A child object (spawned by or attached to this object) was destroyed
0x128EVT_LEVEL_GOAL_COMPLETEtOSIRISEVTLEVELGOALCOMPLETE (level_goal_index)A specific mission objective was completed; index identifies which goal
0x129EVT_ALL_LEVEL_GOALS_COMPLETEtOSIRISEVTALLLEVELGOALSCOMPLETEAll mission objectives for this level have been completed; typically triggers the exit
0x12AEVT_LEVEL_GOAL_ITEM_COMPLETEtOSIRISEVTLEVELGOALITEMCOMPLETE (level_goal_index)A sub-item within a multi-part goal was completed (e.g., one of several reactors destroyed)
0x12BEVT_PLAYER_MOVIE_STARTtOSIRISEVTPLAYERMOVIESTARTA cinematic sequence has begun playing for this player; controls may be disabled
0x12CEVT_PLAYER_MOVIE_ENDtOSIRISEVTPLAYERMOVIEENDThe cinematic sequence has ended; player controls are restored
0x12DEVT_PLAYER_RESPAWNtOSIRISEVTPLAYERRESPAWN (it_handle)A player has respawned after dying (multiplayer); used to reset per-player script state
0x12EEVT_PLAYER_DIEStOSIRISEVTPLAYERDIES (it_handle)A player has been killed; used for score tracking, triggering failure conditions, or death FX

8.3 Renderer Alpha Mode Reference (lib/renderer.h)

ValueConstantAlpha MultiplierBlend FactorsOpenGL Blend FunctionTypical Usage
0AT_ALWAYS1.0No blendingBlending disabledSolid opaque geometry (walls, floors)
1AT_CONSTANTcur_alpha / 255constantSRC_ALPHA, ONE_MINUS_SRC_ALPHAUniform fade: movie overlays, UI transitions
2AT_TEXTURE1.0texture alphaONE, ZEROPer-pixel cutouts from texture: windows, grates
3AT_CONSTANT_TEXTUREcur_alpha / 255texture * constantSRC_ALPHA, ONE_MINUS_SRC_ALPHAFading decals, HUD gauges, scorch marks on walls
4AT_VERTEX1.0per-vertex alphaSRC_ALPHA, ONE_MINUS_SRC_ALPHASmoke trails, soft-edged particles
5AT_CONSTANT_VERTEXcur_alpha / 255vertex * constantSRC_ALPHA, ONE_MINUS_SRC_ALPHAHUD elements with gradient fading
6AT_TEXTURE_VERTEX1.0texture * vertexSRC_ALPHA, ONE_MINUS_SRC_ALPHAComplex particles with texture and edge fading
7AT_CONSTANT_TEXTURE_VERTEXcur_alpha / 255texture * constant * vertexSRC_ALPHA, ONE_MINUS_SRC_ALPHAMaximum-control transparency: cinematic particles
8AT_LIGHTMAP_BLENDcur_alpha / 255dest * src (multiplicative)DST_COLOR, ZEROBaked shadow/lighting maps darkening surfaces
9AT_SATURATE_TEXTUREcur_alpha / 255saturate to white (additive)SRC_ALPHA, ONEExplosions, weapon fire glow, energy auras
12AT_SATURATE_VERTEX1.0vertex saturation (additive)SRC_ALPHA, ONEExpanding blast rings, terrain glow effects
13AT_SATURATE_CONSTANT_VERTEXcur_alpha / 255constant * vertex saturationSRC_ALPHA, ONEColored light rings with brightness control
14AT_SATURATE_TEXTURE_VERTEX1.0texture * vertex saturationSRC_ALPHA, ONETextured blast waves, energy discharge effects
32AT_SPECULAR1.0specular(special handling)Shiny highlights on glossy metal/plastic materials
33AT_LIGHTMAP_BLEND_SATURATEcur_alpha / 255additive lightmapSRC_ALPHA, ONESpecular highlight maps, bright reflections on walls