diff --git a/public/images/structures/dyson-sphere.jpeg b/public/images/structures/dyson-sphere.jpeg new file mode 100644 index 0000000..e0ae00f Binary files /dev/null and b/public/images/structures/dyson-sphere.jpeg differ diff --git a/public/images/structures/refueling-station.jpeg b/public/images/structures/refueling-station.jpeg new file mode 100644 index 0000000..ee65cff Binary files /dev/null and b/public/images/structures/refueling-station.jpeg differ diff --git a/public/images/structures/storage-station.jpeg b/public/images/structures/storage-station.jpeg new file mode 100644 index 0000000..cb6fbcd Binary files /dev/null and b/public/images/structures/storage-station.jpeg differ diff --git a/src/lib/classes/Structure.ts b/src/lib/classes/Structure.ts new file mode 100644 index 0000000..d0de7a9 --- /dev/null +++ b/src/lib/classes/Structure.ts @@ -0,0 +1,70 @@ +import DBStructure from "../../types/db/DBStructure"; +import StructureManager from "./managers/StructureManager"; + +export default class Structure { + manager: StructureManager + data: DBStructure; + level: number; + + constructor(manager: StructureManager, data: DBStructure, level: number) { + this.manager = manager; + this.data = data; + this.level = level; + } + + // async checkRequiredResources(level: number): 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; + + // const required = Math.pow(this.data.multiplier, level) * res.amount; + + // if(resource.amount < required) { + // canBuild = false; + // break; + // } + // } + + // return canBuild; + // } + + // async checkRequirements(): Promise<{ canBuild: boolean, error: string }> { + // const playerBuildings = this.manager.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/LocationManager.ts b/src/lib/classes/managers/LocationManager.ts index b115887..edc5f55 100644 --- a/src/lib/classes/managers/LocationManager.ts +++ b/src/lib/classes/managers/LocationManager.ts @@ -5,6 +5,7 @@ import { getSectorById } from "../../db/sectors"; import { getSystemById } from "../../db/systems"; import { getAllUsers } from "../../db/users"; import User from "../User"; +import StructureManager from "./StructureManager"; export type Galaxy = { _id: ObjectId, @@ -29,6 +30,7 @@ export type System = { sector: Sector, name: string, ownedBy: User, + structures: StructureManager, planets: PlanetManager } @@ -48,6 +50,9 @@ class LocationManager { users: User[] = []; async init() { + this.galaxies = []; + this.users = []; + const users = await getAllUsers(); users.forEach(async user => { this.users.push(new User(user._id, user.username, user.email, user.createdAt, user.updatedAt, user.lastLogin)); @@ -86,9 +91,13 @@ class LocationManager { sector: sectorObject, name: systemData.name, ownedBy: user, + //@ts-ignore + structures: null, planets: new PlanetManager(user) }; + systemObject.structures = await new StructureManager(systemObject).init(systemData.structures); + await systemObject.planets.fillData(systemObject, systemData.planets); sectorObject.systems.push(systemObject); @@ -176,5 +185,5 @@ class LocationManager { } const locationManager = LocationManager.getInstance(); -locationManager.init(); +await locationManager.init(); export default locationManager; \ No newline at end of file diff --git a/src/lib/classes/managers/StructureManager.ts b/src/lib/classes/managers/StructureManager.ts new file mode 100644 index 0000000..2fa85a2 --- /dev/null +++ b/src/lib/classes/managers/StructureManager.ts @@ -0,0 +1,50 @@ +import { updatePlanetBuildings } from '../../db/planets'; +import Building from '../Building'; +import DBStructure from '../../../types/db/DBStructure'; +import { System } from './LocationManager'; +import { getAllStructures } from '../../db/structures'; +import Structure from '../Structure'; +import { updateSystemStructures } from '../../db/systems'; + +export default class StructureManager { + structures: Array = []; + structuresDB: Array = [] + system: System; + + constructor(system: System) { + this.system = system; + } + + async init(structureData: { id: string, level: number }[]) { + this.structuresDB = await getAllStructures(); + structureData.forEach(structure => { + const structureToFind = this.structuresDB.find(s => s.id === structure.id); + + if(structureToFind) this.structures.push(new Structure( + this, + structureToFind, + structure.level + )) + }) + + return this; + } + + getStructureById(id: string) { + return this.structures.find(structure => structure.data.id === id); + } + + addStructure(structure: Structure) { + const findStructure = this.structures.find(s => s.data.id === structure.data.id); + if(!findStructure) this.structures.push(structure); + else findStructure.level++; + } + + removeStructure(id: string) { + this.structures = this.structures.filter(structure => structure.data.id !== id); + } + + async sync() { + await updateSystemStructures(this.system._id, this.structures.map(structure => { return { id: structure.data.id, level: structure.level } })); + } +} \ No newline at end of file diff --git a/src/lib/db/lang.ts b/src/lib/db/lang.ts index d5054d1..7ecbf1c 100644 --- a/src/lib/db/lang.ts +++ b/src/lib/db/lang.ts @@ -7,6 +7,7 @@ export const getLang = async (language = "en") => { buildings: await lang[1].find({}).toArray(), ships: await lang[2].find({}).toArray(), resources: await lang[3].find({}).toArray(), - research: await lang[4].find({}).toArray() + research: await lang[4].find({}).toArray(), + structures: await lang[5].find({}).toArray() } } \ No newline at end of file diff --git a/src/lib/db/mongodb.ts b/src/lib/db/mongodb.ts index 45d82f6..7782473 100644 --- a/src/lib/db/mongodb.ts +++ b/src/lib/db/mongodb.ts @@ -76,6 +76,11 @@ export const Fleet = async() => { return db.collection('fleet'); } +export const Structures = async() => { + const db = await getDB(); + return db.collection('structures'); +} + export const Lang = async (language = "en") => { const db = await getDB(`${config.MONGODB_DB}_${language}`); return [ @@ -83,6 +88,7 @@ export const Lang = async (language = "en") => { await db.collection('buildings'), await db.collection('ships'), await db.collection('resources'), - await db.collection('research') + await db.collection('research'), + await db.collection('structures') ] } \ No newline at end of file diff --git a/src/lib/db/structures.ts b/src/lib/db/structures.ts new file mode 100644 index 0000000..c1bfd23 --- /dev/null +++ b/src/lib/db/structures.ts @@ -0,0 +1,12 @@ +import DBStructure from '../../types/db/DBStructure'; +import { Structures } from '../db/mongodb'; + +export const getAllStructures = async () => { + return (await Structures()).find({}).toArray() as unknown as Array; +} + +export const getStructureById = async (id: string) => { + return (await Structures()).findOne({ + id + }) as unknown as DBStructure; +} \ No newline at end of file diff --git a/src/lib/db/systems.ts b/src/lib/db/systems.ts index 339b1eb..9b55015 100644 --- a/src/lib/db/systems.ts +++ b/src/lib/db/systems.ts @@ -10,4 +10,13 @@ export const getSystemById = async (id: ObjectId) => { return await (await Systems()).findOne({ _id: id }) as DBSystem; +} + +export const updateSystemStructures = async (systemId: ObjectId, structures: Array<{ id: string, level: number }>) => { + const systems = await Systems(); + await systems.updateOne({ _id: systemId }, { + $set: { + structures + } + }); } \ No newline at end of file diff --git a/src/pages/api/structures/createStructure.ts b/src/pages/api/structures/createStructure.ts new file mode 100644 index 0000000..6ae2660 --- /dev/null +++ b/src/pages/api/structures/createStructure.ts @@ -0,0 +1,110 @@ +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 Building from "../../../lib/classes/Building"; +import { getAllResources } from "../../../lib/db/resources"; +import Structure from "../../../lib/classes/Structure"; + +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 } + ) + } + + const userSystem = locationManager.getSystem(new ObjectId(body.system)); + + if(!userSystem) { + return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid system ID" + }), { status: 400 } + ) + } + + const structureId: string = body.structureId; + + const structureObj = userSystem.structures.structuresDB.find(s => s.id === structureId); + + if(!structureObj) return new Response( + JSON.stringify({ + code: 400, + message: "Bad Request", + error: "Invalid structure ID" + }), { status: 400 } + ) + + const structure = new Structure(userSystem.structures, structureObj, 1); + + // const requirements = await structure.checkRequirements(); + // const resources = await building.checkRequiredResources((userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1); + + // 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(building.data.requirements.resources.map(res => { + // return { + // id: res.id, + // amount: Math.pow(building.data.multiplier, (userPlanet.buildings.getBuildingById(buildingId)?.level ?? 0) + 1) * 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); + // userSystem.structures.addStructure(structure); + + // await userSystem.structures.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/systemManager/index.astro b/src/pages/game/systemManager/index.astro index c09d078..8f5ab58 100644 --- a/src/pages/game/systemManager/index.astro +++ b/src/pages/game/systemManager/index.astro @@ -41,10 +41,16 @@ if(Astro.request.method === "POST") { -

Selected system: {currentSystem.name} (change)

-
+

Selected system: {currentSystem.name} in {currentSystem.sector.name} in {currentSystem.sector.galaxy.name} (change)

+ +
{currentSystem.planets.planets.length === 0 ? No planets in this sector : currentSystem.planets.planets.map(planet => ( -
+

{planet.name}

Fields: {planet.fields}

Buildings:

@@ -69,7 +75,27 @@ if(Astro.request.method === "POST") { color: white; } - .planetList { + h1 a { + color: lime; + } + + .system-links { + display: flex; + flex-direction: row; + justify-content: center; + margin-bottom: 1rem; + } + + .system-links a { + color: white; + background-color: #555; + padding: 0.5rem; + margin: 0 1rem; + border-radius: 5px; + text-decoration: none; + } + + .planet-list { display: flex; flex-direction: row; flex-wrap: wrap; diff --git a/src/pages/game/systemManager/structures.astro b/src/pages/game/systemManager/structures.astro new file mode 100644 index 0000000..8c8d794 --- /dev/null +++ b/src/pages/game/systemManager/structures.astro @@ -0,0 +1,223 @@ +--- +import { ObjectId } from "mongodb"; +import NavBar from "../../../components/NavBar.astro"; +import ResourceBar from "../../../components/ResourceBar.astro"; +import Layout from "../../../layouts/Layout.astro"; +import locationManager from "../../../lib/classes/managers/LocationManager"; +import { getUserByAccessToken } from "../../../lib/db/users"; +import { getHighestWeightedLanguage, getLocales, getName, getObj } from "../../../lib/utils/langDriver"; +import ItemCard from "../../../components/ItemCard.astro"; + +const loggedToken = Astro.cookies.get('sessionToken')?.value ?? null; +const username = Astro.cookies.get('username')?.value ?? ""; +if(loggedToken === null || username === "") return Astro.redirect('/logout'); + +const checkUser = await getUserByAccessToken(loggedToken); +if(checkUser === null || checkUser.username !== username) return Astro.redirect('/logout'); + +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 selectedStructureId = (await Astro.request.formData()).get('id') as string | null; + + const request = await (await fetch(Astro.url.origin + '/api/structures/createStructure', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': 'Bearer ' + loggedToken + }, + body: JSON.stringify({ + system: currentSystemId, + structureId: selectedStructureId + }) + })).json(); + + console.log(request); +} + +const lang = await getLocales(Astro.cookies.get('language')?.value ?? await getHighestWeightedLanguage(Astro.request.headers.get('accept-language'))); + +const modalSet: { [key: string]: { resources: Array, research: Array, buildings: Array, energy: number } } = {}; + +const structureList = currentSystem.structures.structuresDB; +for(const structure of structureList) { + modalSet[structure.id] = { + resources: structure.requirements.resources.map(resource => { + return { + id: resource.id, + amount: Math.pow(structure.multiplier, (currentSystem.structures.getStructureById(structure.id)?.level ?? 0) ) * resource.amount + }; + }), + research: structure.requirements.research, + buildings: structure.requirements.buildings, + energy: structure.energy + }; +} +--- + + + + +

Selected system: {currentSystem.name} (change)

+ +
+
+

Required resources

+
None
+

Required buildings

+
None
+

Required research

+
None
+
+
+
+ {currentSystem.structures.structuresDB.map(structure => ( + + ))} +
+
+ + \ No newline at end of file diff --git a/src/types/db/DBStructure.ts b/src/types/db/DBStructure.ts new file mode 100644 index 0000000..65195b3 --- /dev/null +++ b/src/types/db/DBStructure.ts @@ -0,0 +1,15 @@ +import { ObjectId } from "mongodb"; + +export default interface DBStructure { + _id: ObjectId; + id: string; + description: string; + requirements: { + buildings: Array<{ id: string, level: number }>, + research: Array<{ id: string, level: number }>, + resources: Array<{ id: string, amount: number }>, + }; + energy: number; + time: number; + multiplier: number; +} \ No newline at end of file diff --git a/src/types/db/DBSystem.ts b/src/types/db/DBSystem.ts index 9b3030d..171ff87 100644 --- a/src/types/db/DBSystem.ts +++ b/src/types/db/DBSystem.ts @@ -4,5 +4,9 @@ export default interface DBSystem { _id: ObjectId; name: string; ownedBy: ObjectId; + structures: Array<{ + id: string, + level: number + }>; planets: Array; } \ No newline at end of file