mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Added ability to take control of units
And bugfixes
This commit is contained in:
parent
156c730352
commit
4f2debeb4f
@ -49,17 +49,12 @@ const DEMO_UNIT_DATA = {
|
||||
currentTask: "Holding",
|
||||
currentState: "Idle",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000,
|
||||
desiredSpeed: 400,
|
||||
desiredSpeedType: "CAS",
|
||||
desiredAltitude: 3000,
|
||||
desiredAltitudeType: "ASL",
|
||||
isTanker: false,
|
||||
TACANOn: false,
|
||||
TACANChannel: 32,
|
||||
TACANXY: "Y",
|
||||
TACANCallsign: "ASD",
|
||||
radioFrequency: 123.750,
|
||||
radioCallsign: 2,
|
||||
radioCallsignNumber: 3,
|
||||
radioAMFM: "FM"
|
||||
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "Designated",
|
||||
@ -101,8 +96,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 300,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 300,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "Designated",
|
||||
@ -112,7 +107,7 @@ const DEMO_UNIT_DATA = {
|
||||
["3"]:{
|
||||
baseData: {
|
||||
AI: true,
|
||||
name: "2S6 Tunguska",
|
||||
name: "M-60",
|
||||
unitName: "Olympus 1-3",
|
||||
groupName: "Group 4",
|
||||
alive: true,
|
||||
@ -144,8 +139,9 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000,
|
||||
onOff: false
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -157,7 +153,6 @@ const DEMO_UNIT_DATA = {
|
||||
AI: true,
|
||||
name: "2S6 Tunguska",
|
||||
unitName: "Olympus 1-4",
|
||||
groupName: "Group 1",
|
||||
alive: true,
|
||||
category: "GroundUnit",
|
||||
},
|
||||
@ -187,8 +182,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -230,8 +225,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -273,8 +268,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -316,8 +311,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -359,8 +354,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -402,8 +397,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -445,8 +440,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -488,8 +483,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -531,8 +526,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -574,8 +569,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
@ -617,8 +612,8 @@ const DEMO_UNIT_DATA = {
|
||||
taskData: {
|
||||
currentTask: "Example task",
|
||||
activePath: undefined,
|
||||
targetSpeed: 400,
|
||||
targetAltitude: 3000
|
||||
desiredSpeed: 400,
|
||||
desiredAltitude: 3000
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "None",
|
||||
|
||||
32
client/src/@types/unit.d.ts
vendored
32
client/src/@types/unit.d.ts
vendored
@ -3,7 +3,7 @@ interface UpdateData {
|
||||
}
|
||||
|
||||
interface BaseData {
|
||||
AI: boolean;
|
||||
controlled: boolean;
|
||||
name: string;
|
||||
unitName: string;
|
||||
groupName: string;
|
||||
@ -23,7 +23,7 @@ interface MissionData {
|
||||
fuel: number;
|
||||
flags: any;
|
||||
ammo: any;
|
||||
targets: any;
|
||||
contacts: any;
|
||||
hasTask: boolean;
|
||||
coalition: string;
|
||||
}
|
||||
@ -36,10 +36,16 @@ interface TaskData {
|
||||
currentState: string;
|
||||
currentTask: string;
|
||||
activePath: any;
|
||||
targetSpeed: number;
|
||||
targetAltitude: number;
|
||||
desiredSpeed: number;
|
||||
desiredSpeedType: string;
|
||||
desiredAltitude: number;
|
||||
desiredAltitudeType: string;
|
||||
targetLocation: any;
|
||||
isTanker: boolean;
|
||||
isAWACS: boolean;
|
||||
onOff: boolean;
|
||||
followRoads: boolean;
|
||||
targetID: number;
|
||||
}
|
||||
|
||||
interface OptionsData {
|
||||
@ -51,15 +57,6 @@ interface OptionsData {
|
||||
generalSettings: GeneralSettings;
|
||||
}
|
||||
|
||||
interface UnitData {
|
||||
baseData: BaseData;
|
||||
flightData: FlightData;
|
||||
missionData: MissionData;
|
||||
formationData: FormationData;
|
||||
taskData: TaskData;
|
||||
optionsData: OptionsData;
|
||||
}
|
||||
|
||||
interface TACAN {
|
||||
isOn: boolean;
|
||||
channel: number;
|
||||
@ -91,4 +88,13 @@ interface UnitIconOptions {
|
||||
showAmmo: boolean,
|
||||
showSummary: boolean,
|
||||
rotateToHeading: boolean
|
||||
}
|
||||
|
||||
interface UnitData {
|
||||
baseData: BaseData;
|
||||
flightData: FlightData;
|
||||
missionData: MissionData;
|
||||
formationData: FormationData;
|
||||
taskData: TaskData;
|
||||
optionsData: OptionsData;
|
||||
}
|
||||
@ -123,7 +123,7 @@ export abstract class ATCBoard {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( baseData.AI === true ) {
|
||||
if ( baseData.controlled === true ) {
|
||||
// return false;
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ export class UnitDataTable extends Panel {
|
||||
|
||||
for (const unit of unitsArray) {
|
||||
|
||||
const dataset = [unit.getBaseData().unitName, unit.getBaseData().name, unit.getBaseData().category, (unit.getBaseData().AI) ? "AI" : "Human"];
|
||||
const dataset = [unit.getBaseData().unitName, unit.getBaseData().name, unit.getBaseData().category, (unit.getBaseData().controlled) ? "AI" : "Human"];
|
||||
|
||||
addRow(el, dataset);
|
||||
}
|
||||
|
||||
@ -3,34 +3,22 @@ import { getUnitsManager } from "..";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Slider } from "../controls/slider";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { Aircraft, GroundUnit, Unit } from "../units/unit";
|
||||
import { UnitDatabase } from "../units/unitdatabase";
|
||||
import { Unit } from "../units/unit";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
const ROEs: string[] = ["Hold", "Return", "Designated", "Free"];
|
||||
const reactionsToThreat: string[] = ["None", "Manoeuvre", "Passive", "Evade"];
|
||||
const emissionsCountermeasures: string[] = ["Silent", "Attack", "Defend", "Free"];
|
||||
|
||||
const ROEDescriptions: string[] = ["Hold (Never fire)", "Return (Only fire if fired upon)", "Designated (Attack the designated target only)", "Free (Attack anyone)"];
|
||||
const reactionsToThreatDescriptions: string[] = ["None (No reaction)", "Manoeuvre (no countermeasures)", "Passive (Countermeasures only, no manoeuvre)", "Evade (Countermeasures and manoeuvers)"];
|
||||
const emissionsCountermeasuresDescriptions: string[] = ["Silent (Radar OFF, no ECM)", "Attack (Radar only for targeting, ECM only if locked)", "Defend (Radar for searching, ECM if locked)", "Always on (Radar and ECM always on)"];
|
||||
|
||||
const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 };
|
||||
const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 };
|
||||
const speedIncrements: { [key: string]: number } = { Aircraft: 25, Helicopter: 10, NavyUnit: 5, GroundUnit: 5 };
|
||||
const minAltitudeValues: { [key: string]: number } = { Aircraft: 0, Helicopter: 0 };
|
||||
const maxAltitudeValues: { [key: string]: number } = { Aircraft: 50000, Helicopter: 10000 };
|
||||
const altitudeIncrements: { [key: string]: number } = { Aircraft: 500, Helicopter: 100 };
|
||||
import { Switch } from "../controls/switch";
|
||||
import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, speedIncrements } from "../constants/constants";
|
||||
import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils";
|
||||
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
#airspeedSlider: Slider;
|
||||
#altitudeTypeSwitch: Switch;
|
||||
#speedSlider: Slider;
|
||||
#speedTypeSwitch: Switch;
|
||||
#onOffSwitch: Switch;
|
||||
#followRoadsSwitch: Switch;
|
||||
#TACANXYDropdown: Dropdown;
|
||||
#radioDecimalsDropdown: Dropdown;
|
||||
#radioCallsignDropdown: Dropdown;
|
||||
#expectedAltitude: number = -1;
|
||||
#expectedSpeed: number = -1;
|
||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||
#advancedSettingsDialog: HTMLElement;
|
||||
|
||||
@ -38,22 +26,11 @@ export class UnitControlPanel extends Panel {
|
||||
super(ID);
|
||||
|
||||
/* Unit control sliders */
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => {
|
||||
this.#expectedAltitude = value;
|
||||
getUnitsManager().selectedUnitsSetAltitude(value * 0.3048)
|
||||
});
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => { getUnitsManager().selectedUnitsSetAltitude(ftToM(value)); });
|
||||
this.#altitudeTypeSwitch = new Switch("altitude-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetAltitudeType(value? "AGL": "ASL"); });
|
||||
|
||||
this.#airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => {
|
||||
this.#expectedSpeed = value;
|
||||
getUnitsManager().selectedUnitsSetSpeed(value / 1.94384)
|
||||
});
|
||||
|
||||
/* Advanced settings dropdowns */
|
||||
this.#TACANXYDropdown = new Dropdown("TACAN-XY", () => {});
|
||||
this.#TACANXYDropdown.setOptions(["X", "Y"]);
|
||||
this.#radioDecimalsDropdown = new Dropdown("radio-decimals", () => {});
|
||||
this.#radioDecimalsDropdown.setOptions([".000", ".250", ".500", ".750"]);
|
||||
this.#radioCallsignDropdown = new Dropdown("radio-callsign", () => {});
|
||||
this.#speedSlider = new Slider("speed-slider", 0, 100, "kts", (value: number) => { getUnitsManager().selectedUnitsSetSpeed(knotsToMs(value)); });
|
||||
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetSpeedType(value? "GS": "CAS"); });
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["ROE"] = ROEs.map((option: string, index: number) => {
|
||||
@ -72,8 +49,27 @@ export class UnitControlPanel extends Panel {
|
||||
this.getElement().querySelector("#reaction-to-threat-buttons-container")?.append(...this.#optionButtons["reactionToThreat"]);
|
||||
this.getElement().querySelector("#emissions-countermeasures-buttons-container")?.append(...this.#optionButtons["emissionsCountermeasures"]);
|
||||
|
||||
/* On off switch */
|
||||
this.#onOffSwitch = new Switch("on-off-switch", (value: boolean) => {
|
||||
getUnitsManager().selectedUnitsSetOnOff(value);
|
||||
});
|
||||
|
||||
/* Follow roads switch */
|
||||
this.#followRoadsSwitch = new Switch("follow-roads-switch", (value: boolean) => {
|
||||
getUnitsManager().selectedUnitsSetFollowRoads(value);
|
||||
});
|
||||
|
||||
/* Advanced settings dialog */
|
||||
this.#advancedSettingsDialog = <HTMLElement> document.querySelector("#advanced-settings-dialog");
|
||||
|
||||
/* Advanced settings dropdowns */
|
||||
this.#TACANXYDropdown = new Dropdown("TACAN-XY", () => {});
|
||||
this.#TACANXYDropdown.setOptions(["X", "Y"]);
|
||||
this.#radioDecimalsDropdown = new Dropdown("radio-decimals", () => {});
|
||||
this.#radioDecimalsDropdown.setOptions([".000", ".250", ".500", ".750"]);
|
||||
this.#radioCallsignDropdown = new Dropdown("radio-callsign", () => {});
|
||||
|
||||
/* Events and timer */
|
||||
window.setInterval(() => {this.update();}, 25);
|
||||
|
||||
document.addEventListener("unitsSelection", (e: CustomEvent<Unit[]>) => { this.show(); this.addButtons();});
|
||||
@ -82,35 +78,30 @@ export class UnitControlPanel extends Panel {
|
||||
document.addEventListener("showAdvancedSettings", () => {
|
||||
this.#updateAdvancedSettingsDialog(getUnitsManager().getSelectedUnits());
|
||||
this.#advancedSettingsDialog.classList.remove("hide");
|
||||
})
|
||||
});
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
// Do this after panel is hidden (make sure there's a reset)
|
||||
hide() {
|
||||
super.hide();
|
||||
|
||||
this.#expectedAltitude = -1;
|
||||
this.#expectedSpeed = -1;
|
||||
show() {
|
||||
super.show();
|
||||
this.#speedTypeSwitch.resetExpectedValue();
|
||||
this.#altitudeTypeSwitch.resetExpectedValue();
|
||||
this.#onOffSwitch.resetExpectedValue();
|
||||
this.#followRoadsSwitch.resetExpectedValue();
|
||||
this.#altitudeSlider.resetExpectedValue();
|
||||
this.#speedSlider.resetExpectedValue();
|
||||
}
|
||||
|
||||
addButtons() {
|
||||
var units = getUnitsManager().getSelectedUnits();
|
||||
if (units.length < 20) {
|
||||
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => {
|
||||
let database: UnitDatabase | null;
|
||||
if (unit instanceof Aircraft)
|
||||
database = aircraftDatabase;
|
||||
else if (unit instanceof GroundUnit)
|
||||
database = groundUnitsDatabase;
|
||||
else
|
||||
database = null; // TODO add databases for other unit types
|
||||
|
||||
var button = document.createElement("button");
|
||||
var callsign = unit.getBaseData().unitName || "";
|
||||
var label = unit.getDatabase()?.getByName(unit.getBaseData().name)?.label || unit.getBaseData().name;
|
||||
|
||||
button.setAttribute("data-short-label", database?.getByName(unit.getBaseData().name)?.shortLabel || unit.getBaseData().name);
|
||||
button.setAttribute("data-label", label);
|
||||
button.setAttribute("data-callsign", callsign);
|
||||
|
||||
button.setAttribute("data-coalition", unit.getMissionData().coalition);
|
||||
@ -131,11 +122,53 @@ export class UnitControlPanel extends Panel {
|
||||
|
||||
update() {
|
||||
if (this.getVisible()){
|
||||
var units = getUnitsManager().getSelectedUnits();
|
||||
this.getElement().querySelector("#advanced-settings-div")?.classList.toggle("hide", units.length != 1);
|
||||
if (this.getElement() != null && units.length > 0) {
|
||||
this.#showFlightControlSliders(units);
|
||||
const element = this.getElement();
|
||||
const units = getUnitsManager().getSelectedUnits();
|
||||
const selectedUnitsTypes = getUnitsManager().getSelectedUnitsTypes();
|
||||
|
||||
if (element != null && units.length > 0) {
|
||||
/* Toggle visibility of control elements */
|
||||
element.toggleAttribute("data-show-categories-tooltip", selectedUnitsTypes.length > 1);
|
||||
element.toggleAttribute("data-show-speed-slider", selectedUnitsTypes.length == 1);
|
||||
element.toggleAttribute("data-show-altitude-slider", selectedUnitsTypes.length == 1 && (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-roe", true);
|
||||
element.toggleAttribute("data-show-threat", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-emissions-countermeasures", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-on-off", (selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")) && !(selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-follow-roads", (selectedUnitsTypes.length == 1 && selectedUnitsTypes.includes("GroundUnit")));
|
||||
element.toggleAttribute("data-show-advanced-settings-button", units.length == 1);
|
||||
|
||||
/* Flight controls */
|
||||
var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitude});
|
||||
var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitudeType});
|
||||
var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeed});
|
||||
var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeedType});
|
||||
var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().onOff});
|
||||
var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().followRoads});
|
||||
|
||||
if (selectedUnitsTypes.length == 1) {
|
||||
this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "AGL": undefined, false);
|
||||
this.#speedTypeSwitch.setValue(desiredSpeedType != undefined? desiredSpeedType == "GS": undefined, false);
|
||||
|
||||
this.#speedSlider.setMinMax(minSpeedValues[selectedUnitsTypes[0]], maxSpeedValues[selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setMinMax(minAltitudeValues[selectedUnitsTypes[0]], maxAltitudeValues[selectedUnitsTypes[0]]);
|
||||
this.#speedSlider.setIncrement(speedIncrements[selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setIncrement(altitudeIncrements[selectedUnitsTypes[0]]);
|
||||
|
||||
this.#speedSlider.setActive(desiredSpeed != undefined);
|
||||
if (desiredSpeed != undefined)
|
||||
this.#speedSlider.setValue(msToKnots(desiredSpeed), false);
|
||||
|
||||
this.#altitudeSlider.setActive(desiredAltitude != undefined);
|
||||
if (desiredAltitude != undefined)
|
||||
this.#altitudeSlider.setValue(mToFt(desiredAltitude), false);
|
||||
}
|
||||
else {
|
||||
this.#speedSlider.setActive(false);
|
||||
this.#altitudeSlider.setActive(false);
|
||||
}
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().ROE === button.value))
|
||||
});
|
||||
@ -147,75 +180,13 @@ export class UnitControlPanel extends Panel {
|
||||
this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().emissionsCountermeasures === button.value))
|
||||
});
|
||||
|
||||
this.#onOffSwitch.setValue(onOff, false);
|
||||
this.#followRoadsSwitch.setValue(followRoads, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 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;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#updateCanSetSpeedSlider(altitude: number) {
|
||||
if (this.#expectedSpeed < 0 || altitude === this.#expectedSpeed) {
|
||||
this.#expectedSpeed = -1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#showFlightControlSliders(units: Unit[]) {
|
||||
if (getUnitsManager().getSelectedUnitsType() !== undefined)
|
||||
this.#airspeedSlider.show()
|
||||
else
|
||||
this.#airspeedSlider.hide();
|
||||
|
||||
if (getUnitsManager().getSelectedUnitsType() === "Aircraft" || getUnitsManager().getSelectedUnitsType() === "Helicopter")
|
||||
this.#altitudeSlider.show()
|
||||
else
|
||||
this.#altitudeSlider.hide();
|
||||
|
||||
this.getElement().querySelector(`#categories-tooltip`)?.classList.toggle("hide", getUnitsManager().getSelectedUnitsType() !== undefined);
|
||||
|
||||
var unitsType = getUnitsManager().getSelectedUnitsType();
|
||||
var targetAltitude = getUnitsManager().getSelectedUnitsTargetAltitude();
|
||||
var targetSpeed = getUnitsManager().getSelectedUnitsTargetSpeed();
|
||||
|
||||
if (unitsType != undefined) {
|
||||
if (["GroundUnit", "NavyUnit"].includes(unitsType))
|
||||
this.#altitudeSlider.hide()
|
||||
|
||||
this.#airspeedSlider.setMinMax(minSpeedValues[unitsType], maxSpeedValues[unitsType]);
|
||||
this.#altitudeSlider.setMinMax(minAltitudeValues[unitsType], maxAltitudeValues[unitsType]);
|
||||
this.#airspeedSlider.setIncrement(speedIncrements[unitsType]);
|
||||
this.#altitudeSlider.setIncrement(altitudeIncrements[unitsType]);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.#airspeedSlider.setActive(false);
|
||||
this.#altitudeSlider.setActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
#updateAdvancedSettingsDialog(units: Unit[])
|
||||
{
|
||||
if (units.length == 1)
|
||||
|
||||
@ -16,7 +16,7 @@ export class UnitInfoPanel extends Panel {
|
||||
#latitude: HTMLElement;
|
||||
#longitude: HTMLElement;
|
||||
#loadoutContainer: HTMLElement;
|
||||
#silhouette: HTMLElement;
|
||||
#silhouette: HTMLImageElement;
|
||||
#unitControl: HTMLElement;
|
||||
#unitLabel: HTMLElement;
|
||||
#unitName: HTMLElement;
|
||||
@ -24,21 +24,21 @@ export class UnitInfoPanel extends Panel {
|
||||
constructor(ID: string) {
|
||||
super(ID);
|
||||
|
||||
this.#altitude = <HTMLElement>(this.getElement().querySelector("#altitude"));
|
||||
this.#currentTask = <HTMLElement>(this.getElement().querySelector("#current-task"));
|
||||
this.#groundSpeed = <HTMLElement>(this.getElement().querySelector("#ground-speed"));
|
||||
this.#fuelBar = <HTMLElement>(this.getElement().querySelector("#fuel-bar"));
|
||||
this.#fuelPercentage = <HTMLElement>(this.getElement().querySelector("#fuel-percentage"));
|
||||
this.#groupName = <HTMLElement>(this.getElement().querySelector("#group-name"));
|
||||
this.#heading = <HTMLElement>(this.getElement().querySelector("#heading"));
|
||||
this.#name = <HTMLElement>(this.getElement().querySelector("#name"));
|
||||
this.#latitude = <HTMLElement>(this.getElement().querySelector("#latitude"));
|
||||
this.#loadoutContainer = <HTMLElement>(this.getElement().querySelector("#loadout-container"));
|
||||
this.#longitude = <HTMLElement>(this.getElement().querySelector("#longitude"));
|
||||
this.#silhouette = <HTMLElement>(this.getElement().querySelector("#loadout-silhouette"));
|
||||
this.#unitControl = <HTMLElement>(this.getElement().querySelector("#unit-control"));
|
||||
this.#unitLabel = <HTMLElement>(this.getElement().querySelector("#unit-label"));
|
||||
this.#unitName = <HTMLElement>(this.getElement().querySelector("#unit-name"));
|
||||
this.#altitude = (this.getElement().querySelector("#altitude")) as HTMLElement;
|
||||
this.#currentTask = (this.getElement().querySelector("#current-task")) as HTMLElement;
|
||||
this.#groundSpeed = (this.getElement().querySelector("#ground-speed")) as HTMLElement;
|
||||
this.#fuelBar = (this.getElement().querySelector("#fuel-bar")) as HTMLElement;
|
||||
this.#fuelPercentage = (this.getElement().querySelector("#fuel-percentage")) as HTMLElement;
|
||||
this.#groupName = (this.getElement().querySelector("#group-name")) as HTMLElement;
|
||||
this.#heading = (this.getElement().querySelector("#heading")) as HTMLElement;
|
||||
this.#name = (this.getElement().querySelector("#name")) as HTMLElement;
|
||||
this.#latitude = (this.getElement().querySelector("#latitude")) as HTMLElement;
|
||||
this.#loadoutContainer = (this.getElement().querySelector("#loadout-container")) as HTMLElement;
|
||||
this.#longitude = (this.getElement().querySelector("#longitude")) as HTMLElement;
|
||||
this.#silhouette = (this.getElement().querySelector("#loadout-silhouette")) as HTMLImageElement;
|
||||
this.#unitControl = (this.getElement().querySelector("#unit-control")) as HTMLElement;
|
||||
this.#unitLabel = (this.getElement().querySelector("#unit-label")) as HTMLElement;
|
||||
this.#unitName = (this.getElement().querySelector("#unit-name")) as HTMLElement;
|
||||
|
||||
document.addEventListener("unitsSelection", (e: CustomEvent<Unit[]>) => this.#onUnitsSelection(e.detail));
|
||||
document.addEventListener("unitsDeselection", (e: CustomEvent<Unit[]>) => this.#onUnitsDeselection(e.detail));
|
||||
@ -47,69 +47,65 @@ export class UnitInfoPanel extends Panel {
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
|
||||
#onUnitUpdate(unit: Unit) {
|
||||
if (this.getElement() != null && this.getVisible() && unit.getSelected()) {
|
||||
|
||||
const baseData = unit.getBaseData();
|
||||
|
||||
/* Set the unit info */
|
||||
this.#unitLabel.innerText = aircraftDatabase.getByName(baseData.name)?.label || baseData.name;
|
||||
this.#unitName.innerText = baseData.unitName;
|
||||
this.#unitControl.innerText = ( ( baseData.AI ) ? "AI" : "Human" ) + " controlled";
|
||||
// this.#groupName.innerText = baseData.groupName;
|
||||
//this.#name.innerText = baseData.name;
|
||||
//this.#heading.innerText = String(Math.floor(rad2deg(unit.getFlightData().heading)) + " °");
|
||||
//this.#altitude.innerText = String(Math.floor(unit.getFlightData().altitude / 0.3048) + " ft");
|
||||
//this.#groundSpeed.innerText = String(Math.floor(unit.getFlightData().speed * 1.94384) + " kts");
|
||||
this.#unitLabel.innerText = aircraftDatabase.getByName(baseData.name)?.label || baseData.name;
|
||||
this.#unitName.innerText = baseData.unitName;
|
||||
if (unit.getMissionData().flags.Human)
|
||||
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.getMissionData().fuel + "%");
|
||||
this.#fuelPercentage.dataset.percentage = "" + unit.getMissionData().fuel;
|
||||
//this.#latitude.innerText = ConvertDDToDMS(unit.getFlightData().latitude, false);
|
||||
//this.#longitude.innerText = ConvertDDToDMS(unit.getFlightData().longitude, true);
|
||||
this.#currentTask.dataset.currentTask = unit.getTaskData().currentTask !== ""? unit.getTaskData().currentTask: "No task";
|
||||
this.#currentTask.dataset.currentTask = unit.getTaskData().currentTask !== "" ? unit.getTaskData().currentTask : "No task";
|
||||
this.#currentTask.dataset.coalition = unit.getMissionData().coalition;
|
||||
|
||||
this.#silhouette.setAttribute( "style", `--loadout-background-image:url('/images/units/${aircraftDatabase.getByName( baseData.name )?.filename}');` );;
|
||||
|
||||
|
||||
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 == '');
|
||||
|
||||
/* Add the loadout elements */
|
||||
const items = <HTMLElement>this.#loadoutContainer.querySelector( "#loadout-items" );
|
||||
|
||||
|
||||
if ( items ) {
|
||||
|
||||
const ammo = Object.values( unit.getMissionData().ammo );
|
||||
|
||||
if ( ammo.length > 0 ) {
|
||||
const items = <HTMLElement>this.#loadoutContainer.querySelector("#loadout-items");
|
||||
|
||||
if (items) {
|
||||
const ammo = Object.values(unit.getMissionData().ammo);
|
||||
if (ammo.length > 0) {
|
||||
items.replaceChildren(...Object.values(unit.getMissionData().ammo).map(
|
||||
(ammo: any) => {
|
||||
var el = document.createElement("div");
|
||||
el.dataset.qty = ammo.count;
|
||||
el.dataset.qty = ammo.count;
|
||||
el.dataset.item = ammo.desc.displayName;
|
||||
return el;
|
||||
}
|
||||
));
|
||||
|
||||
} else {
|
||||
|
||||
items.innerText = "No loadout";
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#onUnitsSelection(units: Unit[]){
|
||||
if (units.length == 1)
|
||||
#onUnitsSelection(units: Unit[]) {
|
||||
if (units.length == 1) {
|
||||
this.show();
|
||||
this.#onUnitUpdate(units[0]);
|
||||
}
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
|
||||
#onUnitsDeselection(units: Unit[]){
|
||||
if (units.length == 1)
|
||||
#onUnitsDeselection(units: Unit[]) {
|
||||
if (units.length == 1) {
|
||||
this.show();
|
||||
this.#onUnitUpdate(units[0]);
|
||||
}
|
||||
else
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@ -2456,8 +2456,8 @@ export class AircraftDatabase extends UnitDatabase {
|
||||
],
|
||||
"filename": "kc-135.png"
|
||||
},
|
||||
"KC-135MPRS": {
|
||||
"name": "KC-135MPRS",
|
||||
"KC135MPRS": {
|
||||
"name": "KC135MPRS",
|
||||
"label": "KC-135 MPRS Stratotanker",
|
||||
"era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"],
|
||||
"shortLabel": "135M",
|
||||
@ -2476,6 +2476,26 @@ export class AircraftDatabase extends UnitDatabase {
|
||||
],
|
||||
"filename": "kc-135.png"
|
||||
},
|
||||
"S-3B Tanker": {
|
||||
"name": "S-3B Tanker",
|
||||
"label": "S-3B Tanker",
|
||||
"era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"],
|
||||
"shortLabel": "S3B",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Tanker"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Default Tanker"
|
||||
}
|
||||
],
|
||||
"filename": "s-3.png"
|
||||
},
|
||||
"MiG-15bis": {
|
||||
"name": "MiG-15bis",
|
||||
"label": "MiG-15 Fagot",
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map } from 'leaflet';
|
||||
import { getMap, getUnitsManager } from '..';
|
||||
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 { 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 { aircraftDatabase } from './aircraftdatabase';
|
||||
import { groundUnitsDatabase } from './groundunitsdatabase';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { UnitDatabase } from './unitdatabase';
|
||||
import { BOMBING, CARPET_BOMBING, FIRE_AT_AREA, IDLE, MOVE_UNIT } from '../map/map';
|
||||
import { TargetMarker } from '../map/targetmarker';
|
||||
|
||||
var pathIcon = new Icon({
|
||||
iconUrl: '/resources/theme/images/markers/marker-icon.png',
|
||||
@ -19,7 +21,7 @@ export class Unit extends CustomMarker {
|
||||
|
||||
#data: UnitData = {
|
||||
baseData: {
|
||||
AI: false,
|
||||
controlled: false,
|
||||
name: "",
|
||||
unitName: "",
|
||||
groupName: "",
|
||||
@ -37,7 +39,7 @@ export class Unit extends CustomMarker {
|
||||
fuel: 0,
|
||||
flags: {},
|
||||
ammo: {},
|
||||
targets: {},
|
||||
contacts: {},
|
||||
hasTask: false,
|
||||
coalition: "",
|
||||
},
|
||||
@ -48,10 +50,16 @@ export class Unit extends CustomMarker {
|
||||
currentState: "NONE",
|
||||
currentTask: "",
|
||||
activePath: {},
|
||||
targetSpeed: 0,
|
||||
targetAltitude: 0,
|
||||
desiredSpeed: 0,
|
||||
desiredSpeedType: "GS",
|
||||
desiredAltitude: 0,
|
||||
desiredAltitudeType: "AGL",
|
||||
targetLocation: {},
|
||||
isTanker: false,
|
||||
isAWACS: false,
|
||||
onOff: true,
|
||||
followRoads: false,
|
||||
targetID: 0
|
||||
},
|
||||
optionsData: {
|
||||
ROE: "",
|
||||
@ -72,8 +80,10 @@ export class Unit extends CustomMarker {
|
||||
|
||||
#pathMarkers: Marker[] = [];
|
||||
#pathPolyline: Polyline;
|
||||
#targetsPolylines: Polyline[];
|
||||
#contactsPolylines: Polyline[];
|
||||
#miniMapMarker: CircleMarker | null = null;
|
||||
#targetLocationMarker: TargetMarker;
|
||||
#targetLocationPolyline: Polyline;
|
||||
|
||||
#timer: number = 0;
|
||||
|
||||
@ -103,7 +113,10 @@ export class Unit extends CustomMarker {
|
||||
|
||||
this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 });
|
||||
this.#pathPolyline.addTo(getMap());
|
||||
this.#targetsPolylines = [];
|
||||
this.#contactsPolylines = [];
|
||||
|
||||
this.#targetLocationMarker = new TargetMarker(new LatLng(0, 0));
|
||||
this.#targetLocationPolyline = new Polyline([], { color: '#FF0000', weight: 3, opacity: 0.5, smoothFactor: 1 });
|
||||
|
||||
/* Deselect units if they are hidden */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
@ -116,8 +129,6 @@ export class Unit extends CustomMarker {
|
||||
|
||||
/* Set the unit data */
|
||||
this.setData(data);
|
||||
|
||||
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@ -150,11 +161,16 @@ export class Unit extends CustomMarker {
|
||||
if ((this.getBaseData().alive || !selected) && this.getSelectable() && this.getSelected() != selected) {
|
||||
this.#selected = selected;
|
||||
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-selected", selected);
|
||||
if (selected)
|
||||
if (selected) {
|
||||
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
|
||||
else
|
||||
this.#updateMarker();
|
||||
}
|
||||
else {
|
||||
document.dispatchEvent(new CustomEvent("unitDeselection", { detail: this }));
|
||||
this.getGroupMembers().forEach((unit: Unit) => unit.setSelected(selected));
|
||||
this.#clearDetectedUnits();
|
||||
this.#clearPath();
|
||||
this.#clearTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -201,50 +217,23 @@ export class Unit extends CustomMarker {
|
||||
const positionChanged = (data.flightData != undefined && data.flightData.latitude != undefined && data.flightData.longitude != undefined && (this.getFlightData().latitude != data.flightData.latitude || this.getFlightData().longitude != data.flightData.longitude));
|
||||
const headingChanged = (data.flightData != undefined && data.flightData.heading != undefined && this.getFlightData().heading != data.flightData.heading);
|
||||
const aliveChanged = (data.baseData != undefined && data.baseData.alive != undefined && this.getBaseData().alive != data.baseData.alive);
|
||||
var updateMarker = (positionChanged || headingChanged || aliveChanged || !getMap().hasLayer(this));
|
||||
|
||||
if (data.baseData != undefined) {
|
||||
for (let key in this.#data.baseData)
|
||||
if (key in data.baseData)
|
||||
//@ts-ignore
|
||||
this.#data.baseData[key] = data.baseData[key];
|
||||
}
|
||||
|
||||
if (data.flightData != undefined) {
|
||||
for (let key in this.#data.flightData)
|
||||
if (key in data.flightData)
|
||||
//@ts-ignore
|
||||
this.#data.flightData[key] = data.flightData[key];
|
||||
}
|
||||
|
||||
if (data.missionData != undefined) {
|
||||
for (let key in this.#data.missionData)
|
||||
if (key in data.missionData)
|
||||
//@ts-ignore
|
||||
this.#data.missionData[key] = data.missionData[key];
|
||||
}
|
||||
|
||||
if (data.formationData != undefined) {
|
||||
for (let key in this.#data.formationData)
|
||||
if (key in data.formationData)
|
||||
//@ts-ignore
|
||||
this.#data.formationData[key] = data.formationData[key];
|
||||
}
|
||||
|
||||
if (data.taskData != undefined) {
|
||||
for (let key in this.#data.taskData)
|
||||
if (key in data.taskData)
|
||||
//@ts-ignore
|
||||
this.#data.taskData[key] = data.taskData[key];
|
||||
}
|
||||
|
||||
if (data.optionsData != undefined) {
|
||||
for (let key in this.#data.optionsData)
|
||||
if (key in data.optionsData)
|
||||
//@ts-ignore
|
||||
this.#data.optionsData[key] = data.optionsData[key];
|
||||
}
|
||||
const stateChanged = (data.taskData != undefined && data.taskData.currentState != undefined && this.getTaskData().currentState != data.taskData.currentState);
|
||||
const controlledChanged = (data.baseData != undefined && data.baseData.controlled != undefined && this.getBaseData().controlled != data.baseData.controlled);
|
||||
var updateMarker = (positionChanged || headingChanged || aliveChanged || stateChanged || controlledChanged || !getMap().hasLayer(this));
|
||||
|
||||
/* Load the data from the received json */
|
||||
Object.keys(this.#data).forEach((key1: string) => {
|
||||
Object.keys(this.#data[key1 as keyof(UnitData)]).forEach((key2: string) => {
|
||||
if (key1 in data && key2 in data[key1]) {
|
||||
var value1 = this.#data[key1 as keyof(UnitData)];
|
||||
var value2 = value1[key2 as keyof typeof value1];
|
||||
if (typeof data[key1][key2] === typeof value2 || typeof value2 === "undefined")
|
||||
//@ts-ignore
|
||||
this.#data[key1 as keyof(UnitData)][key2 as keyof typeof struct] = data[key1][key2];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/* Fire an event when a unit dies */
|
||||
if (aliveChanged && this.getBaseData().alive == false)
|
||||
document.dispatchEvent(new CustomEvent("unitDeath", { detail: this }));
|
||||
@ -255,13 +244,17 @@ export class Unit extends CustomMarker {
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
|
||||
this.#clearTargets();
|
||||
this.#clearDetectedUnits();
|
||||
if (this.getSelected()) {
|
||||
this.#drawPath();
|
||||
this.#drawTargets();
|
||||
this.#drawDetectedUnits();
|
||||
this.#drawTarget();
|
||||
}
|
||||
else
|
||||
else {
|
||||
this.#clearPath();
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
@ -400,7 +393,7 @@ export class Unit extends CustomMarker {
|
||||
const hiddenUnits = getUnitsManager().getHiddenTypes();
|
||||
if (this.getMissionData().flags.Human && hiddenUnits.includes("human"))
|
||||
hidden = true;
|
||||
else if (this.getBaseData().AI == false && hiddenUnits.includes("dcs"))
|
||||
else if (this.getBaseData().controlled == false && hiddenUnits.includes("dcs"))
|
||||
hidden = true;
|
||||
else if (hiddenUnits.includes(this.getMarkerCategory()))
|
||||
hidden = true;
|
||||
@ -431,6 +424,15 @@ export class Unit extends CustomMarker {
|
||||
return getUnitsManager().getUnitByID(this.getFormationData().leaderID);
|
||||
}
|
||||
|
||||
canRole(roles: string | string[]) {
|
||||
if (typeof(roles) === "string")
|
||||
roles = [roles];
|
||||
|
||||
return this.getDatabase()?.getByName(this.getBaseData().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.getMissionData().flags.Human) {
|
||||
@ -485,11 +487,21 @@ export class Unit extends CustomMarker {
|
||||
setSpeed(this.ID, speed);
|
||||
}
|
||||
|
||||
setSpeedType(speedType: string) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setSpeedType(this.ID, speedType);
|
||||
}
|
||||
|
||||
setAltitude(altitude: number) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setAltitude(this.ID, altitude);
|
||||
}
|
||||
|
||||
setAltitudeType(altitudeType: string) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setAltitudeType(this.ID, altitudeType);
|
||||
}
|
||||
|
||||
setROE(ROE: string) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setROE(this.ID, ROE);
|
||||
@ -510,8 +522,18 @@ export class Unit extends CustomMarker {
|
||||
setLeader(this.ID, isLeader, wingmenIDs);
|
||||
}
|
||||
|
||||
delete() {
|
||||
deleteUnit(this.ID);
|
||||
setOnOff(onOff: boolean) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setOnOff(this.ID, onOff);
|
||||
}
|
||||
|
||||
setFollowRoads(followRoads: boolean) {
|
||||
if (!this.getMissionData().flags.Human)
|
||||
setFollowRoads(this.ID, followRoads);
|
||||
}
|
||||
|
||||
delete(explosion: boolean) {
|
||||
deleteUnit(this.ID, explosion);
|
||||
}
|
||||
|
||||
refuel() {
|
||||
@ -524,6 +546,22 @@ export class Unit extends CustomMarker {
|
||||
setAdvacedOptions(this.ID, isTanker, isAWACS, TACAN, radio, generalSettings);
|
||||
}
|
||||
|
||||
bombPoint(latlng: LatLng) {
|
||||
bombPoint(this.ID, latlng);
|
||||
}
|
||||
|
||||
carpetBomb(latlng: LatLng) {
|
||||
carpetBomb(this.ID, latlng);
|
||||
}
|
||||
|
||||
bombBuilding(latlng: LatLng) {
|
||||
bombBuilding(this.ID, latlng);
|
||||
}
|
||||
|
||||
fireAtArea(latlng: LatLng) {
|
||||
fireAtArea(this.ID, latlng);
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
onAdd(map: Map): this {
|
||||
super.onAdd(map);
|
||||
@ -534,10 +572,9 @@ 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 (getMap().getState() === IDLE || getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
getUnitsManager().deselectAllUnits();
|
||||
}
|
||||
this.setSelected(!this.getSelected());
|
||||
}
|
||||
}
|
||||
@ -554,20 +591,35 @@ export class Unit extends CustomMarker {
|
||||
|
||||
#onContextMenu(e: any) {
|
||||
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"};
|
||||
|
||||
if (getUnitsManager().getSelectedUnits().length > 0 && !(getUnitsManager().getSelectedUnits().length == 1 && (getUnitsManager().getSelectedUnits().includes(this)))) {
|
||||
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"};
|
||||
if (getUnitsManager().getSelectedUnitsType() === "Aircraft")
|
||||
if (getUnitsManager().getSelectedUnitsTypes().length == 1 && getUnitsManager().getSelectedUnitsTypes()[0] === "Aircraft")
|
||||
options["follow"] = {text: "Follow", tooltip: "Follow the unit at a user defined distance and position"};;
|
||||
}
|
||||
else if ((getUnitsManager().getSelectedUnits().length > 0 && (getUnitsManager().getSelectedUnits().includes(this))) || getUnitsManager().getSelectedUnits().length == 0) {
|
||||
else if ((selectedUnits.length > 0 && (selectedUnits.includes(this))) || selectedUnits.length == 0) {
|
||||
if (this.getBaseData().category == "Aircraft") {
|
||||
options["refuel"] = {text: "AAR 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
|
||||
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.getBaseData().category == "Aircraft") || (selectedUnitTypes.length === 1 && ["Aircraft"].includes(selectedUnitTypes[0])))
|
||||
{
|
||||
if (selectedUnits.concat([this]).every((unit: Unit) => {return unit.canRole(["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.getBaseData().category == "GroundUnit") || selectedUnitTypes.length === 1 && ["GroundUnit"].includes(selectedUnitTypes[0])) {
|
||||
if (selectedUnits.concat([this]).every((unit: Unit) => {return unit.canRole(["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) {
|
||||
getMap().showUnitContextMenu(e);
|
||||
getMap().getUnitContextMenu().setOptions(options, (option: string) => {
|
||||
@ -586,6 +638,12 @@ export class Unit extends CustomMarker {
|
||||
getUnitsManager().selectedUnitsRefuel();
|
||||
else if (action === "follow")
|
||||
this.#showFollowOptions(e);
|
||||
else if (action === "bomb")
|
||||
getMap().setState(BOMBING);
|
||||
else if (action === "carpet-bomb")
|
||||
getMap().setState(CARPET_BOMBING);
|
||||
else if (action === "fire-at-area")
|
||||
getMap().setState(FIRE_AT_AREA);
|
||||
}
|
||||
|
||||
#showFollowOptions(e: any) {
|
||||
@ -668,16 +726,18 @@ export class Unit extends CustomMarker {
|
||||
/* Set current unit state */
|
||||
if (this.getMissionData().flags.Human) // Unit is human
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "human");
|
||||
else if (!this.getBaseData().AI) // Unit is under DCS control (not Olympus)
|
||||
else if (!this.getBaseData().controlled) // Unit is under DCS control (not Olympus)
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "dcs");
|
||||
else if ((this.getBaseData().category == "Aircraft" || this.getBaseData().category == "Helicopter") && !this.getMissionData().hasTask)
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "no-task");
|
||||
else // Unit is under Olympus control
|
||||
element.querySelector(".unit")?.setAttribute("data-state", this.getTaskData().currentState.toLowerCase());
|
||||
|
||||
/* Set altitude and speed */
|
||||
if (element.querySelector(".unit-altitude"))
|
||||
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(this.getFlightData().altitude / 0.3048 / 100));
|
||||
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.getFlightData().altitude) / 100));
|
||||
if (element.querySelector(".unit-speed"))
|
||||
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(this.getFlightData().speed * 1.94384));
|
||||
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.getFlightData().speed))) + "GS";
|
||||
|
||||
/* Rotate elements according to heading */
|
||||
element.querySelectorAll("[data-rotate-to-heading]").forEach(el => {
|
||||
@ -768,35 +828,74 @@ export class Unit extends CustomMarker {
|
||||
this.#pathPolyline.setLatLngs([]);
|
||||
}
|
||||
|
||||
#drawTargets() {
|
||||
for (let index in this.getMissionData().targets) {
|
||||
var targetData = this.getMissionData().targets[index];
|
||||
var target = getUnitsManager().getUnitByID(targetData.object["id_"])
|
||||
if (target != null) {
|
||||
var startLatLng = new LatLng(this.getFlightData().latitude, this.getFlightData().longitude)
|
||||
var endLatLng = new LatLng(target.getFlightData().latitude, target.getFlightData().longitude)
|
||||
#drawDetectedUnits() {
|
||||
for (let index in this.getMissionData().contacts) {
|
||||
var targetData = this.getMissionData().contacts[index];
|
||||
if (targetData.object != undefined){
|
||||
var target = getUnitsManager().getUnitByID(targetData.object["id_"])
|
||||
if (target != null) {
|
||||
var startLatLng = new LatLng(this.getFlightData().latitude, this.getFlightData().longitude)
|
||||
var endLatLng = new LatLng(target.getFlightData().latitude, target.getFlightData().longitude)
|
||||
|
||||
var color;
|
||||
if (targetData.detectionMethod === "RADAR")
|
||||
color = "#FFFF00";
|
||||
else if (targetData.detectionMethod === "VISUAL")
|
||||
color = "#FF00FF";
|
||||
else if (targetData.detectionMethod === "RWR")
|
||||
color = "#00FF00";
|
||||
else
|
||||
color = "#FFFFFF";
|
||||
var targetPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 0.4, smoothFactor: 1 });
|
||||
targetPolyline.addTo(getMap());
|
||||
this.#targetsPolylines.push(targetPolyline)
|
||||
var color;
|
||||
if (targetData.detectionMethod === "RADAR")
|
||||
color = "#FFFF00";
|
||||
else if (targetData.detectionMethod === "VISUAL")
|
||||
color = "#FF00FF";
|
||||
else if (targetData.detectionMethod === "RWR")
|
||||
color = "#00FF00";
|
||||
else
|
||||
color = "#FFFFFF";
|
||||
var targetPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 0.4, smoothFactor: 1, dashArray: "4, 8" });
|
||||
targetPolyline.addTo(getMap());
|
||||
this.#contactsPolylines.push(targetPolyline)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#clearTargets() {
|
||||
for (let index in this.#targetsPolylines) {
|
||||
getMap().removeLayer(this.#targetsPolylines[index])
|
||||
#clearDetectedUnits() {
|
||||
for (let index in this.#contactsPolylines) {
|
||||
getMap().removeLayer(this.#contactsPolylines[index])
|
||||
}
|
||||
}
|
||||
|
||||
#drawTarget() {
|
||||
const targetLocation = this.getTaskData().targetLocation;
|
||||
|
||||
if (targetLocation.latitude && targetLocation.longitude && targetLocation.latitude != 0 && targetLocation.longitude != 0) {
|
||||
const lat = targetLocation.latitude;
|
||||
const lng = targetLocation.longitude;
|
||||
if (lat && lng)
|
||||
this.#drawTargetLocation(new LatLng(lat, lng));
|
||||
}
|
||||
else if (this.getTaskData().targetID != 0 && getUnitsManager().getUnitByID(this.getTaskData().targetID)) {
|
||||
const flightData = getUnitsManager().getUnitByID(this.getTaskData().targetID)?.getFlightData();
|
||||
const lat = flightData?.latitude;
|
||||
const lng = flightData?.longitude;
|
||||
if (lat && lng)
|
||||
this.#drawTargetLocation(new LatLng(lat, lng));
|
||||
}
|
||||
else
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
#drawTargetLocation(targetLocation: LatLng) {
|
||||
if (!getMap().hasLayer(this.#targetLocationMarker))
|
||||
this.#targetLocationMarker.addTo(getMap());
|
||||
if (!getMap().hasLayer(this.#targetLocationPolyline))
|
||||
this.#targetLocationPolyline.addTo(getMap());
|
||||
this.#targetLocationMarker.setLatLng(new LatLng(targetLocation.lat, targetLocation.lng));
|
||||
this.#targetLocationPolyline.setLatLngs([new LatLng(this.getFlightData().latitude, this.getFlightData().longitude), new LatLng(targetLocation.lat, targetLocation.lng)])
|
||||
}
|
||||
|
||||
#clearTarget() {
|
||||
if (getMap().hasLayer(this.#targetLocationMarker))
|
||||
this.#targetLocationMarker.removeFrom(getMap());
|
||||
|
||||
if (getMap().hasLayer(this.#targetLocationPolyline))
|
||||
this.#targetLocationPolyline.removeFrom(getMap());
|
||||
}
|
||||
}
|
||||
|
||||
export class AirUnit extends Unit {
|
||||
@ -850,7 +949,7 @@ export class GroundUnit extends Unit {
|
||||
showVvi: false,
|
||||
showHotgroup: true,
|
||||
showUnitIcon: true,
|
||||
showShortLabel: true,
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
local version = "v0.2.1-alpha"
|
||||
local version = "v0.3.0-alpha"
|
||||
|
||||
local debug = true
|
||||
|
||||
@ -98,13 +98,14 @@ function Olympus.buildEnrouteTask(options)
|
||||
end
|
||||
|
||||
-- Builds a valid task depending on the provided options
|
||||
function Olympus.buildTask(options)
|
||||
function Olympus.buildTask(groupName, options)
|
||||
local task = nil
|
||||
|
||||
local group = Group.getByName(groupName)
|
||||
if (Olympus.isArray(options)) then
|
||||
local tasks = {}
|
||||
for idx, subOptions in pairs(options) do
|
||||
tasks[idx] = Olympus.buildTask(subOptions) or Olympus.buildEnrouteTask(subOptions)
|
||||
tasks[idx] = Olympus.buildTask(groupName, subOptions) or Olympus.buildEnrouteTask(subOptions)
|
||||
end
|
||||
task = {
|
||||
id = 'ComboTask',
|
||||
@ -139,29 +140,92 @@ function Olympus.buildTask(options)
|
||||
pattern = options['pattern'] or "Circle"
|
||||
}
|
||||
}
|
||||
if options['altitude'] then
|
||||
if options ['altitudeType'] then
|
||||
if options ['altitudeType'] == "AGL" then
|
||||
local groundHeight = 0
|
||||
if group then
|
||||
local groupPos = mist.getLeadPos(group)
|
||||
groundHeight = land.getHeight({x = groupPos.x, y = groupPos.z})
|
||||
end
|
||||
task['params']['altitude'] = groundHeight + options['altitude']
|
||||
else
|
||||
task['params']['altitude'] = options['altitude']
|
||||
end
|
||||
else
|
||||
task['params']['altitude'] = options['altitude']
|
||||
end
|
||||
end
|
||||
if options['speed'] then
|
||||
task['params']['speed'] = options['speed']
|
||||
end
|
||||
elseif options['id'] == 'Bombing' and options['lat'] and options['lng'] then
|
||||
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
||||
task = {
|
||||
id = 'Bombing',
|
||||
params = {
|
||||
point = {x = point.x, y = point.z},
|
||||
attackQty = 1
|
||||
}
|
||||
}
|
||||
elseif options['id'] == 'CarpetBombing' and options['lat'] and options['lng'] then
|
||||
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
||||
task = {
|
||||
id = 'CarpetBombing',
|
||||
params = {
|
||||
x = point.x,
|
||||
y = point.z,
|
||||
carpetLength = 1000,
|
||||
attackType = 'Carpet',
|
||||
expend = "All",
|
||||
attackQty = 1,
|
||||
attackQtyLimit = true
|
||||
}
|
||||
}
|
||||
elseif options['id'] == 'AttackMapObject' and options['lat'] and options['lng'] then
|
||||
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
||||
task = {
|
||||
id = 'AttackMapObject',
|
||||
params = {
|
||||
point = {x = point.x, y = point.z}
|
||||
}
|
||||
}
|
||||
elseif options['id'] == 'FireAtPoint' and options['lat'] and options['lng'] and options['radius'] then
|
||||
local point = coord.LLtoLO(options['lat'], options['lng'], 0)
|
||||
task = {
|
||||
id = 'FireAtPoint',
|
||||
params = {
|
||||
point = {x = point.x, y = point.z},
|
||||
radius = options['radius']
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
return task
|
||||
end
|
||||
|
||||
-- Move a unit. Since many tasks in DCS are Enroute tasks, this function is an important way to control the unit AI
|
||||
function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions)
|
||||
Olympus.debug("Olympus.move " .. ID .. " (" .. lat .. ", " .. lng ..") " .. altitude .. "m " .. speed .. "m/s " .. category, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedType, category, taskOptions)
|
||||
Olympus.debug("Olympus.move " .. groupName .. " (" .. lat .. ", " .. lng ..") " .. altitude .. "m " .. altitudeType .. " ".. speed .. "m/s " .. category .. " " .. Olympus.serializeTable(taskOptions), 2)
|
||||
local group = Group.getByName(groupName)
|
||||
if group then
|
||||
if category == "Aircraft" then
|
||||
local startPoint = mist.getLeadPos(unit:getGroup())
|
||||
local startPoint = mist.getLeadPos(group)
|
||||
local endPoint = coord.LLtoLO(lat, lng, 0)
|
||||
|
||||
if altitudeType == "AGL" then
|
||||
altitude = land.getHeight({x = endPoint.x, y = endPoint.z}) + altitude
|
||||
end
|
||||
|
||||
local path = {}
|
||||
if taskOptions and taskOptions['id'] == 'Land' then
|
||||
path = {
|
||||
[1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'),
|
||||
[1] = mist.fixedWing.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
|
||||
[2] = mist.fixedWing.buildWP(endPoint, landing, speed, 0, 'AGL')
|
||||
}
|
||||
else
|
||||
path = {
|
||||
[1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'),
|
||||
[1] = mist.fixedWing.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
|
||||
[2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
||||
}
|
||||
end
|
||||
@ -184,7 +248,6 @@ function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions)
|
||||
},
|
||||
},
|
||||
}
|
||||
group = unit:getGroup()
|
||||
local groupCon = group:getController()
|
||||
if groupCon then
|
||||
groupCon:setTask(missionTask)
|
||||
@ -193,20 +256,26 @@ function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions)
|
||||
elseif category == "GroundUnit" then
|
||||
vars =
|
||||
{
|
||||
group = unit:getGroup(),
|
||||
group = group,
|
||||
point = coord.LLtoLO(lat, lng, 0),
|
||||
form = "Off Road",
|
||||
heading = 0,
|
||||
speed = speed,
|
||||
disableRoads = true
|
||||
speed = speed
|
||||
}
|
||||
|
||||
if taskOptions and taskOptions['id'] == 'FollowRoads' and taskOptions['value'] == true then
|
||||
vars["disableRoads"] = false
|
||||
else
|
||||
vars["form"] = "Off Road"
|
||||
vars["disableRoads"] = true
|
||||
end
|
||||
|
||||
mist.groupToRandomPoint(vars)
|
||||
Olympus.debug("Olympus.move executed succesfully on a ground unit", 2)
|
||||
else
|
||||
Olympus.debug("Olympus.move not implemented yet for " .. category, 2)
|
||||
end
|
||||
else
|
||||
Olympus.debug("Error in Olympus.move " .. ID, 2)
|
||||
Olympus.debug("Error in Olympus.move " .. groupName, 2)
|
||||
end
|
||||
end
|
||||
|
||||
@ -228,6 +297,12 @@ function Olympus.smoke(color, lat, lng)
|
||||
trigger.action.smoke(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), colorEnum)
|
||||
end
|
||||
|
||||
-- Creates an explosion on the ground
|
||||
function Olympus.explosion(intensity, lat, lng)
|
||||
Olympus.debug("Olympus.explosion " .. intensity .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
trigger.action.explosion(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), intensity)
|
||||
end
|
||||
|
||||
-- Spawns a single ground unit
|
||||
function Olympus.spawnGroundUnit(coalition, unitType, lat, lng)
|
||||
Olympus.debug("Olympus.spawnGroundUnit " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
@ -279,12 +354,12 @@ end
|
||||
-- payloadName: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
|
||||
-- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase
|
||||
-- payload: a table, if present the unit will receive this specific payload. Overrides payloadName
|
||||
function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions)
|
||||
function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions)
|
||||
local payloadName = spawnOptions["payloadName"]
|
||||
local airbaseName = spawnOptions["airbaseName"]
|
||||
local payload = spawnOptions["payload"]
|
||||
|
||||
Olympus.debug("Olympus.spawnAircraft " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
Olympus.debug("Olympus.spawnAircraft " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..", " .. alt .. ")", 2)
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
||||
|
||||
if payload == nil then
|
||||
@ -304,7 +379,7 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions)
|
||||
["type"] = unitType,
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
["alt"] = 20000 * 0.3048,
|
||||
["alt"] = alt,
|
||||
["alt_type"] = "BARO",
|
||||
["skill"] = "Excellent",
|
||||
["payload"] =
|
||||
@ -359,8 +434,61 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions)
|
||||
},
|
||||
}
|
||||
end
|
||||
else
|
||||
route = {
|
||||
["points"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["alt"] = alt,
|
||||
["alt_type"] = "BARO",
|
||||
["task"] =
|
||||
{
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["number"] = 1,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "EPLRS",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[2] =
|
||||
{
|
||||
["number"] = 2,
|
||||
["auto"] = false,
|
||||
["id"] = "Orbit",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["pattern"] = "Circle"
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
["type"] = "Turning Point",
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
}, -- end of [1]
|
||||
}, -- end of ["points"]
|
||||
} -- end of ["route"]
|
||||
end
|
||||
|
||||
|
||||
local vars =
|
||||
{
|
||||
units = unitTable,
|
||||
@ -390,7 +518,7 @@ function Olympus.clone(ID, lat, lng, category)
|
||||
local spawnOptions = {
|
||||
payload = Olympus.payloadRegistry[unit:getName()]
|
||||
}
|
||||
Olympus.spawnAircraft(coalition, unit:getTypeName(), lat, lng, spawnOptions)
|
||||
Olympus.spawnAircraft(coalition, unit:getTypeName(), lat, lng, unit:getPoint().y, spawnOptions)
|
||||
elseif category == "GroundUnit" then
|
||||
Olympus.spawnGroundUnit(coalition, unit:getTypeName(), lat, lng)
|
||||
end
|
||||
@ -398,11 +526,11 @@ function Olympus.clone(ID, lat, lng, category)
|
||||
Olympus.debug("Olympus.clone completed successfully", 2)
|
||||
end
|
||||
|
||||
function Olympus.delete(ID, lat, lng)
|
||||
Olympus.debug("Olympus.delete " .. ID, 2)
|
||||
function Olympus.delete(ID, explosion)
|
||||
Olympus.debug("Olympus.delete " .. ID .. " " .. tostring(explosion), 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
if unit:getPlayerName() then
|
||||
if unit:getPlayerName() or explosion then
|
||||
trigger.action.explosion(unit:getPoint() , 250 ) --consider replacing with forcibly deslotting the player, however this will work for now
|
||||
Olympus.debug("Olympus.delete completed successfully", 2)
|
||||
else
|
||||
@ -412,46 +540,55 @@ function Olympus.delete(ID, lat, lng)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.setTask(ID, taskOptions)
|
||||
Olympus.debug("Olympus.setTask " .. ID .. " " .. Olympus.serializeTable(taskOptions), 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
local task = Olympus.buildTask(taskOptions);
|
||||
function Olympus.setTask(groupName, taskOptions)
|
||||
Olympus.debug("Olympus.setTask " .. groupName .. " " .. Olympus.serializeTable(taskOptions), 2)
|
||||
local group = Group.getByName(groupName)
|
||||
if group then
|
||||
local task = Olympus.buildTask(groupName, taskOptions);
|
||||
Olympus.debug("Olympus.setTask " .. Olympus.serializeTable(task), 20)
|
||||
if task then
|
||||
unit:getGroup():getController():setTask(task)
|
||||
group:getController():setTask(task)
|
||||
Olympus.debug("Olympus.setTask completed successfully", 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.resetTask(ID)
|
||||
Olympus.debug("Olympus.resetTask " .. ID, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
unit:getGroup():getController():resetTask()
|
||||
function Olympus.resetTask(groupName)
|
||||
Olympus.debug("Olympus.resetTask " .. groupName, 2)
|
||||
local group = Group.getByName(groupName)
|
||||
if group then
|
||||
group:getController():resetTask()
|
||||
Olympus.debug("Olympus.resetTask completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.setCommand(ID, command)
|
||||
Olympus.debug("Olympus.setCommand " .. ID .. " " .. Olympus.serializeTable(command), 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
unit:getGroup():getController():setCommand(command)
|
||||
function Olympus.setCommand(groupName, command)
|
||||
Olympus.debug("Olympus.setCommand " .. groupName .. " " .. Olympus.serializeTable(command), 2)
|
||||
local group = Group.getByName(groupName)
|
||||
if group then
|
||||
group:getController():setCommand(command)
|
||||
Olympus.debug("Olympus.setCommand completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.setOption(ID, optionID, optionValue)
|
||||
Olympus.debug("Olympus.setOption " .. ID .. " " .. optionID .. " " .. tostring(optionValue), 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
unit:getGroup():getController():setOption(optionID, optionValue)
|
||||
function Olympus.setOption(groupName, optionID, optionValue)
|
||||
Olympus.debug("Olympus.setOption " .. groupName .. " " .. optionID .. " " .. tostring(optionValue), 2)
|
||||
local group = Group.getByName(groupName)
|
||||
if group then
|
||||
group:getController():setOption(optionID, optionValue)
|
||||
Olympus.debug("Olympus.setOption completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.setOnOff(groupName, onOff)
|
||||
Olympus.debug("Olympus.setOnOff " .. groupName .. " " .. tostring(onOff), 2)
|
||||
local group = Group.getByName(groupName)
|
||||
if group then
|
||||
group:getController():setOnOff(onOff)
|
||||
Olympus.debug("Olympus.setOnOff completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.serializeTable(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
@ -547,7 +684,7 @@ function Olympus.setMissionData(arg, time)
|
||||
for index, unit in pairs(group:getUnits()) do
|
||||
local unitController = unit:getController()
|
||||
local table = {}
|
||||
table["targets"] = {}
|
||||
table["contacts"] = {}
|
||||
|
||||
for i, target in ipairs(controllerTargets) do
|
||||
for det, enum in pairs(Controller.Detection) do
|
||||
@ -556,7 +693,7 @@ function Olympus.setMissionData(arg, time)
|
||||
|
||||
if detected then
|
||||
target["detectionMethod"] = det
|
||||
table["targets"][#table["targets"] + 1] = target
|
||||
table["contacts"][#table["contacts"] + 1] = target
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -6,6 +6,8 @@
|
||||
#include "measure.h"
|
||||
#include "logger.h"
|
||||
|
||||
#define TASK_CHECK_INIT_VALUE 10
|
||||
|
||||
namespace State
|
||||
{
|
||||
enum States
|
||||
@ -18,7 +20,11 @@ namespace State
|
||||
LAND,
|
||||
REFUEL,
|
||||
AWACS,
|
||||
TANKER
|
||||
TANKER,
|
||||
BOMB_POINT,
|
||||
CARPET_BOMB,
|
||||
BOMB_BUILDING,
|
||||
FIRE_AT_AREA
|
||||
};
|
||||
};
|
||||
|
||||
@ -56,21 +62,24 @@ public:
|
||||
|
||||
/********** Public methods **********/
|
||||
void initialize(json::value json);
|
||||
void setDefaults(bool force = false);
|
||||
int getID() { return ID; }
|
||||
void runAILoop();
|
||||
void updateExportData(json::value json);
|
||||
void updateMissionData(json::value json);
|
||||
json::value getData(long long time);
|
||||
json::value getData(long long time, bool getAll = false);
|
||||
virtual wstring getCategory() { return L"No category"; };
|
||||
|
||||
/********** Base data **********/
|
||||
void setAI(bool newAI) { AI = newAI; addMeasure(L"AI", json::value(newAI)); }
|
||||
void setControlled(bool newControlled) { controlled = newControlled; addMeasure(L"controlled", json::value(newControlled)); }
|
||||
void setName(wstring newName) { name = newName; addMeasure(L"name", json::value(newName));}
|
||||
void setUnitName(wstring newUnitName) { unitName = newUnitName; addMeasure(L"unitName", json::value(newUnitName));}
|
||||
void setGroupName(wstring newGroupName) { groupName = newGroupName; addMeasure(L"groupName", json::value(newGroupName));}
|
||||
void setAlive(bool newAlive) { alive = newAlive; addMeasure(L"alive", json::value(newAlive));}
|
||||
void setType(json::value newType) { type = newType; addMeasure(L"type", newType);}
|
||||
void setCountry(int newCountry) { country = newCountry; addMeasure(L"country", json::value(newCountry));}
|
||||
bool getAI() { return AI; }
|
||||
|
||||
bool getControlled() { return controlled; }
|
||||
wstring getName() { return name; }
|
||||
wstring getUnitName() { return unitName; }
|
||||
wstring getGroupName() { return groupName; }
|
||||
@ -84,6 +93,7 @@ public:
|
||||
void setAltitude(double newAltitude) {altitude = newAltitude; addMeasure(L"altitude", json::value(newAltitude));}
|
||||
void setHeading(double newHeading) {heading = newHeading; addMeasure(L"heading", json::value(newHeading));}
|
||||
void setSpeed(double newSpeed) {speed = newSpeed; addMeasure(L"speed", json::value(newSpeed));}
|
||||
|
||||
double getLatitude() { return latitude; }
|
||||
double getLongitude() { return longitude; }
|
||||
double getAltitude() { return altitude; }
|
||||
@ -93,13 +103,14 @@ public:
|
||||
/********** Mission data **********/
|
||||
void setFuel(double newFuel) { fuel = newFuel; addMeasure(L"fuel", json::value(newFuel));}
|
||||
void setAmmo(json::value newAmmo) { ammo = newAmmo; addMeasure(L"ammo", json::value(newAmmo));}
|
||||
void setTargets(json::value newTargets) {targets = newTargets; addMeasure(L"targets", json::value(newTargets));}
|
||||
void setHasTask(bool newHasTask) { hasTask = newHasTask; addMeasure(L"hasTask", json::value(newHasTask)); }
|
||||
void setContacts(json::value newContacts) {contacts = newContacts; addMeasure(L"contacts", json::value(newContacts));}
|
||||
void setHasTask(bool newHasTask);
|
||||
void setCoalitionID(int newCoalitionID);
|
||||
void setFlags(json::value newFlags) { flags = newFlags; addMeasure(L"flags", json::value(newFlags));}
|
||||
|
||||
double getFuel() { return fuel; }
|
||||
json::value getAmmo() { return ammo; }
|
||||
json::value getTargets() { return targets; }
|
||||
json::value getTargets() { return contacts; }
|
||||
bool getHasTask() { return hasTask; }
|
||||
wstring getCoalition() { return coalition; }
|
||||
int getCoalitionID();
|
||||
@ -108,40 +119,48 @@ public:
|
||||
/********** Formation data **********/
|
||||
void setLeaderID(int newLeaderID) { leaderID = newLeaderID; addMeasure(L"leaderID", json::value(newLeaderID)); }
|
||||
void setFormationOffset(Offset formationOffset);
|
||||
|
||||
int getLeaderID() { return leaderID; }
|
||||
Offset getFormationoffset() { return formationOffset; }
|
||||
|
||||
/********** Task data **********/
|
||||
void setCurrentTask(wstring newCurrentTask) { currentTask = newCurrentTask;addMeasure(L"currentTask", json::value(newCurrentTask)); }
|
||||
virtual void setTargetSpeed(double newTargetSpeed) { targetSpeed = newTargetSpeed; addMeasure(L"targetSpeed", json::value(newTargetSpeed));}
|
||||
virtual void setTargetAltitude(double newTargetAltitude) { targetAltitude = newTargetAltitude; addMeasure(L"targetAltitude", json::value(newTargetAltitude));} //TODO fix, double definition
|
||||
void setCurrentTask(wstring newCurrentTask) { currentTask = newCurrentTask; addMeasure(L"currentTask", json::value(newCurrentTask)); }
|
||||
void setDesiredSpeed(double newDesiredSpeed);
|
||||
void setDesiredAltitude(double newDesiredAltitude);
|
||||
void setDesiredSpeedType(wstring newDesiredSpeedType);
|
||||
void setDesiredAltitudeType(wstring newDesiredAltitudeType);
|
||||
void setActiveDestination(Coords newActiveDestination) { activeDestination = newActiveDestination; addMeasure(L"activeDestination", json::value("")); } // TODO fix
|
||||
void setActivePath(list<Coords> newActivePath);
|
||||
void clearActivePath();
|
||||
void pushActivePathFront(Coords newActivePathFront);
|
||||
void pushActivePathBack(Coords newActivePathBack);
|
||||
void popActivePathFront();
|
||||
void setTargetID(int newTargetID) { targetID = newTargetID; addMeasure(L"targetID", json::value(newTargetID));}
|
||||
void setTargetLocation(Coords newTargetLocation);
|
||||
void setIsTanker(bool newIsTanker);
|
||||
void setIsAWACS(bool newIsAWACS);
|
||||
virtual void setOnOff(bool newOnOff) { onOff = newOnOff; addMeasure(L"onOff", json::value(newOnOff));};
|
||||
virtual void setFollowRoads(bool newFollowRoads) { followRoads = newFollowRoads; addMeasure(L"followRoads", json::value(newFollowRoads)); };
|
||||
|
||||
wstring getCurrentTask() { return currentTask; }
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
virtual double getDesiredSpeed() { return desiredSpeed; };
|
||||
virtual double getDesiredAltitude() { return desiredAltitude; };
|
||||
virtual wstring getDesiredSpeedType() { return desiredSpeedType; };
|
||||
virtual wstring getDesiredAltitudeType() { return desiredAltitudeType; };
|
||||
Coords getActiveDestination() { return activeDestination; }
|
||||
list<Coords> getActivePath() { return activePath; }
|
||||
int getTargetID() { return targetID; }
|
||||
Coords getTargetLocation() { return targetLocation; }
|
||||
bool getIsTanker() { return isTanker; }
|
||||
bool getIsAWACS() { return isAWACS; }
|
||||
bool getOnOff() { return onOff; };
|
||||
bool getFollowRoads() { return followRoads; };
|
||||
|
||||
/********** 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);
|
||||
void setROE(wstring newROE, bool force = false);
|
||||
void setReactionToThreat(wstring newReactionToThreat, bool force = false);
|
||||
void setEmissionsCountermeasures(wstring newEmissionsCountermeasures, bool force = false);
|
||||
void setTACAN(Options::TACAN newTACAN, bool force = false);
|
||||
void setRadio(Options::Radio newradio, bool force = false);
|
||||
void setGeneralSettings(Options::GeneralSettings newGeneralSettings, bool force = false);
|
||||
void setEPLRS(bool newEPLRS, bool force = false);
|
||||
|
||||
wstring getROE() { return ROE; }
|
||||
wstring getReactionToThreat() { return reactionToThreat; }
|
||||
wstring getEmissionsCountermeasures() { return emissionsCountermeasures; };
|
||||
@ -152,19 +171,24 @@ public:
|
||||
|
||||
/********** Control functions **********/
|
||||
void landAt(Coords loc);
|
||||
virtual void changeSpeed(wstring change){};
|
||||
virtual void changeAltitude(wstring change){};
|
||||
virtual void changeSpeed(wstring change) {};
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
void resetActiveDestination();
|
||||
virtual void setState(int newState) { state = newState; };
|
||||
void resetTask();
|
||||
void clearActivePath();
|
||||
void pushActivePathFront(Coords newActivePathFront);
|
||||
void pushActivePathBack(Coords newActivePathBack);
|
||||
void popActivePathFront();
|
||||
|
||||
protected:
|
||||
int ID;
|
||||
|
||||
map<wstring, Measure*> measures;
|
||||
int taskCheckCounter = 0;
|
||||
|
||||
/********** Base data **********/
|
||||
bool AI = false;
|
||||
bool controlled = false;
|
||||
wstring name = L"undefined";
|
||||
wstring unitName = L"undefined";
|
||||
wstring groupName = L"undefined";
|
||||
@ -183,7 +207,7 @@ protected:
|
||||
double fuel = 0;
|
||||
double initialFuel = 0; // Used internally to detect refueling completed
|
||||
json::value ammo = json::value::null();
|
||||
json::value targets = json::value::null();
|
||||
json::value contacts = json::value::null();
|
||||
bool hasTask = false;
|
||||
wstring coalition = L"";
|
||||
json::value flags = json::value::null();
|
||||
@ -194,13 +218,18 @@ protected:
|
||||
|
||||
/********** Task data **********/
|
||||
wstring currentTask = L"";
|
||||
double targetSpeed = 0;
|
||||
double targetAltitude = 0;
|
||||
double desiredSpeed = 0;
|
||||
double desiredAltitude = 0;
|
||||
wstring desiredSpeedType = L"GS";
|
||||
wstring desiredAltitudeType = L"AGL";
|
||||
list<Coords> activePath;
|
||||
Coords activeDestination = Coords(0);
|
||||
Coords activeDestination = Coords(NULL);
|
||||
int targetID = NULL;
|
||||
Coords targetLocation = Coords(NULL);
|
||||
bool isTanker = false;
|
||||
bool isAWACS = false;
|
||||
bool onOff = true;
|
||||
bool followRoads = false;
|
||||
|
||||
/********** Options data **********/
|
||||
wstring ROE = L"Designated";
|
||||
@ -224,4 +253,10 @@ protected:
|
||||
bool isLeaderAlive();
|
||||
virtual void AIloop() = 0;
|
||||
void addMeasure(wstring key, json::value value);
|
||||
bool isDestinationReached(double threshold);
|
||||
bool setActiveDestination();
|
||||
bool updateActivePath(bool looping);
|
||||
void goToDestination(wstring enrouteTask = L"nil");
|
||||
bool checkTaskFailed();
|
||||
void resetTaskFailedCounter();
|
||||
};
|
||||
|
||||
@ -11,10 +11,17 @@ public:
|
||||
~UnitsManager();
|
||||
|
||||
Unit* getUnit(int ID);
|
||||
bool isUnitInGroup(Unit* unit);
|
||||
bool isUnitGroupLeader(Unit* unit);
|
||||
Unit* getGroupLeader(int ID);
|
||||
Unit* getGroupLeader(Unit* unit);
|
||||
vector<Unit*> getGroupMembers(wstring groupName);
|
||||
void updateExportData(lua_State* L);
|
||||
void updateMissionData(json::value missionData);
|
||||
void runAILoop();
|
||||
void getData(json::value& answer, long long time);
|
||||
void deleteUnit(int ID);
|
||||
void deleteUnit(int ID, bool explosion);
|
||||
void acquireControl(int ID);
|
||||
|
||||
private:
|
||||
map<int, Unit*> units;
|
||||
|
||||
@ -17,59 +17,52 @@ Aircraft::Aircraft(json::value json, int ID) : AirUnit(json, ID)
|
||||
{
|
||||
log("New Aircraft created with ID: " + to_string(ID));
|
||||
addMeasure(L"category", json::value(getCategory()));
|
||||
setTargetSpeed(targetSpeed);
|
||||
setTargetAltitude(targetAltitude);
|
||||
|
||||
double desiredSpeed = knotsToMs(300);
|
||||
double desiredAltitude = ftToM(20000);
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
setDesiredAltitude(desiredAltitude);
|
||||
};
|
||||
|
||||
void Aircraft::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
setState(State::IDLE);
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
setTargetSpeed(getTargetSpeed() - 25 / 1.94384);
|
||||
setDesiredSpeed(getDesiredSpeed() - knotsToMs(25));
|
||||
else if (change.compare(L"fast") == 0)
|
||||
setTargetSpeed(getTargetSpeed() + 25 / 1.94384);
|
||||
setDesiredSpeed(getDesiredSpeed() + knotsToMs(25));
|
||||
|
||||
if (getTargetSpeed() < 50 / 1.94384)
|
||||
setTargetSpeed(50 / 1.94384);
|
||||
if (getDesiredSpeed() < knotsToMs(50))
|
||||
setDesiredSpeed(knotsToMs(50));
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Aircraft::changeAltitude(wstring change)
|
||||
{
|
||||
if (change.compare(L"descend") == 0)
|
||||
{
|
||||
if (getTargetAltitude() > 5000)
|
||||
setTargetAltitude(getTargetAltitude() - 2500 / 3.28084);
|
||||
else if (getTargetAltitude() > 0)
|
||||
setTargetAltitude(getTargetAltitude() - 500 / 3.28084);
|
||||
if (getDesiredAltitude() > 5000)
|
||||
setDesiredAltitude(getDesiredAltitude() - ftToM(2500));
|
||||
else if (getDesiredAltitude() > 0)
|
||||
setDesiredAltitude(getDesiredAltitude() - ftToM(500));
|
||||
}
|
||||
else if (change.compare(L"climb") == 0)
|
||||
{
|
||||
if (getTargetAltitude() > 5000)
|
||||
setTargetAltitude(getTargetAltitude() + 2500 / 3.28084);
|
||||
else if (getTargetAltitude() >= 0)
|
||||
setTargetAltitude(getTargetAltitude() + 500 / 3.28084);
|
||||
if (getDesiredAltitude() > 5000)
|
||||
setDesiredAltitude(getDesiredAltitude() + ftToM(2500));
|
||||
else if (getDesiredAltitude() >= 0)
|
||||
setDesiredAltitude(getDesiredAltitude() + ftToM(500));
|
||||
}
|
||||
if (getTargetAltitude() < 0)
|
||||
setTargetAltitude(0);
|
||||
if (getDesiredAltitude() < 0)
|
||||
setDesiredAltitude(0);
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Aircraft::setTargetSpeed(double newTargetSpeed) {
|
||||
targetSpeed = newTargetSpeed;
|
||||
addMeasure(L"targetSpeed", json::value(targetSpeed));
|
||||
if (activeDestination != NULL)
|
||||
goToDestination();
|
||||
}
|
||||
|
||||
void Aircraft::setTargetAltitude(double newTargetAltitude) {
|
||||
targetAltitude = newTargetAltitude;
|
||||
addMeasure(L"targetAltitude", json::value(targetAltitude));
|
||||
if (activeDestination != NULL)
|
||||
goToDestination();
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
@ -20,7 +20,7 @@ AirUnit::AirUnit(json::value json, int ID) : Unit(json, ID)
|
||||
|
||||
void AirUnit::setState(int newState)
|
||||
{
|
||||
/************ Perform any action required when LEAVING a certain state ************/
|
||||
/************ Perform any action required when LEAVING a state ************/
|
||||
if (newState != state) {
|
||||
switch (state) {
|
||||
case State::IDLE: {
|
||||
@ -43,12 +43,18 @@ void AirUnit::setState(int newState)
|
||||
case State::REFUEL: {
|
||||
break;
|
||||
}
|
||||
case State::BOMB_POINT:
|
||||
case State::CARPET_BOMB:
|
||||
case State::BOMB_BUILDING: {
|
||||
setTargetLocation(Coords(NULL));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/************ Perform any action required when ENTERING a certain state ************/
|
||||
/************ Perform any action required when ENTERING a state ************/
|
||||
switch (newState) {
|
||||
case State::IDLE: {
|
||||
clearActivePath();
|
||||
@ -90,6 +96,24 @@ void AirUnit::setState(int newState)
|
||||
addMeasure(L"currentState", json::value(L"Refuel"));
|
||||
break;
|
||||
}
|
||||
case State::BOMB_POINT: {
|
||||
addMeasure(L"currentState", json::value(L"Bombing point"));
|
||||
clearActivePath();
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
case State::CARPET_BOMB: {
|
||||
addMeasure(L"currentState", json::value(L"Carpet bombing"));
|
||||
clearActivePath();
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
case State::BOMB_BUILDING: {
|
||||
addMeasure(L"currentState", json::value(L"Bombing building"));
|
||||
clearActivePath();
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -100,69 +124,6 @@ void AirUnit::setState(int newState)
|
||||
state = newState;
|
||||
}
|
||||
|
||||
bool AirUnit::isDestinationReached()
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
double dist = 0;
|
||||
Geodesic::WGS84().Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, dist);
|
||||
if (dist < AIR_DEST_DIST_THR)
|
||||
{
|
||||
log(unitName + L" destination reached");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AirUnit::setActiveDestination()
|
||||
{
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
activeDestination = activePath.front();
|
||||
log(unitName + L" active destination set to queue front");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeDestination = Coords(0);
|
||||
log(unitName + L" active destination set to NULL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AirUnit::updateActivePath(bool looping)
|
||||
{
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
/* Push the next destination in the queue to the front */
|
||||
if (looping)
|
||||
pushActivePathBack(activePath.front());
|
||||
popActivePathFront();
|
||||
log(unitName + L" active path front popped");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AirUnit::goToDestination(wstring enrouteTask)
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new Move(ID, activeDestination, getTargetSpeed(), getTargetAltitude(), enrouteTask));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
else
|
||||
log(unitName + L" error, no active destination!");
|
||||
}
|
||||
|
||||
void AirUnit::AIloop()
|
||||
{
|
||||
/* State machine */
|
||||
@ -170,19 +131,19 @@ void AirUnit::AIloop()
|
||||
case State::IDLE: {
|
||||
currentTask = L"Idle";
|
||||
|
||||
if (!hasTask)
|
||||
if (!getHasTask())
|
||||
{
|
||||
std::wostringstream taskSS;
|
||||
if (isTanker) {
|
||||
taskSS << "{ [1] = { id = 'Tanker' }, [2] = { id = 'Orbit', pattern = 'Race-Track' } }";
|
||||
taskSS << "{ [1] = { id = 'Tanker' }, [2] = { id = 'Orbit', pattern = 'Race-Track', altitude = " << desiredAltitude << ", speed = " << desiredSpeed << ", altitudeType = '" << desiredAltitudeType << "' } }";
|
||||
}
|
||||
else if (isAWACS) {
|
||||
taskSS << "{ [1] = { id = 'AWACS' }, [2] = { id = 'Orbit', pattern = 'Circle' } }";
|
||||
taskSS << "{ [1] = { id = 'AWACS' }, [2] = { id = 'Orbit', pattern = 'Circle', altitude = " << desiredAltitude << ", speed = " << desiredSpeed << ", altitudeType = '" << desiredAltitudeType << "' } }";
|
||||
}
|
||||
else {
|
||||
taskSS << "{ id = 'Orbit', pattern = 'Circle' }";
|
||||
taskSS << "{ id = 'Orbit', pattern = 'Circle', altitude = " << desiredAltitude << ", speed = " << desiredSpeed << ", altitudeType = '" << desiredAltitudeType << "' }";
|
||||
}
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(ID, taskSS.str()));
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
@ -208,7 +169,7 @@ void AirUnit::AIloop()
|
||||
currentTask = L"Reaching destination";
|
||||
}
|
||||
|
||||
if (activeDestination == NULL || !hasTask)
|
||||
if (activeDestination == NULL || !getHasTask())
|
||||
{
|
||||
if (!setActiveDestination())
|
||||
setState(State::IDLE);
|
||||
@ -216,7 +177,7 @@ void AirUnit::AIloop()
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
else {
|
||||
if (isDestinationReached()) {
|
||||
if (isDestinationReached(AIR_DEST_DIST_THR)) {
|
||||
if (updateActivePath(looping) && setActiveDestination())
|
||||
goToDestination(enrouteTask);
|
||||
else
|
||||
@ -253,7 +214,7 @@ void AirUnit::AIloop()
|
||||
wstring enrouteTask = enrouteTaskSS.str();
|
||||
currentTask = L"Attacking " + getTargetName();
|
||||
|
||||
if (!hasTask)
|
||||
if (!getHasTask())
|
||||
{
|
||||
setActiveDestination();
|
||||
goToDestination(enrouteTask);
|
||||
@ -274,7 +235,7 @@ void AirUnit::AIloop()
|
||||
currentTask = L"Following " + getTargetName();
|
||||
|
||||
Unit* leader = unitsManager->getUnit(leaderID);
|
||||
if (!hasTask) {
|
||||
if (!getHasTask()) {
|
||||
if (leader != nullptr && leader->getAlive() && formationOffset != NULL)
|
||||
{
|
||||
std::wostringstream taskSS;
|
||||
@ -287,7 +248,7 @@ void AirUnit::AIloop()
|
||||
<< "z = " << formationOffset.z
|
||||
<< "},"
|
||||
<< "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(ID, taskSS.str()));
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
@ -297,13 +258,13 @@ void AirUnit::AIloop()
|
||||
case State::REFUEL: {
|
||||
currentTask = L"Refueling";
|
||||
|
||||
if (!hasTask) {
|
||||
if (!getHasTask()) {
|
||||
if (fuel <= initialFuel) {
|
||||
std::wostringstream taskSS;
|
||||
taskSS << "{"
|
||||
<< "id = 'Refuel'"
|
||||
<< "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(ID, taskSS.str()));
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
@ -312,9 +273,44 @@ void AirUnit::AIloop()
|
||||
}
|
||||
}
|
||||
}
|
||||
case State::BOMB_POINT: {
|
||||
currentTask = L"Bombing point";
|
||||
|
||||
if (!getHasTask()) {
|
||||
std::wostringstream taskSS;
|
||||
taskSS << "{id = 'Bombing', lat = " << targetLocation.lat << ", lng = " << targetLocation.lng << "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
}
|
||||
case State::CARPET_BOMB: {
|
||||
currentTask = L"Carpet bombing";
|
||||
|
||||
if (!getHasTask()) {
|
||||
std::wostringstream taskSS;
|
||||
taskSS << "{id = 'CarpetBombing', lat = " << targetLocation.lat << ", lng = " << targetLocation.lng << "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::BOMB_BUILDING: {
|
||||
currentTask = L"Bombing building";
|
||||
|
||||
if (!getHasTask()) {
|
||||
std::wostringstream taskSS;
|
||||
taskSS << "{id = 'AttackMapObject', lat = " << targetLocation.lat << ", lng = " << targetLocation.lng << "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
addMeasure(L"currentTask", json::value(currentTask));
|
||||
}
|
||||
}
|
||||
@ -69,6 +69,7 @@ extern "C" DllExport int coreFrame(lua_State* L)
|
||||
if (unitsManager != nullptr)
|
||||
{
|
||||
unitsManager->updateExportData(L);
|
||||
unitsManager->runAILoop();
|
||||
}
|
||||
before = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
@ -17,58 +17,134 @@ GroundUnit::GroundUnit(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Ground Unit created with ID: " + to_string(ID));
|
||||
addMeasure(L"category", json::value(getCategory()));
|
||||
setTargetSpeed(targetSpeed);
|
||||
setTargetAltitude(targetAltitude);
|
||||
|
||||
double desiredSpeed = 10;
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
};
|
||||
|
||||
void GroundUnit::setState(int newState)
|
||||
{
|
||||
/************ Perform any action required when LEAVING a state ************/
|
||||
if (newState != state) {
|
||||
switch (state) {
|
||||
case State::IDLE: {
|
||||
break;
|
||||
}
|
||||
case State::REACH_DESTINATION: {
|
||||
break;
|
||||
}
|
||||
case State::FIRE_AT_AREA: {
|
||||
setTargetLocation(Coords(NULL));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/************ Perform any action required when ENTERING a state ************/
|
||||
switch (newState) {
|
||||
case State::IDLE: {
|
||||
clearActivePath();
|
||||
resetActiveDestination();
|
||||
addMeasure(L"currentState", json::value(L"Idle"));
|
||||
break;
|
||||
}
|
||||
case State::REACH_DESTINATION: {
|
||||
resetActiveDestination();
|
||||
addMeasure(L"currentState", json::value(L"Reach destination"));
|
||||
break;
|
||||
}
|
||||
case State::FIRE_AT_AREA: {
|
||||
addMeasure(L"currentState", json::value(L"Firing at area"));
|
||||
clearActivePath();
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
resetTask();
|
||||
|
||||
log(unitName + L" setting state from " + to_wstring(state) + L" to " + to_wstring(newState));
|
||||
state = newState;
|
||||
}
|
||||
|
||||
void GroundUnit::AIloop()
|
||||
{
|
||||
/* Set the active destination to be always equal to the first point of the active path. This is in common with all AI units */
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
if (activeDestination != activePath.front())
|
||||
switch (state) {
|
||||
case State::IDLE: {
|
||||
currentTask = L"Idle";
|
||||
if (getHasTask())
|
||||
resetTask();
|
||||
break;
|
||||
}
|
||||
case State::REACH_DESTINATION: {
|
||||
wstring enrouteTask = L"";
|
||||
bool looping = false;
|
||||
|
||||
std::wostringstream taskSS;
|
||||
taskSS << "{ id = 'FollowRoads', value = " << (getFollowRoads() ? "true" : "false") << " }";
|
||||
enrouteTask = taskSS.str();
|
||||
|
||||
if (activeDestination == NULL || !getHasTask())
|
||||
{
|
||||
activeDestination = activePath.front();
|
||||
Command* command = dynamic_cast<Command*>(new Move(ID, activeDestination, getTargetSpeed(), getTargetAltitude(), L"nil"));
|
||||
if (!setActiveDestination())
|
||||
setState(State::IDLE);
|
||||
else
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
else {
|
||||
if (isDestinationReached(GROUND_DEST_DIST_THR)) {
|
||||
if (updateActivePath(looping) && setActiveDestination())
|
||||
goToDestination(enrouteTask);
|
||||
else
|
||||
setState(State::IDLE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::FIRE_AT_AREA: {
|
||||
currentTask = L"Firing at area";
|
||||
|
||||
if (!getHasTask()) {
|
||||
std::wostringstream taskSS;
|
||||
taskSS << "{id = 'FireAtPoint', lat = " << targetLocation.lat << ", lng = " << targetLocation.lng << ", radius = 1000}";
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
log(unitName + L" no more points in active path");
|
||||
activeDestination = Coords(0); // Set the active path to NULL
|
||||
currentTask = L"Idle";
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Ground unit AI Loop */
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
double newDist = 0;
|
||||
Geodesic::WGS84().Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, newDist);
|
||||
if (newDist < GROUND_DEST_DIST_THR)
|
||||
{
|
||||
/* Destination reached */
|
||||
popActivePathFront();
|
||||
log(unitName + L" destination reached");
|
||||
}
|
||||
}
|
||||
addMeasure(L"currentTask", json::value(currentTask));
|
||||
}
|
||||
|
||||
void GroundUnit::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
setState(State::IDLE);
|
||||
else if (change.compare(L"slow") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
setDesiredSpeed(getDesiredSpeed() - knotsToMs(5));
|
||||
else if (change.compare(L"fast") == 0)
|
||||
{
|
||||
setDesiredSpeed(getDesiredSpeed() + knotsToMs(5));
|
||||
|
||||
}
|
||||
if (getDesiredSpeed() < 0)
|
||||
setDesiredSpeed(0);
|
||||
}
|
||||
|
||||
void GroundUnit::setOnOff(bool newOnOff)
|
||||
{
|
||||
Unit::setOnOff(newOnOff);
|
||||
Command* command = dynamic_cast<Command*>(new SetOnOff(groupName, onOff));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
|
||||
void GroundUnit::setFollowRoads(bool newFollowRoads)
|
||||
{
|
||||
Unit::setFollowRoads(newFollowRoads);
|
||||
resetActiveDestination(); /* Reset active destination to apply option*/
|
||||
}
|
||||
@ -17,8 +17,11 @@ Helicopter::Helicopter(json::value json, int ID) : AirUnit(json, ID)
|
||||
{
|
||||
log("New Helicopter created with ID: " + to_string(ID));
|
||||
addMeasure(L"category", json::value(getCategory()));
|
||||
setTargetSpeed(targetSpeed);
|
||||
setTargetAltitude(targetAltitude);
|
||||
|
||||
double desiredSpeed = knotsToMs(100);
|
||||
double desiredAltitude = ftToM(5000);
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
setDesiredAltitude(desiredAltitude);
|
||||
};
|
||||
|
||||
void Helicopter::changeSpeed(wstring change)
|
||||
@ -29,11 +32,11 @@ void Helicopter::changeSpeed(wstring change)
|
||||
clearActivePath();
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
targetSpeed -= 10 / 1.94384;
|
||||
desiredSpeed -= knotsToMs(10);
|
||||
else if (change.compare(L"fast") == 0)
|
||||
targetSpeed += 10 / 1.94384;
|
||||
if (targetSpeed < 0)
|
||||
targetSpeed = 0;
|
||||
desiredSpeed += knotsToMs(10);
|
||||
if (desiredSpeed < 0)
|
||||
desiredSpeed = 0;
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
@ -42,33 +45,20 @@ void Helicopter::changeAltitude(wstring change)
|
||||
{
|
||||
if (change.compare(L"descend") == 0)
|
||||
{
|
||||
if (targetAltitude > 100)
|
||||
targetAltitude -= 100 / 3.28084;
|
||||
else if (targetAltitude > 0)
|
||||
targetAltitude -= 10 / 3.28084;
|
||||
if (desiredAltitude > 100)
|
||||
desiredAltitude -= ftToM(100);
|
||||
else if (desiredAltitude > 0)
|
||||
desiredAltitude -= ftToM(10);
|
||||
}
|
||||
else if (change.compare(L"climb") == 0)
|
||||
{
|
||||
if (targetAltitude > 100)
|
||||
targetAltitude += 100 / 3.28084;
|
||||
else if (targetAltitude >= 0)
|
||||
targetAltitude += 10 / 3.28084;
|
||||
if (desiredAltitude > 100)
|
||||
desiredAltitude += ftToM(100);
|
||||
else if (desiredAltitude >= 0)
|
||||
desiredAltitude += ftToM(10);
|
||||
}
|
||||
if (targetAltitude < 0)
|
||||
targetAltitude = 0;
|
||||
if (desiredAltitude < 0)
|
||||
desiredAltitude = 0;
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
|
||||
void Helicopter::setTargetSpeed(double newTargetSpeed) {
|
||||
targetSpeed = newTargetSpeed;
|
||||
addMeasure(L"targetSpeed", json::value(targetSpeed));
|
||||
goToDestination();
|
||||
}
|
||||
|
||||
void Helicopter::setTargetAltitude(double newTargetAltitude) {
|
||||
targetAltitude = newTargetAltitude;
|
||||
addMeasure(L"targetAltitude", json::value(targetAltitude));
|
||||
goToDestination();
|
||||
}
|
||||
@ -17,8 +17,9 @@ NavyUnit::NavyUnit(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Navy Unit created with ID: " + to_string(ID));
|
||||
addMeasure(L"category", json::value(getCategory()));
|
||||
setTargetSpeed(targetSpeed);
|
||||
setTargetAltitude(targetAltitude);
|
||||
|
||||
double desiredSpeed = 10;
|
||||
setDesiredSpeed(desiredSpeed);
|
||||
};
|
||||
|
||||
void NavyUnit::AIloop()
|
||||
|
||||
@ -59,7 +59,8 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
if (key.compare(L"setPath") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
{
|
||||
wstring unitName = unit->getUnitName();
|
||||
@ -75,15 +76,9 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
newPath.push_back(dest);
|
||||
}
|
||||
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
{
|
||||
unit->setActivePath(newPath);
|
||||
unit->setState(State::REACH_DESTINATION);
|
||||
log(unitName + L" new path set successfully");
|
||||
}
|
||||
else
|
||||
log(unitName + L" not found, request will be discarded");
|
||||
unit->setActivePath(newPath);
|
||||
unit->setState(State::REACH_DESTINATION);
|
||||
log(unitName + L" new path set successfully");
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"smoke") == 0)
|
||||
@ -111,7 +106,8 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
wstring type = value[L"type"].as_string();
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
double altitude = value[L"altitude"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng; loc.alt = altitude;
|
||||
wstring payloadName = value[L"payloadName"].as_string();
|
||||
wstring airbaseName = value[L"airbaseName"].as_string();
|
||||
log(L"Spawning " + coalition + L" air unit of type " + type + L" with payload " + payloadName + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L" " + airbaseName + L")");
|
||||
@ -120,9 +116,10 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
else if (key.compare(L"attackUnit") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
int targetID = value[L"targetID"].as_integer();
|
||||
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
Unit* target = unitsManager->getUnit(targetID);
|
||||
|
||||
wstring unitName;
|
||||
@ -145,12 +142,13 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
else if (key.compare(L"followUnit") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
int leaderID = value[L"targetID"].as_integer();
|
||||
int offsetX = value[L"offsetX"].as_integer();
|
||||
int offsetY = value[L"offsetY"].as_integer();
|
||||
int offsetZ = value[L"offsetZ"].as_integer();
|
||||
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
Unit* leader = unitsManager->getUnit(leaderID);
|
||||
|
||||
wstring unitName;
|
||||
@ -174,30 +172,50 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
else if (key.compare(L"changeSpeed") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
unit->changeSpeed(value[L"change"].as_string());
|
||||
}
|
||||
else if (key.compare(L"changeAltitude") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
unit->changeAltitude(value[L"change"].as_string());
|
||||
}
|
||||
else if (key.compare(L"setSpeed") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
unit->setTargetSpeed(value[L"speed"].as_double());
|
||||
unit->setDesiredSpeed(value[L"speed"].as_double());
|
||||
}
|
||||
else if (key.compare(L"setSpeedType") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
unit->setDesiredSpeedType(value[L"speedType"].as_string());
|
||||
}
|
||||
else if (key.compare(L"setAltitude") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
unit->setTargetAltitude(value[L"altitude"].as_double());
|
||||
unit->setDesiredAltitude(value[L"altitude"].as_double());
|
||||
}
|
||||
else if (key.compare(L"setAltitudeType") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
unit->setDesiredAltitudeType(value[L"altitudeType"].as_string());
|
||||
}
|
||||
else if (key.compare(L"cloneUnit") == 0)
|
||||
{
|
||||
@ -211,28 +229,32 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
else if (key.compare(L"setROE") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
wstring ROE = value[L"ROE"].as_string();
|
||||
unit->setROE(ROE);
|
||||
}
|
||||
else if (key.compare(L"setReactionToThreat") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
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);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(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();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
@ -241,18 +263,21 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
else if (key.compare(L"deleteUnit") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->deleteUnit(ID);
|
||||
bool explosion = value[L"explosion"].as_bool();
|
||||
unitsManager->deleteUnit(ID, explosion);
|
||||
}
|
||||
else if (key.compare(L"refuel") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
unit->setState(State::REFUEL);
|
||||
}
|
||||
else if (key.compare(L"setAdvancedOptions") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
unitsManager->acquireControl(ID);
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
if (unit != nullptr)
|
||||
{
|
||||
/* Advanced tasking */
|
||||
@ -286,6 +311,75 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
unit->resetActiveDestination();
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"setFollowRoads") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
bool followRoads = value[L"followRoads"].as_bool();
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
unit->setFollowRoads(followRoads);
|
||||
}
|
||||
else if (key.compare(L"setOnOff") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
bool onOff = value[L"onOff"].as_bool();
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
unit->setOnOff(onOff);
|
||||
}
|
||||
else if (key.compare(L"explosion") == 0)
|
||||
{
|
||||
int intensity = value[L"intensity"].as_integer();
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
log(L"Adding " + to_wstring(intensity) + L" explosion at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
command = dynamic_cast<Command*>(new Explosion(intensity, loc));
|
||||
}
|
||||
else if (key.compare(L"bombPoint") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
unit->setState(State::BOMB_POINT);
|
||||
unit->setTargetLocation(loc);
|
||||
}
|
||||
else if (key.compare(L"carpetBomb") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
unit->setState(State::CARPET_BOMB);
|
||||
unit->setTargetLocation(loc);
|
||||
}
|
||||
else if (key.compare(L"bombBuilding") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
unit->setState(State::BOMB_BUILDING);
|
||||
unit->setTargetLocation(loc);
|
||||
}
|
||||
else if (key.compare(L"fireAtArea") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsManager->acquireControl(ID);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
Unit* unit = unitsManager->getGroupLeader(ID);
|
||||
unit->setState(State::FIRE_AT_AREA);
|
||||
unit->setTargetLocation(loc);
|
||||
}
|
||||
else
|
||||
{
|
||||
log(L"Unknown command: " + key);
|
||||
|
||||
@ -46,19 +46,30 @@ Unit::~Unit()
|
||||
void Unit::initialize(json::value json)
|
||||
{
|
||||
updateExportData(json);
|
||||
setDefaults();
|
||||
}
|
||||
|
||||
if (getAI()) {
|
||||
void Unit::setDefaults(bool force)
|
||||
{
|
||||
const bool isUnitControlledByOlympus = getControlled();
|
||||
const bool isUnitAlive = getAlive();
|
||||
const bool isUnitLeader = unitsManager->isUnitGroupLeader(this);
|
||||
const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this);
|
||||
const bool isUnitHuman = getFlags()[L"Human"].as_bool();
|
||||
if (isUnitControlledByOlympus && (isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits) && isUnitLeader && !isUnitHuman) {
|
||||
/* 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);
|
||||
setROE(L"Designated", force);
|
||||
setReactionToThreat(L"Evade", force);
|
||||
setEmissionsCountermeasures(L"Defend", force);
|
||||
setTACAN(TACAN, force);
|
||||
setRadio(radio, force);
|
||||
setEPLRS(EPLRS, force);
|
||||
setGeneralSettings(generalSettings, force);
|
||||
setOnOff(onOff);
|
||||
setFollowRoads(followRoads);
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,6 +88,24 @@ void Unit::addMeasure(wstring key, json::value value)
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::runAILoop() {
|
||||
/* If the unit is alive and it is not a human, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */
|
||||
const bool isUnitControlledByOlympus = getControlled();
|
||||
const bool isUnitAlive = getAlive();
|
||||
const bool isUnitLeader = unitsManager->isUnitGroupLeader(this);
|
||||
const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this);
|
||||
const bool isUnitHuman = getFlags()[L"Human"].as_bool();
|
||||
|
||||
// Keep running the AI loop even if the unit is dead if it is the leader of a group which has other members in it
|
||||
if (isUnitControlledByOlympus && (isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits) && isUnitLeader && !isUnitHuman)
|
||||
{
|
||||
if (checkTaskFailed() && state != State::IDLE && State::LAND)
|
||||
setState(State::IDLE);
|
||||
|
||||
AIloop();
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::updateExportData(json::value json)
|
||||
{
|
||||
/* Compute speed (loGetWorldObjects does not provide speed, we compute it for better performance instead of relying on many lua calls) */
|
||||
@ -112,12 +141,8 @@ void Unit::updateExportData(json::value json)
|
||||
setFlags(json[L"Flags"]);
|
||||
|
||||
/* All units which contain the name "Olympus" are automatically under AI control */
|
||||
/* TODO: I don't really like using this method */
|
||||
setAI(getUnitName().find(L"Olympus") != wstring::npos);
|
||||
|
||||
/* If the unit is alive and it is not a human, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */
|
||||
if (getAI() && getAlive() && getFlags()[L"Human"].as_bool() == false)
|
||||
AIloop();
|
||||
if (getUnitName().find(L"Olympus") != wstring::npos)
|
||||
setControlled(true);
|
||||
}
|
||||
|
||||
void Unit::updateMissionData(json::value json)
|
||||
@ -126,19 +151,23 @@ void Unit::updateMissionData(json::value json)
|
||||
setFuel(int(json[L"fuel"].as_number().to_double() * 100));
|
||||
if (json.has_object_field(L"ammo"))
|
||||
setAmmo(json[L"ammo"]);
|
||||
if (json.has_object_field(L"targets"))
|
||||
setTargets(json[L"targets"]);
|
||||
if (json.has_object_field(L"contacts"))
|
||||
setContacts(json[L"contacts"]);
|
||||
if (json.has_boolean_field(L"hasTask"))
|
||||
setHasTask(json[L"hasTask"].as_bool());
|
||||
}
|
||||
|
||||
json::value Unit::getData(long long time)
|
||||
json::value Unit::getData(long long time, bool sendAll)
|
||||
{
|
||||
auto json = json::value::object();
|
||||
|
||||
/* If the unit is in a group, task & option data is given by the group leader */
|
||||
if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this))
|
||||
json = unitsManager->getGroupLeader(this)->getData(time, true);
|
||||
|
||||
/********** Base data **********/
|
||||
json[L"baseData"] = json::value::object();
|
||||
for (auto key : { L"AI", L"name", L"unitName", L"groupName", L"alive", L"category"})
|
||||
for (auto key : { L"controlled", L"name", L"unitName", L"groupName", L"alive", L"category"})
|
||||
{
|
||||
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
|
||||
json[L"baseData"][key] = measures[key]->getValue();
|
||||
@ -146,7 +175,7 @@ json::value Unit::getData(long long time)
|
||||
if (json[L"baseData"].size() == 0)
|
||||
json.erase(L"baseData");
|
||||
|
||||
if (alive) {
|
||||
if (alive || sendAll) {
|
||||
/********** Flight data **********/
|
||||
json[L"flightData"] = json::value::object();
|
||||
for (auto key : { L"latitude", L"longitude", L"altitude", L"speed", L"heading" })
|
||||
@ -159,7 +188,7 @@ json::value Unit::getData(long long time)
|
||||
|
||||
/********** Mission data **********/
|
||||
json[L"missionData"] = json::value::object();
|
||||
for (auto key : { L"fuel", L"ammo", L"targets", L"hasTask", L"coalition", L"flags" })
|
||||
for (auto key : { L"fuel", L"ammo", L"contacts", L"hasTask", L"coalition", L"flags" })
|
||||
{
|
||||
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
|
||||
json[L"missionData"][key] = measures[key]->getValue();
|
||||
@ -177,25 +206,28 @@ json::value Unit::getData(long long time)
|
||||
if (json[L"formationData"].size() == 0)
|
||||
json.erase(L"formationData");
|
||||
|
||||
/********** 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" })
|
||||
{
|
||||
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
|
||||
json[L"taskData"][key] = measures[key]->getValue();
|
||||
}
|
||||
if (json[L"taskData"].size() == 0)
|
||||
json.erase(L"taskData");
|
||||
/* If the unit is in a group, task & option data is given by the group leader */
|
||||
if (unitsManager->isUnitGroupLeader(this)) {
|
||||
/********** Task data **********/
|
||||
json[L"taskData"] = json::value::object();
|
||||
for (auto key : { L"currentState", L"currentTask", L"desiredSpeed", L"desiredAltitude", L"desiredSpeedType", L"desiredAltitudeType", L"activePath", L"isTanker", L"isAWACS", L"onOff", L"followRoads", L"targetID", L"targetLocation" })
|
||||
{
|
||||
if (measures.find(key) != measures.end() && measures[key]->getTime() > time)
|
||||
json[L"taskData"][key] = measures[key]->getValue();
|
||||
}
|
||||
if (json[L"taskData"].size() == 0)
|
||||
json.erase(L"taskData");
|
||||
|
||||
/********** Options data **********/
|
||||
json[L"optionsData"] = json::value::object();
|
||||
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();
|
||||
/********** Options data **********/
|
||||
json[L"optionsData"] = json::value::object();
|
||||
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();
|
||||
}
|
||||
if (json[L"optionsData"].size() == 0)
|
||||
json.erase(L"optionsData");
|
||||
}
|
||||
if (json[L"optionsData"].size() == 0)
|
||||
json.erase(L"optionsData");
|
||||
}
|
||||
|
||||
return json;
|
||||
@ -322,8 +354,10 @@ void Unit::resetActiveDestination()
|
||||
|
||||
void Unit::resetTask()
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new ResetTask(ID));
|
||||
Command* command = dynamic_cast<Command*>(new ResetTask(groupName));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(false);
|
||||
resetTaskFailedCounter();
|
||||
}
|
||||
|
||||
void Unit::setFormationOffset(Offset newFormationOffset)
|
||||
@ -332,10 +366,10 @@ void Unit::setFormationOffset(Offset newFormationOffset)
|
||||
resetTask();
|
||||
}
|
||||
|
||||
void Unit::setROE(wstring newROE) {
|
||||
void Unit::setROE(wstring newROE, bool force) {
|
||||
addMeasure(L"ROE", json::value(newROE));
|
||||
|
||||
if (ROE != newROE) {
|
||||
if (ROE != newROE || force) {
|
||||
ROE = newROE;
|
||||
|
||||
int ROEEnum;
|
||||
@ -352,15 +386,15 @@ void Unit::setROE(wstring newROE) {
|
||||
else
|
||||
return;
|
||||
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::ROE, ROEEnum));
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ROE, ROEEnum));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setReactionToThreat(wstring newReactionToThreat) {
|
||||
void Unit::setReactionToThreat(wstring newReactionToThreat, bool force) {
|
||||
addMeasure(L"reactionToThreat", json::value(newReactionToThreat));
|
||||
|
||||
if (reactionToThreat != newReactionToThreat) {
|
||||
if (reactionToThreat != newReactionToThreat || force) {
|
||||
reactionToThreat = newReactionToThreat;
|
||||
|
||||
int reactionToThreatEnum;
|
||||
@ -377,15 +411,15 @@ void Unit::setReactionToThreat(wstring newReactionToThreat) {
|
||||
else
|
||||
return;
|
||||
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum));
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setEmissionsCountermeasures(wstring newEmissionsCountermeasures) {
|
||||
void Unit::setEmissionsCountermeasures(wstring newEmissionsCountermeasures, bool force) {
|
||||
addMeasure(L"emissionsCountermeasures", json::value(newEmissionsCountermeasures));
|
||||
|
||||
if (emissionsCountermeasures != newEmissionsCountermeasures) {
|
||||
if (emissionsCountermeasures != newEmissionsCountermeasures || force) {
|
||||
emissionsCountermeasures = newEmissionsCountermeasures;
|
||||
|
||||
int radarEnum;
|
||||
@ -420,13 +454,13 @@ void Unit::setEmissionsCountermeasures(wstring newEmissionsCountermeasures) {
|
||||
|
||||
Command* command;
|
||||
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::RADAR_USING, radarEnum));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::RADAR_USING, radarEnum));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::FLARE_USING, flareEnum));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::FLARE_USING, flareEnum));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::ECM_USING, ECMEnum));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ECM_USING, ECMEnum));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
@ -450,7 +484,7 @@ void Unit::setIsAWACS(bool newIsAWACS) {
|
||||
setEPLRS(isAWACS);
|
||||
}
|
||||
|
||||
void Unit::setTACAN(Options::TACAN newTACAN) {
|
||||
void Unit::setTACAN(Options::TACAN newTACAN, bool force) {
|
||||
auto json = json::value();
|
||||
json[L"isOn"] = json::value(newTACAN.isOn);
|
||||
json[L"channel"] = json::value(newTACAN.channel);
|
||||
@ -458,7 +492,7 @@ void Unit::setTACAN(Options::TACAN newTACAN) {
|
||||
json[L"callsign"] = json::value(newTACAN.callsign);
|
||||
addMeasure(L"TACAN", json);
|
||||
|
||||
if (TACAN != newTACAN)
|
||||
if (TACAN != newTACAN || force)
|
||||
{
|
||||
TACAN = newTACAN;
|
||||
if (TACAN.isOn) {
|
||||
@ -473,7 +507,7 @@ void Unit::setTACAN(Options::TACAN newTACAN) {
|
||||
<< "frequency = " << TACANChannelToFrequency(TACAN.channel, TACAN.XY) << ","
|
||||
<< "}"
|
||||
<< "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetCommand(ID, commandSS.str()));
|
||||
Command* command = dynamic_cast<Command*>(new SetCommand(groupName, commandSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
else {
|
||||
@ -483,13 +517,13 @@ void Unit::setTACAN(Options::TACAN newTACAN) {
|
||||
<< "params = {"
|
||||
<< "}"
|
||||
<< "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetCommand(ID, commandSS.str()));
|
||||
Command* command = dynamic_cast<Command*>(new SetCommand(groupName, commandSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setRadio(Options::Radio newRadio) {
|
||||
void Unit::setRadio(Options::Radio newRadio, bool force) {
|
||||
|
||||
auto json = json::value();
|
||||
json[L"frequency"] = json::value(newRadio.frequency);
|
||||
@ -497,7 +531,7 @@ void Unit::setRadio(Options::Radio newRadio) {
|
||||
json[L"callsignNumber"] = json::value(newRadio.callsignNumber);
|
||||
addMeasure(L"radio", json);
|
||||
|
||||
if (radio != newRadio)
|
||||
if (radio != newRadio || force)
|
||||
{
|
||||
radio = newRadio;
|
||||
|
||||
@ -511,7 +545,7 @@ void Unit::setRadio(Options::Radio newRadio) {
|
||||
<< "frequency = " << radio.frequency << ","
|
||||
<< "}"
|
||||
<< "}";
|
||||
command = dynamic_cast<Command*>(new SetCommand(ID, commandSS.str()));
|
||||
command = dynamic_cast<Command*>(new SetCommand(groupName, commandSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
// Clear the stringstream
|
||||
@ -524,16 +558,16 @@ void Unit::setRadio(Options::Radio newRadio) {
|
||||
<< "number = " << radio.callsignNumber << ","
|
||||
<< "}"
|
||||
<< "}";
|
||||
command = dynamic_cast<Command*>(new SetCommand(ID, commandSS.str()));
|
||||
command = dynamic_cast<Command*>(new SetCommand(groupName, commandSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setEPLRS(bool newEPLRS)
|
||||
void Unit::setEPLRS(bool newEPLRS, bool force)
|
||||
{
|
||||
//addMeasure(L"EPLRS", json::value(newEPLRS));
|
||||
//
|
||||
//if (EPLRS != newEPLRS) {
|
||||
//if (EPLRS != newEPLRS || force) {
|
||||
// EPLRS = newEPLRS;
|
||||
//
|
||||
// std::wostringstream commandSS;
|
||||
@ -548,7 +582,7 @@ void Unit::setEPLRS(bool newEPLRS)
|
||||
//}
|
||||
}
|
||||
|
||||
void Unit::setGeneralSettings(Options::GeneralSettings newGeneralSettings) {
|
||||
void Unit::setGeneralSettings(Options::GeneralSettings newGeneralSettings, bool force) {
|
||||
|
||||
auto json = json::value();
|
||||
json[L"prohibitJettison"] = json::value(newGeneralSettings.prohibitJettison);
|
||||
@ -563,16 +597,143 @@ void Unit::setGeneralSettings(Options::GeneralSettings newGeneralSettings) {
|
||||
generalSettings = newGeneralSettings;
|
||||
|
||||
Command* command;
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::PROHIBIT_AA, generalSettings.prohibitAA));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::PROHIBIT_AA, generalSettings.prohibitAA));
|
||||
scheduler->appendCommand(command);
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::PROHIBIT_AG, generalSettings.prohibitAG));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::PROHIBIT_AG, generalSettings.prohibitAG));
|
||||
scheduler->appendCommand(command);
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::PROHIBIT_JETT, generalSettings.prohibitJettison));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::PROHIBIT_JETT, generalSettings.prohibitJettison));
|
||||
scheduler->appendCommand(command);
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::PROHIBIT_AB, generalSettings.prohibitAfterburner));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::PROHIBIT_AB, generalSettings.prohibitAfterburner));
|
||||
scheduler->appendCommand(command);
|
||||
command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::ENGAGE_AIR_WEAPONS, !generalSettings.prohibitAirWpn));
|
||||
command = dynamic_cast<Command*>(new SetOption(groupName, SetCommandType::ENGAGE_AIR_WEAPONS, !generalSettings.prohibitAirWpn));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setDesiredSpeed(double newDesiredSpeed) {
|
||||
desiredSpeed = newDesiredSpeed;
|
||||
addMeasure(L"desiredSpeed", json::value(newDesiredSpeed));
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Unit::setDesiredAltitude(double newDesiredAltitude) {
|
||||
desiredAltitude = newDesiredAltitude;
|
||||
addMeasure(L"desiredAltitude", json::value(newDesiredAltitude));
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Unit::setDesiredSpeedType(wstring newDesiredSpeedType) {
|
||||
desiredSpeedType = newDesiredSpeedType;
|
||||
addMeasure(L"desiredSpeedType", json::value(newDesiredSpeedType));
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Unit::setDesiredAltitudeType(wstring newDesiredAltitudeType) {
|
||||
desiredAltitudeType = newDesiredAltitudeType;
|
||||
addMeasure(L"desiredAltitudeType", json::value(newDesiredAltitudeType));
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Unit::goToDestination(wstring enrouteTask)
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new Move(groupName, activeDestination, getDesiredSpeed(), getDesiredSpeedType(), getDesiredAltitude(), getDesiredAltitudeType(), enrouteTask, getCategory()));
|
||||
scheduler->appendCommand(command);
|
||||
setHasTask(true);
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
double dist = 0;
|
||||
Geodesic::WGS84().Inverse(p->getLatitude(), p->getLongitude(), activeDestination.lat, activeDestination.lng, dist);
|
||||
if (dist < threshold)
|
||||
{
|
||||
log(unitName + L" destination reached");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Unit::setActiveDestination()
|
||||
{
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
activeDestination = activePath.front();
|
||||
log(unitName + L" active destination set to queue front");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeDestination = Coords(0);
|
||||
log(unitName + L" active destination set to NULL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Unit::updateActivePath(bool looping)
|
||||
{
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
/* Push the next destination in the queue to the front */
|
||||
if (looping)
|
||||
pushActivePathBack(activePath.front());
|
||||
popActivePathFront();
|
||||
log(unitName + L" active path front popped");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setTargetLocation(Coords newTargetLocation) {
|
||||
targetLocation = newTargetLocation;
|
||||
auto json = json::value();
|
||||
json[L"latitude"] = json::value(newTargetLocation.lat);
|
||||
json[L"longitude"] = json::value(newTargetLocation.lng);
|
||||
addMeasure(L"targetLocation", json::value(json));
|
||||
}
|
||||
|
||||
bool Unit::checkTaskFailed() {
|
||||
if (getHasTask())
|
||||
return false;
|
||||
else {
|
||||
if (taskCheckCounter > 0)
|
||||
taskCheckCounter--;
|
||||
return taskCheckCounter == 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::resetTaskFailedCounter() {
|
||||
taskCheckCounter = TASK_CHECK_INIT_VALUE;
|
||||
}
|
||||
|
||||
void Unit::setHasTask(bool newHasTask) {
|
||||
hasTask = newHasTask;
|
||||
addMeasure(L"hasTask", json::value(newHasTask));
|
||||
}
|
||||
@ -95,6 +95,14 @@ void UnitsManager::updateMissionData(json::value missionData)
|
||||
}
|
||||
}
|
||||
|
||||
void UnitsManager::runAILoop() {
|
||||
/* Run the AI Loop on all units */
|
||||
for (auto const& unit : units)
|
||||
{
|
||||
unit.second->runAILoop();
|
||||
}
|
||||
}
|
||||
|
||||
void UnitsManager::getData(json::value& answer, long long time)
|
||||
{
|
||||
auto unitsJson = json::value::object();
|
||||
@ -116,3 +124,17 @@ void UnitsManager::deleteUnit(int ID)
|
||||
}
|
||||
}
|
||||
|
||||
void UnitsManager::acquireControl(int ID) {
|
||||
Unit* unit = getUnit(ID);
|
||||
if (unit != nullptr) {
|
||||
for (auto const& groupMember : getGroupMembers(unit->getGroupName())) {
|
||||
if (!groupMember->getControlled()) {
|
||||
groupMember->setControlled(true);
|
||||
groupMember->setState(State::IDLE);
|
||||
groupMember->setDefaults(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user