diff --git a/src/lib/classes/managers/AsteroidManager.ts b/src/lib/classes/managers/AsteroidManager.ts new file mode 100644 index 0000000..09f7ccd --- /dev/null +++ b/src/lib/classes/managers/AsteroidManager.ts @@ -0,0 +1,20 @@ +import Asteroid from '../../../types/Asteroid'; +import SystemManager from './SystemManager'; +import { updateSystemAsteroids } from '../../db/systems'; + +export default class AsteroidManager { + asteroids: Array = []; + system: SystemManager; + + constructor(system: SystemManager) { + this.system = system; + } + + init(asteroids: Array) { + this.asteroids = asteroids; + } + + async sync() { + await updateSystemAsteroids(this.system.data._id, this.asteroids); + } +} \ No newline at end of file diff --git a/src/lib/classes/managers/FleetManager.ts b/src/lib/classes/managers/FleetManager.ts index 579230a..8b9d975 100644 --- a/src/lib/classes/managers/FleetManager.ts +++ b/src/lib/classes/managers/FleetManager.ts @@ -20,7 +20,8 @@ export type Fleet = { returning: boolean, mission: MissionType, ships: Array<{ id: string, amount: number }>, - cargo: Array<{ id: string, amount: number }> + cargo: Array<{ id: string, amount: number }>, + additionalData?: string } export type BattleFleet = { @@ -33,7 +34,7 @@ export type BattleFleet = { export default class FleetManager { data: Fleet; - constructor(id: ObjectId, source: Planet | SystemManager, destination: Planet | SystemManager | Sector, departureTime: Date, arrivalTime: Date, returning: boolean, mission: MissionType, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>) { + constructor(id: ObjectId, source: Planet | SystemManager, destination: Planet | SystemManager | Sector, departureTime: Date, arrivalTime: Date, returning: boolean, mission: MissionType, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>, additionalData?: string) { this.data = { id, source, @@ -43,7 +44,8 @@ export default class FleetManager { returning, mission, ships, - cargo + cargo, + additionalData } } @@ -148,6 +150,30 @@ export default class FleetManager { case 'EXPEDITION': await this.expeditionResults(); + return false; + case 'MINE': + const system = this.data.destination as SystemManager; + const asteroid = system.asteroids.asteroids.find(a => a.id.equals(this.data.additionalData ?? "")); + if(!asteroid) throw new Error("Asteroid not found."); + for(const res of asteroid.resources) { + const resource = this.data.cargo.find(r => r.id === res.id); + if(!resource) this.data.cargo.push(res); + else resource.amount += res.amount; + } + + await this.initiateReturn(); + await sendMail( + null, + this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, + this.data.arrivalTime, + "Asteroid mined", + `Your fleet has arrived at AS-${this.data.additionalData} asteroid.\n + Following resources were added to the cargo inventory:\n${asteroid.resources.map(r => `${r.id} - ${r.amount}`).join('\n')}\n + Fleet will return at ${this.data.arrivalTime}` + ); + + system.asteroids.asteroids = system.asteroids.asteroids.filter(a => !a.id.equals(asteroid.id)); + await system.asteroids.sync(); return false; } } @@ -467,7 +493,8 @@ export default class FleetManager { returning: this.data.returning, mission: this.data.mission, ships: this.data.ships, - cargo: this.data.cargo + cargo: this.data.cargo, + additionalData: this.data.additionalData } await updateFleet(data); diff --git a/src/lib/classes/managers/LocationManager.ts b/src/lib/classes/managers/LocationManager.ts index 2a2f6e1..0ad21ce 100644 --- a/src/lib/classes/managers/LocationManager.ts +++ b/src/lib/classes/managers/LocationManager.ts @@ -94,7 +94,8 @@ class LocationManager { resources: null, //@ts-ignore ships: null, - planets: [] + planets: [], + asteroids: [], }; const s = await new SystemManager(systemObject).fillData(systemData); @@ -139,7 +140,8 @@ class LocationManager { fleet.returning, fleet.mission, fleet.ships, - fleet.cargo + fleet.cargo, + fleet.additionalData )); } } diff --git a/src/lib/classes/managers/SystemManager.ts b/src/lib/classes/managers/SystemManager.ts index 5ff07ea..14400d1 100644 --- a/src/lib/classes/managers/SystemManager.ts +++ b/src/lib/classes/managers/SystemManager.ts @@ -1,4 +1,4 @@ -import { ObjectId } from "mongodb"; +import { ObjectId, UUID } from "mongodb"; import DBSystem from "../../../types/db/DBSystem"; import { getPlanetById } from "../../db/planets"; import User from "../User"; @@ -14,6 +14,8 @@ import PlanetDefenseManager from "./PlanetDefenseManager"; import SystemDefenseManager from "./SystemDefenseManager"; import SystemEnergyManager from "./SystemEnergyManager"; import PlanetEnergyManager from "./PlanetEnergyManager"; +import AsteroidManager from "./AsteroidManager"; +import Asteroid from "../../../types/Asteroid"; export type System = { _id: ObjectId, @@ -25,6 +27,7 @@ export type System = { ships: SystemShipManager, defenses: SystemDefenseManager, planets: Planet[]; + asteroids: Asteroid[]; } export default class SystemManager { @@ -34,6 +37,7 @@ export default class SystemManager { ships: SystemShipManager; defenses: SystemDefenseManager; energy: SystemEnergyManager; + asteroids: AsteroidManager; data: System; constructor(data: System) { @@ -43,6 +47,7 @@ export default class SystemManager { this.ships = new SystemShipManager(this); this.defenses = new SystemDefenseManager(this); this.energy = new SystemEnergyManager(this); + this.asteroids = new AsteroidManager(this); } async fillData(systemData: DBSystem) { @@ -50,6 +55,7 @@ export default class SystemManager { await this.resources.init(systemData.resources); await this.ships.init(systemData.ships); await this.defenses.init(systemData.defenses); + this.asteroids.init(systemData.asteroids); this.energy.update(); await Promise.all(systemData.planets.map(async planetId => { diff --git a/src/lib/db/systems.ts b/src/lib/db/systems.ts index 01fabe6..faee6c9 100644 --- a/src/lib/db/systems.ts +++ b/src/lib/db/systems.ts @@ -1,6 +1,7 @@ import { ObjectId } from "mongodb"; import { Systems } from "./mongodb"; import DBSystem from "../../types/db/DBSystem"; +import Asteroid from "../../types/Asteroid"; export const getAllSystems = async () => { return await (await Systems()).find({}).toArray() as DBSystem[]; @@ -51,4 +52,13 @@ export const updateSystemDefenses = async (systemId: ObjectId, defenses: Array) => { + const systems = await Systems(); + await systems.updateOne({ _id: systemId }, { + $set: { + asteroids + } + }); } \ No newline at end of file diff --git a/src/lib/utils/parseParams.ts b/src/lib/utils/parseParams.ts index c0f3acd..673e462 100644 --- a/src/lib/utils/parseParams.ts +++ b/src/lib/utils/parseParams.ts @@ -1,6 +1,6 @@ export default function parseParams(url: string) { const rawParams = url.split("?")[1]?.split("&"); - if(typeof rawParams === "undefined") return []; + if(typeof rawParams === "undefined") return {}; const params: { [key: string]: any } = {}; for(const rawParam of rawParams) { const k = rawParam.split("=")[0]; diff --git a/src/pages/api/fleet/send.ts b/src/pages/api/fleet/send.ts index 7f72581..9a955a4 100644 --- a/src/pages/api/fleet/send.ts +++ b/src/pages/api/fleet/send.ts @@ -1,7 +1,7 @@ import { APIRoute } from "astro"; import validateAccessToken from "../../../lib/utils/validateAccessToken"; import { getUserByAccessToken } from "../../../lib/db/users"; -import { ObjectId } from "mongodb"; +import { ObjectId, UUID } from "mongodb"; import locationManager from "../../../lib/classes/managers/LocationManager"; import { getAllShips } from "../../../lib/db/ships"; import DBShip from "../../../types/db/DBShip"; @@ -36,7 +36,7 @@ export const POST: APIRoute = async({ request }) => { ) } - let body: { source: string, destination: string, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>, mission: MissionType }; + let body: { source: string, destination: string, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>, mission: MissionType, currentSystem?: string }; try { body = await request.json() } catch(e) { @@ -64,11 +64,16 @@ export const POST: APIRoute = async({ request }) => { const checkCargoBody = checkCargo(body.cargo, body.ships, source, body.mission); if(typeof checkCargoBody.error !== "undefined") return new Response(JSON.stringify(checkCargoBody), { status: checkCargoBody.code }); - let dest; + let dest, additionalData; if(body.mission === "EXPEDITION") { const destinationSector = checkSectorId(body.destination); if(typeof destinationSector.error !== "undefined") return new Response(JSON.stringify(destinationSector), { status: destinationSector.code }); dest = destinationSector.sector; + } else if(body.mission === "MINE") { + const checkAsteroid = checkAsteroidId(body.destination, body.currentSystem ?? ""); + if(typeof checkAsteroid.error !== "undefined") return new Response(JSON.stringify(checkAsteroid), { status: checkAsteroid.code }); + dest = checkAsteroid.system; + additionalData = checkAsteroid.asteroid; } else { const checkDestination = checkPlanetOrSystemId(body.destination, 'destination'); if(typeof checkDestination.error !== "undefined") return new Response(JSON.stringify(checkDestination), { status: checkDestination.code }); @@ -86,7 +91,8 @@ export const POST: APIRoute = async({ request }) => { false, body.mission, body.ships, - body.cargo + body.cargo, + body.mission === "MINE" ? additionalData?.id.toString() : "" ); const resourceDiff = await source.resources.getDifference(body.cargo.map(c => ({ id: c.id, amount: c.amount }))); @@ -123,7 +129,7 @@ export const POST: APIRoute = async({ request }) => { } function checkPlanetOrSystemId(id: string, type: string) { - if(typeof ObjectId === "undefined") return { + if(typeof id === "undefined") return { code: 400, message: "Bad Request", error: `Missing '${type}' in body` @@ -155,8 +161,66 @@ function checkPlanetOrSystemId(id: string, type: string) { } } +function checkAsteroidId(id: string, systemId: string) { + if(typeof id === "undefined") return { + code: 400, + message: "Bad Request", + error: `Missing 'destination' in body` + } + + if(systemId === "") return { + code: 400, + message: "Bad Request", + error: "Missing 'currentSystem' in body" + } + + let idToCheck; + try { + idToCheck = new UUID(id); + } catch(e) { + return { + code: 400, + message: "Bad Request", + error: `Invalid UUID in 'destination'` + } + } + + let systemObjectId; + try { + systemObjectId = new ObjectId(systemId); + } catch(e) { + return { + code: 400, + message: "Bad Request", + error: "Invalid ID in 'currentSystem'" + } + } + + const system = locationManager.getSystem(systemObjectId); + + if(!system) return { + code: 404, + message: "Not Found", + error: `Non-existent system provided in 'currentSystem'` + } + + const asteroid = system.asteroids.asteroids.find(asteroid => asteroid.id.equals(idToCheck)); + if(!asteroid) return { + code: 404, + message: "Not Found", + error: "Non-existent asteroid provided in 'destination'" + } + + return { + code: 200, + message: "OK", + asteroid, + system + } +} + function checkSectorId(id: string) { - if(typeof ObjectId === "undefined") return { + if(typeof id === "undefined") return { code: 400, message: "Bad Request", error: "Missing 'sector' in body" diff --git a/src/pages/api/system/asteroids/scan.ts b/src/pages/api/system/asteroids/scan.ts new file mode 100644 index 0000000..f288bc6 --- /dev/null +++ b/src/pages/api/system/asteroids/scan.ts @@ -0,0 +1,75 @@ +import { type APIRoute } from "astro"; +import validateAccessToken from "../../../../lib/utils/validateAccessToken"; +import { getUserByAccessToken } from "../../../../lib/db/users"; +import locationManager from "../../../../lib/classes/managers/LocationManager"; +import { ObjectId, UUID } from "mongodb"; +import Asteroid from "../../../../types/Asteroid"; + +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 } + ) + } + + const cookies = request.headers.get("Cookie")?.split(";").map((x) => x.trim().split("=")) ?? []; + const systemId = cookies.filter((x) => x[0] === "currentSystem")[0]?.[1]; + + const userSystem = locationManager.getSystem(new ObjectId(systemId)); + + if(!userSystem) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid system ID" + }), { status: 400 } + ) + } + + if(userSystem.asteroids.asteroids.length >= 5) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "You have reached the maximum number of asteroids in this system" + }), { status: 400 } + ) + } + + const asteroidUUID = new UUID(); + const asteroid: Asteroid = { + id: asteroidUUID, + name: `AS-${asteroidUUID}`, + resources: [ + { + id: "coal", + amount: Math.floor(Math.random() * 100) + 1 + }, { + id: "iron", + amount: Math.floor(Math.random() * 100) + 1 + }, { + id: "gold", + amount: Math.floor(Math.random() * 100) + 1 + } + ] + } + + userSystem.asteroids.asteroids.push(asteroid); + await userSystem.asteroids.sync(); + + return new Response( + JSON.stringify({ + code: 200, + message: "OK", + asteroid + }), { status: 200 } + ); +} \ No newline at end of file diff --git a/src/pages/game/systemManager/asteroids/index.astro b/src/pages/game/systemManager/asteroids/index.astro new file mode 100644 index 0000000..5c706bc --- /dev/null +++ b/src/pages/game/systemManager/asteroids/index.astro @@ -0,0 +1,90 @@ +--- +import { ObjectId } from "mongodb"; +import LoggedIn from "../../../../layouts/LoggedIn.astro"; +import locationManager from "../../../../lib/classes/managers/LocationManager"; + +const { token, lang } = Astro.locals; + +const currentSystemId = Astro.cookies.get('currentSystem')?.value ?? null; +if(currentSystemId === null) return Astro.redirect('/game/systemManager/select'); + +const currentSystem = locationManager.getSystem(new ObjectId(currentSystemId)); +if(currentSystem === undefined) { + Astro.cookies.delete('currentSystem'); + return Astro.redirect('/game/systemManager/select'); +} + +const discoveredAsteroids = currentSystem.asteroids.asteroids; +--- + +

Selected system: {currentSystem.data.name} (change)

+ +
+

Avaliable asteroids

+
+ {discoveredAsteroids.map((asteroid) =>
+

{asteroid.name} Send

+

Resources left:

+ {asteroid.resources.map((resource) =>

{resource.id} - {resource.amount}

)} +
)} +
+
+ + + \ No newline at end of file diff --git a/src/pages/game/systemManager/asteroids/send.astro b/src/pages/game/systemManager/asteroids/send.astro new file mode 100644 index 0000000..2c4863f --- /dev/null +++ b/src/pages/game/systemManager/asteroids/send.astro @@ -0,0 +1,165 @@ +--- +import { ObjectId } from "mongodb"; +import ItemCard from "../../../../components/ItemCard.astro"; +import LoggedIn from "../../../../layouts/LoggedIn.astro"; +import locationManager from "../../../../lib/classes/managers/LocationManager"; +import { getName, getObj } from "../../../../lib/utils/langDriver"; +import parseParams from "../../../../lib/utils/parseParams"; +import { getAllSystems } from "../../../../lib/db/systems"; + +const { user, token, lang } = Astro.locals; + +const currentSystemId = Astro.cookies.get('currentSystem')?.value ?? null; +if(currentSystemId === null) return Astro.redirect('/game/systemManager/select'); + +const currentSystem = locationManager.getSystem(new ObjectId(currentSystemId)); +if(currentSystem === undefined) { + Astro.cookies.delete('currentSystem'); + return Astro.redirect('/game/systemManager/select'); +} + +if(Astro.request.method === "POST") { + const form = await Astro.request.formData(); + + const source = form.get('toSystem') ? + form.get('source-system')?.toString() : + form.get('source-planet')?.toString(); + + const fleetData = { + source, + destination: form.get('asteroid-id')?.toString(), + mission: "MINE", + ships: [{ id: "asteroid-miner", amount: 1 }], + cargo: [], + currentSystem: form.get('current-system')?.toString() + } + + const response = await fetch(`${Astro.url.origin}/api/fleet/send`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify(fleetData) + }); + + console.log(await response.json()); + + return Astro.redirect('/game/systemManager/asteroids'); +} + +const params = parseParams(Astro.request.url); + +const systems = locationManager.getSystemsOwnedBy(user.id).map(sys => { return { id: sys.data._id, name: sys.data.name, hasAsteroidMiner: sys.ships.ships.find(ship => ship.data.id === "asteroid-miner") !== undefined, planets: sys.planets.map(planet => { return { id: planet._id, name: planet.name, hasAsteroidMiner: planet.ships.ships.find(ship => ship.data.id === "asteroid-miner") !== undefined } }) } }); +--- + +

<= go backSending

+
+ + + +

System

+ +

Planet

+ +
+ +
+
+ + \ No newline at end of file diff --git a/src/types/Asteroid.ts b/src/types/Asteroid.ts new file mode 100644 index 0000000..3e312fd --- /dev/null +++ b/src/types/Asteroid.ts @@ -0,0 +1,10 @@ +import { UUID } from "mongodb"; + +export default interface Asteroid { + id: UUID; + name: string; + resources: { + id: string; + amount: number; + }[]; +} \ No newline at end of file diff --git a/src/types/MissionType.ts b/src/types/MissionType.ts index 9a739f0..14cb771 100644 --- a/src/types/MissionType.ts +++ b/src/types/MissionType.ts @@ -1,3 +1,3 @@ -type MissionType = "TRANSPORT" | "ATTACK" | "TRANSFER" | "EXPEDITION"; +type MissionType = "TRANSPORT" | "ATTACK" | "TRANSFER" | "EXPEDITION" | "MINE"; export default MissionType; \ No newline at end of file diff --git a/src/types/db/DBFleet.ts b/src/types/db/DBFleet.ts index e5342f2..8d31c4a 100644 --- a/src/types/db/DBFleet.ts +++ b/src/types/db/DBFleet.ts @@ -11,4 +11,5 @@ export default interface DBFleet { mission: MissionType; ships: Array<{ id: string, amount: number }>; cargo: Array<{ id: string, amount: number }>; + additionalData?: string; } \ No newline at end of file diff --git a/src/types/db/DBSystem.ts b/src/types/db/DBSystem.ts index df9138c..735bb31 100644 --- a/src/types/db/DBSystem.ts +++ b/src/types/db/DBSystem.ts @@ -1,4 +1,4 @@ -import { ObjectId } from "mongodb"; +import { ObjectId, UUID } from "mongodb"; export default interface DBSystem { _id: ObjectId; @@ -21,4 +21,12 @@ export default interface DBSystem { id: string, amount: number }>; + asteroids: Array<{ + id: UUID, + name: string, + resources: Array<{ + id: string, + amount: number + }> + }>; } \ No newline at end of file