[High] [Bug] Active disciplines not deactivated when entering the Spire #347

Closed
opened 2026-06-10 10:04:18 +02:00 by Anexim · 3 comments
Owner

Bug: Active disciplines not deactivated when entering the Spire

Steps to reproduce

  1. Start the game and activate one or more disciplines (e.g., Raw Mana Mastery)
  2. Let them accumulate some XP
  3. Go to the Spire Summary tab and click "Enter Spire"
  4. Observe: disciplines remain active and continue to accrue XP and drain mana while climbing the Spire

Expected behavior

When entering the Spire, all active disciplines should be deactivated (stopped). The player is now focused on climbing/combat, not practicing disciplines. Disciplines should not drain mana or gain XP during spire runs.

Actual behavior

Disciplines remain fully active during the entire spire run. They continue to:

  • Drain mana every tick
  • Accrue XP every tick
  • Fire onStartPracticing callbacks (setting currentAction to 'practicing', which conflicts with 'climb')

Root cause analysis

File 1: src/lib/game/stores/combat-descent-actions.ts (lines 247-284)
createEnterSpireMode() sets spireMode: true and currentAction: 'climb' but never calls useDisciplineStore.getState().deactivate() for each active discipline, nor does it clear activeIds.

File 2: src/lib/game/stores/combatStore.ts (lines 186-216)
exitSpireMode() resets currentAction to 'meditate' but also never deactivates disciplines. If disciplines were somehow still active from before entering, they would resume ticking after exit.

File 3: src/lib/game/stores/gameStore.ts (line 231)
The tick() function calls useDisciplineStore.getState().processTick(...) unconditionally — there is no guard like if (ctx.combat.spireMode) return to skip discipline processing during spire runs.

File 4: src/lib/game/stores/discipline-slice.ts
The discipline store has zero awareness of spire mode. It imports no spire-related state and checks no spire flag in processTick().

Evidence

  • grep for spireMode in discipline-slice.ts returns zero matches
  • grep for discipline in combat-descent-actions.ts returns only the computeDisciplineEffects import (for golem summoning), with no discipline deactivation logic
  • The enterSpireMode function in combat-descent-actions.ts does not import or call any discipline store actions
  • The exitSpireMode function in combatStore.ts does not import or call any discipline store actions

Severity: High

This is a high-severity bug because:

  1. Mana drain: Active disciplines drain mana during spire combat, directly harming the player's ability to cast spells and survive
  2. XP exploitation: Players gain discipline XP "for free" during spire runs without the intended trade-off of not being able to practice while climbing
  3. State conflict: The practicingCallbacks system sets currentAction to 'practicing' when disciplines are active, but spire mode sets it to 'climb' — these conflict, and the last setter wins depending on tick ordering
  4. Design intent: The spire is meant to be a focused combat challenge; disciplines represent meditation/practice activities that should be mutually exclusive with climbing

Suggested fix locations

  1. combat-descent-actions.tscreateEnterSpireMode(): After setting spire state, iterate useDisciplineStore.getState().activeIds and call deactivate(id) for each, or add a new deactivateAll() action to the discipline store
  2. gameStore.tstick(): Add an early return or skip for discipline processTick when ctx.combat.spireMode is true (defense-in-depth)
  3. Optionally: combatStore.tsexitSpireMode(): Also deactivate disciplines on exit for safety

Affected files

  • src/lib/game/stores/combat-descent-actions.ts
  • src/lib/game/stores/combatStore.ts
  • src/lib/game/stores/gameStore.ts
  • src/lib/game/stores/discipline-slice.ts
## Bug: Active disciplines not deactivated when entering the Spire ### Steps to reproduce 1. Start the game and activate one or more disciplines (e.g., Raw Mana Mastery) 2. Let them accumulate some XP 3. Go to the Spire Summary tab and click "Enter Spire" 4. Observe: disciplines remain active and continue to accrue XP and drain mana while climbing the Spire ### Expected behavior When entering the Spire, all active disciplines should be deactivated (stopped). The player is now focused on climbing/combat, not practicing disciplines. Disciplines should not drain mana or gain XP during spire runs. ### Actual behavior Disciplines remain fully active during the entire spire run. They continue to: - Drain mana every tick - Accrue XP every tick - Fire `onStartPracticing` callbacks (setting `currentAction` to `'practicing'`, which conflicts with `'climb'`) ### Root cause analysis **File 1: `src/lib/game/stores/combat-descent-actions.ts` (lines 247-284)** `createEnterSpireMode()` sets `spireMode: true` and `currentAction: 'climb'` but **never** calls `useDisciplineStore.getState().deactivate()` for each active discipline, nor does it clear `activeIds`. **File 2: `src/lib/game/stores/combatStore.ts` (lines 186-216)** `exitSpireMode()` resets `currentAction` to `'meditate'` but also **never** deactivates disciplines. If disciplines were somehow still active from before entering, they would resume ticking after exit. **File 3: `src/lib/game/stores/gameStore.ts` (line 231)** The `tick()` function calls `useDisciplineStore.getState().processTick(...)` **unconditionally** — there is no guard like `if (ctx.combat.spireMode) return` to skip discipline processing during spire runs. **File 4: `src/lib/game/stores/discipline-slice.ts`** The discipline store has **zero awareness** of spire mode. It imports no spire-related state and checks no spire flag in `processTick()`. ### Evidence - `grep` for `spireMode` in `discipline-slice.ts` returns **zero matches** - `grep` for `discipline` in `combat-descent-actions.ts` returns only the `computeDisciplineEffects` import (for golem summoning), with **no discipline deactivation logic** - The `enterSpireMode` function in `combat-descent-actions.ts` does not import or call any discipline store actions - The `exitSpireMode` function in `combatStore.ts` does not import or call any discipline store actions ### Severity: High This is a high-severity bug because: 1. **Mana drain**: Active disciplines drain mana during spire combat, directly harming the player's ability to cast spells and survive 2. **XP exploitation**: Players gain discipline XP "for free" during spire runs without the intended trade-off of not being able to practice while climbing 3. **State conflict**: The `practicingCallbacks` system sets `currentAction` to `'practicing'` when disciplines are active, but spire mode sets it to `'climb'` — these conflict, and the last setter wins depending on tick ordering 4. **Design intent**: The spire is meant to be a focused combat challenge; disciplines represent meditation/practice activities that should be mutually exclusive with climbing ### Suggested fix locations 1. `combat-descent-actions.ts` → `createEnterSpireMode()`: After setting spire state, iterate `useDisciplineStore.getState().activeIds` and call `deactivate(id)` for each, or add a new `deactivateAll()` action to the discipline store 2. `gameStore.ts` → `tick()`: Add an early return or skip for discipline `processTick` when `ctx.combat.spireMode` is true (defense-in-depth) 3. Optionally: `combatStore.ts` → `exitSpireMode()`: Also deactivate disciplines on exit for safety ### Affected files - `src/lib/game/stores/combat-descent-actions.ts` - `src/lib/game/stores/combatStore.ts` - `src/lib/game/stores/gameStore.ts` - `src/lib/game/stores/discipline-slice.ts`
Anexim added the ai:todo label 2026-06-10 10:04:18 +02:00
n8n-gitea was assigned by Anexim 2026-06-10 10:04:18 +02:00
Anexim added ai:in-progress and removed ai:todo labels 2026-06-10 11:33:27 +02:00
Author
Owner

Starting work on this bug. Reading affected files to understand current state before implementing fix.

Starting work on this bug. Reading affected files to understand current state before implementing fix.
Author
Owner

Fix applied — all 1136 tests pass (64 test files)

Changes made across 4 files:

1. src/lib/game/stores/discipline-slice.ts

  • Added deactivateAll() action to the discipline store interface and implementation
  • Iterates all activeIds, marks each discipline as paused, clears activeIds, fires onStopPracticing callback

2. src/lib/game/stores/combat-descent-actions.ts

  • Imported useDisciplineStore
  • Added useDisciplineStore.getState().deactivateAll() call in createEnterSpireMode() after setting spire state — this is the primary fix

3. src/lib/game/stores/gameStore.ts

  • Added defense-in-depth guard in tick(): when ctx.combat.spireMode is true, discipline processTick is skipped entirely (returns empty result), preventing any mana drain or XP accrual during spire runs even if disciplines somehow remain active

4. src/lib/game/stores/combatStore.ts

  • Imported useDisciplineStore
  • Added useDisciplineStore.getState().deactivateAll() call in exitSpireMode() as a safety measure to clean up any edge cases

New test file:

  • src/lib/game/__tests__/discipline-deactivate-on-spire-entry.test.ts — 5 regression tests covering:
    • Deactivation on spire entry
    • No XP accrual during spire mode
    • Deactivation on spire exit (safety)
    • deactivateAll() preserves XP and pause state
    • deactivateAll() works with multiple active disciplines
## Fix applied — all 1136 tests pass (64 test files) ### Changes made across 4 files: **1. `src/lib/game/stores/discipline-slice.ts`** - Added `deactivateAll()` action to the discipline store interface and implementation - Iterates all `activeIds`, marks each discipline as paused, clears `activeIds`, fires `onStopPracticing` callback **2. `src/lib/game/stores/combat-descent-actions.ts`** - Imported `useDisciplineStore` - Added `useDisciplineStore.getState().deactivateAll()` call in `createEnterSpireMode()` after setting spire state — this is the primary fix **3. `src/lib/game/stores/gameStore.ts`** - Added defense-in-depth guard in `tick()`: when `ctx.combat.spireMode` is true, discipline `processTick` is skipped entirely (returns empty result), preventing any mana drain or XP accrual during spire runs even if disciplines somehow remain active **4. `src/lib/game/stores/combatStore.ts`** - Imported `useDisciplineStore` - Added `useDisciplineStore.getState().deactivateAll()` call in `exitSpireMode()` as a safety measure to clean up any edge cases ### New test file: - `src/lib/game/__tests__/discipline-deactivate-on-spire-entry.test.ts` — 5 regression tests covering: - Deactivation on spire entry - No XP accrual during spire mode - Deactivation on spire exit (safety) - `deactivateAll()` preserves XP and pause state - `deactivateAll()` works with multiple active disciplines
Anexim added ai:done and removed ai:in-progress labels 2026-06-10 11:42:08 +02:00
Author
Owner

Fix committed and pushed (commit 48eee17). All 1136 tests pass. Pre-commit hooks passed (file sizes, tests, project structure updated).

Fix committed and pushed (commit 48eee17). All 1136 tests pass. Pre-commit hooks passed (file sizes, tests, project structure updated).
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#347