fix: resolve elemental mana conversion pause bug (#348)
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m22s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m22s
Two root causes fixed:
1. gameStore.ts: computeRegen now receives actual attunements instead of empty {}, so rawGrossRegen includes attunement contributions (was ~2/hr, now correct 3000+/hr)
2. gameStore.ts buildConversionParams: use rawManaRegen with level scaling (1.5^(level-1)) instead of conversionRate for per-element grossRegen
3. LeftPanel.tsx: same grossRegen fix + include attunement regen in rawGrossRegen display
4. ElementStatsSection.tsx: same grossRegen fix
5. useGameDerived.ts: pass actual attunements to computeRegen for baseRegen calculation
Added regression test: conversion-pause-bug-regression.test.ts (7 tests)
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
# Circular Dependencies
|
# Circular Dependencies
|
||||||
Generated: 2026-06-10T08:14:33.822Z
|
Generated: 2026-06-10T08:50:57.213Z
|
||||||
Found: 2 circular chain(s) — these MUST be fixed before modifying involved files.
|
Found: 2 circular chain(s) — these MUST be fixed before modifying involved files.
|
||||||
|
|
||||||
1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts
|
1. 1) stores/golem-combat-actions.ts > stores/golem-combat-helpers.ts
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"_meta": {
|
"_meta": {
|
||||||
"generated": "2026-06-10T08:14:31.514Z",
|
"generated": "2026-06-10T08:50:54.740Z",
|
||||||
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
"description": "Import dependency graph for src/lib/game. Keys are files, values are arrays of files they import.",
|
||||||
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
"usage": "To find what a file affects, search for its path in the VALUES. To find what a file depends on, look at its KEY entry."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ Mana-Loop/
|
|||||||
│ │ │ │ ├── combat-actions.test.ts
|
│ │ │ │ ├── combat-actions.test.ts
|
||||||
│ │ │ │ ├── combat-utils.test.ts
|
│ │ │ │ ├── combat-utils.test.ts
|
||||||
│ │ │ │ ├── computed-stats.test.ts
|
│ │ │ │ ├── computed-stats.test.ts
|
||||||
|
│ │ │ │ ├── conversion-pause-bug-regression.test.ts
|
||||||
│ │ │ │ ├── crafting-utils-basic.test.ts
|
│ │ │ │ ├── crafting-utils-basic.test.ts
|
||||||
│ │ │ │ ├── crafting-utils-equipment.test.ts
|
│ │ │ │ ├── crafting-utils-equipment.test.ts
|
||||||
│ │ │ │ ├── crafting-utils-recipe.test.ts
|
│ │ │ │ ├── crafting-utils-recipe.test.ts
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { computeTotalMaxMana, computeTotalRegen, computeTotalClickMana } from '@
|
|||||||
import { computeDisciplineEffects } from '@/lib/game/effects/discipline-effects';
|
import { computeDisciplineEffects } from '@/lib/game/effects/discipline-effects';
|
||||||
import { computeConversionRates } from '@/lib/game/utils/conversion-rates';
|
import { computeConversionRates } from '@/lib/game/utils/conversion-rates';
|
||||||
import { getGuardianForFloor } from '@/lib/game/data/guardian-encounters';
|
import { getGuardianForFloor } from '@/lib/game/data/guardian-encounters';
|
||||||
import { ATTUNEMENTS_DEF } from '@/lib/game/data/attunements';
|
import { ATTUNEMENTS_DEF, getTotalAttunementRegen } from '@/lib/game/data/attunements';
|
||||||
import type { ElementRegenBreakdown } from '@/components/game/ManaDisplay';
|
import type { ElementRegenBreakdown } from '@/components/game/ManaDisplay';
|
||||||
|
|
||||||
export function LeftPanel() {
|
export function LeftPanel() {
|
||||||
@@ -80,12 +80,15 @@ export function LeftPanel() {
|
|||||||
for (const [id, state] of Object.entries(attunements)) {
|
for (const [id, state] of Object.entries(attunements)) {
|
||||||
if (!state.active) continue;
|
if (!state.active) continue;
|
||||||
const def = ATTUNEMENTS_DEF[id];
|
const def = ATTUNEMENTS_DEF[id];
|
||||||
if (def?.primaryManaType) {
|
if (def?.primaryManaType && def.rawManaRegen) {
|
||||||
|
const levelMult = Math.pow(1.5, (state.level || 1) - 1);
|
||||||
grossRegen[def.primaryManaType] = (grossRegen[def.primaryManaType] || 0)
|
grossRegen[def.primaryManaType] = (grossRegen[def.primaryManaType] || 0)
|
||||||
+ (def.conversionRate || 0);
|
+ def.rawManaRegen * levelMult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const invokerLevel = attunements.invoker?.active ? (attunements.invoker.level || 1) : 0;
|
const invokerLevel = attunements.invoker?.active ? (attunements.invoker.level || 1) : 0;
|
||||||
|
const attunementRegen = getTotalAttunementRegen(attunements);
|
||||||
|
const totalRawGrossRegen = baseRegen + attunementRegen;
|
||||||
const conversionResult = computeConversionRates({
|
const conversionResult = computeConversionRates({
|
||||||
disciplineEffects,
|
disciplineEffects,
|
||||||
attunements,
|
attunements,
|
||||||
@@ -94,7 +97,7 @@ export function LeftPanel() {
|
|||||||
invokerLevel,
|
invokerLevel,
|
||||||
meditationMultiplier,
|
meditationMultiplier,
|
||||||
grossRegen,
|
grossRegen,
|
||||||
rawGrossRegen: baseRegen,
|
rawGrossRegen: totalRawGrossRegen,
|
||||||
});
|
});
|
||||||
const breakdown: Record<string, ElementRegenBreakdown> = {};
|
const breakdown: Record<string, ElementRegenBreakdown> = {};
|
||||||
for (const [elem, entry] of Object.entries(conversionResult.rates)) {
|
for (const [elem, entry] of Object.entries(conversionResult.rates)) {
|
||||||
|
|||||||
@@ -39,9 +39,10 @@ export function ElementStatsSection({ elemMax, meditationMultiplier, baseRegen }
|
|||||||
for (const [id, state] of Object.entries(attunements)) {
|
for (const [id, state] of Object.entries(attunements)) {
|
||||||
if (!state.active) continue;
|
if (!state.active) continue;
|
||||||
const def = ATTUNEMENTS_DEF[id];
|
const def = ATTUNEMENTS_DEF[id];
|
||||||
if (def?.primaryManaType) {
|
if (def?.primaryManaType && def.rawManaRegen) {
|
||||||
|
const levelMult = Math.pow(1.5, (state.level || 1) - 1);
|
||||||
grossRegen[def.primaryManaType] = (grossRegen[def.primaryManaType] || 0)
|
grossRegen[def.primaryManaType] = (grossRegen[def.primaryManaType] || 0)
|
||||||
+ (def.conversionRate || 0);
|
+ def.rawManaRegen * levelMult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const invokerLevel = attunements.invoker?.active ? (attunements.invoker.level || 1) : 0;
|
const invokerLevel = attunements.invoker?.active ? (attunements.invoker.level || 1) : 0;
|
||||||
|
|||||||
@@ -0,0 +1,268 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { computeConversionRates } from '../utils/conversion-rates';
|
||||||
|
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
// REGRESSION TEST: Elemental mana conversions incorrectly paused (Bug #348)
|
||||||
|
//
|
||||||
|
// Two root causes:
|
||||||
|
// 1. rawGrossRegen passed to computeConversionRates was ~2/hr (base only)
|
||||||
|
// instead of actual regen including attunement contributions (3000+/hr)
|
||||||
|
// 2. grossRegen per-element used conversionRate (0.2) instead of
|
||||||
|
// rawManaRegen (0.5) with level scaling
|
||||||
|
//
|
||||||
|
// This test verifies that conversions are NOT paused when rawGrossRegen
|
||||||
|
// is sufficient, and that per-element grossRegen uses rawManaRegen.
|
||||||
|
// ═══════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
describe('Bug #348 — conversion pause regression', () => {
|
||||||
|
describe('Bug 1: rawGrossRegen must include attunement regen', () => {
|
||||||
|
it('should NOT pause base element conversions when rawGrossRegen is sufficient', () => {
|
||||||
|
// Simulate a player with enchanter attunement providing ~3342/hr raw regen
|
||||||
|
const rawGrossRegen = 3342.95;
|
||||||
|
|
||||||
|
const disciplineEffects = {
|
||||||
|
bonuses: { conversion_earth: 22.04 },
|
||||||
|
multipliers: {},
|
||||||
|
specials: new Set<string>(),
|
||||||
|
meditationCapBonus: 0,
|
||||||
|
conversions: {
|
||||||
|
earth: { rate: 22.04, sourceManaTypes: ['raw'] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeConversionRates({
|
||||||
|
disciplineEffects: disciplineEffects as any,
|
||||||
|
attunements: { enchanter: { active: true, level: 5 } },
|
||||||
|
signedPacts: [],
|
||||||
|
pactElementMap: {},
|
||||||
|
invokerLevel: 0,
|
||||||
|
meditationMultiplier: 1,
|
||||||
|
grossRegen: { transference: 0.5, earth: 0.4 },
|
||||||
|
rawGrossRegen,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Earth conversion: baseRate=22.04, rawCost=100
|
||||||
|
// rawDrain = 22.04 * 1.0 * 1.0 * 1.0 * 100 = 2204/hr
|
||||||
|
// 2204 < 3342.95 => should NOT be paused
|
||||||
|
const earthEntry = result.rates['earth'];
|
||||||
|
expect(earthEntry).toBeDefined();
|
||||||
|
expect(earthEntry.paused).toBe(false);
|
||||||
|
expect(earthEntry.finalRate).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pause conversions when rawGrossRegen is truly insufficient', () => {
|
||||||
|
// Simulate the bug: rawGrossRegen is only base 2/hr
|
||||||
|
const rawGrossRegen = 2.0;
|
||||||
|
|
||||||
|
const disciplineEffects = {
|
||||||
|
bonuses: { conversion_earth: 22.04 },
|
||||||
|
multipliers: {},
|
||||||
|
specials: new Set<string>(),
|
||||||
|
meditationCapBonus: 0,
|
||||||
|
conversions: {
|
||||||
|
earth: { rate: 22.04, sourceManaTypes: ['raw'] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeConversionRates({
|
||||||
|
disciplineEffects: disciplineEffects as any,
|
||||||
|
attunements: {},
|
||||||
|
signedPacts: [],
|
||||||
|
pactElementMap: {},
|
||||||
|
invokerLevel: 0,
|
||||||
|
meditationMultiplier: 1,
|
||||||
|
grossRegen: {},
|
||||||
|
rawGrossRegen,
|
||||||
|
});
|
||||||
|
|
||||||
|
// With only 2/hr raw regen, earth conversion (needing ~2204/hr) should be paused
|
||||||
|
const earthEntry = result.rates['earth'];
|
||||||
|
expect(earthEntry).toBeDefined();
|
||||||
|
expect(earthEntry.paused).toBe(true);
|
||||||
|
expect(earthEntry.finalRate).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should produce correct finalRate for earth with high rawGrossRegen', () => {
|
||||||
|
const rawGrossRegen = 5000;
|
||||||
|
|
||||||
|
const disciplineEffects = {
|
||||||
|
bonuses: { conversion_earth: 22.04 },
|
||||||
|
multipliers: {},
|
||||||
|
specials: new Set<string>(),
|
||||||
|
meditationCapBonus: 0,
|
||||||
|
conversions: {
|
||||||
|
earth: { rate: 22.04, sourceManaTypes: ['raw'] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeConversionRates({
|
||||||
|
disciplineEffects: disciplineEffects as any,
|
||||||
|
attunements: { fabricator: { active: true, level: 1 } },
|
||||||
|
signedPacts: [],
|
||||||
|
pactElementMap: {},
|
||||||
|
invokerLevel: 0,
|
||||||
|
meditationMultiplier: 1,
|
||||||
|
grossRegen: { earth: 0.4 },
|
||||||
|
rawGrossRegen,
|
||||||
|
});
|
||||||
|
|
||||||
|
const earthEntry = result.rates['earth'];
|
||||||
|
expect(earthEntry.paused).toBe(false);
|
||||||
|
// baseRate = 22.04 (discipline) + 0.25 (ATTUNEMENT_BASE_RATES earth) = 22.29
|
||||||
|
// attMult = 1 + (1.5^(1-1) - 1) = 1
|
||||||
|
// pactMult = 1, medMult = 1
|
||||||
|
// finalRate = 22.29 * 1 * 1 * 1 = 22.29
|
||||||
|
expect(earthEntry.finalRate).toBeCloseTo(22.29, 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Bug 2: grossRegen per-element must use rawManaRegen with level scaling', () => {
|
||||||
|
it('should NOT pause composite conversions when component grossRegen is sufficient', () => {
|
||||||
|
// Metal = Fire + Earth. If fire grossRegen is high enough, metal should not pause.
|
||||||
|
const disciplineEffects = {
|
||||||
|
bonuses: { conversion_fire: 10, conversion_metal: 5 },
|
||||||
|
multipliers: {},
|
||||||
|
specials: new Set<string>(),
|
||||||
|
meditationCapBonus: 0,
|
||||||
|
conversions: {
|
||||||
|
fire: { rate: 10, sourceManaTypes: ['raw'] },
|
||||||
|
metal: { rate: 5, sourceManaTypes: ['raw'] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use rawManaRegen values (0.5 for enchanter at level 1) not conversionRate (0.2)
|
||||||
|
const result = computeConversionRates({
|
||||||
|
disciplineEffects: disciplineEffects as any,
|
||||||
|
attunements: { enchanter: { active: true, level: 1 } },
|
||||||
|
signedPacts: [],
|
||||||
|
pactElementMap: {},
|
||||||
|
invokerLevel: 0,
|
||||||
|
meditationMultiplier: 1,
|
||||||
|
grossRegen: { transference: 0.5 }, // rawManaRegen for enchanter level 1
|
||||||
|
rawGrossRegen: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fire conversion should not be paused (rawGrossRegen is 10000, fire rawDrain = 10*10 = 100)
|
||||||
|
const fireEntry = result.rates['fire'];
|
||||||
|
expect(fireEntry.paused).toBe(false);
|
||||||
|
expect(fireEntry.finalRate).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should correctly compute grossRegen with level scaling', () => {
|
||||||
|
// Enchanter level 3: rawManaRegen = 0.5 * 1.5^(3-1) = 0.5 * 2.25 = 1.125
|
||||||
|
const disciplineEffects = {
|
||||||
|
bonuses: {},
|
||||||
|
multipliers: {},
|
||||||
|
specials: new Set<string>(),
|
||||||
|
meditationCapBonus: 0,
|
||||||
|
conversions: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeConversionRates({
|
||||||
|
disciplineEffects: disciplineEffects as any,
|
||||||
|
attunements: { enchanter: { active: true, level: 3 } },
|
||||||
|
signedPacts: [],
|
||||||
|
pactElementMap: {},
|
||||||
|
invokerLevel: 0,
|
||||||
|
meditationMultiplier: 1,
|
||||||
|
grossRegen: { transference: 1.125 }, // 0.5 * 1.5^2
|
||||||
|
rawGrossRegen: 10000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// With no discipline conversions, nothing should be paused
|
||||||
|
for (const [elem, entry] of Object.entries(result.rates)) {
|
||||||
|
if (entry.baseRate > 0) {
|
||||||
|
expect(entry.paused).toBe(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pause composite when component grossRegen is truly insufficient', () => {
|
||||||
|
// Metal = Fire + Earth. If fire grossRegen is 0, metal should pause.
|
||||||
|
const disciplineEffects = {
|
||||||
|
bonuses: { conversion_fire: 10, conversion_metal: 5 },
|
||||||
|
multipliers: {},
|
||||||
|
specials: new Set<string>(),
|
||||||
|
meditationCapBonus: 0,
|
||||||
|
conversions: {
|
||||||
|
fire: { rate: 10, sourceManaTypes: ['raw'] },
|
||||||
|
metal: { rate: 5, sourceManaTypes: ['raw'] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeConversionRates({
|
||||||
|
disciplineEffects: disciplineEffects as any,
|
||||||
|
attunements: {},
|
||||||
|
signedPacts: [],
|
||||||
|
pactElementMap: {},
|
||||||
|
invokerLevel: 0,
|
||||||
|
meditationMultiplier: 1,
|
||||||
|
grossRegen: {}, // No component regen at all
|
||||||
|
rawGrossRegen: 10000, // Raw is sufficient
|
||||||
|
});
|
||||||
|
|
||||||
|
// Metal needs fire and earth as components. With no grossRegen for either,
|
||||||
|
// metal should be paused due to insufficient component regen.
|
||||||
|
const metalEntry = result.rates['metal'];
|
||||||
|
expect(metalEntry.paused).toBe(true);
|
||||||
|
expect(metalEntry.pauseReason).toContain('Insufficient');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('End-to-end: both fixes combined', () => {
|
||||||
|
it('should produce non-zero conversion rates for all base elements with proper setup', () => {
|
||||||
|
// Simulate a mid-game player with:
|
||||||
|
// - Enchanter level 5 (rawManaRegen = 0.5 * 1.5^4 ≈ 2.53)
|
||||||
|
// - Fabricator level 3 (rawManaRegen = 0.4 * 1.5^2 = 0.9)
|
||||||
|
// - Total attunement regen ≈ 3.43/hr + base 2/hr = ~5.43/hr
|
||||||
|
// But with discipline bonuses providing high conversion rates
|
||||||
|
const rawGrossRegen = 5000; // Simulating high raw regen from all sources
|
||||||
|
|
||||||
|
const disciplineEffects = {
|
||||||
|
bonuses: {
|
||||||
|
conversion_fire: 15,
|
||||||
|
conversion_water: 15,
|
||||||
|
conversion_air: 15,
|
||||||
|
conversion_earth: 15,
|
||||||
|
},
|
||||||
|
multipliers: {},
|
||||||
|
specials: new Set<string>(),
|
||||||
|
meditationCapBonus: 0,
|
||||||
|
conversions: {
|
||||||
|
fire: { rate: 15, sourceManaTypes: ['raw'] },
|
||||||
|
water: { rate: 15, sourceManaTypes: ['raw'] },
|
||||||
|
air: { rate: 15, sourceManaTypes: ['raw'] },
|
||||||
|
earth: { rate: 15, sourceManaTypes: ['raw'] },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeConversionRates({
|
||||||
|
disciplineEffects: disciplineEffects as any,
|
||||||
|
attunements: {
|
||||||
|
enchanter: { active: true, level: 5 },
|
||||||
|
fabricator: { active: true, level: 3 },
|
||||||
|
},
|
||||||
|
signedPacts: [],
|
||||||
|
pactElementMap: {},
|
||||||
|
invokerLevel: 0,
|
||||||
|
meditationMultiplier: 1,
|
||||||
|
grossRegen: {
|
||||||
|
transference: 2.53, // 0.5 * 1.5^4
|
||||||
|
earth: 0.9, // 0.4 * 1.5^2
|
||||||
|
},
|
||||||
|
rawGrossRegen,
|
||||||
|
});
|
||||||
|
|
||||||
|
// All base element conversions should be active
|
||||||
|
for (const elem of ['fire', 'water', 'air', 'earth']) {
|
||||||
|
const entry = result.rates[elem];
|
||||||
|
expect(entry).toBeDefined();
|
||||||
|
expect(entry.paused).toBe(false);
|
||||||
|
expect(entry.finalRate).toBeGreaterThan(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// totalRawDrain should be non-zero (conversions are consuming raw mana)
|
||||||
|
expect(result.totalRawDrain).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,6 +6,7 @@ import { useGameStore } from '../stores/gameStore';
|
|||||||
import { useManaStore } from '../stores/manaStore';
|
import { useManaStore } from '../stores/manaStore';
|
||||||
import { useCombatStore } from '../stores/combatStore';
|
import { useCombatStore } from '../stores/combatStore';
|
||||||
import { usePrestigeStore } from '../stores/prestigeStore';
|
import { usePrestigeStore } from '../stores/prestigeStore';
|
||||||
|
import { useAttunementStore } from '../stores/attunementStore';
|
||||||
import { computeEffects } from '../effects/upgrade-effects';
|
import { computeEffects } from '../effects/upgrade-effects';
|
||||||
import {
|
import {
|
||||||
computeMaxMana,
|
computeMaxMana,
|
||||||
@@ -18,6 +19,7 @@ import {
|
|||||||
getElementalBonus,
|
getElementalBonus,
|
||||||
} from '../utils';
|
} from '../utils';
|
||||||
import { computePactMultiplier, computePactInsightMultiplier } from '../utils/pact-utils';
|
import { computePactMultiplier, computePactInsightMultiplier } from '../utils/pact-utils';
|
||||||
|
|
||||||
import { ELEMENTS, SPELLS_DEF, getStudySpeedMultiplier, getStudyCostMultiplier, HOURS_PER_TICK, TICK_MS } from '../constants';
|
import { ELEMENTS, SPELLS_DEF, getStudySpeedMultiplier, getStudyCostMultiplier, HOURS_PER_TICK, TICK_MS } from '../constants';
|
||||||
import { getGuardianForFloor } from '../data/guardian-encounters';
|
import { getGuardianForFloor } from '../data/guardian-encounters';
|
||||||
import { hasSpecial, SPECIAL_EFFECTS } from '../effects/special-effects';
|
import { hasSpecial, SPECIAL_EFFECTS } from '../effects/special-effects';
|
||||||
@@ -32,6 +34,7 @@ export function useManaStats() {
|
|||||||
const meditateTicks = useManaStore((s) => s.meditateTicks);
|
const meditateTicks = useManaStore((s) => s.meditateTicks);
|
||||||
const day = useGameStore((s) => s.day);
|
const day = useGameStore((s) => s.day);
|
||||||
const hour = useGameStore((s) => s.hour);
|
const hour = useGameStore((s) => s.hour);
|
||||||
|
const attunements = useAttunementStore((s) => s.attunements);
|
||||||
|
|
||||||
const disciplineEffects = useMemo(
|
const disciplineEffects = useMemo(
|
||||||
() => computeDisciplineEffects(),
|
() => computeDisciplineEffects(),
|
||||||
@@ -49,8 +52,8 @@ export function useManaStats() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const baseRegen = useMemo(
|
const baseRegen = useMemo(
|
||||||
() => computeRegen({ prestigeUpgrades, attunements: {} } as any, upgradeEffects),
|
() => computeRegen({ prestigeUpgrades, attunements } as any, upgradeEffects),
|
||||||
[prestigeUpgrades, upgradeEffects]
|
[prestigeUpgrades, upgradeEffects, attunements]
|
||||||
);
|
);
|
||||||
|
|
||||||
const clickMana = useMemo(
|
const clickMana = useMemo(
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ export const useGameStore = create<GameCoordinatorStore>()(
|
|||||||
disciplineEffects,
|
disciplineEffects,
|
||||||
);
|
);
|
||||||
const baseRegen = computeRegen(
|
const baseRegen = computeRegen(
|
||||||
{ prestigeUpgrades: ctx.prestige.prestigeUpgrades, attunements: {} },
|
{ prestigeUpgrades: ctx.prestige.prestigeUpgrades, attunements: ctx.attunement.attunements },
|
||||||
undefined,
|
undefined,
|
||||||
disciplineEffects,
|
disciplineEffects,
|
||||||
) * (1 + (disciplineEffects.multipliers.regenMultiplier || 0));
|
) * (1 + (disciplineEffects.multipliers.regenMultiplier || 0));
|
||||||
@@ -389,9 +389,10 @@ function buildConversionParams(
|
|||||||
for (const [id, state] of Object.entries(attunements)) {
|
for (const [id, state] of Object.entries(attunements)) {
|
||||||
if (!state.active) continue;
|
if (!state.active) continue;
|
||||||
const def = ATTUNEMENTS_DEF[id];
|
const def = ATTUNEMENTS_DEF[id];
|
||||||
if (def?.primaryManaType) {
|
if (def?.primaryManaType && def.rawManaRegen) {
|
||||||
|
const levelMult = Math.pow(1.5, (state.level || 1) - 1);
|
||||||
grossRegen[def.primaryManaType] = (grossRegen[def.primaryManaType] || 0)
|
grossRegen[def.primaryManaType] = (grossRegen[def.primaryManaType] || 0)
|
||||||
+ (def.conversionRate || 0);
|
+ def.rawManaRegen * levelMult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { pactElementMap, grossRegen };
|
return { pactElementMap, grossRegen };
|
||||||
|
|||||||
Reference in New Issue
Block a user