diff --git a/package-lock.json b/package-lock.json index 081c89a6..573523c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "morgan": "^1.10.0", "ncp": "^2.0.0", "typescript": "^5.5", - "warframe-public-export-plus": "^0.5.56", + "warframe-public-export-plus": "^0.5.57", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" @@ -3789,9 +3789,9 @@ } }, "node_modules/warframe-public-export-plus": { - "version": "0.5.56", - "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.56.tgz", - "integrity": "sha512-px+J7tUm6fkSzwKkvL73ySQReDq9oM1UrHSLM3vbYGBvELM892iBgPYG45okIhScCSdwmmXTiWZTf4x/I4qiNQ==" + "version": "0.5.57", + "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.57.tgz", + "integrity": "sha512-CKbg7/2hSDH7I7yYSWwkrP4N2rEAEK1vNEuehj+RD9vMvl1c4u6klHLMwdh+ULxXiW4djWIlNIhs5bi/fm58Mg==" }, "node_modules/warframe-riven-info": { "version": "0.1.2", diff --git a/package.json b/package.json index 15f4597c..330ed606 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "morgan": "^1.10.0", "ncp": "^2.0.0", "typescript": "^5.5", - "warframe-public-export-plus": "^0.5.56", + "warframe-public-export-plus": "^0.5.57", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" diff --git a/src/controllers/api/entratiLabConquestModeController.ts b/src/controllers/api/entratiLabConquestModeController.ts index ae7b5845..e5b6c818 100644 --- a/src/controllers/api/entratiLabConquestModeController.ts +++ b/src/controllers/api/entratiLabConquestModeController.ts @@ -21,10 +21,12 @@ export const entratiLabConquestModeController: RequestHandler = async (req, res) inventory.EntratiVaultCountResetDate = new Date(weekEnd); if (inventory.EntratiLabConquestUnlocked) { inventory.EntratiLabConquestUnlocked = 0; + inventory.EntratiLabConquestCacheScoreMission = 0; inventory.EntratiLabConquestActiveFrameVariants = []; } if (inventory.EchoesHexConquestUnlocked) { inventory.EchoesHexConquestUnlocked = 0; + inventory.EchoesHexConquestCacheScoreMission = 0; inventory.EchoesHexConquestActiveFrameVariants = []; inventory.EchoesHexConquestActiveStickers = []; } diff --git a/src/controllers/api/missionInventoryUpdateController.ts b/src/controllers/api/missionInventoryUpdateController.ts index ead3b56e..bde1c3cf 100644 --- a/src/controllers/api/missionInventoryUpdateController.ts +++ b/src/controllers/api/missionInventoryUpdateController.ts @@ -6,6 +6,7 @@ import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/mi import { getInventory } from "@/src/services/inventoryService"; import { getInventoryResponse } from "./inventoryController"; import { logger } from "@/src/utils/logger"; +import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes"; /* **** INPUT **** @@ -71,8 +72,14 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res) return; } - const { MissionRewards, inventoryChanges, credits, AffiliationMods, SyndicateXPItemReward } = - await addMissionRewards(inventory, missionReport, firstCompletion); + const { + MissionRewards, + inventoryChanges, + credits, + AffiliationMods, + SyndicateXPItemReward, + ConquestCompletedMissionsCount + } = await addMissionRewards(inventory, missionReport, firstCompletion); await inventory.save(); const inventoryResponse = await getInventoryResponse(inventory, true); @@ -86,8 +93,9 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res) ...inventoryUpdates, //FusionPoints: inventoryChanges?.FusionPoints, // This in combination with InventoryJson or InventoryChanges seems to just double the number of endo shown, so unsure when this is needed. SyndicateXPItemReward, - AffiliationMods - }); + AffiliationMods, + ConquestCompletedMissionsCount + } satisfies IMissionInventoryUpdateResponse); }; /* diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index 46d039c1..f03dda81 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -43,7 +43,7 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento import { getEntriesUnsafe } from "@/src/utils/ts-utils"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { handleStoreItemAcquisition } from "./purchaseService"; -import { IMissionReward } from "../types/missionTypes"; +import { IMissionCredits, IMissionReward } from "../types/missionTypes"; import { crackRelic } from "@/src/helpers/relicHelper"; import { createMessage } from "./inboxService"; import kuriaMessage50 from "@/static/fixed_responses/kuriaMessages/fiftyPercent.json"; @@ -586,8 +586,140 @@ interface AddMissionRewardsReturnType { credits?: IMissionCredits; AffiliationMods?: IAffiliationMods[]; SyndicateXPItemReward?: number; + ConquestCompletedMissionsCount?: number; } +interface IConquestReward { + at: number; + pool: IRngResult[]; +} + +const labConquestRewards: IConquestReward[] = [ + { + at: 5, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards" + ][0] as IRngResult[] + }, + { + at: 10, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestSilverRewards" + ][0] as IRngResult[] + }, + { + at: 15, + pool: [ + { + type: "/Lotus/StoreItems/Types/Gameplay/EntratiLab/Resources/EntratiLanthornBundle", + itemCount: 3, + probability: 1 + } + ] + }, + { + at: 20, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards" + ][0] as IRngResult[] + }, + { + at: 28, + pool: [ + { + type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints", + itemCount: 20, + probability: 1 + } + ] + }, + { + at: 31, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestGoldRewards" + ][0] as IRngResult[] + }, + { + at: 34, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/EntratiLabConquestRewards/EntratiLabConquestArcaneRewards" + ][0] as IRngResult[] + }, + { + at: 37, + pool: [ + { + type: "/Lotus/StoreItems/Types/Items/MiscItems/DistillPoints", + itemCount: 50, + probability: 1 + } + ] + } +]; + +const hexConquestRewards: IConquestReward[] = [ + { + at: 5, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards" + ][0] as IRngResult[] + }, + { + at: 10, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestSilverRewards" + ][0] as IRngResult[] + }, + { + at: 15, + pool: [ + { + type: "/Lotus/StoreItems/Types/BoosterPacks/1999StickersPackEchoesArchimedea", + itemCount: 1, + probability: 1 + } + ] + }, + { + at: 20, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards" + ][0] as IRngResult[] + }, + { + at: 28, + pool: [ + { + type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks", + itemCount: 6, + probability: 1 + } + ] + }, + { + at: 31, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestGoldRewards" + ][0] as IRngResult[] + }, + { + at: 34, + pool: ExportRewards[ + "/Lotus/Types/Game/MissionDecks/1999ConquestRewards/1999ConquestArcaneRewards" + ][0] as IRngResult[] + }, + { + at: 37, + pool: [ + { + type: "/Lotus/StoreItems/Types/Items/MiscItems/1999ConquestBucks", + itemCount: 9, + probability: 1 + } + ] + } +]; + //TODO: return type of partial missioninventoryupdate response export const addMissionRewards = async ( inventory: TInventoryDatabaseDocument, @@ -620,6 +752,7 @@ export const addMissionRewards = async ( const inventoryChanges: IInventoryChanges = {}; const AffiliationMods: IAffiliationMods[] = []; let SyndicateXPItemReward; + let ConquestCompletedMissionsCount; let missionCompletionCredits = 0; //inventory change is what the client has not rewarded itself, also the client needs to know the credit changes for display @@ -691,6 +824,62 @@ export const addMissionRewards = async ( }); } + if (rewardInfo.ConquestCompleted !== undefined) { + let score = 1; + if (rewardInfo.ConquestHardModeActive === 1) score += 3; + + if (rewardInfo.ConquestPersonalModifiersActive !== undefined) + score += rewardInfo.ConquestPersonalModifiersActive; + if (rewardInfo.ConquestEquipmentSuggestionsFulfilled !== undefined) + score += rewardInfo.ConquestEquipmentSuggestionsFulfilled; + + score *= rewardInfo.ConquestCompleted + 1; + + if (rewardInfo.ConquestCompleted == 2 && rewardInfo.ConquestHardModeActive === 1) score += 1; + + logger.debug(`completed conquest mission ${rewardInfo.ConquestCompleted + 1} for a score of ${score}`); + + const conquestType = rewardInfo.ConquestType; + const conquestNode = + conquestType == "HexConquest" ? "EchoesHexConquestHardModeUnlocked" : "EntratiLabConquestHardModeUnlocked"; + if (score >= 25 && inventory.NodeIntrosCompleted.indexOf(conquestNode) == -1) + inventory.NodeIntrosCompleted.push(conquestNode); + + if (conquestType == "HexConquest") { + inventory.EchoesHexConquestCacheScoreMission ??= 0; + if (score > inventory.EchoesHexConquestCacheScoreMission) { + for (const reward of hexConquestRewards) { + if (score >= reward.at && inventory.EchoesHexConquestCacheScoreMission < reward.at) { + const rolled = getRandomReward(reward.pool)!; + logger.debug(`rolled hex conquest reward for reaching ${reward.at} points`, rolled); + MissionRewards.push({ + StoreItem: rolled.type, + ItemCount: rolled.itemCount + }); + } + } + inventory.EchoesHexConquestCacheScoreMission = score; + } + } else { + inventory.EntratiLabConquestCacheScoreMission ??= 0; + if (score > inventory.EntratiLabConquestCacheScoreMission) { + for (const reward of labConquestRewards) { + if (score >= reward.at && inventory.EntratiLabConquestCacheScoreMission < reward.at) { + const rolled = getRandomReward(reward.pool)!; + logger.debug(`rolled lab conquest reward for reaching ${reward.at} points`, rolled); + MissionRewards.push({ + StoreItem: rolled.type, + ItemCount: rolled.itemCount + }); + } + } + inventory.EntratiLabConquestCacheScoreMission = score; + } + } + + ConquestCompletedMissionsCount = rewardInfo.ConquestCompleted == 2 ? 0 : rewardInfo.ConquestCompleted + 1; + } + for (const reward of MissionRewards) { const inventoryChange = await handleStoreItemAcquisition( reward.StoreItem, @@ -882,16 +1071,16 @@ export const addMissionRewards = async ( } } - return { inventoryChanges, MissionRewards, credits, AffiliationMods, SyndicateXPItemReward }; + return { + inventoryChanges, + MissionRewards, + credits, + AffiliationMods, + SyndicateXPItemReward, + ConquestCompletedMissionsCount + }; }; -interface IMissionCredits { - MissionCredits: number[]; - CreditBonus: number[]; - TotalCredits: number[]; - DailyMissionBonus?: boolean; -} - //creditBonus is not entirely accurate. //TODO: consider ActiveBoosters export const addCredits = ( diff --git a/src/types/missionTypes.ts b/src/types/missionTypes.ts index c8cab373..be1d08bc 100644 --- a/src/types/missionTypes.ts +++ b/src/types/missionTypes.ts @@ -1,3 +1,5 @@ +import { IAffiliationMods, IInventoryChanges } from "./purchaseTypes"; + export const inventoryFields = ["RawUpgrades", "MiscItems", "Consumables", "Recipes"] as const; export type IInventoryFieldType = (typeof inventoryFields)[number]; @@ -11,3 +13,20 @@ export interface IMissionReward { FromEnemyCache?: boolean; IsStrippedItem?: boolean; } + +export interface IMissionCredits { + MissionCredits: number[]; + CreditBonus: number[]; + TotalCredits: number[]; + DailyMissionBonus?: boolean; +} + +export interface IMissionInventoryUpdateResponse extends Partial { + ConquestCompletedMissionsCount?: number; + InventoryJson?: string; + MissionRewards?: IMissionReward[]; + InventoryChanges?: IInventoryChanges; + FusionPoints?: number; + SyndicateXPItemReward?: number; + AffiliationMods?: IAffiliationMods[]; +} diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index b81fa064..441bf03e 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -125,6 +125,7 @@ export type IMissionInventoryUpdateRequest = { wagerTier?: number; // the index creditsFee?: number; // the index InvasionProgress?: IInvasionProgressClient[]; + ConquestMissionsCompleted?: number; } & { [K in TEquipmentKey]?: IEquipmentClient[]; }; @@ -150,7 +151,12 @@ export interface IRewardInfo { PurgatoryRewardQualifications?: string; rewardSeed?: number | bigint; periodicMissionTag?: string; - + ConquestType?: string; + ConquestCompleted?: number; + ConquestEquipmentSuggestionsFulfilled?: number; + ConquestPersonalModifiersActive?: number; + ConquestStickersActive?: number; + ConquestHardModeActive?: number; // for bounties, only EOM_AFK and node are given from above, plus: JobTier?: number; jobId?: string;