feat: implement per-enemy damage application (spec §3.2)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m6s

- Add applyDamageToRoom() function targeting individual enemies
- Add lowestHPEnemy() helper for focus-fire targeting
- AoE spells distribute damage across all enemies
- Single-target attacks hit lowest HP enemy first
- Refactor processCombatTick spell/equipment/melee/DoT damage to use per-enemy system
- Update golemApplyDamageToRoom in golem-combat pipeline for per-enemy targeting
- Add currentRoom to CombatTickResult and sync through gameStore
- Update combat-actions tests with proper enemy setup for per-enemy tests
- Extract combat-damage.ts module to stay under 400-line limit
This commit is contained in:
2026-06-04 11:37:21 +02:00
parent 8dde423526
commit 23e629f37e
10 changed files with 182 additions and 85 deletions
+28 -6
View File
@@ -5,7 +5,8 @@
import { useCombatStore } from '../combatStore';
import { useManaStore } from '../manaStore';
import { processGolemRoomDuration } from '../golem-combat-actions';
import type { ActiveGolem } from '../../types';
import { lowestHPEnemy } from '../combat-damage';
import type { ActiveGolem, EnemyState } from '../../types';
export interface GolemCombatContext {
addLog: (msg: string) => void;
@@ -38,12 +39,33 @@ export function buildGolemCombatPipeline(_addLog: (msg: string) => void): {
const golemApplyDamageToRoom = (dmg: number) => {
const cs = useCombatStore.getState();
if (dmg > 0) {
const newFloorHP = Math.max(0, cs.floorHP - dmg);
useCombatStore.setState({ floorHP: newFloorHP });
const room = cs.currentRoom;
if (!room || room.enemies.length === 0 || dmg <= 0) {
return { floorHP: cs.floorHP, floorMaxHP: cs.floorMaxHP, roomCleared: false };
}
const roomCleared = useCombatStore.getState().floorHP <= 0;
return { floorHP: cs.floorHP, floorMaxHP: cs.floorMaxHP, roomCleared };
// Golems use focus-fire targeting (spec §9.4) — target lowest HP enemy
const target = lowestHPEnemy(room.enemies);
if (!target) {
return { floorHP: cs.floorHP, floorMaxHP: cs.floorMaxHP, roomCleared: false };
}
const updatedEnemies = room.enemies.map((enemy) => {
if (enemy.id === target.id && enemy.hp > 0) {
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);
useCombatStore.setState({
currentRoom: { ...room, enemies: updatedEnemies },
floorHP: newFloorHP,
});
return { floorHP: newFloorHP, floorMaxHP: cs.floorMaxHP, roomCleared: allDead };
};
return { activeGolems, golemApplyDamageToRoom };