generated from nhcarrigan/template
feat: vampire expansion chunk 4 — API routes and services
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.
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* @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,
|
||||
};
|
||||
Reference in New Issue
Block a user