Skip to content

feat: quest journal decorator with resolved quest data#413

Closed
Gerkinfeltser wants to merge 2 commits into
MinLL:mainfrom
Gerkinfeltser:feature/quest-journal-decorator
Closed

feat: quest journal decorator with resolved quest data#413
Gerkinfeltser wants to merge 2 commits into
MinLL:mainfrom
Gerkinfeltser:feature/quest-journal-decorator

Conversation

@Gerkinfeltser
Copy link
Copy Markdown
Contributor

@Gerkinfeltser Gerkinfeltser commented May 24, 2026

Quest Journal Decorator

Summary

Adds a get_quest_journal() decorator that exposes resolved quest journal content — objective text with <Alias=...> tokens properly resolved, <Global=...> progress counters substituted, and journal body text — for both vanilla and modded quests.

Problem

SkyrimNet's existing quest decorators (get_all_active_quests(), get_selected_quests()) return raw engine data:

  • Unresolved alias tokens — objectives show <Alias.ShortName=QuestGiver> instead of "Ysolda" or <alias=locationAlias> instead of "Fellglow Keep"
  • Unresolved global counters — progress objectives show <Global=FFR07TeethCount>/<Global=FFR07TeethTotal> instead of "1/5"
  • No journal body text — the narrative text players see in their quest log (e.g., "Hadvar suggested that I head to the nearby town of Riverwood...") is completely inaccessible
  • Empty quest names — many misc quests have no GetName() value, producing blank headers

The engine resolves these tokens at UI render time only. Papyrus has no API to resolve them.

Solution

A standalone SKSE plugin (intended for integration into SkyrimNet proper) that hooks into the engine's text resolution pipeline:

Alias Token Resolution

Uses BGSQuestInstanceText::ParseString (RELOCATION_ID 23429/23897) — the same function the Experience mod uses. Normalizes <alias= to <Alias= before resolution. Handles <Alias=Name>, <alias=Name>, and <alias.PronounObj=Name> tokens.

Global Variable Resolution

Looks up TESGlobal forms by editorID to substitute <Global=Name> tokens with actual integer values. Turns (<Global=FFR07TeethCount>/<Global=FFR07TeethTotal>) into (1/5).

Journal Body Text

Uses RELOCATION_ID(24778, 25259) to resolve BGSLocalizedStringDL string IDs into actual journal entry text — the same function the game's journal menu calls.

Template Integration

Updated 0610_party_quests.prompt (based on a prompt by jykej, modified to use journal data) as a demo/test template. Cross-references get_selected_quests() with get_quest_journal(player.UUID):

  • Main quests: resolved quest name, journal narrative, displayed objectives ([DONE] for completed)
  • Misc tasks: flat list of displayed non-completed objectives with resolved text
  • Inactive section (disabled by default): quests in player's questLog with journal text

The template is a working reference for other SkyrimNet prompt authors — customize layout/filtering for your own character setups.

What Changed

Before After
Rare Gifts Bring one Mammoth Tusk to <Alias.ShortName=QuestGiver> Bring one Mammoth Tusk to Ysolda
Retrieve Lost Item Retrieve Lost Item from <alias=locationAlias> Retrieve Lost Item from Fellglow Keep
Ice Wraith Teeth Find 5 Ice Wraith Teeth for Marise (<Global=...>/<Global=...>) Find 5 Ice Wraith Teeth for Marise (1/5)
Before the Storm (no journal text) "Hadvar suggested that I head to the nearby town of Riverwood..."
Blessings of Nature (no journal text) "I've agreed to help Danica Pure-Spring repair the Gildergreen..."
Andurs' Amulet **** (empty name, invisible) Find Amren's Family Sword inside Valtheim Towers (objective text fallback)

Example Output

## Daylong's Party's Active Quests
(Quests remain available indefinitely unless explicitly stated otherwise. Do NOT create urgency, invent deadlines, or repeatedly remind about objectives.)
**Dampened Spirits**
Now that Mallus's position as the owner of Honningbrew Meadery has been secured, I need to find any information explaining how Sabjorn was able to fund this costly operation.
- Speak to Maven Black-Briar [DONE]
- Speak to Mallus Maccius at the Bannered Mare in Whiterun [DONE]
- Speak to Sabjorn at the Honningbrew Meadery outside Whiterun [DONE]
- (OPTIONAL) Warn Sabjorn to cancel the tasting
- Poison the Nest [DONE]
- Poison the Honningbrew Vat [DONE]
- Return to Sabjorn [DONE]
- Attend the Honningbrew Meadery's tasting ceremony [DONE]
- Speak to Mallus Maccius, the new head of Honningbrew Meadery [DONE]
- Search Sabjorn's dresser for information about his silent partner [DONE]
- Return to Maven Black-Briar in Riften
**Balance of Power**
I spoke to Ri'saad, the traveling caravan merchant, about trouble along the road. He handed me a note that has more details.
- Read Ri'saad's note
### Misc. Tasks
- Learn more about the Thieves Guild from Delvin
- Get 3 flawless amethysts for Talen-Jei (0/3)
- Return the Honningbrew Decanter to Delvin
- Find the Suspicious Elf 

Files

  • CppAPI/src/main.quest_journal.cpp — standalone SKSE plugin (332 lines)
  • CppAPI/.buildenv/CMakeLists.txt — build target for SkyrimNetQuestJournalDecorator
  • SKSE/Plugins/SkyrimNetQuestJournalDecorator.dll — compiled plugin
  • SKSE/Plugins/SkyrimNet/prompts/submodules/character_bio/0610_party_quests.prompt — updated template

Technical Notes

  • Registers at kPostPostLoad (not kDataLoaded) to avoid template caching race condition
  • Triple-source quest discovery: PlayerCharacter::questLog (journal entries), questTargets (compass markers at offset 0x598), displayed objectives (states 1/3/5)
  • Quest filtering: ~4000 total quests → ~50-60 active quests
  • Quest name fallback: GetName() → first objective displayTexteditorID
  • Thread-safe: BSReadLockGuard for quest alias data, BSSpinLockGuard for questTargets
  • JSON output includes isMisc (questType==6), inQuestLog, displayed (per-objective) flags
  • Logging: SKSE\SkyrimNetQuestJournalDecorator.log. Set QJ_LOG_ENABLED to 0 to disable (compile-time)

Known Limitations

  • Dormant-objective misc quests invisible — Some Dawnguard radiant quests ("Investigate Rumors of Vampire Activity in Boulder Cave", etc.) have all objectives in kDormant state (0) with no questLog or questTargets entries. Invisible to all three SKSE data sources. Hard limitation.
  • Misc parent toggle inaccessible — The "Miscellaneous" toggle in the quest journal UI is ephemeral Flash/Scaleform state (Journal_QuestsTab::unk30). Not persisted, not in TESQuest::data.flags, not accessible via SKSE. Misc tasks always show when tracked.
  • Inactive quests unreliablePlayerCharacter::questLog includes rumor/auto-triggered quests the player hasn't actively started. show_inactive_quests defaults to false.
  • isMisc is approximate — Based on engine questType==6. Some quests the game shows as misc have different engine types (e.g., 3DNPC's 3DNPCMQ9).
  • ParseString requires live quest — Alias resolution only works while quest is running with populated aliases.
  • No per-stage journal history — Only the most recent log entry (highest stage <= current) is returned.

Integration Path

This is a standalone DLL for development/testing. For integration into SkyrimNet:

  1. Move BuildQuestJournalJson() + helpers into the main SkyrimNet DLL's decorator system
  2. Replace PublicRegisterDecorator call with internal registration
  3. Consider adding trackedByPlayer boolean to simplify template cross-referencing
  4. Remove standalone build target from CMakeLists.txt
  5. Keep the template changes as-is

Standalone SKSE plugin providing get_quest_journal() decorator for SkyrimNet.

Resolves <Alias=...>, <alias=...>, and <Global=Name> tokens in quest names,
objective text, and journal body text. Uses engine's ParseString (RELOCATION_ID
23429/23897) and journal entry resolver (24778/25259).

Triple-source quest discovery: PlayerCharacter::questLog, questTargets (compass
markers), and displayed objectives. Includes demo prompt template based on jykej's
party quests layout.

See PR_DRAFT.md in SkyrimNet_iPrompts repo for full details.
@Gerkinfeltser Gerkinfeltser marked this pull request as draft May 28, 2026 18:17
@Gerkinfeltser
Copy link
Copy Markdown
Contributor Author

(Superseded by #417)

@Gerkinfeltser Gerkinfeltser deleted the feature/quest-journal-decorator branch May 29, 2026 17:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant