diff --git a/public/break_escape/js/core/rooms.js b/public/break_escape/js/core/rooms.js index 077b7df..142de44 100644 --- a/public/break_escape/js/core/rooms.js +++ b/public/break_escape/js/core/rooms.js @@ -111,6 +111,10 @@ function isPositionOverlapping(x, y, roomId, itemSize = TILE_SIZE) { window.discoveredRooms = discoveredRooms; let gameRef = null; +// Room data cache - stores room data returned from unlock API to avoid duplicate fetches +const roomDataCache = new Map(); +window.roomDataCache = roomDataCache; // Make available for unlock system + // ===== ITEM POOL MANAGEMENT (PHASE 2 IMPROVEMENTS) ===== // Moved to module level to avoid temporal dead zone errors // and improve performance (defined once, not on every room load) @@ -555,59 +559,69 @@ const OBJECT_SCALES = { // Function to load a room lazily via API endpoint async function loadRoom(roomId) { const position = window.roomPositions[roomId]; - + if (!position) { console.error(`Cannot load room ${roomId}: missing position`); return; } - - console.log(`Lazy loading room from API: ${roomId}`); - - try { - // Fetch room data from server endpoint - const gameId = window.breakEscapeConfig?.gameId; - if (!gameId) { - console.error('Game ID not available in breakEscapeConfig'); - return; - } - - const response = await fetch(`/break_escape/games/${gameId}/room/${roomId}`); - - if (!response.ok) { - console.error(`Failed to load room ${roomId}: ${response.status} ${response.statusText}`); - return; - } - - const data = await response.json(); - const roomData = data.room; - - if (!roomData) { - console.error(`No room data returned for ${roomId}`); - return; - } - - console.log(`✅ Received room data from API for ${roomId}`); - - // Load NPCs BEFORE creating room visuals - // This ensures NPCs are registered before room objects/sprites are created - if (window.npcLazyLoader && roomData) { - try { - await window.npcLazyLoader.loadNPCsForRoom(roomId, roomData); - } catch (error) { - console.error(`Failed to load NPCs for room ${roomId}:`, error); - // Continue with room creation even if NPC loading fails + + let roomData; + + // Check if roomData is cached (from unlock API response) + if (roomDataCache.has(roomId)) { + console.log(`✅ Using cached room data for ${roomId} (from unlock response)`); + roomData = roomDataCache.get(roomId); + roomDataCache.delete(roomId); // Clear from cache after use + } else { + console.log(`Lazy loading room from API: ${roomId}`); + + try { + // Fetch room data from server endpoint + const gameId = window.breakEscapeConfig?.gameId; + if (!gameId) { + console.error('Game ID not available in breakEscapeConfig'); + return; } + + const response = await fetch(`/break_escape/games/${gameId}/room/${roomId}`); + + if (!response.ok) { + console.error(`Failed to load room ${roomId}: ${response.status} ${response.statusText}`); + return; + } + + const data = await response.json(); + roomData = data.room; + + if (!roomData) { + console.error(`No room data returned for ${roomId}`); + return; + } + + console.log(`✅ Received room data from API for ${roomId}`); + } catch (error) { + console.error(`Error loading room ${roomId}:`, error); + return; } - - createRoom(roomId, roomData, position); - - // Reveal (make visible) but do NOT mark as discovered - // The room will only be marked as "discovered" when the player - // actually enters it via door transition - revealRoom(roomId); - } catch (error) { - console.error(`Error loading room ${roomId}:`, error); } + + // Load NPCs BEFORE creating room visuals + // This ensures NPCs are registered before room objects/sprites are created + if (window.npcLazyLoader && roomData) { + try { + await window.npcLazyLoader.loadNPCsForRoom(roomId, roomData); + } catch (error) { + console.error(`Failed to load NPCs for room ${roomId}:`, error); + // Continue with room creation even if NPC loading fails + } + } + + createRoom(roomId, roomData, position); + + // Reveal (make visible) but do NOT mark as discovered + // The room will only be marked as "discovered" when the player + // actually enters it via door transition + revealRoom(roomId); } export function initializeRooms(gameInstance) { diff --git a/public/break_escape/js/minigames/password/password-minigame.js b/public/break_escape/js/minigames/password/password-minigame.js index 3768e3e..68ae627 100644 --- a/public/break_escape/js/minigames/password/password-minigame.js +++ b/public/break_escape/js/minigames/password/password-minigame.js @@ -450,6 +450,8 @@ export class PasswordMinigame extends MinigameScene { console.log('Server returned container contents:', response.contents); lockable.scenarioData.contents = response.contents; } + // Store server response to pass through callback chain + this.serverResponse = response; this.passwordCorrect(); } else { this.passwordIncorrect(); @@ -466,12 +468,13 @@ export class PasswordMinigame extends MinigameScene { passwordCorrect() { this.cleanup(); this.showSuccess("Password accepted! Access granted.", true, 3000); - + // Set game result for the callback this.gameResult = { success: true, password: this.gameData.password, - attempts: this.gameData.attempts + attempts: this.gameData.attempts, + serverResponse: this.serverResponse // Include server response (roomData for doors, contents for containers) }; } diff --git a/public/break_escape/js/minigames/pin/pin-minigame.js b/public/break_escape/js/minigames/pin/pin-minigame.js index 40e2581..2bf4882 100644 --- a/public/break_escape/js/minigames/pin/pin-minigame.js +++ b/public/break_escape/js/minigames/pin/pin-minigame.js @@ -309,6 +309,9 @@ export class PinMinigame extends MinigameScene { lockable.scenarioData.contents = response.contents; } + // Store server response to pass through callback chain + this.serverResponse = response; + return response.success; } catch (error) { console.error('Server validation error:', error); @@ -444,15 +447,16 @@ export class PinMinigame extends MinigameScene { this.isLocked = true; this.displayElement.classList.add('success'); this.displayElement.textContent = this.currentInput; - + this.showSuccess('PIN Correct! Access Granted.', true, 2000); - + // Set game result this.gameResult = { success: true, pin: this.currentInput, attempts: this.attemptCount, - timeToComplete: Date.now() - this.startTime + timeToComplete: Date.now() - this.startTime, + serverResponse: this.serverResponse // Include server response (roomData for doors, contents for containers) }; } diff --git a/public/break_escape/js/systems/doors.js b/public/break_escape/js/systems/doors.js index 660b8da..be4008a 100644 --- a/public/break_escape/js/systems/doors.js +++ b/public/break_escape/js/systems/doors.js @@ -580,15 +580,21 @@ function handleDoorInteraction(doorSprite) { } // Function to unlock a door (called after successful unlock) -function unlockDoor(doorSprite) { +function unlockDoor(doorSprite, roomData) { const props = doorSprite.doorProperties; console.log(`Unlocking door: ${props.roomId} -> ${props.connectedRoom}`); - + // Mark door as unlocked props.locked = false; - + + // If roomData was provided from server unlock response, cache it + if (roomData && window.roomDataCache) { + console.log(`📦 Caching room data for ${props.connectedRoom} from unlock response`); + window.roomDataCache.set(props.connectedRoom, roomData); + } + // TODO: Implement unlock animation/effect - + // Open the door openDoor(doorSprite); } diff --git a/public/break_escape/js/systems/minigame-starters.js b/public/break_escape/js/systems/minigame-starters.js index ee2138a..cc8fe9b 100644 --- a/public/break_escape/js/systems/minigame-starters.js +++ b/public/break_escape/js/systems/minigame-starters.js @@ -535,11 +535,11 @@ export function startPinMinigame(lockable, type, correctPin, callback) { if (success) { console.log('PIN MINIGAME SUCCESS'); window.gameAlert(`Correct PIN! The ${type} is now unlocked.`, 'success', 'PIN Accepted', 4000); - callback(true); + callback(true, result); // Pass result with serverResponse } else { console.log('PIN MINIGAME FAILED'); window.gameAlert("Failed to enter correct PIN.", 'error', 'PIN Rejected', 3000); - callback(false); + callback(false, result); } } }); @@ -587,11 +587,11 @@ export function startPasswordMinigame(lockable, type, correctPassword, callback, if (success) { console.log('PASSWORD MINIGAME SUCCESS'); window.gameAlert(`Correct password! The ${type} is now unlocked.`, 'success', 'Password Accepted', 4000); - callback(true); + callback(true, result); // Pass result with serverResponse } else { console.log('PASSWORD MINIGAME FAILED'); window.gameAlert("Failed to enter correct password.", 'error', 'Password Rejected', 3000); - callback(false); + callback(false, result); } } }); diff --git a/public/break_escape/js/systems/unlock-system.js b/public/break_escape/js/systems/unlock-system.js index b7a34ef..9de362d 100644 --- a/public/break_escape/js/systems/unlock-system.js +++ b/public/break_escape/js/systems/unlock-system.js @@ -176,13 +176,13 @@ export function handleUnlock(lockable, type) { case 'pin': console.log('PIN CODE REQUESTED (server-side validation)'); // Pass null for required code - will be validated server-side - startPinMinigame(lockable, type, null, (success) => { + startPinMinigame(lockable, type, null, (success, result) => { if (success) { - unlockTarget(lockable, type, lockable.layer); + unlockTarget(lockable, type, lockable.layer, result?.serverResponse); } }); break; - + case 'password': console.log('PASSWORD REQUESTED (server-side validation)'); @@ -197,9 +197,9 @@ export function handleUnlock(lockable, type) { }; // Pass null for required password - will be validated server-side - startPasswordMinigame(lockable, type, null, (success) => { + startPasswordMinigame(lockable, type, null, (success, result) => { if (success) { - unlockTarget(lockable, type, lockable.layer); + unlockTarget(lockable, type, lockable.layer, result?.serverResponse); } }, passwordOptions); break; @@ -470,13 +470,15 @@ export function getLockRequirementsForItem(item) { }; } -export function unlockTarget(lockable, type, layer) { - console.log('🔓 unlockTarget called:', { type, lockable }); - +export function unlockTarget(lockable, type, layer, serverResponse) { + console.log('🔓 unlockTarget called:', { type, lockable, serverResponse }); + if (type === 'door') { // After unlocking, use the proper door unlock function - unlockDoor(lockable); - + // Pass roomData from server if available (avoids separate room API call) + const roomData = serverResponse?.roomData; + unlockDoor(lockable, roomData); + // Emit door unlocked event console.log('🔓 Checking for eventDispatcher:', !!window.eventDispatcher); if (window.eventDispatcher) {