mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-21 11:18:08 +00:00
Optimize unlock flow to use single API call for doors and containers
Server-side changes: - Unlock endpoint already returns roomData for doors Client-side changes: - Pass serverResponse through minigame callback chain - Store serverResponse in minigame gameResult (password & PIN) - Update minigame-starters to pass result to callbacks - Update unlock-system callbacks to accept and pass serverResponse - Pass roomData to unlockDoor for doors - Cache roomData in roomDataCache before loadRoom is called - loadRoom checks cache before making API call Benefits: - Doors: Unlock + room load = 1 API call instead of 2 - Containers: Unlock + contents = 1 API call instead of 2 - More efficient, faster user experience - Consistent pattern for both doors and containers
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user