Files
Mana-Loop/docs/specs/attunements/invoker/systems/pact-system-spec.md
T
n8n-gitea b506f0bcc3
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m18s
feat: implement DoT/debuff runtime system (spec §6, AC-12, AC-13)
- 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)
2026-06-03 18:38:01 +02:00

12 KiB
Raw Blame History

Pact System — Design Spec

Describes the Guardian pact system: ritual flow, boon types, pact slot system, pact persistence, discipline scaling, and how the Invoker gains elemental mana.


1. Objective

The Pact system is the Invoker attunement's core progression mechanic. After defeating a Guardian boss on every 10th floor, the player can sign a pact through a ritual process. Each signed pact grants permanent boons (stat multipliers) and unlocks elemental mana types. Pact slots limit how many pacts can be active simultaneously, and the Invoker's disciplines amplify pact power.

Design goals:

  • Pacts are earned through combat achievement (defeating Guardians)
  • Ritual time creates a meaningful time investment
  • Multiple pacts provide multiplicative power but with interference penalties
  • Boon variety ensures each pact feels distinct
  • Pact affinity (from disciplines) reduces ritual time

2. Pact Ritual Flow

2.1 Step 1: Defeat the Guardian

  • Every 10th floor (10, 20, 30, ...) has a Guardian boss room
  • Defeating the Guardian adds the floor number to defeatedGuardians[]
  • Only defeated Guardians are eligible for pact signing

2.2 Step 2: Start Ritual

startPactRitual(floor):
  1. Validate guardian exists at floor
  2. Check floor is in defeatedGuardians
  3. Check floor is NOT already in signedPacts
  4. Check signedPacts.length < pactSlots (slot available)
  5. Check rawMana >= guardian.pactCost (enough raw mana)
  6. Check pactRitualFloor === null (no other ritual in progress)
  7. Deduct guardian.pactCost raw mana
  8. Set pactRitualFloor = floor, pactRitualProgress = 0

2.3 Step 3: Progress Ritual

Each game tick:

processPactRitual():
  pactAffinity = min(0.9, pactAffinityUpgrade × 0.1 + pactAffinityBonus)
  requiredTime = guardian.pactTime × (1 - pactAffinity)
  pactRitualProgress += HOURS_PER_TICK
  if pactRitualProgress >= requiredTime → completePactRitual()

Pact affinity sources:

  • pactAffinityUpgrade: prestige upgrade level (each level = +0.1, capped at 0.9)
  • pactAffinityBonus: discipline bonus from Pact Attunement discipline

2.4 Step 4: Pact Signed

completePactRitual():
  1. Add floor to signedPacts[]
  2. Remove floor from defeatedGuardians[]
  3. Reset pactRitualFloor = null, pactRitualProgress = 0
  4. For each manaType in guardian.unlocksMana:
       manaStore.unlockElement(manaType, 0)
  5. Log: "📜 Pact signed with {name}! You have gained their boons."
  6. Log: "✨ {ManaType} mana unlocked!" for each new element

2.5 Cancellation

cancelPactRitual() resets pactRitualFloor = null, pactRitualProgress = 0. The raw mana cost is not refunded on cancellation.


3. Guardian Boon Types

Each Guardian grants 2 boons from the following pool of 12 types:

Boon Type Effect
maxMana Flat max raw mana bonus
manaRegen Flat mana regen per hour bonus
castingSpeed Spell cast speed multiplier
elementalDamage Elemental damage multiplier
rawDamage Raw damage multiplier
critChance Critical hit chance bonus
critDamage Critical hit damage multiplier
spellEfficiency Spell efficiency bonus
manaGain Mana gain multiplier
insightGain Insight gain multiplier
studySpeed Study speed multiplier
prestigeInsight Prestige insight bonus

3.1 Boon Application

for (const floor of signedPacts) {
  const guardian = getGuardianForFloor(floor);
  for (const boon of guardian.boons) {
    let value = boon.value × guardianBoonMultiplier;
    // Apply to corresponding bonus stat
  }
}

The guardianBoonMultiplier starts at 1.0 and is increased by the Guardian's Boon discipline and its perks (see §6).


4. Pact Slot System

4.1 Starting Value

pactSlots: 1  // in prestigeStore initial state

4.2 Upgrading

The pactBinding prestige upgrade adds +1 slot per level:

pactSlots: id === 'pactBinding' ? state.pactSlots + 1 : state.pactSlots

Note: The pactBinding upgrade is referenced in the store logic but is not defined in PRESTIGE_DEF constants. This is a known gap — the upgrade exists in code but has no definition, cost, or max level.

4.3 Slot Enforcement

A new pact ritual cannot be started if signedPacts.length >= pactSlots. The player must choose which pacts to maintain.


5. Pact Persistence Through Prestige

5.1 What Persists

Field Persisted Reset on New Loop
signedPacts Yes (via Zustand persist) Yes (reset to [])
signedPactDetails Yes No
pactSlots Yes No
pactRitualFloor Yes Yes (reset to null)
pactRitualProgress Yes Yes (reset to 0)
defeatedGuardians No Yes (reset to [])

5.2 Current Behavior

In the current implementation, signedPacts is reset to [] on startNewLoop, meaning pacts do NOT persist through prestige loops. The player must re-defeat Guardians and re-sign pacts each loop. The signedPactDetails record persists for historical tracking but does not confer active boons.

Design intent vs. implementation: The AGENTS.md states "Signed pacts persist through prestige (bounded by pactSlots)." The current code resets them. This is a known discrepancy.


6. Invoker Discipline Scaling of Pact Power

6.1 Pact Affinity (Ritual Time Reduction)

From the Pact Attunement discipline:

pactAffinity = min(0.9, pactAffinityUpgrade × 0.1 + pactAffinityBonus)
requiredTime = guardian.pactTime × (1 - pactAffinity)
pactAffinity Time Reduction
0.0 0% (full time)
0.3 30% faster
0.5 50% faster
0.9 90% faster (cap)

The pactAffinityBonus starts at +0.05 (base from discipline) and gains +0.05 every 100 XP from the pact-affinity-infinite perk (threshold 200).

6.2 Guardian Boon Multiplier (Boon Power)

From the Guardian's Boon discipline and cross-perks:

Source guardianBoonMultiplier Bonus
Guardian's Boon discipline (base) +0.10
boon-1 perk (once @ 100 XP) +0.10
boon-2 perk (capped, 5 tiers) up to +0.25
pact-power-boost perk (capped, 5 tiers) up to +0.15
Maximum total +0.60 (multiplier = 1.60)

6.3 Pact Multiplier (Damage and Insight)

From pact-utils.ts:

computePactMultiplier(signedPacts, pactInterferenceMitigation):
  baseMult = Π guardian.damageMultiplier for each signed pact
  
  if only 1 pact: return baseMult
  
  numAdditional = signedPacts.length - 1
  basePenalty = 0.5 × numAdditional
  mitigationReduction = min(pactInterferenceMitigation, 5) × 0.1
  effectivePenalty = max(0, basePenalty - mitigationReduction)
  
  if pactInterferenceMitigation >= 5:
    synergyBonus = (pactInterferenceMitigation - 5) × 0.1
    return baseMult × (1 + synergyBonus)
  
  return baseMult × (1 - effectivePenalty)

Example (2 pacts, floors 10+20):

  • Floor 10 damage multiplier: 1.0 + 10 × 0.01 = 1.10
  • Floor 20 damage multiplier: 1.0 + 20 × 0.01 = 1.20
  • baseMult = 1.10 × 1.20 = 1.32
  • With 0 mitigation: 1.32 × (1 - 0.5) = 0.66
  • With 3 mitigation: 1.32 × (1 - 0.2) = 1.056
  • With 5 mitigation: 1.32 × 1 = 1.32
  • With 7 mitigation: 1.32 × 1.2 = 1.584

The same formula applies to computePactInsightMultiplier using guardian.insightMultiplier (1.0 + floor × 0.005).


7. Invoker's Mana Gain from Pacts

7.1 Elemental Unlocks

The Invoker gains elemental mana types exclusively through pact signing. Each guardian's unlocksMana is derived from resolveMultiUnlockChain(element):

Guardian Floor Element Mana Types Unlocked
10 fire fire
20 water water
30 air air
40 earth earth
50 light light
60 dark dark
70 death death
80 transference transference
90 metal fire, earth, metal
100 sand earth, water, sand
110 lightning fire, air, lightning
120 frost air, water, frost
130 blackflame fire, earth, metal
140 radiantflames light, fire
150 miasma air, death
160 shadowglass earth, dark
170+ exotic varies (see guardian-data.ts)

7.2 No Automatic Conversion

The Invoker has conversionRate = 0. It does not automatically convert raw mana to any elemental type. All elemental mana must come from:

  1. Pact unlocks (elemental types become available)
  2. Elemental regen disciplines (once the element type is unlocked)
  3. Equipment with mana regen enchantments

8. Guardian Data Summary

8.1 Tier 1 — Base Elements (Floors 1080)

Floor Name Element Armor Pact Cost Pact Time Boons
10 Ignis Prime fire 10% hp×0.3+power×5+... 3h +5% Fire dmg, +50 max mana
20 Aqua Regia water 15% same formula 4h +5% Water dmg, +0.5 mana regen
30 Ventus Rex air 18% same formula 5h +5% Air dmg, +5% casting speed
40 Terra Firma earth 25% same formula 6h +5% Earth dmg, +100 max mana
50 Lux Aeterna light 20% same formula 7h +10% Light dmg, +10% insight gain
60 Umbra Mortis dark 22% same formula 8h +10% Dark dmg, +15% crit damage
70 Mors Ultima death 25% same formula 9h +10% Death dmg, +10% raw damage
80 Vinculum Arcana transference 20% same formula 10h +150 max mana, +1.0 mana regen

8.2 Tier 2 — Composite Elements (Floors 90160)

Floor Element Armor Pact Time
90 metal 30% 11h
100 sand 25% 12h
110 lightning 22% 13h
120 frost 28% 14h
130 blackflame 32% 15h
140 radiantflames 25% 16h
150 miasma 28% 17h
160 shadowglass 33% 18h

8.3 Tier 3 — Exotic Elements (Floors 170240)

Floor Element Armor Pact Time
170 crystal 35% 19h
180 stellar 30% 20h
190 void 35% 21h
200 soul+stellar+void 35% 22h
210 soul+time+plasma 32% 23h
220 plasma 28% 24h
230 crystal+stellar+void 40% 25h
240 soul+time+plasma 42% 26h

8.4 Tier 4+ — Procedural (Floors 250+)

Every 10 floors, with scaling armor, pact multiplier, damage multiplier, and insight multiplier. Dual-element combinations cycle through 9 pairings, then scale through 8 tiers of increasing complexity.


9. Acceptance Criteria

# Criterion
AC-1 Pact ritual can only be started for defeated Guardians with an available pact slot and sufficient raw mana.
AC-2 Ritual progress accumulates at HOURS_PER_TICK per tick; pact affinity reduces required time.
AC-3 On completion, the floor is added to signedPacts, removed from defeatedGuardians, and mana types are unlocked.
AC-4 Pact affinity is capped at 0.9 (90% time reduction).
AC-5 Guardian boon multiplier from disciplines correctly increases boon values.
AC-6 Pact multiplier formula applies interference penalties for multiple pacts, with mitigation reducing the penalty.
AC-7 At 5+ mitigation, synergy bonus applies instead of penalty.
AC-8 Starting pact slots = 1; each pactBinding upgrade adds +1 slot.
AC-9 Invoker gains elemental mana types exclusively through pact signing.
AC-10 Cancelling a ritual resets progress but does not refund the raw mana cost.
AC-11 Both Invoker disciplines require at least one signed pact (requires: ['signed_pact']).

10. Files Reference

File Role
src/lib/game/stores/prestigeStore.ts Pact ritual state, slot management, start/complete/cancel
src/lib/game/stores/pipelines/pact-ritual.ts Per-tick ritual processing
src/lib/game/utils/pact-utils.ts Pact multiplier, insight multiplier, interference formulas
src/lib/game/data/guardian-data.ts Static guardian definitions (floors 10240)
src/lib/game/data/guardian-encounters.ts Procedural guardian lookup (250+)
src/lib/game/data/disciplines/invoker.ts Invoker disciplines (2)
src/lib/game/utils/guardian-utils.ts Element unlock chain resolution
src/components/game/tabs/GuardianPactsTab.tsx Pact signing UI
src/components/game/tabs/guardian-pacts-components.tsx Pact UI sub-components