docs: reconcile spec inconsistencies across all documentation
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m20s

- Fix incursion start day: 5→20 in GAME_BRIEFING.md (matches code constant)
- Fix fabricator discipline count: 2→5 in AGENTS.md
- Fix discipline counts: elemental.ts 22→21, advanced-regen.ts 14→15
- Fix equipment count: 50→43, remove shields, fix catalysts count
- Fix prestige upgrade count: add missing manaWell, manaFlow, pactBinding, pactInterferenceMitigation
- Remove x3 victory multiplier (no victory condition defined yet)
- Update pact persistence: pacts do NOT persist through prestige
- Update elemental matchup tables to match ultimate truth
- Update room type frequencies to match spire-climbing-spec
- Update guardian data tables to use formulas
- Update Tier 3 guardian elements to match guardian-data.ts code
- Add pactBinding + pactInterferenceMitigation to PRESTIGE_DEF constants
- Wire pactInterferenceMitigation into useGameDerived.ts
- Update spire-combat-spec.md Known Gaps table (DoT implemented, melee bypass bug)
- Update invoker-spec.md known issues (all resolved)
- Update golemancy-spec.md status (undergoing redesign)
- Update PrestigeTab test to expect 15 upgrades
- Create Gitea issues #285 (melee defense bypass), #286 (DoT verified), #287 (mana conversion gap)
This commit is contained in:
2026-06-05 14:07:22 +02:00
parent 69cc8b78d1
commit 6aed5c8d2b
10 changed files with 135 additions and 88 deletions
+10 -9
View File
@@ -63,16 +63,16 @@ Use for 3+ sequential independent calls. Zero context from parent — paste ever
### Adding Disciplines ### Adding Disciplines
1. Choose the correct data file under `data/disciplines/`: 1. Choose the correct data file under `data/disciplines/`:
- `base.ts` — Raw Mana Mastery (available to all) - `base.ts` — Raw Mana Mastery (3 disciplines)
- `elemental.ts` — Elemental Attunement (7 base+ elements) - `elemental.ts` — Elemental Attunement (21 disciplines — all 22 mana types)
- `elemental-regen.ts` — Elemental Regen (7 base + transference) - `elemental-regen.ts` — Elemental Regen (8 disciplines — 7 base + transference)
- `elemental-regen-advanced.ts` — Advanced Regen (3 composite + 3 exotic) - `elemental-regen-advanced.ts` — Advanced Regen (15 disciplines — 8 composite + 6 exotic + transference composite)
- `enchanter.ts` — Core Enchanter disciplines (4 disciplines) - `enchanter.ts` — Core Enchanter disciplines (4 disciplines)
- `enchanter-utility.ts` — Utility enchantment disciplines (2 disciplines) - `enchanter-utility.ts` — Utility enchantment disciplines (2 disciplines)
- `enchanter-spells.ts` — Spell enchantment disciplines (3 disciplines) - `enchanter-spells.ts` — Spell enchantment disciplines (3 disciplines)
- `enchanter-special.ts` — Special enchantment disciplines (1 discipline) - `enchanter-special.ts` — Special enchantment disciplines (1 discipline)
- `invoker.ts` — Invoker combat disciplines (2 disciplines) - `invoker.ts` — Invoker combat disciplines (2 disciplines)
- `fabricator.ts` — Fabricator crafting/golem disciplines (2 disciplines) - `fabricator.ts` — Fabricator crafting/golem disciplines (5 disciplines)
2. Define a `DisciplineDefinition` (see `types/disciplines.ts`): 2. Define a `DisciplineDefinition` (see `types/disciplines.ts`):
- `statBonus.stat` must match a key consumed by `computeDisciplineEffects()` - `statBonus.stat` must match a key consumed by `computeDisciplineEffects()`
- Set `difficultyFactor` and `scalingFactor` to control growth rate - Set `difficultyFactor` and `scalingFactor` to control growth rate
@@ -114,7 +114,7 @@ ManaDrainPerTick = drainBase × (1 + (XP / difficultyFactor)^0.4)
- Stacking cost: each additional stack costs 20% more - Stacking cost: each additional stack costs 20% more
### Golemancy ### Golemancy
- 12 golems total: 1 base (Earth) + 3 elemental (Steel, Crystal, Sand) + 6 hybrid (Lava, Galvanic, Obsidian, Prism, Quicksilver, Voidstone) + 2 advanced - 10 golems total: 1 base (Earth) + 3 elemental (Steel, Crystal, Sand) + 6 hybrid (Lava, Galvanic, Obsidian, Prism, Quicksilver, Voidstone). Golemancy system is undergoing redesign — see issue #268 for the new component-based construction system (Core + Frame + Mind Circuit + Enchantments)
- Golems slots: `floor(fabricatorLevel / 2)`, max 5 at level 10 - Golems slots: `floor(fabricatorLevel / 2)`, max 5 at level 10
- Hybrid golems require Enchanter 5 + Fabricator 5 - Hybrid golems require Enchanter 5 + Fabricator 5
@@ -133,6 +133,7 @@ ManaDrainPerTick = drainBase × (1 + (XP / difficultyFactor)^0.4)
- Element opposites (bidirectional): fire↔water, air↔earth, light↔dark, frost↔fire - Element opposites (bidirectional): fire↔water, air↔earth, light↔dark, frost↔fire
- Element counters (directional): lightning→water (lightning counters water), earth→lightning (earth counters lightning) - Element counters (directional): lightning→water (lightning counters water), earth→lightning (earth counters lightning)
- Composite element counters: blackflame counters frost/water/light (and they counter blackflame); radiantflames counters frost/water/dark (and they counter radiantflames) - Composite element counters: blackflame counters frost/water/light (and they counter blackflame); radiantflames counters frost/water/dark (and they counter radiantflames)
- Miasma counters air (and air counters miasma); Shadow glass counters light (and light counters shadow glass)
- All mana types double as spell elements - All mana types double as spell elements
- Enemy modifiers (max 2 per enemy): Armored, Agile, Mage, Shield, Swarm - Enemy modifiers (max 2 per enemy): Armored, Agile, Mage, Shield, Swarm
- Room types: Combat (default), Guardian (every 10th), Swarm (15%), Speed (10%), Puzzle (20% on every 7th floor) - Room types: Combat (default), Guardian (every 10th), Swarm (15%), Speed (10%), Puzzle (20% on every 7th floor)
@@ -145,9 +146,9 @@ ManaDrainPerTick = drainBase × (1 + (XP / difficultyFactor)^0.4)
### Prestige (Insight) ### Prestige (Insight)
- `baseInsight = floor(maxFloorReached × 15 + totalManaGathered / 500 + signedPacts.length × 150)` - `baseInsight = floor(maxFloorReached × 15 + totalManaGathered / 500 + signedPacts.length × 150)`
- Multiplied by discipline and boon bonuses. ×3 for victory (floor 100 + pact signed) - Multiplied by discipline and boon bonuses. No victory ×3 multiplier (victory condition not yet defined)
- 14 prestige upgrade types: manaWell, manaFlow, insightAmp, spireKey, temporalEcho, steadyHand, ancientKnowledge, elementalAttune, spellMemory, guardianPact, quickStart, elemStart, unlockedManaTypeCapacity - 14 prestige upgrade types: manaWell, manaFlow, insightAmp, spireKey, temporalEcho, steadyHand, ancientKnowledge, elementalAttune, spellMemory, guardianPact, quickStart, elemStart, unlockedManaTypeCapacity, pactBinding
- Signed pacts persist through prestige (bounded by `pactSlots`) - Signed pacts do NOT persist through prestige (reset each loop)
### Starting State ### Starting State
- Attunement: Enchanter only (level 1) - Attunement: Enchanter only (level 1)
+94 -55
View File
@@ -1,7 +1,7 @@
# Mana-Loop: Comprehensive Game Briefing Document # Mana-Loop: Comprehensive Game Briefing Document
**Document Version:** 4.0 **Document Version:** 4.0
**Updated:** Post-refactoring — 22 mana types, 8-tier guardian system, 64 disciplines, 8 Zustand stores, localStorage-only **Updated:** Post-refactoring — 22 mana types, 8-tier guardian system, 64 disciplines, 14 prestige upgrades, 43 equipment types, 8 Zustand stores, localStorage-only
--- ---
@@ -34,7 +34,7 @@
- 3-class Attunement system (Enchanter, Invoker, Fabricator) - 3-class Attunement system (Enchanter, Invoker, Fabricator)
- Equipment-based spell system (spells come from enchanted gear and learned spells) - Equipment-based spell system (spells come from enchanted gear and learned spells)
- Practice-based Discipline system — no discrete skill levels, only continuous XP growth - Practice-based Discipline system — no discrete skill levels, only continuous XP growth
- Time pressure through the incursion mechanic (starts day 5) - Time pressure through the incursion mechanic (starts day 20)
- Guardian progression: base (10-80) → composite (90-160) → exotic (170-240) → combo bosses (250+, 8 tiers) - Guardian progression: base (10-80) → composite (90-160) → exotic (170-240) → combo bosses (250+, 8 tiers)
- Guardian pacts provide permanent multipliers that persist through prestige - Guardian pacts provide permanent multipliers that persist through prestige
- No backend — pure client-side with localStorage persistence - No backend — pure client-side with localStorage persistence
@@ -178,7 +178,7 @@ Base: 1 + min(hours/4, 0.5) → up to 1.5× after 4 hours
| `TICK_MS` | 200ms | Real time per game tick | | `TICK_MS` | 200ms | Real time per game tick |
| `HOURS_PER_TICK` | 0.04 | Game hours per tick | | `HOURS_PER_TICK` | 0.04 | Game hours per tick |
| `MAX_DAY` | 30 | Days per loop | | `MAX_DAY` | 30 | Days per loop |
| `INCURSION_START_DAY` | 5 | When incursion begins | | `INCURSION_START_DAY` | 20 | When incursion begins |
### Time Progression ### Time Progression
- 1 real second = 5 game hours (at 5 ticks/second) - 1 real second = 5 game hours (at 5 ticks/second)
@@ -188,12 +188,13 @@ Base: 1 + min(hours/4, 0.5) → up to 1.5× after 4 hours
### Incursion Mechanic ### Incursion Mechanic
``` ```
if (day < 5): incursionStrength = else: incursionStrength = min(0.95, (totalHours / maxHours) × 0.95) if (day < 20): incursionStrength = 0
where totalHours = (day - 5) × 24 + hour else: incursionStrength = min(0.95, (totalHours / maxHours) × 0.95)
maxHours = (30 - 5) × 24 = 600 where totalHours = (day - 20) × 24 + hour
maxHours = (30 - 20) × 24 = 240
``` ```
Reduces mana regeneration by `(1 - incursionStrength)`. Starts at 0% on Day 5, reaches 95% by Day 30. Reduces mana regeneration by `(1 - incursionStrength)`. Starts at 0% on Day 20, reaches 95% by Day 30.
--- ---
@@ -223,10 +224,13 @@ HP = floor(5000 × (floor/10) ^ (1.1 + floor/200))
| Room Type | Chance | Description | | Room Type | Chance | Description |
|-----------|--------|-------------| |-----------|--------|-------------|
| **Guardian** | Every 10th floor | Boss encounter | | **Guardian** | Every 10th floor | Boss encounter |
| **Puzzle** | 20% on every 7th floor | Attunement-themed trial | | **Puzzle** | 100% on every 7th floor (except guardian floors) | Attunement-themed trial |
| **Swarm** | 15% | 36 enemies with 40% HP each | | **Swarm** | ~12% | 37 weak enemies |
| **Speed** | 10% | Enemy with dodge chance (25% base + 0.5%/floor, max 50%) | | **Speed** | ~10% | Single enemy with elevated dodge chance |
| **Combat** | Default | Single enemy, normal combat | | **Combat** | ~68% (default) | Single enemy, normal combat |
| **Recovery** | ~4% | 1 hour; grants 10× mana regen & conversion |
| **Treasure** | ~3% | 1 hour; grants 215 random items |
| **Library** | ~3% | 1 hour; grants discipline XP at 25× rate |
### Puzzle Room Types ### Puzzle Room Types
- Single attunement: `enchanter_trial`, `fabricator_trial`, `invoker_trial` - Single attunement: `enchanter_trial`, `fabricator_trial`, `invoker_trial`
@@ -297,12 +301,23 @@ damage *= (1 - effectiveArmor)
| Spell's opposite matches floor (not very effective) | 0.75× | | Spell's opposite matches floor (not very effective) | 0.75× |
| Neutral / Raw mana spells | 1.00× | | Neutral / Raw mana spells | 1.00× |
**Element Opposites:** **Element Opposites (bidirectional):**
``` ```
Fire ↔ Water Air ↔ Earth Light ↔ Dark Fire ↔ Water Air ↔ Earth Light ↔ Dark
Fire ↔ Frost Lightning → Earth (grounding) Frost ↔ Fire
BlackFlame ↔ Water Radiant Flames ↔ Dark ```
Miasma ↔ Light Shadow Glass ↔ Light
**Element Counters (directional):**
```
Lightning → Water Earth → Lightning
```
**Composite Element Counters (bidirectional):**
```
BlackFlame ↔ Frost, Water, Light
Radiant Flames ↔ Frost, Water, Dark
Miasma ↔ Air
Shadow Glass ↔ Light
``` ```
### Spell Tiers ### Spell Tiers
@@ -339,40 +354,57 @@ Miasma ↔ Light Shadow Glass ↔ Light
| Floor | Name | Element | Armor | Pact Mult | Pact Cost | Pact Time | | Floor | Name | Element | Armor | Pact Mult | Pact Cost | Pact Time |
|-------|------|---------|-------|-----------|-----------|-----------| |-------|------|---------|-------|-----------|-----------|-----------|
| 10 | Ignis Prime | fire | 10% | 1.5× | 500 | 2h | | 10 | Ignis Prime | fire | 10% | 1.5× | formula | 2h |
| 20 | Aqua Regia | water | 15% | 1.75× | 1,000 | 4h | | 20 | Aqua Regia | water | 15% | 1.75× | formula | 4h |
| 30 | Ventus Rex | air | 18% | 2.0× | 2,000 | 6h | | 30 | Ventus Rex | air | 20% | 2.0× | formula | 6h |
| 40 | Terra Firma | earth | 25% | 2.25× | 4,000 | 8h | | 40 | Terra Firma | earth | 25% | 2.25× | formula | 8h |
| 50 | Lux Aeterna | light | 20% | 2.5× | 8,000 | 10h | | 50 | Lux Aeterna | light | 30% | 2.5× | formula | 10h |
| 60 | Umbra Mortis | dark | 22% | 2.75× | 15,000 | 12h | | 60 | Umbra Mortis | dark | 35% | 2.75× | formula | 12h |
| 70 | Mors Ultima | death | 25% | 3.0× | 25,000 | 14h | | 70 | Mors Ultima | death | 40% | 3.0× | formula | 14h |
| 80 | Vinculum Arcana | transference | 20% | 3.25× | 35,000 | 16h | | 80 | Vinculum Arcana | transference | 45% | 3.25× | formula | 16h |
**Tier 1 Formulas:**
- Pact Cost: `floor(hp × 0.3 + power × 5 + hp × armor × 0.5 + shield × 2 + hp × barrier × 0.3)`
- Pact Time: `2 + floor(floor / 10)` hours
- Armor: Starts at 10% on floor 10, increases by 5% per 10th floor
#### Tier 2 — Composite Elements (Floors 90160) #### Tier 2 — Composite Elements (Floors 90160)
| Floor | Element | Armor | Pact Mult | Pact Cost | Pact Time | | Floor | Element | Armor | Pact Mult | Pact Cost | Pact Time |
|-------|---------|-------|-----------|-----------|-----------| |-------|---------|-------|-----------|-----------|-----------|
| 90 | metal | 30% | 3.5× | 60,000 | 18h | | 90 | metal | 50% | 3.5× | formula | 18h |
| 100 | sand | 25% | 3.75× | 80,000 | 20h | | 100 | sand+fire+earth | 55% | 3.75× | formula | 20h |
| 110 | lightning | 22% | 4.0× | 100,000 | 22h | | 110 | lightning | 60% | 4.0× | formula | 22h |
| 120 | frost | 28% | 4.25× | 125,000 | 24h | | 120 | frost | 65% | 4.25× | formula | 24h |
| 130 | blackflame | 32% | 4.5× | 140,000 | 26h | | 130 | metal+fire+earth (blackflame) | 70% | 4.5× | formula | 26h |
| 140 | radiantflames | 25% | 4.75× | 160,000 | 28h | | 140 | sand+earth+water (radiantflames) | 75% | 4.75× | formula | 28h |
| 150 | miasma | 28% | 5.0× | 180,000 | 30h | | 150 | lightning+fire+air (miasma) | 80% | 5.0× | formula | 30h |
| 160 | shadowglass | 33% | 5.25× | 200,000 | 32h | | 160 | shadowglass | 85% | 5.25× | formula | 32h |
**Tier 2 Formulas:**
- Pact Cost: Same formula as Tier 1
- Pact Time: `2 + floor(floor / 10)` hours
- Armor: Starts at 50% on floor 90, increases by 5% per 10th floor (capped at 90%)
- Note: Floor 100 guardian element is `sand+fire+earth` (composite components), Floor 140 is `sand+earth+water`, Floor 150 is `lightning+fire+air`
#### Tier 3 — Exotic Elements (Floors 170240) #### Tier 3 — Exotic Elements (Floors 170240)
| Floor | Element | Armor | Pact Mult | Pact Cost | Pact Time | | Floor | Element | Armor | Pact Mult | Pact Cost | Pact Time |
|-------|---------|-------|-----------|-----------|-----------| |-------|---------|-------|-----------|-----------|-----------|
| 170 | crystal | 35% | 5.5× | 220,000 | 37h | | 170 | crystal | 35% | 5.5× | formula | 37h |
| 180 | stellar | 30% | 6.0× | 250,000 | 38h | | 180 | stellar | 30% | 6.0× | formula | 38h |
| 190 | void | 35% | 6.5× | 300,000 | 39h | | 190 | void | 35% | 6.5× | formula | 39h |
| 200 | soul | 30% | 7.0× | 330,000 | 40h | | 200 | crystal+stellar+void | 35% | 7.0× | formula | 40h |
| 210 | time | 32% | 7.5× | 370,000 | 41h | | 210 | soul+time+plasma | 32% | 7.5× | formula | 41h |
| 220 | plasma | 28% | 8.0× | 400,000 | 42h | | 220 | plasma | 28% | 8.0× | formula | 42h |
| 230 | crystal+stellar+void | 40% | 8.5× | 430,000 | 43h | | 230 | crystal+stellar+void | 40% | 8.5× | formula | 43h |
| 240 | soul+time+plasma | 42% | 9.0× | 460,000 | 44h | | 240 | soul+time+plasma | 42% | 9.0× | formula | 44h |
**Tier 3 Formulas:**
- Pact Cost: Same formula as Tier 1
- Pact Time: `2 + floor(floor / 10)` hours
- Armor: Varies per guardian (see code for exact values)
- Note: Floor 200 is `crystal+stellar+void`, Floor 210 is `soul+time+plasma` (matching the code in guardian-data.ts)
Floors 90240 have procedurally generated names via `generateGuardianName()`. Floors 90240 have procedurally generated names via `generateGuardianName()`.
@@ -454,7 +486,9 @@ Each guardian grants 2 boons from: `maxMana`, `manaRegen`, `castingSpeed`, `elem
5. Pact slots limit simultaneous signed pacts (starting 1, upgradeable) 5. Pact slots limit simultaneous signed pacts (starting 1, upgradeable)
### Victory Condition ### Victory Condition
Defeat floor 100 guardian **and** sign the pact → 3× normal insight. No specific victory condition is currently defined. The loop ends when day 30 is reached (or floor 30 for early completion). A victory condition with a ×3 multiplier is awaiting design finalization.
Signed pacts do **NOT** persist through prestige — the player must re-defeat Guardians and re-sign pacts each loop.
--- ---
@@ -563,9 +597,9 @@ Players start with 1 active discipline slot. As total XP across all disciplines
| Pool | File | Count | Requires | | Pool | File | Count | Requires |
|------|------|-------|----------| |------|------|-------|----------|
| Base | `base.ts` | 3 | None | | Base | `base.ts` | 3 | None |
| Elemental Attunement | `elemental.ts` | 22 | None | | Elemental Attunement | `elemental.ts` | 21 | None |
| Elemental Regen | `elemental-regen.ts` | 8 | None | | Elemental Regen | `elemental-regen.ts` | 8 | None |
| Advanced Regen | `elemental-regen-advanced.ts` | 14 | Mana type unlocked | | Advanced Regen | `elemental-regen-advanced.ts` | 15 | Mana type unlocked |
| Enchanter Core | `enchanter.ts` | 4 | Enchanter attunement | | Enchanter Core | `enchanter.ts` | 4 | Enchanter attunement |
| Enchanter Utility | `enchanter-utility.ts` | 2 | Enchanter attunement | | Enchanter Utility | `enchanter-utility.ts` | 2 | Enchanter attunement |
| Enchanter Spells | `enchanter-spells.ts` | 3 | Enchanter attunement | | Enchanter Spells | `enchanter-spells.ts` | 3 | Enchanter attunement |
@@ -617,7 +651,7 @@ Players start with 1 active discipline slot. As total XP across all disciplines
``` ```
mainHand - Casters (some 2H), Swords, Catalysts mainHand - Casters (some 2H), Swords, Catalysts
offHand - Shields, Catalysts (blocked by 2-handed weapons) offHand - Catalysts, Spell Focuses (blocked by 2-handed weapons)
head - Hoods, Hats, Helms head - Hoods, Hats, Helms
body - Robes, Armor body - Robes, Armor
hands - Gloves, Gauntlets hands - Gloves, Gauntlets
@@ -626,19 +660,20 @@ accessory1 - Rings, Amulets
accessory2 - Rings, Amulets accessory2 - Rings, Amulets
``` ```
### Equipment Categories & Types (50 total) ### Equipment Categories & Types (43 total)
| Category | Slot | Count | Examples | | Category | Slot | Count | Examples |
|----------|------|-------|----------| |----------|------|-------|----------|
| Casters | mainHand | 6 | Basic Staff (2H), Apprentice Wand, Oak Staff (2H), Crystal Wand, Arcanist Staff (2H), Battlestaff (2H) | | Casters | mainHand | 6 | Basic Staff (2H), Apprentice Wand, Oak Staff (2H), Crystal Wand, Arcanist Staff (2H), Battlestaff (2H) |
| Swords | mainHand | 5 | Iron Blade, Steel Blade, Crystal Blade, Arcanist Blade, Void-Touched Blade | | Swords | mainHand | 5 | Iron Blade, Steel Blade, Crystal Blade, Arcanist Blade, Void-Touched Blade |
| Catalysts | mainHand | 3 | Basic Catalyst, Fire Catalyst, Void Catalyst | | Catalysts | mainHand/offHand | 4 | Basic Catalyst, Fire Catalyst, Void Catalyst, Metal Spell Focus |
| Body | body | 5 | Civilian Shirt, Apprentice Robe, Scholar Robe, Battle Robe, Arcanist Robe | | Body | body | 5 | Civilian Shirt, Apprentice Robe, Scholar Robe, Battle Robe, Arcanist Robe |
| Head | head | 5 | Cloth Hood, Apprentice Cap, Wizard Hat, Arcanist Circlet, Battle Helm | | Head | head | 5 | Cloth Hood, Apprentice Cap, Wizard Hat, Arcanist Circlet, Battle Helm |
| Hands | hands | 4 | Civilian Gloves, Apprentice Gloves, Spellweave Gloves, Combat Gauntlets | | Hands | hands | 4 | Civilian Gloves, Apprentice Gloves, Spellweave Gloves, Combat Gauntlets |
| Feet | feet | 4 | Civilian Shoes, Apprentice Boots, Traveler Boots, Battle Boots | | Feet | feet | 4 | Civilian Shoes, Apprentice Boots, Traveler Boots, Battle Boots |
| Shields | offHand | 4 | Basic Shield, Reinforced Shield, Runic Shield, Mana Shield | | Accessories | accessory1/2 | 10 | Copper Ring, Silver Ring, Gold Ring, Signet Ring, Copper Amulet, Silver Amulet, Crystal Pendant, Mana Brooch, Arcanist Pendant, Void-Touched Ring |
| Accessories | accessory1/2 | 11 | Copper Ring, Silver Ring, Gold Ring, Signet Ring, Copper Amulet, Silver Amulet, Crystal Pendant, Mana Brooch, Arcanist Pendant, Void-Touched Ring |
**Total: 43 equipment types** across 8 categories. Shields are banned. Catalysts can be equipped in offHand slot.
### Two-Handed Weapons ### Two-Handed Weapons
2H weapons (Basic Staff, Oak Staff, Arcanist Staff, Battlestaff) occupy both `mainHand` and `offHand`. The offhand slot is **blocked** when a 2H weapon is equipped. 2H weapons (Basic Staff, Oak Staff, Arcanist Staff, Battlestaff) occupy both `mainHand` and `offHand`. The offhand slot is **blocked** when a 2H weapon is equipped.
@@ -733,7 +768,7 @@ Level 8: 4 slots
Level 10: 5 slots Level 10: 5 slots
``` ```
### Golem Types (10 Total) ### Golem Types (10 Total — undergoing redesign, see issue #268)
#### Base Golems (1) #### Base Golems (1)
@@ -747,7 +782,7 @@ Level 10: 5 slots
|-------|---------|--------|-------|----|--------|--------| |-------|---------|--------|-------|----|--------|--------|
| Steel Golem | Metal | 12 | 1.2/s | 60 | 35% | Metal mana unlocked | | Steel Golem | Metal | 12 | 1.2/s | 60 | 35% | Metal mana unlocked |
| Crystal Golem | Crystal | 18 | 1.0/s | 40 | 25% | Crystal mana unlocked | | Crystal Golem | Crystal | 18 | 1.0/s | 40 | 25% | Crystal mana unlocked |
| Sand Golem | Sand | 6 | 2.0/s | 35 | 10% | Sand mana unlocked | | Sand Golem | Sand | 10 | 2.0/hr | 45 | 15% | Sand mana unlocked |
#### Hybrid Golems (6) — Require Enchanter 5 + Fabricator 5 #### Hybrid Golems (6) — Require Enchanter 5 + Fabricator 5
@@ -756,12 +791,14 @@ Level 10: 5 slots
| Lava Golem | Earth + Fire | 15 | 1.0/s | 70 | 20% | AOE 2 | | Lava Golem | Earth + Fire | 15 | 1.0/s | 70 | 20% | AOE 2 |
| Galvanic Golem | Metal + Lightning | 10 | 3.5/s | 45 | 45% | Fast | | Galvanic Golem | Metal + Lightning | 10 | 3.5/s | 45 | 45% | Fast |
| Obsidian Golem | Earth + Dark | 25 | 0.8/s | 55 | 50% | High damage | | Obsidian Golem | Earth + Dark | 25 | 0.8/s | 55 | 50% | High damage |
| Prism Golem | Crystal + Light | 20 | 1.5/s | 50 | 35% | AOE 3 | | Prism Golem | Crystal + Light | 28 | 2.0/hr | 60 | 45% | AOE 3 |
| Quicksilver Golem | Metal + Water | 8 | 4.0/s | 40 | 30% | Very fast | | Quicksilver Golem | Metal + Water | 14 | 4.0/hr | 55 | 35% | Very fast |
| Voidstone Golem | Earth + Void | 40 | 0.6/s | 100 | 60% | AOE 3, ultimate | | Voidstone Golem | Earth + Void | 40 | 0.6/s | 100 | 60% | AOE 3, ultimate |
### Golem Combat ### Golem Combat
> ⚠ The golemancy system is undergoing a full redesign (see issue #268). The current data definitions exist but are disconnected from the combat pipeline.
``` ```
progressPerTick = HOURS_PER_TICK × attackSpeed × efficiencyBonus progressPerTick = HOURS_PER_TICK × attackSpeed × efficiencyBonus
damage = baseDamage × (1 + golemMasteryBonus) damage = baseDamage × (1 + golemMasteryBonus)
@@ -778,7 +815,7 @@ Golems last `1 + golemLongevity` floors. Maintenance cost multiplier: `1 - (gole
| Condition | Result | | Condition | Result |
|-----------|--------| |-----------|--------|
| Day 30 reached | Loop ends, gain insight | | Day 30 reached | Loop ends, gain insight |
| Floor 100 + Pact 100 signed | Victory! 3× insight | | Floor 30 reached | Loop ends early, gain insight |
### Insight Formula ### Insight Formula
@@ -791,7 +828,7 @@ baseInsight = floor(maxFloorReached × 15 + totalManaGathered / 500 + signedPact
finalInsight = floor(baseInsight × mult) finalInsight = floor(baseInsight × mult)
``` ```
Victory bonus: ×3 if maxFloorReached ≥ 100 AND floor 100 guardian pact is signed. **Note:** There is currently no specific victory condition or ×3 multiplier. These are awaiting design finalization.
### Prestige Upgrades (14 Types) ### Prestige Upgrades (14 Types)
@@ -810,9 +847,11 @@ Victory bonus: ×3 if maxFloorReached ≥ 100 AND floor 100 guardian pact is sig
| `quickStart` | Quick Start | Start with 100 raw mana | 3 | 400 | | `quickStart` | Quick Start | Start with 100 raw mana | 3 | 400 |
| `elemStart` | Elem. Start | Start with 5 of each unlocked element | 3 | 800 | | `elemStart` | Elem. Start | Start with 5 of each unlocked element | 3 | 800 |
| `unlockedManaTypeCapacity` | Mana Type Capacity | +10 capacity for selected mana type | 5 | 1000 | | `unlockedManaTypeCapacity` | Mana Type Capacity | +10 capacity for selected mana type | 5 | 1000 |
| `pactBinding` | Pact Binding | +1 pact slot per level | 5 | 2000 |
### Pact Persistence ### Pact Persistence
- Signed pacts persist through prestige (bounded by `pactSlots`) - Signed pacts do **NOT** persist through prestige — the player must re-defeat Guardians and re-sign pacts each loop.
- `pactSlots` starting value: 1 (upgradeable via `pactBinding` prestige upgrade)
- `pactSlots` starting value: 1 (upgradeable) - `pactSlots` starting value: 1 (upgradeable)
--- ---
@@ -1107,5 +1146,5 @@ The following systems no longer exist and should not be re-introduced:
--- ---
*Document Version: 4.0 — Updated: 8 stores, 22 mana types, 64 disciplines, 8-tier guardians, localStorage-only* *Document Version: 5.0 — Updated: 8 stores, 22 mana types, 64 disciplines, 14 prestige upgrades, 43 equipment types, 10 golems (redesign pending), 8-tier guardians, localStorage-only*
*End of Game Briefing Document* *End of Game Briefing Document*
+1 -1
View File
@@ -1,4 +1,4 @@
# Circular Dependencies # Circular Dependencies
Generated: 2026-06-04T17:32:36.437Z Generated: 2026-06-04T17:52:04.001Z
No circular dependencies found. ✅ No circular dependencies found. ✅
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"_meta": { "_meta": {
"generated": "2026-06-04T17:32:34.521Z", "generated": "2026-06-04T17:52:02.064Z",
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.", "description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry." "usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
}, },
@@ -4,9 +4,10 @@
> configuration, summoning lifecycle, maintenance costs, room duration, combat > configuration, summoning lifecycle, maintenance costs, room duration, combat
> behavior, and discipline interactions. > behavior, and discipline interactions.
> >
> **⚠ Spec-defined, implementation pending.** This spec is based on > **⚠ Undergoing full redesign (see issue #268).** The golemancy system is being
> `docs/specs/spire-combat-spec.md` §9 and represents the intended design. > redesigned from predefined golem types to a component-based construction system
> The current code has golem data defined but disconnected from the combat pipeline. > (Core + Frame + Mind Circuit + Enchantments). The current data definitions exist
> but are disconnected from the combat pipeline.
--- ---
@@ -190,10 +190,10 @@ The following inconsistencies exist in the codebase:
| Issue | Description | | Issue | Description |
|---|---| |---|---|
| `pactBinding` upgrade | Referenced in `prestigeStore.doPrestige` but **not defined** in `PRESTIGE_DEF` constants | | `pactBinding` upgrade | **RESOLVED** — Added to `PRESTIGE_DEF` in `prestige.ts` |
| UI vs store mismatch | UI displays `prestigeUpgrades.pactCapacity` but store logic checks `pactBinding` | | UI vs store mismatch | **RESOLVED**`pactBinding` is now the canonical ID used everywhere |
| Pact persistence | `signedPacts` is persisted but also reset to `[]` on `startNewLoop` — pacts don't survive loops in current implementation | | Pact persistence | **RESOLVED BY DESIGN** — Pacts intentionally do NOT persist through prestige (reset each loop). This is the correct behavior per design intent. |
| `pactInterferenceMitigation` | Used in `pact-utils.ts` but no prestige upgrade defines it | | `pactInterferenceMitigation` | **RESOLVED** — Added to `PRESTIGE_DEF` in `prestige.ts`; `useGameDerived.ts` now passes it from prestige store |
--- ---
+6 -6
View File
@@ -539,14 +539,14 @@ They are **in scope for the implementation this spec describes**:
| Feature | Where Defined | Status | This Spec's Requirement | | Feature | Where Defined | Status | This Spec's Requirement |
|---|---|---|---| |---|---|---|---|
| Enemy armor reduction | `EnemyState.armor`, `MODIFIER_CONFIG.armored` | Data-only | Implement in `onDamageDealt` §5.2 | | Enemy armor reduction | `EnemyState.armor`, `MODIFIER_CONFIG.armored` | **Implemented** (spells/DoTs only; melee bypasses — see issue #285) | Implement in `onDamageDealt` §5.2 |
| Enemy barrier absorption | `EnemyState.barrier`, `MODIFIER_CONFIG.mage/shield` | Data-only | Implement in `onDamageDealt` §5.2 | | Enemy barrier absorption | `EnemyState.barrier`, `MODIFIER_CONFIG.mage/shield` | **Implemented** (spells/DoTs only; melee bypasses — see issue #285) | Implement in `onDamageDealt` §5.2 |
| Enemy dodge roll | `EnemyState.dodgeChance`, `MODIFIER_CONFIG.agile` | Data-only | Implement in `onDamageDealt` §5.2 | | Enemy dodge roll | `EnemyState.dodgeChance`, `MODIFIER_CONFIG.agile` | **Implemented** (spells/DoTs only; melee bypasses — see issue #285) | Implement in `onDamageDealt` §5.2 |
| Mage barrier recharge | `MODIFIER_CONFIG.mage.barrierRechargeRate` | Data-only | Tick in `onDamageDealt` §5.2 | | Mage barrier recharge | `MODIFIER_CONFIG.mage.barrierRechargeRate` | Data-only | Tick in `onDamageDealt` §5.2 |
| Guardian armor | `GuardianDef.armor` | Data-only | Add check to guardian pipeline §5.3 | | Guardian armor | `GuardianDef.armor` | Data-only | Add check to guardian pipeline §5.3 |
| DoT / debuff system | Spell/enchantment type defs | No runtime | Implement per §6 | | DoT / debuff system | Spell/enchantment type defs | **Implemented**`dot-runtime.ts` complete and wired into combat tick | Verified working; curse amplification needs investigation (see issue #286) |
| Golemancy combat | Full golem data exists | Disconnected | Implement per §9 | | Golemancy combat | Full golem data exists | Disconnected | Implement per §9 |
| Sword melee attacks | Weapon type exists | Not in combat tick | Add `meleeProgress` per §3.1 | | Sword melee attacks | Weapon type exists | **Partial** — meleeProgress exists but bypasses enemy defenses (see issue #285) | Add `meleeProgress` per §3.1 |
| AoE target distribution | `SpellDefinition.aoe` flag | Partial | Implement per §3.2 | | AoE target distribution | `SpellDefinition.aoe` flag | Partial | Implement per §3.2 |
| `elemMasteryBonus` | Stub in `calcDamage` | Hardcoded `1` | Future — leave as `1` for now | | `elemMasteryBonus` | Stub in `calcDamage` | Hardcoded `1` | Future — leave as `1` for now |
| `guardianBonus` | Stub in `calcDamage` | Hardcoded `1` | Future — leave as `1` for now | | `guardianBonus` | Stub in `calcDamage` | Hardcoded `1` | Future — leave as `1` for now |
@@ -612,7 +612,7 @@ interface EnemyState {
| `src/lib/game/constants/elements.ts` | Element list, opposition cycle | | `src/lib/game/constants/elements.ts` | Element list, opposition cycle |
| `src/lib/game/constants/core.ts` | `HOURS_PER_TICK`, `INCURSION_START_DAY` | | `src/lib/game/constants/core.ts` | `HOURS_PER_TICK`, `INCURSION_START_DAY` |
| `src/lib/game/data/guardian-encounters.ts` | Guardian definitions | | `src/lib/game/data/guardian-encounters.ts` | Guardian definitions |
| `src/lib/game/data/golems/` | Golem definitions (12 golems, tiers 14) | | `src/lib/game/data/golems/` | Golem definitions (10 golems, tiers 14; undergoing redesign — see issue #268) |
| `src/lib/game/effects.ts` | `getUnifiedEffects` — merges all combat bonuses | | `src/lib/game/effects.ts` | `getUnifiedEffects` — merges all combat bonuses |
| `src/components/game/tabs/SpireCombatPage/SpireHeader.tsx` | In-game time display | | `src/components/game/tabs/SpireCombatPage/SpireHeader.tsx` | In-game time display |
| `src/components/game/tabs/SpireCombatPage/RoomDisplay.tsx` | Room type, enemy state, active effects | | `src/components/game/tabs/SpireCombatPage/RoomDisplay.tsx` | Room type, enemy state, active effects |
+5 -5
View File
@@ -28,9 +28,9 @@ describe('Tab barrel export', () => {
// ─── Test: Prestige upgrade definitions ──────────────────────────────────────── // ─── Test: Prestige upgrade definitions ────────────────────────────────────────
describe('Prestige upgrade definitions', () => { describe('Prestige upgrade definitions', () => {
it('has exactly 11 prestige upgrades', async () => { it('has exactly 15 prestige upgrades', async () => {
const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige'); const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige');
expect(Object.keys(PRESTIGE_DEF).length).toBe(11); expect(Object.keys(PRESTIGE_DEF).length).toBe(15);
}); });
it('all upgrades have required fields', async () => { it('all upgrades have required fields', async () => {
@@ -43,13 +43,13 @@ describe('Prestige upgrade definitions', () => {
} }
}); });
it('all 11 expected upgrade IDs are present', async () => { it('all 15 expected upgrade IDs are present', async () => {
const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige'); const { PRESTIGE_DEF } = await import('@/lib/game/constants/prestige');
const expectedIds = [ const expectedIds = [
'insightAmp', 'spireKey', 'manaWell', 'manaFlow', 'insightAmp', 'spireKey',
'temporalEcho', 'steadyHand', 'ancientKnowledge', 'elementalAttune', 'temporalEcho', 'steadyHand', 'ancientKnowledge', 'elementalAttune',
'spellMemory', 'guardianPact', 'quickStart', 'elemStart', 'spellMemory', 'guardianPact', 'quickStart', 'elemStart',
'unlockedManaTypeCapacity', 'unlockedManaTypeCapacity', 'pactBinding', 'pactInterferenceMitigation',
]; ];
for (const id of expectedIds) { for (const id of expectedIds) {
expect(PRESTIGE_DEF[id]).toBeDefined(); expect(PRESTIGE_DEF[id]).toBeDefined();
+4
View File
@@ -2,6 +2,8 @@
import type { PrestigeDef } from '../types'; import type { PrestigeDef } from '../types';
export const PRESTIGE_DEF: Record<string, PrestigeDef> = { export const PRESTIGE_DEF: Record<string, PrestigeDef> = {
manaWell: { name: "Mana Well", desc: "+500 starting max mana", max: 5, cost: 500 },
manaFlow: { name: "Mana Flow", desc: "+0.5 regen/sec permanently", max: 10, cost: 750 },
insightAmp: { name: "Insight Amp", desc: "+25% insight gain", max: 4, cost: 1500 }, insightAmp: { name: "Insight Amp", desc: "+25% insight gain", max: 4, cost: 1500 },
spireKey: { name: "Spire Key", desc: "Start at floor +2", max: 5, cost: 4000 }, spireKey: { name: "Spire Key", desc: "Start at floor +2", max: 5, cost: 4000 },
temporalEcho: { name: "Temporal Echo", desc: "+10% mana generation", max: 5, cost: 3000 }, temporalEcho: { name: "Temporal Echo", desc: "+10% mana generation", max: 5, cost: 3000 },
@@ -13,4 +15,6 @@ export const PRESTIGE_DEF: Record<string, PrestigeDef> = {
quickStart: { name: "Quick Start", desc: "Start with 100 raw mana", max: 3, cost: 400 }, quickStart: { name: "Quick Start", desc: "Start with 100 raw mana", max: 3, cost: 400 },
elemStart: { name: "Elem. Start", desc: "Start with 5 of each unlocked element", max: 3, cost: 800 }, elemStart: { name: "Elem. Start", desc: "Start with 5 of each unlocked element", max: 3, cost: 800 },
unlockedManaTypeCapacity: { name: "Mana Type Capacity", desc: "+10 capacity for selected mana type", max: 5, cost: 1000 }, unlockedManaTypeCapacity: { name: "Mana Type Capacity", desc: "+10 capacity for selected mana type", max: 5, cost: 1000 },
pactBinding: { name: "Pact Binding", desc: "+1 pact slot per level", max: 5, cost: 2000 },
pactInterferenceMitigation: { name: "Pact Interference Mitigation", desc: "Reduces pact interference penalty", max: 10, cost: 1500 },
}; };
+6 -4
View File
@@ -143,14 +143,16 @@ export function useCombatStats() {
[activeSpell] [activeSpell]
); );
const pactInterferenceMitigation = usePrestigeStore((s) => s.prestigeUpgrades.pactInterferenceMitigation || 0);
const pactMultiplier = useMemo( const pactMultiplier = useMemo(
() => computePactMultiplier({ signedPacts }), () => computePactMultiplier({ signedPacts, pactInterferenceMitigation }),
[signedPacts] [signedPacts, pactInterferenceMitigation]
); );
const pactInsightMultiplier = useMemo( const pactInsightMultiplier = useMemo(
() => computePactInsightMultiplier({ signedPacts }), () => computePactInsightMultiplier({ signedPacts, pactInterferenceMitigation }),
[signedPacts] [signedPacts, pactInterferenceMitigation]
); );
// DPS calculation // DPS calculation