/** * @file Enlightenment service handling eligibility checks and post-enlightenment state building. * @copyright nhcarrigan * @license Naomi's Public License * @author Naomi Carrigan */ /* eslint-disable stylistic/max-len -- Service logic requires long lines */ import { defaultEnlightenmentUpgrades } from "../data/goddessEnlightenmentUpgrades.js"; import { initialGoddessState } from "../data/initialState.js"; import type { EnlightenmentData, GameState } from "@elysium/types"; /** * ID of the final goddess boss — must be defeated to unlock Enlightenment. */ const finalGoddessBossId = "divine_heart_sovereign"; const getCategoryMultiplier = ( purchasedIds: Array, category: string, ): number => { return defaultEnlightenmentUpgrades.filter((upgrade) => { return upgrade.category === category && purchasedIds.includes(upgrade.id); }).reduce((mult, upgrade) => { return mult * upgrade.multiplier; }, 1); }; /** * Computes all five stardust multipliers from the purchased enlightenment upgrade IDs. * @param purchasedUpgradeIds - The array of purchased enlightenment upgrade IDs. * @returns An object containing all five stardust multiplier values. */ const computeEnlightenmentMultipliers = ( purchasedUpgradeIds: Array, ): Omit => { return { stardustCombatMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "combat"), stardustConsecrationDivinityMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "consecration_divinity"), stardustConsecrationThresholdMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "consecration_threshold"), stardustMetaMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "stardust_meta"), stardustPrayersMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "prayers"), }; }; /** * Returns true when the player is eligible for Enlightenment: * they must have defeated the final goddess boss at least once. * @param state - The current game state. * @returns Whether the player is eligible for Enlightenment. */ const isEligibleForEnlightenment = (state: GameState): boolean => { // eslint-disable-next-line capitalized-comments -- v8 ignore /* v8 ignore next 3 -- @preserve */ if (state.goddess === undefined) { return false; } return state.goddess.bosses.some((boss) => { return boss.id === finalGoddessBossId && boss.status === "defeated"; }); }; /** * Calculates the stardust yield from an Enlightenment. * Formula: MAX(1, FLOOR(SQRT(consecrationCount) * metaMultiplier)). * @param consecrationCount - The number of consecrations completed before this Enlightenment. * @param metaMultiplier - Multiplier from prior enlightenment upgrades applied to stardust yield. * @returns The stardust earned. */ const calculateStardustYield = ( consecrationCount: number, metaMultiplier: number, ): number => { return Math.max(1, Math.floor(Math.sqrt(consecrationCount) * metaMultiplier)); }; /** * Builds the updated goddess state after an Enlightenment — a full goddess reset. * Wipes everything including consecration, preserving only equipment, achievements, and enlightenment data. * @param state - The current game state before enlightenment. * @returns The stardust earned and the updated goddess state. */ const buildPostEnlightenmentState = ( state: GameState, ): { stardustEarned: number; updatedGoddess: NonNullable } => { // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Caller must ensure goddess exists const goddess = state.goddess as NonNullable; const metaMultiplier = goddess.enlightenment.stardustMetaMultiplier; const stardustEarned = calculateStardustYield( goddess.consecration.count, metaMultiplier, ); const updatedCount = goddess.enlightenment.count + 1; const updatedStardust = goddess.enlightenment.stardust + stardustEarned; const updatedPurchasedIds = goddess.enlightenment.purchasedUpgradeIds; const updatedMultipliers = computeEnlightenmentMultipliers(updatedPurchasedIds); const updatedEnlightenment: EnlightenmentData = { count: updatedCount, purchasedUpgradeIds: updatedPurchasedIds, stardust: updatedStardust, ...updatedMultipliers, }; const freshGoddess = initialGoddessState(); const updatedGoddess: NonNullable = { ...freshGoddess, achievements: goddess.achievements, bosses: freshGoddess.bosses.map((b) => { const existing = goddess.bosses.find((gb) => { return gb.id === b.id; }); return { ...b, bountyDivinityClaimed: existing?.bountyDivinityClaimed ?? false, }; }), enlightenment: updatedEnlightenment, equipment: goddess.equipment, lastTickAt: Date.now(), lifetimeBossesDefeated: goddess.lifetimeBossesDefeated, lifetimePrayersEarned: goddess.lifetimePrayersEarned, lifetimeQuestsCompleted: goddess.lifetimeQuestsCompleted, totalPrayersEarned: 0, }; return { stardustEarned, updatedGoddess }; }; export { buildPostEnlightenmentState, calculateStardustYield, computeEnlightenmentMultipliers, isEligibleForEnlightenment, };