diff --git a/client/src/atc/unitdatatable.ts b/client/src/atc/unitdatatable.ts index 1f06fc9c..54b0674e 100644 --- a/client/src/atc/unitdatatable.ts +++ b/client/src/atc/unitdatatable.ts @@ -12,8 +12,8 @@ export class UnitDataTable extends Panel { var units = getUnitsManager().getUnits(); const unitsArray = Object.values(units).sort((a: Unit, b: Unit) => { - const aVal = a.getData().unitName?.toLowerCase(); - const bVal = b.getData().unitName?.toLowerCase(); + const aVal = a.getUnitName()?.toLowerCase(); + const bVal = b.getUnitName()?.toLowerCase(); if (aVal > bVal) { return 1; @@ -48,7 +48,7 @@ export class UnitDataTable extends Panel { for (const unit of unitsArray) { - const dataset = [unit.getData().unitName, unit.getData().name, unit.getCategory(), (unit.getData().controlled) ? "AI" : "Human"]; + const dataset = [unit.getUnitName(), unit.getName(), unit.getCategory(), (unit.getControlled()) ? "AI" : "Human"]; addRow(el, dataset); } diff --git a/client/src/map/drawingmarker.ts b/client/src/map/drawingcursor.ts similarity index 100% rename from client/src/map/drawingmarker.ts rename to client/src/map/drawingcursor.ts diff --git a/client/src/map/map.ts b/client/src/map/map.ts index c81c2e82..18c74d99 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -16,7 +16,7 @@ import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_ import { TargetMarker } from "./targetmarker"; import { CoalitionArea } from "./coalitionarea"; import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu"; -import { DrawingCursor } from "./drawingmarker"; +import { DrawingCursor } from "./drawingcursor"; L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect); @@ -37,6 +37,8 @@ export class Map extends L.Map { #panUp: boolean = false; #panDown: boolean = false; #lastMousePosition: L.Point = new L.Point(0, 0); + #shiftKey: boolean = false; + #ctrlKey: boolean = false; #centerUnit: Unit | null = null; #miniMap: ClickableMiniMap | null = null; #miniMapLayerGroup: L.LayerGroup; @@ -454,7 +456,7 @@ export class Map extends L.Map { if (!e.originalEvent.ctrlKey) { getUnitsManager().selectedUnitsClearDestinations(); } - getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, e.originalEvent.shiftKey, this.#destinationGroupRotation) + getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation) this.#destinationGroupRotation = 0; this.#destinationRotationCenter = null; this.#computeDestinationRotation = false; @@ -513,13 +515,13 @@ export class Map extends L.Map { this.#lastMousePosition.x = e.originalEvent.x; this.#lastMousePosition.y = e.originalEvent.y; - this.#updateCursor(e); + this.#updateCursor(); if (this.#state === MOVE_UNIT) { /* Update the position of the destination cursors depeding on mouse rotation */ if (this.#computeDestinationRotation && this.#destinationRotationCenter != null) this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng); - this.#updateDestinationCursors(e); + this.#updateDestinationCursors(); } else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) { this.#targetCursor.setLatLng(this.getMouseCoordinates()); @@ -532,13 +534,17 @@ export class Map extends L.Map { } #onKeyDown(e: any) { - this.#updateDestinationCursors(e); - this.#updateCursor(e); + this.#shiftKey = e.originalEvent.shiftKey; + this.#ctrlKey = e.originalEvent.ctrlKey; + this.#updateCursor(); + this.#updateDestinationCursors(); } #onKeyUp(e: any) { - this.#updateDestinationCursors(e); - this.#updateCursor(e); + this.#shiftKey = e.originalEvent.shiftKey; + this.#ctrlKey = e.originalEvent.ctrlKey; + this.#updateCursor(); + this.#updateDestinationCursors(); } #onZoom(e: any) { @@ -547,7 +553,7 @@ export class Map extends L.Map { } #panToUnit(unit: Unit) { - var unitPosition = new L.LatLng(unit.getData().position.lat, unit.getData().position.lng); + var unitPosition = new L.LatLng(unit.getPosition().lat, unit.getPosition().lng); this.setView(unitPosition, this.getZoom(), { animate: false }); } @@ -582,38 +588,41 @@ export class Map extends L.Map { document.getElementById(this.#ID)?.classList.add("hidden-cursor"); } - #showDestinationCursors(singleCursor: boolean) { - /* Don't create the cursors if there already are the correct number of them available */ - if (singleCursor || getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length != this.#destinationPreviewCursors.length) { - /* Reset the cursors to start from a clean condition */ - this.#hideDestinationCursors(); + #showDestinationCursors() { + const singleCursor = !this.#shiftKey; + const selectedUnitsCount = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length; + if (selectedUnitsCount > 0) { + if (singleCursor && this.#destinationPreviewCursors.length != 1) { + this.#hideDestinationCursors(); + var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false }); + marker.addTo(this); + this.#destinationPreviewCursors = [marker]; + } + else if (!singleCursor) { + while (this.#destinationPreviewCursors.length > selectedUnitsCount) { + this.removeLayer(this.#destinationPreviewCursors[0]); + this.#destinationPreviewCursors.splice(0, 1); + } - if (getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length > 0) { - if (singleCursor) { - var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false }); - marker.addTo(this); - this.#destinationPreviewCursors = [marker]; - } - else { - /* Create the cursors. If a group is selected only one cursor is shown for it, because you can't control single units in a group */ - this.#destinationPreviewCursors = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).map((unit: Unit) => { - var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false }); - marker.addTo(this); - return marker; - }); + while (this.#destinationPreviewCursors.length < selectedUnitsCount) { + var cursor = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false }); + cursor.addTo(this); + this.#destinationPreviewCursors.push(cursor); } + + this.#updateDestinationCursors(); } } } - #updateDestinationCursors(e: any) { + #updateDestinationCursors() { const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates(); if (this.#destinationPreviewCursors.length == 1) this.#destinationPreviewCursors[0].setLatLng(this.getMouseCoordinates()); else { Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => { if (idx < this.#destinationPreviewCursors.length) - this.#destinationPreviewCursors[idx].setLatLng(e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates()); + this.#destinationPreviewCursors[idx].setLatLng(this.#shiftKey? latlng : this.getMouseCoordinates()); }) }; } @@ -653,9 +662,9 @@ export class Map extends L.Map { this.#drawingCursor.removeFrom(this); } - #updateCursor(e?: any) { + #updateCursor() { /* If the ctrl key is being pressed or we are performing an area selection, show the default cursor */ - if (e?.originalEvent.ctrlKey || this.#selecting) { + if (this.#ctrlKey || this.#selecting) { /* Hide all non default cursors */ this.#hideDestinationCursors(); this.#hideTargetCursor(); @@ -671,7 +680,7 @@ export class Map extends L.Map { /* Show the active cursor depending on the active state */ if (this.#state === IDLE || this.#state === COALITIONAREA_INTERACT) this.#showDefaultCursor(); - else if (this.#state === MOVE_UNIT) this.#showDestinationCursors(e && e.originaleEvent && !e.originalEvent.shiftKey); + else if (this.#state === MOVE_UNIT) this.#showDestinationCursors(); else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor(); else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor(); } diff --git a/client/src/panels/mouseinfopanel.ts b/client/src/panels/mouseinfopanel.ts index dfb48d92..3a09436f 100644 --- a/client/src/panels/mouseinfopanel.ts +++ b/client/src/panels/mouseinfopanel.ts @@ -36,7 +36,7 @@ export class MouseInfoPanel extends Panel { var selectedUnitPosition = null; var selectedUnits = getUnitsManager().getSelectedUnits(); if (selectedUnits && selectedUnits.length == 1) - selectedUnitPosition = new LatLng(selectedUnits[0].getData().position.lat, selectedUnits[0].getData().position.lng); + selectedUnitPosition = new LatLng(selectedUnits[0].getPosition().lat, selectedUnits[0].getPosition().lng); /* Draw measures from selected unit, from pin location, and from bullseyes */ this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition); diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index df2b1b0a..0b43acae 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -99,13 +99,13 @@ export class UnitControlPanel extends Panel { if (units.length < 20) { this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => { var button = document.createElement("button"); - var callsign = unit.getData().unitName || ""; - var label = unit.getDatabase()?.getByName(unit.getData().name)?.label || unit.getData().name; + var callsign = unit.getUnitName() || ""; + var label = unit.getDatabase()?.getByName(unit.getName())?.label || unit.getName(); button.setAttribute("data-label", label); button.setAttribute("data-callsign", callsign); - button.setAttribute("data-coalition", unit.getData().coalition); + button.setAttribute("data-coalition", unit.getCoalition()); button.classList.add("pill", "highlight-coalition") button.addEventListener("click", () => { @@ -140,12 +140,12 @@ export class UnitControlPanel extends Panel { element.toggleAttribute("data-show-advanced-settings-button", units.length == 1); /* Flight controls */ - var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredAltitude}); - var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredAltitudeType}); - var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredSpeed}); - var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().desiredSpeedType}); - var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().onOff}); - var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getData().followRoads}); + var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()}); + var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitudeType()}); + var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeed()}); + var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeedType()}); + var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getOnOff()}); + var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getFollowRoads()}); if (selectedUnitsTypes.length == 1) { this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "AGL": undefined, false); @@ -171,15 +171,15 @@ export class UnitControlPanel extends Panel { /* Option buttons */ this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => { - button.classList.toggle("selected", units.every((unit: Unit) => unit.getData().ROE === button.value)) + button.classList.toggle("selected", units.every((unit: Unit) => unit.getROE() === button.value)) }); this.#optionButtons["reactionToThreat"].forEach((button: HTMLButtonElement) => { - button.classList.toggle("selected", units.every((unit: Unit) => unit.getData().reactionToThreat === button.value)) + button.classList.toggle("selected", units.every((unit: Unit) => unit.getReactionToThreat() === button.value)) }); this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => { - button.classList.toggle("selected", units.every((unit: Unit) => unit.getData().emissionsCountermeasures === button.value)) + button.classList.toggle("selected", units.every((unit: Unit) => unit.getEmissionsCountermeasures() === button.value)) }); this.#onOffSwitch.setValue(onOff, false); @@ -208,11 +208,11 @@ export class UnitControlPanel extends Panel { const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement; const unit = units[0]; - const roles = aircraftDatabase.getByName(unit.getData().name)?.loadouts.map((loadout) => {return loadout.roles}) + const roles = aircraftDatabase.getByName(unit.getName())?.loadouts.map((loadout) => {return loadout.roles}) const tanker = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker"); const AWACS = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS"); - const radioMHz = Math.floor(unit.getData().radio.frequency / 1000000); - const radioDecimals = (unit.getData().radio.frequency / 1000000 - radioMHz) * 1000; + const radioMHz = Math.floor(unit.getRadio().frequency / 1000000); + const radioDecimals = (unit.getRadio().frequency / 1000000 - radioMHz) * 1000; /* Activate the correct options depending on unit type */ this.#advancedSettingsDialog.toggleAttribute("data-show-settings", !tanker && !AWACS); @@ -224,28 +224,28 @@ export class UnitControlPanel extends Panel { /* Set common properties */ // Name - unitNameEl.innerText = unit.getData().unitName; + unitNameEl.innerText = unit.getUnitName(); // General settings - prohibitJettisonCheckbox.checked = unit.getData().generalSettings.prohibitJettison; - prohibitAfterburnerCheckbox.checked = unit.getData().generalSettings.prohibitAfterburner; - prohibitAACheckbox.checked = unit.getData().generalSettings.prohibitAA; - prohibitAGCheckbox.checked = unit.getData().generalSettings.prohibitAG; - prohibitAirWpnCheckbox.checked = unit.getData().generalSettings.prohibitAirWpn; + prohibitJettisonCheckbox.checked = unit.getGeneralSettings().prohibitJettison; + prohibitAfterburnerCheckbox.checked = unit.getGeneralSettings().prohibitAfterburner; + prohibitAACheckbox.checked = unit.getGeneralSettings().prohibitAA; + prohibitAGCheckbox.checked = unit.getGeneralSettings().prohibitAG; + prohibitAirWpnCheckbox.checked = unit.getGeneralSettings().prohibitAirWpn; // Tasking - tankerCheckbox.checked = unit.getData().isTanker; - AWACSCheckbox.checked = unit.getData().isAWACS; + tankerCheckbox.checked = unit.getIsTanker(); + AWACSCheckbox.checked = unit.getIsAWACS(); // TACAN - TACANCheckbox.checked = unit.getData().TACAN.isOn; - TACANChannelInput.value = String(unit.getData().TACAN.channel); - TACANCallsignInput.value = String(unit.getData().TACAN.callsign); - this.#TACANXYDropdown.setValue(unit.getData().TACAN.XY); + TACANCheckbox.checked = unit.getTACAN().isOn; + TACANChannelInput.value = String(unit.getTACAN().channel); + TACANCallsignInput.value = String(unit.getTACAN().callsign); + this.#TACANXYDropdown.setValue(unit.getTACAN().XY); // Radio radioMhzInput.value = String(radioMHz); - radioCallsignNumberInput.value = String(unit.getData().radio.callsignNumber); + radioCallsignNumberInput.value = String(unit.getRadio().callsignNumber); this.#radioDecimalsDropdown.setValue("." + radioDecimals); if (tanker) /* Set tanker specific options */ @@ -256,7 +256,7 @@ export class UnitControlPanel extends Panel { this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]); // This must be done after setting the options - if (!this.#radioCallsignDropdown.selectValue(unit.getData().radio.callsign - 1)) // Ensure the selected value is in the acceptable range + if (!this.#radioCallsignDropdown.selectValue(unit.getRadio().callsign - 1)) // Ensure the selected value is in the acceptable range this.#radioCallsignDropdown.selectValue(0); } } diff --git a/client/src/panels/unitinfopanel.ts b/client/src/panels/unitinfopanel.ts index c6e7d724..cedb32bc 100644 --- a/client/src/panels/unitinfopanel.ts +++ b/client/src/panels/unitinfopanel.ts @@ -57,16 +57,16 @@ export class UnitInfoPanel extends Panel { /* Set the unit info */ this.#unitLabel.innerText = aircraftDatabase.getByName(baseData.name)?.label || baseData.name; this.#unitName.innerText = baseData.unitName; - if (unit.getData().human) + if (unit.getHuman()) this.#unitControl.innerText = "Human"; else if (baseData.controlled) this.#unitControl.innerText = "Olympus controlled"; else this.#unitControl.innerText = "DCS Controlled"; - this.#fuelBar.style.width = String(unit.getData().fuel + "%"); - this.#fuelPercentage.dataset.percentage = "" + unit.getData().fuel; - this.#currentTask.dataset.currentTask = unit.getData().task !== "" ? unit.getData().task : "No task"; - this.#currentTask.dataset.coalition = unit.getData().coalition; + this.#fuelBar.style.width = String(unit.getFuel() + "%"); + this.#fuelPercentage.dataset.percentage = "" + unit.getFuel(); + this.#currentTask.dataset.currentTask = unit.getTask() !== "" ? unit.getTask() : "No task"; + this.#currentTask.dataset.coalition = unit.getCoalition(); this.#silhouette.src = `/images/units/${unit.getDatabase()?.getByName(baseData.name)?.filename}`; this.#silhouette.classList.toggle("hide", unit.getDatabase()?.getByName(baseData.name)?.filename == undefined || unit.getDatabase()?.getByName(baseData.name)?.filename == ''); @@ -75,9 +75,9 @@ export class UnitInfoPanel extends Panel { const items = this.#loadoutContainer.querySelector("#loadout-items"); if (items) { - const ammo = Object.values(unit.getData().ammo); + const ammo = Object.values(unit.getAmmo()); if (ammo.length > 0) { - items.replaceChildren(...Object.values(unit.getData().ammo).map( + items.replaceChildren(...Object.values(unit.getAmmo()).map( (ammo: Ammo) => { var el = document.createElement("div"); el.dataset.qty = `${ammo.quantity}`; diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 0bc5ee69..2d10fa62 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -73,7 +73,7 @@ export class Unit extends CustomMarker { }; #ammo: Ammo[] = []; #contacts: Contact[] = []; - #activePath: LatLng[] = []; + #activePath: LatLng[] = []; #selectable: boolean; #selected: boolean = false; @@ -89,6 +89,43 @@ export class Unit extends CustomMarker { #timer: number = 0; #hotgroup: number | null = null; + getAlive() {return this.#alive}; + getHuman() {return this.#human}; + getControlled() {return this.#controlled}; + getCoalition() {return this.#coalition}; + getCountry() {return this.#country}; + getName() {return this.#name}; + getUnitName() {return this.#unitName}; + getGroupName() {return this.#groupName}; + getState() {return this.#state}; + getTask() {return this.#task}; + getHasTask() {return this.#hasTask}; + getPosition() {return this.#position}; + getSpeed() {return this.#speed}; + getHeading() {return this.#heading}; + getIsTanker() {return this.#isTanker}; + getIsAWACS() {return this.#isAWACS}; + getOnOff() {return this.#onOff}; + getFollowRoads() {return this.#followRoads}; + getFuel() {return this.#fuel}; + getDesiredSpeed() {return this.#desiredSpeed}; + getDesiredSpeedType() {return this.#desiredSpeedType}; + getDesiredAltitude() {return this.#desiredAltitude}; + getDesiredAltitudeType() {return this.#desiredAltitudeType}; + getLeaderID() {return this.#leaderID}; + getFormationOffset() {return this.#formationOffset}; + getTargetID() {return this.#targetID}; + getTargetPosition() {return this.#targetPosition}; + getROE() {return this.#ROE}; + getReactionToThreat() {return this.#reactionToThreat}; + getEmissionsCountermeasures() {return this.#emissionsCountermeasures}; + getTACAN() {return this.#TACAN}; + getRadio() {return this.#radio}; + getGeneralSettings() {return this.#generalSettings}; + getAmmo() {return this.#ammo}; + getContacts() {return this.#contacts}; + getActivePath() {return this.#activePath}; + static getConstructor(type: string) { if (type === "GroundUnit") return GroundUnit; if (type === "Aircraft") return Aircraft; @@ -242,10 +279,8 @@ export class Unit extends CustomMarker { } } - getMarkerCategory() { - // Overloaded by child classes - // TODO convert to use getMarkerCategoryByName - return ""; + getMarkerCategory(): string { + return getMarkerCategoryByName(this.getName()); } getDatabase(): UnitDatabase | null { @@ -737,8 +772,10 @@ export class Unit extends CustomMarker { this.#miniMapMarker.bringToBack(); } else { - this.#miniMapMarker.setLatLng(new LatLng(this.#position.lat, this.#position.lng)); - this.#miniMapMarker.bringToBack(); + if (this.#miniMapMarker.getLatLng().lat !== this.getPosition().lat && this.#miniMapMarker.getLatLng().lng !== this.getPosition().lng) { + this.#miniMapMarker.setLatLng(new LatLng(this.#position.lat, this.#position.lng)); + this.#miniMapMarker.bringToBack(); + } } } else { @@ -750,7 +787,9 @@ export class Unit extends CustomMarker { /* Draw the marker */ if (!this.getHidden()) { - this.setLatLng(new LatLng(this.#position.lat, this.#position.lng)); + if (this.getLatLng().lat !== this.getPosition().lat && this.getLatLng().lng !== this.getPosition().lng) { + this.setLatLng(new LatLng(this.#position.lat, this.#position.lng)); + } var element = this.getElement(); if (element != null) { @@ -904,7 +943,7 @@ export class Unit extends CustomMarker { this.#drawtargetPosition(this.#targetPosition); } else if (this.#targetID != 0 && getUnitsManager().getUnitByID(this.#targetID)) { - const position = getUnitsManager().getUnitByID(this.#targetID)?.getData().position; + const position = getUnitsManager().getUnitByID(this.#targetID)?.getPosition(); if (position) this.#drawtargetPosition(position); } @@ -954,10 +993,6 @@ export class Aircraft extends AirUnit { getCategory() { return "Aircraft"; } - - getMarkerCategory() { - return "aircraft"; - } } export class Helicopter extends AirUnit { @@ -968,10 +1003,6 @@ export class Helicopter extends AirUnit { getCategory() { return "Helicopter"; } - - getMarkerCategory() { - return "helicopter"; - } } export class GroundUnit extends Unit { @@ -996,10 +1027,6 @@ export class GroundUnit extends Unit { getCategory() { return "GroundUnit"; } - - getMarkerCategory() { - return getMarkerCategoryByName(this.getData().name); - } } export class NavyUnit extends Unit { @@ -1024,10 +1051,6 @@ export class NavyUnit extends Unit { getCategory() { return "NavyUnit"; } - - getMarkerCategory() { - return "navyunit"; - } } export class Weapon extends Unit { diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 73246697..5d222291 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -32,7 +32,7 @@ export class UnitsManager { getSelectableAircraft() { const units = this.getUnits(); return Object.keys(units).reduce((acc: { [key: number]: Unit }, unitId: any) => { - if (units[unitId].getCategory() === "Aircraft" && units[unitId].getData().alive === true) { + if (units[unitId].getCategory() === "Aircraft" && units[unitId].getAlive() === true) { acc[unitId] = units[unitId]; } return acc; @@ -51,7 +51,7 @@ export class UnitsManager { } getUnitsByHotgroup(hotgroup: number) { - return Object.values(this.#units).filter((unit: Unit) => { return unit.getData().alive && unit.getHotgroup() == hotgroup }); + return Object.values(this.#units).filter((unit: Unit) => { return unit.getAlive() && unit.getHotgroup() == hotgroup }); } addUnit(ID: number, category: string) { @@ -114,7 +114,7 @@ export class UnitsManager { this.deselectAllUnits(); for (let ID in this.#units) { if (this.#units[ID].getHidden() == false) { - var latlng = new LatLng(this.#units[ID].getData().position.lat, this.#units[ID].getData().position.lng); + var latlng = new LatLng(this.#units[ID].getPosition().lat, this.#units[ID].getPosition().lng); if (bounds.contains(latlng)) { this.#units[ID].setSelected(true); } @@ -131,11 +131,11 @@ export class UnitsManager { } if (options) { if (options.excludeHumans) - selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getData().human }); + selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getHuman() }); if (options.onlyOnePerGroup) { var temp: Unit[] = []; for (let unit of selectedUnits) { - if (!temp.some((otherUnit: Unit) => unit.getData().groupName == otherUnit.getData().groupName)) + if (!temp.some((otherUnit: Unit) => unit.getGroupName() == otherUnit.getGroupName())) temp.push(unit); } selectedUnits = temp; @@ -180,7 +180,7 @@ export class UnitsManager { if (this.getSelectedUnits().length == 0) return undefined; return this.getSelectedUnits().map((unit: Unit) => { - return unit.getData().coalition + return unit.getCoalition() })?.reduce((a: any, b: any) => { return a == b ? a : undefined }); @@ -200,8 +200,8 @@ export class UnitsManager { for (let idx in selectedUnits) { const unit = selectedUnits[idx]; /* If a unit is following another unit, and that unit is also selected, send the command to the followed unit */ - if (unit.getData().state === "Follow") { - const leader = this.getUnitByID(unit.getData().leaderID) + if (unit.getState() === "Follow") { + const leader = this.getUnitByID(unit.getLeaderID()) if (leader && leader.getSelected()) leader.addDestination(latlng); else @@ -220,8 +220,8 @@ export class UnitsManager { var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }); for (let idx in selectedUnits) { const unit = selectedUnits[idx]; - if (unit.getData().state === "Follow") { - const leader = this.getUnitByID(unit.getData().leaderID) + if (unit.getState() === "Follow") { + const leader = this.getUnitByID(unit.getLeaderID()) if (leader && leader.getSelected()) leader.clearDestinations(); else @@ -332,13 +332,13 @@ export class UnitsManager { for (let idx in selectedUnits) { selectedUnits[idx].attackUnit(ID); } - this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getData().unitName}`); + this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`); } selectedUnitsDelete(explosion: boolean = false) { var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */ const selectionContainsAHuman = selectedUnits.some( ( unit:Unit ) => { - return unit.getData().human === true; + return unit.getHuman() === true; }); if (selectionContainsAHuman && !confirm( "Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?" ) ) { @@ -399,7 +399,7 @@ export class UnitsManager { } count++; } - this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getData().unitName}`); + this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getUnitName()}`); } selectedUnitsSetHotgroup(hotgroup: number) { @@ -421,7 +421,7 @@ export class UnitsManager { /* Compute the center of the group */ var center = { x: 0, y: 0 }; selectedUnits.forEach((unit: Unit) => { - var mercator = latLngToMercator(unit.getData().position.lat, unit.getData().position.lng); + var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng); center.x += mercator.x / selectedUnits.length; center.y += mercator.y / selectedUnits.length; }); @@ -429,7 +429,7 @@ export class UnitsManager { /* Compute the distances from the center of the group */ var unitDestinations: { [key: number]: LatLng } = {}; selectedUnits.forEach((unit: Unit) => { - var mercator = latLngToMercator(unit.getData().position.lat, unit.getData().position.lat); + var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng); var distancesFromCenter = { dx: mercator.x - center.x, dy: mercator.y - center.y }; /* Rotate the distance according to the group rotation */ @@ -559,8 +559,8 @@ export class UnitsManager { #showActionMessage(units: Unit[], message: string) { if (units.length == 1) - getInfoPopup().setText(`${units[0].getData().unitName} ${message}`); + getInfoPopup().setText(`${units[0].getUnitName()} ${message}`); else if (units.length > 1) - getInfoPopup().setText(`${units[0].getData().unitName} and ${units.length - 1} other units ${message}`); + getInfoPopup().setText(`${units[0].getUnitName()} and ${units.length - 1} other units ${message}`); } } \ No newline at end of file diff --git a/src/core/include/unit.h b/src/core/include/unit.h index c380878e..b3c12707 100644 --- a/src/core/include/unit.h +++ b/src/core/include/unit.h @@ -53,6 +53,7 @@ namespace DataIndex { ammo, contacts, activePath, + lastIndex, endOfData = 255 }; } @@ -94,7 +95,7 @@ public: unsigned int getDataPacket(char*& data); unsigned int getID() { return ID; } - void getData(stringstream& ss, unsigned long long time, bool refresh); + void getData(stringstream& ss, unsigned long long time); Coords getActiveDestination() { return activeDestination; } virtual void changeSpeed(string change) {}; @@ -122,6 +123,9 @@ public: void triggerUpdate(unsigned char datumIndex); + bool hasFreshData(unsigned long long time); + bool checkFreshness(unsigned char datumIndex, unsigned long long time); + /********** Setters **********/ virtual void setCategory(string newValue) { updateValue(category, newValue, DataIndex::category); } virtual void setAlive(bool newValue) { updateValue(alive, newValue, DataIndex::alive); } diff --git a/src/core/include/unitsmanager.h b/src/core/include/unitsmanager.h index 081545a9..71d5f247 100644 --- a/src/core/include/unitsmanager.h +++ b/src/core/include/unitsmanager.h @@ -20,7 +20,7 @@ public: void updateExportData(lua_State* L, double dt = 0); void updateMissionData(json::value missionData); void runAILoop(); - string getUnitData(stringstream &ss, unsigned long long time, bool refresh); + string getUnitData(stringstream &ss, unsigned long long time); void deleteUnit(unsigned int ID, bool explosion); void acquireControl(unsigned int ID); diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index 68398d1d..580b24d7 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -96,13 +96,11 @@ void Server::handle_get(http_request request) time = 0; } } - - bool refresh = (time == 0); unsigned long long updateTime = ms.count(); stringstream ss; ss.write((char*)&updateTime, sizeof(updateTime)); - unitsManager->getUnitData(ss, time, refresh); + unitsManager->getUnitData(ss, time); response.set_body(concurrency::streams::bytestream::open_istream(ss.str())); } else { diff --git a/src/core/src/unit.cpp b/src/core/src/unit.cpp index 4f6212bf..dd912cf7 100644 --- a/src/core/src/unit.cpp +++ b/src/core/src/unit.cpp @@ -170,56 +170,72 @@ void Unit::updateMissionData(json::value json) setHasTask(json[L"hasTask"].as_bool()); } +bool Unit::checkFreshness(unsigned char datumIndex, unsigned long long time) { + auto it = updateTimeMap.find(datumIndex); + if (it == updateTimeMap.end()) + return false; + else + return it->second > time; +} -void Unit::getData(stringstream& ss, unsigned long long time, bool refresh) +bool Unit::hasFreshData(unsigned long long time) { + for (auto it : updateTimeMap) + if (it.second > time) + return true; + return false; +} + +void Unit::getData(stringstream& ss, unsigned long long time) { - Unit* sourceUnit = this; + Unit* leader = this; if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this)) - sourceUnit = unitsManager->getGroupLeader(this); + leader = unitsManager->getGroupLeader(this); + + if (!leader->hasFreshData(time)) return; const unsigned char endOfData = DataIndex::endOfData; ss.write((const char*)&ID, sizeof(ID)); - 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, sourceUnit->human); break; - case DataIndex::controlled: appendNumeric(ss, d.first, sourceUnit->controlled); break; - case DataIndex::coalition: appendNumeric(ss, d.first, sourceUnit->coalition); break; - case DataIndex::country: appendNumeric(ss, d.first, sourceUnit->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, sourceUnit->groupName); break; - case DataIndex::state: appendNumeric(ss, d.first, sourceUnit->state); break; - case DataIndex::task: appendString(ss, d.first, sourceUnit->task); break; - case DataIndex::hasTask: appendNumeric(ss, d.first, sourceUnit->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, sourceUnit->isTanker); break; - case DataIndex::isAWACS: appendNumeric(ss, d.first, sourceUnit->isAWACS); break; - case DataIndex::onOff: appendNumeric(ss, d.first, sourceUnit->onOff); break; - case DataIndex::followRoads: appendNumeric(ss, d.first, sourceUnit->followRoads); break; - case DataIndex::fuel: appendNumeric(ss, d.first, fuel); break; - case DataIndex::desiredSpeed: appendNumeric(ss, d.first, sourceUnit->desiredSpeed); break; - case DataIndex::desiredSpeedType: appendNumeric(ss, d.first, sourceUnit->desiredSpeedType); break; - case DataIndex::desiredAltitude: appendNumeric(ss, d.first, sourceUnit->desiredAltitude); break; - case DataIndex::desiredAltitudeType: appendNumeric(ss, d.first, sourceUnit->desiredAltitudeType); break; - case DataIndex::leaderID: appendNumeric(ss, d.first, sourceUnit->leaderID); break; - case DataIndex::formationOffset: appendNumeric(ss, d.first, sourceUnit->formationOffset); break; - case DataIndex::targetID: appendNumeric(ss, d.first, sourceUnit->targetID); break; - case DataIndex::targetPosition: appendNumeric(ss, d.first, sourceUnit->targetPosition); break; - case DataIndex::ROE: appendNumeric(ss, d.first, sourceUnit->ROE); break; - case DataIndex::reactionToThreat: appendNumeric(ss, d.first, sourceUnit->reactionToThreat); break; - case DataIndex::emissionsCountermeasures: appendNumeric(ss, d.first, sourceUnit->emissionsCountermeasures); break; - case DataIndex::TACAN: appendNumeric(ss, d.first, sourceUnit->TACAN); break; - case DataIndex::radio: appendNumeric(ss, d.first, sourceUnit->radio); break; - case DataIndex::generalSettings: appendNumeric(ss, d.first, sourceUnit->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, sourceUnit->activePath); break; - } + for (unsigned char datumIndex = DataIndex::startOfData + 1; datumIndex < DataIndex::lastIndex; datumIndex++) + { + /* When units are in a group, most data comes from the group leader */ + switch (datumIndex) { + case DataIndex::category: if (checkFreshness(datumIndex, time)) appendString(ss, datumIndex, category); break; + case DataIndex::alive: if (checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, alive); break; + case DataIndex::human: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->human); break; + case DataIndex::controlled: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->controlled); break; + case DataIndex::coalition: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->coalition); break; + case DataIndex::country: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->country); break; + case DataIndex::name: if (checkFreshness(datumIndex, time)) appendString(ss, datumIndex, name); break; + case DataIndex::unitName: if (checkFreshness(datumIndex, time)) appendString(ss, datumIndex, unitName); break; + case DataIndex::groupName: if (leader->checkFreshness(datumIndex, time)) appendString(ss, datumIndex, leader->groupName); break; + case DataIndex::state: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->state); break; + case DataIndex::task: if (leader->checkFreshness(datumIndex, time)) appendString(ss, datumIndex, leader->task); break; + case DataIndex::hasTask: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->hasTask); break; + case DataIndex::position: if (checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, position); break; + case DataIndex::speed: if (checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, speed); break; + case DataIndex::heading: if (checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, heading); break; + case DataIndex::isTanker: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->isTanker); break; + case DataIndex::isAWACS: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->isAWACS); break; + case DataIndex::onOff: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->onOff); break; + case DataIndex::followRoads: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->followRoads); break; + case DataIndex::fuel: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, fuel); break; + case DataIndex::desiredSpeed: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->desiredSpeed); break; + case DataIndex::desiredSpeedType: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->desiredSpeedType); break; + case DataIndex::desiredAltitude: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->desiredAltitude); break; + case DataIndex::desiredAltitudeType: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->desiredAltitudeType); break; + case DataIndex::leaderID: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->leaderID); break; + case DataIndex::formationOffset: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->formationOffset); break; + case DataIndex::targetID: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->targetID); break; + case DataIndex::targetPosition: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->targetPosition); break; + case DataIndex::ROE: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->ROE); break; + case DataIndex::reactionToThreat: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->reactionToThreat); break; + case DataIndex::emissionsCountermeasures: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->emissionsCountermeasures); break; + case DataIndex::TACAN: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->TACAN); break; + case DataIndex::radio: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->radio); break; + case DataIndex::generalSettings: if (leader->checkFreshness(datumIndex, time)) appendNumeric(ss, datumIndex, leader->generalSettings); break; + case DataIndex::ammo: if (checkFreshness(datumIndex, time)) appendVector(ss, datumIndex, ammo); break; + case DataIndex::contacts: if (checkFreshness(datumIndex, time)) appendVector(ss, datumIndex, contacts); break; + case DataIndex::activePath: if (leader->checkFreshness(datumIndex, time)) appendList(ss, datumIndex, leader->activePath); break; } } ss.write((const char*)&endOfData, sizeof(endOfData)); diff --git a/src/core/src/unitsmanager.cpp b/src/core/src/unitsmanager.cpp index 9f4a30c3..3c96ef9c 100644 --- a/src/core/src/unitsmanager.cpp +++ b/src/core/src/unitsmanager.cpp @@ -154,10 +154,10 @@ void UnitsManager::runAILoop() { unit.second->runAILoop(); } -string UnitsManager::getUnitData(stringstream &ss, unsigned long long time, bool refresh) +string UnitsManager::getUnitData(stringstream &ss, unsigned long long time) { for (auto const& p : units) - p.second->getData(ss, time, refresh); + p.second->getData(ss, time); return to_base64(ss.str()); }