Why zero and undefined are different in my narrative system
Inside Coldwater Hollow’s NarrativeFramework: how I represent story state with Gameplay Tags, integer values, and explicit definition state.
Coldwater Hollow is my current solo horror project. Alongside it, I’m developing NarrativeFramework, a C++ foundation for the game’s narrative systems. One small but important part of that foundation is how it represents story state.
The distinction I keep explicit
A fact that has never been defined and a fact whose value is zero are different states in my implementation.
For an illustrative task counter, zero might mean that the task exists but the player has not made progress. An undefined value can mean the task has not entered the story yet. This is an example of the distinction, rather than a description of a specific shipped quest.
The store is a TMap<FGameplayTag, int32>. A registered child tag of Narrative.Fact identifies a fact. Presence in the map records whether it is defined; the integer records its value.
Read the state you actually need
The subsystem exposes separate queries:
IsFactDefinedchecks presence.GetFactValueIfDefinedreturns a value only when one exists.GetFactValueOrDefaultprovides a deliberate fallback.
That makes the choice visible at the call site. A condition that depends on a fact existing does not need to infer existence from a magic number.
Reset is not removal
ResetFactValue sets an existing fact to zero. If the fact is undefined, it stays undefined. UndefineFact removes it from the store.
The condition type also distinguishes IsDefined and IsUndefined from integer comparisons such as equality or greater-than. Flow nodes can therefore express both presence and progress checks.
Ownership and notifications
The store belongs to a UGameInstanceSubsystem. Changing the state emits value and definition notifications, with a separate notification for any fact change. Startup presets establish the initial state when a session begins.
This is the part I want to keep explicit as the game grows: what the story knows, whether it knows it yet, and which systems need to react when that changes. GameInstance ownership describes the store’s runtime lifetime; it does not, by itself, mean the data has been saved to disk.