diff --git a/src/lib/classes/Ship.ts b/src/lib/classes/Ship.ts new file mode 100644 index 0000000..e9b98ae --- /dev/null +++ b/src/lib/classes/Ship.ts @@ -0,0 +1,68 @@ +import DBShip from "../../types/db/DBShip"; +import ShipManager from "./managers/ShipManager"; + +export default class Ship { + manager: ShipManager; + data: DBShip; + amount: number; + + constructor(manager: ShipManager, data: DBShip, amount: number) { + this.manager = manager; + this.data = data; + this.amount = amount; + } + + async checkRequiredResources(): Promise { + const resources = await this.manager.planet.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 }> { + const playerBuildings = this.manager.planet.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 + } + + // research + const playerResearch = this.manager.planet.manager.owner.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/PlanetManager.ts b/src/lib/classes/managers/PlanetManager.ts index e5e22ff..8f04ca1 100644 --- a/src/lib/classes/managers/PlanetManager.ts +++ b/src/lib/classes/managers/PlanetManager.ts @@ -3,6 +3,7 @@ import BuildingManager from "./BuildingManager"; import { getPlanetById } from "../../db/planets"; import ResourceManager from "./ResourceManager"; import User from "../User"; +import ShipManager from "./ShipManager"; export type Planet = { _id: ObjectId; @@ -11,7 +12,7 @@ export type Planet = { fields: number; resources: ResourceManager; buildings: BuildingManager; - ships: Array; + ships: ShipManager; } export default class PlanetManager { @@ -35,11 +36,13 @@ export default class PlanetManager { resources: null, //@ts-ignore buildings: null, - ships: planet.ships + //@ts-ignore + ships: null } planetObject.resources = await new ResourceManager(planetObject).init(planet.resources); planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings); + planetObject.ships = await new ShipManager(planetObject).init(planet.ships); this.planets.push(planetObject); })); diff --git a/src/lib/classes/managers/ShipManager.ts b/src/lib/classes/managers/ShipManager.ts new file mode 100644 index 0000000..0c75efa --- /dev/null +++ b/src/lib/classes/managers/ShipManager.ts @@ -0,0 +1,63 @@ +import { Planet } from './PlanetManager'; +import { updatePlanetBuildings, updatePlanetShips } from '../../db/planets'; +import DBShip from '../../../types/db/DBShip'; +import { getAllShips } from '../../db/ships'; +import Ship from '../Ship'; + +export default class ShipManager { + ships: Array = []; + shipsDB: Array = [] + planet: Planet; + + constructor(planet: Planet) { + this.planet = planet; + } + + async init(shipData: { id: string, amount: number }[]) { + this.shipsDB = await getAllShips(); + + for(const ship of shipData) { + const shipToFind = this.shipsDB.find(s => s.id === ship.id); + + if(shipToFind) this.ships.push(new Ship( + this, + shipToFind, + ship.amount + )) + } + + return this; + } + + getShipById(id: string) { + return this.ships.find(ship => ship.data.id === id); + } + + addShips(id: string, amount: number) { + const findShip = this.ships.find(s => s.data.id === id); + if(!findShip) { + const shipData = this.shipsDB.find(s => s.id === id); + if(!shipData) return; + + this.ships.push(new Ship( + this, + shipData, + amount + )); + } + else findShip.amount += amount; + } + + removeShips(id: string, amount: number) { + const findShip = this.ships.find(s => s.data.id === id); + if(findShip) { + findShip.amount -= amount; + // If no more ships left, remove from array + if(findShip.amount <= 0) this.ships.splice(this.ships.indexOf(findShip), 1); + } + } + + async sync() { + await updatePlanetShips(this.planet._id, this.ships.map(ship => { return { id: ship.data.id, amount: ship.amount } })); + } +} \ No newline at end of file diff --git a/src/lib/db/planets.ts b/src/lib/db/planets.ts index 798bee0..390e625 100644 --- a/src/lib/db/planets.ts +++ b/src/lib/db/planets.ts @@ -26,4 +26,13 @@ export const updatePlanetBuildings = async (planetId: ObjectId, buildings: Array buildings } }); +} + +export const updatePlanetShips = async (planetId: ObjectId, ships: Array<{ id: string, amount: number }>) => { + const planets = await Planets(); + await planets.updateOne({ _id: planetId }, { + $set: { + ships + } + }); } \ No newline at end of file diff --git a/src/pages/api/ships/addShip.ts b/src/pages/api/ships/addShip.ts new file mode 100644 index 0000000..6b60a9b --- /dev/null +++ b/src/pages/api/ships/addShip.ts @@ -0,0 +1,130 @@ +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 Ship from "../../../lib/classes/Ship"; +import { getAllResources } from "../../../lib/db/resources"; + +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.planet || !body.ship || !body.amount) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Missing required fields: planet, ship, 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 } + ) + } + + const userPlanet = locationManager.getPlanet(new ObjectId(body.planet)); + + if(!userPlanet) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid planet ID" + }), { status: 400 } + ) + } + + const shipDB = userPlanet.ships.shipsDB.find(s => s.id === body.ship); + + if(!shipDB) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid ship ID" + }), { status: 400 } + ) + } + + const ship = new Ship(userPlanet.ships, shipDB, amount); + + const requirements = await ship.checkRequirements(); + const resources = await ship.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 userPlanet.resources.getDifference(ship.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 + } + }); + + userPlanet.resources.update(resourcesAfter); + userPlanet.ships.addShips(ship.data.id, amount); + + await userPlanet.ships.sync(); + await userPlanet.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/ships.astro b/src/pages/game/ships.astro index 2b43c7d..f3a0d0c 100644 --- a/src/pages/game/ships.astro +++ b/src/pages/game/ships.astro @@ -6,6 +6,8 @@ import { getHighestWeightedLanguage, getLocales, getObj } from '../../lib/utils/ import ResourceBar from '../../components/ResourceBar.astro'; import { getAllShips } from '../../lib/db/ships'; import ItemCard from '../../components/ItemCard.astro'; +import locationManager from '../../lib/classes/managers/LocationManager'; +import { ObjectId } from 'mongodb'; const loggedToken = Astro.cookies.get('sessionToken')?.value ?? null; const username = Astro.cookies.get('username')?.value ?? ""; @@ -16,6 +18,18 @@ if(checkUser === null || checkUser.username !== username) return Astro.redirect( const ships = await getAllShips(); +const planetId = Astro.cookies.get('planetid')?.value ?? ""; +if(planetId === "") { + console.error("No planet selected"); + return Astro.redirect('/logout'); +} + +const planet = locationManager.getPlanet(new ObjectId(planetId)); +if(!planet) { + console.error("Planet not found"); + return Astro.redirect('/logout'); +} + const lang = await getLocales(await getHighestWeightedLanguage(Astro.request.headers.get('accept-language'))); const modalSet: { [key: string]: { resources: Array, research: Array, buildings: Array } } = {}; @@ -50,6 +64,7 @@ for(const ship of ships) { - \ No newline at end of file