# 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

```typescript
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

```typescript
pactSlots: 1  // in prestigeStore initial state
```

### 4.2 Upgrading

The `pactBinding` prestige upgrade adds +1 slot per level:
```typescript
pactSlots: id === 'pactBinding' ? state.pactSlots + 1 : state.pactSlots
```

> **Note:** The `pactBinding` upgrade is defined in `PRESTIGE_DEF` constants
> (`prestige.ts`) with `max: 5` and `cost: 2000`. It is fully functional in both
> store logic and UI.

### 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.

> **Note:** AGENTS.md states "Signed pacts do NOT persist through prestige (reset
> each loop)." The current code correctly resets `signedPacts` to `[]` on
> `startNewLoop`, matching the documented behavior. There is no 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`:

```typescript
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`, `radiantflames` |
| 150 | miasma | `air`, `death`, `miasma` |
| 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 10–80)

| 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 90–160)

| 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 | light+fire+radiantflames | 25% | 16h |
| 150 | air+death+miasma | 28% | 17h |
| 160 | shadowglass | 33% | 18h |

### 8.3 Tier 3 — Exotic Elements (Floors 170–240)

| Floor | Element | Armor | Pact Time |
|---|---|---|---|
| 170 | crystal | 35% | 19h |
| 180 | stellar | 30% | 20h |
| 190 | void | 35% | 21h |
| 200 | crystal+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 10–240) |
| `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 |
