mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Converted data transfer to binary key-value method
This commit is contained in:
parent
1989219579
commit
4d9dd364b6
42
client/src/@types/unit.d.ts
vendored
42
client/src/@types/unit.d.ts
vendored
@ -46,42 +46,8 @@ interface Contact {
|
||||
detectionMethod: number
|
||||
}
|
||||
|
||||
interface UnitData {
|
||||
ID: number,
|
||||
alive: boolean,
|
||||
human: boolean,
|
||||
controlled: boolean,
|
||||
hasTask: boolean,
|
||||
desiredAltitudeType: string,
|
||||
desiredSpeedType: string,
|
||||
isTanker: boolean,
|
||||
isAWACS: boolean,
|
||||
onOff: boolean,
|
||||
followRoads: boolean,
|
||||
EPLRS: boolean,
|
||||
generalSettings: GeneralSettings
|
||||
position: LatLng,
|
||||
speed: number,
|
||||
heading: number,
|
||||
fuel: number,
|
||||
desiredSpeed: number,
|
||||
desiredAltitude: number,
|
||||
targetID: number,
|
||||
leaderID: number,
|
||||
targetPosition: LatLng,
|
||||
state: string,
|
||||
ROE: string,
|
||||
reactionToThreat: string,
|
||||
emissionsCountermeasures: string,
|
||||
TACAN: TACAN,
|
||||
radio: Radio,
|
||||
activePath: LatLng[],
|
||||
ammo: Ammo[],
|
||||
contacts: Contact[],
|
||||
name: string,
|
||||
unitName: string,
|
||||
groupName: string,
|
||||
category: string,
|
||||
coalition: string,
|
||||
task: string
|
||||
interface Offset {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number
|
||||
}
|
||||
@ -119,7 +119,7 @@ export abstract class ATCBoard {
|
||||
|
||||
const unitCanBeAdded = () => {
|
||||
|
||||
if ( baseData.category !== "Aircraft" ) {
|
||||
if ( unit.getCategory() !== "Aircraft" ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ export class UnitDataTable extends Panel {
|
||||
|
||||
for (const unit of unitsArray) {
|
||||
|
||||
const dataset = [unit.getData().unitName, unit.getData().name, unit.getData().category, (unit.getData().controlled) ? "AI" : "Human"];
|
||||
const dataset = [unit.getData().unitName, unit.getData().name, unit.getCategory(), (unit.getData().controlled) ? "AI" : "Human"];
|
||||
|
||||
addRow(el, dataset);
|
||||
}
|
||||
|
||||
@ -131,4 +131,46 @@ export const COALITIONAREA_INTERACT = "Interact with Coalition Areas"
|
||||
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 IADSRoles: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05};
|
||||
export const IADSRoles: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05};
|
||||
|
||||
export enum DataIndexes {
|
||||
startOfData = 0,
|
||||
category,
|
||||
alive,
|
||||
human,
|
||||
controlled,
|
||||
coalition,
|
||||
country,
|
||||
name,
|
||||
unitName,
|
||||
groupName,
|
||||
state,
|
||||
task,
|
||||
hasTask,
|
||||
position,
|
||||
speed,
|
||||
heading,
|
||||
isTanker,
|
||||
isAWACS,
|
||||
onOff,
|
||||
followRoads,
|
||||
fuel,
|
||||
desiredSpeed,
|
||||
desiredSpeedType,
|
||||
desiredAltitude,
|
||||
desiredAltitudeType,
|
||||
leaderID,
|
||||
formationOffset,
|
||||
targetID,
|
||||
targetPosition,
|
||||
ROE,
|
||||
reactionToThreat,
|
||||
emissionsCountermeasures,
|
||||
TACAN,
|
||||
radio,
|
||||
generalSettings,
|
||||
ammo,
|
||||
contacts,
|
||||
activePath,
|
||||
endOfData = 255
|
||||
};
|
||||
@ -5,6 +5,7 @@ import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { helicopterDatabase } from "../units/helicopterdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { Buffer } from "buffer";
|
||||
import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants";
|
||||
|
||||
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {
|
||||
const φ1 = deg2rad(lat1); // φ, λ in radians
|
||||
@ -253,3 +254,40 @@ export function getUnitDatabaseByCategory(category: string) {
|
||||
export function base64ToBytes(base64: string) {
|
||||
return Buffer.from(base64, 'base64').buffer;
|
||||
}
|
||||
|
||||
export function enumToState(state: number) {
|
||||
if (state < states.length)
|
||||
return states[state];
|
||||
else
|
||||
return states[0];
|
||||
}
|
||||
|
||||
export function enumToROE(ROE: number) {
|
||||
if (ROE < ROEs.length)
|
||||
return ROEs[ROE];
|
||||
else
|
||||
return ROEs[0];
|
||||
}
|
||||
|
||||
export function enumToReactionToThreat(reactionToThreat: number) {
|
||||
if (reactionToThreat < reactionsToThreat.length)
|
||||
return reactionsToThreat[reactionToThreat];
|
||||
else
|
||||
return reactionsToThreat[0];
|
||||
}
|
||||
|
||||
export function enumToEmissioNCountermeasure(emissionCountermeasure: number) {
|
||||
if (emissionCountermeasure < emissionsCountermeasures.length)
|
||||
return emissionsCountermeasures[emissionCountermeasure];
|
||||
else
|
||||
return emissionsCountermeasures[0];
|
||||
}
|
||||
|
||||
export function enumToCoalition(coalitionID: number) {
|
||||
switch (coalitionID){
|
||||
case 0: return "neutral";
|
||||
case 1: return "red";
|
||||
case 2: return "blue";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@ -1,9 +1,8 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { UnitData } from "../@types/unit";
|
||||
import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants";
|
||||
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN } from "../@types/unit";
|
||||
|
||||
export class DataExtractor {
|
||||
#offset = 0;
|
||||
#seekPosition = 0;
|
||||
#dataview: DataView;
|
||||
#decoder: TextDecoder;
|
||||
#buffer: ArrayBuffer;
|
||||
@ -14,181 +13,59 @@ export class DataExtractor {
|
||||
this.#decoder = new TextDecoder("utf-8");
|
||||
}
|
||||
|
||||
extractData(offset: number) {
|
||||
this.#offset = offset;
|
||||
|
||||
const ID = this.extractUInt32();
|
||||
const bitmask = this.extractUInt32();
|
||||
|
||||
const unitData: UnitData = {
|
||||
ID: ID,
|
||||
alive: this.extractFromBitmask(bitmask, 0),
|
||||
human: this.extractFromBitmask(bitmask, 1),
|
||||
controlled: this.extractFromBitmask(bitmask, 2),
|
||||
hasTask: this.extractFromBitmask(bitmask, 3),
|
||||
desiredAltitudeType: this.extractFromBitmask(bitmask, 16)? "AGL": "ASL",
|
||||
desiredSpeedType: this.extractFromBitmask(bitmask, 17)? "GS": "CAS",
|
||||
isTanker: this.extractFromBitmask(bitmask, 18),
|
||||
isAWACS: this.extractFromBitmask(bitmask, 19),
|
||||
onOff: this.extractFromBitmask(bitmask, 20),
|
||||
followRoads: this.extractFromBitmask(bitmask, 21),
|
||||
EPLRS: this.extractFromBitmask(bitmask, 22),
|
||||
generalSettings: {
|
||||
prohibitAA: this.extractFromBitmask(bitmask, 23),
|
||||
prohibitAfterburner: this.extractFromBitmask(bitmask, 24),
|
||||
prohibitAG: this.extractFromBitmask(bitmask, 25),
|
||||
prohibitAirWpn: this.extractFromBitmask(bitmask, 26),
|
||||
prohibitJettison: this.extractFromBitmask(bitmask, 27),
|
||||
},
|
||||
position: new LatLng(
|
||||
this.extractFloat64(),
|
||||
this.extractFloat64(),
|
||||
this.extractFloat64()
|
||||
),
|
||||
speed: this.extractFloat64(),
|
||||
heading: this.extractFloat64(),
|
||||
fuel: this.extractUInt16(),
|
||||
desiredSpeed: this.extractFloat64(),
|
||||
desiredAltitude: this.extractFloat64(),
|
||||
leaderID: this.extractUInt32(),
|
||||
targetID: this.extractUInt32(),
|
||||
targetPosition: new LatLng(
|
||||
this.extractFloat64(),
|
||||
this.extractFloat64(),
|
||||
this.extractFloat64()
|
||||
),
|
||||
state: this.#getState(this.extractUInt8()),
|
||||
ROE: this.#getROE(this.extractUInt8()),
|
||||
reactionToThreat: this.#getReactionToThreat(this.extractUInt8()),
|
||||
emissionsCountermeasures: this.#getEmissionCountermeasure(this.extractUInt8()),
|
||||
coalition: this.#getCoalition(this.extractUInt8()),
|
||||
TACAN: {
|
||||
isOn: this.extractBool(),
|
||||
channel: this.extractUInt8(),
|
||||
XY: this.extractChar(),
|
||||
callsign: this.extractString(4)
|
||||
},
|
||||
radio: {
|
||||
frequency: this.extractUInt32(),
|
||||
callsign: this.extractUInt8(),
|
||||
callsignNumber: this.extractUInt8()
|
||||
},
|
||||
activePath: [],
|
||||
ammo: [],
|
||||
contacts: [],
|
||||
task: "",
|
||||
name: "",
|
||||
unitName: "",
|
||||
groupName: "",
|
||||
category: "",
|
||||
}
|
||||
|
||||
const pathLength = this.extractUInt16();
|
||||
const ammoLength = this.extractUInt16();
|
||||
const contactsLength = this.extractUInt16();
|
||||
const taskLength = this.extractUInt8();
|
||||
|
||||
if (pathLength > 0) {
|
||||
unitData.activePath = [];
|
||||
for (let idx = 0; idx < pathLength; idx++) {
|
||||
unitData.activePath.push(new LatLng(this.extractFloat64(), this.extractFloat64(), this.extractFloat64()));
|
||||
}
|
||||
}
|
||||
|
||||
if (ammoLength > 0) {
|
||||
unitData.ammo = [];
|
||||
for (let idx = 0; idx < pathLength; idx++) {
|
||||
unitData.ammo.push({
|
||||
quantity: this.extractUInt16(),
|
||||
name: this.extractString(32),
|
||||
guidance: this.extractUInt8(),
|
||||
category: this.extractUInt8(),
|
||||
missileCategory: this.extractUInt8()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (contactsLength > 0) {
|
||||
unitData.contacts = [];
|
||||
for (let idx = 0; idx < pathLength; idx++) {
|
||||
unitData.contacts.push({
|
||||
ID: this.extractUInt32(),
|
||||
detectionMethod: this.extractUInt8()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (taskLength > 0) {
|
||||
unitData.task = this.extractString(taskLength);
|
||||
}
|
||||
|
||||
const nameLength = this.extractUInt16();
|
||||
const unitNameLength = this.extractUInt16();
|
||||
const groupNameLength = this.extractUInt16();
|
||||
const categoryLength = this.extractUInt16();
|
||||
|
||||
if (nameLength > 0) {
|
||||
unitData.name = this.extractString(nameLength);
|
||||
}
|
||||
|
||||
if (unitNameLength > 0) {
|
||||
unitData.unitName = this.extractString(unitNameLength);
|
||||
}
|
||||
|
||||
if (groupNameLength > 0) {
|
||||
unitData.groupName = this.extractString(groupNameLength);
|
||||
}
|
||||
|
||||
if (categoryLength > 0) {
|
||||
unitData.category = this.extractString(categoryLength);
|
||||
}
|
||||
|
||||
return {data: unitData, offset: this.#offset};
|
||||
getSeekPosition() {
|
||||
return this.#seekPosition;
|
||||
}
|
||||
|
||||
extractBool() {
|
||||
const value = this.#dataview.getUint8(this.#offset);
|
||||
this.#offset += 1;
|
||||
const value = this.#dataview.getUint8(this.#seekPosition);
|
||||
this.#seekPosition += 1;
|
||||
return value > 0;
|
||||
}
|
||||
|
||||
extractUInt8() {
|
||||
const value = this.#dataview.getUint8(this.#offset);
|
||||
this.#offset += 1;
|
||||
const value = this.#dataview.getUint8(this.#seekPosition);
|
||||
this.#seekPosition += 1;
|
||||
return value;
|
||||
}
|
||||
|
||||
extractUInt16() {
|
||||
const value = this.#dataview.getUint16(this.#offset, true);
|
||||
this.#offset += 2;
|
||||
const value = this.#dataview.getUint16(this.#seekPosition, true);
|
||||
this.#seekPosition += 2;
|
||||
return value;
|
||||
}
|
||||
|
||||
extractUInt32() {
|
||||
const value = this.#dataview.getUint32(this.#offset, true);
|
||||
this.#offset += 4;
|
||||
const value = this.#dataview.getUint32(this.#seekPosition, true);
|
||||
this.#seekPosition += 4;
|
||||
return value;
|
||||
}
|
||||
|
||||
extractUInt64() {
|
||||
const value = this.#dataview.getBigUint64(this.#offset, true);
|
||||
this.#offset += 8;
|
||||
const value = this.#dataview.getBigUint64(this.#seekPosition, true);
|
||||
this.#seekPosition += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
extractFloat64() {
|
||||
const value = this.#dataview.getFloat64(this.#offset, true);
|
||||
this.#offset += 8;
|
||||
const value = this.#dataview.getFloat64(this.#seekPosition, true);
|
||||
this.#seekPosition += 8;
|
||||
return value;
|
||||
}
|
||||
|
||||
extractLatLng() {
|
||||
return new LatLng(this.extractFloat64(), this.extractFloat64(), this.extractFloat64())
|
||||
}
|
||||
|
||||
extractFromBitmask(bitmask: number, position: number) {
|
||||
return ((bitmask >> position) & 1) > 0;
|
||||
}
|
||||
|
||||
extractString(length: number) {
|
||||
const value = this.#decoder.decode(this.#buffer.slice(this.#offset, this.#offset +length));
|
||||
this.#offset += length;
|
||||
extractString(length?: number) {
|
||||
if (length === undefined)
|
||||
length = this.extractUInt16()
|
||||
const value = this.#decoder.decode(this.#buffer.slice(this.#seekPosition, this.#seekPosition + length));
|
||||
this.#seekPosition += length;
|
||||
return value;
|
||||
}
|
||||
|
||||
@ -196,44 +73,78 @@ export class DataExtractor {
|
||||
return this.extractString(1);
|
||||
}
|
||||
|
||||
getOffset() {
|
||||
return this.#offset;
|
||||
extractTACAN() {
|
||||
const value: TACAN = {
|
||||
isOn: this.extractBool(),
|
||||
channel: this.extractUInt8(),
|
||||
XY: this.extractChar(),
|
||||
callsign: this.extractString(4)
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
#getState(state: number) {
|
||||
if (state < states.length)
|
||||
return states[state];
|
||||
else
|
||||
return states[0];
|
||||
}
|
||||
|
||||
#getROE(ROE: number) {
|
||||
if (ROE < ROEs.length)
|
||||
return ROEs[ROE];
|
||||
else
|
||||
return ROEs[0];
|
||||
}
|
||||
|
||||
#getReactionToThreat(reactionToThreat: number) {
|
||||
if (reactionToThreat < reactionsToThreat.length)
|
||||
return reactionsToThreat[reactionToThreat];
|
||||
else
|
||||
return reactionsToThreat[0];
|
||||
}
|
||||
|
||||
#getEmissionCountermeasure(emissionCountermeasure: number) {
|
||||
if (emissionCountermeasure < emissionsCountermeasures.length)
|
||||
return emissionsCountermeasures[emissionCountermeasure];
|
||||
else
|
||||
return emissionsCountermeasures[0];
|
||||
}
|
||||
|
||||
#getCoalition(coalitionID: number) {
|
||||
switch (coalitionID){
|
||||
case 0: return "neutral";
|
||||
case 1: return "red";
|
||||
case 2: return "blue";
|
||||
extractRadio() {
|
||||
const value: Radio = {
|
||||
frequency: this.extractUInt32(),
|
||||
callsign: this.extractUInt8(),
|
||||
callsignNumber: this.extractUInt8()
|
||||
}
|
||||
return "";
|
||||
return value;
|
||||
}
|
||||
|
||||
extractGeneralSettings() {
|
||||
const value: GeneralSettings = {
|
||||
prohibitJettison: this.extractBool(),
|
||||
prohibitAA: this.extractBool(),
|
||||
prohibitAG: this.extractBool(),
|
||||
prohibitAfterburner: this.extractBool(),
|
||||
prohibitAirWpn: this.extractBool(),
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
extractAmmo() {
|
||||
const value: Ammo[] = [];
|
||||
const size = this.extractUInt16();
|
||||
for (let idx = 0; idx < size; idx++) {
|
||||
value.push({
|
||||
quantity: this.extractUInt16(),
|
||||
name: this.extractString(32),
|
||||
guidance: this.extractUInt8(),
|
||||
category: this.extractUInt8(),
|
||||
missileCategory: this.extractUInt8()
|
||||
});
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
extractContacts(){
|
||||
const value: Contact[] = [];
|
||||
const size = this.extractUInt16();
|
||||
for (let idx = 0; idx < size; idx++) {
|
||||
value.push({
|
||||
ID: this.extractUInt32(),
|
||||
detectionMethod: this.extractUInt8()
|
||||
});
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
extractActivePath() {
|
||||
const value: LatLng[] = [];
|
||||
const size = this.extractUInt16();
|
||||
for (let idx = 0; idx < size; idx++) {
|
||||
value.push(this.extractLatLng());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
extractOffset() {
|
||||
const value: Offset = {
|
||||
x: this.extractFloat64(),
|
||||
y: this.extractFloat64(),
|
||||
z: this.extractFloat64(),
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,14 @@
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map } from 'leaflet';
|
||||
import { getMap, getUnitsManager } from '..';
|
||||
import { getMarkerCategoryByName, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg } from '../other/utils';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg } from '../other/utils';
|
||||
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { UnitDatabase } from './unitdatabase';
|
||||
import { TargetMarker } from '../map/targetmarker';
|
||||
import { BOMBING, CARPET_BOMBING, FIRE_AT_AREA, IDLE, MOVE_UNIT, ROEs, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants';
|
||||
import { GeneralSettings, Radio, TACAN, UnitData, UnitIconOptions } from '../@types/unit';
|
||||
import { BOMBING, CARPET_BOMBING, DataIndexes, FIRE_AT_AREA, IDLE, MOVE_UNIT, ROEs, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants';
|
||||
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, UnitIconOptions } from '../@types/unit';
|
||||
import { DataExtractor } from './dataextractor';
|
||||
|
||||
var pathIcon = new Icon({
|
||||
iconUrl: '/resources/theme/images/markers/marker-icon.png',
|
||||
@ -18,77 +19,74 @@ var pathIcon = new Icon({
|
||||
export class Unit extends CustomMarker {
|
||||
ID: number;
|
||||
|
||||
#data: UnitData = {
|
||||
ID: 0,
|
||||
alive: false,
|
||||
human: false,
|
||||
controlled: false,
|
||||
hasTask: false,
|
||||
desiredAltitudeType: "AGL",
|
||||
desiredSpeedType: "GS",
|
||||
isTanker: false,
|
||||
isAWACS: false,
|
||||
onOff: false,
|
||||
followRoads: false,
|
||||
EPLRS: false,
|
||||
generalSettings: {
|
||||
prohibitAA: false,
|
||||
prohibitAfterburner: false,
|
||||
prohibitAG: false,
|
||||
prohibitAirWpn: false,
|
||||
prohibitJettison: false
|
||||
},
|
||||
position: new LatLng(0, 0),
|
||||
speed: 0,
|
||||
heading: 0,
|
||||
fuel: 0,
|
||||
desiredSpeed: 0,
|
||||
desiredAltitude: 0,
|
||||
targetID: 0,
|
||||
leaderID: 0,
|
||||
targetPosition: new LatLng(0, 0),
|
||||
state: states[0],
|
||||
ROE: ROEs[0],
|
||||
reactionToThreat: reactionsToThreat[0],
|
||||
emissionsCountermeasures: emissionsCountermeasures[0],
|
||||
TACAN: {
|
||||
isOn: false,
|
||||
XY: 'X',
|
||||
callsign: '',
|
||||
channel: 0
|
||||
},
|
||||
radio: {
|
||||
frequency: 0,
|
||||
callsign: 0,
|
||||
callsignNumber: 0
|
||||
},
|
||||
activePath: [],
|
||||
ammo: [],
|
||||
contacts: [],
|
||||
name: "",
|
||||
unitName: "",
|
||||
groupName: "",
|
||||
category: "",
|
||||
coalition: "",
|
||||
task: ""
|
||||
#alive: boolean = false;
|
||||
#human: boolean = false;
|
||||
#controlled: boolean = false;
|
||||
#coalition: string = "";
|
||||
#country: number = 0;
|
||||
#name: string = "";
|
||||
#unitName: string = "";
|
||||
#groupName: string = "";
|
||||
#state: string = states[0];
|
||||
#task: string = ""
|
||||
#hasTask: boolean = false;
|
||||
#position: LatLng = new LatLng(0, 0);
|
||||
#speed: number = 0;
|
||||
#heading: number = 0;
|
||||
#isTanker: boolean = false;
|
||||
#isAWACS: boolean = false;
|
||||
#onOff: boolean = false;
|
||||
#followRoads: boolean = false;
|
||||
#fuel: number = 0;
|
||||
#desiredSpeed: number = 0;
|
||||
#desiredSpeedType: string = "GS";
|
||||
#desiredAltitude: number = 0;
|
||||
#desiredAltitudeType: string = "AGL";
|
||||
#leaderID: number = 0;
|
||||
#formationOffset: Offset = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0
|
||||
};
|
||||
#targetID: number = 0;
|
||||
#targetPosition: LatLng = new LatLng(0, 0);
|
||||
#ROE: string = ROEs[0];
|
||||
#reactionToThreat: string = reactionsToThreat[0];
|
||||
#emissionsCountermeasures: string = emissionsCountermeasures[0];
|
||||
#TACAN: TACAN = {
|
||||
isOn: false,
|
||||
XY: 'X',
|
||||
callsign: '',
|
||||
channel: 0
|
||||
};
|
||||
#radio: Radio = {
|
||||
frequency: 0,
|
||||
callsign: 0,
|
||||
callsignNumber: 0
|
||||
};
|
||||
#generalSettings: GeneralSettings = {
|
||||
prohibitAA: false,
|
||||
prohibitAfterburner: false,
|
||||
prohibitAG: false,
|
||||
prohibitAirWpn: false,
|
||||
prohibitJettison: false
|
||||
};
|
||||
#ammo: Ammo[] = [];
|
||||
#contacts: Contact[] = [];
|
||||
#activePath: LatLng[] = [];
|
||||
|
||||
#selectable: boolean;
|
||||
#selected: boolean = false;
|
||||
#hidden: boolean = false;
|
||||
#highlighted: boolean = false;
|
||||
|
||||
#preventClick: boolean = false;
|
||||
|
||||
#pathMarkers: Marker[] = [];
|
||||
#pathPolyline: Polyline;
|
||||
#contactsPolylines: Polyline[];
|
||||
#miniMapMarker: CircleMarker | null = null;
|
||||
#targetPositionMarker: TargetMarker;
|
||||
#targetPositionPolyline: Polyline;
|
||||
|
||||
#timer: number = 0;
|
||||
|
||||
#hotgroup: number | null = null;
|
||||
|
||||
static getConstructor(type: string) {
|
||||
@ -100,7 +98,7 @@ export class Unit extends CustomMarker {
|
||||
if (type === "NavyUnit") return NavyUnit;
|
||||
}
|
||||
|
||||
constructor(ID: number, data: UnitData) {
|
||||
constructor(ID: number) {
|
||||
super(new LatLng(0, 0), { riseOnHover: true, keyboard: false });
|
||||
|
||||
this.ID = ID;
|
||||
@ -126,9 +124,122 @@ export class Unit extends CustomMarker {
|
||||
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
|
||||
});
|
||||
}
|
||||
|
||||
/* Set the unit data */
|
||||
this.setData(data);
|
||||
getCategory() {
|
||||
// Overloaded by child classes
|
||||
return "";
|
||||
}
|
||||
|
||||
/********************** Unit data *************************/
|
||||
setData(dataExtractor: DataExtractor) {
|
||||
var updateMarker = !getMap().hasLayer(this);
|
||||
|
||||
var datumIndex = dataExtractor.extractUInt8();
|
||||
if (datumIndex == DataIndexes.startOfData) {
|
||||
while (datumIndex != DataIndexes.endOfData) {
|
||||
datumIndex = dataExtractor.extractUInt8();
|
||||
switch (datumIndex) {
|
||||
case DataIndexes.alive: this.setAlive(dataExtractor.extractBool()); updateMarker = true; break;
|
||||
case DataIndexes.human: this.#human = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.controlled: this.#controlled = dataExtractor.extractBool(); updateMarker = true; break;
|
||||
case DataIndexes.coalition: this.#coalition = enumToCoalition(dataExtractor.extractUInt8()); break;
|
||||
case DataIndexes.country: this.#country = dataExtractor.extractUInt8(); break;
|
||||
case DataIndexes.name: this.#name = dataExtractor.extractString(); break;
|
||||
case DataIndexes.unitName: this.#unitName = dataExtractor.extractString(); break;
|
||||
case DataIndexes.groupName: this.#groupName = dataExtractor.extractString(); break;
|
||||
case DataIndexes.state: this.#state = enumToState(dataExtractor.extractUInt8()); updateMarker = true; break;
|
||||
case DataIndexes.task: this.#task = dataExtractor.extractString(); break;
|
||||
case DataIndexes.hasTask: this.#hasTask = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.position: this.#position = dataExtractor.extractLatLng(); updateMarker = true; break;
|
||||
case DataIndexes.speed: this.#speed = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
case DataIndexes.heading: this.#heading = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
case DataIndexes.isTanker: this.#isTanker = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.isAWACS: this.#isAWACS = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.onOff: this.#onOff = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.followRoads: this.#followRoads = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.fuel: this.#fuel = dataExtractor.extractUInt16(); break;
|
||||
case DataIndexes.desiredSpeed: this.#desiredSpeed = dataExtractor.extractFloat64(); break;
|
||||
case DataIndexes.desiredSpeedType: this.#desiredSpeedType = dataExtractor.extractBool() ? "GS" : "CAS"; break;
|
||||
case DataIndexes.desiredAltitude: this.#desiredAltitude = dataExtractor.extractFloat64(); break;
|
||||
case DataIndexes.desiredAltitudeType: this.#desiredAltitudeType = dataExtractor.extractFloat64() ? "AGL" : "ASL"; break;
|
||||
case DataIndexes.leaderID: this.#leaderID = dataExtractor.extractUInt32(); break;
|
||||
case DataIndexes.formationOffset: dataExtractor.extractOffset(); break;
|
||||
case DataIndexes.targetID: this.#targetID = dataExtractor.extractUInt32(); break;
|
||||
case DataIndexes.targetPosition: this.#targetPosition = dataExtractor.extractLatLng(); break;
|
||||
case DataIndexes.ROE: this.#ROE = enumToROE(dataExtractor.extractUInt8()); break;
|
||||
case DataIndexes.reactionToThreat: this.#reactionToThreat = enumToReactionToThreat(dataExtractor.extractUInt8()); break;
|
||||
case DataIndexes.emissionsCountermeasures: this.#emissionsCountermeasures = enumToEmissioNCountermeasure(dataExtractor.extractUInt8()); break;
|
||||
case DataIndexes.TACAN: this.#TACAN = dataExtractor.extractTACAN(); break;
|
||||
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.activePath: this.#activePath = dataExtractor.extractActivePath(); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Dead units can't be selected */
|
||||
this.setSelected(this.getSelected() && this.#alive && !this.getHidden())
|
||||
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
|
||||
// TODO dont delete the polylines of the detected units
|
||||
this.#clearDetectedUnits();
|
||||
if (this.getSelected()) {
|
||||
this.#drawPath();
|
||||
this.#drawDetectedUnits();
|
||||
this.#drawTarget();
|
||||
}
|
||||
else {
|
||||
this.#clearPath();
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
getData() {
|
||||
return {
|
||||
alive: this.#alive,
|
||||
human: this.#human,
|
||||
controlled: this.#controlled,
|
||||
coalition: this.#coalition,
|
||||
country: this.#country,
|
||||
name: this.#name,
|
||||
unitName: this.#unitName,
|
||||
groupName: this.#groupName,
|
||||
state: this.#state,
|
||||
task: this.#task,
|
||||
hasTask: this.#hasTask,
|
||||
position: this.#position,
|
||||
speed: this.#speed,
|
||||
heading: this.#heading,
|
||||
isTanker: this.#isTanker,
|
||||
isAWACS: this.#isAWACS,
|
||||
onOff: this.#onOff,
|
||||
followRoads: this.#followRoads,
|
||||
fuel: this.#fuel,
|
||||
desiredSpeed: this.#desiredSpeed,
|
||||
desiredSpeedType: this.#desiredSpeedType,
|
||||
desiredAltitude: this.#desiredAltitude,
|
||||
desiredAltitudeType: this.#desiredAltitudeType,
|
||||
leaderID: this.#leaderID,
|
||||
formationOffset: this.#formationOffset,
|
||||
targetID: this.#targetID,
|
||||
targetPosition: this.#targetPosition,
|
||||
ROE: this.#ROE,
|
||||
reactionToThreat: this.#reactionToThreat,
|
||||
emissionsCountermeasures: this.#emissionsCountermeasures,
|
||||
TACAN: this.#TACAN,
|
||||
radio: this.#radio,
|
||||
generalSettings: this.#generalSettings,
|
||||
ammo: this.#ammo,
|
||||
contacts: this.#contacts,
|
||||
activePath: this.#activePath
|
||||
}
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@ -151,14 +262,20 @@ export class Unit extends CustomMarker {
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
showSummary: false,
|
||||
rotateToHeading: false
|
||||
}
|
||||
}
|
||||
|
||||
setAlive(newAlive: boolean) {
|
||||
if (newAlive != this.#alive)
|
||||
document.dispatchEvent(new CustomEvent("unitDeath", { detail: this }));
|
||||
this.#alive = newAlive;
|
||||
}
|
||||
|
||||
setSelected(selected: boolean) {
|
||||
/* Only alive units can be selected. Some units are not selectable (weapons) */
|
||||
if ((this.getData().alive || !selected) && this.getSelectable() && this.getSelected() != selected) {
|
||||
if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected) {
|
||||
this.#selected = selected;
|
||||
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-selected", selected);
|
||||
if (selected) {
|
||||
@ -208,49 +325,7 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
getGroupMembers() {
|
||||
return Object.values(getUnitsManager().getUnits()).filter((unit: Unit) => {return unit != this && unit.getData().groupName === this.getData().groupName;});
|
||||
}
|
||||
|
||||
/********************** Unit data *************************/
|
||||
setData(data: UnitData) {
|
||||
/* Check if data has changed comparing new values to old values */
|
||||
const positionChanged = this.getData().position.lat != data.position.lat || this.getData().position.lng != data.position.lng;
|
||||
const headingChanged = this.getData().heading != data.heading;
|
||||
const aliveChanged = this.getData().alive != data.alive;
|
||||
const stateChanged = this.getData().state != data.state;
|
||||
const controlledChanged = this.getData().controlled != data.controlled;
|
||||
var updateMarker = positionChanged || headingChanged || aliveChanged || stateChanged || controlledChanged || !getMap().hasLayer(this);
|
||||
|
||||
/* Assign the data */
|
||||
this.#data = data;
|
||||
|
||||
/* Fire an event when a unit dies */
|
||||
if (aliveChanged && this.getData().alive == false)
|
||||
document.dispatchEvent(new CustomEvent("unitDeath", { detail: this }));
|
||||
|
||||
/* Dead units can't be selected */
|
||||
this.setSelected(this.getSelected() && this.getData().alive && !this.getHidden())
|
||||
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
|
||||
// TODO dont delete the polylines of the detected units
|
||||
this.#clearDetectedUnits();
|
||||
if (this.getSelected()) {
|
||||
this.#drawPath();
|
||||
this.#drawDetectedUnits();
|
||||
this.#drawTarget();
|
||||
}
|
||||
else {
|
||||
this.#clearPath();
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.#data;
|
||||
return Object.values(getUnitsManager().getUnits()).filter((unit: Unit) => { return unit != this && unit.#groupName === this.#groupName; });
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
@ -266,7 +341,7 @@ export class Unit extends CustomMarker {
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("unit");
|
||||
el.setAttribute("data-object", `unit-${this.getMarkerCategory()}`);
|
||||
el.setAttribute("data-coalition", this.getData().coalition);
|
||||
el.setAttribute("data-coalition", this.#coalition);
|
||||
|
||||
// Generate and append elements depending on active options
|
||||
// Velocity vector
|
||||
@ -300,7 +375,7 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
// State icon
|
||||
if (this.getIconOptions().showState){
|
||||
if (this.getIconOptions().showState) {
|
||||
var state = document.createElement("div");
|
||||
state.classList.add("unit-state");
|
||||
el.appendChild(state);
|
||||
@ -310,7 +385,7 @@ export class Unit extends CustomMarker {
|
||||
if (this.getIconOptions().showShortLabel) {
|
||||
var shortLabel = document.createElement("div");
|
||||
shortLabel.classList.add("unit-short-label");
|
||||
shortLabel.innerText = getUnitDatabaseByCategory(this.getMarkerCategory())?.getByName(this.getData().name)?.shortLabel || "";
|
||||
shortLabel.innerText = getUnitDatabaseByCategory(this.getMarkerCategory())?.getByName(this.#name)?.shortLabel || "";
|
||||
el.append(shortLabel);
|
||||
}
|
||||
|
||||
@ -325,7 +400,7 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
// Ammo indicator
|
||||
if (this.getIconOptions().showAmmo){
|
||||
if (this.getIconOptions().showAmmo) {
|
||||
var ammoIndicator = document.createElement("div");
|
||||
ammoIndicator.classList.add("unit-ammo");
|
||||
for (let i = 0; i <= 3; i++)
|
||||
@ -339,7 +414,7 @@ export class Unit extends CustomMarker {
|
||||
summary.classList.add("unit-summary");
|
||||
var callsign = document.createElement("div");
|
||||
callsign.classList.add("unit-callsign");
|
||||
callsign.innerText = this.getData().unitName;
|
||||
callsign.innerText = this.#unitName;
|
||||
var altitude = document.createElement("div");
|
||||
altitude.classList.add("unit-altitude");
|
||||
var speed = document.createElement("div");
|
||||
@ -357,15 +432,15 @@ export class Unit extends CustomMarker {
|
||||
updateVisibility() {
|
||||
var hidden = false;
|
||||
const hiddenUnits = getUnitsManager().getHiddenTypes();
|
||||
if (this.getData().human && hiddenUnits.includes("human"))
|
||||
if (this.#human && hiddenUnits.includes("human"))
|
||||
hidden = true;
|
||||
else if (this.getData().controlled == false && hiddenUnits.includes("dcs"))
|
||||
else if (this.#controlled == false && hiddenUnits.includes("dcs"))
|
||||
hidden = true;
|
||||
else if (hiddenUnits.includes(this.getMarkerCategory()))
|
||||
hidden = true;
|
||||
else if (hiddenUnits.includes(this.getData().coalition))
|
||||
else if (hiddenUnits.includes(this.#coalition))
|
||||
hidden = true;
|
||||
this.setHidden(hidden || !this.getData().alive);
|
||||
this.setHidden(hidden || !this.#alive);
|
||||
}
|
||||
|
||||
setHidden(hidden: boolean) {
|
||||
@ -387,24 +462,24 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
getLeader() {
|
||||
return getUnitsManager().getUnitByID(this.getData().leaderID);
|
||||
return getUnitsManager().getUnitByID(this.#leaderID);
|
||||
}
|
||||
|
||||
canFulfillRole(roles: string | string[]) {
|
||||
if (typeof(roles) === "string")
|
||||
if (typeof (roles) === "string")
|
||||
roles = [roles];
|
||||
|
||||
return this.getDatabase()?.getByName(this.getData().name)?.loadouts.some((loadout: LoadoutBlueprint) => {
|
||||
return (roles as string[]).some((role: string) => {return loadout.roles.includes(role)});
|
||||
return this.getDatabase()?.getByName(this.#name)?.loadouts.some((loadout: LoadoutBlueprint) => {
|
||||
return (roles as string[]).some((role: string) => { return loadout.roles.includes(role) });
|
||||
});
|
||||
}
|
||||
|
||||
/********************** Unit commands *************************/
|
||||
addDestination(latlng: L.LatLng) {
|
||||
if (!this.getData().human) {
|
||||
if (!this.#human) {
|
||||
var path: any = {};
|
||||
if (this.getData().activePath.length > 0) {
|
||||
path = this.getData().activePath;
|
||||
if (this.#activePath.length > 0) {
|
||||
path = this.#activePath;
|
||||
path[(Object.keys(path).length).toString()] = latlng;
|
||||
}
|
||||
else {
|
||||
@ -415,86 +490,86 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
clearDestinations() {
|
||||
if (!this.getData().human)
|
||||
this.getData().activePath = [];
|
||||
if (!this.#human)
|
||||
this.#activePath = [];
|
||||
}
|
||||
|
||||
attackUnit(targetID: number) {
|
||||
/* Units can't attack themselves */
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
if (this.ID != targetID)
|
||||
attackUnit(this.ID, targetID);
|
||||
}
|
||||
|
||||
followUnit(targetID: number, offset: { "x": number, "y": number, "z": number }) {
|
||||
/* Units can't follow themselves */
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
if (this.ID != targetID)
|
||||
followUnit(this.ID, targetID, offset);
|
||||
}
|
||||
|
||||
landAt(latlng: LatLng) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
landAt(this.ID, latlng);
|
||||
}
|
||||
|
||||
changeSpeed(speedChange: string) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
changeSpeed(this.ID, speedChange);
|
||||
}
|
||||
|
||||
changeAltitude(altitudeChange: string) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
changeAltitude(this.ID, altitudeChange);
|
||||
}
|
||||
|
||||
setSpeed(speed: number) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setSpeed(this.ID, speed);
|
||||
}
|
||||
|
||||
setSpeedType(speedType: string) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setSpeedType(this.ID, speedType);
|
||||
}
|
||||
|
||||
setAltitude(altitude: number) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setAltitude(this.ID, altitude);
|
||||
}
|
||||
|
||||
setAltitudeType(altitudeType: string) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setAltitudeType(this.ID, altitudeType);
|
||||
}
|
||||
|
||||
setROE(ROE: string) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setROE(this.ID, ROE);
|
||||
}
|
||||
|
||||
setReactionToThreat(reactionToThreat: string) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setReactionToThreat(this.ID, reactionToThreat);
|
||||
}
|
||||
|
||||
setEmissionsCountermeasures(emissionCountermeasure: string) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setEmissionsCountermeasures(this.ID, emissionCountermeasure);
|
||||
}
|
||||
|
||||
setLeader(isLeader: boolean, wingmenIDs: number[] = []) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setLeader(this.ID, isLeader, wingmenIDs);
|
||||
}
|
||||
|
||||
setOnOff(onOff: boolean) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setOnOff(this.ID, onOff);
|
||||
}
|
||||
|
||||
setFollowRoads(followRoads: boolean) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setFollowRoads(this.ID, followRoads);
|
||||
}
|
||||
|
||||
@ -503,12 +578,12 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
refuel() {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
refuel(this.ID);
|
||||
}
|
||||
|
||||
setAdvancedOptions(isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) {
|
||||
if (!this.getData().human)
|
||||
if (!this.#human)
|
||||
setAdvacedOptions(this.ID, isTanker, isAWACS, TACAN, radio, generalSettings);
|
||||
}
|
||||
|
||||
@ -533,7 +608,7 @@ export class Unit extends CustomMarker {
|
||||
super.onAdd(map);
|
||||
/* If this is the first time adding this unit to the map, remove the temporary marker */
|
||||
if (getUnitsManager().getUnitByID(this.ID) == null)
|
||||
getMap().removeTemporaryMarker(new LatLng(this.getData().position.lat, this.getData().position.lng));
|
||||
getMap().removeTemporaryMarker(new LatLng(this.#position.lat, this.#position.lng));
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -541,7 +616,7 @@ export class Unit extends CustomMarker {
|
||||
#onClick(e: any) {
|
||||
if (!this.#preventClick) {
|
||||
if (getMap().getState() === IDLE || getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
getUnitsManager().deselectAllUnits();
|
||||
this.setSelected(!this.getSelected());
|
||||
}
|
||||
@ -558,34 +633,33 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
#onContextMenu(e: any) {
|
||||
var options: {[key: string]: {text: string, tooltip: string}} = {};
|
||||
var options: { [key: string]: { text: string, tooltip: string } } = {};
|
||||
const selectedUnits = getUnitsManager().getSelectedUnits();
|
||||
const selectedUnitTypes = getUnitsManager().getSelectedUnitsTypes();
|
||||
|
||||
options["center-map"] = {text: "Center map", tooltip: "Center the map on the unit and follow it"};
|
||||
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it" };
|
||||
|
||||
if (selectedUnits.length > 0 && !(selectedUnits.length == 1 && (selectedUnits.includes(this)))) {
|
||||
options["attack"] = {text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons"};
|
||||
options["attack"] = { text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons" };
|
||||
if (getUnitsManager().getSelectedUnitsTypes().length == 1 && getUnitsManager().getSelectedUnitsTypes()[0] === "Aircraft")
|
||||
options["follow"] = {text: "Follow", tooltip: "Follow the unit at a user defined distance and position"};;
|
||||
options["follow"] = { text: "Follow", tooltip: "Follow the unit at a user defined distance and position" };;
|
||||
}
|
||||
else if ((selectedUnits.length > 0 && (selectedUnits.includes(this))) || selectedUnits.length == 0) {
|
||||
if (this.getData().category == "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
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if ((selectedUnits.length === 0 && this.getData().category == "Aircraft") || (selectedUnitTypes.length === 1 && ["Aircraft"].includes(selectedUnitTypes[0])))
|
||||
{
|
||||
if (selectedUnits.concat([this]).every((unit: Unit) => {return unit.canFulfillRole(["CAS", "Strike"])})) {
|
||||
options["bomb"] = {text: "Precision bombing", tooltip: "Precision bombing of a specific point"};
|
||||
options["carpet-bomb"] = {text: "Carpet bombing", tooltip: "Carpet bombing close to a point"};
|
||||
if ((selectedUnits.length === 0 && this.getCategory() == "Aircraft") || (selectedUnitTypes.length === 1 && ["Aircraft"].includes(selectedUnitTypes[0]))) {
|
||||
if (selectedUnits.concat([this]).every((unit: Unit) => { return unit.canFulfillRole(["CAS", "Strike"]) })) {
|
||||
options["bomb"] = { text: "Precision bombing", tooltip: "Precision bombing of a specific point" };
|
||||
options["carpet-bomb"] = { text: "Carpet bombing", tooltip: "Carpet bombing close to a point" };
|
||||
}
|
||||
}
|
||||
|
||||
if ((selectedUnits.length === 0 && this.getData().category == "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"])}))
|
||||
options["fire-at-area"] = {text: "Fire at area", tooltip: "Fire at a large area"};
|
||||
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"]) }))
|
||||
options["fire-at-area"] = { text: "Fire at area", tooltip: "Fire at a large area" };
|
||||
}
|
||||
|
||||
if (Object.keys(options).length > 0) {
|
||||
@ -615,17 +689,17 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
#showFollowOptions(e: any) {
|
||||
var options: {[key: string]: {text: string, tooltip: string}} = {};
|
||||
var options: { [key: string]: { text: string, tooltip: string } } = {};
|
||||
|
||||
options = {
|
||||
'trail': {text: "Trail", tooltip: "Follow unit in trail formation"},
|
||||
'echelon-lh': {text: "Echelon (LH)", tooltip: "Follow unit in echelon left formation"},
|
||||
'echelon-rh': {text: "Echelon (RH)", tooltip: "Follow unit in echelon right formation"},
|
||||
'line-abreast-lh': {text: "Line abreast (LH)", tooltip: "Follow unit in line abreast left formation"},
|
||||
'line-abreast-rh': {text: "Line abreast (RH)", tooltip: "Follow unit in line abreast right formation"},
|
||||
'front': {text: "Front", tooltip: "Fly in front of unit"},
|
||||
'diamond': {text: "Diamond", tooltip: "Follow unit in diamond formation"},
|
||||
'custom': {text: "Custom", tooltip: "Set a custom formation position"},
|
||||
'trail': { text: "Trail", tooltip: "Follow unit in trail formation" },
|
||||
'echelon-lh': { text: "Echelon (LH)", tooltip: "Follow unit in echelon left formation" },
|
||||
'echelon-rh': { text: "Echelon (RH)", tooltip: "Follow unit in echelon right formation" },
|
||||
'line-abreast-lh': { text: "Line abreast (LH)", tooltip: "Follow unit in line abreast left formation" },
|
||||
'line-abreast-rh': { text: "Line abreast (RH)", tooltip: "Follow unit in line abreast right formation" },
|
||||
'front': { text: "Front", tooltip: "Fly in front of unit" },
|
||||
'diamond': { text: "Diamond", tooltip: "Follow unit in diamond formation" },
|
||||
'custom': { text: "Custom", tooltip: "Set a custom formation position" },
|
||||
}
|
||||
|
||||
getMap().getUnitContextMenu().setOptions(options, (option: string) => {
|
||||
@ -651,12 +725,12 @@ export class Unit extends CustomMarker {
|
||||
this.updateVisibility();
|
||||
|
||||
/* Draw the minimap marker */
|
||||
if (this.getData().alive) {
|
||||
if (this.#alive) {
|
||||
if (this.#miniMapMarker == null) {
|
||||
this.#miniMapMarker = new CircleMarker(new LatLng(this.getData().position.lat, this.getData().position.lng), { radius: 0.5 });
|
||||
if (this.getData().coalition == "neutral")
|
||||
this.#miniMapMarker = new CircleMarker(new LatLng(this.#position.lat, this.#position.lng), { radius: 0.5 });
|
||||
if (this.#coalition == "neutral")
|
||||
this.#miniMapMarker.setStyle({ color: "#CFD9E8" });
|
||||
else if (this.getData().coalition == "red")
|
||||
else if (this.#coalition == "red")
|
||||
this.#miniMapMarker.setStyle({ color: "#ff5858" });
|
||||
else
|
||||
this.#miniMapMarker.setStyle({ color: "#247be2" });
|
||||
@ -664,7 +738,7 @@ export class Unit extends CustomMarker {
|
||||
this.#miniMapMarker.bringToBack();
|
||||
}
|
||||
else {
|
||||
this.#miniMapMarker.setLatLng(new LatLng(this.getData().position.lat, this.getData().position.lng));
|
||||
this.#miniMapMarker.setLatLng(new LatLng(this.#position.lat, this.#position.lng));
|
||||
this.#miniMapMarker.bringToBack();
|
||||
}
|
||||
}
|
||||
@ -677,39 +751,39 @@ export class Unit extends CustomMarker {
|
||||
|
||||
/* Draw the marker */
|
||||
if (!this.getHidden()) {
|
||||
this.setLatLng(new LatLng(this.getData().position.lat, this.getData().position.lng));
|
||||
this.setLatLng(new LatLng(this.#position.lat, this.#position.lng));
|
||||
|
||||
var element = this.getElement();
|
||||
if (element != null) {
|
||||
/* Draw the velocity vector */
|
||||
element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.getData().speed / 5}px;`);
|
||||
element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.#speed / 5}px;`);
|
||||
|
||||
/* Set fuel data */
|
||||
element.querySelector(".unit-fuel-level")?.setAttribute("style", `width: ${this.getData().fuel}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-fuel", this.getData().fuel < 20);
|
||||
element.querySelector(".unit-fuel-level")?.setAttribute("style", `width: ${this.#fuel}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-fuel", this.#fuel < 20);
|
||||
|
||||
/* Set dead/alive flag */
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.getData().alive);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
|
||||
|
||||
/* Set current unit state */
|
||||
if (this.getData().human) // Unit is human
|
||||
if (this.#human) // Unit is human
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "human");
|
||||
else if (!this.getData().controlled) // Unit is under DCS control (not Olympus)
|
||||
else if (!this.#controlled) // Unit is under DCS control (not Olympus)
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "dcs");
|
||||
else if ((this.getData().category == "Aircraft" || this.getData().category == "Helicopter") && !this.getData().hasTask)
|
||||
else if ((this.getCategory() == "Aircraft" || this.getCategory() == "Helicopter") && !this.#hasTask)
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "no-task");
|
||||
else // Unit is under Olympus control
|
||||
element.querySelector(".unit")?.setAttribute("data-state", this.getData().state.toLowerCase());
|
||||
element.querySelector(".unit")?.setAttribute("data-state", this.#state.toLowerCase());
|
||||
|
||||
/* Set altitude and speed */
|
||||
if (element.querySelector(".unit-altitude"))
|
||||
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.getData().position.alt as number) / 100));
|
||||
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.#position.alt as number) / 100));
|
||||
if (element.querySelector(".unit-speed"))
|
||||
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.getData().speed))) + "GS";
|
||||
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.#speed))) + "GS";
|
||||
|
||||
/* Rotate elements according to heading */
|
||||
element.querySelectorAll("[data-rotate-to-heading]").forEach(el => {
|
||||
const headingDeg = rad2deg(this.getData().heading);
|
||||
const headingDeg = rad2deg(this.#heading);
|
||||
let currentStyle = el.getAttribute("style") || "";
|
||||
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
|
||||
});
|
||||
@ -724,7 +798,7 @@ export class Unit extends CustomMarker {
|
||||
var newHasFox2 = false;
|
||||
var newHasFox3 = false;
|
||||
var newHasOtherAmmo = false;
|
||||
Object.values(this.getData().ammo).forEach((ammo: any) => {
|
||||
Object.values(this.#ammo).forEach((ammo: any) => {
|
||||
if (ammo.desc.category == 1 && ammo.desc.missileCategory == 1) {
|
||||
if (ammo.desc.guidance == 4 || ammo.desc.guidance == 5)
|
||||
newHasFox1 = true;
|
||||
@ -754,30 +828,30 @@ export class Unit extends CustomMarker {
|
||||
|
||||
/* Set vertical offset for altitude stacking */
|
||||
var pos = getMap().latLngToLayerPoint(this.getLatLng()).round();
|
||||
this.setZIndexOffset(1000 + Math.floor(this.getData().position.alt as number) - pos.y + (this.#highlighted || this.#selected ? 5000 : 0));
|
||||
this.setZIndexOffset(1000 + Math.floor(this.#position.alt as number) - pos.y + (this.#highlighted || this.#selected ? 5000 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
#drawPath() {
|
||||
if (this.getData().activePath != undefined) {
|
||||
if (this.#activePath != undefined) {
|
||||
var points = [];
|
||||
points.push(new LatLng(this.getData().position.lat, this.getData().position.lng));
|
||||
points.push(new LatLng(this.#position.lat, this.#position.lng));
|
||||
|
||||
/* Add markers if missing */
|
||||
while (this.#pathMarkers.length < Object.keys(this.getData().activePath).length) {
|
||||
while (this.#pathMarkers.length < Object.keys(this.#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.getData().activePath).length) {
|
||||
while (this.#pathMarkers.length > Object.keys(this.#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.getData().activePath) {
|
||||
var destination = this.getData().activePath[WP];
|
||||
for (let WP in this.#activePath) {
|
||||
var destination = this.#activePath[WP];
|
||||
this.#pathMarkers[parseInt(WP)].setLatLng([destination.lat, destination.lng]);
|
||||
points.push(new LatLng(destination.lat, destination.lng));
|
||||
this.#pathPolyline.setLatLngs(points);
|
||||
@ -797,12 +871,12 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
#drawDetectedUnits() {
|
||||
for (let index in this.getData().contacts) {
|
||||
var targetData = this.getData().contacts[index];
|
||||
for (let index in this.#contacts) {
|
||||
var targetData = this.#contacts[index];
|
||||
var target = getUnitsManager().getUnitByID(targetData.ID)
|
||||
if (target != null) {
|
||||
var startLatLng = new LatLng(this.getData().position.lat, this.getData().position.lng)
|
||||
var endLatLng = new LatLng(target.getData().position.lat, target.getData().position.lng)
|
||||
var startLatLng = new LatLng(this.#position.lat, this.#position.lng)
|
||||
var endLatLng = new LatLng(target.#position.lat, target.#position.lng)
|
||||
|
||||
var color;
|
||||
if (targetData.detectionMethod === 1)
|
||||
@ -827,31 +901,31 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
#drawTarget() {
|
||||
if (this.getData().targetPosition.lat != 0 && this.getData().targetPosition.lng != 0) {
|
||||
this.#drawtargetPosition(this.getData().targetPosition);
|
||||
if (this.#targetPosition.lat != 0 && this.#targetPosition.lng != 0) {
|
||||
this.#drawtargetPosition(this.#targetPosition);
|
||||
}
|
||||
else if (this.getData().targetID != 0 && getUnitsManager().getUnitByID(this.getData().targetID)) {
|
||||
const position = getUnitsManager().getUnitByID(this.getData().targetID)?.getData().position;
|
||||
else if (this.#targetID != 0 && getUnitsManager().getUnitByID(this.#targetID)) {
|
||||
const position = getUnitsManager().getUnitByID(this.#targetID)?.getData().position;
|
||||
if (position)
|
||||
this.#drawtargetPosition(position);
|
||||
}
|
||||
else
|
||||
else
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
#drawtargetPosition(targetPosition: LatLng) {
|
||||
if (!getMap().hasLayer(this.#targetPositionMarker))
|
||||
if (!getMap().hasLayer(this.#targetPositionMarker))
|
||||
this.#targetPositionMarker.addTo(getMap());
|
||||
if (!getMap().hasLayer(this.#targetPositionPolyline))
|
||||
this.#targetPositionPolyline.addTo(getMap());
|
||||
this.#targetPositionMarker.setLatLng(new LatLng(targetPosition.lat, targetPosition.lng));
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(this.getData().position.lat, this.getData().position.lng), new LatLng(targetPosition.lat, targetPosition.lng)])
|
||||
}
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(this.#position.lat, this.#position.lng), new LatLng(targetPosition.lat, targetPosition.lng)])
|
||||
}
|
||||
|
||||
#clearTarget() {
|
||||
if (getMap().hasLayer(this.#targetPositionMarker))
|
||||
this.#targetPositionMarker.removeFrom(getMap());
|
||||
|
||||
|
||||
if (getMap().hasLayer(this.#targetPositionPolyline))
|
||||
this.#targetPositionPolyline.removeFrom(getMap());
|
||||
}
|
||||
@ -874,8 +948,12 @@ export class AirUnit extends Unit {
|
||||
}
|
||||
|
||||
export class Aircraft extends AirUnit {
|
||||
constructor(ID: number, data: UnitData) {
|
||||
super(ID, data);
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "Aircraft";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@ -884,8 +962,12 @@ export class Aircraft extends AirUnit {
|
||||
}
|
||||
|
||||
export class Helicopter extends AirUnit {
|
||||
constructor(ID: number, data: UnitData) {
|
||||
super(ID, data);
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "Helicopter";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@ -894,8 +976,8 @@ export class Helicopter extends AirUnit {
|
||||
}
|
||||
|
||||
export class GroundUnit extends Unit {
|
||||
constructor(ID: number, data: UnitData) {
|
||||
super(ID, data);
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getIconOptions() {
|
||||
@ -912,14 +994,18 @@ export class GroundUnit extends Unit {
|
||||
};
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "GroundUnit";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return getMarkerCategoryByName(this.getData().name);
|
||||
}
|
||||
}
|
||||
|
||||
export class NavyUnit extends Unit {
|
||||
constructor(ID: number, data: UnitData) {
|
||||
super(ID, data);
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getIconOptions() {
|
||||
@ -936,14 +1022,18 @@ export class NavyUnit extends Unit {
|
||||
};
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "NavyUnit";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "navyunit";
|
||||
}
|
||||
}
|
||||
|
||||
export class Weapon extends Unit {
|
||||
constructor(ID: number, data: UnitData) {
|
||||
super(ID, data);
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
this.setSelectable(false);
|
||||
}
|
||||
|
||||
@ -963,8 +1053,12 @@ export class Weapon extends Unit {
|
||||
}
|
||||
|
||||
export class Missile extends Weapon {
|
||||
constructor(ID: number, data: UnitData) {
|
||||
super(ID, data);
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "Missile";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@ -973,8 +1067,12 @@ export class Missile extends Weapon {
|
||||
}
|
||||
|
||||
export class Bomb extends Weapon {
|
||||
constructor(ID: number, data: UnitData) {
|
||||
super(ID, data);
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "Bomb";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
|
||||
@ -6,9 +6,8 @@ import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng,
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
import { Airbase } from "../missionhandler/airbase";
|
||||
import { groundUnitsDatabase } from "./groundunitsdatabase";
|
||||
import { IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants";
|
||||
import { DataIndexes, IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants";
|
||||
import { DataExtractor } from "./dataextractor";
|
||||
import { UnitData } from "../@types/unit";
|
||||
|
||||
export class UnitsManager {
|
||||
#units: { [ID: number]: Unit };
|
||||
@ -33,8 +32,7 @@ export class UnitsManager {
|
||||
getSelectableAircraft() {
|
||||
const units = this.getUnits();
|
||||
return Object.keys(units).reduce((acc: { [key: number]: Unit }, unitId: any) => {
|
||||
const baseData = units[unitId].getData();
|
||||
if (baseData.category === "Aircraft" && baseData.alive === true) {
|
||||
if (units[unitId].getCategory() === "Aircraft" && units[unitId].getData().alive === true) {
|
||||
acc[unitId] = units[unitId];
|
||||
}
|
||||
return acc;
|
||||
@ -56,12 +54,12 @@ export class UnitsManager {
|
||||
return Object.values(this.#units).filter((unit: Unit) => { return unit.getData().alive && unit.getHotgroup() == hotgroup });
|
||||
}
|
||||
|
||||
addUnit(ID: number, data: UnitData) {
|
||||
if (data.category){
|
||||
addUnit(ID: number, category: string) {
|
||||
if (category){
|
||||
/* The name of the unit category is exactly the same as the constructor name */
|
||||
var constructor = Unit.getConstructor(data.category);
|
||||
var constructor = Unit.getConstructor(category);
|
||||
if (constructor != undefined) {
|
||||
this.#units[ID] = new constructor(ID, data);
|
||||
this.#units[ID] = new constructor(ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -71,42 +69,24 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
update(buffer: ArrayBuffer) {
|
||||
var updatedUnits: Unit[] = [];
|
||||
var dataExtractor = new DataExtractor(buffer);
|
||||
var data: {[key: string]: UnitData} = {};
|
||||
|
||||
var updateTime = Number(dataExtractor.extractUInt64());
|
||||
var offset = dataExtractor.getOffset();
|
||||
while (offset < buffer.byteLength) {
|
||||
const result = dataExtractor.extractData(offset);
|
||||
data[result.data.ID] = result.data;
|
||||
offset = result.offset;
|
||||
|
||||
while (dataExtractor.getSeekPosition() < buffer.byteLength) {
|
||||
const ID = dataExtractor.extractUInt32();
|
||||
if (!(ID in this.#units)) {
|
||||
const datumIndex = dataExtractor.extractUInt8();
|
||||
if (datumIndex == DataIndexes.category) {
|
||||
const category = dataExtractor.extractString();
|
||||
this.addUnit(ID, category);
|
||||
}
|
||||
else {
|
||||
// TODO request a refresh since we must have missed some packets
|
||||
}
|
||||
}
|
||||
this.#units[ID]?.setData(dataExtractor);
|
||||
}
|
||||
|
||||
Object.keys(data)
|
||||
.filter((ID: string) => !(ID in this.#units))
|
||||
.reduce((timeout: number, ID: string) => {
|
||||
window.setTimeout(() => {
|
||||
if (!(ID in this.#units))
|
||||
this.addUnit(parseInt(ID), data[ID]);
|
||||
this.#units[parseInt(ID)]?.setData(data[ID]);
|
||||
}, timeout);
|
||||
return timeout + 10;
|
||||
}, 10);
|
||||
|
||||
Object.keys(data)
|
||||
.filter((ID: string) => ID in this.#units)
|
||||
.forEach((ID: string) => {
|
||||
updatedUnits.push(this.#units[parseInt(ID)]);
|
||||
this.#units[parseInt(ID)]?.setData(data[ID]);
|
||||
});
|
||||
|
||||
// TODO why did we do this?
|
||||
//this.getSelectedUnits().forEach((unit: Unit) => {
|
||||
// if (!updatedUnits.includes(unit))
|
||||
// unit.setData(null);
|
||||
//});
|
||||
|
||||
setLastUpdateTime(updateTime);
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,6 @@ class Aircraft : public AirUnit
|
||||
public:
|
||||
Aircraft(json::value json, unsigned int ID);
|
||||
|
||||
virtual string getCategory() { return "Aircraft"; };
|
||||
|
||||
virtual void changeSpeed(string change);
|
||||
virtual void changeAltitude(string change);
|
||||
};
|
||||
@ -14,7 +14,6 @@ public:
|
||||
|
||||
virtual void setState(unsigned char newState);
|
||||
|
||||
virtual string getCategory() = 0;
|
||||
virtual void changeSpeed(string change) = 0;
|
||||
virtual void changeAltitude(string change) = 0;
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ class GroundUnit : public Unit
|
||||
{
|
||||
public:
|
||||
GroundUnit(json::value json, unsigned int ID);
|
||||
virtual string getCategory() { return "GroundUnit"; };
|
||||
|
||||
virtual void setState(unsigned char newState);
|
||||
|
||||
|
||||
@ -6,8 +6,6 @@ class Helicopter : public AirUnit
|
||||
public:
|
||||
Helicopter(json::value json, unsigned int ID);
|
||||
|
||||
virtual string getCategory() { return "Helicopter"; };
|
||||
|
||||
virtual void changeSpeed(string change);
|
||||
virtual void changeAltitude(string change);
|
||||
};
|
||||
@ -7,7 +7,6 @@ public:
|
||||
NavyUnit(json::value json, unsigned int ID);
|
||||
virtual void AIloop();
|
||||
|
||||
virtual string getCategory() { return "NavyUnit"; };
|
||||
virtual void changeSpeed(string change);
|
||||
|
||||
};
|
||||
@ -12,6 +12,50 @@ using namespace std::chrono;
|
||||
|
||||
#define TASK_CHECK_INIT_VALUE 10
|
||||
|
||||
namespace DataIndex {
|
||||
enum DataIndexes {
|
||||
startOfData = 0,
|
||||
category,
|
||||
alive,
|
||||
human,
|
||||
controlled,
|
||||
coalition,
|
||||
country,
|
||||
name,
|
||||
unitName,
|
||||
groupName,
|
||||
state,
|
||||
task,
|
||||
hasTask,
|
||||
position,
|
||||
speed,
|
||||
heading,
|
||||
isTanker,
|
||||
isAWACS,
|
||||
onOff,
|
||||
followRoads,
|
||||
fuel,
|
||||
desiredSpeed,
|
||||
desiredSpeedType,
|
||||
desiredAltitude,
|
||||
desiredAltitudeType,
|
||||
leaderID,
|
||||
formationOffset,
|
||||
targetID,
|
||||
targetPosition,
|
||||
ROE,
|
||||
reactionToThreat,
|
||||
emissionsCountermeasures,
|
||||
TACAN,
|
||||
radio,
|
||||
generalSettings,
|
||||
ammo,
|
||||
contacts,
|
||||
activePath,
|
||||
endOfData = 255
|
||||
};
|
||||
}
|
||||
|
||||
namespace State
|
||||
{
|
||||
enum States
|
||||
@ -33,7 +77,7 @@ namespace State
|
||||
};
|
||||
|
||||
#pragma pack(push, 1)
|
||||
namespace Options {
|
||||
namespace DataTypes {
|
||||
struct TACAN
|
||||
{
|
||||
bool isOn = false;
|
||||
@ -57,9 +101,7 @@ namespace Options {
|
||||
bool prohibitAfterburner = false;
|
||||
bool prohibitAirWpn = false;
|
||||
};
|
||||
}
|
||||
|
||||
namespace DataTypes {
|
||||
struct Ammo {
|
||||
unsigned short quantity = 0;
|
||||
char name[32];
|
||||
@ -72,31 +114,6 @@ namespace DataTypes {
|
||||
unsigned int ID = 0;
|
||||
unsigned char detectionMethod = 0;
|
||||
};
|
||||
|
||||
struct DataPacket {
|
||||
unsigned int ID;
|
||||
unsigned int bitmask;
|
||||
Coords position;
|
||||
double speed;
|
||||
double heading;
|
||||
unsigned short fuel;
|
||||
double desiredSpeed;
|
||||
double desiredAltitude;
|
||||
unsigned int leaderID;
|
||||
unsigned int targetID;
|
||||
Coords targetPosition;
|
||||
unsigned char coalition;
|
||||
unsigned char state;
|
||||
unsigned char ROE;
|
||||
unsigned char reactionToThreat;
|
||||
unsigned char emissionsCountermeasures;
|
||||
Options::TACAN TACAN;
|
||||
Options::Radio Radio;
|
||||
unsigned short pathLength;
|
||||
unsigned short ammoLength;
|
||||
unsigned short contactsLength;
|
||||
unsigned char taskLength;
|
||||
};
|
||||
}
|
||||
#pragma pack(pop)
|
||||
|
||||
@ -106,209 +123,213 @@ public:
|
||||
Unit(json::value json, unsigned int ID);
|
||||
~Unit();
|
||||
|
||||
/********** Public methods **********/
|
||||
/********** Methods **********/
|
||||
void initialize(json::value json);
|
||||
void setDefaults(bool force = false);
|
||||
unsigned int getID() { return ID; }
|
||||
|
||||
void runAILoop();
|
||||
|
||||
void updateExportData(json::value json, double dt = 0);
|
||||
void updateMissionData(json::value json);
|
||||
unsigned int getDataPacket(char* &data);
|
||||
void getData(stringstream &ss, unsigned long long time, bool refresh);
|
||||
virtual string getCategory() { return "No category"; };
|
||||
|
||||
/********** Base data **********/
|
||||
void setControlled(bool newValue) { updateValue(controlled, newValue); }
|
||||
void setName(string newValue) { updateValue(name, newValue); }
|
||||
void setUnitName(string newValue) { updateValue(unitName, newValue); }
|
||||
void setGroupName(string newValue) { updateValue(groupName, newValue); }
|
||||
void setAlive(bool newValue) { updateValue(alive, newValue); }
|
||||
void setCountry(unsigned int newValue) { updateValue(country, newValue); }
|
||||
void setHuman(bool newValue) { updateValue(human, newValue); }
|
||||
|
||||
bool getControlled() { return controlled; }
|
||||
string getName() { return name; }
|
||||
string getUnitName() { return unitName; }
|
||||
string getGroupName() { return groupName; }
|
||||
bool getAlive() { return alive; }
|
||||
unsigned int getCountry() { return country; }
|
||||
bool getHuman() { return human; }
|
||||
|
||||
/********** Flight data **********/
|
||||
void setPosition(Coords newValue) { updateValue(position, newValue); }
|
||||
void setHeading(double newValue) { updateValue(heading, newValue); }
|
||||
void setSpeed(double newValue) { updateValue(speed, newValue); }
|
||||
|
||||
Coords getPosition() { return position; }
|
||||
double getHeading() { return heading; }
|
||||
double getSpeed() { return speed; }
|
||||
|
||||
/********** Mission data **********/
|
||||
void setFuel(unsigned short newValue) { updateValue(fuel, newValue); }
|
||||
void setAmmo(vector<DataTypes::Ammo> newAmmo) { ammo = newAmmo; }
|
||||
void setContacts(vector<DataTypes::Contact> newContacts) { contacts = newContacts; }
|
||||
void setHasTask(bool newValue) { updateValue(hasTask, newValue); }
|
||||
void setCoalition(unsigned char newValue) { updateValue(coalition, newValue);}
|
||||
|
||||
double getFuel() { return fuel; }
|
||||
vector<DataTypes::Ammo> getAmmo() { return ammo; }
|
||||
vector<DataTypes::Contact> getTargets() { return contacts; }
|
||||
bool getHasTask() { return hasTask; }
|
||||
unsigned int getCoalition() { return coalition; }
|
||||
|
||||
/********** Formation data **********/
|
||||
void setLeaderID(unsigned int newValue) { updateValue(leaderID, newValue); }
|
||||
void setFormationOffset(Offset formationOffset);
|
||||
|
||||
unsigned int getLeaderID() { return leaderID; }
|
||||
Offset getFormationoffset() { return formationOffset; }
|
||||
|
||||
/********** Task data **********/
|
||||
void setTask(string newValue) { updateValue(task, newValue); }
|
||||
void setDesiredSpeed(double newValue);
|
||||
void setDesiredAltitude(double newValue);
|
||||
void setDesiredSpeedType(string newValue);
|
||||
void setDesiredAltitudeType(string newValue);
|
||||
void setActiveDestination(Coords newValue) { updateValue(activeDestination, newValue); }
|
||||
void setActivePath(list<Coords> newValue);
|
||||
void setTargetID(unsigned int newValue) { updateValue(targetID, newValue); }
|
||||
void setTargetPosition(Coords newValue) { updateValue(targetPosition, newValue); }
|
||||
void setIsTanker(bool newValue);
|
||||
void setIsAWACS(bool newValue);
|
||||
virtual void setOnOff(bool newValue) { updateValue(onOff, newValue); };
|
||||
virtual void setFollowRoads(bool newValue) { updateValue(followRoads, newValue); };
|
||||
|
||||
string getTask() { return task; }
|
||||
virtual double getDesiredSpeed() { return desiredSpeed; };
|
||||
virtual double getDesiredAltitude() { return desiredAltitude; };
|
||||
virtual bool getDesiredSpeedType() { return desiredSpeedType; };
|
||||
virtual bool getDesiredAltitudeType() { return desiredAltitudeType; };
|
||||
unsigned int getDataPacket(char*& data);
|
||||
unsigned int getID() { return ID; }
|
||||
void getData(stringstream& ss, unsigned long long time, bool refresh);
|
||||
Coords getActiveDestination() { return activeDestination; }
|
||||
list<Coords> getActivePath() { return activePath; }
|
||||
unsigned int getTargetID() { return targetID; }
|
||||
Coords getTargetPosition() { return targetPosition; }
|
||||
bool getIsTanker() { return isTanker; }
|
||||
bool getIsAWACS() { return isAWACS; }
|
||||
bool getOnOff() { return onOff; };
|
||||
bool getFollowRoads() { return followRoads; };
|
||||
|
||||
/********** Options data **********/
|
||||
void setROE(unsigned char newValue, bool force = false);
|
||||
void setReactionToThreat(unsigned char newValue, bool force = false);
|
||||
void setEmissionsCountermeasures(unsigned char newValue, bool force = false);
|
||||
void setTACAN(Options::TACAN newValue, bool force = false);
|
||||
void setRadio(Options::Radio newValue, bool force = false);
|
||||
void setGeneralSettings(Options::GeneralSettings newValue, bool force = false);
|
||||
void setEPLRS(bool newValue, bool force = false);
|
||||
|
||||
unsigned char getROE() { return ROE; }
|
||||
unsigned char getReactionToThreat() { return reactionToThreat; }
|
||||
unsigned char getEmissionsCountermeasures() { return emissionsCountermeasures; };
|
||||
Options::TACAN getTACAN() { return TACAN; }
|
||||
Options::Radio getRadio() { return radio; }
|
||||
Options::GeneralSettings getGeneralSettings() { return generalSettings; }
|
||||
bool getEPLRS() { return EPLRS; }
|
||||
|
||||
/********** Control functions **********/
|
||||
void landAt(Coords loc);
|
||||
virtual void changeSpeed(string change) {};
|
||||
virtual void changeAltitude(string change) {};
|
||||
bool setActiveDestination();
|
||||
void resetActiveDestination();
|
||||
virtual void setState(unsigned char newState) { state = newState; };
|
||||
void resetTask();
|
||||
void landAt(Coords loc);
|
||||
|
||||
bool updateActivePath(bool looping);
|
||||
void clearActivePath();
|
||||
void pushActivePathFront(Coords newActivePathFront);
|
||||
void pushActivePathBack(Coords newActivePathBack);
|
||||
void popActivePathFront();
|
||||
void triggerUpdate() { lastUpdateTime = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); }
|
||||
unsigned long long getLastUpdateTime() { return lastUpdateTime; };
|
||||
void goToDestination(string enrouteTask = "nil");
|
||||
bool isDestinationReached(double threshold);
|
||||
|
||||
protected:
|
||||
unsigned int ID;
|
||||
|
||||
map<string, Measure*> measures;
|
||||
unsigned int taskCheckCounter = 0;
|
||||
|
||||
/********** Base data **********/
|
||||
bool controlled = false;
|
||||
string name = "undefined";
|
||||
string unitName = "undefined";
|
||||
string groupName = "undefined";
|
||||
bool alive = true;
|
||||
bool human = false;
|
||||
unsigned int country = NULL;
|
||||
|
||||
/********** Flight data **********/
|
||||
Coords position = Coords(NULL);
|
||||
double speed = NULL;
|
||||
double heading = NULL;
|
||||
|
||||
/********** Mission data **********/
|
||||
unsigned short fuel = 0;
|
||||
double initialFuel = 0; // Used internally to detect refueling completed
|
||||
vector<DataTypes::Ammo> ammo;
|
||||
vector<DataTypes::Contact> contacts;
|
||||
bool hasTask = false;
|
||||
unsigned char coalition;
|
||||
|
||||
/********** Formation data **********/
|
||||
unsigned int leaderID = NULL;
|
||||
Offset formationOffset = Offset(NULL);
|
||||
|
||||
/********** Task data **********/
|
||||
string task = "";
|
||||
double desiredSpeed = 0;
|
||||
double desiredAltitude = 0;
|
||||
bool desiredSpeedType = 1;
|
||||
bool desiredAltitudeType = 1;
|
||||
list<Coords> activePath;
|
||||
Coords activeDestination = Coords(NULL);
|
||||
unsigned int targetID = NULL;
|
||||
Coords targetPosition = Coords(NULL);
|
||||
bool isTanker = false;
|
||||
bool isAWACS = false;
|
||||
bool onOff = true;
|
||||
bool followRoads = false;
|
||||
|
||||
/********** Options data **********/
|
||||
unsigned char ROE = ROE::OPEN_FIRE_WEAPON_FREE;
|
||||
unsigned char reactionToThreat = ReactionToThreat::EVADE_FIRE;
|
||||
unsigned char emissionsCountermeasures = EmissionCountermeasure::DEFEND;
|
||||
Options::TACAN TACAN;
|
||||
Options::Radio radio;
|
||||
Options::GeneralSettings generalSettings;
|
||||
bool EPLRS = false;
|
||||
|
||||
/********** Data packet **********/
|
||||
DataTypes::DataPacket dataPacket;
|
||||
|
||||
/********** State machine **********/
|
||||
unsigned char state = State::NONE;
|
||||
|
||||
/********** Other **********/
|
||||
unsigned long long lastUpdateTime = 0;
|
||||
Coords oldPosition = Coords(0); // Used to approximate speed
|
||||
|
||||
/********** Functions **********/
|
||||
string getTargetName();
|
||||
string getLeaderName();
|
||||
bool isTargetAlive();
|
||||
bool isLeaderAlive();
|
||||
virtual void AIloop() = 0;
|
||||
bool isDestinationReached(double threshold);
|
||||
bool setActiveDestination();
|
||||
bool updateActivePath(bool looping);
|
||||
void goToDestination(string enrouteTask = "nil");
|
||||
|
||||
void resetTask();
|
||||
bool checkTaskFailed();
|
||||
void resetTaskFailedCounter();
|
||||
|
||||
void triggerUpdate(unsigned char datumIndex);
|
||||
|
||||
/********** Setters **********/
|
||||
virtual void setCategory(string newValue) { updateValue(category, newValue, DataIndex::category); }
|
||||
virtual void setAlive(bool newValue) { updateValue(alive, newValue, DataIndex::alive); }
|
||||
virtual void setHuman(bool newValue) { updateValue(human, newValue, DataIndex::human); }
|
||||
virtual void setControlled(bool newValue) { updateValue(controlled, newValue, DataIndex::controlled); }
|
||||
virtual void setCoalition(unsigned char newValue) { updateValue(coalition, newValue, DataIndex::coalition); }
|
||||
virtual void setCountry(unsigned int newValue) { updateValue(country, newValue, DataIndex::country); }
|
||||
virtual void setName(string newValue) { updateValue(name, newValue, DataIndex::name); }
|
||||
virtual void setUnitName(string newValue) { updateValue(unitName, newValue, DataIndex::unitName); }
|
||||
virtual void setGroupName(string newValue) { updateValue(groupName, newValue, DataIndex::groupName); }
|
||||
virtual void setState(unsigned char newValue) { updateValue(state, newValue, DataIndex::state); };
|
||||
virtual void setTask(string newValue) { updateValue(task, newValue, DataIndex::task); }
|
||||
virtual void setHasTask(bool newValue) { updateValue(hasTask, newValue, DataIndex::hasTask); }
|
||||
virtual void setPosition(Coords newValue) { updateValue(position, newValue, DataIndex::position); }
|
||||
virtual void setSpeed(double newValue) { updateValue(speed, newValue, DataIndex::speed); }
|
||||
virtual void setHeading(double newValue) { updateValue(heading, newValue, DataIndex::heading); }
|
||||
virtual void setIsTanker(bool newValue);
|
||||
virtual void setIsAWACS(bool newValue);
|
||||
virtual void setOnOff(bool newValue) { updateValue(onOff, newValue, DataIndex::onOff); };
|
||||
virtual void setFollowRoads(bool newValue) { updateValue(followRoads, newValue, DataIndex::followRoads); };
|
||||
virtual void setFuel(unsigned short newValue) { updateValue(fuel, newValue, DataIndex::fuel); }
|
||||
virtual void setDesiredSpeed(double newValue);
|
||||
virtual void setDesiredSpeedType(string newValue);
|
||||
virtual void setDesiredAltitude(double newValue);
|
||||
virtual void setDesiredAltitudeType(string newValue);
|
||||
virtual void setLeaderID(unsigned int newValue) { updateValue(leaderID, newValue, DataIndex::leaderID); }
|
||||
virtual void setFormationOffset(Offset formationOffset);
|
||||
virtual void setTargetID(unsigned int newValue) { updateValue(targetID, newValue, DataIndex::targetID); }
|
||||
virtual void setTargetPosition(Coords newValue) { updateValue(targetPosition, newValue, DataIndex::targetPosition); }
|
||||
virtual void setROE(unsigned char newValue, bool force = false);
|
||||
virtual void setReactionToThreat(unsigned char newValue, bool force = false);
|
||||
virtual void setEmissionsCountermeasures(unsigned char newValue, bool force = false);
|
||||
virtual void setTACAN(DataTypes::TACAN newValue, bool force = false);
|
||||
virtual void setRadio(DataTypes::Radio newValue, bool force = false);
|
||||
virtual void setGeneralSettings(DataTypes::GeneralSettings newValue, bool force = false);
|
||||
virtual void setAmmo(vector<DataTypes::Ammo> newAmmo) { ammo = newAmmo; }
|
||||
virtual void setContacts(vector<DataTypes::Contact> newContacts) { contacts = newContacts; }
|
||||
virtual void setActivePath(list<Coords> newValue);
|
||||
|
||||
/********** Getters **********/
|
||||
virtual string getCategory() { return category; };
|
||||
virtual bool getAlive() { return alive; }
|
||||
virtual bool getHuman() { return human; }
|
||||
virtual bool getControlled() { return controlled; }
|
||||
virtual unsigned int getCoalition() { return coalition; }
|
||||
virtual unsigned int getCountry() { return country; }
|
||||
virtual string getName() { return name; }
|
||||
virtual string getUnitName() { return unitName; }
|
||||
virtual string getGroupName() { return groupName; }
|
||||
virtual unsigned char getState() { return state; }
|
||||
virtual string getTask() { return task; }
|
||||
virtual bool getHasTask() { return hasTask; }
|
||||
virtual Coords getPosition() { return position; }
|
||||
virtual double getSpeed() { return speed; }
|
||||
virtual double getHeading() { return heading; }
|
||||
virtual bool getIsTanker() { return isTanker; }
|
||||
virtual bool getIsAWACS() { return isAWACS; }
|
||||
virtual bool getOnOff() { return onOff; };
|
||||
virtual bool getFollowRoads() { return followRoads; };
|
||||
virtual double getFuel() { return fuel; }
|
||||
virtual double getDesiredSpeed() { return desiredSpeed; };
|
||||
virtual bool getDesiredSpeedType() { return desiredSpeedType; };
|
||||
virtual double getDesiredAltitude() { return desiredAltitude; };
|
||||
virtual bool getDesiredAltitudeType() { return desiredAltitudeType; };
|
||||
virtual unsigned int getLeaderID() { return leaderID; }
|
||||
virtual Offset getFormationoffset() { return formationOffset; }
|
||||
virtual unsigned int getTargetID() { return targetID; }
|
||||
virtual Coords getTargetPosition() { return targetPosition; }
|
||||
virtual unsigned char getROE() { return ROE; }
|
||||
virtual unsigned char getReactionToThreat() { return reactionToThreat; }
|
||||
virtual unsigned char getEmissionsCountermeasures() { return emissionsCountermeasures; };
|
||||
virtual DataTypes::TACAN getTACAN() { return TACAN; }
|
||||
virtual DataTypes::Radio getRadio() { return radio; }
|
||||
virtual DataTypes::GeneralSettings getGeneralSettings() { return generalSettings; }
|
||||
virtual vector<DataTypes::Ammo> getAmmo() { return ammo; }
|
||||
virtual vector<DataTypes::Contact> getTargets() { return contacts; }
|
||||
virtual list<Coords> getActivePath() { return activePath; }
|
||||
|
||||
protected:
|
||||
unsigned int ID;
|
||||
|
||||
string category;
|
||||
bool alive = true;
|
||||
bool human = false;
|
||||
bool controlled = false;
|
||||
unsigned char coalition;
|
||||
unsigned int country = NULL;
|
||||
string name = "undefined";
|
||||
string unitName = "undefined";
|
||||
string groupName = "undefined";
|
||||
unsigned char state = State::NONE;
|
||||
string task = "";
|
||||
bool hasTask = false;
|
||||
Coords position = Coords(NULL);
|
||||
double speed = NULL;
|
||||
double heading = NULL;
|
||||
bool isTanker = false;
|
||||
bool isAWACS = false;
|
||||
bool onOff = true;
|
||||
bool followRoads = false;
|
||||
unsigned short fuel = 0;
|
||||
double desiredSpeed = 0;
|
||||
bool desiredSpeedType = 1;
|
||||
double desiredAltitude = 0;
|
||||
bool desiredAltitudeType = 1;
|
||||
unsigned int leaderID = NULL;
|
||||
Offset formationOffset = Offset(NULL);
|
||||
unsigned int targetID = NULL;
|
||||
Coords targetPosition = Coords(NULL);
|
||||
unsigned char ROE = ROE::OPEN_FIRE_WEAPON_FREE;
|
||||
unsigned char reactionToThreat = ReactionToThreat::EVADE_FIRE;
|
||||
unsigned char emissionsCountermeasures = EmissionCountermeasure::DEFEND;
|
||||
DataTypes::TACAN TACAN;
|
||||
DataTypes::Radio radio;
|
||||
DataTypes::GeneralSettings generalSettings;
|
||||
vector<DataTypes::Ammo> ammo;
|
||||
vector<DataTypes::Contact> contacts;
|
||||
list<Coords> activePath;
|
||||
|
||||
/********** Other **********/
|
||||
unsigned int taskCheckCounter = 0;
|
||||
Coords activeDestination = Coords(NULL);
|
||||
double initialFuel = 0;
|
||||
Coords oldPosition = Coords(0);
|
||||
map<unsigned char, unsigned long long> updateTimeMap;
|
||||
|
||||
/********** Private methods **********/
|
||||
virtual void AIloop() = 0;
|
||||
|
||||
/********** Template methods **********/
|
||||
template <typename T>
|
||||
void updateValue(T& value, T& newValue)
|
||||
void updateValue(T& value, T& newValue, unsigned char datumIndex)
|
||||
{
|
||||
if (newValue != value)
|
||||
{
|
||||
triggerUpdate();
|
||||
triggerUpdate(datumIndex);
|
||||
*(&value) = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void appendNumeric(stringstream& ss, const unsigned char& datumIndex, T& datumValue) {
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&datumValue, sizeof(T));
|
||||
}
|
||||
|
||||
void appendString(stringstream& ss, const unsigned char& datumIndex, string& datumValue) {
|
||||
const unsigned short size = datumValue.size();
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&size, sizeof(unsigned short));
|
||||
ss << datumValue;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void appendVector(stringstream& ss, const unsigned char& datumIndex, vector<T>& datumValue) {
|
||||
const unsigned short size = datumValue.size();
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&size, sizeof(unsigned short));
|
||||
ss.write((const char*)&datumValue, size * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void appendList(stringstream& ss, const unsigned char& datumIndex, list<T>& datumValue) {
|
||||
const unsigned short size = datumValue.size();
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&size, sizeof(unsigned short));
|
||||
|
||||
for (auto el: datumValue)
|
||||
ss.write((const char*)&el, sizeof(T));
|
||||
}
|
||||
};
|
||||
|
||||
@ -5,9 +5,6 @@ class Weapon : public Unit
|
||||
{
|
||||
public:
|
||||
Weapon(json::value json, unsigned int ID);
|
||||
|
||||
virtual string getCategory() = 0;
|
||||
|
||||
protected:
|
||||
/* Weapons are not controllable and have no AIloop */
|
||||
virtual void AIloop() {};
|
||||
@ -17,14 +14,10 @@ class Missile : public Weapon
|
||||
{
|
||||
public:
|
||||
Missile(json::value json, unsigned int ID);
|
||||
|
||||
virtual string getCategory() { return "Missile"; };
|
||||
};
|
||||
|
||||
class Bomb : public Weapon
|
||||
{
|
||||
public:
|
||||
Bomb(json::value json, unsigned int ID);
|
||||
|
||||
virtual string getCategory() { return "Bomb"; };
|
||||
};
|
||||
@ -17,10 +17,10 @@ Aircraft::Aircraft(json::value json, unsigned int ID) : AirUnit(json, ID)
|
||||
{
|
||||
log("New Aircraft created with ID: " + to_string(ID));
|
||||
|
||||
double desiredSpeed = knotsToMs(300);
|
||||
double desiredAltitude = ftToM(20000);
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
setDesiredAltitude(desiredAltitude);
|
||||
setCategory("Aircraft");
|
||||
setDesiredSpeed(knotsToMs(300));
|
||||
setDesiredAltitude(ftToM(20000));
|
||||
|
||||
};
|
||||
|
||||
void Aircraft::changeSpeed(string change)
|
||||
|
||||
@ -17,8 +17,8 @@ GroundUnit::GroundUnit(json::value json, unsigned int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Ground Unit created with ID: " + to_string(ID));
|
||||
|
||||
double desiredSpeed = 10;
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
setCategory("GroundUnit");
|
||||
setDesiredSpeed(10);
|
||||
};
|
||||
|
||||
void GroundUnit::setState(unsigned char newState)
|
||||
|
||||
@ -17,10 +17,9 @@ Helicopter::Helicopter(json::value json, unsigned int ID) : AirUnit(json, ID)
|
||||
{
|
||||
log("New Helicopter created with ID: " + to_string(ID));
|
||||
|
||||
double desiredSpeed = knotsToMs(100);
|
||||
double desiredAltitude = ftToM(5000);
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
setDesiredAltitude(desiredAltitude);
|
||||
setCategory("Helicopter");
|
||||
setDesiredSpeed(knotsToMs(100));
|
||||
setDesiredAltitude(ftToM(5000));
|
||||
};
|
||||
|
||||
void Helicopter::changeSpeed(string change)
|
||||
|
||||
@ -17,8 +17,8 @@ NavyUnit::NavyUnit(json::value json, unsigned int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Navy Unit created with ID: " + to_string(ID));
|
||||
|
||||
double desiredSpeed = 10;
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
setCategory("NavyUnit");
|
||||
setDesiredSpeed(10);
|
||||
};
|
||||
|
||||
void NavyUnit::AIloop()
|
||||
|
||||
@ -288,7 +288,7 @@ void Scheduler::handleRequest(string key, json::value value)
|
||||
unit->setIsAWACS(value[L"isAWACS"].as_bool());
|
||||
|
||||
/* TACAN Options */
|
||||
Options::TACAN TACAN;
|
||||
DataTypes::TACAN TACAN;
|
||||
TACAN.isOn = value[L"TACAN"][L"isOn"].as_bool();
|
||||
TACAN.channel = static_cast<unsigned char>(value[L"TACAN"][L"channel"].as_number().to_uint32());
|
||||
TACAN.XY = to_string(value[L"TACAN"][L"XY"]).at(0);
|
||||
|
||||
@ -16,20 +16,20 @@ extern Scheduler* scheduler;
|
||||
extern UnitsManager* unitsManager;
|
||||
|
||||
// TODO: Make dedicated file
|
||||
bool operator==(const Options::TACAN& lhs, const Options::TACAN& rhs)
|
||||
bool operator==(const DataTypes::TACAN& lhs, const DataTypes::TACAN& rhs)
|
||||
{
|
||||
return lhs.isOn == rhs.isOn && lhs.channel == rhs.channel && lhs.XY == rhs.XY && lhs.callsign == rhs.callsign;
|
||||
}
|
||||
|
||||
bool operator==(const Options::Radio& lhs, const Options::Radio& rhs)
|
||||
bool operator==(const DataTypes::Radio& lhs, const DataTypes::Radio& rhs)
|
||||
{
|
||||
return lhs.frequency == rhs.frequency && lhs.callsign == rhs.callsign && lhs.callsignNumber == rhs.callsignNumber;
|
||||
}
|
||||
|
||||
bool operator==(const Options::GeneralSettings& lhs, const Options::GeneralSettings& rhs)
|
||||
bool operator==(const DataTypes::GeneralSettings& lhs, const DataTypes::GeneralSettings& rhs)
|
||||
{
|
||||
return lhs.prohibitAA == rhs.prohibitAA && lhs.prohibitAfterburner == rhs.prohibitAfterburner && lhs.prohibitAG == rhs.prohibitAG &&
|
||||
lhs.prohibitAirWpn == rhs.prohibitAirWpn && lhs.prohibitJettison == rhs.prohibitJettison;
|
||||
lhs.prohibitAirWpn == rhs.prohibitAirWpn && lhs.prohibitJettison == rhs.prohibitJettison;
|
||||
}
|
||||
|
||||
Unit::Unit(json::value json, unsigned int ID) :
|
||||
@ -87,7 +87,6 @@ void Unit::setDefaults(bool force)
|
||||
strcpy_s(TACAN.callsign, 4, "TKR");
|
||||
setTACAN(TACAN, force);
|
||||
setRadio(radio, force);
|
||||
setEPLRS(EPLRS, force);
|
||||
setGeneralSettings(generalSettings, force);
|
||||
}
|
||||
|
||||
@ -101,7 +100,7 @@ void Unit::runAILoop() {
|
||||
const bool isUnitAlive = getAlive();
|
||||
const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this);
|
||||
if (!(isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits)) return;
|
||||
|
||||
|
||||
if (checkTaskFailed() && state != State::IDLE && State::LAND)
|
||||
setState(State::IDLE);
|
||||
|
||||
@ -113,14 +112,14 @@ void Unit::updateExportData(json::value json, double dt)
|
||||
Coords newPosition = Coords(NULL);
|
||||
double newHeading = 0;
|
||||
double newSpeed = 0;
|
||||
|
||||
|
||||
if (json.has_object_field(L"LatLongAlt"))
|
||||
{
|
||||
setPosition({
|
||||
json[L"LatLongAlt"][L"Lat"].as_number().to_double(),
|
||||
json[L"LatLongAlt"][L"Long"].as_number().to_double(),
|
||||
json[L"LatLongAlt"][L"Alt"].as_number().to_double()
|
||||
});
|
||||
});
|
||||
}
|
||||
if (json.has_number_field(L"Heading"))
|
||||
setHeading(json[L"Heading"].as_number().to_double());
|
||||
@ -133,7 +132,7 @@ void Unit::updateExportData(json::value json, double dt)
|
||||
if (dt > 0)
|
||||
setSpeed(getSpeed() * 0.95 + (dist / dt) * 0.05);
|
||||
}
|
||||
|
||||
|
||||
oldPosition = position;
|
||||
}
|
||||
|
||||
@ -180,120 +179,67 @@ void Unit::updateMissionData(json::value json)
|
||||
setHasTask(json[L"hasTask"].as_bool());
|
||||
}
|
||||
|
||||
unsigned int Unit::getDataPacket(char* &data)
|
||||
|
||||
void Unit::getData(stringstream& ss, unsigned long long time, bool refresh)
|
||||
{
|
||||
/* Reserve data for:
|
||||
1) DataPacket;
|
||||
2) Active path;
|
||||
3) Ammo vector;
|
||||
4) Contacts vector;
|
||||
*/
|
||||
data = (char*)malloc(sizeof(DataTypes::DataPacket) +
|
||||
activePath.size() * sizeof(Coords) +
|
||||
ammo.size() * sizeof(Coords) +
|
||||
contacts.size() * sizeof(Coords));
|
||||
unsigned int offset = 0;
|
||||
|
||||
/* Prepare the data packet and copy it to memory */
|
||||
unsigned int bitmask = 0;
|
||||
bitmask |= alive << 0;
|
||||
bitmask |= human << 1;
|
||||
bitmask |= controlled << 2;
|
||||
bitmask |= hasTask << 3;
|
||||
bitmask |= desiredAltitudeType << 16;
|
||||
bitmask |= desiredSpeedType << 17;
|
||||
bitmask |= isTanker << 18;
|
||||
bitmask |= isAWACS << 19;
|
||||
bitmask |= onOff << 20;
|
||||
bitmask |= followRoads << 21;
|
||||
bitmask |= EPLRS << 22;
|
||||
bitmask |= generalSettings.prohibitAA << 23;
|
||||
bitmask |= generalSettings.prohibitAfterburner << 24;
|
||||
bitmask |= generalSettings.prohibitAG << 25;
|
||||
bitmask |= generalSettings.prohibitAirWpn << 26;
|
||||
bitmask |= generalSettings.prohibitJettison << 27;
|
||||
/* If the unit is in a group, get the update data from the group leader and only replace the position: speed and heading */
|
||||
//if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this)) {
|
||||
// DataTypes::DataPacket* p = (DataTypes::DataPacket*)data;
|
||||
// p->position = position;
|
||||
// p->speed = speed;
|
||||
// p->heading = heading;
|
||||
//}
|
||||
|
||||
DataTypes::DataPacket dataPacket{
|
||||
ID,
|
||||
bitmask,
|
||||
position,
|
||||
speed,
|
||||
heading,
|
||||
fuel,
|
||||
desiredSpeed,
|
||||
desiredAltitude,
|
||||
leaderID,
|
||||
targetID,
|
||||
targetPosition,
|
||||
state,
|
||||
ROE,
|
||||
reactionToThreat,
|
||||
emissionsCountermeasures,
|
||||
coalition,
|
||||
TACAN,
|
||||
radio,
|
||||
activePath.size(),
|
||||
ammo.size(),
|
||||
contacts.size(),
|
||||
task.size()
|
||||
};
|
||||
const unsigned char startOfData = DataIndex::startOfData;
|
||||
const unsigned char endOfData = DataIndex::endOfData;
|
||||
|
||||
memcpy(data + offset, &dataPacket, sizeof(dataPacket));
|
||||
offset += sizeof(dataPacket);
|
||||
|
||||
/* Prepare the path vector and copy it to memory */
|
||||
for (const Coords& c : activePath) {
|
||||
memcpy(data + offset, &c, sizeof(Coords));
|
||||
offset += sizeof(Coords);
|
||||
ss.write((const char*)&ID, sizeof(ID));
|
||||
ss.write((const char*)&startOfData, sizeof(startOfData));
|
||||
for (auto d : updateTimeMap) {
|
||||
if (d.second > time) {
|
||||
switch (d.first) {
|
||||
case DataIndex::category: appendString(ss, d.first, category); break;
|
||||
case DataIndex::alive: appendNumeric(ss, d.first, alive); break;
|
||||
case DataIndex::human: appendNumeric(ss, d.first, human); break;
|
||||
case DataIndex::controlled: appendNumeric(ss, d.first, controlled); break;
|
||||
case DataIndex::coalition: appendNumeric(ss, d.first, coalition); break;
|
||||
case DataIndex::country: appendNumeric(ss, d.first, country); break;
|
||||
case DataIndex::name: appendString(ss, d.first, name); break;
|
||||
case DataIndex::unitName: appendString(ss, d.first, unitName); break;
|
||||
case DataIndex::groupName: appendString(ss, d.first, groupName); break;
|
||||
case DataIndex::state: appendNumeric(ss, d.first, state); break;
|
||||
case DataIndex::task: appendString(ss, d.first, task); break;
|
||||
case DataIndex::hasTask: appendNumeric(ss, d.first, hasTask); break;
|
||||
case DataIndex::position: appendNumeric(ss, d.first, position); break;
|
||||
case DataIndex::speed: appendNumeric(ss, d.first, speed); break;
|
||||
case DataIndex::heading: appendNumeric(ss, d.first, heading); break;
|
||||
case DataIndex::isTanker: appendNumeric(ss, d.first, isTanker); break;
|
||||
case DataIndex::isAWACS: appendNumeric(ss, d.first, isAWACS); break;
|
||||
case DataIndex::onOff: appendNumeric(ss, d.first, onOff); break;
|
||||
case DataIndex::followRoads: appendNumeric(ss, d.first, followRoads); break;
|
||||
case DataIndex::fuel: appendNumeric(ss, d.first, fuel); break;
|
||||
case DataIndex::desiredSpeed: appendNumeric(ss, d.first, desiredSpeed); break;
|
||||
case DataIndex::desiredSpeedType: appendNumeric(ss, d.first, desiredSpeedType); break;
|
||||
case DataIndex::desiredAltitude: appendNumeric(ss, d.first, desiredAltitude); break;
|
||||
case DataIndex::desiredAltitudeType: appendNumeric(ss, d.first, desiredAltitudeType); break;
|
||||
case DataIndex::leaderID: appendNumeric(ss, d.first, leaderID); break;
|
||||
case DataIndex::formationOffset: appendNumeric(ss, d.first, formationOffset); break;
|
||||
case DataIndex::targetID: appendNumeric(ss, d.first, targetID); break;
|
||||
case DataIndex::targetPosition: appendNumeric(ss, d.first, targetPosition); break;
|
||||
case DataIndex::ROE: appendNumeric(ss, d.first, ROE); break;
|
||||
case DataIndex::reactionToThreat: appendNumeric(ss, d.first, reactionToThreat); break;
|
||||
case DataIndex::emissionsCountermeasures: appendNumeric(ss, d.first, emissionsCountermeasures); break;
|
||||
case DataIndex::TACAN: appendNumeric(ss, d.first, TACAN); break;
|
||||
case DataIndex::radio: appendNumeric(ss, d.first, radio); break;
|
||||
case DataIndex::generalSettings: appendNumeric(ss, d.first, generalSettings); break;
|
||||
case DataIndex::ammo: appendVector(ss, d.first, ammo); break;
|
||||
case DataIndex::contacts: appendVector(ss, d.first, contacts); break;
|
||||
case DataIndex::activePath: appendList(ss, d.first, activePath); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Copy the ammo vector to memory */
|
||||
memcpy(data + offset, &ammo, ammo.size() * sizeof(DataTypes::Ammo));
|
||||
offset += ammo.size() * sizeof(DataTypes::Ammo);
|
||||
|
||||
/* Copy the contacts vector to memory */
|
||||
memcpy(data + offset, &contacts, contacts.size() * sizeof(DataTypes::Contact));
|
||||
offset += contacts.size() * sizeof(DataTypes::Contact);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
void Unit::getData(stringstream &ss, unsigned long long time, bool refresh)
|
||||
{
|
||||
if (time > lastUpdateTime && !refresh) return;
|
||||
|
||||
char* data;
|
||||
unsigned int size = getDataPacket(data);
|
||||
|
||||
/* Prepare the data packet and copy it to memory */
|
||||
/* If the unit is in a group, get the update data from the group leader and only replace the position, speed and heading */
|
||||
if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this)) {
|
||||
DataTypes::DataPacket* p = (DataTypes::DataPacket*)data;
|
||||
p->position = position;
|
||||
p->speed = speed;
|
||||
p->heading = heading;
|
||||
}
|
||||
|
||||
ss.write(data, size);
|
||||
ss << task;
|
||||
|
||||
unsigned short nameLength = name.length();
|
||||
unsigned short unitNameLength = unitName.length();
|
||||
unsigned short groupNameLength = groupName.length();
|
||||
unsigned short categoryLength = getCategory().length();
|
||||
|
||||
ss.write((char*)&nameLength, sizeof(nameLength));
|
||||
ss.write((char*)&unitNameLength, sizeof(unitNameLength));
|
||||
ss.write((char*)&groupNameLength, sizeof(groupNameLength));
|
||||
ss.write((char*)&categoryLength, sizeof(categoryLength));
|
||||
|
||||
ss << name;
|
||||
ss << unitName;
|
||||
ss << groupName;
|
||||
ss << getCategory();
|
||||
|
||||
|
||||
delete data;
|
||||
ss.write((const char*)&endOfData, sizeof(endOfData));
|
||||
}
|
||||
|
||||
void Unit::setActivePath(list<Coords> newPath)
|
||||
@ -393,17 +339,17 @@ void Unit::setFormationOffset(Offset newFormationOffset)
|
||||
formationOffset = newFormationOffset;
|
||||
resetTask();
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::formationOffset);
|
||||
}
|
||||
|
||||
void Unit::setROE(unsigned char newROE, bool force)
|
||||
void Unit::setROE(unsigned char newROE, bool force)
|
||||
{
|
||||
if (ROE != newROE || force) {
|
||||
ROE = newROE;
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ROE, static_cast<unsigned int>(ROE)));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::ROE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -415,7 +361,7 @@ void Unit::setReactionToThreat(unsigned char newReactionToThreat, bool force)
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::REACTION_ON_THREAT, static_cast<unsigned int>(reactionToThreat)));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::reactionToThreat);
|
||||
}
|
||||
}
|
||||
|
||||
@ -465,39 +411,38 @@ void Unit::setEmissionsCountermeasures(unsigned char newEmissionsCountermeasures
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ECM_USING, ECMEnum));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::emissionsCountermeasures);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::landAt(Coords loc)
|
||||
void Unit::landAt(Coords loc)
|
||||
{
|
||||
clearActivePath();
|
||||
pushActivePathBack(loc);
|
||||
setState(State::LAND);
|
||||
}
|
||||
|
||||
void Unit::setIsTanker(bool newIsTanker)
|
||||
{
|
||||
void Unit::setIsTanker(bool newIsTanker)
|
||||
{
|
||||
if (isTanker != newIsTanker) {
|
||||
isTanker = newIsTanker;
|
||||
resetTask();
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::isTanker);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setIsAWACS(bool newIsAWACS)
|
||||
{
|
||||
{
|
||||
if (isAWACS != newIsAWACS) {
|
||||
isAWACS = newIsAWACS;
|
||||
resetTask();
|
||||
setEPLRS(isAWACS);
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::isAWACS);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setTACAN(Options::TACAN newTACAN, bool force)
|
||||
void Unit::setTACAN(DataTypes::TACAN newTACAN, bool force)
|
||||
{
|
||||
if (TACAN != newTACAN || force)
|
||||
{
|
||||
@ -528,11 +473,11 @@ void Unit::setTACAN(Options::TACAN newTACAN, bool force)
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::TACAN);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setRadio(Options::Radio newRadio, bool force)
|
||||
void Unit::setRadio(DataTypes::Radio newRadio, bool force)
|
||||
{
|
||||
if (radio != newRadio || force)
|
||||
{
|
||||
@ -564,30 +509,11 @@ void Unit::setRadio(Options::Radio newRadio, bool force)
|
||||
command = dynamic_cast<Command*>(new SetCommand(groupName, commandSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::radio);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setEPLRS(bool newEPLRS, bool force)
|
||||
{
|
||||
//addMeasure("EPLRS", json::value(newEPLRS));
|
||||
//
|
||||
//if (EPLRS != newEPLRS || force) {
|
||||
// EPLRS = newEPLRS;
|
||||
//
|
||||
// std::ostringstream commandSS;
|
||||
// commandSS << "{"
|
||||
// << "id = 'EPLRS',"
|
||||
// << "params = {"
|
||||
// << "value = " << (EPLRS ? "true" : "false") << ", "
|
||||
// << "}"
|
||||
// << "}";
|
||||
// Command* command = dynamic_cast<Command*>(new SetCommand(ID, commandSS.str()));
|
||||
// scheduler->appendCommand(command);
|
||||
//}
|
||||
}
|
||||
|
||||
void Unit::setGeneralSettings(Options::GeneralSettings newGeneralSettings, bool force)
|
||||
void Unit::setGeneralSettings(DataTypes::GeneralSettings newGeneralSettings, bool force)
|
||||
{
|
||||
if (generalSettings != newGeneralSettings)
|
||||
{
|
||||
@ -605,22 +531,22 @@ void Unit::setGeneralSettings(Options::GeneralSettings newGeneralSettings, bool
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ENGAGE_AIR_WEAPONS, !generalSettings.prohibitAirWpn));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::generalSettings);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setDesiredSpeed(double newDesiredSpeed)
|
||||
void Unit::setDesiredSpeed(double newDesiredSpeed)
|
||||
{
|
||||
desiredSpeed = newDesiredSpeed;
|
||||
desiredSpeed = newDesiredSpeed;
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::desiredSpeed);
|
||||
}
|
||||
|
||||
void Unit::setDesiredAltitude(double newDesiredAltitude)
|
||||
void Unit::setDesiredAltitude(double newDesiredAltitude)
|
||||
{
|
||||
desiredAltitude = newDesiredAltitude;
|
||||
if (state == State::IDLE)
|
||||
@ -628,10 +554,10 @@ void Unit::setDesiredAltitude(double newDesiredAltitude)
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::desiredAltitude);
|
||||
}
|
||||
|
||||
void Unit::setDesiredSpeedType(string newDesiredSpeedType)
|
||||
void Unit::setDesiredSpeedType(string newDesiredSpeedType)
|
||||
{
|
||||
desiredSpeedType = newDesiredSpeedType.compare("GS") == 0;
|
||||
if (state == State::IDLE)
|
||||
@ -639,10 +565,10 @@ void Unit::setDesiredSpeedType(string newDesiredSpeedType)
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::desiredSpeedType);
|
||||
}
|
||||
|
||||
void Unit::setDesiredAltitudeType(string newDesiredAltitudeType)
|
||||
void Unit::setDesiredAltitudeType(string newDesiredAltitudeType)
|
||||
{
|
||||
desiredAltitudeType = newDesiredAltitudeType.compare("AGL") == 0;
|
||||
if (state == State::IDLE)
|
||||
@ -650,14 +576,14 @@ void Unit::setDesiredAltitudeType(string newDesiredAltitudeType)
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::desiredAltitudeType);
|
||||
}
|
||||
|
||||
void Unit::goToDestination(string enrouteTask)
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new Move(groupName, activeDestination, getDesiredSpeed(), getDesiredSpeedType()? "GS": "CAS", getDesiredAltitude(), getDesiredAltitudeType()? "AGL" : "ASL", enrouteTask, getCategory()));
|
||||
Command* command = dynamic_cast<Command*>(new Move(groupName, activeDestination, getDesiredSpeed(), getDesiredSpeedType() ? "GS" : "CAS", getDesiredAltitude(), getDesiredAltitudeType() ? "AGL" : "ASL", enrouteTask, getCategory()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
@ -668,7 +594,7 @@ bool Unit::isDestinationReached(double threshold)
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
/* Check if any unit in the group has reached the point */
|
||||
for (auto const& p: unitsManager->getGroupMembers(groupName))
|
||||
for (auto const& p : unitsManager->getGroupMembers(groupName))
|
||||
{
|
||||
double dist = 0;
|
||||
Geodesic::WGS84().Inverse(p->getPosition().lat, p->getPosition().lng, activeDestination.lat, activeDestination.lng, dist);
|
||||
@ -694,7 +620,7 @@ bool Unit::setActiveDestination()
|
||||
activeDestination = activePath.front();
|
||||
log(unitName + " active destination set to queue front");
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::activePath);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -702,7 +628,7 @@ bool Unit::setActiveDestination()
|
||||
activeDestination = Coords(0);
|
||||
log(unitName + " active destination set to NULL");
|
||||
|
||||
triggerUpdate();
|
||||
triggerUpdate(DataIndex::activePath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -723,9 +649,9 @@ bool Unit::updateActivePath(bool looping)
|
||||
}
|
||||
}
|
||||
|
||||
bool Unit::checkTaskFailed()
|
||||
bool Unit::checkTaskFailed()
|
||||
{
|
||||
if (getHasTask())
|
||||
if (getHasTask())
|
||||
return false;
|
||||
else {
|
||||
if (taskCheckCounter > 0)
|
||||
@ -737,3 +663,7 @@ bool Unit::checkTaskFailed()
|
||||
void Unit::resetTaskFailedCounter() {
|
||||
taskCheckCounter = TASK_CHECK_INIT_VALUE;
|
||||
}
|
||||
|
||||
void Unit::triggerUpdate(unsigned char datumIndex) {
|
||||
updateTimeMap[datumIndex] = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
@ -22,10 +22,12 @@ Weapon::Weapon(json::value json, unsigned int ID) : Unit(json, ID)
|
||||
Missile::Missile(json::value json, unsigned int ID) : Weapon(json, ID)
|
||||
{
|
||||
log("New Missile created with ID: " + to_string(ID));
|
||||
setCategory("Missile");
|
||||
};
|
||||
|
||||
/* Bomb */
|
||||
Bomb::Bomb(json::value json, unsigned int ID) : Weapon(json, ID)
|
||||
{
|
||||
log("New Bomb created with ID: " + to_string(ID));
|
||||
setCategory("Bomb");
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user