[priority: high] Climbing the Spire crashes the game #186

Closed
opened 2026-05-28 15:57:23 +02:00 by Anexim · 1 comment
Owner

Bug: Climbing the Spire causes game crash

Description

Clicking "Climb the Spire" button causes the game to crash/hang.

Root Cause Analysis

The crash likely occurs due to one of these issues:

  1. Infinite loop in processCombatTick: In combat-actions.ts, the while (castProgress >= 1 && canAffordSpellCost(...)) loop processes complete casts. If the spell cost is 0 or the cost check passes incorrectly, this could loop infinitely. The loop does have a floor-clear break condition, but if floorHP never reaches 0 (e.g., damage is 0 or NaN), it would loop forever.

  2. NaN damage: If calcDamage() returns NaN (e.g., from undefined discipline effects or missing spell definition), then floorHP = Math.max(0, floorHP - NaN) = NaN, and the floorHP <= 0 check would be false (NaN comparisons are always false), causing an infinite loop.

  3. generateSpireFloorState with invalid floor: If currentFloor exceeds the guardian data range, getGuardianForFloor() might return undefined, and the guardian room generation could fail.

  4. SpireCombatPage component: The useSpireStats hook calls computeDisciplineEffects() and getUnifiedEffects() on every render. If either of these throws (e.g., due to corrupted discipline state), the component would crash.

  5. Missing maxFloorReached update in enterSpireMode: The enterSpireMode action in combatStore.ts resets currentFloor to 1 but doesn't update maxFloorReached. If the spire page relies on maxFloorReached for something, this could cause issues.

Affected Files

  • src/lib/game/stores/combatStore.tsenterSpireMode action
  • src/lib/game/stores/combat-actions.tsprocessCombatTick function
  • src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx — main spire component
  • src/lib/game/utils/spire-utils.tsgenerateSpireFloorState
  • src/lib/game/utils/floor-utils.tsgetFloorMaxHP

Steps to Reproduce

  1. Start a new game
  2. Gather some mana / progress through basic gameplay
  3. Click "Climb the Spire" button in the left panel
  4. Observe crash/hang

Expected Behavior

  • Spire mode should activate without crashing
  • Combat should progress normally through spire floors
  • User should be able to climb up and down

Suggested Fix Direction

  1. Add NaN guards in processCombatTick — check that damage is a valid number before applying
  2. Add a safety counter to the while loop (max 100 iterations per tick)
  3. Verify calcDamage handles all edge cases
  4. Add error boundary around SpireCombatPage
  5. Check that enterSpireMode properly initializes all required state
## Bug: Climbing the Spire causes game crash ### Description Clicking "Climb the Spire" button causes the game to crash/hang. ### Root Cause Analysis The crash likely occurs due to one of these issues: 1. **Infinite loop in `processCombatTick`**: In `combat-actions.ts`, the `while (castProgress >= 1 && canAffordSpellCost(...))` loop processes complete casts. If the spell cost is 0 or the cost check passes incorrectly, this could loop infinitely. The loop does have a floor-clear break condition, but if `floorHP` never reaches 0 (e.g., damage is 0 or NaN), it would loop forever. 2. **NaN damage**: If `calcDamage()` returns NaN (e.g., from undefined discipline effects or missing spell definition), then `floorHP = Math.max(0, floorHP - NaN)` = `NaN`, and the `floorHP <= 0` check would be false (NaN comparisons are always false), causing an infinite loop. 3. **`generateSpireFloorState` with invalid floor**: If `currentFloor` exceeds the guardian data range, `getGuardianForFloor()` might return undefined, and the guardian room generation could fail. 4. **`SpireCombatPage` component**: The `useSpireStats` hook calls `computeDisciplineEffects()` and `getUnifiedEffects()` on every render. If either of these throws (e.g., due to corrupted discipline state), the component would crash. 5. **Missing `maxFloorReached` update in `enterSpireMode`**: The `enterSpireMode` action in `combatStore.ts` resets `currentFloor` to 1 but doesn't update `maxFloorReached`. If the spire page relies on `maxFloorReached` for something, this could cause issues. ### Affected Files - `src/lib/game/stores/combatStore.ts` — `enterSpireMode` action - `src/lib/game/stores/combat-actions.ts` — `processCombatTick` function - `src/components/game/tabs/SpireCombatPage/SpireCombatPage.tsx` — main spire component - `src/lib/game/utils/spire-utils.ts` — `generateSpireFloorState` - `src/lib/game/utils/floor-utils.ts` — `getFloorMaxHP` ### Steps to Reproduce 1. Start a new game 2. Gather some mana / progress through basic gameplay 3. Click "Climb the Spire" button in the left panel 4. Observe crash/hang ### Expected Behavior - Spire mode should activate without crashing - Combat should progress normally through spire floors - User should be able to climb up and down ### Suggested Fix Direction 1. Add NaN guards in `processCombatTick` — check that damage is a valid number before applying 2. Add a safety counter to the while loop (max 100 iterations per tick) 3. Verify `calcDamage` handles all edge cases 4. Add error boundary around SpireCombatPage 5. Check that `enterSpireMode` properly initializes all required state
Anexim added the ai:todo label 2026-05-28 15:57:23 +02:00
n8n-gitea was assigned by Anexim 2026-05-28 15:57:23 +02:00
Author
Owner

Fix applied for issue #186: Climbing the Spire crashes the game

Root causes addressed:

  1. Infinite loop in processCombatTickwhile (castProgress >= 1 && canAffordSpellCost(...)) could loop forever if calcDamage returned NaN (making floorHP stay NaN, which never satisfies floorHP <= 0).
  2. enterSpireMode not preserving maxFloorReached — Reset currentFloor to 1 but didn't cap maxFloorReached at the previous value.
  3. SpireCombatPage crashing from corrupted discipline statecomputeDisciplineEffects() called on every render with no error handling.

Changes:

  1. combat-actions.ts — Added MAX_CASTS_PER_TICK = 100 safety counter to both main spell and equipment spell while loops. Added Number.isFinite() NaN guard on finalDamage / eFinalDamage — logs a warning and breaks the loop if damage is invalid.

  2. combat-utils.ts — Added final NaN guard in calcDamage(): if the computed damage is not finite, returns a safe fallback of 5.

  3. combatStore.tsenterSpireMode now uses the functional set((s) => ...) form and preserves maxFloorReached: Math.max(s.maxFloorReached, s.currentFloor).

  4. SpireCombatPage.tsx — Wrapped computeDisciplineEffects() in try-catch with a safe fallback discipline effects object.

## Fix applied for issue #186: Climbing the Spire crashes the game ### Root causes addressed: 1. **Infinite loop in `processCombatTick`** — `while (castProgress >= 1 && canAffordSpellCost(...))` could loop forever if `calcDamage` returned NaN (making `floorHP` stay NaN, which never satisfies `floorHP <= 0`). 2. **`enterSpireMode` not preserving `maxFloorReached`** — Reset currentFloor to 1 but didn't cap `maxFloorReached` at the previous value. 3. **`SpireCombatPage` crashing from corrupted discipline state** — `computeDisciplineEffects()` called on every render with no error handling. ### Changes: 1. **`combat-actions.ts`** — Added `MAX_CASTS_PER_TICK = 100` safety counter to both main spell and equipment spell while loops. Added `Number.isFinite()` NaN guard on `finalDamage` / `eFinalDamage` — logs a warning and breaks the loop if damage is invalid. 2. **`combat-utils.ts`** — Added final NaN guard in `calcDamage()`: if the computed damage is not finite, returns a safe fallback of 5. 3. **`combatStore.ts`** — `enterSpireMode` now uses the functional `set((s) => ...)` form and preserves `maxFloorReached: Math.max(s.maxFloorReached, s.currentFloor)`. 4. **`SpireCombatPage.tsx`** — Wrapped `computeDisciplineEffects()` in try-catch with a safe fallback discipline effects object.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#186