[Low] [Task] Combat spec gap: implement AoE target distribution and applyDamageToRoom (spec §3.2) #260

Closed
opened 2026-06-03 13:36:13 +02:00 by Anexim · 1 comment
Owner

Combat Spec Gap: AoE Target Distribution and applyDamageToRoom

Source of truth: docs/specs/spire-combat-spec.md §3.2, §11
Affected ACs: None directly (infrastructure for correct damage application)

Gap Summary

The combat tick pipeline in processCombatTick (stores/combat-actions.ts) applies damage directly to floorHP (a single number) rather than to individual enemies in the room. There is no applyDamageToRoom() function, no AoE target distribution, and no focus-fire targeting logic. This means multi-enemy rooms (swarm, rooms with multiple enemies) don't work correctly — all damage is subtracted from a single pool rather than being distributed across enemies.

What Exists

  • FloorState.enemies: EnemyState[] — rooms can have multiple enemies
  • SpellDefinition.aoe flag exists in spell definitions
  • processCombatTick tracks floorHP as a single number (sum of all enemy HP)
  • Damage is applied as floorHP = Math.max(0, floorHP - finalDamage) — no per-enemy targeting

What's Missing

1. applyDamageToRoom function (spec §3.2)

applyDamageToRoom(dmg, targetEnemy?):
  if spell is AoE and targetEnemy is null:
    // distribute damage across all enemies
    for each enemy in room:
      enemy.hp = max(0, enemy.hp - dmg)
  else:
    target = targetEnemy ?? lowestHPEnemy()
    target.hp = max(0, target.hp - dmg)

  if all enemies.hp === 0:
    onRoomCleared()

2. Focus-fire targeting (spec §3.2)

Non-AoE attacks must target the enemy with the lowest current HP by default:

target = lowestHPEnemy()  // enemy with minimum hp in room

3. Per-enemy damage application in processCombatTick

Current code applies damage to floorHP directly. Must be refactored to:

  1. Select target enemy (lowest HP for single-target, all for AoE)
  2. Apply damage to enemy.hp
  3. Recalculate floorHP as sum of all enemy HP
  4. Check if all enemies are dead → onRoomCleared()

4. AoE distribution (spec §3.2)

For AoE spells, damage is distributed across all enemies in the room (each enemy takes full damage, or damage is split — spec says "distribute damage across all enemies" which per the pseudocode means each enemy takes the full AoE damage).

Files to Change

File Change
stores/combat-actions.ts Add applyDamageToRoom() function; refactor processCombatTick to apply damage per-enemy instead of to floorHP directly; add lowestHPEnemy() helper; add AoE branch
stores/pipelines/combat-tick.ts May need to pass enemy reference through onDamageDealt for per-enemy defense application

Acceptance Criteria

  • Non-AoE attacks target the enemy with lowest current HP (focus-fire)
  • AoE attacks distribute damage across all enemies in the room
  • onRoomCleared() is only triggered when ALL enemies reach 0 HP
  • floorHP is correctly recalculated as sum of all enemy HP after each damage application

Dependencies

  • Should be implemented alongside or after issue #257 (regular enemy defenses) since per-enemy damage application is needed for armor/barrier/dodge to work correctly

Out of Scope

  • Visual targeting UI (focus-fire is implicit per spec)
  • New AoE spell definitions (wiring existing aoe flag only)
# Combat Spec Gap: AoE Target Distribution and applyDamageToRoom > **Source of truth:** `docs/specs/spire-combat-spec.md` §3.2, §11 > **Affected ACs:** None directly (infrastructure for correct damage application) ## Gap Summary The combat tick pipeline in `processCombatTick` (`stores/combat-actions.ts`) applies damage directly to `floorHP` (a single number) rather than to individual enemies in the room. There is no `applyDamageToRoom()` function, no AoE target distribution, and no focus-fire targeting logic. This means multi-enemy rooms (swarm, rooms with multiple enemies) don't work correctly — all damage is subtracted from a single pool rather than being distributed across enemies. ## What Exists - `FloorState.enemies: EnemyState[]` — rooms can have multiple enemies - `SpellDefinition.aoe` flag exists in spell definitions - `processCombatTick` tracks `floorHP` as a single number (sum of all enemy HP) - Damage is applied as `floorHP = Math.max(0, floorHP - finalDamage)` — no per-enemy targeting ## What's Missing ### 1. applyDamageToRoom function (spec §3.2) ``` applyDamageToRoom(dmg, targetEnemy?): if spell is AoE and targetEnemy is null: // distribute damage across all enemies for each enemy in room: enemy.hp = max(0, enemy.hp - dmg) else: target = targetEnemy ?? lowestHPEnemy() target.hp = max(0, target.hp - dmg) if all enemies.hp === 0: onRoomCleared() ``` ### 2. Focus-fire targeting (spec §3.2) Non-AoE attacks must target the enemy with the lowest current HP by default: ``` target = lowestHPEnemy() // enemy with minimum hp in room ``` ### 3. Per-enemy damage application in processCombatTick Current code applies damage to `floorHP` directly. Must be refactored to: 1. Select target enemy (lowest HP for single-target, all for AoE) 2. Apply damage to `enemy.hp` 3. Recalculate `floorHP` as sum of all enemy HP 4. Check if all enemies are dead → `onRoomCleared()` ### 4. AoE distribution (spec §3.2) For AoE spells, damage is distributed across all enemies in the room (each enemy takes full damage, or damage is split — spec says "distribute damage across all enemies" which per the pseudocode means each enemy takes the full AoE damage). ## Files to Change | File | Change | |---|---| | `stores/combat-actions.ts` | Add `applyDamageToRoom()` function; refactor `processCombatTick` to apply damage per-enemy instead of to `floorHP` directly; add `lowestHPEnemy()` helper; add AoE branch | | `stores/pipelines/combat-tick.ts` | May need to pass enemy reference through `onDamageDealt` for per-enemy defense application | ## Acceptance Criteria - [ ] Non-AoE attacks target the enemy with lowest current HP (focus-fire) - [ ] AoE attacks distribute damage across all enemies in the room - [ ] `onRoomCleared()` is only triggered when ALL enemies reach 0 HP - [ ] `floorHP` is correctly recalculated as sum of all enemy HP after each damage application ## Dependencies - Should be implemented alongside or after issue #257 (regular enemy defenses) since per-enemy damage application is needed for armor/barrier/dodge to work correctly ## Out of Scope - Visual targeting UI (focus-fire is implicit per spec) - New AoE spell definitions (wiring existing `aoe` flag only)
Anexim added the ai:todo label 2026-06-03 13:36:13 +02:00
n8n-gitea was assigned by Anexim 2026-06-03 13:36:13 +02:00
Anexim added ai:in-progress and removed ai:todo labels 2026-06-04 08:54:39 +02:00
Anexim added ai:done and removed ai:in-progress labels 2026-06-04 11:38:24 +02:00
Author
Owner

Implemented per-enemy damage application (spec §3.2):

  • Added applyDamageToRoom() in new combat-damage.ts module — applies damage to individual enemies with AoE distribution and focus-fire targeting (lowest HP enemy)
  • Added lowestHPEnemy() helper for focus-fire targeting
  • Refactored processCombatTick to use per-enemy damage for: main spell, equipment spells, melee swords, DoT effects
  • Updated golemApplyDamageToRoom in golem-combat.ts pipeline to target individual enemies via focus-fire
  • Added currentRoom to CombatTickResult type and sync through gameStore.ts combat writes
  • Fixed floorMaxHP to use tracked value from advanceRoomOrFloor instead of recalculating via getFloorMaxHP
  • Updated combat-actions.test.ts with proper enemy setup for per-enemy tests
  • Updated melee-auto-attack.test.ts to include currentRoom in mock CombatTickResult objects

All 937 tests pass. No circular dependencies. All files under 400 lines.

Implemented per-enemy damage application (spec §3.2): - Added `applyDamageToRoom()` in new `combat-damage.ts` module — applies damage to individual enemies with AoE distribution and focus-fire targeting (lowest HP enemy) - Added `lowestHPEnemy()` helper for focus-fire targeting - Refactored `processCombatTick` to use per-enemy damage for: main spell, equipment spells, melee swords, DoT effects - Updated `golemApplyDamageToRoom` in `golem-combat.ts` pipeline to target individual enemies via focus-fire - Added `currentRoom` to `CombatTickResult` type and sync through `gameStore.ts` combat writes - Fixed `floorMaxHP` to use tracked value from `advanceRoomOrFloor` instead of recalculating via `getFloorMaxHP` - Updated `combat-actions.test.ts` with proper enemy setup for per-enemy tests - Updated `melee-auto-attack.test.ts` to include `currentRoom` in mock `CombatTickResult` objects All 937 tests pass. No circular dependencies. All files under 400 lines.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#260