From 49edebc1eb57a17054c69a54b6441f072e8f6391 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Sat, 5 Apr 2025 06:52:35 -0700 Subject: [PATCH] chore: fix controllers exporting non-RequestHandler things (#1468) I'm surprised JavaScript allows circular includes, but they still don't feel good. Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1468 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com> --- .../api/claimCompletedRecipeController.ts | 2 +- .../api/getNewRewardSeedController.ts | 7 +- .../giveKeyChainTriggeredItemsController.ts | 8 +- .../giveKeyChainTriggeredMessageController.ts | 2 +- src/controllers/api/giveQuestKey.ts | 4 +- .../api/giveStartingGearController.ts | 85 +------------ .../api/infestedFoundryController.ts | 118 ++---------------- src/controllers/api/inventoryController.ts | 15 +-- src/controllers/api/upgradesController.ts | 2 +- .../getProfileViewingDataController.ts | 2 +- src/helpers/stringHelpers.ts | 10 ++ src/services/infestedFoundryService.ts | 110 ++++++++++++++++ src/services/inventoryService.ts | 94 ++++++++++++-- src/services/itemDataService.ts | 2 +- src/services/missionInventoryUpdateService.ts | 10 +- src/services/questService.ts | 19 ++- src/types/inventoryTypes/inventoryTypes.ts | 2 + src/types/requestTypes.ts | 7 ++ 18 files changed, 243 insertions(+), 256 deletions(-) create mode 100644 src/services/infestedFoundryService.ts diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index f95a950f..880b2267 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -19,7 +19,7 @@ import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; -export interface IClaimCompletedRecipeRequest { +interface IClaimCompletedRecipeRequest { RecipeIds: IOid[]; } diff --git a/src/controllers/api/getNewRewardSeedController.ts b/src/controllers/api/getNewRewardSeedController.ts index dc4cec8e..4ff82405 100644 --- a/src/controllers/api/getNewRewardSeedController.ts +++ b/src/controllers/api/getNewRewardSeedController.ts @@ -1,4 +1,5 @@ import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; +import { generateRewardSeed } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { logger } from "@/src/utils/logger"; import { RequestHandler } from "express"; @@ -18,9 +19,3 @@ export const getNewRewardSeedController: RequestHandler = async (req, res) => { ); res.json({ rewardSeed: rewardSeed }); }; - -export function generateRewardSeed(): number { - const min = -Number.MAX_SAFE_INTEGER; - const max = Number.MAX_SAFE_INTEGER; - return Math.floor(Math.random() * (max - min + 1)) + min; -} diff --git a/src/controllers/api/giveKeyChainTriggeredItemsController.ts b/src/controllers/api/giveKeyChainTriggeredItemsController.ts index bff70c85..df8e8a80 100644 --- a/src/controllers/api/giveKeyChainTriggeredItemsController.ts +++ b/src/controllers/api/giveKeyChainTriggeredItemsController.ts @@ -2,8 +2,8 @@ import { RequestHandler } from "express"; import { parseString } from "@/src/helpers/general"; import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getInventory } from "@/src/services/inventoryService"; -import { IGroup } from "@/src/types/loginTypes"; import { giveKeyChainItem } from "@/src/services/questService"; +import { IKeyChainRequest } from "@/src/types/requestTypes"; export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => { const accountId = parseString(req.query.accountId); @@ -15,9 +15,3 @@ export const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res.send(inventoryChanges); }; - -export interface IKeyChainRequest { - KeyChain: string; - ChainStage: number; - Groups?: IGroup[]; -} diff --git a/src/controllers/api/giveKeyChainTriggeredMessageController.ts b/src/controllers/api/giveKeyChainTriggeredMessageController.ts index dec4b8a1..3bc41c21 100644 --- a/src/controllers/api/giveKeyChainTriggeredMessageController.ts +++ b/src/controllers/api/giveKeyChainTriggeredMessageController.ts @@ -1,7 +1,7 @@ -import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { getInventory } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { giveKeyChainMessage } from "@/src/services/questService"; +import { IKeyChainRequest } from "@/src/types/requestTypes"; import { RequestHandler } from "express"; export const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => { diff --git a/src/controllers/api/giveQuestKey.ts b/src/controllers/api/giveQuestKey.ts index 8cd4b135..80846234 100644 --- a/src/controllers/api/giveQuestKey.ts +++ b/src/controllers/api/giveQuestKey.ts @@ -20,11 +20,11 @@ export const giveQuestKeyRewardController: RequestHandler = async (req, res) => //TODO: consider whishlist changes }; -export interface IQuestKeyRewardRequest { +interface IQuestKeyRewardRequest { reward: IQuestKeyReward; } -export interface IQuestKeyReward { +interface IQuestKeyReward { RewardType: string; CouponType: string; Icon: string; diff --git a/src/controllers/api/giveStartingGearController.ts b/src/controllers/api/giveStartingGearController.ts index 74d45e29..6556de93 100644 --- a/src/controllers/api/giveStartingGearController.ts +++ b/src/controllers/api/giveStartingGearController.ts @@ -1,20 +1,8 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers"; -import { InventoryDocumentProps } from "@/src/models/inventoryModels/inventoryModel"; -import { - addEquipment, - addItem, - addPowerSuit, - combineInventoryChanges, - getInventory, - updateSlots -} from "@/src/services/inventoryService"; +import { addStartingGear, getInventory } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; -import { IInventoryClient, IInventoryDatabase, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; -import { IInventoryChanges } from "@/src/types/purchaseTypes"; +import { TPartialStartingGear } from "@/src/types/inventoryTypes/inventoryTypes"; import { RequestHandler } from "express"; -import { HydratedDocument } from "mongoose"; - -type TPartialStartingGear = Pick; export const giveStartingGearController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -26,72 +14,3 @@ export const giveStartingGearController: RequestHandler = async (req, res) => { res.send(inventoryChanges); }; - -//TODO: RawUpgrades might need to return a LastAdded -const awakeningRewards = [ - "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1", - "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2", - "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3", - "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4", - "/Lotus/Types/Restoratives/LisetAutoHack", - "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod" -]; - -export const addStartingGear = async ( - inventory: HydratedDocument, - startingGear: TPartialStartingGear | undefined = undefined -): Promise => { - const { LongGuns, Pistols, Suits, Melee } = startingGear || { - LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }], - Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }], - Suits: [{ ItemType: "/Lotus/Powersuits/Excalibur/Excalibur" }], - Melee: [{ ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" }] - }; - - //TODO: properly merge weapon bin changes it is currently static here - const inventoryChanges: IInventoryChanges = {}; - addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges); - addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges); - addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges); - await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges); - addEquipment( - inventory, - "DataKnives", - "/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon", - undefined, - inventoryChanges, - { XP: 450_000 } - ); - addEquipment( - inventory, - "Scoops", - "/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest", - undefined, - inventoryChanges - ); - - updateSlots(inventory, InventorySlot.SUITS, 0, 1); - updateSlots(inventory, InventorySlot.WEAPONS, 0, 3); - inventoryChanges.SuitBin = { count: 1, platinum: 0, Slots: -1 }; - inventoryChanges.WeaponBin = { count: 3, platinum: 0, Slots: -3 }; - - await addItem(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"); - inventory.ActiveQuest = "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"; - - inventory.PremiumCredits = 50; - inventory.PremiumCreditsFree = 50; - inventoryChanges.PremiumCredits = 50; - inventoryChanges.PremiumCreditsFree = 50; - inventory.RegularCredits = 3000; - inventoryChanges.RegularCredits = 3000; - - for (const item of awakeningRewards) { - const inventoryDelta = await addItem(inventory, item); - combineInventoryChanges(inventoryChanges, inventoryDelta); - } - - inventory.PlayedParkourTutorial = true; - inventory.ReceivedStartingGear = true; - - return inventoryChanges; -}; diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index d63c2203..28e89adc 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -6,20 +6,21 @@ import { IOid } from "@/src/types/commonTypes"; import { IConsumedSuit, IHelminthFoodRecord, - IInfestedFoundryClient, - IInfestedFoundryDatabase, IInventoryClient, IMiscItem, - InventorySlot, - ITypeCount + InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; -import { ExportMisc, ExportRecipes } from "warframe-public-export-plus"; +import { ExportMisc } from "warframe-public-export-plus"; import { getRecipe } from "@/src/services/itemDataService"; -import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { toMongoDate } from "@/src/helpers/inventoryHelpers"; import { logger } from "@/src/utils/logger"; import { colorToShard } from "@/src/helpers/shardHelper"; import { config } from "@/src/services/configService"; +import { + addInfestedFoundryXP, + applyCheatsToInfestedFoundry, + handleSubsumeCompletion +} from "@/src/services/infestedFoundryService"; export const infestedFoundryController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -383,116 +384,11 @@ interface IHelminthFeedRequest { }[]; } -export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundryDatabase, delta: number): ITypeCount[] => { - const recipeChanges: ITypeCount[] = []; - infestedFoundry.XP ??= 0; - const prevXP = infestedFoundry.XP; - infestedFoundry.XP += delta; - if (prevXP < 2250_00 && infestedFoundry.XP >= 2250_00) { - infestedFoundry.Slots ??= 0; - infestedFoundry.Slots += 3; - } - if (prevXP < 5625_00 && infestedFoundry.XP >= 5625_00) { - recipeChanges.push({ - ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldsBlueprint", - ItemCount: 1 - }); - } - if (prevXP < 10125_00 && infestedFoundry.XP >= 10125_00) { - recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthHackBlueprint", ItemCount: 1 }); - } - if (prevXP < 15750_00 && infestedFoundry.XP >= 15750_00) { - infestedFoundry.Slots ??= 0; - infestedFoundry.Slots += 10; - } - if (prevXP < 22500_00 && infestedFoundry.XP >= 22500_00) { - recipeChanges.push({ - ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthAmmoEfficiencyBlueprint", - ItemCount: 1 - }); - } - if (prevXP < 30375_00 && infestedFoundry.XP >= 30375_00) { - recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStunBlueprint", ItemCount: 1 }); - } - if (prevXP < 39375_00 && infestedFoundry.XP >= 39375_00) { - infestedFoundry.Slots ??= 0; - infestedFoundry.Slots += 20; - } - if (prevXP < 60750_00 && infestedFoundry.XP >= 60750_00) { - recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStatusBlueprint", ItemCount: 1 }); - } - if (prevXP < 73125_00 && infestedFoundry.XP >= 73125_00) { - infestedFoundry.Slots = 1; - } - if (prevXP < 86625_00 && infestedFoundry.XP >= 86625_00) { - recipeChanges.push({ - ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldArmorBlueprint", - ItemCount: 1 - }); - } - if (prevXP < 101250_00 && infestedFoundry.XP >= 101250_00) { - recipeChanges.push({ - ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthProcBlockBlueprint", - ItemCount: 1 - }); - } - if (prevXP < 117000_00 && infestedFoundry.XP >= 117000_00) { - recipeChanges.push({ - ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthEnergyShareBlueprint", - ItemCount: 1 - }); - } - if (prevXP < 133875_00 && infestedFoundry.XP >= 133875_00) { - recipeChanges.push({ - ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthMaxStatusBlueprint", - ItemCount: 1 - }); - } - if (prevXP < 151875_00 && infestedFoundry.XP >= 151875_00) { - recipeChanges.push({ - ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthTreasureBlueprint", - ItemCount: 1 - }); - } - return recipeChanges; -}; - interface IHelminthSubsumeRequest { SuitId: IOid; Recipe: string; } -export const handleSubsumeCompletion = (inventory: TInventoryDatabaseDocument): ITypeCount[] => { - const [recipeType] = Object.entries(ExportRecipes).find( - ([_recipeType, recipe]) => - recipe.secretIngredientAction == "SIA_WARFRAME_ABILITY" && - recipe.secretIngredients![0].ItemType == inventory.InfestedFoundry!.LastConsumedSuit!.ItemType - )!; - inventory.InfestedFoundry!.LastConsumedSuit = undefined; - inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = undefined; - const recipeChanges: ITypeCount[] = [ - { - ItemType: recipeType, - ItemCount: 1 - } - ]; - addRecipes(inventory, recipeChanges); - return recipeChanges; -}; - -export const applyCheatsToInfestedFoundry = (infestedFoundry: IInfestedFoundryClient): void => { - if (config.infiniteHelminthMaterials) { - infestedFoundry.Resources = [ - { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthCalx", Count: 1000 }, - { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBiotics", Count: 1000 }, - { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthSynthetics", Count: 1000 }, - { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthPheromones", Count: 1000 }, - { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBile", Count: 1000 }, - { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthOxides", Count: 1000 } - ]; - } -}; - interface IHelminthOfferingsUpdate { OfferingsIndex: number; SuitTypes: string[]; diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index ab0391a6..2c659717 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -13,9 +13,10 @@ import { ExportResources, ExportVirtuals } from "warframe-public-export-plus"; -import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "./infestedFoundryController"; +import { applyCheatsToInfestedFoundry, handleSubsumeCompletion } from "@/src/services/infestedFoundryService"; import { addMiscItems, allDailyAffiliationKeys, createLibraryDailyTask } from "@/src/services/inventoryService"; import { logger } from "@/src/utils/logger"; +import { catBreadHash } from "@/src/helpers/stringHelpers"; export const inventoryController: RequestHandler = async (request, response) => { const accountId = await getAccountIdForRequest(request); @@ -269,7 +270,7 @@ export const getInventoryResponse = async ( return inventoryResponse; }; -export const addString = (arr: string[], str: string): void => { +const addString = (arr: string[], str: string): void => { if (!arr.find(x => x == str)) { arr.push(str); } @@ -299,13 +300,3 @@ const resourceGetParent = (resourceName: string): string | undefined => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition return ExportVirtuals[resourceName]?.parentName; }; - -// This is FNV1a-32 except operating under modulus 2^31 because JavaScript is stinky and likes producing negative integers out of nowhere. -export const catBreadHash = (name: string): number => { - let hash = 2166136261; - for (let i = 0; i != name.length; ++i) { - hash = (hash ^ name.charCodeAt(i)) & 0x7fffffff; - hash = (hash * 16777619) & 0x7fffffff; - } - return hash; -}; diff --git a/src/controllers/api/upgradesController.ts b/src/controllers/api/upgradesController.ts index ae547674..855d2aa7 100644 --- a/src/controllers/api/upgradesController.ts +++ b/src/controllers/api/upgradesController.ts @@ -11,7 +11,7 @@ import { getAccountIdForRequest } from "@/src/services/loginService"; import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { getRecipeByResult } from "@/src/services/itemDataService"; import { IInventoryChanges } from "@/src/types/purchaseTypes"; -import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "./infestedFoundryController"; +import { addInfestedFoundryXP, applyCheatsToInfestedFoundry } from "@/src/services/infestedFoundryService"; import { config } from "@/src/services/configService"; export const upgradesController: RequestHandler = async (req, res) => { diff --git a/src/controllers/dynamic/getProfileViewingDataController.ts b/src/controllers/dynamic/getProfileViewingDataController.ts index c1134604..766e40a2 100644 --- a/src/controllers/dynamic/getProfileViewingDataController.ts +++ b/src/controllers/dynamic/getProfileViewingDataController.ts @@ -18,7 +18,7 @@ import { ITypeXPItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { RequestHandler } from "express"; -import { catBreadHash } from "../api/inventoryController"; +import { catBreadHash } from "@/src/helpers/stringHelpers"; import { ExportCustoms, ExportDojoRecipes } from "warframe-public-export-plus"; import { IStatsClient } from "@/src/types/statTypes"; import { toStoreItem } from "@/src/services/itemDataService"; diff --git a/src/helpers/stringHelpers.ts b/src/helpers/stringHelpers.ts index f24fab32..8925aea2 100644 --- a/src/helpers/stringHelpers.ts +++ b/src/helpers/stringHelpers.ts @@ -27,6 +27,16 @@ export const getIndexAfter = (str: string, searchWord: string): number => { return index + searchWord.length; }; +// This is FNV1a-32 except operating under modulus 2^31 because JavaScript is stinky and likes producing negative integers out of nowhere. +export const catBreadHash = (name: string): number => { + let hash = 2166136261; + for (let i = 0; i != name.length; ++i) { + hash = (hash ^ name.charCodeAt(i)) & 0x7fffffff; + hash = (hash * 16777619) & 0x7fffffff; + } + return hash; +}; + export const regexEscape = (str: string): string => { str = str.split(".").join("\\."); str = str.split("\\").join("\\\\"); diff --git a/src/services/infestedFoundryService.ts b/src/services/infestedFoundryService.ts new file mode 100644 index 00000000..5afc93fa --- /dev/null +++ b/src/services/infestedFoundryService.ts @@ -0,0 +1,110 @@ +import { ExportRecipes } from "warframe-public-export-plus"; +import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; +import { IInfestedFoundryClient, IInfestedFoundryDatabase, ITypeCount } from "../types/inventoryTypes/inventoryTypes"; +import { addRecipes } from "./inventoryService"; +import { config } from "./configService"; + +export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundryDatabase, delta: number): ITypeCount[] => { + const recipeChanges: ITypeCount[] = []; + infestedFoundry.XP ??= 0; + const prevXP = infestedFoundry.XP; + infestedFoundry.XP += delta; + if (prevXP < 2250_00 && infestedFoundry.XP >= 2250_00) { + infestedFoundry.Slots ??= 0; + infestedFoundry.Slots += 3; + } + if (prevXP < 5625_00 && infestedFoundry.XP >= 5625_00) { + recipeChanges.push({ + ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldsBlueprint", + ItemCount: 1 + }); + } + if (prevXP < 10125_00 && infestedFoundry.XP >= 10125_00) { + recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthHackBlueprint", ItemCount: 1 }); + } + if (prevXP < 15750_00 && infestedFoundry.XP >= 15750_00) { + infestedFoundry.Slots ??= 0; + infestedFoundry.Slots += 10; + } + if (prevXP < 22500_00 && infestedFoundry.XP >= 22500_00) { + recipeChanges.push({ + ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthAmmoEfficiencyBlueprint", + ItemCount: 1 + }); + } + if (prevXP < 30375_00 && infestedFoundry.XP >= 30375_00) { + recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStunBlueprint", ItemCount: 1 }); + } + if (prevXP < 39375_00 && infestedFoundry.XP >= 39375_00) { + infestedFoundry.Slots ??= 0; + infestedFoundry.Slots += 20; + } + if (prevXP < 60750_00 && infestedFoundry.XP >= 60750_00) { + recipeChanges.push({ ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthStatusBlueprint", ItemCount: 1 }); + } + if (prevXP < 73125_00 && infestedFoundry.XP >= 73125_00) { + infestedFoundry.Slots = 1; + } + if (prevXP < 86625_00 && infestedFoundry.XP >= 86625_00) { + recipeChanges.push({ + ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthShieldArmorBlueprint", + ItemCount: 1 + }); + } + if (prevXP < 101250_00 && infestedFoundry.XP >= 101250_00) { + recipeChanges.push({ + ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthProcBlockBlueprint", + ItemCount: 1 + }); + } + if (prevXP < 117000_00 && infestedFoundry.XP >= 117000_00) { + recipeChanges.push({ + ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthEnergyShareBlueprint", + ItemCount: 1 + }); + } + if (prevXP < 133875_00 && infestedFoundry.XP >= 133875_00) { + recipeChanges.push({ + ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthMaxStatusBlueprint", + ItemCount: 1 + }); + } + if (prevXP < 151875_00 && infestedFoundry.XP >= 151875_00) { + recipeChanges.push({ + ItemType: "/Lotus/Types/Recipes/AbilityOverrides/HelminthTreasureBlueprint", + ItemCount: 1 + }); + } + return recipeChanges; +}; + +export const handleSubsumeCompletion = (inventory: TInventoryDatabaseDocument): ITypeCount[] => { + const [recipeType] = Object.entries(ExportRecipes).find( + ([_recipeType, recipe]) => + recipe.secretIngredientAction == "SIA_WARFRAME_ABILITY" && + recipe.secretIngredients![0].ItemType == inventory.InfestedFoundry!.LastConsumedSuit!.ItemType + )!; + inventory.InfestedFoundry!.LastConsumedSuit = undefined; + inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = undefined; + const recipeChanges: ITypeCount[] = [ + { + ItemType: recipeType, + ItemCount: 1 + } + ]; + addRecipes(inventory, recipeChanges); + return recipeChanges; +}; + +export const applyCheatsToInfestedFoundry = (infestedFoundry: IInfestedFoundryClient): void => { + if (config.infiniteHelminthMaterials) { + infestedFoundry.Resources = [ + { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthCalx", Count: 1000 }, + { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBiotics", Count: 1000 }, + { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthSynthetics", Count: 1000 }, + { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthPheromones", Count: 1000 }, + { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthBile", Count: 1000 }, + { ItemType: "/Lotus/Types/Items/InfestedFoundry/HelminthOxides", Count: 1000 } + ]; + } +}; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 3e4835da..4a711c6c 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -1,10 +1,6 @@ -import { - Inventory, - InventoryDocumentProps, - TInventoryDatabaseDocument -} from "@/src/models/inventoryModels/inventoryModel"; +import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { config } from "@/src/services/configService"; -import { HydratedDocument, Types } from "mongoose"; +import { Types } from "mongoose"; import { SlotNames, IInventoryChanges, IBinChanges, slotNames } from "@/src/types/purchaseTypes"; import { IChallengeProgress, @@ -19,16 +15,16 @@ import { TEquipmentKey, IFusionTreasure, IDailyAffiliations, - IInventoryDatabase, IKubrowPetEggDatabase, IKubrowPetEggClient, ILibraryDailyTaskInfo, ICalendarProgress, IDroneClient, - IUpgradeClient + IUpgradeClient, + TPartialStartingGear } from "@/src/types/inventoryTypes/inventoryTypes"; import { IGenericUpdate, IUpdateNodeIntrosResponse } from "../types/genericUpdate"; -import { IMissionInventoryUpdateRequest } from "../types/requestTypes"; +import { IKeyChainRequest, IMissionInventoryUpdateRequest } from "../types/requestTypes"; import { logger } from "@/src/utils/logger"; import { convertInboxMessage, fromStoreItem, getKeyChainItems } from "@/src/services/itemDataService"; import { @@ -62,10 +58,7 @@ import { TStandingLimitBin } from "warframe-public-export-plus"; import { createShip } from "./shipService"; -import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; import { toOid } from "../helpers/inventoryHelpers"; -import { generateRewardSeed } from "@/src/controllers/api/getNewRewardSeedController"; -import { addStartingGear } from "@/src/controllers/api/giveStartingGearController"; import { addQuestKey, completeQuest } from "@/src/services/questService"; import { handleBundleAcqusition } from "./purchaseService"; import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json"; @@ -114,6 +107,81 @@ export const createInventory = async ( } }; +export const generateRewardSeed = (): number => { + const min = -Number.MAX_SAFE_INTEGER; + const max = Number.MAX_SAFE_INTEGER; + return Math.floor(Math.random() * (max - min + 1)) + min; +}; + +//TODO: RawUpgrades might need to return a LastAdded +const awakeningRewards = [ + "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem1", + "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem2", + "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem3", + "/Lotus/Types/StoreItems/AvatarImages/AvatarImageItem4", + "/Lotus/Types/Restoratives/LisetAutoHack", + "/Lotus/Upgrades/Mods/Warframe/AvatarShieldMaxMod" +]; + +export const addStartingGear = async ( + inventory: TInventoryDatabaseDocument, + startingGear: TPartialStartingGear | undefined = undefined +): Promise => { + const { LongGuns, Pistols, Suits, Melee } = startingGear || { + LongGuns: [{ ItemType: "/Lotus/Weapons/Tenno/Rifle/Rifle" }], + Pistols: [{ ItemType: "/Lotus/Weapons/Tenno/Pistol/Pistol" }], + Suits: [{ ItemType: "/Lotus/Powersuits/Excalibur/Excalibur" }], + Melee: [{ ItemType: "/Lotus/Weapons/Tenno/Melee/LongSword/LongSword" }] + }; + + //TODO: properly merge weapon bin changes it is currently static here + const inventoryChanges: IInventoryChanges = {}; + addEquipment(inventory, "LongGuns", LongGuns[0].ItemType, undefined, inventoryChanges); + addEquipment(inventory, "Pistols", Pistols[0].ItemType, undefined, inventoryChanges); + addEquipment(inventory, "Melee", Melee[0].ItemType, undefined, inventoryChanges); + await addPowerSuit(inventory, Suits[0].ItemType, inventoryChanges); + addEquipment( + inventory, + "DataKnives", + "/Lotus/Weapons/Tenno/HackingDevices/TnHackingDevice/TnHackingDeviceWeapon", + undefined, + inventoryChanges, + { XP: 450_000 } + ); + addEquipment( + inventory, + "Scoops", + "/Lotus/Weapons/Tenno/Speedball/SpeedballWeaponTest", + undefined, + inventoryChanges + ); + + updateSlots(inventory, InventorySlot.SUITS, 0, 1); + updateSlots(inventory, InventorySlot.WEAPONS, 0, 3); + inventoryChanges.SuitBin = { count: 1, platinum: 0, Slots: -1 }; + inventoryChanges.WeaponBin = { count: 3, platinum: 0, Slots: -3 }; + + await addItem(inventory, "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"); + inventory.ActiveQuest = "/Lotus/Types/Keys/VorsPrize/VorsPrizeQuestKeyChain"; + + inventory.PremiumCredits = 50; + inventory.PremiumCreditsFree = 50; + inventoryChanges.PremiumCredits = 50; + inventoryChanges.PremiumCreditsFree = 50; + inventory.RegularCredits = 3000; + inventoryChanges.RegularCredits = 3000; + + for (const item of awakeningRewards) { + const inventoryDelta = await addItem(inventory, item); + combineInventoryChanges(inventoryChanges, inventoryDelta); + } + + inventory.PlayedParkourTutorial = true; + inventory.ReceivedStartingGear = true; + + return inventoryChanges; +}; + /** * Combines two inventory changes objects into one. * @@ -1302,7 +1370,7 @@ export const addBooster = (ItemType: string, time: number, inventory: TInventory }; export const updateSyndicate = ( - inventory: HydratedDocument, + inventory: TInventoryDatabaseDocument, syndicateUpdate: IMissionInventoryUpdateRequest["AffiliationChanges"] ): void => { syndicateUpdate?.forEach(affiliation => { diff --git a/src/services/itemDataService.ts b/src/services/itemDataService.ts index 2fc6c0da..f6feae12 100644 --- a/src/services/itemDataService.ts +++ b/src/services/itemDataService.ts @@ -1,4 +1,4 @@ -import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; +import { IKeyChainRequest } from "@/src/types/requestTypes"; import { getIndexAfter } from "@/src/helpers/stringHelpers"; import { ITypeCount } from "@/src/types/inventoryTypes/inventoryTypes"; import { logger } from "@/src/utils/logger"; diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index 70ca6eea..60fda2d4 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -10,7 +10,7 @@ import { import { IMissionInventoryUpdateRequest, IRewardInfo } from "../types/requestTypes"; import { logger } from "@/src/utils/logger"; import { IRngResult, getRandomElement, getRandomReward } from "@/src/services/rngService"; -import { equipmentKeys, IInventoryDatabase, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes"; +import { equipmentKeys, TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes"; import { addBooster, addChallenges, @@ -32,10 +32,10 @@ import { updateSyndicate } from "@/src/services/inventoryService"; import { updateQuestKey } from "@/src/services/questService"; -import { HydratedDocument, Types } from "mongoose"; +import { Types } from "mongoose"; import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService"; -import { InventoryDocumentProps, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; +import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { getEntriesUnsafe } from "@/src/utils/ts-utils"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { handleStoreItemAcquisition } from "./purchaseService"; @@ -73,7 +73,7 @@ const getRandomRewardByChance = (pool: IReward[]): IRngResult | undefined => { //const knownUnhandledKeys: readonly string[] = ["test"] as const; // for unimplemented but important keys export const addMissionInventoryUpdates = async ( - inventory: HydratedDocument, + inventory: TInventoryDatabaseDocument, inventoryUpdates: IMissionInventoryUpdateRequest ): Promise => { const inventoryChanges: IInventoryChanges = {}; @@ -661,7 +661,7 @@ interface IMissionCredits { //creditBonus is not entirely accurate. //TODO: consider ActiveBoosters export const addCredits = ( - inventory: HydratedDocument, + inventory: TInventoryDatabaseDocument, { missionDropCredits, missionCompletionCredits, diff --git a/src/services/questService.ts b/src/services/questService.ts index 7b82b304..6319a724 100644 --- a/src/services/questService.ts +++ b/src/services/questService.ts @@ -1,4 +1,4 @@ -import { IKeyChainRequest } from "@/src/controllers/api/giveKeyChainTriggeredItemsController"; +import { IKeyChainRequest } from "@/src/types/requestTypes"; import { isEmptyObject } from "@/src/helpers/general"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { createMessage } from "@/src/services/inboxService"; @@ -9,14 +9,9 @@ import { getLevelKeyRewards, getQuestCompletionItems } from "@/src/services/itemDataService"; -import { - IInventoryDatabase, - IQuestKeyClient, - IQuestKeyDatabase, - IQuestStage -} from "@/src/types/inventoryTypes/inventoryTypes"; +import { IQuestKeyClient, IQuestKeyDatabase, IQuestStage } from "@/src/types/inventoryTypes/inventoryTypes"; import { logger } from "@/src/utils/logger"; -import { HydratedDocument, Types } from "mongoose"; +import { Types } from "mongoose"; import { ExportKeys } from "warframe-public-export-plus"; import { addFixedLevelRewards } from "./missionInventoryUpdateService"; import { IInventoryChanges } from "../types/purchaseTypes"; @@ -31,7 +26,7 @@ export interface IUpdateQuestRequest { } export const updateQuestKey = async ( - inventory: HydratedDocument, + inventory: TInventoryDatabaseDocument, questKeyUpdate: IUpdateQuestRequest["QuestKeys"] ): Promise => { if (questKeyUpdate.length > 1) { @@ -45,7 +40,7 @@ export const updateQuestKey = async ( throw new Error(`quest key ${questKeyUpdate[0].ItemType} not found`); } - inventory.QuestKeys[questKeyIndex] = questKeyUpdate[0]; + inventory.QuestKeys[questKeyIndex].overwrite(questKeyUpdate[0]); let inventoryChanges: IInventoryChanges = {}; if (questKeyUpdate[0].Completed) { @@ -57,12 +52,12 @@ export const updateQuestKey = async ( logger.debug(`quest completion items`, questCompletionItems); if (questCompletionItems) { - inventoryChanges = await addItems(inventory as TInventoryDatabaseDocument, questCompletionItems); + inventoryChanges = await addItems(inventory, questCompletionItems); } inventory.ActiveQuest = ""; if (questKeyUpdate[0].ItemType == "/Lotus/Types/Keys/NewWarQuest/NewWarQuestKeyChain") { - setupKahlSyndicate(inventory as TInventoryDatabaseDocument); + setupKahlSyndicate(inventory); } } return inventoryChanges; diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 4eee21b6..c25b7123 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -1168,3 +1168,5 @@ export interface ILockedWeaponGroupDatabase { m?: Types.ObjectId; sn?: Types.ObjectId; } + +export type TPartialStartingGear = Pick; diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index 7c14f696..d9139ec7 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -21,6 +21,7 @@ import { ILockedWeaponGroupClient, ILoadOutPresets } from "./inventoryTypes/inventoryTypes"; +import { IGroup } from "./loginTypes"; export interface IAffiliationChange { Tag: string; @@ -175,3 +176,9 @@ export interface IVoidTearParticipantInfo { RewardProjection: string; HardModeReward: ITypeCount; } + +export interface IKeyChainRequest { + KeyChain: string; + ChainStage: number; + Groups?: IGroup[]; +}