Initial commit
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m45s
All checks were successful
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m45s
This commit is contained in:
215
src/lib/game/effects.ts
Executable file
215
src/lib/game/effects.ts
Executable file
@@ -0,0 +1,215 @@
|
||||
// ─── Unified Effect System ─────────────────────────────────────────────────────────
|
||||
// This module consolidates ALL effect sources into a single computation:
|
||||
// - Skill upgrade effects (from milestone upgrades)
|
||||
// - Equipment enchantment effects (from enchanted gear)
|
||||
// - Direct skill bonuses (from skill levels)
|
||||
|
||||
import type { GameState, EquipmentInstance } from './types';
|
||||
import { ENCHANTMENT_EFFECTS } from './data/enchantment-effects';
|
||||
import { computeEffects, hasSpecial, SPECIAL_EFFECTS, type ComputedEffects } from './upgrade-effects';
|
||||
|
||||
// Re-export for convenience
|
||||
export { computeEffects, hasSpecial, SPECIAL_EFFECTS, type ComputedEffects };
|
||||
|
||||
// ─── Equipment Effect Computation ────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Compute all effects from equipped enchantments
|
||||
*/
|
||||
export function computeEquipmentEffects(
|
||||
equipmentInstances: Record<string, EquipmentInstance>,
|
||||
equippedInstances: Record<string, string | null>
|
||||
): {
|
||||
bonuses: Record<string, number>;
|
||||
multipliers: Record<string, number>;
|
||||
specials: Set<string>;
|
||||
} {
|
||||
const bonuses: Record<string, number> = {};
|
||||
const multipliers: Record<string, number> = {};
|
||||
const specials = new Set<string>();
|
||||
|
||||
// Iterate through all equipped items
|
||||
for (const instanceId of Object.values(equippedInstances)) {
|
||||
if (!instanceId) continue;
|
||||
const instance = equipmentInstances[instanceId];
|
||||
if (!instance) continue;
|
||||
|
||||
// Process each enchantment on the item
|
||||
for (const ench of instance.enchantments) {
|
||||
const effectDef = ENCHANTMENT_EFFECTS[ench.effectId];
|
||||
if (!effectDef) continue;
|
||||
|
||||
const { effect } = effectDef;
|
||||
|
||||
if (effect.type === 'bonus' && effect.stat && effect.value) {
|
||||
// Bonus effects add to the stat
|
||||
bonuses[effect.stat] = (bonuses[effect.stat] || 0) + effect.value * ench.stacks;
|
||||
} else if (effect.type === 'multiplier' && effect.stat && effect.value) {
|
||||
// Multiplier effects multiply together
|
||||
// For multipliers, we need to track them separately and apply as product
|
||||
const key = effect.stat;
|
||||
if (!multipliers[key]) {
|
||||
multipliers[key] = 1;
|
||||
}
|
||||
// Each stack applies the multiplier
|
||||
for (let i = 0; i < ench.stacks; i++) {
|
||||
multipliers[key] *= effect.value;
|
||||
}
|
||||
} else if (effect.type === 'special' && effect.specialId) {
|
||||
specials.add(effect.specialId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { bonuses, multipliers, specials };
|
||||
}
|
||||
|
||||
// ─── Unified Computed Effects ────────────────────────────────────────────────────
|
||||
|
||||
export interface UnifiedEffects extends ComputedEffects {
|
||||
// Equipment bonuses
|
||||
equipmentBonuses: Record<string, number>;
|
||||
equipmentMultipliers: Record<string, number>;
|
||||
equipmentSpecials: Set<string>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute all effects from all sources: skill upgrades + equipment enchantments
|
||||
*/
|
||||
export function computeAllEffects(
|
||||
skillUpgrades: Record<string, string[]>,
|
||||
skillTiers: Record<string, number>,
|
||||
equipmentInstances: Record<string, EquipmentInstance>,
|
||||
equippedInstances: Record<string, string | null>
|
||||
): UnifiedEffects {
|
||||
// Get skill upgrade effects
|
||||
const upgradeEffects = computeEffects(skillUpgrades, skillTiers);
|
||||
|
||||
// Get equipment effects
|
||||
const equipmentEffects = computeEquipmentEffects(equipmentInstances, equippedInstances);
|
||||
|
||||
// Merge the effects
|
||||
const merged: UnifiedEffects = {
|
||||
...upgradeEffects,
|
||||
// Merge equipment bonuses with upgrade bonuses
|
||||
maxManaBonus: upgradeEffects.maxManaBonus + (equipmentEffects.bonuses.maxMana || 0),
|
||||
regenBonus: upgradeEffects.regenBonus + (equipmentEffects.bonuses.regen || 0),
|
||||
clickManaBonus: upgradeEffects.clickManaBonus + (equipmentEffects.bonuses.clickMana || 0),
|
||||
baseDamageBonus: upgradeEffects.baseDamageBonus + (equipmentEffects.bonuses.baseDamage || 0),
|
||||
elementCapBonus: upgradeEffects.elementCapBonus + (equipmentEffects.bonuses.elementCap || 0),
|
||||
// Merge equipment multipliers with upgrade multipliers
|
||||
maxManaMultiplier: upgradeEffects.maxManaMultiplier * (equipmentEffects.multipliers.maxMana || 1),
|
||||
regenMultiplier: upgradeEffects.regenMultiplier * (equipmentEffects.multipliers.regen || 1),
|
||||
clickManaMultiplier: upgradeEffects.clickManaMultiplier * (equipmentEffects.multipliers.clickMana || 1),
|
||||
baseDamageMultiplier: upgradeEffects.baseDamageMultiplier * (equipmentEffects.multipliers.baseDamage || 1),
|
||||
attackSpeedMultiplier: upgradeEffects.attackSpeedMultiplier * (equipmentEffects.multipliers.attackSpeed || 1),
|
||||
elementCapMultiplier: upgradeEffects.elementCapMultiplier * (equipmentEffects.multipliers.elementCap || 1),
|
||||
// Merge specials
|
||||
specials: new Set([...upgradeEffects.specials, ...equipmentEffects.specials]),
|
||||
// Store equipment effects for reference
|
||||
equipmentBonuses: equipmentEffects.bonuses,
|
||||
equipmentMultipliers: equipmentEffects.multipliers,
|
||||
equipmentSpecials: equipmentEffects.specials,
|
||||
};
|
||||
|
||||
// Handle special stats that are equipment-only
|
||||
if (equipmentEffects.bonuses.critChance) {
|
||||
merged.critChanceBonus += equipmentEffects.bonuses.critChance;
|
||||
}
|
||||
if (equipmentEffects.bonuses.meditationEfficiency) {
|
||||
// This is a multiplier in equipment, convert to additive for simplicity
|
||||
// Equipment gives +10% per stack, so add it to the base
|
||||
merged.meditationEfficiency *= (equipmentEffects.multipliers.meditationEfficiency || 1);
|
||||
}
|
||||
if (equipmentEffects.bonuses.studySpeed) {
|
||||
merged.studySpeedMultiplier *= (equipmentEffects.multipliers.studySpeed || 1);
|
||||
}
|
||||
if (equipmentEffects.bonuses.insightGain) {
|
||||
// Store separately - insight multiplier
|
||||
(merged as any).insightGainMultiplier = (equipmentEffects.multipliers.insightGain || 1);
|
||||
}
|
||||
if (equipmentEffects.bonuses.guardianDamage) {
|
||||
(merged as any).guardianDamageMultiplier = (equipmentEffects.multipliers.guardianDamage || 1);
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to get unified effects from game state
|
||||
*/
|
||||
export function getUnifiedEffects(state: Pick<GameState, 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>): UnifiedEffects {
|
||||
return computeAllEffects(
|
||||
state.skillUpgrades || {},
|
||||
state.skillTiers || {},
|
||||
state.equipmentInstances,
|
||||
state.equippedInstances
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Stat Computation with All Effects ───────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Compute max mana with all effect sources
|
||||
*/
|
||||
export function computeTotalMaxMana(
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
||||
effects?: UnifiedEffects
|
||||
): number {
|
||||
const pu = state.prestigeUpgrades;
|
||||
const base =
|
||||
100 +
|
||||
(state.skills.manaWell || 0) * 100 +
|
||||
(pu.manaWell || 0) * 500;
|
||||
|
||||
if (!effects) {
|
||||
effects = getUnifiedEffects(state);
|
||||
}
|
||||
|
||||
return Math.floor((base + effects.maxManaBonus) * effects.maxManaMultiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute regen with all effect sources
|
||||
*/
|
||||
export function computeTotalRegen(
|
||||
state: Pick<GameState, 'skills' | 'prestigeUpgrades' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
||||
effects?: UnifiedEffects
|
||||
): number {
|
||||
const pu = state.prestigeUpgrades;
|
||||
const temporalBonus = 1 + (pu.temporalEcho || 0) * 0.1;
|
||||
const base =
|
||||
2 +
|
||||
(state.skills.manaFlow || 0) * 1 +
|
||||
(state.skills.manaSpring || 0) * 2 +
|
||||
(pu.manaFlow || 0) * 0.5;
|
||||
|
||||
let regen = base * temporalBonus;
|
||||
|
||||
if (!effects) {
|
||||
effects = getUnifiedEffects(state);
|
||||
}
|
||||
|
||||
regen = (regen + effects.regenBonus + effects.permanentRegenBonus) * effects.regenMultiplier;
|
||||
|
||||
return regen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute click mana with all effect sources
|
||||
*/
|
||||
export function computeTotalClickMana(
|
||||
state: Pick<GameState, 'skills' | 'skillUpgrades' | 'skillTiers' | 'equipmentInstances' | 'equippedInstances'>,
|
||||
effects?: UnifiedEffects
|
||||
): number {
|
||||
const base =
|
||||
1 +
|
||||
(state.skills.manaTap || 0) * 1 +
|
||||
(state.skills.manaSurge || 0) * 3;
|
||||
|
||||
if (!effects) {
|
||||
effects = getUnifiedEffects(state as any);
|
||||
}
|
||||
|
||||
return Math.floor((base + effects.clickManaBonus) * effects.clickManaMultiplier);
|
||||
}
|
||||
Reference in New Issue
Block a user