Skip to main content
Glama
HANDOFF_LIVING_WORLD_PHASE_2_OUTLINE.md14.6 kB
# HANDOFF: Living World - Phase 2 (Social Hearing Mechanics) - OUTLINE ONLY **Status:** OUTLINE FOR FUTURE IMPLEMENTATION **Dependencies:** Phase 1 + blocking issue fixes (EDGE-001, EDGE-006) **Estimated Time:** 4-5 days with tests --- ## ⚠️ DO NOT START THIS YET This is a **forward-looking document** created for planning purposes. **Prerequisites that must be completed first:** 1. ✅ Phase 1 (Spatial Graph) - MUST BE DONE 2. ✅ EDGE-001 fix (self-theft prevention) - MUST BE DONE 3. ✅ EDGE-006 fix (victim/fence conflict) - MUST BE DONE 4. ⏳ Game design decisions finalized (hearing ranges, etc.) - MUST DECIDE **Only start Phase 2 after all prerequisites are complete and approved.** --- ## PHASE 2 OBJECTIVE Implement spatial-aware social mechanics where: - NPCs can hear conversations based on distance and volume - Stealth vs Perception rolls determine if overhearing succeeds - Conversation memories are recorded only for those who should hear - Environment affects hearing ranges (tavern noise vs forest silence) ### PLAYER EXPERIENCE **Before Phase 2:** ``` Rogue: "I whisper to the Wizard about picking the lock" Everyone in the party hears (no distance awareness) The Barbarian 30 feet away somehow heard the secret plan ``` **After Phase 2:** ``` Rogue whispers (WHISPER volume at target) → Wizard hears (always) → Nearby NPCs within 5 feet hear (Stealth vs Perception) → Barbarian 30 feet away hears nothing (outside whisper range) ``` --- ## PHASE 2 DELIVERABLES (OVERVIEW) ### 1. Hearing Radius Engine **File:** `src/engine/social/hearing.ts` ```typescript interface HearingRangeConfig { volume: 'WHISPER' | 'TALK' | 'SHOUT'; environment: RoomNode; // For atmospheric effects listenerPerception: number; // WIS modifier } function calculateHearingRadius(config: HearingRangeConfig): number { // Returns feet-based hearing radius // Accounts for volume, atmospherics, perception } ``` **Game Design Decisions Needed:** - Option A: Linear (Whisper = 5ft, Talk = 30ft, Shout = 100ft) - Option B: Perception-based (base + WIS modifier) - Option C: Environment-aware (varies by room atmospherics) - Option D: Complex (combines all factors) **Recommendation:** Option C for immersion ### 2. Extended NPC Memory Tools **File:** `src/server/npc-memory-tools.ts` (extend existing) **New Tool:** `interact_socially` ```typescript interface InteractSociallyInput { speaker_id: string; // UUID of speaker target_id?: string; // UUID of intended target (optional for broadcast) content: string; // What they're saying volume: 'WHISPER' | 'TALK' | 'SHOUT'; intent: string; // 'gossip', 'interrogate', 'negotiate', etc. } interface InteractSociallyOutput { success: boolean; targetHeard: boolean; eavesdroppers: Array<{ listenerId: string; heardFully: boolean; // true if target, false if eavesdropped stealthCheck?: { speakerRoll: number; listenerRoll: number; success: boolean; }; }>; recordedMemories: number; // How many characters recorded this? } ``` **Logic:** 1. Validate speaker exists and is in a room 2. Get room and all entities in room + adjacent rooms 3. For target: Record full conversation 4. For eavesdroppers: Roll Stealth (speaker) vs Perception (listener) - Success: Record "overheard conversation about..." - Fail: Record "saw whispering but couldn't hear content" 5. Store in conversation_memories for each listener ### 3. Helper: Stealth vs Perception Opposed Roll **Location:** `src/engine/social/stealth-perception.ts` ```typescript interface OpposedRollResult { speakerRoll: number; speakerModifier: number; speakerTotal: number; listenerRoll: number; listenerModifier: number; listenerTotal: number; success: boolean; // listenerTotal >= speakerTotal margin: number; // How much they beat it by } function rollStealthVsPerception( speaker: Character, // For stealth check listener: Character, // For perception check baseModifier: number = 0 // From environment ): OpposedRollResult { // Roll d20 for each // Apply DEX modifier for speaker (stealth) // Apply WIS modifier for listener (perception) // Compare totals // Return result } ``` ### 4. Test Suite **File:** `tests/social-hearing.test.ts` **Must cover:** - [ ] Whisper heard only by target + adjacent characters - [ ] Shout heard across entire location - [ ] Stealth roll beats low Perception (common case) - [ ] Perception beats low Stealth (opposite case) - [ ] Target ALWAYS hears full message (no roll) - [ ] Eavesdroppers get "overheard" log, not full content - [ ] SILENCE atmosphere reduces hearing range - [ ] Multiple listeners with different Perception scores - [ ] Adjacent room boundaries (hearing through walls?) - [ ] Conversation memory records only what should be heard - [ ] Same conversation appears in different listeners' memory with different content - [ ] Environment modifiers affect hearing rolls - [ ] No hearing through obstacles (walls, closed doors) - [ ] DARKNESS doesn't affect hearing (sound still works) - [ ] Integration with Phase 1 rooms ### 5. Integration Points **Need to connect with existing systems:** 1. **NPC Memory System** - Record in `conversation_memories` table - Use existing `record_conversation_memory` tool - Link with relationship/importance tracking 2. **Spatial System (Phase 1)** - Query room for entities - Check room atmospherics - Determine hearing ranges based on room type 3. **Character Stats** - DEX modifier for Stealth - WIS modifier for Perception - Conditions that affect hearing (deafness, etc.) 4. **Combat System** - Could integrate with surprise/stealth checks in combat --- ## PHASE 2 RISKS & MITIGATIONS ### Risk 1: Complex Opposed Rolls **Problem:** Stealth vs Perception can be confusing with many listeners **Mitigation:** - Resolve all rolls server-side (LLM doesn't see rolls) - Return clean output: "Overheard" vs "Heard nothing" - Log rolls in conversation_memories for DM review ### Risk 2: Performance with Many Listeners **Problem:** If 20 NPCs in same room, checking hearing for each is slow **Mitigation:** - Query only entities in room + adjacent rooms - Cache hearing radius calculations - Batch roll calculations ### Risk 3: Immersion Breaking **Problem:** "You hear the Barbarian whisper to himself" **Mitigation:** - Whisper range so small (5 ft) that many characters won't hear - SILENCE atmosphere can prevent eavesdropping entirely - DM can design room layout to prevent unwanted overhearing ### Risk 4: Game Design Decisions Not Made **Problem:** Without knowing hearing ranges, can't implement correctly **Mitigation:** - Document 4 options in TRIAGE document - Get approval before starting implementation - Make ranges configurable (easy to tweak later) --- ## GAME DESIGN DECISIONS FOR PHASE 2 These MUST be decided before implementation starts: ### Decision 1: Base Hearing Ranges ``` Option A: Fixed (Easy to implement) Whisper: 5 feet Talk: 30 feet Shout: 100 feet Option B: Perception-based (More complex) Whisper: 5 + listener.wis_modifier (can be 0-20 feet) Talk: 30 + listener.wis_modifier (can be 20-50 feet) Shout: 100 + listener.wis_modifier (can be 90-120 feet) Option C: Environment-aware (Most immersive) Tavern (loud): Whisper=0, Talk=10, Shout=30 Forest (quiet): Whisper=10, Talk=60, Shout=300 Desert (open): Whisper=15, Talk=100, Shout=500 Option D: Hybrid (Complex) Base range from environment + Listener.wis_modifier - Speaker.dex_modifier (stealth helps) ``` **RECOMMENDATION:** Option C (environment-aware) ### Decision 2: Stealth Bonus for Whispering ``` Should a character get bonus to Stealth when whispering? Option A: No (stealth is separate from volume) Whisper requires low Perception check but no stealth roll Option B: Yes, minor (+2 bonus) Whispering gives +2 to hide attempts Option C: Yes, major (+5 bonus) Whispering significantly harder to detect Option D: Automatic (no roll for whispers) If distance > hearing range, automatic success ``` **RECOMMENDATION:** Option A (separate concerns) ### Decision 3: Adjacent Room Hearing ``` Can characters in adjacent rooms hear conversations? Option A: No (walls block sound) Closed doors prevent all hearing Option B: Partial (sound travels through walls) Loud shouts heard in adjacent rooms, whispers don't Option C: Yes, with penalty Adjacent room listeners take -5 penalty to perception Option D: Complex (depends on door type) Open doors allow hearing, closed doors block, thin walls give -5 ``` **RECOMMENDATION:** Option B (realistic, balanced) ### Decision 4: SILENCE Atmosphere Effect ``` What does SILENCE do to hearing? Option A: Complete silence No sounds heard at all (even shouts) Option B: Suppressed ranges All hearing ranges reduced to 50% Option C: Magical silence Can't hear specific target(s), others OK Option D: Configurable SILENCE can specify what's affected ``` **RECOMMENDATION:** Option B (50% reduction) --- ## PHASE 2 IMPLEMENTATION SEQUENCE 1. **Game Design Review** (4 hours) - Finalize hearing ranges - Finalize stealth/perception rules - Document as config constants 2. **Hearing Engine** (1 day) - Create `src/engine/social/hearing.ts` - Implement `calculateHearingRadius()` - Unit tests for radius calculations 3. **Stealth vs Perception** (0.5 day) - Create `src/engine/social/stealth-perception.ts` - Implement opposed roll logic - Unit tests for roll mechanics 4. **MCP Tool** (1 day) - Create `interact_socially` in npc-memory-tools.ts - Implement witness loop - Register in MCP registry 5. **Integration** (1 day) - Connect to Phase 1 rooms - Connect to character stats - Connect to memory system 6. **Testing** (1 day) - Write 20+ integration tests - Test all scenarios in test suite section - Test edge cases 7. **Polish** (0.5 day) - Performance optimization - Documentation - Example scenarios --- ## PHASE 2 SUCCESS CRITERIA All of the following must work: ```typescript // 1. Whisper heard only by target const whisper = await interact_socially({ speaker_id: 'rogue-1', target_id: 'wizard-1', content: 'I pick the lock', volume: 'WHISPER' }); expect(whisper.targetHeard).toBe(true); expect(whisper.eavesdroppers).toHaveLength(0); // 15+ feet away // 2. Shout heard across location const shout = await interact_socially({ speaker_id: 'barbarian-1', content: 'CHARGE!', volume: 'SHOUT' }); expect(shout.recordedMemories).toBeGreaterThan(5); // 3. Stealth beats low perception const lowPer = createChar({ stats: { wis: 8 } }); const results = []; for (let i = 0; i < 100; i++) { const r = await interact_socially({...}); results.push(r.eavesdroppers.length); } expect(average(results)).toBeLessThan(30); // Low success rate // 4. High perception beats stealth const highPer = createChar({ stats: { wis: 18 } }); // ... similar test, expect higher eavesdrop rate // 5. Memory stores different content for listeners const target = await getConversationHistory(wizard.id); expect(target.conversations[0].content).toContain('pick the lock'); const eavesdropper = await getConversationHistory(barbarian.id); expect(eavesdropper.conversations[0].content).toContain('overheard'); expect(eavesdropper.conversations[0].content).not.toContain('lock'); ``` --- ## PHASE 2 BLOCKING FIXES NEEDED FIRST Before starting Phase 2, these MUST be fixed: ### EDGE-001: Self-Theft Logic Flaw ``` // Currently allows this (shouldn't): await steal_item(thief_id: 'same-char', victim_id: 'same-char', item_id) // Fix needed: if (thief_id === victim_id) return { error: 'Cannot steal from yourself' } ``` **Impact on Phase 2:** Prevents nonsensical "overheard self" scenarios ### EDGE-006: Victim Can Be Fence ``` // Currently allows this (shouldn't): await register_fence(merchant_id) // merchant was stolen from earlier // Fix needed: Check if character was recent theft victim Prevent fence registration or add warning ``` **Impact on Phase 2:** Prevents "character buys back own stolen goods" ### Character Stats: Add Perception/Stealth ``` // Currently missing: character.skills.stealth (for DEX-based checks) character.skills.perception (for WIS-based checks) // Or simpler: Just use stats.dex and stats.wis directly No need for separate skill scores ``` **Impact on Phase 2:** Needed for opposed rolls --- ## REFERENCE: PHASE 1 OUTPUTS USED BY PHASE 2 Phase 2 depends on Phase 1 providing: ```typescript // Room with entities and atmospherics const room = spatialRepo.findById(characterRoom.id); room.entityIds // Who's in room? room.atmospherics // ['SILENCE'], ['DARKNESS'], etc. room.exits // For adjacent room queries // Character with stats const char = characterRepo.findById(listenerId); char.stats.wis // For perception char.stats.dex // For stealth char.conditions // Deafness, etc. // Conversation recording await recordConversationMemory({ character_id: listenerId, npc_id: speakerId, content: whatTheyHeard, importance: 'medium', timestamp: now }); ``` --- ## WHAT NOT TO DO IN PHASE 2 - ❌ Don't implement combat stealth yet (save for Phase 3) - ❌ Don't create "sound propagation simulation" (too complex) - ❌ Don't allow LLM to modify hearing ranges (hardcode them) - ❌ Don't implement multiple languages yet - ❌ Don't handle "recording devices" or scrying spells - ❌ Don't implement lip-reading or signing - ❌ Don't add dynamic atmosphere changes during conversation --- ## READY FOR IMPLEMENTATION WHEN: 1. ✅ Phase 1 (Spatial Graph) is complete and tested 2. ✅ EDGE-001, EDGE-006, and stat additions are fixed 3. ✅ Game design decisions (above) are approved 4. ✅ You have buy-in from DM/designer on hearing mechanics 5. ✅ You're ready to write 20+ tests Only then should Phase 2 handoff be created and assigned. --- ## ESTIMATED PHASE 2 TIMELINE - Game design review: 4 hours - Hearing engine: 8 hours - Stealth rolls: 4 hours - MCP tool: 8 hours - Integration: 8 hours - Tests: 8 hours - Polish: 4 hours - **Total: 4-5 days of focused work** --- ## CONCLUSION Phase 2 is well-defined but depends on: 1. Phase 1 completing successfully 2. Blocking issues fixed 3. Game design decisions made Once those prerequisites are met, Phase 2 can be implemented following this outline. **DO NOT START until prerequisites are complete.** --- **This document is ready for reference but implementation should not begin until Phase 1 and blocking issues are resolved.**

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Mnehmos/rpg-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server