XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

SpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 1 Star 0
返回提交历史

XFEstudio/SpaceNinjaServer

feat: opening relics in endless missions (#713)

3903b973
Sainan <sainan@calamity.inc>
提交于

代码差异

3 个文件 +120 -0
Added src/controllers/api/getVoidProjectionRewardsController.ts +99 -0
@@ -0,0 +1,99 @@
1 import { getJSONfromString } from "@/src/helpers/stringHelpers";
2 import { addMiscItems, getInventory } from "@/src/services/inventoryService";
3 import { getAccountIdForRequest } from "@/src/services/loginService";
4 import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
5 import { getRandomWeightedReward2 } from "@/src/services/rngService";
6 import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes";
7 import { logger } from "@/src/utils/logger";
8 import { RequestHandler } from "express";
9 import { ExportRelics, ExportRewards, TRarity } from "warframe-public-export-plus";
10
11 export const getVoidProjectionRewardsController: RequestHandler = async (req, res) => {
12 const accountId = await getAccountIdForRequest(req);
13 const data = getJSONfromString(String(req.body)) as IVoidProjectionRewardRequest;
14 const response: IVoidProjectionRewardResponse = {
15 CurrentWave: data.CurrentWave,
16 ParticipantInfo: data.ParticipantInfo,
17 DifficultyTier: data.DifficultyTier
18 };
19 if (data.ParticipantInfo.QualifiesForReward) {
20 const relic = ExportRelics[data.ParticipantInfo.VoidProjection];
21 const weights = refinementToWeights[relic.quality];
22 logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights);
23 const reward = getRandomWeightedReward2(
24 ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
25 weights
26 )!;
27 logger.debug(`relic rolled`, reward);
28 response.ParticipantInfo.Reward = reward.type;
29
30 // Remove relic
31 const inventory = await getInventory(accountId);
32 addMiscItems(inventory, [
33 {
34 ItemType: data.ParticipantInfo.VoidProjection,
35 ItemCount: -1
36 }
37 ]);
38 await inventory.save();
39
40 // Give reward
41 await handleStoreItemAcquisition(reward.type, accountId, reward.itemCount);
42 }
43 res.json(response);
44 };
45
46 const refinementToWeights = {
47 VPQ_BRONZE: {
48 COMMON: 0.76,
49 UNCOMMON: 0.22,
50 RARE: 0.02,
51 LEGENDARY: 0
52 },
53 VPQ_SILVER: {
54 COMMON: 0.7,
55 UNCOMMON: 0.26,
56 RARE: 0.04,
57 LEGENDARY: 0
58 },
59 VPQ_GOLD: {
60 COMMON: 0.6,
61 UNCOMMON: 0.34,
62 RARE: 0.06,
63 LEGENDARY: 0
64 },
65 VPQ_PLATINUM: {
66 COMMON: 0.5,
67 UNCOMMON: 0.4,
68 RARE: 0.1,
69 LEGENDARY: 0
70 }
71 };
72
73 interface IVoidProjectionRewardRequest {
74 CurrentWave: number;
75 ParticipantInfo: IParticipantInfo;
76 VoidTier: string;
77 DifficultyTier: number;
78 VoidProjectionRemovalHash: string;
79 }
80
81 interface IVoidProjectionRewardResponse {
82 CurrentWave: number;
83 ParticipantInfo: IParticipantInfo;
84 DifficultyTier: number;
85 }
86
87 interface IParticipantInfo {
88 AccountId: string;
89 Name: string;
90 ChosenRewardOwner: string;
91 MissionHash: string;
92 VoidProjection: string;
93 Reward: string;
94 QualifiesForReward: boolean;
95 HaveRewardResponse: boolean;
96 RewardsMultiplier: number;
97 RewardProjection: string;
98 HardModeReward: ITypeCount;
99 }
Modified src/routes/api.ts +2 -0
@@ -26,6 +26,7 @@ import { getIgnoredUsersController } from "@/src/controllers/api/getIgnoredUsers
26 26 import { getNewRewardSeedController } from "@/src/controllers/api/getNewRewardSeedController";
27 27 import { getShipController } from "@/src/controllers/api/getShipController";
28 28 import { getVendorInfoController } from "@/src/controllers/api/getVendorInfoController";
29 import { getVoidProjectionRewardsController } from "@/src/controllers/api/getVoidProjectionRewardsController";
29 30 import { gildWeaponController } from "@/src/controllers/api/gildWeaponController";
30 31 import { guildTechController } from "../controllers/api/guildTechController";
31 32 import { hostSessionController } from "@/src/controllers/api/hostSessionController";
@@ -120,6 +121,7 @@ apiRouter.post("/focus.php", focusController);
120 121 apiRouter.post("/fusionTreasures.php", fusionTreasuresController);
121 122 apiRouter.post("/genericUpdate.php", genericUpdateController);
122 123 apiRouter.post("/getAlliance.php", getAllianceController);
124 apiRouter.post("/getVoidProjectionRewards.php", getVoidProjectionRewardsController);
123 125 apiRouter.post("/gildWeapon.php", gildWeaponController);
124 126 apiRouter.post("/guildTech.php", guildTechController);
125 127 apiRouter.post("/hostSession.php", hostSessionController);
Modified src/services/rngService.ts +19 -0
@@ -40,3 +40,22 @@ export const getRandomWeightedReward = (
40 40 }
41 41 return getRandomReward(resultPool);
42 42 };
43
44 export const getRandomWeightedReward2 = (
45 pool: { type: string; itemCount: number; rarity: TRarity }[],
46 weights: Record<TRarity, number>
47 ): IRngResult | undefined => {
48 const resultPool: IRngResult[] = [];
49 const rarityCounts: Record<TRarity, number> = { COMMON: 0, UNCOMMON: 0, RARE: 0, LEGENDARY: 0 };
50 for (const entry of pool) {
51 ++rarityCounts[entry.rarity];
52 }
53 for (const entry of pool) {
54 resultPool.push({
55 type: entry.type,
56 itemCount: entry.itemCount,
57 probability: weights[entry.rarity] / rarityCounts[entry.rarity]
58 });
59 }
60 return getRandomReward(resultPool);
61 };