Implement fleet manager and logic

This commit is contained in:
Aelita4 2024-11-01 18:35:44 +01:00
parent fd3dfcab3f
commit a12b82f470
Signed by: Aelita4
GPG Key ID: E44490C2025906C1
14 changed files with 825 additions and 44 deletions

View File

@ -15,7 +15,7 @@ const planet = locationManager.getPlanet(planetId);
if(!planet) return;
planet.resources.calculateCurrentAvailableResources();
await planet.resources.calculateCurrentAvailableResources();
const resourceArray: Resource[] = [];
for(const key of planet.resources.resources) {

View File

@ -1,7 +1,6 @@
import { ObjectId } from "mongodb";
import ResearchManager from "./managers/ResearchManager";
import { Planet } from "./managers/PlanetManager";
import FleetManager from "./managers/FleetManager";
export default class User {
id: ObjectId;
@ -11,7 +10,6 @@ export default class User {
updatedAt: Date;
lastLogin: Date;
research: ResearchManager;
fleet: FleetManager;
mainPlanet!: Planet;
constructor(id: ObjectId, username: string, email: string, createdAt: Date, updatedAt: Date, lastLogin: Date) {
@ -22,7 +20,6 @@ export default class User {
this.updatedAt = updatedAt;
this.lastLogin = lastLogin;
this.research = new ResearchManager(this);
this.fleet = new FleetManager(this);
}
async init() {

View File

@ -1,13 +1,99 @@
import FleetShip from "../FleetShip";
import User from "../User";
import { ObjectId } from "mongodb";
import MissionType from "../../../types/MissionType";
import DBFleet from "../../../types/db/DBFleet";
import { updateFleet } from "../../db/fleet";
import { Planet } from "./PlanetManager";
export type Fleet = {
id: ObjectId,
source: Planet,
destination: Planet,
departureTime: Date,
arrivalTime: Date,
returning: boolean,
mission: MissionType,
ships: Array<{ id: string, amount: number }>,
cargo: Array<{ id: string, amount: number }>
}
export default class FleetManager {
user: User;
fleet: Array<FleetShip> = [];
data: Fleet;
constructor(user: User) {
this.user = user;
constructor(id: ObjectId, source: Planet, destination: Planet, departureTime: Date, arrivalTime: Date, returning: boolean, mission: MissionType, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>) {
this.data = {
id,
source,
destination,
departureTime,
arrivalTime,
returning,
mission,
ships,
cargo
}
}
async checkStatus(): Promise<{ finished: boolean, fleet: Fleet }> {
if(this.data.arrivalTime.getTime() < Date.now()) {
const finished = await this.finish();
return { finished, fleet: this.data };
}
return { finished: false, fleet: this.data };
}
async finish() {
if(this.data.returning) {
for(const ship of this.data.ships) {
this.data.source.ships.addShips(ship.id, ship.amount);
}
await this.data.source.resources.updateAmount(this.data.cargo);
await this.data.source.ships.sync();
await this.data.source.resources.sync();
return true;
} else {
switch(this.data.mission) {
case 'ATTACK':
return false;
case 'TRANSPORT':
await this.data.destination.resources.updateAmount(this.data.cargo);
await this.data.destination.resources.sync();
this.data.cargo = [];
await this.initiateReturn();
return false;
case 'TRANSFER':
await this.data.destination.resources.updateAmount(this.data.cargo);
await this.data.destination.resources.sync();
this.data.cargo = [];
for(const ship of this.data.ships) {
this.data.destination.ships.addShips(ship.id, ship.amount);
}
await this.data.destination.ships.sync();
return true;
}
}
}
async initiateReturn() {
this.data.returning = true;
this.data.departureTime = new Date();
this.data.arrivalTime = new Date(this.data.departureTime.getTime() + 1000 * 30);
await this.sync();
}
async sync() {
const data: DBFleet = {
_id: this.data.id,
source: this.data.source._id,
destination: this.data.destination._id,
departureTime: this.data.departureTime,
arrivalTime: this.data.arrivalTime,
returning: this.data.returning,
mission: this.data.mission,
ships: this.data.ships,
cargo: this.data.cargo
}
await updateFleet(data);
}
}

View File

@ -6,6 +6,8 @@ import { getSystemById } from "../../db/systems";
import { getAllUsers } from "../../db/users";
import User from "../User";
import StructureManager from "./StructureManager";
import FleetManager from "./FleetManager";
import { getAllFleet } from "../../db/fleet";
export type Galaxy = {
_id: ObjectId,
@ -48,10 +50,14 @@ class LocationManager {
galaxies: Galaxy[] = [];
users: User[] = [];
fleet: FleetManager[] = [];
async init() {
const currentTime = new Date();
this.galaxies = [];
this.users = [];
this.fleet = [];
const users = await getAllUsers();
users.forEach(async user => {
@ -125,6 +131,28 @@ class LocationManager {
user.mainPlanet = mainPlanet;
};
const fleets = await getAllFleet();
for(const fleet of fleets) {
if(fleet.arrivalTime > currentTime) {
const source = this.getPlanet(fleet.source);
const destination = this.getPlanet(fleet.destination);
if(source && destination) this.fleet.push(new FleetManager(
fleet._id,
source,
destination,
fleet.departureTime,
fleet.arrivalTime,
fleet.returning,
fleet.mission,
fleet.ships,
fleet.cargo
));
}
}
console.log(`Loaded ${this.fleet.length} active fleet from database`);
return this;
}
@ -182,6 +210,24 @@ class LocationManager {
getUser(_id: ObjectId) {
return this.users.find(user => user.id.equals(_id));
}
async updateFleet() {
for(const fleet of this.fleet) {
const { finished } = await fleet.checkStatus();
if(finished) {
this.fleet = this.fleet.filter(f => !f.data.id.equals(fleet.data.id));
}
}
}
async getAllFleetByUserId(userId: string) {
await this.updateFleet();
return userId === '' ? [] : this.fleet.filter(fleet => fleet.data.source.manager.owner.id.equals(userId) || fleet.data.destination.manager.owner.id.equals(userId));
}
addFleet(fleet: FleetManager) {
this.fleet.push(fleet);
}
}
const locationManager = LocationManager.getInstance();

View File

@ -13,6 +13,7 @@ export type Resource = {
export default class ResourceManager {
resources: Array<Resource> = [];
resourcesDB: DBResource[] = [];
planet: Planet;
constructor(planet: Planet) {
@ -22,6 +23,8 @@ export default class ResourceManager {
async init(resourceData: { id: string, amount: number, lastUpdated: Date, perHourMiningRate: number }[]) {
const resources = await getAllResources();
this.resourcesDB = resources;
if(resourceData.length === 0) {
resourceData = [
{
@ -109,8 +112,7 @@ export default class ResourceManager {
res.amount += amountToAdd;
res.lastUpdated = new Date();
});
await updatePlanetResources(this.planet._id, this.resources);
return this.resources;
@ -135,8 +137,34 @@ export default class ResourceManager {
return difference;
}
update(resources: Resource[]) {
this.resources = resources;
add(resources: Resource[]) {
for(const res of resources) {
const resource = this.resources.find(r => r.id === res.id);
if(resource) resource.amount += res.amount;
else this.resources.push(res);
}
}
async updateAmount(resources: { id: string, amount: number }[]) {
await this.calculateCurrentAvailableResources();
for(const res of resources) {
const resource = this.resources.find(r => r.id === res.id);
if(resource) resource.amount += res.amount;
}
}
setAmount(resources: { id: string, amount: number }[]) {
for(const res of resources) {
const resource = this.resources.find(r => r.id === res.id);
if(resource) resource.amount = res.amount;
else this.resources.push({
id: res.id,
amount: res.amount,
lastUpdated: new Date(),
perHourMiningRate: 0,
data: this.resourcesDB.find(r => r.id === res.id)
});
}
}
async sync() {

View File

@ -1,6 +1,5 @@
import { ObjectId } from 'mongodb';
import DBFleet from '../../types/db/DBFleet';
import { Planet } from '../classes/managers/PlanetManager';
import { Fleet, Planets } from '../db/mongodb';
export const getAllFleet = async () => {
@ -26,15 +25,10 @@ export const getAllFleetByUser = async (userId: ObjectId) => {
return Array.from(fleets.values());
}
export const createFleet = async (source: Planet, destination: Planet, mission: string, ships: Array<{ id: string, amount: number }>) => {
const fleet = {
source: source._id,
destination: destination._id,
finished: false,
returning: false,
mission,
ships
}
await (await Fleet()).insertOne(fleet);
export const createFleet = async (fleet: DBFleet) => {
return await (await Fleet()).insertOne(fleet);
}
export const updateFleet = async (fleet: DBFleet) => {
return await (await Fleet()).updateOne({ _id: fleet._id }, { $set: fleet }, { upsert: true });
}

View File

@ -0,0 +1,14 @@
export default function parseParams(url: string) {
const rawParams = url.split("?")[1]?.split("&");
if(typeof rawParams === "undefined") return [];
const params: { [key: string]: any } = {};
for(const rawParam of rawParams) {
const k = rawParam.split("=")[0];
const v = rawParam.split("=")[1];
if(v === "true" || v === "false") params[k] = v === "true";
else if(!Number.isNaN(Number(v))) params[k] = Number(v);
else params[k] = v;
}
return params;
}

View File

@ -5,12 +5,9 @@ import { getUserById } from "../db/users";
import locationManager from "../classes/managers/LocationManager";
export default async function validateAccessToken(request: Request): Promise<Response | AccessToken> {
let accessToken = request.url.split("?")[1]?.split("&").filter((x) => x.split("=")[0] === "token")[0].split("=")[1];
if(accessToken === undefined) {
const authorization = request.headers.get("Authorization");
if(authorization !== null && authorization.startsWith("Bearer ")) accessToken = authorization.split(" ")[1];
}
let accessToken;
const authorization = request.headers.get("Authorization");
if(authorization !== null && authorization.startsWith("Bearer ")) accessToken = authorization.split(" ")[1];
const cookies = request.headers.get("Cookie")?.split(";").map((x) => x.trim().split("=")) ?? [];

268
src/pages/api/fleet/send.ts Normal file
View File

@ -0,0 +1,268 @@
import { 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 { getAllShips } from "../../../lib/db/ships";
import DBShip from "../../../types/db/DBShip";
import { Planet } from "../../../lib/classes/managers/PlanetManager";
import MissionType from "../../../types/MissionType";
import { getAllResources } from "../../../lib/db/resources";
import FleetManager from "../../../lib/classes/managers/FleetManager";
export const POST: APIRoute = async({ request }) => {
const response = await validateAccessToken(request);
if(response instanceof Response) return response;
const userDB = await getUserByAccessToken(response);
if(userDB === null) {
return new Response(
JSON.stringify({
code: 401,
message: "Unauthorized"
}), { status: 401 }
)
}
const user = locationManager.getUser(userDB._id);
if(!user) {
return new Response(
JSON.stringify({
code: 401,
message: "Unauthorized"
}), { status: 401 }
)
}
let body: { source: string, destination: string, ships: Array<{ id: string, amount: number }>, cargo: Array<{ id: string, amount: number }>, mission: MissionType };
try {
body = await request.json()
} catch(e) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Invalid JSON body"
}), { status: 400 }
)
}
const checkSource = checkPlanetId(body.source, 'source');
if(typeof checkSource.error !== "undefined") return new Response(JSON.stringify(checkSource), { status: checkSource.code });
const checkDestination = checkPlanetId(body.destination, 'destination');
if(typeof checkDestination.error !== "undefined") return new Response(JSON.stringify(checkDestination), { status: checkDestination.code });
const source = checkSource.planet;
const destination = checkDestination.planet;
const shipsDB = await getAllShips();
const checkShipsBody = checkShips(body.ships, shipsDB, source);
if(typeof checkShipsBody.error !== "undefined") return new Response(JSON.stringify(checkShipsBody), { status: checkShipsBody.code });
const resourcesDB = await getAllResources();
await source.resources.calculateCurrentAvailableResources();
const checkCargoBody = checkCargo(body.cargo, body.ships, source, body.mission);
if(typeof checkCargoBody.error !== "undefined") return new Response(JSON.stringify(checkCargoBody), { status: checkCargoBody.code });
const fleetManager = new FleetManager(
new ObjectId(),
source,
destination,
new Date(),
new Date(Date.now() + 1000 * 30), //TODO: calculate time based on distance
false,
body.mission,
body.ships,
body.cargo
);
const resourceDiff = await source.resources.getDifference(body.cargo.map(c => ({ id: c.id, amount: c.amount })));
for(const res of resourceDiff) {
if(res.amount < 0) {
return new Response(
JSON.stringify({
code: 400,
message: "Bad Request",
error: "Not enough resource with ID '" + res.id + "' on source planet"
}), { status: 400 }
)
}
}
source.resources.setAmount(resourceDiff);
await source.resources.sync();
for(const ship of body.ships) {
source.ships.removeShips(ship.id, ship.amount);
}
await source.ships.sync();
await fleetManager.sync();
locationManager.addFleet(fleetManager);
return new Response(
JSON.stringify({
code: 200,
message: "OK"
}), { status: 200 }
)
}
function checkPlanetId(id: string, type: string) {
if(typeof ObjectId === "undefined") return {
code: 400,
message: "Bad Request",
error: `Missing '${type}' in body`
}
let idToCheck;
try {
idToCheck = new ObjectId(id);
} catch(e) {
return {
code: 400,
message: "Bad Request",
error: `Invalid ID in '${type}'`
}
}
const planet = locationManager.getPlanet(idToCheck);
if(!planet) return {
code: 404,
message: "Not Found",
error: `Non-existent planet provided in '${type}'`
}
return {
code: 200,
message: "OK",
planet
}
}
function checkShips(ships: Array<{ id: string, amount: number }>, shipsDB: Array<DBShip>, sourcePlanet: Planet) {
if(typeof ships === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing 'ships' in body"
}
for(let i = 0; i < ships.length; i++) {
if(typeof ships[i].id === "undefined" || ships[i].id === "") return {
code: 400,
message: "Bad Request",
error: "Missing ship ID at position " + i
}
if(!shipsDB.find(ship => ship.id === ships[i].id)) return {
code: 404,
message: "Not Found",
error: "Non-existent ship ID '" + ships[i].id + "' at position " + i
}
if(typeof ships[i].amount === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing ship amount for ID '" + ships[i].id + "' at position " + i
}
if(ships[i].amount % 1 !== 0 || ships[i].amount < 0) return {
code: 400,
message: "Bad Request",
error: "Ship amount for ID '" + ships[i].id + "' is not a non-negative integer at position " + i
}
if(ships[i].amount > (sourcePlanet.ships.getShipById(ships[i].id)?.amount ?? 0)) return {
code: 400,
message: "Bad Request",
error: "Not enough ships on planet with ID '" + ships[i].id + "' at position " + i
}
}
return {
code: 200,
message: "OK"
}
}
function checkCargo(cargo: Array<{ id: string, amount: number }>, ships: Array<{ id: string, amount: number }>, planet: Planet, missionType: MissionType) {
if(missionType === "TRANSPORT" && cargo.length === 0) return {
code: 400,
message: "Bad Request",
error: "Missing 'cargo' in body for requested mission 'TRANSPORT'"
}
for(let i = 0; i < cargo.length; i++) {
if(typeof cargo[i].id === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing resource ID at position " + i
}
if(!planet.resources.resourcesDB.find(resource => resource.id === cargo[i].id)) return {
code: 404,
message: "Not Found",
error: "Non-existent resource ID '" + cargo[i].id + "' at position " + i
}
if(typeof cargo[i].amount === "undefined") return {
code: 400,
message: "Bad Request",
error: "Missing resource amount for ID '" + cargo[i].id + "' at position " + i
}
if(cargo[i].amount % 1 !== 0 || cargo[i].amount < 0) return {
code: 400,
message: "Bad Request",
error: "Resource amount for ID '" + cargo[i].id + "' is not a non-negative integer at position " + i
}
if(cargo[i].amount > (planet.resources.resources.find(res => res.id === cargo[i].id)?.amount ?? 0)) return {
code: 400,
message: "Bad Request",
error: "Not enough resources on planet with ID '" + cargo[i].id + "' at position " + i
}
}
const totalCargoAvailable = {
solid: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.solid ?? 0) * ship.amount, 0),
liquid: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.liquid ?? 0) * ship.amount, 0),
gas: ships.reduce((acc, ship) => acc + (planet.ships.shipsDB.find(s => s.id === ship.id)?.capacity.gas ?? 0) * ship.amount, 0)
}
const totalCargoUsed = {
solid: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "solid" ? acc + resource.amount : acc, 0),
liquid: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "liquid" ? acc + resource.amount : acc, 0),
gas: cargo.reduce((acc, resource) => planet.resources.resourcesDB.find(res => res.id === resource.id)?.type === "gas" ? acc + resource.amount : acc, 0)
}
if(totalCargoUsed.solid > totalCargoAvailable.solid) return {
code: 400,
message: "Bad Request",
error: "Not enough solid cargo capacity on ships"
}
if(totalCargoUsed.liquid > totalCargoAvailable.liquid) return {
code: 400,
message: "Bad Request",
error: "Not enough liquid cargo capacity on ships"
}
if(totalCargoUsed.gas > totalCargoAvailable.gas) return {
code: 400,
message: "Bad Request",
error: "Not enough gas cargo capacity on ships"
}
//TODO: check for fuel
return {
code: 200,
message: "OK"
}
}

View File

@ -0,0 +1,36 @@
import { APIRoute } from "astro";
import validateAccessToken from "../../../lib/utils/validateAccessToken";
import { getUserByAccessToken } from "../../../lib/db/users";
import { getAllFleetByUser } from "../../../lib/db/fleet";
import parseParams from "../../../lib/utils/parseParams";
export const GET: 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 allFleet = (await getAllFleetByUser(user._id)).filter(f => f.arrivalTime.getTime() > Date.now());
const params = {
}
const URLParams = parseParams(request.url);
return new Response(
JSON.stringify({
code: 200,
message: "OK",
data: allFleet
}), { status: 200 }
)
}

View File

@ -7,6 +7,7 @@ import ResourceBar from '../../components/ResourceBar.astro';
import locationManager from '../../lib/classes/managers/LocationManager';
import { getAllFleetByUser } from '../../lib/db/fleet';
import { getAllShips } from '../../lib/db/ships';
import { ObjectId } from 'mongodb';
const loggedToken = Astro.cookies.get('sessionToken')?.value ?? null;
const username = Astro.cookies.get('username')?.value ?? "";
@ -18,11 +19,67 @@ if(checkUser === null || checkUser.username !== username) return Astro.redirect(
const user = locationManager.getUser(checkUser._id);
if(!user) return Astro.redirect('/logout');
const planetId = Astro.cookies.get('planetid')?.value ?? "";
if(planetId === "") return "No planet selected";
const planet = locationManager.getPlanet(new ObjectId(planetId));
if(!planet) return "Planet not found";
const ships = await getAllShips();
const fleet = await getAllFleetByUser(user.id);
if(Astro.request.method === "POST") {
const form = await Astro.request.formData();
const cargo: Array<{ id: string, amount: number }> = [];
form.forEach((v, k) => {
if(k.match(/cargo\[\d\]\[id\]/)) {
const amount = parseInt(form.get(`${k.substring(0, k.indexOf("]"))}][amount]`)?.toString() ?? "0");
if(amount === 0 || isNaN(amount)) return;
cargo.push({
id: v.toString(),
amount
});
}
});
const fleetData = {
source: planet._id,
destination: new ObjectId(form.get('destination-planet')?.toString() ?? ""),
mission: form.get('mission')?.toString() ?? "NULL",
ships: ships.map(ship => {
const amount = parseInt(form.get(`ship-amount-${ship.id}`)?.toString() ?? "0");
if(amount === 0) return null;
return {
id: ship.id,
amount
}
}).filter(s => s !== null),
cargo
}
const response = await fetch(`${Astro.url.origin}/api/fleet/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${loggedToken}`
},
body: JSON.stringify(fleetData)
});
console.log(await response.json());
return Astro.redirect('/game/fleet');
}
await locationManager.updateFleet();
const fleet = (await getAllFleetByUser(user.id)).filter(f => new Date().getTime() - f.arrivalTime.getTime() < 0);
fleet.sort((a, b) => a.departureTime.getTime() - b.departureTime.getTime());
const userSystems = locationManager.getSystemsOwnedBy(user.id);
const galaxies = locationManager.galaxies;
let own = 0;
let friendly = 0;
@ -33,7 +90,7 @@ for(const system of userSystems) {
for(const f of fleet) {
if(f.source.equals(planet._id)) own++;
else if(f.destination.equals(planet._id)) {
if(f.mission === 'attack') enemy++;
if(f.mission === 'ATTACK') enemy++;
else {
const source = locationManager.getPlanet(f.source)?.manager.owner.id;
const destination = locationManager.getPlanet(f.destination)?.manager.owner.id;
@ -44,13 +101,37 @@ for(const system of userSystems) {
}
}
const sectorsList = galaxies.map(galaxy => {
return {
_id: galaxy._id,
name: galaxy.name,
sectors: galaxy.sectors.map(sector => {
return {
_id: sector._id,
name: sector.name,
systems: sector.systems.map(system => {
return {
_id: system._id,
name: system.name,
planets: system.planets.planets.map(planet => {
return {
_id: planet._id,
name: planet.name
}
})
}
})
}
})
}
});
const lang = await getLocales(Astro.cookies.get('language')?.value ?? await getHighestWeightedLanguage(Astro.request.headers.get('accept-language')));
---
<Layout title="Fleet">
<NavBar loggedIn="true" active="fleet" />
<ResourceBar />
<label for="fleet-toggle">
<input type="checkbox" id="fleet-toggle">
<div class="fleet-status">
@ -58,18 +139,74 @@ const lang = await getLocales(Astro.cookies.get('language')?.value ?? await getH
<ul>
{fleet.map(f => <li>
<div class="ship-cards">
{locationManager.getPlanet(f.source)?.name ?? "?"} =&gt; {locationManager.getPlanet(f.destination)?.name ?? "?"} | {f.mission}<br />
{locationManager.getPlanet(f.source)?.name ?? "?"} =&gt; {locationManager.getPlanet(f.destination)?.name ?? "?"} | {f.mission}{f.returning ? " (R)" : ""}<br />
Containing: {f.ships.map(s => {
const ship = ships.find(ship => ship.id === s.id);
return `${getName(lang, 'ships', s.id)} x${s.amount}`;
}).join(', ')} <br />
Cargo: {typeof f.cargo === "undefined" || f.cargo.length === 0 ? <>None</> : f.cargo.map(c => `${c.amount} ${getName(lang, 'resources', c.id)}`).join(', ')} <br />
Departured at: {f.departureTime.toISOString()} <br />
Arrives: {f.arrivalTime.toISOString()} ({Math.floor((f.arrivalTime.getTime() - new Date().getTime()) / 1000)})
</div>
</li>)}
</ul>
</div>
</label>
<div class="fleet-send-container">
<form method="post">
<h1>Sending fleet from {planet.name}</h1>
<hr />
<h2>Ships</h2>
<div class="fleet-send-ships">
{planet.ships.ships.map(ship => <div class="fleet-ship-card">
<h3>{getName(lang, 'ships', ship.data.id)} - {ship.amount}</h3>
<input type="number" value="0" min="0" max={ship.amount} id={ship.data.id} name={`ship-amount-${ship.data.id}`} />
</div>)}
</div>
<hr />
<h2>Mission</h2>
<label for="attack"><input type="radio" name="mission" value="ATTACK" id="attack" required />Attack</label>
<label for="transport"><input type="radio" name="mission" value="TRANSPORT" id="transport" />Transport</label>
<label for="transfer"><input type="radio" name="mission" value="TRANSFER" id="transfer" />Transfer</label>
<hr />
<h2>Send to:</h2>
<div class="fleet-destination">
<h3>Galaxy</h3>
<select id="destination-galaxy" name="destination-galaxy">
{galaxies.map(galaxy => <option value={galaxy._id.toString()}>{galaxy.name}</option>)}
</select>
<h3>Sector</h3>
<select id="destination-sector" name="destination-sector"></select>
<h3>System</h3>
<select id="destination-system" name="destination-system"></select>
<div id="destination-system-error"></div>
<h3>Planet</h3>
<select id="destination-planet" name="destination-planet"></select>
<div id="destination-planet-error"></div>
</div>
<hr />
<h2>Cargo</h2>
<div class="cargo-container">
<div class="cargo-item">
<select name="cargo[0][id]" class="select">
<option value="coal">Coal</option>
<option value="iron">Iron</option>
<option value="gold">Gold</option>
<option value="water">Water</option>
<option value="sulfuric-acid">Sulfuric Acid</option>
<option value="liquid-nitrogen">Liquid Nitrogen</option>
<option value="hydrogen">Hydrogen</option>
<option value="oxygen">Oxygen</option>
<option value="helium-3">Helium-3</option>
</select>
<input type="number" name="cargo[0][amount]" class="input" />
</div>
</div>
<div id="cargo-add-new">Add new +</div>
<hr />
<button type="submit">Send fleet</button>
</form>
</div>
</Layout>
<style>
@ -157,7 +294,6 @@ const lang = await getLocales(Astro.cookies.get('language')?.value ?? await getH
flex-wrap: wrap;
row-gap: 40px;
column-gap: 2%;
margin-top: 40px;
}
#ship-modal-background {
@ -222,4 +358,177 @@ const lang = await getLocales(Astro.cookies.get('language')?.value ?? await getH
height: auto;
max-height: 1000px;
}
</style>
.fleet-send-container {
margin-top: 40px;
background-color: gray;
border-radius: 10px;
padding: 2px;
}
.fleet-send-ships {
display: flex;
flex-direction: row;
flex-wrap: wrap;
row-gap: 40px;
column-gap: 2%;
margin-top: 40px;
}
.fleet-ship-card {
background-color: gray;
border-radius: 10px;
padding: 2px;
}
.fleet-ship-card h3 {
margin: 0;
}
.fleet-ship-card input {
width: 10rem;
color: black;
}
.fleet-destination select {
width: 10rem;
height: 3rem;
color: black;
background-color: darkgray;
}
.cargo-add-new {
background-color: #aaaaaa;
border-radius: 10px;
padding: 2px;
width: fit-content;
color: black;
}
.select, .input {
color: black;
}
</style>
<script define:vars={{ sectorsList }}>
const destinationGalaxy = document.getElementById('destination-galaxy');
const destinationSector = document.getElementById('destination-sector');
const destinationSystem = document.getElementById('destination-system');
const destinationPlanet = document.getElementById('destination-planet');
if(!destinationGalaxy || !destinationSector || !destinationSystem || !destinationPlanet) {
console.error('Could not find destination elements');
return;
};
destinationGalaxy.addEventListener('change', () => {
const galaxy = sectorsList.find(galaxy => galaxy._id === destinationGalaxy.value);
if(!galaxy) return;
destinationSector.innerHTML = '';
for(const sector of galaxy.sectors) {
const opt = document.createElement('option');
opt.value = sector._id;
opt.innerText = sector.name;
destinationSector.appendChild(opt);
}
if(destinationSector.children.length === 0) {
const opt = document.createElement('option');
opt.value = '';
opt.innerText = 'No sectors';
destinationSector.appendChild(opt);
}
destinationSector.dispatchEvent(new Event('change'));
});
destinationSector.addEventListener('change', () => {
const galaxy = sectorsList.find(galaxy => galaxy._id === destinationGalaxy.value);
if(!galaxy) return;
const sector = galaxy.sectors.find(sector => sector._id === destinationSector.value);
if(!sector) return;
destinationSystem.innerHTML = '';
document.getElementById('destination-system-error').innerHTML = '';
for(const system of sector.systems) {
const opt = document.createElement('option');
opt.value = system._id;
opt.innerText = system.name;
destinationSystem.appendChild(opt);
}
if(destinationSystem.children.length === 0) {
const opt = document.createElement('option');
opt.value = '';
opt.innerText = 'No systems';
destinationSystem.appendChild(opt);
document.getElementById('destination-system-error').innerHTML = "No systems";
}
destinationSystem.dispatchEvent(new Event('change'));
});
destinationSystem.addEventListener('change', () => {
const galaxy = sectorsList.find(galaxy => galaxy._id === destinationGalaxy.value);
if(!galaxy) return;
const sector = galaxy.sectors.find(sector => sector._id === destinationSector.value);
if(!sector) return;
const system = sector.systems.find(system => system._id === destinationSystem.value);
if(!system) {
destinationPlanet.innerHTML = '';
const opt = document.createElement('option');
opt.value = '';
opt.innerText = 'No planets';
destinationPlanet.appendChild(opt);
document.getElementById('destination-planet-error').innerHTML = "No planets";
return;
}
destinationPlanet.innerHTML = '';
document.getElementById('destination-planet-error').innerHTML = '';
for(const planet of system.planets) {
const opt = document.createElement('option');
opt.value = planet._id;
opt.innerText = planet.name;
destinationPlanet.appendChild(opt);
}
if(destinationPlanet.children.length === 0) {
const opt = document.createElement('option');
opt.value = '';
opt.innerText = 'No planets';
destinationPlanet.appendChild(opt);
}
});
destinationGalaxy.dispatchEvent(new Event('change'));
const addNew = document.getElementById('cargo-add-new');
addNew.addEventListener('click', () => {
const cargoContainer = document.querySelector('.cargo-container');
const cargoItem = document.createElement('div');
cargoItem.classList.add('cargo-item');
cargoItem.innerHTML += `
<select name="cargo[${cargoContainer.children.length}][id]" class="select">
<option value="coal">Coal</option>
<option value="iron">Iron</option>
<option value="gold">Gold</option>
<option value="water">Water</option>
<option value="sulfuric-acid">Sulfuric Acid</option>
<option value="liquid-nitrogen">Liquid Nitrogen</option>
<option value="hydrogen">Hydrogen</option>
<option value="oxygen">Oxygen</option>
<option value="helium-3">Helium-3</option>
</select>
<input type="number" name="cargo[${cargoContainer.children.length}][amount]" class="input" />`;
cargoContainer.appendChild(cargoItem);
});
</script>

View File

@ -44,7 +44,7 @@ const currentLanguage = Astro.cookies.get('language')?.value ?? "en";
<Layout title="Profile">
<NavBar loggedIn="true" active="profile" />
<ResourceBar loggedIn="true" />
{loggedToken}
<div class="wrapper">
<h3>{format(getName(lang, 'general', 'user-creation-date'), user?.createdAt.toISOString().slice(0, 19).replace(/-/g, "/").replace("T", " ").toString() ?? "")}</h3>
<a href="/logout" class="a-button">{getName(lang, 'general', 'nav-logout')}</a>

3
src/types/MissionType.ts Normal file
View File

@ -0,0 +1,3 @@
type MissionType = "TRANSPORT" | "ATTACK" | "TRANSFER";
export default MissionType;

View File

@ -1,11 +1,14 @@
import { ObjectId } from "mongodb";
import MissionType from "../MissionType";
export default interface DBFleet {
_id: ObjectId;
source: ObjectId;
destination: ObjectId;
finished: boolean;
departureTime: Date;
arrivalTime: Date;
returning: boolean;
mission: string;
mission: MissionType;
ships: Array<{ id: string, amount: number }>;
cargo: Array<{ id: string, amount: number }>;
}