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.
- Fix summonGolemsForRoom to use golemLoadout instead of removed enabledGolems
- Fix discBonus hardcoded to 0 in golem-combat.ts pipeline (now computed from discipline effects)
- Remove deprecated types: SummonedGolem, ActiveGolem, GolemDef
- Remove legacy GolemancyState fields: enabledGolems, summonedGolems, legacyActiveGolems
- Remove orphaned store actions: toggleGolem, setEnabledGolems
- Delete orphaned legacy data files: base-golems.ts, elemental-golems.ts, hybrid-golems.ts
- Update GolemDebugSection to use new golemLoadout system
- Update golemancy-spec.md §17 to reflect all features as complete
- Update all test files to match new type shapes
- All 958 tests passing
The curse debuff was stored on enemies via dot-runtime.ts but the
amplification was never applied to incoming damage. Added curse
magnitude check in applyEnemyDefenses (combat-tick.ts) that multiplies
incoming damage by (1 + magnitude) for each active curse effect.
- Curse amplification applied BEFORE dodge/barrier/armse defenses
- Multiple curse effects stack multiplicatively
- Non-curse effects (burn, freeze, etc.) are ignored for amplification
Also updated spire-combat-spec.md Known Gaps table to reflect:
- Melee defense bypass fixed (issue #285)
- Curse amplification now implemented (issue #286)
Added 9 regression tests in curse-amplification.test.ts.
All 957 tests pass (50 test files).
Bug: Melee sword attacks passed null as the enemy to applyEnemyDefenses,
causing all enemy defenses (armor, barrier, dodge) to be bypassed.
Spell damage and DoT effects correctly went through defenses.
Fix: In combat-actions.ts melee loop, get the current target enemy
from currentRoom.enemies (lowest HP, matching focus-fire targeting)
and pass it to applyEnemyDefenses instead of null.
Added 7 regression tests in melee-defense-bypass.test.ts to verify:
- Melee damage is reduced by armor
- Melee damage is reduced by barrier
- Unarmored enemies take more damage than armored ones
- Full damage dealt when no defenses
- Focus-fire targeting (lowest HP enemy)
- Graceful handling of empty enemy list
- Comparison proving defense application
All 948 tests pass (49 test files).
- Add new golem component types (Core, Frame, MindCircuit, Enchantment)
- Create 4 Core tiers, 7 Frames, 4 Mind Circuits, 8 Enchantments
- Rewrite golem utils for component-based stat computation
- Update GolemancyState with new fields (golemDesigns, golemLoadout, activeGolems)
- Update combat store, actions, and pipelines for new golem system
- Rewrite GolemancyTab with component selection UI
- Update fabricator discipline perks for new system
- Add comprehensive tests for component registries and utilities
- All files under 400 lines, all 743 tests passing
- New files: element-distance.ts, conversion-costs.ts, conversion-rates.ts
- All conversion types (discipline, attunement, pact) use unified formula
- Conversion costs scale exponentially by element tier (10^(d+1) raw, 10*(d+1) per component)
- Costs deducted from regen, not from mana pool
- Auto-pause on insufficient regen with UI warning
- Meditation boosts conversion rates (reduced by distance)
- Attunement levels provide +50% multiplicative bonus per level
- Guardian pacts provide +0.15/hr base rate + invoker level bonus
- Removed convertMana, processConvertAction, craftComposite from manaStore
- Stats tab shows per-element conversion breakdown with formulas
- ManaDisplay shows per-element net regen rates
- All 916 tests pass, all files under 400 lines
- Add applyDamageToRoom() function targeting individual enemies
- Add lowestHPEnemy() helper for focus-fire targeting
- AoE spells distribute damage across all enemies
- Single-target attacks hit lowest HP enemy first
- Refactor processCombatTick spell/equipment/melee/DoT damage to use per-enemy system
- Update golemApplyDamageToRoom in golem-combat pipeline for per-enemy targeting
- Add currentRoom to CombatTickResult and sync through gameStore
- Update combat-actions tests with proper enemy setup for per-enemy tests
- Extract combat-damage.ts module to stay under 400-line limit
- Add calcMeleeDamage() with elemental matchup for enchanted swords
- Add meleeSwordProgress per-instance accumulator to combat state
- Add melee branch in processCombatTick (no mana cost, no Executioner/Berserker)
- Add baseDamage/attackSpeed stats to all 5 sword types
- Wire equippedSwords through gameStore to combat tick pipeline
- 16 new regression tests, all 937 tests pass
- 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)
- Add ActiveGolem interface and activeGolems to GolemancyState
- Add maxRoomDuration to all 12 golem definitions
- Create golem-combat-actions.ts with pure golem combat logic (summoning, maintenance, attacks, room-duration)
- Create golem-combat.ts pipeline for golem combat setup
- Wire golem maintenance and attacks into processCombatTick
- Wire golem summoning into advanceRoomOrFloor on room entry
- Wire golem room-duration countdown into onFloorCleared callback
- Update combat-actions tests for new processCombatTick signature
- All 921 tests pass, all files under 400-line limit
Closes#259
Enchanter test:
- Use data-testid selectors for debug buttons
- Add waitForBridge pattern
- Switch baseURL to localhost:3000
Fabricator test:
- Use data-testid selectors for all debug buttons (attunements, elements, disciplines, materials)
- Activate discipline via toggle button before adding XP
- Unlock recipes via discipline XP + store unlockRecipes
- Replace waitForTimeout with runTicks for crafting (instant tick-based waiting)
- Add ticksForHours helper for deterministic craft completion
- Verify each craft completed via store check instead of currentAction polling
- Remove direct store manipulation for attunement/element unlock (use debug UI buttons)
- Disciplines section starts collapsed: click header to expand
- Raw Mana Mastery must be activated before XP can be added (handleAddXP bails if discipline not in store)
- Also needed because debugBridge changes require a fresh build (dev server was stale)
- Rewrite e2e/fabricator-happy-path.spec.ts from scratch:
craft 8 gear pieces (1 per slot) via Fabricator UI,
equip all via store bridge, verify equipment effects
- Add debugBridge.ts: exposes Zustand stores on window.__TEST__
so Playwright can call store actions via page.evaluate()
- Import debugBridge in page.tsx as side-effect
- Uses Debug tab UI for attunement/element unlocking
- Uses store bridge for discipline XP, mana capacity, materials,
recipe unlocking, and equipment slot assignment
- Test passes in ~3.5 minutes (155s craft time + setup)