Files
BreakEscape/test-phone-chat-minigame.html
Z. Cliffe Schreuders f369b41547 feat: Implement voice messages in phone chat minigame
- 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.
2025-10-30 02:45:05 +00:00

578 lines
22 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Phone Chat Minigame Test</title>
<!-- VT323 font for pixel-art aesthetic -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
<!-- Minigame styles -->
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/minigames-framework.css">
<link rel="stylesheet" href="css/phone-chat-minigame.css">
<style>
body {
font-family: 'VT323', monospace;
background: #1a1a1a;
color: #fff;
padding: 20px;
}
h1 {
color: #5fcf69;
text-align: center;
margin-bottom: 30px;
}
.test-controls {
max-width: 800px;
margin: 0 auto 30px;
display: flex;
flex-direction: column;
gap: 15px;
}
.test-section {
background: #2a2a2a;
border: 2px solid #5fcf69;
padding: 20px;
}
.test-section h2 {
color: #5fcf69;
margin-top: 0;
font-size: 24px;
}
.button-group {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.test-btn {
background: #333;
color: #5fcf69;
border: 2px solid #555;
padding: 12px 20px;
cursor: pointer;
font-family: 'VT323', monospace;
font-size: 18px;
transition: all 0.3s ease;
font-weight: bold;
}
.test-btn:hover {
background: #555;
border-color: #5fcf69;
box-shadow: 0 0 10px rgba(95, 207, 105, 0.5);
}
.console-output {
background: #000;
border: 2px solid #5fcf69;
padding: 15px;
font-family: 'VT323', monospace;
font-size: 14px;
max-height: 300px;
overflow-y: auto;
color: #5fcf69;
}
.console-output .log-entry {
margin-bottom: 4px;
}
.console-output .log-entry.error {
color: #ff6b6b;
}
.console-output .log-entry.success {
color: #6acc6a;
}
.console-output .log-entry.info {
color: #4a9eff;
}
#minigame-container {
display: none;
}
#minigame-container.active {
display: block;
}
</style>
</head>
<body>
<h1>📱 Phone Chat Minigame Test</h1>
<div class="test-controls">
<div class="test-section">
<h2>1. Setup & Initialization</h2>
<div class="button-group">
<button class="test-btn" onclick="testSetup()">Initialize Systems</button>
<button class="test-btn" onclick="testRegisterNPCs()">Register Test NPCs</button>
<button class="test-btn" onclick="checkSystems()">Check Systems Status</button>
</div>
</div>
<div class="test-section">
<h2>2. Phone Chat Tests</h2>
<div class="button-group">
<button class="test-btn" onclick="testAliceChat()">Test Alice Chat</button>
<button class="test-btn" onclick="testBobChat()">Test Bob Chat</button>
<button class="test-btn" onclick="testPhoneWithMultipleNPCs()">Open Phone</button>
<button class="test-btn" onclick="testSimpleMessageConversion()" style="background: #4CAF50;">🔄 Test Simple Message Conversion</button>
</div>
</div>
<div class="test-section">
<h2>3. History Tests</h2>
<div class="button-group">
<button class="test-btn" onclick="testSendMessages()">Send Test Messages</button>
<button class="test-btn" onclick="testViewHistory()">View Conversation History</button>
<button class="test-btn" onclick="testClearHistory()">Clear History</button>
</div>
</div>
<div class="test-section">
<h2>Console Output</h2>
<div class="console-output" id="console-output">
<div class="log-entry info">📝 Waiting for tests to run...</div>
</div>
<button class="test-btn" onclick="clearConsole()" style="margin-top: 10px;">Clear Console</button>
</div>
</div>
<!-- Minigame container -->
<div id="minigame-container"></div>
<!-- Load dependencies -->
<script src="assets/vendor/ink.js"></script>
<script type="module">
import InkEngine from './js/systems/ink/ink-engine.js';
import NPCEventDispatcher from './js/systems/npc-events.js';
import NPCManager from './js/systems/npc-manager.js';
import NPCBarkSystem from './js/systems/npc-barks.js';
import { MinigameFramework } from './js/minigames/framework/minigame-manager.js';
import { PhoneChatMinigame } from './js/minigames/phone-chat/phone-chat-minigame.js';
// Make available globally for test functions
window.InkEngine = InkEngine;
window.NPCEventDispatcher = NPCEventDispatcher;
window.NPCManager = NPCManager;
window.NPCBarkSystem = NPCBarkSystem;
window.MinigameFramework = MinigameFramework;
window.PhoneChatMinigame = PhoneChatMinigame;
// Register minigame
MinigameFramework.registerScene('phone-chat', PhoneChatMinigame);
log('✅ Modules loaded successfully', 'success');
</script>
<script>
// Console logging
function log(message, type = 'info') {
const output = document.getElementById('console-output');
const entry = document.createElement('div');
entry.className = `log-entry ${type}`;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
output.appendChild(entry);
output.scrollTop = output.scrollHeight;
console.log(message);
}
function clearConsole() {
document.getElementById('console-output').innerHTML = '';
log('Console cleared', 'info');
}
// Test functions
function testSetup() {
log('🔧 Initializing systems...', 'info');
try {
// Initialize event dispatcher
window.eventDispatcher = new window.NPCEventDispatcher();
log('✅ Event dispatcher initialized', 'success');
// Initialize bark system
window.barkSystem = new window.NPCBarkSystem();
window.barkSystem.init();
log('✅ Bark system initialized', 'success');
// Initialize NPC manager
window.npcManager = new window.NPCManager(window.eventDispatcher, window.barkSystem);
log('✅ NPC manager initialized', 'success');
log('✅ All systems initialized!', 'success');
} catch (error) {
log(`❌ Error during setup: ${error.message}`, 'error');
console.error(error);
}
}
function testRegisterNPCs() {
log('👥 Registering test NPCs...', 'info');
if (!window.npcManager) {
log('❌ NPC manager not initialized. Run Setup first.', 'error');
return;
}
try {
// Register Alice with her complex story
window.npcManager.registerNPC('alice', {
displayName: 'Alice - Security Consultant',
storyPath: 'scenarios/compiled/alice-chat.json',
avatar: 'assets/npc/avatars/npc_alice.png',
currentKnot: 'start',
phoneId: 'player_phone',
npcType: 'phone'
});
log('✅ Registered Alice', 'success');
// Register Bob with generic story
window.npcManager.registerNPC('bob', {
displayName: 'Bob - IT Manager',
storyPath: 'scenarios/compiled/generic-npc.json',
avatar: 'assets/npc/avatars/npc_bob.png',
currentKnot: 'start',
phoneId: 'player_phone',
npcType: 'phone'
});
log('✅ Registered Bob', 'success');
// Register Charlie with generic story
window.npcManager.registerNPC('charlie', {
displayName: 'Charlie - Security Guard',
storyPath: 'scenarios/compiled/generic-npc.json',
avatar: null,
currentKnot: 'start',
phoneId: 'player_phone',
npcType: 'phone'
});
log('✅ Registered Charlie', 'success');
// Register Security Team with simple one-way message
window.npcManager.registerNPC('security_team', {
displayName: 'Security Team',
storyPath: 'scenarios/compiled/simple-message.json',
avatar: null,
currentKnot: 'start',
phoneId: 'player_phone',
npcType: 'phone'
});
log('✅ Registered Security Team (simple message)', 'success');
// Register IT Team with voice message
window.npcManager.registerNPC('it_team', {
displayName: 'IT Team',
storyPath: 'scenarios/compiled/voice-message-example.json',
avatar: null,
currentKnot: 'start',
phoneId: 'player_phone',
npcType: 'phone'
});
log('✅ Registered IT Team (voice message)', 'success');
// Register David with mixed message types (text + voice)
window.npcManager.registerNPC('david', {
displayName: 'David - Tech Support',
storyPath: 'scenarios/compiled/mixed-message-example.json',
avatar: null,
currentKnot: 'start',
phoneId: 'player_phone',
npcType: 'phone'
});
log('✅ Registered David (mixed messages)', 'success');
log('✅ All NPCs registered!', 'success');
// Start timed messages system
window.npcManager.startTimedMessages();
log('✅ Timed messages system started!', 'success');
// Schedule some timed messages for testing
window.npcManager.scheduleTimedMessage({
npcId: 'alice',
text: '⏰ Hey! This is a timed message arriving 5 seconds after game start.',
triggerTime: 5000, // 5 seconds
phoneId: 'player_phone'
});
window.npcManager.scheduleTimedMessage({
npcId: 'bob',
text: '⏰ Bob here! This message arrives 10 seconds in.',
triggerTime: 10000, // 10 seconds
phoneId: 'player_phone'
});
window.npcManager.scheduleTimedMessage({
npcId: 'alice',
text: '⏰ Follow-up from Alice at 15 seconds!',
triggerTime: 15000, // 15 seconds
phoneId: 'player_phone'
});
log('✅ Scheduled 3 timed messages (5s, 10s, 15s)', 'success');
} catch (error) {
log(`❌ Error registering NPCs: ${error.message}`, 'error');
console.error(error);
}
}
function checkSystems() {
log('🔍 Checking systems status...', 'info');
const systems = {
'InkEngine': window.InkEngine,
'NPCEventDispatcher': window.eventDispatcher,
'NPCBarkSystem': window.barkSystem,
'NPCManager': window.npcManager,
'MinigameFramework': window.MinigameFramework
};
for (const [name, system] of Object.entries(systems)) {
if (system) {
log(`${name}: Ready`, 'success');
} else {
log(`${name}: Not initialized`, 'error');
}
}
// Check NPCs
if (window.npcManager) {
const npcCount = window.npcManager.npcs.size;
log(`📊 Registered NPCs: ${npcCount}`, 'info');
window.npcManager.npcs.forEach((npc, id) => {
const historyCount = window.npcManager.getConversationHistory(id).length;
log(` - ${npc.displayName}: ${historyCount} messages in history`, 'info');
});
}
}
function testAliceChat() {
log('💬 Opening chat with Alice...', 'info');
if (!window.npcManager) {
log('❌ NPC manager not initialized', 'error');
return;
}
try {
window.MinigameFramework.startMinigame('phone-chat', null, {
npcId: 'alice',
npcName: 'Alice - Security Consultant',
title: 'Chat with Alice'
});
log('✅ Phone chat opened', 'success');
} catch (error) {
log(`❌ Error opening chat: ${error.message}`, 'error');
console.error(error);
}
}
function testBobChat() {
log('💬 Opening chat with Bob...', 'info');
if (!window.npcManager) {
log('❌ NPC manager not initialized', 'error');
return;
}
try {
window.MinigameFramework.startMinigame('phone-chat', null, {
npcId: 'bob',
npcName: 'Bob - IT Manager',
title: 'Chat with Bob'
});
log('✅ Phone chat opened', 'success');
} catch (error) {
log(`❌ Error opening chat: ${error.message}`, 'error');
console.error(error);
}
}
function testPhoneWithMultipleNPCs() {
log('📱 Opening phone with multiple NPCs...', 'info');
if (!window.npcManager) {
log('❌ NPC manager not initialized', 'error');
return;
}
try {
window.MinigameFramework.startMinigame('phone-chat', null, {
phoneId: 'player_phone',
title: 'Messages'
});
log('✅ Phone opened with contact list', 'success');
} catch (error) {
log(`❌ Error opening phone: ${error.message}`, 'error');
console.error(error);
}
}
async function testSimpleMessageConversion() {
log('🔄 Testing simple message conversion...', 'info');
try {
// Import the converter
const { default: PhoneMessageConverter } = await import('./js/utils/phone-message-converter.js');
// Use a static phone ID to avoid duplicates on repeated clicks
const testNpcId = 'test_reception_phone';
// Check if already registered
if (window.npcManager.getNPC(testNpcId)) {
log(' Test NPC already registered, skipping...', 'info');
// Just open the phone
window.MinigameFramework.startMinigame('phone-chat', null, {
phoneId: 'default_phone',
title: 'Test Simple Message'
});
log('✅ Opened existing test message', 'success');
return;
}
// Create a simple phone object (old format)
const simplePhone = {
"type": "phone",
"name": "Reception Phone",
"takeable": false,
"voice": "Welcome to the Computer Science Department! The CyBOK backup is in the Professor's safe. The door through to the offices is also locked, so I guess it's safe for now.",
"sender": "Receptionist",
"timestamp": "Now",
"observations": "The reception phone plays back a voicemail message",
"phoneId": "default_phone"
};
log('📞 Old format phone object:', 'info');
console.log(simplePhone);
// Convert to Ink JSON
const inkJSON = PhoneMessageConverter.toInkJSON(simplePhone);
log('✅ Converted to Ink JSON:', 'success');
console.log(inkJSON);
// Create virtual NPC with static ID
const virtualNPC = PhoneMessageConverter.createVirtualNPC(simplePhone);
// Override the timestamp-based ID with our static one
virtualNPC.id = testNpcId;
log('✅ Created virtual NPC:', 'success');
console.log(virtualNPC);
// Register it
window.npcManager.registerNPC(virtualNPC);
log(`✅ Registered as NPC: ${virtualNPC.id}`, 'success');
// Open the phone to test
window.MinigameFramework.startMinigame('phone-chat', null, {
phoneId: 'default_phone',
title: 'Test Simple Message'
});
log('✅ Test complete - check the phone UI!', 'success');
} catch (error) {
log(`❌ Error: ${error.message}`, 'error');
console.error(error);
}
}
function testSendMessages() {
log('📤 Sending test messages...', 'info');
if (!window.npcManager) {
log('❌ NPC manager not initialized', 'error');
return;
}
try {
// Add test messages to Alice
window.npcManager.addMessage('alice', 'npc', 'Hey! I need to talk to you about something important.');
window.npcManager.addMessage('alice', 'player', 'What\'s up?');
window.npcManager.addMessage('alice', 'npc', 'I found some anomalies in the security logs.');
log('✅ Added messages to Alice conversation', 'success');
// Add test messages to Bob
window.npcManager.addMessage('bob', 'npc', 'Quick question about the server room access.');
window.npcManager.addMessage('bob', 'player', 'Go ahead');
log('✅ Added messages to Bob conversation', 'success');
} catch (error) {
log(`❌ Error sending messages: ${error.message}`, 'error');
console.error(error);
}
}
function testViewHistory() {
log('📜 Viewing conversation history...', 'info');
if (!window.npcManager) {
log('❌ NPC manager not initialized', 'error');
return;
}
try {
const npcs = ['alice', 'bob', 'charlie'];
npcs.forEach(npcId => {
const history = window.npcManager.getConversationHistory(npcId);
const npc = window.npcManager.getNPC(npcId);
if (history.length === 0) {
log(` ${npc?.displayName || npcId}: No messages`, 'info');
} else {
log(` ${npc?.displayName || npcId}: ${history.length} messages`, 'info');
history.forEach((msg, idx) => {
log(` ${idx + 1}. [${msg.type}] ${msg.text.substring(0, 50)}...`, 'info');
});
}
});
} catch (error) {
log(`❌ Error viewing history: ${error.message}`, 'error');
console.error(error);
}
}
function testClearHistory() {
log('🗑️ Clearing conversation history...', 'info');
if (!window.npcManager) {
log('❌ NPC manager not initialized', 'error');
return;
}
try {
const npcs = ['alice', 'bob', 'charlie'];
npcs.forEach(npcId => {
window.npcManager.clearConversationHistory(npcId);
log(`✅ Cleared history for ${npcId}`, 'success');
});
} catch (error) {
log(`❌ Error clearing history: ${error.message}`, 'error');
console.error(error);
}
}
// Auto-run setup on page load
window.addEventListener('load', () => {
log('🚀 Page loaded, ready for testing', 'success');
log('💡 Click "Initialize Systems" to begin', 'info');
});
</script>
</body>
</html>