first commit
This commit is contained in:
985
api/docs/STORY_PROGRESSION.md
Normal file
985
api/docs/STORY_PROGRESSION.md
Normal file
@@ -0,0 +1,985 @@
|
||||
# Story Progression System
|
||||
|
||||
**Status:** Active
|
||||
**Phase:** 4 (AI Integration + Story Progression)
|
||||
**Timeline:** Week 8 of Phase 4 (Days 8-14)
|
||||
**Last Updated:** November 23, 2025
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Story Progression System is the core single-player gameplay loop where players interact with the AI Dungeon Master through turn-based actions. Unlike combat (which uses structured mechanics), story progression allows players to explore the world, gather information, travel, and engage with narrative content.
|
||||
|
||||
**Key Principles:**
|
||||
- **Solo sessions only** - Story progression is single-player focused
|
||||
- **Button-based actions** - Structured prompts reduce AI costs and improve response quality
|
||||
- **Tier-based features** - Free tier has basic actions, paid tiers unlock more options
|
||||
- **Turn-based gameplay** - Player action → AI response → state update → repeat
|
||||
- **Quest integration** - Quests are offered during story turns based on context and location
|
||||
- **Location-based world** - Structured world with regions, locations, and travel
|
||||
- **Persistent NPCs** - Characters with personalities, knowledge, and relationship tracking
|
||||
|
||||
---
|
||||
|
||||
## World Structure
|
||||
|
||||
### Regions and Locations
|
||||
|
||||
The game world is organized hierarchically:
|
||||
|
||||
```
|
||||
World
|
||||
└── Regions (e.g., Crossville Province)
|
||||
└── Locations (e.g., Crossville Village, The Rusty Anchor Tavern)
|
||||
└── NPCs (e.g., Grom Ironbeard, Elara the Herbalist)
|
||||
```
|
||||
|
||||
**Regions** group related locations together for organizational purposes. Each region has:
|
||||
- Regional lore and atmosphere
|
||||
- List of contained locations
|
||||
- Region-wide events (future feature)
|
||||
|
||||
**Locations** are the atomic units of the world map:
|
||||
- Each location has a type (town, tavern, wilderness, dungeon, etc.)
|
||||
- Locations contain NPCs who reside there
|
||||
- Travel connections define which locations can be reached from where
|
||||
- Some locations are "starting locations" where new characters spawn
|
||||
|
||||
### Location Discovery
|
||||
|
||||
Players don't know about all locations initially. Locations are discovered through:
|
||||
|
||||
1. **Starting location**: All new characters begin at a designated starting location (Crossville Village)
|
||||
2. **Travel exploration**: Adjacent locations listed in `discoverable_locations`
|
||||
3. **NPC conversations**: NPCs can reveal hidden locations via `reveals_locations`
|
||||
4. **Story events**: AI narrative can unlock new destinations
|
||||
|
||||
**Example Location Discovery Flow:**
|
||||
```
|
||||
1. Player starts in Crossville Village
|
||||
└── Discovered: [crossville_village]
|
||||
|
||||
2. Player explores village, finds paths to tavern and market
|
||||
└── Discovered: [crossville_village, crossville_tavern, crossville_market]
|
||||
|
||||
3. Player talks to tavern keeper, learns about old mines
|
||||
└── Discovered: [..., crossville_old_mines]
|
||||
```
|
||||
|
||||
### Travel System
|
||||
|
||||
Travel moves players between discovered locations:
|
||||
|
||||
1. **Check available destinations**: `GET /api/v1/travel/available?session_id={id}`
|
||||
2. **Travel to location**: `POST /api/v1/travel` with `session_id` and `location_id`
|
||||
3. **AI narration**: Travel triggers narrative description of journey and arrival
|
||||
4. **State update**: Player's `current_location_id` updated, NPCs at new location available
|
||||
|
||||
**Travel Restrictions:**
|
||||
- Can only travel to discovered locations
|
||||
- Some locations may require prerequisites (quest completion, level, etc.)
|
||||
- Travel may trigger random encounters (future feature)
|
||||
|
||||
---
|
||||
|
||||
## NPC Interaction System
|
||||
|
||||
### NPC Overview
|
||||
|
||||
NPCs are persistent characters with rich data for AI dialogue generation:
|
||||
|
||||
- **Personality**: Traits, speech patterns, and quirks
|
||||
- **Knowledge**: What they know (public) and secrets they may reveal
|
||||
- **Relationships**: How they feel about other NPCs
|
||||
- **Inventory**: Items for sale (if merchant)
|
||||
- **Quest connections**: Quests they give and locations they reveal
|
||||
|
||||
### Talking to NPCs
|
||||
|
||||
When players interact with NPCs:
|
||||
|
||||
1. **NPC selection**: Player chooses NPC from location's NPC list
|
||||
2. **Initial greeting or response**: Player either greets NPC or responds to previous dialogue
|
||||
3. **AI prompt built**: Includes NPC personality, knowledge, relationship state, and conversation history
|
||||
4. **Dialogue generated**: AI creates in-character response
|
||||
5. **State updated**: Interaction tracked, dialogue exchange saved, secrets potentially revealed
|
||||
|
||||
**Initial Greeting Flow:**
|
||||
```
|
||||
Player: Clicks "Greet" on Grom Ironbeard
|
||||
└── Topic: "greeting"
|
||||
|
||||
System builds prompt with:
|
||||
- NPC personality (gruff, honest, protective)
|
||||
- NPC speech style (short sentences, dwarven expressions)
|
||||
- Public knowledge (tavern history, knows travelers)
|
||||
- Conditional knowledge (if relationship >= 70, mention goblins)
|
||||
- Current relationship level (65 - friendly)
|
||||
|
||||
AI generates response as Grom:
|
||||
"Welcome to the Rusty Anchor! What'll it be?"
|
||||
```
|
||||
|
||||
**Bidirectional Conversation Flow:**
|
||||
|
||||
Players can respond to NPC dialogue to continue conversations:
|
||||
|
||||
```
|
||||
Player: Types "Have you heard any rumors lately?"
|
||||
└── player_response: "Have you heard any rumors lately?"
|
||||
|
||||
System builds prompt with:
|
||||
- All personality/knowledge context (same as above)
|
||||
- Previous dialogue history (last 3 exchanges for context)
|
||||
- Player's current response
|
||||
|
||||
AI generates continuation as Grom:
|
||||
"Aye, I've heard things. *leans in* Strange folk been coming
|
||||
through lately. Watch yerself on the roads, friend."
|
||||
|
||||
System saves exchange:
|
||||
- player_line: "Have you heard any rumors lately?"
|
||||
- npc_response: "Aye, I've heard things..."
|
||||
- timestamp: "2025-11-24T10:30:00Z"
|
||||
```
|
||||
|
||||
**Conversation History Display:**
|
||||
|
||||
The UI shows the full conversation history with each NPC:
|
||||
- Previous exchanges are displayed with reduced opacity
|
||||
- Current exchange is highlighted
|
||||
- Player can type new responses to continue the conversation
|
||||
- Last 10 exchanges per NPC are stored for context
|
||||
|
||||
### Relationship Tracking
|
||||
|
||||
Each character-NPC pair has an `NPCInteractionState`:
|
||||
|
||||
| Relationship Level | Status | Effects |
|
||||
|--------------------|--------|---------|
|
||||
| 0-20 | Hostile | NPC refuses to help, may report player |
|
||||
| 21-40 | Unfriendly | Minimal assistance, no secrets |
|
||||
| 41-60 | Neutral | Normal interaction |
|
||||
| 61-80 | Friendly | Better prices, more information |
|
||||
| 81-100 | Trusted | Full access to secrets, special quests |
|
||||
|
||||
**Relationship modifiers:**
|
||||
- Successful interactions: +1 to +5 relationship
|
||||
- Failed attempts: -1 to -5 relationship
|
||||
- Quest completion for NPC: +10 to +20 relationship
|
||||
- Helping NPC's friends: +5 relationship
|
||||
- Harming NPC's allies: -10 to -20 relationship
|
||||
|
||||
### Secret Knowledge Reveals
|
||||
|
||||
NPCs can hold secrets that unlock under conditions:
|
||||
|
||||
```yaml
|
||||
knowledge:
|
||||
secret:
|
||||
- "Saw strange lights in the forest last week"
|
||||
will_share_if:
|
||||
- condition: "relationship_level >= 70"
|
||||
reveals: "Has heard rumors of goblins gathering in the old mines"
|
||||
- condition: "interaction_count >= 5"
|
||||
reveals: "Knows about a hidden cave behind the waterfall"
|
||||
```
|
||||
|
||||
When a condition is met:
|
||||
1. Secret index added to `revealed_secrets` list
|
||||
2. Information included in AI prompt context
|
||||
3. NPC "remembers" sharing this in future conversations
|
||||
|
||||
### Location Reveals from NPCs
|
||||
|
||||
NPCs can unlock new locations for players:
|
||||
|
||||
```yaml
|
||||
reveals_locations:
|
||||
- "crossville_old_mines"
|
||||
- "hidden_waterfall_cave"
|
||||
```
|
||||
|
||||
When an NPC reveals a location:
|
||||
1. Location ID added to character's `discovered_locations`
|
||||
2. Location becomes available for travel
|
||||
3. AI narration describes how player learned about it
|
||||
|
||||
---
|
||||
|
||||
## Gameplay Loop
|
||||
|
||||
### Turn Structure
|
||||
|
||||
```
|
||||
1. Display current state (location, quests, conversation history)
|
||||
2. Present available actions (buttons based on tier + context)
|
||||
3. Player selects action (or enters custom text if Premium/Elite)
|
||||
4. Action sent to backend API
|
||||
5. AI processes action and generates narrative response
|
||||
6. Game state updated (location changes, quest progress, etc.)
|
||||
7. Quest offering check (context-aware + location-based)
|
||||
8. Response displayed to player
|
||||
9. Next turn begins
|
||||
```
|
||||
|
||||
### Turn Flow Diagram
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Player Views Current State │
|
||||
│ - Location │
|
||||
│ - Active Quests │
|
||||
│ - Conversation History │
|
||||
└────────────┬────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ System Presents Action Options │
|
||||
│ - Tier-based button selection │
|
||||
│ - Context-aware prompts │
|
||||
│ - Custom input (Premium/Elite) │
|
||||
└────────────┬────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Player Selects Action │
|
||||
│ POST /api/v1/sessions/{id}/action │
|
||||
└────────────┬────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Backend Processing │
|
||||
│ - Validate action │
|
||||
│ - Build AI prompt (context) │
|
||||
│ - Call AI API (tier-based model) │
|
||||
│ - Parse response │
|
||||
└────────────┬────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Update Game State │
|
||||
│ - Location changes │
|
||||
│ - Quest progress │
|
||||
│ - Conversation history │
|
||||
└────────────┬────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Quest Offering Check │
|
||||
│ - Context-aware analysis │
|
||||
│ - Location-based roll │
|
||||
│ - Max 2 active quests limit │
|
||||
└────────────┬────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────┐
|
||||
│ Return Response to Player │
|
||||
│ - AI narrative text │
|
||||
│ - State changes │
|
||||
│ - Quest offered (if any) │
|
||||
└────────────┬────────────────────────┘
|
||||
│
|
||||
▼
|
||||
Next Turn
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Action System
|
||||
|
||||
### Action Categories
|
||||
|
||||
Actions are organized into three primary categories:
|
||||
|
||||
#### 1. Ask Questions
|
||||
Information-gathering actions that don't change location or trigger major events.
|
||||
|
||||
#### 2. Travel
|
||||
Movement actions that change the player's current location.
|
||||
|
||||
#### 3. Gather Information
|
||||
Social interaction actions that gather rumors, quests, and world knowledge.
|
||||
|
||||
### Tier-Based Action Availability
|
||||
|
||||
| Tier | Action Count | Free-Form Input | AI Model | Cost/Turn |
|
||||
|------|--------------|-----------------|----------|-----------|
|
||||
| **Free** | 4 basic actions | ❌ No | Replicate (free tier) | $0 |
|
||||
| **Basic** | 4 basic actions | ❌ No | Claude Haiku | ~$0.01 |
|
||||
| **Premium** | 7 actions | ✅ Yes (250 chars) | Claude Sonnet | ~$0.03 |
|
||||
| **Elite** | 10 actions | ✅ Yes (500 chars) | Claude Opus | ~$0.10 |
|
||||
|
||||
### Action Prompt Definitions
|
||||
|
||||
#### Free Tier Actions (4 total)
|
||||
|
||||
**Ask Questions:**
|
||||
1. **"What do I see around me?"** - Describe current location and visible surroundings
|
||||
2. **"Are there any dangers nearby?"** - Check for enemies, hazards, or threats
|
||||
|
||||
**Travel:**
|
||||
3. **"Travel to [dropdown: known towns]"** - Fast travel to discovered towns
|
||||
4. **"Explore the area"** - Random exploration in current region
|
||||
|
||||
#### Premium Tier Additional Actions (+3 more, 7 total)
|
||||
|
||||
**Ask Questions:**
|
||||
5. **"What do I remember about this place?"** - Recall location history (ties to Memory Thief origin)
|
||||
|
||||
**Gather Information:**
|
||||
6. **"Ask around the streets"** - Gather rumors from common folk (town/city only)
|
||||
7. **"Visit the local tavern"** - Gather information and hear tales (town/city only)
|
||||
|
||||
#### Elite Tier Additional Actions (+3 more, 10 total)
|
||||
|
||||
**Ask Questions:**
|
||||
8. **"Search for hidden secrets"** - Thorough investigation of area (may find loot or lore)
|
||||
|
||||
**Gather Information:**
|
||||
9. **"Seek out the town elder"** - Get official information and potential quests (town/city only)
|
||||
|
||||
**Travel:**
|
||||
10. **"Chart a course to unexplored lands"** - Discover new locations (wilderness only)
|
||||
|
||||
#### Premium/Elite: Custom Free-Form Input
|
||||
|
||||
**"Ask the DM a custom question"** (Premium/Elite only)
|
||||
- Premium: 250 character limit
|
||||
- Elite: 500 character limit
|
||||
- Examples: "I want to climb the tower and look for a vantage point", "Can I search the abandoned house for clues?"
|
||||
|
||||
---
|
||||
|
||||
## Action Prompt Data Model
|
||||
|
||||
### ActionPrompt Dataclass
|
||||
|
||||
```python
|
||||
@dataclass
|
||||
class ActionPrompt:
|
||||
"""Represents a button-based action prompt available to players."""
|
||||
|
||||
prompt_id: str # Unique identifier (e.g., "ask_surroundings")
|
||||
category: str # "ask", "travel", "gather"
|
||||
display_text: str # Button text shown to player
|
||||
description: str # Tooltip/help text
|
||||
tier_required: str # "free", "basic", "premium", "elite"
|
||||
context_filter: Optional[str] # "town", "wilderness", "any" (where action is available)
|
||||
dm_prompt_template: str # Jinja2 template for AI prompt
|
||||
|
||||
def is_available(self, user_tier: str, location_type: str) -> bool:
|
||||
"""Check if this action is available to the user."""
|
||||
# Check tier requirement
|
||||
tier_hierarchy = ["free", "basic", "premium", "elite"]
|
||||
if tier_hierarchy.index(user_tier) < tier_hierarchy.index(self.tier_required):
|
||||
return False
|
||||
|
||||
# Check context filter
|
||||
if self.context_filter and self.context_filter != "any":
|
||||
return location_type == self.context_filter
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
### YAML Action Definitions
|
||||
|
||||
Actions will be defined in `/app/data/action_prompts.yaml`:
|
||||
|
||||
```yaml
|
||||
actions:
|
||||
- prompt_id: "ask_surroundings"
|
||||
category: "ask"
|
||||
display_text: "What do I see around me?"
|
||||
description: "Get a description of your current surroundings"
|
||||
tier_required: "free"
|
||||
context_filter: "any"
|
||||
dm_prompt_template: |
|
||||
The player is currently in {{ location_name }}.
|
||||
Describe what they see, hear, and sense around them.
|
||||
Include atmosphere, notable features, and any immediate points of interest.
|
||||
|
||||
- prompt_id: "check_dangers"
|
||||
category: "ask"
|
||||
display_text: "Are there any dangers nearby?"
|
||||
description: "Check for threats, enemies, or hazards in the area"
|
||||
tier_required: "free"
|
||||
context_filter: "any"
|
||||
dm_prompt_template: |
|
||||
The player is in {{ location_name }} and wants to know about nearby dangers.
|
||||
Assess the area for threats: enemies, traps, environmental hazards.
|
||||
Be honest about danger level but maintain narrative tension.
|
||||
|
||||
- prompt_id: "travel_town"
|
||||
category: "travel"
|
||||
display_text: "Travel to..."
|
||||
description: "Fast travel to a known town or city"
|
||||
tier_required: "free"
|
||||
context_filter: "any"
|
||||
dm_prompt_template: |
|
||||
The player is traveling from {{ current_location }} to {{ destination }}.
|
||||
Describe a brief travel montage. Include:
|
||||
- Journey description (terrain, weather)
|
||||
- Any minor encounters or observations
|
||||
- Arrival at destination
|
||||
|
||||
- prompt_id: "explore_area"
|
||||
category: "travel"
|
||||
display_text: "Explore the area"
|
||||
description: "Wander and explore your current region"
|
||||
tier_required: "free"
|
||||
context_filter: "any"
|
||||
dm_prompt_template: |
|
||||
The player explores {{ location_name }}.
|
||||
Generate a random encounter or discovery:
|
||||
- 30% chance: Minor combat encounter
|
||||
- 30% chance: Interesting location/landmark
|
||||
- 20% chance: NPC interaction
|
||||
- 20% chance: Nothing significant
|
||||
|
||||
- prompt_id: "recall_memory"
|
||||
category: "ask"
|
||||
display_text: "What do I remember about this place?"
|
||||
description: "Recall memories or knowledge about current location"
|
||||
tier_required: "premium"
|
||||
context_filter: "any"
|
||||
dm_prompt_template: |
|
||||
The player tries to recall memories about {{ location_name }}.
|
||||
{% if character.origin == "memory_thief" %}
|
||||
The player has fragmented memories due to their amnesia.
|
||||
Provide vague, incomplete recollections that hint at a past here.
|
||||
{% else %}
|
||||
Provide relevant historical knowledge or personal memories.
|
||||
{% endif %}
|
||||
|
||||
- prompt_id: "gather_street_rumors"
|
||||
category: "gather"
|
||||
display_text: "Ask around the streets"
|
||||
description: "Talk to common folk and gather local rumors"
|
||||
tier_required: "premium"
|
||||
context_filter: "town"
|
||||
dm_prompt_template: |
|
||||
The player mingles with townsfolk in {{ location_name }}.
|
||||
Generate 2-3 local rumors or pieces of information:
|
||||
- Local events or concerns
|
||||
- Potential quest hooks
|
||||
- World lore or history
|
||||
|
||||
- prompt_id: "visit_tavern"
|
||||
category: "gather"
|
||||
display_text: "Visit the local tavern"
|
||||
description: "Gather information and hear tales at the tavern"
|
||||
tier_required: "premium"
|
||||
context_filter: "town"
|
||||
dm_prompt_template: |
|
||||
The player enters the tavern in {{ location_name }}.
|
||||
Describe the atmosphere and provide:
|
||||
- Overheard conversations
|
||||
- Rumors and tales
|
||||
- Potential quest opportunities
|
||||
- NPC interactions
|
||||
|
||||
- prompt_id: "search_secrets"
|
||||
category: "ask"
|
||||
display_text: "Search for hidden secrets"
|
||||
description: "Thoroughly investigate the area for hidden items or lore"
|
||||
tier_required: "elite"
|
||||
context_filter: "any"
|
||||
dm_prompt_template: |
|
||||
The player conducts a thorough search of {{ location_name }}.
|
||||
Roll for discovery (60% chance of finding something):
|
||||
- Hidden items or treasures
|
||||
- Secret passages or areas
|
||||
- Lore fragments or journals
|
||||
- Environmental storytelling clues
|
||||
|
||||
- prompt_id: "seek_elder"
|
||||
category: "gather"
|
||||
display_text: "Seek out the town elder"
|
||||
description: "Get official information and guidance from town leadership"
|
||||
tier_required: "elite"
|
||||
context_filter: "town"
|
||||
dm_prompt_template: |
|
||||
The player seeks audience with the town elder of {{ location_name }}.
|
||||
The elder provides:
|
||||
- Official town history and current situation
|
||||
- Important quests or tasks
|
||||
- Warnings about regional threats
|
||||
- Access to restricted information
|
||||
|
||||
- prompt_id: "chart_course"
|
||||
category: "travel"
|
||||
display_text: "Chart a course to unexplored lands"
|
||||
description: "Venture into unknown territory to discover new locations"
|
||||
tier_required: "elite"
|
||||
context_filter: "wilderness"
|
||||
dm_prompt_template: |
|
||||
The player ventures into unexplored territory from {{ location_name }}.
|
||||
Generate a new location discovery:
|
||||
- Name and type of location
|
||||
- Initial description
|
||||
- Potential dangers or opportunities
|
||||
- Add to player's discovered locations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Session Management
|
||||
|
||||
### Solo Session Creation
|
||||
|
||||
**Endpoint:** `POST /api/v1/sessions`
|
||||
|
||||
**Request Body:**
|
||||
```json
|
||||
{
|
||||
"character_id": "char_abc123",
|
||||
"session_type": "solo"
|
||||
}
|
||||
```
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"session_id": "session_xyz789",
|
||||
"character_id": "char_abc123",
|
||||
"session_type": "solo",
|
||||
"current_location": "thornfield_plains",
|
||||
"turn_number": 0,
|
||||
"status": "active",
|
||||
"created_at": "2025-11-16T10:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Taking a Story Action
|
||||
|
||||
**Endpoint:** `POST /api/v1/sessions/{session_id}/action`
|
||||
|
||||
**Request Body (Button Action):**
|
||||
```json
|
||||
{
|
||||
"action_type": "prompt",
|
||||
"prompt_id": "ask_surroundings"
|
||||
}
|
||||
```
|
||||
|
||||
**Request Body (Custom Free-Form - Premium/Elite Only):**
|
||||
```json
|
||||
{
|
||||
"action_type": "custom",
|
||||
"custom_text": "I want to climb the old tower and scout the horizon"
|
||||
}
|
||||
```
|
||||
|
||||
**Request Body (Ask DM - Out-of-Character Question):**
|
||||
```json
|
||||
{
|
||||
"action_type": "ask_dm",
|
||||
"question": "Is there anyone around who might see me steal this item?"
|
||||
}
|
||||
```
|
||||
|
||||
> **Note:** Ask DM questions are informational only. They don't advance the story, increment the turn number, or cause game state changes. They use a separate rate limit from regular actions.
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"session_id": "session_xyz789",
|
||||
"turn_number": 1,
|
||||
"action_taken": "What do I see around me?",
|
||||
"dm_response": "You stand in the windswept Thornfield Plains, tall grasses swaying in the breeze. To the north, you can make out the silhouette of a crumbling watchtower against the grey sky. The air smells of rain and distant smoke. A worn path leads east toward what might be a settlement.",
|
||||
"state_changes": {
|
||||
"discovered_locations": ["old_watchtower"]
|
||||
},
|
||||
"quest_offered": null,
|
||||
"ai_cost": 0.025,
|
||||
"timestamp": "2025-11-16T10:01:23Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Getting Session State
|
||||
|
||||
**Endpoint:** `GET /api/v1/sessions/{session_id}`
|
||||
|
||||
**Response:**
|
||||
```json
|
||||
{
|
||||
"session_id": "session_xyz789",
|
||||
"character_id": "char_abc123",
|
||||
"session_type": "solo",
|
||||
"current_location": "thornfield_plains",
|
||||
"location_type": "wilderness",
|
||||
"turn_number": 5,
|
||||
"status": "active",
|
||||
"active_quests": ["quest_001"],
|
||||
"discovered_locations": ["thornfield_plains", "old_watchtower", "riverwatch"],
|
||||
"conversation_history": [
|
||||
{
|
||||
"turn": 1,
|
||||
"action": "What do I see around me?",
|
||||
"dm_response": "You stand in the windswept Thornfield Plains..."
|
||||
}
|
||||
],
|
||||
"available_actions": [
|
||||
{
|
||||
"prompt_id": "ask_surroundings",
|
||||
"display_text": "What do I see around me?",
|
||||
"category": "ask"
|
||||
},
|
||||
{
|
||||
"prompt_id": "check_dangers",
|
||||
"display_text": "Are there any dangers nearby?",
|
||||
"category": "ask"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AI Prompt Engineering
|
||||
|
||||
### Context Building
|
||||
|
||||
Each player action requires building a comprehensive prompt for the AI Dungeon Master:
|
||||
|
||||
```python
|
||||
def build_dm_prompt(session: GameSession, character: Character, action: ActionPrompt, custom_text: Optional[str] = None) -> str:
|
||||
"""Build the AI prompt with full context."""
|
||||
|
||||
context = {
|
||||
# Character info
|
||||
"character_name": character.name,
|
||||
"character_class": character.player_class.name,
|
||||
"character_level": character.level,
|
||||
"character_origin": character.origin,
|
||||
|
||||
# Location info
|
||||
"location_name": session.game_state.current_location,
|
||||
"location_type": get_location_type(session.game_state.current_location),
|
||||
"discovered_locations": session.game_state.discovered_locations,
|
||||
|
||||
# Quest info
|
||||
"active_quests": get_quest_details(session.game_state.active_quests),
|
||||
|
||||
# Recent history (last 3 turns)
|
||||
"recent_history": session.conversation_history[-3:],
|
||||
|
||||
# Player action
|
||||
"player_action": custom_text or action.display_text,
|
||||
}
|
||||
|
||||
# Render base prompt template
|
||||
if custom_text:
|
||||
prompt = render_custom_action_prompt(context, custom_text)
|
||||
else:
|
||||
prompt = render_template(action.dm_prompt_template, context)
|
||||
|
||||
return prompt
|
||||
```
|
||||
|
||||
### AI Model Selection by Tier
|
||||
|
||||
```python
|
||||
def get_ai_model_for_tier(user_tier: str) -> str:
|
||||
"""Select AI model based on user subscription tier."""
|
||||
model_map = {
|
||||
"free": "replicate/llama-3-8b", # Free tier model
|
||||
"basic": "claude-haiku", # Fast, cheap
|
||||
"premium": "claude-sonnet", # Balanced
|
||||
"elite": "claude-opus" # Best quality
|
||||
}
|
||||
return model_map.get(user_tier, "replicate/llama-3-8b")
|
||||
```
|
||||
|
||||
### Response Parsing and Item Extraction
|
||||
|
||||
AI responses include both narrative text and structured game actions. The system automatically parses responses to extract items, gold, and experience to apply to the player's character.
|
||||
|
||||
**Response Format:**
|
||||
|
||||
The AI returns narrative followed by a structured `---GAME_ACTIONS---` block:
|
||||
|
||||
```
|
||||
The kind vendor smiles and presses a few items into your hands...
|
||||
|
||||
---GAME_ACTIONS---
|
||||
{
|
||||
"items_given": [
|
||||
{"name": "Stale Bread", "type": "consumable", "description": "A half-loaf of bread", "value": 1},
|
||||
{"item_id": "health_potion"}
|
||||
],
|
||||
"items_taken": [],
|
||||
"gold_given": 5,
|
||||
"gold_taken": 0,
|
||||
"experience_given": 10,
|
||||
"quest_offered": null,
|
||||
"quest_completed": null,
|
||||
"location_change": null
|
||||
}
|
||||
```
|
||||
|
||||
**Item Grant Types:**
|
||||
|
||||
1. **Existing Items** (by `item_id`): References items from the game data registry
|
||||
2. **Generic Items** (by name/type/description): Creates simple mundane items
|
||||
|
||||
**Item Validation:**
|
||||
|
||||
All AI-granted items are validated before being added to inventory:
|
||||
|
||||
- **Level requirements**: Items requiring higher level than character are rejected
|
||||
- **Class restrictions**: Class-specific items validated against character class
|
||||
- **Validation logging**: Failed items logged for review but don't fail the request
|
||||
|
||||
**Processing Flow:**
|
||||
|
||||
```python
|
||||
# 1. Parse AI response
|
||||
parsed_response = parse_ai_response(ai_response.narrative)
|
||||
|
||||
# 2. Validate and resolve items
|
||||
for item_grant in parsed_response.game_changes.items_given:
|
||||
item, error = validator.validate_and_resolve_item(item_grant, character)
|
||||
if item:
|
||||
character.add_item(item)
|
||||
|
||||
# 3. Apply gold/experience
|
||||
character.add_gold(parsed_response.game_changes.gold_given)
|
||||
character.add_experience(parsed_response.game_changes.experience_given)
|
||||
|
||||
# 4. Save to database
|
||||
```
|
||||
|
||||
**Generic Item Templates:**
|
||||
|
||||
Common mundane items have predefined templates in `/app/data/generic_items.yaml`:
|
||||
- Light sources: torch, lantern, candle
|
||||
- Food: bread, cheese, rations, ale
|
||||
- Tools: rope, flint, bedroll, crowbar
|
||||
- Supplies: ink, parchment, bandages
|
||||
|
||||
---
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### Story Gameplay Screen Layout
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ Code of Conquest [Char] [Logout] │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 📍 Current Location: Thornfield Plains │
|
||||
│ 🎯 Active Quests: 1 │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Conversation History │
|
||||
│ ┌───────────────────────────────────────────────┐ │
|
||||
│ │ │ │
|
||||
│ │ Turn 1 │ │
|
||||
│ │ You: What do I see around me? │ │
|
||||
│ │ │ │
|
||||
│ │ DM: You stand in the windswept Thornfield │ │
|
||||
│ │ Plains, tall grasses swaying in the breeze. │ │
|
||||
│ │ To the north, you can make out the │ │
|
||||
│ │ silhouette of a crumbling watchtower... │ │
|
||||
│ │ │ │
|
||||
│ │ Turn 2 │ │
|
||||
│ │ You: Explore the area │ │
|
||||
│ │ │ │
|
||||
│ │ DM: As you wander through the plains, you │ │
|
||||
│ │ stumble upon fresh tracks leading north... │ │
|
||||
│ │ │ │
|
||||
│ └───────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Take Action │
|
||||
│ │
|
||||
│ Ask Questions: │
|
||||
│ [What do I see?] [Any dangers?] │
|
||||
│ [What do I remember?] 🔒Premium │
|
||||
│ │
|
||||
│ Travel: │
|
||||
│ [Travel to ▼] [Explore area] │
|
||||
│ │
|
||||
│ Gather Information: │
|
||||
│ [Ask around streets] 🔒Premium │
|
||||
│ [Visit tavern] 🔒Premium │
|
||||
│ │
|
||||
│ Custom (Premium/Elite): │
|
||||
│ ┌───────────────────────────────────────────────┐ │
|
||||
│ │ I want to... │ │
|
||||
│ └───────────────────────────────────────────────┘ │
|
||||
│ [Ask the DM] (250 chars) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Action Button States
|
||||
|
||||
- **Available**: Full color, clickable
|
||||
- **Tier-locked**: Greyed out with 🔒 icon and "Premium" or "Elite" badge
|
||||
- **Context-locked**: Hidden (e.g., "Visit tavern" not shown in wilderness)
|
||||
- **Loading**: Disabled with spinner during AI processing
|
||||
|
||||
### Conversation History Display
|
||||
|
||||
- Scrollable container with newest at bottom
|
||||
- Turn numbers clearly visible
|
||||
- Player actions in different style from DM responses
|
||||
- Quest notifications highlighted
|
||||
- Location changes noted
|
||||
- Timestamps optional (show on hover)
|
||||
|
||||
---
|
||||
|
||||
## Cost Management
|
||||
|
||||
### Per-Turn Cost Estimates
|
||||
|
||||
| Tier | AI Model | Est. Tokens | Cost/Turn | Daily Limit (Turns) |
|
||||
|------|----------|-------------|-----------|---------------------|
|
||||
| Free | Replicate Llama-3 8B | ~500 | $0 | Unlimited |
|
||||
| Basic | Claude Haiku | ~800 | ~$0.01 | 200 turns/day |
|
||||
| Premium | Claude Sonnet | ~1200 | ~$0.03 | 100 turns/day |
|
||||
| Elite | Claude Opus | ~1500 | ~$0.10 | 50 turns/day |
|
||||
|
||||
### Cost Control Measures
|
||||
|
||||
1. **Token limits**: Cap AI responses at 500 tokens (narrative should be concise)
|
||||
2. **Daily turn limits**: Prevent abuse and runaway costs
|
||||
3. **Prompt caching**: Cache location descriptions, character context (Phase 4B)
|
||||
4. **Model selection**: Free tier uses free model, paid tiers get better quality
|
||||
5. **Monitoring**: Track per-user AI spending daily
|
||||
|
||||
---
|
||||
|
||||
## Implementation Timeline
|
||||
|
||||
### Week 8 of Phase 4 (Days 8-14)
|
||||
|
||||
**Day 8: Action Prompt System**
|
||||
- Create ActionPrompt dataclass
|
||||
- Create ActionPromptLoader (YAML-based)
|
||||
- Define all 10 action prompts in `action_prompts.yaml`
|
||||
- Write unit tests for action availability logic
|
||||
|
||||
**Day 9: Session Service**
|
||||
- Create SessionService (create solo sessions, manage state)
|
||||
- Session creation logic
|
||||
- Session state retrieval
|
||||
- Turn advancement logic
|
||||
- Database operations
|
||||
|
||||
**Day 10: Story Progression API**
|
||||
- Implement story action endpoints:
|
||||
- `POST /api/v1/sessions` - Create solo session
|
||||
- `GET /api/v1/sessions/{id}` - Get session state
|
||||
- `POST /api/v1/sessions/{id}/action` - Take action
|
||||
- `GET /api/v1/sessions/{id}/history` - Get conversation history
|
||||
- Validate actions based on tier and context
|
||||
- Integrate with AI service (from Week 7)
|
||||
|
||||
**Day 11-12: Story Progression UI**
|
||||
- Create `templates/game/story.html` - Main story gameplay screen
|
||||
- Action button rendering (tier-based, context-aware)
|
||||
- Conversation history display (scrollable, formatted)
|
||||
- HTMX integration for real-time turn updates
|
||||
- Loading states during AI processing
|
||||
|
||||
**Day 13-14: Integration Testing**
|
||||
- Test full story turn flow
|
||||
- Test tier restrictions
|
||||
- Test action context filtering
|
||||
- Test AI prompt building and response parsing
|
||||
- Test session state persistence
|
||||
- Performance testing (response times, cost tracking)
|
||||
|
||||
---
|
||||
|
||||
## Testing Criteria
|
||||
|
||||
### Unit Tests
|
||||
- ✅ ActionPrompt.is_available() logic
|
||||
- ✅ ActionPromptLoader loads all prompts correctly
|
||||
- ✅ Tier hierarchy validation
|
||||
- ✅ Context filtering (town vs wilderness)
|
||||
|
||||
### Integration Tests
|
||||
- ✅ Create solo session
|
||||
- ✅ Take action (button-based)
|
||||
- ✅ Take action (custom text, Premium/Elite)
|
||||
- ✅ Verify tier restrictions enforced
|
||||
- ✅ Verify context filtering works
|
||||
- ✅ Verify conversation history persists
|
||||
- ✅ Verify AI cost tracking
|
||||
- ✅ Verify daily turn limits
|
||||
|
||||
### Manual Testing
|
||||
- ✅ Full story turn flow (10+ turns)
|
||||
- ✅ All action types tested
|
||||
- ✅ Custom text input (Premium/Elite)
|
||||
- ✅ UI responsiveness
|
||||
- ✅ Conversation history display
|
||||
- ✅ Quest offering during turns (tested in Quest System phase)
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- ✅ All 10 action prompts defined and loadable from YAML
|
||||
- ✅ Action availability logic working (tier + context)
|
||||
- ✅ Solo session creation and management functional
|
||||
- ✅ Story action API endpoints operational
|
||||
- ✅ AI prompt building with full context
|
||||
- ✅ AI responses parsed and state updated correctly
|
||||
- ✅ Story gameplay UI functional with HTMX
|
||||
- ✅ Tier restrictions enforced (Free vs Premium vs Elite)
|
||||
- ✅ Custom text input working for Premium/Elite tiers
|
||||
- ✅ Conversation history persisted and displayed
|
||||
- ✅ Cost tracking and daily limits enforced
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancements (Post-MVP)
|
||||
|
||||
### Phase 13+
|
||||
- **Dynamic action generation**: AI suggests context-specific actions
|
||||
- **Voice narration**: Text-to-speech for DM responses (Elite tier)
|
||||
- **Branching narratives**: Major story choices with consequences
|
||||
- **Location memory**: AI remembers previous visits and descriptions
|
||||
- ~~**NPC persistence**: Recurring NPCs with memory of past interactions~~ ✅ Implemented
|
||||
- **Session sharing**: Export/share story sessions as Markdown
|
||||
- **Illustrations**: AI-generated scene images (Elite tier)
|
||||
- **Random encounters**: Travel between locations may trigger events
|
||||
- **NPC schedules**: NPCs move between locations based on time of day
|
||||
|
||||
---
|
||||
|
||||
## Related Documentation
|
||||
|
||||
- **[QUEST_SYSTEM.md](QUEST_SYSTEM.md)** - Quest offering and tracking during story turns
|
||||
- **[GAME_SYSTEMS.md](GAME_SYSTEMS.md)** - Combat system (separate turn-based gameplay)
|
||||
- **[DATA_MODELS.md](DATA_MODELS.md)** - GameSession, ConversationEntry, ActionPrompt, Location, NPC models
|
||||
- **[API_REFERENCE.md](API_REFERENCE.md)** - Full API endpoint documentation (includes Travel and NPC APIs)
|
||||
- **[ROADMAP.md](ROADMAP.md)** - Phase 4 timeline and milestones
|
||||
|
||||
### Data Files
|
||||
- `/app/data/regions/crossville.yaml` - Crossville region locations
|
||||
- `/app/data/npcs/crossville_npcs.yaml` - NPCs in Crossville region
|
||||
- `/app/data/action_prompts.yaml` - Player action definitions
|
||||
|
||||
### Services
|
||||
- `LocationLoader` - Loads and caches location/region data
|
||||
- `NPCLoader` - Loads and caches NPC definitions
|
||||
- `SessionService` - Manages game sessions and state
|
||||
|
||||
---
|
||||
|
||||
**Document Version:** 1.2
|
||||
**Created:** November 16, 2025
|
||||
**Last Updated:** November 24, 2025
|
||||
Reference in New Issue
Block a user