Add ship building functionality
This commit is contained in:
parent
ceefef38df
commit
32671fc1d5
|
@ -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: ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<any>;
|
||||
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);
|
||||
}));
|
||||
|
|
|
@ -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 } }));
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
});
|
||||
}
|
|
@ -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 }
|
||||
);
|
||||
}
|
|
@ -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<any>, research: Array<any>, buildings: Array<any> } } = {};
|
||||
|
@ -50,6 +64,7 @@ for(const ship of ships) {
|
|||
<ItemCard
|
||||
id={ship.id}
|
||||
name={getObj(lang, "ships", ship.id).name}
|
||||
level={planet.ships.getShipById(ship.id)?.amount.toString() ?? "0"}
|
||||
description={getObj(lang, "ships", ship.id).description ?? ""}
|
||||
image={`/images/ships/${ship.id}.jpeg`}
|
||||
button_type="general"
|
||||
|
@ -171,7 +186,7 @@ for(const ship of ships) {
|
|||
z-index: 101;
|
||||
}
|
||||
</style>
|
||||
<script define:vars={{ modalSet, lang }}>
|
||||
<script define:vars={{ modalSet, lang, planetId }}>
|
||||
const modalResources = document.getElementById("ship-modal-req-resources");
|
||||
const modalBuildings = document.getElementById("ship-modal-req-buildings");
|
||||
const modalResearch = document.getElementById("ship-modal-req-research");
|
||||
|
@ -183,14 +198,10 @@ for(const ship of ships) {
|
|||
if(!modalDiv) return;
|
||||
modalDiv.style.display = 'block';
|
||||
|
||||
console.log(modalSet)
|
||||
|
||||
const reqResources = modalSet[el.parentElement.parentElement.dataset.id]?.resources ?? [];
|
||||
const reqBuildings = modalSet[el.parentElement.parentElement.dataset.id]?.buildings ?? [];
|
||||
const reqResearch = modalSet[el.parentElement.parentElement.dataset.id]?.research ?? [];
|
||||
|
||||
console.log(modalSet)
|
||||
|
||||
modalResources.innerHTML = reqResources.length === 0 ? "None" : reqResources.map(resource => {
|
||||
return `${lang['resources'].find(r => r.id === resource.id).name}: ${resource.amount}`;
|
||||
}).join("<br />");
|
||||
|
@ -223,23 +234,29 @@ for(const ship of ships) {
|
|||
backgroundDiv.style.display = 'none';
|
||||
});
|
||||
|
||||
const allButtons = document.getElementsByClassName("a-button");
|
||||
const allButtons = document.getElementsByClassName("item-card-build");
|
||||
|
||||
for(const researchButton of allButtons) {
|
||||
researchButton.addEventListener("click", async () => {
|
||||
const id = researchButton.id.split("_")[1];
|
||||
for(const shipButton of allButtons) {
|
||||
shipButton.addEventListener("click", async () => {
|
||||
const id = shipButton.id.split("_")[1];
|
||||
|
||||
console.log(id);
|
||||
|
||||
await fetch('/api/research/performResearch', {
|
||||
const response = await fetch('/api/ships/addShip', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
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>
|
Loading…
Reference in New Issue