Compare commits

...

2 Commits

Author SHA1 Message Date
Aelita4 37097bb361
Fix bug with building not showing requirements error 2024-09-25 13:59:17 +02:00
Aelita4 32671fc1d5
Add ship building functionality 2024-09-25 13:58:02 +02:00
8 changed files with 307 additions and 17 deletions

View File

@ -52,7 +52,7 @@ export default class Building {
const playerResearch = this.manager.planet.manager.owner.research; const playerResearch = this.manager.planet.manager.owner.research;
let playerResearchCanBuild = { canBuild: true, missing: "" }; let playerResearchCanBuild = { canBuild: true, missing: "" };
for(const researchReq of this.data.requirements.research) { for(const researchReq of this.data.requirements.research) {
if(playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0 < researchReq.level) { 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}` }; playerResearchCanBuild = { canBuild: false, missing: `${researchReq.id} level ${researchReq.level} required, found ${playerResearch.research.find((research) => research.id === researchReq.id)?.level ?? 0}` };
} }
}; };

68
src/lib/classes/Ship.ts Normal file
View File

@ -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<boolean> {
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: ""
}
}
}

View File

@ -3,6 +3,7 @@ import BuildingManager from "./BuildingManager";
import { getPlanetById } from "../../db/planets"; import { getPlanetById } from "../../db/planets";
import ResourceManager from "./ResourceManager"; import ResourceManager from "./ResourceManager";
import User from "../User"; import User from "../User";
import ShipManager from "./ShipManager";
export type Planet = { export type Planet = {
_id: ObjectId; _id: ObjectId;
@ -11,7 +12,7 @@ export type Planet = {
fields: number; fields: number;
resources: ResourceManager; resources: ResourceManager;
buildings: BuildingManager; buildings: BuildingManager;
ships: Array<any>; ships: ShipManager;
} }
export default class PlanetManager { export default class PlanetManager {
@ -35,11 +36,13 @@ export default class PlanetManager {
resources: null, resources: null,
//@ts-ignore //@ts-ignore
buildings: null, buildings: null,
ships: planet.ships //@ts-ignore
ships: null
} }
planetObject.resources = await new ResourceManager(planetObject).init(planet.resources); planetObject.resources = await new ResourceManager(planetObject).init(planet.resources);
planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings); planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings);
planetObject.ships = await new ShipManager(planetObject).init(planet.ships);
this.planets.push(planetObject); this.planets.push(planetObject);
})); }));

View File

@ -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<Ship> = [];
shipsDB: Array<DBShip> = []
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 } }));
}
}

View File

@ -27,3 +27,12 @@ export const updatePlanetBuildings = async (planetId: ObjectId, buildings: Array
} }
}); });
} }
export const updatePlanetShips = async (planetId: ObjectId, ships: Array<{ id: string, amount: number }>) => {
const planets = await Planets();
await planets.updateOne({ _id: planetId }, {
$set: {
ships
}
});
}

View File

@ -67,7 +67,7 @@ export const POST: APIRoute = async({ request }) => {
JSON.stringify({ JSON.stringify({
code: 400, code: 400,
message: "Bad Request", message: "Bad Request",
error: requirements.error + " | " + resources ? "" : "Not enough resources" error: `${requirements.error} | ${resources ? "" : "Not enough resources"}`
}), { status: 400 } }), { status: 400 }
) )
} }

View File

@ -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 }
);
}

View File

@ -6,6 +6,8 @@ import { getHighestWeightedLanguage, getLocales, getObj } from '../../lib/utils/
import ResourceBar from '../../components/ResourceBar.astro'; import ResourceBar from '../../components/ResourceBar.astro';
import { getAllShips } from '../../lib/db/ships'; import { getAllShips } from '../../lib/db/ships';
import ItemCard from '../../components/ItemCard.astro'; 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 loggedToken = Astro.cookies.get('sessionToken')?.value ?? null;
const username = Astro.cookies.get('username')?.value ?? ""; 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 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 lang = await getLocales(await getHighestWeightedLanguage(Astro.request.headers.get('accept-language')));
const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any> } } = {}; const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any> } } = {};
@ -50,6 +64,7 @@ for(const ship of ships) {
<ItemCard <ItemCard
id={ship.id} id={ship.id}
name={getObj(lang, "ships", ship.id).name} name={getObj(lang, "ships", ship.id).name}
level={planet.ships.getShipById(ship.id)?.amount.toString() ?? "0"}
description={getObj(lang, "ships", ship.id).description ?? ""} description={getObj(lang, "ships", ship.id).description ?? ""}
image={`/images/ships/${ship.id}.jpeg`} image={`/images/ships/${ship.id}.jpeg`}
button_type="general" button_type="general"
@ -171,7 +186,7 @@ for(const ship of ships) {
z-index: 101; z-index: 101;
} }
</style> </style>
<script define:vars={{ modalSet, lang }}> <script define:vars={{ modalSet, lang, planetId }}>
const modalResources = document.getElementById("ship-modal-req-resources"); const modalResources = document.getElementById("ship-modal-req-resources");
const modalBuildings = document.getElementById("ship-modal-req-buildings"); const modalBuildings = document.getElementById("ship-modal-req-buildings");
const modalResearch = document.getElementById("ship-modal-req-research"); const modalResearch = document.getElementById("ship-modal-req-research");
@ -183,14 +198,10 @@ for(const ship of ships) {
if(!modalDiv) return; if(!modalDiv) return;
modalDiv.style.display = 'block'; modalDiv.style.display = 'block';
console.log(modalSet)
const reqResources = modalSet[el.parentElement.parentElement.dataset.id]?.resources ?? []; const reqResources = modalSet[el.parentElement.parentElement.dataset.id]?.resources ?? [];
const reqBuildings = modalSet[el.parentElement.parentElement.dataset.id]?.buildings ?? []; const reqBuildings = modalSet[el.parentElement.parentElement.dataset.id]?.buildings ?? [];
const reqResearch = modalSet[el.parentElement.parentElement.dataset.id]?.research ?? []; const reqResearch = modalSet[el.parentElement.parentElement.dataset.id]?.research ?? [];
console.log(modalSet)
modalResources.innerHTML = reqResources.length === 0 ? "None" : reqResources.map(resource => { modalResources.innerHTML = reqResources.length === 0 ? "None" : reqResources.map(resource => {
return `${lang['resources'].find(r => r.id === resource.id).name}: ${resource.amount}`; return `${lang['resources'].find(r => r.id === resource.id).name}: ${resource.amount}`;
}).join("<br />"); }).join("<br />");
@ -223,23 +234,29 @@ for(const ship of ships) {
backgroundDiv.style.display = 'none'; backgroundDiv.style.display = 'none';
}); });
const allButtons = document.getElementsByClassName("a-button"); const allButtons = document.getElementsByClassName("item-card-build");
for(const researchButton of allButtons) { for(const shipButton of allButtons) {
researchButton.addEventListener("click", async () => { shipButton.addEventListener("click", async () => {
const id = researchButton.id.split("_")[1]; const id = shipButton.id.split("_")[1];
console.log(id); const response = await fetch('/api/ships/addShip', {
await fetch('/api/research/performResearch', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({ body: JSON.stringify({
research: id planet: planetId,
ship: id,
amount: 1
}) })
}); });
if(response.status === 200) {
window.location.reload();
} else {
alert("Failed to build ship: " + JSON.stringify(await response.json()));
}
}); });
} }
</script> </script>