diff --git a/js/core/game.js b/js/core/game.js index 84b9650..f445de8 100644 --- a/js/core/game.js +++ b/js/core/game.js @@ -54,7 +54,12 @@ export function preload() { // Load tileset images referenced by the new Tiled map this.load.image('office-updated', 'assets/tiles/rooms/room1.png'); this.load.image('door_sheet_32', 'assets/tiles/door_sheet_32.png'); - this.load.image('door_side_sheet_32', 'assets/tiles/door_side_sheet_32.png'); + + // Load side door spritesheet for east/west doors (6 frames: closed, opening, open, etc.) + this.load.spritesheet('door_side_sheet_32', 'assets/tiles/door_side_sheet_32.png', { + frameWidth: 32, + frameHeight: 32 + }); // Load table tileset images this.load.image('desk-ceo1', 'assets/tables/desk-ceo1.png'); diff --git a/js/core/rooms.js b/js/core/rooms.js index 1ced5ef..6e12c3c 100644 --- a/js/core/rooms.js +++ b/js/core/rooms.js @@ -969,6 +969,84 @@ function positionNorthSingle(currentRoom, connectedRoom, currentPos, dimensions) return alignToGrid(x, y); } +/** + * Validate if multiple connections can fit in the given direction + * Returns true if valid, false with console error if invalid + */ +function validateMultipleConnections(direction, currentRoom, connectedRooms, currentDim, dimensions) { + if (direction === 'north' || direction === 'south') { + // Check if rooms can fit side-by-side when centered on door positions + const edgeInset = TILE_SIZE * 1.5; // 48px + const availableWidth = currentDim.widthPx - (edgeInset * 2); + const doorCount = connectedRooms.length; + const doorSpacing = availableWidth / (doorCount - 1); + + // Calculate total span of rooms when centered on doors + let minX = Infinity; + let maxX = -Infinity; + + connectedRooms.forEach((roomId, index) => { + const connectedDim = dimensions[roomId]; + const doorX = edgeInset + (doorSpacing * index); + const roomLeft = doorX - (connectedDim.widthPx / 2); + const roomRight = doorX + (connectedDim.widthPx / 2); + + minX = Math.min(minX, roomLeft); + maxX = Math.max(maxX, roomRight); + }); + + const totalSpan = maxX - minX; + const overhang = Math.max(0, totalSpan - currentDim.widthPx); + + if (overhang > GRID_UNIT_WIDTH_PX / 2) { // Allow some small overhang (half grid unit) + console.error(`❌ VALIDATION ERROR: Room "${currentRoom}" (${currentDim.gridWidth}×${currentDim.gridHeight} GU, ${currentDim.widthPx}px wide) has ${doorCount} ${direction} connections, but they don't fit!`); + console.error(` Connected rooms total span: ${totalSpan.toFixed(0)}px, overhang: ${overhang.toFixed(0)}px`); + console.error(` Recommendation: Reduce number of connections to ${Math.floor(doorCount * currentDim.widthPx / totalSpan)} or use a wider room (${Math.ceil(totalSpan / GRID_UNIT_WIDTH_PX)}+ GU)`); + connectedRooms.forEach((roomId, index) => { + const dim = dimensions[roomId]; + console.error(` - ${roomId}: ${dim.gridWidth}×${dim.gridHeight} GU (${dim.widthPx}px wide)`); + }); + return false; + } + } else if (direction === 'east' || direction === 'west') { + // Check if rooms can fit stacked vertically when centered on door positions + const topY = TILE_SIZE * 2; + const bottomY = currentDim.heightPx - (TILE_SIZE * 3); + const doorSpacing = (bottomY - topY) / (connectedRooms.length - 1); + + // Calculate total span of rooms when centered on doors + let minY = Infinity; + let maxY = -Infinity; + + connectedRooms.forEach((roomId, index) => { + const connectedDim = dimensions[roomId]; + const doorY = topY + (doorSpacing * index); + // Door is positioned at 2 tiles from top of room + const roomTop = doorY - (TILE_SIZE * 2); + const roomBottom = roomTop + connectedDim.heightPx; + + minY = Math.min(minY, roomTop); + maxY = Math.max(maxY, roomBottom); + }); + + const totalSpan = maxY - minY; + const overhang = Math.max(0, totalSpan - currentDim.heightPx); + + if (overhang > GRID_UNIT_HEIGHT_PX / 2) { // Allow some small overhang (half grid unit) + console.error(`❌ VALIDATION ERROR: Room "${currentRoom}" (${currentDim.gridWidth}×${currentDim.gridHeight} GU, ${currentDim.heightPx}px tall) has ${connectedRooms.length} ${direction} connections, but they don't fit!`); + console.error(` Connected rooms total span: ${totalSpan.toFixed(0)}px, overhang: ${overhang.toFixed(0)}px`); + console.error(` Recommendation: Reduce number of connections or use a taller room`); + connectedRooms.forEach((roomId, index) => { + const dim = dimensions[roomId]; + console.error(` - ${roomId}: ${dim.gridWidth}×${dim.gridHeight} GU (${dim.heightPx}px tall)`); + }); + return false; + } + } + + return true; +} + /** * Position multiple rooms to the north of current room */ @@ -976,26 +1054,30 @@ function positionNorthMultiple(currentRoom, connectedRooms, currentPos, dimensio const currentDim = dimensions[currentRoom]; const positions = {}; - // Calculate total width of all connected rooms - const totalWidth = connectedRooms.reduce((sum, roomId) => { - return sum + dimensions[roomId].widthPx; - }, 0); + // CRITICAL: Position rooms based on where doors will be, not just centering widths + // This ensures doors align properly between different-sized rooms - // Determine starting X position (center the group) - const startX = currentPos.x + (currentDim.widthPx - totalWidth) / 2; + // Calculate where doors will be placed on current room's north wall + // (uses same logic as placeNorthDoorsMultiple in doors.js) + const edgeInset = TILE_SIZE * 1.5; // 48px + const availableWidth = currentDim.widthPx - (edgeInset * 2); + const doorCount = connectedRooms.length; + const doorSpacing = availableWidth / (doorCount - 1); - // Position each room left to right - let currentX = startX; - connectedRooms.forEach(roomId => { + connectedRooms.forEach((roomId, index) => { const connectedDim = dimensions[roomId]; + // Calculate where the door will be on current room's north wall + const doorX = currentPos.x + edgeInset + (doorSpacing * index); + + // Center the connected room on this door position + const x = doorX - (connectedDim.widthPx / 2); + // Y position is based on stacking height const y = currentPos.y - connectedDim.stackingHeightPx; // Align to grid - positions[roomId] = alignToGrid(currentX, y); - - currentX += connectedDim.widthPx; + positions[roomId] = alignToGrid(x, y); }); return positions; @@ -1023,26 +1105,30 @@ function positionSouthMultiple(currentRoom, connectedRooms, currentPos, dimensio const currentDim = dimensions[currentRoom]; const positions = {}; - // Calculate total width - const totalWidth = connectedRooms.reduce((sum, roomId) => { - return sum + dimensions[roomId].widthPx; - }, 0); + // CRITICAL: Position rooms based on where doors will be, not just centering widths + // This ensures doors align properly between different-sized rooms - // Determine starting X position (center the group) - const startX = currentPos.x + (currentDim.widthPx - totalWidth) / 2; + // Calculate where doors will be placed on current room's south wall + // (uses same logic as placeSouthDoorsMultiple in doors.js) + const edgeInset = TILE_SIZE * 1.5; // 48px + const availableWidth = currentDim.widthPx - (edgeInset * 2); + const doorCount = connectedRooms.length; + const doorSpacing = availableWidth / (doorCount - 1); - // Position each room left to right - let currentX = startX; - connectedRooms.forEach(roomId => { + connectedRooms.forEach((roomId, index) => { const connectedDim = dimensions[roomId]; + // Calculate where the door will be on current room's south wall + const doorX = currentPos.x + edgeInset + (doorSpacing * index); + + // Center the connected room on this door position + const x = doorX - (connectedDim.widthPx / 2); + // Y position below current room const y = currentPos.y + currentDim.stackingHeightPx; // Align to grid - positions[roomId] = alignToGrid(currentX, y); - - currentX += connectedDim.widthPx; + positions[roomId] = alignToGrid(x, y); }); return positions; @@ -1070,18 +1156,29 @@ function positionEastMultiple(currentRoom, connectedRooms, currentPos, dimension const currentDim = dimensions[currentRoom]; const positions = {}; + // CRITICAL: Position rooms based on where doors will be, not just stacking vertically + // This ensures doors align properly between different-sized rooms + // Position to the right const x = currentPos.x + currentDim.widthPx; - // Stack vertically starting at current Y - let currentY = currentPos.y; - connectedRooms.forEach(roomId => { + // Calculate where doors will be placed on current room's east wall + // (uses same logic as placeEastDoorsMultiple in doors.js) + const topY = currentPos.y + (TILE_SIZE * 2); + const bottomY = currentPos.y + currentDim.heightPx - (TILE_SIZE * 3); + const doorSpacing = (bottomY - topY) / (connectedRooms.length - 1); + + connectedRooms.forEach((roomId, index) => { const connectedDim = dimensions[roomId]; - // Align to grid - positions[roomId] = alignToGrid(x, currentY); + // Calculate where the door will be on current room's east wall + const doorY = topY + (doorSpacing * index); - currentY += connectedDim.stackingHeightPx; + // Center the connected room on this door position vertically + const y = doorY - (TILE_SIZE * 2); // Align with door at 2 tiles from top + + // Align to grid + positions[roomId] = alignToGrid(x, y); }); return positions; @@ -1105,20 +1202,32 @@ function positionWestSingle(currentRoom, connectedRoom, currentPos, dimensions) * Position multiple rooms to the west of current room */ function positionWestMultiple(currentRoom, connectedRooms, currentPos, dimensions) { + const currentDim = dimensions[currentRoom]; const positions = {}; - // Stack vertically starting at current Y - let currentY = currentPos.y; - connectedRooms.forEach(roomId => { + // CRITICAL: Position rooms based on where doors will be, not just stacking vertically + // This ensures doors align properly between different-sized rooms + + // Calculate where doors will be placed on current room's west wall + // (uses same logic as placeWestDoorsMultiple in doors.js) + const topY = currentPos.y + (TILE_SIZE * 2); + const bottomY = currentPos.y + currentDim.heightPx - (TILE_SIZE * 3); + const doorSpacing = (bottomY - topY) / (connectedRooms.length - 1); + + connectedRooms.forEach((roomId, index) => { const connectedDim = dimensions[roomId]; // Position to the left const x = currentPos.x - connectedDim.widthPx; - // Align to grid - positions[roomId] = alignToGrid(x, currentY); + // Calculate where the door will be on current room's west wall + const doorY = topY + (doorSpacing * index); - currentY += connectedDim.stackingHeightPx; + // Center the connected room on this door position vertically + const y = doorY - (TILE_SIZE * 2); // Align with door at 2 tiles from top + + // Align to grid + positions[roomId] = alignToGrid(x, y); }); return positions; @@ -1223,7 +1332,15 @@ export function calculateRoomPositions(gameInstance) { const gridCoords = worldToGrid(position.x, position.y); console.log(` ${roomId}: positioned at world(${position.x}, ${position.y}) = grid(${gridCoords.gridX}, ${gridCoords.gridY})`); } else { - // Multiple room connections + // Multiple room connections - validate first + const currentDim = dimensions[currentRoomId]; + const isValid = validateMultipleConnections(direction, currentRoomId, unprocessed, currentDim, dimensions); + + if (!isValid) { + console.warn(`⚠️ Skipping invalid connections for ${currentRoomId} ${direction}. Layout may be broken.`); + // Still process them to avoid breaking the game, but with a warning + } + const newPositions = positionMultipleRooms(direction, currentRoomId, unprocessed, currentPos, dimensions); unprocessed.forEach(roomId => { diff --git a/js/systems/collision.js b/js/systems/collision.js index acb45c6..8a734ba 100644 --- a/js/systems/collision.js +++ b/js/systems/collision.js @@ -369,8 +369,10 @@ export function removeWallTilesForDoorInRoom(roomId, fromRoomId, direction, door doorWidth = TILE_SIZE * 2; doorHeight = TILE_SIZE; } else if (direction === 'east' || direction === 'west') { - // For east/west connections, calculate Y position based on room configuration - doorY = roomPosition.y + roomHeight / 2; // Center of room + // For east/west connections: single tile per room, positioned 3 tiles down from top + // Doors are 3 tiles down from top (below top 2 wall tiles) + doorY = roomPosition.y + (TILE_SIZE * 2); // 2 tiles down from top (at tile 3) + if (direction === 'east') { // Original door is east, so new door should be west doorX = roomPosition.x + TILE_SIZE; @@ -378,8 +380,9 @@ export function removeWallTilesForDoorInRoom(roomId, fromRoomId, direction, door // Original door is west, so new door should be east doorX = roomPosition.x + roomWidth - TILE_SIZE; } + // Single tile per room for east/west doors doorWidth = TILE_SIZE; - doorHeight = TILE_SIZE * 2; + doorHeight = TILE_SIZE; } else { console.log(`Unknown direction: ${direction}`); return; diff --git a/js/systems/doors.js b/js/systems/doors.js index 5d7a7ab..54e27e9 100644 --- a/js/systems/doors.js +++ b/js/systems/doors.js @@ -402,27 +402,46 @@ export function createDoorSpritesForRoom(roomId, position) { doorPositions.forEach(doorInfo => { const { x: doorX, y: doorY, direction, connectedRoom } = doorInfo; - // Set door size based on direction - let doorWidth = TILE_SIZE; - let doorHeight = TILE_SIZE * 2; + // Set door size and texture based on direction + let doorWidth, doorHeight, doorTexture, flipX; - if (direction === 'east' || direction === 'west') { - doorWidth = TILE_SIZE * 2; + if (direction === 'north' || direction === 'south') { + // North/South doors: 1 tile wide, 2 tiles tall + doorWidth = TILE_SIZE; + doorHeight = TILE_SIZE * 2; + doorTexture = 'door_32'; + flipX = false; + } else { + // East/West doors: 1 tile wide, 1 tile tall (single tile per room) + doorWidth = TILE_SIZE; doorHeight = TILE_SIZE; + doorTexture = 'door_side_sheet_32'; + // West-facing doors (left room) should be flipped horizontally + flipX = (direction === 'west'); } console.log(`Creating door sprite at (${doorX}, ${doorY}) for ${roomId} -> ${connectedRoom} (${direction})`); - // Create a colored rectangle as a fallback if door texture fails + // Create door sprite with appropriate texture let doorSprite; try { - doorSprite = gameRef.add.sprite(doorX, doorY, 'door_32'); + doorSprite = gameRef.add.sprite(doorX, doorY, doorTexture); + // Set the initial frame (frame 0 = closed) + doorSprite.setFrame(0); + // Apply horizontal flip for west-facing doors + if (flipX) { + doorSprite.setFlipX(true); + } } catch (error) { - console.warn(`Failed to create door sprite with 'door_32' texture, creating colored rectangle instead:`, error); + console.warn(`Failed to create door sprite with '${doorTexture}' texture, creating colored rectangle instead:`, error); // Create a colored rectangle as fallback const graphics = gameRef.add.graphics(); graphics.fillStyle(0xff0000, 1); // Red color - graphics.fillRect(-TILE_SIZE/2, -TILE_SIZE, TILE_SIZE, TILE_SIZE * 2); + if (direction === 'north' || direction === 'south') { + graphics.fillRect(-TILE_SIZE/2, -TILE_SIZE, TILE_SIZE, TILE_SIZE * 2); + } else { + graphics.fillRect(-TILE_SIZE/2, -TILE_SIZE/2, TILE_SIZE, TILE_SIZE); + } graphics.setPosition(doorX, doorY); doorSprite = graphics; } @@ -740,7 +759,8 @@ function createAnimatedDoorOnOppositeSide(roomId, fromRoomId, direction, doorWor doorWidth = TILE_SIZE * 2; doorHeight = TILE_SIZE; } else if (direction === 'east' || direction === 'west') { - doorWidth = TILE_SIZE * 2; + // Single tile per room for east/west doors + doorWidth = TILE_SIZE; doorHeight = TILE_SIZE; } else { console.log(`Unknown direction: ${direction}`);