From 7bf170847411fdd9f3596c804e116d72ea9b6836 Mon Sep 17 00:00:00 2001 From: Master Date: Tue, 25 Jun 2024 08:52:35 +0800 Subject: [PATCH] fix and update quest system --- .../giveKeyChainTriggeredItemsController.ts | 16 +++ .../giveKeyChainTriggeredMessageController.ts | 16 +++ .../api/setActiveQuestController.ts | 23 +--- src/controllers/api/updateQuestController.ts | 26 +---- src/services/inventoryService.ts | 14 ++- src/services/missionInventoryUpdateService.ts | 14 ++- src/services/questService.ts | 102 ++++++++++++++++++ src/types/questTypes.ts | 30 ++++++ src/types/requestTypes.ts | 4 +- 9 files changed, 193 insertions(+), 52 deletions(-) create mode 100644 src/controllers/api/giveKeyChainTriggeredItemsController.ts create mode 100644 src/controllers/api/giveKeyChainTriggeredMessageController.ts create mode 100644 src/services/questService.ts create mode 100644 src/types/questTypes.ts diff --git a/src/controllers/api/giveKeyChainTriggeredItemsController.ts b/src/controllers/api/giveKeyChainTriggeredItemsController.ts new file mode 100644 index 00000000..80eca74a --- /dev/null +++ b/src/controllers/api/giveKeyChainTriggeredItemsController.ts @@ -0,0 +1,16 @@ +import { RequestHandler } from "express"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { giveKeyChainTriggeredItems } from "@/src/services/questService"; +import { IGiveKeyChainTriggeredItemsRequest } from "@/src/types/questTypes"; + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +const giveKeyChainTriggeredItemsController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const payload = getJSONfromString(req.body as string) as IGiveKeyChainTriggeredItemsRequest; + const result = await giveKeyChainTriggeredItems(accountId, payload.KeyChain, payload.ChainStage); + if (result != null) res.json(result); + else res.status(200).end(); +}; + +export { giveKeyChainTriggeredItemsController }; diff --git a/src/controllers/api/giveKeyChainTriggeredMessageController.ts b/src/controllers/api/giveKeyChainTriggeredMessageController.ts new file mode 100644 index 00000000..e1cf1e76 --- /dev/null +++ b/src/controllers/api/giveKeyChainTriggeredMessageController.ts @@ -0,0 +1,16 @@ +import { RequestHandler } from "express"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { giveKeyChainTriggeredMessage } from "@/src/services/questService"; +import { IGiveKeyChainTriggeredMessageRequest } from "@/src/types/questTypes"; + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +const giveKeyChainTriggeredMessageController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const payload = getJSONfromString(req.body as string) as IGiveKeyChainTriggeredMessageRequest; + const result = await giveKeyChainTriggeredMessage(accountId, payload.KeyChain, payload.ChainStage); + if (result != null) res.json(result); + else res.status(200).end(); +}; + +export { giveKeyChainTriggeredMessageController }; diff --git a/src/controllers/api/setActiveQuestController.ts b/src/controllers/api/setActiveQuestController.ts index abc19790..3f6ba835 100644 --- a/src/controllers/api/setActiveQuestController.ts +++ b/src/controllers/api/setActiveQuestController.ts @@ -1,30 +1,13 @@ -import { getInventory } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; +import { setActiveQuest } from "@/src/services/questService"; import { RequestHandler } from "express"; // eslint-disable-next-line @typescript-eslint/no-misused-promises const setActiveQuestController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); const quest = req.query.quest as string; - - const inventory = await getInventory(accountId); - const questKey = inventory.QuestKeys.find(q => q.ItemType == quest); - if (questKey == null) - inventory.QuestKeys.push({ ItemType: quest }); - inventory.ActiveQuest = quest; - await inventory.save(); - - res.json({ - inventoryChanges: { - QuestKey: [{ - ItemType: quest - }], - Herses: [], - PremiumCreditsFree: 0, - PremiumCredits: 0, - RegularCredits: 0 - } - }); + const result = await setActiveQuest(accountId, quest); + res.json(result); }; export { setActiveQuestController }; diff --git a/src/controllers/api/updateQuestController.ts b/src/controllers/api/updateQuestController.ts index 68bc708f..d2df9aff 100644 --- a/src/controllers/api/updateQuestController.ts +++ b/src/controllers/api/updateQuestController.ts @@ -1,37 +1,13 @@ import { RequestHandler } from "express"; import { getJSONfromString } from "@/src/helpers/stringHelpers"; import { getAccountIdForRequest } from "@/src/services/loginService"; -import { IQuestKeyDatabase } from "@/src/types/inventoryTypes/inventoryTypes"; -import { logger } from "@/src/utils/logger"; -import { Inventory } from "@/src/models/inventoryModels/inventoryModel"; -export interface IUpdateQuestRequest { - QuestKeys: IQuestKeyDatabase[]; - PS: string; - questCompletion: boolean; - PlayerShipEvents: []; - crossPlaySetting: string; -} - -export interface IUpdateQuestResponse { - CustomData?: string; - MissionRewards: []; -} // eslint-disable-next-line @typescript-eslint/no-misused-promises const updateQuestController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); const payload = getJSONfromString(req.body as string) as IUpdateQuestRequest; - logger.debug("quest: " + payload.QuestKeys[0].ItemType); - - const inventory = await Inventory.findOne({ accountOwnerId: accountId }); - if (inventory) { - /* empty */ - } - - const result: IUpdateQuestResponse = { - MissionRewards: [] - }; + const result = await updateQuest(accountId, payload); res.json(result); }; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index d5a4edb9..76d0656c 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -28,6 +28,7 @@ import { WeaponTypeInternal, getWeaponType, getExalted } from "@/src/services/it import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes"; import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes"; import { ExportCustoms, ExportFlavour, ExportRecipes, ExportResources } from "warframe-public-export-plus"; +import { updateQuestKeys } from "./questService"; export const createInventory = async ( accountOwnerId: Types.ObjectId, @@ -121,9 +122,9 @@ export const addItem = async ( } // Path-based duck typing - switch (typeName.substr(1).split("/")[1]) { + switch (typeName.substring(1).split("/")[1]) { case "Powersuits": - switch (typeName.substr(1).split("/")[2]) { + switch (typeName.substring(1).split("/")[2]) { default: { const suit = await addPowerSuit(typeName, accountId); await updateSlots(accountId, InventorySlot.SUITS, 0, 1); @@ -196,7 +197,7 @@ export const addItem = async ( }; } case "Types": - switch (typeName.substr(1).split("/")[2]) { + switch (typeName.substring(1).split("/")[2]) { case "Sentinels": // TOOD: Sentinels should also grant their DefaultUpgrades & SentinelWeapon. const sentinel = await addSentinel(typeName, accountId); @@ -208,7 +209,7 @@ export const addItem = async ( } }; case "Items": { - switch (typeName.substr(1).split("/")[3]) { + switch (typeName.substring(1).split("/")[3]) { case "ShipDecos": { const inventory = await getInventory(accountId); const changes = [ @@ -622,7 +623,7 @@ const addMissionComplete = (inventory: IInventoryDatabaseDocument, { Tag, Comple const gearKeys = ["Suits", "Pistols", "LongGuns", "Melee"] as const; export const missionInventoryUpdate = async (data: IMissionInventoryUpdateRequest, accountId: string) => { - const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress, FusionPoints, Consumables, Recipes, Missions } = + const { RawUpgrades, MiscItems, RegularCredits, ChallengeProgress, FusionPoints, Consumables, Recipes, Missions, QuestKeys } = data; const inventory = await getInventory(accountId); @@ -678,6 +679,9 @@ export const missionInventoryUpdate = async (data: IMissionInventoryUpdateReques if (Missions) { addMissionComplete(inventory, Missions); } + if(QuestKeys) { + await updateQuestKeys(inventory, QuestKeys); + } const changedInventory = await inventory.save(); return changedInventory.toJSON(); diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index afcabd36..f6861ae4 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -7,7 +7,9 @@ import { ExportGear, ExportRecipes, ExportRelics, - IReward + IReward, + ExportKeys, + ExportResources } from "warframe-public-export-plus"; import { IMissionInventoryUpdateRequest } from "../types/requestTypes"; import { logger } from "@/src/utils/logger"; @@ -64,6 +66,14 @@ const getRewards = ({ } } + if (RewardInfo.node in ExportKeys) { + const quest = ExportKeys[RewardInfo.node]; + const rewards = quest.rewards ?? []; + if (rewards.length == 0) { + return { InventoryChanges: {}, MissionRewards: [] }; + } + } + logger.debug("Mission rewards:", drops); return formatRewardsToInventoryType(drops); }; @@ -181,6 +191,8 @@ const formatRewardsToInventoryType = ( addRewardResponse(InventoryChanges, MissionRewards, type, reward.itemCount, "Recipes"); } else if (type in ExportRelics) { addRewardResponse(InventoryChanges, MissionRewards, type, reward.itemCount, "MiscItems"); + } else if (type in ExportResources) { + addRewardResponse(InventoryChanges, MissionRewards, type, reward.itemCount, "MiscItems"); } else { logger.error(`rolled reward ${reward.itemCount}X ${reward.type} but unsure how to give it`); } diff --git a/src/services/questService.ts b/src/services/questService.ts new file mode 100644 index 00000000..db15bc20 --- /dev/null +++ b/src/services/questService.ts @@ -0,0 +1,102 @@ +import { IInventoryDatabaseDocument, IQuestKeyDatabase } from "@/src/types/inventoryTypes/inventoryTypes"; +import { IUpdateQuestRequest, IUpdateQuestResponse } from "@/src/types/questTypes"; +import { addItem, getInventory } from "./inventoryService"; +import { logger } from "../utils/logger"; +import { ExportKeys } from "warframe-public-export-plus"; + +export const setActiveQuest = async (accountId: string, quest: string) => { + const inventory = await getInventory(accountId); + const questKey = inventory.QuestKeys.find(q => q.ItemType == quest); + if (questKey == null) inventory.QuestKeys.push({ ItemType: quest }); + inventory.ActiveQuest = quest; + await inventory.save(); + + return { + inventoryChanges: { + QuestKey: [ + { + ItemType: quest + } + ], + Herses: [], + PremiumCreditsFree: 0, + PremiumCredits: 0, + RegularCredits: 0 + } + }; +}; + +export const updateQuestKeys = async (inventory: IInventoryDatabaseDocument, questKeys: IQuestKeyDatabase[]) => { + const questKeyIndex = inventory.QuestKeys.findIndex(questKey => questKey.ItemType === questKeys[0].ItemType); + + inventory.QuestKeys[questKeyIndex] = questKeys[0]; + + if (questKeys[0].Completed) { + inventory.QuestKeys[questKeyIndex].CompletionDate = new Date(); + } + + await inventory.save(); +}; + +export const updateQuest = async (accountId: string, updateQuest: IUpdateQuestRequest) => { + const inventory = await getInventory(accountId); + + await updateQuestKeys(inventory, updateQuest.QuestKeys); + + const result: IUpdateQuestResponse = { + MissionRewards: [] + }; + + if (updateQuest.QuestKeys[0].Completed) { + const quest = ExportKeys[updateQuest.QuestKeys[0].ItemType]; + if (quest.rewards != null) { + for (const reward of quest.rewards) { + switch (reward.rewardType) { + case "RT_STORE_ITEM": + await addItem(accountId, reward.itemType.replace("/Lotus/StoreItems/", "/Lotus/"), 1); + break; + case "RT_RECIPE": + await addItem(accountId, reward.itemType, 1); + break; + case "RT_CREDITS": + inventory.RegularCredits += reward.amount; + await inventory.save(); + break; + } + // push MissionRewards + // result.MissionRewards.push({}); + } + } + } + + return result; +}; + +export const giveKeyChainTriggeredItems = async (accountId: string, keyChain: string, chainStage: number) => { + logger.debug("keyChain: " + keyChain + " chainStage: " + chainStage); + + // TODO:rewards + const quest = ExportKeys[keyChain]; + + if (quest.chainStages) { + for (const chainStage of quest.chainStages) { + if (chainStage.itemsToGiveWhenTriggered.length > 0) { + let itemType = chainStage.itemsToGiveWhenTriggered[0]; + if (itemType.indexOf("") > 0) { + itemType = itemType.replace("/Lotus/StoreItems/", "/Lotus/"); + } + await addItem(accountId, itemType, 1); + } + } + } + + return null; +}; + +export const giveKeyChainTriggeredMessage = async (accountId: string, keyChain: string, chainStage: number) => { + logger.debug("keyChain: " + keyChain + " chainStage: " + chainStage); + + // TODO:message + + return null; +}; diff --git a/src/types/questTypes.ts b/src/types/questTypes.ts new file mode 100644 index 00000000..0c38cdd2 --- /dev/null +++ b/src/types/questTypes.ts @@ -0,0 +1,30 @@ +import { IQuestKeyDatabase } from "./inventoryTypes/inventoryTypes"; + +export interface IUpdateQuestRequest { + QuestKeys: IQuestKeyDatabase[]; + PS: string; + questCompletion: boolean; + PlayerShipEvents: []; + crossPlaySetting: string; +} + +export interface IUpdateQuestResponse { + CustomData?: string; + MissionRewards: []; +} + +export interface IGiveKeyChainTriggeredItemsRequest { + KeyChain: string; + ChainStage: number; +} + +export interface IGiveKeyChainTriggeredMessageGroup { + experiment: string; + experimentGroup: string; +} + +export interface IGiveKeyChainTriggeredMessageRequest { + KeyChain: string; + ChainStage: number; + Groups: IGiveKeyChainTriggeredMessageGroup[]; +} \ No newline at end of file diff --git a/src/types/requestTypes.ts b/src/types/requestTypes.ts index 3fc4c0f6..120283ce 100644 --- a/src/types/requestTypes.ts +++ b/src/types/requestTypes.ts @@ -11,7 +11,8 @@ import { IMission, IRawUpgrade, ISeasonChallenge, - TEquipmentKey + TEquipmentKey, + IQuestKeyDatabase } from "./inventoryTypes/inventoryTypes"; export interface IArtifactsRequest { @@ -57,6 +58,7 @@ export interface IMissionInventoryUpdateRequest { RewardInfo?: IMissionInventoryUpdateRequestRewardInfo; Missions?: IMission; EvolutionProgress?: IEvolutionProgress[]; + QuestKeys?: IQuestKeyDatabase[]; FusionPoints?: number; // Not a part of the request, but we put it in this struct as an intermediate storage. }