Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Sentinel Values

A sentinel is a value inside a field’s ordinary range that the engine treats as meaning “none”, “unset” or “go and look somewhere else”. They are used heavily across the KotOR formats, several appear on more than one page meaning different things, and the pages refer to the convention as though it had a home. This is that home.

The pattern matters more than any single value. KotOR rarely signals absence by omitting a field. It writes an in-range number that a naive reader will accept, use, and get wrong. PortraitId of 0 is portrait row zero, which is a real portrait; TrapType of 0 is a real traps.2da row; ObjectId of 0 is a legitimate object. In each case the sentinel is something else entirely, so a reader that defaults these fields to zero produces a file that parses cleanly and behaves wrongly.

A field absent from here has not been cleared

These are the recurring sentinels, not all of them. A sentinel is a per-field fact, so the full set is spread across the format pages and each states its own. Collected here are the values that turn up on more than one page, that catch people, or that show one of the three shapes below clearly. A blueprint page will name sentinels this table does not.

So use it to learn the shapes and to check a value you have already met, not as a checklist. A field missing from this page has not been cleared. It has not been asked about.

One row in the table carries not established in place of a meaning, which is a third state again: the value is known and its handling is not. It is there rather than left out so that the gap is visible, since a sentinel nobody has followed downstream reads exactly like one nobody has found.

The values

ValueWhereMeans
-1SSF sound slotsSlot unset. 0 is a valid StrRef, so this is the only way to say “no sound”.
-1Walkmesh adjacencyEdge has no neighbour. Terminates a walk at the mesh boundary rather than skipping.
-1Walkmesh edge transitionNo room beyond this edge. The only value checked before the field is used as a room index.
-1Walkmesh AABB node face indexInterior node. A real face index marks a leaf.
-10ARE MiniGame.Num_LoopsNot established. The engine substitutes it for an absent field, then passes it into a setter whose own handling of it was never traced.
0xFFUTD, UTP, UTT TrapTypeNo trap. 0 is a real traps.2da row, so a reader defaulting to zero arms a trap.
0xFFFFMDL face adjacencyNo neighbouring face.
0xFFFEUTC, UTD, UTP, UTT PortraitIdA threshold, not an equality. Below it the id is used; at or above it the engine consults the Portrait resref instead.
0x7F000000GIT ObjectId, all object listsNo runtime object. 0 is a valid object id, which is why this is not zero.
0xFFFFFFFFGIT CurrentWeatherForced by the engine on interior areas, overriding whatever the file carries.
0xFFFFFFFFDLG DelaySubstitute the root DelayEntry/DelayReply instead. Note this is not the field’s absent value, which is a plain 0.
0x8000TLK entry flagsReject sentinel rather than a presence bit.
0x22EUTD PortraitIdSubstituted by the engine when the file carries 0.
10075UTP animationApplied unconditionally when Open resolves non-zero, in place of reading the animation fields at all.
10000.0ARE fog rangesThe absent-value default, an effectively infinite distance.

Three shapes, and they behave differently

An equality sentinel is one specific value meaning “none”: -1, 0xFF, 0x7F000000. Compare for equality and take the other branch.

A threshold sentinel is a boundary rather than a value, and PortraitId is the one that catches people. The engine tests < 0xFFFE, so 0xFFFE, 0xFFFF and anything above all route to the resref. A reader comparing against 0xFFFF alone handles one of three cases.

A substitution sentinel does not mean “none” at all. It means “the real value is elsewhere”. Delay’s 0xFFFFFFFF sends the engine to a different field; PortraitId of 0 on a door becomes 0x22E. Nothing is absent in either case, and treating them as absence loses the value the engine actually uses.

Important

A sentinel is not the same as an absent-value default, and several fields have both. Delay is the clearest: its sentinel is 0xFFFFFFFF and its absent value is 0. A file that omits the field does not get the substitution behaviour, so “the field is missing” and “the field says look elsewhere” are different states with different outcomes.

PortraitId goes the other way. Its absent value is the sentinel, so omitting it and writing 0xFFFF are indistinguishable to the engine. Which of those two a field does is a per-field fact, recorded on the format pages rather than derivable from the sentinel itself.

For a writer

Use the sentinel rather than omitting the field, unless a format page says otherwise for that specific field. The engine’s own writers do, and a field carrying its sentinel is unambiguous where an absent field depends on the reader agreeing with you about the default.

Zero is almost never the right “nothing” here. Every sentinel in the table above is some other value precisely because zero was already taken.