GFF Creature and Dialogue - OpenKotOR/PyKotor GitHub Wiki
Creature templates and dialogue trees are the two GFF types that most directly define how characters behave and speak. A UTC stores everything about a creature β stats, appearance, inventory, scripts [UTC] β while DLG encodes branching conversation logic with conditions, animations, and voice-over references [DLG].
Part of the GFF File Format Documentation.
UTC files define creature templates including NPCs, party members, enemies, and the player character. They are comprehensive GFF files containing all data needed to spawn and control a creature in the game world. UTC files are loaded with the same resource resolution order as other resources (override, MOD/SAV, KEY/BIF).
Official Bioware Documentation: For the authoritative Bioware Aurora Engine Creature format specification, see Bioware Aurora Creature Format.
For mod developers:
- To modify creature templates in your mods, see the TSLPatcher GFFList Syntax Guide.
- For general modding, see HoloPatcher README for Mod Developers.
Related formats:
- 2DA (appearance, portraits, classes, feats, racialtypes, creaturespeed)
- SSF
- TLK
- MDL
- TPC
- GFF-UTI (inventory items)
UTC support in PyKotor is implemented by the in-memory UTC model and GFF reader/writer pipeline (utc.py L36+, construct_utc L494+, read_utc L982+, write_utc L992+). UTC is identified as GFFContent.UTC in the shared GFF type map and uses the common binary GFF loader (gff_data.py L150, io_gff.py L82+). The Holocron Toolset exposes these fields in its creature editor (toolset/gui/editors/utc.py), and other engines parse UTC through their generic GFF stacks (reone gff.cpp, reone gffreader.cpp, KotOR.js GFFObject.ts L24+, Kotor.NET GFF.cs L18+). For practical creature-editing workflow examples and troubleshooting, the community index at Home links to current forum threads.
| Field | Type | Description |
|---|---|---|
TemplateResRef |
ResRef | Template identifier for this creature (max 16 characters; should match UTC filename without extension) |
Tag |
CExoString | Unique tag for script/conversation references |
FirstName |
CExoLocString | Creature's first name (localized) |
LastName |
CExoLocString | Creature's last name (localized) |
Comment |
CExoString | Developer comment/notes |
| Field | Type | Description |
|---|---|---|
Appearance_Type |
UInt32 | Index into appearance.2da
|
PortraitId |
word | Index into portraits.2da; 65535 (0xFFFF) = use Portrait ResRef instead |
Gender |
byte | 0=Male, 1=Female, 2=Both, 3=Other, 4=None [utc.py L221] |
Race |
byte | Index into racialtypes.2da [utc.py L219]. |
SubraceIndex |
byte | Index into subrace.2da; refines race (e.g. subspecies). |
BodyVariation |
byte | Body model variation index [utc.py L231] |
TextureVar |
byte |
texture variation index [utc.py L232] |
SoundSetFile |
word | Index into sound set table |
| Field | Type | Description |
|---|---|---|
Str |
byte | Strength score (3-255) |
Dex |
byte | Dexterity score (3-255) |
Con |
byte | Constitution score (3-255) |
Int |
byte | Intelligence score (3-255) |
Wis |
byte | Wisdom score (3-255) |
Cha |
byte | Charisma score (3-255) |
HitPoints |
int16 | Current hit points |
CurrentHitPoints |
int16 | Alias for hit points |
MaxHitPoints |
int16 | Maximum hit points |
ForcePoints |
int16 | Current Force points (KotOR specific) |
CurrentForce |
int16 | Alias for Force points |
MaxForcePoints |
int16 | Maximum Force points |
| Field | Type | Description |
|---|---|---|
ClassList |
List | List of character classes with levels |
Experience |
UInt32 | Total experience points |
LevelUpStack |
List | Pending level-up choices |
SkillList |
List | Skill ranks (index + rank) |
FeatList |
List | Acquired feats |
SpecialAbilityList |
List | Special abilities/powers |
ClassList Struct fields:
-
Class(int32): Index intoclasses.2da(class definitions) -
ClassLevel(int16): Levels in this class
SkillList Struct fields:
-
Rank(byte): Skill rank value
FeatList Struct fields:
-
Feat(word): Index intofeat.2da(feat definitions)
| Field | Type | Description |
|---|---|---|
FactionID |
word | Faction identifier (determines hostility) |
NaturalAC |
byte | Natural armor class bonus |
ChallengeRating |
float | CR for encounter calculations |
PerceptionRange |
byte | Perception distance category |
WalkRate |
int32 | Row index into creaturespeed.2da; sets walk/run movement speed used for pathfinding and animation. |
Interruptable |
byte | Can be interrupted during actions |
NoPermDeath |
byte | Cannot permanently die |
IsPC |
byte | Is player character |
Plot |
byte | Plot-critical (cannot die) |
MinOneHP |
byte | Cannot drop below 1 HP |
PartyInteract |
byte | Shows party selection interface |
Hologram |
byte | Rendered as hologram |
| Field | Type | Description |
|---|---|---|
ItemList |
List | Inventory items |
Equip_ItemList |
List | Equipped items with slots |
EquippedRes |
ResRef | Deprecated equipment field |
ItemList Struct fields:
-
InventoryRes(ResRef): UTI template ResRef -
Repos_PosX(word): Inventory grid X position -
Repos_Posy(word): Inventory grid Y position -
Dropable(byte): Can be dropped/removed
Equip_ItemList Struct fields:
-
EquippedRes(ResRef): UTI template ResRef - Equipment slots reference
equipmentslots.2da
| Field | Type | Description |
|---|---|---|
ScriptAttacked |
ResRef | Fires when attacked |
ScriptDamaged |
ResRef | Fires when damaged |
ScriptDeath |
ResRef | Fires on death |
ScriptDialogue |
ResRef | Fires when conversation starts |
ScriptDisturbed |
ResRef | Fires when inventory disturbed |
ScriptEndRound |
ResRef | Fires at combat round end |
ScriptEndDialogue |
ResRef | Fires when conversation ends |
ScriptHeartbeat |
ResRef | Fires periodically |
ScriptOnBlocked |
ResRef | Fires when movement blocked |
ScriptOnNotice |
ResRef | Fires when notices something |
ScriptRested |
ResRef | Fires after rest |
ScriptSpawn |
ResRef | Fires on spawn |
ScriptSpellAt |
ResRef | Fires when spell cast at creature |
ScriptUserDefine |
ResRef | Fires on user-defined events |
Alignment:
-
GoodEvil(byte): 0-100 scale (0=Dark, 100=Light) -
LawfulChaotic(byte): Unused in KotOR
Multiplayer (Unused in KotOR):
-
Deity(CExoString) -
Subrace(CExoString) -
Morale(byte) -
MorealBreak(byte)
Special Abilities:
- Stored in
SpecialAbilityListreferencingspells.2daor feat-based abilities
When editing UTCs in the Holocron Toolset, numeric fields use the following ranges (from GFF types and engine behavior):
- TemplateResRef, Conversation, script ResRefs: Max 16 characters (engine ResRef limit).
-
Tag: No engine-enforced max length; keep unique per module for script lookups (e.g.
GetObjectByTag). - FirstName, LastName, Comment: Localized or plain string; no fixed max in GFF.
- Appearance_Type, PortraitId, SoundSetFile, FactionID: WORD (0β65535); use valid 2DA row indices.
-
Race, Gender, SubraceIndex, PerceptionRange, NaturalAC, ability scores (StrβCha), skill Rank: BYTE (0β255). Race: row index into
racialtypes.2da(0 to row count β 1). Save bonuses (fortbonus, refbonus, willbonus): SHORT (-32768β32767). - WalkRate: INT; row index into creaturespeed.2da (0 to row count β 1). Invalid index can cause default or broken movement.
- HitPoints, CurrentHitPoints, MaxHitPoints, CurrentForce, ForcePoints: SHORT (0β32767 typical; game does not use negative HP/FP).
- ChallengeRating: Float (0β100 typical). GoodEvil (alignment): 0β100 (0=Dark, 100=Light).
- ClassLevel: SHORT (0β32767); typical level 1β20. MultiplierSet (K2): INT32; 0 = default.
- BlindSpot (K2): Float 0β360 degrees.
All script hooks store a ResRef to an NCS file; leave blank for no script. The toolset provides tooltips on each field describing how the game uses it and how modders can change it.
UTC files are loaded during module initialization or creature spawning. PyKotor reads a UTC via construct_utc, which deserializes each GFF field into the UTC data object [utc.py L354].
Common Use Cases:
- Party Members: Full UTC with all progression data, complex equipment
- Plot NPCs: Basic stats, specific appearance, dialogue scripts
- Generic Enemies: Minimal data, shared appearance, basic AI scripts
- Vendors: Specialized with store inventory, merchant scripts
- Placeables As Creatures: Invisible creatures for complex scripting
-
GFF File Format - Generic format underlying UTC
-
GFF-UTI (Item) - Item templates in creature inventory
-
Common lookup tables:
-
Bioware Aurora Creature Format - Official creature specification
Part of the GFF File Format Documentation.
DLG files store conversation trees, forming the core of KotOR's narrative interaction. A dialogue consists of a hierarchy of Entry nodes (NPC lines) and Reply nodes (Player options), connected by Links.
Official Bioware Documentation: For the authoritative Bioware Aurora Engine Conversation format specification, see Bioware Aurora Conversation Format.
DLG files are loaded with the same resource resolution order as other resources:
For mod developers:
- To edit dialogues in the toolset, use the DLG editor.
- For mod patches, see TSLPatcher GFFList Syntax.
- HoloPatcher README for Mod Developers.
Related formats:
- TLK / StrRef β displayed text
- WAV β voice-over
- NCS β scripts
- GFF-JRL β journal updates
- MDL β camera models
PyKotor implements DLG as a graph of entries, replies, and links with GFF-based read/write support (dlg/base.py L36+, construct_dlg L29+, read_dlg L566+, write_dlg L593+). DLG is identified as GFFContent.DLG and decoded by the shared GFF binary reader (gff_data.py L160, io_gff.py L82+). PyKotor also supports optional Twine interchange for dialogue authoring flows (dlg/io/twine.py). The Holocron Toolset exposes DLG editing in the GUI workflow (Holocron Toolset: Getting Started), and other engines/tools load DLG through generic GFF or dialogue-specific wrappers (reone gff.cpp, reone gffreader.cpp, KotOR.js DLGObject.ts L31+, KotOR.js GFFObject.ts L24+, Kotor.NET GFF.cs L18+).
| Field | Type | Description |
|---|---|---|
DelayEntry |
int32 | Delay before conversation starts |
DelayReply |
int32 | Delay before player reply options appear |
NumWords |
int32 | Total word count (unused) |
PreventSkipping |
byte | Prevents skipping dialogue lines |
Skippable |
byte | Allows skipping dialogue |
Sound |
ResRef | Background sound loop |
AmbientTrack |
int32 | Background music track ID |
CameraModel |
ResRef | Camera model for cutscenes |
ComputerType |
byte | Interface style (0=Modern, 1=Ancient) |
ConversationType |
byte | 0=Human, 1=Computer, 2=Other |
OldHitCheck |
byte | Legacy hit check flag (unused) |
Conversation types:
- Human: Cinematic camera, voice-over support, standard UI
- Computer: Full-screen terminal interface, no voice-over, green text
- Other: Overhead text bubbles (bark strings)
| Field | Type | Description |
|---|---|---|
EndConversation |
ResRef | Fires when conversation ends normally |
EndConverAbort |
ResRef | Fires when conversation is aborted |
node Lists
DLG files use two main lists for nodes and one for starting points:
| List field | Contains | Description |
|---|---|---|
EntryList |
DLGEntry | NPC dialogue lines |
ReplyList |
DLGReply | Player response options |
StartingList |
DLGLink | Entry points into the dialogue tree |
Graph structure:
- StartingList links to EntryList nodes (NPC starts)
- EntryList nodes link to ReplyList nodes (Player responds)
- ReplyList nodes link to EntryList nodes (NPC responds)
- Links can be conditional (Script checks)
Both Entry and Reply nodes share common fields:
| Field | Type | Description |
|---|---|---|
Text |
CExoLocString | Dialogue text |
VO_ResRef |
ResRef | Voice-over audio file |
Sound |
ResRef | Sound effect ResRef |
Script |
ResRef | Script to execute (Action) |
Delay |
int32 | Delay before text appears |
Comment |
CExoString | Developer comment |
Speaker |
CExoString | Speaker tag (Entry only) |
Listener |
CExoString | Listener tag (unused) |
Quest |
CExoString | Journal tag to update |
QuestEntry |
int32 | journal entry ID |
PlotIndex |
int32 | Plot index (legacy) |
PlotXPPercentage |
float | XP reward percentage |
Cinematic fields:
-
CameraAngle: Camera angle ID -
CameraID: Specific camera ID -
CameraAnimation: animation to play -
CamFieldOfView: Camera FOV -
CamHeightOffset: Camera height -
CamVidEffect: Video effect ID
animation List:
- List of animations to play on participants
-
Participant: Tag of object to animate -
Animation: animation ID
Links connect nodes and define flow control:
| Field | Type | Description |
|---|---|---|
Index |
int32 | Index of target node in Entry/Reply list |
Active |
ResRef | Conditional script (returns TRUE/FALSE) |
Script |
ResRef | Action script (executed on transition) |
IsChild |
byte | 1 if linking to node in list, 0 if logic link |
LinkComment |
CExoString | Developer comment |
Conditional Logic:
- Active script determines if link is available
- If script returns FALSE, link is skipped
- Engine evaluates links top-to-bottom
- First valid link is taken (for NPC lines)
- All valid links displayed (for Player replies)
KotOR 2 Logic Extensions:
-
Logic: 0=AND, 1=OR (combines Active conditions) -
Not: Negates condition result
Flow Evaluation:
- Conversation starts
- Engine evaluates
StartingListlinks - First link with valid
Activecondition is chosen - Transition to target
EntryListnode - Execute Entry
Script, playVO, showText - Evaluate Entry's links to
ReplyList - Display all valid Replies to player
- Player selects Reply
- Transition to target
ReplyListnode - Evaluate Reply's links to
EntryList - Loop until no links remain or
EndConversationcalled
Computer Dialogues:
-
ComputerType=1(Ancient) changes font/background - No cinematic cameras
- Used for terminals and datapads
Bark strings:
ConversationType=2- No cinematic mode, text floats over head
- Non-blocking interaction
Journal Integration:
-
QuestandQuestEntryfields update journal entries directly - Eliminates need for scripts to update quests
PyKotor exposes a Twine bridge for DLGs to support authoring and visualization in story tools:
- Export uses
Libraries/PyKotor/src/pykotor/resource/generics/dlg/io/twine.py::_dlg_to_storyto turn starters, entries, and replies intoTwinePassageobjects. It emits unique names for duplicate speakers, preservesis_childandActivescript on links, and writes KotOR metadata intoPassageMetadata.custom(camera anim/angle/id, fade type, quest, sound, VO, plustext_<language>_<gender>variants). - Import uses
Libraries/PyKotor/src/pykotor/resource/generics/dlg/io/twine.py::_story_to_dlgtogether withFormatConverter.restore_kotor_metadatato hydrateDLGEntry/DLGReplyobjects, restoring multilingual text fromcustomkeys and mapping camera/sound/quest metadata back onto the nodes. - Twine-only data (style, script, tag colors, format info, zoom, creator metadata) is stored in
[DLG](GFF-File-Format#dlg-dialogue).commentas JSON viaFormatConverter.store_twine_metadataand restored on export -
tag_colorsare kept asColorvalues (seeLibraries/PyKotor/src/pykotor/resource/generics/dlg/io/twine_data.py). - Start node selection mirrors engine behavior: first starter becomes
startnodewhen exporting, and missingstartnodeon import falls back to the first entry passage.
- GFF File Format - Generic format underlying DLG
- TLK File Format -- Talk table container
- StrRef -- String index semantics used by DLG fields
- GFF-JRL (Journal) - Journal entries referenced by Quest/QuestEntry
- Bioware Aurora Conversation Format - Official conversation specification