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

Journal state and tracking

A journal entry definition describes what an entry is. A quest graph decides when that entry becomes active, inactive, succeeded, or failed. Visited state and quest tracking are related presentation state, but they are not synonyms for those lifecycle states.

This chapter applies that distinction to the exact Lab 1 nodes. Read Journal trees and typed paths first if realPath, className, or fileEntryIndex is unfamiliar.

Definition versus runtime state

The Lab 1 journal defines three quest-lifecycle entries beneath its two container entries:

cqa001                         gameJournalQuest
└── cqa001_01                  gameJournalQuestPhase
    └── cqa001_01_obj_wait     gameJournalQuestObjective

Those objects do not carry the current save’s active/succeeded state as an authoring property. The questphase sends operations to their paths at runtime.

Important entry properties are still part of the definition:

Entry typeRelevant authored properties
gameJournalQuestid, title.value, type, districtID, recommendedLevelID
gameJournalQuestPhaseid, locationPrefabRef
gameJournalQuestObjectiveid, description.value, counter, optional, districtID, itemID, locationPrefabRef
gameJournalQuestDescriptionid, description.value

Lab 1 leaves district, item, location, and prefab fields empty or zero. That is not a hidden automatic setup: the lab has no world target, item counter, description child, or map pin.

The journal operation node

The graph node is questJournalNodeDefinition. Its type property holds a node-type handle that supplies the target path and presentation options.

Lab 1 uses questJournalQuestEntry_NodeType with these properties:

Property below typeLab 1 value
optional0
sendNotification1
trackQuest1
versionInitial
path.editorPathempty string
path.fileEntryIndex2

Another common payload is questJournalEntry_NodeType. It contains path and sendNotification, but not the quest-specific tracking, optional, or version fields.

Do not infer a target entry’s class from this payload type alone. Observed in vanilla: some phone-message nodes use questJournalQuestEntry_NodeType, while other working flows use the simpler questJournalEntry_NodeType. Inspect the whole node, its sockets, path, and surrounding presentation intent.

The incoming socket selects the operation

The same node type and journal path can request different states. The incoming socket is decisive:

Input socketRequested journal operation
ActiveMake the target active
InactiveMake the target inactive
SucceededMark the target succeeded
FailedMark the target failed

The node then emits Out. CutDestination, when present, belongs to interruption routing and is not another journal state.

Not every serialized node retains every unused state socket. Use WolvenKit’s Show Unused Sockets command when constructing the graph, and confirm the specific sockets required by the flow before connecting it.

Lab 1’s exact state sequence

Lab 1 uses four separate nodes so each transition is explicit:

Node IDTargetIncoming socketResult
11quests/minor_quest/cqa001ActiveActivate the quest
12quests/minor_quest/cqa001/cqa001_01/cqa001_01_obj_waitActiveActivate the objective
14same objective pathSucceededSucceed the objective after the delay
16quests/minor_quest/cqa001SucceededSucceed the quest

The successful flow is:

activate quest
  -> activate objective
  -> wait ten real-time seconds
  -> succeed objective
  -> set cqa001_completed = 1
  -> succeed quest
  -> terminating output

IDs 12 and 14 intentionally share the same payload path. Their different incoming sockets make them different operations. Editing the objective’s journal definition cannot substitute for the missing Succeeded edge.

The completion fact is also separate from journal success. Setting cqa001_completed gives the one-shot guard persistent control state; it does not automatically mark the quest succeeded. Conversely, succeeding the quest does not set that authored fact.

See Configure the journal operations and connect every socket for the click-by-click authoring procedure.

State, visited, and tracked are separate

Treat these as separate questions:

DimensionExample questionHow it is addressed
Lifecycle stateIs the objective active or succeeded?Journal-node state socket or state condition
Visited stateDid the player open or select the entry?questJournalEntryVisited_ConditionType
TrackingIs this quest selected for tracker/navigation presentation?trackQuest on a journal operation plus runtime UI state
NotificationWas a presentation request sent for this operation?sendNotification on the node payload

Activating a phone choice group does not prove that one choice was selected. Activating a message does not prove that the player opened it. Marking an objective succeeded does not prove that the quest is still tracked.

For a state condition, use:

questJournalCondition
└── type                         questJournalEntryState_ConditionType
    ├── path                     gameJournalPath
    ├── state                    Succeeded
    └── inverted                 0

For a visited condition, use:

questJournalCondition
└── type                         questJournalEntryVisited_ConditionType
    ├── path                     gameJournalPath
    └── visited                  1

Both condition payloads require the same full typed path discipline described in the previous chapter. A condition can be used beneath an immediate condition node or a wait-until node; that surrounding graph node determines whether the graph samples now or pauses for a later change.

Presentation flags are requests, not proof

sendNotification: 1 requests presentation for the journal operation. trackQuest: 1 requests tracking behavior where that node/entry combination supports it. Neither flag proves what the player actually saw.

Presentation depends on more than the two booleans:

  • the target entry family and requested state;
  • whether its parent entries are available;
  • whether localization resolves;
  • current tracking and mappin state;
  • timing relative to adjacent journal operations;
  • state already retained in the save.

Legacy runtime evidence also showed that activating an onscreen entry made it available in the journal without producing an obvious item-acquisition notification. Do not generalize one entry family’s presentation to another.

In Lab 1, preserve the supplied notification and tracking flags. Check the objective text, notifications, tracking, and final Completed presentation in game.

Completion is an ordered lifecycle

A robust success path makes every side effect explicit:

finish the player action
  -> succeed the active objective
  -> perform any reward or cleanup
  -> set the authored completion fact
  -> succeed the quest
  -> terminate the phase

Lab 1 has no reward or cleanup dependency, so its shorter order is deliberate. Later chapters add those operations without pretending that quest journal success grants a reward or cleans world state automatically.

There is no universal placement for every quest’s reward, cleanup, or completion fact. State the invariant for that flow, then test interruption and reload at the boundaries where partial completion would matter.

Observed in vanilla: the following phase contains multiple objective journal nodes, phone-message presentation, mappin changes, and a separate reward manager in one larger lifecycle:

base\open_world\street_stories\watson\kabuki\sts_wat_kab_05\phases\sts_wat_kab_05_openworld.questphase

Extract it from your own game and inspect focused nodes; do not copy the whole resource or assume its street-story orchestration belongs in a minimal quest.

Save-backed behavior

Journal state is stored in the save. Installing a changed .journal or .questphase does not rewind an entry that was already activated, visited, succeeded, failed, or tracked.

Lab 1’s acceptance matrix therefore separates:

  1. activation from a manual save created before the first install;
  2. saving and reloading while the delay and objective are active;
  3. reloading after quest success;
  4. reinstalling the identical build over the completed save;
  5. replaying from the untouched pre-install save.

Keep the starting save and fresh logs with your observations. Resetting a fact does not clear the journal; repeat from the pre-mod save for a clean test.

Failure checks

If a transition does not appear:

  1. Confirm the edge enters the intended state socket, especially Active versus Succeeded.
  2. Confirm type.path.realPath, className, and fileEntryIndex resolve to the supplied journal entry.
  3. Confirm the journal resource and its ArchiveXL registration loaded before diagnosing graph timing.
  4. Check whether the entry was already changed in the test save.
  5. If state changes but text is blank, compare the journal localization key with the onscreen secondaryKey.
  6. If text appears but tracking or notification differs, record that outcome separately rather than reporting the entire journal operation as failed.
  7. Repeat the first-run case from the original pre-install save before changing the resource again.

These checks preserve the difference between a missing resource, a bad typed path, a wrong graph socket, a localization failure, and stale save state.