b0eea7dadd
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 35s
- Split 636-line skills-v2-defs.ts into 9 category files (all under 400 lines) - Add skills-v2-registry.ts to build SKILLS_V2 flat record from modules - Fix missing re-export of SKILLS_V2 from skills-v2.ts - Fix clickMana clamping: remove Math.round to allow fractional values - Fix golemDuration clamping: remove Math.round to allow fractional values - Fix guardianConstructs effect: duration uses 'add' mode instead of 'multiply' - All 70 existing tests pass
269 lines
7.1 KiB
TypeScript
269 lines
7.1 KiB
TypeScript
import type { SkillV2Def, SkillEffect, StatKey, ComputedStats } from './skills-v2-types';
|
|
import { SKILLS_V2 } from './skills-v2-defs';
|
|
|
|
// Re-export individual skills from category modules
|
|
export {
|
|
manaWell,
|
|
manaFlow,
|
|
manaOverflow,
|
|
manaTap,
|
|
manaSurge,
|
|
manaSpring,
|
|
quickLearner,
|
|
focusedMind,
|
|
knowledgeRetention,
|
|
meditation,
|
|
deepTrance,
|
|
voidMeditation,
|
|
} from './skills-core';
|
|
|
|
export {
|
|
enchanting,
|
|
efficientEnchant,
|
|
enchantSpeed,
|
|
essenceRefining,
|
|
disenchanting,
|
|
} from './skills-enchant';
|
|
|
|
export {
|
|
arcaneFury,
|
|
combatTraining,
|
|
precision,
|
|
elementalMastery,
|
|
attackSpeed,
|
|
armorPiercing,
|
|
spellDamage,
|
|
} from './skills-combat';
|
|
|
|
export {
|
|
golemMastery,
|
|
golemEfficiency,
|
|
golemLongevity,
|
|
} from './skills-golemancy';
|
|
|
|
export {
|
|
invocation,
|
|
pactMastery,
|
|
guardianLore,
|
|
} from './skills-invocation';
|
|
|
|
export {
|
|
effCrafting,
|
|
fieldRepair,
|
|
} from './skills-crafting';
|
|
|
|
export {
|
|
fireManaCap,
|
|
waterManaCap,
|
|
airManaCap,
|
|
earthManaCap,
|
|
lightManaCap,
|
|
darkManaCap,
|
|
deathManaCap,
|
|
metalManaCap,
|
|
sandManaCap,
|
|
lightningManaCap,
|
|
transferenceManaCap,
|
|
} from './skills-element-caps';
|
|
|
|
export {
|
|
pactWeaving,
|
|
guardianConstructs,
|
|
enchantedGolemancy,
|
|
} from './skills-hybrid';
|
|
|
|
export {
|
|
researchManaSpells,
|
|
researchFireSpells,
|
|
researchWaterSpells,
|
|
researchAirSpells,
|
|
researchEarthSpells,
|
|
researchLightSpells,
|
|
researchDarkSpells,
|
|
researchLifeDeathSpells,
|
|
researchAdvancedFire,
|
|
researchAdvancedWater,
|
|
researchAdvancedAir,
|
|
researchAdvancedEarth,
|
|
researchAdvancedLight,
|
|
researchAdvancedDark,
|
|
researchMasterFire,
|
|
researchMasterWater,
|
|
researchMasterEarth,
|
|
researchDamageEffects,
|
|
researchCombatEffects,
|
|
researchManaEffects,
|
|
researchAdvancedManaEffects,
|
|
researchUtilityEffects,
|
|
researchSpecialEffects,
|
|
researchOverpower,
|
|
researchMetalSpells,
|
|
researchSandSpells,
|
|
researchLightningSpells,
|
|
researchAdvancedMetal,
|
|
researchAdvancedSand,
|
|
researchAdvancedLightning,
|
|
researchMasterMetal,
|
|
researchMasterSand,
|
|
researchMasterLightning,
|
|
researchTransferenceSpells,
|
|
researchAdvancedTransference,
|
|
researchMasterTransference,
|
|
researchAdvancedFireCap,
|
|
researchAdvancedWaterCap,
|
|
researchAdvancedAirCap,
|
|
researchAdvancedEarthCap,
|
|
researchAdvancedLightCap,
|
|
researchAdvancedDarkCap,
|
|
researchAdvancedDeathCap,
|
|
researchMasterFireCap,
|
|
researchMasterWaterCap,
|
|
researchMasterAirCap,
|
|
researchMasterEarthCap,
|
|
researchMasterLightCap,
|
|
researchMasterDarkCap,
|
|
researchMasterDeathCap,
|
|
researchMetalCapacity,
|
|
researchAdvancedMetalCap,
|
|
researchSandCapacity,
|
|
researchAdvancedSandCap,
|
|
researchLightningCapacity,
|
|
researchAdvancedLightningCap,
|
|
researchCrystalCapacity,
|
|
researchAdvancedCrystalCap,
|
|
researchStellarCapacity,
|
|
researchAdvancedStellarCap,
|
|
researchVoidCapacity,
|
|
researchAdvancedVoidCap,
|
|
} from './skills-research';
|
|
|
|
export { SKILLS_V2 };
|
|
|
|
// Default Base Stats
|
|
export const BASE_STATS: ComputedStats = {
|
|
maxMana: 100,
|
|
manaRegen: 2,
|
|
clickMana: 1,
|
|
elementCap: 10,
|
|
studySpeed: 1,
|
|
studyCostMult: 1,
|
|
meditationEfficiency: 1,
|
|
enchantCapacity: 100,
|
|
enchantSpeed: 1,
|
|
enchantPower: 1,
|
|
disenchantRecovery: 1,
|
|
baseDamage: 5,
|
|
damageMultiplier: 1,
|
|
attackSpeed: 1,
|
|
critChance: 0,
|
|
critMultiplier: 1.5,
|
|
armorPierce: 0,
|
|
insightGain: 1,
|
|
golemDamage: 1,
|
|
golemDuration: 1,
|
|
pactMultiplier: 1,
|
|
conversionRate: 1,
|
|
spellDamage: 1,
|
|
guardianDamage: 1,
|
|
craftSpeed: 1,
|
|
repairSpeed: 1,
|
|
fireCap: 0, waterCap: 0, airCap: 0, earthCap: 0,
|
|
lightCap: 0, darkCap: 0, deathCap: 0,
|
|
metalCap: 0, sandCap: 0, lightningCap: 0,
|
|
transferenceCap: 0, crystalCap: 0, stellarCap: 0, voidCap: 0,
|
|
elementalDamage: 1,
|
|
};
|
|
|
|
const ELEMENT_CAP_STATS: Record<string, keyof ComputedStats> = {
|
|
fire: 'fireCap', water: 'waterCap', air: 'airCap', earth: 'earthCap',
|
|
light: 'lightCap', dark: 'darkCap', death: 'deathCap', metal: 'metalCap',
|
|
sand: 'sandCap', lightning: 'lightningCap', transference: 'transferenceCap',
|
|
crystal: 'crystalCap', stellar: 'stellarCap', void: 'voidCap',
|
|
};
|
|
|
|
/**
|
|
* Compute all game stats from skill levels.
|
|
*/
|
|
export function computeStats(
|
|
skills: Record<string, number>,
|
|
prestigeUpgrades: Record<string, number> = {}
|
|
): ComputedStats {
|
|
const stats: ComputedStats = { ...BASE_STATS };
|
|
|
|
for (const [skillId, level] of Object.entries(skills)) {
|
|
if (level <= 0) continue;
|
|
const def = SKILLS_V2[skillId];
|
|
if (!def) continue;
|
|
for (const effect of def.effects) {
|
|
const key = effect.stat as keyof ComputedStats;
|
|
const currentVal = stats[key] as number;
|
|
if (effect.mode === 'add') {
|
|
(stats[key] as number) = currentVal + effect.valuePerLevel * level;
|
|
} else {
|
|
const perLevelMultiplier = 1 + effect.valuePerLevel;
|
|
let result = currentVal;
|
|
for (let i = 0; i < level; i++) result *= perLevelMultiplier;
|
|
(stats[key] as number) = result;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prestigeUpgrades.manaWell) stats.maxMana += prestigeUpgrades.manaWell * 500;
|
|
if (prestigeUpgrades.manaFlow) stats.manaRegen += prestigeUpgrades.manaFlow * 0.5;
|
|
if (prestigeUpgrades.elementalAttune) stats.elementCap += prestigeUpgrades.elementalAttune * 25;
|
|
if (prestigeUpgrades.pactBinding) stats.pactMultiplier += prestigeUpgrades.pactBinding * 0.1;
|
|
if (prestigeUpgrades.insightAmp) stats.insightGain *= 1 + prestigeUpgrades.insightAmp * 0.25;
|
|
|
|
let elementCapFromSkills = 0;
|
|
for (const [, statKey] of Object.entries(ELEMENT_CAP_STATS)) {
|
|
const val = stats[statKey] as number;
|
|
if (val > 0) elementCapFromSkills += val;
|
|
}
|
|
if (elementCapFromSkills > 0) stats.elementCap += elementCapFromSkills;
|
|
|
|
stats.maxMana = Math.max(1, Math.round(stats.maxMana));
|
|
stats.manaRegen = Math.round(stats.manaRegen * 100) / 100;
|
|
stats.clickMana = Math.max(1, stats.clickMana);
|
|
stats.elementCap = Math.max(10, Math.round(stats.elementCap));
|
|
stats.baseDamage = Math.max(1, Math.round(stats.baseDamage));
|
|
stats.critChance = Math.min(1, Math.max(0, stats.critChance));
|
|
stats.armorPierce = Math.min(1, Math.max(0, stats.armorPierce));
|
|
stats.attackSpeed = Math.max(0.1, stats.attackSpeed);
|
|
stats.insightGain = Math.max(0, stats.insightGain);
|
|
stats.golemDamage = Math.max(0.1, stats.golemDamage);
|
|
stats.golemDuration = Math.max(1, stats.golemDuration);
|
|
stats.enchantCapacity = Math.max(10, Math.round(stats.enchantCapacity));
|
|
stats.conversionRate = Math.max(0, stats.conversionRate);
|
|
|
|
return stats;
|
|
}
|
|
|
|
/**
|
|
* Get the base key for a tiered skill (strips _tN suffix).
|
|
*/
|
|
export function getBaseSkillId(skillId: string): string {
|
|
const match = skillId.match(/^(.+?)_t(\d+)$/);
|
|
return match ? match[1] : skillId;
|
|
}
|
|
|
|
/**
|
|
* Check if a skill has prerequisites that are met.
|
|
*/
|
|
export function hasPrerequisites(
|
|
skills: Record<string, number>,
|
|
prerequisites?: Record<string, number>
|
|
): boolean {
|
|
if (!prerequisites) return true;
|
|
for (const [reqId, reqLevel] of Object.entries(prerequisites)) {
|
|
if ((skills[reqId] || 0) < reqLevel) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/** Legacy compat wrapper */
|
|
export function computeStatsLegacy(state: {
|
|
skills: Record<string, number>;
|
|
prestigeUpgrades?: Record<string, number>;
|
|
}): ComputedStats {
|
|
return computeStats(state.skills, state.prestigeUpgrades || {});
|
|
} |