fix: resolve priority 4 issues — discipline mutation, skill→discipline migration, uiStore persistence, game loop interval, toast listener leak, page re-renders
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m21s
- Issue 70: Fix discipline-slice.ts nested element mutation (use immutable spread) - Issue 71: Add persist middleware to uiStore for paused/gameOver/victory - Issue 72: Wire discipline effects into calcDamage (spell-casting, void-manipulation) - Issue 73: Fix useGameLoop interval recreation (use getState() + empty deps) - Issue 74: Fix use-toast.ts listener leak (change [state] dep to []) - Issue 75: Reduce page.tsx re-renders with useShallow for multi-field subscriptions - Issue 76: Fix createGatherMana hardcoded click mana (use computeClickMana with discipline effects) - Issue 77: Pass discipline effects to computeMaxMana/computeRegen/calcInsight in tick() - Export DisciplineBonuses type and useDisciplineStore from barrel exports - Update tests to match new function signatures
This commit is contained in:
@@ -5,16 +5,23 @@ import type { ComputedEffects } from '../effects/upgrade-effects.types';
|
||||
import { HOURS_PER_TICK } from '../constants';
|
||||
import { getTotalAttunementRegen, getTotalAttunementConversionDrain } from '../data/attunements';
|
||||
|
||||
export interface DisciplineBonuses {
|
||||
bonuses: Record<string, number>;
|
||||
multipliers: Record<string, number>;
|
||||
}
|
||||
|
||||
export function computeMaxMana(
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'skillUpgrades' | 'skillTiers'>,
|
||||
effects?: ComputedEffects
|
||||
effects?: ComputedEffects,
|
||||
discipline?: DisciplineBonuses,
|
||||
): number {
|
||||
const pu = state.prestigeUpgrades;
|
||||
const base =
|
||||
const base =
|
||||
100 +
|
||||
((state.skills || {}).manaWell || 0) * 100 +
|
||||
((pu || {}).manaWell || 0) * 500;
|
||||
|
||||
((pu || {}).manaWell || 0) * 500 +
|
||||
(discipline?.bonuses?.maxManaBonus || 0);
|
||||
|
||||
// Apply upgrade effects if provided
|
||||
if (effects) {
|
||||
return Math.floor((base + effects.maxManaBonus) * effects.maxManaMultiplier);
|
||||
@@ -28,7 +35,8 @@ export function computeMaxMana(
|
||||
|
||||
export function computeRegen(
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'skillUpgrades' | 'skillTiers' | 'attunements'>,
|
||||
effects?: ComputedEffects
|
||||
effects?: ComputedEffects,
|
||||
discipline?: DisciplineBonuses,
|
||||
): number {
|
||||
const pu = state.prestigeUpgrades;
|
||||
const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1;
|
||||
@@ -37,35 +45,41 @@ export function computeRegen(
|
||||
((state.skills || {}).manaFlow || 0) * 1 +
|
||||
((state.skills || {}).manaSpring || 0) * 2 +
|
||||
((pu || {}).manaFlow || 0) * 0.5;
|
||||
|
||||
|
||||
let regen = base * temporalBonus;
|
||||
|
||||
|
||||
// Add attunement raw mana regen
|
||||
const attunementRegen = getTotalAttunementRegen(state.attunements || {});
|
||||
regen += attunementRegen;
|
||||
|
||||
|
||||
// Apply discipline regen bonus
|
||||
if (discipline?.bonuses?.regenBonus) {
|
||||
regen += discipline.bonuses.regenBonus;
|
||||
}
|
||||
|
||||
// Apply upgrade effects if provided
|
||||
if (effects) {
|
||||
regen = (regen + effects.regenBonus + effects.permanentRegenBonus) * effects.regenMultiplier;
|
||||
}
|
||||
|
||||
|
||||
return regen;
|
||||
}
|
||||
|
||||
// Compute the effective regen (raw regen minus conversion drains) for display purposes
|
||||
export function computeEffectiveRegenForDisplay(
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'skillUpgrades' | 'skillTiers' | 'attunements'>,
|
||||
effects?: ComputedEffects
|
||||
effects?: ComputedEffects,
|
||||
discipline?: DisciplineBonuses,
|
||||
): { rawRegen: number; conversionDrain: number; effectiveRegen: number } {
|
||||
// Get the full raw regen (without conversion drain)
|
||||
const rawRegen = computeRegen(state, effects);
|
||||
|
||||
const rawRegen = computeRegen(state, effects, discipline);
|
||||
|
||||
// Calculate conversion drain
|
||||
const conversionDrain = getTotalAttunementConversionDrain(state.attunements || {});
|
||||
|
||||
|
||||
// Effective regen is what actually increases raw mana
|
||||
const effectiveRegen = Math.max(0, rawRegen - conversionDrain);
|
||||
|
||||
|
||||
return { rawRegen, conversionDrain, effectiveRegen };
|
||||
}
|
||||
|
||||
@@ -74,25 +88,29 @@ export function computeEffectiveRegenForDisplay(
|
||||
*/
|
||||
export function computeEffectiveRegen(
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'rawMana' | 'incursionStrength' | 'skillUpgrades' | 'skillTiers' | 'attunements'>,
|
||||
effects?: ComputedEffects
|
||||
effects?: ComputedEffects,
|
||||
discipline?: DisciplineBonuses,
|
||||
): number {
|
||||
// Base regen from existing function
|
||||
let regen = computeRegen(state, effects);
|
||||
|
||||
let regen = computeRegen(state, effects, discipline);
|
||||
|
||||
const incursionStrength = state.incursionStrength || 0;
|
||||
|
||||
|
||||
// Apply incursion penalty
|
||||
regen *= (1 - incursionStrength);
|
||||
|
||||
|
||||
return regen;
|
||||
}
|
||||
|
||||
export function computeClickMana(state: Pick<GameState, 'skills'>): number {
|
||||
return (
|
||||
1 +
|
||||
((state.skills || {}).manaTap || 0) * 1 +
|
||||
((state.skills || {}).manaSurge || 0) * 3
|
||||
);
|
||||
export function computeClickMana(
|
||||
state: Pick<GameState, 'skills'>,
|
||||
discipline?: DisciplineBonuses,
|
||||
): number {
|
||||
const skillTap = ((state.skills || {}).manaTap || 0) * 1;
|
||||
const skillSurge = ((state.skills || {}).manaSurge || 0) * 3;
|
||||
const discClickMult = discipline?.bonuses?.clickManaMultiplier || 0;
|
||||
|
||||
return 1 + skillTap + skillSurge + discClickMult;
|
||||
}
|
||||
|
||||
// Meditation bonus now affects regen rate directly
|
||||
@@ -100,29 +118,29 @@ export function getMeditationBonus(meditateTicks: number, skills: Record<string,
|
||||
const hasMeditation = skills.meditation === 1;
|
||||
const hasDeepTrance = skills.deepTrance === 1;
|
||||
const hasVoidMeditation = skills.voidMeditation === 1;
|
||||
|
||||
|
||||
const hours = meditateTicks * HOURS_PER_TICK;
|
||||
|
||||
|
||||
// Base meditation: ramps up over 4 hours to 1.5x
|
||||
let bonus = 1 + Math.min(hours / 4, 0.5);
|
||||
|
||||
|
||||
// With Meditation Focus: up to 2.5x after 4 hours
|
||||
if (hasMeditation && hours >= 4) {
|
||||
bonus = 2.5;
|
||||
}
|
||||
|
||||
|
||||
// With Deep Trance: up to 3.0x after 6 hours
|
||||
if (hasDeepTrance && hours >= 6) {
|
||||
bonus = 3.0;
|
||||
}
|
||||
|
||||
|
||||
// With Void Meditation: up to 5.0x after 8 hours
|
||||
if (hasVoidMeditation && hours >= 8) {
|
||||
bonus = 5.0;
|
||||
}
|
||||
|
||||
|
||||
// Apply meditation efficiency from upgrades (Deep Wellspring, etc.)
|
||||
bonus *= meditationEfficiency;
|
||||
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user