Splitted weapons and units managers

This commit is contained in:
Pax1601
2023-07-27 16:27:59 +02:00
parent 875f3ebe68
commit 0150ae9df1
38 changed files with 5616 additions and 4883 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,621 @@
import { getMissionHandler } from "..";
import { GAME_MASTER } from "../constants/constants";
import { UnitDatabase } from "./unitdatabase"
export class HelicopterDatabase extends UnitDatabase {
constructor() {
super();
this.blueprints = {
"AH-64D_BLK_II": {
"name": "AH-64D_BLK_II",
"coalition": "blue",
"era": "Modern",
"label": "AH-64D Apache",
"shortLabel": "AH64",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "AGM-114k Hellfire",
"quantity": 8
},
{
"name": "M151 Rocket Pod",
"quantity": 2
}
],
"roles": [
"CAS"
],
"code": "2 * M261: M151 (6PD), 2 * Hellfire station: 4*AGM-114K",
"name": "Gun / ATGM / Rocket"
},
{
"fuel": 1,
"items": [
{
"name": "AGM-114K Hellfire",
"quantity": 16
}
],
"roles": [
"CAS"
],
"code": "4 * Hellfire station: 4*AGM-114K",
"name": "Gun / ATGM"
},
{
"fuel": 1,
"items": [
],
"roles": [
""
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "ah-64.png"
},
"Ka-50_3": {
"name": "Ka-50_3",
"coalition": "red",
"era": "Late Cold War",
"label": "Ka-50 Hokum A",
"shortLabel": "K50",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "Igla",
"quantity": 4
}
],
"roles": [
"CAP"
],
"code": "4xIgla",
"name": "Gun / Fox 2"
},
{
"fuel": 1,
"items": [
{
"name": "Igla",
"quantity": 4
},
{
"name": "S-13",
"quantity": 10
},
{
"name": "Kh-25ML",
"quantity": 2
}
],
"roles": [
"Anti-Ship"
],
"code": "2xKh-25ML, 10xS-13, 4xIgla",
"name": "Gun / ASM / Rockets / Fox 2"
},
{
"fuel": 1,
"items": [
{
"name": "Igla",
"quantity": 4
},
{
"name": "S-80FP",
"quantity": 40
},
{
"name": "Vikhr-M",
"quantity": 12
}
],
"roles": [
"CAS"
],
"code": "12x9A4172, 40xS-8OFP, 4xIgla",
"name": "Gun / ATGM / Rockets / Fox 2"
},
{
"fuel": 1,
"items": [
{
"name": "Igla",
"quantity": 4
},
{
"name": "S-80FP",
"quantity": 40
},
{
"name": "Vikhr-M",
"quantity": 12
}
],
"roles": [
"CAS"
],
"code": "12x9A4172, 40xS-8OFP, 4xIgla",
"name": "Gun / ATGM"
},
{
"fuel": 1,
"items": [
{
"name": "Igla",
"quantity": 4
},
{
"name": "FAB-500",
"quantity": 2
},
{
"name": "S-13",
"quantity": 10
}
],
"roles": [
"Strike"
],
"code": "10xS-13, 2xFAB-500, 4xIgla",
"name": "Gun / Bombs / Rockets / Fox 2"
},
{
"fuel": 1,
"items": [
],
"roles": [
""
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "ka-50.png"
},
"Mi-24P": {
"name": "Mi-24P",
"coalition": "red",
"era": "Mid Cold War",
"label": "Mi-24P Hind",
"shortLabel": "Mi24",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "S-8KOM",
"quantity": 40
},
{
"name": "9M114 ATGM",
"quantity": 8
}
],
"roles": [
"CAS"
],
"code": "2xB8V20 (S-8KOM)+8xATGM 9M114",
"name": "Gun / ATGM / Rockets"
},
{
"fuel": 1,
"items": [
{
"name": "S-24B",
"quantity": 4
},
{
"name": "9M114 ATGM",
"quantity": 4
}
],
"roles": [
"Strike"
],
"code": "4xS-24B+4xATGM 9M114",
"name": "Gun / ATGM / Rockets"
},
{
"fuel": 1,
"items": [
{
"name": "GUV-1 Grenade Launcher",
"quantity": 4
},
{
"name": "9M114 ATGM",
"quantity": 4
}
],
"roles": [
"CAS"
],
"code": "4xGUV-1 AP30+4xATGM 9M114",
"name": "Gun / ATGM / Grenade Launcher"
},
{
"fuel": 1,
"items": [
],
"roles": [
""
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "mi-24.png"
},
"SA342L": {
"name": "SA342L",
"coalition": "blue",
"era": "Mid Cold War",
"label": "SA342L Gazelle",
"shortLabel": "342",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "20mm Cannon",
"quantity": 1
},
{
"name": "SNEB68",
"quantity": 8
}
],
"roles": [
"Recon"
],
"code": "M621, 8xSNEB68 EAP",
"name": "Gun / ATGM / Rockets"
}
],
"filename": "sa-342.png"
},
"SA342M": {
"name": "SA342M",
"coalition": "blue",
"era": "Mid Cold War",
"label": "SA342M Gazelle",
"shortLabel": "342",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "HOT3",
"quantity": 4
}
],
"roles": [
"CAS"
],
"code": "HOT3x4",
"name": "ATGM"
}
],
"filename": "sa-342.png"
},
"SA342Mistral": {
"name": "SA342Mistral",
"coalition": "blue",
"era": "Mid Cold War",
"label": "SA342Mistral Gazelle",
"shortLabel": "342",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "Mistral",
"quantity": 4
}
],
"roles": [
"CAP"
],
"code": "Mistral x 4",
"name": "Fox 2"
},
{
"fuel": 1,
"items": [
],
"roles": [
""
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "sa-342.png"
},
"AH-1W": {
"name": "AH-1W",
"coalition": "blue",
"era": "Mid Cold War",
"label": "AH-1W Cobra",
"shortLabel": "AH1",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "BGM-71 TOW",
"quantity": 8
},
{
"name": "Hydra-70 WP",
"quantity": 38
}
],
"roles": [
"CAS"
],
"code": "8xBGM-71, 38xHYDRA-70 WP",
"name": "TOW / Hydra"
},
{
"fuel": 1,
"items": [
{
"name": "Hydra-70",
"quantity": 76
}
],
"roles": [
"CAS"
],
"code": "76xHYDRA-70",
"name": "Hydra"
},
{
"fuel": 1,
"items": [
],
"roles": [
""
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "ah-1.png"
},
"Mi-26": {
"name": "Mi-26",
"coalition": "red",
"era": "Late Cold War",
"label": "Mi-26 Halo",
"shortLabel": "M26",
"loadouts": [
{
"fuel": 1,
"items": [
],
"roles": [
"Transport"
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "mi-26.png"
},
"Mi-28N": {
"name": "Mi-28N",
"coalition": "red",
"era": "Modern",
"label": "Mi-28N Havoc",
"shortLabel": "M28",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "9M114 Shturm",
"quantity": 16
},
{
"name": "S-8",
"quantity": 40
}
],
"roles": [
"CAS"
],
"code": "16x9M114, 40xS-8",
"name": "ATGM / S-8"
},
{
"fuel": 1,
"items": [
],
"roles": [
""
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "mi-28.png"
},
"Mi-8MT": {
"name": "Mi-8MT",
"coalition": "red",
"era": "Mid Cold War",
"label": "Mi-8MT Hip",
"shortLabel": "Mi8",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "UPK",
"quantity": 2
},
{
"name": "B8",
"quantity": 2
}
],
"roles": [
"CAS"
],
"code": "2 x UPK +2 x B8",
"name": "Rockets / Gunpods"
},
{
"fuel": 1,
"items": [
],
"roles": [
"Transport"
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "mi-8.png"
},
"SH-60B": {
"name": "SH-60B",
"coalition": "blue",
"era": "Mid Cold War",
"label": "SH-60B Seahawk",
"shortLabel": "S60",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "AGM-119 ASM",
"quantity": 1
}
],
"roles": [
"CAS"
],
"code": "AGM-119",
"name": "ASM"
},
{
"fuel": 1,
"items": [
],
"roles": [
"Transport"
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "uh-60.png"
},
"UH-60A": {
"name": "UH-60A",
"coalition": "blue",
"era": "Mid Cold War",
"label": "UH-60A Blackhawk",
"shortLabel": "U60",
"loadouts": [
{
"fuel": 1,
"items": [
],
"roles": [
"Transport"
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "uh-60.png"
},
"UH-1H": {
"name": "UH-1H",
"coalition": "blue",
"era": "Early Cold War",
"label": "UH-1H Huey",
"shortLabel": "UH1",
"loadouts": [
{
"fuel": 1,
"items": [
{
"name": "M134 Minigun",
"quantity": 2
},
{
"name": "XM-158",
"quantity": 2
}
],
"roles": [
"CAS"
],
"code": "M134 Minigun*2, XM158*2",
"name": "Miniguns / XM158"
},
{
"fuel": 1,
"items": [
],
"roles": [
"Transport"
],
"code": "",
"name": "Empty Loadout"
}
],
"filename": "uh-1.png"
}
}
}
getSpawnPointsByName(name: string) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);
if (blueprint?.era == "WW2")
return 20;
else if (blueprint?.era == "Early Cold War")
return 50;
else if (blueprint?.era == "Mid Cold War")
return 100;
else if (blueprint?.era == "Late Cold War")
return 200;
else if (blueprint?.era == "Modern")
return 400;
return 0;
}
getCategory() {
return "Helicopter";
}
}
export var helicopterDatabase = new HelicopterDatabase();

View File

@@ -0,0 +1,486 @@
import { getMissionHandler } from "..";
import { GAME_MASTER } from "../constants/constants";
import { UnitDatabase } from "./unitdatabase"
export class NavyUnitDatabase extends UnitDatabase {
constructor() {
super();
this.blueprints = {
"Type_052B": {
"name": "Type_052B",
"coalition": "red",
"type": "Destroyer",
"era": "Modern",
"label": "052B DDG-168 Guangzhou",
"shortLabel": "Type 52B",
"range": "Short",
"filename": ""
},
"Type_052C": {
"name": "Type_052C",
"coalition": "red",
"type": "Destroyer",
"era": "Modern",
"label": "052C DDG-171 Haikou",
"shortLabel": "Type 52C",
"range": "Short",
"filename": ""
},
"Type_054A": {
"name": "",
"coalition": "red",
"type": "Frigate",
"era": "Modern",
"label": "054A FFG-538 Yantai",
"shortLabel": "Type 54A",
"range": "Medium",
"filename": ""
},
"Type_071": {
"name": "Type_071",
"coalition": "red",
"type": "Transport",
"era": "Modern",
"label": "Type 071",
"shortLabel": "Type 071",
"range": "",
"filename": ""
},
"Type_093": {
"name": "Type_093",
"coalition": "red",
"type": "Submarine",
"era": "Modern",
"label": "Type 093",
"shortLabel": "Type 093",
"range": "",
"filename": ""
},
"santafe": {
"name": "santafe",
"coalition": "",
"type": "Submarine",
"era": "Early Cold War",
"label": "ARA Santa Fe S-21",
"shortLabel": "ARA Santa",
"range": "",
"filename": ""
},
"ara_vdm": {
"name": "ara_vdm",
"coalition": "",
"type": "Aircraft Carrier",
"era": "Mid Cold War",
"label": "ARA Vienticinco de Mayo",
"shortLabel": "ARA Vienticinco de Mayo",
"range": "",
"filename": ""
},
"kuznecow": {
"name": "kuznecow",
"coalition": "red",
"type": "Aircraft Carrier",
"era": "Late Cold War",
"label": "Admiral Kuznetsov",
"shortLabel": "Admiral Kuznetsov",
"range": "Medium",
"filename": ""
},
"albatros": {
"name": "albatros",
"coalition": "red",
"type": "Aircraft Carrier",
"era": "Early Cold War",
"label": "Albatros (Grisha-5)",
"shortLabel": "Albatros",
"range": "",
"filename": ""
},
"leander-gun-condell": {
"name": "leander-gun-condell",
"coalition": "",
"type": "Frigate",
"era": "Mid Cold War",
"label": "Almirante Condell PFG-06",
"shortLabel": "Almirante Condell",
"range": "",
"filename": ""
},
"Boat Armed Hi-Speed": {
"name": "Boat Armed Hi-Speed",
"coalition": "",
"type": "Fast Attack Craft",
"era": "Mid Cold War",
"label": "Boat Armed Hi-Speed",
"shortLabel": "Boat Armed Hi-Speed",
"range": "",
"filename": ""
},
"HandyWind": {
"name": "HandyWind",
"coalition": "blue",
"type": "Cargoship",
"era": "Late Cold War",
"label": "Bulker Handy Wind",
"shortLabel": "Bulker Handy Wind",
"range": "",
"filename": ""
},
"CV_1143_5": {
"name": "CV_1143_5",
"coalition": "red",
"type": "Aircraft Carrier",
"era": "Modern",
"label": "CV Admiral Kuznetsov(2017)",
"shortLabel": "Admiral Kuznetsov(2017)",
"range": "Medium",
"filename": ""
},
"CV_59": {
"name": "CV_59",
"coalition": "blue",
"type": "Aircraft Carrier",
"era": "Early Cold War",
"label": "CV-59 Forrestal",
"shortLabel": "CV-59",
"range": "Short",
"filename": ""
},
"CVN_71": {
"name": "CVN_71",
"coalition": "blue",
"type": "Super Aircraft Carrier",
"era": "Late Cold War",
"label": "CVN-71 Theodore Roosevelt",
"shortLabel": "CVN-71",
"range": "Short",
"filename": ""
},
"CVN_72": {
"name": "CVN_72",
"coalition": "blue",
"type": "Super Aircraft Carrier",
"era": "Late Cold War",
"label": "CVN-72 Abraham Lincoln",
"shortLabel": "CVN-72",
"range": "Short",
"filename": ""
},
"CVN_73": {
"name": "CVN_73",
"coalition": "blue",
"type": "Super Aircraft Carrier",
"era": "Late Cold War",
"label": "CVN-73 George Washington",
"shortLabel": "CVN-73",
"range": "Medium",
"filename": ""
},
"Stennis": {
"name": "Stennis",
"coalition": "blue",
"type": "Aircraft Carrier",
"era": "Late Cold War",
"label": "CVN-74 John C. Stennis",
"shortLabel": "CVN-74",
"range": "Medium",
"filename": ""
},
"CVN_75": {
"name": "CVN_75",
"coalition": "blue",
"type": "Aircraft Carrier",
"era": "Late Cold War",
"label": "CVN-75 Harry S. Truman",
"shortLabel": "CVN-75",
"range": "Medium",
"filename": ""
},
"CastleClass_01": {
"name": "CastleClass_01",
"coalition": "blue",
"type": "Patrol",
"era": "Mid Cold War",
"label": "HMS Leeds Castle (P-258)",
"shortLabel": "HMS Leeds Castle (P-258)",
"range": "",
"filename": ""
},
"USS_Arleigh_Burke_IIa": {
"name": "USS_Arleigh_Burke_IIa",
"coalition": "blue",
"type": "Destroyer",
"era": "Late Cold War",
"label": "DDG Arleigh Burke lla",
"shortLabel": "DDG Arleigh Burke",
"range": "Medium",
"filename": ""
},
"barge-1": {
"name": "barge-1",
"coalition": "red",
"type": "Cargoship",
"era": "Late Cold War",
"label": "Dry cargo ship Ivanov",
"shortLabel": "Dry cargo ship Ivanov",
"range": "",
"filename": ""
},
"barge-2": {
"name": "barge-2",
"coalition": "red",
"type": "Cargoship",
"era": "Late Cold War",
"label": "Dry cargo ship Yakushev",
"shortLabel": "Dry cargo ship Yakushev",
"range": "",
"filename": ""
},
"elnya": {
"name": "elnya",
"coalition": "red",
"type": "Tanker",
"era": "Late Cold War",
"label": "Elnya tanker",
"shortLabel": "Elnya tanker",
"range": "",
"filename": ""
},
"La_Combattante_II": {
"name": "La_Combattante_II",
"coalition": "blue",
"type": "Fast Attack Craft",
"era": "Mid Cold War",
"label": "FAC La Combattante lla",
"shortLabel": "FAC La Combattante",
"range": "",
"filename": ""
},
"leander-gun-achilles": {
"name": "leander-gun-achilles",
"coalition": "blue",
"type": "Frigate",
"era": "Mid Cold War",
"label": "HMS Achilles (F12)",
"shortLabel": "HMS Achilles",
"range": "",
"filename": ""
},
"leander-gun-andromeda": {
"name": "leander-gun-andromeda",
"coalition": "blue",
"type": "Frigate",
"era": "Mid Cold War",
"label": "HMS Andromeda (F57)",
"shortLabel": "HMS Andromeda",
"range": "",
"filename": ""
},
"leander-gun-ariadne": {
"name": "leander-gun-ariadne",
"coalition": "blue",
"type": "Frigate",
"era": "Mid Cold War",
"label": "HMS Ariadne (F72)",
"shortLabel": "HMS Ariadne",
"range": "",
"filename": ""
},
"leander-gun-lynch": {
"name": "leander-gun-lynch",
"coalition": "",
"type": "Frigate",
"era": "Mid Cold War",
"label": "CNS Almirante Lynch (PFG-07)",
"shortLabel": "CNS Almirante Lynch",
"range": "",
"filename": ""
},
"hms_invincible": {
"name": "hms_invincible",
"coalition": "blue",
"type": "Aircraft Carrier",
"era": "Mid Cold War",
"label": "HMS Invincible (R05)",
"shortLabel": "HMS Invincible",
"range": "",
"filename": ""
},
"HarborTug": {
"name": "HarborTug",
"coalition": "",
"type": "Tug",
"era": "Mid Cold War",
"label": "Harbor Tug",
"shortLabel": "Harbor Tug",
"range": "",
"filename": ""
},
"kilo_636": {
"name": "kilo_636",
"coalition": "red",
"type": "Submarine",
"era": "Late Cold War",
"label": "Project 636 Varshavyanka Improved",
"shortLabel": "Varshavyanka Improved",
"range": "Medium",
"filename": ""
},
"kilo": {
"name": "kilo",
"coalition": "red",
"type": "Submarine",
"era": "Late Cold War",
"label": "Project 636 Varshavyanka Basic",
"shortLabel": "Varshavyanka Basic",
"range": "Medium",
"filename": ""
},
"LHA_Tarawa": {
"name": "LHA_Tarawa",
"coalition": "blue",
"type": "Aircraft Carrier",
"era": "Mid Cold War",
"label": "LHA-1 Tarawa",
"shortLabel": "LHA-1 Tarawa",
"range": "Short",
"filename": ""
},
"BDK-775": {
"name": "BDK-775",
"coalition": "blue",
"type": "Landing Craft",
"era": "Mid Cold War",
"label": "LS Ropucha",
"shortLabel": "LS Ropucha",
"range": "",
"filename": ""
},
"molniya": {
"name": "molniya",
"coalition": "",
"type": "Fast Attack Craft",
"era": "Late Cold War",
"label": "Molniya (Tarantul-3)",
"shortLabel": "Molniya",
"range": "Short",
"filename": ""
},
"moscow": {
"name": "moscow",
"coalition": "red",
"type": "Cruiser",
"era": "Late Cold War",
"label": "Moscow",
"shortLabel": "Moscow",
"range": "Medium",
"filename": ""
},
"neustrash": {
"name": "neustrash",
"coalition": "red",
"type": "Frigate",
"era": "Late Cold War",
"label": "Neustrashimy",
"shortLabel": "Neustrashimy",
"range": "Short",
"filename": ""
},
"perry": {
"name": "perry",
"coalition": "blue",
"type": "Frigate",
"era": "Mid Cold War",
"label": "Oliver H. Perry",
"shortLabel": "Oliver H. Perry",
"range": "Medium",
"filename": ""
},
"piotr_velikiy": {
"name": "piotr_velikiy",
"coalition": "red",
"type": "Cruiser",
"era": "Late Cold War",
"label": "Pyotr Velikiy",
"shortLabel": "Pyotr Velikiy",
"range": "Medium",
"filename": ""
},
"rezky": {
"name": "Rezky (Krivak-2)",
"coalition": "red",
"type": "Frigate",
"era": "Early Cold War",
"label": "Rezky (Krivak-2)",
"shortLabel": "Rezky",
"range": "Short",
"filename": ""
},
"Ship_Tilde_Supply": {
"name": "Ship_Tilde_Supply",
"coalition": "blue",
"type": "Transport",
"era": "Late Cold War",
"label": "Supply Ship MV Tilde",
"shortLabel": "Supply Ship Tilde",
"range": "",
"filename": ""
},
"Seawise_Giant": {
"name": "Seawise_Giant",
"coalition": "blue",
"type": "Tanker",
"era": "Late Cold War",
"label": "Tanker Seawise Giant",
"shortLabel": "Seawise Giant",
"range": "",
"filename": ""
},
"TICONDEROG": {
"name": "TICONDEROG",
"coalition": "blue",
"type": "Cruiser",
"era": "Late Cold War",
"label": "Ticonderoga",
"shortLabel": "Ticonderoga",
"range": "Medium",
"filename": ""
},
"zwezdny": {
"name": "zwezdny",
"coalition": "",
"type": "Civilian Boat",
"era": "Modern",
"label": "Zwezdny",
"shortLabel": "Zwezdny",
"range": "",
"filename": ""
}
}
}
getSpawnPointsByName(name: string) {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
return 0;
const blueprint = this.getByName(name);
if (blueprint?.era == "WW2")
return 20;
else if (blueprint?.era == "Early Cold War")
return 50;
else if (blueprint?.era == "Mid Cold War")
return 100;
else if (blueprint?.era == "Late Cold War")
return 200;
else if (blueprint?.era == "Modern")
return 400;
return 0;
}
getCategory() {
return "NavyUnit";
}
}
export var navyUnitDatabase = new NavyUnitDatabase();

1149
client/src/unit/unit.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,196 @@
import { LatLng } from "leaflet";
import { getMissionHandler, getUnitsManager } from "..";
import { GAME_MASTER } from "../constants/constants";
export class UnitDatabase {
blueprints: { [key: string]: UnitBlueprint } = {};
constructor() {
}
getCategory() {
return "";
}
/* Gets a specific blueprint by name */
getByName(name: string) {
if (name in this.blueprints)
return this.blueprints[name];
return null;
}
/* Gets a specific blueprint by label */
getByLabel(label: string) {
for (let unit in this.blueprints) {
if (this.blueprints[unit].label === label)
return this.blueprints[unit];
}
return null;
}
getBlueprints() {
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
return this.blueprints;
else {
var filteredBlueprints: { [key: string]: UnitBlueprint } = {};
for (let unit in this.blueprints) {
const blueprint = this.blueprints[unit];
if (this.getSpawnPointsByName(blueprint.name) <= getMissionHandler().getAvailableSpawnPoints() &&
getMissionHandler().getCommandModeOptions().eras.includes(blueprint.era) &&
(!getMissionHandler().getCommandModeOptions().restrictToCoalition || blueprint.coalition === getMissionHandler().getCommandedCoalition())) {
filteredBlueprints[unit] = blueprint;
}
}
return filteredBlueprints;
}
}
/* Returns a list of all possible roles in a database */
getRoles() {
var roles: string[] = [];
var filteredBlueprints = this.getBlueprints();
for (let unit in filteredBlueprints) {
var loadouts = filteredBlueprints[unit].loadouts;
if (loadouts) {
for (let loadout of loadouts) {
for (let role of loadout.roles) {
if (role !== "" && !roles.includes(role))
roles.push(role);
}
}
}
}
return roles;
}
/* Returns a list of all possible types in a database */
getTypes() {
var filteredBlueprints = this.getBlueprints();
var types: string[] = [];
for (let unit in filteredBlueprints) {
var type = filteredBlueprints[unit].type;
if (type && type !== "" && !types.includes(type))
types.push(type);
}
return types;
}
/* Returns a list of all possible periods in a database */
getEras() {
var filteredBlueprints = this.getBlueprints();
var eras: string[] = [];
for (let unit in filteredBlueprints) {
var era = filteredBlueprints[unit].era;
if (era && era !== "" && !eras.includes(era))
eras.push(era);
}
return eras;
}
/* Returns a list of all possible ranges in a database */
getRanges() {
var filteredBlueprints = this.getBlueprints();
var ranges: string[] = [];
for (let unit in filteredBlueprints) {
var range = filteredBlueprints[unit].range;
if (range && range !== "" && !ranges.includes(range))
ranges.push(range);
}
return ranges;
}
/* Get all blueprints by range */
getByRange(range: string) {
var filteredBlueprints = this.getBlueprints();
var unitswithrange = [];
for (let unit in filteredBlueprints) {
if (filteredBlueprints[unit].range === range) {
unitswithrange.push(filteredBlueprints[unit]);
}
}
return unitswithrange;
}
/* Get all blueprints by type */
getByType(type: string) {
var filteredBlueprints = this.getBlueprints();
var units = [];
for (let unit in filteredBlueprints) {
if (filteredBlueprints[unit].type === type) {
units.push(filteredBlueprints[unit]);
}
}
return units;
}
/* Get all blueprints by role */
getByRole(role: string) {
var filteredBlueprints = this.getBlueprints();
var units = [];
for (let unit in filteredBlueprints) {
var loadouts = filteredBlueprints[unit].loadouts;
if (loadouts) {
for (let loadout of loadouts) {
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) {
units.push(filteredBlueprints[unit])
break;
}
}
}
}
return units;
}
/* Get the names of all the loadouts for a specific unit and for a specific role */
getLoadoutNamesByRole(name: string, role: string) {
var filteredBlueprints = this.getBlueprints();
var loadoutsByRole = [];
var loadouts = filteredBlueprints[name].loadouts;
if (loadouts) {
for (let loadout of loadouts) {
if (loadout.roles.includes(role) || loadout.roles.includes("")) {
loadoutsByRole.push(loadout.name)
}
}
}
return loadoutsByRole;
}
/* Get the loadout content from the unit name and loadout name */
getLoadoutByName(name: string, loadoutName: string) {
var loadouts = this.blueprints[name].loadouts;
if (loadouts) {
for (let loadout of loadouts) {
if (loadout.name === loadoutName)
return loadout;
}
}
return null;
}
generateTestGrid(initialPosition: LatLng) {
var filteredBlueprints = this.getBlueprints();
const step = 0.01;
var nUnits = Object.values(filteredBlueprints).length;
var gridSize = Math.ceil(Math.sqrt(nUnits));
Object.values(filteredBlueprints).forEach((unitBlueprint: UnitBlueprint, idx: number) => {
var row = Math.floor(idx / gridSize);
var col = idx - row * gridSize;
var location = new LatLng(initialPosition.lat + col * step, initialPosition.lng + row * step)
getUnitsManager().spawnUnits(this.getCategory(), [{unitType: unitBlueprint.name, location: location, altitude: 1000, loadout: ""}]);
})
}
getSpawnPointsByLabel(label: string) {
var blueprint = this.getByLabel(label);
if (blueprint)
return this.getSpawnPointsByName(blueprint.name);
else
return Infinity;
}
getSpawnPointsByName(name: string) {
return Infinity;
}
}

View File

@@ -0,0 +1,748 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager } from "..";
import { Unit } from "./unit";
import { cloneUnit, deleteUnit, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server";
import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils";
import { CoalitionArea } from "../map/coalitionarea";
import { groundUnitDatabase } from "./groundunitdatabase";
import { DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT, NONE } from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { Contact } from "../@types/unit";
import { citiesDatabase } from "./citiesDatabase";
import { aircraftDatabase } from "./aircraftdatabase";
import { helicopterDatabase } from "./helicopterdatabase";
import { navyUnitDatabase } from "./navyunitdatabase";
export class UnitsManager {
#units: { [ID: number]: Unit };
#copiedUnits: any[];
#selectionEventDisabled: boolean = false;
#pasteDisabled: boolean = false;
#hiddenTypes: string[] = [];
#requestDetectionUpdate: boolean = false;
constructor() {
this.#units = {};
this.#copiedUnits = [];
document.addEventListener('copy', () => this.copyUnits());
document.addEventListener('paste', () => this.pasteUnits());
document.addEventListener('unitSelection', (e: CustomEvent) => this.#onUnitSelection(e.detail));
document.addEventListener('unitDeselection', (e: CustomEvent) => this.#onUnitDeselection(e.detail));
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete());
document.addEventListener('explodeSelectedUnits', () => this.selectedUnitsDelete(true));
document.addEventListener('keyup', (event) => this.#onKeyUp(event));
document.addEventListener('exportToFile', () => this.exportToFile());
document.addEventListener('importFromFile', () => this.importFromFile());
document.addEventListener('contactsUpdated', (e: CustomEvent) => {this.#requestDetectionUpdate = true});
document.addEventListener("commandModeOptionsChanged", () => {Object.values(this.#units).forEach((unit: Unit) => unit.updateVisibility())});
}
getSelectableAircraft() {
const units = this.getUnits();
return Object.keys(units).reduce((acc: { [key: number]: Unit }, unitId: any) => {
if (units[unitId].getCategory() === "Aircraft" && units[unitId].getAlive() === true) {
acc[unitId] = units[unitId];
}
return acc;
}, {});
}
getUnits() {
return this.#units;
}
getUnitByID(ID: number) {
if (ID in this.#units)
return this.#units[ID];
else
return null;
}
getUnitsByHotgroup(hotgroup: number) {
return Object.values(this.#units).filter((unit: Unit) => { return unit.getAlive() && unit.getHotgroup() == hotgroup });
}
addUnit(ID: number, category: string) {
if (category){
/* The name of the unit category is exactly the same as the constructor name */
var constructor = Unit.getConstructor(category);
if (constructor != undefined) {
this.#units[ID] = new constructor(ID);
}
}
}
update(buffer: ArrayBuffer) {
var dataExtractor = new DataExtractor(buffer);
var updateTime = Number(dataExtractor.extractUInt64());
var requestRefresh = false;
while (dataExtractor.getSeekPosition() < buffer.byteLength) {
const ID = dataExtractor.extractUInt32();
if (!(ID in this.#units)) {
const datumIndex = dataExtractor.extractUInt8();
if (datumIndex == DataIndexes.category) {
const category = dataExtractor.extractString();
this.addUnit(ID, category);
}
else {
requestRefresh = true;
}
}
this.#units[ID]?.setData(dataExtractor);
}
if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
for (let ID in this.#units) {
var unit = this.#units[ID];
if (!unit.belongsToCommandedCoalition())
unit.setDetectionMethods(this.getUnitDetectedMethods(unit));
}
this.#requestDetectionUpdate = false;
}
for (let ID in this.#units) {
if (this.#units[ID].getSelected())
this.#units[ID].drawLines();
};
return updateTime;
}
setHiddenType(key: string, value: boolean) {
if (value) {
if (this.#hiddenTypes.includes(key))
delete this.#hiddenTypes[this.#hiddenTypes.indexOf(key)];
}
else
this.#hiddenTypes.push(key);
Object.values(this.getUnits()).forEach((unit: Unit) => unit.updateVisibility());
}
getHiddenTypes() {
return this.#hiddenTypes;
}
selectUnit(ID: number, deselectAllUnits: boolean = true) {
if (deselectAllUnits)
this.getSelectedUnits().filter((unit: Unit) => unit.ID !== ID).forEach((unit: Unit) => unit.setSelected(false));
this.#units[ID]?.setSelected(true);
}
selectFromBounds(bounds: LatLngBounds) {
this.deselectAllUnits();
for (let ID in this.#units) {
if (this.#units[ID].getHidden() == false) {
var latlng = new LatLng(this.#units[ID].getPosition().lat, this.#units[ID].getPosition().lng);
if (bounds.contains(latlng)) {
this.#units[ID].setSelected(true);
}
}
}
}
getSelectedUnits(options?: { excludeHumans?: boolean, onlyOnePerGroup?: boolean }) {
var selectedUnits = [];
for (let ID in this.#units) {
if (this.#units[ID].getSelected()) {
selectedUnits.push(this.#units[ID]);
}
}
if (options) {
if (options.excludeHumans)
selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getHuman() });
if (options.onlyOnePerGroup) {
var temp: Unit[] = [];
for (let unit of selectedUnits) {
if (!temp.some((otherUnit: Unit) => unit.getGroupName() == otherUnit.getGroupName()))
temp.push(unit);
}
selectedUnits = temp;
}
}
return selectedUnits;
}
deselectAllUnits() {
for (let ID in this.#units) {
this.#units[ID].setSelected(false);
}
}
selectUnitsByHotgroup(hotgroup: number) {
this.deselectAllUnits();
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setSelected(true))
}
getSelectedUnitsTypes() {
const selectedUnits = this.getSelectedUnits();
if (selectedUnits.length == 0)
return [];
return selectedUnits.map((unit: Unit) => {
return unit.getCategory();
})?.filter((value: any, index: any, array: string[]) => {
return array.indexOf(value) === index;
});
};
/* Gets the value of a variable from the selected units. If all the units have the same value, returns the value, else returns undefined */
getSelectedUnitsVariable(variableGetter: CallableFunction) {
const selectedUnits = this.getSelectedUnits();
if (selectedUnits.length == 0)
return undefined;
return selectedUnits.map((unit: Unit) => {
return variableGetter(unit);
})?.reduce((a: any, b: any) => {
return a === b ? a : undefined
});
};
getSelectedUnitsCoalition() {
const selectedUnits = this.getSelectedUnits();
if (selectedUnits.length == 0)
return undefined;
return selectedUnits.map((unit: Unit) => {
return unit.getCoalition()
})?.reduce((a: any, b: any) => {
return a == b ? a : undefined
});
};
getByType(type: string) {
Object.values(this.getUnits()).filter((unit: Unit) => {
return unit.getType() === type;
})
}
getUnitDetectedMethods(unit: Unit) {
var detectionMethods: number[] = [];
for (let idx in this.#units) {
if (this.#units[idx].getAlive() && this.#units[idx].getIsLeader() && this.#units[idx].getCoalition() !== "neutral" && this.#units[idx].getCoalition() != unit.getCoalition())
{
this.#units[idx].getContacts().forEach((contact: Contact) => {
if (contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod))
detectionMethods.push(contact.detectionMethod);
});
}
}
return detectionMethods;
}
/*********************** Actions on selected units ************************/
selectedUnitsAddDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
/* Compute the destination for each unit. If mantainRelativePosition is true, compute the destination so to hold the relative distances */
var unitDestinations: { [key: number]: LatLng } = {};
if (mantainRelativePosition)
unitDestinations = this.selectedUnitsComputeGroupDestination(latlng, rotation);
else
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng; });
for (let idx in selectedUnits) {
const unit = selectedUnits[idx];
/* If a unit is following another unit, and that unit is also selected, send the command to the followed unit */
if (unit.getState() === "Follow") {
const leader = this.getUnitByID(unit.getLeaderID())
if (leader && leader.getSelected())
leader.addDestination(latlng);
else
unit.addDestination(latlng);
}
else {
if (unit.ID in unitDestinations)
unit.addDestination(unitDestinations[unit.ID]);
}
}
this.#showActionMessage(selectedUnits, " new destination added");
}
selectedUnitsClearDestinations() {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
const unit = selectedUnits[idx];
if (unit.getState() === "Follow") {
const leader = this.getUnitByID(unit.getLeaderID())
if (leader && leader.getSelected())
leader.clearDestinations();
else
unit.clearDestinations();
}
else
unit.clearDestinations();
}
}
selectedUnitsLandAt(latlng: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].landAt(latlng);
}
this.#showActionMessage(selectedUnits, " landing");
}
selectedUnitsChangeSpeed(speedChange: string) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].changeSpeed(speedChange);
}
}
selectedUnitsChangeAltitude(altitudeChange: string) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].changeAltitude(altitudeChange);
}
}
selectedUnitsSetSpeed(speed: number) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setSpeed(speed);
}
this.#showActionMessage(selectedUnits, `setting speed to ${msToKnots(speed)} kts`);
}
selectedUnitsSetSpeedType(speedType: string) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setSpeedType(speedType);
}
this.#showActionMessage(selectedUnits, `setting speed type to ${speedType}`);
}
selectedUnitsSetAltitude(altitude: number) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setAltitude(altitude);
}
this.#showActionMessage(selectedUnits, `setting altitude to ${mToFt(altitude)} ft`);
}
selectedUnitsSetAltitudeType(altitudeType: string) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setAltitudeType(altitudeType);
}
this.#showActionMessage(selectedUnits, `setting altitude type to ${altitudeType}`);
}
selectedUnitsSetROE(ROE: string) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setROE(ROE);
}
this.#showActionMessage(selectedUnits, `ROE set to ${ROE}`);
}
selectedUnitsSetReactionToThreat(reactionToThreat: string) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setReactionToThreat(reactionToThreat);
}
this.#showActionMessage(selectedUnits, `reaction to threat set to ${reactionToThreat}`);
}
selectedUnitsSetEmissionsCountermeasures(emissionCountermeasure: string) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setEmissionsCountermeasures(emissionCountermeasure);
}
this.#showActionMessage(selectedUnits, `emissions & countermeasures set to ${emissionCountermeasure}`);
}
selectedUnitsSetOnOff(onOff: boolean) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setOnOff(onOff);
}
this.#showActionMessage(selectedUnits, `unit active set to ${onOff}`);
}
selectedUnitsSetFollowRoads(followRoads: boolean) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].setFollowRoads(followRoads);
}
this.#showActionMessage(selectedUnits, `follow roads set to ${followRoads}`);
}
selectedUnitsAttackUnit(ID: number) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].attackUnit(ID);
}
this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
}
selectedUnitsDelete(explosion: boolean = false) {
var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
const selectionContainsAHuman = selectedUnits.some( ( unit:Unit ) => {
return unit.getHuman() === true;
});
if (selectionContainsAHuman && !confirm( "Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?" ) ) {
return;
}
var immediate = false;
if (selectedUnits.length > 20)
immediate = confirm(`You are trying to delete ${selectedUnits.length} units, do you want to delete them immediately? This may cause lag for players.`)
for (let idx in selectedUnits) {
selectedUnits[idx].delete(explosion, immediate);
}
this.#showActionMessage(selectedUnits, `deleted`);
}
selectedUnitsRefuel() {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].refuel();
}
this.#showActionMessage(selectedUnits, `sent to nearest tanker`);
}
selectedUnitsFollowUnit(ID: number, offset?: { "x": number, "y": number, "z": number }, formation?: string) {
if (offset == undefined) {
/* Simple formations with fixed offsets */
// X: front-rear, positive front
// Y: top-bottom, positive top
// Z: left-right, positive right
offset = { "x": 0, "y": 0, "z": 0 };
if (formation === "trail") { offset.x = -50; offset.y = -30; offset.z = 0; }
else if (formation === "echelon-lh") { offset.x = -50; offset.y = -10; offset.z = -50; }
else if (formation === "echelon-rh") { offset.x = -50; offset.y = -10; offset.z = 50; }
else if (formation === "line-abreast-rh") { offset.x = 0; offset.y = 0; offset.z = 50; }
else if (formation === "line-abreast-lh") { offset.x = 0; offset.y = 0; offset.z = -50; }
else if (formation === "front") { offset.x = 100; offset.y = 0; offset.z = 0; }
else offset = undefined;
}
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
var count = 1;
var xr = 0; var yr = 1; var zr = -1;
var layer = 1;
for (let idx in selectedUnits) {
var unit = selectedUnits[idx];
if (offset != undefined)
/* Offset is set, apply it */
unit.followUnit(ID, { "x": offset.x * count, "y": offset.y * count, "z": offset.z * count });
else {
/* More complex formations with variable offsets */
if (formation === "diamond") {
var xl = xr * Math.cos(Math.PI / 4) - yr * Math.sin(Math.PI / 4);
var yl = xr * Math.sin(Math.PI / 4) + yr * Math.cos(Math.PI / 4);
unit.followUnit(ID, { "x": -yl * 50, "y": zr * 10, "z": xl * 50 });
if (yr == 0) { layer++; xr = 0; yr = layer; zr = -layer; }
else {
if (xr < layer) { xr++; zr--; }
else { yr--; zr++; }
}
}
}
count++;
}
this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getUnitName()}`);
}
selectedUnitsSetHotgroup(hotgroup: number) {
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHotgroup(null));
this.selectedUnitsAddToHotgroup(hotgroup);
}
selectedUnitsAddToHotgroup(hotgroup: number) {
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits) {
selectedUnits[idx].setHotgroup(hotgroup);
}
this.#showActionMessage(selectedUnits, `added to hotgroup ${hotgroup}`);
getHotgroupPanel().refreshHotgroups();
}
selectedUnitsComputeGroupDestination(latlng: LatLng, rotation: number) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
/* Compute the center of the group */
var center = { x: 0, y: 0 };
selectedUnits.forEach((unit: Unit) => {
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
center.x += mercator.x / selectedUnits.length;
center.y += mercator.y / selectedUnits.length;
});
/* Compute the distances from the center of the group */
var unitDestinations: { [key: number]: LatLng } = {};
selectedUnits.forEach((unit: Unit) => {
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
var distancesFromCenter = { dx: mercator.x - center.x, dy: mercator.y - center.y };
/* Rotate the distance according to the group rotation */
var rotatedDistancesFromCenter: { dx: number, dy: number } = { dx: 0, dy: 0 };
rotatedDistancesFromCenter.dx = distancesFromCenter.dx * Math.cos(deg2rad(rotation)) - distancesFromCenter.dy * Math.sin(deg2rad(rotation));
rotatedDistancesFromCenter.dy = distancesFromCenter.dx * Math.sin(deg2rad(rotation)) + distancesFromCenter.dy * Math.cos(deg2rad(rotation));
/* Compute the final position of the unit */
var destMercator = latLngToMercator(latlng.lat, latlng.lng); // Convert destination point to mercator
var unitMercator = { x: destMercator.x + rotatedDistancesFromCenter.dx, y: destMercator.y + rotatedDistancesFromCenter.dy }; // Compute final position of this unit in mercator coordinates
var unitLatLng = mercatorToLatLng(unitMercator.x, unitMercator.y);
unitDestinations[unit.ID] = new LatLng(unitLatLng.lat, unitLatLng.lng);
});
return unitDestinations;
}
selectedUnitsBombPoint(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].bombPoint(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
selectedUnitsCarpetBomb(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].carpetBomb(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
selectedUnitsBombBuilding(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].bombBuilding(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
selectedUnitsFireAtArea(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].fireAtArea(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
// TODO add undo group
selectedUnitsCreateGroup() {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: false });
var units = [];
var coalition = "neutral";
for (let idx in selectedUnits) {
var unit = selectedUnits[idx];
coalition = unit.getCoalition();
deleteUnit(unit.ID, false, true);
units.push({unitType: unit.getName(), location: unit.getPosition()});
}
const category = this.getSelectedUnitsTypes()[0];
this.spawnUnits(category, units, coalition, true);
}
/***********************************************/
copyUnits() {
this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => {return unit.getData()}))); /* Can be applied to humans too */
getInfoPopup().setText(`${this.#copiedUnits.length} units copied`);
}
pasteUnits() {
if (!this.#pasteDisabled && getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER) {
/* Compute the position of the center of the copied units */
var nUnits = this.#copiedUnits.length;
var avgLat = 0;
var avgLng = 0;
for (let idx in this.#copiedUnits) {
var unit = this.#copiedUnits[idx];
avgLat += unit.position.lat / nUnits;
avgLng += unit.position.lng / nUnits;
}
/* Organize the copied units in groups */
var groups: {[key: string]: any} = {};
this.#copiedUnits.forEach((unit: any) => {
if (!(unit.groupName in groups))
groups[unit.groupName] = [];
groups[unit.groupName].push(unit);
});
for (let groupName in groups) {
/* Paste the units as groups. Only for ground and navy units because of loadouts, TODO: find a better solution so it works for them too*/
if (!["Aircraft", "Helicopter"].includes(groups[groupName][0].category)) {
var units = groups[groupName].map((unit: any) => {
var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng);
getMap().addTemporaryMarker(position, unit.name, unit.coalition);
return {unitType: unit.name, location: position};
});
this.spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
}
else {
groups[groupName].forEach((unit: any) => {
var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng);
getMap().addTemporaryMarker(position, unit.name, unit.coalition);
cloneUnit(unit.ID, position);
});
}
}
getInfoPopup().setText(`${this.#copiedUnits.length - 1} units pasted`);
}
else {
getInfoPopup().setText(`Unit cloning is disabled in ${getMissionHandler().getCommandModeOptions().commandMode} mode`);
}
}
createIADS(coalitionArea: CoalitionArea, types: {[key: string]: boolean}, eras: {[key: string]: boolean}, ranges: {[key: string]: boolean}, density: number, distribution: number) {
const activeTypes = Object.keys(types).filter((key: string) => { return types[key]; });
const activeEras = Object.keys(eras).filter((key: string) => { return eras[key]; });
const activeRanges = Object.keys(ranges).filter((key: string) => { return ranges[key]; });
citiesDatabase.forEach((city: {lat: number, lng: number, pop: number}) => {
if (polyContains(new LatLng(city.lat, city.lng), coalitionArea)) {
var pointsNumber = 2 + Math.pow(city.pop, 0.2) * density / 100;
for (let i = 0; i < pointsNumber; i++) {
var bearing = Math.random() * 360;
var distance = Math.random() * distribution * 100;
const latlng = bearingAndDistanceToLatLng(city.lat, city.lng, bearing, distance);
if (polyContains(latlng, coalitionArea)) {
const type = activeTypes[Math.floor(Math.random() * activeTypes.length)];
if (Math.random() < IADSDensities[type]) {
const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {type: type, eras: activeEras, ranges: activeRanges});
if (unitBlueprint) {
this.spawnUnits("GroundUnit", [{unitType: unitBlueprint.name, location: latlng}], coalitionArea.getCoalition(), true);
getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition());
}
}
}
}
}
})
}
exportToFile() {
var unitsToExport: {[key: string]: any} = {};
for (let ID in this.#units) {
var unit = this.#units[ID];
if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) {
var data: any = unit.getData();
if (unit.getGroupName() in unitsToExport)
unitsToExport[unit.getGroupName()].push(data);
else
unitsToExport[unit.getGroupName()] = [data];
}
}
var a = document.createElement("a");
var file = new Blob([JSON.stringify(unitsToExport)], {type: 'text/plain'});
a.href = URL.createObjectURL(file);
a.download = 'export.json';
a.click();
}
importFromFile() {
var input = document.createElement("input");
input.type = "file";
input.addEventListener("change", (e: any) => {
var file = e.target.files[0];
if (!file) {
return;
}
var reader = new FileReader();
reader.onload = function(e: any) {
var contents = e.target.result;
var groups = JSON.parse(contents);
for (let groupName in groups) {
if (groupName !== "" && groups[groupName].length > 0 && groups[groupName].every((unit: any) => {return unit.category == "GroundUnit";})) {
var units = groups[groupName].map((unit: any) => {return {unitType: unit.name, location: unit.position}});
getUnitsManager().spawnUnits("GroundUnit", units, groups[groupName][0].coalition, true);
}
}
};
reader.readAsText(file);
})
input.click();
}
spawnUnits(category: string, units: any, coalition: string = "blue", immediate: boolean = true, airbase: string = "") {
var spawnPoints = 0;
if (category === "Aircraft") {
if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
getInfoPopup().setText("Aircrafts can be air spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: any) => {return points + aircraftDatabase.getSpawnPointsByName(unit.unitType)}, 0);
spawnAircrafts(units, coalition, airbase, immediate, spawnPoints);
} else if (category === "Helicopter") {
if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
getInfoPopup().setText("Helicopters can be air spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: any) => {return points + helicopterDatabase.getSpawnPointsByName(unit.unitType)}, 0);
spawnHelicopters(units, coalition, airbase, immediate, spawnPoints);
} else if (category === "GroundUnit") {
if (getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
getInfoPopup().setText("Ground units can be spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: any) => {return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
spawnGroundUnits(units, coalition, immediate, spawnPoints);
} else if (category === "NavyUnit") {
if (getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
getInfoPopup().setText("Navy units can be spawned during the SETUP phase only");
return false;
}
spawnPoints = units.reduce((points: number, unit: any) => {return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
spawnNavyUnits(units, coalition, immediate, spawnPoints);
}
if (spawnPoints <= getMissionHandler().getAvailableSpawnPoints()) {
getMissionHandler().setSpentSpawnPoints(spawnPoints);
return true;
} else {
getInfoPopup().setText("Not enough spawn points available!");
return false;
}
}
/***********************************************/
#onKeyUp(event: KeyboardEvent) {
if (!keyEventWasInInput(event)) {
if (event.key === "Delete")
this.selectedUnitsDelete();
else if (event.key === "a" && event.ctrlKey)
Object.values(this.getUnits()).filter((unit: Unit) => {return !unit.getHidden()}).forEach((unit: Unit) => unit.setSelected(true));
}
}
#onUnitSelection(unit: Unit) {
if (this.getSelectedUnits().length > 0) {
getMap().setState(MOVE_UNIT);
/* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */
if (!this.#selectionEventDisabled) {
window.setTimeout(() => {
document.dispatchEvent(new CustomEvent("unitsSelection", { detail: this.getSelectedUnits() }));
this.#selectionEventDisabled = false;
}, 100);
this.#selectionEventDisabled = true;
}
}
else {
getMap().setState(IDLE);
document.dispatchEvent(new CustomEvent("clearSelection"));
}
}
#onUnitDeselection(unit: Unit) {
if (this.getSelectedUnits().length == 0) {
getMap().setState(IDLE);
document.dispatchEvent(new CustomEvent("clearSelection"));
}
else
document.dispatchEvent(new CustomEvent("unitsDeselection", { detail: this.getSelectedUnits() }));
}
#showActionMessage(units: any[], message: string) {
if (units.length == 1)
getInfoPopup().setText(`${units[0].getUnitName()} ${message}`);
else if (units.length > 1)
getInfoPopup().setText(`${units[0].getUnitName()} and ${units.length - 1} other units ${message}`);
}
}