Quest System¶
Status: Current
Last reviewed: 2026-09-08
Source: shared/src/quests.ts, server/src/game/quests.ts
Quests are data-driven and server-authoritative. Definitions load from a JSON document; progress lives on the player and persists. The design goal is to teach systems and point players at sandbox activities, not to build a themepark.
Schema¶
A QuestDef has:
| Field | Meaning |
|---|---|
id |
Stable quest id. |
title |
Display name. |
giverNpcId |
The npcId that offers and completes it. |
description |
Flavour and instruction. |
objectives |
Ordered list of QuestObjective. |
rewards |
QuestReward: items and/or skillUses. |
requires |
Optional list of quest ids that must be turned in first. |
repeatable |
Optional flag. |
chapter, region, giverName |
Optional journal labels; existing quests can derive them from the zone. |
giverArea, conclusion |
Optional navigation area and text after turn-in. |
A QuestObjective is { id, type, description, target, count }, with optional area and requires (earlier objective IDs). A QuestReward grants item stacks and skill-use amounts (fed through the normal use-based progression, so a reward can nudge a skill without a fake XP system).
Objective Types¶
Gameplay code reports events as string keys of the form type:target. The quest engine advances any active objective whose type:target matches.
| Type | Target | Fires when |
|---|---|---|
talk_to_npc |
npcId |
You interact with that NPC. |
kill_mob |
variant | You help defeat that variant; recent living contributors within 35 metres receive credit. |
collect_item |
itemId |
Your inventory count of the item reaches count. |
loot_item |
itemId |
You loot that item (cumulative). |
reach_area |
trigger id | You stand inside that quest-trigger volume. |
interact_object |
object/prefab id | You interact with a matching object. |
hit_target_weapon |
<prefab>:<weaponId> |
You strike a target prop with that weapon. |
hit_target_spell |
<prefab>:<spellId> |
You strike a target prop with that spell. |
hit_target_arrow |
prefab | You strike a target prop with a drawn arrow. |
raise_ember |
level name | Ember pressure reaches stirring or awakened. |
defeat_boss |
variant | You defeat the named mini-boss. |
Example keys: kill_mob:scrapper, loot_item:goblin_ear, hit_target_weapon:training_dummy:sword, raise_ember:awakened.
Progress Tracking¶
Each player has a questLog mapping quest id to { state, counts[] }, where counts is one counter per objective. States are:
stateDiagram-v2
[*] --> available
available --> active: accept
active --> ready: all objectives met
ready --> active: progress lost (rare)
ready --> turned_in: turn in at giver
turned_in --> [*]
QuestSystem.onEvent advances matching counters and re-evaluates completion. When all objectives are met the quest flips to ready and the player is told to return to the giver. onInventoryChanged re-syncs collect_item objectives whenever the bag changes (looting, using, reward grants).
Quest Events¶
The server pushes quest_update (per-objective progress, with an objective index for combat-log lines) and quest_event (accepted, ready, turned_in with rewards). The client renders a HUD tracker, a quest log panel (J), a completed-quest popup, and combat-log lines as objectives tick up. NPC dialogue lists the quests that NPC can offer or complete, and pressing G in dialogue accepts or turns in the actionable one.
Quest Editor¶
Definitions load from server/data/quests/starter_quests.json, seeded from DEFAULT_QUESTS in shared/src/quests.ts on first boot. The /admin editor has a Quests button that opens the JSON, and Save Quests writes it back via the save_quests admin command. Players pick up new definitions on their next join. See Adding Quests.
Example Starter Quests¶
The eight original quests remain intact, from the training yard through Emberwake and Ironwood:
- A Blade Before Breakfast (Captain Rusk) — hit a training dummy 3 times with the sword and 3 with the mace. Rewards coins and a healing draught.
- Feathers in the Mud (Quartermaster Elen) — hit archery targets 3 times with drawn shots. Rewards arrows and Archery progress.
- The Old Spark (Old Varyn) — strike magic targets with Fireball, Lightning Bolt, and Rift Burst. Rewards a mana draught.
- Goblins at the Fenline (Captain Rusk, requires quest 1) — kill 3 Goblin Scrappers and loot 2 goblin ears. Rewards coins and Defense progress.
- Smoke from the Shrine (Old Varyn, requires quest 4) — reach the shrine trigger and raise ember pressure to Stirring. Rewards coins and Arcane progress.
-
The First Emberwake (Captain Rusk, requires quest 5) — push pressure to Awakened and defeat the Goblin Champion. Rewards the Emberglass Trinket and coins.
-
Ironwood Tracks — follow the tougher orc branch after the Fenline quest.
- Break the Warband — conclude the Ironwood branch.
M1 adds Tracks by the Road, The Hollow Howl, Greyfen's Missing Stores and Quiet the Watch, bringing the catalogue to twelve. For current objective counts, prerequisites and rewards, use the generated quest catalogue and implemented journal.
M1 authority and persistence¶
shared/src/starterContent.ts defines stable quest/interaction IDs. The server resolves interaction positions from authored zone objects and checks range, height, sight line, personal eligibility and cooldown. Supply bundles have separate objectives; repeated clicks on one cannot satisfy the other. Rescue requires that character's alarm objective. Accepting and turning in also requires the correct nearby giver and their offered quest list.
The server marks a quest returned before granting its rewards, making repeated turn-in messages harmless. Existing JSON persistence saves objective counts and rewards; it remains a local prototype store, not a transactional account backend. Acceptance seeds Emberwake objectives if the relay is already at the requested level, allowing later arrivals to continue the shared encounter.
npm run content:install adds the M1 zone objects and quest definitions to existing data, keeps content backups and leaves player records untouched. npm run test:starter verifies personal multiplayer progress, ordering, persistence, reward replay refusal, combat and animation.