[Medium] [Task] Combat spec gap: implement sword/melee auto-attack system (spec §2.3, §3.1, §4.3) #256

Closed
opened 2026-06-03 13:32:35 +02:00 by Anexim · 2 comments
Owner

Combat Spec Gap: Sword/Melee Auto-Attack System

Source of truth: docs/specs/spire-combat-spec.md §2.3, §3.1, §4.3
Affected ACs: AC-2, AC-5

Gap Summary

The combat tick pipeline (combat-actions.tsprocessCombatTick) only processes spell autocasting. There is no sword/melee auto-attack branch. The spec defines three independent damage sources (staves, swords, golems) but only staves are wired.

What Exists

  • Sword equipment data: data/equipment/swords.ts with attackSpeed stat
  • Elemental matchup utility: getElementalBonus() in combat-utils.ts (works for both spells and swords)
  • EnemyState type has all needed fields

What's Missing

1. Melee progress accumulator (spec §3.1)

  • No meleeSwordProgress: Record<instanceId, number> in combat state
  • Each equipped sword needs its own independent meleeProgress counter

2. Melee damage calculation (spec §4.3)

baseDmg = sword.baseDamage + sword.elementalEnchantDamage
damage  = baseDmg × getElementalBonus(sword.enchantElement, enemy.element)
  • No calcMeleeDamage() function exists
  • No crit, no discipline damage bonus for melee (per spec)

3. Melee branch in processCombatTick (spec §3.1)

for each equipped sword (each on own meleeProgress):
  meleeProgress += HOURS_PER_TICK × sword.attackSpeed
  while meleeProgress >= 1:
    calcMeleeDamage() → elemental matchup applied
    onDamageDealt(dmg) → enemy defenses (no specials for melee)
    applyDamageToRoom(finalDmg)

4. Executioner/Berserker exclusion

  • Melee attacks must NOT trigger Executioner or Berserker discipline specials (spec §4.4)

Files to Change

File Change
stores/combat-state.types.ts Add meleeSwordProgress: Record<instanceId, number>
stores/combat-actions.ts Add melee branch in processCombatTick; add calcMeleeDamage()
stores/pipelines/combat-tick.ts Ensure onDamageDealt can distinguish melee from spell (skip specials)
utils/combat-utils.ts Add calcMeleeDamage() function

Acceptance Criteria

  • AC-2: Swords auto-attack on their own timer with no mana cost; elemental matchup applies
  • AC-5: Elemental matchup applies correctly for both spells and swords

Out of Scope

  • Sword equipment UI changes
  • New sword types or enchantments
  • Melee-specific discipline specials (future work)
# Combat Spec Gap: Sword/Melee Auto-Attack System > **Source of truth:** `docs/specs/spire-combat-spec.md` §2.3, §3.1, §4.3 > **Affected ACs:** AC-2, AC-5 ## Gap Summary The combat tick pipeline (`combat-actions.ts` → `processCombatTick`) only processes spell autocasting. There is no sword/melee auto-attack branch. The spec defines three independent damage sources (staves, swords, golems) but only staves are wired. ## What Exists - Sword equipment data: `data/equipment/swords.ts` with `attackSpeed` stat - Elemental matchup utility: `getElementalBonus()` in `combat-utils.ts` (works for both spells and swords) - `EnemyState` type has all needed fields ## What's Missing ### 1. Melee progress accumulator (spec §3.1) - No `meleeSwordProgress: Record<instanceId, number>` in combat state - Each equipped sword needs its own independent `meleeProgress` counter ### 2. Melee damage calculation (spec §4.3) ``` baseDmg = sword.baseDamage + sword.elementalEnchantDamage damage = baseDmg × getElementalBonus(sword.enchantElement, enemy.element) ``` - No `calcMeleeDamage()` function exists - No crit, no discipline damage bonus for melee (per spec) ### 3. Melee branch in processCombatTick (spec §3.1) ``` for each equipped sword (each on own meleeProgress): meleeProgress += HOURS_PER_TICK × sword.attackSpeed while meleeProgress >= 1: calcMeleeDamage() → elemental matchup applied onDamageDealt(dmg) → enemy defenses (no specials for melee) applyDamageToRoom(finalDmg) ``` ### 4. Executioner/Berserker exclusion - Melee attacks must NOT trigger Executioner or Berserker discipline specials (spec §4.4) ## Files to Change | File | Change | |---|---| | `stores/combat-state.types.ts` | Add `meleeSwordProgress: Record<instanceId, number>` | | `stores/combat-actions.ts` | Add melee branch in `processCombatTick`; add `calcMeleeDamage()` | | `stores/pipelines/combat-tick.ts` | Ensure `onDamageDealt` can distinguish melee from spell (skip specials) | | `utils/combat-utils.ts` | Add `calcMeleeDamage()` function | ## Acceptance Criteria - [ ] AC-2: Swords auto-attack on their own timer with no mana cost; elemental matchup applies - [ ] AC-5: Elemental matchup applies correctly for both spells and swords ## Out of Scope - Sword equipment UI changes - New sword types or enchantments - Melee-specific discipline specials (future work)
Anexim added the ai:todo label 2026-06-03 13:32:35 +02:00
n8n-gitea was assigned by Anexim 2026-06-03 13:32:35 +02:00
Anexim added ai:in-progress and removed ai:todo labels 2026-06-03 18:48:57 +02:00
Author
Owner

Progress Update: Sword/Melee Auto-Attack System Implementation

Changes Made

New Function: calcMeleeDamage() (src/lib/game/utils/combat-utils.ts)

  • Calculates melee damage per spec §4.3: baseDmg × getElementalBonus(swordElement, enemyElement)
  • Maps sword enchantment special IDs to elements (fireBlade→fire, frostBlade→frost, etc.)
  • Falls back to base damage (5) for unenchanted swords
  • Exported from barrel file

New State: meleeSwordProgress (src/lib/game/stores/combat-state.types.ts + combatStore.ts)

  • Per-instance progress accumulator for each equipped sword
  • Initialized to {}, persisted through localStorage
  • Independent timers per sword (different attack speeds)

Melee Branch in processCombatTick (src/lib/game/stores/combat-actions.ts)

  • Runs after spell casting, before golem/DoT phases
  • Each equipped sword with stats.attackSpeed ticks its own progress counter
  • When progress ≥ 1: calculates melee damage with elemental matchup
  • Applies enemy defenses (dodge, barrier, armor) — but NOT Executioner/Berserker (spec §4.4)
  • No mana cost for melee attacks
  • Progress wraps to fractional part after each hit

Equipment Stats (src/lib/game/data/equipment/types.ts + swords.ts)

  • Added optional stats field to EquipmentType interface
  • Added baseDamage and attackSpeed to all 5 sword types:
    • Iron Blade: 8 dmg, 1.2 speed
    • Steel Blade: 12 dmg, 1.3 speed
    • Crystal Blade: 18 dmg, 1.1 speed
    • Arcanist Blade: 22 dmg, 1.4 speed
    • Void-Touched Blade: 25 dmg, 1.0 speed

Game Store Integration (src/lib/game/stores/gameStore.ts)

  • Builds equippedSwords map from crafting store's equippedInstances + equipmentInstances
  • Passes to processCombatTick as new parameter
  • Writes back meleeSwordProgress to combat store

Tests (src/lib/game/__tests__/melee-auto-attack.test.ts)

  • 16 tests covering calcMeleeDamage() and melee auto-attack in processCombatTick
  • Tests elemental matchup, progress accumulation, multi-sword support

Test Results

  • All 937 tests pass (46 test files, 0 failures)
  • All files under 400-line limit
## Progress Update: Sword/Melee Auto-Attack System Implementation ### Changes Made **New Function: `calcMeleeDamage()`** (`src/lib/game/utils/combat-utils.ts`) - Calculates melee damage per spec §4.3: `baseDmg × getElementalBonus(swordElement, enemyElement)` - Maps sword enchantment special IDs to elements (fireBlade→fire, frostBlade→frost, etc.) - Falls back to base damage (5) for unenchanted swords - Exported from barrel file **New State: `meleeSwordProgress`** (`src/lib/game/stores/combat-state.types.ts` + `combatStore.ts`) - Per-instance progress accumulator for each equipped sword - Initialized to `{}`, persisted through localStorage - Independent timers per sword (different attack speeds) **Melee Branch in `processCombatTick`** (`src/lib/game/stores/combat-actions.ts`) - Runs after spell casting, before golem/DoT phases - Each equipped sword with `stats.attackSpeed` ticks its own progress counter - When progress ≥ 1: calculates melee damage with elemental matchup - Applies enemy defenses (dodge, barrier, armor) — but NOT Executioner/Berserker (spec §4.4) - No mana cost for melee attacks - Progress wraps to fractional part after each hit **Equipment Stats** (`src/lib/game/data/equipment/types.ts` + `swords.ts`) - Added optional `stats` field to `EquipmentType` interface - Added `baseDamage` and `attackSpeed` to all 5 sword types: - Iron Blade: 8 dmg, 1.2 speed - Steel Blade: 12 dmg, 1.3 speed - Crystal Blade: 18 dmg, 1.1 speed - Arcanist Blade: 22 dmg, 1.4 speed - Void-Touched Blade: 25 dmg, 1.0 speed **Game Store Integration** (`src/lib/game/stores/gameStore.ts`) - Builds `equippedSwords` map from crafting store's `equippedInstances` + `equipmentInstances` - Passes to `processCombatTick` as new parameter - Writes back `meleeSwordProgress` to combat store **Tests** (`src/lib/game/__tests__/melee-auto-attack.test.ts`) - 16 tests covering `calcMeleeDamage()` and melee auto-attack in `processCombatTick` - Tests elemental matchup, progress accumulation, multi-sword support ### Test Results - All 937 tests pass (46 test files, 0 failures) - All files under 400-line limit
Anexim added ai:done and removed ai:in-progress labels 2026-06-03 22:00:35 +02:00
Author
Owner

Implementation complete and pushed to master (8dde423).

All acceptance criteria met:

  • AC-2: Swords auto-attack on their own timer with no mana cost; elemental matchup applies
  • AC-5: Elemental matchup applies correctly for both spells and swords

Files changed (10 files, +568/-187 lines):

  • src/lib/game/utils/combat-utils.ts — Added calcMeleeDamage() function
  • src/lib/game/stores/combat-state.types.ts — Added meleeSwordProgress state
  • src/lib/game/stores/combatStore.ts — Added initial state and persistence
  • src/lib/game/stores/combat-actions.ts — Added melee branch in processCombatTick
  • src/lib/game/stores/gameStore.ts — Wired equippedSwords through tick pipeline
  • src/lib/game/data/equipment/types.ts — Added optional stats field
  • src/lib/game/data/equipment/swords.ts — Added baseDamage/attackSpeed to all 5 swords
  • src/lib/game/constants/index.ts — Exported EQUIPMENT_TYPES
  • src/lib/game/__tests__/melee-auto-attack.test.ts — 16 new regression tests

All 937 tests pass (46 files, 0 failures). All files under 400-line limit.

Implementation complete and pushed to master (8dde423). All acceptance criteria met: - AC-2: Swords auto-attack on their own timer with no mana cost; elemental matchup applies - AC-5: Elemental matchup applies correctly for both spells and swords Files changed (10 files, +568/-187 lines): - `src/lib/game/utils/combat-utils.ts` — Added `calcMeleeDamage()` function - `src/lib/game/stores/combat-state.types.ts` — Added `meleeSwordProgress` state - `src/lib/game/stores/combatStore.ts` — Added initial state and persistence - `src/lib/game/stores/combat-actions.ts` — Added melee branch in `processCombatTick` - `src/lib/game/stores/gameStore.ts` — Wired `equippedSwords` through tick pipeline - `src/lib/game/data/equipment/types.ts` — Added optional `stats` field - `src/lib/game/data/equipment/swords.ts` — Added `baseDamage`/`attackSpeed` to all 5 swords - `src/lib/game/constants/index.ts` — Exported `EQUIPMENT_TYPES` - `src/lib/game/__tests__/melee-auto-attack.test.ts` — 16 new regression tests All 937 tests pass (46 files, 0 failures). All files under 400-line limit.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#256