feat: apply & track daily standing limit when trading in medallions #788

Merged
Sainan merged 4 commits from daily-standing-limit into main 2025-01-16 20:27:12 -08:00
3 changed files with 74 additions and 27 deletions

View File

@ -1,19 +1,19 @@
import { RequestHandler } from "express"; import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService"; 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 { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes";
import { IOid } from "@/src/types/commonTypes"; 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) => { export const syndicateStandingBonusController: RequestHandler = async (req, res) => {
const accountId = await getAccountIdForRequest(req); const accountId = await getAccountIdForRequest(req);
const request = JSON.parse(String(req.body)) as ISyndicateStandingBonusRequest; const request = JSON.parse(String(req.body)) as ISyndicateStandingBonusRequest;
const syndicateMeta = ExportSyndicates[request.Operation.AffiliationTag];
let gainedStanding = 0; let gainedStanding = 0;
request.Operation.Items.forEach(item => { request.Operation.Items.forEach(item => {
const medallion = (ExportSyndicates[request.Operation.AffiliationTag].medallions ?? []).find( const medallion = (syndicateMeta.medallions ?? []).find(medallion => medallion.itemType == item.ItemType);
medallion => medallion.itemType == item.ItemType
);
if (medallion) { if (medallion) {
gainedStanding += medallion.standing * item.ItemCount; 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); let syndicate = inventory.Affiliations.find(x => x.Tag == request.Operation.AffiliationTag);
if (!syndicate) { if (!syndicate) {
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) { if (syndicate.Standing + gainedStanding > max) {
gainedStanding = max - syndicate.Standing; gainedStanding = max - syndicate.Standing;
} }
if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) {
gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin);
}
syndicate.Standing += gainedStanding; syndicate.Standing += gainedStanding;
// TODO: Subtract from daily limit bin; maybe also a cheat to skip that. updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding);
await inventory.save(); await inventory.save();
@ -63,8 +68,7 @@ interface ISyndicateStandingBonusRequest {
ModularWeaponId: IOid; // Seems to just be "000000000000000000000000", also note there's a "Category" query field ModularWeaponId: IOid; // Seems to just be "000000000000000000000000", also note there's a "Category" query field
} }
const getMaxStanding = (affiliationTag: string, title: number): number => { const getMaxStanding = (syndicate: ISyndicate, title: number): number => {
const syndicate = ExportSyndicates[affiliationTag];
if (!syndicate.titles) { if (!syndicate.titles) {
// LibrarySyndicate // LibrarySyndicate
return 125000; return 125000;

View File

@ -16,7 +16,8 @@ import {
IWeaponSkinClient, IWeaponSkinClient,
TEquipmentKey, TEquipmentKey,
equipmentKeys, equipmentKeys,
IFusionTreasure IFusionTreasure,
IDailyAffiliations
} from "@/src/types/inventoryTypes/inventoryTypes"; } from "@/src/types/inventoryTypes/inventoryTypes";
import { IGenericUpdate } from "../types/genericUpdate"; import { IGenericUpdate } from "../types/genericUpdate";
import { import {
@ -36,7 +37,8 @@ import {
ExportRecipes, ExportRecipes,
ExportResources, ExportResources,
ExportSentinels, ExportSentinels,
ExportUpgrades ExportUpgrades,
TStandingLimitBin
} from "warframe-public-export-plus"; } from "warframe-public-export-plus";
import { createShip } from "./shipService"; import { createShip } from "./shipService";
@ -492,6 +494,43 @@ export const updateCurrencyByAccountId = async (
return currencyChanges; return currencyChanges;
}; };
const standingLimitBinToInventoryKey: Record<
Exclude<TStandingLimitBin, "STANDING_LIMIT_BIN_NONE">,
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). // TODO: AffiliationMods support (Nightwave).
export const updateGeneric = async (data: IGenericUpdate, accountId: string): Promise<void> => { export const updateGeneric = async (data: IGenericUpdate, accountId: string): Promise<void> => {
const inventory = await getInventory(accountId); const inventory = await getInventory(accountId);

View File

@ -110,7 +110,25 @@ export type TSolarMapRegion =
export interface IPendingRecipeResponse extends Omit<IPendingRecipe, "CompletionDate"> { export interface IPendingRecipeResponse extends Omit<IPendingRecipe, "CompletionDate"> {
CompletionDate: IMongoDate; 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[]; Horses: IEquipmentDatabase[];
DrifterMelee: IEquipmentDatabase[]; DrifterMelee: IEquipmentDatabase[];
DrifterGuns: IEquipmentDatabase[]; DrifterGuns: IEquipmentDatabase[];
@ -138,9 +156,6 @@ export interface IInventoryResponse {
OperatorAmpBin: ISlots; OperatorAmpBin: ISlots;
CrewShipSalvageBin: ISlots; CrewShipSalvageBin: ISlots;
TradesRemaining: number; TradesRemaining: number;
DailyAffiliation: number;
DailyAffiliationPvp: number;
DailyAffiliationLibrary: number;
DailyFocus: number; DailyFocus: number;
GiftsRemaining: number; GiftsRemaining: number;
HandlerPoints: number; HandlerPoints: number;
@ -220,8 +235,6 @@ export interface IInventoryResponse {
ActiveAvatarImageType: string; ActiveAvatarImageType: string;
KubrowPets: IEquipmentDatabase[]; KubrowPets: IEquipmentDatabase[];
ShipDecorations: IConsumable[]; ShipDecorations: IConsumable[];
DailyAffiliationCetus: number;
DailyAffiliationQuills: number;
DiscoveredMarkers: IDiscoveredMarker[]; DiscoveredMarkers: IDiscoveredMarker[];
CompletedJobs: ICompletedJob[]; CompletedJobs: ICompletedJob[];
FocusAbility: string; FocusAbility: string;
@ -232,7 +245,6 @@ export interface IInventoryResponse {
KubrowPetPrints: IKubrowPetPrint[]; KubrowPetPrints: IKubrowPetPrint[];
AlignmentReplay: IAlignment; AlignmentReplay: IAlignment;
PersonalGoalProgress: IPersonalGoalProgress[]; PersonalGoalProgress: IPersonalGoalProgress[];
DailyAffiliationSolaris: number;
SpecialItems: IEquipmentDatabase[]; SpecialItems: IEquipmentDatabase[];
ThemeStyle: string; ThemeStyle: string;
ThemeBackground: string; ThemeBackground: string;
@ -241,8 +253,6 @@ export interface IInventoryResponse {
ChallengeInstanceStates: IChallengeInstanceState[]; ChallengeInstanceStates: IChallengeInstanceState[];
LoginMilestoneRewards: string[]; LoginMilestoneRewards: string[];
OperatorLoadOuts: IOperatorConfigClient[]; OperatorLoadOuts: IOperatorConfigClient[];
DailyAffiliationVentkids: number;
DailyAffiliationVox: number;
RecentVendorPurchases: Array<number | string>; RecentVendorPurchases: Array<number | string>;
Hoverboards: IEquipmentDatabase[]; Hoverboards: IEquipmentDatabase[];
NodeIntrosCompleted: string[]; NodeIntrosCompleted: string[];
@ -268,8 +278,6 @@ export interface IInventoryResponse {
TradeBannedUntil?: IMongoDate; TradeBannedUntil?: IMongoDate;
PlayedParkourTutorial: boolean; PlayedParkourTutorial: boolean;
SubscribedToEmailsPersonalized: number; SubscribedToEmailsPersonalized: number;
DailyAffiliationEntrati: number;
DailyAffiliationNecraloid: number;
MechSuits: IEquipmentDatabase[]; MechSuits: IEquipmentDatabase[];
InfestedFoundry?: IInfestedFoundry; InfestedFoundry?: IInfestedFoundry;
BlessingCooldown: IMongoDate; BlessingCooldown: IMongoDate;
@ -279,11 +287,7 @@ export interface IInventoryResponse {
AdultOperatorLoadOuts: IOperatorConfigClient[]; AdultOperatorLoadOuts: IOperatorConfigClient[];
LotusCustomization: ILotusCustomization; LotusCustomization: ILotusCustomization;
UseAdultOperatorLoadout?: boolean; UseAdultOperatorLoadout?: boolean;
DailyAffiliationZariman: number;
NemesisAbandonedRewards: string[]; NemesisAbandonedRewards: string[];
DailyAffiliationKahl: number;
DailyAffiliationCavia: number;
DailyAffiliationHex: number;
LastInventorySync: IOid; LastInventorySync: IOid;
NextRefill: IMongoDate; // Next time argon crystals will have a decay tick NextRefill: IMongoDate; // Next time argon crystals will have a decay tick
FoundToday?: IMiscItem[]; // for Argon Crystals FoundToday?: IMiscItem[]; // for Argon Crystals