Files
BreakEscape/js/main.js
Z. Cliffe Schreuders 72e2e6293f feat: Implement NPC security guard interaction for lockpicking detection
- Added a new security guard NPC with conversation flow for lockpicking attempts.
- Integrated influence system to determine NPC reactions based on player choices.
- Created a new JSON scenario for the security guard's behavior and interactions.
- Refactored lockpicking system to allow NPC interruptions during attempts.
- Developed a test scenario to visualize NPC patrol and line-of-sight detection.
- Added a debug panel for testing line-of-sight visualization in the game.
2025-11-11 01:07:05 +00:00

299 lines
10 KiB
JavaScript

import { GAME_CONFIG } from './utils/constants.js?v=8';
import { preload, create, update } from './core/game.js?v=40';
import { initializeNotifications } from './systems/notifications.js?v=7';
// Bluetooth scanner is now handled as a minigame
// Biometrics is now handled as a minigame
import { startLockpickingMinigame } from './systems/minigame-starters.js?v=1';
import { initializeDebugSystem } from './systems/debug.js?v=7';
import { initializeUI } from './ui/panels.js?v=9';
import { initializeModals } from './ui/modals.js?v=7';
// Import minigame framework
import './minigames/index.js';
// Import NPC systems
import './systems/ink/ink-engine.js?v=1';
import NPCEventDispatcher from './systems/npc-events.js?v=1';
import NPCManager from './systems/npc-manager.js?v=2';
import NPCBarkSystem from './systems/npc-barks.js?v=1';
import NPCLazyLoader from './systems/npc-lazy-loader.js?v=1';
import './systems/npc-game-bridge.js'; // Bridge for NPCs to influence game state
// Global game variables
window.game = null;
window.gameScenario = null;
window.player = null;
window.cursors = null;
window.rooms = {};
window.currentRoom = null;
window.inventory = {
items: [],
container: null
};
window.objectsGroup = null;
window.wallsLayer = null;
window.discoveredRooms = new Set();
window.pathfinder = null;
window.currentPath = [];
window.isMoving = false;
window.targetPoint = null;
window.lastPathUpdateTime = 0;
window.stuckTimer = 0;
window.lastPosition = null;
window.stuckTime = 0;
window.currentPlayerRoom = null;
window.lastPlayerPosition = { x: 0, y: 0 };
window.gameState = {
biometricSamples: [],
biometricUnlocks: [],
bluetoothDevices: [],
notes: [],
startTime: null
};
window.lastBluetoothScan = 0;
// Initialize the game
function initializeGame() {
// Set up game configuration with scene functions
const config = {
...GAME_CONFIG,
scene: {
preload: preload,
create: create,
update: update
},
inventory: {
items: [],
display: null
}
};
// Create the Phaser game instance
window.game = new Phaser.Game(config);
// Initialize all systems
initializeNotifications();
// Bluetooth scanner and biometrics are now handled as minigames
// Initialize NPC systems
console.log('🎭 Initializing NPC systems...');
window.eventDispatcher = new NPCEventDispatcher();
window.barkSystem = new NPCBarkSystem();
window.npcManager = new NPCManager(window.eventDispatcher, window.barkSystem);
window.npcLazyLoader = new NPCLazyLoader(window.npcManager);
console.log('✅ NPC lazy loader initialized');
// Start timed message system
window.npcManager.startTimedMessages();
console.log('✅ NPC systems initialized');
if (window.npcBarkSystem) {
window.npcBarkSystem.init();
}
// Make lockpicking function available globally
window.startLockpickingMinigame = startLockpickingMinigame;
initializeDebugSystem();
initializeUI();
initializeModals();
// Calculate optimal integer scale factor for current browser window
const calculateOptimalScale = () => {
const container = document.getElementById('game-container');
if (!container) return 2; // Default fallback
const containerWidth = container.clientWidth;
const containerHeight = container.clientHeight;
// Base resolution
const baseWidth = 640;
const baseHeight = 480;
// Calculate scale factors for both dimensions
const scaleX = containerWidth / baseWidth;
const scaleY = containerHeight / baseHeight;
// Use the smaller scale to maintain aspect ratio
const maxScale = Math.min(scaleX, scaleY);
// Find the best integer scale factor (prefer 2x or higher for pixel art)
let bestScale = 2; // Minimum for good pixel art
// Check integer scales from 2x up to the maximum that fits
for (let scale = 2; scale <= Math.floor(maxScale); scale++) {
const scaledWidth = baseWidth * scale;
const scaledHeight = baseHeight * scale;
// If this scale fits within the container, use it
if (scaledWidth <= containerWidth && scaledHeight <= containerHeight) {
bestScale = scale;
} else {
break; // Stop at the largest scale that fits
}
}
return bestScale;
};
// Setup pixel-perfect rendering with optimal scaling
const setupPixelArt = () => {
if (game && game.canvas && game.scale) {
const canvas = game.canvas;
// Set pixel-perfect rendering
canvas.style.imageRendering = 'pixelated';
canvas.style.imageRendering = '-moz-crisp-edges';
canvas.style.imageRendering = 'crisp-edges';
// Calculate and apply optimal scale
const optimalScale = calculateOptimalScale();
game.scale.setZoom(optimalScale);
console.log(`Applied ${optimalScale}x scaling for pixel art`);
}
};
// Handle orientation changes and fullscreen
const handleOrientationChange = () => {
if (game && game.scale) {
setTimeout(() => {
game.scale.refresh();
const optimalScale = calculateOptimalScale();
game.scale.setZoom(optimalScale);
console.log(`Orientation change: Applied ${optimalScale}x scaling`);
}, 100);
}
};
// Handle window resize
const handleResize = () => {
if (game && game.scale) {
setTimeout(() => {
game.scale.refresh();
const optimalScale = calculateOptimalScale();
game.scale.setZoom(optimalScale);
console.log(`Resize: Applied ${optimalScale}x scaling`);
}, 16);
}
};
// Add event listeners
window.addEventListener('resize', handleResize);
window.addEventListener('orientationchange', handleOrientationChange);
document.addEventListener('fullscreenchange', handleOrientationChange);
// Check for LOS visualization debug flag
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.has('debug-los') || urlParams.has('los')) {
// Delay to ensure scene is ready
setTimeout(() => {
const mainScene = window.game?.scene?.scenes?.[0];
if (mainScene && window.npcManager) {
console.log('🔍 Enabling LOS visualization (from URL parameter)');
window.npcManager.setLOSVisualization(true, mainScene);
}
}, 1000);
}
// Add console helper
window.enableLOS = function() {
console.log('🔍 enableLOS() called');
console.log(' game:', !!window.game);
console.log(' game.scene:', !!window.game?.scene);
console.log(' scenes:', window.game?.scene?.scenes?.length ?? 0);
const mainScene = window.game?.scene?.scenes?.[0];
console.log(' mainScene:', !!mainScene, mainScene?.key);
console.log(' npcManager:', !!window.npcManager);
if (!mainScene) {
console.error('❌ Could not get main scene');
// Try to find any active scene
if (window.game?.scene?.scenes) {
for (let i = 0; i < window.game.scene.scenes.length; i++) {
console.log(` Available scene[${i}]:`, window.game.scene.scenes[i].key, 'isActive:', window.game.scene.scenes[i].isActive());
}
}
return;
}
if (!window.npcManager) {
console.error('❌ npcManager not available');
return;
}
console.log('🎯 Setting LOS visualization with scene:', mainScene.key);
window.npcManager.setLOSVisualization(true, mainScene);
console.log('✅ LOS visualization enabled');
};
window.disableLOS = function() {
if (window.npcManager) {
window.npcManager.setLOSVisualization(false);
console.log('✅ LOS visualization disabled');
} else {
console.error('❌ npcManager not available');
}
};
// Test graphics rendering
window.testGraphics = function() {
console.log('🧪 Testing graphics rendering...');
const scene = window.game?.scene?.scenes?.[0];
if (!scene) {
console.error('❌ No scene found');
return;
}
console.log('📊 Scene:', scene.key, 'Active:', scene.isActive());
const test = scene.add.graphics();
console.log('✅ Created graphics object:', {
exists: !!test,
hasScene: !!test.scene,
depth: test.depth,
alpha: test.alpha,
visible: test.visible
});
test.fillStyle(0xff0000, 0.5);
test.fillRect(100, 100, 50, 50);
console.log('✅ Drew red square at (100, 100)');
console.log(' If you see a RED SQUARE on screen, graphics rendering is working!');
console.log(' If NOT, check browser console for errors');
// Clean up after 5 seconds
setTimeout(() => {
test.destroy();
console.log('🧹 Test graphics cleaned up');
}, 5000);
};
// Get detailed LOS status
window.losStatus = function() {
console.log('📡 LOS System Status:');
console.log(' Enabled:', window.npcManager?.losVisualizationEnabled ?? 'N/A');
console.log(' NPCs loaded:', window.npcManager?.npcs?.size ?? 0);
console.log(' Graphics objects:', window.npcManager?.losVisualizations?.size ?? 0);
if (window.npcManager?.npcs?.size > 0) {
for (const npc of window.npcManager.npcs.values()) {
console.log(` NPC: "${npc.id}"`);
console.log(` LOS enabled: ${npc.los?.enabled ?? false}`);
console.log(` Position: (${npc.sprite?.x.toFixed(0) ?? 'N/A'}, ${npc.sprite?.y.toFixed(0) ?? 'N/A'})`);
console.log(` Facing: ${npc.facingDirection ?? npc.direction ?? 'N/A'}°`);
}
}
};
// Initial setup
setTimeout(setupPixelArt, 100);
}
// Initialize when DOM is ready
document.addEventListener('DOMContentLoaded', initializeGame);
// Export for global access
window.initializeGame = initializeGame;