diff --git a/client/public/stylesheets/layout.css b/client/public/stylesheets/layout.css index a595382d..dfa23432 100644 --- a/client/public/stylesheets/layout.css +++ b/client/public/stylesheets/layout.css @@ -177,7 +177,7 @@ dl.ol-data-grid dd { } .ol-text-input input { - height: 40px; + height: 32px; border-radius: 5px; color: var(--background-offwhite); background-color: var(--background-grey); diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index 64647448..941a97b2 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -163,7 +163,10 @@ form>div { align-items: center; background-color: var(--background-grey); border-radius: var(--border-radius-sm); - padding: 1em 30px 1em 20px; + height: 32px; + padding-right: 30px; + padding-left: 20px; + width: calc(100%); overflow: hidden; text-overflow: ellipsis; diff --git a/client/public/stylesheets/unitcontrolpanel.css b/client/public/stylesheets/unitcontrolpanel.css index 44bd5802..a0f7b507 100644 --- a/client/public/stylesheets/unitcontrolpanel.css +++ b/client/public/stylesheets/unitcontrolpanel.css @@ -31,7 +31,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel { border-radius: var(--border-radius-lg); display: flex; font-size: 11px; - height: 30px; + height: 32px; padding: 8px 0; position: relative; width: 100%; @@ -103,6 +103,31 @@ body.feature-forceShowUnitControlPanel #unit-control-panel { width: 400px; } +#advanced-settings-dialog:not([data-show-settings]) #general-settings { + display: none; +} + +#advanced-settings-dialog:not([data-show-tasking]) #tasking { + display: none; +} + +#advanced-settings-dialog:not([data-show-tanker]) #tanker-checkbox { + display: none; +} + +#advanced-settings-dialog:not([data-show-AWACS]) #AWACS-checkbox { + display: none; +} + +#advanced-settings-dialog:not([data-show-TACAN]) #TACAN-options { + display: none; +} + +#advanced-settings-dialog:not([data-show-radio]) #radio-options { + display: none; +} + + #advanced-settings-dialog>.ol-dialog-content { margin-top: 10px; margin-bottom: 10px; @@ -112,9 +137,15 @@ body.feature-forceShowUnitControlPanel #unit-control-panel { row-gap: 10px; } -#advanced-settings-dialog>.ol-dialog-content>.ol-group { - justify-content: space-between; +#advanced-settings-dialog>.ol-dialog-content>div { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + row-gap: 10px; +} +#advanced-settings-dialog>.ol-dialog-content>div>.ol-group { + justify-content: space-between; } #advanced-settings-dialog h4 { @@ -126,12 +157,12 @@ body.feature-forceShowUnitControlPanel #unit-control-panel { margin-bottom: 10px; } -#advanced-options-grid { +#general-settings-grid { display: grid; grid-template-columns: 1fr 1fr; row-gap: 10px; } -#advanced-options-grid>div { +#general-settings-grid>div { width: 49%; } \ No newline at end of file diff --git a/client/src/@types/unit.d.ts b/client/src/@types/unit.d.ts index d2f0d109..f3741e27 100644 --- a/client/src/@types/unit.d.ts +++ b/client/src/@types/unit.d.ts @@ -40,17 +40,15 @@ interface TaskData { targetAltitude: number; isTanker: boolean; isAWACS: boolean; - TACANChannel: number; - TACANXY: string; - TACANCallsign: string; - radioFrequency: number; - radioCallsign: number; - radioCallsignNumber: number; } interface OptionsData { ROE: string; reactionToThreat: string; + emissionsCountermeasures: string; + TACAN: TACAN; + radio: Radio; + generalSettings: GeneralSettings; } interface UnitData { @@ -61,3 +59,24 @@ interface UnitData { taskData: TaskData; optionsData: OptionsData; } + +interface TACAN { + isOn: boolean; + channel: number; + XY: string; + callsign: string; +} + +interface Radio { + frequency: number; + callsign: number; + callsignNumber: number; +} + +interface GeneralSettings { + prohibitJettison: boolean; + prohibitAA: boolean; + prohibitAG: boolean; + prohibitAfterburner: boolean; + prohibitAirWpn: boolean; +} \ No newline at end of file diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index 23b9c3cb..a205669e 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -142,11 +142,15 @@ export class UnitControlPanel extends Panel { this.#optionButtons["reactionToThreat"].forEach((button: HTMLButtonElement) => { button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().reactionToThreat === button.value)) }); + + this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => { + button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().emissionsCountermeasures === button.value)) + }); } } } - // Update function will only be allowed to update the sliders once it's matched the expected value for the first time (due to lag of Ajax request) + /* Update function will only be allowed to update the sliders once it's matched the expected value for the first time (due to lag of Ajax request) */ #updateCanSetAltitudeSlider(altitude: number) { if (this.#expectedAltitude < 0 || altitude === this.#expectedAltitude) { this.#expectedAltitude = -1; @@ -191,19 +195,15 @@ export class UnitControlPanel extends Panel { this.#airspeedSlider.setActive(targetSpeed != undefined); if (targetSpeed != undefined) { - targetSpeed *= 1.94384; - if (this.#updateCanSetSpeedSlider(targetSpeed)) { this.#airspeedSlider.setValue(targetSpeed); } - } this.#altitudeSlider.setActive(targetAltitude != undefined); if (targetAltitude != undefined) { targetAltitude /= 0.3048; - if (this.#updateCanSetAltitudeSlider(targetAltitude)) { this.#altitudeSlider.setValue(targetAltitude); } @@ -219,83 +219,125 @@ export class UnitControlPanel extends Panel { { if (units.length == 1) { + /* HTML Elements */ + const unitNameEl = this.#advancedSettingsDialog.querySelector("#unit-name") as HTMLElement; + const prohibitJettisonCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-jettison-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAfterburnerCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-afterburner-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAACheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-AA-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAGCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-AG-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAirWpnCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-air-wpn-checkbox")?.querySelector("input") as HTMLInputElement; + const tankerCheckbox = this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input") as HTMLInputElement; + const AWACSCheckbox = this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.querySelector("input") as HTMLInputElement; + const TACANCheckbox = this.#advancedSettingsDialog.querySelector("#TACAN-checkbox")?.querySelector("input") as HTMLInputElement; + const TACANChannelInput = this.#advancedSettingsDialog.querySelector("#TACAN-channel")?.querySelector("input") as HTMLInputElement; + const TACANCallsignInput = this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input") as HTMLInputElement; + const radioMhzInput = this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input") as HTMLInputElement; + const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement; + const unit = units[0]; - (this.#advancedSettingsDialog.querySelector("#unit-name")).innerText = unit.getBaseData().unitName; + const roles = aircraftDatabase.getByName(unit.getBaseData().name)?.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.getOptionsData().radio.frequency / 1000000); + const radioDecimals = (unit.getOptionsData().radio.frequency / 1000000 - radioMHz) * 1000; - if (getUnitsManager().getSelectedUnits().length == 1) - { - var radioMHz = Math.floor(unit.getTaskData().radioFrequency / 1000000); - var radioDecimals = (unit.getTaskData().radioFrequency / 1000000 - radioMHz) * 1000; + /* Activate the correct options depending on unit type */ + this.#advancedSettingsDialog.toggleAttribute("data-show-settings", !tanker && !AWACS); + this.#advancedSettingsDialog.toggleAttribute("data-show-tasking", tanker || AWACS); + this.#advancedSettingsDialog.toggleAttribute("data-show-tanker", tanker); + this.#advancedSettingsDialog.toggleAttribute("data-show-AWACS", AWACS); + this.#advancedSettingsDialog.toggleAttribute("data-show-TACAN", tanker); + this.#advancedSettingsDialog.toggleAttribute("data-show-radio", tanker || AWACS); - // Default values for "normal" units + /* Set common properties */ + // Name + unitNameEl.innerText = unit.getBaseData().unitName; + + // General settings + prohibitJettisonCheckbox.checked = unit.getOptionsData().generalSettings.prohibitJettison; + prohibitAfterburnerCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAfterburner; + prohibitAACheckbox.checked = unit.getOptionsData().generalSettings.prohibitAA; + prohibitAGCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAG; + prohibitAirWpnCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAirWpn; + + // Tasking + tankerCheckbox.checked = unit.getTaskData().isTanker; + AWACSCheckbox.checked = unit.getTaskData().isAWACS; + + // TACAN + TACANCheckbox.checked = unit.getOptionsData().TACAN.isOn; + TACANChannelInput.value = String(unit.getOptionsData().TACAN.channel); + TACANCallsignInput.value = String(unit.getOptionsData().TACAN.callsign); + this.#TACANXYDropdown.setValue(unit.getOptionsData().TACAN.XY); + + // Radio + radioMhzInput.value = String(radioMHz); + radioCallsignNumberInput.value = String(unit.getOptionsData().radio.callsignNumber); + this.#radioDecimalsDropdown.setValue("." + radioDecimals); + + if (tanker) /* Set tanker specific options */ + this.#radioCallsignDropdown.setOptions(["Texaco", "Arco", "Shell"]); + else if (AWACS) /* Set AWACS specific options */ + this.#radioCallsignDropdown.setOptions(["Overlord", "Magic", "Wizard", "Focus", "Darkstar"]); + else this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]); - this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1); - // Input values - var tankerCheckbox = this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input") - var AWACSCheckbox = this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.querySelector("input") - - var TACANChannelInput = this.#advancedSettingsDialog.querySelector("#TACAN-channel")?.querySelector("input"); - var TACANCallsignInput = this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input"); - var radioMhzInput = this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input"); - var radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input"); - - if (tankerCheckbox) tankerCheckbox.checked = unit.getTaskData().isTanker; - if (AWACSCheckbox) AWACSCheckbox.checked = unit.getTaskData().isAWACS; - if (TACANChannelInput) TACANChannelInput.value = String(unit.getTaskData().TACANChannel); - if (TACANCallsignInput) TACANCallsignInput.value = String(unit.getTaskData().TACANCallsign); - if (radioMhzInput) radioMhzInput.value = String(radioMHz); - if (radioCallsignNumberInput) radioCallsignNumberInput.value = String(unit.getTaskData().radioCallsignNumber); - - this.#TACANXYDropdown.setValue(unit.getTaskData().TACANXY); - this.#radioDecimalsDropdown.setValue("." + radioDecimals); - - // Make sure its in the valid range - if (!this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1)) - this.#radioCallsignDropdown.selectValue(0); - - // Set options for tankers - var roles = aircraftDatabase.getByName(unit.getBaseData().name)?.loadouts.map((loadout) => {return loadout.roles}) - if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker")){ - this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.remove("hide"); - this.#radioCallsignDropdown.setOptions(["Texaco", "Arco", "Shell"]); - this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1); - } - else { - this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.classList.add("hide"); - } - - // Set options for AWACS - if (roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS")){ - this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.remove("hide"); - this.#radioCallsignDropdown.setOptions(["Overlord", "Magic", "Wizard", "Focus", "Darkstar"]); - this.#radioCallsignDropdown.selectValue(unit.getTaskData().radioCallsign - 1); - } else { - this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.classList.add("hide"); - } - } + // This must be done after setting the options + if (!this.#radioCallsignDropdown.selectValue(unit.getOptionsData().radio.callsign - 1)) // Ensure the selected value is in the acceptable range + this.#radioCallsignDropdown.selectValue(0); } } #applyAdvancedSettings() { - const isTanker = this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input")?.checked? true: false; - const isAWACS = this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.querySelector("input")?.checked? true: false; + /* HTML Elements */ + const prohibitJettisonCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-jettison-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAfterburnerCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-afterburner-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAACheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-AA-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAGCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-AG-checkbox")?.querySelector("input") as HTMLInputElement; + const prohibitAirWpnCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-air-wpn-checkbox")?.querySelector("input") as HTMLInputElement; + const tankerCheckbox = this.#advancedSettingsDialog.querySelector("#tanker-checkbox")?.querySelector("input") as HTMLInputElement; + const AWACSCheckbox = this.#advancedSettingsDialog.querySelector("#AWACS-checkbox")?.querySelector("input") as HTMLInputElement; + const TACANCheckbox = this.#advancedSettingsDialog.querySelector("#TACAN-checkbox")?.querySelector("input") as HTMLInputElement; + const TACANChannelInput = this.#advancedSettingsDialog.querySelector("#TACAN-channel")?.querySelector("input") as HTMLInputElement; + const TACANCallsignInput = this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input") as HTMLInputElement; + const radioMhzInput = this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input") as HTMLInputElement; + const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement; - const TACANChannel = Number(this.#advancedSettingsDialog.querySelector("#TACAN-channel")?.querySelector("input")?.value); - const TACANXY = this.#TACANXYDropdown.getValue(); - const TACANCallsign = this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input")?.value - - const radioMHz = Number(this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input")?.value); + /* Tasking */ + const isTanker = tankerCheckbox.checked? true: false; + const isAWACS = AWACSCheckbox.checked? true: false; + + /* TACAN */ + const TACAN: TACAN = { + isOn: TACANCheckbox.checked? true: false, + channel: Number(TACANChannelInput.value), + XY: this.#TACANXYDropdown.getValue(), + callsign: TACANCallsignInput.value as string + } + + /* Radio */ + const radioMHz = Number(radioMhzInput.value); const radioDecimals = this.#radioDecimalsDropdown.getValue(); - const radioCallsign = this.#radioCallsignDropdown.getIndex() + 1; - const radioCallsignNumber = Number(this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input")?.value); - - var radioFrequency = (radioMHz * 1000 + Number(radioDecimals.substring(1))) * 1000; + const radio: Radio = { + frequency: (radioMHz * 1000 + Number(radioDecimals.substring(1))) * 1000, + callsign: this.#radioCallsignDropdown.getIndex() + 1, + callsignNumber: Number(radioCallsignNumberInput.value) + } + /* General settings */ + const generalSettings: GeneralSettings = { + prohibitJettison: prohibitJettisonCheckbox.checked? true: false, + prohibitAfterburner: prohibitAfterburnerCheckbox.checked? true: false, + prohibitAA: prohibitAACheckbox.checked? true: false, + prohibitAG: prohibitAGCheckbox.checked? true: false, + prohibitAirWpn: prohibitAirWpnCheckbox.checked? true: false + } + + /* Send command and close */ var units = getUnitsManager().getSelectedUnits(); if (units.length > 0) - units[0].setAdvancedOptions(isTanker, isAWACS, TACANChannel, TACANXY, TACANCallsign, radioFrequency, radioCallsign, radioCallsignNumber); + units[0].setAdvancedOptions(isTanker, isAWACS, TACAN, radio, generalSettings); this.#advancedSettingsDialog.classList.add("hide"); } diff --git a/client/src/server/server.ts b/client/src/server/server.ts index 2452bff4..d9e4e2f3 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -210,7 +210,7 @@ export function setReactionToThreat(ID: number, reactionToThreat: string) { } export function setEmissionsCountermeasures(ID: number, emissionCountermeasure: string) { - var command = {"ID": ID, "emissionCountermeasure": emissionCountermeasure} + var command = {"ID": ID, "emissionsCountermeasures": emissionCountermeasure} var data = {"setEmissionsCountermeasures": command} POST(data, () => { }); } @@ -221,17 +221,14 @@ export function refuel(ID: number) { POST(data, () => { }); } -export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACANChannel: number, TACANXY: string, TACANCallsign: string, radioFrequency: number, radioCallsign: number, radioCallsignNumber: number) +export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) { var command = { "ID": ID, "isTanker": isTanker, "isAWACS": isAWACS, - "TACANChannel": TACANChannel, - "TACANXY": TACANXY, - "TACANCallsign": TACANCallsign, - "radioFrequency": radioFrequency, - "radioCallsign": radioCallsign, - "radioCallsignNumber": radioCallsignNumber + "TACAN": TACAN, + "radio": radio, + "generalSettings": generalSettings }; var data = { "setAdvancedOptions": command }; diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 2648fc72..4de97930 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -4,7 +4,6 @@ import { rad2deg } from '../other/utils'; import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures } from '../server/server'; import { aircraftDatabase } from './aircraftdatabase'; import { groundUnitsDatabase } from './groundunitsdatabase'; -import { field } from 'geomag' var pathIcon = new Icon({ iconUrl: 'images/marker-icon.png', @@ -43,23 +42,21 @@ export class Unit extends Marker { leaderID: 0 }, taskData: { - currentState: "IDLE", + currentState: "NONE", currentTask: "", activePath: {}, targetSpeed: 0, targetAltitude: 0, isTanker: false, isAWACS: false, - TACANChannel: 0, - TACANXY: "X", - TACANCallsign: "", - radioFrequency: 0, - radioCallsign: 0, - radioCallsignNumber: 0 }, optionsData: { ROE: "", reactionToThreat: "", + emissionsCountermeasures: "", + TACAN: { isOn: false, channel: 0, XY: "X", callsign: "" }, + radio: { frequency: 0, callsign: 1, callsignNumber: 1}, + generalSettings: { prohibitJettison: false, prohibitAA: false, prohibitAG: false, prohibitAfterburner: false, prohibitAirWpn: false} } }; @@ -385,9 +382,9 @@ export class Unit extends Marker { refuel(this.ID); } - setAdvancedOptions(isTanker: boolean, isAWACS: boolean, TACANChannel: number, TACANXY: string, TACANcallsign: string, radioFrequency: number, radioCallsign: number, radioCallsignNumber: number) { + setAdvancedOptions(isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) { if (!this.getMissionData().flags.Human) - setAdvacedOptions(this.ID, isTanker, isAWACS, TACANChannel, TACANXY, TACANcallsign, radioFrequency, radioCallsign, radioCallsignNumber); + setAdvacedOptions(this.ID, isTanker, isAWACS, TACAN, radio, generalSettings); } /***********************************************/ diff --git a/client/views/dialogs.ejs b/client/views/dialogs.ejs index 1e667624..2421f78a 100644 --- a/client/views/dialogs.ejs +++ b/client/views/dialogs.ejs @@ -30,116 +30,147 @@
-
-

General settings

-
-
-
-
- -
- -
- -
- -
- -
- -
- -
-
- -
-

Tasking

-
-
- -
- -
- -
- -
- -
-

Radio options

-
-
-
- - + +
-
- +

General settings

+
+
+
+
+
-
-
X
-
-
+
+
- -
- -
-
-
- -
- - -
-
- -
- -
-
.000
-
-
-
-
-
- -
- - -
-
-
-
-
-
- - -
- +
+ +
+ +
+ +
+ +
+ +
+
+
+ + +
+
+

Tasking

+
+
+ +
+ +
+ +
+ +
+
+ + +
+
+

TACAN options

+
+
+
+ +
+ +
+ + +
+
+ +
+ +
+
X
+
+
+
+ +
+ +
+
+
+
+ + +
+
+

Radio options

+
+
+ +
+ + +
+
+ +
+ +
+
.000
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+ + + +
+ +
diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index ad5a0d46..80346a9c 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -1,6 +1,6 @@ local version = "v0.2.1-alpha" -local debug = false +local debug = true Olympus.unitCounter = 1 Olympus.payloadRegistry = {} @@ -427,7 +427,7 @@ function Olympus.setCommand(ID, command) end function Olympus.setOption(ID, optionID, optionValue) - Olympus.debug("Olympus.setOption " .. ID .. " " .. optionID .. " " .. optionValue, 2) + Olympus.debug("Olympus.setOption " .. ID .. " " .. optionID .. " " .. tostring(optionValue), 2) local unit = Olympus.getUnitByID(ID) if unit then unit:getGroup():getController():setOption(optionID, optionValue) diff --git a/src/core/include/commands.h b/src/core/include/commands.h index 17383cf1..7752eea8 100644 --- a/src/core/include/commands.h +++ b/src/core/include/commands.h @@ -2,6 +2,7 @@ #include "framework.h" #include "luatools.h" #include "utils.h" +#include "logger.h" namespace CommandPriority { enum CommandPriorities { LOW, MEDIUM, HIGH }; @@ -24,6 +25,7 @@ namespace SetCommandType { PROHIBIT_AG = 17, MISSILE_ATTACK = 18, PROHIBIT_WP_PASS_REPORT = 19, + ENGAGE_AIR_WEAPONS = 20, OPTION_RADIO_USAGE_CONTACT = 21, OPTION_RADIO_USAGE_ENGAGE = 22, OPTION_RADIO_USAGE_KILL = 23, @@ -43,7 +45,7 @@ namespace ROE { } namespace ReactionToThreat { - enum ReactionToThreats { + enum ReactionsToThreat { NO_REACTION = 0, PASSIVE_DEFENCE = 1, EVADE_FIRE = 2, @@ -52,6 +54,35 @@ namespace ReactionToThreat { }; } +namespace RadarUse { + enum RadarUses { + NEVER = 0, + FOR_ATTACK_ONLY = 1, + FOR_SEARCH_IF_REQUIRED = 2, + FOR_CONTINUOUS_SEARCH = 3 + }; +} + +namespace FlareUse { + enum FlareUses { + NEVER = 0, + AGAINST_FIRED_MISSILE = 1, + WHEN_FLYING_IN_SAM_WEZ = 2, + WHEN_FLYING_NEAR_ENEMIES = 3 + }; +} + +namespace ECMUse { + enum ECMUses { + NEVER_USE = 0, + USE_IF_ONLY_LOCK_BY_RADAR = 1, + USE_IF_DETECTED_LOCK_BY_RADAR = 2, + ALWAYS_USE = 3 + }; +} + + + /* Base command class */ class Command { @@ -243,7 +274,19 @@ public: SetOption(int ID, int optionID, int optionValue) : ID(ID), optionID(optionID), - optionValue(optionValue) + optionValue(optionValue), + optionBool(false), + isBoolean(false) + { + priority = CommandPriority::HIGH; + }; + + SetOption(int ID, int optionID, bool optionBool) : + ID(ID), + optionID(optionID), + optionValue(0), + optionBool(optionBool), + isBoolean(true) { priority = CommandPriority::HIGH; }; @@ -254,4 +297,6 @@ private: const int ID; const int optionID; const int optionValue; + const bool optionBool; + const bool isBoolean; }; \ No newline at end of file diff --git a/src/core/include/unit.h b/src/core/include/unit.h index e54401ff..37e456f3 100644 --- a/src/core/include/unit.h +++ b/src/core/include/unit.h @@ -9,20 +9,44 @@ namespace State { enum States { + NONE = 0, IDLE, REACH_DESTINATION, ATTACK, - WINGMAN, FOLLOW, LAND, REFUEL, AWACS, - EWR, - TANKER, - RUN_AWAY + TANKER }; }; +namespace Options { + struct TACAN + { + bool isOn = false; + int channel = 40; + wstring XY = L"X"; + wstring callsign = L"TKR"; + }; + + struct Radio + { + int frequency = 124000000; // MHz + int callsign = 1; + int callsignNumber = 1; + }; + + struct GeneralSettings + { + bool prohibitJettison = false; + bool prohibitAA = false; + bool prohibitAG = false; + bool prohibitAfterburner = false; + bool prohibitAirWpn = false; + }; +} + class Unit { public: @@ -30,6 +54,7 @@ public: ~Unit(); /********** Public methods **********/ + void initialize(json::value json); int getID() { return ID; } void updateExportData(json::value json); void updateMissionData(json::value json); @@ -98,15 +123,7 @@ public: void setTargetID(int newTargetID) { targetID = newTargetID; addMeasure(L"targetID", json::value(newTargetID));} void setIsTanker(bool newIsTanker); void setIsAWACS(bool newIsAWACS); - void setTACANChannel(int newTACANChannel); - void setTACANXY(wstring newTACANXY); - void setTACANCallsign(wstring newTACANCallsign); - void setTACAN(); - void setEPLRS(bool state); - void setRadioFrequency(int newRadioFrequency); - void setRadioCallsign(int newRadioCallsign); - void setRadioCallsignNumber(int newRadioCallsignNumber); - void setRadio(); + wstring getCurrentTask() { return currentTask; } virtual double getTargetSpeed() { return targetSpeed; }; virtual double getTargetAltitude() { return targetAltitude; }; @@ -115,18 +132,22 @@ public: int getTargetID() { return targetID; } bool getIsTanker() { return isTanker; } bool getIsAWACS() { return isAWACS; } - int getTACANChannel() { return TACANChannel; } - wstring getTACANXY() { return TACANXY; } - wstring getTACANCallsign() { return TACANCallsign; } - int getRadioFrequency() { return radioFrequency; } - int getRadioCallsign() { return radioCallsign; } - int getRadioCallsignNumber() { return radioCallsignNumber; } /********** Options data **********/ void setROE(wstring newROE); void setReactionToThreat(wstring newReactionToThreat); + void setEmissionsCountermeasures(wstring newEmissionsCountermeasures); + void setTACAN(Options::TACAN newTACAN); + void setRadio(Options::Radio newradio); + void setGeneralSettings(Options::GeneralSettings newGeneralSettings); + void setEPLRS(bool newEPLRS); wstring getROE() { return ROE; } - wstring getReactionToThreat() {return reactionToThreat;} + wstring getReactionToThreat() { return reactionToThreat; } + wstring 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); @@ -179,19 +200,18 @@ protected: int targetID = NULL; bool isTanker = false; bool isAWACS = false; - int TACANChannel = 40; - wstring TACANXY = L"X"; - wstring TACANCallsign = L"TKR"; - int radioFrequency = 260000000; // MHz - int radioCallsign = 1; - int radioCallsignNumber = 1; - + /********** Options data **********/ - wstring ROE = L""; - wstring reactionToThreat = L""; + wstring ROE = L"Designated"; + wstring reactionToThreat = L"Evade"; + wstring emissionsCountermeasures = L"Defend"; + Options::TACAN TACAN; + Options::Radio radio; + Options::GeneralSettings generalSettings; + bool EPLRS = false; /********** State machine **********/ - int state = State::IDLE; + int state = State::NONE; /********** Other **********/ Coords oldPosition = Coords(0); // Used to approximate speed diff --git a/src/core/src/airunit.cpp b/src/core/src/airunit.cpp index 1c313d19..f40f65be 100644 --- a/src/core/src/airunit.cpp +++ b/src/core/src/airunit.cpp @@ -185,7 +185,7 @@ void AirUnit::AIloop() } Command* command = dynamic_cast(new SetTask(ID, taskSS.str())); scheduler->appendCommand(command); - hasTask = true; + setHasTask(true); } break; } @@ -290,7 +290,7 @@ void AirUnit::AIloop() << "}"; Command* command = dynamic_cast(new SetTask(ID, taskSS.str())); scheduler->appendCommand(command); - hasTask = true; + setHasTask(true); } } break; @@ -306,7 +306,7 @@ void AirUnit::AIloop() << "}"; Command* command = dynamic_cast(new SetTask(ID, taskSS.str())); scheduler->appendCommand(command); - hasTask = true; + setHasTask(true); } else { setState(State::IDLE); diff --git a/src/core/src/commands.cpp b/src/core/src/commands.cpp index 68f633da..662e62ab 100644 --- a/src/core/src/commands.cpp +++ b/src/core/src/commands.cpp @@ -62,7 +62,7 @@ wstring SpawnAircraft::getString(lua_State* L) optionsSS.precision(10); optionsSS << "{" << "payloadName = \"" << payloadName << "\", " - << "airbaseName = \"" << airbaseName << "\"," + << "airbaseName = \"" << airbaseName << "\", " << "}"; std::wostringstream commandSS; @@ -71,7 +71,7 @@ wstring SpawnAircraft::getString(lua_State* L) << "\"" << coalition << "\"" << ", " << "\"" << unitType << "\"" << ", " << location.lat << ", " - << location.lng << "," + << location.lng << ", " << optionsSS.str(); return commandSS.str(); } @@ -113,7 +113,7 @@ wstring SetTask::getString(lua_State* L) std::wostringstream commandSS; commandSS.precision(10); commandSS << "Olympus.setTask, " - << ID << "," + << ID << ", " << task; return commandSS.str(); @@ -136,7 +136,7 @@ wstring SetCommand::getString(lua_State* L) std::wostringstream commandSS; commandSS.precision(10); commandSS << "Olympus.setCommand, " - << ID << "," + << ID << ", " << command; return commandSS.str(); @@ -147,10 +147,17 @@ wstring SetOption::getString(lua_State* L) { std::wostringstream commandSS; commandSS.precision(10); - commandSS << "Olympus.setOption, " - << ID << "," - << optionID << "," - << optionValue; + if (!isBoolean) { + commandSS << "Olympus.setOption, " + << ID << ", " + << optionID << ", " + << optionValue; + } else { + commandSS << "Olympus.setOption, " + << ID << ", " + << optionID << ", " + << (optionBool? "true": "false"); + } return commandSS.str(); } \ No newline at end of file diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index 8bf448e9..ad46db11 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -222,6 +222,13 @@ void Scheduler::handleRequest(wstring key, json::value value) wstring reactionToThreat = value[L"reactionToThreat"].as_string(); unit->setReactionToThreat(reactionToThreat); } + else if (key.compare(L"setEmissionsCountermeasures") == 0) + { + int ID = value[L"ID"].as_integer(); + Unit* unit = unitsManager->getUnit(ID); + wstring emissionsCountermeasures = value[L"emissionsCountermeasures"].as_string(); + unit->setEmissionsCountermeasures(emissionsCountermeasures); + } else if (key.compare(L"landAt") == 0) { int ID = value[L"ID"].as_integer(); @@ -248,18 +255,33 @@ void Scheduler::handleRequest(wstring key, json::value value) Unit* unit = unitsManager->getUnit(ID); if (unit != nullptr) { + /* Advanced tasking */ unit->setIsTanker(value[L"isTanker"].as_bool()); unit->setIsAWACS(value[L"isAWACS"].as_bool()); - unit->setTACANChannel(value[L"TACANChannel"].as_number().to_int32()); - unit->setTACANXY(value[L"TACANXY"].as_string()); - unit->setTACANCallsign(value[L"TACANCallsign"].as_string()); - unit->setTACAN(); + /* TACAN Options */ + auto TACAN = value[L"TACAN"]; + unit->setTACAN({ TACAN[L"isOn"].as_bool(), + TACAN[L"channel"].as_number().to_int32(), + TACAN[L"XY"].as_string(), + TACAN[L"callsign"].as_string() + }); - unit->setRadioFrequency(value[L"radioFrequency"].as_number().to_int32()); - unit->setRadioCallsign(value[L"radioCallsign"].as_number().to_int32()); - unit->setRadioCallsignNumber(value[L"radioCallsignNumber"].as_number().to_int32()); - unit->setRadio(); + /* Radio Options */ + auto radio = value[L"radio"]; + unit->setRadio({ radio[L"frequency"].as_number().to_int32(), + radio[L"callsign"].as_number().to_int32(), + radio[L"callsignNumber"].as_number().to_int32() + }); + + /* General Settings */ + auto generalSettings = value[L"generalSettings"]; + unit->setGeneralSettings({ generalSettings[L"prohibitJettison"].as_bool(), + generalSettings[L"prohibitAA"].as_bool(), + generalSettings[L"prohibitAG"].as_bool(), + generalSettings[L"prohibitAfterburner"].as_bool(), + generalSettings[L"prohibitAirWpn"].as_bool(), + }); unit->resetActiveDestination(); } diff --git a/src/core/src/unit.cpp b/src/core/src/unit.cpp index e68e1ccb..b32dc785 100644 --- a/src/core/src/unit.cpp +++ b/src/core/src/unit.cpp @@ -15,22 +15,27 @@ using namespace GeographicLib; extern Scheduler* scheduler; extern UnitsManager* unitsManager; +// TODO: Make dedicated file +bool operator==(const Options::TACAN& lhs, const Options::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) +{ + return lhs.frequency == rhs.frequency && lhs.callsign == rhs.callsign && lhs.callsignNumber == rhs.callsignNumber; +} + +bool operator==(const Options::GeneralSettings& lhs, const Options::GeneralSettings& rhs) +{ + return lhs.prohibitAA == rhs.prohibitAA && lhs.prohibitAfterburner == rhs.prohibitAfterburner && lhs.prohibitAG == rhs.prohibitAG && + lhs.prohibitAirWpn == rhs.prohibitAirWpn && lhs.prohibitJettison == rhs.prohibitJettison; +} + Unit::Unit(json::value json, int ID) : ID(ID) { log("Creating unit with ID: " + to_string(ID)); - addMeasure(L"currentState", json::value(L"Idle")); - - addMeasure(L"TACANChannel", json::value(TACANChannel)); - addMeasure(L"TACANXY", json::value(TACANXY)); - addMeasure(L"TACANCallsign", json::value(TACANCallsign)); - - addMeasure(L"radioFrequency", json::value(radioFrequency)); - addMeasure(L"radioCallsign", json::value(radioCallsign)); - addMeasure(L"radioCallsignNumber", json::value(radioCallsignNumber)); - - addMeasure(L"ROE", json::value(L"Designated")); - addMeasure(L"reactionToThreat", json::value(L"Evade")); } Unit::~Unit() @@ -38,6 +43,25 @@ Unit::~Unit() } +void Unit::initialize(json::value json) +{ + updateExportData(json); + + if (getAI()) { + /* Set the default IDLE state */ + setState(State::IDLE); + + /* Set the default options (these are all defaults so will only affect the export data, no DCS command will be sent) */ + setROE(L"Designated"); + setReactionToThreat(L"Evade"); + setEmissionsCountermeasures(L"Defend"); + setTACAN(TACAN); + setRadio(radio); + setEPLRS(EPLRS); + setGeneralSettings(generalSettings); + } +} + void Unit::addMeasure(wstring key, json::value value) { milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); @@ -155,7 +179,7 @@ json::value Unit::getData(long long time) /********** Task data **********/ json[L"taskData"] = json::value::object(); - for (auto key : { L"currentState", L"currentTask", L"targetSpeed", L"targetAltitude", L"activePath", L"isTanker", L"isAWACS", L"TACANChannel", L"TACANXY", L"TACANCallsign", L"radioFrequency", L"radioCallsign", L"radioCallsignNumber" }) + for (auto key : { L"currentState", L"currentTask", L"targetSpeed", L"targetAltitude", L"activePath", L"isTanker", L"isAWACS" }) { if (measures.find(key) != measures.end() && measures[key]->getTime() > time) json[L"taskData"][key] = measures[key]->getValue(); @@ -165,7 +189,7 @@ json::value Unit::getData(long long time) /********** Options data **********/ json[L"optionsData"] = json::value::object(); - for (auto key : { L"ROE", L"reactionToThreat" }) + for (auto key : { L"ROE", L"reactionToThreat", L"emissionsCountermeasures", L"TACAN", L"radio", L"generalSettings"}) { if (measures.find(key) != measures.end() && measures[key]->getTime() > time) json[L"optionsData"][key] = measures[key]->getValue(); @@ -309,43 +333,102 @@ void Unit::setFormationOffset(Offset newFormationOffset) } void Unit::setROE(wstring newROE) { - ROE = newROE; - int ROEEnum; - if (newROE.compare(L"Free") == 0) - ROEEnum = ROE::WEAPON_FREE; - else if (newROE.compare(L"Designated free") == 0) - ROEEnum = ROE::OPEN_FIRE_WEAPON_FREE; - else if (newROE.compare(L"Designated") == 0) - ROEEnum = ROE::OPEN_FIRE; - else if (newROE.compare(L"Return") == 0) - ROEEnum = ROE::RETURN_FIRE; - else if (newROE.compare(L"Hold") == 0) - ROEEnum = ROE::WEAPON_HOLD; - else - return; - Command* command = dynamic_cast(new SetOption(ID, SetCommandType::ROE, ROEEnum)); - scheduler->appendCommand(command); addMeasure(L"ROE", json::value(newROE)); + + if (ROE != newROE) { + ROE = newROE; + + int ROEEnum; + if (ROE.compare(L"Free") == 0) + ROEEnum = ROE::WEAPON_FREE; + else if (ROE.compare(L"Designated free") == 0) + ROEEnum = ROE::OPEN_FIRE_WEAPON_FREE; + else if (ROE.compare(L"Designated") == 0) + ROEEnum = ROE::OPEN_FIRE; + else if (ROE.compare(L"Return") == 0) + ROEEnum = ROE::RETURN_FIRE; + else if (ROE.compare(L"Hold") == 0) + ROEEnum = ROE::WEAPON_HOLD; + else + return; + + Command* command = dynamic_cast(new SetOption(ID, SetCommandType::ROE, ROEEnum)); + scheduler->appendCommand(command); + } } void Unit::setReactionToThreat(wstring newReactionToThreat) { - reactionToThreat = newReactionToThreat; - int reactionToThreatEnum; - if (newReactionToThreat.compare(L"None") == 0) - reactionToThreatEnum = ReactionToThreat::NO_REACTION; - else if (newReactionToThreat.compare(L"Passive") == 0) - reactionToThreatEnum = ReactionToThreat::PASSIVE_DEFENCE; - else if (newReactionToThreat.compare(L"Evade") == 0) - reactionToThreatEnum = ReactionToThreat::EVADE_FIRE; - else if (newReactionToThreat.compare(L"Escape") == 0) - reactionToThreatEnum = ReactionToThreat::BYPASS_AND_ESCAPE; - else if (newReactionToThreat.compare(L"Abort") == 0) - reactionToThreatEnum = ReactionToThreat::ALLOW_ABORT_MISSION; - else - return; - Command* command = dynamic_cast(new SetOption(ID, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum)); - scheduler->appendCommand(command); addMeasure(L"reactionToThreat", json::value(newReactionToThreat)); + + if (reactionToThreat != newReactionToThreat) { + reactionToThreat = newReactionToThreat; + + int reactionToThreatEnum; + if (reactionToThreat.compare(L"None") == 0) + reactionToThreatEnum = ReactionToThreat::NO_REACTION; + else if (reactionToThreat.compare(L"Passive") == 0) + reactionToThreatEnum = ReactionToThreat::PASSIVE_DEFENCE; + else if (reactionToThreat.compare(L"Evade") == 0) + reactionToThreatEnum = ReactionToThreat::EVADE_FIRE; + else if (reactionToThreat.compare(L"Escape") == 0) + reactionToThreatEnum = ReactionToThreat::BYPASS_AND_ESCAPE; + else if (reactionToThreat.compare(L"Abort") == 0) + reactionToThreatEnum = ReactionToThreat::ALLOW_ABORT_MISSION; + else + return; + + Command* command = dynamic_cast(new SetOption(ID, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum)); + scheduler->appendCommand(command); + } +} + +void Unit::setEmissionsCountermeasures(wstring newEmissionsCountermeasures) { + addMeasure(L"emissionsCountermeasures", json::value(newEmissionsCountermeasures)); + + if (emissionsCountermeasures != newEmissionsCountermeasures) { + emissionsCountermeasures = newEmissionsCountermeasures; + + int radarEnum; + int flareEnum; + int ECMEnum; + if (emissionsCountermeasures.compare(L"Silent") == 0) + { + radarEnum = RadarUse::NEVER; + flareEnum = FlareUse::NEVER; + ECMEnum = ECMUse::NEVER_USE; + } + else if (emissionsCountermeasures.compare(L"Attack") == 0) + { + radarEnum = RadarUse::FOR_ATTACK_ONLY; + flareEnum = FlareUse::AGAINST_FIRED_MISSILE; + ECMEnum = ECMUse::USE_IF_ONLY_LOCK_BY_RADAR; + } + else if (emissionsCountermeasures.compare(L"Defend") == 0) + { + radarEnum = RadarUse::FOR_SEARCH_IF_REQUIRED; + flareEnum = FlareUse::WHEN_FLYING_IN_SAM_WEZ; + ECMEnum = ECMUse::USE_IF_DETECTED_LOCK_BY_RADAR; + } + else if (emissionsCountermeasures.compare(L"Free") == 0) + { + radarEnum = RadarUse::FOR_CONTINUOUS_SEARCH; + flareEnum = FlareUse::WHEN_FLYING_NEAR_ENEMIES; + ECMEnum = ECMUse::ALWAYS_USE; + } + else + return; + + Command* command; + + command = dynamic_cast(new SetOption(ID, SetCommandType::RADAR_USING, radarEnum)); + scheduler->appendCommand(command); + + command = dynamic_cast(new SetOption(ID, SetCommandType::FLARE_USING, flareEnum)); + scheduler->appendCommand(command); + + command = dynamic_cast(new SetOption(ID, SetCommandType::ECM_USING, ECMEnum)); + scheduler->appendCommand(command); + } } void Unit::landAt(Coords loc) { @@ -364,93 +447,131 @@ void Unit::setIsAWACS(bool newIsAWACS) { isAWACS = newIsAWACS; resetTask(); addMeasure(L"isAWACS", json::value(newIsAWACS)); - setEPLRS(true); + setEPLRS(isAWACS); } -void Unit::setTACANChannel(int newTACANChannel) { - TACANChannel = newTACANChannel; - addMeasure(L"TACANChannel", json::value(newTACANChannel)); -} +void Unit::setTACAN(Options::TACAN newTACAN) { + auto json = json::value(); + json[L"isOn"] = json::value(newTACAN.isOn); + json[L"channel"] = json::value(newTACAN.channel); + json[L"XY"] = json::value(newTACAN.XY); + json[L"callsign"] = json::value(newTACAN.callsign); + addMeasure(L"TACAN", json); -void Unit::setTACANXY(wstring newTACANXY) { - TACANXY = newTACANXY; - addMeasure(L"TACANXY", json::value(newTACANXY)); -} -void Unit::setTACANCallsign(wstring newTACANCallsign) { - TACANCallsign = newTACANCallsign; - addMeasure(L"TACANCallsign", json::value(newTACANCallsign)); -} - -void Unit::setRadioFrequency(int newRadioFrequency) { - radioFrequency = newRadioFrequency; - addMeasure(L"radioFrequency", json::value(newRadioFrequency)); -} - -void Unit::setRadioCallsign(int newRadioCallsign) { - radioCallsign = newRadioCallsign; - addMeasure(L"radioCallsign", json::value(newRadioCallsign)); -} - -void Unit::setRadioCallsignNumber(int newRadioCallsignNumber) { - radioCallsignNumber = newRadioCallsignNumber; - addMeasure(L"radioCallsignNumber", json::value(newRadioCallsignNumber)); -} - -void Unit::setEPLRS(bool state) -{ - std::wostringstream commandSS; - commandSS << "{" - << "id = 'EPLRS'," - << "params = {" - << "value = " << (state? "true": "false") << ", " - << "}" - << "}"; - Command* command = dynamic_cast(new SetCommand(ID, commandSS.str())); - scheduler->appendCommand(command); -} - -void Unit::setTACAN() -{ - std::wostringstream commandSS; - commandSS << "{" - << "id = 'ActivateBeacon'," - << "params = {" - << "type = " << ((TACANXY.compare(L"X") == 0)? 4: 5) << "," - << "system = 3," - << "name = \"Olympus_TACAN\"," - << "callsign = \"" << TACANCallsign << "\", " - << "frequency = " << TACANChannelToFrequency(TACANChannel, TACANXY) << "," - << "}" - << "}"; - Command* command = dynamic_cast(new SetCommand(ID, commandSS.str())); - scheduler->appendCommand(command); -} - -void Unit::setRadio() -{ + if (TACAN != newTACAN) { - std::wostringstream commandSS; - commandSS << "{" - << "id = 'SetFrequency'," - << "params = {" - << "modulation = 0," // TODO Allow selection - << "frequency = " << radioFrequency << "," - << "}" - << "}"; - Command* command = dynamic_cast(new SetCommand(ID, commandSS.str())); - scheduler->appendCommand(command); + TACAN = newTACAN; + if (TACAN.isOn) { + std::wostringstream commandSS; + commandSS << "{" + << "id = 'ActivateBeacon'," + << "params = {" + << "type = " << ((TACAN.XY.compare(L"X") == 0) ? 4 : 5) << "," + << "system = 3," + << "name = \"Olympus_TACAN\"," + << "callsign = \"" << TACAN.callsign << "\", " + << "frequency = " << TACANChannelToFrequency(TACAN.channel, TACAN.XY) << "," + << "}" + << "}"; + Command* command = dynamic_cast(new SetCommand(ID, commandSS.str())); + scheduler->appendCommand(command); + } + else { + std::wostringstream commandSS; + commandSS << "{" + << "id = 'DeactivateBeacon'," + << "params = {" + << "}" + << "}"; + Command* command = dynamic_cast(new SetCommand(ID, commandSS.str())); + scheduler->appendCommand(command); + } } +} +void Unit::setRadio(Options::Radio newRadio) { + + auto json = json::value(); + json[L"frequency"] = json::value(newRadio.frequency); + json[L"callsign"] = json::value(newRadio.callsign); + json[L"callsignNumber"] = json::value(newRadio.callsignNumber); + addMeasure(L"radio", json); + + if (radio != newRadio) { + radio = newRadio; + std::wostringstream commandSS; + Command* command; + commandSS << "{" - << "id = 'SetCallsign'," - << "params = {" - << "callname = " << radioCallsign << "," - << "number = " << radioCallsignNumber << "," - << "}" - << "}"; - Command* command = dynamic_cast(new SetCommand(ID, commandSS.str())); + << "id = 'SetFrequency'," + << "params = {" + << "modulation = 0," // TODO Allow selection + << "frequency = " << radio.frequency << "," + << "}" + << "}"; + command = dynamic_cast(new SetCommand(ID, commandSS.str())); + scheduler->appendCommand(command); + + // Clear the stringstream + commandSS.str(wstring()); + + commandSS << "{" + << "id = 'SetCallsign'," + << "params = {" + << "callname = " << radio.callsign << "," + << "number = " << radio.callsignNumber << "," + << "}" + << "}"; + command = dynamic_cast(new SetCommand(ID, commandSS.str())); + scheduler->appendCommand(command); + } +} + +void Unit::setEPLRS(bool newEPLRS) +{ + //addMeasure(L"EPLRS", json::value(newEPLRS)); + // + //if (EPLRS != newEPLRS) { + // EPLRS = newEPLRS; + // + // std::wostringstream 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) { + + auto json = json::value(); + json[L"prohibitJettison"] = json::value(newGeneralSettings.prohibitJettison); + json[L"prohibitAA"] = json::value(newGeneralSettings.prohibitAA); + json[L"prohibitAG"] = json::value(newGeneralSettings.prohibitAG); + json[L"prohibitAfterburner"] = json::value(newGeneralSettings.prohibitAfterburner); + json[L"prohibitAirWpn"] = json::value(newGeneralSettings.prohibitAirWpn); + addMeasure(L"generalSettings", json); + + if (generalSettings != newGeneralSettings) + { + generalSettings = newGeneralSettings; + + Command* command; + command = dynamic_cast(new SetOption(ID, SetCommandType::PROHIBIT_AA, generalSettings.prohibitAA)); + scheduler->appendCommand(command); + command = dynamic_cast(new SetOption(ID, SetCommandType::PROHIBIT_AG, generalSettings.prohibitAG)); + scheduler->appendCommand(command); + command = dynamic_cast(new SetOption(ID, SetCommandType::PROHIBIT_JETT, generalSettings.prohibitJettison)); + scheduler->appendCommand(command); + command = dynamic_cast(new SetOption(ID, SetCommandType::PROHIBIT_AB, generalSettings.prohibitAfterburner)); + scheduler->appendCommand(command); + command = dynamic_cast(new SetOption(ID, SetCommandType::ENGAGE_AIR_WEAPONS, !generalSettings.prohibitAirWpn)); scheduler->appendCommand(command); } } diff --git a/src/core/src/unitsmanager.cpp b/src/core/src/unitsmanager.cpp index bd05a61c..00b1b8cd 100644 --- a/src/core/src/unitsmanager.cpp +++ b/src/core/src/unitsmanager.cpp @@ -64,11 +64,14 @@ void UnitsManager::updateExportData(lua_State* L) units[ID] = dynamic_cast(new Bomb(p.second, ID)); } } + /* Initialize the unit if creation was successfull */ + if (units.count(ID) != 0) + units[ID]->initialize(p.second); } - /* Update the unit if present*/ - if (units.count(ID) != 0) - { - units[ID]->updateExportData(p.second); + else { + /* Update the unit if present*/ + if (units.count(ID) != 0) + units[ID]->updateExportData(p.second); } }