Quest Journal

Alegacy VS Quest includes a custom Quest Journal UI that displays journal entries created by quests and actions. The journal is opened via a hotkey (default: N) and is separate from the vanilla Vintage Story journal, though it can also write to it.


Overview

The journal serves as a lore book and quest tracker in one interface. Players can browse entries organized by quest giver, read multi-chapter stories, view active quest progress, and see achievements.

graph TD
    A[Player presses N] --> B[Journal UI opens]
    B --> C{Select tab}
    C -->|All| D[All entries]
    C -->|Quests| E[Quest entries by giver]
    C -->|Notes| F[Standalone notes]
    C -->|Achievements| G[Unlockable achievements]
    C -->|Active Quests| H[Current quest progress cards]
    D --> I[Click entry to read chapters]
    E --> I
    F --> I

Data Model

Journal entries are stored per-player in WatchedAttributes as a JSON array. Each entry has the following structure:

FieldTypeDescription
QuestIdstringGroup ID — used by the UI to organize entries under tabs and quest giver headers.
LoreCodestringUnique identifier for this entry within the group. Used to find/update existing entries.
TitlestringDisplay title for the entry. Can be a localization key or raw text.
Chaptersstring[]Ordered list of chapter text strings. Each chapter is a separate page of the entry.
IsNoteboolIf true, the entry appears in the Notes tab instead of the Quests tab.

Storage Keys

KeyDescription
alegacyvsquest:journal:entriesJSON array of all QuestJournalEntry values
alegacyvsquest:journal:<groupId>:lorecodesString array of LoreCode values written for the group (used for cleanup)

UI Tabs

The journal UI has 5 vertical tabs on the left side:

TabContent
AllEvery entry in the journal, grouped by quest giver.
QuestsOnly entries linked to actual quests (entries whose QuestId exists in the quest registry).
NotesStandalone entries marked with IsNote: true. Not tied to any quest.
AchievementsAchievement unlocks rendered as themed cards.
Active QuestsCurrent active quests shown as cards with progress info.

Active Quests Tab

This tab shows quest cards instead of text entries. Each card displays:

  • Quest title and description
  • Current progress (e.g., "Killed 3/5 wolves")
  • Stage information for multi-stage quests
  • Clicking a card opens the full quest details

Writing Entries (Quest Actions)

Entries are created/updated via two actions:

addjournalentry (Custom Journal)

Writes to the framework's custom journal. This is the recommended approach.

New format (recommended):

addjournalentry <groupId> <loreCode> <title> [overwrite] <chapter1> <chapter2> ...

Legacy format (with title):

addjournalentry <loreCode> <title> <chapter1> <chapter2> ...

Minimal format:

addjournalentry <loreCode> <chapter1> <chapter2> ...

Example in quest JSON:

{
  "id": "addjournalentry",
  "args": [
    "alstory:epic-journey",
    "epic-intro",
    "alstory:journal-epic-title",
    "alstory:journal-epic-ch1",
    "alstory:journal-epic-ch2",
    "alstory:journal-epic-ch3"
  ]
}

What happens when you call addjournalentry:

  • New entry — If no entry with this loreCode exists in the group, a new entry is created.
  • Update existing — If the entry exists, new chapters are appended (deduplicated by exact text match).
  • Overwrite — If the overwrite flag is present, the existing entry is replaced entirely with the new chapters.
  • Notification — Player sees a notification and hears a writing sound.

addvanillajournalentry (Vanilla Journal)

Writes to the vanilla Vintage Story journal (the ModJournal system). Use this if you want entries to appear in the base game journal.

addvanillajournalentry <loreCode> <chapter1> <chapter2> ...

Example:

{
  "id": "addvanillajournalentry",
  "args": [
    "alstory:vanilla-note",
    "alstory:vanilla-note-text"
  ]
}

Notes vs Quests

The journal distinguishes between two types of entries:

TypeHow to createTabBehavior
Quest entryaddjournalentry with a QuestId that exists in the quest registryQuestsTied to quest lifecycle. Can be removed when quest is reset.
Noteaddjournalentry with IsNote: true or legacy title "note"NotesStandalone. Not tied to any quest. Survives quest resets.

Legacy Note Auto-Detection

If you use the legacy format and set the title to "note", the framework automatically sets IsNote = true and promotes the first chapter to the title:

{
  "id": "addjournalentry",
  "args": [
    "my-note",
    "note",
    "alstory:note-title",
    "alstory:note-body"
  ]
}

This becomes:

  • Title = "alstory:note-title"
  • IsNote = true
  • Chapters = ["alstory:note-body"]

Similarly, "quest" and "overwrite" as titles are auto-converted.


Localization

All text in the journal is client-side localized. The server stores raw localization keys (or text), and the client resolves them when displaying the UI.

Example lang file:

{
  "alstory:journal-epic-title": "The Great Journey",
  "alstory:journal-epic-ch1": "You set out from the village at dawn...",
  "alstory:journal-epic-ch2": "The mountain path was treacherous...",
  "alstory:journal-epic-ch3": "At the summit, you found the ancient altar."
}

Quest giver titles are also localized. The UI tries to resolve <questId>-title as the group header. If no localization key exists, it falls back to the quest ID after the colon.


Lifecycle & Cleanup

When Quests Are Reset

When a quest is reset (via /avq qforgive or /avq qfa), the framework can optionally remove all custom journal entries associated with that quest:

/avq qforgive <questId> [playerName]

This removes:

  • All custom journal entries with QuestId = questId
  • All vanilla journal entries whose LoreCode matches the quest's addjournalentry actions

Admin Commands

CommandPrivilegeDescription
/avq qforgive <questId> [playerName]giveResets a quest and optionally removes its journal entries.
/avq qfall [playerName]giveResets all active quests and removes all journal entries.
/avq nforgive [playerName]giveRemoves all note entries (standalone entries, not quest-linked).

Entry Removal Strategy

The framework tracks which lore codes were written for each quest group via alegacyvsquest:journal:<groupId>:lorecodes. When a quest is reset:

  • The framework reads the tracked lore codes for the quest.
  • It removes matching entries from the custom journal.
  • It removes matching entries from the vanilla journal (via reflection into ModJournal).
  • It clears the tracking key so the entries won't be removed again.

Tips

  • Use groupId to organize entries — All entries with the same QuestId appear under the same quest giver header in the UI.
  • Use overwrite for stage updates — When a quest advances to a new stage, overwrite the journal entry with the new stage's lore.
  • Use IsNote for standalone lore — Background stories, worldbuilding, or hints that aren't tied to any specific quest.
  • Keep chapters short — Each chapter is displayed as a separate page. 3-5 chapters per entry is a good length.
  • Use localization keys — Always use localization keys for titles and chapters, so the journal works in all languages.
  • Test with /avq qforgive — After adding journal entries, test resetting the quest to verify entries are cleaned up correctly.
  • Sound feedback — The journal plays a writing sound (sounds/effect/writing) when entries are added. Players know something happened even if the UI isn't open.

Complete Example

A quest that writes a journal entry at each stage:

{
  "id": "yourmod:epic-journey",
  "stages": [
    {
      "stageTitleLangKey": "yourmod:epic-stage1",
      "gatherObjectives": [
        { "validCodes": ["game:herb-*"], "demand": 5 }
      ],
      "onStageCompleteActions": [
        {
          "id": "addjournalentry",
          "args": [
            "yourmod:epic-journey",
            "stage1-complete",
            "yourmod:journal-stage1-title",
            "yourmod:journal-stage1-text"
          ]
        }
      ]
    },
    {
      "stageTitleLangKey": "yourmod:epic-stage2",
      "actionObjectives": [
        {
          "id": "reach_temple",
          "objectiveId": "reachwaypoint",
          "args": ["5000,70,5000", "5"]
        }
      ],
      "onStageCompleteActions": [
        {
          "id": "addjournalentry",
          "args": [
            "yourmod:epic-journey",
            "stage2-complete",
            "yourmod:journal-stage2-title",
            "yourmod:journal-stage2-text"
          ]
        }
      ]
    }
  ]
}

Lang file:

{
  "yourmod:epic-journey-title": "The Epic Journey",
  "yourmod:journal-stage1-title": "Stage 1: Gathering",
  "yourmod:journal-stage1-text": "You collected the herbs required for the ritual.",
  "yourmod:journal-stage2-title": "Stage 2: The Temple",
  "yourmod:journal-stage2-text": "You reached the ancient temple where the ritual must be performed."
}

What the player sees:

  • Quest accepted → no journal entry yet.
  • Stage 1 complete → Journal gets entry "Stage 1: Gathering" with the text.
  • Stage 2 complete → Journal gets entry "Stage 2: The Temple" with the text.
  • Both entries appear under the "The Epic Journey" group in the Quests tab.

Press ESC to close