generated from nhcarrigan/template
8fa5d12f05
Adds six new routes (vampire-boss, vampire-upgrade, vampire-craft, vampire-explore, siring, vampire-awakening) with matching siring and awakening services, and all necessary request/response types.
138 lines
5.2 KiB
TypeScript
138 lines
5.2 KiB
TypeScript
/**
|
|
* @file Awakening service handling eligibility checks and post-awakening state building.
|
|
* @copyright nhcarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
|
|
/* eslint-disable stylistic/max-len -- Service logic requires long lines */
|
|
import { initialVampireState } from "../data/initialState.js";
|
|
import { defaultVampireAwakeningUpgrades } from "../data/vampireAwakeningUpgrades.js";
|
|
import type { AwakeningData, GameState } from "@elysium/types";
|
|
|
|
/**
|
|
* The ID of the final vampire boss whose defeat triggers eligibility for awakening.
|
|
*/
|
|
const finalVampireBossId = "eternal_darkness";
|
|
|
|
const getCategoryMultiplier = (
|
|
purchasedIds: Array<string>,
|
|
category: string,
|
|
): number => {
|
|
return defaultVampireAwakeningUpgrades.filter((upgrade) => {
|
|
return upgrade.category === category && purchasedIds.includes(upgrade.id);
|
|
}).reduce((mult, upgrade) => {
|
|
return mult * upgrade.multiplier;
|
|
}, 1);
|
|
};
|
|
|
|
/**
|
|
* Computes all five soul shard multipliers from the purchased awakening upgrade IDs.
|
|
* @param purchasedUpgradeIds - The array of purchased awakening upgrade IDs.
|
|
* @returns An object containing all five soul shard multiplier values.
|
|
*/
|
|
const computeAwakeningMultipliers = (
|
|
purchasedUpgradeIds: Array<string>,
|
|
): Omit<AwakeningData, "count" | "soulShards" | "purchasedUpgradeIds"> => {
|
|
return {
|
|
soulShardsBloodMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "blood"),
|
|
soulShardsCombatMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "combat"),
|
|
soulShardsMetaMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "soulshards_meta"),
|
|
soulShardsSiringIchorMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "siring_ichor"),
|
|
soulShardsSiringThresholdMultiplier: getCategoryMultiplier(purchasedUpgradeIds, "siring_threshold"),
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Returns true if the player is eligible to awaken:
|
|
* the final vampire boss must have been defeated.
|
|
* @param state - The current game state.
|
|
* @returns Whether the player is eligible for awakening.
|
|
*/
|
|
const isEligibleForAwakening = (state: GameState): boolean => {
|
|
// eslint-disable-next-line capitalized-comments -- v8 ignore
|
|
/* v8 ignore next 3 -- @preserve */
|
|
if (state.vampire === undefined) {
|
|
return false;
|
|
}
|
|
return state.vampire.bosses.some((boss) => {
|
|
return boss.id === finalVampireBossId && boss.status === "defeated";
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Calculates the soul shards yield from an awakening.
|
|
* Formula: MAX(1, FLOOR(SQRT(siringCount) * metaMultiplier)).
|
|
* @param siringCount - The number of sirings completed.
|
|
* @param metaMultiplier - Multiplier from soul shard meta upgrades applied to yield.
|
|
* @returns The soul shards earned.
|
|
*/
|
|
const calculateSoulShardsYield = (
|
|
siringCount: number,
|
|
metaMultiplier: number,
|
|
): number => {
|
|
return Math.max(1, Math.floor(Math.sqrt(siringCount) * metaMultiplier));
|
|
};
|
|
|
|
/**
|
|
* Builds the updated vampire state after an awakening reset.
|
|
* Resets the current run including siring data (bosses, quests, thralls, upgrades, zones, siring data).
|
|
* Preserves: equipment, achievements, awakening data (updated), eternal sovereignty, lifetime stats.
|
|
* @param state - The current game state before awakening.
|
|
* @returns The soul shards earned and the updated vampire state.
|
|
*/
|
|
const buildPostAwakeningState = (
|
|
state: GameState,
|
|
): { soulShardsEarned: number; updatedVampire: NonNullable<GameState["vampire"]> } => {
|
|
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- Caller must ensure vampire exists */
|
|
const vampire = state.vampire as NonNullable<GameState["vampire"]>;
|
|
|
|
const metaMultiplier = vampire.awakening.soulShardsMetaMultiplier;
|
|
const soulShardsEarned = calculateSoulShardsYield(vampire.siring.count, metaMultiplier);
|
|
|
|
const updatedCount = vampire.awakening.count + 1;
|
|
const updatedSoulShards = vampire.awakening.soulShards + soulShardsEarned;
|
|
const updatedPurchasedIds = vampire.awakening.purchasedUpgradeIds;
|
|
const updatedMultipliers = computeAwakeningMultipliers(updatedPurchasedIds);
|
|
|
|
const updatedAwakening: AwakeningData = {
|
|
count: updatedCount,
|
|
purchasedUpgradeIds: updatedPurchasedIds,
|
|
soulShards: updatedSoulShards,
|
|
...updatedMultipliers,
|
|
};
|
|
|
|
const freshVampire = initialVampireState();
|
|
|
|
const updatedVampire: NonNullable<GameState["vampire"]> = {
|
|
...freshVampire,
|
|
achievements: vampire.achievements,
|
|
awakening: updatedAwakening,
|
|
bosses: freshVampire.bosses.map((b) => {
|
|
const existing = vampire.bosses.find((vb) => {
|
|
return vb.id === b.id;
|
|
});
|
|
return {
|
|
...b,
|
|
bountyIchorClaimed: existing?.bountyIchorClaimed ?? false,
|
|
};
|
|
}),
|
|
equipment: vampire.equipment,
|
|
eternalSovereignty: vampire.eternalSovereignty,
|
|
lastTickAt: Date.now(),
|
|
lifetimeBloodEarned: vampire.lifetimeBloodEarned,
|
|
lifetimeBossesDefeated: vampire.lifetimeBossesDefeated,
|
|
lifetimeQuestsCompleted: vampire.lifetimeQuestsCompleted,
|
|
totalBloodEarned: 0,
|
|
};
|
|
|
|
return { soulShardsEarned, updatedVampire };
|
|
};
|
|
|
|
export {
|
|
buildPostAwakeningState,
|
|
calculateSoulShardsYield,
|
|
computeAwakeningMultipliers,
|
|
isEligibleForAwakening,
|
|
};
|