From 7ab5dde8833d59af274e91cb6e4c405b06f6f70b Mon Sep 17 00:00:00 2001 From: Sainan Date: Sun, 2 Mar 2025 17:02:11 +0100 Subject: [PATCH] WIP: feat: resource extractor drones --- package-lock.json | 8 +- package.json | 2 +- src/controllers/api/dronesController.ts | 132 ++++++++++++++++++- src/models/inventoryModels/inventoryModel.ts | 19 ++- src/routes/api.ts | 1 + src/types/inventoryTypes/inventoryTypes.ts | 7 + 6 files changed, 160 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8f67ed87..ad2dcbc8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "copyfiles": "^2.4.1", "express": "^5", "mongoose": "^8.11.0", - "warframe-public-export-plus": "^0.5.38", + "warframe-public-export-plus": "^0.5.39", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" @@ -4083,9 +4083,9 @@ } }, "node_modules/warframe-public-export-plus": { - "version": "0.5.38", - "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.38.tgz", - "integrity": "sha512-yvc86eOmYPSnnU8LzLBhg/lR1AS1RHID24TqFHVcZuOzMYc934NL8Cv7rtllyefWAMyl7iA5x9tyXSuJWbi6CA==" + "version": "0.5.39", + "resolved": "https://registry.npmjs.org/warframe-public-export-plus/-/warframe-public-export-plus-0.5.39.tgz", + "integrity": "sha512-sEGZedtW4I/M2ceoDs6MQ5eHD7sJgv1KRNLt8BWByXLuDa7qTR3Y9px5TGxqt/rBHKGUyPO1LUxu4bDGZi6yXw==" }, "node_modules/warframe-riven-info": { "version": "0.1.2", diff --git a/package.json b/package.json index f7a70d38..aae92ef4 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "copyfiles": "^2.4.1", "express": "^5", "mongoose": "^8.11.0", - "warframe-public-export-plus": "^0.5.38", + "warframe-public-export-plus": "^0.5.39", "warframe-riven-info": "^0.1.2", "winston": "^3.17.0", "winston-daily-rotate-file": "^5.0.0" diff --git a/src/controllers/api/dronesController.ts b/src/controllers/api/dronesController.ts index bff5086c..e4672800 100644 --- a/src/controllers/api/dronesController.ts +++ b/src/controllers/api/dronesController.ts @@ -1,7 +1,133 @@ +import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers"; +import { addMiscItems, getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getRandomElement, getRandomInt } from "@/src/services/rngService"; +import { IMongoDate, IOid } from "@/src/types/commonTypes"; +import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { RequestHandler } from "express"; +import { ExportDrones, ExportResources, ExportSystems } from "warframe-public-export-plus"; -const dronesController: RequestHandler = (_req, res) => { - res.json({}); +export const dronesController: RequestHandler = async (req, res) => { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + if ("GetActive" in req.query) { + const activeDrones: IActiveDrone[] = []; + for (const drone of inventory.Drones) { + if (drone.DeployTime) { + activeDrones.push({ + DeployTime: toMongoDate(drone.DeployTime), + System: drone.System!, + ItemId: toOid(drone._id), + ItemType: drone.ItemType, + CurrentHP: drone.CurrentHP, + DamageTime: drone.DamageTime ? toMongoDate(drone.DamageTime) : undefined, + PendingDamage: drone.PendingDamage, + Resources: [ + { + ItemType: drone.ResourceType!, + BinTotal: drone.ResourceCount!, + StartTime: toMongoDate(drone.DeployTime) + } + ] + }); + } + } + res.json({ + ActiveDrones: activeDrones + }); + } else if ("droneId" in req.query && "systemIndex" in req.query) { + const instantResourceDrones = true; + const dronesAlwaysTakeDamage = true; + const drone = inventory.Drones.id(req.query.droneId as string)!; + const droneMeta = ExportDrones[drone.ItemType]; + drone.DeployTime = instantResourceDrones ? new Date(0) : new Date(); + if (drone.RepairStart) { + const repairMinutes = (Date.now() - drone.RepairStart.getTime()) / 60_000; + const hpPerMinute = droneMeta.repairRate / 60; + drone.CurrentHP = Math.min(drone.CurrentHP + Math.round(repairMinutes * hpPerMinute), droneMeta.durability); + drone.RepairStart = undefined; + } + drone.System = parseInt(req.query.systemIndex as string); + const system = ExportSystems[drone.System - 1]; + if (dronesAlwaysTakeDamage || Math.random() < system.damageChance) { + drone.DamageTime = instantResourceDrones + ? new Date() + : new Date(Date.now() + getRandomInt(3, 4) * 3600_000); + drone.PendingDamage = getRandomInt(system.droneDamage.minValue, system.droneDamage.maxValue); + } + const resource = getRandomElement(system.resources); + drone.ResourceType = "/Lotus/" + resource.StoreItem.substring(18); + const resourceMeta = ExportResources[drone.ResourceType]; + if (resourceMeta.pickupQuantity) { + const pickupsToCollect = droneMeta.binCapacity * droneMeta.capacityMultipliers[resource.Rarity]; + drone.ResourceCount = 0; + for (let i = 0; i != pickupsToCollect; ++i) { + drone.ResourceCount += getRandomInt( + resourceMeta.pickupQuantity.minValue, + resourceMeta.pickupQuantity.maxValue + ); + } + } else { + drone.ResourceCount = 1; + } + await inventory.save(); + res.json({}); + } else if ("collectDroneId" in req.query) { + const drone = inventory.Drones.id(req.query.collectDroneId as string)!; + + if (drone.DamageTime && new Date() >= drone.DamageTime) { + drone.CurrentHP -= drone.PendingDamage!; + drone.RepairStart = new Date(); + } + + const inventoryChanges: IInventoryChanges = {}; + if (drone.CurrentHP <= 0) { + inventory.RegularCredits += 100; + inventoryChanges.RegularCredits = 100; + inventory.Drones.pull({ _id: req.query.collectDroneId as string }); + // TODO: Response format? + } else { + const completionTime = drone.DeployTime!.getTime() + ExportDrones[drone.ItemType].fillRate * 3600_000; + if (Date.now() >= completionTime) { + const miscItemChanges = [ + { + ItemType: drone.ResourceType!, + ItemCount: drone.ResourceCount! + } + ]; + addMiscItems(inventory, miscItemChanges); + inventoryChanges.MiscItems = miscItemChanges; + } + + drone.DeployTime = undefined; + drone.System = undefined; + drone.DamageTime = undefined; + drone.PendingDamage = undefined; + drone.ResourceType = undefined; + drone.ResourceCount = undefined; + } + + await inventory.save(); + res.json({ + // TODO: Let the client know the new HP of this drone... somehow + InventoryChanges: inventoryChanges + }); + } else { + throw new Error(`drones.php query not handled`); + } }; -export { dronesController }; +interface IActiveDrone { + DeployTime: IMongoDate; + System: number; + ItemId: IOid; + ItemType: string; + CurrentHP: number; + DamageTime?: IMongoDate; + PendingDamage?: number; + Resources: { + ItemType: string; + BinTotal: number; + StartTime: IMongoDate; + }[]; +} diff --git a/src/models/inventoryModels/inventoryModel.ts b/src/models/inventoryModels/inventoryModel.ts index b21eee02..2aa16bb9 100644 --- a/src/models/inventoryModels/inventoryModel.ts +++ b/src/models/inventoryModels/inventoryModel.ts @@ -336,7 +336,14 @@ const droneSchema = new Schema( { ItemType: String, CurrentHP: Number, - RepairStart: { type: Date, default: undefined } + RepairStart: { type: Date, default: undefined }, + + DeployTime: { type: Date, default: undefined }, + System: Number, + DamageTime: { type: Date, default: undefined }, + PendingDamage: Number, + ResourceType: String, + ResourceCount: Number }, { id: false } ); @@ -347,6 +354,16 @@ droneSchema.set("toJSON", { const db = obj as IDroneDatabase; client.ItemId = toOid(db._id); + if (db.RepairStart) { + client.RepairStart = toMongoDate(db.RepairStart); + } + + delete db.DeployTime; + delete db.System; + delete db.DamageTime; + delete db.PendingDamage; + delete db.ResourceType; + delete db.ResourceCount; delete obj._id; delete obj.__v; diff --git a/src/routes/api.ts b/src/routes/api.ts index af8de31a..c303059d 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -145,6 +145,7 @@ apiRouter.post("/claimCompletedRecipe.php", claimCompletedRecipeController); apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController); apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController); apiRouter.post("/createGuild.php", createGuildController); +apiRouter.post("/drones.php", dronesController); apiRouter.post("/endlessXp.php", endlessXpController); apiRouter.post("/evolveWeapon.php", evolveWeaponController); apiRouter.post("/findSessions.php", findSessionsController); diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index d81491c1..0496a050 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -520,6 +520,13 @@ export interface IDroneDatabase { CurrentHP: number; _id: Types.ObjectId; RepairStart?: Date; + + DeployTime?: Date; + System?: number; + DamageTime?: Date; + PendingDamage?: number; + ResourceType?: string; + ResourceCount?: number; } export interface ITypeXPItem {