- Added support for voice messages prefixed with "voice:" in Ink files. - Updated UI rendering to display voice message UI with play button and transcript. - Implemented automatic conversion for scenario JSON with voice properties. - Created test examples for pure voice messages and mixed content. - Fixed issues with NPC registration and message duplication in test scenarios. - Documented feature details, use cases, and testing procedures.
12 KiB
Phone Integration Plan: Bridging Phone-Messages and Phone-Chat
Current State Analysis
Existing Phone System (phone-messages)
Purpose: Display pre-recorded voice/text messages from scenario JSON
Trigger: Player interacts with phone objects in rooms
Data Source: Scenario JSON objects with type: "phone"
Current Structure:
{
"type": "phone",
"name": "Reception Phone",
"readable": true,
"voice": "Security alert: Unauthorized access...",
"text": "Optional text transcription",
"sender": "Security Team",
"timestamp": "02:45 AM"
}
Features:
- Voice playback using Web Speech API
- Text display
- Message list UI
- Mark as read/unread
- One-way communication (player listens only)
New Phone System (phone-chat)
Purpose: Interactive NPC conversations with branching dialogue Trigger: Bark notifications or direct phone access Data Source: Ink story files + NPCManager
Features:
- Two-way conversations (player makes choices)
- Branching dialogue
- State persistence
- History tracking
- Event-driven responses
- Timed messages
- Multiple NPCs per phone
Integration Strategy
Option 1: Unified Phone UI (Recommended)
Merge both systems into a single phone interface that can display:
- Static messages (old system)
- Interactive chats (new system)
Pros:
- Single unified UI
- Better user experience
- One phone button/interaction
- Natural flow between message types
Cons:
- More complex implementation
- Need to refactor existing phone minigame
- Potential backward compatibility issues
Option 2: Separate Systems with Router
Keep both systems separate but add routing logic:
- Phone objects specify
phoneType: "messages" | "chat" | "unified" - Interaction system routes to appropriate minigame
Pros:
- Minimal changes to existing code
- Backward compatible
- Clear separation of concerns
Cons:
- Two different UIs for "phone" concept
- User confusion (why do some phones work differently?)
Option 3: Phone-Chat as Messages Tab (Hybrid)
Extend phone-messages with a new "Chats" tab:
- Tab 1: Messages (existing system)
- Tab 2: Chats (new NPC system)
Pros:
- Best of both worlds
- Familiar tab interface
- Unified phone object
- Gradual migration path
Cons:
- Medium complexity
- Need to coordinate both systems
Recommended Approach: Option 3 (Hybrid)
Phase 1: Add Phone Type Detection
Update interactions.js:
// Enhanced phone interaction detection
if (data.type === 'phone') {
const phoneType = data.phoneType || 'auto'; // 'messages', 'chat', 'unified', 'auto'
// Auto-detect based on content
if (phoneType === 'auto') {
const hasStaticMessages = data.text || data.voice;
const hasNPCs = data.npcIds && data.npcIds.length > 0;
const phoneId = data.phoneId || 'player_phone';
const registeredNPCs = window.npcManager?.getNPCsByPhone(phoneId) || [];
if (registeredNPCs.length > 0 || hasNPCs) {
phoneType = 'unified'; // Both static and chat
} else if (hasStaticMessages) {
phoneType = 'messages'; // Only static
} else {
phoneType = 'chat'; // Only chat
}
}
startPhoneMinigame(data, phoneType);
}
Phase 2: Create Unified Phone Minigame
New file: js/minigames/phone/phone-unified-minigame.js
import { MinigameScene } from '../framework/base-minigame.js';
import { PhoneMessagesMinigame } from './phone-messages-minigame.js';
import { PhoneChatMinigame } from '../phone-chat/phone-chat-minigame.js';
export class PhoneUnifiedMinigame extends MinigameScene {
constructor(container, params) {
super(container, params);
this.currentTab = 'messages'; // or 'chats'
this.hasMessages = params.messages && params.messages.length > 0;
this.hasChats = params.npcIds || (params.phoneId && this.getNPCCount(params.phoneId) > 0);
// If only one type, go straight to it
if (this.hasMessages && !this.hasChats) {
this.currentTab = 'messages';
} else if (!this.hasMessages && this.hasChats) {
this.currentTab = 'chats';
}
}
start() {
this.renderTabs();
this.showCurrentTab();
}
renderTabs() {
// Create tab interface
const tabsHTML = `
<div class="phone-tabs">
<button class="phone-tab ${this.currentTab === 'messages' ? 'active' : ''}"
data-tab="messages"
${!this.hasMessages ? 'disabled' : ''}>
📧 Messages ${this.hasMessages ? `(${this.params.messages.length})` : ''}
</button>
<button class="phone-tab ${this.currentTab === 'chats' ? 'active' : ''}"
data-tab="chats"
${!this.hasChats ? 'disabled' : ''}>
💬 Chats ${this.hasChats ? this.getUnreadBadge() : ''}
</button>
</div>
<div class="phone-tab-content"></div>
`;
this.container.innerHTML = tabsHTML;
// Set up tab switching
this.container.querySelectorAll('.phone-tab').forEach(tab => {
tab.addEventListener('click', (e) => {
this.switchTab(e.target.dataset.tab);
});
});
}
switchTab(tabName) {
this.currentTab = tabName;
this.renderTabs();
this.showCurrentTab();
}
showCurrentTab() {
const content = this.container.querySelector('.phone-tab-content');
if (this.currentTab === 'messages') {
// Render phone-messages UI
this.renderMessages(content);
} else {
// Render phone-chat UI
this.renderChats(content);
}
}
// ... rest of implementation
}
Phase 3: Update Scenario JSON Schema
New schema for phone objects:
{
"type": "phone",
"name": "Office Phone",
"phoneType": "unified",
"phoneId": "office_phone",
"messages": [
{
"type": "voice",
"sender": "Security",
"voice": "Alert: Server room PIN is 5923",
"timestamp": "02:45 AM"
}
],
"npcIds": ["alice", "bob"],
"observations": "The office phone is ringing"
}
Phase 4: Inventory Phone Item
Add phone to player inventory:
{
"startItemsInInventory": [
{
"type": "phone",
"name": "Player Phone",
"takeable": true,
"phoneType": "chat",
"phoneId": "player_phone",
"observations": "Your personal phone with contacts"
}
]
}
Update inventory.js to handle phone items:
// When player clicks phone in inventory
if (item.type === 'phone') {
window.MinigameFramework.startMinigame('phone-unified', null, {
phoneType: item.phoneType || 'chat',
phoneId: item.phoneId || 'player_phone',
title: item.name || 'Phone'
});
}
Implementation Checklist
✅ Prerequisites (Already Complete)
- Phone-chat minigame working
- NPCManager with conversation history
- Bark system operational
- Timed messages system
📋 Phase 1: Detection & Routing (Week 1)
- Add phoneType detection to interactions.js
- Create routing function for phone types
- Test with existing phone objects (backward compatibility)
- Add phoneId to phone objects in scenarios
📋 Phase 2: Unified Phone UI (Week 2)
- Create PhoneUnifiedMinigame class
- Implement tab switching UI
- Integrate PhoneMessagesMinigame content
- Integrate PhoneChatMinigame content
- Add unread badge calculation
- Style tabs to match phone aesthetic
📋 Phase 3: Inventory Integration (Week 3)
- Add phone item to startItemsInInventory
- Update inventory.js to handle phone items
- Add phone button to UI (bottom-right corner)
- Implement unread badge on phone button
- Test phone access from inventory vs room objects
📋 Phase 4: Scenario Updates (Week 4)
- Update biometric_breach.json with NPCs
- Add phone item to player inventory in scenarios
- Convert static phone messages to new format
- Test all existing scenarios for compatibility
- Create documentation for scenario designers
📋 Phase 5: Polish (Week 5)
- Add transition animations between tabs
- Implement "new message" notification sounds
- Add vibration effect for incoming messages
- Polish unread badge styling
- Performance testing with many messages
Backward Compatibility Plan
Existing Phone Objects
All existing phone objects will continue to work:
phoneTypedefaults to "auto" → detects messages and uses phone-messages UI- No breaking changes to scenario JSON
- Gradual migration path
Migration Path
- Phase 1: All existing phones work as before (messages only)
- Phase 2: Add phoneId to phones that should support chat
- Phase 3: Register NPCs in scenario JSON
- Phase 4: Test unified phone with both message types
- Phase 5: Deprecate standalone phone-messages (optional)
Data Flow Diagram
Player Interacts with Phone
↓
interactions.js detects phone type
↓
┌───────┴───────┐
↓ ↓
Messages Only Has NPCs/Chat?
↓ ↓
phone-messages phone-unified
↓
┌───────┴───────┐
↓ ↓
Messages Tab Chats Tab
↓ ↓
Static Messages phone-chat
(voice/text) (interactive)
File Structure
js/minigames/
phone/
phone-messages-minigame.js (existing)
phone-unified-minigame.js (new)
phone-chat/
phone-chat-minigame.js (existing)
phone-chat-ui.js (existing)
phone-chat-conversation.js (existing)
phone-chat-history.js (existing)
css/
phone.css (existing)
phone-chat-minigame.css (existing)
phone-unified.css (new - tab styles)
scenarios/
biometric_breach.json (update)
- Add phoneId to phone objects
- Add npcs array
- Add phone to startItemsInInventory
Testing Strategy
Unit Tests
- Phone type detection logic
- Tab switching functionality
- Message/chat content rendering
- Unread badge calculation
Integration Tests
- Phone-messages content in unified UI
- Phone-chat content in unified UI
- Switching between tabs preserves state
- Inventory phone item works
- Room phone objects work
Scenario Tests
- Existing scenarios still work (backward compat)
- New scenarios with NPCs work
- Mixed content (messages + chats) works
- Multiple phones with different content
User Experience Tests
- Smooth tab transitions
- Unread badges update correctly
- Phone button shows correct badge count
- Notifications work for new messages
Timeline
Week 1: Detection & Routing (3-5 hours) Week 2: Unified UI (8-12 hours) Week 3: Inventory Integration (4-6 hours) Week 4: Scenario Updates (6-8 hours) Week 5: Polish & Testing (4-6 hours)
Total: 25-37 hours over 5 weeks
Next Immediate Steps
- Review this plan with stakeholders
- Choose integration option (recommend Option 3)
- Start Phase 1: Add phone type detection
- Create branch for phone-integration work
- Begin implementation of PhoneUnifiedMinigame
Document Version: 1.0 Date: 2025-10-30 Status: Ready for Review