From a5c45bb646ab6118acd2f8f29d3986e73fb978ea Mon Sep 17 00:00:00 2001 From: Sainan Date: Wed, 26 Feb 2025 15:41:07 -0800 Subject: [PATCH] fix: consume a slot when item is crafted instead of bought via plat (#1029) Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1029 --- .../api/claimCompletedRecipeController.ts | 2 +- src/controllers/api/guildTechController.ts | 6 +- src/controllers/custom/addItemsController.ts | 2 +- src/services/inventoryService.ts | 93 ++++++++++--------- src/services/purchaseService.ts | 24 +++-- src/types/purchaseTypes.ts | 64 ++++++++----- 6 files changed, 103 insertions(+), 88 deletions(-) diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index ec9087bab..d8208f7f2 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -91,7 +91,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) = } InventoryChanges = { ...InventoryChanges, - ...(await addItem(inventory, recipe.resultType, recipe.num)).InventoryChanges + ...(await addItem(inventory, recipe.resultType, recipe.num, false)).InventoryChanges }; await inventory.save(); res.json({ InventoryChanges }); diff --git a/src/controllers/api/guildTechController.ts b/src/controllers/api/guildTechController.ts index 340226993..b9d3b75d3 100644 --- a/src/controllers/api/guildTechController.ts +++ b/src/controllers/api/guildTechController.ts @@ -54,10 +54,8 @@ export const guildTechController: RequestHandler = async (req, res) => { } } addMiscItems(inventory, miscItemChanges); - const inventoryChanges: IInventoryChanges = { - ...updateCurrency(inventory, contributions.RegularCredits, false), - MiscItems: miscItemChanges - }; + const inventoryChanges: IInventoryChanges = updateCurrency(inventory, contributions.RegularCredits, false); + inventoryChanges.MiscItems = miscItemChanges; if (techProject.ReqCredits == 0 && !techProject.ReqItems.find(x => x.ItemCount > 0)) { // This research is now fully funded. diff --git a/src/controllers/custom/addItemsController.ts b/src/controllers/custom/addItemsController.ts index 1eb50ed64..15837602c 100644 --- a/src/controllers/custom/addItemsController.ts +++ b/src/controllers/custom/addItemsController.ts @@ -7,7 +7,7 @@ export const addItemsController: RequestHandler = async (req, res) => { const requests = req.body as IAddItemRequest[]; const inventory = await getInventory(accountId); for (const request of requests) { - await addItem(inventory, request.ItemType, request.ItemCount); + await addItem(inventory, request.ItemType, request.ItemCount, true); } await inventory.save(); res.end(); diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index a2071b2c0..f4e48ca84 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -5,7 +5,7 @@ import { } from "@/src/models/inventoryModels/inventoryModel"; import { config } from "@/src/services/configService"; import { HydratedDocument, Types } from "mongoose"; -import { SlotNames, IInventoryChanges, IBinChanges, ICurrencyChanges } from "@/src/types/purchaseTypes"; +import { SlotNames, IInventoryChanges, IBinChanges, slotNames } from "@/src/types/purchaseTypes"; import { IChallengeProgress, IConsumable, @@ -126,13 +126,17 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del for (const item of right) { left.push(item); } - } else if (typeof delta[key] == "object") { - console.assert(key.substring(-3) == "Bin"); - console.assert(key != "InfestedFoundry"); - const left = InventoryChanges[key] as IBinChanges; - const right = delta[key] as IBinChanges; - left.count += right.count; - left.platinum += right.platinum; + } else if (slotNames.indexOf(key as SlotNames) != -1) { + const left = InventoryChanges[key as SlotNames]!; + const right = delta[key as SlotNames]!; + if (right.count) { + left.count ??= 0; + left.count += right.count; + } + if (right.platinum) { + left.platinum ??= 0; + left.platinum += right.platinum; + } left.Slots += right.Slots; if (right.Extra) { left.Extra ??= 0; @@ -159,10 +163,32 @@ export const getInventory = async ( return inventory; }; +const occupySlot = ( + inventory: TInventoryDatabaseDocument, + bin: InventorySlot, + premiumPurchase: boolean +): IInventoryChanges => { + const slotChanges = { + Slots: 0, + Extra: 0 + }; + if (premiumPurchase) { + slotChanges.Extra += 1; + } else { + // { count: 1, platinum: 0, Slots: -1 } + slotChanges.Slots -= 1; + } + updateSlots(inventory, bin, slotChanges.Slots, slotChanges.Extra); + const inventoryChanges: IInventoryChanges = {}; + inventoryChanges[bin] = slotChanges satisfies IBinChanges; + return inventoryChanges; +}; + export const addItem = async ( inventory: TInventoryDatabaseDocument, typeName: string, - quantity: number = 1 + quantity: number = 1, + premiumPurchase: boolean = false ): Promise<{ InventoryChanges: IInventoryChanges }> => { // Bundles are technically StoreItems but a) they don't have a normal counterpart, and b) they are used in non-StoreItem contexts, e.g. email attachments. if (typeName in ExportBundles) { @@ -302,14 +328,13 @@ export const addItem = async ( const inventoryChanges = addEquipment(inventory, weapon.productCategory, typeName); if (weapon.additionalItems) { for (const item of weapon.additionalItems) { - combineInventoryChanges(inventoryChanges, await addItem(inventory, item, 1)); + combineInventoryChanges(inventoryChanges, (await addItem(inventory, item, 1)).InventoryChanges); } } - updateSlots(inventory, InventorySlot.WEAPONS, 0, 1); return { InventoryChanges: { ...inventoryChanges, - WeaponBin: { count: 1, platinum: 0, Slots: -1 } + ...occupySlot(inventory, InventorySlot.WEAPONS, premiumPurchase) } }; } else { @@ -378,44 +403,26 @@ export const addItem = async ( case "Powersuits": switch (typeName.substr(1).split("/")[2]) { default: { - const inventoryChanges = addPowerSuit(inventory, typeName); - updateSlots(inventory, InventorySlot.SUITS, 0, 1); return { InventoryChanges: { - ...inventoryChanges, - SuitBin: { - count: 1, - platinum: 0, - Slots: -1 - } + ...addPowerSuit(inventory, typeName), + ...occupySlot(inventory, InventorySlot.SUITS, premiumPurchase) } }; } case "Archwing": { - const inventoryChanges = addSpaceSuit(inventory, typeName); - updateSlots(inventory, InventorySlot.SPACESUITS, 0, 1); return { InventoryChanges: { - ...inventoryChanges, - SpaceSuitBin: { - count: 1, - platinum: 0, - Slots: -1 - } + ...addSpaceSuit(inventory, typeName), + ...occupySlot(inventory, InventorySlot.SPACESUITS, premiumPurchase) } }; } case "EntratiMech": { - const inventoryChanges = addMechSuit(inventory, typeName); - updateSlots(inventory, InventorySlot.MECHSUITS, 0, 1); return { InventoryChanges: { - ...inventoryChanges, - MechBin: { - count: 1, - platinum: 0, - Slots: -1 - } + ...addMechSuit(inventory, typeName), + ...occupySlot(inventory, InventorySlot.MECHSUITS, premiumPurchase) } }; } @@ -446,12 +453,10 @@ export const addItem = async ( case "Types": switch (typeName.substr(1).split("/")[2]) { case "Sentinels": { - const inventoryChanges = addSentinel(inventory, typeName); - updateSlots(inventory, InventorySlot.SENTINELS, 0, 1); return { InventoryChanges: { - ...inventoryChanges, - SentinelBin: { count: 1, platinum: 0, Slots: -1 } + ...addSentinel(inventory, typeName), + ...occupySlot(inventory, InventorySlot.SENTINELS, premiumPurchase) } }; } @@ -531,9 +536,9 @@ export const addItems = async ( let inventoryDelta; for (const item of items) { if (typeof item === "string") { - inventoryDelta = await addItem(inventory, item); + inventoryDelta = await addItem(inventory, item, 1, true); } else { - inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount); + inventoryDelta = await addItem(inventory, item.ItemType, item.ItemCount, true); } combineInventoryChanges(inventoryChanges, inventoryDelta.InventoryChanges); } @@ -682,8 +687,8 @@ export const updateCurrency = ( inventory: TInventoryDatabaseDocument, price: number, usePremium: boolean -): ICurrencyChanges => { - const currencyChanges: ICurrencyChanges = {}; +): IInventoryChanges => { + const currencyChanges: IInventoryChanges = {}; if (price != 0 && isCurrencyTracked(usePremium)) { if (usePremium) { if (inventory.PremiumCreditsFree > 0) { diff --git a/src/services/purchaseService.ts b/src/services/purchaseService.ts index 76a6d1113..2d1c66d3e 100644 --- a/src/services/purchaseService.ts +++ b/src/services/purchaseService.ts @@ -164,7 +164,7 @@ export const handlePurchase = async ( addMiscItems(inventory, [invItem]); purchaseResponse.InventoryChanges.MiscItems ??= []; - (purchaseResponse.InventoryChanges.MiscItems as IMiscItem[]).push(invItem); + purchaseResponse.InventoryChanges.MiscItems.push(invItem); } else if (!config.infiniteRegalAya) { inventory.PrimeTokens -= offer.PrimePrice! * purchaseRequest.PurchaseParams.Quantity; } @@ -191,11 +191,11 @@ const handleItemPrices = ( addMiscItems(inventory, [invItem]); inventoryChanges.MiscItems ??= []; - const change = (inventoryChanges.MiscItems as IMiscItem[]).find(x => x.ItemType == item.ItemType); + const change = inventoryChanges.MiscItems.find(x => x.ItemType == item.ItemType); if (change) { change.ItemCount += invItem.ItemCount; } else { - (inventoryChanges.MiscItems as IMiscItem[]).push(invItem); + inventoryChanges.MiscItems.push(invItem); } } }; @@ -251,7 +251,7 @@ export const handleStoreItemAcquisition = async ( } switch (storeCategory) { default: { - purchaseResponse = await addItem(inventory, internalName, quantity); + purchaseResponse = await addItem(inventory, internalName, quantity, true); break; } case "Types": @@ -300,16 +300,14 @@ const handleSlotPurchase = ( logger.debug(`added ${slotsPurchased} slot ${slotName}`); - return { - InventoryChanges: { - [slotName]: { - count: 0, - platinum: 1, - Slots: slotsPurchased, - Extra: slotsPurchased - } - } + const inventoryChanges: IInventoryChanges = {}; + inventoryChanges[slotName] = { + count: 0, + platinum: 1, + Slots: slotsPurchased, + Extra: slotsPurchased }; + return { InventoryChanges: inventoryChanges }; }; const handleBoosterPackPurchase = async ( diff --git a/src/types/purchaseTypes.ts b/src/types/purchaseTypes.ts index f1e864be7..e921d1367 100644 --- a/src/types/purchaseTypes.ts +++ b/src/types/purchaseTypes.ts @@ -1,5 +1,5 @@ import { IEquipmentClient } from "./inventoryTypes/commonInventoryTypes"; -import { IDroneClient, IInfestedFoundryClient, TEquipmentKey } from "./inventoryTypes/inventoryTypes"; +import { IDroneClient, IInfestedFoundryClient, IMiscItem, TEquipmentKey } from "./inventoryTypes/inventoryTypes"; export interface IPurchaseRequest { PurchaseParams: IPurchaseParams; @@ -22,20 +22,31 @@ export interface IPurchaseParams { IsWeekly?: boolean; // for Source 7 } -export interface ICurrencyChanges { - RegularCredits?: number; - PremiumCredits?: number; - PremiumCreditsFree?: number; -} - export type IInventoryChanges = { [_ in SlotNames]?: IBinChanges; } & { [_ in TEquipmentKey]?: IEquipmentClient[]; -} & ICurrencyChanges & { - InfestedFoundry?: IInfestedFoundryClient; - Drones?: IDroneClient[]; - } & Record; +} & { + RegularCredits?: number; + PremiumCredits?: number; + PremiumCreditsFree?: number; + InfestedFoundry?: IInfestedFoundryClient; + Drones?: IDroneClient[]; + MiscItems?: IMiscItem[]; +} & Record< + Exclude< + string, + | SlotNames + | TEquipmentKey + | "RegularCredits" + | "PremiumCredits" + | "PremiumCreditsFree" + | "InfestedFoundry" + | "Drones" + | "MiscItems" + >, + number | object[] + >; export interface IAffiliationMods { Tag: string; @@ -51,8 +62,8 @@ export interface IPurchaseResponse { } export type IBinChanges = { - count: number; - platinum: number; + count?: number; + platinum?: number; Slots: number; Extra?: number; }; @@ -69,18 +80,21 @@ export type SlotPurchaseName = | "TwoCrewShipSalvageSlotItem" | "CrewMemberSlotItem"; -export type SlotNames = - | "SuitBin" - | "WeaponBin" - | "MechBin" - | "PveBonusLoadoutBin" - | "SentinelBin" - | "SpaceSuitBin" - | "SpaceWeaponBin" - | "OperatorAmpBin" - | "RandomModBin" - | "CrewShipSalvageBin" - | "CrewMemberBin"; +export const slotNames = [ + "SuitBin", + "WeaponBin", + "MechBin", + "PveBonusLoadoutBin", + "SentinelBin", + "SpaceSuitBin", + "SpaceWeaponBin", + "OperatorAmpBin", + "RandomModBin", + "CrewShipSalvageBin", + "CrewMemberBin" +] as const; + +export type SlotNames = (typeof slotNames)[number]; export type SlotPurchase = { [P in SlotPurchaseName]: { name: SlotNames; slotsPerPurchase: number };