From d8845bc4786bfadad9f29ff771b936ef80f96ae9 Mon Sep 17 00:00:00 2001 From: Sainan Date: Fri, 17 Jan 2025 05:27:12 +0100 Subject: [PATCH] feat: apply & track daily standing limit when trading in medallions (#788) --- .../api/syndicateStandingBonusController.ts | 24 ++++++----- src/services/inventoryService.ts | 43 ++++++++++++++++++- src/types/inventoryTypes/inventoryTypes.ts | 34 ++++++++------- 3 files changed, 74 insertions(+), 27 deletions(-) diff --git a/src/controllers/api/syndicateStandingBonusController.ts b/src/controllers/api/syndicateStandingBonusController.ts index 4513f25f1..5fafd89d2 100644 --- a/src/controllers/api/syndicateStandingBonusController.ts +++ b/src/controllers/api/syndicateStandingBonusController.ts @@ -1,19 +1,19 @@ import { RequestHandler } from "express"; import { getAccountIdForRequest } from "@/src/services/loginService"; -import { addMiscItems, getInventory } from "@/src/services/inventoryService"; +import { addMiscItems, getInventory, getStandingLimit, updateStandingLimit } from "@/src/services/inventoryService"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { IOid } from "@/src/types/commonTypes"; -import { ExportSyndicates } from "warframe-public-export-plus"; +import { ExportSyndicates, ISyndicate } from "warframe-public-export-plus"; export const syndicateStandingBonusController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); const request = JSON.parse(String(req.body)) as ISyndicateStandingBonusRequest; + const syndicateMeta = ExportSyndicates[request.Operation.AffiliationTag]; + let gainedStanding = 0; request.Operation.Items.forEach(item => { - const medallion = (ExportSyndicates[request.Operation.AffiliationTag].medallions ?? []).find( - medallion => medallion.itemType == item.ItemType - ); + const medallion = (syndicateMeta.medallions ?? []).find(medallion => medallion.itemType == item.ItemType); if (medallion) { gainedStanding += medallion.standing * item.ItemCount; } @@ -27,17 +27,22 @@ export const syndicateStandingBonusController: RequestHandler = async (req, res) let syndicate = inventory.Affiliations.find(x => x.Tag == request.Operation.AffiliationTag); if (!syndicate) { syndicate = - inventory.Affiliations[inventory.Affiliations.push({ Tag: request.Operation.AffiliationTag, Standing: 0 })]; + inventory.Affiliations[ + inventory.Affiliations.push({ Tag: request.Operation.AffiliationTag, Standing: 0 }) - 1 + ]; } - const max = getMaxStanding(request.Operation.AffiliationTag, syndicate.Title ?? 0); + const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0); if (syndicate.Standing + gainedStanding > max) { gainedStanding = max - syndicate.Standing; } + if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) { + gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin); + } syndicate.Standing += gainedStanding; - // TODO: Subtract from daily limit bin; maybe also a cheat to skip that. + updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding); await inventory.save(); @@ -63,8 +68,7 @@ interface ISyndicateStandingBonusRequest { ModularWeaponId: IOid; // Seems to just be "000000000000000000000000", also note there's a "Category" query field } -const getMaxStanding = (affiliationTag: string, title: number): number => { - const syndicate = ExportSyndicates[affiliationTag]; +const getMaxStanding = (syndicate: ISyndicate, title: number): number => { if (!syndicate.titles) { // LibrarySyndicate return 125000; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 28c6cbbad..754473c90 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -16,7 +16,8 @@ import { IWeaponSkinClient, TEquipmentKey, equipmentKeys, - IFusionTreasure + IFusionTreasure, + IDailyAffiliations } from "@/src/types/inventoryTypes/inventoryTypes"; import { IGenericUpdate } from "../types/genericUpdate"; import { @@ -36,7 +37,8 @@ import { ExportRecipes, ExportResources, ExportSentinels, - ExportUpgrades + ExportUpgrades, + TStandingLimitBin } from "warframe-public-export-plus"; import { createShip } from "./shipService"; @@ -492,6 +494,43 @@ export const updateCurrencyByAccountId = async ( return currencyChanges; }; +const standingLimitBinToInventoryKey: Record< + Exclude, + keyof IDailyAffiliations +> = { + STANDING_LIMIT_BIN_NORMAL: "DailyAffiliation", + STANDING_LIMIT_BIN_PVP: "DailyAffiliationPvp", + STANDING_LIMIT_BIN_LIBRARY: "DailyAffiliationLibrary", + STANDING_LIMIT_BIN_CETUS: "DailyAffiliationCetus", + STANDING_LIMIT_BIN_QUILLS: "DailyAffiliationQuills", + STANDING_LIMIT_BIN_SOLARIS: "DailyAffiliationSolaris", + STANDING_LIMIT_BIN_VENTKIDS: "DailyAffiliationVentkids", + STANDING_LIMIT_BIN_VOX: "DailyAffiliationVox", + STANDING_LIMIT_BIN_ENTRATI: "DailyAffiliationEntrati", + STANDING_LIMIT_BIN_NECRALOID: "DailyAffiliationNecraloid", + STANDING_LIMIT_BIN_ZARIMAN: "DailyAffiliationZariman", + STANDING_LIMIT_BIN_KAHL: "DailyAffiliationKahl", + STANDING_LIMIT_BIN_CAVIA: "DailyAffiliationCavia", + STANDING_LIMIT_BIN_HEX: "DailyAffiliationHex" +}; + +export const getStandingLimit = (inventory: IDailyAffiliations, bin: TStandingLimitBin): number => { + if (bin == "STANDING_LIMIT_BIN_NONE") { + return Number.MAX_SAFE_INTEGER; + } + return inventory[standingLimitBinToInventoryKey[bin]]; +}; + +export const updateStandingLimit = ( + inventory: IDailyAffiliations, + bin: TStandingLimitBin, + subtrahend: number +): void => { + if (bin != "STANDING_LIMIT_BIN_NONE") { + inventory[standingLimitBinToInventoryKey[bin]] -= subtrahend; + } +}; + // TODO: AffiliationMods support (Nightwave). export const updateGeneric = async (data: IGenericUpdate, accountId: string): Promise => { const inventory = await getInventory(accountId); diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 6463a1560..de7894191 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -110,7 +110,25 @@ export type TSolarMapRegion = export interface IPendingRecipeResponse extends Omit { CompletionDate: IMongoDate; } -export interface IInventoryResponse { + +export interface IDailyAffiliations { + DailyAffiliation: number; + DailyAffiliationPvp: number; + DailyAffiliationLibrary: number; + DailyAffiliationCetus: number; + DailyAffiliationQuills: number; + DailyAffiliationSolaris: number; + DailyAffiliationVentkids: number; + DailyAffiliationVox: number; + DailyAffiliationEntrati: number; + DailyAffiliationNecraloid: number; + DailyAffiliationZariman: number; + DailyAffiliationKahl: number; + DailyAffiliationCavia: number; + DailyAffiliationHex: number; +} + +export interface IInventoryResponse extends IDailyAffiliations { Horses: IEquipmentDatabase[]; DrifterMelee: IEquipmentDatabase[]; DrifterGuns: IEquipmentDatabase[]; @@ -138,9 +156,6 @@ export interface IInventoryResponse { OperatorAmpBin: ISlots; CrewShipSalvageBin: ISlots; TradesRemaining: number; - DailyAffiliation: number; - DailyAffiliationPvp: number; - DailyAffiliationLibrary: number; DailyFocus: number; GiftsRemaining: number; HandlerPoints: number; @@ -220,8 +235,6 @@ export interface IInventoryResponse { ActiveAvatarImageType: string; KubrowPets: IEquipmentDatabase[]; ShipDecorations: IConsumable[]; - DailyAffiliationCetus: number; - DailyAffiliationQuills: number; DiscoveredMarkers: IDiscoveredMarker[]; CompletedJobs: ICompletedJob[]; FocusAbility: string; @@ -232,7 +245,6 @@ export interface IInventoryResponse { KubrowPetPrints: IKubrowPetPrint[]; AlignmentReplay: IAlignment; PersonalGoalProgress: IPersonalGoalProgress[]; - DailyAffiliationSolaris: number; SpecialItems: IEquipmentDatabase[]; ThemeStyle: string; ThemeBackground: string; @@ -241,8 +253,6 @@ export interface IInventoryResponse { ChallengeInstanceStates: IChallengeInstanceState[]; LoginMilestoneRewards: string[]; OperatorLoadOuts: IOperatorConfigClient[]; - DailyAffiliationVentkids: number; - DailyAffiliationVox: number; RecentVendorPurchases: Array; Hoverboards: IEquipmentDatabase[]; NodeIntrosCompleted: string[]; @@ -268,8 +278,6 @@ export interface IInventoryResponse { TradeBannedUntil?: IMongoDate; PlayedParkourTutorial: boolean; SubscribedToEmailsPersonalized: number; - DailyAffiliationEntrati: number; - DailyAffiliationNecraloid: number; MechSuits: IEquipmentDatabase[]; InfestedFoundry?: IInfestedFoundry; BlessingCooldown: IMongoDate; @@ -279,11 +287,7 @@ export interface IInventoryResponse { AdultOperatorLoadOuts: IOperatorConfigClient[]; LotusCustomization: ILotusCustomization; UseAdultOperatorLoadout?: boolean; - DailyAffiliationZariman: number; NemesisAbandonedRewards: string[]; - DailyAffiliationKahl: number; - DailyAffiliationCavia: number; - DailyAffiliationHex: number; LastInventorySync: IOid; NextRefill: IMongoDate; // Next time argon crystals will have a decay tick FoundToday?: IMiscItem[]; // for Argon Crystals