// ─── Room Generation Functions ──────────────────────────────────────────────── // Extracted from store.ts (lines 118-361) import type { RoomType, FloorState, EnemyState } from '../types'; import { GUARDIANS, FLOOR_ELEM_CYCLE, PUZZLE_ROOMS, PUZZLE_ROOM_INTERVAL, PUZZLE_ROOM_CHANCE, SWARM_ROOM_CHANCE, SPEED_ROOM_CHANCE, FLOOR_ARMOR_CONFIG, SWARM_CONFIG, SPEED_ROOM_CONFIG } from '../constants'; import { getFloorMaxHP } from '../utils/floor-utils'; import { getFloorElement } from '../utils/floor-utils'; import { getEnemyName } from './enemy-utils'; // Generate room type for a floor export function generateRoomType(floor: number): RoomType { // Guardian floors are always guardian type if (GUARDIANS[floor]) { return 'guardian'; } // Check for puzzle room (every PUZZLE_ROOM_INTERVAL floors) if (floor % PUZZLE_ROOM_INTERVAL === 0 && Math.random() < PUZZLE_ROOM_CHANCE) { return 'puzzle'; } // Check for swarm room if (Math.random() < SWARM_ROOM_CHANCE) { return 'swarm'; } // Check for speed room if (Math.random() < SPEED_ROOM_CHANCE) { return 'speed'; } // Default to combat return 'combat'; } // Get armor for a non-guardian floor export function getFloorArmor(floor: number): number { if (GUARDIANS[floor]) { return GUARDIANS[floor].armor || 0; } // Armor becomes more common on higher floors if (floor < 10) return 0; const armorChance = Math.min(FLOOR_ARMOR_CONFIG.maxArmorChance, FLOOR_ARMOR_CONFIG.baseChance + (floor - 10) * FLOOR_ARMOR_CONFIG.chancePerFloor); if (Math.random() > armorChance) return 0; // Scale armor with floor const armorRange = FLOOR_ARMOR_CONFIG.maxArmor - FLOOR_ARMOR_CONFIG.minArmor; const floorProgress = Math.min(1, (floor - 10) / 90); return FLOOR_ARMOR_CONFIG.minArmor + armorRange * floorProgress * Math.random(); } // Get dodge chance for a speed room export function getDodgeChance(floor: number): number { return Math.min( SPEED_ROOM_CONFIG.maxDodge, SPEED_ROOM_CONFIG.baseDodgeChance + floor * SPEED_ROOM_CONFIG.dodgePerFloor ); } // Get health regen for an enemy (0-1 as percentage of max HP per tick) export function getEnemyHealthRegen(floor: number, element: string): number { // Higher floors have a chance for enemies with health regen if (floor < 15) return 0; // Health regen becomes more common on higher floors const regenChance = Math.min(0.3, (floor - 15) * 0.005); // Max 30% chance if (Math.random() > regenChance) return 0; // Scale regen with floor (0.5% to 3% of max HP per tick) const floorProgress = Math.min(1, (floor - 15) / 85); return 0.005 + floorProgress * 0.025; } // Get barrier for an enemy (0-1 as percentage of max HP) export function getEnemyBarrier(floor: number, element: string): number { // Barrier appears on higher floors, more common with certain elements if (floor < 20) return 0; // Barrier chance based on element - light/water/earth more likely const barrierElements = ['light', 'water', 'earth']; const baseChance = barrierElements.includes(element) ? 0.15 : 0.08; const floorBonus = Math.min(0.25, (floor - 20) * 0.003); // Max 25% additional chance const barrierChance = Math.min(0.4, baseChance + floorBonus); if (Math.random() > barrierChance) return 0; // Barrier is 10% to 30% of max HP const floorProgress = Math.min(1, (floor - 20) / 80); return 0.1 + floorProgress * 0.2; } // Generate enemies for a swarm room export function generateSwarmEnemies(floor: number): EnemyState[] { const baseHP = getFloorMaxHP(floor); const element = getFloorElement(floor); const numEnemies = SWARM_CONFIG.minEnemies + Math.floor(Math.random() * (SWARM_CONFIG.maxEnemies - SWARM_CONFIG.minEnemies + 1)); const enemies: EnemyState[] = []; for (let i = 0; i < numEnemies; i++) { const enemyName = getEnemyName(element, floor); enemies.push({ id: `enemy_${i}`, name: enemyName, hp: Math.floor(baseHP * SWARM_CONFIG.hpMultiplier), maxHP: Math.floor(baseHP * SWARM_CONFIG.hpMultiplier), armor: SWARM_CONFIG.armorBase + Math.floor(floor / 10) * SWARM_CONFIG.armorPerFloor, dodgeChance: 0, healthRegen: getEnemyHealthRegen(floor, element), barrier: getEnemyBarrier(floor, element), element, }); } return enemies; } // Generate initial floor state export function generateFloorState(floor: number): FloorState { const roomType = generateRoomType(floor); const element = getFloorElement(floor); const baseHP = getFloorMaxHP(floor); const guardian = GUARDIANS[floor]; switch (roomType) { case 'guardian': return { roomType: 'guardian', enemies: [{ id: 'guardian', name: guardian.name, hp: guardian.hp, maxHP: guardian.hp, armor: guardian.armor || 0, dodgeChance: 0, healthRegen: 0.01, // Guardians have 1% HP regen per tick barrier: 0, element: guardian.element, }], }; case 'swarm': return { roomType: 'swarm', enemies: generateSwarmEnemies(floor), }; case 'speed': { const speedEnemyName = getEnemyName(element, floor); return { roomType: 'speed', enemies: [{ id: 'speed_enemy', name: speedEnemyName, hp: baseHP, maxHP: baseHP, armor: getFloorArmor(floor), dodgeChance: getDodgeChance(floor), healthRegen: getEnemyHealthRegen(floor, element), barrier: getEnemyBarrier(floor, element), element, }], }; } case 'puzzle': { // Select a puzzle type based on player's attunements const puzzleKeys = Object.keys(PUZZLE_ROOMS); const selectedPuzzle = puzzleKeys[Math.floor(Math.random() * puzzleKeys.length)]; const puzzle = PUZZLE_ROOMS[selectedPuzzle]; return { roomType: 'puzzle', enemies: [], puzzleProgress: 0, puzzleRequired: 1, puzzleId: selectedPuzzle, puzzleAttunements: puzzle.attunements, }; } default: // combat const combatEnemyName = getEnemyName(element, floor); return { roomType: 'combat', enemies: [{ id: 'enemy', name: combatEnemyName, hp: baseHP, maxHP: baseHP, armor: getFloorArmor(floor), dodgeChance: 0, healthRegen: getEnemyHealthRegen(floor, element), barrier: getEnemyBarrier(floor, element), element, }], }; } } // Get puzzle progress speed based on attunements export function getPuzzleProgressSpeed( puzzleId: string, attunements: Record ): number { const puzzle = PUZZLE_ROOMS[puzzleId]; if (!puzzle) return 0.02; // Default slow progress let speed = puzzle.baseProgressPerTick; // Add bonus for each relevant attunement level for (const attId of puzzle.attunements) { const attState = attunements[attId]; if (attState?.active) { speed += puzzle.attunementBonus * (attState.level || 1); } } return speed; }