From 1148ef8de41e8f92014213dc35f71cf32f36e70b Mon Sep 17 00:00:00 2001 From: Master Date: Sat, 6 Jul 2024 01:29:08 +0800 Subject: [PATCH] fix quest system --- .../giveKeyChainTriggeredItemsController.ts | 17 ++ .../giveKeyChainTriggeredMessageController.ts | 28 +++ .../api/setActiveQuestController.ts | 10 +- src/controllers/api/updateQuestController.ts | 16 ++ src/services/inventoryService.ts | 15 +- src/services/questService.ts | 193 ++++++++++++++++++ src/types/inventoryTypes/inventoryTypes.ts | 1 + src/types/questTypes.ts | 45 ++++ 8 files changed, 319 insertions(+), 6 deletions(-) create mode 100644 src/controllers/api/giveKeyChainTriggeredItemsController.ts create mode 100644 src/controllers/api/giveKeyChainTriggeredMessageController.ts create mode 100644 src/controllers/api/updateQuestController.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..0b5a4e0d --- /dev/null +++ b/src/controllers/api/giveKeyChainTriggeredItemsController.ts @@ -0,0 +1,17 @@ +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; + if (payload) { + const result = await giveKeyChainTriggeredItems(accountId, payload); + if (result) res.json(result); + } else res.json({}); +}; + +export { giveKeyChainTriggeredItemsController }; diff --git a/src/controllers/api/giveKeyChainTriggeredMessageController.ts b/src/controllers/api/giveKeyChainTriggeredMessageController.ts new file mode 100644 index 00000000..fde8836a --- /dev/null +++ b/src/controllers/api/giveKeyChainTriggeredMessageController.ts @@ -0,0 +1,28 @@ +import { RequestHandler } from "express"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { giveKeyChainTriggeredMessage } from "@/src/services/questService"; + +export interface IGiveKeyChainTriggeredMessageGroup { + experiment: string; + experimentGroup: string; +} + +export interface IGiveKeyChainTriggeredMessageRequest { + KeyChain: string; + ChainStage: number; + Groups: IGiveKeyChainTriggeredMessageGroup[]; +} + +// 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 = giveKeyChainTriggeredMessage(accountId, payload.KeyChain, payload.ChainStage); + + if (result) 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 a8f06a25..3f6ba835 100644 --- a/src/controllers/api/setActiveQuestController.ts +++ b/src/controllers/api/setActiveQuestController.ts @@ -1,7 +1,13 @@ +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { setActiveQuest } from "@/src/services/questService"; import { RequestHandler } from "express"; -const setActiveQuestController: RequestHandler = (_req, res) => { - res.sendStatus(200); +// 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 result = await setActiveQuest(accountId, quest); + res.json(result); }; export { setActiveQuestController }; diff --git a/src/controllers/api/updateQuestController.ts b/src/controllers/api/updateQuestController.ts new file mode 100644 index 00000000..a3c56651 --- /dev/null +++ b/src/controllers/api/updateQuestController.ts @@ -0,0 +1,16 @@ +import { RequestHandler } from "express"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { IUpdateQuestRequest } from "@/src/types/questTypes"; +import { updateQuest } from "@/src/services/questService"; + +// 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; + const result = await updateQuest(accountId, payload); + res.json(result); +}; + +export { updateQuestController }; + diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index c32a8af1..82ef39e6 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -19,18 +19,18 @@ import { equipmentKeys, IFusionTreasure } from "@/src/types/inventoryTypes/inventoryTypes"; -import { IGenericUpdate } from "../types/genericUpdate"; +import { IGenericUpdate } from "@/src/types/genericUpdate"; import { IArtifactsRequest, IMissionInventoryUpdateRequest, IThemeUpdateRequest, IUpdateChallengeProgressRequest -} from "../types/requestTypes"; +} from "@/src/types/requestTypes"; import { logger } from "@/src/utils/logger"; import { getWeaponType, getExalted } from "@/src/services/itemDataService"; import { getRandomWeightedReward } from "@/src/services/rngService"; -import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "../types/syndicateTypes"; -import { IEquipmentClient } from "../types/inventoryTypes/commonInventoryTypes"; +import { ISyndicateSacrifice, ISyndicateSacrificeResponse } from "@/src/types/syndicateTypes"; +import { IEquipmentClient, IEquipmentDatabase } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { ExportBoosterPacks, ExportCustoms, @@ -870,3 +870,10 @@ export const upgradeMod = async (artifactsData: IArtifactsRequest, accountId: st throw error; } }; + +export const addHerse = async (ItemType: string, accountId: string): Promise => { + const inventory = await getInventory(accountId); + const herseIndex = inventory.Horses.push({ ItemType: ItemType, Configs: [], UpgradeVer: 101 }); + const changedInventory = await inventory.save(); + return changedInventory.Horses[herseIndex - 1].toJSON(); +}; diff --git a/src/services/questService.ts b/src/services/questService.ts new file mode 100644 index 00000000..9e9ad798 --- /dev/null +++ b/src/services/questService.ts @@ -0,0 +1,193 @@ +import { ExportKeys, ExportRecipes, ExportResources, IKey } from "warframe-public-export-plus"; +import { addHerse, addItem, getInventory } from "./inventoryService"; +import { logger } from "@/src/utils/logger"; +import { + IGiveKeyChainTriggeredItemsRequest, + IGiveKeyChainTriggeredItemsResponse, + ISetActiveQuestResponse, + IUpdateQuestRequest, + IUpdateQuestResponse +} from "@/src/types/questTypes"; +import { IInventoryDatabaseDocument, IQuestKeyDatabase } from "@/src/types/inventoryTypes/inventoryTypes"; +import { toOid } from "@/src/helpers/inventoryHelpers"; + +const getQuest = (quest: string): IKey | undefined => { + for (const [k, v] of Object.entries(ExportKeys)) { + if (k == quest) { + return v; + } + } + return undefined; +}; + +export const setActiveQuest = async (accountId: string, quest: string): Promise => { + 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(); + + const questData = getQuest(quest); + if (questData) { + console.log(questData); + } + const result: ISetActiveQuestResponse = { + inventoryChanges: { + QuestKey: [], + Herses: [], + PremiumCreditsFree: 0, + PremiumCredits: 0, + RegularCredits: 0 + } + }; + switch (quest) { + case "/Lotus/Types/Keys/DuviriQuest/DuviriQuestKeyChain": + // eslint-disable-next-line no-case-declarations + const inventory = await getInventory(accountId); + // eslint-disable-next-line no-case-declarations + const questKey = inventory.QuestKeys.find(q => q.ItemType == quest); + if (questKey == null) inventory.QuestKeys.push({ ItemType: quest }); + inventory.ActiveQuest = quest; + await inventory.save(); + // eslint-disable-next-line no-case-declarations + const herse = await addHerse("/Lotus/Types/NeutralCreatures/ErsatzHorse/ErsatzHorsePowerSuit", accountId); + result.inventoryChanges.QuestKey.push({ ItemType: quest }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + result.inventoryChanges.Herses.push({ ItemType: herse.ItemType, ItemId: toOid(herse._id) }); + result.inventoryChanges.PremiumCreditsFree = 50; + result.inventoryChanges.PremiumCredits = 50; + result.inventoryChanges.RegularCredits = 3000; + break; + default: + result.inventoryChanges.QuestKey.push({ ItemType: quest }); + break; + } + return result; +}; + +export const updateQuestKeys = async ( + inventory: IInventoryDatabaseDocument, + questKeys: IQuestKeyDatabase[] +): Promise => { + logger.debug("quest: " + questKeys[0].ItemType); + + 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 +): Promise => { + 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, + payload: IGiveKeyChainTriggeredItemsRequest +): Promise => { + logger.debug("keyChain: " + payload.KeyChain + " chainStage: " + payload.ChainStage); + const inventory = await getInventory(accountId); + + const quest = ExportKeys[payload.KeyChain]; + + const result: IGiveKeyChainTriggeredItemsResponse = {}; + + if (quest.chainStages) { + const stage = quest.chainStages[payload.ChainStage]; + + if (stage.key && stage.key in ExportKeys) { + const stageQuest = ExportKeys[stage.key]; + if (stageQuest.rewards) { + for (const item of stageQuest.rewards) { + switch (item.rewardType) { + case "RT_STORE_ITEM": + await addItem(accountId, item.itemType.replace("/Lotus/StoreItems/", "/Lotus/"), 1); + break; + case "RT_CREDITS": + inventory.RegularCredits += item.amount; + await inventory.save(); + break; + } + } + } + } + + if (stage.itemsToGiveWhenTriggered.length > 0) { + let itemType = stage.itemsToGiveWhenTriggered[0]; + if (itemType.indexOf("") > 0) { + itemType = itemType.replace("/Lotus/StoreItems/", "/Lotus/"); + } + await addItem(accountId, itemType, 1); + + if (itemType in ExportRecipes) { + result.Recipes = [ + { + ItemType: itemType, + ItemCount: 1 + } + ]; + } + + if (itemType in ExportResources) { + result.WishlistChanges = [itemType]; + result.MiscItems = [ + { + ItemType: itemType, + ItemCount: 1 + } + ]; + } + + // more + } else { + result.MissionRewards = []; + } + } + + return result; +}; + +export const giveKeyChainTriggeredMessage = (accountId: string, keyChain: string, chainStage: number): null => { + logger.debug("accountId:" + accountId + "keyChain: " + keyChain + " chainStage: " + chainStage); + // TODO:message + + return null; +}; diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 3c55823e..23abfc8f 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -290,6 +290,7 @@ export interface IInventoryResponse { PendingCoupon: IPendingCoupon; Harvestable: boolean; DeathSquadable: boolean; + ActiveQuest: string; } export interface IAffiliation { diff --git a/src/types/questTypes.ts b/src/types/questTypes.ts new file mode 100644 index 00000000..2e4c891f --- /dev/null +++ b/src/types/questTypes.ts @@ -0,0 +1,45 @@ +import { IOid } from "./commonTypes"; +import { IQuestKeyDatabase } from "./inventoryTypes/inventoryTypes"; + +export interface ISetActiveQuestKey { + ItemType: string; +} + +export interface ISetActiveQuestHerse extends ISetActiveQuestKey { + ItemId: IOid; +} + +export interface ISetActiveQuestResponse { + inventoryChanges: { + QuestKey: ISetActiveQuestKey[]; + Herses: ISetActiveQuestHerse[]; + PremiumCreditsFree: number; + PremiumCredits: number; + RegularCredits: number; + }; +} + +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 IGiveKeyChainTriggeredItemsResponse { + WishlistChanges?: [string]; + MiscItems?: [{ ItemType: string; ItemCount: number }]; + Recipes?: [{ ItemType: string; ItemCount: number }]; + MissionRewards?: []; +}