mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-21 11:18:08 +00:00
fix: Resolve room layout issues for mixed sizes and east/west doors
This commit addresses three critical issues identified during testing: 1. **Fixed mixed room size door alignment (Issue #1)** - Updated room positioning logic in positionNorthMultiple, positionSouthMultiple, positionEastMultiple, and positionWestMultiple functions - Rooms are now positioned centered on their corresponding door positions - Ensures doors align properly between different-sized rooms (e.g., 1×1 GU closet connecting to 2×1 GU hallway) - Uses same door spacing logic as door placement (edgeInset + doorSpacing) 2. **Implemented East/West single-tile doors (Issue #2)** - Changed door_side_sheet_32 from image to spritesheet (6 frames, 32×32px each) - Updated door sprite creation to use single tile per room for east/west doors - West-facing doors (left room) now properly flip horizontally - Doors positioned 3 tiles down from top (TILE_SIZE * 2) as specified - Updated wall tile removal and animated door creation to use correct dimensions - Both sides of east/west connections now use matching door system 3. **Added connection limit validation (Issue #3)** - Created validateMultipleConnections function to check if connections fit - Validates that multiple connections won't cause rooms to overhang excessively - Logs detailed error messages with recommendations when connections don't fit - Example: 2GU room can't have 3 north connections (not enough width) - Helps developers identify invalid room layouts during scenario design Files modified: - js/core/game.js: Load door_side_sheet_32 as spritesheet - js/core/rooms.js: Fix positioning, add validation - js/systems/doors.js: Single-tile east/west doors with flipping - js/systems/collision.js: Update wall tile removal for single-tile doors
This commit is contained in:
@@ -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');
|
||||
|
||||
191
js/core/rooms.js
191
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 => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
Reference in New Issue
Block a user