From b35cf5a9642d6ac72aecddd3a840f68955247bcb Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Mon, 5 Jan 2026 21:48:19 +1100 Subject: [PATCH] Add openable doors to generated rooms --- src/core/terrain.ts | 8 ++++-- src/engine/world/generator.ts | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/core/terrain.ts b/src/core/terrain.ts index dc9d408..152b53b 100644 --- a/src/core/terrain.ts +++ b/src/core/terrain.ts @@ -6,7 +6,9 @@ export const TileType = { EMPTY_DECO: 24, WALL_DECO: 12, EXIT: 8, - WATER: 63 // Unused but kept for safety/legacy + WATER: 63, // Unused but kept for safety/legacy + DOOR_CLOSED: 5, + DOOR_OPEN: 6 } as const; export type TileType = typeof TileType[keyof typeof TileType]; @@ -28,7 +30,9 @@ export const TILE_DEFINITIONS: Record = { [TileType.EMPTY_DECO]: { id: TileType.EMPTY_DECO, isBlocking: false, isDestructible: false }, [TileType.WALL_DECO]: { id: TileType.WALL_DECO, isBlocking: true, isDestructible: false }, [TileType.EXIT]: { id: TileType.EXIT, isBlocking: false, isDestructible: false }, - [TileType.WATER]: { id: TileType.WATER, isBlocking: true, isDestructible: false } + [TileType.WATER]: { id: TileType.WATER, isBlocking: true, isDestructible: false }, + [TileType.DOOR_CLOSED]: { id: TileType.DOOR_CLOSED, isBlocking: false, isDestructible: true, isDestructibleByWalk: true, blocksVision: true, destructsTo: TileType.DOOR_OPEN }, + [TileType.DOOR_OPEN]: { id: TileType.DOOR_OPEN, isBlocking: false, isDestructible: false } }; export function isBlocking(tile: number): boolean { diff --git a/src/engine/world/generator.ts b/src/engine/world/generator.ts index 72a666d..1d42031 100644 --- a/src/engine/world/generator.ts +++ b/src/engine/world/generator.ts @@ -57,8 +57,19 @@ export function generateWorld(floor: number, runState: RunState): { world: World }; placeEnemies(floor, rooms, actors, random); + + // Place doors for dungeon levels (Uniform/Digger) + // Caves (Floors 10+) shouldn't have manufactured doors + if (floor <= 9) { + placeDoors(width, height, tiles, rooms, random); + } + decorate(width, height, tiles, random, exit); + // CRITICAL FIX: Ensure player start position is always clear! + // Otherwise spawning in Grass (which blocks vision) makes the player blind. + tiles[playerY * width + playerX] = TileType.EMPTY; + return { world: { width, height, tiles, actors, exit }, playerId @@ -347,3 +358,40 @@ function placeEnemies(floor: number, rooms: Room[], actors: Map export const makeTestWorld = generateWorld; + +function placeDoors(width: number, height: number, tiles: Tile[], rooms: Room[], random: () => number): void { + const checkAndPlaceDoor = (x: number, y: number) => { + const i = idx({ width, height } as any, x, y); + if (tiles[i] === TileType.EMPTY) { + // Found a connection (floor tile on perimeter) + + // 50% chance to place a door + if (random() < 0.5) { + // 90% chance for closed door, 10% for open + tiles[i] = random() < 0.9 ? TileType.DOOR_CLOSED : TileType.DOOR_OPEN; + } + } + }; + + for (const room of rooms) { + // Scan top and bottom walls + const topY = room.y - 1; + const bottomY = room.y + room.height; + + // Scan horizontal perimeters (iterate x from left-1 to right+1 to cover corners too if needed, + // but usually doors are in the middle segments. Let's cover the full range adjacent to room.) + for (let x = room.x; x < room.x + room.width; x++) { + if (topY >= 0) checkAndPlaceDoor(x, topY); + if (bottomY < height) checkAndPlaceDoor(x, bottomY); + } + + // Scan left and right walls + const leftX = room.x - 1; + const rightX = room.x + room.width; + + for (let y = room.y; y < room.y + room.height; y++) { + if (leftX >= 0) checkAndPlaceDoor(leftX, y); + if (rightX < width) checkAndPlaceDoor(rightX, y); + } + } +}