Merge remote-tracking branch 'origin/performance-optimization' into 326-add-advanced-rts-options

This commit is contained in:
Pax1601
2023-07-19 17:03:35 +02:00
43 changed files with 1197 additions and 602 deletions

View File

@@ -20,6 +20,7 @@ interface CustomEventMap {
"mapContextMenu": CustomEvent<>,
"visibilityModeChanged": CustomEvent<string>,
"RTSOptionsChanged": CustomEvent<>,
"contactsUpdated": CustomEvent<Unit>,
}
declare global {

View File

@@ -142,8 +142,8 @@ export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area";
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
export const visibilityControlsTootlips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"];
export const IADSTypes = ["AAA", "MANPADS", "SAM Sites", "Radar"];
export const IADSDensities: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05};
export const IADSTypes = ["AAA", "MANPADS", "SAM Site", "Radar"];
export const IADSDensities: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Site": 0.1, "Radar": 0.05};
export enum DataIndexes {
startOfData = 0,
@@ -184,5 +184,6 @@ export enum DataIndexes {
ammo,
contacts,
activePath,
isLeader,
endOfData = 255
};

View File

@@ -67,7 +67,7 @@ export class Map extends L.Map {
constructor(ID: string) {
/* Init the leaflet map */
//@ts-ignore Needed because the boxSelect option is non-standard
super(ID, { doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 });
super(ID, { preferCanvas: true, doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 });
this.setView([37.23, -115.8], 10);
this.#ID = ID;
@@ -295,6 +295,10 @@ export class Map extends L.Map {
}
}
getCenterUnit() {
return this.#centerUnit;
}
setTheatre(theatre: string) {
var bounds = new L.LatLngBounds([-90, -180], [90, 180]);
var miniMapZoom = 5;
@@ -424,9 +428,6 @@ export class Map extends L.Map {
#onDoubleClick(e: any) {
this.deselectAllCoalitionAreas();
var db = groundUnitDatabase;
db.generateTestGrid(this.getMouseCoordinates())
}
#onContextMenu(e: any) {

View File

@@ -278,9 +278,17 @@ export function getMarkerCategoryByName(name: string) {
else if (helicopterDatabase.getByName(name) != null)
return "helicopter";
else if (groundUnitDatabase.getByName(name) != null){
// TODO this is very messy
var type = groundUnitDatabase.getByName(name)?.type;
return (type?.includes("SAM")) ? "groundunit-sam" : "groundunit-other";
if (type === "SAM")
return "groundunit-sam";
else if (type === "SAM Search radar" || type === "SAM Track radar" || type === "SAM Search/Track radar")
return "groundunit-sam-radar";
else if (type === "SAM Launcher")
return "groundunit-sam-launcher";
else if (type === "Radar")
return "groundunit-ewr";
else
return "groundunit-other";
}
else
return "groundunit-other"; // TODO add other unit types

View File

@@ -8,4 +8,12 @@ export class ConnectionStatusPanel extends Panel {
update(connected: boolean) {
this.getElement().toggleAttribute( "data-is-connected", connected );
}
setMetrics(frameRate: number, load: number) {
const dt = this.getElement().querySelector("dt");
if (dt) {
dt.dataset["framerate"] = String(frameRate);
dt.dataset["load"] = String(load);
}
}
}

View File

@@ -141,7 +141,7 @@ export class UnitControlPanel extends Panel {
element.toggleAttribute("data-show-advanced-settings-button", units.length == 1);
/* Flight controls */
var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()});
var desiredAltitude = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()});
var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitudeType()});
var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeed()});
var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeedType()});

View File

@@ -18,7 +18,7 @@ var username = "";
var password = "";
var sessionHash: string | null = null;
var lastUpdateTime = 0;
var lastUpdateTimes: {[key: string]: number} = {}
var demoEnabled = false;
export function toggleDemoEnabled() {
@@ -54,9 +54,14 @@ export function GET(callback: CallableFunction, uri: string, options?: { time?:
/* Success */
setConnected(true);
if (xmlHttp.responseType == 'arraybuffer')
callback(xmlHttp.response);
else
callback(JSON.parse(xmlHttp.responseText));
lastUpdateTimes[uri] = callback(xmlHttp.response);
else {
const result = JSON.parse(xmlHttp.responseText);
lastUpdateTimes[uri] = callback(result);
if ("frameRate" in result && "load" in result)
getConnectionStatusPanel().setMetrics(result.frameRate, result.load);
}
} else if (xmlHttp.status == 401) {
/* Bad credentials */
console.error("Incorrect username/password");
@@ -103,10 +108,6 @@ export function setAddress(address: string, port: number) {
console.log(`Setting REST address to ${REST_ADDRESS}`)
}
export function setLastUpdateTime(newLastUpdateTime: number) {
lastUpdateTime = newLastUpdateTime;
}
export function getAirbases(callback: CallableFunction) {
GET(callback, AIRBASES_URI);
}
@@ -115,8 +116,8 @@ export function getBullseye(callback: CallableFunction) {
GET(callback, BULLSEYE_URI);
}
export function getLogs(callback: CallableFunction) {
GET(callback, LOGS_URI);
export function getLogs(callback: CallableFunction, refresh: boolean = false) {
GET(callback, LOGS_URI, { time: refresh ? 0 : lastUpdateTimes[LOGS_URI]});
}
export function getMission(callback: CallableFunction) {
@@ -124,7 +125,7 @@ export function getMission(callback: CallableFunction) {
}
export function getUnits(callback: CallableFunction, refresh: boolean = false) {
GET(callback, `${UNITS_URI}`, { time: refresh ? 0 : lastUpdateTime }, 'arraybuffer');
GET(callback, UNITS_URI, { time: refresh ? 0 : lastUpdateTimes[UNITS_URI] }, 'arraybuffer');
}
export function addDestination(ID: number, path: any) {
@@ -320,7 +321,14 @@ export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolea
}
export function startUpdate() {
getUnits((buffer: ArrayBuffer) => getUnitsManager()?.update(buffer), true /* Does a full refresh */);
/* On the first connection, force request of full data */
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
getMission((data: any) => {
getMissionData()?.update(data);
checkSessionHash(data.sessionHash);
});
getUnits((buffer: ArrayBuffer) => {return getUnitsManager()?.update(buffer), true /* Does a full refresh */});
requestUpdate();
requestRefresh();
@@ -329,7 +337,7 @@ export function startUpdate() {
export function requestUpdate() {
/* Main update rate = 250ms is minimum time, equal to server update time. */
if (!getPaused()) {
getUnits((buffer: ArrayBuffer) => { getUnitsManager()?.update(buffer); }, false);
getUnits((buffer: ArrayBuffer) => { return getUnitsManager()?.update(buffer); }, false);
}
window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000);
@@ -351,6 +359,13 @@ export function requestRefresh() {
checkSessionHash(data.sessionHash);
getMissionHandler()?.updateMission(data);
});
getLogs((data: any) => {
for (let key in data.logs) {
if (key != "requestTime")
console.log(data.logs[key]);
}
return data.time;
});
// Update the list of existing units
getUnitDataTable()?.update();

View File

@@ -15,7 +15,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "SA-2 SAM Battery",
"range": "Long",
"filename": "",
"type": "SAM Sites"
"type": "SAM Site"
},
"SA-3 SAM Battery": {
"name": "SA-3 SAM Battery",
@@ -25,7 +25,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "SA-3 SAM Battery",
"range": "Medium",
"filename": "",
"type": "SAM Sites"
"type": "SAM Site"
},
"SA-6 SAM Battery": {
"name": "SA-6 SAM Battery",
@@ -35,7 +35,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "SA-6 SAM Battery",
"range": "Medium",
"filename": "",
"type": "SAM Sites"
"type": "SAM Site"
},
"SA-10 SAM Battery": {
"name": "SA-10 SAM Battery",
@@ -45,7 +45,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "SA-10 SAM Battery",
"range": "Long",
"filename": "",
"type": "SAM Sites"
"type": "SAM Site"
},
"SA-11 SAM Battery": {
"name": "SA-11 SAM Battery",
@@ -55,7 +55,17 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "SA-11 SAM Battery",
"range": "Medium",
"filename": "",
"type": "SAM Sites"
"type": "SAM Site"
},
"SA-5 SAM Battery": {
"name": "SA-5 SAM Battery",
"coalition": "Red",
"era": "Mid Cold War",
"label": "SA-5 SAM Battery",
"shortLabel": "SA-5 SAM Battery",
"range": "Long",
"filename": "",
"type": "SAM Site"
},
"Patriot site": {
"name": "Patriot site",
@@ -65,7 +75,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "Patriot site",
"range": "Long",
"filename": "",
"type": "SAM Sites"
"type": "SAM Site"
},
"Hawk SAM Battery": {
"name": "Hawk SAM Battery",
@@ -75,7 +85,25 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "Hawk SAM Battery",
"range": "Medium",
"filename": "",
"type": "SAM Sites"
"type": "SAM Site"
},
"SNR_75V": {
"name": "SNR_75V",
"coalition": "Red",
"era": "Early Cold War",
"label": "SA-2 Fan Song",
"shortLabel": "SNR 75V",
"filename": "",
"type": "SAM Track radar"
},
"S_75M_Volhov": {
"name": "S_75M_Volhov",
"coalition": "Red",
"era": "Early Cold War",
"label": "SA-2 Launcher",
"shortLabel": "S75M Volhov",
"filename": "",
"type": "SAM Launcher"
},
"2B11 mortar": {
"name": "2B11 mortar",
@@ -460,61 +488,61 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "Kub 2P25 ln",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-6 Kub 2P25 ln",
"label": "SA-6 Launcher",
"shortLabel": "Kub 2P25 ln",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"5p73 s-125 ln": {
"name": "5p73 s-125 ln",
"coalition": "red",
"era": "Early Cold War",
"label": "SA-3 5p73 s-125 ln",
"label": "SA-3 Launcher",
"shortLabel": "5p73 s-125 ln",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"S-300PS 5P85C ln": {
"name": "S-300PS 5P85C ln",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-10 S-300PS 5P85C ln",
"label": "SA-10 Launcher (5P85C)",
"shortLabel": "S-300PS 5P85C ln",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"S-300PS 5P85D ln": {
"name": "S-300PS 5P85D ln",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-10 S-300PS 5P85D ln",
"label": "SA-10 Launcher (5P85D)",
"shortLabel": "S-300PS 5P85D ln",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"SA-11 Buk LN 9A310M1": {
"name": "SA-11 Buk LN 9A310M1",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-11 Buk LN 9A310M1",
"label": "SA-11 Launcher",
"shortLabel": "SA-11 Buk LN 9A310M1",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"Osa 9A33 ln": {
"name": "Osa 9A33 ln",
"coalition": "red",
"era": "Mid Cold War",
"label": "SA-8 Osa 9A33 ln",
"label": "SA-8 Launcher",
"shortLabel": "Osa 9A33 ln",
"range": "Short",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"Tor 9A331": {
"name": "Tor 9A331",
@@ -550,11 +578,11 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "SA-11 Buk CC 9S470M1",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-11 Buk CC 9S470M1",
"label": "SA-11 Command Post",
"shortLabel": "SA-11 Buk CC 9S470M1",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Support vehicle"
},
"SA-8 Osa LD 9T217": {
"name": "SA-8 Osa LD 9T217",
@@ -570,21 +598,21 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "Patriot AMG",
"coalition": "blue",
"era": "Modern",
"label": "Patriot AMG",
"label": "Patriot Antenna Mast Group",
"shortLabel": "Patriot AMG",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Support vehicle"
},
"Patriot ECS": {
"name": "Patriot ECS",
"coalition": "blue",
"era": "Modern",
"label": "Patriot ECS",
"label": "Patriot Engagement Control Station",
"shortLabel": "Patriot ECS",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Support vehicle"
},
"Gepard": {
"name": "Gepard",
@@ -599,11 +627,11 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "Hawk pcp",
"coalition": "blue",
"era": "Late Cold War",
"label": "Hawk pcp",
"label": "Hawk Platoon Command Post",
"shortLabel": "Hawk pcp",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Support vehicle"
},
"SA-18 Igla manpad": {
"name": "SA-18 Igla manpad",
@@ -635,6 +663,36 @@ export class GroundUnitDatabase extends UnitDatabase {
"filename": "",
"type": "MANPADS"
},
"RPC_5N62V": {
"name": "RPC_5N62V",
"coalition": "Red",
"era": "Mid Cold War",
"label": "SA-5 Square Pair",
"shortLabel": "RPC 5N62V",
"range": "Long",
"filename": "",
"type": "SAM Track radar"
},
"RLS_19J6": {
"name": "RLS_19J6",
"coalition": "Red",
"era": "Mid Cold War",
"label": "SA-5 Thin Shield",
"shortLabel": "RLS 19J6",
"range": "Long",
"filename": "",
"type": "SAM Search radar"
},
"S-200_Launcher": {
"name": "S-200_Launcher",
"coalition": "Red",
"era": "Mid Cold War",
"label": "SA-5 Launcher",
"shortLabel": "S-200 Launcher",
"range": "Long",
"filename": "",
"type": "SAM Launcher"
},
"Vulcan": {
"name": "Vulcan",
"coalition": "blue",
@@ -648,10 +706,10 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "Hawk ln",
"coalition": "blue",
"era": "Late Cold War",
"label": "Hawk ln",
"label": "Hawk Launcher",
"shortLabel": "Hawk ln",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"M48 Chaparral": {
"name": "M48 Chaparral",
@@ -675,11 +733,11 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "Patriot ln",
"coalition": "blue",
"era": "Late Cold War",
"label": "Patriot ln",
"label": "Patriot Launcher",
"shortLabel": "Patriot ln",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Launcher"
},
"M1097 Avenger": {
"name": "M1097 Avenger",
@@ -694,21 +752,21 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "Patriot EPP",
"coalition": "blue",
"era": "Late Cold War",
"label": "Patriot EPP",
"label": "Patriot Electric Power Plant",
"shortLabel": "Patriot EPP",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Support vehicle"
},
"Patriot cp": {
"name": "Patriot cp",
"coalition": "blue",
"era": "Late Cold War",
"label": "Patriot cp",
"label": "Patriot Command Post",
"shortLabel": "Patriot cp",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Support vehicle"
},
"Roland ADS": {
"name": "Roland ADS",
@@ -723,11 +781,11 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "S-300PS 54K6 cp",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-10 S-300PS 54K6 cp",
"label": "SA-10 Command Post",
"shortLabel": "S-300PS 54K6 cp",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Support vehicle"
},
"Stinger manpad GRG": {
"name": "Stinger manpad GRG",
@@ -737,7 +795,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "Stinger manpad GRG",
"range": "Short",
"filename": "",
"type": "SAM"
"type": "MANPADS"
},
"Stinger manpad dsr": {
"name": "Stinger manpad dsr",
@@ -757,7 +815,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "Stinger comm dsr",
"range": "Short",
"filename": "",
"type": "SAM"
"type": "MANPADS"
},
"Stinger manpad": {
"name": "Stinger manpad",
@@ -777,7 +835,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"shortLabel": "Stinger comm",
"range": "Short",
"filename": "",
"type": "SAM"
"type": "MANPADS"
},
"ZSU-23-4 Shilka": {
"name": "ZSU-23-4 Shilka",
@@ -846,7 +904,7 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "1L13 EWR",
"coalition": "red",
"era": "Late Cold War",
"label": "1L13 EWR",
"label": "Box Spring",
"shortLabel": "1L13 EWR",
"filename": "",
"type": "Radar"
@@ -855,37 +913,37 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "Kub 1S91 str",
"coalition": "red",
"era": "Mid Cold War",
"label": "SA-6 Kub 1S91 str",
"label": "SA-6 Straight flush",
"shortLabel": "Kub 1S91 str",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Search/Track radar"
},
"S-300PS 40B6M tr": {
"name": "S-300PS 40B6M tr",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-10 S-300PS 40B6M tr",
"label": "SA-10 Tin Shield",
"shortLabel": "S-300PS 40B6M tr",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Track radar"
},
"S-300PS 40B6MD sr": {
"name": "S-300PS 40B6MD sr",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-10 S-300PS 40B6MD sr",
"label": "SA-10 Clam Shell",
"shortLabel": "S-300PS 40B6MD sr",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Search radar"
},
"55G6 EWR": {
"name": "55G6 EWR",
"coalition": "red",
"era": "Early Cold War",
"label": "55G6 EWR",
"label": "Tall Rack",
"shortLabel": "55G6 EWR",
"filename": "",
"type": "Radar"
@@ -894,98 +952,98 @@ export class GroundUnitDatabase extends UnitDatabase {
"name": "S-300PS 64H6E sr",
"coalition": "red",
"era": "Late Cold War",
"label": "SA-10 S-300PS 64H6E sr",
"label": "SA-10 Big Bird",
"shortLabel": "S-300PS 64H6E sr",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Search radar"
},
"SA-11 Buk SR 9S18M1": {
"name": "SA-11 Buk SR 9S18M1",
"coalition": "red",
"era": "Mid Cold War",
"label": "SA-11 Buk SR 9S18M1",
"label": "SA-11 Snown Drift",
"shortLabel": "SA-11 Buk SR 9S18M1",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Search radar"
},
"Dog Ear radar": {
"name": "Dog Ear radar",
"coalition": "red",
"era": "Mid Cold War",
"label": "Dog Ear radar",
"label": "Dog Ear",
"shortLabel": "Dog Ear radar",
"filename": "",
"type": "SAM"
"type": "SAM Search radar"
},
"Hawk tr": {
"name": "Hawk tr",
"coalition": "blue",
"era": "Early Cold War",
"label": "Hawk tr",
"label": "Hawk Track radar",
"shortLabel": "Hawk tr",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Track radar"
},
"Hawk sr": {
"name": "Hawk sr",
"coalition": "blue",
"era": "Early Cold War",
"label": "Hawk sr",
"label": "Hawk Search radar",
"shortLabel": "Hawk sr",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Search radar"
},
"Patriot str": {
"name": "Patriot str",
"coalition": "blue",
"era": "Late Cold War",
"label": "Patriot str",
"label": "Patriot Search/Track radar",
"shortLabel": "Patriot str",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Search/Track radar"
},
"Hawk cwar": {
"name": "Hawk cwar",
"coalition": "blue",
"era": "Early Cold War",
"label": "Hawk cwar",
"label": "Hawk Continous Wave Acquisition Radar",
"shortLabel": "Hawk cwar",
"range": "Long",
"filename": "",
"type": "SAM"
"type": "SAM Track radar"
},
"p-19 s-125 sr": {
"name": "p-19 s-125 sr",
"coalition": "red",
"era": "Mid Cold War",
"label": "SA-3 p-19 s-125 sr",
"shortLabel": "p-19 s-125 sr",
"label": "SA-3 Flat Face B",
"shortLabel": "Flat Face B",
"filename": "",
"type": "SAM"
"type": "SAM Search radar"
},
"Roland Radar": {
"name": "Roland Radar",
"coalition": "blue",
"era": "Mid Cold War",
"label": "Roland Radar",
"label": "Roland Search radar",
"shortLabel": "Roland Radar",
"filename": "",
"type": "SAM"
"type": "SAM Search radar"
},
"snr s-125 tr": {
"name": "snr s-125 tr",
"coalition": "red",
"era": "Early Cold War",
"label": "SA-3 snr s-125 tr",
"label": "SA-3 Low Blow",
"shortLabel": "snr s-125 tr",
"range": "Medium",
"filename": "",
"type": "SAM"
"type": "SAM Track radar"
},
"house1arm": {
"name": "house1arm",

View File

@@ -9,6 +9,8 @@ import { TargetMarker } from '../map/targetmarker';
import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, HIDE_ALL, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants';
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, UnitIconOptions } from '../@types/unit';
import { DataExtractor } from './dataextractor';
import { groundUnitDatabase } from './groundunitdatabase';
import { navyUnitDatabase } from './navyunitdatabase';
var pathIcon = new Icon({
iconUrl: '/resources/theme/images/markers/marker-icon.png',
@@ -74,6 +76,7 @@ export class Unit extends CustomMarker {
#ammo: Ammo[] = [];
#contacts: Contact[] = [];
#activePath: LatLng[] = [];
#isLeader: boolean = false;
#selectable: boolean;
#selected: boolean = false;
@@ -126,6 +129,7 @@ export class Unit extends CustomMarker {
getAmmo() {return this.#ammo};
getContacts() {return this.#contacts};
getActivePath() {return this.#activePath};
getIsLeader() {return this.#isLeader};
static getConstructor(type: string) {
if (type === "GroundUnit") return GroundUnit;
@@ -162,6 +166,8 @@ export class Unit extends CustomMarker {
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
});
getMap().on("zoomend", () => {this.#onZoom();})
}
getCategory() {
@@ -212,8 +218,9 @@ export class Unit extends CustomMarker {
case DataIndexes.radio: this.#radio = dataExtractor.extractRadio(); break;
case DataIndexes.generalSettings: this.#generalSettings = dataExtractor.extractGeneralSettings(); break;
case DataIndexes.ammo: this.#ammo = dataExtractor.extractAmmo(); break;
case DataIndexes.contacts: this.#contacts = dataExtractor.extractContacts(); break;
case DataIndexes.contacts: this.#contacts = dataExtractor.extractContacts(); document.dispatchEvent(new CustomEvent("contactsUpdated", {detail: this})); break;
case DataIndexes.activePath: this.#activePath = dataExtractor.extractActivePath(); break;
case DataIndexes.isLeader: this.#isLeader = dataExtractor.extractBool(); break;
}
}
@@ -223,25 +230,20 @@ export class Unit extends CustomMarker {
if (updateMarker)
this.#updateMarker();
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
if (this.getSelected() || getMap().getCenterUnit() === this)
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
}
drawLines() {
// TODO dont delete the polylines of the detected units
this.#clearContacts();
if (this.getSelected()) {
this.#drawPath();
this.#drawContacts();
this.#drawTarget();
}
else {
this.#clearPath();
this.#clearTarget();
}
this.#drawPath();
this.#drawContacts();
this.#drawTarget();
}
getData() {
return {
category: this.getCategory(),
ID: this.ID,
alive: this.#alive,
human: this.#human,
controlled: this.#controlled,
@@ -277,7 +279,8 @@ export class Unit extends CustomMarker {
generalSettings: this.#generalSettings,
ammo: this.#ammo,
contacts: this.#contacts,
activePath: this.#activePath
activePath: this.#activePath,
isLeader: this.#isLeader
}
}
@@ -315,7 +318,7 @@ export class Unit extends CustomMarker {
/* Only alive units can be selected. Some units are not selectable (weapons) */
if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected && this.belongsToCommandedCoalition()) {
this.#selected = selected;
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-selected", selected);
if (selected) {
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
this.#updateMarker();
@@ -326,6 +329,15 @@ export class Unit extends CustomMarker {
this.#clearPath();
this.#clearTarget();
}
this.getElement()?.querySelector(`.unit`)?.toggleAttribute("data-is-selected", selected);
if (getMap().getZoom() < 13) {
if (this.#isLeader)
this.getGroupMembers().forEach((unit: Unit) => unit.setSelected(selected));
else
this.#updateMarker();
}
}
}
@@ -374,6 +386,10 @@ export class Unit extends CustomMarker {
return true;
}
getType() {
return "";
}
/********************** Icon *************************/
createIcon(): void {
/* Set the icon */
@@ -476,21 +492,16 @@ export class Unit extends CustomMarker {
/********************** Visibility *************************/
updateVisibility() {
var hidden = false;
const hiddenUnits = getUnitsManager().getHiddenTypes();
if (this.#human && hiddenUnits.includes("human"))
hidden = true;
if (this.#controlled == false && hiddenUnits.includes("dcs"))
hidden = true;
if (hiddenUnits.includes(this.getMarkerCategory()))
hidden = true;
if (hiddenUnits.includes(this.#coalition))
hidden = true;
if (getUnitsManager().getCommandMode() === HIDE_ALL)
hidden = true;
if (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0) {
hidden = true;
}
var hidden = ((this.#human && hiddenUnits.includes("human")) ||
(this.#controlled == false && hiddenUnits.includes("dcs")) ||
(hiddenUnits.includes(this.getMarkerCategory())) ||
(hiddenUnits.includes(this.#coalition)) ||
(getUnitsManager().getCommandMode() === HIDE_ALL) ||
(!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0) ||
(!this.#isLeader && this.getCategory() == "GroundUnit" && getMap().getZoom() < 13)) &&
!(this.getSelected());
this.setHidden(hidden || !this.#alive);
}
@@ -716,7 +727,7 @@ export class Unit extends CustomMarker {
}
else if ((selectedUnits.length > 0 && (selectedUnits.includes(this))) || selectedUnits.length == 0) {
if (this.getCategory() == "Aircraft") {
options["refuel"] = { text: "Air to air refuel", tooltip: "Refuel unit at the nearest AAR Tanker. If no tanker is available the unit will RTB." }; // TODO Add some way of knowing which aircraft can AAR
options["refuel"] = { text: "Air to air refuel", tooltip: "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB." }; // TODO Add some way of knowing which aircraft can AAR
}
}
@@ -728,10 +739,13 @@ export class Unit extends CustomMarker {
}
if ((selectedUnits.length === 0 && this.getCategory() == "GroundUnit") || selectedUnitTypes.length === 1 && ["GroundUnit"].includes(selectedUnitTypes[0])) {
if (selectedUnits.concat([this]).every((unit: Unit) => { return unit.canFulfillRole(["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank"]) }))
if (selectedUnits.concat([this]).every((unit: Unit) => { return ["Gun Artillery", "Rocket Artillery", "Infantry", "IFV", "Tank"].includes(this.getType()) }))
options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" };
}
if (selectedUnitTypes.length === 1 && ["NavyUnit", "GroundUnit"].includes(selectedUnitTypes[0]) && getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) !== undefined)
options["group"] = { text: "Create group", tooltip: "Create a group from the selected units." };
if (Object.keys(options).length > 0) {
getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
getMap().getUnitContextMenu().setOptions(options, (option: string) => {
@@ -748,6 +762,8 @@ export class Unit extends CustomMarker {
getUnitsManager().selectedUnitsAttackUnit(this.ID);
else if (action === "refuel")
getUnitsManager().selectedUnitsRefuel();
else if (action === "group")
getUnitsManager().selectedUnitsCreateGroup();
else if (action === "follow")
this.#showFollowOptions(e);
else if (action === "bomb")
@@ -756,6 +772,7 @@ export class Unit extends CustomMarker {
getMap().setState(CARPET_BOMBING);
else if (action === "fire-at-area")
getMap().setState(FIRE_AT_AREA);
}
#showFollowOptions(e: any) {
@@ -947,10 +964,11 @@ export class Unit extends CustomMarker {
}
#drawContacts() {
this.#clearContacts();
for (let index in this.#contacts) {
var contactData = this.#contacts[index];
var contact = getUnitsManager().getUnitByID(contactData.ID)
if (contact != null) {
if (contact != null && contact.getAlive()) {
var startLatLng = new LatLng(this.#position.lat, this.#position.lng);
var endLatLng: LatLng;
if (contactData.detectionMethod === RWR) {
@@ -1015,6 +1033,10 @@ export class Unit extends CustomMarker {
if (getMap().hasLayer(this.#targetPositionPolyline))
this.#targetPositionPolyline.removeFrom(getMap());
}
#onZoom() {
this.#updateMarker();
}
}
export class AirUnit extends Unit {
@@ -1077,6 +1099,11 @@ export class GroundUnit extends Unit {
getCategory() {
return "GroundUnit";
}
getType() {
var blueprint = groundUnitDatabase.getByName(this.getName());
return blueprint?.type? blueprint.type: "";
}
}
export class NavyUnit extends Unit {
@@ -1106,6 +1133,11 @@ export class NavyUnit extends Unit {
getCategory() {
return "NavyUnit";
}
getType() {
var blueprint = navyUnitDatabase.getByName(this.getName());
return blueprint?.type? blueprint.type: "";
}
}
export class Weapon extends Unit {
@@ -1113,21 +1145,6 @@ export class Weapon extends Unit {
super(ID);
this.setSelectable(false);
}
getIconOptions() {
return {
showState: false,
showVvi: false,
showHotgroup: false,
showUnitIcon: true,
showShortLabel: false,
showFuel: false,
showAmmo: false,
showSummary: false,
showCallsign: false,
rotateToHeading: true
};
}
}
export class Missile extends Weapon {
@@ -1140,7 +1157,25 @@ export class Missile extends Weapon {
}
getMarkerCategory() {
return "missile";
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC))
return "missile";
else
return "aircraft";
}
getIconOptions() {
return {
showState: false,
showVvi: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
showHotgroup: false,
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
showShortLabel: false,
showFuel: false,
showAmmo: false,
showSummary: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
showCallsign: false,
rotateToHeading: this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC)
};
}
}
@@ -1154,6 +1189,24 @@ export class Bomb extends Weapon {
}
getMarkerCategory() {
return "bomb";
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC))
return "bomb";
else
return "aircraft";
}
getIconOptions() {
return {
showState: false,
showVvi: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
showHotgroup: false,
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
showShortLabel: false,
showFuel: false,
showAmmo: false,
showSummary: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
showCallsign: false,
rotateToHeading: this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC)
};
}
}

View File

@@ -1,7 +1,7 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager } from "..";
import { Unit } from "./unit";
import { cloneUnit, setLastUpdateTime, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server";
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";
@@ -15,11 +15,12 @@ import { navyUnitDatabase } from "./navyunitdatabase";
export class UnitsManager {
#units: { [ID: number]: Unit };
#copiedUnits: Unit[];
#copiedUnits: any[];
#selectionEventDisabled: boolean = false;
#pasteDisabled: boolean = false;
#hiddenTypes: string[] = [];
#commandMode: string = HIDE_ALL;
#requestDetectionUpdate: boolean = false;
constructor() {
this.#units = {};
@@ -34,6 +35,7 @@ export class UnitsManager {
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});
}
getSelectableAircraft() {
@@ -94,17 +96,21 @@ export class UnitsManager {
this.#units[ID]?.setData(dataExtractor);
}
for (let ID in this.#units) {
var unit = this.#units[ID];
if (!unit.belongsToCommandedCoalition())
unit.setDetectionMethods(this.getUnitDetectedMethods(unit));
if (this.#requestDetectionUpdate) {
for (let ID in this.#units) {
var unit = this.#units[ID];
if (!unit.belongsToCommandedCoalition())
unit.setDetectionMethods(this.getUnitDetectedMethods(unit));
}
this.#requestDetectionUpdate = false;
}
setLastUpdateTime(updateTime);
for (let ID in this.#units) {
this.#units[ID].drawLines();
if (this.#units[ID].getSelected())
this.#units[ID].drawLines();
};
return updateTime;
}
setHiddenType(key: string, value: boolean) {
@@ -200,36 +206,59 @@ export class UnitsManager {
}
getSelectedUnitsTypes() {
if (this.getSelectedUnits().length == 0)
const selectedUnits = this.getSelectedUnits();
if (selectedUnits.length == 0)
return [];
return this.getSelectedUnits().map((unit: Unit) => {
return unit.constructor.name
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) {
if (this.getSelectedUnits().length == 0)
const selectedUnits = this.getSelectedUnits();
if (selectedUnits.length == 0)
return undefined;
return this.getSelectedUnits().map((unit: Unit) => {
return selectedUnits.map((unit: Unit) => {
return variableGetter(unit);
})?.reduce((a: any, b: any) => {
return a == b ? a : undefined
return a === b ? a : undefined
});
};
getSelectedUnitsCoalition() {
if (this.getSelectedUnits().length == 0)
const selectedUnits = this.getSelectedUnits();
if (selectedUnits.length == 0)
return undefined;
return this.getSelectedUnits().map((unit: Unit) => {
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 });
@@ -239,7 +268,7 @@ export class UnitsManager {
if (mantainRelativePosition)
unitDestinations = this.selectedUnitsComputeGroupDestination(latlng, rotation);
else
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng });
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng; });
for (let idx in selectedUnits) {
const unit = selectedUnits[idx];
@@ -527,36 +556,66 @@ export class UnitsManager {
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
getUnitDetectedMethods(unit: Unit) {
var detectionMethods: number[] = [];
for (let idx in this.#units) {
if (this.#units[idx].getCoalition() !== "neutral" && this.#units[idx].getCoalition() != unit.getCoalition())
{
this.#units[idx].getContacts().forEach((contact: Contact) => {
if (this.#units[idx].getAlive() && contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod))
detectionMethods.push(contact.detectionMethod);
});
}
// 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()});
}
return detectionMethods;
const category = this.getSelectedUnitsTypes()[0];
this.spawnUnit(category, units, coalition, true);
}
/***********************************************/
copyUnits() {
this.#copiedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
this.#showActionMessage(this.#copiedUnits, `copied`);
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 && getUnitsManager().getCommandMode() == 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];
//getMap().addTemporaryMarker(getMap().getMouseCoordinates());
cloneUnit(unit.ID, getMap().getMouseCoordinates());
this.#showActionMessage(this.#copiedUnits, `pasted`);
avgLat += unit.position.lat / nUnits;
avgLng += unit.position.lng / nUnits;
}
this.#pasteDisabled = true;
window.setTimeout(() => this.#pasteDisabled = false, 250);
/* 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.spawnUnit(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 ${getUnitsManager().getCommandMode()} mode`);
@@ -596,7 +655,6 @@ export class UnitsManager {
var unit = this.#units[ID];
if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) {
var data: any = unit.getData();
data.category = unit.getCategory();
if (unit.getGroupName() in unitsToExport)
unitsToExport[unit.getGroupName()].push(data);
else
@@ -676,8 +734,11 @@ export class UnitsManager {
/***********************************************/
#onKeyUp(event: KeyboardEvent) {
if (!keyEventWasInInput(event) && event.key === "Delete" ) {
this.selectedUnitsDelete();
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));
}
}
@@ -708,7 +769,7 @@ export class UnitsManager {
document.dispatchEvent(new CustomEvent("unitsDeselection", { detail: this.getSelectedUnits() }));
}
#showActionMessage(units: Unit[], message: string) {
#showActionMessage(units: any[], message: string) {
if (units.length == 1)
getInfoPopup().setText(`${units[0].getUnitName()} ${message}`);
else if (units.length > 1)