From 72c4251fc4fda0ae36232db73668a96cdac11c16 Mon Sep 17 00:00:00 2001 From: Kyle Banicevic Date: Sat, 7 Feb 2026 14:30:53 +1100 Subject: [PATCH] Added more side rooms, 8-10 per level --- src/engine/world/generator.ts | 108 +++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 33 deletions(-) diff --git a/src/engine/world/generator.ts b/src/engine/world/generator.ts index 6d608fd..ecdcc2a 100644 --- a/src/engine/world/generator.ts +++ b/src/engine/world/generator.ts @@ -236,50 +236,89 @@ function generateTrackLevel(width: number, height: number, tiles: Tile[], _floor } // 4. Branch Side Rooms off the main path - const numSideRooms = 25 + Math.floor(random() * 15); - for (let i = 0; i < numSideRooms; i++) { + const targetSideRooms = 10; + let attempts = 0; + const maxAttempts = 300; + + while (rooms.length < targetSideRooms + anchors.length && attempts < maxAttempts) { + attempts++; const sourcePathIdx = Math.floor(random() * trackPath.length); const source = trackPath[sourcePathIdx]; - const rw = 5 + Math.floor(random() * 7); - const rh = 4 + Math.floor(random() * 7); + const rw = 5 + Math.floor(random() * 5); // Slightly smaller rooms to fit better + const rh = 4 + Math.floor(random() * 5); - const side = random() < 0.5 ? -1 : 1; - let rx, ry; + // Try multiple offsets to find a gap + const distances = [5, 6, 7, 8]; + const sides = [-1, 1]; + let placed = false; - if (random() < 0.5) { - rx = source.x + (side * Math.floor(rw / 2 + 3)); - ry = source.y - Math.floor(rh / 2); - } else { - rx = source.x - Math.floor(rw / 2); - ry = source.y + (side * Math.floor(rh / 2 + 3)); - } + for (const dist of distances) { + for (const side of sides) { + let rx, ry; + if (random() < 0.5) { // Try horizontal offset + rx = source.x + (side * dist); + ry = source.y - Math.floor(rh / 2); + } else { // Try vertical offset + rx = source.x - Math.floor(rw / 2); + ry = source.y + (side * dist); + } - rx = Math.max(1, Math.min(width - rw - 1, rx)); - ry = Math.max(1, Math.min(height - rh - 1, ry)); + rx = Math.max(1, Math.min(width - rw - 1, rx)); + ry = Math.max(1, Math.min(height - rh - 1, ry)); - const room = { x: rx, y: ry, width: rw, height: rh }; - const overlap = rooms.some(r => !(room.x + room.width < r.x - 1 || room.x > r.x + r.width + 1 || room.y + room.height < r.y - 1 || room.y > r.y + r.height + 1)); - if (overlap) continue; + const room = { x: rx, y: ry, width: rw, height: rh }; - for (let y = ry + 1; y < ry + rh - 1; y++) { - for (let x = rx + 1; x < rx + rw - 1; x++) { - tiles[y * width + x] = TileType.EMPTY; + // 1. Check overlap with existing rooms (strict padding) + const overlapRooms = rooms.some(r => !(room.x + room.width < r.x - 1 || room.x > r.x + r.width + 1 || room.y + room.height < r.y - 1 || room.y > r.y + r.height + 1)); + if (overlapRooms) continue; + + // 2. Check overlap with existing core structures (EMPTY tiles) + let overlapEmpty = false; + for (let y = ry - 1; y < ry + rh + 1; y++) { + for (let x = rx - 1; x < rx + rw + 1; x++) { + if (tiles[y * width + x] === TileType.EMPTY) { + overlapEmpty = true; + break; + } + } + if (overlapEmpty) break; + } + if (overlapEmpty) continue; + + // Valid spot found! + for (let y = ry + 1; y < ry + rh - 1; y++) { + for (let x = rx + 1; x < rx + rw - 1; x++) { + tiles[y * width + x] = TileType.EMPTY; + } + } + + digCorridor(width, tiles, source.x, source.y, rx + Math.floor(rw / 2), ry + Math.floor(rh / 2)); + + // Place door at room boundary + let ex = rx + Math.floor(rw / 2); + let ey = ry + (source.y <= ry ? 0 : rh - 1); + if (source.x < rx) { + ex = rx; ey = ry + Math.floor(rh / 2); + } else if (source.x >= rx + rw) { + ex = rx + rw - 1; ey = ry + Math.floor(rh / 2); + } else if (source.y < ry) { + ex = rx + Math.floor(rw / 2); ey = ry; + } else if (source.y >= ry + rh) { + ex = rx + Math.floor(rw / 2); ey = ry + rh - 1; + } + + tiles[ey * width + ex] = TileType.DOOR_CLOSED; + rooms.push(room); + placed = true; + break; } + if (placed) break; } - - digCorridor(width, tiles, source.x, source.y, rx + Math.floor(rw / 2), ry + Math.floor(rh / 2)); - - let ex = rx + Math.floor(rw / 2); - let ey = ry + (source.y <= ry ? 0 : rh - 1); - if (Math.abs(source.x - (rx + rw / 2)) > Math.abs(source.y - (ry + rh / 2))) { - ex = (source.x <= rx ? 0 : rw - 1) + rx; - ey = ry + Math.floor(rh / 2); - } - tiles[ey * width + ex] = TileType.DOOR_CLOSED; - rooms.push(room); } + console.log(`[generator] Final side rooms placed: ${rooms.length - anchors.length} after ${attempts} attempts.`); + // Place visual exit at track end const lastNode = trackPath[trackPath.length - 1]; tiles[lastNode.y * width + lastNode.x] = TileType.EXIT; @@ -298,7 +337,10 @@ function digCorridor(width: number, tiles: Tile[], x1: number, y1: number, x2: n } else if (currY !== y2) { currY += y2 > currY ? 1 : -1; } - tiles[currY * width + currX] = TileType.EMPTY; + // Only dig if it's currently a wall + if (tiles[currY * width + currX] === TileType.WALL) { + tiles[currY * width + currX] = TileType.EMPTY; + } } }