Add NPC sprite test scenario, server for development, and HTML test pages

- Created a new JSON scenario file for testing NPC sprite functionality.
- Implemented a simple HTTP server with caching headers for development purposes.
- Added an HTML page for testing NPC interactions, including system checks and game controls.
- Introduced a separate HTML page for testing item delivery through person chat interactions.
This commit is contained in:
Z. Cliffe Schreuders
2025-11-04 14:16:48 +00:00
parent 5fd7ad9307
commit e73a6a038b
60 changed files with 18726 additions and 122 deletions

417
test-npc-interaction.html Normal file
View File

@@ -0,0 +1,417 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NPC Interaction Test</title>
<link rel="stylesheet" href="css/main.css">
<link rel="stylesheet" href="css/panels.css">
<link rel="stylesheet" href="css/modals.css">
<link rel="stylesheet" href="css/inventory.css">
<link rel="stylesheet" href="css/person-chat-minigame.css">
<link rel="stylesheet" href="css/npc-interactions.css">
<style>
body {
margin: 0;
padding: 20px;
background-color: #222;
color: #fff;
font-family: Arial, sans-serif;
}
.test-container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
color: #4da6ff;
border-bottom: 2px solid #4da6ff;
padding-bottom: 10px;
}
.test-section {
background-color: #333;
border: 2px solid #4da6ff;
border-radius: 4px;
padding: 15px;
margin: 15px 0;
}
.test-button {
background-color: #4da6ff;
color: #000;
border: none;
padding: 10px 20px;
margin: 5px;
cursor: pointer;
font-weight: bold;
border-radius: 4px;
font-size: 14px;
}
.test-button:hover {
background-color: #6dbfff;
}
.test-output {
background-color: #222;
border: 1px solid #666;
border-radius: 4px;
padding: 10px;
margin-top: 10px;
font-family: monospace;
font-size: 12px;
max-height: 400px;
overflow-y: auto;
white-space: pre-wrap;
word-wrap: break-word;
}
.status {
display: inline-block;
padding: 3px 8px;
border-radius: 3px;
font-weight: bold;
margin: 0 5px;
}
.status.ok {
background-color: #4db84d;
color: white;
}
.status.error {
background-color: #d9534f;
color: white;
}
.status.warning {
background-color: #f0ad4e;
color: black;
}
#gameContainer {
margin-top: 20px;
border: 2px solid #666;
background-color: #000;
}
.instructions {
background-color: #1a3a1a;
border-left: 4px solid #4db84d;
padding: 10px;
margin: 10px 0;
border-radius: 3px;
}
</style>
</head>
<body>
<div class="test-container">
<h1>🎭 NPC Interaction System Test</h1>
<div class="instructions">
<strong>Test Procedure:</strong>
<ol>
<li>Click "Load NPC Test Scenario" to start the game</li>
<li>Walk the player character near either NPC</li>
<li>Look for "Press E to talk to [NPC Name]" prompt at the bottom</li>
<li>Press E to trigger the conversation</li>
<li>Verify the conversation UI appears with portraits and dialogue</li>
</ol>
</div>
<div class="test-section">
<h2>🔧 System Checks</h2>
<button class="test-button" onclick="checkNPCSystem()">Check NPC System</button>
<button class="test-button" onclick="checkNPCProximity()">Check Proximity Detection</button>
<button class="test-button" onclick="listNPCs()">List All NPCs</button>
<button class="test-button" onclick="testInteractionPrompt()">Test Interaction Prompt</button>
<button class="test-button" onclick="clearConsole()">Clear Output</button>
<div id="systemOutput" class="test-output">Waiting for tests...</div>
</div>
<div class="test-section">
<h2>🎮 Game Controls</h2>
<button class="test-button" onclick="startGame()">Load NPC Test Scenario</button>
<button class="test-button" onclick="resetGame()">Reset Game</button>
<div id="gameOutput" class="test-output">Game status: Ready</div>
</div>
<div class="test-section">
<h2>📊 Debug Info</h2>
<button class="test-button" onclick="showDebugInfo()">Show Full Debug Info</button>
<button class="test-button" onclick="manuallyTriggerInteraction()">Manually Trigger E-Key</button>
<div id="debugOutput" class="test-output">Debug info will appear here...</div>
</div>
<div id="gameContainer"></div>
</div>
<script type="module">
import('./js/main.js').then(() => {
updateGameStatus('Game module loaded');
});
window.testFunctions = {
checkNPCSystem,
checkNPCProximity,
listNPCs,
testInteractionPrompt,
clearConsole,
startGame,
resetGame,
showDebugInfo,
manuallyTriggerInteraction
};
function log(message, type = 'info') {
console.log(`[${type.toUpperCase()}] ${message}`);
}
function output(text, elementId = 'systemOutput') {
const elem = document.getElementById(elementId);
if (elem) {
elem.textContent += text + '\n';
elem.scrollTop = elem.scrollHeight;
}
}
function clearOutput(elementId = 'systemOutput') {
const elem = document.getElementById(elementId);
if (elem) {
elem.textContent = '';
}
}
function checkNPCSystem() {
clearOutput('systemOutput');
output('🔍 Checking NPC System...\n');
const checks = [
{ name: 'window.npcManager', check: () => window.npcManager },
{ name: 'window.player', check: () => window.player },
{ name: 'window.MinigameFramework', check: () => window.MinigameFramework },
{ name: 'window.checkNPCProximity', check: () => window.checkNPCProximity },
{ name: 'window.tryInteractWithNearest', check: () => window.tryInteractWithNearest }
];
let passed = 0;
let failed = 0;
checks.forEach(({ name, check }) => {
try {
const result = check();
if (result) {
output(`${name}`);
passed++;
} else {
output(`${name} - exists but is falsy`);
failed++;
}
} catch (e) {
output(`${name} - ${e.message}`);
failed++;
}
});
output(`\n📊 Results: ${passed} passed, ${failed} failed`);
}
function checkNPCProximity() {
clearOutput('systemOutput');
output('🔍 Checking NPC Proximity Detection...\n');
if (!window.npcManager) {
output('❌ npcManager not available');
return;
}
output(`NPCs registered: ${window.npcManager.npcs.size}\n`);
if (window.npcManager.npcs.size === 0) {
output('⚠️ No NPCs registered. Load a scenario first.\n');
return;
}
if (!window.player) {
output('❌ Player not available');
return;
}
output(`Player position: (${window.player.x}, ${window.player.y})\n`);
// Calculate distances to all NPCs
let found = false;
window.npcManager.npcs.forEach((npc) => {
if (npc._sprite) {
const dx = npc._sprite.x - window.player.x;
const dy = npc._sprite.y - window.player.y;
const distance = Math.sqrt(dx * dx + dy * dy);
const inRange = distance <= 64;
output(`${inRange ? '✅' : '⚠️'} ${npc.displayName} (${npc.id})`);
output(` Position: (${npc._sprite.x}, ${npc._sprite.y})`);
output(` Distance: ${distance.toFixed(2)}px (${inRange ? 'IN RANGE' : 'OUT OF RANGE'})`);
output(` Type: ${npc.npcType}`);
output(` Sprite active: ${npc._sprite.active}\n`);
if (inRange) found = true;
}
});
if (found) {
output('🎯 At least one NPC is in range!\n');
} else {
output('⚠️ No NPCs are in range. Move player closer.\n');
}
// Manually run proximity check
if (window.checkNPCProximity) {
output('Running checkNPCProximity()...\n');
window.checkNPCProximity();
const prompt = document.getElementById('npc-interaction-prompt');
if (prompt) {
output(`✅ Prompt created: "${prompt.querySelector('.prompt-text').textContent}"`);
} else {
output('⚠️ No prompt created');
}
}
}
function listNPCs() {
clearOutput('systemOutput');
output('📋 NPC Listing\n');
if (!window.npcManager) {
output('❌ npcManager not available');
return;
}
const count = window.npcManager.npcs.size;
output(`Total NPCs: ${count}\n`);
if (count === 0) {
output('No NPCs registered.');
return;
}
window.npcManager.npcs.forEach((npc, id) => {
output(`\n📌 ${npc.displayName} (${id})`);
output(` Type: ${npc.npcType}`);
output(` Phone ID: ${npc.phoneId || 'none'}`);
output(` Story: ${npc.storyPath || 'none'}`);
output(` Room: ${npc.roomId || 'none'}`);
if (npc._sprite) {
output(` Sprite: YES @ (${npc._sprite.x}, ${npc._sprite.y})`);
output(` Active: ${npc._sprite.active}`);
} else {
output(` Sprite: NO`);
}
});
}
function testInteractionPrompt() {
clearOutput('systemOutput');
output('🧪 Testing Interaction Prompt\n');
if (!window.npcManager) {
output('❌ npcManager not available');
return;
}
const npcs = Array.from(window.npcManager.npcs.values());
if (npcs.length === 0) {
output('❌ No NPCs to test');
return;
}
const testNpc = npcs[0];
output(`Testing with: ${testNpc.displayName}\n`);
// Manually create prompt
output('Creating prompt...\n');
if (window.updateNPCInteractionPrompt) {
window.updateNPCInteractionPrompt(testNpc);
const prompt = document.getElementById('npc-interaction-prompt');
if (prompt) {
output('✅ Prompt created');
output(` Element: ${prompt.id}`);
output(` Text: ${prompt.querySelector('.prompt-text').textContent}`);
output(` NPC ID: ${prompt.dataset.npcId}\n`);
output('Prompt is visible on screen\n');
// Clear it
output('Clearing prompt...\n');
window.updateNPCInteractionPrompt(null);
if (!document.getElementById('npc-interaction-prompt')) {
output('✅ Prompt cleared successfully');
} else {
output('❌ Prompt still exists');
}
} else {
output('❌ Prompt not created');
}
} else {
output('❌ updateNPCInteractionPrompt not available');
}
}
function clearConsole() {
clearOutput('systemOutput');
output('🗑️ Output cleared');
}
function startGame() {
clearOutput('gameOutput');
output('🎮 Loading NPC Test Scenario...', 'gameOutput');
// Redirect to scenario_select with test scenario
window.location.href = 'scenario_select.html?scenario=npc-sprite-test';
}
function resetGame() {
clearOutput('gameOutput');
output('🔄 Resetting game...', 'gameOutput');
// Reload page
location.reload();
}
function showDebugInfo() {
clearOutput('debugOutput');
const info = {
'Player': window.player ? `(${window.player.x}, ${window.player.y})` : 'Not loaded',
'NPC Manager': window.npcManager ? `${window.npcManager.npcs.size} NPCs` : 'Not loaded',
'Current Prompt': document.getElementById('npc-interaction-prompt') ? 'Yes' : 'No',
'MinigameFramework': window.MinigameFramework ? 'Loaded' : 'Not loaded',
'E-Key Handler': window.tryInteractWithNearest ? 'Ready' : 'Not ready'
};
output('📊 DEBUG INFO\n');
Object.entries(info).forEach(([key, value]) => {
output(`${key}: ${value}`);
});
}
function manuallyTriggerInteraction() {
clearOutput('debugOutput');
output('🎯 Manually triggering E-Key interaction...\n');
if (!window.tryInteractWithNearest) {
output('❌ tryInteractWithNearest not available');
return;
}
try {
window.tryInteractWithNearest();
output('✅ Interaction triggered');
} catch (e) {
output(`❌ Error: ${e.message}`);
}
}
function updateGameStatus(msg) {
const elem = document.getElementById('gameOutput');
if (elem) {
elem.textContent = `📊 ${msg}`;
}
}
window.updateGameStatus = updateGameStatus;
</script>
</body>
</html>