From c4ab496aa3569ccde5579e43f296923060c5156b Mon Sep 17 00:00:00 2001 From: Sainan Date: Wed, 5 Mar 2025 23:54:47 -0800 Subject: [PATCH] feat: dojo decorations (#1079) Closes #525 Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/1079 --- .../api/abortDojoComponentController.ts | 16 +++- .../contributeToDojoComponentController.ts | 89 +++++++++++++------ .../api/destroyDojoDecoController.ts | 19 ++++ .../api/dojoComponentRushController.ts | 42 ++++++--- src/controllers/api/getGuildDojoController.ts | 3 +- .../api/placeDecoInComponentController.ts | 43 +++++++++ ...queueDojoComponentDestructionController.ts | 15 +--- .../api/startDojoRecipeController.ts | 3 +- src/models/guildModel.ts | 18 +++- src/routes/api.ts | 4 + src/services/guildService.ts | 45 +++++++++- src/types/guildTypes.ts | 31 +++++-- 12 files changed, 260 insertions(+), 68 deletions(-) create mode 100644 src/controllers/api/destroyDojoDecoController.ts create mode 100644 src/controllers/api/placeDecoInComponentController.ts diff --git a/src/controllers/api/abortDojoComponentController.ts b/src/controllers/api/abortDojoComponentController.ts index da10a839..02c69cec 100644 --- a/src/controllers/api/abortDojoComponentController.ts +++ b/src/controllers/api/abortDojoComponentController.ts @@ -1,4 +1,4 @@ -import { getDojoClient, getGuildForRequestEx } from "@/src/services/guildService"; +import { getDojoClient, getGuildForRequestEx, removeDojoDeco, removeDojoRoom } from "@/src/services/guildService"; import { getInventory } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; import { RequestHandler } from "express"; @@ -8,12 +8,20 @@ export const abortDojoComponentController: RequestHandler = async (req, res) => const inventory = await getInventory(accountId); const guild = await getGuildForRequestEx(req, inventory); const request = JSON.parse(String(req.body)) as IAbortDojoComponentRequest; + // TODO: Move already-contributed credits & items to the clan vault - guild.DojoComponents.pull({ _id: request.ComponentId }); + if (request.DecoId) { + removeDojoDeco(guild, request.ComponentId, request.DecoId); + } else { + removeDojoRoom(guild, request.ComponentId); + } + await guild.save(); - res.json(getDojoClient(guild, 0)); + res.json(getDojoClient(guild, 0, request.ComponentId)); }; -export interface IAbortDojoComponentRequest { +interface IAbortDojoComponentRequest { + DecoType?: string; ComponentId: string; + DecoId?: string; } diff --git a/src/controllers/api/contributeToDojoComponentController.ts b/src/controllers/api/contributeToDojoComponentController.ts index 827475b0..bde6d6ff 100644 --- a/src/controllers/api/contributeToDojoComponentController.ts +++ b/src/controllers/api/contributeToDojoComponentController.ts @@ -1,10 +1,26 @@ +import { TGuildDatabaseDocument } from "@/src/models/guildModel"; +import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { getDojoClient, getGuildForRequestEx, scaleRequiredCount } from "@/src/services/guildService"; import { addMiscItems, getInventory, updateCurrency } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; +import { IDojoContributable } from "@/src/types/guildTypes"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { RequestHandler } from "express"; -import { ExportDojoRecipes } from "warframe-public-export-plus"; +import { ExportDojoRecipes, IDojoRecipe } from "warframe-public-export-plus"; + +interface IContributeToDojoComponentRequest { + ComponentId: string; + DecoId?: string; + DecoType?: string; + IngredientContributions: { + ItemType: string; + ItemCount: number; + }[]; + RegularCredits: number; + VaultIngredientContributions: []; + VaultCredits: number; +} export const contributeToDojoComponentController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -13,21 +29,54 @@ export const contributeToDojoComponentController: RequestHandler = async (req, r // Any clan member should have permission to contribute although notably permission is denied if they have not crafted the dojo key and were simply invited in. const request = JSON.parse(String(req.body)) as IContributeToDojoComponentRequest; const component = guild.DojoComponents.id(request.ComponentId)!; - const componentMeta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!; + const inventoryChanges: IInventoryChanges = {}; + if (!component.CompletionTime) { + // Room is in "Collecting Materials" state + if (request.DecoId) { + throw new Error("attempt to contribute to a deco in an unfinished room?!"); + } + const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!; + await processContribution(guild, request, inventory, inventoryChanges, meta, component); + } else { + // Room is past "Collecting Materials" + if (request.DecoId) { + const deco = component.Decos!.find(x => x._id.equals(request.DecoId))!; + const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type)!; + await processContribution(guild, request, inventory, inventoryChanges, meta, deco); + } + } + + await guild.save(); + await inventory.save(); + res.json({ + ...getDojoClient(guild, 0, component._id), + InventoryChanges: inventoryChanges + }); +}; + +const processContribution = async ( + guild: TGuildDatabaseDocument, + request: IContributeToDojoComponentRequest, + inventory: TInventoryDatabaseDocument, + inventoryChanges: IInventoryChanges, + meta: IDojoRecipe, + component: IDojoContributable +): Promise => { component.RegularCredits ??= 0; - if (component.RegularCredits + request.RegularCredits > scaleRequiredCount(componentMeta.price)) { - request.RegularCredits = scaleRequiredCount(componentMeta.price) - component.RegularCredits; + if (component.RegularCredits + request.RegularCredits > scaleRequiredCount(meta.price)) { + request.RegularCredits = scaleRequiredCount(meta.price) - component.RegularCredits; } component.RegularCredits += request.RegularCredits; - const inventoryChanges: IInventoryChanges = updateCurrency(inventory, request.RegularCredits, false); + inventoryChanges.RegularCredits = -request.RegularCredits; + updateCurrency(inventory, request.RegularCredits, false); component.MiscItems ??= []; const miscItemChanges: IMiscItem[] = []; for (const ingredientContribution of request.IngredientContributions) { const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredientContribution.ItemType); if (componentMiscItem) { - const ingredientMeta = componentMeta.ingredients.find(x => x.ItemType == ingredientContribution.ItemType)!; + const ingredientMeta = meta.ingredients.find(x => x.ItemType == ingredientContribution.ItemType)!; if ( componentMiscItem.ItemCount + ingredientContribution.ItemCount > scaleRequiredCount(ingredientMeta.ItemCount) @@ -47,9 +96,9 @@ export const contributeToDojoComponentController: RequestHandler = async (req, r addMiscItems(inventory, miscItemChanges); inventoryChanges.MiscItems = miscItemChanges; - if (component.RegularCredits >= scaleRequiredCount(componentMeta.price)) { + if (component.RegularCredits >= scaleRequiredCount(meta.price)) { let fullyFunded = true; - for (const ingredient of componentMeta.ingredients) { + for (const ingredient of meta.ingredients) { const componentMiscItem = component.MiscItems.find(x => x.ItemType == ingredient.ItemType); if (!componentMiscItem || componentMiscItem.ItemCount < scaleRequiredCount(ingredient.ItemCount)) { fullyFunded = false; @@ -57,27 +106,13 @@ export const contributeToDojoComponentController: RequestHandler = async (req, r } } if (fullyFunded) { + if (request.IngredientContributions.length) { + // We've already updated subpaths of MiscItems, we need to allow MongoDB to save this before we remove MiscItems. + await guild.save(); + } component.RegularCredits = undefined; component.MiscItems = undefined; - component.CompletionTime = new Date(Date.now() + componentMeta.time * 1000); + component.CompletionTime = new Date(Date.now() + meta.time * 1000); } } - - await guild.save(); - await inventory.save(); - res.json({ - ...getDojoClient(guild, 0, component._id), - InventoryChanges: inventoryChanges - }); }; - -export interface IContributeToDojoComponentRequest { - ComponentId: string; - IngredientContributions: { - ItemType: string; - ItemCount: number; - }[]; - RegularCredits: number; - VaultIngredientContributions: []; - VaultCredits: number; -} diff --git a/src/controllers/api/destroyDojoDecoController.ts b/src/controllers/api/destroyDojoDecoController.ts new file mode 100644 index 00000000..d6884fe4 --- /dev/null +++ b/src/controllers/api/destroyDojoDecoController.ts @@ -0,0 +1,19 @@ +import { getDojoClient, getGuildForRequest, removeDojoDeco } from "@/src/services/guildService"; +import { RequestHandler } from "express"; + +export const destroyDojoDecoController: RequestHandler = async (req, res) => { + const guild = await getGuildForRequest(req); + const request = JSON.parse(String(req.body)) as IDestroyDojoDecoRequest; + + removeDojoDeco(guild, request.ComponentId, request.DecoId); + // TODO: The client says this is supposed to refund the resources to the clan vault, so we should probably do that. + + await guild.save(); + res.json(getDojoClient(guild, 0, request.ComponentId)); +}; + +interface IDestroyDojoDecoRequest { + DecoType: string; + ComponentId: string; + DecoId: string; +} diff --git a/src/controllers/api/dojoComponentRushController.ts b/src/controllers/api/dojoComponentRushController.ts index 91f5f13e..3058f8ef 100644 --- a/src/controllers/api/dojoComponentRushController.ts +++ b/src/controllers/api/dojoComponentRushController.ts @@ -1,8 +1,18 @@ import { getDojoClient, getGuildForRequestEx, scaleRequiredCount } from "@/src/services/guildService"; import { getInventory, updateCurrency } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; +import { IDojoContributable } from "@/src/types/guildTypes"; import { RequestHandler } from "express"; -import { ExportDojoRecipes } from "warframe-public-export-plus"; +import { ExportDojoRecipes, IDojoRecipe } from "warframe-public-export-plus"; + +interface IDojoComponentRushRequest { + DecoType?: string; + DecoId?: string; + ComponentId: string; + Amount: number; + VaultAmount: number; + AllianceVaultAmount: number; +} export const dojoComponentRushController: RequestHandler = async (req, res) => { const accountId = await getAccountIdForRequest(req); @@ -10,14 +20,16 @@ export const dojoComponentRushController: RequestHandler = async (req, res) => { const guild = await getGuildForRequestEx(req, inventory); const request = JSON.parse(String(req.body)) as IDojoComponentRushRequest; const component = guild.DojoComponents.id(request.ComponentId)!; - const componentMeta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!; - const fullPlatinumCost = scaleRequiredCount(componentMeta.skipTimePrice); - const fullDurationSeconds = componentMeta.time; - const secondsPerPlatinum = fullDurationSeconds / fullPlatinumCost; - component.CompletionTime = new Date( - component.CompletionTime!.getTime() - secondsPerPlatinum * request.Amount * 1000 - ); + if (request.DecoId) { + const deco = component.Decos!.find(x => x._id.equals(request.DecoId))!; + const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type)!; + processContribution(deco, meta, request.Amount); + } else { + const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf)!; + processContribution(component, meta, request.Amount); + } + const inventoryChanges = updateCurrency(inventory, request.Amount, true); await guild.save(); @@ -28,9 +40,11 @@ export const dojoComponentRushController: RequestHandler = async (req, res) => { }); }; -interface IDojoComponentRushRequest { - ComponentId: string; - Amount: number; - VaultAmount: number; - AllianceVaultAmount: number; -} +const processContribution = (component: IDojoContributable, meta: IDojoRecipe, platinumDonated: number): void => { + const fullPlatinumCost = scaleRequiredCount(meta.skipTimePrice); + const fullDurationSeconds = meta.time; + const secondsPerPlatinum = fullDurationSeconds / fullPlatinumCost; + component.CompletionTime = new Date( + component.CompletionTime!.getTime() - secondsPerPlatinum * platinumDonated * 1000 + ); +}; diff --git a/src/controllers/api/getGuildDojoController.ts b/src/controllers/api/getGuildDojoController.ts index dbfa0af9..560bf045 100644 --- a/src/controllers/api/getGuildDojoController.ts +++ b/src/controllers/api/getGuildDojoController.ts @@ -18,7 +18,8 @@ export const getGuildDojoController: RequestHandler = async (req, res) => { _id: new Types.ObjectId(), pf: "/Lotus/Levels/ClanDojo/DojoHall.level", ppf: "", - CompletionTime: new Date(Date.now()) + CompletionTime: new Date(Date.now()), + DecoCapacity: 600 }); await guild.save(); } diff --git a/src/controllers/api/placeDecoInComponentController.ts b/src/controllers/api/placeDecoInComponentController.ts new file mode 100644 index 00000000..d25ac548 --- /dev/null +++ b/src/controllers/api/placeDecoInComponentController.ts @@ -0,0 +1,43 @@ +import { getDojoClient, getGuildForRequest } from "@/src/services/guildService"; +import { RequestHandler } from "express"; +import { Types } from "mongoose"; +import { ExportDojoRecipes } from "warframe-public-export-plus"; + +export const placeDecoInComponentController: RequestHandler = async (req, res) => { + const guild = await getGuildForRequest(req); + const request = JSON.parse(String(req.body)) as IPlaceDecoInComponentRequest; + // At this point, we know that a member of the guild is making this request. Assuming they are allowed to place decorations. + const component = guild.DojoComponents.id(request.ComponentId)!; + + if (component.DecoCapacity === undefined) { + component.DecoCapacity = Object.values(ExportDojoRecipes.rooms).find( + x => x.resultType == component.pf + )!.decoCapacity; + } + + component.Decos ??= []; + component.Decos.push({ + _id: new Types.ObjectId(), + Type: request.Type, + Pos: request.Pos, + Rot: request.Rot, + Name: request.Name + }); + + const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == request.Type); + if (meta && meta.capacityCost) { + component.DecoCapacity -= meta.capacityCost; + } + + await guild.save(); + res.json(getDojoClient(guild, 0, component._id)); +}; + +interface IPlaceDecoInComponentRequest { + ComponentId: string; + Revision: number; + Type: string; + Pos: number[]; + Rot: number[]; + Name?: string; +} diff --git a/src/controllers/api/queueDojoComponentDestructionController.ts b/src/controllers/api/queueDojoComponentDestructionController.ts index 750f8392..2e30dc25 100644 --- a/src/controllers/api/queueDojoComponentDestructionController.ts +++ b/src/controllers/api/queueDojoComponentDestructionController.ts @@ -1,19 +1,12 @@ -import { getDojoClient, getGuildForRequest } from "@/src/services/guildService"; +import { getDojoClient, getGuildForRequest, removeDojoRoom } from "@/src/services/guildService"; import { RequestHandler } from "express"; -import { ExportDojoRecipes } from "warframe-public-export-plus"; export const queueDojoComponentDestructionController: RequestHandler = async (req, res) => { const guild = await getGuildForRequest(req); const componentId = req.query.componentId as string; - const component = guild.DojoComponents.splice( - guild.DojoComponents.findIndex(x => x._id.toString() === componentId), - 1 - )[0]; - const room = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf); - if (room) { - guild.DojoCapacity -= room.capacity; - guild.DojoEnergy -= room.energy; - } + + removeDojoRoom(guild, componentId); + await guild.save(); res.json(getDojoClient(guild, 1)); }; diff --git a/src/controllers/api/startDojoRecipeController.ts b/src/controllers/api/startDojoRecipeController.ts index 96a1e99c..0a0dfc66 100644 --- a/src/controllers/api/startDojoRecipeController.ts +++ b/src/controllers/api/startDojoRecipeController.ts @@ -29,7 +29,8 @@ export const startDojoRecipeController: RequestHandler = async (req, res) => { ppf: request.PlacedComponent.ppf, pi: new Types.ObjectId(request.PlacedComponent.pi!.$oid), op: request.PlacedComponent.op, - pp: request.PlacedComponent.pp + pp: request.PlacedComponent.pp, + DecoCapacity: room?.decoCapacity }) - 1 ]; if (config.noDojoRoomBuildStage) { diff --git a/src/models/guildModel.ts b/src/models/guildModel.ts index 417eeb7e..b3f47e04 100644 --- a/src/models/guildModel.ts +++ b/src/models/guildModel.ts @@ -2,12 +2,23 @@ import { IGuildDatabase, IDojoComponentDatabase, ITechProjectDatabase, - ITechProjectClient + ITechProjectClient, + IDojoDecoDatabase } from "@/src/types/guildTypes"; import { Document, Model, model, Schema, Types } from "mongoose"; import { typeCountSchema } from "./inventoryModels/inventoryModel"; import { toMongoDate } from "../helpers/inventoryHelpers"; +const dojoDecoSchema = new Schema({ + Type: String, + Pos: [Number], + Rot: [Number], + Name: String, + RegularCredits: Number, + MiscItems: { type: [typeCountSchema], default: undefined }, + CompletionTime: Date +}); + const dojoComponentSchema = new Schema({ pf: { type: String, required: true }, ppf: String, @@ -18,7 +29,10 @@ const dojoComponentSchema = new Schema({ Message: String, RegularCredits: Number, MiscItems: { type: [typeCountSchema], default: undefined }, - CompletionTime: Date + CompletionTime: Date, + DestructionTime: Date, + Decos: [dojoDecoSchema], + DecoCapacity: Number }); const techProjectSchema = new Schema( diff --git a/src/routes/api.ts b/src/routes/api.ts index 3437d665..72229b15 100644 --- a/src/routes/api.ts +++ b/src/routes/api.ts @@ -16,6 +16,7 @@ import { contributeToDojoComponentController } from "@/src/controllers/api/contr import { createGuildController } from "@/src/controllers/api/createGuildController"; import { creditsController } from "@/src/controllers/api/creditsController"; import { deleteSessionController } from "@/src/controllers/api/deleteSessionController"; +import { destroyDojoDecoController } from "@/src/controllers/api/destroyDojoDecoController"; import { dojoComponentRushController } from "@/src/controllers/api/dojoComponentRushController"; import { dojoController } from "@/src/controllers/api/dojoController"; import { dronesController } from "@/src/controllers/api/dronesController"; @@ -59,6 +60,7 @@ import { missionInventoryUpdateController } from "@/src/controllers/api/missionI import { modularWeaponCraftingController } from "@/src/controllers/api/modularWeaponCraftingController"; import { modularWeaponSaleController } from "@/src/controllers/api/modularWeaponSaleController"; import { nameWeaponController } from "@/src/controllers/api/nameWeaponController"; +import { placeDecoInComponentController } from "@/src/controllers/api/placeDecoInComponentController"; import { playerSkillsController } from "@/src/controllers/api/playerSkillsController"; import { projectionManagerController } from "@/src/controllers/api/projectionManagerController"; import { purchaseController } from "@/src/controllers/api/purchaseController"; @@ -150,6 +152,7 @@ apiRouter.post("/clearDialogueHistory.php", clearDialogueHistoryController); apiRouter.post("/completeRandomModChallenge.php", completeRandomModChallengeController); apiRouter.post("/contributeToDojoComponent.php", contributeToDojoComponentController); apiRouter.post("/createGuild.php", createGuildController); +apiRouter.post("/destroyDojoDeco.php", destroyDojoDecoController); apiRouter.post("/dojoComponentRush.php", dojoComponentRushController); apiRouter.post("/drones.php", dronesController); apiRouter.post("/endlessXp.php", endlessXpController); @@ -175,6 +178,7 @@ apiRouter.post("/login.php", loginController); apiRouter.post("/missionInventoryUpdate.php", missionInventoryUpdateController); apiRouter.post("/modularWeaponCrafting.php", modularWeaponCraftingController); apiRouter.post("/nameWeapon.php", nameWeaponController); +apiRouter.post("/placeDecoInComponent.php", placeDecoInComponentController); apiRouter.post("/playerSkills.php", playerSkillsController); apiRouter.post("/projectionManager.php", projectionManagerController); apiRouter.post("/purchase.php", purchaseController); diff --git a/src/services/guildService.ts b/src/services/guildService.ts index 5a4658e1..19a273f2 100644 --- a/src/services/guildService.ts +++ b/src/services/guildService.ts @@ -6,6 +6,7 @@ import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/invento import { IDojoClient, IDojoComponentClient } from "@/src/types/guildTypes"; import { toMongoDate, toOid } from "@/src/helpers/inventoryHelpers"; import { Types } from "mongoose"; +import { ExportDojoRecipes } from "warframe-public-export-plus"; export const getGuildForRequest = async (req: Request): Promise => { const accountId = await getAccountIdForRequest(req); @@ -31,7 +32,7 @@ export const getGuildForRequestEx = async ( export const getDojoClient = ( guild: TGuildDatabaseDocument, status: number, - componentId: Types.ObjectId | undefined = undefined + componentId: Types.ObjectId | string | undefined = undefined ): IDojoClient => { const dojo: IDojoClient = { _id: { $oid: guild._id.toString() }, @@ -46,14 +47,14 @@ export const getDojoClient = ( DojoComponents: [] }; guild.DojoComponents.forEach(dojoComponent => { - if (!componentId || componentId == dojoComponent._id) { + if (!componentId || dojoComponent._id.equals(componentId)) { const clientComponent: IDojoComponentClient = { id: toOid(dojoComponent._id), pf: dojoComponent.pf, ppf: dojoComponent.ppf, Name: dojoComponent.Name, Message: dojoComponent.Message, - DecoCapacity: 600 + DecoCapacity: dojoComponent.DecoCapacity ?? 600 }; if (dojoComponent.pi) { clientComponent.pi = toOid(dojoComponent.pi); @@ -66,6 +67,20 @@ export const getDojoClient = ( clientComponent.RegularCredits = dojoComponent.RegularCredits; clientComponent.MiscItems = dojoComponent.MiscItems; } + if (dojoComponent.Decos) { + clientComponent.Decos = []; + for (const deco of dojoComponent.Decos) { + clientComponent.Decos.push({ + id: toOid(deco._id), + Type: deco.Type, + Pos: deco.Pos, + Rot: deco.Rot, + CompletionTime: deco.CompletionTime ? toMongoDate(deco.CompletionTime) : undefined, + RegularCredits: deco.RegularCredits, + MiscItems: deco.MiscItems + }); + } + } dojo.DojoComponents.push(clientComponent); } }); @@ -76,3 +91,27 @@ export const scaleRequiredCount = (count: number): number => { // The recipes in the export are for Moon clans. For now we'll just assume we only have Ghost clans. return Math.max(1, Math.trunc(count / 100)); }; + +export const removeDojoRoom = (guild: TGuildDatabaseDocument, componentId: string): void => { + const component = guild.DojoComponents.splice( + guild.DojoComponents.findIndex(x => x._id.equals(componentId)), + 1 + )[0]; + const meta = Object.values(ExportDojoRecipes.rooms).find(x => x.resultType == component.pf); + if (meta) { + guild.DojoCapacity -= meta.capacity; + guild.DojoEnergy -= meta.energy; + } +}; + +export const removeDojoDeco = (guild: TGuildDatabaseDocument, componentId: string, decoId: string): void => { + const component = guild.DojoComponents.id(componentId)!; + const deco = component.Decos!.splice( + component.Decos!.findIndex(x => x._id.equals(decoId)), + 1 + )[0]; + const meta = Object.values(ExportDojoRecipes.decos).find(x => x.resultType == deco.Type); + if (meta && meta.capacityCost) { + component.DecoCapacity! += meta.capacityCost; + } +}; diff --git a/src/types/guildTypes.ts b/src/types/guildTypes.ts index 4b909708..176cfeea 100644 --- a/src/types/guildTypes.ts +++ b/src/types/guildTypes.ts @@ -41,18 +41,33 @@ export interface IDojoComponentClient { CompletionTime?: IMongoDate; RushPlatinum?: number; DestructionTime?: IMongoDate; + Decos?: IDojoDecoClient[]; DecoCapacity?: number; } export interface IDojoComponentDatabase - extends Omit< - IDojoComponentClient, - "id" | "pi" | "CompletionTime" | "RushPlatinum" | "DestructionTime" | "DecoCapacity" - > { + extends Omit { _id: Types.ObjectId; pi?: Types.ObjectId; CompletionTime?: Date; - //DestructionTime?: Date; + DestructionTime?: Date; + Decos?: IDojoDecoDatabase[]; +} + +export interface IDojoDecoClient { + id: IOid; + Type: string; + Pos: number[]; + Rot: number[]; + Name?: string; // for teleporters + RegularCredits?: number; + MiscItems?: IMiscItem[]; + CompletionTime?: IMongoDate; +} + +export interface IDojoDecoDatabase extends Omit { + _id: Types.ObjectId; + CompletionTime?: Date; } export interface ITechProjectClient { @@ -66,3 +81,9 @@ export interface ITechProjectClient { export interface ITechProjectDatabase extends Omit { CompletionDate?: Date; } + +export interface IDojoContributable { + RegularCredits?: number; + MiscItems?: IMiscItem[]; + CompletionTime?: Date; +}