diff --git a/src/controllers/api/fishmongerController.ts b/src/controllers/api/fishmongerController.ts index 898f5e4c..d85f7a4c 100644 --- a/src/controllers/api/fishmongerController.ts +++ b/src/controllers/api/fishmongerController.ts @@ -1,10 +1,9 @@ import { getJSONfromString } from "@/src/helpers/stringHelpers"; -import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper"; -import { addMiscItems, getInventory, getStandingLimit, updateStandingLimit } from "@/src/services/inventoryService"; +import { addMiscItems, addStanding, getInventory } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { RequestHandler } from "express"; -import { ExportResources, ExportSyndicates } from "warframe-public-export-plus"; +import { ExportResources } from "warframe-public-export-plus"; export const fishmongerController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -31,32 +30,15 @@ export const fishmongerController: RequestHandler = async (req, res) => { miscItemChanges.push({ ItemType: fish.ItemType, ItemCount: fish.ItemCount * -1 }); } addMiscItems(inventory, miscItemChanges); - if (gainedStanding && syndicateTag) { - let syndicate = inventory.Affiliations.find(x => x.Tag == syndicateTag); - if (!syndicate) { - syndicate = inventory.Affiliations[inventory.Affiliations.push({ Tag: syndicateTag, Standing: 0 }) - 1]; - } - const syndicateMeta = ExportSyndicates[syndicateTag]; - - 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; - - updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding); - } + let affiliationMod; + if (gainedStanding && syndicateTag) affiliationMod = addStanding(inventory, syndicateTag, gainedStanding); await inventory.save(); res.json({ InventoryChanges: { MiscItems: miscItemChanges }, SyndicateTag: syndicateTag, - StandingChange: gainedStanding + StandingChange: affiliationMod?.Standing || 0 }); }; diff --git a/src/controllers/api/missionInventoryUpdateController.ts b/src/controllers/api/missionInventoryUpdateController.ts index 1a8e6318..738002fd 100644 --- a/src/controllers/api/missionInventoryUpdateController.ts +++ b/src/controllers/api/missionInventoryUpdateController.ts @@ -55,8 +55,10 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res) const inventory = await getInventory(accountId); const inventoryUpdates = await addMissionInventoryUpdates(inventory, missionReport); - // skip mission rewards if not GS_SUCCESS and not a bounty (by presence of jobId, as there's a reward every stage but only the last stage has GS_SUCCESS) - if (missionReport.MissionStatus !== "GS_SUCCESS" && !missionReport.RewardInfo?.jobId) { + if ( + missionReport.MissionStatus !== "GS_SUCCESS" && + !(missionReport.RewardInfo?.jobId || missionReport.RewardInfo?.challengeMissionId) + ) { await inventory.save(); const inventoryResponse = await getInventoryResponse(inventory, true); res.json({ @@ -66,7 +68,8 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res) return; } - const { MissionRewards, inventoryChanges, credits } = await addMissionRewards(inventory, missionReport); + const { MissionRewards, inventoryChanges, credits, AffiliationMods, SyndicateXPItemReward } = + await addMissionRewards(inventory, missionReport); await inventory.save(); const inventoryResponse = await getInventoryResponse(inventory, true); @@ -78,7 +81,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res) MissionRewards, ...credits, ...inventoryUpdates, - FusionPoints: inventoryChanges?.FusionPoints + FusionPoints: inventoryChanges?.FusionPoints, + SyndicateXPItemReward, + AffiliationMods }); }; diff --git a/src/controllers/api/syndicateStandingBonusController.ts b/src/controllers/api/syndicateStandingBonusController.ts index 4067e9db..3170bc93 100644 --- a/src/controllers/api/syndicateStandingBonusController.ts +++ b/src/controllers/api/syndicateStandingBonusController.ts @@ -1,16 +1,9 @@ import { RequestHandler } from "express"; import { getAccountIdForRequest } from "@/src/services/loginService"; -import { - addMiscItems, - freeUpSlot, - getInventory, - getStandingLimit, - updateStandingLimit -} from "@/src/services/inventoryService"; +import { addMiscItems, addStanding, freeUpSlot, getInventory } from "@/src/services/inventoryService"; import { IMiscItem, InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; import { IOid } from "@/src/types/commonTypes"; import { ExportSyndicates, ExportWeapons } from "warframe-public-export-plus"; -import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper"; import { logger } from "@/src/utils/logger"; import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { EquipmentFeatures } from "@/src/types/inventoryTypes/commonInventoryTypes"; @@ -61,38 +54,13 @@ export const syndicateStandingBonusController: RequestHandler = async (req, res) inventoryChanges[slotBin] = { count: -1, platinum: 0, Slots: 1 }; } - 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 }) - 1 - ]; - } - - const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0); - if (syndicate.Standing + gainedStanding > max) { - gainedStanding = max - syndicate.Standing; - } - - if (syndicateMeta.medallionsCappedByDailyLimit) { - if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) { - gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin); - } - updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding); - } - - syndicate.Standing += gainedStanding; + const affiliationMod = addStanding(inventory, request.Operation.AffiliationTag, gainedStanding, true); await inventory.save(); res.json({ InventoryChanges: inventoryChanges, - AffiliationMods: [ - { - Tag: request.Operation.AffiliationTag, - Standing: gainedStanding - } - ] + AffiliationMods: [affiliationMod] }); }; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 078e7cd2..e29ea0a5 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -65,6 +65,7 @@ import { handleBundleAcqusition } from "./purchaseService"; import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json"; import { getRandomElement, getRandomInt, SRng } from "./rngService"; import { createMessage } from "./inboxService"; +import { getMaxStanding } from "@/src/helpers/syndicateStandingHelper"; export const createInventory = async ( accountOwnerId: Types.ObjectId, @@ -930,23 +931,50 @@ const standingLimitBinToInventoryKey: Record< export const allDailyAffiliationKeys: (keyof IDailyAffiliations)[] = Object.values(standingLimitBinToInventoryKey); -export const getStandingLimit = (inventory: IDailyAffiliations, bin: TStandingLimitBin): number => { +const getStandingLimit = (inventory: IDailyAffiliations, bin: TStandingLimitBin): number => { if (bin == "STANDING_LIMIT_BIN_NONE" || config.noDailyStandingLimits) { return Number.MAX_SAFE_INTEGER; } return inventory[standingLimitBinToInventoryKey[bin]]; }; -export const updateStandingLimit = ( - inventory: IDailyAffiliations, - bin: TStandingLimitBin, - subtrahend: number -): void => { +const updateStandingLimit = (inventory: IDailyAffiliations, bin: TStandingLimitBin, subtrahend: number): void => { if (bin != "STANDING_LIMIT_BIN_NONE" && !config.noDailyStandingLimits) { inventory[standingLimitBinToInventoryKey[bin]] -= subtrahend; } }; +export const addStanding = ( + inventory: TInventoryDatabaseDocument, + syndicateTag: string, + gainedStanding: number, + isMedallion: boolean = false +): IAffiliationMods => { + let syndicate = inventory.Affiliations.find(x => x.Tag == syndicateTag); + const syndicateMeta = ExportSyndicates[syndicateTag]; + + if (!syndicate) { + syndicate = + inventory.Affiliations[inventory.Affiliations.push({ Tag: syndicateTag, Standing: 0, Title: 0 }) - 1]; + } + + const max = getMaxStanding(syndicateMeta, syndicate.Title ?? 0); + if (syndicate.Standing + gainedStanding > max) gainedStanding = max - syndicate.Standing; + + if (!isMedallion || (isMedallion && syndicateMeta.medallionsCappedByDailyLimit)) { + if (gainedStanding > getStandingLimit(inventory, syndicateMeta.dailyLimitBin)) { + gainedStanding = getStandingLimit(inventory, syndicateMeta.dailyLimitBin); + } + updateStandingLimit(inventory, syndicateMeta.dailyLimitBin, gainedStanding); + } + + syndicate.Standing += gainedStanding; + return { + Tag: syndicateTag, + Standing: gainedStanding + }; +}; + // TODO: AffiliationMods support (Nightwave). export const updateGeneric = async (data: IGenericUpdate, accountId: string): Promise => { const inventory = await getInventory(accountId, "NodeIntrosCompleted MiscItems"); diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index b52b09d7..e6492a6e 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -28,13 +28,14 @@ import { addMods, addRecipes, addShipDecorations, + addStanding, combineInventoryChanges, updateCurrency, updateSyndicate } from "@/src/services/inventoryService"; import { updateQuestKey } from "@/src/services/questService"; import { Types } from "mongoose"; -import { IInventoryChanges } from "@/src/types/purchaseTypes"; +import { IAffiliationMods, IInventoryChanges } from "@/src/types/purchaseTypes"; import { getLevelKeyRewards, toStoreItem } from "@/src/services/itemDataService"; import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { getEntriesUnsafe } from "@/src/utils/ts-utils"; @@ -529,6 +530,8 @@ interface AddMissionRewardsReturnType { MissionRewards: IMissionReward[]; inventoryChanges?: IInventoryChanges; credits?: IMissionCredits; + AffiliationMods?: IAffiliationMods[]; + SyndicateXPItemReward?: number; } //TODO: return type of partial missioninventoryupdate response @@ -555,6 +558,8 @@ export const addMissionRewards = async ( const MissionRewards: IMissionReward[] = getRandomMissionDrops(rewardInfo, wagerTier); logger.debug("random mission drops:", MissionRewards); const inventoryChanges: IInventoryChanges = {}; + const AffiliationMods: IAffiliationMods[] = []; + let SyndicateXPItemReward; let missionCompletionCredits = 0; //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display @@ -718,7 +723,97 @@ export const addMissionRewards = async ( inventoryChanges.Nemesis.InfNodes = inventory.Nemesis.InfNodes; } } - return { inventoryChanges, MissionRewards, credits }; + + if (rewardInfo.JobStage != undefined && rewardInfo.jobId) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [jobType, tierStr, hubNode, syndicateId, locationTag] = rewardInfo.jobId.split("_"); + const tier = Number(tierStr); + + const worldState = getWorldState(); + let syndicateEntry = worldState.SyndicateMissions.find(m => m._id.$oid === syndicateId); + if (!syndicateEntry) syndicateEntry = worldState.SyndicateMissions.find(m => m.Tag === syndicateId); // Sometimes syndicateId can be tag + if (syndicateEntry && syndicateEntry.Jobs) { + let currentJob = syndicateEntry.Jobs[tier]; + if (syndicateEntry.Tag === "EntratiSyndicate") { + const vault = syndicateEntry.Jobs.find(j => j.locationTag === locationTag); + if (vault) currentJob = vault; + let medallionAmount = currentJob.xpAmounts[rewardInfo.JobStage]; + + if ( + ["DeimosEndlessAreaDefenseBounty", "DeimosEndlessExcavateBounty", "DeimosEndlessPurifyBounty"].some( + ending => jobType.endsWith(ending) + ) + ) { + const endlessJob = syndicateEntry.Jobs.find(j => j.endless); + if (endlessJob) { + const index = rewardInfo.JobStage % endlessJob.xpAmounts.length; + const excess = Math.floor(rewardInfo.JobStage / endlessJob.xpAmounts.length); + medallionAmount = Math.floor(endlessJob.xpAmounts[index] * (1 + 0.15000001 * excess)); + } + } + await addItem(inventory, "/Lotus/Types/Items/Deimos/EntratiFragmentUncommonB", medallionAmount); + MissionRewards.push({ + StoreItem: "/Lotus/StoreItems/Types/Items/Deimos/EntratiFragmentUncommonB", + ItemCount: medallionAmount + }); + SyndicateXPItemReward = medallionAmount; + } else { + if (tier >= 0) { + AffiliationMods.push( + addStanding(inventory, syndicateEntry.Tag, currentJob.xpAmounts[rewardInfo.JobStage]) + ); + } else { + if (jobType.endsWith("Heists/HeistProfitTakerBountyOne") && rewardInfo.JobStage === 2) { + AffiliationMods.push(addStanding(inventory, syndicateEntry.Tag, 1000)); + } + if (jobType.endsWith("Hunts/AllTeralystsHunt") && rewardInfo.JobStage === 2) { + AffiliationMods.push(addStanding(inventory, syndicateEntry.Tag, 5000)); + } + if ( + [ + "Hunts/TeralystHunt", + "Heists/HeistProfitTakerBountyTwo", + "Heists/HeistProfitTakerBountyThree", + "Heists/HeistProfitTakerBountyFour", + "Heists/HeistExploiterBountyOne" + ].some(ending => jobType.endsWith(ending)) + ) { + AffiliationMods.push(addStanding(inventory, syndicateEntry.Tag, 1000)); + } + } + } + } + } + + if (rewardInfo.challengeMissionId) { + const [syndicateTag, tierStr] = rewardInfo.challengeMissionId.split("_"); // TODO: third part in HexSyndicate jobs - Chemistry points + const tier = Number(tierStr); + const isSteelPath = missions?.Tier; + if (syndicateTag === "ZarimanSyndicate") { + let medallionAmount = tier + 1; + if (isSteelPath) medallionAmount = Math.round(medallionAmount * 1.5); + await addItem(inventory, "/Lotus/Types/Gameplay/Zariman/Resources/ZarimanDogTagBounty", medallionAmount); + MissionRewards.push({ + StoreItem: "/Lotus/StoreItems/Types/Gameplay/Zariman/Resources/ZarimanDogTagBounty", + ItemCount: medallionAmount + }); + SyndicateXPItemReward = medallionAmount; + } else { + let standingAmount = (tier + 1) * 1000; + if (tier > 5) standingAmount = 7500; // InfestedLichBounty + if (isSteelPath) standingAmount *= 1.5; + AffiliationMods.push(addStanding(inventory, syndicateTag, standingAmount)); + } + if (isSteelPath) { + await addItem(inventory, "/Lotus/Types/Items/MiscItems/SteelEssence", 1); + MissionRewards.push({ + StoreItem: "/Lotus/StoreItems/Types/Items/MiscItems/SteelEssence", + ItemCount: 1 + }); + } + } + + return { inventoryChanges, MissionRewards, credits, AffiliationMods, SyndicateXPItemReward }; }; interface IMissionCredits { diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index 6e732f83..494e23f4 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -150,6 +150,7 @@ export interface IRewardInfo { JobStage?: number; Q?: boolean; // likely indicates that the bonus objective for this stage was completed CheckpointCounter?: number; // starts at 1, is incremented with each job stage upload, and does not reset when starting a new job + challengeMissionId?: string; } export type IMissionStatus = "GS_SUCCESS" | "GS_FAILURE" | "GS_DUMPED" | "GS_QUIT" | "GS_INTERRUPTED";