Add battle support
This commit is contained in:
parent
f6a27eec59
commit
bf54589069
|
@ -8,6 +8,7 @@ import { sendMail } from "../../db/mails";
|
|||
import { Sector } from "./LocationManager";
|
||||
import { getRandomInRange, weightedRandom } from "../../utils/math";
|
||||
import { getAllShips } from "../../db/ships";
|
||||
import FleetShip from "../FleetShip";
|
||||
|
||||
export type Fleet = {
|
||||
id: ObjectId,
|
||||
|
@ -21,6 +22,13 @@ export type Fleet = {
|
|||
cargo: Array<{ id: string, amount: number }>
|
||||
}
|
||||
|
||||
export type BattleFleet = {
|
||||
id: string,
|
||||
hitpoints: number,
|
||||
attack: number,
|
||||
defense: number
|
||||
}
|
||||
|
||||
export default class FleetManager {
|
||||
data: Fleet;
|
||||
|
||||
|
@ -94,7 +102,7 @@ export default class FleetManager {
|
|||
} else {
|
||||
switch(this.data.mission) {
|
||||
case 'ATTACK':
|
||||
return false;
|
||||
return await this.battleResults([{ id: 'fighter', amount: 100 }]);
|
||||
case 'TRANSPORT':
|
||||
await (this.data.destination as Planet | SystemManager).resources.updateAmount(this.data.cargo);
|
||||
await (this.data.destination as Planet | SystemManager).resources.sync();
|
||||
|
@ -146,16 +154,134 @@ export default class FleetManager {
|
|||
await this.sync();
|
||||
}
|
||||
|
||||
private async sendMail(description: string) {
|
||||
private async sendMail(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,
|
||||
this.data.arrivalTime,
|
||||
"Expedition Results",
|
||||
title,
|
||||
description
|
||||
);
|
||||
}
|
||||
|
||||
async battleResults(enemyFleet: { id: string, amount: number }[]) {
|
||||
const allShips = await getAllShips();
|
||||
|
||||
const playerStats = this.data.ships.reduce((acc, ship) => {
|
||||
const dbShip = allShips.find(s => s.id === ship.id);
|
||||
if(!dbShip) return acc;
|
||||
acc.attack += dbShip.structure.attack * ship.amount;
|
||||
acc.defense += dbShip.structure.defense * ship.amount;
|
||||
acc.hitpoints += dbShip.structure.hitpoints * ship.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;
|
||||
acc.attack += dbShip.structure.attack * ship.amount;
|
||||
acc.defense += dbShip.structure.defense * ship.amount;
|
||||
acc.hitpoints += dbShip.structure.hitpoints * ship.amount;
|
||||
return acc;
|
||||
}, { attack: 0, defense: 0, hitpoints: 0 });
|
||||
|
||||
const playerShipsStructure: BattleFleet[] = [];
|
||||
for(const playerShip of this.data.ships) {
|
||||
for(let i = 0; i < playerShip.amount; i++) playerShipsStructure.push({
|
||||
id: playerShip.id,
|
||||
hitpoints: allShips.find(s => s.id === playerShip.id)?.structure.hitpoints ?? 0,
|
||||
attack: allShips.find(s => s.id === playerShip.id)?.structure.attack ?? 0,
|
||||
defense: allShips.find(s => s.id === playerShip.id)?.structure.defense ?? 0
|
||||
});
|
||||
}
|
||||
|
||||
const enemyShipsStructure: BattleFleet[] = [];
|
||||
for(const enemyShip of enemyFleet) {
|
||||
for(let i = 0; i < enemyShip.amount; i++) enemyShipsStructure.push({
|
||||
id: enemyShip.id,
|
||||
hitpoints: allShips.find(s => s.id === enemyShip.id)?.structure.hitpoints ?? 0,
|
||||
attack: allShips.find(s => s.id === enemyShip.id)?.structure.attack ?? 0,
|
||||
defense: allShips.find(s => s.id === enemyShip.id)?.structure.defense ?? 0
|
||||
});
|
||||
}
|
||||
|
||||
roundLoop: for(let i = 0; i < 3; i++) {
|
||||
for(const playerShip of playerShipsStructure) {
|
||||
const enemyShip = enemyShipsStructure[Math.floor(Math.random() * enemyShipsStructure.length)];
|
||||
|
||||
if(!enemyShip) break roundLoop;
|
||||
const typeCount = playerShipsStructure.filter(s => s.id === playerShip.id).length;
|
||||
const additionalShipAttack = typeCount > 1 ? Math.floor(Math.random() * typeCount) * playerShip.attack : 0;
|
||||
const playerDamage = Math.max(0, (playerShip.attack + additionalShipAttack) - enemyShip.defense);
|
||||
enemyShip.hitpoints -= playerDamage;
|
||||
}
|
||||
|
||||
for(const enemyShip of enemyShipsStructure) {
|
||||
const playerShip = playerShipsStructure[Math.floor(Math.random() * playerShipsStructure.length)];
|
||||
|
||||
if(!playerShip) break roundLoop;
|
||||
const typeCount = enemyShipsStructure.filter(s => s.id === enemyShip.id).length;
|
||||
const additionalShipAttack = typeCount > 1 ? Math.floor(Math.random() * typeCount) * enemyShip.attack : 0;
|
||||
const enemyDamage = Math.max(0, (enemyShip.attack + additionalShipAttack) - playerShip.defense);
|
||||
playerShip.hitpoints -= enemyDamage;
|
||||
}
|
||||
|
||||
playerShipsStructure.forEach((ship, index) => {
|
||||
if(ship.hitpoints <= 0) playerShipsStructure.splice(index, 1);
|
||||
});
|
||||
|
||||
enemyShipsStructure.forEach((ship, index) => {
|
||||
if(ship.hitpoints <= 0) enemyShipsStructure.splice(index, 1);
|
||||
});
|
||||
}
|
||||
|
||||
const playerBalance = playerStats.defense - enemyStats.attack;
|
||||
const enemyBalance = enemyStats.defense - playerStats.attack;
|
||||
|
||||
const playerShipsLeft = playerShipsStructure.reduce((acc, ship) => { acc[ship.id] = (acc[ship.id] ?? 0) + 1; return acc }, {} as { [key: string]: number });
|
||||
const enemyShipsLeft = enemyShipsStructure.reduce((acc, ship) => { acc[ship.id] = (acc[ship.id] ?? 0) + 1; return acc }, {} as { [key: string]: number });
|
||||
|
||||
const resourcesStolen: { id: string, amount: number }[] = [];
|
||||
|
||||
const previousShips = JSON.parse(JSON.stringify(this.data.ships)) as Array<{ id: string, amount: number }>;
|
||||
if(playerShipsStructure.length > 0) {
|
||||
this.data.ships = Object.keys(playerShipsLeft).map(id => { return { id, amount: playerShipsLeft[id] } });
|
||||
if(playerBalance > enemyBalance) {
|
||||
const enemyResources = await (this.data.destination as Planet | SystemManager).resources;
|
||||
await enemyResources.calculateCurrentAvailableResources();
|
||||
let cargoSpaceFree = this.data.ships.reduce((acc, ship) => {
|
||||
const dbShip = allShips.find(s => s.id === ship.id);
|
||||
if(!dbShip) return acc;
|
||||
return acc + dbShip.capacity.solid * ship.amount;
|
||||
}, 0);
|
||||
|
||||
for(const res of enemyResources.resources) {
|
||||
if(cargoSpaceFree <= 0) break;
|
||||
const amount = Math.min(Math.floor(Math.random() * res.amount), cargoSpaceFree);
|
||||
cargoSpaceFree -= amount;
|
||||
this.data.cargo.push({ id: res.id, amount });
|
||||
resourcesStolen.push({ id: res.id, amount });
|
||||
enemyResources.setAmount([{ id: res.id, amount: res.amount - amount }]);
|
||||
};
|
||||
}
|
||||
|
||||
await this.initiateReturn();
|
||||
} else this.data.ships = [];
|
||||
|
||||
await this.sendMail(
|
||||
`Battle Results (${playerBalance > enemyBalance ? "Victory" : playerBalance < enemyBalance ? "Defeat" : "Draw"})`,
|
||||
`Player ships:\n${previousShips.map(ship => `${ship.amount} ${ship.id}`).join(', ')}\n
|
||||
Enemy ships:\n${enemyFleet.map(ship => `${ship.amount} ${ship.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]}\n`)}\n
|
||||
Enemy ships left:\n${Object.keys(enemyShipsLeft).map(key => `${key} - ${enemyShipsLeft[key]}\n`)}\n
|
||||
${playerBalance > enemyBalance ? `Resources stolen:\n${resourcesStolen.map(res => `${res.id} - ${res.amount}\n`)}` : ""}`
|
||||
);
|
||||
|
||||
return !(playerShipsStructure.length > 0);
|
||||
}
|
||||
|
||||
async expeditionResults() {
|
||||
const expeditionRandom = Math.random(); //TODO: make use of "expedition" from DBSector
|
||||
|
||||
|
@ -169,7 +295,7 @@ export default class FleetManager {
|
|||
|
||||
if(expeditionRandom < 0.02) { // 2% chance; lost all ships, black hole
|
||||
this.data.ships = [];
|
||||
await this.sendMail(`Your expedition to ${(this.data.destination as Sector).name} sector encountered a black hole. All ships were lost.`);
|
||||
await this.sendMail("Expedition Results", `Your expedition to ${(this.data.destination as Sector).name} sector encountered a black hole. All ships were lost.`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -184,7 +310,7 @@ export default class FleetManager {
|
|||
else ship.amount += s.amount;
|
||||
}
|
||||
|
||||
await this.sendMail(`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("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;
|
||||
}
|
||||
|
@ -216,16 +342,19 @@ export default class FleetManager {
|
|||
else resource.amount += res.amount;
|
||||
}
|
||||
|
||||
await this.sendMail(`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("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;
|
||||
}
|
||||
|
||||
if(expeditionRandom < 0.35) { // 15% chance; pirates/aliens
|
||||
//TODO: implement fight mechanic
|
||||
const pirates: { id: string, amount: number }[] = [
|
||||
{ id: 'fighter', amount: getRandomInRange(0, 100) + valueAdded },
|
||||
{ id: 'transporter', amount: getRandomInRange(0, 100) + valueAdded }
|
||||
];
|
||||
|
||||
await this.sendMail(`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.initiateReturn();
|
||||
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.battleResults(pirates);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -273,12 +402,12 @@ export default class FleetManager {
|
|||
else resource.amount += res.amount;
|
||||
}
|
||||
|
||||
await this.sendMail(`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("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(`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("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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue