[priority: highest] fix: Discipline perk numerical bonuses are dead code — once/infinite/capped perks don't apply #137

Closed
opened 2026-05-26 15:26:04 +02:00 by Anexim · 2 comments
Owner

Problem

Discipline perks (once, infinite, capped) fire and their IDs are added to a specials set in computeDisciplineEffects(), but nothing in the game engine reads those IDs back out to apply numerical bonuses. The perk descriptions document bonuses (e.g. "+50 Max Mana", "Every 100 XP: +25 Max Mana") that never actually affect any game stat.

Additionally, perk.unlocksEffects is defined on ~40 enchantment-study perks but the field is never read anywhere — effects are only unlocked through a separate path in discipline-slice.ts processTick().

Root Cause

In src/lib/game/effects/discipline-effects.ts:

  • once/infinite perks → specials.add(perk.id) — but no code checks for discipline perk IDs in the specials set. Only SPECIAL_EFFECTS.* enum values (like MANA_CASCADE, ENCH_MASTERY) are consumed.
  • capped perks → multipliers['perk_enchant-2'] — but computeAllEffects() only reads known keys like maxMana, regen, baseDamage, etc. Namespaced perk_* keys are never consumed.
  • unlocksEffects on perks — dead field. Effect unlocking happens in discipline-slice.ts via processedPerks tracking, not through this field.

Perk Data Issues

Most once perks have value: 0 with the bonus amount only in the description string. Most infinite perks encode the interval in value (e.g. 100 = "every 100 XP") but the bonus amount is only in the description string. There is no structured way to parse or compute these.

What Needs to Change

  1. Add a BonusSpec type to types/disciplines.ts with concrete fields: { stat: string; amount: number } so perks carry structured bonus data instead of hiding it in description strings.
  2. Update all discipline data files to populate the new bonus field on each perk.
  3. Rewrite computeDisciplineEffects() to:
    • For once perks: add bonus.amount to bonuses[bonus.stat] when threshold is met
    • For infinite perks: calculate tier via calculatePerkTier() and add tier * bonus.amount to bonuses[bonus.stat]
    • For capped perks: calculate tier and add to a known multiplier key (not a namespaced orphan)
  4. Remove specials.add(perk.id) for perks that now apply their bonuses through bonuses/multipliers directly.

Files Involved

  • src/lib/game/types/disciplines.ts — add BonusSpec type
  • src/lib/game/effects/discipline-effects.ts — rewrite perk handling
  • src/lib/game/effects.ts — ensure merged stat keys consume the new bonus paths
  • All src/lib/game/data/disciplines/*.ts — populate structured bonus data on each perk

Test References

  • src/lib/game/__tests__/discipline-math.test.tscalculatePerkTier and getUnlockedPerks tests exist but don't verify numerical application
## Problem Discipline perks (`once`, `infinite`, `capped`) fire and their IDs are added to a `specials` set in `computeDisciplineEffects()`, but **nothing in the game engine reads those IDs back out to apply numerical bonuses**. The perk descriptions document bonuses (e.g. "+50 Max Mana", "Every 100 XP: +25 Max Mana") that never actually affect any game stat. Additionally, `perk.unlocksEffects` is defined on ~40 enchantment-study perks but the field is **never read anywhere** — effects are only unlocked through a separate path in `discipline-slice.ts processTick()`. ## Root Cause In `src/lib/game/effects/discipline-effects.ts`: - `once`/`infinite` perks → `specials.add(perk.id)` — but no code checks for discipline perk IDs in the specials set. Only `SPECIAL_EFFECTS.*` enum values (like `MANA_CASCADE`, `ENCH_MASTERY`) are consumed. - `capped` perks → `multipliers['perk_enchant-2']` — but `computeAllEffects()` only reads known keys like `maxMana`, `regen`, `baseDamage`, etc. Namespaced `perk_*` keys are never consumed. - `unlocksEffects` on perks — dead field. Effect unlocking happens in `discipline-slice.ts` via `processedPerks` tracking, not through this field. ## Perk Data Issues Most `once` perks have `value: 0` with the bonus amount only in the `description` string. Most `infinite` perks encode the **interval** in `value` (e.g. 100 = "every 100 XP") but the **bonus amount** is only in the description string. There is no structured way to parse or compute these. ## What Needs to Change 1. **Add a `BonusSpec` type** to `types/disciplines.ts` with concrete fields: `{ stat: string; amount: number }` so perks carry structured bonus data instead of hiding it in description strings. 2. **Update all discipline data files** to populate the new bonus field on each perk. 3. **Rewrite `computeDisciplineEffects()`** to: - For `once` perks: add `bonus.amount` to `bonuses[bonus.stat]` when threshold is met - For `infinite` perks: calculate tier via `calculatePerkTier()` and add `tier * bonus.amount` to `bonuses[bonus.stat]` - For `capped` perks: calculate tier and add to a known multiplier key (not a namespaced orphan) 4. **Remove `specials.add(perk.id)`** for perks that now apply their bonuses through `bonuses`/`multipliers` directly. ## Files Involved - `src/lib/game/types/disciplines.ts` — add `BonusSpec` type - `src/lib/game/effects/discipline-effects.ts` — rewrite perk handling - `src/lib/game/effects.ts` — ensure merged stat keys consume the new bonus paths - All `src/lib/game/data/disciplines/*.ts` — populate structured bonus data on each perk ## Test References - `src/lib/game/__tests__/discipline-math.test.ts` — `calculatePerkTier` and `getUnlockedPerks` tests exist but don't verify numerical application
Anexim added the ai:todo label 2026-05-26 15:26:04 +02:00
n8n-gitea was assigned by Anexim 2026-05-26 15:26:04 +02:00
Anexim changed title from fix: Discipline perk numerical bonuses are dead code — once/infinite/capped perks don't apply to [priority: highest] fix: Discipline perk numerical bonuses are dead code — once/infinite/capped perks don't apply 2026-05-26 15:28:51 +02:00
Author
Owner

Starting work on making discipline perk numerical bonuses functional. Will: (1) add BonusSpec type, (2) update discipline data files, (3) rewrite computeDisciplineEffects() perk handling.

Starting work on making discipline perk numerical bonuses functional. Will: (1) add BonusSpec type, (2) update discipline data files, (3) rewrite computeDisciplineEffects() perk handling.
Author
Owner

Fixed. All three critical bugs resolved:

  1. Type system: Added PerkBonus interface and optional bonus field to DisciplinePerk in types/disciplines.ts

  2. Data files: Added structured bonus data to 39 perks across 5 discipline files:

    • base.ts: raw-mastery-1 (+50 maxManaBonus), raw-mastery-2 (infinite +25 maxManaBonus per 100 XP)
    • elemental.ts: 7 elemental attunement perks (+10 elementCap per element)
    • elemental-regen.ts: 16 regen perks (+regen per element, both once and infinite)
    • elemental-regen-advanced.ts: 12 regen perks (composite + exotic elements)
    • invoker.ts: spell-1 (+10 baseDamageBonus), spell-2 (infinite +5 baseDamageBonus per 300 XP)
  3. Effects computation: Rewrote computeDisciplineEffects() in discipline-effects.ts to:

    • once perks: add bonus.amount to bonuses[stat]
    • infinite perks: calculate tier via calculatePerkTier(), add tier * bonus.amount
    • capped perks: calculate tier and add to bonus stat
    • Preserve unlocksEffects handling (delegated to discipline-slice.ts)
    • Fallback to specials.add() for qualitative perks
  4. Per-element routing: Added elementCap_* handling in effects.ts computeAllEffects() to merge into perElementCapBonus map

All 885 tests pass. Pre-commit checks pass. Committed as da4f9ec.

Fixed. All three critical bugs resolved: 1. **Type system**: Added `PerkBonus` interface and optional `bonus` field to `DisciplinePerk` in `types/disciplines.ts` 2. **Data files**: Added structured bonus data to 39 perks across 5 discipline files: - `base.ts`: raw-mastery-1 (+50 maxManaBonus), raw-mastery-2 (infinite +25 maxManaBonus per 100 XP) - `elemental.ts`: 7 elemental attunement perks (+10 elementCap per element) - `elemental-regen.ts`: 16 regen perks (+regen per element, both once and infinite) - `elemental-regen-advanced.ts`: 12 regen perks (composite + exotic elements) - `invoker.ts`: spell-1 (+10 baseDamageBonus), spell-2 (infinite +5 baseDamageBonus per 300 XP) 3. **Effects computation**: Rewrote `computeDisciplineEffects()` in `discipline-effects.ts` to: - `once` perks: add `bonus.amount` to `bonuses[stat]` - `infinite` perks: calculate tier via `calculatePerkTier()`, add `tier * bonus.amount` - `capped` perks: calculate tier and add to bonus stat - Preserve `unlocksEffects` handling (delegated to discipline-slice.ts) - Fallback to `specials.add()` for qualitative perks 4. **Per-element routing**: Added `elementCap_*` handling in `effects.ts` `computeAllEffects()` to merge into `perElementCapBonus` map All 885 tests pass. Pre-commit checks pass. Committed as da4f9ec.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Anexim/Mana-Loop#137