diff --git a/src/controllers/api/gardeningController.ts b/src/controllers/api/gardeningController.ts new file mode 100644 index 00000000..1913bd63 --- /dev/null +++ b/src/controllers/api/gardeningController.ts @@ -0,0 +1,84 @@ +import { toMongoDate } from "@/src/helpers/inventoryHelpers"; +import { getJSONfromString } from "@/src/helpers/stringHelpers"; +import { addMiscItem, getInventory } from "@/src/services/inventoryService"; +import { toStoreItem } from "@/src/services/itemDataService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { createGarden, getPersonalRooms } from "@/src/services/personalRoomsService"; +import { IMongoDate } from "@/src/types/commonTypes"; +import { IMissionReward } from "@/src/types/missionTypes"; +import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes"; +import { IInventoryChanges } from "@/src/types/purchaseTypes"; +import { IGardeningClient } from "@/src/types/shipTypes"; +import { RequestHandler } from "express"; +import { dict_en, ExportResources } from "warframe-public-export-plus"; + +export const gardeningController: RequestHandler = async (req, res) => { + const data = getJSONfromString(String(req.body)); + if (data.Mode != "HarvestAll") { + throw new Error(`unexpected gardening mode: ${data.Mode}`); + } + + const accountId = await getAccountIdForRequest(req); + const [inventory, personalRooms] = await Promise.all([ + getInventory(accountId, "MiscItems"), + getPersonalRooms(accountId, "Apartment") + ]); + + // Harvest plants + const inventoryChanges: IInventoryChanges = {}; + const rewards: Record = {}; + for (const planter of personalRooms.Apartment.Gardening.Planters) { + rewards[planter.Name] = []; + for (const plant of planter.Plants) { + const itemType = + "/Lotus/Types/Gameplay/Duviri/Resource/DuviriPlantItem" + + plant.PlantType.substring(plant.PlantType.length - 1); + const itemCount = Math.random() < 0.775 ? 2 : 4; + + addMiscItem(inventory, itemType, itemCount, inventoryChanges); + + rewards[planter.Name].push([ + { + StoreItem: toStoreItem(itemType), + TypeName: itemType, + ItemCount: itemCount, + DailyCooldown: false, + Rarity: itemCount == 2 ? 0.7743589743589744 : 0.22564102564102564, + TweetText: `${itemCount}x ${dict_en[ExportResources[itemType].name]} (Resource)`, + ProductCategory: "MiscItems" + } + ]); + } + } + + // Refresh garden + personalRooms.Apartment.Gardening = createGarden(); + + await Promise.all([inventory.save(), personalRooms.save()]); + + const planter = personalRooms.Apartment.Gardening.Planters[personalRooms.Apartment.Gardening.Planters.length - 1]; + const plant = planter.Plants[planter.Plants.length - 1]; + res.json({ + GardenTagName: planter.Name, + PlantType: plant.PlantType, + PlotIndex: plant.PlotIndex, + EndTime: toMongoDate(plant.EndTime), + InventoryChanges: inventoryChanges, + Gardening: personalRooms.toJSON().Apartment.Gardening, + Rewards: rewards + } satisfies IGardeningResponse); +}; + +interface IGardeningRequest { + Mode: string; +} + +interface IGardeningResponse { + GardenTagName: string; + PlantType: string; + PlotIndex: number; + EndTime: IMongoDate; + InventoryChanges: IInventoryChanges; + Gardening: IGardeningClient; + Rewards: Record; +} diff --git a/src/routes/api.ts b/src/routes/api.ts index b11af3e4..f594c845 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -48,6 +48,7 @@ import { findSessionsController } from "@/src/controllers/api/findSessionsContro import { fishmongerController } from "@/src/controllers/api/fishmongerController"; import { focusController } from "@/src/controllers/api/focusController"; import { fusionTreasuresController } from "@/src/controllers/api/fusionTreasuresController"; +import { gardeningController } from "@/src/controllers/api/gardeningController"; import { genericUpdateController } from "@/src/controllers/api/genericUpdateController"; import { getAllianceController } from "@/src/controllers/api/getAllianceController"; import { getDailyDealStockLevelsController } from "@/src/controllers/api/getDailyDealStockLevelsController"; @@ -240,6 +241,7 @@ apiRouter.post("/findSessions.php", findSessionsController); apiRouter.post("/fishmonger.php", fishmongerController); apiRouter.post("/focus.php", focusController); apiRouter.post("/fusionTreasures.php", fusionTreasuresController); +apiRouter.post("/gardening.php", gardeningController); apiRouter.post("/genericUpdate.php", genericUpdateController); apiRouter.post("/getAlliance.php", getAllianceController); apiRouter.post("/getFriends.php", getFriendsController); diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 9d1bf0ed..b73c4da5 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -213,6 +213,15 @@ export const combineInventoryChanges = (InventoryChanges: IInventoryChanges, del for (const key in delta) { if (!(key in InventoryChanges)) { InventoryChanges[key] = delta[key]; + } else if (key == "MiscItems") { + for (const deltaItem of delta[key]!) { + const existing = InventoryChanges[key]!.find(x => x.ItemType == deltaItem.ItemType); + if (existing) { + existing.ItemCount += deltaItem.ItemCount; + } else { + InventoryChanges[key]!.push(deltaItem); + } + } } else if (Array.isArray(delta[key])) { const left = InventoryChanges[key] as object[]; const right: object[] = delta[key]; @@ -1468,6 +1477,22 @@ export const addGearExpByCategory = ( }); }; +export const addMiscItem = ( + inventory: TInventoryDatabaseDocument, + type: string, + count: number, + inventoryChanges: IInventoryChanges +): void => { + const miscItemChanges: IMiscItem[] = [ + { + ItemType: type, + ItemCount: count + } + ]; + addMiscItems(inventory, miscItemChanges); + combineInventoryChanges(inventoryChanges, { MiscItems: miscItemChanges }); +}; + export const addMiscItems = (inventory: TInventoryDatabaseDocument, itemsArray: IMiscItem[]): void => { const { MiscItems } = inventory; diff --git a/src/services/personalRoomsService.ts b/src/services/personalRoomsService.ts index 310c2538..97f1b41d 100644 --- a/src/services/personalRoomsService.ts +++ b/src/services/personalRoomsService.ts @@ -4,8 +4,11 @@ import { TPersonalRoomsDatabaseDocument } from "../types/personalRoomsTypes"; import { IGardeningDatabase } from "../types/shipTypes"; import { getRandomElement } from "./rngService"; -export const getPersonalRooms = async (accountId: string): Promise => { - const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }); +export const getPersonalRooms = async ( + accountId: string, + projection?: string +): Promise => { + const personalRooms = await PersonalRooms.findOne({ personalRoomsOwnerId: accountId }, projection); if (!personalRooms) { throw new Error(`personal rooms not found for account ${accountId}`); diff --git a/src/types/missionTypes.ts b/src/types/missionTypes.ts index be1d08bc..3de75318 100644 --- a/src/types/missionTypes.ts +++ b/src/types/missionTypes.ts @@ -8,6 +8,8 @@ export interface IMissionReward { TypeName?: string; UpgradeLevel?: number; ItemCount: number; + DailyCooldown?: boolean; + Rarity?: number; TweetText?: string; ProductCategory?: string; FromEnemyCache?: boolean;