diff --git a/client/src/@types/unit.d.ts b/client/src/@types/unit.d.ts index 703b8852..ec6aaa39 100644 --- a/client/src/@types/unit.d.ts +++ b/client/src/@types/unit.d.ts @@ -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 } \ No newline at end of file diff --git a/client/src/atc/atcboard.ts b/client/src/atc/atcboard.ts index a65f55bd..832cd691 100644 --- a/client/src/atc/atcboard.ts +++ b/client/src/atc/atcboard.ts @@ -119,7 +119,7 @@ export abstract class ATCBoard { const unitCanBeAdded = () => { - if ( baseData.category !== "Aircraft" ) { + if ( unit.getCategory() !== "Aircraft" ) { return false; } diff --git a/client/src/atc/unitdatatable.ts b/client/src/atc/unitdatatable.ts index f54a4553..1f06fc9c 100644 --- a/client/src/atc/unitdatatable.ts +++ b/client/src/atc/unitdatatable.ts @@ -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); } diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 1cff2f7d..aa9c1189 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -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}; \ No newline at end of file +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 +}; \ No newline at end of file diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index 6043539d..39f95722 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -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 ""; +} \ No newline at end of file diff --git a/client/src/units/dataextractor.ts b/client/src/units/dataextractor.ts index d3265a9a..7b5eaf32 100644 --- a/client/src/units/dataextractor.ts +++ b/client/src/units/dataextractor.ts @@ -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; } } \ No newline at end of file diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index c71d0d3f..fe90f149 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -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")) - (element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.getData().position.alt as number) / 100)); + (element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.#position.alt as number) / 100)); if (element.querySelector(".unit-speed")) - (element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.getData().speed))) + "GS"; + (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() { diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index c9124df6..73246697 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -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); } diff --git a/src/core/include/aircraft.h b/src/core/include/aircraft.h index 87c48a5f..be27e35b 100644 --- a/src/core/include/aircraft.h +++ b/src/core/include/aircraft.h @@ -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); }; \ No newline at end of file diff --git a/src/core/include/airunit.h b/src/core/include/airunit.h index 7c72c88d..04ebb1ee 100644 --- a/src/core/include/airunit.h +++ b/src/core/include/airunit.h @@ -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; diff --git a/src/core/include/groundunit.h b/src/core/include/groundunit.h index fcb8da22..13b236e2 100644 --- a/src/core/include/groundunit.h +++ b/src/core/include/groundunit.h @@ -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); diff --git a/src/core/include/helicopter.h b/src/core/include/helicopter.h index 94f3d09b..3c72693b 100644 --- a/src/core/include/helicopter.h +++ b/src/core/include/helicopter.h @@ -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); }; \ No newline at end of file diff --git a/src/core/include/navyunit.h b/src/core/include/navyunit.h index d1c6e31e..c82ee83d 100644 --- a/src/core/include/navyunit.h +++ b/src/core/include/navyunit.h @@ -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); }; \ No newline at end of file diff --git a/src/core/include/unit.h b/src/core/include/unit.h index 2022b0d4..c9670358 100644 --- a/src/core/include/unit.h +++ b/src/core/include/unit.h @@ -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 newAmmo) { ammo = newAmmo; } - void setContacts(vector newContacts) { contacts = newContacts; } - void setHasTask(bool newValue) { updateValue(hasTask, newValue); } - void setCoalition(unsigned char newValue) { updateValue(coalition, newValue);} - - double getFuel() { return fuel; } - vector getAmmo() { return ammo; } - vector 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 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 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(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 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 ammo; - vector 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 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 newAmmo) { ammo = newAmmo; } + virtual void setContacts(vector newContacts) { contacts = newContacts; } + virtual void setActivePath(list 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 getAmmo() { return ammo; } + virtual vector getTargets() { return contacts; } + virtual list 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 ammo; + vector contacts; + list activePath; + + /********** Other **********/ + unsigned int taskCheckCounter = 0; + Coords activeDestination = Coords(NULL); + double initialFuel = 0; + Coords oldPosition = Coords(0); + map updateTimeMap; + + /********** Private methods **********/ + virtual void AIloop() = 0; + + /********** Template methods **********/ template - void updateValue(T& value, T& newValue) + void updateValue(T& value, T& newValue, unsigned char datumIndex) { if (newValue != value) { - triggerUpdate(); + triggerUpdate(datumIndex); *(&value) = newValue; } } + + template + 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 + void appendVector(stringstream& ss, const unsigned char& datumIndex, vector& 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 + void appendList(stringstream& ss, const unsigned char& datumIndex, list& 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)); + } }; diff --git a/src/core/include/weapon.h b/src/core/include/weapon.h index 1597ffec..96744423 100644 --- a/src/core/include/weapon.h +++ b/src/core/include/weapon.h @@ -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"; }; }; \ No newline at end of file diff --git a/src/core/src/aircraft.cpp b/src/core/src/aircraft.cpp index c1cca92e..e8a932dd 100644 --- a/src/core/src/aircraft.cpp +++ b/src/core/src/aircraft.cpp @@ -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) diff --git a/src/core/src/groundunit.cpp b/src/core/src/groundunit.cpp index dc61510c..1811565d 100644 --- a/src/core/src/groundunit.cpp +++ b/src/core/src/groundunit.cpp @@ -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) diff --git a/src/core/src/helicopter.cpp b/src/core/src/helicopter.cpp index 5215a484..6d24022e 100644 --- a/src/core/src/helicopter.cpp +++ b/src/core/src/helicopter.cpp @@ -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) diff --git a/src/core/src/navyunit.cpp b/src/core/src/navyunit.cpp index b3746803..7b86fb72 100644 --- a/src/core/src/navyunit.cpp +++ b/src/core/src/navyunit.cpp @@ -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() diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index 98c855ff..bcfe724e 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -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(value[L"TACAN"][L"channel"].as_number().to_uint32()); TACAN.XY = to_string(value[L"TACAN"][L"XY"]).at(0); diff --git a/src/core/src/unit.cpp b/src/core/src/unit.cpp index 3d2650b2..18748335 100644 --- a/src/core/src/unit.cpp +++ b/src/core/src/unit.cpp @@ -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 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(new SetOption(groupName, SetCommandType::ROE, static_cast(ROE))); scheduler->appendCommand(command); - triggerUpdate(); + triggerUpdate(DataIndex::ROE); } } @@ -415,7 +361,7 @@ void Unit::setReactionToThreat(unsigned char newReactionToThreat, bool force) Command* command = dynamic_cast(new SetOption(groupName, SetCommandType::REACTION_ON_THREAT, static_cast(reactionToThreat))); scheduler->appendCommand(command); - triggerUpdate(); + triggerUpdate(DataIndex::reactionToThreat); } } @@ -465,39 +411,38 @@ void Unit::setEmissionsCountermeasures(unsigned char newEmissionsCountermeasures command = dynamic_cast(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(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(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(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(new Move(groupName, activeDestination, getDesiredSpeed(), getDesiredSpeedType()? "GS": "CAS", getDesiredAltitude(), getDesiredAltitudeType()? "AGL" : "ASL", enrouteTask, getCategory())); + Command* command = dynamic_cast(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(system_clock::now().time_since_epoch()).count(); +} diff --git a/src/core/src/weapon.cpp b/src/core/src/weapon.cpp index 3308c1e8..64a1850a 100644 --- a/src/core/src/weapon.cpp +++ b/src/core/src/weapon.cpp @@ -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"); }; \ No newline at end of file