diff --git a/AGENTS.md b/AGENTS.md index f57093d..5d3914d 100755 --- a/AGENTS.md +++ b/AGENTS.md @@ -63,16 +63,16 @@ Use for 3+ sequential independent calls. Zero context from parent — paste ever ### Adding Disciplines 1. Choose the correct data file under `data/disciplines/`: - - `base.ts` — Raw Mana Mastery (available to all) - - `elemental.ts` — Elemental Attunement (7 base+ elements) - - `elemental-regen.ts` — Elemental Regen (7 base + transference) - - `elemental-regen-advanced.ts` — Advanced Regen (3 composite + 3 exotic) + - `base.ts` — Raw Mana Mastery (3 disciplines) + - `elemental.ts` — Elemental Attunement (21 disciplines — all 22 mana types) + - `elemental-regen.ts` — Elemental Regen (8 disciplines — 7 base + transference) + - `elemental-regen-advanced.ts` — Advanced Regen (15 disciplines — 8 composite + 6 exotic + transference composite) - `enchanter.ts` — Core Enchanter disciplines (4 disciplines) - `enchanter-utility.ts` — Utility enchantment disciplines (2 disciplines) - `enchanter-spells.ts` — Spell enchantment disciplines (3 disciplines) - `enchanter-special.ts` — Special enchantment disciplines (1 discipline) - `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`): - `statBonus.stat` must match a key consumed by `computeDisciplineEffects()` - 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 ### 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 - 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 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) +- Miasma counters air (and air counters miasma); Shadow glass counters light (and light counters shadow glass) - All mana types double as spell elements - 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) @@ -145,9 +146,9 @@ ManaDrainPerTick = drainBase × (1 + (XP / difficultyFactor)^0.4) ### Prestige (Insight) - `baseInsight = floor(maxFloorReached × 15 + totalManaGathered / 500 + signedPacts.length × 150)` -- Multiplied by discipline and boon bonuses. ×3 for victory (floor 100 + pact signed) -- 14 prestige upgrade types: manaWell, manaFlow, insightAmp, spireKey, temporalEcho, steadyHand, ancientKnowledge, elementalAttune, spellMemory, guardianPact, quickStart, elemStart, unlockedManaTypeCapacity -- Signed pacts persist through prestige (bounded by `pactSlots`) +- 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, pactBinding +- Signed pacts do NOT persist through prestige (reset each loop) ### Starting State - Attunement: Enchanter only (level 1) diff --git a/docs/GAME_BRIEFING.md b/docs/GAME_BRIEFING.md index c661a79..f4e61e4 100644 --- a/docs/GAME_BRIEFING.md +++ b/docs/GAME_BRIEFING.md @@ -1,7 +1,7 @@ # Mana-Loop: Comprehensive Game Briefing Document **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) - Equipment-based spell system (spells come from enchanted gear and learned spells) - 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 pacts provide permanent multipliers that persist through prestige - 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 | | `HOURS_PER_TICK` | 0.04 | Game hours per tick | | `MAX_DAY` | 30 | Days per loop | -| `INCURSION_START_DAY` | 5 | When incursion begins | +| `INCURSION_START_DAY` | 20 | When incursion begins | ### Time Progression - 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 ``` -if (day < 5): incursionStrength = else: incursionStrength = min(0.95, (totalHours / maxHours) × 0.95) - where totalHours = (day - 5) × 24 + hour - maxHours = (30 - 5) × 24 = 600 +if (day < 20): incursionStrength = 0 +else: incursionStrength = min(0.95, (totalHours / maxHours) × 0.95) + 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 | |-----------|--------|-------------| | **Guardian** | Every 10th floor | Boss encounter | -| **Puzzle** | 20% on every 7th floor | Attunement-themed trial | -| **Swarm** | 15% | 3–6 enemies with 40% HP each | -| **Speed** | 10% | Enemy with dodge chance (25% base + 0.5%/floor, max 50%) | -| **Combat** | Default | Single enemy, normal combat | +| **Puzzle** | 100% on every 7th floor (except guardian floors) | Attunement-themed trial | +| **Swarm** | ~12% | 3–7 weak enemies | +| **Speed** | ~10% | Single enemy with elevated dodge chance | +| **Combat** | ~68% (default) | Single enemy, normal combat | +| **Recovery** | ~4% | 1 hour; grants 10× mana regen & conversion | +| **Treasure** | ~3% | 1 hour; grants 2–15 random items | +| **Library** | ~3% | 1 hour; grants discipline XP at 25× rate | ### Puzzle Room Types - 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× | | Neutral / Raw mana spells | 1.00× | -**Element Opposites:** +**Element Opposites (bidirectional):** ``` Fire ↔ Water Air ↔ Earth Light ↔ Dark -Fire ↔ Frost Lightning → Earth (grounding) -BlackFlame ↔ Water Radiant Flames ↔ Dark -Miasma ↔ Light Shadow Glass ↔ Light +Frost ↔ Fire +``` + +**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 @@ -339,40 +354,57 @@ Miasma ↔ Light Shadow Glass ↔ Light | Floor | Name | Element | Armor | Pact Mult | Pact Cost | Pact Time | |-------|------|---------|-------|-----------|-----------|-----------| -| 10 | Ignis Prime | fire | 10% | 1.5× | 500 | 2h | -| 20 | Aqua Regia | water | 15% | 1.75× | 1,000 | 4h | -| 30 | Ventus Rex | air | 18% | 2.0× | 2,000 | 6h | -| 40 | Terra Firma | earth | 25% | 2.25× | 4,000 | 8h | -| 50 | Lux Aeterna | light | 20% | 2.5× | 8,000 | 10h | -| 60 | Umbra Mortis | dark | 22% | 2.75× | 15,000 | 12h | -| 70 | Mors Ultima | death | 25% | 3.0× | 25,000 | 14h | -| 80 | Vinculum Arcana | transference | 20% | 3.25× | 35,000 | 16h | +| 10 | Ignis Prime | fire | 10% | 1.5× | formula | 2h | +| 20 | Aqua Regia | water | 15% | 1.75× | formula | 4h | +| 30 | Ventus Rex | air | 20% | 2.0× | formula | 6h | +| 40 | Terra Firma | earth | 25% | 2.25× | formula | 8h | +| 50 | Lux Aeterna | light | 30% | 2.5× | formula | 10h | +| 60 | Umbra Mortis | dark | 35% | 2.75× | formula | 12h | +| 70 | Mors Ultima | death | 40% | 3.0× | formula | 14h | +| 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 90–160) | Floor | Element | Armor | Pact Mult | Pact Cost | Pact Time | |-------|---------|-------|-----------|-----------|-----------| -| 90 | metal | 30% | 3.5× | 60,000 | 18h | -| 100 | sand | 25% | 3.75× | 80,000 | 20h | -| 110 | lightning | 22% | 4.0× | 100,000 | 22h | -| 120 | frost | 28% | 4.25× | 125,000 | 24h | -| 130 | blackflame | 32% | 4.5× | 140,000 | 26h | -| 140 | radiantflames | 25% | 4.75× | 160,000 | 28h | -| 150 | miasma | 28% | 5.0× | 180,000 | 30h | -| 160 | shadowglass | 33% | 5.25× | 200,000 | 32h | +| 90 | metal | 50% | 3.5× | formula | 18h | +| 100 | sand+fire+earth | 55% | 3.75× | formula | 20h | +| 110 | lightning | 60% | 4.0× | formula | 22h | +| 120 | frost | 65% | 4.25× | formula | 24h | +| 130 | metal+fire+earth (blackflame) | 70% | 4.5× | formula | 26h | +| 140 | sand+earth+water (radiantflames) | 75% | 4.75× | formula | 28h | +| 150 | lightning+fire+air (miasma) | 80% | 5.0× | formula | 30h | +| 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 170–240) | Floor | Element | Armor | Pact Mult | Pact Cost | Pact Time | |-------|---------|-------|-----------|-----------|-----------| -| 170 | crystal | 35% | 5.5× | 220,000 | 37h | -| 180 | stellar | 30% | 6.0× | 250,000 | 38h | -| 190 | void | 35% | 6.5× | 300,000 | 39h | -| 200 | soul | 30% | 7.0× | 330,000 | 40h | -| 210 | time | 32% | 7.5× | 370,000 | 41h | -| 220 | plasma | 28% | 8.0× | 400,000 | 42h | -| 230 | crystal+stellar+void | 40% | 8.5× | 430,000 | 43h | -| 240 | soul+time+plasma | 42% | 9.0× | 460,000 | 44h | +| 170 | crystal | 35% | 5.5× | formula | 37h | +| 180 | stellar | 30% | 6.0× | formula | 38h | +| 190 | void | 35% | 6.5× | formula | 39h | +| 200 | crystal+stellar+void | 35% | 7.0× | formula | 40h | +| 210 | soul+time+plasma | 32% | 7.5× | formula | 41h | +| 220 | plasma | 28% | 8.0× | formula | 42h | +| 230 | crystal+stellar+void | 40% | 8.5× | formula | 43h | +| 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 90–240 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) ### 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 | |------|------|-------|----------| | Base | `base.ts` | 3 | None | -| Elemental Attunement | `elemental.ts` | 22 | None | +| Elemental Attunement | `elemental.ts` | 21 | 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 Utility | `enchanter-utility.ts` | 2 | 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 -offHand - Shields, Catalysts (blocked by 2-handed weapons) +offHand - Catalysts, Spell Focuses (blocked by 2-handed weapons) head - Hoods, Hats, Helms body - Robes, Armor hands - Gloves, Gauntlets @@ -626,19 +660,20 @@ accessory1 - Rings, Amulets accessory2 - Rings, Amulets ``` -### Equipment Categories & Types (50 total) +### Equipment Categories & Types (43 total) | Category | Slot | Count | Examples | |----------|------|-------|----------| | 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 | -| 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 | | Head | head | 5 | Cloth Hood, Apprentice Cap, Wizard Hat, Arcanist Circlet, Battle Helm | | Hands | hands | 4 | Civilian Gloves, Apprentice Gloves, Spellweave Gloves, Combat Gauntlets | | 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 | 11 | Copper Ring, Silver Ring, Gold Ring, Signet Ring, Copper Amulet, Silver Amulet, Crystal Pendant, Mana Brooch, Arcanist Pendant, Void-Touched Ring | +| 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 | + +**Total: 43 equipment types** across 8 categories. Shields are banned. Catalysts can be equipped in offHand slot. ### 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. @@ -733,7 +768,7 @@ Level 8: 4 slots Level 10: 5 slots ``` -### Golem Types (10 Total) +### Golem Types (10 Total — undergoing redesign, see issue #268) #### Base Golems (1) @@ -747,7 +782,7 @@ Level 10: 5 slots |-------|---------|--------|-------|----|--------|--------| | Steel Golem | Metal | 12 | 1.2/s | 60 | 35% | Metal 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 @@ -756,12 +791,14 @@ Level 10: 5 slots | Lava Golem | Earth + Fire | 15 | 1.0/s | 70 | 20% | AOE 2 | | Galvanic Golem | Metal + Lightning | 10 | 3.5/s | 45 | 45% | Fast | | Obsidian Golem | Earth + Dark | 25 | 0.8/s | 55 | 50% | High damage | -| Prism Golem | Crystal + Light | 20 | 1.5/s | 50 | 35% | AOE 3 | -| Quicksilver Golem | Metal + Water | 8 | 4.0/s | 40 | 30% | Very fast | +| Prism Golem | Crystal + Light | 28 | 2.0/hr | 60 | 45% | AOE 3 | +| 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 | ### 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 damage = baseDamage × (1 + golemMasteryBonus) @@ -778,7 +815,7 @@ Golems last `1 + golemLongevity` floors. Maintenance cost multiplier: `1 - (gole | Condition | Result | |-----------|--------| | 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 @@ -791,7 +828,7 @@ baseInsight = floor(maxFloorReached × 15 + totalManaGathered / 500 + signedPact 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) @@ -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 | | `elemStart` | Elem. Start | Start with 5 of each unlocked element | 3 | 800 | | `unlockedManaTypeCapacity` | Mana Type Capacity | +10 capacity for selected mana type | 5 | 1000 | +| `pactBinding` | Pact Binding | +1 pact slot per level | 5 | 2000 | ### 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) --- @@ -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* diff --git a/docs/circular-deps.txt b/docs/circular-deps.txt index 48e8631..0f7182f 100644 --- a/docs/circular-deps.txt +++ b/docs/circular-deps.txt @@ -1,4 +1,4 @@ # Circular Dependencies -Generated: 2026-06-04T17:32:36.437Z +Generated: 2026-06-04T17:52:04.001Z No circular dependencies found. ✅ diff --git a/docs/dependency-graph.json b/docs/dependency-graph.json index 6a66b51..9c836b2 100644 --- a/docs/dependency-graph.json +++ b/docs/dependency-graph.json @@ -1,6 +1,6 @@ { "_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.", "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." }, diff --git a/docs/specs/attunements/fabricator/systems/golemancy-spec.md b/docs/specs/attunements/fabricator/systems/golemancy-spec.md index 61be614..9ab92db 100644 --- a/docs/specs/attunements/fabricator/systems/golemancy-spec.md +++ b/docs/specs/attunements/fabricator/systems/golemancy-spec.md @@ -4,9 +4,10 @@ > 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. +> **⚠ Undergoing full redesign (see issue #268).** The golemancy system is being +> redesigned from predefined golem types to a component-based construction system +> (Core + Frame + Mind Circuit + Enchantments). The current data definitions exist +> but are disconnected from the combat pipeline. --- diff --git a/docs/specs/attunements/invoker/invoker-spec.md b/docs/specs/attunements/invoker/invoker-spec.md index 4d12bdd..a484cba 100644 --- a/docs/specs/attunements/invoker/invoker-spec.md +++ b/docs/specs/attunements/invoker/invoker-spec.md @@ -190,10 +190,10 @@ 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 | +| `pactBinding` upgrade | ✅ **RESOLVED** — Added to `PRESTIGE_DEF` in `prestige.ts` | +| UI vs store mismatch | ✅ **RESOLVED** — `pactBinding` is now the canonical ID used everywhere | +| Pact persistence | ✅ **RESOLVED BY DESIGN** — Pacts intentionally do NOT persist through prestige (reset each loop). This is the correct behavior per design intent. | +| `pactInterferenceMitigation` | ✅ **RESOLVED** — Added to `PRESTIGE_DEF` in `prestige.ts`; `useGameDerived.ts` now passes it from prestige store | --- diff --git a/docs/specs/spire-combat-spec.md b/docs/specs/spire-combat-spec.md index 0de450a..f4be9e5 100644 --- a/docs/specs/spire-combat-spec.md +++ b/docs/specs/spire-combat-spec.md @@ -539,14 +539,14 @@ They are **in scope for the implementation this spec describes**: | Feature | Where Defined | Status | This Spec's Requirement | |---|---|---|---| -| Enemy armor reduction | `EnemyState.armor`, `MODIFIER_CONFIG.armored` | Data-only | Implement in `onDamageDealt` §5.2 | -| Enemy barrier absorption | `EnemyState.barrier`, `MODIFIER_CONFIG.mage/shield` | Data-only | Implement in `onDamageDealt` §5.2 | -| Enemy dodge roll | `EnemyState.dodgeChance`, `MODIFIER_CONFIG.agile` | 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` | **Implemented** (spells/DoTs only; melee bypasses — see issue #285) | 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 | | 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 | -| 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 | | `elemMasteryBonus` | 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/core.ts` | `HOURS_PER_TICK`, `INCURSION_START_DAY` | | `src/lib/game/data/guardian-encounters.ts` | Guardian definitions | -| `src/lib/game/data/golems/` | Golem definitions (12 golems, tiers 1–4) | +| `src/lib/game/data/golems/` | Golem definitions (10 golems, tiers 1–4; undergoing redesign — see issue #268) | | `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/RoomDisplay.tsx` | Room type, enemy state, active effects | \ No newline at end of file diff --git a/src/components/game/tabs/PrestigeTab.test.ts b/src/components/game/tabs/PrestigeTab.test.ts index ba14a24..e2ee55b 100644 --- a/src/components/game/tabs/PrestigeTab.test.ts +++ b/src/components/game/tabs/PrestigeTab.test.ts @@ -28,9 +28,9 @@ describe('Tab barrel export', () => { // ─── Test: 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'); - expect(Object.keys(PRESTIGE_DEF).length).toBe(11); + expect(Object.keys(PRESTIGE_DEF).length).toBe(15); }); 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 expectedIds = [ - 'insightAmp', 'spireKey', + 'manaWell', 'manaFlow', 'insightAmp', 'spireKey', 'temporalEcho', 'steadyHand', 'ancientKnowledge', 'elementalAttune', 'spellMemory', 'guardianPact', 'quickStart', 'elemStart', - 'unlockedManaTypeCapacity', + 'unlockedManaTypeCapacity', 'pactBinding', 'pactInterferenceMitigation', ]; for (const id of expectedIds) { expect(PRESTIGE_DEF[id]).toBeDefined(); diff --git a/src/lib/game/constants/prestige.ts b/src/lib/game/constants/prestige.ts index 7e23157..d32433e 100644 --- a/src/lib/game/constants/prestige.ts +++ b/src/lib/game/constants/prestige.ts @@ -2,6 +2,8 @@ import type { PrestigeDef } from '../types'; export const PRESTIGE_DEF: Record = { + 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 }, 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 }, @@ -13,4 +15,6 @@ export const PRESTIGE_DEF: Record = { 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 }, 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 }, }; diff --git a/src/lib/game/hooks/useGameDerived.ts b/src/lib/game/hooks/useGameDerived.ts index 85419b4..7c12b3d 100644 --- a/src/lib/game/hooks/useGameDerived.ts +++ b/src/lib/game/hooks/useGameDerived.ts @@ -143,14 +143,16 @@ export function useCombatStats() { [activeSpell] ); + const pactInterferenceMitigation = usePrestigeStore((s) => s.prestigeUpgrades.pactInterferenceMitigation || 0); + const pactMultiplier = useMemo( - () => computePactMultiplier({ signedPacts }), - [signedPacts] + () => computePactMultiplier({ signedPacts, pactInterferenceMitigation }), + [signedPacts, pactInterferenceMitigation] ); const pactInsightMultiplier = useMemo( - () => computePactInsightMultiplier({ signedPacts }), - [signedPacts] + () => computePactInsightMultiplier({ signedPacts, pactInterferenceMitigation }), + [signedPacts, pactInterferenceMitigation] ); // DPS calculation