返回提交历史
Modified
src/controllers/api/getVoidProjectionRewardsController.ts
+12
-72
Added
src/helpers/relicHelper.ts
+60
-0
Modified
src/services/missionInventoryUpdateService.ts
+12
-1
Modified
src/types/requestTypes.ts
+19
-0
XFEstudio/XFESpaceNinjaServer
feat: cracking relics in non-endless missions (#1010)
Closes #415 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1010 Co-authored-by: Sainan <sainan@calamity.inc> Co-committed-by: Sainan <sainan@calamity.inc>
39f0f7de
代码差异
4 个文件
+103
-73
@@ -1,77 +1,31 @@
1
1
import { getJSONfromString } from "@/src/helpers/stringHelpers";
2
import { addMiscItems, getInventory } from "@/src/services/inventoryService";
2
import { crackRelic } from "@/src/helpers/relicHelper";
3
import { getInventory } from "@/src/services/inventoryService";
3
4
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";
5
import { IVoidTearParticipantInfo } from "@/src/types/requestTypes";
8
6
import { RequestHandler } from "express";
9
import { ExportRelics, ExportRewards, TRarity } from "warframe-public-export-plus";
10
7
11
8
export const getVoidProjectionRewardsController: RequestHandler = async (req, res) => {
12
9
const accountId = await getAccountIdForRequest(req);
13
10
const data = getJSONfromString<IVoidProjectionRewardRequest>(String(req.body));
11
12
if (data.ParticipantInfo.QualifiesForReward && !data.ParticipantInfo.HaveRewardResponse) {
13
const inventory = await getInventory(accountId);
14
await crackRelic(inventory, data.ParticipantInfo);
15
await inventory.save();
16
}
17
14
18
const response: IVoidProjectionRewardResponse = {
15
19
CurrentWave: data.CurrentWave,
16
20
ParticipantInfo: data.ParticipantInfo,
17
21
DifficultyTier: data.DifficultyTier
18
22
};
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
const inventory = await getInventory(accountId);
31
// Remove relic
32
addMiscItems(inventory, [
33
{
34
ItemType: data.ParticipantInfo.VoidProjection,
35
ItemCount: -1
36
}
37
]);
38
// Give reward
39
await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount);
40
await inventory.save();
41
}
42
23
res.json(response);
43
24
};
44
25
45
const refinementToWeights = {
46
VPQ_BRONZE: {
47
COMMON: 0.76,
48
UNCOMMON: 0.22,
49
RARE: 0.02,
50
LEGENDARY: 0
51
},
52
VPQ_SILVER: {
53
COMMON: 0.7,
54
UNCOMMON: 0.26,
55
RARE: 0.04,
56
LEGENDARY: 0
57
},
58
VPQ_GOLD: {
59
COMMON: 0.6,
60
UNCOMMON: 0.34,
61
RARE: 0.06,
62
LEGENDARY: 0
63
},
64
VPQ_PLATINUM: {
65
COMMON: 0.5,
66
UNCOMMON: 0.4,
67
RARE: 0.1,
68
LEGENDARY: 0
69
}
70
};
71
72
26
interface IVoidProjectionRewardRequest {
73
27
CurrentWave: number;
74
ParticipantInfo: IParticipantInfo;
28
ParticipantInfo: IVoidTearParticipantInfo;
75
29
VoidTier: string;
76
30
DifficultyTier: number;
77
31
VoidProjectionRemovalHash: string;
@@ -79,20 +33,6 @@ interface IVoidProjectionRewardRequest {
79
33
80
34
interface IVoidProjectionRewardResponse {
81
35
CurrentWave: number;
82
ParticipantInfo: IParticipantInfo;
36
ParticipantInfo: IVoidTearParticipantInfo;
83
37
DifficultyTier: number;
84
38
}
85
86
interface IParticipantInfo {
87
AccountId: string;
88
Name: string;
89
ChosenRewardOwner: string;
90
MissionHash: string;
91
VoidProjection: string;
92
Reward: string;
93
QualifiesForReward: boolean;
94
HaveRewardResponse: boolean;
95
RewardsMultiplier: number;
96
RewardProjection: string;
97
HardModeReward: ITypeCount;
98
}
@@ -0,0 +1,60 @@
1
import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel";
2
import { IVoidTearParticipantInfo } from "@/src/types/requestTypes";
3
import { ExportRelics, ExportRewards, TRarity } from "warframe-public-export-plus";
4
import { getRandomWeightedReward2 } from "@/src/services/rngService";
5
import { logger } from "@/src/utils/logger";
6
import { addMiscItems } from "@/src/services/inventoryService";
7
import { handleStoreItemAcquisition } from "@/src/services/purchaseService";
8
9
export const crackRelic = async (
10
inventory: TInventoryDatabaseDocument,
11
participant: IVoidTearParticipantInfo
12
): Promise<void> => {
13
const relic = ExportRelics[participant.VoidProjection];
14
const weights = refinementToWeights[relic.quality];
15
logger.debug(`opening a relic of quality ${relic.quality}; rarity weights are`, weights);
16
const reward = getRandomWeightedReward2(
17
ExportRewards[relic.rewardManifest][0] as { type: string; itemCount: number; rarity: TRarity }[], // rarity is nullable in PE+ typings, but always present for relics
18
weights
19
)!;
20
logger.debug(`relic rolled`, reward);
21
participant.Reward = reward.type;
22
23
// Remove relic
24
addMiscItems(inventory, [
25
{
26
ItemType: participant.VoidProjection,
27
ItemCount: -1
28
}
29
]);
30
31
// Give reward
32
await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount);
33
};
34
35
const refinementToWeights = {
36
VPQ_BRONZE: {
37
COMMON: 0.76,
38
UNCOMMON: 0.22,
39
RARE: 0.02,
40
LEGENDARY: 0
41
},
42
VPQ_SILVER: {
43
COMMON: 0.7,
44
UNCOMMON: 0.26,
45
RARE: 0.04,
46
LEGENDARY: 0
47
},
48
VPQ_GOLD: {
49
COMMON: 0.6,
50
UNCOMMON: 0.34,
51
RARE: 0.06,
52
LEGENDARY: 0
53
},
54
VPQ_PLATINUM: {
55
COMMON: 0.5,
56
UNCOMMON: 0.4,
57
RARE: 0.1,
58
LEGENDARY: 0
59
}
60
};
@@ -33,6 +33,7 @@ import { getEntriesUnsafe } from "@/src/utils/ts-utils";
33
33
import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes";
34
34
import { handleStoreItemAcquisition } from "./purchaseService";
35
35
import { IMissionReward } from "../types/missionTypes";
36
import { crackRelic } from "@/src/helpers/relicHelper";
36
37
37
38
const getRotations = (rotationCount: number): number[] => {
38
39
if (rotationCount === 0) return [0];
@@ -221,7 +222,8 @@ export const addMissionRewards = async (
221
222
RewardInfo: rewardInfo,
222
223
LevelKeyName: levelKeyName,
223
224
Missions: missions,
224
RegularCredits: creditDrops
225
RegularCredits: creditDrops,
226
VoidTearParticipantsCurrWave: voidTearWave
225
227
}: IMissionInventoryUpdateRequest
226
228
) => {
227
229
if (!rewardInfo) {
@@ -291,6 +293,15 @@ export const addMissionRewards = async (
291
293
rngRewardCredits: inventoryChanges.RegularCredits ?? 0
292
294
});
293
295
296
if (
297
voidTearWave &&
298
voidTearWave.Participants[0].QualifiesForReward &&
299
!voidTearWave.Participants[0].HaveRewardResponse
300
) {
301
await crackRelic(inventory, voidTearWave.Participants[0]);
302
MissionRewards.push({ StoreItem: voidTearWave.Participants[0].Reward, ItemCount: 1 });
303
}
304
294
305
return { inventoryChanges, MissionRewards, credits };
295
306
};
296
307
@@ -87,6 +87,11 @@ export type IMissionInventoryUpdateRequest = {
87
87
PlayerSkillGains: IPlayerSkills;
88
88
CustomMarkers?: ICustomMarkers[];
89
89
LoreFragmentScans?: ILoreFragmentScan[];
90
VoidTearParticipantsCurrWave?: {
91
Wave: number;
92
IsFinalWave: boolean;
93
Participants: IVoidTearParticipantInfo[];
94
};
90
95
} & {
91
96
[K in TEquipmentKey]?: IEquipmentClient[];
92
97
};
@@ -136,3 +141,17 @@ export interface IUnlockShipFeatureRequest {
136
141
KeyChain: string;
137
142
ChainStage: number;
138
143
}
144
145
export interface IVoidTearParticipantInfo {
146
AccountId: string;
147
Name: string;
148
ChosenRewardOwner: string;
149
MissionHash: string;
150
VoidProjection: string;
151
Reward: string;
152
QualifiesForReward: boolean;
153
HaveRewardResponse: boolean;
154
RewardsMultiplier: number;
155
RewardProjection: string;
156
HardModeReward: ITypeCount;
157
}