From f0bb281f550d5ba66bbd4fa5abeedbccb072643e Mon Sep 17 00:00:00 2001 From: Sainan Date: Thu, 20 Jun 2024 13:05:07 +0200 Subject: [PATCH] feat: implement incarnon genesis installation, challenges & skill tree (#333) --- src/controllers/api/evolveWeaponController.ts | 42 +++++++++++++++++++ .../api/setWeaponSkillTreeController.ts | 24 +++++++++++ src/controllers/api/upgradesController.ts | 8 ++-- src/models/inventoryModels/inventoryModel.ts | 14 ++++++- src/routes/api.ts | 4 ++ src/services/inventoryService.ts | 16 +++++++ .../inventoryTypes/commonInventoryTypes.ts | 7 ++++ src/types/inventoryTypes/inventoryTypes.ts | 8 +++- src/types/requestTypes.ts | 2 + 9 files changed, 118 insertions(+), 7 deletions(-) create mode 100644 src/controllers/api/evolveWeaponController.ts create mode 100644 src/controllers/api/setWeaponSkillTreeController.ts diff --git a/src/controllers/api/evolveWeaponController.ts b/src/controllers/api/evolveWeaponController.ts new file mode 100644 index 000000000..fea82844c --- /dev/null +++ b/src/controllers/api/evolveWeaponController.ts @@ -0,0 +1,42 @@ +import { RequestHandler } from "express"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getInventory } from "@/src/services/inventoryService"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { WeaponTypeInternal } from "@/src/services/itemDataService"; +import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes"; + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +export const evolveWeaponController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const payload = getJSONfromString(req.body.toString()) as IEvolveWeaponRequest; + console.assert(payload.Action == "EWA_INSTALL"); + + // TODO: We should remove the Genesis item & its resources, but currently we don't know these "recipes". + + const item = inventory[payload.Category].find(item => item._id.toString() == (req.query.ItemId as string))!; + item.Features ??= 0; + item.Features |= EquipmentFeatures.INCARNON_GENESIS; + + item.SkillTree = "0"; + + inventory.EvolutionProgress ??= []; + if (!inventory.EvolutionProgress.find(entry => entry.ItemType == payload.EvoType)) { + inventory.EvolutionProgress.push({ + Progress: 0, + Rank: 1, + ItemType: payload.EvoType + }); + } + + await inventory.save(); + res.end(); +}; + +interface IEvolveWeaponRequest { + Action: "EWA_INSTALL"; + Category: WeaponTypeInternal; + Recipe: string; // e.g. "/Lotus/Types/Items/MiscItems/IncarnonAdapters/UnlockerBlueprints/DespairIncarnonBlueprint" + UninstallRecipe: ""; + EvoType: string; // e.g. "/Lotus/Weapons/Tenno/ThrowingWeapons/StalkerKunai" +} diff --git a/src/controllers/api/setWeaponSkillTreeController.ts b/src/controllers/api/setWeaponSkillTreeController.ts new file mode 100644 index 000000000..b3b0be6f7 --- /dev/null +++ b/src/controllers/api/setWeaponSkillTreeController.ts @@ -0,0 +1,24 @@ +import { RequestHandler } from "express"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getInventory } from "@/src/services/inventoryService"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { WeaponTypeInternal } from "@/src/services/itemDataService"; + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +export const setWeaponSkillTreeController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + const payload = getJSONfromString(req.body.toString()) as ISetWeaponSkillTreeRequest; + + const item = inventory[req.query.Category as WeaponTypeInternal].find( + item => item._id.toString() == (req.query.ItemId as string) + )!; + item.SkillTree = payload.SkillTree; + + await inventory.save(); + res.end(); +}; + +interface ISetWeaponSkillTreeRequest { + SkillTree: string; +} diff --git a/src/controllers/api/upgradesController.ts b/src/controllers/api/upgradesController.ts index e2f7f0525..9d89da708 100644 --- a/src/controllers/api/upgradesController.ts +++ b/src/controllers/api/upgradesController.ts @@ -1,6 +1,6 @@ import { RequestHandler } from "express"; import { IUpgradesRequest } from "@/src/types/requestTypes"; -import { FocusSchool, IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes"; +import { FocusSchool, IEquipmentDatabase, EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IMiscItem, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService"; @@ -32,7 +32,7 @@ export const upgradesController: RequestHandler = async (req, res) => { for (const item of inventory[payload.ItemCategory as TEquipmentKey] as IEquipmentDatabase[]) { if (item._id.toString() == payload.ItemId.$oid) { item.Features ??= 0; - item.Features |= 1; + item.Features |= EquipmentFeatures.DOUBLE_CAPACITY; break; } } @@ -42,7 +42,7 @@ export const upgradesController: RequestHandler = async (req, res) => { for (const item of inventory[payload.ItemCategory as TEquipmentKey] as IEquipmentDatabase[]) { if (item._id.toString() == payload.ItemId.$oid) { item.Features ??= 0; - item.Features |= 2; + item.Features |= EquipmentFeatures.UTILITY_SLOT; break; } } @@ -53,7 +53,7 @@ export const upgradesController: RequestHandler = async (req, res) => { for (const item of inventory[payload.ItemCategory as TEquipmentKey] as IEquipmentDatabase[]) { if (item._id.toString() == payload.ItemId.$oid) { item.Features ??= 0; - item.Features |= 32; + item.Features |= EquipmentFeatures.ARCANE_SLOT; break; } } diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index aed248007..4ef0d856f 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -35,7 +35,8 @@ import { ITauntHistory, IPeriodicMissionCompletionDatabase, IPeriodicMissionCompletionResponse, - ILoreFragmentScan + ILoreFragmentScan, + IEvolutionProgress } from "../../types/inventoryTypes/inventoryTypes"; import { IOid } from "../../types/commonTypes"; import { @@ -562,6 +563,15 @@ const loreFragmentScansSchema = new Schema( { _id: false } ); +const evolutionProgressSchema = new Schema( + { + Progress: Number, + Rank: Number, + ItemType: String + }, + { _id: false } +); + const inventorySchema = new Schema( { accountOwnerId: Schema.Types.ObjectId, @@ -881,7 +891,7 @@ const inventorySchema = new Schema( //Progress+Rank+ItemType(ZarimanPumpShotgun) //https://warframe.fandom.com/wiki/Incarnon - EvolutionProgress: [Schema.Types.Mixed], + EvolutionProgress: { type: [evolutionProgressSchema], default: undefined }, //Unknown and system DuviriInfo: DuviriInfoSchema, diff --git a/src/routes/api.ts b/src/routes/api.ts index 15192fe8c..bf7440def 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -7,6 +7,7 @@ import { createGuildController } from "@/src/controllers/api/createGuildControll import { deleteSessionController } from "@/src/controllers/api/deleteSessionController"; import { dojoController } from "@/src/controllers/api/dojoController"; import { dronesController } from "@/src/controllers/api/dronesController"; +import { evolveWeaponController } from "@/src/controllers/api/evolveWeaponController"; import { findSessionsController } from "@/src/controllers/api/findSessionsController"; import { focusController } from "@/src/controllers/api/focusController"; import { genericUpdateController } from "@/src/controllers/api/genericUpdateController"; @@ -47,6 +48,7 @@ import { setActiveShipController } from "@/src/controllers/api/setActiveShipCont import { setBootLocationController } from "@/src/controllers/api/setBootLocationController"; import { setShipCustomizationsController } from "@/src/controllers/api/setShipCustomizationsController"; import { setSupportedSyndicateController } from "@/src/controllers/api/setSupportedSyndicateController"; +import { setWeaponSkillTreeController } from "../controllers/api/setWeaponSkillTreeController"; import { shipDecorationsController } from "@/src/controllers/api/shipDecorationsController"; import { startDojoRecipeController } from "@/src/controllers/api/startDojoRecipeController"; import { startRecipeController } from "@/src/controllers/api/startRecipeController"; @@ -97,6 +99,7 @@ apiRouter.post("/addFriendImage.php", addFriendImageController); apiRouter.post("/artifacts.php", artifactsController); apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController); apiRouter.post("/createGuild.php", createGuildController); +apiRouter.post("/evolveWeapon.php", evolveWeaponController); apiRouter.post("/findSessions.php", findSessionsController); apiRouter.post("/focus.php", focusController); apiRouter.post("/genericUpdate.php", genericUpdateController); @@ -115,6 +118,7 @@ apiRouter.post("/rerollRandomMod.php", rerollRandomModController); apiRouter.post("/saveLoadout.php", saveLoadoutController); apiRouter.post("/sell.php", sellController); apiRouter.post("/setShipCustomizations.php", setShipCustomizationsController); +apiRouter.post("/setWeaponSkillTree.php", setWeaponSkillTreeController); apiRouter.post("/shipDecorations.php", shipDecorationsController); apiRouter.post("/startDojoRecipe.php", startDojoRecipeController); apiRouter.post("/startRecipe.php", startRecipeController); diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 36bda69e9..b29a5241c 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -629,6 +629,22 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdateReques // Gear XP gearKeys.forEach(key => addGearExpByCategory(inventory, data[key], key)); + // Incarnon Challenges + if (data.EvolutionProgress) { + for (const evoProgress of data.EvolutionProgress) { + const entry = inventory.EvolutionProgress + ? inventory.EvolutionProgress.find(entry => entry.ItemType == evoProgress.ItemType) + : undefined; + if (entry) { + entry.Progress = evoProgress.Progress; + entry.Rank = evoProgress.Rank; + } else { + inventory.EvolutionProgress ??= []; + inventory.EvolutionProgress.push(evoProgress); + } + } + } + // other addMods(inventory, RawUpgrades); addMiscItems(inventory, MiscItems); diff --git a/src/types/inventoryTypes/commonInventoryTypes.ts b/src/types/inventoryTypes/commonInventoryTypes.ts index aadc1b620..2c4343110 100644 --- a/src/types/inventoryTypes/commonInventoryTypes.ts +++ b/src/types/inventoryTypes/commonInventoryTypes.ts @@ -82,6 +82,13 @@ export interface IEquipmentClient extends Omit { ItemId: IOid; } +export enum EquipmentFeatures { + DOUBLE_CAPACITY = 1, + UTILITY_SLOT = 2, + ARCANE_SLOT = 32, + INCARNON_GENESIS = 512 +} + export interface IEquipmentDatabase { ItemType: string; ItemName?: string; diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 1a2018884..1555799bb 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -246,7 +246,7 @@ export interface IInventoryResponse { LastInventorySync: IOid; NextRefill: IMongoDate; ActiveLandscapeTraps: any[]; - EvolutionProgress: any[]; + EvolutionProgress?: IEvolutionProgress[]; RepVotes: any[]; LeagueTickets: any[]; Quests: any[]; @@ -867,3 +867,9 @@ export interface IWebFlags { Anniversary2021: boolean; HitDownloadBtn: IMongoDate; } + +export interface IEvolutionProgress { + Progress: number; + Rank: number; + ItemType: string; +} diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index 6dc35b765..ce7ea1e04 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -5,6 +5,7 @@ import { IChallengeProgress, IConsumable, ICrewShipSalvagedWeaponSkin, + IEvolutionProgress, IMiscItem, IMission, IRawUpgrade, @@ -53,6 +54,7 @@ export interface IMissionInventoryUpdateRequest { RewardInfo?: IMissionInventoryUpdateRequestRewardInfo; FusionPoints?: number; Missions?: IMission; + EvolutionProgress?: IEvolutionProgress[]; } export interface IMissionInventoryUpdateRequestRewardInfo {