UTE Format (Encounter Blueprint)
Description: The Encounter (.ute) blueprint defines interactive spawn points and boundary triggers across a level map. Instead of acting merely as a spatial zone, encounters handle complex difficulty scaling, bubble-sort creature limits, and explicit coordinate vertices to dynamically deploy combatants when a player crosses their geometry bounds.
At a Glance
| Property | Value |
|---|---|
| Extension(s) | .ute |
| Magic Signature | UTE / V3.2 |
| Type | Encounter Blueprint |
| Rust Reference | View rakata_generics::Ute in Rustdocs |
Data Model Structure
Rakata maps the Encounter definition directly into the rakata_generics::Ute struct. To view the exhaustive binary schema and strict GFF field mappings, please refer to the Rustdocs for this struct, where each field is explicitly documented.
An Encounter breaks down into four main categories:
- Spawn Population (
CreatureList): The list of creature blueprints the encounter can spawn. - Difficulty & Limits: Setting how many creatures spawn at once and how difficult they should be relative to the player (e.g.,
MaxCreatures,DifficultyIndex). - Trigger Boundaries (
Geometry): The coordinates defining the physical tripwire that triggers the spawn. - Behavioral Hooks (
Scripts): The scripts that run when a player enters or exits the trigger, or when the spawn pool runs dry (e.g.,OnEntered,OnExhausted).
- Model Validation:
rakata-lintchecks the data against engine constraints to prevent fatal runtime crashes.
Engine Audits & Decompilation
The following information documents the engine’s exact load sequence and field requirements for .ute files mapped from swkotor.exe.
(Decompilation logic for this section was audited and verified via native Ghidra pipeline against swkotor.exe, explicitly pulling from the primary dispatcher CSWSEncounter::LoadEncounter at 0x00593830.)
Structural Load Phasing
The engine processes an Encounter structurally across several chunked subroutines, each responsible for unique spatial and logic bindings.
| Function | Size | Behavior |
|---|---|---|
ReadEncounterFromGff | 3445 B | The initial pass that sets up the encounter’s identity, difficulty limits, and the spawn list. |
ReadEncounterScriptsFromGff | 567 B | Attaches scripts that trigger when players enter, exit, or exhaust the spawn pool. |
LoadEncounterSpawnPoints | 364 B | Reads the coordinates so the engine knows exactly where to spawn the creatures. |
LoadEncounterGeometry | 651 B | Reads the coordinates that trace the trigger’s boundaries on the floor. |
Core Structural Findings
The engine rigorously evaluates geometric and spatial boundaries. Improper definitions break the spawn mapping algorithm.
Warning
Understanding Fatal Log Drops While minor coordinate math errors usually just cause creatures to spawn inside walls, failing strict geometry constraints causes KOTOR to abruptly abort parsing the Encounter. Specifically, if a
.utefile declares it has geometry boundaries but fails to provide the actual coordinate vertices, the engine dumps a fatal error to its trace log and refuses to spawn the encounter at all.
| Engine Rule | Runtime Behavior |
|---|---|
| Tag Overrides | The engine forcefully converts any Tag to all-lowercase via CSWSObject::SetTag. Any static casing is lost immediately upon load. |
| Geometry Integrity | If Geometry is explicitly defined but has 0 vertices, the engine logs a “has geometry, but no vertices” error and aborts loading the encounter entirely. |
| Geometry Synthesis | If the Geometry list is completely omitted from the blueprint, the engine falls back and safely synthesizes a default 4-vertex spatial box. |
| Difficulty Resolution | The engine prioritizes using DifficultyIndex to look up the difficulty in encdifficulty.2da. The static Difficulty field is ignored unless the 2DA table fails to resolve. |
| Bubble Sorting | Upon loading the CreatureList, the engine runs a Bubble Sort algorithm to firmly re-order the encounter’s spawn pool by ascending CR (Challenge Rating), completely overriding any custom static display order. |
| Area Instantiation | AreaList buffer allocation size is strictly dictated by AreaListMaxSize. If the real list exceeds this size, the buffer will silently overrun. |
Legacy & Ignored Data
| Finding Type | Explanation |
|---|---|
| Passive Legacy Artifacts | Unused fields left over from older tools or Odyssey branches (e.g., TemplateResRef, Comment, PaletteID) are completely dark. The engine inherently ignores them. |
| Superseded Legacy Fields | The static Difficulty field is a completely inactive legacy metric as long as DifficultyIndex maps to a valid row inside encdifficulty.2da. |
Implemented Linter Rules (Rakata-Lint)
These rules are documented for engine parity but are not yet implemented into rakata-lint/src/rules/.
- Dead Difficulty Traces: (Pending) Flags instances where a file statically defines
Difficultyalongside a validDifficultyIndex. - Deficient Spawn Loops: (Pending) Warns when an Encounter evaluates as
Activebut initializes a completely emptyCreatureList. - Dead Field Evaluation: (Pending) Maps extraneous legacy engine artifacts (
TemplateResRef,Comment,PaletteID) as dead fields.