diff --git a/src/constants/timeConstants.ts b/src/constants/timeConstants.ts index 4a5a374f..32a03742 100644 --- a/src/constants/timeConstants.ts +++ b/src/constants/timeConstants.ts @@ -3,11 +3,13 @@ const secondsPerMinute = 60; const minutesPerHour = 60; const hoursPerDay = 24; +const unixSecond = millisecondsPerSecond; const unixMinute = secondsPerMinute * millisecondsPerSecond; const unixHour = unixMinute * minutesPerHour; const unixDay = hoursPerDay * unixHour; export const unixTimesInMs = { + second: unixSecond, minute: unixMinute, hour: unixHour, day: unixDay diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index 4aef62da..02120194 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -3,8 +3,62 @@ import { Request, RequestHandler, Response } from "express"; import { logger } from "@/src/utils/logger"; +import { getItemByBlueprint, getItemCategoryByUniqueName } from "@/src/services/itemDataService"; +import { IOid } from "@/src/types/commonTypes"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { getInventory } from "@/src/services/inventoryService"; +import { IInventoryDatabase } from "@/src/types/inventoryTypes/inventoryTypes"; -export const claimCompletedRecipeController: RequestHandler = async (_req: Request, res: Response) => { - logger.debug("Claiming Completed Recipe"); - res.json({ status: "success" }); +export interface IClaimCompletedRecipeRequest { + RecipeIds: IOid[]; +} + +// eslint-disable-next-line @typescript-eslint/no-misused-promises +export const claimCompletedRecipeController: RequestHandler = async (req, res) => { + const claimCompletedRecipeRequest = getJSONfromString(req.body.toString()) as IClaimCompletedRecipeRequest; + const accountId = req.query.accountId as string; + if (!accountId) throw new Error("no account id"); + + console.log(claimCompletedRecipeRequest); + const inventory = await getInventory(accountId); + const pendingRecipe = inventory.PendingRecipes.find( + recipe => recipe._id?.toString() === claimCompletedRecipeRequest.RecipeIds[0].$oid + ); + console.log(pendingRecipe); + if (!pendingRecipe) { + logger.error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`); + throw new Error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`); + } + + //check recipe is indeed ready to be completed + // if (pendingRecipe.CompletionDate > new Date()) { + // logger.error(`recipe ${pendingRecipe._id} is not ready to be completed`); + // throw new Error(`recipe ${pendingRecipe._id} is not ready to be completed`); + // } + + //get completed Items + const completedItemName = getItemByBlueprint(pendingRecipe.ItemType)?.uniqueName; + + if (!completedItemName) { + logger.error(`no completed item found for recipe ${pendingRecipe._id}`); + throw new Error(`no completed item found for recipe ${pendingRecipe._id}`); + } + const itemCategory = getItemCategoryByUniqueName(completedItemName) as keyof typeof inventory; + console.log(itemCategory); + //TODO: remove all Schema.Mixed for inventory[itemCategory] not to be any + //add item + //inventory[itemCategory]. + + //add additional item components like mods or weapons for a sentinel. + //const additionalItemComponents = itemComponents[uniqueName] + //add these items to inventory + //return changes as InventoryChanges + + //remove pending recipe + inventory.PendingRecipes.pull(pendingRecipe._id); + // await inventory.save(); + + logger.debug("Claiming Completed Recipe", { completedItemName }); + + res.json({ InventoryChanges: {} }); }; diff --git a/src/helpers/customHelpers/addItemHelpers.ts b/src/helpers/customHelpers/addItemHelpers.ts index 7ee47820..d26d571f 100644 --- a/src/helpers/customHelpers/addItemHelpers.ts +++ b/src/helpers/customHelpers/addItemHelpers.ts @@ -23,20 +23,20 @@ interface IAddItemRequest { InternalName: string; accountId: string; } -export const isInternalName = (internalName: string): boolean => { +export const isInternalItemName = (internalName: string): boolean => { const item = items.find(i => i.uniqueName === internalName); return Boolean(item); }; -const parseInternalName = (internalName: unknown): string => { - if (!isString(internalName) || !isInternalName(internalName)) { +const parseInternalItemName = (internalName: unknown): string => { + if (!isString(internalName) || !isInternalItemName(internalName)) { throw new Error("incorrect internal name"); } return internalName; }; -const toAddItemRequest = (body: unknown): IAddItemRequest => { +export const toAddItemRequest = (body: unknown): IAddItemRequest => { if (!body || typeof body !== "object") { throw new Error("incorrect or missing add item request data"); } @@ -44,12 +44,10 @@ const toAddItemRequest = (body: unknown): IAddItemRequest => { if ("type" in body && "internalName" in body && "accountId" in body) { return { type: parseItemType(body.type), - InternalName: parseInternalName(body.internalName), + InternalName: parseInternalItemName(body.internalName), accountId: parseString(body.accountId) }; } throw new Error("malformed add item request"); }; - -export { toAddItemRequest }; diff --git a/src/services/itemDataService.ts b/src/services/itemDataService.ts index 9052ff63..f0c3eddc 100644 --- a/src/services/itemDataService.ts +++ b/src/services/itemDataService.ts @@ -1,6 +1,5 @@ import { logger } from "@/src/utils/logger"; import Items, { Buildable, Category, Item, Warframe, Weapon } from "warframe-items"; -import { log } from "winston"; type MinWeapon = Omit; type MinItem = Omit; @@ -80,14 +79,46 @@ export const blueprintNames = Object.fromEntries( .map(name => [name, craftNames[name]]) ); -const items2 = new Items({ - category: ["Warframes", "Gear", "Melee", "Primary", "Secondary", "Sentinels", "Misc", "Arch-Gun", "Arch-Melee"] -}); +const buildables = items.filter(item => !!(item as Buildable).components); -items2.flatMap(item => item.components || []); -// for (const item of items2) { -// console.log(item.category === "Warframes"); -// if (item.category === "Warframes") { -// console.log(item); -// } -// } +export const getItemByBlueprint = (uniqueName: string): (MinItem & Buildable) | undefined => { + const item = buildables.find( + item => (item as Buildable).components?.find(component => component.uniqueName === uniqueName) + ); + return item; +}; + +export const getItemCategoryByUniqueName = (uniqueName: string) => { + //Lotus/Types/Items/MiscItems/PolymerBundle + + let splitWord = "Items/"; + if (!uniqueName.includes("/Items/")) { + splitWord = "/Types/"; + } + + const index = getIndexAfter(uniqueName, splitWord); + if (index === -1) { + logger.error(`error parsing item category ${uniqueName}`); + throw new Error(`error parsing item category ${uniqueName}`); + } + const category = uniqueName.substring(index).split("/")[0]; + return category; +}; + +export const getIndexAfter = (str: string, searchWord: string) => { + const index = str.indexOf(searchWord); + if (index === -1) { + return -1; + } + return index + searchWord.length; +}; + +export const getItemByUniqueName = (uniqueName: string) => { + const item = items.find(item => item.uniqueName === uniqueName); + return item; +}; + +export const getItemByName = (name: string) => { + const item = items.find(item => item.name === name); + return item; +}; diff --git a/src/services/recipeService.ts b/src/services/recipeService.ts index 7306cdc0..8bfdcc93 100644 --- a/src/services/recipeService.ts +++ b/src/services/recipeService.ts @@ -1,14 +1,68 @@ +import { unixTimesInMs } from "@/src/constants/timeConstants"; import { getInventory } from "@/src/services/inventoryService"; +import { getItemByBlueprint, getItemCategoryByUniqueName } from "@/src/services/itemDataService"; +import { logger } from "@/src/utils/logger"; import { Types } from "mongoose"; -export const startRecipe = async (recipeName: string, accountId: string) => { - //get recipe data +export interface IResource { + uniqueName: string; + count: number; +} + +// export const updateResources = async (accountId: string, components: IResource[]) => { +// const inventory = await getInventory(accountId); + +// for (const component of components) { +// const category = getItemCategoryByUniqueName(component.uniqueName) as keyof typeof inventory; +// //validate category + +// console.log(component.uniqueName); +// console.log("cate", category); + +// const invItem = inventory[category]; +// console.log("invItem", invItem); + +// inventory["MiscItems"]; +// } +// }; + +export const startRecipe = async (recipeName: string, accountId: string) => { + const recipe = getItemByBlueprint(recipeName); + + if (!recipe) { + logger.error(`unknown recipe ${recipeName}`); + throw new Error(`unknown recipe ${recipeName}`); + } + + const componentsNeeded = recipe.components?.map(component => ({ + uniqueName: component.uniqueName, + count: component.itemCount + })); + + if (!componentsNeeded) { + logger.error(`recipe ${recipeName} has no components`); + throw new Error(`recipe ${recipeName} has no components`); + } + + //TODO: consume components used + //await updateResources(accountId, componentsNeeded); + + //might be redundant + if (recipe.consumeOnBuild) { + //consume + } + + if (!recipe.buildTime) { + logger.error(`recipe ${recipeName} has no build time`); + throw new Error(`recipe ${recipeName} has no build time`); + } + //buildtime is in seconds + const completionDate = new Date(Date.now() + recipe.buildTime * unixTimesInMs.second); - //consume resources const inventory = await getInventory(accountId); inventory.PendingRecipes.push({ ItemType: recipeName, - CompletionDate: new Date(), + CompletionDate: completionDate, _id: new Types.ObjectId() });