fix: apply mana drain to conversion disciplines (bug #379)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m24s
- Remove isConversionDiscipline guard in discipline-slice.ts:processTick that was skipping mana drain for all 24 conversion disciplines - Update test to verify conversion disciplines DO drain mana - Add regression tests for auto-pause and XP accrual on conversion disciplines - Fix misleading comments in elemental-regen.ts and elemental-regen-advanced.ts All 1196 tests pass.
This commit is contained in:
@@ -131,7 +131,7 @@ describe('DisciplineStore', () => {
|
||||
expect(result.elements.fire.current).toBe(98);
|
||||
});
|
||||
|
||||
it('should not drain mana for conversion disciplines (sourceManaTypes)', () => {
|
||||
it('should drain mana for conversion disciplines (regression: was zero cost)', () => {
|
||||
useDisciplineStore.setState({
|
||||
disciplines: {},
|
||||
activeIds: [],
|
||||
@@ -139,13 +139,45 @@ describe('DisciplineStore', () => {
|
||||
totalXP: 0,
|
||||
});
|
||||
// regen-fire is a conversion discipline with sourceManaTypes: ['raw']
|
||||
// It has manaType='fire' and drainBase=1.5 — it MUST drain fire mana
|
||||
useDisciplineStore.getState().activate('regen-fire', {
|
||||
elements: { fire: { unlocked: true, current: 100, max: 100, baseMax: 100 } },
|
||||
});
|
||||
const result = useDisciplineStore.getState().processTick({ rawMana: 1000, elements: { fire: { unlocked: true, current: 100, max: 100, baseMax: 100 } } });
|
||||
// Conversion disciplines should NOT drain from pools
|
||||
expect(result.rawMana).toBe(1000);
|
||||
expect(result.elements.fire.current).toBe(100);
|
||||
// Conversion disciplines MUST drain from their mana pool (bug fix #379)
|
||||
// regen-fire: drainBase=1.5, xp=0, difficultyFactor=120
|
||||
// drain = 1.5 * (1 + (0/120)^0.4) = 1.5
|
||||
expect(result.elements.fire.current).toBeCloseTo(98.5, 1);
|
||||
});
|
||||
|
||||
it('should auto-pause conversion discipline when element mana is insufficient', () => {
|
||||
useDisciplineStore.setState({
|
||||
disciplines: {},
|
||||
activeIds: [],
|
||||
concurrentLimit: 1,
|
||||
totalXP: 0,
|
||||
});
|
||||
// regen-fire has drainBase=1.5, needs at least 1.5 fire mana
|
||||
useDisciplineStore.getState().activate('regen-fire', {
|
||||
elements: { fire: { unlocked: true, current: 1, max: 100, baseMax: 100 } },
|
||||
});
|
||||
const result = useDisciplineStore.getState().processTick({ rawMana: 1000, elements: { fire: { unlocked: true, current: 1, max: 100, baseMax: 100 } } });
|
||||
expect(useDisciplineStore.getState().disciplines['regen-fire'].autoPaused).toBe(true);
|
||||
expect(result.autoPausedNames).toContain('Fire Mana Conversion Speed');
|
||||
});
|
||||
|
||||
it('should still accrue XP for conversion disciplines when mana is sufficient', () => {
|
||||
useDisciplineStore.setState({
|
||||
disciplines: {},
|
||||
activeIds: [],
|
||||
concurrentLimit: 1,
|
||||
totalXP: 0,
|
||||
});
|
||||
useDisciplineStore.getState().activate('regen-fire', {
|
||||
elements: { fire: { unlocked: true, current: 100, max: 100, baseMax: 100 } },
|
||||
});
|
||||
useDisciplineStore.getState().processTick({ rawMana: 1000, elements: { fire: { unlocked: true, current: 100, max: 100, baseMax: 100 } } });
|
||||
expect(useDisciplineStore.getState().disciplines['regen-fire'].xp).toBe(1);
|
||||
});
|
||||
|
||||
it('should re-activate auto-paused discipline when mana is restored', () => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// NEW MODEL: Disciplines contribute to conversion_{element} stat bonus.
|
||||
// The unified conversion-rates.ts calculator handles rate computation.
|
||||
// No direct mana drain — costs are deducted from regen.
|
||||
// Mana drain is applied from the target mana type pool each tick.
|
||||
|
||||
import { DisciplinesAttunementType } from '../../types/disciplines';
|
||||
import type { DisciplineDefinition } from '../../types/disciplines';
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//
|
||||
// NEW MODEL: Disciplines contribute to conversion_{element} stat bonus.
|
||||
// The unified conversion-rates.ts calculator handles rate computation.
|
||||
// No direct mana drain — costs are deducted from regen.
|
||||
// Mana drain is applied from the target mana type pool each tick.
|
||||
|
||||
import { DisciplinesAttunementType } from '../../types/disciplines';
|
||||
import type { DisciplineDefinition } from '../../types/disciplines';
|
||||
|
||||
@@ -212,13 +212,11 @@ export const useDisciplineStore = create<DisciplineStore>()(
|
||||
const def = DISCIPLINE_MAP[id];
|
||||
if (!def) continue;
|
||||
|
||||
// ── Mana drain (skip for conversion disciplines) ──────────────
|
||||
// Conversion disciplines (with sourceManaTypes) are handled by
|
||||
// the unified conversion system — they contribute to conversion
|
||||
// rates, not direct pool drain.
|
||||
const isConversionDiscipline = !!(def.sourceManaTypes && def.sourceManaTypes.length > 0);
|
||||
|
||||
if (!isConversionDiscipline) {
|
||||
// ── Mana drain ──────────────────────────────────────────────────
|
||||
// All disciplines (including conversion) drain from their mana pool.
|
||||
// Conversion disciplines both drain their target mana type AND
|
||||
// contribute to conversion rates.
|
||||
{
|
||||
const drain = calculateManaDrain(def.drainBase, disc.xp, def.difficultyFactor);
|
||||
|
||||
// Determine which mana pool to drain from
|
||||
|
||||
Reference in New Issue
Block a user