feat: implement DoT/debuff runtime system (spec §6, AC-12, AC-13)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s

- Add ActiveEffect, EffectType types to game.ts; activeEffects + effectiveArmor on EnemyState
- Add SpellOnHitEffect + onHitEffect field to SpellDefinition
- Wire onHitEffect to fire (burn), death (curse), lightning (armor_corrode), frost (freeze), soul (bypassArmor burn)
- Add applyOnHitEffect() — applies on-hit effect on successful spell hit (spec §6.2)
- Add processDoTPhase() — ticks all active effects after weapon/golem attacks (spec §6.3)
- Add bypassArmor/bypassBarrier support in applyEnemyDefenses() (AC-13)
- Export standalone applyEnemyDefenses from combat-tick.ts for DoT pipeline
- Split DoT runtime into separate dot-runtime.ts (135 lines) to keep combat-actions.ts under 400 lines
- Update all enemy generation sites with activeEffects/effectiveArmor defaults
- Fix test helpers for new required fields

All 921 tests pass (45 test files)
This commit is contained in:
2026-06-03 18:38:01 +02:00
parent a2cdf6d21c
commit b506f0bcc3
30 changed files with 3272 additions and 71 deletions
@@ -0,0 +1,229 @@
# Invoker Attunement — Design Spec
> Describes the Invoker attunement: identity, unlock flow, mana behavior, full
> discipline list with stats/perks, systems unlocked, pact interactions, and
> attunement level interactions.
---
## 1. Objective
The Invoker is the pact-focused attunement that transforms Guardian defeats into
permanent power. Unlike the other attunements, the Invoker has no primary mana type
and no automatic mana conversion — it gains elemental mana exclusively by signing
pacts with Guardians. Its disciplines amplify pact power, boon effectiveness, and
guardian-related multipliers.
---
## 2. Identity
| Property | Value |
|---|---|
| **ID** | `invoker` |
| **Slot** | `chest` |
| **Icon** | `💜` |
| **Color** | `#9B59B6` (Purple) |
| **Primary Mana** | None (gains elemental mana from pacts) |
| **Raw Mana Regen** | +0.3/hour (base, scales with `1.5^(level-1)`) |
| **Conversion Rate** | None (0 at all levels) |
| **Unlock** | Defeat first Guardian |
| **Capabilities** | `['pacts', 'guardianPowers', 'elementalMastery']` |
| **Skill Categories** | `['invocation', 'pact']` |
---
## 3. Unlock Condition and Flow
**Condition:** Defeat the first Guardian (floor 10).
**Unlock flow:**
1. Defeat the floor 10 Guardian (Ignis Prime)
2. Invoker becomes available for activation
3. Player activates Invoker → initialized at `{ active: true, level: 1, experience: 0 }`
4. Invoker disciplines become available: `pact-attunement`, `guardians-boon`
The unlock condition is stored as a descriptive string:
`"Defeat your first guardian and choose the path of the Invoker"`
---
## 4. Raw Mana Regen Contribution
Base regen: **+0.3/hour** (at level 1). Scales exponentially:
```
effectiveRegen = 0.3 × 1.5^(level - 1)
```
| Level | Raw Regen |
|---|---|
| 1 | 0.300/hr |
| 5 | 1.519/hr |
| 10 | 11.533/hr |
---
## 5. Mana Gain from Pacts (No Conversion)
The Invoker has **no automatic mana conversion**. Instead, it gains elemental mana
types exclusively through Guardian pacts:
When a pact is signed (`completePactRitual`):
```typescript
for (const manaType of guardian.unlocksMana || []) {
manaStore.unlockElement(manaType, 0);
}
```
Each guardian's `unlocksMana` is resolved via `resolveMultiUnlockChain(element)`,
which walks the element recipe tree to unlock the guardian's element and all base
components:
| Guardian | Element | Unlocks Mana Types |
|---|---|---|
| Floor 10 (Ignis Prime) | fire | `fire` |
| Floor 20 (Aqua Regia) | water | `water` |
| Floor 40 (Terra Firma) | earth | `earth` |
| Floor 90 (Metal) | metal | `fire`, `earth`, `metal` |
| Floor 130 (BlackFlame) | blackflame | `fire`, `earth`, `metal` |
| Floor 150 (Lightning) | lightning | `fire`, `air`, `lightning` |
Signing pacts is the **only** way for the Invoker to access elemental mana for
casting elemental spells and running elemental disciplines.
---
## 6. Disciplines
The Invoker's discipline pool contains **2 disciplines**.
### 6.1 Pact Attunement (`pact-attunement`)
| Field | Value |
|---|---|
| **Mana Type** | `raw` |
| **Base Cost** | 12 |
| **Requires** | `['signed_pact']` |
| **Stat Bonus** | `pactAffinityBonus` +0.05 (base) |
| **Scaling Factor** | 80 |
| **Difficulty Factor** | 150 |
| **Drain Base** | 4 |
**Perks:**
| Perk ID | Type | Threshold | Bonus |
|---|---|---|---|
| `pact-affinity-scaling` | `once` | 100 | Unlock pact affinity scaling |
| `pact-affinity-infinite` | `infinite` | 200 | Every 100 XP: `pactAffinityBonus` +0.05 |
| `pact-power-boost` | `capped` | 500 | Every 200 XP: `guardianBoonMultiplier` +0.03, max 5 tiers |
### 6.2 Guardian's Boon (`guardians-boon`)
| Field | Value |
|---|---|
| **Mana Type** | `raw` |
| **Base Cost** | 18 |
| **Requires** | `['signed_pact']` |
| **Stat Bonus** | `guardianBoonMultiplier` +0.10 (base) |
| **Scaling Factor** | 100 |
| **Difficulty Factor** | 200 |
| **Drain Base** | 6 |
**Perks:**
| Perk ID | Type | Threshold | Bonus |
|---|---|---|---|
| `boon-1` | `once` | 100 | `guardianBoonMultiplier` +0.10 |
| `boon-2` | `capped` | 200 | Every 350 XP: `guardianBoonMultiplier` +0.05, max 5 tiers |
### 6.3 Guardian Boon Multiplier Scaling
Maximum theoretical `guardianBoonMultiplier` from disciplines:
| Source | Value |
|---|---|
| Base (Guardian's Boon discipline) | +0.10 |
| `boon-1` perk (once @ 100 XP) | +0.10 |
| `boon-2` perk (capped, 5 tiers × 0.05) | +0.25 |
| `pact-power-boost` perk (capped, 5 tiers × 0.03) | +0.15 |
| **Maximum total** | **+0.60** |
With the base multiplier of 1.0, the maximum guardian boon multiplier is **1.60**.
---
## 7. Systems Unlocked
The Invoker attunement gates the **Pact System** (see `pact-system-spec.md`):
- Sign pacts with defeated Guardians
- Gain permanent boons and elemental mana unlocks
- Pact slots limit simultaneous signed pacts
- Pact affinity reduces ritual time
---
## 8. Puzzle Room Behavior
In the spire, every 7th floor has a puzzle room. When the room type is
`invoker_trial`, progress scales at 2.53% per tick per Invoker level.
---
## 9. Attunement Level Interactions
Higher Invoker level affects:
1. **Raw mana regen**: `0.3 × 1.5^(level-1)` per hour
2. **No conversion**: Invoker never has automatic mana conversion
3. **Pact affinity**: Higher raw regen supports the raw mana cost of pact rituals
Attunement level does **not** directly affect pact multipliers or boon power —
those scale through discipline XP.
---
## 10. Known Code Issues
The following inconsistencies exist in the codebase:
| Issue | Description |
|---|---|
| `pactBinding` upgrade | Referenced in `prestigeStore.doPrestige` but **not defined** in `PRESTIGE_DEF` constants |
| UI vs store mismatch | UI displays `prestigeUpgrades.pactCapacity` but store logic checks `pactBinding` |
| Pact persistence | `signedPacts` is persisted but also reset to `[]` on `startNewLoop` — pacts don't survive loops in current implementation |
| `pactInterferenceMitigation` | Used in `pact-utils.ts` but no prestige upgrade defines it |
---
## 11. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | Invoker is locked until the first Guardian is defeated. |
| AC-2 | Invoker has no primary mana type and no automatic conversion at any level. |
| AC-3 | Signing a pact unlocks the guardian's element and all component elements. |
| AC-4 | Both Invoker disciplines require at least one signed pact to activate. |
| AC-5 | `pact-affinity-infinite` perk grants +0.05 pactAffinityBonus every 100 XP beyond threshold 200. |
| AC-6 | `boon-2` capped perk grants +0.05 guardianBoonMultiplier per tier, max 5 tiers, interval 350 XP. |
| AC-7 | `pact-power-boost` capped perk grants +0.03 guardianBoonMultiplier per tier, max 5 tiers, interval 200 XP. |
| AC-8 | Maximum theoretical guardianBoonMultiplier from disciplines is 1.60 (base 1.0 + 0.60). |
| AC-9 | Invoker `invoker_trial` puzzle rooms grant bonus progress per Invoker level. |
| AC-10 | Invoker level scales raw regen by `1.5^(level-1)`. |
---
## 12. Files Reference
| File | Role |
|---|---|
| `src/lib/game/data/attunements.ts` | Invoker definition |
| `src/lib/game/data/disciplines/invoker.ts` | Invoker disciplines (2) |
| `src/lib/game/stores/prestigeStore.ts` | Pact ritual state, slot management |
| `src/lib/game/stores/pipelines/pact-ritual.ts` | Pact ritual tick processing |
| `src/lib/game/utils/pact-utils.ts` | Pact multiplier calculations |
| `src/lib/game/data/guardian-data.ts` | Static guardian definitions |
| `src/lib/game/data/guardian-encounters.ts` | Procedural guardian lookup |
| `src/components/game/tabs/GuardianPactsTab.tsx` | Pact signing UI |
| `docs/specs/attunements/invoker/systems/pact-system-spec.md` | Pact system spec |