Enhance key and lock systems with normalization and debugging improvements

This commit is contained in:
Z. Cliffe Schreuders
2025-11-14 20:51:25 +00:00
parent 7dfe91c4fd
commit 5b75d75a01
7 changed files with 117 additions and 10 deletions

View File

@@ -942,6 +942,15 @@ function normalizeScenarioKeyPins(scenario) {
return Math.round(25 + (value / 100) * 40);
}
// IMPORTANT: Normalize keyPins in ALL places they appear in the scenario!
// KeyPins must be normalized exactly once at scenario load time, BEFORE any items are dropped or used.
// If keyPins appear in multiple places (startItemsInInventory, room objects, NPC itemsHeld, etc.),
// they ALL need normalization or keys/locks won't match when used.
// For example:
// - A key in an NPC's itemsHeld must be normalized the same way as a room object key
// - The door's room-level keyPins must be normalized the same way
// - Otherwise when the NPC drops the key, it won't open the door!
// Normalize keyPins in startItemsInInventory (for starting keys)
if (scenario.startItemsInInventory && Array.isArray(scenario.startItemsInInventory)) {
scenario.startItemsInInventory.forEach((item, index) => {
@@ -962,6 +971,21 @@ function normalizeScenarioKeyPins(scenario) {
console.log(`🔄 Normalized room keyPins for ${roomId}:`, roomData.keyPins);
}
// Normalize NPC itemsHeld keyPins (items NPCs start with/carry)
// CRITICAL: This ensures keys dropped by NPCs match the door's keyPins
if (roomData.npcs && Array.isArray(roomData.npcs)) {
roomData.npcs.forEach((npc, npcIndex) => {
if (npc.itemsHeld && Array.isArray(npc.itemsHeld)) {
npc.itemsHeld.forEach((item, itemIndex) => {
if (item.keyPins && Array.isArray(item.keyPins)) {
item.keyPins = item.keyPins.map(convertKeyPin);
console.log(`🔄 Normalized NPC itemsHeld keyPins for ${roomId}.npcs[${npcIndex}].itemsHeld[${itemIndex}] (${item.type}):`, item.keyPins);
}
});
}
});
}
// Convert keyPins for all objects in the room
if (roomData.objects && Array.isArray(roomData.objects)) {
roomData.objects.forEach((obj, index) => {

View File

@@ -1979,14 +1979,29 @@ function createNPCSpritesForRoom(roomId, roomData) {
console.log(` npcManager: ${!!window.npcManager}`);
console.log(` gameRef: ${!!gameRef}`);
// Get the current scene - use gameRef.scene (the current scene manager) or fall back to window.game
const currentScene = gameRef.scene || window.game?.scene?.scenes?.[0];
console.log(` currentScene: ${!!currentScene}, key: ${currentScene?.key}`);
// Get the current scene instance - need to get it from the scene manager
// gameRef.scene is the SceneManager, we need gameRef.scene.getScene() to get the actual scene
let currentScene = null;
if (gameRef && gameRef.scene && typeof gameRef.scene.getScene === 'function') {
// Get the running scene from the scene manager
currentScene = gameRef.scene.getScene('default') ||
gameRef.scene.scenes?.[0];
}
if (!currentScene && window.game?.scene) {
currentScene = window.game.scene.getScene('default') ||
window.game.scene.scenes?.[0];
}
if (currentScene) {
console.log(` currentScene: ${!!currentScene}, key: ${currentScene?.key}, isScene: ${currentScene?.add ? 'yes' : 'no'}`);
if (currentScene && typeof currentScene.add?.graphics === 'function') {
window.npcManager.setLOSVisualization(true, currentScene);
} else {
console.warn(`⚠️ Cannot get current scene for LOS visualization`);
console.warn(`⚠️ Cannot get valid Phaser scene for LOS visualization`, {
currentScene: !!currentScene,
hasAddMethod: !!currentScene?.add,
hasGraphicsMethod: typeof currentScene?.add?.graphics
});
}
} else {
console.log(`👁️ No NPCs requesting LOS visualization in room ${roomId}`);

0
js/core/title-screen.js Normal file
View File

View File

@@ -227,6 +227,18 @@ export function createDoorSpritesForRoom(roomId, position) {
// Check for both keyPins (camelCase) and key_pins (snake_case) in the room data
const keyPinsArray = connectedRoomData?.keyPins || connectedRoomData?.key_pins;
// DEBUG: Log what we're finding
if (connectedRoomData?.locked) {
console.log(`🔍 Door keyPins lookup for ${connectedRoom}:`, {
connectedRoomData_keyPins: connectedRoomData?.keyPins,
connectedRoomData_key_pins: connectedRoomData?.key_pins,
finalKeyPinsArray: keyPinsArray,
locked: connectedRoomData?.locked,
lockType: connectedRoomData?.lockType,
requires: connectedRoomData?.requires
});
}
// Set up door properties
doorSprite.doorProperties = {
roomId: roomId,

View File

@@ -364,12 +364,22 @@ function addKeyToInventory(sprite) {
};
}
// DEBUG: Check properties before adding
const keyId = sprite.scenarioData?.key_id || sprite.key_id;
const keyPins = sprite.scenarioData?.keyPins || sprite.keyPins;
console.log(`🔑 BEFORE adding key to ring (sprite object):`, {
sprite_key_id: sprite.key_id,
sprite_keyPins: sprite.keyPins,
scenarioData_key_id: sprite.scenarioData?.key_id,
scenarioData_keyPins: sprite.scenarioData?.keyPins,
resolved_key_id: keyId,
resolved_keyPins: keyPins
});
// Add the key to the key ring
window.inventory.keyRing.keys.push(sprite);
// Log key storage with keyPins
const keyId = sprite.scenarioData?.key_id || sprite.key_id;
const keyPins = sprite.scenarioData?.keyPins || sprite.keyPins;
console.log(`✓ Key "${sprite.scenarioData?.name}" added to key ring:`, {
key_id: keyId,
keyPins: keyPins,
@@ -380,6 +390,13 @@ function addKeyToInventory(sprite) {
// Update or create the key ring display
updateKeyRingDisplay();
// IMPORTANT: Reinitialize key-lock mappings now that we have a new key
// This is critical for newly acquired keys (e.g., dropped by NPCs) to unlock doors
if (window.initializeKeyLockMappings) {
console.log('🔑 Reinitializing key-lock mappings after adding key to inventory');
window.initializeKeyLockMappings();
}
// Apply pulse animation to the key ring slot instead of showing notification
const keyRingSlot = window.inventory.keyRing.slot;
if (keyRingSlot) {
@@ -436,12 +453,18 @@ function updateKeyRingDisplay() {
tooltip.textContent = keyRing.keys.length === 1 ? keyRing.keys[0].scenarioData.name : 'Key Ring';
// Add item data - use the first key's data as the primary data
const allKeysData = keyRing.keys.map(k => k.scenarioData);
console.log(`🔑 Building key ring scenarioData with ${keyRing.keys.length} keys:`, {
firstKeyScenarioData: keyRing.keys[0].scenarioData,
allKeysData: allKeysData
});
itemImg.scenarioData = {
...keyRing.keys[0].scenarioData,
name: keyRing.keys.length === 1 ? keyRing.keys[0].scenarioData.name : 'Key Ring',
type: 'key_ring',
keyCount: keyRing.keys.length,
allKeys: keyRing.keys.map(k => k.scenarioData)
allKeys: allKeysData
};
itemImg.name = 'key';
itemImg.objectId = 'inventory_key_ring';

View File

@@ -114,14 +114,36 @@ function assignKeysToLocks() {
const key = playerKeys.find(k => k.scenarioData.key_id === keyId);
if (key) {
// Get the actual scenario keyPins for this lock
let scenarioKeyPins = null;
if (lock.objectIndex !== undefined) {
// Object lock - get keyPins from the object
const obj = window.gameState?.scenario?.rooms?.[lock.roomId]?.objects?.[lock.objectIndex];
scenarioKeyPins = obj?.keyPins || obj?.key_pins;
} else {
// Room lock - get keyPins from the room
const room = window.gameState?.scenario?.rooms?.[lock.roomId];
scenarioKeyPins = room?.keyPins || room?.key_pins;
}
// Use scenario keyPins if available, otherwise generate random ones
const pinHeights = scenarioKeyPins || generatePinHeightsForLock(lock.roomId, keyId);
// Create a lock configuration for this specific lock
const lockConfig = {
id: `${lock.roomId}_${lock.objectIndex !== undefined ? `obj_${lock.objectIndex}` : 'room'}`,
pinCount: 4, // Default pin count
pinHeights: generatePinHeightsForLock(lock.roomId, keyId), // Generate consistent pin heights
pinCount: pinHeights?.length || 4, // Use actual pin count from keyPins, default 4
pinHeights: pinHeights, // Use scenario keyPins or generated ones
difficulty: 'medium'
};
console.log(`📌 Lock mapping for key "${key.scenarioData.name}" (${keyId}):`, {
lockLocation: `${lock.roomName}${lock.objectName ? ` - ${lock.objectName}` : ''}`,
scenarioKeyPins: scenarioKeyPins,
pinHeights: pinHeights,
pinCount: lockConfig.pinCount
});
// Store the mapping
window.keyLockMappings[keyId] = {
lockId: lockConfig.id,

View File

@@ -177,6 +177,17 @@ function dropNPCItems(npcId) {
interactable: true
};
// DEBUG: Log key properties if this is a key
if (item.type === 'key') {
console.log(`🔑 Dropped key "${item.name}":`, {
source: 'npc-hostile.js dropNPCItems',
item_key_id: item.key_id,
item_keyPins: item.keyPins,
droppedItemData_key_id: droppedItemData.key_id,
droppedItemData_keyPins: droppedItemData.keyPins
});
}
// Apply scenario properties to sprite
spriteObj.scenarioData = droppedItemData;
spriteObj.interactable = true;