941 lines
28 KiB
Markdown
941 lines
28 KiB
Markdown
# Phase 4: AI Integration + Story Progression + Quest System
|
|
## Implementation Plan
|
|
|
|
> **Note:** This document contains detailed implementation tasks for developers building the API backend.
|
|
> For high-level project roadmap and progress tracking, see [/docs/ROADMAP.md](../../docs/ROADMAP.md).
|
|
|
|
**Document Version:** 1.2
|
|
**Created:** November 16, 2025
|
|
**Last Updated:** November 23, 2025
|
|
**Status:** In Progress
|
|
**Duration:** 3 weeks (21 days)
|
|
**Total Tasks:** 45 tasks
|
|
**Completed Tasks:** 22/45 (49%)
|
|
|
|
---
|
|
|
|
### Next Tasks
|
|
- Task 8.29: Create story gameplay template with HTMX
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
Phase 4 delivers the core single-player gameplay experience for Code of Conquest. This phase integrates AI narrative generation via Replicate (Llama-3 and Claude models), implements turn-based story progression with button-based actions, and creates a context-aware quest system.
|
|
|
|
> **Architecture Note:** All AI models (Llama-3, Claude Haiku/Sonnet/Opus) are accessed through the Replicate API for unified billing and management.
|
|
|
|
**Key Deliverables:**
|
|
- AI narrative generation with tier-based model selection
|
|
- Turn-based story progression system
|
|
- YAML-driven quest system with context-aware offering
|
|
- Cost tracking and usage limits
|
|
- Complete solo gameplay loop
|
|
|
|
**Development Approach:**
|
|
- Tasks are moderately granular (4 hours each)
|
|
- Testing bundled into implementation tasks
|
|
- Verification checkpoints after major features
|
|
- YAML data created inline with features
|
|
|
|
---
|
|
|
|
## Week 8: Story Progression System (Days 8-14)
|
|
|
|
**Goal:** Implement turn-based story progression with button-based actions
|
|
|
|
### Task Group 8: Story UI & Integration (Tasks 29-31)
|
|
|
|
#### Task 8.29: Create story gameplay template with HTMX
|
|
**Duration:** 5 hours
|
|
**File:** `templates/game/story.html`
|
|
|
|
**Implementation:**
|
|
- Create main story gameplay page layout:
|
|
- Header: Character info, turn count, location
|
|
- Left sidebar: Quest tracker (placeholder for Week 9)
|
|
- Main area: Latest DM response
|
|
- Action panel: Available action buttons
|
|
- Footer: Custom input (if Premium/Elite)
|
|
- Right sidebar: Conversation history (collapsible)
|
|
- Add HTMX for dynamic updates:
|
|
- Action buttons trigger `hx-post="/api/v1/sessions/{id}/action"`
|
|
- Poll job status with `hx-trigger="every 2s"`
|
|
- Update content when job completes
|
|
- Add loading spinner during AI processing
|
|
- Style with dark fantasy theme (match existing CSS)
|
|
- Add responsive design (mobile-friendly)
|
|
- Write Flask route to render template
|
|
- Test UI rendering
|
|
|
|
**Dependencies:** Tasks 8.25-8.28 (API endpoints)
|
|
**Deliverable:** Story gameplay UI template
|
|
|
|
**Key HTMX Patterns:**
|
|
```html
|
|
<!-- Action button -->
|
|
<button
|
|
hx-post="/api/v1/sessions/{{ session_id }}/action"
|
|
hx-vals='{"action_type": "button", "prompt_id": "ask_locals"}'
|
|
hx-target="#dm-response"
|
|
hx-swap="innerHTML"
|
|
hx-indicator="#loading">
|
|
Ask locals for information
|
|
</button>
|
|
|
|
<!-- Job status polling -->
|
|
<div
|
|
hx-get="/api/v1/jobs/{{ job_id }}/status"
|
|
hx-trigger="every 2s"
|
|
hx-swap="outerHTML">
|
|
Processing...
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 8.30: Build action button UI with tier filtering
|
|
**Duration:** 4 hours
|
|
**File:** `templates/game/story.html` (extend), `app/routes/game.py`
|
|
|
|
**Implementation:**
|
|
- Create Jinja2 macro for rendering action buttons
|
|
- Filter actions based on user tier (passed from backend)
|
|
- Show locked actions with upgrade prompt for higher tiers
|
|
- Add tooltips with action descriptions
|
|
- Implement custom text input area:
|
|
- Only visible for Premium/Elite
|
|
- Character counter (250 for Premium, 500 for Elite)
|
|
- Submit button with validation
|
|
- Add HTMX for seamless submission
|
|
- Style buttons with RPG aesthetic (icons optional)
|
|
- Disable buttons during AI processing
|
|
- Write Flask route to provide available actions
|
|
|
|
**Dependencies:** Task 8.29 (Story template)
|
|
**Deliverable:** Dynamic action button system
|
|
|
|
**Jinja2 Macro:**
|
|
```jinja
|
|
{% macro render_action(action, user_tier, locked=False) %}
|
|
<button
|
|
class="action-btn {% if locked %}locked{% endif %}"
|
|
{% if not locked %}
|
|
hx-post="/api/v1/sessions/{{ session_id }}/action"
|
|
hx-vals='{"action_type": "button", "prompt_id": "{{ action.prompt_id }}"}'
|
|
{% else %}
|
|
disabled
|
|
{% endif %}
|
|
title="{{ action.description }}">
|
|
{{ action.display_text }}
|
|
{% if locked %}<span class="lock-icon">🔒</span>{% endif %}
|
|
</button>
|
|
{% endmacro %}
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 8.31: ✅ CHECKPOINT - Full story turn integration test
|
|
**Duration:** 4 hours
|
|
|
|
**Verification Steps:**
|
|
1. Create a new session via UI
|
|
2. Click an action button
|
|
3. Verify loading state appears
|
|
4. Wait for AI response
|
|
5. Verify DM response displayed
|
|
6. Check conversation history updated
|
|
7. Verify turn number incremented
|
|
8. Test with different action buttons
|
|
9. Test custom text input (Premium tier)
|
|
10. Verify tier restrictions enforced (Free can't use Premium actions)
|
|
11. Test rate limiting
|
|
12. Verify Realtime updates work
|
|
|
|
**Success Criteria:**
|
|
- Full story turn loop works end-to-end
|
|
- UI updates smoothly with HTMX
|
|
- AI responses are coherent and relevant
|
|
- Tier filtering works correctly
|
|
- Rate limits enforced
|
|
- No errors in browser console or server logs
|
|
|
|
---
|
|
|
|
## Week 9: Quest System (Days 15-21)
|
|
|
|
**Goal:** Implement YAML-driven quest system with context-aware offering
|
|
|
|
### Task Group 9: Quest Data Models (Tasks 32-34)
|
|
|
|
#### Task 9.32: Create Quest dataclasses
|
|
**Duration:** 5 hours
|
|
**File:** `app/models/quest.py`
|
|
|
|
**Implementation:**
|
|
- Create `QuestObjective` dataclass:
|
|
- `objective_id`, `description`, `objective_type` (enum)
|
|
- `required_progress`, `current_progress`, `completed`
|
|
- Create `QuestReward` dataclass:
|
|
- `gold`, `experience`, `items` (List[Item])
|
|
- `reputation` (optional, for future use)
|
|
- Create `Quest` dataclass:
|
|
- `quest_id`, `name`, `description`, `quest_giver`
|
|
- `difficulty` (enum: EASY, MEDIUM, HARD, EPIC)
|
|
- `objectives` (List[QuestObjective])
|
|
- `rewards` (QuestReward)
|
|
- `offering_triggers` (QuestTriggers)
|
|
- `narrative_hooks` (List[str])
|
|
- `status` (enum: AVAILABLE, ACTIVE, COMPLETED, FAILED)
|
|
- Implement methods:
|
|
- `is_complete()` - Check if all objectives done
|
|
- `get_next_objective()` - Get first incomplete objective
|
|
- `update_progress(objective_id, amount)` - Increment progress
|
|
- Add to_dict() / from_dict() serialization
|
|
- Write unit tests for all methods
|
|
|
|
**Dependencies:** None
|
|
**Deliverable:** Quest data models
|
|
|
|
**Example:**
|
|
```python
|
|
quest = Quest(
|
|
quest_id="quest_goblin_cave",
|
|
name="Clear the Goblin Cave",
|
|
description="A nearby cave is infested with goblins...",
|
|
quest_giver="Village Elder",
|
|
difficulty=QuestDifficulty.EASY,
|
|
objectives=[
|
|
QuestObjective(
|
|
objective_id="kill_goblins",
|
|
description="Defeat 5 goblins",
|
|
objective_type=ObjectiveType.KILL,
|
|
required_progress=5,
|
|
current_progress=0
|
|
)
|
|
],
|
|
rewards=QuestReward(gold=50, experience=100, items=[])
|
|
)
|
|
|
|
quest.update_progress("kill_goblins", 1)
|
|
quest.is_complete() # False (4 more goblins needed)
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.33: Create QuestTriggers with offering logic
|
|
**Duration:** 4 hours
|
|
**File:** `app/models/quest.py` (extend)
|
|
|
|
**Implementation:**
|
|
- Create `QuestTriggers` dataclass:
|
|
- `location_types` (List[LocationType]) - Where quest can be offered
|
|
- `specific_locations` (List[str]) - Optional specific location names
|
|
- `min_character_level`, `max_character_level` (int)
|
|
- `required_quests_completed` (List[str]) - Quest prerequisites
|
|
- `probability_weights` (Dict[LocationType, float]) - Offer chance by location
|
|
- Implement methods:
|
|
- `can_offer(character_level, completed_quests)` - Check eligibility
|
|
- `get_offer_probability(location_type)` - Get chance for location
|
|
- Add validation (probabilities 0.0-1.0)
|
|
- Write unit tests for offering logic
|
|
- Document trigger system in docstrings
|
|
|
|
**Dependencies:** Task 9.32 (Quest model)
|
|
**Deliverable:** Quest offering trigger system
|
|
|
|
**Example:**
|
|
```python
|
|
triggers = QuestTriggers(
|
|
location_types=[LocationType.TOWN, LocationType.TAVERN],
|
|
min_character_level=1,
|
|
max_character_level=5,
|
|
probability_weights={
|
|
LocationType.TOWN: 0.30,
|
|
LocationType.TAVERN: 0.35
|
|
}
|
|
)
|
|
|
|
triggers.can_offer(character_level=3, completed_quests=[]) # True
|
|
triggers.get_offer_probability(LocationType.TAVERN) # 0.35 (35% chance)
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.34: ✅ CHECKPOINT - Verify quest model serialization
|
|
**Duration:** 2 hours
|
|
|
|
**Verification Steps:**
|
|
1. Create sample quest with all fields
|
|
2. Convert to dict with to_dict()
|
|
3. Serialize to JSON
|
|
4. Deserialize from JSON
|
|
5. Recreate quest with from_dict()
|
|
6. Verify all fields match original
|
|
7. Test quest methods (is_complete, update_progress)
|
|
8. Test trigger methods (can_offer, get_offer_probability)
|
|
9. Test edge cases (invalid progress, level requirements)
|
|
|
|
**Success Criteria:**
|
|
- Quest serialization round-trips correctly
|
|
- All methods work as expected
|
|
- Offering logic accurate
|
|
- No data loss during serialization
|
|
|
|
---
|
|
|
|
### Task Group 10: Quest Content & Loading (Tasks 35-38)
|
|
|
|
#### Task 9.35: Create quest YAML schema
|
|
**Duration:** 3 hours
|
|
**File:** `app/data/quests/schema.yaml` (documentation), update `docs/QUEST_SYSTEM.md`
|
|
|
|
**Implementation:**
|
|
- Document YAML structure for quests
|
|
- Define all required and optional fields
|
|
- Provide examples for each objective type
|
|
- Document narrative_hooks usage
|
|
- Create template quest file
|
|
- Add validation rules
|
|
- Update QUEST_SYSTEM.md with schema details
|
|
|
|
**Dependencies:** Task 9.32 (Quest models)
|
|
**Deliverable:** Quest YAML schema documentation
|
|
|
|
**Schema Example:**
|
|
```yaml
|
|
quest_id: quest_goblin_cave
|
|
name: Clear the Goblin Cave
|
|
description: |
|
|
A nearby cave has been overrun by goblins who are raiding nearby farms.
|
|
The village elder asks you to clear them out.
|
|
quest_giver: Village Elder
|
|
difficulty: EASY
|
|
|
|
objectives:
|
|
- objective_id: kill_goblins
|
|
description: Defeat 5 goblins
|
|
objective_type: KILL
|
|
required_progress: 5
|
|
|
|
rewards:
|
|
gold: 50
|
|
experience: 100
|
|
items: []
|
|
|
|
offering_triggers:
|
|
location_types: [TOWN, TAVERN]
|
|
min_character_level: 1
|
|
max_character_level: 5
|
|
probability_weights:
|
|
TOWN: 0.30
|
|
TAVERN: 0.35
|
|
|
|
narrative_hooks:
|
|
- "The village elder looks worried about recent goblin attacks"
|
|
- "You hear farmers complaining about lost livestock"
|
|
- "A town guard mentions a cave to the north"
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.36: Write 10 example quests
|
|
**Duration:** 5 hours
|
|
**Files:** `app/data/quests/easy/*.yaml`, `app/data/quests/medium/*.yaml`, etc.
|
|
|
|
**Implementation:**
|
|
- Create quest files organized by difficulty:
|
|
- **Easy (4 quests):** Levels 1-3, simple objectives
|
|
1. Clear Goblin Cave (kill 5 goblins)
|
|
2. Gather Healing Herbs (collect 10 herbs)
|
|
3. Deliver Message to Town (travel to location)
|
|
4. Find Lost Cat (discover location)
|
|
- **Medium (3 quests):** Levels 3-7, multi-objective
|
|
5. Investigate Bandit Camp (kill + collect + discover)
|
|
6. Rescue Kidnapped Villager (travel + interact)
|
|
7. Ancient Artifact Recovery (discover + collect)
|
|
- **Hard (2 quests):** Levels 7-10, complex chains
|
|
8. Stop the Necromancer (multi-step with prerequisites)
|
|
9. Dragon's Hoard (discover + kill boss + collect)
|
|
- **Epic (1 quest):** Level 10+, multi-chapter
|
|
10. The Demon Lord's Return (epic multi-objective chain)
|
|
- Include rich narrative_hooks for each quest
|
|
- Vary reward amounts by difficulty
|
|
- Add location variety
|
|
- Ensure proper level gating
|
|
|
|
**Dependencies:** Task 9.35 (YAML schema)
|
|
**Deliverable:** 10 complete quest YAML files
|
|
|
|
---
|
|
|
|
#### Task 9.37: Implement QuestService with YAML loader
|
|
**Duration:** 5 hours
|
|
**File:** `app/services/quest_service.py`
|
|
|
|
**Implementation:**
|
|
- Create `QuestService` class
|
|
- Implement `load_quests_from_yaml(directory)` method
|
|
- Parse all YAML files in quest directory
|
|
- Convert to Quest objects
|
|
- Validate quest structure and fields
|
|
- Cache loaded quests in memory
|
|
- Implement methods:
|
|
- `get_quest_by_id(quest_id)`
|
|
- `get_eligible_quests(character_level, location_type, completed_quests)`
|
|
- `filter_by_difficulty(difficulty)`
|
|
- `get_all_quests()`
|
|
- Add error handling for malformed YAML
|
|
- Write unit tests with sample quests
|
|
- Add logging for quest loading
|
|
|
|
**Dependencies:** Task 9.36 (Quest YAML files)
|
|
**Deliverable:** Quest loading and filtering service
|
|
|
|
**Usage:**
|
|
```python
|
|
service = QuestService()
|
|
service.load_quests_from_yaml("app/data/quests/")
|
|
|
|
# Get eligible quests
|
|
eligible = service.get_eligible_quests(
|
|
character_level=3,
|
|
location_type=LocationType.TAVERN,
|
|
completed_quests=[]
|
|
)
|
|
# Returns: [Quest(...), Quest(...)]
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.38: ✅ CHECKPOINT - Verify quest loading and validation
|
|
**Duration:** 2 hours
|
|
|
|
**Verification Steps:**
|
|
1. Load all 10 quests from YAML files
|
|
2. Verify all quests parsed correctly
|
|
3. Check quest filtering by level works
|
|
4. Test filtering by location type
|
|
5. Verify offering probability calculations
|
|
6. Test get_eligible_quests() with various inputs
|
|
7. Verify error handling for invalid YAML
|
|
8. Check quest caching works
|
|
|
|
**Success Criteria:**
|
|
- All 10 quests load without errors
|
|
- Filtering logic accurate
|
|
- Offering probabilities correct
|
|
- No performance issues loading quests
|
|
- Error handling graceful
|
|
|
|
---
|
|
|
|
### Task Group 11: Quest Offering & Management (Tasks 39-42)
|
|
|
|
#### Task 9.39: Implement context-aware quest offering logic
|
|
**Duration:** 5 hours
|
|
**File:** `app/services/quest_offering_service.py`
|
|
|
|
**Implementation:**
|
|
- Create `QuestOfferingService` class
|
|
- Implement two-stage offering:
|
|
1. **Location probability roll:**
|
|
- Get location type probability from triggers
|
|
- Roll random 0.0-1.0, check if < probability
|
|
- If fail, no quest offered
|
|
2. **Context-aware AI selection:**
|
|
- Get eligible quests from QuestService
|
|
- Build AI prompt with narrative_hooks
|
|
- Ask AI to select most contextually relevant quest
|
|
- Parse AI response to get selected quest_id
|
|
- Implement `should_offer_quest(location_type)` method (probability roll)
|
|
- Implement `select_quest_for_context(eligible_quests, game_context)` method (AI selection)
|
|
- Implement main `offer_quest(session_id)` method (full flow)
|
|
- Add validation (max 2 active quests)
|
|
- Write integration tests with mocked AI
|
|
- Add logging for quest offerings
|
|
|
|
**Dependencies:** Tasks 7.10 (NarrativeGenerator), 9.37 (QuestService)
|
|
**Deliverable:** Context-aware quest offering system
|
|
|
|
**Flow:**
|
|
```python
|
|
offering_service = QuestOfferingService()
|
|
|
|
# Called after each story turn
|
|
if offering_service.should_offer_quest(LocationType.TAVERN):
|
|
eligible = quest_service.get_eligible_quests(...)
|
|
selected_quest = offering_service.select_quest_for_context(
|
|
eligible_quests=eligible,
|
|
game_context={
|
|
"location": "The Rusty Anchor",
|
|
"recent_actions": ["talked to locals", "rested"],
|
|
"active_quests": []
|
|
}
|
|
)
|
|
# Returns: Quest object or None
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.40: Integrate quest offering into story turns
|
|
**Duration:** 4 hours
|
|
**File:** `app/tasks/ai_tasks.py` (extend generate_dm_response job)
|
|
|
|
**Implementation:**
|
|
- Update `generate_dm_response()` RQ job
|
|
- After AI narrative generated and before saving:
|
|
1. Check if quest should be offered (probability roll)
|
|
2. If yes, get eligible quests
|
|
3. Call AI to select contextually relevant quest
|
|
4. Add quest offering to response data
|
|
5. Store offered quest in session state (pending acceptance)
|
|
- Add quest offering to conversation entry:
|
|
```python
|
|
{
|
|
"turn": 5,
|
|
"action": "...",
|
|
"dm_response": "...",
|
|
"quest_offered": {
|
|
"quest_id": "quest_goblin_cave",
|
|
"quest_name": "Clear the Goblin Cave"
|
|
}
|
|
}
|
|
```
|
|
- Update API response format to include quest offering
|
|
- Write integration tests for offering flow
|
|
- Add logging for quest offerings
|
|
|
|
**Dependencies:** Task 9.39 (Quest offering logic)
|
|
**Deliverable:** Integrated quest offering in story turns
|
|
|
|
**Updated Response:**
|
|
```json
|
|
{
|
|
"status": 200,
|
|
"result": {
|
|
"dm_response": "As you chat with the locals...",
|
|
"quest_offered": {
|
|
"quest_id": "quest_goblin_cave",
|
|
"name": "Clear the Goblin Cave",
|
|
"description": "...",
|
|
"rewards": {"gold": 50, "experience": 100}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.41: Implement quest accept endpoint
|
|
**Duration:** 4 hours
|
|
**File:** `app/routes/quests.py` (new file)
|
|
|
|
**Implementation:**
|
|
- Create `POST /api/v1/quests/accept` endpoint
|
|
- Validate request body:
|
|
```json
|
|
{
|
|
"session_id": "sess_789",
|
|
"quest_id": "quest_goblin_cave"
|
|
}
|
|
```
|
|
- Validate quest is currently offered to session
|
|
- Check max 2 active quests limit
|
|
- Add quest to session's active_quests
|
|
- Initialize quest with status ACTIVE
|
|
- Store quest state in character (or session)
|
|
- Return accepted quest details
|
|
- Add @require_auth decorator
|
|
- Write integration tests
|
|
- Document in API_REFERENCE.md
|
|
|
|
**Dependencies:** Task 9.39 (Quest offering)
|
|
**Deliverable:** Quest acceptance endpoint
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": 200,
|
|
"result": {
|
|
"quest_id": "quest_goblin_cave",
|
|
"status": "ACTIVE",
|
|
"objectives": [
|
|
{
|
|
"objective_id": "kill_goblins",
|
|
"description": "Defeat 5 goblins",
|
|
"progress": "0/5"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.42: Implement quest complete endpoint with rewards
|
|
**Duration:** 5 hours
|
|
**File:** `app/routes/quests.py` (extend)
|
|
|
|
**Implementation:**
|
|
- Create `POST /api/v1/quests/complete` endpoint
|
|
- Validate request body:
|
|
```json
|
|
{
|
|
"session_id": "sess_789",
|
|
"quest_id": "quest_goblin_cave"
|
|
}
|
|
```
|
|
- Verify quest is active for session
|
|
- Check all objectives completed
|
|
- Grant rewards:
|
|
- Add gold to character
|
|
- Add experience to character
|
|
- Add items to inventory
|
|
- Check for level up
|
|
- Update quest status to COMPLETED
|
|
- Remove from active_quests
|
|
- Add to completed_quests list
|
|
- Return completion details with level up info
|
|
- Add @require_auth decorator
|
|
- Write integration tests
|
|
- Document in API_REFERENCE.md
|
|
|
|
**Dependencies:** Task 9.41 (Quest accept)
|
|
**Deliverable:** Quest completion and reward system
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"status": 200,
|
|
"result": {
|
|
"quest_id": "quest_goblin_cave",
|
|
"status": "COMPLETED",
|
|
"rewards_granted": {
|
|
"gold": 50,
|
|
"experience": 100,
|
|
"items": []
|
|
},
|
|
"level_up": {
|
|
"leveled_up": true,
|
|
"new_level": 4,
|
|
"skill_points_gained": 1
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Task Group 12: Quest UI & Final Testing (Tasks 43-45)
|
|
|
|
#### Task 9.43: Create quest tracker sidebar UI
|
|
**Duration:** 4 hours
|
|
**File:** `templates/game/story.html` (extend left sidebar)
|
|
|
|
**Implementation:**
|
|
- Add quest tracker to left sidebar
|
|
- Display active quests (max 2)
|
|
- Show quest name and description
|
|
- Display objective progress (X/Y format)
|
|
- Add "View Details" button for each quest
|
|
- Style with RPG theme
|
|
- Add HTMX for dynamic updates when objectives progress
|
|
- Show "No active quests" message when empty
|
|
- Add quest complete notification (toast/modal)
|
|
- Test UI rendering
|
|
|
|
**Dependencies:** Tasks 9.41-9.42 (Quest endpoints)
|
|
**Deliverable:** Quest tracker sidebar UI
|
|
|
|
**UI Structure:**
|
|
```html
|
|
<div class="quest-tracker">
|
|
<h3>Active Quests ({{ active_quests|length }}/2)</h3>
|
|
{% for quest in active_quests %}
|
|
<div class="quest-card">
|
|
<h4>{{ quest.name }}</h4>
|
|
<div class="objectives">
|
|
{% for obj in quest.objectives %}
|
|
<div class="objective {% if obj.completed %}completed{% endif %}">
|
|
{{ obj.description }}: {{ obj.current_progress }}/{{ obj.required_progress }}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
<button hx-get="/quests/{{ quest.quest_id }}/details">View Details</button>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.44: Create quest offering modal UI
|
|
**Duration:** 4 hours
|
|
**File:** `templates/game/partials/quest_offer_modal.html`
|
|
|
|
**Implementation:**
|
|
- Create modal component for quest offering
|
|
- Display when quest offered in DM response
|
|
- Show quest name, description, quest giver
|
|
- Display objectives and rewards clearly
|
|
- Add "Accept Quest" button (HTMX post to /api/v1/quests/accept)
|
|
- Add "Decline" button (closes modal)
|
|
- Style modal with RPG theme (parchment background)
|
|
- Add HTMX to update quest tracker when accepted
|
|
- Show error if max quests reached (2/2)
|
|
- Test modal behavior
|
|
|
|
**Dependencies:** Task 9.41 (Quest accept endpoint)
|
|
**Deliverable:** Quest offering modal UI
|
|
|
|
**Modal Structure:**
|
|
```html
|
|
<div class="modal quest-offer-modal">
|
|
<div class="modal-content parchment">
|
|
<h2>{{ quest.quest_giver }} offers you a quest!</h2>
|
|
<h3>{{ quest.name }}</h3>
|
|
<p>{{ quest.description }}</p>
|
|
|
|
<div class="objectives">
|
|
<h4>Objectives:</h4>
|
|
<ul>
|
|
{% for obj in quest.objectives %}
|
|
<li>{{ obj.description }}</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="rewards">
|
|
<h4>Rewards:</h4>
|
|
<p>Gold: {{ quest.rewards.gold }} | XP: {{ quest.rewards.experience }}</p>
|
|
</div>
|
|
|
|
<div class="actions">
|
|
<button
|
|
hx-post="/api/v1/quests/accept"
|
|
hx-vals='{"session_id": "{{ session_id }}", "quest_id": "{{ quest.quest_id }}"}'
|
|
hx-target="#quest-tracker"
|
|
class="btn-accept">Accept Quest</button>
|
|
<button class="btn-decline" onclick="closeModal()">Decline</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
```
|
|
|
|
---
|
|
|
|
#### Task 9.45: ✅ FINAL CHECKPOINT - Full quest integration test
|
|
**Duration:** 4 hours
|
|
|
|
**Comprehensive Test Flow:**
|
|
|
|
**Setup:**
|
|
1. Create new character (level 1)
|
|
2. Create solo session
|
|
3. Verify starting state
|
|
|
|
**Quest Offering:**
|
|
4. Take multiple story actions in a town/tavern
|
|
5. Wait for quest offering (may take several turns)
|
|
6. Verify quest modal appears
|
|
7. Check quest details display correctly
|
|
8. Accept quest
|
|
9. Verify quest appears in tracker (1/2 active)
|
|
|
|
**Quest Progress:**
|
|
10. Simulate quest progress (update objective manually via API or through combat)
|
|
11. Verify tracker updates in real-time
|
|
12. Complete all objectives
|
|
13. Verify completion indicator
|
|
|
|
**Quest Completion:**
|
|
14. Call complete quest endpoint
|
|
15. Verify rewards granted (gold, XP)
|
|
16. Check for level up if applicable
|
|
17. Verify quest removed from active list
|
|
18. Verify quest in completed list
|
|
|
|
**Edge Cases:**
|
|
19. Try accepting 3rd quest (should fail with max 2 message)
|
|
20. Try completing incomplete quest (should fail)
|
|
21. Test with ineligible quest (wrong level)
|
|
22. Verify offering probabilities work (multiple sessions)
|
|
|
|
**Success Criteria:**
|
|
- Full quest lifecycle works end-to-end
|
|
- Quest offering feels natural in story flow
|
|
- UI updates smoothly with HTMX
|
|
- Rewards granted correctly
|
|
- Level up system works
|
|
- Max 2 quest limit enforced
|
|
- Error handling graceful
|
|
- No bugs in browser console or server logs
|
|
|
|
---
|
|
|
|
## Deferred Tasks
|
|
|
|
### Task 7.15: Set up cost monitoring and alerts
|
|
**Duration:** 3 hours
|
|
**Files:** `app/tasks/monitoring_tasks.py`, `app/services/alert_service.py`
|
|
|
|
**Implementation:**
|
|
- Create `calculate_daily_cost()` RQ job (runs daily at midnight)
|
|
- Aggregate all AI usage from previous day
|
|
- Calculate total cost by summing estimated costs
|
|
- Store daily cost in Redis timeseries
|
|
- Create `AlertService` class
|
|
- Implement alert triggers:
|
|
- Daily cost > $50 → Warning email
|
|
- Daily cost > $100 → Critical email
|
|
- Monthly projection > $1500 → Warning email
|
|
- Add email sending via SMTP or service (e.g., SendGrid)
|
|
- Create admin dashboard endpoint: `GET /admin/costs`
|
|
- Write tests for cost calculation
|
|
|
|
**Dependencies:** Task 7.13 (Usage tracking)
|
|
**Deliverable:** Automated cost monitoring with alerts
|
|
|
|
**Daily Job:**
|
|
```python
|
|
# Runs at midnight UTC
|
|
@job('monitoring_tasks')
|
|
def calculate_daily_cost():
|
|
yesterday = date.today() - timedelta(days=1)
|
|
total_cost = sum_all_user_costs(yesterday)
|
|
|
|
if total_cost > 100:
|
|
send_alert(f"CRITICAL: Daily AI cost ${total_cost}")
|
|
elif total_cost > 50:
|
|
send_alert(f"WARNING: Daily AI cost ${total_cost}")
|
|
|
|
store_cost_metric(yesterday, total_cost)
|
|
```
|
|
|
|
---
|
|
|
|
## Phase 4 Complete!
|
|
|
|
**Deliverables Summary:**
|
|
|
|
### Week 7: AI Engine Foundation ✅
|
|
- Redis/RQ infrastructure working
|
|
- AI clients for Replicate + Anthropic (Haiku/Sonnet/Opus)
|
|
- Model selection with tier-based routing
|
|
- Jinja2 prompt templates (4 types)
|
|
- Narrative generator wrapper
|
|
- Async AI task jobs with Appwrite integration
|
|
- Usage tracking and cost monitoring
|
|
- Daily limits per tier enforced
|
|
|
|
### Week 8: Story Progression System ✅
|
|
- 10 action prompts defined in YAML
|
|
- ActionPrompt loader with tier/context filtering
|
|
- Solo GameSession model with state tracking
|
|
- SessionService for CRUD operations
|
|
- Conversation history management
|
|
- 4 API endpoints (create, state, action, history)
|
|
- Story gameplay UI with HTMX
|
|
- Dynamic action buttons with tier filtering
|
|
- Full story turn loop working
|
|
|
|
### Week 9: Quest System ✅
|
|
- Quest data models (Quest, Objective, Reward, Triggers)
|
|
- 10 example quests in YAML (4 easy, 3 medium, 2 hard, 1 epic)
|
|
- QuestService with YAML loader
|
|
- Context-aware quest offering logic
|
|
- Quest offering integrated into story turns
|
|
- Quest accept/complete API endpoints
|
|
- Quest tracker sidebar UI
|
|
- Quest offering modal UI
|
|
- Full quest lifecycle tested
|
|
|
|
**Next Phase:** Phase 5 - Combat System + Skill Tree UI (Week 10-11)
|
|
|
|
---
|
|
|
|
## Dependencies Graph
|
|
|
|
```
|
|
Week 7 (AI Engine):
|
|
Task 7.1 (Redis) → 7.2 (RQ) → 7.3 (AI jobs) → 7.4 (✅ Verify)
|
|
Task 7.5 (Replicate+Claude) → 7.7 (Model selector) → 7.8 (✅ Verify)
|
|
│
|
|
Task 7.9 (Templates) ───────────────────┘→ 7.10 (Narrative gen) → 7.11 (AI jobs) → 7.12 (✅ Verify)
|
|
│
|
|
Task 7.13 (Usage track) → 7.14 (Rate limits) ────────────────────────┘
|
|
└→ 7.15 (Cost monitoring)
|
|
|
|
Note: Task 7.6 merged into 7.5 (all models via Replicate API)
|
|
|
|
Week 8 (Story Progression):
|
|
Task 8.16 (ActionPrompt) → 8.17 (YAML) → 8.18 (Loader) → 8.19 (✅ Verify)
|
|
│
|
|
Task 8.20 (GameSession) → 8.21 (SessionService) ──────────┤
|
|
└→ 8.22 (History) ──────────────────┤
|
|
└→ 8.23 (State tracking) → 8.24 (✅ Verify)
|
|
│
|
|
Task 8.25 (Create API) ───────────────────────────┤
|
|
Task 8.26 (Action API) ───────────────────────────┤
|
|
Task 8.27 (State API) ────────────────────────────┤
|
|
Task 8.28 (History API) ──────────────────────────┤
|
|
│
|
|
Task 8.29 (Story UI) → 8.30 (Action buttons) → 8.31 (✅ Integration test)
|
|
|
|
Week 9 (Quest System):
|
|
Task 9.32 (Quest models) → 9.33 (Triggers) → 9.34 (✅ Verify)
|
|
│
|
|
Task 9.35 (YAML schema) → 9.36 (10 quests) → 9.37 (QuestService) → 9.38 (✅ Verify)
|
|
│
|
|
Task 9.39 (Offering logic) ──────────────────────────────────────────┤
|
|
Task 9.40 (Story integration) → 9.41 (Accept API) → 9.42 (Complete API)
|
|
│
|
|
Task 9.43 (Tracker UI) ──────────────────────────────┤
|
|
Task 9.44 (Offer modal) ─────────────────────────────┤
|
|
│
|
|
Task 9.45 (✅ Final integration test)
|
|
```
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
**Testing Strategy:**
|
|
- Unit tests bundled into each implementation task
|
|
- Integration tests at verification checkpoints
|
|
- Manual testing for UI/UX flows
|
|
- Use docs/API_TESTING.md for endpoint testing
|
|
|
|
**Cost Management:**
|
|
- Target: < $500/month total AI costs
|
|
- Free tier users cost $0 (Replicate)
|
|
- Monitor daily costs via Task 7.15
|
|
- Adjust tier limits if costs spike
|
|
|
|
**Development Tips:**
|
|
- Start each week by reviewing previous week's work
|
|
- Commit frequently with conventional commit messages
|
|
- Update API_REFERENCE.md as you build endpoints
|
|
- Test with real AI calls periodically (not just mocks)
|
|
- Keep YAML files well-documented and validated
|
|
|
|
**Estimated Timeline:**
|
|
- Week 7: ~40 hours (5 days at 8 hours/day)
|
|
- Week 8: ~44 hours (5.5 days at 8 hours/day)
|
|
- Week 9: ~42 hours (5.25 days at 8 hours/day)
|
|
- **Total: ~126 hours (~16 days of focused work)**
|
|
|
|
**Success Metrics:**
|
|
- All 45 tasks completed
|
|
- All verification checkpoints passed
|
|
- No critical bugs in core gameplay loop
|
|
- AI costs within budget (<$50/day during development)
|
|
- Story progression feels engaging and responsive
|
|
- Quest system feels natural and rewarding
|
|
|
|
---
|
|
|
|
**Document History:**
|
|
- v1.0 (2025-11-16): Initial Phase 4 implementation plan created
|