'use strict'; // null = FLOOR (default), explicit values for special tiles // Outer ring = WALL perimeter const NORMAL_TEMPLATES = [ // Empty room [ [1, 1, 1, 1, 1], [1, null, null, null, 1], [1, null, null, null, 1], [1, null, null, null, 1], [1, 1, 1, 1, 1], ], // With rock [ [1, 1, 1, 1, 1], [1, null, null, null, 1], [1, null, null, null, 1], [1, null, null, 11, 1], [1, 1, 1, 1, 1], ], // With softwall [ [1, 1, 1, 1, 1], [1, null, null, null, 1], [1, null, 2, null, 1], [1, null, null, null, 1], [1, 1, 1, 1, 1], ], // Rock + torch [ [1, 1, 1, 1, 1], [1, 5, null, null, 1], [1, null, null, null, 1], [1, null, null, 11, 1], [1, 1, 1, 1, 1], ], ]; const ENTRANCE_TEMPLATES = [ // EXIT at bottom [ [1, 1, 1, 1, 1], [1, null, null, null, 1], [1, null, 6, null, 1], [1, null, null, null, 1], [1, 1, 14, 1, 1], ], // EXIT at top [ [1, 1, 14, 1, 1], [1, null, null, null, 1], [1, null, 6, null, 1], [1, null, null, null, 1], [1, 1, 1, 1, 1], ], // EXIT at left [ [1, 1, 1, 1, 1], [14, null, null, null, 1], [1, null, null, 6, 1], [1, null, null, null, 1], [1, 1, 1, 1, 1], ], // EXIT at right [ [1, 1, 1, 1, 1], [1, null, null, null, 14], [1, null, 6, null, 1], [1, null, null, null, 1], [1, 1, 1, 1, 1], ], ]; const BOSS_TEMPLATES = [ // Open arena, corner torches, EXIT bottom center [ [1, 1, 1, 1, 1, 1, 1], [1, 5, null, null, null, 5, 1], [1, null, null, null, null, null, 1], [1, null, null, null, null, null, 1], [1, null, null, null, null, null, 1], [1, 5, null, null, null, 5, 1], [1, 1, 1, 14, 1, 1, 1], ], // Arena with center softwall [ [1, 1, 1, 1, 1, 1, 1], [1, 5, null, null, null, 5, 1], [1, null, null, null, null, null, 1], [1, null, null, 2, null, null, 1], [1, null, null, null, null, null, 1], [1, 5, null, null, null, 5, 1], [1, 1, 1, 14, 1, 1, 1], ], // Arena with two softwalls [ [1, 1, 1, 1, 1, 1, 1], [1, 5, null, null, null, 5, 1], [1, null, null, 2, null, null, 1], [1, null, null, null, null, null, 1], [1, null, null, 2, null, null, 1], [1, 5, null, null, null, 5, 1], [1, 1, 1, 14, 1, 1, 1], ], ]; const TREASURE_TEMPLATES = [ // Chest in center, clean [ [1, 1, 1, 1, 1], [1, null, null, null, 1], [1, null, 12, null, 1], [1, null, null, null, 1], [1, 1, 1, 1, 1], ], // Chest with traps [ [1, 1, 1, 1, 1], [1, 9, null, 9, 1], [1, null, 12, null, 1], [1, null, null, null, 1], [1, 1, 1, 1, 1], ], // Chest with traps + rock [ [1, 1, 1, 1, 1], [1, 9, null, 9, 1], [1, null, 12, null, 1], [1, null, 11, null, 1], [1, 1, 1, 1, 1], ], // Chest surrounded by traps [ [1, 1, 1, 1, 1], [1, 9, 9, 9, 1], [1, 9, 12, 9, 1], [1, 9, 9, 9, 1], [1, 1, 1, 1, 1], ], ]; const PUZZLE_TEMPLATES = [ // Lever + target [ [1, 1, 1, 1, 1, 1], [1, null, null, 7, null, 1], [1, null, null, null, null, 1], [1, null, null, null, null, 1], [1, null, 10, null, null, 1], [1, 1, 1, 1, 1, 1], ], // Rock + target [ [1, 1, 1, 1, 1, 1], [1, null, null, null, 11, 1], [1, null, null, null, null, 1], [1, null, null, null, null, 1], [1, null, 10, null, null, 1], [1, 1, 1, 1, 1, 1], ], // Switch + target [ [1, 1, 1, 1, 1, 1], [1, null, null, 8, null, 1], [1, null, null, null, null, 1], [1, null, null, null, null, 1], [1, null, 10, null, null, 1], [1, 1, 1, 1, 1, 1], ], // Two levers + target [ [1, 1, 1, 1, 1, 1], [1, null, 7, null, null, 1], [1, null, null, null, null, 1], [1, null, null, null, null, 1], [1, null, 10, null, 7, 1], [1, 1, 1, 1, 1, 1], ], ]; const TRAP_TEMPLATES = [ // 2 traps [ [1, 1, 1, 1, 1], [1, null, 9, null, 1], [1, null, null, null, 1], [1, null, null, 9, 1], [1, 1, 1, 1, 1], ], // 3 traps [ [1, 1, 1, 1, 1], [1, 9, null, null, 1], [1, null, 9, null, 1], [1, null, null, 9, 1], [1, 1, 1, 1, 1], ], // 4 traps [ [1, 1, 1, 1, 1], [1, 9, null, null, 1], [1, null, null, null, 1], [1, null, null, 9, 1], [1, 1, 9, 9, 1], ], // Lava traps [ [1, 1, 1, 1, 1], [1, null, 3, null, 1], [1, null, null, null, 1], [1, null, 3, null, 1], [1, 1, 1, 1, 1], ], ]; function getRoomLayoutTemplate(roomType, rng) { let templates; switch (roomType) { case 'entrance': templates = ENTRANCE_TEMPLATES; break; case 'boss': templates = BOSS_TEMPLATES; break; case 'treasure': templates = TREASURE_TEMPLATES; break; case 'puzzle': templates = PUZZLE_TEMPLATES; break; case 'trap': templates = TRAP_TEMPLATES; break; default: templates = NORMAL_TEMPLATES; break; } const idx = Math.floor(rng() * templates.length); return templates[idx].map(row => [...row]); } function generateRoomLayout(room, theme, rng) { const template = getRoomLayoutTemplate(room.type, rng); const tW = template[0].length; const tH = template.length; const rW = room.width; const rH = room.height; // Initialize room grid: walls on perimeter, floor inside const grid = []; for (let y = 0; y < rH; y++) { grid[y] = []; for (let x = 0; x < rW; x++) { if (y === 0 || y === rH - 1 || x === 0 || x === rW - 1) { grid[y][x] = TILE_TYPES.WALL; } else { grid[y][x] = TILE_TYPES.FLOOR; } } } // Map template tiles to room grid for (let ty = 0; ty < tH; ty++) { for (let tx = 0; tx < tW; tx++) { const tile = template[ty][tx]; if (tile === null) continue; let rx, ry; // Interior tile if (ty > 0 && ty < tH - 1 && tx > 0 && tx < tW - 1) { rx = Math.round((tx - 1) * (rW - 2) / (tW - 2)) + 1; ry = Math.round((ty - 1) * (rH - 2) / (tH - 2)) + 1; } // Top edge else if (ty === 0 && tx > 0 && tx < tW - 1) { rx = Math.round((tx / (tW - 1)) * (rW - 1)); ry = 0; } // Bottom edge else if (ty === tH - 1 && tx > 0 && tx < tW - 1) { rx = Math.round((tx / (tW - 1)) * (rW - 1)); ry = rH - 1; } // Left edge else if (tx === 0 && ty > 0 && ty < tH - 1) { rx = 0; ry = Math.round((ty / (tH - 1)) * (rH - 1)); } // Right edge else if (tx === tW - 1 && ty > 0 && ty < tH - 1) { rx = rW - 1; ry = Math.round((ty / (tH - 1)) * (rH - 1)); } // Corners (wall overlaps, skip unless special) else { continue; } if (rx >= 0 && rx < rW && ry >= 0 && ry < rH) { grid[ry][rx] = tile; } } } // Add corner torches based on RNG if (rng() < 0.6) { grid[1][1] = TILE_TYPES.TORCH; } if (rng() < 0.4) { grid[1][rW - 2] = TILE_TYPES.TORCH; } if (rng() < 0.4) { grid[rH - 2][1] = TILE_TYPES.TORCH; } if (rng() < 0.6) { grid[rH - 2][rW - 2] = TILE_TYPES.TORCH; } // Add theme-appropriate floor decorations if (theme && theme.floorHazards) { for (const hazard of theme.floorHazards) { if (rng() < hazard.chance) { const hx = 1 + Math.floor(rng() * (rW - 2)); const hy = 1 + Math.floor(rng() * (rH - 2)); if (grid[hy][hx] === TILE_TYPES.FLOOR) { grid[hy][hx] = hazard.tileType; } } } } room.tiles = grid; applyThemeToRoom(room, theme); return grid; } function applyThemeToRoom(room, theme) { if (!theme) return; room.theme = theme; room.floorColor = theme.floorColor; room.floorDarkColor = theme.floorDarkColor; room.wallColor = theme.wallColor; room.wallTopColor = theme.wallTopColor; room.wallFrontColor = theme.wallFrontColor; room.wallBottomColor = theme.wallBottomColor; room.wallBorderColor = theme.wallBorderColor; for (const deco of room.decorations) { switch (deco.type) { case 'torch': deco.glowColor = theme.torchGlow; deco.stickColor = theme.torchStickColor; deco.flameColor = theme.torchFlameColor; deco.coreColor = theme.torchCoreColor; break; case 'rock': deco.color = theme.rockColor || '#888888'; deco.darkColor = theme.rockDarkColor || '#666666'; deco.lightColor = theme.rockLightColor || '#AAAAAA'; break; case 'chest': deco.closedColor = theme.chestClosedColor || '#8B4513'; deco.goldColor = theme.chestGoldColor || '#DAA520'; deco.openColor = theme.chestOpenColor || '#A0522D'; break; case 'lever': deco.color = theme.leverColor || '#B8860B'; break; case 'switch': deco.color = theme.switchFloorColor || '#DAA520'; break; case 'target': deco.color = theme.targetColor || '#FFD700'; deco.glowColor = theme.targetGlow || 'rgba(255,215,0,0.3)'; break; case 'trap': deco.color = theme.trapColor || '#8B008B'; break; case 'lava': deco.color = theme.lavaColor || '#FF4500'; deco.lightColor = theme.lavaLightColor || '#FF6347'; break; case 'water': deco.color = theme.waterColor || '#4169AA'; deco.lightColor = theme.waterLightColor || '#5F9EA0'; break; } } } if (typeof module !== 'undefined' && module.exports) { module.exports = { generateRoomLayout, getRoomLayoutTemplate, applyThemeToRoom }; }