From 119cf70e86ab06300cc0182a960746f97544a442 Mon Sep 17 00:00:00 2001 From: Aelita4 Date: Mon, 13 Jan 2025 10:49:30 +0100 Subject: [PATCH] Add defense system --- src/components/NavBar.astro | 7 + src/lib/classes/Defense.ts | 84 ++++++++ src/lib/classes/managers/FleetManager.ts | 52 +++-- .../classes/managers/PlanetDefenseManager.ts | 17 ++ src/lib/classes/managers/PlanetManager.ts | 2 + .../classes/managers/SystemDefenseManager.ts | 17 ++ src/lib/classes/managers/SystemManager.ts | 11 +- .../managers/abstract/DefenseManager.ts | 62 ++++++ src/lib/db/defenses.ts | 12 ++ src/lib/db/lang.ts | 3 +- src/lib/db/mongodb.ts | 8 +- src/lib/db/planets.ts | 9 + src/lib/db/systems.ts | 9 + src/pages/api/defenses/add.ts | 146 ++++++++++++++ src/pages/game/defenses.astro | 184 ++++++++++++++++++ src/types/db/DBDefenses.ts | 18 ++ src/types/db/DBPlanet.ts | 1 + src/types/db/DBSystem.ts | 4 + 18 files changed, 631 insertions(+), 15 deletions(-) create mode 100644 src/lib/classes/Defense.ts create mode 100644 src/lib/classes/managers/PlanetDefenseManager.ts create mode 100644 src/lib/classes/managers/SystemDefenseManager.ts create mode 100644 src/lib/classes/managers/abstract/DefenseManager.ts create mode 100644 src/lib/db/defenses.ts create mode 100644 src/pages/api/defenses/add.ts create mode 100644 src/pages/game/defenses.astro create mode 100644 src/types/db/DBDefenses.ts diff --git a/src/components/NavBar.astro b/src/components/NavBar.astro index eb93594..641d013 100644 --- a/src/components/NavBar.astro +++ b/src/components/NavBar.astro @@ -101,6 +101,13 @@ const listOfElements: Array = [{ url: "/game/fleet", show: "loggedInOnly", position: "bottom" +},{ + id: "defenses", + title: getName(lang, "general", "nav-defenses"), + type: "simple", + url: "/game/defenses", + show: "loggedInOnly", + position: "bottom" }, { id: "galaxyView", title: getName(lang, "general", "nav-galaxy-view"), diff --git a/src/lib/classes/Defense.ts b/src/lib/classes/Defense.ts new file mode 100644 index 0000000..8fa69ec --- /dev/null +++ b/src/lib/classes/Defense.ts @@ -0,0 +1,84 @@ +import DBDefenses from "../../types/db/DBDefenses"; +import DefenseManager from "./managers/abstract/DefenseManager"; +import SystemManager from "./managers/SystemManager"; + +export default class Defense { + manager: DefenseManager; + data: DBDefenses; + amount: number; + + constructor(manager: DefenseManager, data: DBDefenses, amount: number) { + this.manager = manager; + this.data = data; + this.amount = amount; + } + + async checkRequiredResources(): Promise { + const resources = await this.manager.manager.resources.calculateCurrentAvailableResources(); + + const requirements = this.data.requirements.resources; + + let canBuild = true; + + for(let res of requirements) { + const resource = resources.find(r => r.id === res.id); + if(!resource) return false; + + if(resource.amount < (res.amount * this.amount)) { + canBuild = false; + break; + } + } + + return canBuild; + } + + async checkRequirements(): Promise<{ canBuild: boolean, error: string }> { + if(!(this.manager.manager instanceof SystemManager)) { + const playerBuildings = this.manager.manager.buildings.buildings; + let playerBuildingsCanBuild = { canBuild: true, missing: "" }; + this.data.requirements.buildings.forEach((buildingReq: any) => { + if((playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0) < buildingReq.level) { + playerBuildingsCanBuild = { canBuild: false, missing: `${buildingReq.id} level ${buildingReq.level} required, found ${playerBuildings.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0}` }; + return; + } + }); + if(!playerBuildingsCanBuild.canBuild) return { + canBuild: false, + error: playerBuildingsCanBuild.missing + } + } /*else { //TODO: check for structures requirements + const structures = this.manager.manager.structures.structures; + let playerStructuresCanBuild = { canBuild: true, missing: "" }; + this.data.requirements.structures.forEach((buildingReq: any) => { + if((structures.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0) < buildingReq.level) { + playerStructuresCanBuild = { canBuild: false, missing: `${buildingReq.id} level ${buildingReq.level} required, found ${structures.filter((structure) => structure.data.id === buildingReq.id)[0]?.level ?? 0}` }; + return; + } + }); + if(!playerStructuresCanBuild.canBuild) return { + canBuild: false, + error: playerStructuresCanBuild.missing + } + }*/ + + // research + const playerResearch = this.manager.manager instanceof SystemManager ? this.manager.manager.data.ownedBy.research : this.manager.manager.system.data.ownedBy.research; + let playerResearchCanBuild = { canBuild: true, missing: "" }; + for(const researchReq of this.data.requirements.research) { + if(playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0 < researchReq.level) { + playerResearchCanBuild = { canBuild: false, missing: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0}` }; + } + }; + + if(!playerResearchCanBuild.canBuild) return { + canBuild: false, + error: playerResearchCanBuild.missing + } + + return { + canBuild: true, + error: "" + } + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/FleetManager.ts b/src/lib/classes/managers/FleetManager.ts index 206e16c..579230a 100644 --- a/src/lib/classes/managers/FleetManager.ts +++ b/src/lib/classes/managers/FleetManager.ts @@ -9,6 +9,7 @@ import { getRandomInRange, weightedRandom } from "../../utils/math"; import { Sector } from "./LocationManager"; import { Planet } from "./PlanetManager"; import SystemManager from "./SystemManager"; +import { getAllDefenses } from "../../db/defenses"; export type Fleet = { id: ObjectId, @@ -104,7 +105,7 @@ export default class FleetManager { case 'ATTACK': if(!("expedition" in this.data.destination)) { const enemyShips = this.data.destination.ships.ships; - return await this.battleResults(enemyShips.map(ship => { return { id: ship.data.id, amount: ship.amount } })); + return await this.battleResults(enemyShips.map(ship => { return { id: ship.data.id, amount: ship.amount } }), this.data.destination.defenses.defenses.map(defense => { return { id: defense.data.id, amount: defense.amount } })); } else { throw new Error("Cannot attack sector."); } @@ -161,18 +162,19 @@ export default class FleetManager { await this.sync(); } - private async sendMail(title: string, description: string) { + private async sendMail(user: ObjectId, title: string, description: string) { await sendMail( null, - this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, + user, this.data.arrivalTime, title, description ); } - async battleResults(enemyFleet: { id: string, amount: number }[]) { + async battleResults(enemyFleet: { id: string, amount: number }[], enemyDefenses: { id: string, amount: number }[] = []) { const allShips = await getAllShips(); + const allDefenses = await getAllDefenses(); const playerStats = this.data.ships.reduce((acc, ship) => { const dbShip = allShips.find(s => s.id === ship.id); @@ -183,6 +185,14 @@ export default class FleetManager { return acc; }, { attack: 0, defense: 0, hitpoints: 0 }); + const enemyDefensesStats = enemyDefenses.reduce((acc, defense) => { + const dbDefense = allDefenses.find(d => d.id === defense.id); + if(!dbDefense) return acc; + acc.defense += dbDefense.structure.defense * defense.amount; + acc.hitpoints += dbDefense.structure.hitpoints * defense.amount; + return acc; + }, { attack: 0, defense: 0, hitpoints: 0 }); + const enemyStats = enemyFleet.reduce((acc, ship) => { const dbShip = allShips.find(s => s.id === ship.id); if(!dbShip) return acc; @@ -190,7 +200,7 @@ export default class FleetManager { acc.defense += dbShip.structure.defense * ship.amount; acc.hitpoints += dbShip.structure.hitpoints * ship.amount; return acc; - }, { attack: 0, defense: 0, hitpoints: 0 }); + }, enemyDefensesStats); const playerShipsStructure: BattleFleet[] = []; for(const playerShip of this.data.ships) { @@ -282,10 +292,12 @@ export default class FleetManager { } else this.data.ships = []; await this.sendMail( + this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, `Battle Results (${playerBalance > enemyBalance ? "Victory" : playerBalance < enemyBalance ? "Defeat" : "Draw"})`, `Results of battle at ${(this.data.destination instanceof SystemManager ? this.data.destination.data.name : this.data.destination.name)}:\n Player ships:\n${previousShips.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n - Enemy ships:\n${enemyFleet.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n\n + Enemy ships:\n${enemyFleet.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n + Enemy defenses:\n${enemyDefenses.map(defense => `${defense.amount} ${defense.id}`).join('\n')}\n Player stats: ${playerStats.hitpoints} HP, ${playerStats.attack} ATK, ${playerStats.defense} DEF\n Enemy stats: ${enemyStats.hitpoints} HP, ${enemyStats.attack} ATK, ${enemyStats.defense} DEF\n\n Player ships left:\n${Object.keys(playerShipsLeft).map(key => `${key} - ${playerShipsLeft[key]}`).join('\n')}\n @@ -293,6 +305,22 @@ export default class FleetManager { ${playerBalance > enemyBalance ? `Resources stolen:\n${resourcesStolen.map(res => `${res.id} - ${res.amount}`).join('\n')}` : ""}` ); + if(!("expedition" in this.data.destination)) { + await this.sendMail( + this.data.destination instanceof SystemManager ? this.data.destination.data.ownedBy.id : this.data.destination.system.data.ownedBy.id, + `Battle Results (${playerBalance < enemyBalance ? "Victory" : playerBalance > enemyBalance ? "Defeat" : "Draw"})`, + `Results of battle at ${(this.data.destination instanceof SystemManager ? this.data.destination.data.name : this.data.destination.name)}:\n + Enemy ships:\n${previousShips.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n + Player ships:\n${enemyFleet.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n + Player defenses:\n${enemyDefenses.map(defense => `${defense.amount} ${defense.id}`).join('\n')}\n + Enemy stats: ${playerStats.hitpoints} HP, ${playerStats.attack} ATK, ${playerStats.defense} DEF\n + Player stats: ${enemyStats.hitpoints} HP, ${enemyStats.attack} ATK, ${enemyStats.defense} DEF\n\n + Enemy ships left:\n${Object.keys(playerShipsLeft).map(key => `${key} - ${playerShipsLeft[key]}`).join('\n')}\n + Player ships left:\n${Object.keys(enemyShipsLeft).map(key => `${key} - ${enemyShipsLeft[key]}`).join('\n')}\n + ${playerBalance > enemyBalance ? `Resources stolen:\n${resourcesStolen.map(res => `${res.id} - ${res.amount}`).join('\n')}` : ""}` + ); + } + return !(playerShipsStructure.length > 0); } @@ -309,7 +337,7 @@ export default class FleetManager { if(expeditionRandom < 0.02) { // 2% chance; lost all ships, black hole this.data.ships = []; - await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered a black hole. All ships were lost.`); + await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered a black hole. All ships were lost.`); return; } @@ -324,7 +352,7 @@ export default class FleetManager { else ship.amount += s.amount; } - await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered abandoned shipyard. Following ships were added to the fleet:\n${ships.map(s => `${s.id} - ${s.amount}`).join(', ')}`); + await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered abandoned shipyard. Following ships were added to the fleet:\n${ships.map(s => `${s.id} - ${s.amount}`).join(', ')}`); await this.initiateReturn(); return; } @@ -356,7 +384,7 @@ export default class FleetManager { else resource.amount += res.amount; } - await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich asteroid. Following resources were added to the cargo inventory:\n${resources.map(r => `${r.id} - ${r.amount}`).join('\n')}`); + await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich asteroid. Following resources were added to the cargo inventory:\n${resources.map(r => `${r.id} - ${r.amount}`).join('\n')}`); await this.initiateReturn(); return; } @@ -367,7 +395,7 @@ export default class FleetManager { { id: 'transporter', amount: getRandomInRange(0, 100) + valueAdded } ]; - await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered drunk pirates. After attempting to communicate with them, they attacked your fleet.`) + await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered drunk pirates. After attempting to communicate with them, they attacked your fleet.`) await this.battleResults(pirates); return; } @@ -416,12 +444,12 @@ export default class FleetManager { else resource.amount += res.amount; } - await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich rouge planet. Your fleet could not extract all resources. Following resources were added to the cargo inventory:\n${addedResources.map(r => `${r.id} - ${r.amount}`).join('\n')}`); + await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich rouge planet. Your fleet could not extract all resources. Following resources were added to the cargo inventory:\n${addedResources.map(r => `${r.id} - ${r.amount}`).join('\n')}`); await this.initiateReturn(); return; } - await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector scanned the sector for a long time, yet it haven't found anything.`); + await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector scanned the sector for a long time, yet it haven't found anything.`); await this.initiateReturn(); return; } diff --git a/src/lib/classes/managers/PlanetDefenseManager.ts b/src/lib/classes/managers/PlanetDefenseManager.ts new file mode 100644 index 0000000..5eb898d --- /dev/null +++ b/src/lib/classes/managers/PlanetDefenseManager.ts @@ -0,0 +1,17 @@ +import { updatePlanetDefenses } from "../../db/planets"; +import { Planet } from "./PlanetManager"; +import DefenseManager from "./abstract/DefenseManager"; + +export default class PlanetDefenseManager extends DefenseManager { + constructor(planet: Planet) { + super(planet); + } + + get manager() { + return this._manager as Planet; + } + + async sync() { + await updatePlanetDefenses(this.manager._id, this.defenses.map(def => { return { id: def.data.id, amount: def.amount } })); + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/PlanetManager.ts b/src/lib/classes/managers/PlanetManager.ts index 8152cfc..d51e085 100644 --- a/src/lib/classes/managers/PlanetManager.ts +++ b/src/lib/classes/managers/PlanetManager.ts @@ -3,6 +3,7 @@ import BuildingManager from "./BuildingManager"; import PlanetResourceManager from "./PlanetResourceManager"; import ShipManager from "./PlanetShipManager"; import SystemManager from "./SystemManager"; +import PlanetDefenseManager from "./PlanetDefenseManager"; export type Planet = { _id: ObjectId; @@ -12,4 +13,5 @@ export type Planet = { resources: PlanetResourceManager; buildings: BuildingManager; ships: ShipManager; + defenses: PlanetDefenseManager; } \ No newline at end of file diff --git a/src/lib/classes/managers/SystemDefenseManager.ts b/src/lib/classes/managers/SystemDefenseManager.ts new file mode 100644 index 0000000..d8ab0da --- /dev/null +++ b/src/lib/classes/managers/SystemDefenseManager.ts @@ -0,0 +1,17 @@ +import { updateSystemDefenses } from "../../db/systems"; +import SystemManager from "./SystemManager"; +import DefenseManager from "./abstract/DefenseManager"; + +export default class SystemDefenseManager extends DefenseManager { + constructor(system: SystemManager) { + super(system); + } + + get manager() { + return this._manager as SystemManager; + } + + async sync() { + await updateSystemDefenses(this.manager.data._id, this.defenses.map(def => { return { id: def.data.id, amount: def.amount } })); + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/SystemManager.ts b/src/lib/classes/managers/SystemManager.ts index f5e402e..f8d0750 100644 --- a/src/lib/classes/managers/SystemManager.ts +++ b/src/lib/classes/managers/SystemManager.ts @@ -10,6 +10,8 @@ import PlanetShipManager from "./PlanetShipManager"; import StructureManager from "./StructureManager"; import SystemResourceManager from "./SystemResourceManager"; import SystemShipManager from "./SystemShipManager"; +import PlanetDefenseManager from "./PlanetDefenseManager"; +import SystemDefenseManager from "./SystemDefenseManager"; export type System = { _id: ObjectId, @@ -19,6 +21,7 @@ export type System = { structures: StructureManager, resources: SystemResourceManager, ships: SystemShipManager, + defenses: SystemDefenseManager, planets: Planet[]; } @@ -27,6 +30,7 @@ export default class SystemManager { structures: StructureManager; resources: SystemResourceManager; ships: SystemShipManager; + defenses: SystemDefenseManager; data: System; constructor(data: System) { @@ -34,12 +38,14 @@ export default class SystemManager { this.structures = new StructureManager(this); this.resources = new SystemResourceManager(this); this.ships = new SystemShipManager(this); + this.defenses = new SystemDefenseManager(this); } async fillData(systemData: DBSystem) { await this.structures.init(systemData.structures); await this.resources.init(systemData.resources); await this.ships.init(systemData.ships); + await this.defenses.init(systemData.defenses); await Promise.all(systemData.planets.map(async planetId => { const planet = await getPlanetById(planetId); @@ -54,12 +60,15 @@ export default class SystemManager { //@ts-ignore buildings: null, //@ts-ignore - ships: null + ships: null, + //@ts-ignore + defenses: null } planetObject.resources = await new PlanetResourceManager(planetObject).init(planet.resources); planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings); planetObject.ships = await new PlanetShipManager(planetObject).init(planet.ships); + planetObject.defenses = await new PlanetDefenseManager(planetObject).init(planet.defenses); this.planets.push(planetObject); })); diff --git a/src/lib/classes/managers/abstract/DefenseManager.ts b/src/lib/classes/managers/abstract/DefenseManager.ts new file mode 100644 index 0000000..6d7dbcc --- /dev/null +++ b/src/lib/classes/managers/abstract/DefenseManager.ts @@ -0,0 +1,62 @@ +import { Planet } from '../PlanetManager'; +import SystemManager from '../SystemManager'; +import Defense from '../../Defense'; +import DBDefenses from '../../../../types/db/DBDefenses'; +import { getAllDefenses } from '../../../db/defenses'; + +export default abstract class DefenseManager { + defenses: Array = []; + defensesDB: Array = []; + protected _manager: Planet | SystemManager; + + constructor(manager: Planet | SystemManager) { + this._manager = manager; + } + + abstract sync(): Promise; + + abstract get manager(): Planet | SystemManager; + + async init(defenseData: { id: string, amount: number }[]) { + this.defensesDB = await getAllDefenses(); + + for(const def of defenseData) { + const defToFind = this.defensesDB.find(d => d.id === def.id); + + if(defToFind) this.defenses.push(new Defense( + this, + defToFind, + def.amount + )) + } + + return this; + } + + getDefenseById(id: string) { + return this.defenses.find(def => def.data.id === id); + } + + addDefenses(id: string, amount: number) { + const findDef = this.defenses.find(d => d.data.id === id); + if(!findDef) { + const defData = this.defensesDB.find(d => d.id === id); + if(!defData) return; + + this.defenses.push(new Defense( + this, + defData, + amount + )); + } + else findDef.amount += amount; + } + + removeDefenses(id: string, amount: number) { + const findDef = this.defenses.find(d => d.data.id === id); + if(findDef) { + findDef.amount -= amount; + if(findDef.amount <= 0) this.defenses.splice(this.defenses.indexOf(findDef), 1); + } + } +} \ No newline at end of file diff --git a/src/lib/db/defenses.ts b/src/lib/db/defenses.ts new file mode 100644 index 0000000..8026556 --- /dev/null +++ b/src/lib/db/defenses.ts @@ -0,0 +1,12 @@ +import DBDefenses from '../../types/db/DBDefenses'; +import { Defenses } from '../db/mongodb'; + +export const getAllDefenses = async () => { + return (await Defenses()).find({}).toArray() as unknown as Array; +} + +export const getDefenseById = async (id: string) => { + return (await Defenses()).findOne({ + id + }) as unknown as DBDefenses; +} \ No newline at end of file diff --git a/src/lib/db/lang.ts b/src/lib/db/lang.ts index 8b0a4a5..4cce2f9 100644 --- a/src/lib/db/lang.ts +++ b/src/lib/db/lang.ts @@ -8,6 +8,7 @@ export const getLang = async (language = "en") => { ships: (await lang[2].find({}).toArray()).map(({ _id, ...rest }) => rest), resources: (await lang[3].find({}).toArray()).map(({ _id, ...rest }) => rest), research: (await lang[4].find({}).toArray()).map(({ _id, ...rest }) => rest), - structures: (await lang[5].find({}).toArray()).map(({ _id, ...rest }) => rest) + structures: (await lang[5].find({}).toArray()).map(({ _id, ...rest }) => rest), + defenses: (await lang[6].find({}).toArray()).map(({ _id, ...rest }) => rest) } } \ No newline at end of file diff --git a/src/lib/db/mongodb.ts b/src/lib/db/mongodb.ts index 0409886..39ce3ee 100644 --- a/src/lib/db/mongodb.ts +++ b/src/lib/db/mongodb.ts @@ -67,6 +67,11 @@ export const Ships = async() => { return db.collection('ships'); } +export const Defenses = async() => { + const db = await getDB(); + return db.collection('defenses'); +} + export const Fleet = async() => { const db = await getDB(); return db.collection('fleet'); @@ -85,7 +90,8 @@ export const Lang = async (language = "en") => { await db.collection('ships'), await db.collection('resources'), await db.collection('research'), - await db.collection('structures') + await db.collection('structures'), + await db.collection('defenses') ] } diff --git a/src/lib/db/planets.ts b/src/lib/db/planets.ts index 990b4a7..159ee74 100644 --- a/src/lib/db/planets.ts +++ b/src/lib/db/planets.ts @@ -40,4 +40,13 @@ export const updatePlanetShips = async (planetId: ObjectId, ships: Array<{ id: s ships } }); +} + +export const updatePlanetDefenses = async (planetId: ObjectId, defenses: Array<{ id: string, amount: number }>) => { + const planets = await Planets(); + await planets.updateOne({ _id: planetId }, { + $set: { + defenses + } + }); } \ No newline at end of file diff --git a/src/lib/db/systems.ts b/src/lib/db/systems.ts index 3506bf4..01fabe6 100644 --- a/src/lib/db/systems.ts +++ b/src/lib/db/systems.ts @@ -42,4 +42,13 @@ export const updateSystemShips = async (systemId: ObjectId, ships: Array) = ships } }); +} + +export const updateSystemDefenses = async (systemId: ObjectId, defenses: Array) => { + const systems = await Systems(); + await systems.updateOne({ _id: systemId }, { + $set: { + defenses + } + }); } \ No newline at end of file diff --git a/src/pages/api/defenses/add.ts b/src/pages/api/defenses/add.ts new file mode 100644 index 0000000..5c9a705 --- /dev/null +++ b/src/pages/api/defenses/add.ts @@ -0,0 +1,146 @@ +import { type APIRoute } from "astro"; +import validateAccessToken from "../../../lib/utils/validateAccessToken"; +import { getUserByAccessToken } from "../../../lib/db/users"; +import { ObjectId } from "mongodb"; +import locationManager from "../../../lib/classes/managers/LocationManager"; +import { getAllResources } from "../../../lib/db/resources"; +import Defense from "../../../lib/classes/Defense"; +import { Planet } from "../../../lib/classes/managers/PlanetManager"; +import SystemManager from "../../../lib/classes/managers/SystemManager"; + +export const POST: APIRoute = async({ request }) => { + const response = await validateAccessToken(request); + if(response instanceof Response) return response; + + const user = await getUserByAccessToken(response); + if(user === null) { + return new Response( + JSON.stringify({ + code: 401, + message: "Unauthorized" + }), { status: 401 } + ) + } + + let body; + try { + body = await request.json() + } catch(e) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid JSON body" + }), { status: 400 } + ) + } + + if(!body.id || !body.defense || !body.amount) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Missing required fields: id, defense, amount" + }), { status: 400 } + ) + } + + const amount = parseInt(body.amount); + if(isNaN(amount) || amount <= 0) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid amount" + }), { status: 400 } + ) + } + + let id: ObjectId; + + try { + id = new ObjectId(body.id); + } catch(e) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid ID" + }), { status: 400 } + ) + } + + let planetOrSystem: Planet | SystemManager | undefined = locationManager.getPlanet(id); + if(!planetOrSystem) planetOrSystem = locationManager.getSystem(id); + if(!planetOrSystem) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid ID" + }), { status: 400 } + ) + } + + const defenseDB = planetOrSystem.defenses.defensesDB.find(d => d.id === body.defense); + + if(!defenseDB) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid defense ID" + }), { status: 400 } + ) + } + + const defense = new Defense(planetOrSystem.defenses, defenseDB, amount); + + const requirements = await defense.checkRequirements(); + const resources = await defense.checkRequiredResources(); + + if(!requirements.canBuild || !resources) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: `${requirements.error} | ${resources ? "" : "Not enough resources"}` + }), { status: 400 } + ) + } + + const resourcesDiff = await planetOrSystem.resources.getDifference(defense.data.requirements.resources.map(res => { + return { + id: res.id, + amount: amount * res.amount + } + })); + + const resourceDB = await getAllResources(); + const resourcesAfter = resourcesDiff.map(res => { + const data = resourceDB.find(r => r.id === res.id); + + if(!data) throw new Error("Resource not found"); + + return { + id: res.id, + amount: res.amount, + lastUpdated: res.lastUpdated, + perHourMiningRate: res.perHourMiningRate, + data + } + }); + + planetOrSystem.resources.updateAmount(resourcesAfter.map(res => { return { id: res.id, amount: res.amount } })); + planetOrSystem.defenses.addDefenses(defense.data.id, amount); + + await planetOrSystem.defenses.sync(); + await planetOrSystem.resources.sync(); + + return new Response( + JSON.stringify({ + code: 200, + message: "OK" + }), { status: 200 } + ); +} \ No newline at end of file diff --git a/src/pages/game/defenses.astro b/src/pages/game/defenses.astro new file mode 100644 index 0000000..05857af --- /dev/null +++ b/src/pages/game/defenses.astro @@ -0,0 +1,184 @@ +--- +import ItemCard from '../../components/ItemCard.astro'; +import LoggedIn from '../../layouts/LoggedIn.astro'; +import { Planet } from '../../lib/classes/managers/PlanetManager'; +import SystemManager from '../../lib/classes/managers/SystemManager'; +import { getAllDefenses } from '../../lib/db/defenses'; +import { getObj } from '../../lib/utils/langDriver'; + +const { token, lang } = Astro.locals; +const active: SystemManager | Planet = Astro.locals.active; + +const defenses = await getAllDefenses(); + +if(Astro.request.method === "POST") { + const body = await Astro.request.formData(); + + const id = body.get("id") as string; + const amount = parseInt(body.get("amount") as string ?? "1"); + + await fetch(Astro.url.origin + '/api/defenses/add', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + token + }, + body: JSON.stringify({ + id: active instanceof SystemManager ? active.data._id : active._id, + defense: id, + amount + }) + }); +} + +const modalSet: { [key: string]: { resources: Array, research: Array, buildings: Array } } = {}; + +for(const def of defenses) { + modalSet[def.id] = { + resources: def.requirements.resources, + research: def.requirements.research, + buildings: def.requirements.buildings, + // energy: building.energy + }; +} + +const planetId = active instanceof SystemManager ? active.data._id : active._id; +--- + +
+
+

Required resources

+
None
+

Required buildings

+
None
+

Required research

+
None
+
+
+ +
+ {defenses.map(def => <> + + )} +
+
+ + + \ No newline at end of file diff --git a/src/types/db/DBDefenses.ts b/src/types/db/DBDefenses.ts new file mode 100644 index 0000000..dedcc24 --- /dev/null +++ b/src/types/db/DBDefenses.ts @@ -0,0 +1,18 @@ +import { ObjectId } from "mongodb"; + +export default interface DBDefenses { + _id: ObjectId; + id: string; + requirements: { + buildings: Array<{ id: string, level: number }>, + research: Array<{ id: string, level: number }>, + resources: Array<{ id: string, amount: number }>, + }; + energy: number; + time: number; + structure: { + hitpoints: number; + defense: number; + attack: number; + }; +} \ No newline at end of file diff --git a/src/types/db/DBPlanet.ts b/src/types/db/DBPlanet.ts index b26274e..aabe0e0 100644 --- a/src/types/db/DBPlanet.ts +++ b/src/types/db/DBPlanet.ts @@ -8,4 +8,5 @@ export default interface DBPlanet { resources: Array<{ id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }>; buildings: Array<{ id: string, level: number }>; ships: Array<{ id: string, amount: number }>; + defenses: Array<{ id: string, amount: number }>; } \ No newline at end of file diff --git a/src/types/db/DBSystem.ts b/src/types/db/DBSystem.ts index 1a9661e..df9138c 100644 --- a/src/types/db/DBSystem.ts +++ b/src/types/db/DBSystem.ts @@ -17,4 +17,8 @@ export default interface DBSystem { id: string, amount: number }>; + defenses: Array<{ + id: string, + amount: number + }>; } \ No newline at end of file