# 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