From 06bc0123baee78b6d339bea8da8c734132b49620 Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 5 Jan 2025 07:16:48 +0100 Subject: [PATCH] feat: all server-side metamorphosis levels (#716) --- .../api/infestedFoundryController.ts | 85 ++++++++++++++++--- src/controllers/api/upgradesController.ts | 15 +++- src/services/inventoryService.ts | 3 +- src/types/purchaseTypes.ts | 8 +- 4 files changed, 95 insertions(+), 16 deletions(-) diff --git a/src/controllers/api/infestedFoundryController.ts b/src/controllers/api/infestedFoundryController.ts index bcb741cd..4fa5f694 100644 --- a/src/controllers/api/infestedFoundryController.ts +++ b/src/controllers/api/infestedFoundryController.ts @@ -71,14 +71,14 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { const snack = ExportMisc.helminthSnacks[contribution.ItemType]; // Note: Currently ignoring loss of apetite - totalPercentagePointsGained += snack.gain / 0.01; + totalPercentagePointsGained += snack.gain * 100; // 30% would be gain=0.3, so percentage points is equal to gain * 100. const resource = inventory.InfestedFoundry.Resources.find(x => x.ItemType == snack.type); if (resource) { resource.Count += Math.trunc(snack.gain * 1000); } else { inventory.InfestedFoundry.Resources.push({ ItemType: snack.type, - Count: Math.trunc(snack.gain * 1000) + Count: Math.trunc(snack.gain * 1000) // 30% would be gain=0.3 or Count=300, so Count=gain*1000. }); } @@ -91,19 +91,21 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { } } - addInfestedFoundryXP(inventory.InfestedFoundry, 666 * totalPercentagePointsGained); + const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry, 666 * totalPercentagePointsGained); + addRecipes(inventory, recipeChanges); addMiscItems(inventory, miscItemChanges); await inventory.save(); res.json({ InventoryChanges: { + Recipes: recipeChanges, InfestedFoundry: { XP: inventory.InfestedFoundry.XP, Resources: inventory.InfestedFoundry.Resources, Slots: inventory.InfestedFoundry.Slots - } - }, - MiscItems: miscItemChanges + }, + MiscItems: miscItemChanges + } }); break; } @@ -144,18 +146,21 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { if (suit.Configs && suit.Configs[0] && suit.Configs[0].pricol) { consumedSuit.c = suit.Configs[0].pricol; } - inventory.InfestedFoundry!.Slots!--; + if ((inventory.InfestedFoundry!.XP ?? 0) < 73125_00) { + inventory.InfestedFoundry!.Slots!--; + } inventory.InfestedFoundry!.ConsumedSuits ??= []; inventory.InfestedFoundry!.ConsumedSuits?.push(consumedSuit); inventory.InfestedFoundry!.LastConsumedSuit = suit; inventory.InfestedFoundry!.AbilityOverrideUnlockCooldown = new Date( new Date().getTime() + 24 * 60 * 60 * 1000 ); - addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00); + const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 1600_00); + addRecipes(inventory, recipeChanges); await inventory.save(); - console.log(inventory.toJSON().InfestedFoundry); res.json({ InventoryChanges: { + Recipes: recipeChanges, RemovedIdItems: [ { ItemId: request.SuitId @@ -196,7 +201,8 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { suit.OffensiveUpgrade = request.OffensiveUpgradeType; suit.DefensiveUpgrade = request.DefensiveUpgradeType; suit.UpgradesExpiry = upgradesExpiry; - addInfestedFoundryXP(inventory.InfestedFoundry!, 4800_00); + const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, 4800_00); + addRecipes(inventory, recipeChanges); for (let i = 0; i != request.ResourceTypes.length; ++i) { inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == request.ResourceTypes[i])!.Count -= request.ResourceCosts[i]; @@ -210,6 +216,7 @@ export const infestedFoundryController: RequestHandler = async (req, res) => { DefensiveUpgrade: request.DefensiveUpgradeType, UpgradesExpiry: toMongoDate(upgradesExpiry), InventoryChanges: { + Recipes: recipeChanges, InfestedFoundry: inventory.toJSON().InfestedFoundry } }); @@ -254,7 +261,8 @@ const colorToShard: Record = { ACC_PURPLE_MYTHIC: "/Lotus/Types/Gameplay/NarmerSorties/ArchonCrystalVioletMythic" }; -const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): void => { +export const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): ITypeCount[] => { + const recipeChanges: ITypeCount[] = []; infestedFoundry.XP ??= 0; const prevXP = infestedFoundry.XP; infestedFoundry.XP += delta; @@ -262,14 +270,69 @@ const addInfestedFoundryXP = (infestedFoundry: IInfestedFoundry, delta: number): 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 { diff --git a/src/controllers/api/upgradesController.ts b/src/controllers/api/upgradesController.ts index e21d54d0..eea06d2e 100644 --- a/src/controllers/api/upgradesController.ts +++ b/src/controllers/api/upgradesController.ts @@ -8,13 +8,16 @@ import { } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { getAccountIdForRequest } from "@/src/services/loginService"; -import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService"; +import { addMiscItems, addRecipes, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { getRecipeByResult } from "@/src/services/itemDataService"; +import { IInventoryChanges } from "@/src/types/purchaseTypes"; +import { addInfestedFoundryXP } from "./infestedFoundryController"; export const upgradesController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); const payload = JSON.parse(String(req.body)) as IUpgradesRequest; const inventory = await getInventory(accountId); + const inventoryChanges: IInventoryChanges = {}; for (const operation of payload.Operations) { if ( operation.UpgradeRequirement == "/Lotus/Types/Items/MiscItems/ModSlotUnlocker" || @@ -35,6 +38,7 @@ export const upgradesController: RequestHandler = async (req, res) => { const suit = inventory.Suits.find(x => x._id.toString() == payload.ItemId.$oid)!; let newAbilityOverride: IAbilityOverride | undefined; + let totalPercentagePointsConsumed = 0; if (operation.UpgradeRequirement != "") { newAbilityOverride = { Ability: operation.UpgradeRequirement, @@ -43,6 +47,7 @@ export const upgradesController: RequestHandler = async (req, res) => { const recipe = getRecipeByResult(operation.UpgradeRequirement)!; for (const ingredient of recipe.ingredients) { + totalPercentagePointsConsumed += ingredient.ItemCount / 10; inventory.InfestedFoundry!.Resources!.find(x => x.ItemType == ingredient.ItemType)!.Count -= ingredient.ItemCount; } @@ -52,6 +57,12 @@ export const upgradesController: RequestHandler = async (req, res) => { suit.Configs[entry.Slot] ??= {}; suit.Configs[entry.Slot].AbilityOverride = newAbilityOverride; } + + const recipeChanges = addInfestedFoundryXP(inventory.InfestedFoundry!, totalPercentagePointsConsumed * 8); + addRecipes(inventory, recipeChanges); + + inventoryChanges.Recipes = recipeChanges; + inventoryChanges.InfestedFoundry = inventory.toJSON().InfestedFoundry; } else switch (operation.UpgradeRequirement) { case "/Lotus/Types/Items/MiscItems/OrokinReactor": @@ -146,7 +157,7 @@ export const upgradesController: RequestHandler = async (req, res) => { } } await inventory.save(); - res.json({ InventoryChanges: {} }); + res.json({ InventoryChanges: inventoryChanges }); }; const setSlotPolarity = (item: IEquipmentDatabase, slot: number, polarity: ArtifactPolarity): void => { diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index ad33b565..652fcaf3 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -79,8 +79,9 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del } } else if (typeof delta[key] == "object") { console.assert(key.substring(-3) == "Bin"); + console.assert(key != "InfestedFoundry"); const left = InventoryChanges[key] as IBinChanges; - const right: IBinChanges = delta[key]; + const right = delta[key] as IBinChanges; left.count += right.count; left.platinum += right.platinum; left.Slots += right.Slots; diff --git a/src/types/purchaseTypes.ts b/src/types/purchaseTypes.ts index f2b1ff9b..19e8ff33 100644 --- a/src/types/purchaseTypes.ts +++ b/src/types/purchaseTypes.ts @@ -1,3 +1,5 @@ +import { IInfestedFoundry } from "./inventoryTypes/inventoryTypes"; + export interface IPurchaseRequest { PurchaseParams: IPurchaseParams; buildLabel: string; @@ -25,8 +27,10 @@ export interface ICurrencyChanges { export type IInventoryChanges = { [_ in SlotNames]?: IBinChanges; -} & ICurrencyChanges & - Record; +} & ICurrencyChanges & { InfestedFoundry?: IInfestedFoundry } & Record< + string, + IBinChanges | number | object[] | IInfestedFoundry + >; export interface IPurchaseResponse { InventoryChanges: IInventoryChanges;