// ─── Per-Enemy Damage Application (spec §3.2) ───────────────────────────────── // Extracted from combat-actions.ts to stay under the 400-line file limit. import type { CombatStore, CombatState } from './combat-state.types'; import type { EnemyState } from '../types'; /** * Find the enemy with the lowest current HP in the room (focus-fire targeting). * Returns null if no living enemies exist. */ export function lowestHPEnemy(enemies: EnemyState[]): EnemyState | null { let lowest: EnemyState | null = null; for (const enemy of enemies) { if (enemy.hp <= 0) continue; if (!lowest || enemy.hp < lowest.hp) { lowest = enemy; } } return lowest; } /** * Apply damage to enemies in the current room. * * Spec §3.2: * - AoE spells: each enemy takes full damage * - Single-target: target the enemy with lowest HP (focus-fire) * - Recalculates floorHP as sum of all enemy HP * - Triggers onRoomCleared when all enemies reach 0 HP * * @returns { floorHP, floorMaxHP, roomCleared } */ export function applyDamageToRoom( get: () => CombatStore, set: (state: Partial) => void, dmg: number, isAoe: boolean, ): { floorHP: number; floorMaxHP: number; roomCleared: boolean } { const state = get(); const room = state.currentRoom; if (!room || room.enemies.length === 0) { // No enemies in room — fall back to direct floorHP subtraction const newFloorHP = Math.max(0, state.floorHP - dmg); set({ floorHP: newFloorHP }); return { floorHP: newFloorHP, floorMaxHP: state.floorMaxHP, roomCleared: newFloorHP <= 0 }; } // For single-target, find the lowest HP enemy once (focus-fire) const singleTarget = isAoe ? null : lowestHPEnemy(room.enemies); const updatedEnemies = room.enemies.map((enemy) => { if (enemy.hp <= 0) return enemy; if (isAoe) { // AoE: each enemy takes full damage return { ...enemy, hp: Math.max(0, enemy.hp - dmg) }; } // Single-target: only damage the lowest HP enemy if (singleTarget && enemy.id === singleTarget.id) { return { ...enemy, hp: Math.max(0, enemy.hp - dmg) }; } return enemy; }); const newFloorHP = updatedEnemies.reduce((sum, e) => sum + e.hp, 0); const allDead = updatedEnemies.every((e) => e.hp <= 0); set({ currentRoom: { ...room, enemies: updatedEnemies }, floorHP: newFloorHP, }); return { floorHP: newFloorHP, floorMaxHP: state.floorMaxHP, roomCleared: allDead }; }