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 { 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);
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -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
|
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 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>
|
Loading…
Reference in New Issue