diff --git a/src/controllers/api/artifactsController.ts b/src/controllers/api/artifactsController.ts index 93aa47afa..a56c586ca 100644 --- a/src/controllers/api/artifactsController.ts +++ b/src/controllers/api/artifactsController.ts @@ -1,19 +1,18 @@ +import { parseString } from "@/src/helpers/general"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { upgradeMod } from "@/src/services/inventoryService"; +import { IArtifactsRequest } from "@/src/types/requestTypes"; import { RequestHandler } from "express"; // eslint-disable-next-line @typescript-eslint/no-misused-promises const artifactsController: RequestHandler = async (req, res) => { - const [data] = String(req.body).split("\n"); - const id = req.query.accountId as string; - - // TODO - salt check + const accountId = parseString(req.query.accountId); try { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const parsedData = JSON.parse(data); - + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call + const artifactsData = getJSONfromString(req.body.toString()) as IArtifactsRequest; // eslint-disable-next-line @typescript-eslint/no-unsafe-argument - const upgradeModId = await upgradeMod(parsedData, id); + const upgradeModId = await upgradeMod(artifactsData, accountId); res.send(upgradeModId); } catch (err) { console.error("Error parsing JSON data:", err); diff --git a/src/controllers/api/missionInventoryUpdateController.ts b/src/controllers/api/missionInventoryUpdateController.ts index 3d8fcaff5..e53ff359b 100644 --- a/src/controllers/api/missionInventoryUpdateController.ts +++ b/src/controllers/api/missionInventoryUpdateController.ts @@ -1,7 +1,9 @@ import { RequestHandler } from "express"; import { missionInventoryUpdate } from "@/src/services/inventoryService"; import { combineRewardAndLootInventory, getRewards } from "@/src/services/missionInventoryUpdateService "; -import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { parseString } from "@/src/helpers/general"; +import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes"; /* **** INPUT **** - [ ] crossPlaySetting @@ -20,7 +22,7 @@ import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType" - [ ] CurrentLoadOutIds - [ ] AliveTime - [ ] MissionTime -- [ ] Missions +- [x] Missions - [ ] CompletedAlerts - [ ] LastRegionPlayed - [ ] GameModeId @@ -43,23 +45,20 @@ import { IMissionInventoryUpdate } from "@/src/types/missionInventoryUpdateType" */ // eslint-disable-next-line @typescript-eslint/no-misused-promises -const missionInventoryUpdateController: RequestHandler = async (req, res) => { - const [data] = String(req.body).split("\n"); - const id = req.query.accountId as string; +const missionInventoryUpdateController: RequestHandler = async (req, res): Promise => { + const accountId = parseString(req.query.accountId); try { - const lootInventory = JSON.parse(data) as IMissionInventoryUpdate; - if (typeof lootInventory !== "object" || lootInventory === null) { - throw new Error("Invalid data format"); - } + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call + const lootInventory = getJSONfromString(req.body.toString()) as IMissionInventoryUpdateRequest; - const { InventoryChanges, MissionRewards } = getRewards(lootInventory.RewardInfo); + const { InventoryChanges, MissionRewards } = getRewards(lootInventory); const { combinedInventoryChanges, TotalCredits, CreditsBonus, MissionCredits, FusionPoints } = combineRewardAndLootInventory(InventoryChanges, lootInventory); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const InventoryJson = JSON.stringify(await missionInventoryUpdate(combinedInventoryChanges, id)); + const InventoryJson = JSON.stringify(await missionInventoryUpdate(combinedInventoryChanges, accountId)); res.json({ // InventoryJson, // this part will reset game data and missions will be locked MissionRewards, diff --git a/src/helpers/stringHelpers.ts b/src/helpers/stringHelpers.ts index 679953824..8cf21e808 100644 --- a/src/helpers/stringHelpers.ts +++ b/src/helpers/stringHelpers.ts @@ -1,4 +1,4 @@ -const getJSONfromString = (str: string): any => { +export const getJSONfromString = (str: string): any => { const jsonSubstring = str.substring(0, str.lastIndexOf("}") + 1); return JSON.parse(jsonSubstring); }; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 58b678c68..aecd2293a 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -2,20 +2,20 @@ import { Inventory } from "@/src/models/inventoryModel"; import new_inventory from "@/static/fixed_responses/postTutorialInventory.json"; import config from "@/config.json"; import { Types } from "mongoose"; -import { ISuitResponse } from "@/src/types/inventoryTypes/SuitTypes"; +import { ISuitDatabase, ISuitResponse } from "@/src/types/inventoryTypes/SuitTypes"; import { SlotType } from "@/src/types/purchaseTypes"; -import { IWeaponResponse } from "@/src/types/inventoryTypes/weaponTypes"; +import { IWeaponDatabase, IWeaponResponse } from "@/src/types/inventoryTypes/weaponTypes"; import { IChallengeProgress, IConsumable, - ICrewShipSalvagedWeaponSkin, IFlavourItem, IInventoryDatabaseDocument, IMiscItem, + IMission, IRawUpgrade } from "@/src/types/inventoryTypes/inventoryTypes"; -import { IMissionInventoryUpdate, IMissionInventoryUpdateGear } from "../types/missionInventoryUpdateType"; import { IGenericUpdate } from "../types/genericUpdate"; +import { IArtifactsRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes"; const createInventory = async (accountOwnerId: Types.ObjectId) => { try { @@ -139,17 +139,17 @@ export const addCustomization = async (customizatonName: string, accountId: stri const addGearExpByCategory = ( inventory: IInventoryDatabaseDocument, - gearArray: IMissionInventoryUpdateGear[] | undefined, + gearArray: ISuitDatabase[] | IWeaponDatabase[] | undefined, categoryName: "Pistols" | "LongGuns" | "Melee" | "Suits" ) => { const category = inventory[categoryName]; gearArray?.forEach(({ ItemId, XP }) => { - const itemIndex = category.findIndex(item => item._id?.equals(ItemId.$oid)); + const itemIndex = ItemId ? category.findIndex(item => item._id?.equals(ItemId.$oid)) : -1; const item = category[itemIndex]; if (itemIndex !== -1 && item.XP != undefined) { - item.XP += XP; + item.XP += XP || 0; inventory.markModified(`${categoryName}.${itemIndex}.XP`); } }); @@ -229,11 +229,24 @@ const addChallenges = (inventory: IInventoryDatabaseDocument, itemsArray: IChall }); }; +const addMissionComplete = (inventory: IInventoryDatabaseDocument, { Tag, Completes }: IMission) => { + const { Missions } = inventory; + const itemIndex = Missions.findIndex(item => item.Tag === Tag); + + if (itemIndex !== -1) { + Missions[itemIndex].Completes += Completes; + inventory.markModified(`Missions.${itemIndex}.Completes`); + } else { + Missions.push({ Tag, Completes }); + } +}; + const gearKeys = ["Suits", "Pistols", "LongGuns", "Melee"] as const; type GearKeysType = (typeof gearKeys)[number]; -export const missionInventoryUpdate = async (data: IMissionInventoryUpdate, accountId: string) => { - const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress, FusionPoints, Consumables, Recipes } = data; +export const missionInventoryUpdate = async (data: IMissionInventoryUpdateRequest, accountId: string) => { + const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress, FusionPoints, Consumables, Recipes, Missions } = + data; const inventory = await getInventory(accountId); // credits @@ -251,6 +264,7 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdate, acco addConsumables(inventory, Consumables); addRecipes(inventory, Recipes); addChallenges(inventory, ChallengeProgress); + addMissionComplete(inventory, Missions!); const changedInventory = await inventory.save(); return changedInventory.toJSON(); @@ -275,15 +289,8 @@ export const addBooster = async (ItemType: string, time: number, accountId: stri await inventory.save(); }; -export const upgradeMod = async ( - { - Upgrade, - LevelDiff, - Cost, - FusionPointCost - }: { Upgrade: ICrewShipSalvagedWeaponSkin; LevelDiff: number; Cost: number; FusionPointCost: number }, - accountId: string -): Promise => { +export const upgradeMod = async (artifactsData: IArtifactsRequest, accountId: string): Promise => { + const { Upgrade, LevelDiff, Cost, FusionPointCost } = artifactsData; try { const inventory = await getInventory(accountId); const { Upgrades, RawUpgrades } = inventory; diff --git a/src/services/missionInventoryUpdateService .ts b/src/services/missionInventoryUpdateService .ts index a3cbb9b38..cfee223af 100644 --- a/src/services/missionInventoryUpdateService .ts +++ b/src/services/missionInventoryUpdateService .ts @@ -1,29 +1,26 @@ -import { - IMissionInventoryUpdate, - IMissionInventoryUpdateRewardInfo, - IMissionRewardResponse, - IReward, - IInventoryFieldType, - inventoryFields -} from "@/src/types/missionInventoryUpdateType"; +import { IMissionRewardResponse, IReward, IInventoryFieldType, inventoryFields } from "@/src/types/missionTypes"; import missionsDropTable from "@/static/json/missions-drop-table.json"; import { modNames, relicNames, miscNames, resourceNames, gearNames, blueprintNames } from "@/static/data/items"; +import { IMissionInventoryUpdateRequest } from "../types/requestTypes"; // need reverse engineer rewardSeed, otherwise ingame displayed rotation reward will be different than added to db or displayed on mission end -const getRewards = ( - rewardInfo: IMissionInventoryUpdateRewardInfo | undefined -): { InventoryChanges: IMissionInventoryUpdate; MissionRewards: IMissionRewardResponse[] } => { - if (!rewardInfo) { +const getRewards = ({ + RewardInfo +}: IMissionInventoryUpdateRequest): { + InventoryChanges: IMissionInventoryUpdateRequest; + MissionRewards: IMissionRewardResponse[]; +} => { + if (!RewardInfo) { return { InventoryChanges: {}, MissionRewards: [] }; } - const rewards = (missionsDropTable as { [key: string]: IReward[] })[rewardInfo.node]; + const rewards = (missionsDropTable as { [key: string]: IReward[] })[RewardInfo.node]; if (!rewards) { return { InventoryChanges: {}, MissionRewards: [] }; } - const rotationCount = rewardInfo.rewardQualifications?.length || 0; + const rotationCount = RewardInfo.rewardQualifications?.length || 0; const rotations = getRotations(rotationCount); const drops: IReward[] = []; for (const rotation of rotations) { @@ -54,6 +51,7 @@ const getRewards = ( // { chance: 10.82, name: "2X Orokin Cell", rotation: "C" }, // { chance: 10.82, name: "Arrow Mutation", rotation: "C" }, // { chance: 10.82, name: "200 Endo", rotation: "C" }, + // { chance: 10.82, name: "200 Endo", rotation: "C" }, // { chance: 10.82, name: "2,000,000 Credits Cache", rotation: "C" }, // { chance: 7.69, name: "Health Restore (Large)", rotation: "C" }, // { chance: 7.69, name: "Vapor Specter Blueprint", rotation: "C" } @@ -66,8 +64,8 @@ const getRewards = ( }; const combineRewardAndLootInventory = ( - rewardInventory: IMissionInventoryUpdate, - lootInventory: IMissionInventoryUpdate + rewardInventory: IMissionInventoryUpdateRequest, + lootInventory: IMissionInventoryUpdateRequest ) => { const missionCredits = lootInventory.RegularCredits || 0; const creditsBonus = rewardInventory.RegularCredits || 0; @@ -98,12 +96,10 @@ const getRotations = (rotationCount: number): (string | undefined)[] => { if (rotationCount === 0) return [undefined]; const rotationPattern = ["A", "A", "B", "C"]; - let rotationIndex = 0; const rotatedValues = []; - for (let i = 1; i <= rotationCount; i++) { - rotatedValues.push(rotationPattern[rotationIndex]); - rotationIndex = (rotationIndex + 1) % 3; + for (let i = 0; i < rotationCount; i++) { + rotatedValues.push(rotationPattern[i % rotationPattern.length]); } return rotatedValues; @@ -128,8 +124,8 @@ const getRandomRewardByChance = (data: IReward[] | undefined): IReward | undefin const formatRewardsToInventoryType = ( rewards: IReward[] -): { InventoryChanges: IMissionInventoryUpdate; MissionRewards: IMissionRewardResponse[] } => { - const InventoryChanges: IMissionInventoryUpdate = {}; +): { InventoryChanges: IMissionInventoryUpdateRequest; MissionRewards: IMissionRewardResponse[] } => { + const InventoryChanges: IMissionInventoryUpdateRequest = {}; const MissionRewards: IMissionRewardResponse[] = []; for (const reward of rewards) { if (itemCheck(InventoryChanges, MissionRewards, reward.name)) { @@ -152,7 +148,7 @@ const formatRewardsToInventoryType = ( }; const itemCheck = ( - InventoryChanges: IMissionInventoryUpdate, + InventoryChanges: IMissionInventoryUpdateRequest, MissionRewards: IMissionRewardResponse[], name: string ) => { @@ -184,7 +180,7 @@ const getCountFromName = (name: string) => { }; const addRewardResponse = ( - InventoryChanges: IMissionInventoryUpdate, + InventoryChanges: IMissionInventoryUpdateRequest, MissionRewards: IMissionRewardResponse[], ItemName: string, ItemType: string, diff --git a/src/types/inventoryTypes/SuitTypes.ts b/src/types/inventoryTypes/SuitTypes.ts index 033b19678..7e1f68256 100644 --- a/src/types/inventoryTypes/SuitTypes.ts +++ b/src/types/inventoryTypes/SuitTypes.ts @@ -24,6 +24,7 @@ export interface ISuitDatabase { FocusLens?: string; UnlockLevel?: number; _id: Types.ObjectId; + ItemId?: IOid; } export interface SuitConfig { diff --git a/src/types/inventoryTypes/weaponTypes.ts b/src/types/inventoryTypes/weaponTypes.ts index 032becba9..365c869b4 100644 --- a/src/types/inventoryTypes/weaponTypes.ts +++ b/src/types/inventoryTypes/weaponTypes.ts @@ -22,6 +22,7 @@ export interface IWeaponDatabase { ModularParts?: string[]; UnlockLevel?: number; _id?: Types.ObjectId; + ItemId?: IOid; } export interface WeaponConfig { diff --git a/src/types/missionInventoryUpdateType.ts b/src/types/missionInventoryUpdateType.ts deleted file mode 100644 index 9268810b7..000000000 --- a/src/types/missionInventoryUpdateType.ts +++ /dev/null @@ -1,94 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { IOid } from "./commonTypes"; -import { IDate } from "./inventoryTypes/inventoryTypes"; - -export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const; -export type IInventoryFieldType = (typeof inventoryFields)[number]; -export interface IMissionInventoryUpdateGear { - ItemType: string; - ItemName: string; - ItemId: IOid; - XP: number; - UpgradeVer: number; - Features: number; - Polarized: number; - CustomizationSlotPurchases: number; - ModSlotPurchases: number; - FocusLens: string; - Expiry: IDate; - Polarity: any[]; - Configs: any[]; - ModularParts: any[]; - SkillTree: string; - UpgradeType: string; - UpgradeFingerprint: string; - OffensiveUpgrade: string; - DefensiveUpgrade: string; - UpgradesExpiry: IDate; - ArchonCrystalUpgrades: any[]; -} - -export interface IMissionInventoryUpdateItem { - ItemCount: number; - ItemType: string; -} - -export interface IMissionInventoryUpdateCard extends IMissionInventoryUpdateItem { - ItemId: IOid; - UpgradeFingerprint: string; - PendingRerollFingerprint: string; - LastAdded: IOid; -} - -export interface IMissionInventoryUpdateChallange { - Name: string; - Progress: number; - Completed: any[]; -} - -export interface IMissionInventoryUpdateRewardInfo { - node: string; - rewardTier?: number; - nightmareMode?: boolean; - useVaultManifest?: boolean; - EnemyCachesFound?: number; - toxinOk?: boolean; - lostTargetWave?: number; - defenseTargetCount?: number; - EOM_AFK?: number; - rewardQualifications?: string; - PurgatoryRewardQualifications?: string; - rewardSeed?: number; -} - -export interface IMissionInventoryUpdate { - rewardsMultiplier?: number; - ActiveBoosters?: any[]; - LongGuns?: IMissionInventoryUpdateGear[]; - Pistols?: IMissionInventoryUpdateGear[]; - Suits?: IMissionInventoryUpdateGear[]; - Melee?: IMissionInventoryUpdateGear[]; - RawUpgrades?: IMissionInventoryUpdateItem[]; - MiscItems?: IMissionInventoryUpdateItem[]; - Consumables?: IMissionInventoryUpdateItem[]; - Recipes?: IMissionInventoryUpdateItem[]; - RegularCredits?: number; - ChallengeProgress?: IMissionInventoryUpdateChallange[]; - RewardInfo?: IMissionInventoryUpdateRewardInfo; - FusionPoints?: number; -} - -export interface IMissionRewardResponse { - StoreItem?: string; - TypeName: string; - UpgradeLevel?: number; - ItemCount: number; - TweetText: string; - ProductCategory: string; -} - -export interface IReward { - name: string; - chance: number; - rotation?: string; -} diff --git a/src/types/missionTypes.ts b/src/types/missionTypes.ts new file mode 100644 index 000000000..193534f89 --- /dev/null +++ b/src/types/missionTypes.ts @@ -0,0 +1,17 @@ +export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const; +export type IInventoryFieldType = (typeof inventoryFields)[number]; + +export interface IMissionRewardResponse { + StoreItem?: string; + TypeName: string; + UpgradeLevel?: number; + ItemCount: number; + TweetText: string; + ProductCategory: string; +} + +export interface IReward { + name: string; + chance: number; + rotation?: string; +} diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts new file mode 100644 index 000000000..050955d9a --- /dev/null +++ b/src/types/requestTypes.ts @@ -0,0 +1,53 @@ +import { + IBooster, + IChallengeProgress, + IConsumable, + ICrewShipSalvagedWeaponSkin, + IMiscItem, + IMission, + IRawUpgrade +} from "./inventoryTypes/inventoryTypes"; +import { IWeaponDatabase } from "./inventoryTypes/weaponTypes"; +import { ISuitDatabase } from "./inventoryTypes/SuitTypes"; + +interface IArtifactsRequest { + Upgrade: ICrewShipSalvagedWeaponSkin; + LevelDiff: number; + Cost: number; + FusionPointCost: number; +} + +interface IMissionInventoryUpdateRequest { + rewardsMultiplier?: number; + ActiveBoosters?: IBooster[]; + LongGuns?: IWeaponDatabase[]; + Pistols?: IWeaponDatabase[]; + Suits?: ISuitDatabase[]; + Melee?: IWeaponDatabase[]; + RawUpgrades?: IRawUpgrade[]; + MiscItems?: IMiscItem[]; + Consumables?: IConsumable[]; + Recipes?: IConsumable[]; + RegularCredits?: number; + ChallengeProgress?: IChallengeProgress[]; + RewardInfo?: IMissionInventoryUpdateRequestRewardInfo; + FusionPoints?: number; + Missions?: IMission; +} + +interface IMissionInventoryUpdateRequestRewardInfo { + node: string; + rewardTier?: number; + nightmareMode?: boolean; + useVaultManifest?: boolean; + EnemyCachesFound?: number; + toxinOk?: boolean; + lostTargetWave?: number; + defenseTargetCount?: number; + EOM_AFK?: number; + rewardQualifications?: string; + PurgatoryRewardQualifications?: string; + rewardSeed?: number; +} + +export { IArtifactsRequest, IMissionInventoryUpdateRequest };