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,348 @@
# Attunement System — Design Spec
> Describes the three-attunement class system: Enchanter, Invoker, and Fabricator.
> Covers slot assignments, unlock conditions, leveling, regen/conversion scaling,
> discipline pool gating, and interaction with mana conversion and the incursion system.
---
## 1. Objective
Attunements are class-like specializations that gate access to discipline pools and
unique capabilities. A player can have multiple attunements active simultaneously,
each contributing raw mana regen and (for Enchanter and Fabricator) automatic mana
conversion. Attunements level up independently through attunement-specific XP sources,
scaling their regen and conversion rates exponentially.
**Design goals:**
- Three distinct attunements with unique identities and roles
- Attunements unlock over time, expanding the player's options
- Leveling provides meaningful exponential scaling without being mandatory
- Discipline pool access is gated behind attunement unlock status
- Invoker's lack of primary mana creates a distinct pact-dependent playstyle
---
## 2. The Three Attunements
### 2.1 Enchanter (Right Hand) — Starting Attunement
| Property | Value |
|---|---|
| **ID** | `enchanter` |
| **Slot** | `rightHand` |
| **Icon** | `✨` |
| **Color** | `#1ABC9C` (Teal) |
| **Primary Mana** | `transference` |
| **Raw Mana Regen** | +0.5/hour (base) |
| **Conversion Rate** | 0.2 raw→transference/hour (base) |
| **Unlock** | Starting (unlocked by default) |
| **Capabilities** | `['enchanting']` |
| **Skill Categories** | `['enchant', 'effectResearch']` |
**Disciplines:** 10 disciplines across 4 files (core: 4, utility: 2, spells: 3, special: 1)
### 2.2 Invoker (Chest) — Locked
| 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) |
| **Conversion Rate** | None (0 at all levels) |
| **Unlock** | Defeat first Guardian |
| **Capabilities** | `['pacts', 'guardianPowers', 'elementalMastery']` |
| **Skill Categories** | `['invocation', 'pact']` |
**Disciplines:** 2 disciplines
### 2.3 Fabricator (Left Hand) — Locked
| Property | Value |
|---|---|
| **ID** | `fabricator` |
| **Slot** | `leftHand` |
| **Icon** | `⚒️` |
| **Color** | `#F4A261` (Earth) |
| **Primary Mana** | `earth` |
| **Raw Mana Regen** | +0.4/hour (base) |
| **Conversion Rate** | 0.25 raw→earth/hour (base) |
| **Unlock** | Prove crafting worth |
| **Capabilities** | `['golemCrafting', 'gearCrafting', 'earthShaping']` |
| **Skill Categories** | `['fabrication', 'golemancy']` |
**Disciplines:** 5 disciplines
---
## 3. Unlock Conditions
| Attunement | Condition | Implementation |
|---|---|---|
| **Enchanter** | Starting | Present in initial state: `{ active: true, level: 1, experience: 0 }` |
| **Invoker** | Defeat first Guardian | Descriptive: `"Defeat your first guardian and choose the path of the Invoker"` |
| **Fabricator** | Prove crafting worth | Descriptive: `"Prove your worth as a crafter"` |
Unlocking is performed via `debugUnlockAttunement(attunementId)` in the store, which
initializes the attunement at `{ active: true, level: 1, experience: 0 }`. The
conditions are currently descriptive strings rather than hard-coded mechanical checks.
---
## 4. Attunement Leveling
### 4.1 XP Thresholds
```
Level 1: 0 XP (starting)
Level 2: 1,000 XP
Level ≥ 3: Math.floor(1000 * Math.pow(2, level - 2) * 1.25)
```
| Level | XP Threshold | Cumulative XP |
|---|---|---|
| 1 | 0 | 0 |
| 2 | 1,000 | 1,000 |
| 3 | 2,500 | 3,500 |
| 4 | 5,000 | 8,500 |
| 5 | 10,000 | 18,500 |
| 6 | 20,000 | 38,500 |
| 7 | 40,000 | 78,500 |
| 8 | 80,000 | 158,500 |
| 9 | 160,000 | 318,500 |
| 10 | 320,000 | 638,500 |
**Max Level:** `MAX_ATTUNEMENT_LEVEL = 10`
### 4.2 Level-Up Mechanism
```
addAttunementXP(attunementId, amount):
state.experience += amount
while state.experience >= xpForNextLevel && level < MAX:
state.experience -= xpForNextLevel
level += 1
log("Attunement leveled up!")
```
XP does **not** roll over beyond the threshold check — the threshold amount is
subtracted and any remainder carries into the next level.
### 4.3 Regen and Conversion Rate Scaling
Both raw mana regen and conversion rate use the same exponential formula:
```
scaledValue = baseValue × 1.5^(level - 1)
```
**Effective raw mana regen by level (per attunement):**
| Level | Enchanter (0.5) | Invoker (0.3) | Fabricator (0.4) |
|---|---|---|---|
| 1 | 0.500/hr | 0.300/hr | 0.400/hr |
| 2 | 0.750/hr | 0.450/hr | 0.600/hr |
| 3 | 1.125/hr | 0.675/hr | 0.900/hr |
| 4 | 1.688/hr | 1.013/hr | 1.350/hr |
| 5 | 2.531/hr | 1.519/hr | 2.025/hr |
| 6 | 3.797/hr | 2.278/hr | 3.038/hr |
| 7 | 5.695/hr | 3.417/hr | 4.556/hr |
| 8 | 8.543/hr | 5.126/hr | 6.834/hr |
| 9 | 12.814/hr | 7.689/hr | 10.252/hr |
| 10 | 19.221/hr | 11.533/hr | 15.377/hr |
**Effective conversion rate by level:**
| Level | Enchanter (0.2) | Fabricator (0.25) |
|---|---|---|
| 1 | 0.200/hr | 0.250/hr |
| 2 | 0.300/hr | 0.375/hr |
| 3 | 0.450/hr | 0.563/hr |
| 4 | 0.675/hr | 0.844/hr |
| 5 | 1.013/hr | 1.266/hr |
| 6 | 1.519/hr | 1.898/hr |
| 7 | 2.278/hr | 2.848/hr |
| 8 | 3.417/hr | 4.271/hr |
| 9 | 5.126/hr | 6.407/hr |
| 10 | 7.689/hr | 9.610/hr |
Invoker has `conversionRate = 0` at all levels — no auto-conversion.
**Total regen** = sum of `baseRegen × 1.5^(level-1)` across all active attunements.
**Total conversion drain** = sum of `baseConversionRate × 1.5^(level-1)` across active attunements
that have a non-zero conversion rate. This drain is applied to the raw mana pool.
---
## 5. Attunement XP Gain Sources
### 5.1 Enchanting → Enchanter XP
```typescript
calculateEnchantingXP(capacityUsed: number): number {
return Math.max(1, Math.floor(capacityUsed / 10));
}
```
- 1 Enchanter XP per 10 capacity used (floored), minimum 1 XP per enchant.
### 5.2 Other Sources
The `addAttunementXP(attunementId, amount)` store action is the generic mechanism.
Any system can call it to award XP to any attunement. In the codebase as-is,
only enchanting has an explicit calculation function. Invoker and Fabricator XP
gain is expected to be called from their respective systems (pact signing and
item fabrication) but explicit calculation functions are not yet defined.
---
## 6. Discipline Pool Gating
### 6.1 Skill Categories
Attunements gate discipline access through **skill categories**:
| Category | Disciplines |
|---|---|
| Always available | `mana`, `study`, `research` |
| Enchanter | `enchant`, `effectResearch` |
| Invoker | `invocation`, `pact` |
| Fabricator | `fabrication`, `golemancy` |
The function `getAvailableSkillCategories()` iterates all **active** attunements,
collects their `skillCategories` into a Set, and returns the deduplicated array.
### 6.2 Discipline Pool Counts per Attunement
| Attunement | File | Count |
|---|---|---|
| Enchanter Core | `enchanter.ts` | 4 |
| Enchanter Utility | `enchanter-utility.ts` | 2 |
| Enchanter Spells | `enchanter-spells.ts` | 3 |
| Enchanter Special | `enchanter-special.ts` | 1 |
| Invoker | `invoker.ts` | 2 |
| Fabricator | `fabricator.ts` | 5 |
| **Attunement-gated total** | | **17** |
The remaining 47 disciplines are available regardless of attunement status (base,
elemental, elemental-regen, elemental-regen-advanced pools).
### 6.3 Capability Gating
Each attunement grants `capabilities` that unlock specific game systems:
| Capability | System |
|---|---|
| `enchanting` | Enchantment Design/Prepare/Apply pipeline |
| `pacts` | Guardian pact signing and boon system |
| `guardianPowers` | Guardian power access |
| `elementalMastery` | Element mastery bonuses |
| `golemCrafting` | Golem summoning (Golemancy) |
| `gearCrafting` | Gear fabrication recipes |
| `earthShaping` | Earth mana shaping |
---
## 7. Mana Conversion Interaction
### 7.1 Conversion Flow
Each tick, the mana system:
1. Computes total raw regen (base + attunement regen + discipline bonus + equipment) × temporalEcho × meditationMultiplier
2. Subtracts incursion reduction: `× (1 - incursionStrength)`
3. Computes total conversion drain: sum of all active attunement conversion rates
4. Applies: `rawMana += totalRegen - totalConversionDrain` (per tick)
5. For each attunement with conversion: adds `conversionRate × HOURS_PER_TICK` to the target element
### 7.2 Invoker's Unique Position
The Invoker has **no automatic conversion**`conversionRate = 0`. Instead, it gains
elemental mana types exclusively by signing Guardian pacts. Each guardian's
`unlocksMana` array is resolved through `resolveMultiUnlockChain(element)`, which
unlocks the guardian's element and all base components.
Example: Signing a Metal guardian (floor 90) unlocks `fire`, `earth`, and `metal`.
### 7.3 Conversion and Incursion
Incursion reduces net raw mana regeneration:
```
effectiveRegen = max(0, baseRegen × (1 - incursionStrength) × meditationMult - totalConversionPerTick)
```
As incursion strength approaches 95% (day 30), conversion drains can exceed regen,
causing raw mana to decrease. Since conversion is contingent on available raw mana,
attunement conversion effectively stalls during peak incursion if the raw pool is
insufficient.
---
## 8. Puzzle Room Interaction
From `spire-climbing-spec.md` §4.3, puzzle rooms appear on every 7th floor and have
per-attunement variants:
| Room Type | Description |
|---|---|
| `enchanter_trial` | Enchanter-themed puzzle challenge |
| `fabricator_trial` | Fabricator-themed puzzle challenge |
| `invoker_trial` | Invoker-themed puzzle challenge |
| `hybrid_enchanter_fabricator` | Dual attunement challenge |
| `hybrid_enchanter_invoker` | Dual attunement challenge |
| `hybrid_fabricator_invoker` | Dual attunement challenge |
Progress scales at 1.52% per tick base, with attunement bonus of 2.53% per
relevant attunement level.
---
## 9. State Fields
```typescript
interface AttunementState {
id: string;
active: boolean;
level: number; // 110
experience: number; // current XP toward next level
}
// Initial state (prestige):
attunements: {
enchanter: { id: 'enchanter', active: true, level: 1, experience: 0 }
}
```
---
## 10. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | Enchanter is the only active attunement at game start (level 1, 0 XP). |
| AC-2 | Invoker and Fabricator are locked until unlocked; their unlock conditions are displayed in the Attunements tab. |
| AC-3 | Attunement XP accumulates and triggers level-ups at the correct thresholds; each level requires the exact XP specified in the formula. |
| AC-4 | Regen and conversion rates scale by `1.5^(level-1)` — a level 10 Enchanter converts at 7.69 raw→transference/hour. |
| AC-5 | Both raw regen and conversion from all active attunements are summed and applied each tick. |
| AC-6 | Invoker has no automatic mana conversion at any level. |
| AC-7 | Enchanting awards Enchanter XP at 1 per 10 capacity used (minimum 1). |
| AC-8 | Attunement skill categories correctly gate discipline pool access — Enchanter disciplines require Enchanter to be active. |
| AC-9 | Attunement tab shows unlocked/locked visual distinction, XP progress bar, level badge, and all attunement capabilities. |
| AC-10 | Puzzle rooms on every 7th floor use per-attunement room types with the correct progress scaling. |
| AC-11 | Incursion correctly reduces net raw mana regeneration, potentially stalling conversion at peak incursion. |
---
## 11. Files Reference
| File | Role |
|---|---|
| `src/lib/game/data/attunements.ts` | Attunement definitions (the 3 attunements) |
| `src/lib/game/stores/attunementStore.ts` | Attunement state, leveling, XP, unlock |
| `src/lib/game/types/attunements.ts` | Attunement type definitions |
| `src/components/game/tabs/AttunementsTab.tsx` | Attunement UI display |
| `src/lib/game/stores/manaStore.ts` | Mana regen, conversion, incursion effects |
| `docs/specs/spire-climbing-spec.md` | Puzzle room types per attunement |
@@ -0,0 +1,363 @@
# Enchanter Attunement — Design Spec
> Describes the Enchanter attunement: identity, unlock flow, mana behavior, full
> discipline list with stats/perks, systems unlocked, and attunement level interactions.
---
## 1. Objective
The Enchanter is the starting attunement and the gateway to the enchanting system.
It provides access to Transference-based disciplines that unlock enchantment
effects, boost enchantment power, and provide study/utility bonuses. The Enchanter
is always the first attunement a player uses, and it remains relevant throughout
all stages of the game through its 10 disciplines and the deep enchanting pipeline.
---
## 2. Identity
| Property | Value |
|---|---|
| **ID** | `enchanter` |
| **Slot** | `rightHand` |
| **Icon** | `✨` |
| **Color** | `#1ABC9C` (Teal) |
| **Primary Mana** | `transference` |
| **Raw Mana Regen** | +0.5/hour (base, scales with `1.5^(level-1)`) |
| **Conversion Rate** | 0.2 raw→transference/hour (base, scales with `1.5^(level-1)`) |
| **Unlock** | Starting attunement (unlocked by default) |
| **Capabilities** | `['enchanting']` |
| **Skill Categories** | `['enchant', 'effectResearch']` |
---
## 3. Unlock Condition and Flow
The Enchanter is **always unlocked** — it is present in the initial game state:
```typescript
attunements: {
enchanter: { id: 'enchanter', active: true, level: 1, experience: 0 }
}
```
No unlock flow is required. The player begins the game with Enchanter active.
---
## 4. Raw Mana Regen Contribution
Base regen: **+0.5/hour** (at level 1). Scales exponentially:
```
effectiveRegen = 0.5 × 1.5^(level - 1)
```
| Level | Raw Regen |
|---|---|
| 1 | 0.500/hr |
| 5 | 2.531/hr |
| 10 | 19.221/hr |
---
## 5. Mana Conversion Behavior
The Enchanter is the **only attunement that converts raw mana to Transference**:
```
effectiveConversionRate = 0.2 × 1.5^(level - 1)
```
This is an automatic per-hour conversion. Each tick:
- `0.2 × 1.5^(level-1) × HOURS_PER_TICK` raw mana is consumed
- The same amount is added to the Transference mana pool
At level 10, the Enchanter converts **7.69 raw→transference/hour**.
---
## 6. Disciplines
The Enchanter's discipline pool contains **10 disciplines** across 4 files.
### 6.1 Core Disciplines (`enchanter.ts`) — 4 disciplines
#### Enchantment Crafting (`enchant-crafting`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 8 |
| **Stat Bonus** | `enchantPower` +8 (base) |
| **Scaling Factor** | 60 |
| **Difficulty Factor** | 120 |
| **Drain Base** | 3 |
| Perk ID | Type | Threshold | Bonus |
|---|---|---|---|
| `enchant-1` | `infinite` | 150 | +5 enchantPower per tier (repeats every 150 XP) |
| `enchant-2` | `capped` | 300 | +10 enchantPower per tier, interval 200 XP, max 3 tiers |
#### Mana Channeling (`mana-channeling`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 12 |
| **Stat Bonus** | `clickManaMultiplier` +0.3 (base) |
| **Scaling Factor** | 90 |
| **Difficulty Factor** | 180 |
| **Drain Base** | 5 |
| Perk ID | Type | Threshold | Bonus |
|---|---|---|---|
| `channel-1` | `once` | 250 | `elementCap_lightning` +15 |
#### Study Basic Weapon Enchantments (`study-basic-weapon-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 10 |
| **Stat Bonus** | `enchantPower` +3 (base) |
| **Scaling Factor** | 80 |
| **Difficulty Factor** | 100 |
| **Drain Base** | 2 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `basic-weapon-fire` | `once` | 50 | `sword_fire` |
| `basic-weapon-frost` | `once` | 100 | `sword_frost` |
| `basic-weapon-lightning` | `once` | 150 | `sword_lightning` |
#### Study Advanced Weapon Enchantments (`study-advanced-weapon-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 20 |
| **Requires** | `study-basic-weapon-enchantments` |
| **Stat Bonus** | `enchantPower` +5 (base) |
| **Scaling Factor** | 120 |
| **Difficulty Factor** | 200 |
| **Drain Base** | 4 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `advanced-weapon-void` | `once` | 100 | `sword_void` |
| `advanced-weapon-damage-5` | `once` | 150 | `damage_5` |
| `advanced-weapon-crit` | `once` | 200 | `crit_5` |
| `advanced-weapon-attack-speed` | `once` | 250 | `attack_speed_10` |
### 6.2 Utility Disciplines (`enchanter-utility.ts`) — 2 disciplines
#### Study Utility Enchantments (`study-utility-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 8 |
| **Stat Bonus** | `studySpeed` +0.05 (base) |
| **Scaling Factor** | 60 |
| **Difficulty Factor** | 80 |
| **Drain Base** | 2 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `utility-meditate` | `once` | 50 | `meditate_10` |
| `utility-study` | `once` | 100 | `study_10` |
| `utility-insight` | `once` | 150 | `insight_5` |
#### Study Mana Enchantments (`study-mana-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 15 |
| **Stat Bonus** | `maxManaBonus` +10 (base) |
| **Scaling Factor** | 100 |
| **Difficulty Factor** | 150 |
| **Drain Base** | 3 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `mana-cap-50` | `once` | 75 | `mana_cap_50` |
| `mana-cap-100` | `once` | 150 | `mana_cap_100` |
| `mana-regen-1` | `once` | 100 | `mana_regen_1` |
| `mana-regen-2` | `once` | 200 | `mana_regen_2` |
| `click-mana-1` | `once` | 125 | `click_mana_1` |
| `click-mana-3` | `once` | 225 | `click_mana_3` |
### 6.3 Spell Disciplines (`enchanter-spells.ts`) — 3 disciplines
#### Study Basic Spell Enchantments (`study-basic-spell-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 18 |
| **Stat Bonus** | `enchantPower` +4 (base) |
| **Scaling Factor** | 100 |
| **Difficulty Factor** | 160 |
| **Drain Base** | 3 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `spell-mana-bolt` | `once` | 50 | `spell_manaBolt` |
| `spell-fireball` | `once` | 100 | `spell_fireball` |
| `spell-water-jet` | `once` | 100 | `spell_waterJet` |
| `spell-gust` | `once` | 100 | `spell_gust` |
| `spell-stone-bullet` | `once` | 100 | `spell_stoneBullet` |
| `spell-light-lance` | `once` | 150 | `spell_lightLance` |
| `spell-shadow-bolt` | `once` | 150 | `spell_shadowBolt` |
| `spell-drain` | `once` | 150 | `spell_drain` |
#### Study Intermediate Spell Enchantments (`study-intermediate-spell-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 25 |
| **Requires** | `study-basic-spell-enchantments` |
| **Stat Bonus** | `enchantPower` +6 (base) |
| **Scaling Factor** | 150 |
| **Difficulty Factor** | 250 |
| **Drain Base** | 5 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `spell-inferno` | `once` | 100 | `spell_inferno` |
| `spell-tidal-wave` | `once` | 100 | `spell_tidalWave` |
| `spell-earthquake` | `once` | 120 | `spell_earthquake` |
| `spell-chain-lightning` | `once` | 100 | `spell_chainLightning` |
| `spell-metal-shard` | `once` | 80 | `spell_metalShard` |
| `spell-sand-blast` | `once` | 80 | `spell_sandBlast` |
#### Study Advanced Spell Enchantments (`study-advanced-spell-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 35 |
| **Requires** | `study-intermediate-spell-enchantments` |
| **Stat Bonus** | `enchantPower` +10 (base) |
| **Scaling Factor** | 200 |
| **Difficulty Factor** | 350 |
| **Drain Base** | 7 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `spell-pyroclasm` | `once` | 100 | `spell_pyroclasm` |
| `spell-tsunami` | `once` | 100 | `spell_tsunami` |
| `spell-meteor-strike` | `once` | 120 | `spell_meteorStrike` |
| `spell-heaven-light` | `once` | 100 | `spell_heavenLight` |
| `spell-oblivion` | `once` | 100 | `spell_oblivion` |
| `spell-furnace-blast` | `once` | 100 | `spell_furnaceBlast` |
| `spell-dune-collapse` | `once` | 100 | `spell_duneCollapse` |
| `spell-stellar-nova` | `once` | 200 | `spell_stellarNova` |
| `spell-void-collapse` | `once` | 180 | `spell_voidCollapse` |
| `spell-crystal-shatter` | `once` | 160 | `spell_crystalShatter` |
### 6.4 Special Discipline (`enchanter-special.ts`) — 1 discipline
#### Study Special Enchantments (`study-special-enchantments`)
| Field | Value |
|---|---|
| **Mana Type** | `transference` |
| **Base Cost** | 22 |
| **Requires** | `study-advanced-weapon-enchantments` |
| **Stat Bonus** | `enchantPower` +5 (base) |
| **Scaling Factor** | 130 |
| **Difficulty Factor** | 220 |
| **Drain Base** | 4 |
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `special-spell-echo` | `once` | 100 | `spell_echo_10` |
| `special-guardian-dmg` | `once` | 80 | `guardian_dmg_10` |
| `special-overpower` | `once` | 150 | `overpower_80` |
| `special-first-strike` | `once` | 120 | `first_strike` |
| `special-combo-master` | `once` | 200 | `combo_master` |
| `special-adrenaline-rush` | `once` | 180 | `adrenaline_rush` |
---
## 7. Systems Unlocked
The Enchanter attunement gates the **Enchanting System** (see `enchanting-spec.md`):
- **Design** stage: Create named enchantment designs
- **Prepare** stage: Clear existing enchantments, ready equipment
- **Apply** stage: Apply saved designs to prepared equipment
---
## 8. Puzzle Room Behavior
In the spire, every 7th floor has a puzzle room. When the room type is
`enchanter_trial`, progress scales at 2.53% per tick per Enchanter level.
---
## 9. Attunement Level Interactions
Higher Enchanter level affects:
1. **Raw mana regen**: `0.5 × 1.5^(level-1)` per hour
2. **Transference conversion rate**: `0.2 × 1.5^(level-1)` per hour
3. **Enchanting XP → Attunement XP**: Enchanting awards Enchanter XP (1 per 10 capacity used), feeding back into leveling
Attunement level does **not** directly affect enchantment strength or discipline
power — those scale through discipline XP alone.
---
## 10. Discipline Dependency Chain
```
enchant-crafting (root)
mana-channeling (root)
study-basic-weapon-enchantments (root)
└── study-advanced-weapon-enchantments
└── study-special-enchantments
study-utility-enchantments (root)
study-mana-enchantments (root)
study-basic-spell-enchantments (root)
└── study-intermediate-spell-enchantments
└── study-advanced-spell-enchantments
```
6 root disciplines. Maximum dependency depth: 3.
---
## 11. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | Enchanter starts unlocked at level 1 with 0 XP. |
| AC-2 | All 10 Enchanter disciplines are available when Enchanter is active. |
| AC-3 | Discipline dependency chains are enforced — Advanced Weapon Enchantments requires Basic Weapon Enchantments. |
| AC-4 | All perk thresholds unlock the correct enchantment effects at the specified XP values. |
| AC-5 | Enchantment Power stat bonus from all active Enchanter disciplines stacks additively. |
| AC-6 | The `enchant-1` infinite perk grants +5 enchantPower every 150 XP beyond threshold. |
| AC-7 | The `enchant-2` capped perk grants +10 enchantPower per tier, max 3 tiers, interval 200 XP beyond threshold. |
| AC-8 | Enchanting system is accessible when Enchanter is active, locked when inactive. |
| AC-9 | Enchanter `enchanter_trial` puzzle rooms grant bonus progress per Enchanter level. |
| AC-10 | Enchanter level scales raw regen and conversion rate by `1.5^(level-1)`. |
---
## 12. Files Reference
| File | Role |
|---|---|
| `src/lib/game/data/attunements.ts` | Enchanter definition |
| `src/lib/game/data/disciplines/enchanter.ts` | Core Enchanter disciplines (4) |
| `src/lib/game/data/disciplines/enchanter-utility.ts` | Utility enchantment disciplines (2) |
| `src/lib/game/data/disciplines/enchanter-spells.ts` | Spell enchantment disciplines (3) |
| `src/lib/game/data/disciplines/enchanter-special.ts` | Special enchantment discipline (1) |
| `docs/specs/attunements/enchanter/systems/enchanting-spec.md` | Enchanting system spec |
@@ -0,0 +1,655 @@
# Enchanting System — Design Spec
> Describes the three-stage enchanting pipeline: Design → Prepare → Apply.
> Covers stage timings, mana costs, auto-transitions, enchantment capacity system,
> full enchantment effect categories, disenchanting, and discipline perk interactions.
---
## 1. Objective
Enchanting is the Enchanter attunement's primary system for enhancing equipment. It
transforms raw mana and materials into permanent equipment bonuses through a
three-stage pipeline. The player creates reusable designs, prepares equipment by
stripping existing enchantments, then applies designs to prepared equipment.
**Design goals:**
- Three distinct stages encourage planning and resource management
- Capacity and stacking systems allow deep customization of individual items
- Discipline perks progressively unlock more powerful enchantment types
- Mana costs scale with design complexity, creating meaningful trade-offs
- Auto-transitions keep the pipeline flowing without manual state management
---
## 2. Controls / API
### 2.1 Player Actions
| Action | Stage | Trigger |
|---|---|---|
| **Create Design** | Design | Select effects, name design, click "Create Design" |
| **Start Prepare** | Prepare | Select equipped item, click "Prepare" |
| **Apply Enchantment** | Apply | Select saved design + prepared item, click "Apply" |
| **Disenchant** | Prepare | Initiate prepare on already-enchanted equipment (enchantments removed) |
| **Cancel** | Any | Click "Cancel" during any active stage |
### 2.2 Auto-Transitions
- Design complete → returns to idle (Meditate)
- Prepare complete → returns to idle (Meditate), item gains "Ready for Enchantment" tag
- Apply complete → returns to idle (Meditate), selection state resets
---
## 3. Stage 1: Design
### 3.1 Flow
1. Player selects an equipment type from the type selector
2. Player adds effects from the unlocked pool via the EffectSelector
3. Player sets stack count per effect (up to `maxStacks`)
4. Player names the design
5. Player clicks "Create Design" → design begins
6. `designProgress` accumulates at `HOURS_PER_TICK` per tick
7. When `designProgress >= requiredTime` → design saved to `completedDesigns`
### 3.2 Timing Formula
```
calculateDesignTime(effects):
time = 1 // base 1 hour
for each effect: time += 0.5 * stacks
return time
```
| Design Complexity | Time |
|---|---|
| 1 effect, 1 stack | 1.5 hours |
| 3 effects, 1 stack each | 2.5 hours |
| 2 effects, 3 stacks each | 4.0 hours |
Progress per tick: `HOURS_PER_TICK = 0.04` hours.
### 3.3 Hasty Enchanter (Special Effect)
If the player has the `HASTY_ENCHANTER` special effect and the design is a **repeat**
(re-creating a previously completed design):
```
time *= 0.75 // 25% faster
```
### 3.4 Instant Designs (Special Effect)
Per tick, if the player has the `INSTANT_DESIGNS` special effect:
```typescript
const INSTANT_DESIGN_CHANCE = 0.10; // 10%
if (Math.random() < INSTANT_DESIGN_CHANCE) {
designProgress = requiredTime; // instant completion
}
```
### 3.5 Dual Design Slot
A second concurrent design slot is available when:
- The first design slot has an active design (`designProgress` exists)
- The second slot is empty (`designProgress2 === null`)
- The player has the `ENCHANT_MASTERY` special boolean
### 3.6 Design Mana Cost
**None.** The Design stage has no mana cost.
### 3.7 Design Validation
- `enchantingLevel >= 1` (enchanter attunement must be active)
- Each effect must exist in `ENCHANTMENT_EFFECTS`
- Each effect's `allowedEquipmentCategories` must include the equipment's category
- Stacks cannot exceed the effect's `maxStacks`
### 3.8 Enchanting XP Award
```typescript
calculateEnchantingXP(capacityUsed: number): number {
return Math.max(1, Math.floor(capacityUsed / 10));
}
```
Awarded to Enchanter attunement XP on design completion. This is **Attunement XP**,
not discipline XP.
---
## 4. Stage 2: Prepare
### 4.1 Flow
1. Player selects an equipped item to prepare
2. System checks: `'Ready for Enchantment'` tag required if item was previously prepared
3. If item has existing enchantments, a confirmation dialog warns they will be removed
4. Player confirms → preparation begins
5. Mana is deducted over the prep duration
6. On completion: all enchantments removed, `usedCapacity` reset to 0, rarity reset to `'common'`, `'Ready for Enchantment'` tag added
### 4.2 Timing Formula
```
calculatePrepTime(equipmentCapacity):
time = 2 + floor(equipmentCapacity / 50)
```
| Capacity | Prep Time |
|---|---|
| 15 (shoes) | 2 hours |
| 30 (body) | 2 hours |
| 50 (caster) | 3 hours |
| 80 (robe) | 3 hours |
### 4.3 Mana Cost Formula
```
totalMana = equipmentCapacity × 10
manaPerHour = totalMana / prepTime
manaPerTick = manaPerHour × HOURS_PER_TICK
```
| Capacity | Total Mana Cost |
|---|---|
| 15 | 150 |
| 30 | 300 |
| 50 | 500 |
| 80 | 800 |
### 4.4 Disenchant Recovery
When preparing equipment that has existing enchantments, mana is partially recovered:
```
recoveryRate = 0.10 + disenchantLevel × 0.20
manaRecovered = Σ floor(enchantment.actualCost × recoveryRate)
```
| Disenchant Level | Recovery Rate |
|---|---|
| 0 | 10% |
| 1 | 30% |
| 2 | 50% |
| 3 | 70% |
| 4 | 90% |
| 5 | 110% |
> **Note:** `disenchantLevel` is currently hardcoded to `0` in the codebase, so the
> effective recovery rate is always **10%**.
### 4.5 Cancellation Refund
```
remainingFraction = (required - progress) / required
refundRate = remainingFraction + (1 - remainingFraction) × 0.5
manaRefund = floor(manaSpent × refundRate)
```
Unspent progress gets 100% refund; spent progress gets 50% refund; blended proportionally.
---
## 5. Stage 3: Apply
### 5.1 Flow
1. Player selects a saved design and a prepared equipment instance
2. System validates: `currentAction === 'meditate'`, item has `'Ready for Enchantment'` tag, capacity fits
3. Player clicks "Apply" → application begins
4. Mana is deducted per hour over the application duration
5. On completion: design's effects applied to equipment, `usedCapacity` updated, design consumed
### 5.2 Timing Formula
```
calculateApplicationTime(design):
time = 2 + Σ(stacks) for all effects in design
```
| Design | Apply Time |
|---|---|
| 1 effect, 1 stack | 3 hours |
| 3 effects, 1 stack each | 5 hours |
| 2 effects, 3 stacks each | 8 hours |
### 5.3 Mana Cost Formula
```
manaPerHour = 20 + Σ(stacks × 5) for all effects
manaPerTick = manaPerHour × HOURS_PER_TICK
```
| Design | Mana/Hour |
|---|---|
| 1 effect, 1 stack | 25 |
| 3 effects, 1 stack each | 35 |
| 2 effects, 3 stacks each | 50 |
### 5.4 Free Enchant Chances
Per tick, the system checks for free enchant chances. These are **additive**:
| Special Effect | Chance |
|---|---|
| `ENCHANT_PRESERVATION` | 25% |
| `THRIFTY_ENCHANTER` | 10% |
| `OPTIMIZED_ENCHANTING` | 25% |
| **Maximum combined** | **60%** |
On trigger: `applicationProgress = requiredTime` (instant completion for that tick),
**no mana consumed** for that tick.
### 5.5 Pure Essence (Special Effect)
If the player has the `PURE_ESSENCE` special effect:
```typescript
const PURE_ESSENCE_STACK_BONUS = 1.25;
const PURE_ESSENCE_COST_CAP = 100;
if (effect.baseCapacityCost < PURE_ESSENCE_COST_CAP) {
actualStacks = Math.ceil(baseStacks × PURE_ESSENCE_STACK_BONUS);
}
```
Effects with `baseCapacityCost < 100` get **25% more stacks** (rounded up).
### 5.6 Cancellation Refund
Same formula as Prepare stage (§4.5).
---
## 6. Enchantment Capacity System
### 6.1 Base Capacity Per Equipment Type
| Category | Equipment | Base Capacity |
|---|---|---|
| **Caster** | basicStaff | 50 |
| | apprenticeWand | 35 |
| | oakStaff | 65 |
| | crystalWand | 45 |
| | arcanistStaff | 80 |
| | battlestaff | 70 |
| **Catalyst** | basicCatalyst | 40 |
| | fireCatalyst | 55 |
| | voidCatalyst | 75 |
| **Sword** | ironBlade | 30 |
| | steelBlade | 40 |
| | crystalBlade | 55 |
| | arcanistBlade | 65 |
| | voidBlade | 50 |
| **Head** | clothHood | 25 |
| | apprenticeCap | 30 |
| | wizardHat | 45 |
| | arcanistCirclet | 40 |
| | battleHelm | 50 |
| **Body** | civilianShirt | 30 |
| | apprenticeRobe | 45 |
| | scholarRobe | 55 |
| | battleRobe | 65 |
| | arcanistRobe | 80 |
| **Hands** | civilianGloves | 20 |
| | apprenticeGloves | 30 |
| | spellweaveGloves | 40 |
| | combatGauntlets | 35 |
| **Feet** | civilianShoes | 15 |
| | apprenticeBoots | 25 |
| | travelerBoots | 30 |
| | battleBoots | 35 |
| **Accessory** | copperRing | 15 |
| | silverRing | 25 |
| | goldRing | 35 |
| | signetRing | 30 |
| | copperAmulet | 20 |
| | silverAmulet | 30 |
| | crystalPendant | 45 |
| | manaBrooch | 40 |
| | arcanistPendant | 55 |
| | voidTouchedRing | 50 |
### 6.2 Stacking Cost Formula
```
calculateEffectCapacityCost(effectId, stacks, efficiencyBonus):
totalCost = 0
for i in 0..stacks-1:
stackMultiplier = 1 + (i × 0.2)
totalCost += baseCapacityCost × stackMultiplier
return floor(totalCost × (1 - efficiencyBonus))
```
| Stack Index | Multiplier |
|---|---|
| 0 (1st) | 1.0× |
| 1 (2nd) | 1.2× |
| 2 (3rd) | 1.4× |
| 3 (4th) | 1.6× |
| 4 (5th) | 1.8× |
Example: 3 stacks of a cost-20 effect:
`20×1.0 + 20×1.2 + 20×1.4 = 20 + 24 + 28 = 72` capacity used.
### 6.3 Efficiency Bonus
The `efficiencyBonus` reduces total capacity cost. Sources include discipline perks
(e.g., Crafting Efficiency discipline from Fabricator pool). Applied as:
`totalCost × (1 - efficiencyBonus)`.
---
## 7. Enchantment Effect Categories
### 7.1 Spell Effects (category: `'spell'`) — Casters only
**Basic Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_manaBolt` | Mana Bolt | 50 | 1 |
| `spell_manaStrike` | Mana Strike | 40 | 1 |
| `spell_fireball` | Fireball | 80 | 1 |
| `spell_emberShot` | Ember Shot | 60 | 1 |
| `spell_waterJet` | Water Jet | 70 | 1 |
| `spell_iceShard` | Ice Shard | 75 | 1 |
| `spell_gust` | Gust | 60 | 1 |
| `spell_stoneBullet` | Stone Bullet | 80 | 1 |
| `spell_lightLance` | Light Lance | 95 | 1 |
| `spell_shadowBolt` | Shadow Bolt | 95 | 1 |
| `spell_drain` | Drain | 85 | 1 |
| `spell_rotTouch` | Rot Touch | 80 | 1 |
| `spell_windSlash` | Wind Slash | 72 | 1 |
| `spell_rockSpike` | Rock Spike | 88 | 1 |
| `spell_radiance` | Radiance | 80 | 1 |
| `spell_darkPulse` | Dark Pulse | 68 | 1 |
**Tier 2 Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_inferno` | Inferno | 180 | 1 |
| `spell_tidalWave` | Tidal Wave | 175 | 1 |
| `spell_hurricane` | Hurricane | 170 | 1 |
| `spell_earthquake` | Earthquake | 200 | 1 |
| `spell_solarFlare` | Solar Flare | 190 | 1 |
| `spell_voidRift` | Void Rift | 175 | 1 |
| `spell_flameWave` | Flame Wave | 165 | 1 |
| `spell_iceStorm` | Ice Storm | 170 | 1 |
| `spell_windBlade` | Wind Blade | 155 | 1 |
| `spell_stoneBarrage` | Stone Barrage | 175 | 1 |
| `spell_divineSmite` | Divine Smite | 175 | 1 |
| `spell_shadowStorm` | Shadow Storm | 168 | 1 |
| `spell_soulRend` | Soul Rend | 170 | 1 |
**Tier 3 Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_pyroclasm` | Pyroclasm | 400 | 1 |
| `spell_tsunami` | Tsunami | 380 | 1 |
| `spell_meteorStrike` | Meteor Strike | 420 | 1 |
| `spell_cosmicStorm` | Cosmic Storm | 370 | 1 |
| `spell_heavenLight` | Heaven's Light | 390 | 1 |
| `spell_oblivion` | Oblivion | 385 | 1 |
| `spell_deathMark` | Death Mark | 370 | 1 |
**Legendary Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_stellarNova` | Stellar Nova | 600 | 1 |
| `spell_voidCollapse` | Void Collapse | 550 | 1 |
| `spell_crystalShatter` | Crystal Shatter | 500 | 1 |
**Lightning Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_spark` | Spark | 70 | 1 |
| `spell_lightningBolt` | Lightning Bolt | 90 | 1 |
| `spell_chainLightning` | Chain Lightning | 160 | 1 |
| `spell_stormCall` | Storm Call | 190 | 1 |
| `spell_thunderStrike` | Thunder Strike | 350 | 1 |
**Frost Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_frostBite` | Frost Bite | 78 | 1 |
| `spell_iceShard` | Ice Shard | 95 | 1 |
| `spell_frostNova` | Frost Nova | 165 | 1 |
| `spell_glacialSpike` | Glacial Spike | 200 | 1 |
| `spell_absoluteZero` | Absolute Zero | 380 | 1 |
**Metal Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_metalShard` | Metal Shard | 85 | 1 |
| `spell_ironFist` | Iron Fist | 120 | 1 |
| `spell_steelTempest` | Steel Tempest | 190 | 1 |
| `spell_furnaceBlast` | Furnace Blast | 400 | 1 |
**Sand Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_sandBlast` | Sand Blast | 72 | 1 |
| `spell_sandstorm` | Sandstorm | 100 | 1 |
| `spell_desertWind` | Desert Wind | 155 | 1 |
| `spell_duneCollapse` | Dune Collapse | 300 | 1 |
**BlackFlame Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_blackFire` | Black Fire | 82 | 1 |
| `spell_shadowEmber` | Shadow Ember | 105 | 1 |
| `spell_darkInferno` | Dark Inferno | 175 | 1 |
| `spell_umbralBlaze` | Umbral Blaze | 210 | 1 |
| `spell_hellfireCurse` | Hellfire Curse | 410 | 1 |
**Radiant Flames Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_radiantBurst` | Radiant Burst | 85 | 1 |
| `spell_holyFlame` | Holy Flame | 108 | 1 |
| `spell_blindingSun` | Blinding Sun | 180 | 1 |
| `spell_purifyingFire` | Purifying Fire | 215 | 1 |
| `spell_supernovaBlast` | Supernova Blast | 420 | 1 |
**Miasma Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_toxicCloud` | Toxic Cloud | 76 | 1 |
| `spell_plagueTouch` | Plague Touch | 100 | 1 |
| `spell_miasmaBurst` | Miasma Burst | 165 | 1 |
| `spell_pestilence` | Pestilence | 195 | 1 |
| `spell_deathMiasma` | Death Miasma | 390 | 1 |
**Shadow Glass Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_shadowSpike` | Shadow Spike | 88 | 1 |
| `spell_darkShard` | Dark Shard | 115 | 1 |
| `spell_obsidianStorm` | Obsidian Storm | 185 | 1 |
| `spell_voidBlade` | Void Blade | 225 | 1 |
| `spell_shadowGlassCataclysm` | Shadow Glass Cataclysm | 415 | 1 |
**Exotic Spells:**
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `spell_soulPierce` | Soul Pierce | 500 | 1 |
| `spell_spiritBlast` | Spirit Blast | 650 | 1 |
| `spell_temporalWarp` | Temporal Warp | 520 | 1 |
| `spell_chronoStasis` | Chrono Stasis | 680 | 1 |
| `spell_plasmaBolt` | Plasma Bolt | 510 | 1 |
| `spell_plasmaStorm` | Plasma Storm | 660 | 1 |
### 7.2 Mana Effects (category: `'mana'`)
**General Mana** — Allowed on: `['caster', 'catalyst', 'head', 'body', 'accessory']`
| Effect ID | Name | Description | Base Cost | Max Stacks |
|---|---|---|---|---|
| `mana_cap_50` | Mana Reserve | +50 max mana | 20 | 3 |
| `mana_cap_100` | Mana Reservoir | +100 max mana | 35 | 3 |
| `mana_regen_1` | Trickle | +1 mana/hour regen | 15 | 5 |
| `mana_regen_2` | Stream | +2 mana/hour regen | 28 | 4 |
| `mana_regen_5` | River | +5 mana/hour regen | 50 | 3 |
| `click_mana_1` | Mana Tap | +1 mana per click | 20 | 5 |
| `click_mana_3` | Mana Surge | +3 mana per click | 35 | 3 |
**Weapon Mana** — Allowed on: `['caster', 'catalyst', 'sword']`
| Effect ID | Name | Base Cost | Max Stacks |
|---|---|---|---|
| `weapon_mana_cap_20` | Mana Cell | 25 | 5 |
| `weapon_mana_cap_50` | Mana Vessel | 50 | 3 |
| `weapon_mana_cap_100` | Mana Core | 80 | 2 |
| `weapon_mana_regen_1` | Mana Wick | 20 | 5 |
| `weapon_mana_regen_2` | Mana Siphon | 35 | 3 |
| `weapon_mana_regen_5` | Mana Well | 60 | 2 |
**Per-Element Capacity** — Allowed on: `['caster', 'catalyst', 'head', 'body', 'accessory']`
Generated for each non-utility element (21 elements). Three tiers per element:
- `{element}_cap_10`: cost 30, max 5 stacks
- `{element}_cap_25`: cost 60, max 3 stacks
- `{element}_cap_50`: cost 100, max 2 stacks
### 7.3 Combat Effects (category: `'combat'`) — Casters, Hands
| Effect ID | Name | Description | Base Cost | Max Stacks |
|---|---|---|---|---|
| `damage_5` | Minor Power | +5 base damage | 15 | 5 |
| `damage_10` | Moderate Power | +10 base damage | 28 | 4 |
| `damage_pct_10` | Amplification | +10% damage | 30 | 3 |
| `crit_5` | Sharp Edge | +5% crit chance | 20 | 4 |
| `attack_speed_10` | Swift Casting | +10% attack speed | 22 | 4 |
### 7.4 Elemental Effects (category: `'elemental'`) — Casters, Swords
| Effect ID | Name | Description | Base Cost | Max Stacks |
|---|---|---|---|---|
| `sword_fire` | Fire Enchant | Burns enemies | 40 | 1 |
| `sword_frost` | Frost Enchant | Prevents dodge | 40 | 1 |
| `sword_lightning` | Lightning Enchant | 30% armor pierce | 50 | 1 |
| `sword_void` | Void Enchant | +20% damage | 60 | 1 |
### 7.5 Utility Effects (category: `'utility'`)
| Effect ID | Name | Base Cost | Max Stacks | Allowed On |
|---|---|---|---|---|
| `meditate_10` | Meditative Focus | 18 | 5 | head, body, accessory |
| `study_10` | Quick Study | 22 | 4 | caster, catalyst, head, body, hands, feet, accessory |
| `insight_5` | Insightful | 25 | 4 | head, accessory |
### 7.6 Special Effects (category: `'special'`)
| Effect ID | Name | Base Cost | Max Stacks | Allowed On |
|---|---|---|---|---|
| `spell_echo_10` | Echo Chamber | 60 | 2 | caster |
| `guardian_dmg_10` | Bane | 35 | 3 | caster, catalyst, accessory |
| `overpower_80` | Overpower | 55 | 1 | caster, hands |
| `first_strike` | First Strike | 45 | 1 | caster, hands |
| `combo_master` | Combo Master | 65 | 1 | caster, hands |
| `adrenaline_rush` | Adrenaline Rush | 50 | 1 | caster, hands |
### 7.7 Defense Effects (category: `'defense'`)
**Empty** — No defense effects are currently defined.
---
## 8. Discipline Perks That Affect Enchanting
| Discipline | Perk | Threshold | Effect |
|---|---|---|---|
| Enchantment Crafting | `enchant-1` (infinite) | 150 XP | +5 enchantPower per tier |
| Enchantment Crafting | `enchant-2` (capped) | 300 XP | +10 enchantPower/tier, max 3 |
| Study Basic Weapon Enchantments | `basic-weapon-fire` | 50 XP | Unlocks `sword_fire` |
| Study Basic Weapon Enchantments | `basic-weapon-frost` | 100 XP | Unlocks `sword_frost` |
| Study Basic Weapon Enchantments | `basic-weapon-lightning` | 150 XP | Unlocks `sword_lightning` |
| Study Advanced Weapon Enchantments | `advanced-weapon-void` | 100 XP | Unlocks `sword_void` |
| Study Advanced Weapon Enchantments | `advanced-weapon-damage-5` | 150 XP | Unlocks `damage_5` |
| Study Advanced Weapon Enchantments | `advanced-weapon-crit` | 200 XP | Unlocks `crit_5` |
| Study Advanced Weapon Enchantments | `advanced-weapon-attack-speed` | 250 XP | Unlocks `attack_speed_10` |
| Study Utility Enchantments | `utility-meditate` | 50 XP | Unlocks `meditate_10` |
| Study Utility Enchantments | `utility-study` | 100 XP | Unlocks `study_10` |
| Study Utility Enchantments | `utility-insight` | 150 XP | Unlocks `insight_5` |
| Study Mana Enchantments | `mana-cap-50` | 75 XP | Unlocks `mana_cap_50` |
| Study Mana Enchantments | `mana-cap-100` | 150 XP | Unlocks `mana_cap_100` |
| Study Mana Enchantments | `mana-regen-1` | 100 XP | Unlocks `mana_regen_1` |
| Study Mana Enchantments | `mana-regen-2` | 200 XP | Unlocks `mana_regen_2` |
| Study Mana Enchantments | `click-mana-1` | 125 XP | Unlocks `click_mana_1` |
| Study Mana Enchantments | `click-mana-3` | 225 XP | Unlocks `click_mana_3` |
| Study Basic Spell Enchantments | 8 perks | 50150 XP | Unlock 8 basic spell enchants |
| Study Intermediate Spell Enchantments | 6 perks | 80120 XP | Unlock 6 intermediate spell enchants |
| Study Advanced Spell Enchantments | 10 perks | 100200 XP | Unlock 10 advanced spell enchants |
| Study Special Enchantments | 6 perks | 80200 XP | Unlock 6 special enchants |
---
## 9. Attunement Level Interactions
Enchanter level does **not** directly affect enchanting mechanics (timings, costs,
capacity). It affects:
1. **Raw mana regen**: `0.5 × 1.5^(level-1)` per hour — more raw mana for enchanting
2. **Transference conversion**: `0.2 × 1.5^(level-1)` per hour — more transference mana for Enchanter disciplines
3. **Enchanting XP → Attunement XP**: 1 Enchanter XP per 10 capacity used
---
## 10. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | Design stage takes `1 + 0.5 × totalStacks` hours; progress accumulates at 0.04 hours/tick. |
| AC-2 | Hasty Enchanter reduces design time by 25% on repeat designs only. |
| AC-3 | Instant Designs has a 10% chance per tick to complete the design immediately. |
| AC-4 | Dual design slot is available when Enchant Mastery is active and first slot is occupied. |
| AC-5 | Prepare stage takes `2 + floor(capacity/50)` hours and costs `capacity × 10` total mana. |
| AC-6 | Prepare removes all enchantments, resets usedCapacity to 0, resets rarity to 'common'. |
| AC-7 | Disenchant recovery rate is `0.10 + disenchantLevel × 0.20` of each enchantment's actual cost. |
| AC-8 | Apply stage takes `2 + totalStacks` hours and costs `20 + sum(stacks × 5)` mana/hour. |
| AC-9 | Free enchant chances are additive (max 60%) and skip mana cost for that tick. |
| AC-10 | Pure Essence grants 1.25× stacks (ceil) for effects with base cost < 100. |
| AC-11 | Stacking cost formula: `baseCost × (1 + i × 0.2)` for stack index i, reduced by efficiencyBonus. |
| AC-12 | Cancellation refunds unspent progress at 100% and spent progress at 50%, blended. |
| AC-13 | All enchantment effects are gated behind discipline perk thresholds and cannot be used until unlocked. |
| AC-14 | Equipment type capacity limits are enforced — designs exceeding capacity are rejected. |
| AC-15 | Spell effects can only be applied to caster equipment. |
---
## 11. Files Reference
| File | Role |
|---|---|
| `src/lib/game/crafting-design.ts` | Design stage logic, timing, validation |
| `src/lib/game/crafting-prep.ts` | Prepare stage logic, disenchant recovery |
| `src/lib/game/crafting-apply.ts` | Apply stage logic, free enchant, Pure Essence |
| `src/lib/game/crafting-utils.ts` | Shared utilities, capacity cost, cancellation refund |
| `src/lib/game/crafting-attunements.ts` | Attunement-crafting integration, enchanting XP |
| `src/lib/game/data/enchantments/` | All enchantment effect definitions (7 categories) |
| `src/lib/game/crafting-actions/design-actions.ts` | Design stage store actions |
| `src/lib/game/crafting-actions/preparation-actions.ts` | Prepare stage store actions |
| `src/lib/game/crafting-actions/application-actions.ts` | Apply stage store actions |
| `src/lib/game/crafting-actions/disenchant-actions.ts` | Disenchant action |
| `src/components/game/tabs/CraftingTab.tsx` | Crafting tab wrapper |
| `src/components/game/crafting/EnchantmentDesigner.tsx` | Design UI |
| `src/components/game/crafting/EnchantmentPreparer.tsx` | Prepare UI |
| `src/components/game/crafting/EnchantmentApplier.tsx` | Apply UI |
@@ -0,0 +1,264 @@
# Fabricator Attunement — Design Spec
> Describes the Fabricator attunement: identity, unlock flow, mana behavior, full
> discipline list with stats/perks, systems unlocked, and attunement level interactions.
---
## 1. Objective
The Fabricator is the crafting and golemancy attunement. It provides access to
Earth-based disciplines that unlock equipment fabrication recipes, golem summoning,
and crafting cost reduction. The Fabricator is the primary source of custom
equipment and the golem combat system.
---
## 2. Identity
| Property | Value |
|---|---|
| **ID** | `fabricator` |
| **Slot** | `leftHand` |
| **Icon** | `⚒️` |
| **Color** | `#F4A261` (Earth) |
| **Primary Mana** | `earth` |
| **Raw Mana Regen** | +0.4/hour (base, scales with `1.5^(level-1)`) |
| **Conversion Rate** | 0.25 raw→earth/hour (base, scales with `1.5^(level-1)`) |
| **Unlock** | Prove crafting worth |
| **Capabilities** | `['golemCrafting', 'gearCrafting', 'earthShaping']` |
| **Skill Categories** | `['fabrication', 'golemancy']` |
---
## 3. Unlock Condition and Flow
**Condition:** Prove your worth as a crafter.
**Unlock flow:**
1. Meet the crafting-related unlock condition
2. Fabricator becomes available for activation
3. Player activates Fabricator → initialized at `{ active: true, level: 1, experience: 0 }`
4. Fabricator disciplines become available (5 total)
The unlock condition is stored as a descriptive string:
`"Prove your worth as a crafter"`
---
## 4. Raw Mana Regen Contribution
Base regen: **+0.4/hour** (at level 1). Scales exponentially:
```
effectiveRegen = 0.4 × 1.5^(level - 1)
```
| Level | Raw Regen |
|---|---|
| 1 | 0.400/hr |
| 5 | 2.025/hr |
| 10 | 15.377/hr |
---
## 5. Mana Conversion Behavior
The Fabricator converts raw mana to Earth:
```
effectiveConversionRate = 0.25 × 1.5^(level - 1)
```
At level 10, the Fabricator converts **9.61 raw→earth/hour**.
---
## 6. Disciplines
The Fabricator's discipline pool contains **5 disciplines**.
### 6.1 Golem Crafting (`golem-crafting`)
| Field | Value |
|---|---|
| **Mana Type** | `earth` |
| **Base Cost** | 10 |
| **Stat Bonus** | `golemCapacity` +2 (base) |
| **Scaling Factor** | 80 |
| **Difficulty Factor** | 150 |
| **Drain Base** | 4 |
**Perks:**
| Perk ID | Type | Threshold | Bonus |
|---|---|---|---|
| `golem-1` | `once` | 200 | Unlock golem summoning |
| `golem-2` | `capped` | 500 | +1 Golem Capacity per tier, interval 500 XP, max 2 tiers |
### 6.2 Crafting Efficiency (`crafting-efficiency`)
| Field | Value |
|---|---|
| **Mana Type** | `earth` |
| **Base Cost** | 12 |
| **Stat Bonus** | `craftingCostReduction` +15 (base) |
| **Scaling Factor** | 90 |
| **Difficulty Factor** | 180 |
| **Drain Base** | 6 |
**Perks:**
| Perk ID | Type | Threshold | Bonus |
|---|---|---|---|
| `efficiency-1` | `once` | 300 | +10% Crafting Cost Reduction |
### 6.3 Study Fabricator Recipes (`study-fabricator-recipes`)
| Field | Value |
|---|---|
| **Mana Type** | `earth` |
| **Base Cost** | 10 |
| **Stat Bonus** | `enchantPower` +3 (base) |
| **Scaling Factor** | 80 |
| **Difficulty Factor** | 100 |
| **Drain Base** | 2 |
**Perks:**
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `fabricator-earth` | `once` | 50 | `earthHelm`, `earthChest`, `earthBoots` |
| `fabricator-metal` | `once` | 100 | `metalBlade`, `metalShield`, `metalGloves` |
| `fabricator-sand` | `once` | 150 | `sandBoots`, `sandGloves`, `sandVest` |
| `fabricator-crystal` | `once` | 200 | `crystalWand`, `crystalRing`, `crystalAmulet` |
### 6.4 Study Wizard Equipment (`study-wizard-branch`)
| Field | Value |
|---|---|
| **Mana Type** | `earth` |
| **Base Cost** | 15 |
| **Requires** | `study-fabricator-recipes` |
| **Stat Bonus** | `enchantPower` +5 (base) |
| **Scaling Factor** | 100 |
| **Difficulty Factor** | 150 |
| **Drain Base** | 3 |
**Perks:**
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `wizard-oak` | `once` | 50 | `oakStaff` |
| `wizard-arcanist-staff` | `once` | 100 | `arcanistStaff` |
| `wizard-battlestaff` | `once` | 150 | `battlestaff` |
| `wizard-arcanist-gear` | `once` | 200 | `arcanistCirclet`, `arcanistRobe` |
| `wizard-void-catalyst` | `once` | 250 | `voidCatalyst` |
| `wizard-arcanist-pendant` | `once` | 300 | `arcanistPendant` |
### 6.5 Study Physical Equipment (`study-physical-branch`)
| Field | Value |
|---|---|
| **Mana Type** | `earth` |
| **Base Cost** | 15 |
| **Requires** | `study-fabricator-recipes` |
| **Stat Bonus** | `enchantPower` +5 (base) |
| **Scaling Factor** | 100 |
| **Difficulty Factor** | 150 |
| **Drain Base** | 3 |
**Perks:**
| Perk ID | Type | Threshold | Unlocks |
|---|---|---|---|
| `physical-crystal-blade` | `once` | 50 | `crystalBlade` |
| `physical-arcanist-blade` | `once` | 100 | `arcanistBlade` |
| `physical-void-blade` | `once` | 150 | `voidBlade` |
| `physical-battle-gear` | `once` | 200 | `battleHelm`, `battleRobe` |
| `physical-battle-boots` | `once` | 250 | `battleBoots` |
| `physical-combat-gauntlets` | `once` | 300 | `combatGauntlets` |
---
## 7. Systems Unlocked
The Fabricator attunement gates two systems:
1. **Golemancy** (see `golemancy-spec.md`): Summon and maintain golems for spire combat
2. **Item Fabrication** (see `item-fabrication-spec.md`): Craft equipment and materials from recipes
---
## 8. Puzzle Room Behavior
In the spire, every 7th floor has a puzzle room. When the room type is
`fabricator_trial`, progress scales at 2.53% per tick per Fabricator level.
---
## 9. Attunement Level Interactions
Higher Fabricator level affects:
1. **Raw mana regen**: `0.4 × 1.5^(level-1)` per hour
2. **Earth conversion rate**: `0.25 × 1.5^(level-1)` per hour
3. **Golem slots**: `floor(fabricatorLevel / 2)` — Fabricator level directly determines golem capacity
| Fabricator Level | Golem Slots |
|---|---|
| 1 | 0 |
| 23 | 1 |
| 45 | 2 |
| 67 | 3 |
| 89 | 4 |
| 10 | 5 |
---
## 10. Discipline Dependency Chain
```
golem-crafting (root)
crafting-efficiency (root)
study-fabricator-recipes (root)
└── study-wizard-branch
└── study-physical-branch
```
3 root disciplines. Maximum dependency depth: 2.
---
## 11. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | Fabricator is locked until the unlock condition is met. |
| AC-2 | All 5 Fabricator disciplines are available when Fabricator is active. |
| AC-3 | `study-wizard-branch` and `study-physical-branch` require `study-fabricator-recipes`. |
| AC-4 | Golem summoning is unlocked at Golem Crafting discipline threshold 200 XP. |
| AC-5 | Golem capacity is 2 (base) + up to 2 (from capped perk) = max 4 from disciplines. |
| AC-6 | Golem slots from attunement level: `floor(fabricatorLevel / 2)`, max 5 at level 10. |
| AC-7 | All recipe unlock perks fire at the correct discipline XP thresholds. |
| AC-8 | Crafting Efficiency discipline reduces material costs by 15% (base) + 10% (perk). |
| AC-9 | Fabricator `fabricator_trial` puzzle rooms grant bonus progress per Fabricator level. |
| AC-10 | Fabricator level scales raw regen and earth conversion by `1.5^(level-1)`. |
---
## 12. Files Reference
| File | Role |
|---|---|
| `src/lib/game/data/attunements.ts` | Fabricator definition |
| `src/lib/game/data/disciplines/fabricator.ts` | Fabricator disciplines (5) |
| `src/lib/game/data/golems/` | Golem definitions (10 golems) |
| `src/lib/game/crafting-fabricator.ts` | Fabrication crafting logic |
| `src/lib/game/data/fabricator-recipes.ts` | Core equipment recipes |
| `src/lib/game/data/fabricator-material-recipes.ts` | Material recipes |
| `src/lib/game/data/fabricator-physical-recipes.ts` | Physical branch recipes |
| `src/lib/game/data/fabricator-wizard-recipes.ts` | Wizard branch recipes |
| `src/components/game/tabs/GolemancyTab.tsx` | Golemancy UI |
| `docs/specs/attunements/fabricator/systems/golemancy-spec.md` | Golemancy system spec |
| `docs/specs/attunements/fabricator/systems/item-fabrication-spec.md` | Item fabrication spec |
@@ -0,0 +1,333 @@
# Golemancy System — Design Spec
> Describes the Fabricator attunement's combat system: golem types, loadout
> configuration, summoning lifecycle, maintenance costs, room duration, combat
> behavior, and discipline interactions.
>
> **⚠ Spec-defined, implementation pending.** This spec is based on
> `docs/specs/spire-combat-spec.md` §9 and represents the intended design.
> The current code has golem data defined but disconnected from the combat pipeline.
---
## 1. Objective
Golemancy is the Fabricator attunement's combat contribution. The player configures
a golem loadout outside the spire, then golems are automatically summoned at each
room entry, fight alongside the player, and disappear after a fixed number of rooms
or if their maintenance cost cannot be met.
**Design goals:**
- Golems provide parallel combat damage independent of the player's spells
- Different golem types offer tactical variety (single-target, AoE, fast, tanky)
- Maintenance cost and room duration create resource management decisions
- Hybrid golems require dual-attunement investment (Enchanter 5 + Fabricator 5)
- Golem loadout configuration outside spire allows strategic planning
---
## 2. Golem Slot Formula
Golem slots come from **two sources** that add together:
### 2.1 From Attunement Level
```
attunementSlots = floor(fabricatorLevel / 2)
```
| Fabricator Level | Slots |
|---|---|
| 1 | 0 |
| 23 | 1 |
| 45 | 2 |
| 67 | 3 |
| 89 | 4 |
| 10 | 5 |
### 2.2 From Discipline
The Golem Crafting discipline provides:
- Base `golemCapacity`: +2
- Perk `golem-2` (capped, threshold 500, maxTier 2): +1 per tier = up to +2
**Maximum total golem slots: 5 (attunement) + 2 (discipline) = 7**
> **Note:** The AGENTS.md states `floor(fabricatorLevel / 2)` with max 5 at level 10.
> The discipline-based capacity is additive on top of this.
---
## 3. Golem Loadout Configuration
The player configures a **golem loadout** from the Golemancy tab before entering
the spire. The loadout defines which golems to attempt to summon and in what order.
This configuration persists across rooms but not across spire runs.
The loadout is a prioritized list of golem IDs. On each room entry, the system
iterates the loadout in order, attempting to summon each golem.
---
## 4. All 10 Golem Types
### 4.1 Base Golems (1)
| Field | Earth Golem |
|---|---|
| **ID** | `earthGolem` |
| **Tier** | 1 |
| **Element** | Earth |
| **Damage** | 8 |
| **Attack Speed** | 1.5/hr |
| **HP** (display) | 50 |
| **Armor Pierce** | 15% |
| **AoE** | No |
| **Max Room Duration** | 3 |
| **Summon Cost** | 10 earth |
| **Maintenance Cost** | 0.5 earth/hr |
| **Unlock** | Fabricator level 2 |
### 4.2 Elemental Golems (3)
| Field | Steel Golem | Crystal Golem | Sand Golem |
|---|---|---|---|
| **ID** | `steelGolem` | `crystalGolem` | `sandGolem` |
| **Tier** | 2 | 3 | 2 |
| **Element** | Metal | Crystal | Sand |
| **Damage** | 12 | 18 | 10 |
| **Attack Speed** | 1.2/hr | 1.0/hr | 2.0/hr |
| **HP** (display) | 60 | 40 | 45 |
| **Armor Pierce** | 35% | 25% | 15% |
| **AoE** | No | No | **Yes (2 targets)** |
| **Max Room Duration** | 3 | 4 | 3 |
| **Summon Cost** | 8 metal + 5 earth | 6 crystal + 3 earth | 10 sand + 4 earth |
| **Maintenance Cost** | 0.6 metal + 0.2 earth/hr | 0.4 crystal + 0.2 earth/hr | 0.6 sand + 0.25 earth/hr |
| **Unlock** | Metal mana unlocked | Crystal mana unlocked | Sand mana unlocked |
### 4.3 Hybrid Golems (6) — Require Enchanter 5 + Fabricator 5
| Field | Lava Golem | Galvanic Golem | Obsidian Golem |
|---|---|---|---|
| **ID** | `lavaGolem` | `galvanicGolem` | `obsidianGolem` |
| **Tier** | 3 | 3 | 4 |
| **Elements** | Earth + Fire | Metal + Lightning | Earth + Dark |
| **Damage** | 15 | 10 | 25 |
| **Attack Speed** | 1.0/hr | 3.5/hr | 0.8/hr |
| **HP** (display) | 70 | 45 | 55 |
| **Armor Pierce** | 20% | 45% | 50% |
| **AoE** | **Yes (2 targets)** | No | No |
| **Max Room Duration** | 4 | 4 | 5 |
| **Summon Cost** | 15 earth + 12 fire | 12 metal + 8 lightning | 18 earth + 10 dark |
| **Maintenance Cost** | 0.6 earth + 0.7 fire/hr | 0.4 metal + 0.7 lightning/hr | 0.5 earth + 0.6 dark/hr |
| **Special** | Burn DoT | Lightning Speed | Devastating Strike |
| **Unlock** | Enchanter 5 + Fabricator 5 | Enchanter 5 + Fabricator 5 | Enchanter 5 + Fabricator 5 |
| Field | Prism Golem | Quicksilver Golem | Voidstone Golem |
|---|---|---|---|
| **ID** | `prismGolem` | `quicksilverGolem` | `voidstoneGolem` |
| **Tier** | 4 | 3 | 4 |
| **Elements** | Crystal + Light | Metal + Water | Earth + Void |
| **Damage** | 28 | 14 | **40** |
| **Attack Speed** | 2.0/hr | **4.0/hr** | 0.6/hr |
| **HP** (display) | 60 | 55 | **100** |
| **Armor Pierce** | 45% | 35% | **60%** |
| **AoE** | **Yes (3 targets)** | No | **Yes (3 targets)** |
| **Max Room Duration** | 5 | 4 | 5 |
| **Summon Cost** | 16 crystal + 10 light | 10 metal + 8 water | 22 earth + 14 void |
| **Maintenance Cost** | 0.6 crystal + 0.6 light/hr | 0.4 metal + 0.4 water/hr | 0.5 earth + 0.9 void/hr |
| **Special** | Piercing Beams | Flow (evasion) | Void Infusion |
| **Unlock** | Enchanter 5 + Fabricator 5 | Enchanter 5 + Fabricator 5 | Enchanter 5 + Fabricator 5 |
### 4.4 Summary Table
| Golem | Tier | DMG | SPD | HP | Pierce | AoE | Targets | Rooms | Unlock |
|---|---|---|---|---|---|---|---|---|---|
| Earth | 1 | 8 | 1.5 | 50 | 15% | No | 1 | 3 | Fabricator Lv2 |
| Steel | 2 | 12 | 1.2 | 60 | 35% | No | 1 | 3 | Metal mana |
| Crystal | 3 | 18 | 1.0 | 40 | 25% | No | 1 | 4 | Crystal mana |
| Sand | 2 | 10 | 2.0 | 45 | 15% | Yes | 2 | 3 | Sand mana |
| Lava | 3 | 15 | 1.0 | 70 | 20% | Yes | 2 | 4 | Ench5+Fab5 |
| Galvanic | 3 | 10 | 3.5 | 45 | 45% | No | 1 | 4 | Ench5+Fab5 |
| Obsidian | 4 | 25 | 0.8 | 55 | 50% | No | 1 | 5 | Ench5+Fab5 |
| Prism | 4 | 28 | 2.0 | 60 | 45% | Yes | 3 | 5 | Ench5+Fab5 |
| Quicksilver | 3 | 14 | 4.0 | 55 | 35% | No | 1 | 4 | Ench5+Fab5 |
| Voidstone | 4 | 40 | 0.6 | 100 | 60% | Yes | 3 | 5 | Ench5+Fab5 |
---
## 5. Summoning on Room Entry
When the player enters a new combat room:
```
onRoomEntry():
for each golem in golemLoadout:
if player has enough mana of golem.summonCostType >= golem.summonCost:
deductMana(golem.summonCost, golem.summonCostType)
activeGolems.push({
...golemDef,
roomsRemaining: golemDef.maxRoomDuration,
attackProgress: 0,
})
activityLog("${golem.name} summoned")
else:
activityLog("Not enough mana to summon ${golem.name} — skipped")
```
**Key rules:**
- Golems that cannot be summoned (insufficient mana) are **not re-attempted** within the same room
- Failed golems will be attempted again on the next room entry
- Summoning order follows the loadout priority list
---
## 6. Golem Combat
Each active golem attacks on its own `attackProgress` timer, identical to swords:
```
golemProgress += HOURS_PER_TICK × golem.attackSpeed
while golemProgress >= 1:
dmg = golem.baseDamage
if golem.element:
dmg ×= getElementalBonus(golem.element, enemy.element)
applyGolemEffects(golem, dmg, enemy)
applyDamageToRoom(dmg)
golemProgress -= 1
```
**Key rules:**
- Golems ignore Executioner and Berserker discipline specials
- AoE golems distribute damage across multiple targets
- Elemental matchup applies if the golem has an element
---
## 7. Maintenance Cost
Each tick, each active golem checks its maintenance cost:
```
tickGolemMaintenance(golem):
if player mana[golem.maintenanceCostType] >= golem.maintenanceCost × HOURS_PER_TICK:
deductMana(golem.maintenanceCost × HOURS_PER_TICK, golem.maintenanceCostType)
else:
dismiss(golem)
activityLog("${golem.name} dismissed — insufficient ${golem.maintenanceCostType} mana")
```
**Key rules:**
- A dismissed golem is **not re-summoned mid-room**
- It will be re-attempted on the next room entry if mana has recovered
- Maintenance is checked every tick, not just on room transitions
---
## 8. Room Duration Limit
```
onRoomCleared():
for each activeGolem:
activeGolem.roomsRemaining -= 1
if activeGolem.roomsRemaining <= 0:
dismiss(golem)
activityLog("${golem.name} has faded after ${maxRoomDuration} rooms")
```
**Key rules:**
- Room duration ticks down on room **clear**, not on room **entry**
- Golems persist through the full room they were summoned in
- When `roomsRemaining` reaches 0, the golem is dismissed
---
## 9. Golem Data Shape
```typescript
interface GolemDefinition {
id: string;
name: string;
tier: number; // 14 (determines general power)
baseDamage: number;
attackSpeed: number; // attacks per in-game hour
element?: ElementType; // optional elemental type for matchup
maxRoomDuration: number; // rooms before disappearing
summonCost: number;
summonCostType: ElementType | 'raw';
maintenanceCost: number; // per in-game hour
maintenanceCostType: ElementType | 'raw';
onHitEffect?: GolemHitEffect; // DoT, AoE, etc.
armorPierce?: number; // 0-1, bypasses this fraction of enemy armor
aoe?: boolean;
aoeTargets?: number;
}
```
---
## 10. Discipline Interactions
### 10.1 Golem Crafting Discipline
| Perk | Effect |
|---|---|
| `golem-1` (once @ 200 XP) | Unlocks golem summoning ability |
| `golem-2` (capped @ 500, maxTier 2) | +1 Golem Capacity per tier (max +2) |
### 10.2 Fabricator Level
Directly determines base golem slots: `floor(fabricatorLevel / 2)`.
### 10.3 Dual Attunement Requirement
All 6 hybrid golems require **Enchanter 5 + Fabricator 5**. This means the player
must have both attunements active and leveled to at least 5 to access the most
powerful golem types.
---
## 11. Known Gaps / Implementation Status
| Feature | Status |
|---|---|
| Golem data definitions | ✅ Complete (10 golems in `data/golems/`) |
| Golem loadout UI | ✅ Partial (GolemancyTab exists) |
| Summoning on room entry | ❌ Not wired into combat tick |
| Maintenance cost per tick | ❌ Not wired into combat tick |
| Room duration tracking | ❌ Not wired into room clear |
| Golem combat (attack timer) | ❌ Not wired into combat tick |
| Golemancy combat pipeline | ❌ `golem-combat-actions.ts` exists but disconnected |
---
## 12. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | Golem slots = `floor(fabricatorLevel / 2)` + discipline bonus. |
| AC-2 | Golems are summoned on room entry if mana allows; failed summons are skipped for that room. |
| AC-3 | Each golem attacks on its own timer using its `attackSpeed` stat. |
| AC-4 | Elemental matchup applies to golem attacks when the golem has an element. |
| AC-5 | AoE golems distribute damage across `aoeTargets` enemies. |
| AC-6 | Maintenance cost is deducted each tick; golems dismiss if cost cannot be met. |
| AC-7 | Dismissed golems are not re-summoned mid-room. |
| AC-8 | Room duration ticks down on room clear, not entry. |
| AC-9 | Golems disappear after `maxRoomDuration` rooms. |
| AC-10 | Hybrid golems require Enchanter 5 + Fabricator 5. |
| AC-11 | Golem loadout is configured outside the spire and persists across rooms. |
| AC-12 | Golem HP is display-only; golems don't take damage from enemies. |
---
## 13. Files Reference
| File | Role |
|---|---|
| `src/lib/game/data/golems/golems-data.ts` | All 10 golem definitions |
| `src/lib/game/data/golems/types.ts` | Golem type definitions |
| `src/lib/game/data/disciplines/fabricator.ts` | Golem Crafting discipline |
| `src/lib/game/stores/golem-combat-actions.ts` | Golem combat actions (disconnected) |
| `src/lib/game/stores/pipelines/golem-combat.ts` | Golem combat pipeline (disconnected) |
| `src/components/game/tabs/GolemancyTab.tsx` | Golemancy UI |
| `docs/specs/spire-combat-spec.md` §9 | Authoritative golemancy spec |
@@ -0,0 +1,347 @@
# Item Fabrication System — Design Spec
> Describes the Fabricator attunement's crafting system: recipe categories, unlock
> gates, material costs, crafting flow, and how fabricated items differ from base loot.
---
## 1. Objective
Item Fabrication is the Fabricator attunement's non-combat crafting system. It allows
the player to craft materials and equipment using mana and component items. Recipes
are unlocked through Fabricator discipline perks, and the resulting equipment can
carry pre-applied enchantments, making fabrication a parallel path to the Enchanter's
enchanting system.
**Design goals:**
- Fabricated equipment provides an alternative to loot drops
- Material crafting creates a multi-tier resource pipeline
- Discipline-gated recipe unlocks reward Fabricator attunement investment
- Pre-applied enchantments on crafted gear offer unique combinations
- Crafting Efficiency discipline reduces material costs
---
## 2. Recipe Categories
### 2.1 Overview
| Category | File | Count | Unlock Gate |
|---|---|---|---|
| Material Recipes | `fabricator-material-recipes.ts` | 15 | None (base recipes) |
| Core Equipment (Elemental) | `fabricator-recipes.ts` | 12 | Study Fabricator Recipes discipline |
| Wizard Branch | `fabricator-wizard-recipes.ts` | 14 | Study Wizard Equipment discipline |
| Physical Branch | `fabricator-physical-recipes.ts` | 7 | Study Physical Equipment discipline |
| **Total** | | **48** | |
### 2.2 Recipe Type Structure
```typescript
interface FabricatorRecipe {
id: string;
name: string;
description: string;
manaType: string; // Mana type required (must be unlocked)
equipmentTypeId: string; // Equipment type ID produced
slot: EquipmentSlot; // Slot the equipment occupies
materials: Record<string, number>; // materialId -> count required
manaCost: number; // Mana cost in the recipe's mana type
craftTime: number; // Craft time in hours
rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
gearTrait: string; // Flavor text for gear properties
bonusEnchantments?: AppliedEnchantment[]; // Pre-applied enchantments
recipeType?: 'equipment' | 'material';
resultMaterial?: string; // For material recipes: material ID produced
resultAmount?: number; // For material recipes: how many are produced
}
```
---
## 3. Material Recipes
### 3.1 Tier 1: Basic Materials
| ID | Name | Mana Type | Mana Cost | Input | Output | Time |
|---|---|---|---|---|---|---|
| `manaCrystal` | Mana Crystal | raw | 500 | — | 1× manaCrystal | 1h |
| `manaCrystalDustCraft` | Mana Crystal Dust | raw | 10 | 1× manaCrystal | 2× manaCrystalDust | 1h |
### 3.2 Tier 2: Elemental Crystals
All cost 100 of the respective element mana, take 1 hour, produce 1 crystal.
| ID | Mana Type | Element |
|---|---|---|
| `fireCrystal` | fire | Fire |
| `waterCrystal` | water | Water |
| `airCrystal` | air | Air |
| `earthCrystal` | earth | Earth |
| `lightCrystal` | light | Light |
| `darkCrystal` | dark | Dark |
| `metalCrystal` | metal | Metal |
| `crystalCrystal` | crystal | Crystal |
### 3.3 Tier 3: Shards and Cores
| ID | Mana Type | Mana Cost | Input | Output | Time |
|---|---|---|---|---|---|
| `earthShardCraft` | earth | 50 | 1× earthCrystal | 1× earthShard | 1h |
| `elementalCore` | raw | 100 | 10× manaCrystal | 1× elementalCore | 10h |
### 3.4 Tier 4: Advanced Materials
| ID | Mana Type | Mana Cost | Input | Output | Time |
|---|---|---|---|---|---|
| `aetherWeave` | air | 500 | 3× airCrystal, 3× lightCrystal, 2× elementalCore | 1× aetherWeave | 12h |
| `voidCloth` | dark | 500 | 3× airCrystal, 3× darkCrystal, 2× voidEssence | 1× voidCloth | 12h |
| `liquidCrystalLattice` | crystal | 800 | 5× crystalCrystal, 3× elementalCore, 2× voidEssence, 1× celestialFragment | 1× liquidCrystalLattice | 20h |
### 3.5 Material Dependency Chain
```
Raw Mana (500) → Mana Crystal (1)
Mana Crystal (1) + Raw Mana (10) → Mana Crystal Dust (2)
Mana Crystal (1) + Element Mana (100) → Element Crystal (1) [per element]
Element Crystal (1) + Element Mana (50) → Element Shard (1) [earth only]
Mana Crystal (10) + Raw Mana (100) → Elemental Core (1) [10hr]
Air Crystal (3) + Light Crystal (3) + Elemental Core (2) → Aether Weave (1) [12hr]
Air Crystal (3) + Dark Crystal (3) + Void Essence (2) → Void Cloth (1) [12hr]
Crystal Crystal (5) + Elemental Core (3) + Void Essence (2) + Celestial Fragment (1) → Liquid Crystal Lattice (1) [20hr]
```
---
## 4. Equipment Recipes
### 4.1 Earth Gear (Unlock: Study Fabricator Recipes @ 50 XP)
| ID | Name | Slot | Mana Cost | Materials | Rarity | Time |
|---|---|---|---|---|---|---|
| `earthHelm` | Earthen Helm | head | 200 earth | 4× manaCrystalDust, 2× earthShard | uncommon | 3h |
| `earthChest` | Stoneguard Armor | body | 500 earth | 8× manaCrystalDust, 4× earthShard, 1× elementalCore | rare | 6h |
| `earthBoots` | Stonegreaves | feet | 150 earth | 3× manaCrystalDust, 1× earthShard | uncommon | 2h |
### 4.2 Metal Gear (Unlock: Study Fabricator Recipes @ 100 XP)
| ID | Name | Slot | Mana Cost | Materials | Rarity | Time |
|---|---|---|---|---|---|---|
| `metalBlade` | Metal Blade | mainHand | 400 metal | 6× manaCrystalDust, 3× metalShard, 2× elementalCore | rare | 5h |
| `metalShield` | Metal Spell Focus | offHand | 450 metal | 7× manaCrystalDust, 4× metalShard, 1× elementalCore | rare | 5h |
| `metalGloves` | Metalweave Gauntlets | hands | 250 metal | 4× manaCrystalDust, 2× metalShard | uncommon | 3h |
### 4.3 Sand Gear (Unlock: Study Fabricator Recipes @ 150 XP)
| ID | Name | Slot | Mana Cost | Materials | Rarity | Time |
|---|---|---|---|---|---|---|
| `sandBoots` | Sandstrider Boots | feet | 120 sand | 3× manaCrystalDust, 1× sandShard | uncommon | 2h |
| `sandGloves` | Sandweave Gloves | hands | 140 sand | 3× manaCrystalDust, 2× sandShard | uncommon | 2h |
| `sandVest` | Sandcloth Vest | body | 300 sand | 5× manaCrystalDust, 2× sandShard, 1× elementalCore | rare | 4h |
### 4.4 Crystal Gear (Unlock: Study Fabricator Recipes @ 200 XP)
| ID | Name | Slot | Mana Cost | Materials | Rarity | Time |
|---|---|---|---|---|---|---|
| `crystalWand` | Crystal Focus Wand | mainHand | 600 crystal | 10× manaCrystalDust, 5× crystalShard, 3× elementalCore | epic | 6h |
| `crystalRing` | Crystal Ring | accessory1 | 350 crystal | 5× manaCrystalDust, 3× crystalShard, 1× elementalCore | rare | 3h |
| `crystalAmulet` | Crystal Pendant | accessory2 | 400 crystal | 6× manaCrystalDust, 3× crystalShard, 2× elementalCore | rare | 4h |
### 4.5 Wizard Branch (Unlock: Study Wizard Equipment discipline)
| ID | Name | Slot | Unlock (XP) | Mana Cost | Materials | Rarity | Time |
|---|---|---|---|---|---|---|---|
| `oakStaff` | Oak Staff | mainHand | 50 | 200 earth | 5× manaCrystalDust, 2× earthShard | uncommon | 3h |
| `arcanistStaff` | Arcanist Staff | mainHand | 100 | 700 crystal | 12× manaCrystalDust, 6× crystalShard, 3× elementalCore | epic | 8h |
| `battlestaff` | Battlestaff | mainHand | 150 | 500 metal | 8× manaCrystalDust, 4× metalShard, 2× elementalCore | rare | 6h |
| `arcanistCirclet` | Arcanist Circlet | head | 150 | 300 crystal | 6× manaCrystalDust, 2× crystalShard, 1× lightCrystal | rare | 4h |
| `arcanistRobe` | Arcanist Robe | body | 150 | 800 crystal | 14× manaCrystalDust, 7× crystalShard, 3× elementalCore | epic | 8h |
| `voidCatalyst` | Void Catalyst | mainHand | 200 | 600 crystal | 10× manaCrystalDust, 3× darkCrystal, 2× voidEssence, 2× elementalCore | epic | 7h |
| `arcanistPendant` | Arcanist Pendant | accessory1 | 250 | 500 crystal | 8× manaCrystalDust, 4× crystalShard, 2× elementalCore | epic | 5h |
**Advanced Wizard Gear:**
| ID | Name | Slot | Mana Cost | Materials | Rarity | Time |
|---|---|---|---|---|---|---|
| `aetherRobe` | Aetherweave Robe | body | 1200 crystal | 3× aetherWeave, 15× manaCrystalDust, 8× crystalShard, 4× elementalCore | legendary | 15h |
| `aetherCirclet` | Aetherweave Circlet | head | 900 crystal | 2× aetherWeave, 10× manaCrystalDust, 3× lightCrystal, 3× elementalCore | epic | 10h |
| `voidRobe` | Voidweave Robe | body | 1200 sand | 3× voidCloth, 15× manaCrystalDust, 8× crystalShard, 3× voidEssence | legendary | 15h |
| `voidCowl` | Voidweave Cowl | head | 900 sand | 2× voidCloth, 10× manaCrystalDust, 3× darkCrystal, 2× voidEssence | epic | 10h |
| `latticeStaff` | Crystal Lattice Staff | mainHand | 2000 crystal | 2× liquidCrystalLattice, 2× aetherWeave, 2× voidCloth, 5× elementalCore | legendary | 25h |
| `latticeAmulet` | Crystal Lattice Amulet | accessory1 | 1500 crystal | 1× liquidCrystalLattice, 5× crystalCrystal, 4× elementalCore, 2× voidEssence | legendary | 18h |
### 4.6 Physical Branch (Unlock: Study Physical Equipment discipline)
| ID | Name | Slot | Unlock (XP) | Mana Cost | Materials | Rarity | Time |
|---|---|---|---|---|---|---|---|
| `crystalBlade` | Crystal Blade | mainHand | 50 | 500 crystal | 8× manaCrystalDust, 4× crystalShard, 2× elementalCore | rare | 5h |
| `arcanistBlade` | Arcanist Blade | mainHand | 100 | 600 metal | 10× manaCrystalDust, 5× metalShard, 3× elementalCore | epic | 7h |
| `voidBlade` | Void-Touched Blade | mainHand | 150 | 550 crystal | 9× manaCrystalDust, 3× darkCrystal, 2× voidEssence, 2× elementalCore | epic | 6h |
| `battleHelm` | Battle Helm | head | 200 | 350 metal | 6× manaCrystalDust, 3× metalShard, 1× elementalCore | rare | 4h |
| `battleRobe` | Battle Robe | body | 200 | 400 sand | 8× manaCrystalDust, 3× sandShard, 2× elementalCore | rare | 5h |
| `battleBoots` | Battle Boots | feet | 250 | 180 sand | 4× manaCrystalDust, 2× sandShard | uncommon | 3h |
| `combatGauntlets` | Combat Gauntlets | hands | 300 | 300 metal | 5× manaCrystalDust, 2× metalShard, 1× elementalCore | uncommon | 3h |
---
## 5. Recipe Unlock Gates
### 5.1 Study Fabricator Recipes Discipline
| XP Threshold | Recipes Unlocked |
|---|---|
| 50 | Earth gear (helm, chest, boots) |
| 100 | Metal gear (blade, shield, gloves) |
| 150 | Sand gear (boots, gloves, vest) |
| 200 | Crystal gear (wand, ring, amulet) |
### 5.2 Study Wizard Equipment Discipline
| XP Threshold | Recipes Unlocked |
|---|---|
| 50 | Oak Staff |
| 100 | Arcanist Staff |
| 150 | Battlestaff, Arcanist Circlet, Arcanist Robe |
| 200 | Void Catalyst |
| 250 | Arcanist Pendant |
| 300 | (advanced recipes via material availability) |
### 5.3 Study Physical Equipment Discipline
| XP Threshold | Recipes Unlocked |
|---|---|
| 50 | Crystal Blade |
| 100 | Arcanist Blade |
| 150 | Void Blade |
| 200 | Battle Helm, Battle Robe |
| 250 | Battle Boots |
| 300 | Combat Gauntlets |
---
## 6. Crafting Flow
### 6.1 Pre-Craft Checks
```
checkFabricatorCosts(recipe, materials, rawMana, elements):
- Verify all material counts are sufficient
- Verify mana (raw or elemental) is sufficient
- Return { canCraft, missingMana, missingMaterials }
```
### 6.2 Crafting Execution
```
executeMaterialCraft(recipe, materials):
1. Deduct mana cost from raw or elemental pool
2. Deduct input materials from inventory
3. Add resultAmount of resultMaterial to inventory
makeFabricatorProgress(recipeId, equipmentTypeId, craftTime, manaCost):
1. Create EquipmentCraftingProgress object
2. blueprintId = "fabricator-{recipeId}"
3. Progress accumulates at HOURS_PER_TICK per tick
4. On completion: create equipment instance with bonusEnchantments
```
### 6.3 Cancellation Refund
```
remainingFraction = (required - progress) / required
refundRate = remainingFraction + (1 - remainingFraction) × 0.5
manaRefund = floor(manaSpent × refundRate)
materialRefund = floor(materialsSpent × 0.5)
```
---
## 7. Crafting Efficiency Discipline Interaction
The **Crafting Efficiency** discipline provides:
| Source | Effect |
|---|---|
| Base stat bonus | `craftingCostReduction` +15 |
| Perk `efficiency-1` (once @ 300 XP) | +10% Crafting Cost Reduction |
The `craftingCostReduction` stat reduces material costs for all fabrication recipes.
Applied as: `actualCost = baseCost × (1 - craftingCostReduction / 100)`.
At maximum: 15 (base) + 10 (perk) = **25% cost reduction**.
---
## 8. How Fabricated Items Differ from Base Loot
| Property | Loot Drops | Fabricated Items |
|---|---|---|
| **Source** | Enemy drops, treasure rooms | Crafting recipes |
| **Enchantments** | None (must be enchanted) | Pre-applied `bonusEnchantments` |
| **Rarity** | Random (commonlegendary) | Fixed per recipe |
| **Quality** | Random (0100) | Fixed per recipe |
| **Stats** | Base for type | Base for type + enchantment bonuses |
| **Control** | None (random) | Full (player chooses recipe) |
Fabricated items are created with `bonusEnchantments` — pre-applied enchantment
objects with `effectId`, `stacks`, and `actualCost`. These enchantments are
permanent and cannot be removed without the Enchanter's disenchant process.
---
## 9. Equipment Types Producible via Fabrication
| Slot | Equipment Types |
|---|---|
| mainHand | Metal Blade, Crystal Focus Wand, Oak Staff, Arcanist Staff, Battlestaff, Void Catalyst, Crystal Lattice Staff |
| offHand | Metal Spell Focus |
| head | Earthen Helm, Arcanist Circlet, Aetherweave Circlet, Voidweave Cowl, Battle Helm |
| body | Stoneguard Armor, Sandcloth Vest, Arcanist Robe, Aetherweave Robe, Voidweave Robe, Battle Robe |
| hands | Metalweave Gauntlets, Sandweave Gloves, Combat Gauntlets |
| feet | Stonegreaves, Sandstrider Boots, Battle Boots |
| accessory1 | Crystal Ring, Arcanist Pendant, Crystal Lattice Amulet |
| accessory2 | Crystal Pendant |
---
## 10. Rarity Distribution
| Rarity | Count | Examples |
|---|---|---|
| common | 2 | Mana Crystal Dust, Earth Shard |
| uncommon | 14 | Earth gear, Sand gear, Oak Staff, Battle Boots, Combat Gauntlets, Mana Crystal |
| rare | 14 | Earth Chest, Metal gear, Crystal Ring/Amulet, Sand Vest, Crystal Blade, Battle Helm/Robe |
| epic | 10 | Crystal Wand, Arcanist Staff/Robe, Void Blade/Catalyst, Arcanist Pendant, Aether Circlet, Void Cowl |
| legendary | 5 | Aether Robe, Void Robe, Lattice Staff, Lattice Amulet, Liquid Crystal Lattice |
---
## 11. Acceptance Criteria
| # | Criterion |
|---|---|
| AC-1 | All 48 recipes are accessible when the Fabricator attunement is active. |
| AC-2 | Recipe unlock gates fire at the correct discipline XP thresholds. |
| AC-3 | Material crafting correctly consumes mana and input materials, producing the correct output. |
| AC-4 | Equipment crafting produces items with the correct pre-applied enchantments. |
| AC-5 | Crafting Efficiency discipline reduces material costs by the correct percentage. |
| AC-6 | Cancellation refunds mana at the blended rate (100% unspent, 50% spent) and materials at 50%. |
| AC-7 | Fabricated items cannot be crafted without the required mana type unlocked. |
| AC-8 | Material dependency chain is correct: Mana Crystal → Element Crystal → Elemental Core → Advanced Materials. |
| AC-9 | Craft time ranges from 1h (basic materials) to 25h (Crystal Lattice Staff). |
| AC-10 | Mana cost ranges from 10 (Mana Crystal Dust) to 2000 (Crystal Lattice Staff). |
---
## 12. Files Reference
| File | Role |
|---|---|
| `src/lib/game/data/fabricator-material-recipes.ts` | Material recipes (15) |
| `src/lib/game/data/fabricator-recipes.ts` | Core equipment recipes (12) |
| `src/lib/game/data/fabricator-wizard-recipes.ts` | Wizard branch recipes (14) |
| `src/lib/game/data/fabricator-physical-recipes.ts` | Physical branch recipes (7) |
| `src/lib/game/data/fabricator-recipe-types.ts` | Recipe type definitions |
| `src/lib/game/crafting-fabricator.ts` | Fabrication crafting logic |
| `src/lib/game/data/disciplines/fabricator.ts` | Fabricator disciplines (5) |
| `src/components/game/tabs/CraftingTab.tsx` | Crafting tab wrapper |
| `src/components/game/tabs/CraftingTab/FabricatorSubTab.tsx` | Fabricator crafting UI |
@@ -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 |
@@ -0,0 +1,356 @@
# 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 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`:
```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` |
| 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 |