Better unitmarker.ts integration. Added scheduler load divider. Rationalized units.ts and added interfaces (to be completed)

This commit is contained in:
Pax1601 2023-02-26 20:19:31 +01:00
parent 236523ee2b
commit c5a1b00cfd
22 changed files with 1773 additions and 2281 deletions

View File

@ -2,6 +2,9 @@
<html lang="en">
<head>
<title>Olympus UI Kit</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;600&display=swap" rel="stylesheet">
<link rel="stylesheet" type="text/css" href="stylesheets/olympus.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/uikit.css" />
</head>

6
client/src/@types/server.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
interface ServerData {
units: {[key: string]: UnitData},
bullseye: any, //TODO
airbases: any, //TODO
logs: any //TODO
}

52
client/src/@types/unit.d.ts vendored Normal file
View File

@ -0,0 +1,52 @@
interface FlightData {
latitude: number;
longitude: number;
altitude: number;
heading: number;
speed: number;
}
interface MissionData {
fuel: number;
flags: any;
ammo: any;
targets: any;
hasTask: boolean;
coalition: string;
}
interface FormationData {
formation: string;
isLeader: boolean;
isWingman: boolean;
leaderID: number;
wingmen: Unit[];
wingmenIDs: number[];
}
interface TaskData {
currentTask: string;
activePath: any;
targetSpeed: number;
targetAltitude: number;
}
interface OptionsData {
ROE: string;
reactionToThreat: string;
}
interface UnitData {
AI: boolean;
name: string;
unitName: string;
groupName: string;
alive: boolean;
category: string;
flightData: FlightData;
missionData: MissionData;
formationData: FormationData;
taskData: TaskData;
optionsData: OptionsData;
}

View File

@ -1,260 +0,0 @@
import * as L from 'leaflet'
import { getUnitsManager, setConnected } from '..';
import { ConvertDDToDMS } from '../other/utils';
/* Edit here to change server address */
var RESTaddress = "http://localhost:30000/restdemo";
export function getDataFromDCS(callback: CallableFunction) {
/* Request the updated unit data from the server */
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", RESTaddress, true);
xmlHttp.onload = function (e) {
var data = JSON.parse(xmlHttp.responseText);
callback(data);
setConnected(true);
};
xmlHttp.onerror = function () {
console.error("An error occurred during the XMLHttpRequest");
setConnected(false);
};
xmlHttp.send(null);
}
export function addDestination(ID: number, path: any) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => { };
var command = { "ID": ID, "path": path }
var data = { "setPath": command }
xhr.send(JSON.stringify(data));
}
export function spawnSmoke(color: string, latlng: L.LatLng) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Added " + color + " smoke at " + ConvertDDToDMS(latlng.lat, false) + " " + ConvertDDToDMS(latlng.lng, true));
}
};
var command = { "color": color, "location": latlng };
var data = { "smoke": command }
xhr.send(JSON.stringify(data));
}
export function spawnGroundUnit(type: string, latlng: L.LatLng, coalition: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Added " + coalition + " " + type + " at " + ConvertDDToDMS(latlng.lat, false) + " " + ConvertDDToDMS(latlng.lng, true));
}
};
var command = { "type": type, "location": latlng, "coalition": coalition };
var data = { "spawnGround": command }
xhr.send(JSON.stringify(data));
}
export function spawnAircraft(type: string, latlng: L.LatLng, coalition: string, payloadName: string | null = null, airbaseName: string | null = null) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Added " + coalition + " " + type + " at " + ConvertDDToDMS(latlng.lat, false) + " " + ConvertDDToDMS(latlng.lng, true));
}
};
var command = { "type": type, "location": latlng, "coalition": coalition, "payloadName": payloadName != null? payloadName: "", "airbaseName": airbaseName != null? airbaseName: ""};
var data = { "spawnAir": command }
xhr.send(JSON.stringify(data));
}
export function attackUnit(ID: number, targetID: number) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " attack " + getUnitsManager().getUnitByID(targetID).unitName);
}
};
var command = { "ID": ID, "targetID": targetID };
var data = { "attackUnit": command }
xhr.send(JSON.stringify(data));
}
export function cloneUnit(ID: number, latlng: L.LatLng) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned");
}
};
var command = { "ID": ID, "location": latlng };
var data = { "cloneUnit": command }
xhr.send(JSON.stringify(data));
}
export function deleteUnit(ID: number) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned");
}
};
var command = { "ID": ID};
var data = { "deleteUnit": command }
xhr.send(JSON.stringify(data));
}
export function landAt(ID: number, latlng: L.LatLng) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned");
}
};
var command = { "ID": ID, "location": latlng };
var data = { "landAt": command }
xhr.send(JSON.stringify(data));
}
export function changeSpeed(ID: number, speedChange: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "change": speedChange}
var data = {"changeSpeed": command}
xhr.send(JSON.stringify(data));
}
export function setSpeed(ID: number, speed: number) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "speed": speed}
var data = {"setSpeed": command}
xhr.send(JSON.stringify(data));
}
export function changeAltitude(ID: number, altitudeChange: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " altitude change request: " + altitudeChange);
}
};
var command = {"ID": ID, "change": altitudeChange}
var data = {"changeAltitude": command}
xhr.send(JSON.stringify(data));
}
export function setAltitude(ID: number, altitude: number) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "altitude": altitude}
var data = {"setAltitude": command}
xhr.send(JSON.stringify(data));
}
export function createFormation(ID: number, isLeader: boolean, wingmenIDs: number[]) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " created formation with: " + wingmenIDs);
}
};
var command = {"ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader}
var data = {"setLeader": command}
xhr.send(JSON.stringify(data));
}
export function setROE(ID: number, ROE: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "ROE": ROE}
var data = {"setROE": command}
xhr.send(JSON.stringify(data));
}
export function setReactionToThreat(ID: number, reactionToThreat: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "reactionToThreat": reactionToThreat}
var data = {"setReactionToThreat": command}
xhr.send(JSON.stringify(data));
}

View File

@ -1,5 +1,5 @@
import { Map } from "./map/map"
import { getDataFromDCS } from "./dcs/dcs"
import { getDataFromDCS } from "./server/server"
import { UnitsManager } from "./units/unitsmanager";
import { UnitInfoPanel } from "./panels/unitinfopanel";
import { ContextMenu } from "./controls/contextmenu";
@ -39,9 +39,6 @@ var aicHelpButton: Button;
var atc: ATC;
var atcToggleButton: Button;
var altitudeSlider: Slider;
var airspeedSlider: Slider;
var connected: boolean;
var activeCoalition: string;
@ -132,7 +129,7 @@ function requestUpdate() {
connectionStatusPanel.update(getConnected());
}
export function update(data: JSON) {
export function update(data: ServerData) {
unitsManager.update(data);
missionData.update(data);
logPanel.update(data);
@ -182,9 +179,4 @@ export function getConnected() {
return connected;
}
export function getUnitControlSliders() {
return {altitude: altitudeSlider, airspeed: airspeedSlider}
}
window.onload = setup;

View File

@ -1,9 +1,9 @@
import * as L from "leaflet"
import { getContextMenu, getUnitsManager, getActiveCoalition, getMouseInfoPanel } from "..";
import { spawnAircraft, spawnGroundUnit, spawnSmoke } from "../dcs/dcs";
import { spawnAircraft, spawnGroundUnit, spawnSmoke } from "../server/server";
import { bearing, distance, zeroAppend } from "../other/utils";
import { getAircraftLabelsByRole, getLoadoutsByName, getLoadoutNamesByRole, getAircraftNameByLabel } from "../units/aircraftdatabase";
import { unitTypes } from "../units/unitTypes";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { unitTypes } from "../units/unittypes";
import { BoxSelect } from "./boxselect";
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
@ -238,7 +238,7 @@ export class Map extends L.Map {
var selectedUnits = getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1)
{
selectedUnitPosition = new L.LatLng(selectedUnits[0].latitude, selectedUnits[0].longitude);
selectedUnitPosition = new L.LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude);
}
getMouseInfoPanel().update(<L.LatLng>e.latlng, this.#measurePoint, selectedUnitPosition);
@ -309,10 +309,10 @@ export class Map extends L.Map {
/* Show unit selection for air units */
#selectAircraft(e: SpawnEvent, role: string) {
this.hideContextMenu();
var options = getAircraftLabelsByRole(role);
var options = aircraftDatabase.getLabelsByRole(role);
this.showContextMenu(e, "Select aircraft", options, (label: string) => {
this.hideContextMenu();
var name = getAircraftNameByLabel(label);
var name = aircraftDatabase.getNameByLabel(label);
if (name != null)
this.#unitSelectPayload(e, name, role);
}, true);
@ -321,14 +321,14 @@ export class Map extends L.Map {
/* Show weapon selection for air units */
#unitSelectPayload(e: SpawnEvent, unitType: string, role: string) {
this.hideContextMenu();
var options = getLoadoutNamesByRole(unitType, role);
var options = aircraftDatabase.getLoadoutNamesByRole(unitType, role);
//options = payloadNames[unitType]
if (options != undefined && options.length > 0) {
options.sort();
this.showContextMenu({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout", options, (loadoutName: string) => {
this.hideContextMenu();
var loadout = getLoadoutsByName(unitType, loadoutName);
spawnAircraft(unitType, e.latlng, getActiveCoalition(), loadout.code, e.airbaseName);
var loadout = aircraftDatabase.getLoadoutsByName(unitType, loadoutName);
spawnAircraft(unitType, e.latlng, getActiveCoalition(), loadout != null? loadout.code: "", e.airbaseName);
}, true);
}
else {

View File

@ -27,7 +27,7 @@ export class MissionData
this.#airbasesMarkers = {};
}
update(data: any)
update(data: ServerData)
{
this.#bullseyes = data.bullseye;
this.#airbases = data.airbases;

View File

@ -147,7 +147,7 @@ export class UnitControlPanel extends Panel {
for (let unit of units)
{
this.#addUnitButton(unit, this.#selectedUnitsContainer);
if (unit.isLeader)
if (unit.getFormationData().isLeader)
for (let wingman of unit.getWingmen())
this.#addUnitButton(wingman, this.#selectedUnitsContainer);
}
@ -160,16 +160,16 @@ export class UnitControlPanel extends Panel {
/* Unit name (actually type, but DCS calls it name for some reason) */
var nameDiv = document.createElement("div");
nameDiv.classList.add("ol-rounded-container-small");
if (unit.name.length >= 7)
nameDiv.innerHTML = `${unit.name.substring(0, 4)} ...`;
if (unit.getData().name.length >= 7)
nameDiv.innerHTML = `${unit.getData().name.substring(0, 4)} ...`;
else
nameDiv.innerHTML = `${unit.name}`;
nameDiv.innerHTML = `${unit.getData().name}`;
/* Unit icon */
var icon = document.createElement("img");
if (unit.isLeader)
if (unit.getFormationData().isLeader)
icon.src = "images/icons/formation.png"
else if (unit.isWingman)
else if (unit.getFormationData().isWingman)
{
var wingmen = unit.getLeader()?.getWingmen();
if (wingmen && wingmen.lastIndexOf(unit) == wingmen.length - 1)
@ -181,7 +181,7 @@ export class UnitControlPanel extends Panel {
else
icon.src = "images/icons/singleton.png"
el.innerHTML = unit.unitName;
el.innerHTML = unit.getData().unitName;
el.prepend(nameDiv);
@ -195,12 +195,12 @@ export class UnitControlPanel extends Panel {
el.classList.add("not-selected")
/* Set background color */
el.classList.toggle("red", unit.coalitionID == 1);
icon.classList.toggle("red", unit.coalitionID == 1);
el.classList.toggle("blue", unit.coalitionID == 2);
icon.classList.toggle("blue", unit.coalitionID == 2);
el.classList.toggle("neutral", unit.coalitionID == 0);
icon.classList.toggle("neutral", unit.coalitionID == 0);
el.classList.toggle("red", unit.getMissionData().coalition === "red");
icon.classList.toggle("red", unit.getMissionData().coalition === "red");
el.classList.toggle("blue", unit.getMissionData().coalition === "blue");
icon.classList.toggle("blue", unit.getMissionData().coalition === "blue");
el.classList.toggle("neutral", unit.getMissionData().coalition === "neutral");
icon.classList.toggle("neutral", unit.getMissionData().coalition === "neutral");
el.addEventListener("click", () => getUnitsManager().selectUnit(unit.ID));
container.appendChild(el);
@ -275,14 +275,14 @@ export class UnitControlPanel extends Panel {
var leaderFound = false;
for (let unit of units)
{
if (unit.isLeader)
if (unit.getFormationData().isLeader)
{
if (leaderFound)
return false
else
leaderFound = true;
}
if (!unit.isLeader)
if (!unit.getFormationData().isLeader)
return false
}
return true
@ -291,7 +291,7 @@ export class UnitControlPanel extends Panel {
#checkUnitsAlreadyInFormation(units: Unit[])
{
for (let unit of units)
if (unit.isLeader)
if (unit.getFormationData().isLeader)
return true
return false
}
@ -301,10 +301,10 @@ export class UnitControlPanel extends Panel {
var airspeed = null;
for (let unit of units)
{
if (unit.targetSpeed != airspeed && airspeed != null)
if (unit.getTaskData().targetSpeed != airspeed && airspeed != null)
return null
else
airspeed = unit.targetSpeed;
airspeed = unit.getTaskData().targetSpeed;
}
return airspeed;
}
@ -314,10 +314,10 @@ export class UnitControlPanel extends Panel {
var altitude = null;
for (let unit of units)
{
if (unit.targetAltitude != altitude && altitude != null)
if (unit.getTaskData().targetAltitude != altitude && altitude != null)
return null
else
altitude = unit.targetAltitude;
altitude = unit.getTaskData().targetAltitude;
}
return altitude;
}
@ -327,10 +327,10 @@ export class UnitControlPanel extends Panel {
var ROE = null;
for (let unit of units)
{
if (unit.ROE !== ROE && ROE != null)
if (unit.getOptionsData().ROE !== ROE && ROE != null)
return null
else
ROE = unit.ROE;
ROE = unit.getOptionsData().ROE;
}
return ROE;
}
@ -340,10 +340,10 @@ export class UnitControlPanel extends Panel {
var reactionToThreat = null;
for (let unit of units)
{
if (unit.reactionToThreat !== reactionToThreat && reactionToThreat != null)
if (unit.getOptionsData().reactionToThreat !== reactionToThreat && reactionToThreat != null)
return null
else
reactionToThreat = unit.reactionToThreat;
reactionToThreat = unit.getOptionsData().reactionToThreat;
}
return reactionToThreat;
}

View File

@ -36,35 +36,35 @@ export class UnitInfoPanel extends Panel {
update(unit: Unit) {
if (this.getElement() != null) {
/* Set the unit info */
this.#unitName.innerHTML = unit.unitName;
this.#groupName.innerHTML = unit.groupName;
this.#name.innerHTML = unit.name;
this.#heading.innerHTML = String(Math.floor(rad2deg(unit.heading)) + " °");
this.#altitude.innerHTML = String(Math.floor(unit.altitude / 0.3048) + " ft");
this.#groundSpeed.innerHTML = String(Math.floor(unit.speed * 1.94384) + " kts");
this.#fuel.innerHTML = String(unit.fuel + "%");
this.#latitude.innerHTML = ConvertDDToDMS(unit.latitude, false);
this.#longitude.innerHTML = ConvertDDToDMS(unit.longitude, true);
this.#task.innerHTML = unit.currentTask !== ""? unit.currentTask: "No task";
this.#unitName.innerHTML = unit.getData().unitName;
this.#groupName.innerHTML = unit.getData().groupName;
this.#name.innerHTML = unit.getData().name;
this.#heading.innerHTML = String(Math.floor(rad2deg(unit.getFlightData().heading)) + " °");
this.#altitude.innerHTML = String(Math.floor(unit.getFlightData().altitude / 0.3048) + " ft");
this.#groundSpeed.innerHTML = String(Math.floor(unit.getFlightData().speed * 1.94384) + " kts");
this.#fuel.innerHTML = String(unit.getMissionData().fuel + "%");
this.#latitude.innerHTML = ConvertDDToDMS(unit.getFlightData().latitude, false);
this.#longitude.innerHTML = ConvertDDToDMS(unit.getFlightData().longitude, true);
this.#task.innerHTML = unit.getTaskData().currentTask !== ""? unit.getTaskData().currentTask: "No task";
/* Set the class of the task container */
this.#task.classList.toggle("red", unit.coalitionID == 1);
this.#task.classList.toggle("blue", unit.coalitionID == 2);
this.#task.classList.toggle("neutral", unit.coalitionID == 0);
this.#task.classList.toggle("red", unit.getMissionData().coalition === "red");
this.#task.classList.toggle("blue", unit.getMissionData().coalition === "blue");
this.#task.classList.toggle("neutral", unit.getMissionData().coalition === "neutral");
/* Add the loadout elements */
var els = this.getElement().getElementsByClassName("js-loadout-element");
while (els.length > 0)
this.#loadoutContainer.removeChild(els[0]);
for (let index in unit.ammo)
for (let index in unit.getMissionData().ammo)
this.#addLoadoutElement(unit, index);
}
}
#addLoadoutElement(unit: Unit, index: string)
{
var ammo = unit.ammo[index];
var ammo = unit.getMissionData().ammo[index];
var displayName = ammo.desc.displayName;
var amount = ammo.count;
var el = document.createElement("div")

View File

@ -49,29 +49,29 @@ export class VisibilityControlPanel {
var uncontrolledVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#uncontrolled-visibility");
var uncontrolledVisibility = !uncontrolledVisibilityCheckbox.checked;
var airVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#air-visibility");
if (airVisibilityCheckbox.checked)
AirUnit.setVisibility({human: "full", ai: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
else
AirUnit.setVisibility({human: "hidden", ai: "hidden", uncontrolled: "hidden", dead: "hidden"});
var groundVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#ground-visibility");
if (groundVisibilityCheckbox.checked)
GroundUnit.setVisibility({human: activeVisibility, ai: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
else
GroundUnit.setVisibility({human: "hidden", ai: "hidden", uncontrolled: "hidden", dead: "hidden"});
var navyVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#navy-visibility");
if (navyVisibilityCheckbox.checked)
NavyUnit.setVisibility({human: activeVisibility, ai: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
else
NavyUnit.setVisibility({human: "hidden", ai: "hidden", uncontrolled: "hidden", dead: "hidden"});
var weaponVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#weapon-visibility");
if (weaponVisibilityCheckbox.checked)
Weapon.setVisibility({human: activeVisibility, ai: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
else
Weapon.setVisibility({human: "hidden", ai: "hidden", uncontrolled: "hidden", dead: "hidden"});
//var airVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#air-visibility");
//if (airVisibilityCheckbox.checked)
// AirUnit.setVisibility({human: "full", AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
//else
// AirUnit.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
//
//var groundVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#ground-visibility");
//if (groundVisibilityCheckbox.checked)
// GroundUnit.setVisibility({human: activeVisibility, AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
//else
// GroundUnit.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
//
//var navyVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#navy-visibility");
//if (navyVisibilityCheckbox.checked)
// NavyUnit.setVisibility({human: activeVisibility, AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
//else
// NavyUnit.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
//
//var weaponVisibilityCheckbox = <HTMLInputElement> this.#element.querySelector("#weapon-visibility");
//if (weaponVisibilityCheckbox.checked)
// Weapon.setVisibility({human: activeVisibility, AI: activeVisibility, uncontrolled: uncontrolledVisibility? activeVisibility: "hidden", dead: "hidden"});
//else
// Weapon.setVisibility({human: "hidden", AI: "hidden", uncontrolled: "hidden", dead: "hidden"});
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,14 +1,8 @@
import { Marker, LatLng, Polyline, Icon } from 'leaflet';
import { getMap, getUnitsManager} from '..';
import { getMap, getUnitsManager } from '..';
import { UnitMarker, MarkerOptions, AircraftMarker, HelicopterMarker, GroundUnitMarker, NavyUnitMarker, WeaponMarker, MissileMarker, BombMarker } from './unitmarker';
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, landAt, setAltitude, setReactionToThreat, setROE, setSpeed } from '../dcs/dcs';
interface VisibilityOptions {
dead: string;
ai: string;
uncontrolled: string;
human: string;
}
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, landAt, setAltitude, setReactionToThreat, setROE, setSpeed } from '../server/server';
import { aircraftDatabase } from './aircraftdatabase';
var pathIcon = new Icon({
iconUrl: 'images/marker-icon.png',
@ -17,60 +11,37 @@ var pathIcon = new Icon({
});
export class Unit {
ID: number = -1;
AI: boolean = false;
formation: string = "";
name: string = "";
unitName: string = "";
groupName: string = "";
latitude: number = 0;
longitude: number = 0;
altitude: number = 0;
heading: number = 0;
speed: number = 0;
coalitionID: number = -1;
alive: boolean = true;
currentTask: string = "";
fuel: number = 0;
type: any = null;
flags: any = null;
activePath: any = null;
ammo: any = null;
targets: any = null;
hasTask: boolean = false;
isLeader: boolean = false;
isWingman: boolean = false;
leaderID: number = 0;
wingmen: Unit[] = [];
wingmenIDs: number[] = [];
targetSpeed: number = 0;
targetAltitude: number = 0;
ROE: string = "";
reactionToThreat: string = "";
ID: number;
#data: UnitData;
#marker: UnitMarker;
#selectable: boolean;
#selected: boolean = false;
#preventClick: boolean = false;
#pathMarkers: Marker[] = [];
#pathPolyline: Polyline;
#targetsPolylines: Polyline[];
#marker: UnitMarker;
#timer: number = 0;
#forceUpdate: boolean = false;
static getConstructor(name: string) {
if (name === "GroundUnit") return GroundUnit;
if (name === "Aircraft") return Aircraft;
if (name === "Helicopter") return Helicopter;
if (name === "Missile") return Missile;
if (name === "Bomb") return Bomb;
if (name === "NavyUnit") return NavyUnit;
static getConstructor(type: string) {
if (type === "GroundUnit") return GroundUnit;
if (type === "Aircraft") return Aircraft;
if (type === "Helicopter") return Helicopter;
if (type === "Missile") return Missile;
if (type === "Bomb") return Bomb;
if (type === "NavyUnit") return NavyUnit;
}
constructor(ID: number, marker: UnitMarker) {
constructor(ID: number, marker: UnitMarker, data: UnitData) {
this.ID = ID;
this.#selectable = true;
this.#data = data;
/* The marker is set by the inherited class */
this.#marker = marker;
@ -83,42 +54,34 @@ export class Unit {
this.#targetsPolylines = [];
}
update(response: any) {
update(response: UnitData) {
var updateMarker = false;
if (this.latitude != response['latitude'] || this.longitude != response['longitude'] || this.alive != response['alive'] || this.#forceUpdate)
if (this.#data.flightData.latitude != response.flightData.latitude || this.#data.flightData.longitude != response.flightData.longitude || this.#data.alive != response.alive || this.#forceUpdate)
updateMarker = true;
for (let entry in response) {
// @ts-ignore TODO handle better
this[entry] = response[entry];
}
// TODO handle better
if (response['activePath'] == undefined)
this.activePath = null
this.#data = response;
/* Dead units can't be selected */
this.setSelected(this.getSelected() && this.alive)
this.setSelected(this.getSelected() && this.#data.alive)
if (updateMarker)
this.#updateMarker();
this.#clearTargets();
if (this.getSelected() && this.activePath != null)
{
if (this.getSelected()) {
this.#drawPath();
this.#drawTargets();
}
else
this.#clearPath();
this.#clearPath();
}
setSelected(selected: boolean) {
/* Only alive units can be selected. Some units are not selectable (weapons) */
if ((this.alive || !selected) && this.#selectable && this.#selected != selected) {
if ((this.#data.alive || !selected) && this.#selectable && this.#selected != selected) {
this.#selected = selected;
this.#marker.setSelected(selected);
document.dispatchEvent(new CustomEvent("unitSelection", {detail: this}));
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
}
}
@ -136,8 +99,8 @@ export class Unit {
addDestination(latlng: L.LatLng) {
var path: any = {};
if (this.activePath != null) {
path = this.activePath;
if (this.#data.taskData.activePath != null) {
path = this.#data.taskData.activePath;
path[(Object.keys(path).length + 1).toString()] = latlng;
}
else {
@ -147,7 +110,7 @@ export class Unit {
}
clearDestinations() {
this.activePath = null;
this.#data.taskData.activePath = null;
}
getHidden() {
@ -155,7 +118,7 @@ export class Unit {
}
getLeader() {
return getUnitsManager().getUnitByID(this.leaderID);
return getUnitsManager().getUnitByID(this.#data.formationData.leaderID);
}
getFormation() {
@ -164,10 +127,8 @@ export class Unit {
getWingmen() {
var wingmen: Unit[] = [];
if (this.wingmenIDs != null)
{
for (let ID of this.wingmenIDs)
{
if (this.#data.formationData.wingmenIDs != null) {
for (let ID of this.#data.formationData.wingmenIDs) {
var unit = getUnitsManager().getUnitByID(ID)
if (unit)
wingmen.push(unit);
@ -176,11 +137,86 @@ export class Unit {
return wingmen;
}
forceUpdate()
{
forceUpdate() {
this.#forceUpdate = true;
}
getData() {
return this.#data;
}
getFlightData() {
return this.#data.flightData;
}
getTaskData() {
return this.#data.taskData;
}
getMissionData() {
return this.#data.missionData;
}
getFormationData() {
return this.#data.formationData;
}
getOptionsData() {
return this.#data.optionsData;
}
attackUnit(targetID: number) {
/* Call DCS attackUnit function */
if (this.ID != targetID) {
attackUnit(this.ID, targetID);
}
else {
// TODO: show a message
}
}
landAt(latlng: LatLng) {
landAt(this.ID, latlng);
}
changeSpeed(speedChange: string) {
changeSpeed(this.ID, speedChange);
}
changeAltitude(altitudeChange: string) {
changeAltitude(this.ID, altitudeChange);
}
setSpeed(speed: number) {
setSpeed(this.ID, speed);
}
setAltitude(altitude: number) {
setAltitude(this.ID, altitude);
}
setROE(ROE: string) {
setROE(this.ID, ROE);
}
setReactionToThreat(reactionToThreat: string) {
setReactionToThreat(this.ID, reactionToThreat);
}
delete() {
deleteUnit(this.ID);
}
/*
setformation(formation)
{
}
*/
setLeader(isLeader: boolean, wingmenIDs: number[] = []) {
setLeader(this.ID, isLeader, wingmenIDs);
}
#onClick(e: any) {
this.#timer = setTimeout(() => {
if (!this.#preventClick) {
@ -206,7 +242,7 @@ export class Unit {
'Follow'
]
getMap().showContextMenu(e.originalEvent, "Action: " + this.unitName, options, (action: string) => this.#executeAction(action));
getMap().showContextMenu(e.originalEvent, "Action: " + this.#data.unitName, options, (action: string) => this.#executeAction(action));
}
#executeAction(action: string) {
@ -225,14 +261,13 @@ export class Unit {
if (getMap().hasLayer(this.#marker) && this.getHidden()) {
getMap().removeLayer(this.#marker);
}
else
{
this.#marker.setLatLng(new LatLng(this.latitude, this.longitude));
else {
this.#marker.setLatLng(new LatLng(this.#data.flightData.latitude, this.#data.flightData.longitude));
this.#marker.draw({
heading: this.heading,
speed: this.speed,
altitude: this.altitude,
alive: this.alive
heading: this.#data.flightData.heading,
speed: this.#data.flightData.speed,
altitude: this.#data.flightData.altitude,
alive: this.#data.alive
});
}
@ -240,25 +275,25 @@ export class Unit {
}
#drawPath() {
if (this.activePath != null) {
if (this.#data.taskData.activePath != null) {
var points = [];
points.push(new LatLng(this.latitude, this.longitude));
points.push(new LatLng(this.#data.flightData.latitude, this.#data.flightData.longitude));
/* Add markers if missing */
while (this.#pathMarkers.length < Object.keys(this.activePath).length) {
while (this.#pathMarkers.length < Object.keys(this.#data.taskData.activePath).length) {
var marker = new Marker([0, 0], { icon: pathIcon }).addTo(getMap());
this.#pathMarkers.push(marker);
}
/* Remove markers if too many */
while (this.#pathMarkers.length > Object.keys(this.activePath).length) {
while (this.#pathMarkers.length > Object.keys(this.#data.taskData.activePath).length) {
getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]);
this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1)
}
/* Update the position of the existing markers (to avoid creating markers uselessly) */
for (let WP in this.activePath) {
var destination = this.activePath[WP];
for (let WP in this.#data.taskData.activePath) {
var destination = this.#data.taskData.activePath[WP];
this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]);
points.push(new LatLng(destination.lat, destination.lng));
this.#pathPolyline.setLatLngs(points);
@ -274,36 +309,25 @@ export class Unit {
this.#pathPolyline.setLatLngs([]);
}
#drawTargets()
{
for (let typeIndex in this.targets)
{
for (let index in this.targets[typeIndex])
{
var targetData = this.targets[typeIndex][index];
#drawTargets() {
for (let typeIndex in this.getMissionData().targets) {
for (let index in this.getMissionData().targets[typeIndex]) {
var targetData = this.getMissionData().targets[typeIndex][index];
var target = getUnitsManager().getUnitByID(targetData.object["id_"])
if (target != null){
var startLatLng = new LatLng(this.latitude, this.longitude)
var endLatLng = new LatLng(target.latitude, target.longitude)
if (target != null) {
var startLatLng = new LatLng(this.#data.flightData.latitude, this.#data.flightData.longitude)
var endLatLng = new LatLng(target.getFlightData().latitude, target.getFlightData().longitude)
var color;
if (typeIndex === "radar")
{
color = "#FFFF00";
}
else if (typeIndex === "visual")
{
color = "#FF00FF";
}
else if (typeIndex === "rwr")
{
color = "#00FF00";
}
else
{
color = "#FFFFFF";
}
var targetPolyline = new Polyline([startLatLng, endLatLng], {color: color, weight: 3, opacity: 1, smoothFactor: 1});
var targetPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 1, smoothFactor: 1 });
targetPolyline.addTo(getMap());
this.#targetsPolylines.push(targetPolyline)
}
@ -311,242 +335,102 @@ export class Unit {
}
}
#clearTargets()
{
for (let index in this.#targetsPolylines)
{
#clearTargets() {
for (let index in this.#targetsPolylines) {
getMap().removeLayer(this.#targetsPolylines[index])
}
}
attackUnit(targetID: number) {
/* Call DCS attackUnit function */
if (this.ID != targetID) {
attackUnit(this.ID, targetID);
}
else {
// TODO: show a message
}
}
landAt(latlng: LatLng)
{
landAt(this.ID, latlng);
}
changeSpeed(speedChange: string)
{
changeSpeed(this.ID, speedChange);
}
changeAltitude(altitudeChange: string)
{
changeAltitude(this.ID, altitudeChange);
}
setSpeed(speed: number)
{
setSpeed(this.ID, speed);
}
setAltitude(altitude: number)
{
setAltitude(this.ID, altitude);
}
setROE(ROE: string)
{
setROE(this.ID, ROE);
}
setReactionToThreat(reactionToThreat: string)
{
setReactionToThreat(this.ID, reactionToThreat);
}
delete()
{
deleteUnit(this.ID);
}
/*
setformation(formation)
{
// TODO move in dedicated file
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log(this.unitName + " formation change: " + formation);
}
};
var command = {"ID": this.ID, "formation": formation}
var data = {"setFormation": command}
xhr.send(JSON.stringify(data));
}
*/
setLeader(isLeader: boolean, wingmenIDs: number[] = [])
{
setLeader(this.ID, isLeader, wingmenIDs);
}
}
export class AirUnit extends Unit {
static visibility: VisibilityOptions = {dead: "hidden", ai: "partial", uncontrolled: "partial", human: "full"}
static setVisibility(visibility: VisibilityOptions)
{
getUnitsManager().forceUpdate();
AirUnit.visibility = visibility;
}
static getVisibility()
{
return AirUnit.visibility;
}
getHidden() {
if (this.alive)
{
if (this.flags.user)
return AirUnit.getVisibility().human === "hidden"
if (this.AI)
return AirUnit.getVisibility().ai === "hidden"
else
return AirUnit.getVisibility().uncontrolled === "hidden"
}
else
{
return AirUnit.getVisibility().dead === "hidden"
}
return false;
}
}
export class Aircraft extends AirUnit {
constructor(ID: number, options: MarkerOptions) {
var marker = new AircraftMarker(options);
super(ID, marker);
constructor(ID: number, data: UnitData) {
var marker = new AircraftMarker({
AI: data.AI,
unitName: data.unitName,
name: aircraftDatabase.getShortLabelByName(data.name),
human: data.missionData.flags.human,
coalition: data.missionData.coalition});
super(ID, marker, data);
}
}
export class Helicopter extends AirUnit {
constructor(ID: number, options: MarkerOptions) {
var marker = new HelicopterMarker(options);
super(ID, marker);
constructor(ID: number, data: UnitData) {
var marker = new HelicopterMarker({
AI: data.AI,
unitName: data.unitName,
name: "H",
human: data.missionData.flags.human,
coalition: data.missionData.coalition});
super(ID, marker, data);
}
}
export class GroundUnit extends Unit {
static visibility: VisibilityOptions = {dead: "hidden", ai: "partial", uncontrolled: "partial", human: "partial"}
static setVisibility(visibility: VisibilityOptions)
{
getUnitsManager().forceUpdate();
GroundUnit.visibility = visibility;
}
static getVisibility()
{
return GroundUnit.visibility;
}
constructor(ID: number, options: MarkerOptions) {
var marker = new GroundUnitMarker(options);
super(ID, marker);
constructor(ID: number, data: UnitData) {
var marker = new GroundUnitMarker({
AI: data.AI,
unitName: data.unitName,
name: "G",
human: data.missionData.flags.human,
coalition: data.missionData.coalition});
super(ID, marker, data);
}
getHidden() {
if (this.alive)
{
if (this.flags.user)
return GroundUnit.getVisibility().human === "hidden"
if (this.AI)
return GroundUnit.getVisibility().ai === "hidden"
else
return GroundUnit.getVisibility().uncontrolled === "hidden"
}
else
{
return GroundUnit.getVisibility().dead === "hidden"
}
return false;
}
}
export class NavyUnit extends Unit {
static visibility: VisibilityOptions = {dead: "hidden", ai: "partial", uncontrolled: "partial", human: "partial"}
static setVisibility(visibility: VisibilityOptions)
{
getUnitsManager().forceUpdate();
NavyUnit.visibility = visibility;
}
static getVisibility()
{
return NavyUnit.visibility;
}
constructor(ID: number, options: MarkerOptions) {
var marker = new NavyUnitMarker(options);
super(ID, marker);
constructor(ID: number, data: UnitData) {
var marker = new NavyUnitMarker({
AI: data.AI,
unitName: data.unitName,
name: "N",
human: data.missionData.flags.human,
coalition: data.missionData.coalition});
super(ID, marker, data);
}
getHidden() {
if (this.alive)
{
if (this.AI)
return NavyUnit.getVisibility().ai === "hidden"
else
return NavyUnit.getVisibility().uncontrolled === "hidden"
}
else
{
return NavyUnit.getVisibility().dead === "hidden"
}
return false;
}
}
export class Weapon extends Unit {
static visibility: VisibilityOptions = {dead: "hidden", ai: "partial", uncontrolled: "partial", human: "partial"}
static setVisibility(visibility: VisibilityOptions)
{
getUnitsManager().forceUpdate();
Weapon.visibility = visibility;
}
static getVisibility()
{
return Weapon.visibility;
}
constructor(ID: number, marker: UnitMarker)
{
super(ID, marker);
constructor(ID: number, marker: UnitMarker, data: UnitData) {
super(ID, marker, data);
this.setSelectable(false);
}
getHidden() {
if (this.alive)
return Weapon.getVisibility().uncontrolled === "hidden"
else
return true;
}
}
export class Missile extends Weapon {
constructor(ID: number, options: MarkerOptions) {
var marker = new MissileMarker(options);
super(ID, marker);
constructor(ID: number, data: UnitData) {
var marker = new MissileMarker({
AI: data.AI,
unitName: data.unitName,
name: "M",
human: data.missionData.flags.human,
coalition: data.missionData.coalition});
super(ID, marker, data);
}
}
export class Bomb extends Weapon {
constructor(ID: number, options: MarkerOptions) {
var marker = new BombMarker(options);
super(ID, marker);
constructor(ID: number, data: UnitData) {
var marker = new BombMarker({
AI: data.AI,
unitName: data.unitName,
name: "B",
human: data.missionData.flags.human,
coalition: data.missionData.coalition});
super(ID, marker, data);
}
}

View File

@ -0,0 +1,92 @@
export interface LoadoutItemBlueprint {
name: string;
quantity: number;
}
export interface LoadoutBlueprint {
fuel: number;
items: LoadoutItemBlueprint[];
roles: string[];
code: string;
name: string;
}
export interface UnitBlueprint {
name: string;
label: string;
shortLabel: string;
loadouts: LoadoutBlueprint[];
}
export class UnitDatabase {
units: {[key: string]: UnitBlueprint} = {};
constructor()
{
}
getLabelsByRole(role: string)
{
var units = [];
for (let unit in this.units)
{
for (let loadout of this.units[unit].loadouts)
{
if (loadout.roles.includes(role))
{
units.push(this.units[unit].label)
break;
}
}
}
return units;
}
getLoadoutNamesByRole(unit: string, role: string)
{
var loadouts = [];
for (let loadout of this.units[unit].loadouts)
{
if (loadout.roles.includes(role) || loadout.roles.includes(""))
{
loadouts.push(loadout.name)
}
}
return loadouts;
}
getLoadoutsByName(unit: string, loadoutName: string)
{
for (let loadout of this.units[unit].loadouts)
{
if (loadout.name === loadoutName)
{
return loadout;
}
}
return null;
}
getNameByLabel(label: string)
{
for (let name in this.units)
{
if (this.units[name].label === label)
{
return name;
}
}
return null;
}
getLabelByName(name: string)
{
return this.units[name] === undefined? name: this.units[name].label;
}
getShortLabelByName(name: string)
{
return this.units[name] === undefined? name: this.units[name].shortLabel;
}
}

View File

@ -1,58 +1,43 @@
import * as L from 'leaflet'
import { getMap } from '..'
import { rad2deg } from '../other/utils'
import { getAircrafImage, getAircraftLabelByName } from './aircraftdatabase'
import { AirUnit, GroundUnit, NavyUnit, Weapon } from './unit'
export interface MarkerOptions {
unitName: string
name: string
human: boolean
coalitionID: number
type: any
unitName: string,
name: string,
human: boolean,
coalition: string,
AI: boolean
}
export interface MarkerData {
heading: number
speed: number
altitude: number
heading: number,
speed: number,
altitude: number,
alive: boolean
}
export class UnitMarker extends L.Marker {
#unitName: string
#name: string
#human: boolean
#AI: boolean
#alive: boolean = true
#options: MarkerOptions;
#data: MarkerData;
#selected: boolean = false
constructor(options: MarkerOptions) {
super(new L.LatLng(0, 0), { riseOnHover: true });
this.#unitName = options.unitName;
this.#name = getAircraftLabelByName(options.name);
this.#human = options.human;
this.#AI = options.AI;
var coalition = "";
if (options.coalitionID == 1)
coalition = "red"
else if (options.coalitionID == 2)
coalition = "blue"
else
coalition = "neutral"
this.#options = options;
this.#data = {heading: 0, speed: 0, altitude: 0, alive: true};
var icon = new L.DivIcon({
html: `<div class="unit"
data-coalition=${coalition}
data-pilot=${this.#human? "human": "ai"}>
data-coalition=${this.#options.coalition}
data-pilot=${this.#options.human? "human": "ai"}>
<div class="unit-spotlight">
<div class="unit-selected-border">
<div class="unit-vvi">
<div class="unit-vvi-heading"></div>
</div>
<div class="unit-id">${this.#name}</div>
<div class="unit-id">${this.#options.name}</div>
</div>
</div>
<div class="unit-hotgroup">
@ -68,7 +53,7 @@ export class UnitMarker extends L.Marker {
<div data-ammo-type="other"></div>
</div>
<div class="unit-summary">
<div class="unit-callsign">${this.#unitName}</div>
<div class="unit-callsign">${this.#options.unitName}</div>
<div class="unit-heading"></div>
<div class="unit-altitude"></div>
</div>
@ -87,14 +72,14 @@ export class UnitMarker extends L.Marker {
}
draw(data: MarkerData) {
this.#alive = data.alive;
this.#data;
var element = this.getElement();
if (element != null)
{
element.querySelector(".unit")?.setAttribute("data-is-selected", String(this.getSelected()));
element.querySelector(".unit-vvi-heading")?.setAttribute("style",`transform: rotate(${rad2deg(data.heading)}deg); width: ${data.speed / 5}px`);
element.querySelector(".unit-vvi-heading")?.setAttribute("style",`transform: rotate(${rad2deg(data.heading)}deg); width: ${15 + data.speed / 5}px`);
element.querySelector(".unit")?.setAttribute("data-fuel-level", "20");
element.querySelector(".unit")?.setAttribute("data-has-fox-1", "true");
var unitHeadingDiv = element.querySelector(".unit-heading");
if (unitHeadingDiv != null)
@ -102,7 +87,7 @@ export class UnitMarker extends L.Marker {
var unitAltitudeDiv = element.querySelector(".unit-altitude");
if (unitAltitudeDiv != null)
unitAltitudeDiv.innerHTML = String(Math.floor(data.altitude / 1000));
unitAltitudeDiv.innerHTML = String(Math.floor(data.altitude / 0.3048 / 1000));
}
var pos = getMap().latLngToLayerPoint(this.getLatLng()).round();
this.setZIndexOffset(Math.floor(data.altitude) - pos.y);
@ -110,9 +95,7 @@ export class UnitMarker extends L.Marker {
setSelected(selected: boolean) {
this.#selected = selected;
this.getElement()?.querySelector("#icon")?.classList.remove("ol-unit-marker-hovered");
this.getElement()?.querySelector("#ring")?.classList.toggle("ol-unit-marker-selected", selected);
this.getElement()?.querySelector("#background")?.classList.toggle("ol-unit-marker-selected", selected);
this.getElement()?.querySelector(".unit")?.setAttribute("data-is-selected", String(this.getSelected()));
}
getSelected() {
@ -120,141 +103,46 @@ export class UnitMarker extends L.Marker {
}
setHovered(hovered: boolean) {
this.getElement()?.querySelector("#icon")?.classList.toggle("ol-unit-marker-hovered", hovered && this.#alive);
this.getElement()?.querySelector("#icon")?.classList.toggle("ol-unit-marker-hovered", hovered && this.#data.alive);
}
getName() {
return this.#name;
getData() {
return this.#data;
}
getHuman() {
return this.#human;
}
getAI() {
return this.#AI;
}
getAlive() {
return this.#alive;
}
getVisibility() {
return "full";
}
getUnitImage() {
return new Image().src = "images/units/unit.png"
getOptions() {
return this.#options;
}
}
export class AirUnitMarker extends UnitMarker {
getVisibility() {
if (this.getAlive())
{
if (this.getSelected())
return "full";
else if (this.getHuman())
return AirUnit.getVisibility().human;
else if (this.getAI())
return AirUnit.getVisibility().ai;
else
return AirUnit.getVisibility().uncontrolled;
}
else
return "minimal";
}
}
export class AircraftMarker extends AirUnitMarker {
getUnitImage()
{
return new Image().src = "images/units/" + getAircrafImage(this.getName());
}
}
export class HelicopterMarker extends AirUnitMarker {
getUnitImage()
{
return new Image().src = "images/units/airUnit.png"
}
}
export class GroundUnitMarker extends UnitMarker {
/* Are user driven units recognized as human? */
getVisibility() {
if (this.getAlive())
{
if (this.getSelected())
return "full";
else if (this.getHuman())
return GroundUnit.getVisibility().human;
else if (this.getAI())
return GroundUnit.getVisibility().ai;
else
return GroundUnit.getVisibility().uncontrolled;
}
else
return "minimal";
}
getUnitImage()
{
return new Image().src = "images/units/groundUnit.png"
}
}
export class NavyUnitMarker extends UnitMarker {
getVisibility() {
if (this.getAlive())
{
if (this.getSelected())
return "full";
else if (this.getHuman())
return NavyUnit.getVisibility().human;
else if (this.getAI())
return NavyUnit.getVisibility().ai;
else
return NavyUnit.getVisibility().uncontrolled;
}
else
return "minimal";
}
getUnitImage()
{
return new Image().src = "images/units/navyUnit.png"
}
}
export class WeaponMarker extends UnitMarker {
getVisibility() {
if (this.getAlive())
{
if (this.getSelected())
return "full";
else if (this.getHuman())
return Weapon.getVisibility().human;
else if (this.getAI())
return Weapon.getVisibility().ai;
else
return Weapon.getVisibility().uncontrolled;
}
else
return "minimal";
}
}
export class BombMarker extends WeaponMarker {
getUnitImage()
{
return new Image().src = "images/units/bomb.png"
}
}
export class MissileMarker extends WeaponMarker {
getUnitImage()
{
return new Image().src = "images/units/missile.png"
}
}

View File

@ -1,7 +1,7 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getMap, getUnitControlPanel, getUnitInfoPanel } from "..";
import { Unit, GroundUnit } from "./unit";
import { cloneUnit } from "../dcs/dcs";
import { cloneUnit } from "../server/server";
export class UnitsManager {
#units: { [ID: number]: Unit };
@ -40,19 +40,11 @@ export class UnitsManager {
return this.#units;
}
addUnit(ID: number, data: any) {
addUnit(ID: number, data: UnitData) {
/* The name of the unit category is exactly the same as the constructor name */
var constructor = Unit.getConstructor(data.category);
if (constructor != undefined) {
var options = {
unitName: data.unitName,
name: data.name,
human: data.flags.Human,
coalitionID: data.coalitionID,
type: data.type,
AI: data.AI
}
this.#units[ID] = new constructor(ID, options);
this.#units[ID] = new constructor(ID, data);
}
}
@ -80,13 +72,13 @@ export class UnitsManager {
this.#units[ID]?.setSelected(true);
}
update(data: any) {
for (let ID in data["units"]) {
update(data: ServerData) {
for (let ID in data.units) {
/* Create the unit if missing from the local array, then update the data. Drawing is handled by leaflet. */
if (!(ID in this.#units)) {
this.addUnit(parseInt(ID), data["units"][ID]);
this.addUnit(parseInt(ID), data.units[ID]);
}
this.#units[parseInt(ID)].update(data["units"][ID]);
this.#units[parseInt(ID)].update(data.units[ID]);
}
/* Update the unit info panel */
@ -124,7 +116,7 @@ export class UnitsManager {
{
if (this.#units[ID].getHidden() == false)
{
var latlng = new LatLng(this.#units[ID].latitude, this.#units[ID].longitude);
var latlng = new LatLng(this.#units[ID].getFlightData().latitude, this.#units[ID].getFlightData().longitude);
if (bounds.contains(latlng))
{
this.#units[ID].setSelected(true);
@ -148,9 +140,9 @@ export class UnitsManager {
for (let idx in this.getSelectedUnits())
{
var unit = this.getSelectedUnits()[idx];
if (unit.isLeader)
if (unit.getFormationData().isLeader)
leaders.push(unit);
else if (unit.isWingman)
else if (unit.getFormationData().isWingman)
{
var leader = unit.getLeader();
if (leader && !leaders.includes(leader))
@ -165,7 +157,7 @@ export class UnitsManager {
for (let idx in this.getSelectedUnits())
{
var unit = this.getSelectedUnits()[idx];
if (!unit.isLeader && !unit.isWingman)
if (!unit.getFormationData().isLeader && !unit.getFormationData().isWingman)
singletons.push(unit);
}
return singletons;
@ -290,12 +282,12 @@ export class UnitsManager {
var wingmenIDs = [];
for (let idx in selectedUnits)
{
if (selectedUnits[idx].isWingman)
if (selectedUnits[idx].getFormationData().isWingman)
{
//console.log(selectedUnits[idx].unitName + " is already in a formation.");
return;
}
else if (selectedUnits[idx].isLeader)
else if (selectedUnits[idx].getFormationData().isLeader)
{
//console.log(selectedUnits[idx].unitName + " is already in a formation.");
return;

View File

@ -1,14 +1,14 @@
<div class="ol-panel" id="unit-control-panel">
<!--
<div id="ol-title-label">Selected units</div>
<div id="selected-units-container" class="ol-scrollable">
-->
<!-- This is where all the unit selection buttons will be shown-->
<!--
</div>
<!--
<div id="formation-creation-container" data-feature-switch="ai-formations">
<div class="rectangular-button white" id="create-formation"><img src="images\buttons\create.svg">Create formation</div>
<div class="rectangular-button white" id="undo-formation"><img src="images\buttons\erase.svg">Undo formation</div>

View File

@ -1,4 +1,3 @@
<<<<<<< HEAD
Olympus.unitPayloads = {
["A-10A"] = { ["MK-84*2 , LAU-68*2 , AGM-65K*2"] = { [1] = { ["CLSID"] = "{6D21ECEA-F85B-4E8D-9D51-31DC9B8AA4EF}" },
[2] = { ["CLSID"] = "{174C6E6D-0C3D-42ff-BCB3-0853CB371F5C}" },
@ -25177,5 +25176,4 @@ Olympus.unitPayloads = {["A-10A"]={["MK-84*2 , LAU-68*2 , AGM-65K*2"]={[1] = {["
[2] = {["CLSID"]="XM158_MK5"},
[5] = {["CLSID"]="XM158_MK5"},
[6] = {["CLSID"]="M134_R"}}},
["UH-60A"]={}}
>>>>>>> 433b4bdf569d0aca79794685ee41021190722f5b
["UH-60A"]={}}

View File

@ -17,7 +17,7 @@ namespace SetCommandType {
REACTION_ON_THREAT = 1,
RADAR_USING = 3,
FLARE_USING = 4,
Formation = 5,
FORMATION = 5,
RTB_ON_BINGO = 6,
SILENCE = 7,
RTB_ON_OUT_OF_AMMO = 10,
@ -56,9 +56,6 @@ namespace ReactionToThreat {
};
}
/* Base command class */
class Command
{
@ -66,6 +63,7 @@ public:
int getPriority() { return priority; }
int getType() { return type; }
virtual wstring getString(lua_State* L) = 0;
virtual int getLoad() = 0;
protected:
int priority = CommandPriority::LOW;
@ -88,6 +86,7 @@ public:
type = CommandType::MOVE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 5; }
private:
const int ID;
@ -110,6 +109,7 @@ public:
type = CommandType::SMOKE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 5; }
private:
const wstring color;
@ -129,6 +129,7 @@ public:
type = CommandType::SPAWN_GROUND;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; }
private:
const wstring coalition;
@ -150,8 +151,8 @@ public:
priority = CommandPriority::LOW;
type = CommandType::SPAWN_AIR;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; }
private:
const wstring coalition;
@ -173,6 +174,7 @@ public:
type = CommandType::CLONE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; }
private:
const int ID;
@ -190,6 +192,7 @@ public:
type = CommandType::CLONE;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 20; }
private:
const int ID;
@ -207,6 +210,7 @@ public:
type = CommandType::FOLLOW;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;
@ -224,6 +228,7 @@ public:
type = CommandType::RESET_TASK;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;
@ -241,6 +246,7 @@ public:
type = CommandType::RESET_TASK;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;
@ -260,6 +266,7 @@ public:
type = CommandType::RESET_TASK;
};
virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; }
private:
const int ID;

View File

@ -15,5 +15,6 @@ public:
private:
list<Command*> commands;
int load;
};

View File

@ -7,9 +7,10 @@
extern UnitsManager* unitsManager;
Scheduler::Scheduler(lua_State* L)
Scheduler::Scheduler(lua_State* L):
load(0)
{
LogInfo(L, "Units Factory constructor called successfully");
LogInfo(L, "Scheduler constructor called successfully");
}
Scheduler::~Scheduler()
@ -24,6 +25,13 @@ void Scheduler::appendCommand(Command* command)
void Scheduler::execute(lua_State* L)
{
/* Decrease the active computation load. New commands can be sent only if the load has reached 0.
This is needed to avoid server lag. */
if (load > 0) {
load--;
return;
}
int priority = CommandPriority::HIGH;
while (priority >= CommandPriority::LOW)
{
@ -33,9 +41,8 @@ void Scheduler::execute(lua_State* L)
{
wstring commandString = L"Olympus.protectedCall(" + command->getString(L) + L")";
if (dostring_in(L, "server", to_string(commandString)))
{
log(L"Error executing command " + commandString);
}
load = command->getLoad();
commands.remove(command);
return;
}

View File

@ -61,15 +61,11 @@ void Unit::updateExportData(json::value json)
/* All units which contain the name "Olympus" are automatically under AI control */
/* TODO: I don't really like using this method */
if (unitName.find(L"Olympus") != wstring::npos)
{
AI = true;
}
/* If the unit is alive and it is not a human, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */
if (AI && alive && flags[L"Human"].as_bool() == false)
{
AIloop();
}
}
void Unit::updateMissionData(json::value json)
@ -88,44 +84,56 @@ json::value Unit::json()
{
auto json = json::value::object();
json[L"alive"] = alive;
/********** Base data **********/
json[L"AI"] = AI;
json[L"name"] = json::value::string(name);
json[L"unitName"] = json::value::string(unitName);
json[L"groupName"] = json::value::string(groupName);
json[L"type"] = type;
json[L"country"] = country;
json[L"coalitionID"] = coalitionID;
json[L"latitude"] = latitude;
json[L"longitude"] = longitude;
json[L"altitude"] = altitude;
json[L"speed"] = speed;
json[L"heading"] = heading;
json[L"flags"] = flags;
json[L"alive"] = alive;
json[L"category"] = json::value::string(getCategory());
json[L"currentTask"] = json::value::string(getCurrentTask());
json[L"isLeader"] = isLeader;
json[L"isWingman"] = isWingman;
json[L"formation"] = json::value::string(formation);
json[L"fuel"] = fuel;
json[L"ammo"] = ammo;
json[L"targets"] = targets;
json[L"targetSpeed"] = getTargetSpeed();
json[L"targetAltitude"] = getTargetAltitude();
json[L"hasTask"] = hasTask;
json[L"ROE"] = json::value::string(ROE);
json[L"reactionToThreat"] = json::value::string(reactionToThreat);
/********** Flight data **********/
json[L"flightData"] = json::value::object();
json[L"flightData"][L"latitude"] = latitude;
json[L"flightData"][L"longitude"] = longitude;
json[L"flightData"][L"altitude"] = altitude;
json[L"flightData"][L"speed"] = speed;
json[L"flightData"][L"heading"] = heading;
/********** Mission data **********/
json[L"missionData"] = json::value::object();
json[L"missionData"][L"fuel"] = fuel;
json[L"missionData"][L"ammo"] = ammo;
json[L"missionData"][L"targets"] = targets;
json[L"missionData"][L"hasTask"] = hasTask;
if (coalitionID == 0)
json[L"missionData"][L"coalition"] = json::value::string(L"neutral");
else if (coalitionID == 1)
json[L"missionData"][L"coalition"] = json::value::string(L"red");
else
json[L"missionData"][L"coalition"] = json::value::string(L"blue");
json[L"missionData"][L"flags"] = flags;
/********** Formation data **********/
json[L"formationData"] = json::value::object();
json[L"formationData"][L"isLeader"] = isLeader;
json[L"formationData"][L"isWingman"] = isWingman;
json[L"formationData"][L"formation"] = json::value::string(formation);
int i = 0;
for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++)
json[L"wingmenIDs"][i++] = (*itr)->getID();
json[L"formationData"][L"wingmenIDs"][i++] = (*itr)->getID();
if (leader != nullptr)
json[L"leaderID"] = leader->getID();
json[L"formationData"][L"leaderID"] = leader->getID();
/********** Task data **********/
json[L"taskData"] = json::value::object();
json[L"taskData"][L"currentTask"] = json::value::string(getCurrentTask());
json[L"taskData"][L"targetSpeed"] = getTargetSpeed();
json[L"taskData"][L"targetAltitude"] = getTargetAltitude();
/* Send the active path as a json object */
auto path = json::value::object();
if (activePath.size() > 0) {
auto path = json::value::object();
int count = 1;
for (auto& destination : activePath)
{
@ -135,8 +143,13 @@ json::value Unit::json()
json[L"alt"] = destination.alt;
path[to_wstring(count++)] = json;
}
json[L"activePath"] = path;
}
json[L"taskData"][L"activePath"] = path;
/********** Options data **********/
json[L"optionsData"] = json::value::object();
json[L"optionsData"][L"ROE"] = json::value::string(ROE);
json[L"optionsData"][L"reactionToThreat"] = json::value::string(reactionToThreat);
return json;
}
@ -178,7 +191,6 @@ bool Unit::isTargetAlive()
return false;
}
/* This function reset the activation so that the AI lopp will call again the MoveCommand. This is useful to change speed and altitude, for example */
void Unit::resetActiveDestination()
{
activeDestination = Coords(NULL);

View File

@ -60,21 +60,17 @@ extern "C" DllExport int coreFrame(lua_State* L)
const std::chrono::duration<double> duration = std::chrono::system_clock::now() - before;
// TODO make intervals editable
/* TODO make intervals editable */
if (duration.count() > UPDATE_TIME_INTERVAL)
{
if (unitsManager != nullptr)
{
unitsManager->updateExportData(L);
}
// TODO allow for different intervals
if (scheduler != nullptr)
{
scheduler->execute(L);
}
before = std::chrono::system_clock::now();
}
if (scheduler != nullptr)
scheduler->execute(L);
return(0);
}