diff --git a/src/controllers/api/getVoidProjectionRewardsController.ts b/src/controllers/api/getVoidProjectionRewardsController.ts new file mode 100644 index 00000000..9a956a20 --- /dev/null +++ b/src/controllers/api/getVoidProjectionRewardsController.ts @@ -0,0 +1,99 @@ +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { addMiscItems, getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { handleStoreItemAcquisition } from "@/src/services/purchaseService"; +import { getRandomWeightedReward2 } from "@/src/services/rngService"; +import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; +import { logger } from "@/src/utils/logger"; +import { RequestHandler } from "express"; +import { ExportRelics, ExportRewards, TRarity } from "warframe-public-export-plus"; + +export const getVoidProjectionRewardsController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const data = getJSONfromString(String(req.body)) as IVoidProjectionRewardRequest; + const response: IVoidProjectionRewardResponse = { + CurrentWave: data.CurrentWave, + ParticipantInfo: data.ParticipantInfo, + DifficultyTier: data.DifficultyTier + }; + if (data.ParticipantInfo.QualifiesForReward) { + const relic = ExportRelics[data.ParticipantInfo.VoidProjection]; + const weights = refinementToWeights[relic.quality]; + logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights); + const reward = getRandomWeightedReward2( + ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics + weights + )!; + logger.debug(`relic rolled`, reward); + response.ParticipantInfo.Reward = reward.type; + + // Remove relic + const inventory = await getInventory(accountId); + addMiscItems(inventory, [ + { + ItemType: data.ParticipantInfo.VoidProjection, + ItemCount: -1 + } + ]); + await inventory.save(); + + // Give reward + await handleStoreItemAcquisition(reward.type, accountId, reward.itemCount); + } + res.json(response); +}; + +const refinementToWeights = { + VPQ_BRONZE: { + COMMON: 0.76, + UNCOMMON: 0.22, + RARE: 0.02, + LEGENDARY: 0 + }, + VPQ_SILVER: { + COMMON: 0.7, + UNCOMMON: 0.26, + RARE: 0.04, + LEGENDARY: 0 + }, + VPQ_GOLD: { + COMMON: 0.6, + UNCOMMON: 0.34, + RARE: 0.06, + LEGENDARY: 0 + }, + VPQ_PLATINUM: { + COMMON: 0.5, + UNCOMMON: 0.4, + RARE: 0.1, + LEGENDARY: 0 + } +}; + +interface IVoidProjectionRewardRequest { + CurrentWave: number; + ParticipantInfo: IParticipantInfo; + VoidTier: string; + DifficultyTier: number; + VoidProjectionRemovalHash: string; +} + +interface IVoidProjectionRewardResponse { + CurrentWave: number; + ParticipantInfo: IParticipantInfo; + DifficultyTier: number; +} + +interface IParticipantInfo { + AccountId: string; + Name: string; + ChosenRewardOwner: string; + MissionHash: string; + VoidProjection: string; + Reward: string; + QualifiesForReward: boolean; + HaveRewardResponse: boolean; + RewardsMultiplier: number; + RewardProjection: string; + HardModeReward: ITypeCount; +} diff --git a/src/routes/api.ts b/src/routes/api.ts index 4194f44c..c72787ae 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -26,6 +26,7 @@ import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsers import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController"; import { getShipController } from "@/src/controllers/api/getShipController"; import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController"; +import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController"; import { gildWeaponController } from "@/src/controllers/api/gildWeaponController"; import { guildTechController } from "../controllers/api/guildTechController"; import { hostSessionController } from "@/src/controllers/api/hostSessionController"; @@ -120,6 +121,7 @@ apiRouter.post("/focus.php", focusController); apiRouter.post("/fusionTreasures.php", fusionTreasuresController); apiRouter.post("/genericUpdate.php", genericUpdateController); apiRouter.post("/getAlliance.php", getAllianceController); +apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController); apiRouter.post("/gildWeapon.php", gildWeaponController); apiRouter.post("/guildTech.php", guildTechController); apiRouter.post("/hostSession.php", hostSessionController); diff --git a/src/services/rngService.ts b/src/services/rngService.ts index fad3a4ff..1dca83ec 100644 --- a/src/services/rngService.ts +++ b/src/services/rngService.ts @@ -40,3 +40,22 @@ export const getRandomWeightedReward = ( } return getRandomReward(resultPool); }; + +export const getRandomWeightedReward2 = ( + pool: { type: string; itemCount: number; rarity: TRarity }[], + weights: Record +): IRngResult | undefined => { + const resultPool: IRngResult[] = []; + const rarityCounts: Record = { COMMON: 0, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 }; + for (const entry of pool) { + ++rarityCounts[entry.rarity]; + } + for (const entry of pool) { + resultPool.push({ + type: entry.type, + itemCount: entry.itemCount, + probability: weights[entry.rarity] / rarityCounts[entry.rarity] + }); + } + return getRandomReward(resultPool); +};