diff --git a/js/core/rooms_new.js b/js/core/rooms_new.js deleted file mode 100644 index 0c989ab..0000000 --- a/js/core/rooms_new.js +++ /dev/null @@ -1,1519 +0,0 @@ -/** - * ROOM MANAGEMENT SYSTEM - SIMPLIFIED DEPTH LAYERING APPROACH - * =========================================================== - * - * This system implements a simplified depth-based layering approach where all elements - * use their world Y position + layer offset for depth calculation. - * - * DEPTH CALCULATION PHILOSOPHY: - * ----------------------------- - * 1. **World Y Position**: All depth calculations are based on the world Y position - * of the element (bottom of sprites, room ground level). - * - * 2. **Layer Offsets**: Each element type has a fixed layer offset added to its Y position - * to create proper layering hierarchy. - * - * 3. **Room Y Offset**: Room Y position is considered to be 2 tiles south of the actual - * room position (where door sprites are positioned). - * - * DEPTH HIERARCHY: - * ---------------- - * Room Layers (world Y + layer offset): - * - Floor: roomWorldY + 0.1 - * - Collision: roomWorldY + 0.15 - * - Walls: roomWorldY + 0.2 - * - Props: roomWorldY + 0.3 - * - Other: roomWorldY + 0.4 - * - * Interactive Elements (world Y + layer offset): - * - Doors: doorY + 0.45 (between room tiles and sprites) - * - Door Tops: doorY + 0.55 (above doors, below sprites) - * - Animated Doors: doorBottomY + 0.45 (bottom Y + door layer offset) - * - Animated Door Tops: doorBottomY + 0.55 (bottom Y + door top layer offset) - * - Player: playerBottomY + 0.5 (dynamic based on Y position) - * - Objects: objectBottomY + 0.5 (dynamic based on Y position) - * - * DEPTH CALCULATION CONSISTENCY: - * ------------------------------ - * ✅ All elements use world Y position + layer offset - * ✅ Room Y is 2 tiles south of room position - * ✅ Player and objects use bottom Y position - * ✅ Simple and consistent across all elements - */ - -// Room management system -import { TILE_SIZE, DOOR_ALIGN_OVERLAP, GRID_SIZE, INTERACTION_RANGE_SQ, INTERACTION_CHECK_INTERVAL } from '../utils/constants.js?v=7'; - -// Import the new system modules -import { createDoorSpritesForRoom, checkDoorTransitions, updateDoorSpritesVisibility } from '../systems/doors.js'; -import { setupChairCollisions, setupExistingChairsWithNewRoom, calculateChairSpinDirection, updateSwivelChairRotation, updateSpriteDepth } from '../systems/object-physics.js'; -import { createPlayerBumpEffect, createPlantSwayEffect } from '../systems/player-effects.js'; -import { createWallCollisionBoxes, removeTilesUnderDoor, removeWallTilesForDoorInRoom, removeWallTilesAtWorldPosition } from '../systems/collision.js'; - -export let rooms = {}; -export let currentRoom = ''; -export let currentPlayerRoom = ''; -export let discoveredRooms = new Set(); - -// Helper function to check if a position overlaps with existing items -function isPositionOverlapping(x, y, roomId, itemSize = TILE_SIZE) { - const room = rooms[roomId]; - if (!room || !room.objects) return false; - - // Check against all existing objects in the room - for (const obj of Object.values(room.objects)) { - if (!obj || !obj.active) continue; - - // Calculate overlap with some padding - const padding = TILE_SIZE * 0.5; // Half tile padding - const objLeft = obj.x - padding; - const objRight = obj.x + obj.width + padding; - const objTop = obj.y - padding; - const objBottom = obj.y + obj.height + padding; - - const newLeft = x; - const newRight = x + itemSize; - const newTop = y; - const newBottom = y + itemSize; - - // Check for overlap - if (newLeft < objRight && newRight > objLeft && - newTop < objBottom && newBottom > objTop) { - return true; // Overlap detected - } - } - - return false; // No overlap -} - -// Make discoveredRooms available globally -window.discoveredRooms = discoveredRooms; -let gameRef = null; - -// Define scale factors for different object types -const OBJECT_SCALES = { - 'notes': 0.75, - 'key': 0.75, - 'phone': 1, - 'tablet': 0.75, - 'bluetooth_scanner': 0.7 -}; - -// Function to load a room lazily -function loadRoom(roomId) { - const gameScenario = window.gameScenario; - const roomData = gameScenario.rooms[roomId]; - const position = window.roomPositions[roomId]; - - if (!roomData || !position) { - console.error(`Cannot load room ${roomId}: missing data or position`); - return; - } - - console.log(`Lazy loading room: ${roomId}`); - createRoom(roomId, roomData, position); - revealRoom(roomId); -} - -export function initializeRooms(gameInstance) { - gameRef = gameInstance; - console.log('Initializing rooms'); - rooms = {}; - window.rooms = rooms; // Ensure window.rooms references the same object - currentRoom = ''; - currentPlayerRoom = ''; - window.currentPlayerRoom = ''; - discoveredRooms = new Set(); - // Update global reference - window.discoveredRooms = discoveredRooms; - - // Calculate room positions for lazy loading - window.roomPositions = calculateRoomPositions(gameInstance); - console.log('Room positions calculated for lazy loading'); - - // Initialize the new system modules - if (window.initializeDoors) { - window.initializeDoors(gameInstance, rooms); - } - if (window.initializeObjectPhysics) { - window.initializeObjectPhysics(gameInstance, rooms); - } - if (window.initializePlayerEffects) { - window.initializePlayerEffects(gameInstance, rooms); - } - if (window.initializeCollision) { - window.initializeCollision(gameInstance, rooms); - } -} - -// Door validation is now handled by the sprite-based door system -export function validateDoorsByRoomOverlap() { - console.log('Door validation is now handled by the sprite-based door system'); -} - -// Calculate world bounds -export function calculateWorldBounds(gameInstance) { - console.log('Calculating world bounds'); - const gameScenario = window.gameScenario; - if (!gameScenario || !gameScenario.rooms) { - console.error('Game scenario not loaded properly'); - return { - x: -1800, - y: -1800, - width: 3600, - height: 3600 - }; - } - - let minX = -1800, minY = -1800, maxX = 1800, maxY = 1800; - - // Check all room positions to determine world bounds - const roomPositions = calculateRoomPositions(gameInstance); - Object.entries(gameScenario.rooms).forEach(([roomId, room]) => { - const position = roomPositions[roomId]; - if (position) { - // Get actual room dimensions - const map = gameInstance.cache.tilemap.get(room.type); - let roomWidth = 800, roomHeight = 600; // fallback - - if (map) { - let width, height; - if (map.json) { - width = map.json.width; - height = map.json.height; - } else if (map.data) { - width = map.data.width; - height = map.data.height; - } else { - width = map.width; - height = map.height; - } - - if (width && height) { - roomWidth = width * TILE_SIZE; // tile width is TILE_SIZE - roomHeight = height * TILE_SIZE; // tile height is TILE_SIZE - } - } - - minX = Math.min(minX, position.x); - minY = Math.min(minY, position.y); - maxX = Math.max(maxX, position.x + roomWidth); - maxY = Math.max(maxY, position.y + roomHeight); - } - }); - - // Add some padding - const padding = 200; - return { - x: minX - padding, - y: minY - padding, - width: (maxX - minX) + (padding * 2), - height: (maxY - minY) + (padding * 2) - }; -} - -export function calculateRoomPositions(gameInstance) { - const OVERLAP = 64; - const positions = {}; - const gameScenario = window.gameScenario; - - console.log('=== Starting Room Position Calculations ==='); - - // Get room dimensions from tilemaps - const roomDimensions = {}; - Object.entries(gameScenario.rooms).forEach(([roomId, roomData]) => { - const map = gameInstance.cache.tilemap.get(roomData.type); - console.log(`Debug - Room ${roomId}:`, { - mapData: map, - fullData: map?.data, - json: map?.json - }); - - // Try different ways to access the data - if (map) { - let width, height; - if (map.json) { - width = map.json.width; - height = map.json.height; - } else if (map.data) { - width = map.data.width; - height = map.data.height; - } else { - width = map.width; - height = map.height; - } - - roomDimensions[roomId] = { - width: width * TILE_SIZE, // tile width is TILE_SIZE - height: height * TILE_SIZE // tile height is TILE_SIZE - }; - } else { - console.error(`Could not find tilemap data for room ${roomId}`); - // Fallback to default dimensions if needed - roomDimensions[roomId] = { - width: 320, // default width (10 tiles at 32px) - height: 288 // default height (9 tiles at 32px) - }; - } - }); - - // Start with reception room at origin - positions[gameScenario.startRoom] = { x: 0, y: 0 }; - console.log(`Starting room ${gameScenario.startRoom} position:`, positions[gameScenario.startRoom]); - - // Process rooms level by level, starting from reception - const processed = new Set([gameScenario.startRoom]); - const queue = [gameScenario.startRoom]; - - while (queue.length > 0) { - const currentRoomId = queue.shift(); - const currentRoom = gameScenario.rooms[currentRoomId]; - const currentPos = positions[currentRoomId]; - const currentDimensions = roomDimensions[currentRoomId]; - - console.log(`\nProcessing room ${currentRoomId}`); - console.log('Current position:', currentPos); - console.log('Connections:', currentRoom.connections); - - Object.entries(currentRoom.connections).forEach(([direction, connected]) => { - console.log(`\nProcessing ${direction} connection:`, connected); - - if (Array.isArray(connected)) { - const roomsToProcess = connected.filter(r => !processed.has(r)); - console.log('Unprocessed connected rooms:', roomsToProcess); - if (roomsToProcess.length === 0) return; - - if (direction === 'north' || direction === 'south') { - const firstRoom = roomsToProcess[0]; - const firstRoomWidth = roomDimensions[firstRoom].width; - const firstRoomHeight = roomDimensions[firstRoom].height; - - const secondRoom = roomsToProcess[1]; - const secondRoomWidth = roomDimensions[secondRoom].width; - const secondRoomHeight = roomDimensions[secondRoom].height; - - if (direction === 'north') { - // First room - right edge aligns with current room's left edge - positions[firstRoom] = { - x: currentPos.x - firstRoomWidth + DOOR_ALIGN_OVERLAP, - y: currentPos.y - firstRoomHeight + OVERLAP - }; - - // Second room - left edge aligns with current room's right edge - positions[secondRoom] = { - x: currentPos.x + currentDimensions.width - DOOR_ALIGN_OVERLAP, - y: currentPos.y - secondRoomHeight + OVERLAP - }; - } else if (direction === 'south') { - // First room - left edge aligns with current room's right edge - positions[firstRoom] = { - x: currentPos.x - firstRoomWidth + DOOR_ALIGN_OVERLAP, - y: currentPos.y + currentDimensions.height - OVERLAP - }; - - // Second room - right edge aligns with current room's left edge - positions[secondRoom] = { - x: currentPos.x + currentDimensions.width - DOOR_ALIGN_OVERLAP, - y: currentPos.y + currentDimensions.height - secondRoomHeight - OVERLAP - }; - } - - roomsToProcess.forEach(roomId => { - processed.add(roomId); - queue.push(roomId); - console.log(`Positioned room ${roomId} at:`, positions[roomId]); - }); - } - } else { - if (processed.has(connected)) { - return; - } - - const connectedDimensions = roomDimensions[connected]; - - // Center the connected room - const x = currentPos.x + - (currentDimensions.width - connectedDimensions.width) / 2; - const y = direction === 'north' - ? currentPos.y - connectedDimensions.height + OVERLAP - : currentPos.y + currentDimensions.height - OVERLAP; - - - positions[connected] = { x, y }; - processed.add(connected); - queue.push(connected); - - console.log(`Positioned single room ${connected} at:`, positions[connected]); - } - }); - } - - console.log('\n=== Final Room Positions ==='); - Object.entries(positions).forEach(([roomId, pos]) => { - console.log(`${roomId}:`, pos); - }); - - return positions; -} - -export function createRoom(roomId, roomData, position) { - try { - console.log(`Creating room ${roomId} of type ${roomData.type}`); - const gameScenario = window.gameScenario; - - const map = gameRef.make.tilemap({ key: roomData.type }); - const tilesets = []; - - // Add tilesets - console.log('Available tilesets:', map.tilesets.map(t => ({ - name: t.name, - columns: t.columns, - firstgid: t.firstgid, - tilecount: t.tilecount - }))); - - const regularTilesets = map.tilesets.filter(t => - !t.name.includes('Interiors_48x48') && - t.name !== 'objects' && // Skip the objects tileset as it's handled separately - t.name !== 'tables' && // Skip the tables tileset as it's also an ImageCollection - !t.name.includes('../objects/') && // Skip individual object tilesets - !t.name.includes('../tables/') && // Skip individual table tilesets - t.columns > 0 // Only process tilesets with columns (regular tilesets) - ); - - console.log('Filtered tilesets to process:', regularTilesets.map(t => t.name)); - - regularTilesets.forEach(tileset => { - console.log(`Attempting to add tileset: ${tileset.name}`); - const loadedTileset = map.addTilesetImage(tileset.name, tileset.name); - if (loadedTileset) { - tilesets.push(loadedTileset); - console.log(`Added regular tileset: ${tileset.name}`); - } else { - console.log(`Failed to add tileset: ${tileset.name}`); - } - }); - - // Initialize room data structure first - rooms[roomId] = { - map, - layers: {}, - wallsLayers: [], - objects: {}, - position - }; - - // Ensure window.rooms is updated - window.rooms = rooms; - - const layers = rooms[roomId].layers; - const wallsLayers = rooms[roomId].wallsLayers; - - // IMPORTANT: This counter ensures unique layer IDs across ALL rooms and should not be removed - if (!window.globalLayerCounter) window.globalLayerCounter = 0; - - // Calculate base depth for this room's layers - // Use world Y position + layer offset (room Y is 2 tiles south of actual room position) - const roomWorldY = position.y + TILE_SIZE * 2; // Room Y is 2 tiles south of room position - - // Create door sprites based on gameScenario connections - const doorSprites = createDoorSpritesForRoom(roomId, position); - rooms[roomId].doorSprites = doorSprites; - console.log(`Stored ${doorSprites.length} door sprites in room ${roomId}`); - - // Store door positions for wall tile removal - const doorPositions = doorSprites.map(doorSprite => ({ - x: doorSprite.x, - y: doorSprite.y, - width: doorSprite.body ? doorSprite.body.width : TILE_SIZE, - height: doorSprite.body ? doorSprite.body.height : TILE_SIZE * 2 - })); - - // Create other layers with appropriate depths - map.layers.forEach((layerData, index) => { - // Skip the doors layer since we're using sprite-based doors - if (layerData.name.toLowerCase().includes('doors')) { - console.log(`Skipping doors layer: ${layerData.name} in room ${roomId}`); - return; - } - - window.globalLayerCounter++; - const uniqueLayerId = `${roomId}_${layerData.name}_${window.globalLayerCounter}`; - - const layer = map.createLayer(index, tilesets, position.x, position.y); - if (layer) { - layer.name = uniqueLayerId; - // remove tiles under doors - removeTilesUnderDoor(layer, roomId, position); - - - // Set depth based on layer type and room position - if (layerData.name.toLowerCase().includes('floor')) { - layer.setDepth(roomWorldY + 0.1); - console.log(`Floor layer depth: ${roomWorldY + 0.1}`); - } else if (layerData.name.toLowerCase().includes('walls')) { - layer.setDepth(roomWorldY + 0.2); - console.log(`Wall layer depth: ${roomWorldY + 0.2}`); - - // Remove wall tiles under doors - removeTilesUnderDoor(layer, roomId, position); - - // Set up wall layer (collision disabled - using custom collision boxes instead) - try { - // Disabled: layer.setCollisionByExclusion([-1]); - console.log(`Wall layer ${uniqueLayerId} - using custom collision boxes instead of tile collision`); - - wallsLayers.push(layer); - console.log(`Added wall layer: ${uniqueLayerId}`); - - // Disabled: Old collision system between player and wall layer - // const player = window.player; - // if (player && player.body) { - // gameRef.physics.add.collider(player, layer); - // console.log(`Added collision between player and wall layer: ${uniqueLayerId}`); - // } - - // Create thin collision boxes for wall tiles - createWallCollisionBoxes(layer, roomId, position); - } catch (e) { - console.warn(`Error setting up collisions for ${uniqueLayerId}:`, e); - } - } else if (layerData.name.toLowerCase().includes('collision')) { - layer.setDepth(roomWorldY + 0.15); - console.log(`Collision layer depth: ${roomWorldY + 0.15}`); - - // Set up collision layer (collision disabled - using custom collision boxes instead) - try { - // Disabled: layer.setCollisionByExclusion([-1]); - console.log(`Collision layer ${uniqueLayerId} - using custom collision boxes instead of tile collision`); - - // Disabled: Old collision system between player and collision layer - // const player = window.player; - // if (player && player.body) { - // gameRef.physics.add.collider(player, layer); - // console.log(`Added collision between player and collision layer: ${uniqueLayerId}`); - // } - } catch (e) { - console.warn(`Error setting up collision layer ${uniqueLayerId}:`, e); - } - } else if (layerData.name.toLowerCase().includes('props')) { - layer.setDepth(roomWorldY + 0.3); - console.log(`Props layer depth: ${roomWorldY + 0.3}`); - } else { - // Other layers (decorations, etc.) - layer.setDepth(roomWorldY + 0.4); - console.log(`Other layer depth: ${roomWorldY + 0.4}`); - } - - layers[uniqueLayerId] = layer; - layer.setVisible(false); - layer.setAlpha(0); - } - }); - - // Handle new Tiled object layers with grouping logic - const objectLayers = [ - 'tables', 'table_items', 'conditional_table_items', - 'items', 'conditional_items' - ]; - - // First, collect all objects by layer - const objectsByLayer = {}; - objectLayers.forEach(layerName => { - const objectLayer = map.getObjectLayer(layerName); - if (objectLayer && objectLayer.objects.length > 0) { - objectsByLayer[layerName] = objectLayer.objects; - console.log(`Collected ${layerName} layer with ${objectLayer.objects.length} objects`); - } - }); - - // Process tables first to establish base positions - const tableObjects = []; - if (objectsByLayer.tables) { - objectsByLayer.tables.forEach(obj => { - const processedObj = processObject(obj, position, roomId, 'table'); - if (processedObj) { - tableObjects.push(processedObj); - } - }); - } - - // Group table items with their closest tables - const tableGroups = []; - tableObjects.forEach(table => { - const group = { - table: table, - items: [], - baseDepth: table.sprite.depth - }; - tableGroups.push(group); - }); - - // Process table items and assign them to groups - if (objectsByLayer.table_items) { - objectsByLayer.table_items.forEach(obj => { - const processedObj = processObject(obj, position, roomId, 'table_item'); - if (processedObj) { - // Find the closest table - const closestTable = findClosestTable(processedObj.sprite, tableObjects); - if (closestTable) { - const group = tableGroups.find(g => g.table === closestTable); - if (group) { - group.items.push(processedObj); - } - } - } - }); - } - - // Conditional table items are now handled by scenario matching system - - // Set z-index ordering for each group (table first, then items from north to south) - tableGroups.forEach(group => { - // Table is already at the correct depth - console.log(`Setting up group for table at depth ${group.baseDepth}`); - - // Sort items from north to south (lower Y values first) - group.items.sort((a, b) => a.sprite.y - b.sprite.y); - - // Set items to share the same base depth as the table - group.items.forEach((item, index) => { - // Table items don't need elevation - they're grouped with the table - const itemDepth = group.baseDepth + (index + 1) * 0.01; // Slight offset for proper ordering - item.sprite.setDepth(itemDepth); - - // No elevation for table items - item.sprite.elevation = 0; - console.log(`Set item ${item.sprite.name} to depth ${itemDepth} (north to south order, no elevation)`); - }); - }); - - // Process scenario objects with conditional item matching first - const usedItems = processScenarioObjectsWithConditionalMatching(roomId, position, objectsByLayer); - - // Process all non-conditional items (chairs, plants, etc.) - // Give them default properties if not used in scenario - if (objectsByLayer.items) { - objectsByLayer.items.forEach(obj => { - const imageName = getImageNameFromObject(obj); - const baseType = extractBaseTypeFromImageName(imageName); - - // Skip if this base type was used by scenario objects - if (imageName && (usedItems.has(imageName) || usedItems.has(baseType))) { - console.log(`Skipping regular item ${imageName} (baseType: ${baseType}) - used by scenario object`); - return; - } - processObject(obj, position, roomId, 'item'); - }); - } - - // Helper function to process scenario objects with conditional matching - function processScenarioObjectsWithConditionalMatching(roomId, position, objectsByLayer) { - const gameScenario = window.gameScenario; - if (!gameScenario.rooms[roomId].objects) { - return new Set(); - } - - const usedItems = new Set(); - console.log(`Processing ${gameScenario.rooms[roomId].objects.length} scenario objects for room ${roomId}`); - - // Create maps of all available items by type - const regularItemsByType = {}; - const conditionalItemsByType = {}; - const conditionalTableItemsByType = {}; - - // Process regular items layer - if (objectsByLayer.items) { - objectsByLayer.items.forEach(obj => { - const imageName = getImageNameFromObject(obj); - if (imageName && imageName !== 'unknown') { - const baseType = extractBaseTypeFromImageName(imageName); - if (!regularItemsByType[baseType]) { - regularItemsByType[baseType] = []; - } - regularItemsByType[baseType].push(obj); - } - }); - } - - // Process conditional items layer - if (objectsByLayer.conditional_items) { - objectsByLayer.conditional_items.forEach(obj => { - const imageName = getImageNameFromObject(obj); - if (imageName && imageName !== 'unknown') { - const baseType = extractBaseTypeFromImageName(imageName); - if (!conditionalItemsByType[baseType]) { - conditionalItemsByType[baseType] = []; - } - conditionalItemsByType[baseType].push(obj); - } - }); - } - - // Process conditional table items layer - if (objectsByLayer.conditional_table_items) { - console.log(`Processing ${objectsByLayer.conditional_table_items.length} conditional table items`); - objectsByLayer.conditional_table_items.forEach((obj, index) => { - const imageName = getImageNameFromObject(obj); - console.log(`Conditional table item ${index}: GID ${obj.gid} -> imageName: ${imageName}`); - if (imageName && imageName !== 'unknown') { - const baseType = extractBaseTypeFromImageName(imageName); - console.log(`Conditional table item ${imageName} -> baseType: ${baseType}`); - if (!conditionalTableItemsByType[baseType]) { - conditionalTableItemsByType[baseType] = []; - } - conditionalTableItemsByType[baseType].push(obj); - console.log(`Added ${baseType} to conditional table items (total: ${conditionalTableItemsByType[baseType].length})`); - } else { - console.log(`No valid imageName found for conditional table item ${index} with GID ${obj.gid} (imageName: ${imageName})`); - } - }); - } - - // Process each scenario object - gameScenario.rooms[roomId].objects.forEach((scenarioObj, index) => { - const objType = scenarioObj.type; - - // Skip items that should be in inventory - if (scenarioObj.inInventory) { - return; - } - - let sprite = null; - let usedItem = null; - let isTableItem = false; - - console.log(`Looking for scenario object type: ${objType}`); - console.log(`Available regular items for ${objType}: ${regularItemsByType[objType] ? regularItemsByType[objType].length : 0}`); - console.log(`Available conditional items for ${objType}: ${conditionalItemsByType[objType] ? conditionalItemsByType[objType].length : 0}`); - console.log(`Available conditional table items for ${objType}: ${conditionalTableItemsByType[objType] ? conditionalTableItemsByType[objType].length : 0}`); - - // First, try to find a matching regular item - if (regularItemsByType[objType] && regularItemsByType[objType].length > 0) { - usedItem = regularItemsByType[objType].shift(); - console.log(`Using regular item for ${objType}`); - } - // Then try conditional items - else if (conditionalItemsByType[objType] && conditionalItemsByType[objType].length > 0) { - usedItem = conditionalItemsByType[objType].shift(); - console.log(`Using conditional item for ${objType}`); - } - // Finally try conditional table items - else if (conditionalTableItemsByType[objType] && conditionalTableItemsByType[objType].length > 0) { - usedItem = conditionalTableItemsByType[objType].shift(); - isTableItem = true; - console.log(`Using conditional table item for ${objType}`); - } - - if (usedItem) { - // Create sprite using the found item - const imageName = getImageNameFromObject(usedItem); - sprite = gameRef.add.sprite( - position.x + usedItem.x, - position.y + usedItem.y - usedItem.height, - imageName - ); - - if (usedItem.rotation) { - sprite.setRotation(Phaser.Math.DegToRad(usedItem.rotation)); - } - - console.log(`Created ${objType} using ${imageName}`); - - // Track this item as used - usedItems.add(imageName); - const baseType = extractBaseTypeFromImageName(imageName); - usedItems.add(baseType); - - // If it's a table item, find the closest table and group it - if (isTableItem && tableObjects.length > 0) { - const closestTable = findClosestTable(sprite, tableObjects); - if (closestTable) { - const group = tableGroups.find(g => g.table === closestTable); - if (group) { - // Table items don't need elevation - they're grouped with the table - const itemDepth = group.baseDepth + (group.items.length + 1) * 0.01; - sprite.setDepth(itemDepth); - - // No elevation for table items - sprite.elevation = 0; - group.items.push({ sprite, type: 'conditional_table_item' }); - } - } - } - } else { - // No matching item found, create at random position - const roomWidth = 10 * TILE_SIZE; - const roomHeight = 9 * TILE_SIZE; - const padding = TILE_SIZE * 2; - - // Find a valid position that doesn't overlap with existing items - let randomX, randomY; - let attempts = 0; - const maxAttempts = 50; - - do { - randomX = position.x + padding + Math.random() * (roomWidth - padding * 2); - randomY = position.y + padding + Math.random() * (roomHeight - padding * 2); - attempts++; - } while (attempts < maxAttempts && isPositionOverlapping(randomX, randomY, roomId, TILE_SIZE)); - - sprite = gameRef.add.sprite(randomX, randomY, objType); - console.log(`Created ${objType} at random position - no matching item found (attempts: ${attempts})`); - } - - // Set common properties - sprite.setOrigin(0, 0); - sprite.name = usedItem ? getImageNameFromObject(usedItem) : objType; - sprite.objectId = `${roomId}_${objType}_${index}`; - sprite.setInteractive({ useHandCursor: true }); - - // Set depth based on world Y position (unless already set for table items) - if (!isTableItem || !usedItem) { - const objectBottomY = sprite.y + sprite.height; - - // Calculate elevation for items on the back wall (top 2 tiles of room) - const roomTopY = position.y; - const backWallThreshold = roomTopY + (2 * 32); // Back wall is top 2 tiles - const itemBottomY = sprite.y + sprite.height; - const elevation = itemBottomY < backWallThreshold ? (backWallThreshold - itemBottomY) : 0; - - const objectDepth = objectBottomY + 0.5 + elevation; - sprite.setDepth(objectDepth); - - // Store elevation for debugging - sprite.elevation = elevation; - } - - // Store scenario data with sprite - sprite.scenarioData = scenarioObj; - sprite.interactable = true; // Mark scenario items as interactable - console.log(`Applied scenario data to ${objType}:`, { - name: scenarioObj.name, - type: scenarioObj.type, - takeable: scenarioObj.takeable, - readable: scenarioObj.readable, - text: scenarioObj.text, - observations: scenarioObj.observations - }); - - // Initially hide the object - sprite.setVisible(false); - - // Store the object - rooms[roomId].objects[sprite.objectId] = sprite; - - // Add click handler - sprite.on('pointerdown', (pointer, localX, localY, event) => { - // Check if player is in range for interaction - const player = window.player; - if (player) { - const dx = player.x - sprite.x; - const dy = player.y - sprite.y; - const distanceSq = dx * dx + dy * dy; - const INTERACTION_RANGE_SQ = 64 * 64; // 64 pixels squared - - if (distanceSq <= INTERACTION_RANGE_SQ) { - // Player is in range - prevent movement and trigger interaction - if (event && event.preventDefault) { - event.preventDefault(); - } - // Set flag to prevent player movement - window.preventPlayerMovement = true; - if (window.handleObjectInteraction) { - window.handleObjectInteraction(sprite); - } - // Reset flag after a short delay - setTimeout(() => { - window.preventPlayerMovement = false; - }, 100); - } else { - // Player is out of range - allow movement to the item - console.log('Scenario item out of range, allowing player movement'); - // Don't prevent movement - let the player move to the item - } - } - }); - }); - - // Re-sort table groups after adding scenario items to maintain north-to-south order - tableGroups.forEach(group => { - // Sort items from north to south (lower Y values first) - group.items.sort((a, b) => a.sprite.y - b.sprite.y); - - // Recalculate depths for all items in the group - group.items.forEach((item, index) => { - // Table items don't need elevation - they're grouped with the table - const itemDepth = group.baseDepth + (index + 1) * 0.01; - item.sprite.setDepth(itemDepth); - - // No elevation for table items - item.sprite.elevation = 0; - console.log(`Re-sorted item ${item.sprite.name} to depth ${itemDepth} (north to south order, no elevation)`); - }); - }); - - // Log summary of item usage - console.log(`=== Item Usage Summary ===`); - Object.entries(regularItemsByType).forEach(([baseType, items]) => { - console.log(`Regular items for ${baseType}: ${items.length} available`); - }); - Object.entries(conditionalItemsByType).forEach(([baseType, items]) => { - console.log(`Conditional items for ${baseType}: ${items.length} available`); - }); - Object.entries(conditionalTableItemsByType).forEach(([baseType, items]) => { - console.log(`Conditional table items for ${baseType}: ${items.length} available`); - }); - - return usedItems; - } - - // Helper function to get image name from Tiled object - function getImageNameFromObject(obj) { - // Find the tileset that contains this GID - // Handle multiple tileset instances by finding the most recent one - let tileset = null; - let localTileId = 0; - let bestMatch = null; - let bestMatchIndex = -1; - - for (let i = 0; i < map.tilesets.length; i++) { - const ts = map.tilesets[i]; - const maxGid = ts.tilecount ? ts.firstgid + ts.tilecount : ts.firstgid + 1; - if (obj.gid >= ts.firstgid && obj.gid < maxGid) { - // Prefer objects tilesets, and among those, prefer the most recent (highest index) - if (ts.name === 'objects' || ts.name.includes('objects/') || ts.name.includes('tables/')) { - if (bestMatchIndex < i) { - bestMatch = ts; - bestMatchIndex = i; - tileset = ts; - localTileId = obj.gid - ts.firstgid; - } - } else if (!bestMatch) { - // Fallback to any matching tileset if no objects tileset found - tileset = ts; - localTileId = obj.gid - ts.firstgid; - } - } - } - - if (tileset && (tileset.name === 'objects' || tileset.name.includes('objects/') || tileset.name.includes('tables/'))) { - let imageName = null; - - if (tileset.images && tileset.images[localTileId]) { - const imageData = tileset.images[localTileId]; - if (imageData && imageData.name) { - imageName = imageData.name; - } - } else if (tileset.tileData && tileset.tileData[localTileId]) { - const tileData = tileset.tileData[localTileId]; - if (tileData && tileData.image) { - const imagePath = tileData.image; - imageName = imagePath.split('/').pop().replace('.png', ''); - } - } else if (tileset.name.includes('objects/') || tileset.name.includes('tables/')) { - imageName = tileset.name.split('/').pop().replace('.png', ''); - } - - return imageName; - } - - return null; - } - - // Helper function to extract base type from image name - function extractBaseTypeFromImageName(imageName) { - // Check if imageName is null or undefined - if (!imageName) { - console.log('Warning: extractBaseTypeFromImageName called with null/undefined imageName'); - return 'unknown'; - } - - // Remove numbers and common suffixes to get base type - // e.g., "pc2.png" -> "pc", "laptop3.png" -> "laptop", "phone4" -> "phone" - let baseType = imageName.replace(/\d+$/, ''); // Remove trailing numbers - baseType = baseType.replace(/\.png$/, ''); // Remove .png extension - - // Handle special cases where scenario uses plural but items use singular - if (baseType === 'note') { - // Convert note1 -> notes1, note2 -> notes2, etc. - const number = imageName.match(/\d+/); - if (number) { - baseType = 'notes' + number[0]; - } else { - baseType = 'notes'; // Fallback for note without number - } - } - - console.log(`Extracting base type: ${imageName} -> ${baseType}`); - return baseType; - } - - // Helper function to process individual objects - function processObject(obj, position, roomId, type) { - // Find the tileset that contains this GID - // Handle multiple tileset instances by finding the most recent one - let tileset = null; - let localTileId = 0; - let bestMatch = null; - let bestMatchIndex = -1; - - for (let i = 0; i < map.tilesets.length; i++) { - const ts = map.tilesets[i]; - // Handle tilesets with undefined tilecount (individual object tilesets) - const maxGid = ts.tilecount ? ts.firstgid + ts.tilecount : ts.firstgid + 1; - if (obj.gid >= ts.firstgid && obj.gid < maxGid) { - // Prefer objects tilesets, and among those, prefer the most recent (highest index) - if (ts.name === 'objects' || ts.name.includes('objects/') || ts.name.includes('tables/')) { - if (bestMatchIndex < i) { - bestMatch = ts; - bestMatchIndex = i; - tileset = ts; - localTileId = obj.gid - ts.firstgid; - } - } else if (!bestMatch) { - // Fallback to any matching tileset if no objects tileset found - tileset = ts; - localTileId = obj.gid - ts.firstgid; - } - } - } - - if (tileset && (tileset.name === 'objects' || tileset.name.includes('objects/') || tileset.name.includes('tables/'))) { - // This is an ImageCollection or individual object tileset, get the image data - let imageName = null; - - // Check if this is an ImageCollection with images array - if (tileset.images && tileset.images[localTileId]) { - // Get image from the images array - const imageData = tileset.images[localTileId]; - if (imageData && imageData.name) { - imageName = imageData.name; - } - } else if (tileset.tileData && tileset.tileData[localTileId]) { - // Fallback: get from tileData - const tileData = tileset.tileData[localTileId]; - if (tileData && tileData.image) { - const imagePath = tileData.image; - imageName = imagePath.split('/').pop().replace('.png', ''); - } - } else if (tileset.name.includes('objects/') || tileset.name.includes('tables/')) { - // This is an individual object or table tileset, extract name from tileset name - imageName = tileset.name.split('/').pop().replace('.png', ''); - } - - if (imageName) { - console.log(`Creating object from ImageCollection: ${imageName} at (${obj.x}, ${obj.y})`); - - // Create sprite at the object's position - const sprite = gameRef.add.sprite( - position.x + obj.x, - position.y + obj.y - obj.height, // Adjust for Tiled's coordinate system - imageName - ); - - // Set sprite properties - sprite.setOrigin(0, 0); - sprite.name = imageName; - sprite.objectId = `${roomId}_${imageName}_${obj.id}`; - sprite.setInteractive({ useHandCursor: true }); - - // Check if this is a chair with wheels - if (imageName.startsWith('chair-') && !imageName.startsWith('chair-waiting')) { - sprite.hasWheels = true; - - // Check if this is a swivel chair - if (imageName.startsWith('chair-exec-rotate') || - imageName.startsWith('chair-white-1-rotate') || - imageName.startsWith('chair-white-2-rotate')) { - sprite.isSwivelChair = true; - - // Determine starting frame based on image name - let frameNumber; - if (imageName.startsWith('chair-exec-rotate')) { - frameNumber = parseInt(imageName.replace('chair-exec-rotate', '')); - } else if (imageName.startsWith('chair-white-1-rotate')) { - frameNumber = parseInt(imageName.replace('chair-white-1-rotate', '')); - } else if (imageName.startsWith('chair-white-2-rotate')) { - frameNumber = parseInt(imageName.replace('chair-white-2-rotate', '')); - } - - sprite.currentFrame = frameNumber - 1; // Convert to 0-based index - sprite.rotationSpeed = 0; - sprite.maxRotationSpeed = 0.15; // Slower maximum rotation speed - sprite.originalTexture = imageName; // Store original texture name - sprite.spinDirection = 0; // -1 for counter-clockwise, 1 for clockwise, 0 for no spin - - } - - // Calculate elevation for chairs (same as other objects) - const roomTopY = position.y; - const backWallThreshold = roomTopY + (2 * 32); // Back wall is top 2 tiles - const itemBottomY = sprite.y + sprite.height; - const elevation = itemBottomY < backWallThreshold ? (backWallThreshold - itemBottomY) : 0; - sprite.elevation = elevation; - - } - - // Check if this is a plant that can sway - if (imageName.startsWith('plant-large')) { - sprite.canSway = true; - sprite.originalScaleX = sprite.scaleX; - sprite.originalScaleY = sprite.scaleY; - sprite.originalX = sprite.x; - sprite.originalY = sprite.y; - sprite.originalWidth = sprite.width; - sprite.originalHeight = sprite.height; - sprite.originalSkewX = 0; - sprite.originalSkewY = 0; - - // Add displacement FX for realistic sway effect - // Use a custom displacement texture for wind-like movement - sprite.preFX.addDisplacement('wind_displacement', 0.01, 0.01); - // Store reference to the displacement FX (it's the last added effect) - sprite.displacementFX = sprite.preFX.list[sprite.preFX.list.length - 1]; - - console.log(`Plant ${imageName} can sway with displacement FX`); - } - - // Set depth based on world Y position with elevation - const objectBottomY = sprite.y + sprite.height; - - // Calculate elevation for items on the back wall (top 2 tiles of room) - const roomTopY = position.y; - const backWallThreshold = roomTopY + (2 * 32); // Back wall is top 2 tiles - const itemBottomY = sprite.y + sprite.height; - const elevation = itemBottomY < backWallThreshold ? (backWallThreshold - itemBottomY) : 0; - - const objectDepth = objectBottomY + 0.5 + elevation; - sprite.setDepth(objectDepth); - - // Store elevation for debugging - sprite.elevation = elevation; - - // Apply rotation if specified - if (obj.rotation) { - sprite.setRotation(Phaser.Math.DegToRad(obj.rotation)); - } - - // Initially hide the object - sprite.setVisible(false); - - // Set up collision for tables - if (type === 'table') { - // Add physics body to table (static body) - gameRef.physics.add.existing(sprite, true); - - // Wait for the next frame to ensure body is fully initialized - gameRef.time.delayedCall(0, () => { - if (sprite.body) { - // Use direct property assignment (fallback method) - sprite.body.immovable = true; - - // Set custom collision box - bottom quarter of height, inset 10px from sides - const tableWidth = sprite.width; - const tableHeight = sprite.height; - const collisionWidth = tableWidth - 20; // 10px inset on each side - const collisionHeight = tableHeight / 4; // Bottom quarter - const offsetX = 10; // 10px inset from left - const offsetY = tableHeight - collisionHeight; // Bottom quarter - - sprite.body.setSize(collisionWidth, collisionHeight); - sprite.body.setOffset(offsetX, offsetY); - - console.log(`Set table ${imageName} collision box: ${collisionWidth}x${collisionHeight} at offset (${offsetX}, ${offsetY})`); - - // Add collision with player - const player = window.player; - if (player && player.body) { - gameRef.physics.add.collider(player, sprite); - console.log(`Added collision between player and table: ${imageName}`); - } - } - }); - } - - // Set up physics for chairs with wheels - if (sprite.hasWheels) { - // Add physics body to chair (dynamic body for movement) - gameRef.physics.add.existing(sprite, false); - - // Wait for the next frame to ensure body is fully initialized - gameRef.time.delayedCall(0, () => { - if (sprite.body) { - // Set chair as movable - sprite.body.immovable = false; - sprite.body.setImmovable(false); - - // Set collision box at base of chair - const chairWidth = sprite.width; - const chairHeight = sprite.height; - const collisionWidth = chairWidth - 10; // 5px inset on each side - const collisionHeight = chairHeight / 3; // Bottom third - const offsetX = 5; // 5px inset from left - const offsetY = chairHeight - collisionHeight; // Bottom third - - sprite.body.setSize(collisionWidth, collisionHeight); - sprite.body.setOffset(offsetX, offsetY); - - // Set physics properties for bouncing - sprite.body.setBounce(0.3, 0.3); - sprite.body.setDrag(100, 100); - sprite.body.setMaxVelocity(200, 200); - - - // Add collision with player - const player = window.player; - if (player && player.body) { - // Create collision callback function - const collisionCallback = (player, chair) => { - if (chair.isSwivelChair) { - calculateChairSpinDirection(player, chair); - } - }; - - gameRef.physics.add.collider(player, sprite, collisionCallback); - } - - // Store chair reference for collision detection - if (!window.chairs) { - window.chairs = []; - } - window.chairs.push(sprite); - - // Set up collision with other chairs and items - setupChairCollisions(sprite); - } - }); - } - - // Store the object in the room - if (!rooms[roomId].objects) { - rooms[roomId].objects = {}; - } - rooms[roomId].objects[sprite.objectId] = sprite; - - // Give default properties to regular items (non-scenario items) - if (type === 'item' || type === 'table_item') { - // Strip out suffix after first dash and any numbers for cleaner names - const cleanName = imageName.replace(/-.*$/, '').replace(/\d+$/, ''); - sprite.scenarioData = { - name: cleanName, - type: cleanName, - takeable: false, - readable: false, - observations: `A ${cleanName} in the room` - }; - console.log(`Applied default properties to ${type} ${imageName} -> ${cleanName}`); - } - - // Add click handler - sprite.on('pointerdown', (pointer, localX, localY, event) => { - console.log('Tiled object clicked:', { name: imageName, id: sprite.objectId, interactable: sprite.interactable }); - // Only trigger interaction for interactable items - if (sprite.interactable && window.handleObjectInteraction) { - // Check if player is in range for interaction - const player = window.player; - if (player) { - const dx = player.x - sprite.x; - const dy = player.y - sprite.y; - const distanceSq = dx * dx + dy * dy; - const INTERACTION_RANGE_SQ = 64 * 64; // 64 pixels squared - - if (distanceSq <= INTERACTION_RANGE_SQ) { - // Player is in range - prevent movement and trigger interaction - if (event && event.preventDefault) { - event.preventDefault(); - } - // Set flag to prevent player movement - window.preventPlayerMovement = true; - window.handleObjectInteraction(sprite); - // Reset flag after a short delay - setTimeout(() => { - window.preventPlayerMovement = false; - }, 100); - } else { - // Player is out of range - allow movement to the item - console.log('Regular item out of range, allowing player movement'); - // Don't prevent movement - let the player move to the item - } - } - } - }); - - console.log(`Created Tiled object: ${sprite.objectId} at (${sprite.x}, ${sprite.y})`); - - return { sprite, type }; - } else { - console.log(`No image data found for GID ${obj.gid} in objects tileset`); - } - } else if (tileset && tileset.name !== 'objects' && !tileset.name.includes('objects/')) { - // Handle other tilesets (like tables) normally - console.log(`Skipping non-objects tileset: ${tileset.name}`); - } else { - console.log(`No tileset found for GID ${obj.gid}`); - } - - return null; - } - - // Helper function to find the closest table to an item - function findClosestTable(itemSprite, tableObjects) { - let closestTable = null; - let closestDistance = Infinity; - - tableObjects.forEach(table => { - // Calculate distance between item and table centers - const itemCenterX = itemSprite.x + itemSprite.width / 2; - const itemCenterY = itemSprite.y + itemSprite.height / 2; - const tableCenterX = table.sprite.x + table.sprite.width / 2; - const tableCenterY = table.sprite.y + table.sprite.height / 2; - - const distance = Math.sqrt( - Math.pow(itemCenterX - tableCenterX, 2) + - Math.pow(itemCenterY - tableCenterY, 2) - ); - - if (distance < closestDistance) { - closestDistance = distance; - closestTable = table; - } - }); - - console.log(`Found closest table for item ${itemSprite.name} at distance ${closestDistance}`); - return closestTable; - } - - // Handle objects layer (legacy) - const objectsLayer = map.getObjectLayer('Object Layer 1'); - console.log(`Object layer found for room ${roomId}:`, objectsLayer ? `${objectsLayer.objects.length} objects` : 'No objects layer'); - if (objectsLayer) { - - // Handle collision objects - objectsLayer.objects.forEach(obj => { - if (obj.name.toLowerCase().includes('collision') || obj.type === 'collision') { - console.log(`Creating collision object: ${obj.name} at (${obj.x}, ${obj.y})`); - - // Create invisible collision body - const collisionBody = gameRef.add.rectangle( - position.x + obj.x + obj.width/2, - position.y + obj.y + obj.height/2, - obj.width, - obj.height - ); - - // Make it invisible but with collision - collisionBody.setVisible(false); - collisionBody.setAlpha(0); - gameRef.physics.add.existing(collisionBody, true); - - // Add collision with player - const player = window.player; - if (player && player.body) { - gameRef.physics.add.collider(player, collisionBody); - console.log(`Added collision object: ${obj.name}`); - } - - // Store collision body in room for cleanup - if (!room.collisionBodies) { - room.collisionBodies = []; - } - room.collisionBodies.push(collisionBody); - } - }); - - // Create a map of room objects by type for easy lookup - const roomObjectsByType = {}; - objectsLayer.objects.forEach(obj => { - if (!roomObjectsByType[obj.name]) { - roomObjectsByType[obj.name] = []; - } - roomObjectsByType[obj.name].push(obj); - }); - - // Legacy scenario object processing removed - now handled by conditional matching system - } - - // Set up pending wall collision boxes if player is ready - const room = rooms[roomId]; - if (room && room.pendingWallCollisionBoxes && window.player && window.player.body) { - room.pendingWallCollisionBoxes.forEach(collisionBox => { - gameRef.physics.add.collider(window.player, collisionBox); - }); - console.log(`Set up ${room.pendingWallCollisionBoxes.length} pending wall collision boxes for room ${roomId}`); - // Clear pending collision boxes - room.pendingWallCollisionBoxes = []; - } - - // Set up collisions between existing chairs and new room objects - setupExistingChairsWithNewRoom(roomId); - } catch (error) { - console.error(`Error creating room ${roomId}:`, error); - console.error('Error details:', error.stack); - } -} - -export function revealRoom(roomId) { - if (rooms[roomId]) { - const room = rooms[roomId]; - - // Reveal all layers - Object.values(room.layers).forEach(layer => { - if (layer && layer.setVisible) { - layer.setVisible(true); - layer.setAlpha(1); - } - }); - - // Show door sprites for this room - if (room.doorSprites) { - room.doorSprites.forEach(doorSprite => { - doorSprite.setVisible(true); - doorSprite.setAlpha(1); - console.log(`Made door sprite visible for room ${roomId}`); - }); - } - - // Show all objects - if (room.objects) { - console.log(`Revealing ${Object.keys(room.objects).length} objects in room ${roomId}`); - Object.values(room.objects).forEach(obj => { - if (obj && obj.setVisible && obj.active) { // Only show active objects - obj.setVisible(true); - obj.alpha = obj.active ? (obj.originalAlpha || 1) : 0.3; - console.log(`Made object visible: ${obj.objectId} at (${obj.x}, ${obj.y})`); - } - }); - } else { - console.log(`No objects found in room ${roomId}`); - } - - discoveredRooms.add(roomId); - // Update global reference - window.discoveredRooms = discoveredRooms; - } - currentRoom = roomId; -} - -export function updatePlayerRoom() { - // Check which room the player is currently in - const player = window.player; - if (!player) { - return; // Player not created yet - } - - // Check for door transitions first - const doorTransitionRoom = checkDoorTransitions(player); - if (doorTransitionRoom && doorTransitionRoom !== currentPlayerRoom) { - // Door transition detected to a different room - console.log(`Door transition detected: ${currentPlayerRoom} -> ${doorTransitionRoom}`); - currentPlayerRoom = doorTransitionRoom; - window.currentPlayerRoom = doorTransitionRoom; - - // Reveal the room if not already discovered - if (!discoveredRooms.has(doorTransitionRoom)) { - revealRoom(doorTransitionRoom); - } - - // Player depth is now handled by the simplified updatePlayerDepth function in player.js - return; // Exit early to prevent overlap-based detection from overriding - } - - // Only do overlap-based room detection if no door transition occurred - // and if we don't have a current room (fallback) - if (currentPlayerRoom) { - return; // Keep current room if no door transition and we already have one - } - - // Fallback to overlap-based room detection - let overlappingRooms = []; - - // Check all rooms for overlap with proper threshold - Object.entries(rooms).forEach(([roomId, room]) => { - const roomBounds = { - x: room.position.x, - y: room.position.y, - width: room.map.widthInPixels, - height: room.map.heightInPixels - }; - - if (isPlayerInBounds(player, roomBounds)) { - overlappingRooms.push({ - roomId: roomId, - position: room.position.y // Use Y position for northernmost sorting - }); - - // Reveal room if not already discovered - if (!discoveredRooms.has(roomId)) { - console.log(`Player overlapping room: ${roomId}`); - revealRoom(roomId); - } - } - }); - - // If we're not overlapping any rooms - if (overlappingRooms.length === 0) { - currentPlayerRoom = null; - window.currentPlayerRoom = null; - return null; - } - - // Sort overlapping rooms by Y position (northernmost first - lower Y values) - overlappingRooms.sort((a, b) => a.position - b.position); - - // Use the northernmost room (lowest Y position) as the main room - const northernmostRoom = overlappingRooms[0].roomId; - - // Update current room (use the northernmost overlapping room as the "main" room) - if (currentPlayerRoom !== northernmostRoom) { - console.log(`Player's main room changed to: ${northernmostRoom} (northernmost of ${overlappingRooms.length} overlapping rooms)`); - currentPlayerRoom = northernmostRoom; - window.currentPlayerRoom = northernmostRoom; - - // Player depth is now handled by the simplified updatePlayerDepth function in player.js - } -} - -// Helper function to check if player properly overlaps with room bounds -function isPlayerInBounds(player, bounds) { - // Use the player's physics body bounds for more precise detection - const playerBody = player.body; - const playerBounds = { - left: playerBody.x, - right: playerBody.x + playerBody.width, - top: playerBody.y, - bottom: playerBody.y + playerBody.height - }; - - // Calculate the overlap area between player and room - const overlapWidth = Math.min(playerBounds.right, bounds.x + bounds.width) - - Math.max(playerBounds.left, bounds.x); - const overlapHeight = Math.min(playerBounds.bottom, bounds.y + bounds.height) - - Math.max(playerBounds.top, bounds.y); - - // Require a minimum overlap percentage (50% of player width/height) - const minOverlapPercent = 0.5; - const playerWidth = playerBounds.right - playerBounds.left; - const playerHeight = playerBounds.bottom - playerBounds.top; - - const widthOverlapPercent = overlapWidth / playerWidth; - const heightOverlapPercent = overlapHeight / playerHeight; - - return overlapWidth > 0 && - overlapHeight > 0 && - widthOverlapPercent >= minOverlapPercent && - heightOverlapPercent >= minOverlapPercent; -} - -// Door collisions are now handled by sprite-based system -export function setupDoorCollisions() { - console.log('Door collisions are now handled by sprite-based system'); -} - -// Export for global access -window.initializeRooms = initializeRooms; -window.setupDoorCollisions = setupDoorCollisions; -window.loadRoom = loadRoom; - -// Export functions for module imports -export { updateDoorSpritesVisibility }; diff --git a/js/systems/doors.js b/js/systems/doors.js index f864217..28937ea 100644 --- a/js/systems/doors.js +++ b/js/systems/doors.js @@ -221,6 +221,9 @@ export function createDoorSpritesForRoom(roomId, position) { }); console.log(`Door depth: ${doorSprite.depth} (roomDepth: ${doorY}, between tiles and sprites)`); + // Get lock properties from the destination room (the room you're trying to enter) + const connectedRoomData = gameScenario.rooms[connectedRoom]; + // Set up door properties doorSprite.doorProperties = { roomId: roomId, @@ -229,11 +232,19 @@ export function createDoorSpritesForRoom(roomId, position) { worldX: doorX, worldY: doorY, open: false, - locked: roomData.locked || false, - lockType: roomData.lockType || null, - requires: roomData.requires || null + locked: connectedRoomData?.locked || false, + lockType: connectedRoomData?.lockType || null, + requires: connectedRoomData?.requires || null }; + // Debug door properties + console.log(`Door properties set for ${roomId} -> ${connectedRoom}:`, { + locked: doorSprite.doorProperties.locked, + lockType: doorSprite.doorProperties.lockType, + requires: doorSprite.doorProperties.requires, + connectedRoomData: connectedRoomData + }); + // Set up door info for transition detection doorSprite.doorInfo = { roomId: roomId, @@ -307,13 +318,141 @@ function handleDoorInteraction(doorSprite) { if (props.locked) { console.log(`Door is locked. Type: ${props.lockType}, Requires: ${props.requires}`); - // Use the proper lock system from interactions.js - handleUnlock(doorSprite, 'door'); + // Use the door properties directly since we already have the lock information + handleDoorUnlockDirect(doorSprite, props); } else { openDoor(doorSprite); } } +// Function to handle door unlocking directly using door properties +function handleDoorUnlockDirect(doorSprite, props) { + console.log('DOOR UNLOCK ATTEMPT (direct)'); + + switch(props.lockType) { + case 'key': + const requiredKey = props.requires; + console.log('KEY REQUIRED', requiredKey); + + // Get all keys from player's inventory + const playerKeys = window.inventory.items.filter(item => + item && item.scenarioData && + item.scenarioData.type === 'key' + ); + + if (playerKeys.length > 0) { + // Show key selection interface + if (window.startKeySelectionMinigame) { + window.startKeySelectionMinigame(doorSprite, 'door', playerKeys, requiredKey); + } else { + console.log('Key selection minigame not available'); + window.gameAlert(`Requires key: ${requiredKey}`, 'error', 'Locked', 4000); + } + } else { + // Check for lockpick kit + const hasLockpick = window.inventory.items.some(item => + item && item.scenarioData && + item.scenarioData.type === 'lockpick' + ); + + if (hasLockpick) { + console.log('LOCKPICK AVAILABLE'); + if (confirm("Would you like to attempt picking this lock?")) { + let difficulty = 'medium'; + + console.log('STARTING LOCKPICK MINIGAME', { difficulty }); + if (window.startLockpickingMinigame) { + window.startLockpickingMinigame(doorSprite, window.game, difficulty, (success) => { + if (success) { + unlockDoor(doorSprite); + window.gameAlert(`Successfully picked the lock!`, 'success', 'Lock Picked', 4000); + } else { + console.log('LOCKPICK FAILED'); + window.gameAlert('Failed to pick the lock. Try again.', 'error', 'Pick Failed', 3000); + } + }); + } + } + } else { + console.log('NO KEYS OR LOCKPICK AVAILABLE'); + window.gameAlert(`Requires key: ${requiredKey}`, 'error', 'Locked', 4000); + } + } + break; + + case 'pin': + console.log('PIN CODE REQUESTED'); + const pinInput = prompt(`Enter PIN code:`); + if (pinInput === props.requires) { + unlockDoor(doorSprite); + console.log('PIN CODE SUCCESS'); + window.gameAlert(`Correct PIN! The door is now unlocked.`, 'success', 'PIN Accepted', 4000); + } else if (pinInput !== null) { + console.log('PIN CODE FAIL'); + window.gameAlert("Incorrect PIN code.", 'error', 'PIN Rejected', 3000); + } + break; + + case 'password': + console.log('PASSWORD REQUESTED'); + if (window.showPasswordModal) { + window.showPasswordModal(function(passwordInput) { + if (passwordInput === props.requires) { + unlockDoor(doorSprite); + console.log('PASSWORD SUCCESS'); + window.gameAlert(`Correct password! The door is now unlocked.`, 'success', 'Password Accepted', 4000); + } else if (passwordInput !== null) { + console.log('PASSWORD FAIL'); + window.gameAlert("Incorrect password.", 'error', 'Password Rejected', 3000); + } + }); + } else { + // Fallback to prompt + const passwordInput = prompt(`Enter password:`); + if (passwordInput === props.requires) { + unlockDoor(doorSprite); + console.log('PASSWORD SUCCESS'); + window.gameAlert(`Correct password! The door is now unlocked.`, 'success', 'Password Accepted', 4000); + } else if (passwordInput !== null) { + console.log('PASSWORD FAIL'); + window.gameAlert("Incorrect password.", 'error', 'Password Rejected', 3000); + } + } + break; + + case 'biometric': + console.log('BIOMETRIC REQUIRED'); + const hasBiometric = window.gameState?.biometricSamples?.length > 0; + if (hasBiometric) { + if (confirm("Use biometric authentication?")) { + unlockDoor(doorSprite); + window.gameAlert(`Biometric authentication successful!`, 'success', 'Access Granted', 4000); + } + } else { + window.gameAlert(`Biometric authentication required.`, 'error', 'Access Denied', 4000); + } + break; + + case 'bluetooth': + console.log('BLUETOOTH REQUIRED'); + const hasBluetooth = window.gameState?.bluetoothDevices?.length > 0; + if (hasBluetooth) { + if (confirm("Use Bluetooth device?")) { + unlockDoor(doorSprite); + window.gameAlert(`Bluetooth authentication successful!`, 'success', 'Access Granted', 4000); + } + } else { + window.gameAlert(`Bluetooth device required.`, 'error', 'Access Denied', 4000); + } + break; + + default: + console.log('UNKNOWN LOCK TYPE:', props.lockType); + window.gameAlert(`Unknown lock type: ${props.lockType}`, 'error', 'Locked', 4000); + break; + } +} + // Function to unlock a door (called by interactions.js after successful unlock) function unlockDoor(doorSprite) { const props = doorSprite.doorProperties;