Add defense system

This commit is contained in:
Aelita4 2025-01-13 10:49:30 +01:00
parent 8b5eef482f
commit 119cf70e86
Signed by: Aelita4
GPG Key ID: E44490C2025906C1
18 changed files with 631 additions and 15 deletions

View File

@ -101,6 +101,13 @@ const listOfElements: Array<NavElement> = [{
url: "/game/fleet",
show: "loggedInOnly",
position: "bottom"
},{
id: "defenses",
title: getName(lang, "general", "nav-defenses"),
type: "simple",
url: "/game/defenses",
show: "loggedInOnly",
position: "bottom"
}, {
id: "galaxyView",
title: getName(lang, "general", "nav-galaxy-view"),

View File

@ -0,0 +1,84 @@
import DBDefenses from "../../types/db/DBDefenses";
import DefenseManager from "./managers/abstract/DefenseManager";
import SystemManager from "./managers/SystemManager";
export default class Defense {
manager: DefenseManager;
data: DBDefenses;
amount: number;
constructor(manager: DefenseManager, data: DBDefenses, amount: number) {
this.manager = manager;
this.data = data;
this.amount = amount;
}
async checkRequiredResources(): Promise<boolean> {
const resources = await this.manager.manager.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 }> {
if(!(this.manager.manager instanceof SystemManager)) {
const playerBuildings = this.manager.manager.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
}
} /*else { //TODO: check for structures requirements
const structures = this.manager.manager.structures.structures;
let playerStructuresCanBuild = { canBuild: true, missing: "" };
this.data.requirements.structures.forEach((buildingReq: any) => {
if((structures.filter((building) => building.data.id === buildingReq.id)[0]?.level ?? 0) < buildingReq.level) {
playerStructuresCanBuild = { canBuild: false, missing: `${buildingReq.id} level ${buildingReq.level} required, found ${structures.filter((structure) => structure.data.id === buildingReq.id)[0]?.level ?? 0}` };
return;
}
});
if(!playerStructuresCanBuild.canBuild) return {
canBuild: false,
error: playerStructuresCanBuild.missing
}
}*/
// research
const playerResearch = this.manager.manager instanceof SystemManager ? this.manager.manager.data.ownedBy.research : this.manager.manager.system.data.ownedBy.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

@ -9,6 +9,7 @@ import { getRandomInRange, weightedRandom } from "../../utils/math";
import { Sector } from "./LocationManager";
import { Planet } from "./PlanetManager";
import SystemManager from "./SystemManager";
import { getAllDefenses } from "../../db/defenses";
export type Fleet = {
id: ObjectId,
@ -104,7 +105,7 @@ export default class FleetManager {
case 'ATTACK':
if(!("expedition" in this.data.destination)) {
const enemyShips = this.data.destination.ships.ships;
return await this.battleResults(enemyShips.map(ship => { return { id: ship.data.id, amount: ship.amount } }));
return await this.battleResults(enemyShips.map(ship => { return { id: ship.data.id, amount: ship.amount } }), this.data.destination.defenses.defenses.map(defense => { return { id: defense.data.id, amount: defense.amount } }));
} else {
throw new Error("Cannot attack sector.");
}
@ -161,18 +162,19 @@ export default class FleetManager {
await this.sync();
}
private async sendMail(title: string, description: string) {
private async sendMail(user: ObjectId, title: string, description: string) {
await sendMail(
null,
this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id,
user,
this.data.arrivalTime,
title,
description
);
}
async battleResults(enemyFleet: { id: string, amount: number }[]) {
async battleResults(enemyFleet: { id: string, amount: number }[], enemyDefenses: { id: string, amount: number }[] = []) {
const allShips = await getAllShips();
const allDefenses = await getAllDefenses();
const playerStats = this.data.ships.reduce((acc, ship) => {
const dbShip = allShips.find(s => s.id === ship.id);
@ -183,6 +185,14 @@ export default class FleetManager {
return acc;
}, { attack: 0, defense: 0, hitpoints: 0 });
const enemyDefensesStats = enemyDefenses.reduce((acc, defense) => {
const dbDefense = allDefenses.find(d => d.id === defense.id);
if(!dbDefense) return acc;
acc.defense += dbDefense.structure.defense * defense.amount;
acc.hitpoints += dbDefense.structure.hitpoints * defense.amount;
return acc;
}, { attack: 0, defense: 0, hitpoints: 0 });
const enemyStats = enemyFleet.reduce((acc, ship) => {
const dbShip = allShips.find(s => s.id === ship.id);
if(!dbShip) return acc;
@ -190,7 +200,7 @@ export default class FleetManager {
acc.defense += dbShip.structure.defense * ship.amount;
acc.hitpoints += dbShip.structure.hitpoints * ship.amount;
return acc;
}, { attack: 0, defense: 0, hitpoints: 0 });
}, enemyDefensesStats);
const playerShipsStructure: BattleFleet[] = [];
for(const playerShip of this.data.ships) {
@ -282,10 +292,12 @@ export default class FleetManager {
} else this.data.ships = [];
await this.sendMail(
this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id,
`Battle Results (${playerBalance > enemyBalance ? "Victory" : playerBalance < enemyBalance ? "Defeat" : "Draw"})`,
`Results of battle at ${(this.data.destination instanceof SystemManager ? this.data.destination.data.name : this.data.destination.name)}:\n
Player ships:\n${previousShips.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n
Enemy ships:\n${enemyFleet.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n\n
Enemy ships:\n${enemyFleet.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n
Enemy defenses:\n${enemyDefenses.map(defense => `${defense.amount} ${defense.id}`).join('\n')}\n
Player stats: ${playerStats.hitpoints} HP, ${playerStats.attack} ATK, ${playerStats.defense} DEF\n
Enemy stats: ${enemyStats.hitpoints} HP, ${enemyStats.attack} ATK, ${enemyStats.defense} DEF\n\n
Player ships left:\n${Object.keys(playerShipsLeft).map(key => `${key} - ${playerShipsLeft[key]}`).join('\n')}\n
@ -293,6 +305,22 @@ export default class FleetManager {
${playerBalance > enemyBalance ? `Resources stolen:\n${resourcesStolen.map(res => `${res.id} - ${res.amount}`).join('\n')}` : ""}`
);
if(!("expedition" in this.data.destination)) {
await this.sendMail(
this.data.destination instanceof SystemManager ? this.data.destination.data.ownedBy.id : this.data.destination.system.data.ownedBy.id,
`Battle Results (${playerBalance < enemyBalance ? "Victory" : playerBalance > enemyBalance ? "Defeat" : "Draw"})`,
`Results of battle at ${(this.data.destination instanceof SystemManager ? this.data.destination.data.name : this.data.destination.name)}:\n
Enemy ships:\n${previousShips.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n
Player ships:\n${enemyFleet.map(ship => `${ship.amount} ${ship.id}`).join('\n')}\n
Player defenses:\n${enemyDefenses.map(defense => `${defense.amount} ${defense.id}`).join('\n')}\n
Enemy stats: ${playerStats.hitpoints} HP, ${playerStats.attack} ATK, ${playerStats.defense} DEF\n
Player stats: ${enemyStats.hitpoints} HP, ${enemyStats.attack} ATK, ${enemyStats.defense} DEF\n\n
Enemy ships left:\n${Object.keys(playerShipsLeft).map(key => `${key} - ${playerShipsLeft[key]}`).join('\n')}\n
Player ships left:\n${Object.keys(enemyShipsLeft).map(key => `${key} - ${enemyShipsLeft[key]}`).join('\n')}\n
${playerBalance > enemyBalance ? `Resources stolen:\n${resourcesStolen.map(res => `${res.id} - ${res.amount}`).join('\n')}` : ""}`
);
}
return !(playerShipsStructure.length > 0);
}
@ -309,7 +337,7 @@ export default class FleetManager {
if(expeditionRandom < 0.02) { // 2% chance; lost all ships, black hole
this.data.ships = [];
await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered a black hole. All ships were lost.`);
await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered a black hole. All ships were lost.`);
return;
}
@ -324,7 +352,7 @@ export default class FleetManager {
else ship.amount += s.amount;
}
await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered abandoned shipyard. Following ships were added to the fleet:\n${ships.map(s => `${s.id} - ${s.amount}`).join(', ')}`);
await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered abandoned shipyard. Following ships were added to the fleet:\n${ships.map(s => `${s.id} - ${s.amount}`).join(', ')}`);
await this.initiateReturn();
return;
}
@ -356,7 +384,7 @@ export default class FleetManager {
else resource.amount += res.amount;
}
await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich asteroid. Following resources were added to the cargo inventory:\n${resources.map(r => `${r.id} - ${r.amount}`).join('\n')}`);
await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich asteroid. Following resources were added to the cargo inventory:\n${resources.map(r => `${r.id} - ${r.amount}`).join('\n')}`);
await this.initiateReturn();
return;
}
@ -367,7 +395,7 @@ export default class FleetManager {
{ id: 'transporter', amount: getRandomInRange(0, 100) + valueAdded }
];
await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered drunk pirates. After attempting to communicate with them, they attacked your fleet.`)
await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered drunk pirates. After attempting to communicate with them, they attacked your fleet.`)
await this.battleResults(pirates);
return;
}
@ -416,12 +444,12 @@ export default class FleetManager {
else resource.amount += res.amount;
}
await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich rouge planet. Your fleet could not extract all resources. Following resources were added to the cargo inventory:\n${addedResources.map(r => `${r.id} - ${r.amount}`).join('\n')}`);
await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered resource-rich rouge planet. Your fleet could not extract all resources. Following resources were added to the cargo inventory:\n${addedResources.map(r => `${r.id} - ${r.amount}`).join('\n')}`);
await this.initiateReturn();
return;
}
await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector scanned the sector for a long time, yet it haven't found anything.`);
await this.sendMail(this.data.source instanceof SystemManager ? this.data.source.data.ownedBy.id : this.data.source.system.data.ownedBy.id, "Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector scanned the sector for a long time, yet it haven't found anything.`);
await this.initiateReturn();
return;
}

View File

@ -0,0 +1,17 @@
import { updatePlanetDefenses } from "../../db/planets";
import { Planet } from "./PlanetManager";
import DefenseManager from "./abstract/DefenseManager";
export default class PlanetDefenseManager extends DefenseManager {
constructor(planet: Planet) {
super(planet);
}
get manager() {
return this._manager as Planet;
}
async sync() {
await updatePlanetDefenses(this.manager._id, this.defenses.map(def => { return { id: def.data.id, amount: def.amount } }));
}
}

View File

@ -3,6 +3,7 @@ import BuildingManager from "./BuildingManager";
import PlanetResourceManager from "./PlanetResourceManager";
import ShipManager from "./PlanetShipManager";
import SystemManager from "./SystemManager";
import PlanetDefenseManager from "./PlanetDefenseManager";
export type Planet = {
_id: ObjectId;
@ -12,4 +13,5 @@ export type Planet = {
resources: PlanetResourceManager;
buildings: BuildingManager;
ships: ShipManager;
defenses: PlanetDefenseManager;
}

View File

@ -0,0 +1,17 @@
import { updateSystemDefenses } from "../../db/systems";
import SystemManager from "./SystemManager";
import DefenseManager from "./abstract/DefenseManager";
export default class SystemDefenseManager extends DefenseManager {
constructor(system: SystemManager) {
super(system);
}
get manager() {
return this._manager as SystemManager;
}
async sync() {
await updateSystemDefenses(this.manager.data._id, this.defenses.map(def => { return { id: def.data.id, amount: def.amount } }));
}
}

View File

@ -10,6 +10,8 @@ import PlanetShipManager from "./PlanetShipManager";
import StructureManager from "./StructureManager";
import SystemResourceManager from "./SystemResourceManager";
import SystemShipManager from "./SystemShipManager";
import PlanetDefenseManager from "./PlanetDefenseManager";
import SystemDefenseManager from "./SystemDefenseManager";
export type System = {
_id: ObjectId,
@ -19,6 +21,7 @@ export type System = {
structures: StructureManager,
resources: SystemResourceManager,
ships: SystemShipManager,
defenses: SystemDefenseManager,
planets: Planet[];
}
@ -27,6 +30,7 @@ export default class SystemManager {
structures: StructureManager;
resources: SystemResourceManager;
ships: SystemShipManager;
defenses: SystemDefenseManager;
data: System;
constructor(data: System) {
@ -34,12 +38,14 @@ export default class SystemManager {
this.structures = new StructureManager(this);
this.resources = new SystemResourceManager(this);
this.ships = new SystemShipManager(this);
this.defenses = new SystemDefenseManager(this);
}
async fillData(systemData: DBSystem) {
await this.structures.init(systemData.structures);
await this.resources.init(systemData.resources);
await this.ships.init(systemData.ships);
await this.defenses.init(systemData.defenses);
await Promise.all(systemData.planets.map(async planetId => {
const planet = await getPlanetById(planetId);
@ -54,12 +60,15 @@ export default class SystemManager {
//@ts-ignore
buildings: null,
//@ts-ignore
ships: null
ships: null,
//@ts-ignore
defenses: null
}
planetObject.resources = await new PlanetResourceManager(planetObject).init(planet.resources);
planetObject.buildings = await new BuildingManager(planetObject).init(planet.buildings);
planetObject.ships = await new PlanetShipManager(planetObject).init(planet.ships);
planetObject.defenses = await new PlanetDefenseManager(planetObject).init(planet.defenses);
this.planets.push(planetObject);
}));

View File

@ -0,0 +1,62 @@
import { Planet } from '../PlanetManager';
import SystemManager from '../SystemManager';
import Defense from '../../Defense';
import DBDefenses from '../../../../types/db/DBDefenses';
import { getAllDefenses } from '../../../db/defenses';
export default abstract class DefenseManager {
defenses: Array<Defense> = [];
defensesDB: Array<DBDefenses> = [];
protected _manager: Planet | SystemManager;
constructor(manager: Planet | SystemManager) {
this._manager = manager;
}
abstract sync(): Promise<void>;
abstract get manager(): Planet | SystemManager;
async init(defenseData: { id: string, amount: number }[]) {
this.defensesDB = await getAllDefenses();
for(const def of defenseData) {
const defToFind = this.defensesDB.find(d => d.id === def.id);
if(defToFind) this.defenses.push(new Defense(
this,
defToFind,
def.amount
))
}
return this;
}
getDefenseById(id: string) {
return this.defenses.find(def => def.data.id === id);
}
addDefenses(id: string, amount: number) {
const findDef = this.defenses.find(d => d.data.id === id);
if(!findDef) {
const defData = this.defensesDB.find(d => d.id === id);
if(!defData) return;
this.defenses.push(new Defense(
this,
defData,
amount
));
}
else findDef.amount += amount;
}
removeDefenses(id: string, amount: number) {
const findDef = this.defenses.find(d => d.data.id === id);
if(findDef) {
findDef.amount -= amount;
if(findDef.amount <= 0) this.defenses.splice(this.defenses.indexOf(findDef), 1);
}
}
}

12
src/lib/db/defenses.ts Normal file
View File

@ -0,0 +1,12 @@
import DBDefenses from '../../types/db/DBDefenses';
import { Defenses } from '../db/mongodb';
export const getAllDefenses = async () => {
return (await Defenses()).find({}).toArray() as unknown as Array<DBDefenses>;
}
export const getDefenseById = async (id: string) => {
return (await Defenses()).findOne({
id
}) as unknown as DBDefenses;
}

View File

@ -8,6 +8,7 @@ export const getLang = async (language = "en") => {
ships: (await lang[2].find({}).toArray()).map(({ _id, ...rest }) => rest),
resources: (await lang[3].find({}).toArray()).map(({ _id, ...rest }) => rest),
research: (await lang[4].find({}).toArray()).map(({ _id, ...rest }) => rest),
structures: (await lang[5].find({}).toArray()).map(({ _id, ...rest }) => rest)
structures: (await lang[5].find({}).toArray()).map(({ _id, ...rest }) => rest),
defenses: (await lang[6].find({}).toArray()).map(({ _id, ...rest }) => rest)
}
}

View File

@ -67,6 +67,11 @@ export const Ships = async() => {
return db.collection('ships');
}
export const Defenses = async() => {
const db = await getDB();
return db.collection('defenses');
}
export const Fleet = async() => {
const db = await getDB();
return db.collection('fleet');
@ -85,7 +90,8 @@ export const Lang = async (language = "en") => {
await db.collection('ships'),
await db.collection('resources'),
await db.collection('research'),
await db.collection('structures')
await db.collection('structures'),
await db.collection('defenses')
]
}

View File

@ -40,4 +40,13 @@ export const updatePlanetShips = async (planetId: ObjectId, ships: Array<{ id: s
ships
}
});
}
export const updatePlanetDefenses = async (planetId: ObjectId, defenses: Array<{ id: string, amount: number }>) => {
const planets = await Planets();
await planets.updateOne({ _id: planetId }, {
$set: {
defenses
}
});
}

View File

@ -42,4 +42,13 @@ export const updateSystemShips = async (systemId: ObjectId, ships: Array<any>) =
ships
}
});
}
export const updateSystemDefenses = async (systemId: ObjectId, defenses: Array<any>) => {
const systems = await Systems();
await systems.updateOne({ _id: systemId }, {
$set: {
defenses
}
});
}

View File

@ -0,0 +1,146 @@
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 { getAllResources } from "../../../lib/db/resources";
import Defense from "../../../lib/classes/Defense";
import { Planet } from "../../../lib/classes/managers/PlanetManager";
import SystemManager from "../../../lib/classes/managers/SystemManager";
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.id || !body.defense || !body.amount) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Missing required fields: id, defense, 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 }
)
}
let id: ObjectId;
try {
id = new ObjectId(body.id);
} catch(e) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid ID"
}), { status: 400 }
)
}
let planetOrSystem: Planet | SystemManager | undefined = locationManager.getPlanet(id);
if(!planetOrSystem) planetOrSystem = locationManager.getSystem(id);
if(!planetOrSystem) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid ID"
}), { status: 400 }
)
}
const defenseDB = planetOrSystem.defenses.defensesDB.find(d => d.id === body.defense);
if(!defenseDB) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid defense ID"
}), { status: 400 }
)
}
const defense = new Defense(planetOrSystem.defenses, defenseDB, amount);
const requirements = await defense.checkRequirements();
const resources = await defense.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 planetOrSystem.resources.getDifference(defense.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
}
});
planetOrSystem.resources.updateAmount(resourcesAfter.map(res => { return { id: res.id, amount: res.amount } }));
planetOrSystem.defenses.addDefenses(defense.data.id, amount);
await planetOrSystem.defenses.sync();
await planetOrSystem.resources.sync();
return new Response(
JSON.stringify({
code: 200,
message: "OK"
}), { status: 200 }
);
}

View File

@ -0,0 +1,184 @@
---
import ItemCard from '../../components/ItemCard.astro';
import LoggedIn from '../../layouts/LoggedIn.astro';
import { Planet } from '../../lib/classes/managers/PlanetManager';
import SystemManager from '../../lib/classes/managers/SystemManager';
import { getAllDefenses } from '../../lib/db/defenses';
import { getObj } from '../../lib/utils/langDriver';
const { token, lang } = Astro.locals;
const active: SystemManager | Planet = Astro.locals.active;
const defenses = await getAllDefenses();
if(Astro.request.method === "POST") {
const body = await Astro.request.formData();
const id = body.get("id") as string;
const amount = parseInt(body.get("amount") as string ?? "1");
await fetch(Astro.url.origin + '/api/defenses/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({
id: active instanceof SystemManager ? active.data._id : active._id,
defense: id,
amount
})
});
}
const modalSet: { [key: string]: { resources: Array<any>, research: Array<any>, buildings: Array<any> } } = {};
for(const def of defenses) {
modalSet[def.id] = {
resources: def.requirements.resources,
research: def.requirements.research,
buildings: def.requirements.buildings,
// energy: building.energy
};
}
const planetId = active instanceof SystemManager ? active.data._id : active._id;
---
<LoggedIn id="defenses" title="Defenses">
<div id="ship-modal-background">
<div id="ship-modal-details" data-building-id="">
<h3>Required resources</h3>
<div class="ship-modal-text" id="ship-modal-req-resources">None</div>
<h3>Required buildings</h3>
<div class="ship-modal-text" id="ship-modal-req-buildings">None</div>
<h3>Required research</h3>
<div class="ship-modal-text" id="ship-modal-req-research">None</div>
</div>
</div>
<div class="ship-cards">
{defenses.map(def => <>
<ItemCard
category="ships"
id={def.id}
name={getObj(lang, "defenses", def.id).name}
level={active.defenses.getDefenseById(def.id)?.amount.toString() ?? "0"}
description={getObj(lang, "defenses", def.id).description ?? ""}
image={`/images/defenses/${def.id}.png`}
button_type="general"
button_name="nav-build"
has_amount_input="true" />
</>)}
</div>
</LoggedIn>
<style>
.ship-cards {
display: flex;
flex-direction: row;
flex-wrap: wrap;
row-gap: 40px;
column-gap: 2%;
margin-top: 40px;
}
#ship-modal-background {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 100;
}
#ship-modal-details {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
max-width: 800px;
background: rgba(0, 0, 0, 0.9);
border-radius: 8px;
padding: 1rem;
z-index: 101;
color: white;
}
</style>
<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");
document.querySelectorAll('.item-card-info-button').forEach((el) => {
el.addEventListener('click', () => {
// modal
const modalDiv = document.getElementById('ship-modal-details');
if(!modalDiv) return;
modalDiv.style.display = 'block';
const reqResources = modalSet[el.parentElement.parentElement.parentElement.dataset.id]?.resources ?? [];
const reqBuildings = modalSet[el.parentElement.parentElement.parentElement.dataset.id]?.buildings ?? [];
const reqResearch = modalSet[el.parentElement.parentElement.parentElement.dataset.id]?.research ?? [];
modalResources.innerHTML = reqResources.length === 0 ? "None" : reqResources.map(resource => {
return `${lang['resources'].find(r => r.id === resource.id).name}: ${resource.amount}`;
}).join("<br />");
modalBuildings.innerHTML = reqBuildings.length === 0 ? "None" : reqBuildings.map(building => {
return `${lang['buildings'].find(b => b.id === building.id).name}: ${building.level}`;
}).join("<br />");
modalResearch.innerHTML = reqResearch.length === 0 ? "None" : reqResearch.map(research => {
return `${lang['research'].find(r => r.id === research.id).name}: ${research.level}`;
}).join("<br />");
// background
const backgroundDiv = document.getElementById('ship-modal-background');
if(!backgroundDiv) return;
backgroundDiv.style.display = 'block';
});
});
// close modal on background click
const bg = document.getElementById('ship-modal-background');
bg?.addEventListener('click', () => {
const modalDiv = document.getElementById('ship-modal-details');
if(!modalDiv) return;
modalDiv.style.display = 'none';
const backgroundDiv = document.getElementById('ship-modal-background');
if(!backgroundDiv) return;
backgroundDiv.style.display = 'none';
});
const allButtons = document.getElementsByClassName("item-card-build");
for(const shipButton of allButtons) {
shipButton.addEventListener("click", async () => {
const id = shipButton.id.split("_")[1];
const response = await fetch('/api/ships/addShip', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
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>

View File

@ -0,0 +1,18 @@
import { ObjectId } from "mongodb";
export default interface DBDefenses {
_id: ObjectId;
id: string;
requirements: {
buildings: Array<{ id: string, level: number }>,
research: Array<{ id: string, level: number }>,
resources: Array<{ id: string, amount: number }>,
};
energy: number;
time: number;
structure: {
hitpoints: number;
defense: number;
attack: number;
};
}

View File

@ -8,4 +8,5 @@ export default interface DBPlanet {
resources: Array<{ id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }>;
buildings: Array<{ id: string, level: number }>;
ships: Array<{ id: string, amount: number }>;
defenses: Array<{ id: string, amount: number }>;
}

View File

@ -17,4 +17,8 @@ export default interface DBSystem {
id: string,
amount: number
}>;
defenses: Array<{
id: string,
amount: number
}>;
}