feat: Implement Phase 5 Quest System (100% complete)

Add YAML-driven quest system with context-aware offering:

Core Implementation:
- Quest data models (Quest, QuestObjective, QuestReward, QuestTriggers)
- QuestService for YAML loading and caching
- QuestEligibilityService with level, location, and probability filtering
- LoreService stub (MockLoreService) ready for Phase 6 Weaviate integration

Quest Content:
- 5 example quests across difficulty tiers (2 easy, 2 medium, 1 hard)
- Quest-centric design: quests define their NPC givers
- Location-based probability weights for natural quest offering

AI Integration:
- Quest offering section in npc_dialogue.j2 template
- Response parser extracts [QUEST_OFFER:quest_id] markers
- AI naturally weaves quest offers into NPC conversations

API Endpoints:
- POST /api/v1/quests/accept - Accept quest offer
- POST /api/v1/quests/decline - Decline quest offer
- POST /api/v1/quests/progress - Update objective progress
- POST /api/v1/quests/complete - Complete quest, claim rewards
- POST /api/v1/quests/abandon - Abandon active quest
- GET /api/v1/characters/{id}/quests - List character quests
- GET /api/v1/quests/{quest_id} - Get quest details

Frontend:
- Quest tracker sidebar with HTMX integration
- Quest offer modal for accept/decline flow
- Quest detail modal for viewing progress
- Combat service integration for kill objective tracking

Testing:
- Unit tests for Quest models and serialization
- Integration tests for full quest lifecycle
- Comprehensive test coverage for eligibility service

Documentation:
- Reorganized docs into /docs/phases/ structure
- Added Phase 5-12 planning documents
- Updated ROADMAP.md with new structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-29 15:42:55 -06:00
parent e7e329e6ed
commit df26abd207
42 changed files with 8421 additions and 2227 deletions

View File

@@ -0,0 +1,109 @@
# Cellar Rats - Entry level quest for new players
quest_id: quest_cellar_rats
name: "Rat Problem in the Cellar"
description: |
Giant rats have infested the Rusty Anchor's cellar, threatening
the tavern's food supplies and scaring away customers. Grom the
bartender needs someone to clear them out.
difficulty: easy
# NPC Quest Givers (quest-centric design)
quest_giver_npc_ids:
- npc_grom_ironbeard
quest_giver_name: "Grom Ironbeard"
# Location context
location_id: crossville_tavern
region_id: crossville
# Offering conditions
offering_triggers:
location_types: ["tavern"]
specific_locations: ["crossville_tavern"]
min_character_level: 1
max_character_level: 5
required_quests_completed: []
probability_weights:
tavern: 0.35
town: 0.15
# NPC-specific offer dialogue
npc_offer_dialogues:
npc_grom_ironbeard:
dialogue: |
*leans in conspiratorially* Got a problem, friend. Giant rats
in me cellar. Been scaring off the paying customers and getting
into the ale stores. 50 gold for whoever clears 'em out.
conditions:
min_relationship: 30
required_flags: []
forbidden_flags: ["refused_rat_quest"]
# What NPCs know about this quest
npc_quest_knowledge:
npc_grom_ironbeard:
- "The rats started appearing about a week ago"
- "They seem bigger than normal rats - something's not right"
- "Old smuggling tunnels under the cellar might be where they're coming from"
- "Lost three kegs of dwarven stout to the vermin already"
# Embedded lore for AI context
lore_context:
backstory: |
The cellar of the Rusty Anchor connects to old smuggling tunnels
from Captain Morgath's days, sealed decades ago. Recent earthquakes
may have reopened them, allowing creatures to emerge.
world_connections:
- "The earthquakes that opened the tunnels also disturbed the Old Mines"
- "Giant rats are unnatural - possibly corrupted by dark magic from below"
regional_hints:
- "Smuggling was common 50 years ago in Crossville"
- "Captain Morgath was a notorious smuggler who disappeared mysteriously"
# Narrative hooks for natural dialogue
dialogue_templates:
narrative_hooks:
- "mentions unusual scratching sounds from below"
- "complains about spoiled food supplies"
- "nervously glances toward the cellar door"
- "asks if you've ever dealt with vermin before"
ambient_hints:
- "You notice scratch marks near the cellar door"
- "A faint squeaking echoes from somewhere below"
- "The air carries a musty, unpleasant odor"
# Objectives
objectives:
- objective_id: kill_rats
description: "Clear out the giant rats"
objective_type: kill
required_progress: 10
target_enemy_type: giant_rat
- objective_id: find_source
description: "Discover where the rats are coming from"
objective_type: discover
required_progress: 1
target_location_id: smuggler_tunnel_entrance
# Rewards
rewards:
gold: 50
experience: 100
items: []
relationship_bonuses:
npc_grom_ironbeard: 10
unlocks_quests: ["quest_tunnel_mystery"]
reveals_locations: ["crossville_tunnels"]
# Completion dialogue
completion_dialogue:
npc_grom_ironbeard: |
*actually smiles* Well done, friend! Rats are gone, cellar's safe.
Here's your coin, well earned. Drink's on the house tonight.
*pauses* You say you found old tunnels down there? Interesting...
tags:
- starter
- combat
- exploration

View File

@@ -0,0 +1,109 @@
# Missing Tools - Simple fetch quest for new players
quest_id: quest_missing_tools
name: "The Blacksmith's Missing Tools"
description: |
Hilda the blacksmith's best hammers and tongs have gone missing.
She suspects they were stolen by mischievous goblins who've been
spotted near the forest edge.
difficulty: easy
# NPC Quest Givers
quest_giver_npc_ids:
- npc_blacksmith_hilda
quest_giver_name: "Hilda Stoneforge"
# Location context
location_id: crossville_forge
region_id: crossville
# Offering conditions
offering_triggers:
location_types: ["town", "shop"]
specific_locations: ["crossville_forge", "crossville_village"]
min_character_level: 1
max_character_level: 4
required_quests_completed: []
probability_weights:
town: 0.25
shop: 0.30
# NPC-specific offer dialogue
npc_offer_dialogues:
npc_blacksmith_hilda:
dialogue: |
*sighs heavily* Some thieving little beasts made off with me
best tools last night. Saw goblin tracks leading toward the
forest. Can't do proper work without 'em. Help me out?
conditions:
min_relationship: 20
required_flags: []
forbidden_flags: []
# What NPCs know about this quest
npc_quest_knowledge:
npc_blacksmith_hilda:
- "The tools were passed down from her father"
- "Goblins have been more active lately near the forest"
- "She heard strange noises two nights ago but thought it was just animals"
- "The tools are distinctive - marked with the Stoneforge family crest"
# Embedded lore
lore_context:
backstory: |
The Stoneforge family has been the village's blacksmiths for
four generations. Hilda's tools are not just valuable - they're
family heirlooms with sentimental worth.
world_connections:
- "Goblins typically avoid settlements unless driven by something"
- "Recent disturbances in the region have made all creatures more bold"
regional_hints:
- "Goblin warrens exist in the rocky hills east of the forest"
- "The Stoneforge family once forged weapons for the village militia"
# Narrative hooks
dialogue_templates:
narrative_hooks:
- "mentions being unable to fill orders without proper tools"
- "looks frustrated at a half-finished project"
- "grumbles about creatures getting bolder"
- "offers a discount on future work as additional thanks"
ambient_hints:
- "You notice the forge is quieter than usual"
- "Several half-finished weapons sit abandoned on workbenches"
# Objectives
objectives:
- objective_id: track_goblins
description: "Follow the goblin tracks into the forest"
objective_type: travel
required_progress: 1
target_location_id: forest_goblin_camp
- objective_id: recover_tools
description: "Recover Hilda's stolen tools"
objective_type: collect
required_progress: 1
target_item_id: stoneforge_tools
# Rewards
rewards:
gold: 35
experience: 75
items: []
relationship_bonuses:
npc_blacksmith_hilda: 15
unlocks_quests: []
reveals_locations: ["forest_goblin_camp"]
# Completion dialogue
completion_dialogue:
npc_blacksmith_hilda: |
*eyes light up* My tools! You found them! By the ancestors,
I thought they were gone forever. Here's your coin, and know
that you've got a friend at the Stoneforge. Come by anytime
you need gear - I'll give you the family discount.
tags:
- starter
- fetch
- exploration

View File

@@ -0,0 +1,146 @@
# Dungeon Depths - Challenging dungeon crawl quest
quest_id: quest_dungeon_depths
name: "Secrets of the Old Mines"
description: |
The sealed entrance to Crossville's abandoned mines has been breached.
Strange sounds and foul odors emanate from within, and livestock have
begun to disappear. The mayor needs someone brave enough to investigate
what ancient evil has awakened in the depths.
difficulty: hard
# NPC Quest Givers
quest_giver_npc_ids:
- npc_mayor_aldric
quest_giver_name: "Mayor Aldric Thornwood"
# Location context
location_id: crossville_village
region_id: crossville
# Offering conditions
offering_triggers:
location_types: ["town", "official"]
specific_locations: ["crossville_village", "crossville_town_hall"]
min_character_level: 6
max_character_level: 15
required_quests_completed:
- quest_bandit_threat # Must have proven yourself
probability_weights:
town: 0.15
official: 0.25
# NPC-specific offer dialogue
npc_offer_dialogues:
npc_mayor_aldric:
dialogue: |
*closes the door and speaks in hushed tones* What I'm about
to tell you cannot leave this room. The Old Mines... they were
sealed for a reason. My grandfather saw to it fifty years ago.
Now something has broken through. I need someone I can trust
to go down there and find out what we're dealing with.
*pauses* And to ensure it stays buried.
conditions:
min_relationship: 60
required_flags: ["completed_bandit_quest"]
forbidden_flags: []
# What NPCs know
npc_quest_knowledge:
npc_mayor_aldric:
- "The mines were sealed after a cave-in killed twenty miners"
- "His grandfather's journal mentions 'that which must not wake'"
- "The recent earthquakes may have disturbed ancient seals"
- "He's being blackmailed by someone who knows the truth"
- "The Thornwood family has guarded this secret for generations"
# Embedded lore
lore_context:
backstory: |
Fifty years ago, miners in Crossville dug too deep and broke into
ancient catacombs predating human settlement. What they found
drove the survivors mad. The mines were sealed, the entrance
buried, and the truth hidden. The Thornwood family has kept
this secret ever since, fearing what might happen if the truth
emerged.
world_connections:
- "The catacombs may predate the Five Kingdoms themselves"
- "Similar sealed places exist across the realm - ancient evils best forgotten"
- "The earthquakes affecting the region all seem centered on these old sites"
regional_hints:
- "Old-timers speak of the 'quiet years' after the mine closed"
- "The Thornwood family rose to prominence around the same time"
- "Some say the mayor's nightmares are why he never drinks"
# Narrative hooks
dialogue_templates:
narrative_hooks:
- "speaks of family duty and burden"
- "reveals more than intended when stressed"
- "offers to share grandfather's journal"
- "warns of dangers beyond mere monsters"
ambient_hints:
- "The mayor's hands shake when the mines are mentioned"
- "Old maps on his wall show the mine entrance marked with warnings"
- "A locked chest in the corner bears the Thornwood seal"
# Objectives
objectives:
- objective_id: enter_mines
description: "Enter the Old Mines through the breached seal"
objective_type: travel
required_progress: 1
target_location_id: old_mines_entrance
- objective_id: explore_depths
description: "Explore the mine depths"
objective_type: discover
required_progress: 3
target_location_id: mine_depths
- objective_id: defeat_guardians
description: "Defeat the awakened guardians"
objective_type: kill
required_progress: 5
target_enemy_type: ancient_guardian
- objective_id: find_crypt
description: "Discover the ancient crypt"
objective_type: discover
required_progress: 1
target_location_id: ancient_crypt
- objective_id: seal_evil
description: "Reseal or destroy what lies within"
objective_type: interact
required_progress: 1
target_npc_id: ancient_seal
# Rewards
rewards:
gold: 500
experience: 750
items:
- ancient_medallion
- thornwood_journal
relationship_bonuses:
npc_mayor_aldric: 30
unlocks_quests: ["quest_ancient_truth"]
reveals_locations: ["old_mines", "ancient_crypt"]
# Completion dialogue
completion_dialogue:
npc_mayor_aldric: |
*sinks into his chair* It's done then? The seal holds?
*long pause* You've done what my family couldn't for fifty
years. You've seen what lies beneath our village. I trust
you understand why this must remain secret.
*slides a heavy chest across the desk*
Your reward. And my eternal gratitude. The Thornwood family
owes you a debt we can never fully repay. If you ever need
anything... anything at all... my door is always open to you.
tags:
- dungeon
- combat
- story
- secret

View File

@@ -0,0 +1,121 @@
# Bandit Threat - Combat-focused medium quest
quest_id: quest_bandit_threat
name: "The Bandit Threat"
description: |
A gang of bandits has been terrorizing travelers on the roads
near Crossville. Mayor Aldric is offering a substantial reward
for anyone who can deal with the threat permanently.
difficulty: medium
# NPC Quest Givers
quest_giver_npc_ids:
- npc_mayor_aldric
quest_giver_name: "Mayor Aldric Thornwood"
# Location context
location_id: crossville_village
region_id: crossville
# Offering conditions
offering_triggers:
location_types: ["town", "official"]
specific_locations: ["crossville_village", "crossville_town_hall"]
min_character_level: 3
max_character_level: 8
required_quests_completed: []
probability_weights:
town: 0.20
official: 0.40
# NPC-specific offer dialogue
npc_offer_dialogues:
npc_mayor_aldric:
dialogue: |
*straightens his chain of office* Crossville faces a grave threat.
Bandits have been attacking merchants on the eastern road. Trade
is suffering, and I fear for our people's safety. The village
will pay handsomely for someone who can... resolve this situation.
conditions:
min_relationship: 35
required_flags: []
forbidden_flags: ["failed_bandit_quest"]
# What NPCs know
npc_quest_knowledge:
npc_mayor_aldric:
- "The bandits have a camp somewhere in the eastern forest"
- "They're led by a man called 'Blackhand' - a former soldier"
- "At least a dozen bandits, well-armed and organized"
- "Three merchant caravans have been hit in the past month"
- "The militia is too small to mount an assault"
# Embedded lore
lore_context:
backstory: |
The bandit leader Blackhand was once a soldier in the King's
army. After the war ended, he turned to brigandry rather than
return to a life of farming. His military training makes his
gang far more dangerous than common thieves.
world_connections:
- "Many veterans turned to banditry after the war - a kingdom-wide problem"
- "The mayor may know more about Blackhand than he lets on"
regional_hints:
- "The eastern forest road is the main trade route to the capital"
- "Without trade, Crossville's economy will suffer greatly"
# Narrative hooks
dialogue_templates:
narrative_hooks:
- "speaks of the economic impact on the village"
- "mentions concerned merchants refusing to travel"
- "hints at political pressure from higher authorities"
- "shows genuine worry for the common folk"
ambient_hints:
- "You notice fewer merchants in the market than expected"
- "Guards at the village gate look nervous and understaffed"
- "Wanted posters for 'Blackhand' are posted on walls"
# Objectives
objectives:
- objective_id: find_camp
description: "Locate the bandit camp in the eastern forest"
objective_type: discover
required_progress: 1
target_location_id: bandit_camp
- objective_id: defeat_bandits
description: "Defeat the bandits"
objective_type: kill
required_progress: 8
target_enemy_type: bandit
- objective_id: defeat_blackhand
description: "Defeat or capture Blackhand"
objective_type: kill
required_progress: 1
target_enemy_type: blackhand_leader
# Rewards
rewards:
gold: 200
experience: 350
items:
- blackhand_sword
relationship_bonuses:
npc_mayor_aldric: 20
unlocks_quests: ["quest_mayors_secret"]
reveals_locations: ["bandit_camp"]
# Completion dialogue
completion_dialogue:
npc_mayor_aldric: |
*visible relief* The bandits are dealt with? This is excellent
news. You've done Crossville a great service. *hands over a
heavy coin purse* Your reward, as promised. And... I may have
other matters that require someone of your capabilities. We
should speak again soon.
tags:
- combat
- exploration
- story

View File

@@ -0,0 +1,143 @@
# Missing Shipment - Investigation and exploration quest
quest_id: quest_missing_shipment
name: "The Missing Shipment"
description: |
A shipment of dwarven goods, including rare ale and tools, never
arrived from the mountain passes. The merchant who was transporting
it has not been seen in days. Something must have happened on the road.
difficulty: medium
# NPC Quest Givers
quest_giver_npc_ids:
- npc_grom_ironbeard
- npc_blacksmith_hilda
quest_giver_name: "Grom Ironbeard"
# Location context
location_id: crossville_tavern
region_id: crossville
# Offering conditions
offering_triggers:
location_types: ["tavern", "shop"]
specific_locations: ["crossville_tavern", "crossville_forge"]
min_character_level: 4
max_character_level: 10
required_quests_completed:
- quest_cellar_rats # Must have helped Grom before
probability_weights:
tavern: 0.30
shop: 0.25
# NPC-specific offer dialogue
npc_offer_dialogues:
npc_grom_ironbeard:
dialogue: |
*polishes glass nervously* My cousin Durgin was supposed to
arrive three days ago with a shipment from the mountain holds.
Dwarven stout, quality ore, fine tools. He's never late.
*voice drops* Something's happened on the mountain road.
I'm too old to go myself, but I'll pay well for news of him.
conditions:
min_relationship: 50
required_flags: ["completed_cellar_rats"]
forbidden_flags: []
npc_blacksmith_hilda:
dialogue: |
*frowns at empty shelves* My shipment of dwarven ore never
arrived. Durgin Stonefoot was bringing it - he's Grom's cousin.
Without that ore, I can't forge anything worth selling.
The mountain road can be dangerous... would you look into it?
conditions:
min_relationship: 40
required_flags: []
forbidden_flags: []
# What NPCs know
npc_quest_knowledge:
npc_grom_ironbeard:
- "Durgin always takes the north pass - it's longer but safer"
- "He travels with two guards and a pack mule"
- "The shipment is worth at least 500 gold"
- "There have been rumors of strange creatures in the mountains lately"
npc_blacksmith_hilda:
- "Durgin is reliable - never missed a delivery in 15 years"
- "The ore he brings is from the deep mines - best quality"
- "Grom is worried sick about his cousin"
# Embedded lore
lore_context:
backstory: |
Durgin Stonefoot has been making the mountain crossing for years,
bringing goods from the dwarven holds to the human settlements
below. He's a seasoned traveler who knows every rock and crevice
of the mountain road.
world_connections:
- "The mountain passes have seen increased monster activity"
- "Trade routes between dwarves and humans are vital to both economies"
regional_hints:
- "The north pass winds through old ruins from the First Age"
- "Travelers speak of hearing strange howls at night"
# Narrative hooks
dialogue_templates:
narrative_hooks:
- "mentions family connection to the missing merchant"
- "worries about more than just the lost goods"
- "offers personal items as a bonus reward"
- "knows something about the mountain dangers"
ambient_hints:
- "Empty barrels line the cellar where shipments should be"
- "Other merchants discuss avoiding the mountain road"
# Objectives
objectives:
- objective_id: search_road
description: "Search the mountain road for signs of the shipment"
objective_type: travel
required_progress: 1
target_location_id: mountain_pass
- objective_id: find_durgin
description: "Discover what happened to Durgin"
objective_type: discover
required_progress: 1
target_location_id: durgin_campsite
- objective_id: recover_goods
description: "Recover the missing goods (if possible)"
objective_type: collect
required_progress: 1
target_item_id: dwarven_shipment
# Rewards
rewards:
gold: 150
experience: 300
items:
- dwarven_ale_keg
relationship_bonuses:
npc_grom_ironbeard: 15
npc_blacksmith_hilda: 10
unlocks_quests: ["quest_mountain_menace"]
reveals_locations: ["mountain_pass", "durgin_campsite"]
# Completion dialogue
completion_dialogue:
npc_grom_ironbeard: |
*relief floods his face* Durgin is alive? Thank the ancestors!
*takes the recovered goods* And you brought back what you could.
You've done more than I could ask. Here's your payment, and...
*pulls out a small flask* ...my personal reserve. Drink it in
good health, friend.
npc_blacksmith_hilda: |
*examines the recovered ore* This will do nicely. Thank you
for finding out what happened. Poor Durgin - I hope he recovers.
You've earned both coin and my gratitude.
tags:
- investigation
- exploration
- story