UTC Format (Creature Blueprint)
Description: The Creature (.utc) blueprint format defines the attributes, stats, and behavior of all in-scene NPCs and monsters. It covers a creature’s identity, class/level, appearance, equipment, and event scripts. Because they hold so much state, Creatures are one of the most dynamic and memory-heavy templates processed by the Odyssey Engine.
At a Glance
| Property | Value |
|---|---|
| Extension(s) | .utc |
| Magic Signature | UTC / V3.2 |
| Type | Creature Blueprint |
| Rust Reference | View rakata_generics::Utc in Rustdocs |
Data Model Structure
Rakata maps the Creature definition directly into the rakata_generics::Utc 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.
A Creature breaks down into six main categories:
- Core Statistics: The basic stats that define the creature’s physical capabilities (e.g.,
Strength,Dexterity, baseHitPoints). - Identity & Graphics: Identifiers that define who the creature is and what 3D model they use (e.g.,
Tag,Appearance_Type,Conversation). - Class & Skill Progression: The mechanics that define their level, classes, and skills (e.g.,
ClassList,SkillList). - Combat Capabilities: The specific feats and Force powers the creature can use (e.g.,
FeatList,SpellList). - Inventory & Equipment: The exact items the creature spawns with, including both equipped gear and inventory drops (e.g.,
Equip_ItemList,ItemList). - Event Hooks (
Scripts): The behavior scripts that run when the creature reacts to the world, such as taking damage or noticing an enemy (e.g.,OnNotice,OnDamaged).
- State Validation:
rakata-lintchecks the data against engine constraints to prevent fatal runtime crashes.
Engine Audits & Decompilation
The following documents the engine’s exact load sequence and field requirements for .utc files mapped from swkotor.exe.
(Decompilation logic for this section was entirely audited and verified via native Ghidra pipeline against swkotor.exe, explicitly pulling from CSWSCreatureStats::ReadStatsFromGff at 0x005afce0.)
Structural Load Phasing
| Function | Size | Behavior |
|---|---|---|
ReadStatsFromGff | 7835 B | The massive initial pass that parses 57 basic creature scalars including strength, dexterity, and physical appearance. |
LoadCreature | – | Sets up how the creature physically sits in the world, handling their stealth states, collision size, and idle animations. |
ReadScriptsFromGff | – | Attaches all the custom event scripts that fire when the creature notices an enemy, takes damage, dies, or simply stands around (heartbeat). |
ReadItemsFromGff | – | Pulls all loot into memory, structuring items specifically into equipped slots, the backpack, or dropping them entirely if a creature spawns dead. |
ReadSpellsFromGff | – | Specifically extracts the list of any Force powers or combat feats the creature is allowed to use. |
Note
Zeroed Data Elements Legacy structures referencing
TailandWingsare explicitly hardcoded to0during parsing and completely bypassed by the binary loader.
Core Structural Findings
The engine strictly validates parameters when loading a .utc file. Improper formatting will trigger some of KOTOR’s most notorious game crashes.
Warning
Understanding Fatal Crash Codes (
0x5fX) When the game engine parses a file and hits an invalid stat, it completely aborts loading. Instead of recovering gracefully, the engine deliberately triggers a fatal crash to your desktop and returns a specific hexadecimal error code (e.g.,0x5f7or0x5f4). The rules below track the specific scenarios where the game will crash.
| Engine Rule | Runtime Behavior |
|---|---|
| Class Limits | The engine expects a strict limit of 2 discrete class types. Providing duplicate class configuration completely crashes the game (Engine Error 0x5f7). |
| Race Bounds | The engine compares Race against the compiled row count of racialtypes.2da. Exceeding this boundary fatally crashes the map loader (Engine Error 0x5f4). |
| Saves Calculation | Pre-computed saving throws (SaveWill, SaveFortitude) in the .utc file are completely ignored dead data. The engine overrides them exclusively by reading willbonus and fortbonus. |
| Perception Faults | A non-PC PerceptionRange initiates a read against appearance.2da for PERCEPTIONDIST. Failing to resolve this distance fails the entire creature load (Engine Error 0x5f5). |
| Movement Fallbacks | If a unique MovementRate isn’t declared, the engine logic falls back directly to default WalkRate parameters. |
| Hard Clamping | The engine strictly limits specific numeric bounds upon load: Gender is clamped structurally at a maximum of 4, and GoodEvil is fiercely clamped so that it cannot exceed 100. |
| Appearance Shifting | If Appearance_Head is 0, the engine overrides it to 1 to prevent rendering bugs. |
Legacy & Ignored Data
| Finding Type | Explanation |
|---|---|
| Legacy Engine Artifacts | A staggering 17 .utc fields (such as Morale, SaveWill, BlindSpot, PaletteID) present in older files are actually Neverwinter Nights or KOTOR 2 superset metrics that the K1 engine natively ignores. |
Vanilla Data Anomalies
Corpus surveys of the K1 GOG .utc set surface two anomalies in the SpecAbilityList field. Both are vanilla data quirks rather than decoder bugs; the structural reader represents them faithfully.
Stacked SpecAbilityList entries on the Bastila variants
Six Bastila .utc templates (bastila00c, p_bastilla, p_bastilla001, p_bastilla003, p_bastilla005, p_bastilla006) each carry 99 identical entries of Spell = 52 (SPECIAL_ABILITY_BODY_FUEL) in their SpecAbilityList. The engine’s loader (the SpecAbilityList block of CSWSCreatureStats::ReadStatsFromGff at 0x005afce0) walks each list element and unconditionally appends (Spell, SpellFlags, SpellCasterLevel) to the in-memory special_abilities_ array; there is no deduplication step. Each of the 99 entries occupies its own array slot with independent SpellFlags and SpellCasterLevel, so the stacking is faithfully preserved at runtime.
The loop iteration count itself is taken from CResGFF::GetListCount cast to a single byte (uVar17 & 0xff), so any UTC with more than 255 SpecAbilityList entries would have its tail silently truncated at load. Bastila’s 99 sits comfortably below that cap.
Out-of-range Spell id on the partymember template
The partymember.utc template references Spell = 299. Vanilla K1 spells.2da has 132 rows (0–131), so 299 does not resolve to any row. The SpecAbilityList loader does not validate Spell against spells.2da at load time; the value is stored verbatim in the in-memory entry. spells.2da is itself read into a per-row struct array sized exactly to row_count (CSWClass::LoadSpellsTable at 0x005be4c0), so a use-time lookup of Spell = 299 indexes past the end of that array. The realised behaviour depends on heap layout at runtime and is not deterministic from the load path alone.
Both anomalies are candidate targets for future Phase 2 / Phase 3 UTC lint rules (e.g., “SpecAbilityList[].Spell must resolve to a row in spells.2da”; optionally “warn on stacked-duplicate SpecAbilityList entries unless explicitly whitelisted as a known vanilla pattern”).
Implemented Linter Rules (Rakata-Lint)
Phase 1 (intra-resource, no context)
Implemented under rakata_lint::rules::utc.
- UTC-001 (Appearance Correction): Warns when
Appearance_Head == 0; the engine forces this to 1 at runtime. - UTC-002 (Class Limit): Warns when more than 2 entries appear in
ClassList; the engine ignores classes beyond the second. - UTC-003 (Class Duplications): Errors when duplicate class IDs exist in
ClassList; causes a fatal engine crash (0x5f7) on load. - UTC-004 (Dead Save Fields): Informs when
SaveWillorSaveFortitudeare populated; the engine readswillbonus/fortbonusinstead. - UTC-005 (Gender Clamp): Warns when
Gender > 4; the engine clamps to a maximum of 4. - UTC-006 (GoodEvil Clamp): Warns when
GoodEvil > 100; the engine clamps to a maximum of 100. - UTC-007 (Toolset / Legacy Fields): Informs when any of
Comment,Morale*,PaletteID,BodyVariation,TextureVar,BlindSpot,MultiplierSet,NoPermDeath,IgnoreCrePath,Hologram,WillNotRender, orLawfulChaoticare set; never read by the K1 engine.
Phase 2 (range / 2DA / resref existence, requires LintContext)
Implemented under rakata_lint::rules::utc_range.
- UTC-008 (Race Bounds): Errors when
Racedoes not resolve to a row inracialtypes.2da; engine crash0x5f4on load. - UTC-009 (Class Bounds): Errors when any
ClassList[].Classdoes not resolve to a row inclasses.2da(or is negative); engine load failure. - UTC-010 (Appearance Bounds): Errors when
Appearancedoes not resolve to a row inappearance.2da; engine renders missing model. - UTC-011 (Portrait Bounds): Errors when
PortraitId(when not the0xFFFE“use string Portrait” sentinel) does not resolve to a row inportraits.2da. - UTC-012 (Resref Existence): Warns when
Conversation(.dlg),Portrait(.tga), any of the 14Script*hooks (.ncs),Equip_ItemList[i].EquippedRes(.uti), orItemList[i].InventoryRes(.uti) does not resolve in the configured resource sources.