Feature 07: Quests & Dungeon Runs
Overview
The core gameplay loop: browse the quest board, start a run (which consumes real time), and review the results when it completes. This feature connects the combat engine (Feature 06) to the API and job queue, making the game actually playable.
Dependencies
- Feature 06 (Combat Engine)
- Feature 05 (Skill Queue) — active queue used for the run
- Feature 03 (Items & Inventory) — equipped gear snapshot, loot rewards
Technical Tasks
1. Static Game Data — Quests
- Create
data/quests/directory:bounties/— short quests (1-3 encounters, 15-45 min)questlines/— medium quests (5-8 encounters, 1-3 hours)dungeon_crawls/— long quests (10-20 encounters, 3-8 hours)- Per quest: id, name, type, level_range, base_duration_secs, encounters (ordered list), loot_table, difficulty_options, description, zone
- Each encounter: type (combat/trap/decision/hazard/rest/puzzle), enemy_ids (for combat), difficulty_class (for skill checks), description
- Start with 10-15 quests covering levels 1-10
2. Quest Board Logic (crates/game)
- Create
src/quest_board.rs:generate_quest_board(character_level: u16, zone: Option<&str>, rng: &mut impl Rng) -> Vec<QuestBoardEntry>:- Return 6-10 level-appropriate quests
- Mix of bounties, questlines, and dungeon crawls
- Refreshes every 6 hours (timestamp-seeded for consistency)
QuestBoardEntry: quest_id, name, type, difficulty, estimated_duration, encounter_count, level_range, recommended_attributescalculate_run_duration(quest: &QuestDefinition, difficulty: Difficulty, is_patron: bool) -> Duration:- Base duration from quest data
- Multiply by difficulty modifier (Trivial 0.5x, Normal 1x, Hard 1.5x, Deadly 2x)
- Patron: multiply by 0.667 (50% faster = 2/3 time)
3. Run Management — Database
- Create migration
0006_create_runs.sql:runstable: id, character_id, quest_id, difficulty, status (in_progress/completed/failed), skill_queue (JSONB snapshot), loadout (JSONB snapshot), supplies (JSONB snapshot), party_id, started_at, completes_at, completed_at, run_log (JSONB), rewards (JSONB)- Indexes: character_id, status WHERE in_progress, completes_at WHERE in_progress
4. Run Queries (crates/db)
- Create
src/queries/runs.rs:create_run(pool, params) -> Runfind_active_run(pool, character_id) -> Option<Run>find_run_by_id(pool, id) -> Option<Run>find_runs_by_character(pool, character_id, limit, offset) -> Vec<Run>complete_run(pool, run_id, status, run_log, rewards)
5. Run Start Flow (crates/api)
- Create
src/routes/runs.rs:GET /api/quest-board?character_id=:- Generate quest board for character’s level
- Return list of available quests with details
POST /api/runs:- Body:
{ character_id, quest_id, difficulty } - Validations:
- Character not already in a run
- Character meets quest level requirements
- Character has an active skill queue
- Character has gear equipped (at minimum a weapon)
- Snapshot the character’s current state:
- Skill queue (copied, not referenced — changes after start don’t affect the run)
- Equipped gear (stats frozen at start time)
- Supplies (deducted from inventory at start)
- Calculate completes_at timestamp
- Create run record (status: in_progress)
- Enqueue delayed job:
resolve-runwith delay = duration - Return
{ run_id, completes_at }
- Body:
6. Run Resolution Worker (crates/workers)
- Create
src/simulation_worker.rs:- Register job handler for
resolve-runqueue - On job received:
- Load run record from DB
- Load character snapshot + quest definition
- Build SimulationContext with seeded RNG
- Call
resolve_run()from the game crate - Write results to DB:
- Update run: status, completed_at, run_log (JSONB), rewards
- If success: grant XP, gold, loot items to character
- Update weapon proficiency (based on weapon used)
- Check for level up, apply if needed
- Create notification record (Feature 14)
- If character has push enabled, send push notification
- Register job handler for
7. Run Results & History (crates/api)
- Extend
src/routes/runs.rs:GET /api/runs/:id:- Return run details: status, quest info, timestamps
- If completed: include run_log, rewards, failure analysis (if failed)
GET /api/characters/:id/runs:- Paginated run history
- Summary view: quest name, difficulty, status, rewards, date
POST /api/runs/:id/collect:- Collect loot rewards from a completed run
- Move items from run rewards into character’s backpack
- Grant XP and gold to character
- Idempotent (can’t collect twice)
8. Supplies System
- Define supply items in
data/items/consumables/supplies.toml:- Rations (restore HP during rest encounters)
- Torches (improve Perception in dark encounters)
- Healing Potions (emergency heal — auto-used when HP < 20%)
- Antidotes (cure poison)
- Repair Kits (prevent gear durability loss)
POST /api/runsdeducts selected supplies from inventory at run start- Supplies affect encounter resolution (checked during simulation)
9. Client — Quest Board & Runs
- Create
routes/(game)/quest-board/+page.svelte:- Quest board: list of available quests as cards
- Each card: quest name, type badge, difficulty selector, encounter count, estimated duration, level range, reward preview
- “Start Run” button per quest → confirmation modal showing:
- Active skill queue summary
- Equipped gear summary
- Supply selection (optional)
- Estimated completion time
- Create
routes/(game)/runs/+page.svelte:- Active run panel (if any): quest name, progress bar to completesAt, countdown timer
- Run history: paginated list of past runs
- Create
routes/(game)/runs/[id]/+page.svelte:- Run result detail page
- Encounter-by-encounter timeline (collapsible)
- Each encounter: rounds, actions, rolls, outcomes (the Run Replay Viewer from the tech architecture)
- Loot display with “Collect All” button
- If failed: failure analysis (which encounter, what went wrong)
- Wire polling: active run status polls every 60s; on timer expiry, immediate refetch
Tests
Unit Tests
generate_quest_board: returns level-appropriate questsgenerate_quest_board: includes mix of quest typesgenerate_quest_board: deterministic for same time window + seedcalculate_run_duration: Patron gets 2/3 timecalculate_run_duration: difficulty modifier applied correctly
Integration Tests
GET /api/quest-boardreturns quests appropriate to character levelPOST /api/runscreates run with correct completes_at timestampPOST /api/runssnapshots skill queue and loadoutPOST /api/runsdeducts supplies from inventoryPOST /api/runsrejects if character already in a runPOST /api/runsrejects if no active skill queuePOST /api/runsrejects if quest level too highGET /api/runs/:idreturns in_progress run with completes_at- Worker resolves run and updates DB with results and log
- Worker grants XP and gold to character on successful run
- Worker creates loot items in character’s rewards
- Worker updates weapon proficiency after run
- Worker triggers level-up if XP threshold reached
POST /api/runs/:id/collectmoves loot to backpack and grants XP/goldPOST /api/runs/:id/collectsecond call → 409 (already collected)GET /api/characters/:id/runsreturns paginated history- Full end-to-end: create character → equip gear → set queue → start run → worker resolves → collect loot → verify character state updated