Files
Mana-Loop/src/lib/game/utils/mana-utils.ts
T
n8n-gitea ab3afae2a6
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m2s
feat: overhaul mana conversion system to unified regen-deduction model
- New files: element-distance.ts, conversion-costs.ts, conversion-rates.ts
- All conversion types (discipline, attunement, pact) use unified formula
- Conversion costs scale exponentially by element tier (10^(d+1) raw, 10*(d+1) per component)
- Costs deducted from regen, not from mana pool
- Auto-pause on insufficient regen with UI warning
- Meditation boosts conversion rates (reduced by distance)
- Attunement levels provide +50% multiplicative bonus per level
- Guardian pacts provide +0.15/hr base rate + invoker level bonus
- Removed convertMana, processConvertAction, craftComposite from manaStore
- Stats tab shows per-element conversion breakdown with formulas
- ManaDisplay shows per-element net regen rates
- All 916 tests pass, all files under 400 lines
2026-06-04 18:12:41 +02:00

128 lines
4.9 KiB
TypeScript

// ─── Mana & Regen Utilities ──────────────────────────────────────────────────
import type { AttunementState } from '../types';
import type { ComputedEffects } from '../effects/upgrade-effects.types';
import { HOURS_PER_TICK } from '../constants';
import { getTotalAttunementRegen } from '../data/attunements';
export interface DisciplineBonuses {
bonuses: Record<string, number>;
multipliers: Record<string, number>;
}
// ─── Mana Params ────────────────────────────────────────────────────────────
export interface ManaComputeParams {
prestigeUpgrades?: Record<string, number>;
}
export interface RegenComputeParams extends ManaComputeParams {
attunements: Record<string, AttunementState>;
}
export interface EffectiveRegenParams extends RegenComputeParams {
rawMana: number;
incursionStrength: number;
}
// ─── Max Mana ────────────────────────────────────────────────────────────────
export function computeMaxMana(
state: Partial<ManaComputeParams>,
effects?: ComputedEffects,
discipline?: DisciplineBonuses,
): number {
const pu = state.prestigeUpgrades || {};
const base =
100 +
(pu.manaWell || 0) * 500 +
(discipline?.bonuses?.maxManaBonus || 0);
if (effects) {
return Math.floor((base + effects.maxManaBonus) * effects.maxManaMultiplier);
}
return base;
}
// ─── Regen ────────────────────────────────────────────────────────────────────
export function computeRegen(
state: Pick<RegenComputeParams, 'prestigeUpgrades' | 'attunements'>,
effects?: ComputedEffects,
discipline?: DisciplineBonuses,
): number {
const pu = state.prestigeUpgrades || {};
const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1;
const base =
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;
}
// ─── Effective Regen for Display ──────────────────────────────────────────────
export function computeEffectiveRegenForDisplay(
state: Pick<RegenComputeParams, 'prestigeUpgrades' | 'attunements'>,
effects?: ComputedEffects,
discipline?: DisciplineBonuses,
): { rawRegen: number; conversionDrain: number; effectiveRegen: number } {
const rawRegen = computeRegen(state, effects, discipline);
return { rawRegen, conversionDrain: 0, effectiveRegen: rawRegen };
}
// ─── Effective Regen (dynamic) ────────────────────────────────────────────────
export function computeEffectiveRegen(
state: Pick<RegenComputeParams, 'prestigeUpgrades' | 'attunements'> & { rawMana: number; incursionStrength: number },
effects?: ComputedEffects,
discipline?: DisciplineBonuses,
): number {
let regen = computeRegen(state, effects, discipline);
const incursionStrength = state.incursionStrength || 0;
regen *= (1 - incursionStrength);
return regen;
}
// ─── Click Mana ───────────────────────────────────────────────────────────────
export function computeClickMana(
discipline?: DisciplineBonuses,
): number {
const discClickBonus = discipline?.bonuses?.clickManaBonus || 0;
return 1 + discClickBonus;
}
// ─── Meditation Bonus ─────────────────────────────────────────────────────────
export function getMeditationBonus(
meditateTicks: number,
meditationEfficiency: number = 1,
disciplineMeditationCap: number = 0,
): number {
const hours = meditateTicks * HOURS_PER_TICK;
// Continuous ramp: 1 + (hours / 8) * 4, capped at 2.5 + disciplineMeditationCap
const maxMultiplier = 2.5 + disciplineMeditationCap;
const bonus = Math.min(1 + (hours / 8) * 4, maxMultiplier);
// Apply meditation efficiency from upgrades
return bonus * meditationEfficiency;
}