Issue #346: Added floorHP > 0 guard to primary and equipment spell casting
while loops in combat-actions.ts. Previously spells continued casting and
draining mana after all enemies were dead.
Issue #345 Bug A: Populated equipmentSpellStates from equipped gear in
gameStore.ts combat tick setup using getActiveEquipmentSpells(). Previously
equipment spell enchantments (e.g., spell_manaBolt on casters) never fired
during combat because equipmentSpellStates was always empty.
Issue #345 Bug B: Added deductWeaponEnchantCosts() helper in combat-damage.ts
and wired it into the melee loop in combat-actions.ts. Weapon enchant spells
(fireBlade, frostBlade, lightningBlade, voidBlade) now properly deduct mana
per melee hit instead of providing free elemental bonus damage.
All 1141 tests pass (65 test files), no regressions. Added 5 new regression tests.
Files changed:
- combat-actions.ts: +floorHP>0 guards, weapon enchant cost deduction
- combat-damage.ts: +deductWeaponEnchantCosts() helper
- gameStore.ts: +equipment spell state population from equipped gear
- spell-cast-floorhp-guard.test.ts: new regression test file
Two root causes fixed:
1. gameStore.ts: computeRegen now receives actual attunements instead of empty {}, so rawGrossRegen includes attunement contributions (was ~2/hr, now correct 3000+/hr)
2. gameStore.ts buildConversionParams: use rawManaRegen with level scaling (1.5^(level-1)) instead of conversionRate for per-element grossRegen
3. LeftPanel.tsx: same grossRegen fix + include attunement regen in rawGrossRegen display
4. ElementStatsSection.tsx: same grossRegen fix
5. useGameDerived.ts: pass actual attunements to computeRegen for baseRegen calculation
Added regression test: conversion-pause-bug-regression.test.ts (7 tests)
- processTick() now calls calculateManaDrain() for non-conversion disciplines
- Insufficient mana triggers auto-paused state (skips XP accrual)
- Conversion disciplines (sourceManaTypes) correctly skip pool drain
- Auto-paused disciplines can be re-activated when mana is restored
- Updated 7 tests across 3 files to reflect drain model
- Use formatHour() instead of raw {hour} to display time in HH:MM format
(e.g. '15:00' instead of '14.999999999999998')
- Export formatHour from stores barrel index
- Fixes#336
- Add missing elements to mana store migration: when loading an old save
that doesn't have all elements from ELEMENTS (e.g. saves from before
composite/exotic elements were added), the migration now creates the
missing elements with proper default state
- Harden unlockElement action to handle the case where an element doesn't
exist in the store (creates a valid element state instead of corrupting
with { unlocked: true } missing current/max/baseMax)
- Add regression tests (14 tests) for Earth element unlock and migration
scenarios including old saves with missing elements
Fixes#338, #339
- Add activeCraftingSubTab state + setActiveCraftingSubTab action to
craftingStore (persisted to localStorage via zustand persist middleware)
- Add CraftingAttunement type to craftingStore.types and re-export from
stores/index.ts barrel
- Update CraftingTab.tsx to read/write active sub-tab from store instead
of local useState, so selection survives component remount
- Add default 'fabricator' value in craft-initial-state.ts
- Add getTwoHandedBlocker() helper to EquipmentSlotGrid that detects when
mainHand has a two-handed weapon and returns the weapon name
- Render offHand slot with Lock icon + 'Blocked: <weapon name>' label +
reduced opacity when blocked, distinct from normal 'Empty' dashed-border slot
- Add regression test verifying all 4 two-handed types are correctly flagged
and non-two-handed types remain unblocked (11 tests)
DISC-2: Removed old pool-drain model from discipline-slice.ts processTick()
- Disciplines no longer drain rawMana or element pools
- canProceedDiscipline() no longer checks mana sufficiency
- Removed auto-pause on insufficient mana from processTick()
- Removed drain display and auto-paused message from DisciplineCard.tsx
- Removed auto-paused log from gameStore.ts tick()
DISC-4: Audited crafting pipeline — no composite crafting logic remains
- craftComposite already removed from manaStore.ts (comment only)
- No other composite crafting references found
DISC-5: Added collapsible formula reference to Conversion Stats section
- Shows unified formula, multipliers, cost formulas, and constraints
DISC-6: Added per-element net regen summary line
- Shows 'Net Fire Regen: +0.50/hr − 0.15/hr = +0.35/hr' per element
DISC-7: Created dedicated 'Conversion Stats' section in Stats tab
- Renamed from 'Conversion Breakdown' to dedicated section header
DISC-8: Added detailed per-element regen breakdown to ManaDisplay
- Each element card now expandable to show produced rate and downstream drains
- New ElementRegenBreakdown type and elementRegenBreakdown prop
Tests: Updated 4 test files to reflect new no-drain behavior
- All 1090 tests pass
- D2: Move spell_iceShard to basic-spells.ts at cost 75 (canonical), remove duplicate from frost-spells.ts
- D7: disenchantEquipment now adds 'Ready for Enchantment' tag and resets rarity to 'common'
- D8: disenchantEquipment now credits recovered mana to raw mana pool
- D14: Wire enchantPower stat from discipline effects into efficiencyBonus via new getEnchantingEfficiencyBonus() helper
- D3: Fix spec file reference from crafting-attunements.ts to data/attunements.ts
- D1/D6: Add missing metalSpellFocus to spec capacity table
- D-SLOT-01: Verified slot cap of 7 matches spec §2.2 (no change needed)
- D-COMB-03: Implement AoE damage distribution for Sand/Shadowglass frames
- D-COMB-01: Reconcile armor pierce formula to spire-combat spec §9.4 (dmg × (1 + armorPierce))
- D-CIRC-01: Fix Simple Logic Circuit summon cost from raw to earth mana
- D-ENCHANT-03: Add dual_attunement unlockRequirement to all golem enchantments
- D-CORE-01/02: Add Guardian Core runtime override mechanism for guardian-specific mana
Also increased test timeouts for module import tests that timeout in full suite runs.
- Fix conversion rate level scaling from linear (1+level*0.5) to exponential (1.5^(level-1)) in conversion-rates.ts
- Fix getAttunementLevelMultiplier formula to match spec §4.3
- Add level-up logging in attunementStore.ts via combat store addActivityLog
- Clarify getAttunementConversionRate returns flat base rate (level scaling applied separately)
- Update spec §8 to describe time-based puzzle room system matching code implementation
- Add 17 regression tests verifying exponential scaling, base rate behavior, and spec table values
D-01: Implement per-weapon cast progress (weaponCastProgress record)
D-04: Bypass Executioner/Berserker discipline specials for golem attacks
D-09: Fix lightning counter direction (lightning→water, not lightning→earth)
D-10: Add full composite element counters (blackflame/radiantflames ↔ frost/water/light/dark)
D-15: Fix Executioner to check per-enemy HP < 25% instead of floorHP ratio
D-20: Fix dodge formula to match spec (min(0.55, floor × 0.003), starts at 0)
D-22: Fix shield modifier to use flat HP pool instead of percentage barrier
D-23: Wire up applyMageBarrierRecharge in the damage pipeline
D-25: Move guardian regen from per-damage-event to once-per-tick
D-26: Add guardian armor reduction to the guardian defensive pipeline
D-31: Fix armor_corrode to be temporary (restore armor on effect expiry)
D-38: Implement AoE damage distribution across enemies
All 1069 tests pass. No files exceed 400 lines.
- Remove Spells tab trigger from TabTriggers in page.tsx
- Remove TabsContent value='spells' block in page.tsx
- Remove lazy import of SpellsTab in page.tsx
- Change default activeTab from 'spells' to 'disciplines'
- Remove SpellsTab re-export from tabs/index.ts
- Remove SpellsTab re-export from game/index.ts
- Delete src/components/game/tabs/SpellsTab.tsx
Spell data (SPELLS_DEF, spells store state) preserved - spells still exist as enchantments.
Fixes#297: Library room XP now uses 25× rate without undocumented floor multiplier
Fixes#305: Prep time mana-per-tick now applies Math.floor(capacity/50) per spec
Fixes#304: Dual design slot correctly returns false when first slot is empty
Fixes#299: Seed calculation now includes runId component per spec (seed = floor × 12345 + runId)
Fixes#298: Treasure loot now uses seeded random instead of Math.random()
Changes:
- Added runId field to CombatState type
- Generated random runId on spire entry in createEnterSpireMode
- Updated getRoomsForFloor, generateSpireRoomType, generateSpireFloorState, generateTreasureLoot to accept and use runId
- Updated all call sites in combat-descent-actions.ts and combatStore.ts
- Treasure loot item count now uses seeded RNG instead of Math.random()
- Fix multi-element guardians (floors 130,140,150,170,190,200,210,230,240) to have exactly 2 boons per spec instead of 3-4
- Fix signedPactDetails never being populated: pipeline processPactRitual now includes signedPactDetails in writes with floor, guardianId, signedAt time, and skillLevels
- Fix completePactRitual in prestigeStore to also populate signedPactDetails
- Update gameStore.ts call site to pass signedPactDetails and current day/hour to processPactRitual
Fixes#309, fixes#308
Bug #293: elementDrain was computed but never subtracted from element pools.
The tick pipeline now applies net regen (produced - drained) instead of
gross production to element pools, matching the spec §8 regen deduction model.