205 lines
6.3 KiB
TypeScript
205 lines
6.3 KiB
TypeScript
// ─── Room Generation Functions ───────────────────────────────────────
|
|
// Moved from store-modules/room-utils.ts to eliminate legacy dependencies
|
|
|
|
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 './floor-utils';
|
|
import { getFloorElement } from './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 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,
|
|
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,
|
|
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),
|
|
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,
|
|
barrier: getEnemyBarrier(floor, element),
|
|
element,
|
|
}],
|
|
};
|
|
}
|
|
}
|
|
|
|
// Get puzzle progress speed based on attunements
|
|
export function getPuzzleProgressSpeed(
|
|
puzzleId: string,
|
|
attunements: Record<string, any>
|
|
): 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;
|
|
}
|