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 --> IData Model
Journal entries are stored per-player in WatchedAttributes as a JSON array. Each entry has the following structure:
| Field | Type | Description |
|---|---|---|
QuestId | string | Group ID — used by the UI to organize entries under tabs and quest giver headers. |
LoreCode | string | Unique identifier for this entry within the group. Used to find/update existing entries. |
Title | string | Display title for the entry. Can be a localization key or raw text. |
Chapters | string[] | Ordered list of chapter text strings. Each chapter is a separate page of the entry. |
IsNote | bool | If true, the entry appears in the Notes tab instead of the Quests tab. |
Storage Keys
| Key | Description |
|---|---|
alegacyvsquest:journal:entries | JSON array of all QuestJournalEntry values |
alegacyvsquest:journal:<groupId>:lorecodes | String array of LoreCode values written for the group (used for cleanup) |
UI Tabs
The journal UI has 5 vertical tabs on the left side:
| Tab | Content |
|---|---|
| All | Every entry in the journal, grouped by quest giver. |
| Quests | Only entries linked to actual quests (entries whose QuestId exists in the quest registry). |
| Notes | Standalone entries marked with IsNote: true. Not tied to any quest. |
| Achievements | Achievement unlocks rendered as themed cards. |
| Active Quests | Current 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
loreCodeexists 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
overwriteflag 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:
| Type | How to create | Tab | Behavior |
|---|---|---|---|
| Quest entry | addjournalentry with a QuestId that exists in the quest registry | Quests | Tied to quest lifecycle. Can be removed when quest is reset. |
| Note | addjournalentry with IsNote: true or legacy title "note" | Notes | Standalone. 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 = trueChapters = ["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
LoreCodematches the quest'saddjournalentryactions
Admin Commands
| Command | Privilege | Description |
|---|---|---|
/avq qforgive <questId> [playerName] | give | Resets a quest and optionally removes its journal entries. |
/avq qfall [playerName] | give | Resets all active quests and removes all journal entries. |
/avq nforgive [playerName] | give | Removes 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
groupIdto organize entries — All entries with the sameQuestIdappear under the same quest giver header in the UI. - Use
overwritefor stage updates — When a quest advances to a new stage, overwrite the journal entry with the new stage's lore. - Use
IsNotefor 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.