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

MissionInventoryUpdate(not completed), Mod upgrade, Booster purchase (#49)

Co-authored-by: OrdisPrime <134585663+OrdisPrime@users.noreply.github.com> Co-authored-by: Ângelo Tadeucci <angelo_tadeucci@hotmail.com.br>

01cfecd9
Nicat <52883148+holmityd@users.noreply.github.com>
提交于

代码差异

14 个文件 +593 -51
Modified config.json +1 -1
@@ -8,4 +8,4 @@
8 8 "skipTutorial": true,
9 9 "testMission": true,
10 10 "testQuestKey": true
11 }
11 }
Added src/controllers/api/artifactsController.ts +23 -0
@@ -0,0 +1,23 @@
1 import { upgradeMod } from "@/src/services/inventoryService";
2 import { RequestHandler } from "express";
3
4 // eslint-disable-next-line @typescript-eslint/no-misused-promises
5 const artifactsController: RequestHandler = async (req, res) => {
6 const [data] = String(req.body).split("\n");
7 const id = req.query.accountId as string;
8
9 // TODO - salt check
10
11 try {
12 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
13 const parsedData = JSON.parse(data);
14
15 // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
16 const upgradeModId = await upgradeMod(parsedData, id);
17 res.send(upgradeModId);
18 } catch (err) {
19 console.error("Error parsing JSON data:", err);
20 }
21 };
22
23 export { artifactsController };
Modified src/controllers/api/missionInventoryUpdateController.ts +36 -9
@@ -1,7 +1,9 @@
1 1 import { RequestHandler } from "express";
2 2 import { missionInventoryUpdate } from "@/src/services/inventoryService";
3 import { combineRewardAndLootInventory, getRewards } from "@/src/services/missionInventoryUpdateService ";
3 4 import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType";
4 5 /*
6 **** INPUT ****
5 7 - [ ] crossPlaySetting
6 8 - [ ] rewardsMultiplier
7 9 - [ ] ActiveBoosters
@@ -27,7 +29,7 @@ import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType"
27 29 - [ ] SeasonChallengeHistory
28 30 - [ ] PS (Passive anti-cheat data which includes your username, module list, process list, and system name.)
29 31 - [ ] ActiveDojoColorResearch
30 - [ ] RewardInfo
32 - [x] RewardInfo
31 33 - [ ] ReceivedCeremonyMsg
32 34 - [ ] LastCeremonyResetDate
33 35 - [ ] MissionPTS (Used to validate the mission/alive time above.)
@@ -42,20 +44,45 @@ import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType"
42 44
43 45 // eslint-disable-next-line @typescript-eslint/no-misused-promises
44 46 const missionInventoryUpdateController: RequestHandler = async (req, res) => {
45 const id = req.query.accountId as string;
46
47 47 const [data] = String(req.body).split("\n");
48 const id = req.query.accountId as string;
48 49
49 50 try {
50 const parsedData = JSON.parse(data) as IMissionInventoryUpdate;
51 if (typeof parsedData !== "object") throw new Error("Invalid data format");
52 await missionInventoryUpdate(parsedData, id);
51 const lootInventory = JSON.parse(data) as IMissionInventoryUpdate;
52 if (typeof lootInventory !== "object" || lootInventory === null) {
53 throw new Error("Invalid data format");
54 }
55
56 const { InventoryChanges, MissionRewards } = getRewards(lootInventory.RewardInfo);
57
58 const { combinedInventoryChanges, TotalCredits, CreditsBonus, MissionCredits, FusionPoints } =
59 combineRewardAndLootInventory(InventoryChanges, lootInventory);
60
61 // eslint-disable-next-line @typescript-eslint/no-unused-vars
62 const InventoryJson = JSON.stringify(await missionInventoryUpdate(combinedInventoryChanges, id));
63 res.json({
64 // InventoryJson, // this part will reset game data and missions will be locked
65 MissionRewards,
66 InventoryChanges,
67 TotalCredits,
68 CreditsBonus,
69 MissionCredits,
70 ...(FusionPoints !== undefined && { FusionPoints })
71 });
53 72 } catch (err) {
54 73 console.error("Error parsing JSON data:", err);
55 74 }
56
57 // TODO - Return the updated inventory the way the game does it.
58 res.json({});
59 75 };
60 76
77 /*
78 **** OUTPUT ****
79 - [x] InventoryJson
80 - [x] MissionRewards
81 - [x] TotalCredits
82 - [x] CreditsBonus
83 - [x] MissionCredits
84 - [x] InventoryChanges
85 - [x] FusionPoints
86 */
87
61 88 export { missionInventoryUpdateController };
Modified src/models/inventoryModel.ts +32 -4
@@ -7,7 +7,7 @@ import {
7 7 IBooster
8 8 } from "../types/inventoryTypes/inventoryTypes";
9 9 import { IOid } from "../types/commonTypes";
10 import { ISuitDatabase, ISuitDocument } from "@/src/types/inventoryTypes/SuitTypes";
10 import { ISuitDatabase } from "@/src/types/inventoryTypes/SuitTypes";
11 11 import { IWeaponDatabase } from "@/src/types/inventoryTypes/weaponTypes";
12 12
13 13 const abilityOverrideSchema = new Schema({
@@ -77,6 +77,34 @@ const BoosterSchema = new Schema({
77 77 ItemType: String
78 78 });
79 79
80 const RawUpgrades = new Schema({
81 ItemType: String,
82 ItemCount: Number
83 });
84
85 RawUpgrades.set("toJSON", {
86 transform(_document, returnedObject) {
87 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
88 returnedObject.LastAdded = { $oid: returnedObject._id.toString() } satisfies IOid;
89 delete returnedObject._id;
90 delete returnedObject.__v;
91 }
92 });
93
94 const Upgrade = new Schema({
95 UpgradeFingerprint: String,
96 ItemType: String
97 });
98
99 Upgrade.set("toJSON", {
100 transform(_document, returnedObject) {
101 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
102 returnedObject.ItemId = { $oid: returnedObject._id.toString() } satisfies IOid;
103 delete returnedObject._id;
104 delete returnedObject.__v;
105 }
106 });
107
80 108 WeaponSchema.set("toJSON", {
81 109 transform(_document, returnedObject) {
82 110 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-call
@@ -187,7 +215,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>({
187 215 MiscItems: [Schema.Types.Mixed],
188 216 ChallengesFixVersion: Number,
189 217 ChallengeProgress: [Schema.Types.Mixed],
190 RawUpgrades: [Schema.Types.Mixed],
218 RawUpgrades: [RawUpgrades],
191 219 ReceivedStartingGear: Boolean,
192 220 Suits: [suitSchema],
193 221 LongGuns: [WeaponSchema],
@@ -209,7 +237,7 @@ const inventorySchema = new Schema<IInventoryDatabase, InventoryDocumentProps>({
209 237 PendingRecipes: [Schema.Types.Mixed],
210 238 TrainingDate: Schema.Types.Mixed,
211 239 PlayerLevel: Number,
212 Upgrades: [Schema.Types.Mixed],
240 Upgrades: [Upgrade],
213 241 EquippedGear: [String],
214 242 DeathMarks: [String],
215 243 FusionTreasures: [Schema.Types.Mixed],
@@ -350,7 +378,7 @@ type InventoryDocumentProps = {
350 378 Boosters: Types.DocumentArray<IBooster>;
351 379 };
352 380
353 type InventoryModelType = Model<IInventoryDatabase, {}, InventoryDocumentProps>;
381 type InventoryModelType = Model<IInventoryDatabase, object, InventoryDocumentProps>;
354 382
355 383 const Inventory = model<IInventoryDatabase, InventoryModelType>("Inventory", inventorySchema);
356 384
Modified src/routes/api.ts +2 -0
@@ -29,6 +29,7 @@ import { updateSessionGetController, updateSessionPostController } from "@/src/c
29 29 import { viewController } from "@/src/controllers/api/viewController";
30 30 import { joinSessionController } from "@/src/controllers/api/joinSessionController";
31 31 import { saveLoadoutController } from "@/src/controllers/api/saveLoadout";
32 import { artifactsController } from "../controllers/api/artifactsController";
32 33
33 34 import express from "express";
34 35
@@ -58,6 +59,7 @@ apiRouter.get("/deleteSession.php", deleteSessionController);
58 59 apiRouter.get("/logout.php", logoutController);
59 60
60 61 // post
62 apiRouter.post("/artifacts.php", artifactsController);
61 63 apiRouter.post("/findSessions.php", findSessionsController);
62 64 // eslint-disable-next-line @typescript-eslint/no-misused-promises
63 65 apiRouter.post("/purchase.php", purchaseController);
Modified src/services/inventoryService.ts +131 -26
@@ -7,15 +7,14 @@ import { SlotType } from "@/src/types/purchaseTypes";
7 7 import { IWeaponResponse } from "@/src/types/inventoryTypes/weaponTypes";
8 8 import {
9 9 IChallengeProgress,
10 IConsumable,
11 ICrewShipSalvagedWeaponSkin,
10 12 IFlavourItem,
11 IInventoryDatabaseDocument
13 IInventoryDatabaseDocument,
14 IMiscItem,
15 IRawUpgrade
12 16 } from "@/src/types/inventoryTypes/inventoryTypes";
13 import {
14 IMissionInventoryUpdate,
15 IMissionInventoryUpdateCard,
16 IMissionInventoryUpdateGear,
17 IMissionInventoryUpdateItem
18 } from "../types/missionInventoryUpdateType";
17 import { IMissionInventoryUpdate, IMissionInventoryUpdateGear } from "../types/missionInventoryUpdateType";
19 18 import { IGenericUpdate } from "../types/genericUpdate";
20 19
21 20 const createInventory = async (accountOwnerId: Types.ObjectId) => {
@@ -146,7 +145,7 @@ const addGearExpByCategory = (
146 145 const category = inventory[categoryName];
147 146
148 147 gearArray?.forEach(({ ItemId, XP }) => {
149 const itemIndex = category.findIndex(i => i._id?.equals(ItemId.$oid));
148 const itemIndex = category.findIndex(item => item._id?.equals(ItemId.$oid));
150 149 const item = category[itemIndex];
151 150
152 151 if (itemIndex !== -1 && item.XP != undefined) {
@@ -156,21 +155,61 @@ const addGearExpByCategory = (
156 155 });
157 156 };
158 157
159 const addItemsByCategory = (
160 inventory: IInventoryDatabaseDocument,
161 itemsArray: (IMissionInventoryUpdateItem | IMissionInventoryUpdateCard)[] | undefined,
162 categoryName: "RawUpgrades" | "MiscItems"
163 ) => {
164 const category = inventory[categoryName];
158 const addMiscItems = (inventory: IInventoryDatabaseDocument, itemsArray: IMiscItem[] | undefined) => {
159 const { MiscItems } = inventory;
160
161 itemsArray?.forEach(({ ItemCount, ItemType }) => {
162 const itemIndex = MiscItems.findIndex(miscItem => miscItem.ItemType === ItemType);
163
164 if (itemIndex !== -1) {
165 MiscItems[itemIndex].ItemCount += ItemCount;
166 inventory.markModified(`MiscItems.${itemIndex}.ItemCount`);
167 } else {
168 MiscItems.push({ ItemCount, ItemType });
169 }
170 });
171 };
172
173 const addConsumables = (inventory: IInventoryDatabaseDocument, itemsArray: IConsumable[] | undefined) => {
174 const { Consumables } = inventory;
165 175
166 176 itemsArray?.forEach(({ ItemCount, ItemType }) => {
167 const itemIndex = category.findIndex(i => i.ItemType === ItemType);
177 const itemIndex = Consumables.findIndex(i => i.ItemType === ItemType);
178
179 if (itemIndex !== -1) {
180 Consumables[itemIndex].ItemCount += ItemCount;
181 inventory.markModified(`Consumables.${itemIndex}.ItemCount`);
182 } else {
183 Consumables.push({ ItemCount, ItemType });
184 }
185 });
186 };
187
188 const addRecipes = (inventory: IInventoryDatabaseDocument, itemsArray: IConsumable[] | undefined) => {
189 const { Recipes } = inventory;
190
191 itemsArray?.forEach(({ ItemCount, ItemType }) => {
192 const itemIndex = Recipes.findIndex(i => i.ItemType === ItemType);
193
194 if (itemIndex !== -1) {
195 Recipes[itemIndex].ItemCount += ItemCount;
196 inventory.markModified(`Recipes.${itemIndex}.ItemCount`);
197 } else {
198 Recipes.push({ ItemCount, ItemType });
199 }
200 });
201 };
202
203 const addMods = (inventory: IInventoryDatabaseDocument, itemsArray: IRawUpgrade[] | undefined) => {
204 const { RawUpgrades } = inventory;
205 itemsArray?.forEach(({ ItemType, ItemCount }) => {
206 const itemIndex = RawUpgrades.findIndex(i => i.ItemType === ItemType);
168 207
169 208 if (itemIndex !== -1) {
170 category[itemIndex].ItemCount += ItemCount;
171 inventory.markModified(`${categoryName}.${itemIndex}.ItemCount`);
209 RawUpgrades[itemIndex].ItemCount += ItemCount;
210 inventory.markModified(`RawUpgrades.${itemIndex}.ItemCount`);
172 211 } else {
173 category.push({ ItemCount, ItemType });
212 RawUpgrades.push({ ItemCount, ItemType });
174 213 }
175 214 });
176 215 };
@@ -193,20 +232,28 @@ const addChallenges = (inventory: IInventoryDatabaseDocument, itemsArray: IChall
193 232 const gearKeys = ["Suits", "Pistols", "LongGuns", "Melee"] as const;
194 233 type GearKeysType = (typeof gearKeys)[number];
195 234
196 export const missionInventoryUpdate = async (data: IMissionInventoryUpdate, accountId: string): Promise<void> => {
197 const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress } = data;
235 export const missionInventoryUpdate = async (data: IMissionInventoryUpdate, accountId: string) => {
236 const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress, FusionPoints, Consumables, Recipes } = data;
198 237 const inventory = await getInventory(accountId);
199 238
239 // credits
240 inventory.RegularCredits += RegularCredits || 0;
241
242 // endo
243 inventory.FusionPoints += FusionPoints || 0;
244
200 245 // Gear XP
201 246 gearKeys.forEach((key: GearKeysType) => addGearExpByCategory(inventory, data[key], key));
202 247
203 // Other
204 // TODO: Ensure mods have a valid fusion level and items have a valid quantity, preferably inside of the functions themselves.
205 addItemsByCategory(inventory, RawUpgrades, "RawUpgrades");
206 addItemsByCategory(inventory, MiscItems, "MiscItems");
248 // other
249 addMods(inventory, RawUpgrades);
250 addMiscItems(inventory, MiscItems);
251 addConsumables(inventory, Consumables);
252 addRecipes(inventory, Recipes);
207 253 addChallenges(inventory, ChallengeProgress);
208 254
209 await inventory.save();
255 const changedInventory = await inventory.save();
256 return changedInventory.toJSON();
210 257 };
211 258
212 259 export const addBooster = async (ItemType: string, time: number, accountId: string): Promise<void> => {
@@ -215,7 +262,7 @@ export const addBooster = async (ItemType: string, time: number, accountId: stri
215 262 const inventory = await getInventory(accountId);
216 263 const { Boosters } = inventory;
217 264
218 const itemIndex = Boosters.findIndex(i => i.ItemType === ItemType);
265 const itemIndex = Boosters.findIndex(booster => booster.ItemType === ItemType);
219 266
220 267 if (itemIndex !== -1) {
221 268 const existingBooster = Boosters[itemIndex];
@@ -228,4 +275,62 @@ export const addBooster = async (ItemType: string, time: number, accountId: stri
228 275 await inventory.save();
229 276 };
230 277
278 export const upgradeMod = async (
279 {
280 Upgrade,
281 LevelDiff,
282 Cost,
283 FusionPointCost
284 }: { Upgrade: ICrewShipSalvagedWeaponSkin; LevelDiff: number; Cost: number; FusionPointCost: number },
285 accountId: string
286 ): Promise<string | undefined> => {
287 try {
288 const inventory = await getInventory(accountId);
289 const { Upgrades, RawUpgrades } = inventory;
290 const { ItemType, UpgradeFingerprint, ItemId } = Upgrade;
291
292 const safeUpgradeFingerprint = UpgradeFingerprint || '{"lvl":0}';
293 // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
294 const parsedUpgradeFingerprint = JSON.parse(safeUpgradeFingerprint);
295 parsedUpgradeFingerprint.lvl += LevelDiff;
296 const stringifiedUpgradeFingerprint = JSON.stringify(parsedUpgradeFingerprint);
297
298 let itemIndex = Upgrades.findIndex(upgrade => upgrade._id?.equals(ItemId!.$oid));
299
300 if (itemIndex !== -1) {
301 Upgrades[itemIndex].UpgradeFingerprint = stringifiedUpgradeFingerprint;
302 inventory.markModified(`Upgrades.${itemIndex}.UpgradeFingerprint`);
303 } else {
304 itemIndex =
305 Upgrades.push({
306 UpgradeFingerprint: stringifiedUpgradeFingerprint,
307 ItemType
308 }) - 1;
309
310 const rawItemIndex = RawUpgrades.findIndex(rawUpgrade => rawUpgrade.ItemType === ItemType);
311 RawUpgrades[rawItemIndex].ItemCount--;
312 if (RawUpgrades[rawItemIndex].ItemCount > 0) {
313 inventory.markModified(`RawUpgrades.${rawItemIndex}.UpgradeFingerprint`);
314 } else {
315 RawUpgrades.splice(rawItemIndex, 1);
316 }
317 }
318
319 inventory.RegularCredits -= Cost;
320 inventory.FusionPoints -= FusionPointCost;
321
322 const changedInventory = await inventory.save();
323 const itemId = changedInventory.toJSON().Upgrades[itemIndex]?.ItemId?.$oid;
324
325 if (!itemId) {
326 throw new Error("Item Id not found in upgradeMod");
327 }
328
329 return itemId;
330 } catch (error) {
331 console.error("Error in upgradeMod:", error);
332 throw error;
333 }
334 };
335
231 336 export { createInventory, addPowerSuit };
Added src/services/missionInventoryUpdateService .ts +249 -0
@@ -0,0 +1,249 @@
1 import {
2 IMissionInventoryUpdate,
3 IMissionInventoryUpdateRewardInfo,
4 IMissionRewardResponse,
5 IReward,
6 IInventoryFieldType,
7 inventoryFields
8 } from "@/src/types/missionInventoryUpdateType";
9
10 import missionsDropTable from "@/static/json/missions-drop-table.json";
11 import { modNames, relicNames, miscNames, resourceNames, gearNames, blueprintNames } from "@/static/data/items";
12
13 // need reverse engineer rewardSeed, otherwise ingame displayed rotation reward will be different than added to db or displayed on mission end
14 const getRewards = (
15 rewardInfo: IMissionInventoryUpdateRewardInfo | undefined
16 ): { InventoryChanges: IMissionInventoryUpdate; MissionRewards: IMissionRewardResponse[] } => {
17 if (!rewardInfo) {
18 return { InventoryChanges: {}, MissionRewards: [] };
19 }
20
21 const rewards = (missionsDropTable as { [key: string]: IReward[] })[rewardInfo.node];
22 if (!rewards) {
23 return { InventoryChanges: {}, MissionRewards: [] };
24 }
25
26 const rotationCount = rewardInfo.rewardQualifications?.length || 0;
27 const rotations = getRotations(rotationCount);
28 const drops: IReward[] = [];
29 for (const rotation of rotations) {
30 const rotationRewards = rewards.filter(reward => reward.rotation === rotation);
31
32 // Separate guaranteed and chance drops
33 const guaranteedDrops: IReward[] = [];
34 const chanceDrops: IReward[] = [];
35 for (const reward of rotationRewards) {
36 if (reward.chance === 100) {
37 guaranteedDrops.push(reward);
38 } else {
39 chanceDrops.push(reward);
40 }
41 }
42
43 const randomDrop = getRandomRewardByChance(chanceDrops);
44 if (randomDrop) {
45 guaranteedDrops.push(randomDrop);
46 }
47
48 drops.push(...guaranteedDrops);
49 }
50
51 // const testDrops = [
52 // { chance: 7.69, name: "Lith W3 Relic", rotation: "B" },
53 // { chance: 7.69, name: "Lith W3 Relic", rotation: "B" },
54 // { chance: 10.82, name: "2X Orokin Cell", rotation: "C" },
55 // { chance: 10.82, name: "Arrow Mutation", rotation: "C" },
56 // { chance: 10.82, name: "200 Endo", rotation: "C" },
57 // { chance: 10.82, name: "2,000,000 Credits Cache", rotation: "C" },
58 // { chance: 7.69, name: "Health Restore (Large)", rotation: "C" },
59 // { chance: 7.69, name: "Vapor Specter Blueprint", rotation: "C" }
60 // ];
61 // console.log("Mission rewards:", testDrops);
62 // return formatRewardsToInventoryType(testDrops);
63
64 console.log("Mission rewards:", drops);
65 return formatRewardsToInventoryType(drops);
66 };
67
68 const combineRewardAndLootInventory = (
69 rewardInventory: IMissionInventoryUpdate,
70 lootInventory: IMissionInventoryUpdate
71 ) => {
72 const missionCredits = lootInventory.RegularCredits || 0;
73 const creditsBonus = rewardInventory.RegularCredits || 0;
74 const totalCredits = missionCredits + creditsBonus;
75 const FusionPoints = (lootInventory.FusionPoints || 0) + (rewardInventory.FusionPoints || 0) || undefined;
76
77 lootInventory.RegularCredits = totalCredits;
78 if (FusionPoints) {
79 lootInventory.FusionPoints = FusionPoints;
80 }
81 inventoryFields.forEach((field: IInventoryFieldType) => {
82 if (rewardInventory[field] && !lootInventory[field]) {
83 lootInventory[field] = [];
84 }
85 rewardInventory[field]?.forEach(item => lootInventory[field]!.push(item));
86 });
87
88 return {
89 combinedInventoryChanges: lootInventory,
90 TotalCredits: [totalCredits, totalCredits],
91 CreditsBonus: [creditsBonus, creditsBonus],
92 MissionCredits: [missionCredits, missionCredits],
93 ...(FusionPoints !== undefined && { FusionPoints })
94 };
95 };
96
97 const getRotations = (rotationCount: number): (string | undefined)[] => {
98 if (rotationCount === 0) return [undefined];
99
100 const rotationPattern = ["A", "A", "B", "C"];
101 let rotationIndex = 0;
102 const rotatedValues = [];
103
104 for (let i = 1; i <= rotationCount; i++) {
105 rotatedValues.push(rotationPattern[rotationIndex]);
106 rotationIndex = (rotationIndex + 1) % 3;
107 }
108
109 return rotatedValues;
110 };
111
112 const getRandomRewardByChance = (data: IReward[] | undefined): IReward | undefined => {
113 if (!data || data.length == 0) return;
114
115 const totalChance = data.reduce((sum, item) => sum + item.chance, 0);
116 const randomValue = Math.random() * totalChance;
117
118 let cumulativeChance = 0;
119 for (const item of data) {
120 cumulativeChance += item.chance;
121 if (randomValue <= cumulativeChance) {
122 return item;
123 }
124 }
125
126 return;
127 };
128
129 const formatRewardsToInventoryType = (
130 rewards: IReward[]
131 ): { InventoryChanges: IMissionInventoryUpdate; MissionRewards: IMissionRewardResponse[] } => {
132 const InventoryChanges: IMissionInventoryUpdate = {};
133 const MissionRewards: IMissionRewardResponse[] = [];
134 for (const reward of rewards) {
135 if (itemCheck(InventoryChanges, MissionRewards, reward.name)) {
136 continue;
137 }
138
139 if (reward.name.includes(" Endo")) {
140 if (!InventoryChanges.FusionPoints) {
141 InventoryChanges.FusionPoints = 0;
142 }
143 InventoryChanges.FusionPoints += getCountFromName(reward.name);
144 } else if (reward.name.includes(" Credits Cache") || reward.name.includes("Return: ")) {
145 if (!InventoryChanges.RegularCredits) {
146 InventoryChanges.RegularCredits = 0;
147 }
148 InventoryChanges.RegularCredits += getCountFromName(reward.name);
149 }
150 }
151 return { InventoryChanges, MissionRewards };
152 };
153
154 const itemCheck = (
155 InventoryChanges: IMissionInventoryUpdate,
156 MissionRewards: IMissionRewardResponse[],
157 name: string
158 ) => {
159 const rewardCheck = {
160 RawUpgrades: modNames[name],
161 Consumables: gearNames[name],
162 MiscItems:
163 miscNames[name] ||
164 miscNames[name.replace(/\d+X\s*/, "")] ||
165 resourceNames[name] ||
166 resourceNames[name.replace(/\d+X\s*/, "")] ||
167 relicNames[name.replace("Relic", "Intact")] ||
168 relicNames[name.replace("Relic (Radiant)", "Radiant")],
169 Recipes: blueprintNames[name]
170 };
171 for (const key of Object.keys(rewardCheck) as IInventoryFieldType[]) {
172 if (rewardCheck[key]) {
173 addRewardResponse(InventoryChanges, MissionRewards, name, rewardCheck[key]!, key);
174 return true;
175 }
176 }
177 return false;
178 };
179
180 const getCountFromName = (name: string) => {
181 const regex = /(^(?:\d{1,3}(?:,\d{3})*(?:\.\d+)?)(\s|X))|(\s(?:\d{1,3}(?:,\d{3})*(?:\.\d+)?)$)/;
182 const countMatches = name.match(regex);
183 return countMatches ? parseInt(countMatches[0].replace(/,/g, ""), 10) : 1;
184 };
185
186 const addRewardResponse = (
187 InventoryChanges: IMissionInventoryUpdate,
188 MissionRewards: IMissionRewardResponse[],
189 ItemName: string,
190 ItemType: string,
191 InventoryCategory: IInventoryFieldType
192 ) => {
193 if (!ItemType) return;
194
195 if (!InventoryChanges[InventoryCategory]) {
196 InventoryChanges[InventoryCategory] = [];
197 }
198
199 const ItemCount = getCountFromName(ItemName);
200 const TweetText = `${ItemName}`;
201
202 const existReward = InventoryChanges[InventoryCategory]!.find(item => item.ItemType === ItemType);
203 if (existReward) {
204 existReward.ItemCount += ItemCount;
205 const missionReward = MissionRewards.find(missionReward => missionReward.TypeName === ItemType);
206 if (missionReward) {
207 missionReward.ItemCount += ItemCount;
208 }
209 } else {
210 InventoryChanges[InventoryCategory]!.push({ ItemType, ItemCount });
211 MissionRewards.push({
212 ItemCount,
213 TweetText,
214 ProductCategory: InventoryCategory,
215 StoreItem: ItemType.replace("/Lotus/", "/Lotus/StoreItems/"),
216 TypeName: ItemType
217 });
218 }
219 };
220
221 // eslint-disable-next-line @typescript-eslint/no-unused-vars
222 const _missionRewardsCheckAllNamings = () => {
223 let tempRewards: IReward[] = [];
224 Object.values(missionsDropTable as { [key: string]: IReward[] }).forEach(rewards => {
225 rewards.forEach(reward => {
226 tempRewards.push(reward);
227 });
228 });
229 tempRewards = tempRewards
230 .filter(reward => !modNames[reward.name])
231 .filter(reward => !miscNames[reward.name])
232 .filter(reward => !miscNames[reward.name.replace(/\d+X\s*/, "")])
233 .filter(reward => !resourceNames[reward.name])
234 .filter(reward => !resourceNames[reward.name.replace(/\d+X\s*/, "")])
235 .filter(reward => !gearNames[reward.name])
236 .filter(reward => {
237 return (
238 !relicNames[reward.name.replace("Relic", "Intact")] &&
239 !relicNames[reward.name.replace("Relic (Radiant)", "Radiant")]
240 );
241 })
242 .filter(reward => !blueprintNames[reward.name])
243 .filter(reward => !reward.name.includes(" Endo"))
244 .filter(reward => !reward.name.includes(" Credits Cache") && !reward.name.includes("Return: "));
245 console.log(tempRewards);
246 };
247 // _missionRewardsCheckAllNamings();
248
249 export { getRewards, combineRewardAndLootInventory };
Modified src/types/genericUpdate.ts +1 -1
@@ -1,4 +1,4 @@
1 1 export interface IGenericUpdate {
2 2 NodeIntrosCompleted: string | string[];
3 3 // AffiliationMods: any[];
4 }
4 }
Modified src/types/inventoryTypes/inventoryTypes.ts +5 -8
@@ -1,3 +1,4 @@
1 /* eslint-disable @typescript-eslint/no-explicit-any */
1 2 import { Document, Types } from "mongoose";
2 3 import { IOid } from "../commonTypes";
3 4 import { IAbilityOverride, IColor, FocusSchool, IPolarity } from "@/src/types/inventoryTypes/commonInventoryTypes";
@@ -33,7 +34,7 @@ export interface IInventoryResponse {
33 34 DailyFocus: number;
34 35 GiftsRemaining: number;
35 36 HandlerPoints: number;
36 MiscItems: IConsumable[];
37 MiscItems: IMiscItem[];
37 38 ChallengesFixVersion: number;
38 39 ChallengeProgress: IChallengeProgress[];
39 40 RawUpgrades: IRawUpgrade[];
@@ -319,7 +320,8 @@ export interface ICrewShipSalvageBinClass {
319 320 export interface ICrewShipSalvagedWeaponSkin {
320 321 ItemType: string;
321 322 UpgradeFingerprint?: string;
322 ItemId: IOid;
323 ItemId?: IOid;
324 _id?: Types.ObjectId;
323 325 }
324 326
325 327 export interface ICrewShipWeapon {
@@ -375,11 +377,6 @@ export interface IFlavourItem {
375 377 ItemType: string;
376 378 }
377 379
378 export interface IRawUpgrade {
379 ItemCount: number;
380 ItemType: string;
381 }
382
383 380 export interface IMiscItem {
384 381 ItemCount: number;
385 382 ItemType: string;
@@ -933,9 +930,9 @@ export interface IProgress {
933 930 }
934 931
935 932 export interface IRawUpgrade {
933 ItemType: string;
936 934 ItemCount: number;
937 935 LastAdded?: IOid;
938 ItemType: string;
939 936 }
940 937
941 938 export interface IScoop {
Modified src/types/missionInventoryUpdateType.ts +38 -1
@@ -1,6 +1,9 @@
1 /* eslint-disable @typescript-eslint/no-explicit-any */
1 2 import { IOid } from "./commonTypes";
2 3 import { IDate } from "./inventoryTypes/inventoryTypes";
3 4
5 export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const;
6 export type IInventoryFieldType = (typeof inventoryFields)[number];
4 7 export interface IMissionInventoryUpdateGear {
5 8 ItemType: string;
6 9 ItemName: string;
@@ -43,6 +46,21 @@ export interface IMissionInventoryUpdateChallange {
43 46 Completed: any[];
44 47 }
45 48
49 export interface IMissionInventoryUpdateRewardInfo {
50 node: string;
51 rewardTier?: number;
52 nightmareMode?: boolean;
53 useVaultManifest?: boolean;
54 EnemyCachesFound?: number;
55 toxinOk?: boolean;
56 lostTargetWave?: number;
57 defenseTargetCount?: number;
58 EOM_AFK?: number;
59 rewardQualifications?: string;
60 PurgatoryRewardQualifications?: string;
61 rewardSeed?: number;
62 }
63
46 64 export interface IMissionInventoryUpdate {
47 65 rewardsMultiplier?: number;
48 66 ActiveBoosters?: any[];
@@ -50,8 +68,27 @@ export interface IMissionInventoryUpdate {
50 68 Pistols?: IMissionInventoryUpdateGear[];
51 69 Suits?: IMissionInventoryUpdateGear[];
52 70 Melee?: IMissionInventoryUpdateGear[];
53 RawUpgrades?: IMissionInventoryUpdateCard[];
71 RawUpgrades?: IMissionInventoryUpdateItem[];
54 72 MiscItems?: IMissionInventoryUpdateItem[];
73 Consumables?: IMissionInventoryUpdateItem[];
74 Recipes?: IMissionInventoryUpdateItem[];
55 75 RegularCredits?: number;
56 76 ChallengeProgress?: IMissionInventoryUpdateChallange[];
77 RewardInfo?: IMissionInventoryUpdateRewardInfo;
78 FusionPoints?: number;
79 }
80
81 export interface IMissionRewardResponse {
82 StoreItem?: string;
83 TypeName: string;
84 UpgradeLevel?: number;
85 ItemCount: number;
86 TweetText: string;
87 ProductCategory: string;
88 }
89
90 export interface IReward {
91 name: string;
92 chance: number;
93 rotation?: string;
57 94 }
Modified src/types/session.ts +1 -0
@@ -1,3 +1,4 @@
1 /* eslint-disable @typescript-eslint/no-explicit-any */
1 2 export interface ISession {
2 3 sessionId: string;
3 4 creatorId: string;
Modified static/data/items.ts +43 -1
@@ -1,4 +1,4 @@
1 import Items, { Item, Weapon } from "warframe-items";
1 import Items, { Category, Item, Warframe, Weapon } from "warframe-items";
2 2
3 3 type MinWeapon = Omit<Weapon, "patchlogs">;
4 4 type MinItem = Omit<Item, "patchlogs">;
@@ -16,3 +16,45 @@ export const items: MinItem[] = new Items({ category: ["All"] }).map(item => {
16 16 delete next.patchlogs;
17 17 return next;
18 18 });
19
20 const getNamesObj = (category: Category) =>
21 new Items({ category: [category] }).reduce((acc, item) => {
22 acc[item.name!.replace("'S", "'s")] = item.uniqueName!;
23 return acc;
24 }, {} as ImportAssertions);
25
26 export const modNames = getNamesObj("Mods");
27 export const resourceNames = getNamesObj("Resources");
28 export const miscNames = getNamesObj("Misc");
29 export const relicNames = getNamesObj("Relics");
30 export const skinNames = getNamesObj("Skins");
31 export const arcaneNames = getNamesObj("Arcanes");
32 export const gearNames = getNamesObj("Gear");
33
34 export const craftNames: ImportAssertions = Object.fromEntries(
35 (
36 new Items({
37 category: [
38 "Warframes",
39 "Gear",
40 "Melee",
41 "Primary",
42 "Secondary",
43 "Sentinels",
44 "Misc",
45 "Arch-Gun",
46 "Arch-Melee"
47 ]
48 }) as Warframe[]
49 )
50 .flatMap(item => item.components || [])
51 .filter(item => item.drops && item.drops[0])
52 .map(item => [item.drops![0].type, item.uniqueName])
53 );
54 craftNames["Forma Blueprint"] = "/Lotus/Types/Recipes/Components/FormaBlueprint";
55
56 export const blueprintNames: ImportAssertions = Object.fromEntries(
57 Object.keys(craftNames)
58 .filter(name => name.includes("Blueprint"))
59 .map(name => [name, craftNames[name]])
60 );
Added static/json/missions-drop-table.json +1 -0
Added static/json/scripts/missions-drop-table-get-script.js +30 -0