mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
feat(radar state): initial scaffolding
This commit is contained in:
@@ -96,6 +96,12 @@ export const states: string[] = [
|
||||
UnitState.LAND_AT_POINT,
|
||||
];
|
||||
|
||||
export enum RADAR_STATES {
|
||||
RED = 'red',
|
||||
GREEN = 'green',
|
||||
AUTO = 'auto'
|
||||
}
|
||||
|
||||
export const ROEs: string[] = ["free", "designated", "", "return", "hold"];
|
||||
export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"];
|
||||
export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"];
|
||||
@@ -448,6 +454,7 @@ export enum DataIndexes {
|
||||
startOfData = 0,
|
||||
category,
|
||||
alive,
|
||||
radarState,
|
||||
human,
|
||||
controlled,
|
||||
coalition,
|
||||
|
||||
@@ -83,6 +83,7 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
/* Data controlled directly by the backend. No setters are provided to avoid misalignments */
|
||||
#alive: boolean = false;
|
||||
#radarState: string = '';
|
||||
#human: boolean = false;
|
||||
#controlled: boolean = false;
|
||||
#coalition: string = "neutral";
|
||||
@@ -488,6 +489,10 @@ export abstract class Unit extends CustomMarker {
|
||||
this.setAlive(dataExtractor.extractBool());
|
||||
updateMarker = true;
|
||||
break;
|
||||
case DataIndexes.radarState:
|
||||
this.setRadarState(dataExtractor.extractString());
|
||||
updateMarker = true;
|
||||
break;
|
||||
case DataIndexes.human:
|
||||
this.#human = dataExtractor.extractBool();
|
||||
break;
|
||||
@@ -764,6 +769,15 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
setRadarState(newRadarState: string) {
|
||||
if (newRadarState != this.#radarState) {
|
||||
this.#radarState = newRadarState;
|
||||
// TODO: check if an event is needed -- surely yes to update the UI
|
||||
console.log('----radar state updated: ', this.#radarState);
|
||||
this.#updateMarker();
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the unit as user-selected
|
||||
*
|
||||
* @param selected (boolean)
|
||||
@@ -1542,149 +1556,152 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.getHidden()) return; // We won't draw the marker if the unit is hidden
|
||||
|
||||
/* Draw the marker */
|
||||
if (!this.getHidden()) {
|
||||
if (this.getLatLng().lat !== this.#position.lat || this.getLatLng().lng !== this.#position.lng) {
|
||||
this.setLatLng(new LatLng(this.#position.lat, this.#position.lng));
|
||||
}
|
||||
if (this.getLatLng().lat !== this.#position.lat || this.getLatLng().lng !== this.#position.lng) {
|
||||
this.setLatLng(new LatLng(this.#position.lat, this.#position.lng));
|
||||
}
|
||||
|
||||
var element = this.getElement();
|
||||
if (element != null) {
|
||||
/* Draw the velocity vector */
|
||||
element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.#speed / 5}px;`);
|
||||
var element = this.getElement();
|
||||
if (element != null) {
|
||||
/* Draw the velocity vector */
|
||||
element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.#speed / 5}px;`);
|
||||
|
||||
/* Set fuel data */
|
||||
element.querySelector(".unit-fuel-level")?.setAttribute("style", `width: ${this.#fuel}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-fuel", this.#fuel < 20);
|
||||
/* Set fuel data */
|
||||
element.querySelector(".unit-fuel-level")?.setAttribute("style", `width: ${this.#fuel}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-fuel", this.#fuel < 20);
|
||||
|
||||
/* Set health data */
|
||||
element.querySelector(".unit-health-level")?.setAttribute("style", `width: ${this.#health}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-health", this.#health < 20);
|
||||
/* Set health data */
|
||||
element.querySelector(".unit-health-level")?.setAttribute("style", `width: ${this.#health}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-health", this.#health < 20);
|
||||
|
||||
/* Set dead/alive flag */
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
|
||||
/* Set dead/alive flag */
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
|
||||
|
||||
/* Set current unit state */
|
||||
if (this.#human) {
|
||||
// Unit is human
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "human");
|
||||
} else if (!this.#controlled) {
|
||||
// Unit is under DCS control (not Olympus)
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "dcs");
|
||||
} else if ((this.getCategory() == "Aircraft" || this.getCategory() == "Helicopter") && !this.#hasTask) {
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "no-task");
|
||||
} else {
|
||||
// Unit is under Olympus control
|
||||
if (this.#onOff) {
|
||||
if (this.#isActiveTanker) element.querySelector(".unit")?.setAttribute("data-state", "tanker");
|
||||
else if (this.#isActiveAWACS) element.querySelector(".unit")?.setAttribute("data-state", "AWACS");
|
||||
else element.querySelector(".unit")?.setAttribute("data-state", this.#state.toLowerCase());
|
||||
} else {
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "off");
|
||||
}
|
||||
}
|
||||
/* Set RED/GREEN state*/
|
||||
if (this.#radarState !== '') element.querySelector(".unit")?.setAttribute("data-radar-state", this.#radarState);
|
||||
|
||||
/* Set altitude and speed */
|
||||
if (element.querySelector(".unit-altitude"))
|
||||
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + zeroAppend(Math.floor(mToFt(this.#position.alt as number) / 100), 3);
|
||||
if (element.querySelector(".unit-speed"))
|
||||
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.#speed))) + "GS";
|
||||
|
||||
/* Rotate elements according to heading */
|
||||
element.querySelectorAll("[data-rotate-to-heading]").forEach((el) => {
|
||||
const headingDeg = rad2deg(this.#track);
|
||||
let currentStyle = el.getAttribute("style") || "";
|
||||
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
|
||||
});
|
||||
|
||||
/* Turn on ammo indicators */
|
||||
var hasFox1 = element.querySelector(".unit")?.hasAttribute("data-has-fox-1");
|
||||
var hasFox2 = element.querySelector(".unit")?.hasAttribute("data-has-fox-2");
|
||||
var hasFox3 = element.querySelector(".unit")?.hasAttribute("data-has-fox-3");
|
||||
var hasOtherAmmo = element.querySelector(".unit")?.hasAttribute("data-has-other-ammo");
|
||||
|
||||
var newHasFox1 = false;
|
||||
var newHasFox2 = false;
|
||||
var newHasFox3 = false;
|
||||
var newHasOtherAmmo = false;
|
||||
Object.values(this.#ammo).forEach((ammo: Ammo) => {
|
||||
if (ammo.category == 1 && ammo.missileCategory == 1) {
|
||||
if (ammo.guidance == 4 || ammo.guidance == 5) newHasFox1 = true;
|
||||
else if (ammo.guidance == 2) newHasFox2 = true;
|
||||
else if (ammo.guidance == 3) newHasFox3 = true;
|
||||
} else newHasOtherAmmo = true;
|
||||
});
|
||||
|
||||
if (hasFox1 != newHasFox1) element.querySelector(".unit")?.toggleAttribute("data-has-fox-1", newHasFox1);
|
||||
if (hasFox2 != newHasFox2) element.querySelector(".unit")?.toggleAttribute("data-has-fox-2", newHasFox2);
|
||||
if (hasFox3 != newHasFox3) element.querySelector(".unit")?.toggleAttribute("data-has-fox-3", newHasFox3);
|
||||
if (hasOtherAmmo != newHasOtherAmmo) element.querySelector(".unit")?.toggleAttribute("data-has-other-ammo", newHasOtherAmmo);
|
||||
|
||||
/* Draw the hotgroup element */
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-in-hotgroup", this.#hotgroup != null);
|
||||
if (this.#hotgroup) {
|
||||
const hotgroupEl = element.querySelector(".unit-hotgroup-id") as HTMLElement;
|
||||
if (hotgroupEl) hotgroupEl.innerText = String(this.#hotgroup);
|
||||
}
|
||||
|
||||
/* Set bullseyes positions */
|
||||
const bullseyes = getApp().getMissionManager().getBullseyes();
|
||||
if (Object.keys(bullseyes).length > 0) {
|
||||
const bullseye = `${computeBearingRangeString(bullseyes[coalitionToEnum(getApp().getMap().getOptions().AWACSCoalition)].getLatLng(), this.getPosition())}`;
|
||||
if (element.querySelector(".unit-bullseye")) (<HTMLElement>element.querySelector(".unit-bullseye")).innerText = `${bullseye}`;
|
||||
}
|
||||
|
||||
/* Set BRAA */
|
||||
const reference = getApp().getUnitsManager().getAWACSReference();
|
||||
if (reference && reference !== this && reference.getAlive()) {
|
||||
const BRAA = `${computeBearingRangeString(reference.getPosition(), this.getPosition())}`;
|
||||
if (element.querySelector(".unit-braa")) (<HTMLElement>element.querySelector(".unit-braa")).innerText = `${BRAA}`;
|
||||
} else if (element.querySelector(".unit-braa")) (<HTMLElement>element.querySelector(".unit-braa")).innerText = ``;
|
||||
|
||||
/* Set operate as */
|
||||
element
|
||||
.querySelector(".unit")
|
||||
?.setAttribute(
|
||||
"data-operate-as",
|
||||
this.getState() === UnitState.MISS_ON_PURPOSE || this.getState() === UnitState.SCENIC_AAA || this.getState() === UnitState.SIMULATE_FIRE_FIGHT
|
||||
? this.#operateAs
|
||||
: "neutral"
|
||||
);
|
||||
}
|
||||
|
||||
/* Set vertical offset for altitude stacking */
|
||||
var pos = getApp().getMap().latLngToLayerPoint(this.getLatLng()).round();
|
||||
this.setZIndexOffset(1000 + Math.floor(this.#position.alt as number) - pos.y + (this.#highlighted || this.#selected ? 5000 : 0));
|
||||
|
||||
/* Get the cluster this unit is in to position the label correctly */
|
||||
let cluster = Object.values(getApp().getUnitsManager().getClusters()).find((cluster) => cluster.includes(this));
|
||||
if (cluster && cluster.length > 1) {
|
||||
let clusterMean = turf.centroid(turf.featureCollection(cluster.map((unit) => turf.point([unit.getPosition().lng, unit.getPosition().lat]))));
|
||||
let bearingFromCluster = bearing(
|
||||
clusterMean.geometry.coordinates[1],
|
||||
clusterMean.geometry.coordinates[0],
|
||||
this.getPosition().lat,
|
||||
this.getPosition().lng,
|
||||
false
|
||||
);
|
||||
|
||||
if (bearingFromCluster < 0) bearingFromCluster += 360;
|
||||
let trackIndex = Math.round(bearingFromCluster / 45);
|
||||
|
||||
for (let idx = 0; idx < bearingStrings.length; idx++) element?.querySelector(".unit-summary")?.classList.remove("cluster-" + bearingStrings[idx]);
|
||||
element?.querySelector(".unit-summary")?.classList.add("cluster-" + bearingStrings[trackIndex]);
|
||||
/* Set current unit state */
|
||||
if (this.#human) {
|
||||
// Unit is human
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "human");
|
||||
} else if (!this.#controlled) {
|
||||
// Unit is under DCS control (not Olympus)
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "dcs");
|
||||
} else if ((this.getCategory() == "Aircraft" || this.getCategory() == "Helicopter") && !this.#hasTask) {
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "no-task");
|
||||
} else {
|
||||
for (let idx = 0; idx < bearingStrings.length; idx++) element?.querySelector(".unit-summary")?.classList.remove("cluster-" + bearingStrings[idx]);
|
||||
element?.querySelector(".unit-summary")?.classList.add("cluster-north-east");
|
||||
// Unit is under Olympus control
|
||||
if (this.#onOff) {
|
||||
if (this.#isActiveTanker) element.querySelector(".unit")?.setAttribute("data-state", "tanker");
|
||||
else if (this.#isActiveAWACS) element.querySelector(".unit")?.setAttribute("data-state", "AWACS");
|
||||
else element.querySelector(".unit")?.setAttribute("data-state", this.#state.toLowerCase());
|
||||
} else {
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "off");
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw the contact trail */
|
||||
if (/*TODO getApp().getMap().getOptions().AWACSMode*/ false) {
|
||||
this.#trailPolylines = this.#trailPositions.map(
|
||||
(latlng, idx) => new Polyline([latlng, latlng], { color: colors.WHITE, opacity: 1 - (idx + 1) / TRAIL_LENGTH })
|
||||
);
|
||||
this.#trailPolylines.forEach((polyline) => polyline.addTo(getApp().getMap()));
|
||||
/* Set altitude and speed */
|
||||
if (element.querySelector(".unit-altitude"))
|
||||
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + zeroAppend(Math.floor(mToFt(this.#position.alt as number) / 100), 3);
|
||||
if (element.querySelector(".unit-speed"))
|
||||
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.#speed))) + "GS";
|
||||
|
||||
/* Rotate elements according to heading */
|
||||
element.querySelectorAll("[data-rotate-to-heading]").forEach((el) => {
|
||||
const headingDeg = rad2deg(this.#track);
|
||||
let currentStyle = el.getAttribute("style") || "";
|
||||
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
|
||||
});
|
||||
|
||||
/* Turn on ammo indicators */
|
||||
var hasFox1 = element.querySelector(".unit")?.hasAttribute("data-has-fox-1");
|
||||
var hasFox2 = element.querySelector(".unit")?.hasAttribute("data-has-fox-2");
|
||||
var hasFox3 = element.querySelector(".unit")?.hasAttribute("data-has-fox-3");
|
||||
var hasOtherAmmo = element.querySelector(".unit")?.hasAttribute("data-has-other-ammo");
|
||||
|
||||
var newHasFox1 = false;
|
||||
var newHasFox2 = false;
|
||||
var newHasFox3 = false;
|
||||
var newHasOtherAmmo = false;
|
||||
Object.values(this.#ammo).forEach((ammo: Ammo) => {
|
||||
if (ammo.category == 1 && ammo.missileCategory == 1) {
|
||||
if (ammo.guidance == 4 || ammo.guidance == 5) newHasFox1 = true;
|
||||
else if (ammo.guidance == 2) newHasFox2 = true;
|
||||
else if (ammo.guidance == 3) newHasFox3 = true;
|
||||
} else newHasOtherAmmo = true;
|
||||
});
|
||||
|
||||
if (hasFox1 != newHasFox1) element.querySelector(".unit")?.toggleAttribute("data-has-fox-1", newHasFox1);
|
||||
if (hasFox2 != newHasFox2) element.querySelector(".unit")?.toggleAttribute("data-has-fox-2", newHasFox2);
|
||||
if (hasFox3 != newHasFox3) element.querySelector(".unit")?.toggleAttribute("data-has-fox-3", newHasFox3);
|
||||
if (hasOtherAmmo != newHasOtherAmmo) element.querySelector(".unit")?.toggleAttribute("data-has-other-ammo", newHasOtherAmmo);
|
||||
|
||||
/* Draw the hotgroup element */
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-in-hotgroup", this.#hotgroup != null);
|
||||
if (this.#hotgroup) {
|
||||
const hotgroupEl = element.querySelector(".unit-hotgroup-id") as HTMLElement;
|
||||
if (hotgroupEl) hotgroupEl.innerText = String(this.#hotgroup);
|
||||
}
|
||||
|
||||
/* Set bullseyes positions */
|
||||
const bullseyes = getApp().getMissionManager().getBullseyes();
|
||||
if (Object.keys(bullseyes).length > 0) {
|
||||
const bullseye = `${computeBearingRangeString(bullseyes[coalitionToEnum(getApp().getMap().getOptions().AWACSCoalition)].getLatLng(), this.getPosition())}`;
|
||||
if (element.querySelector(".unit-bullseye")) (<HTMLElement>element.querySelector(".unit-bullseye")).innerText = `${bullseye}`;
|
||||
}
|
||||
|
||||
/* Set BRAA */
|
||||
const reference = getApp().getUnitsManager().getAWACSReference();
|
||||
if (reference && reference !== this && reference.getAlive()) {
|
||||
const BRAA = `${computeBearingRangeString(reference.getPosition(), this.getPosition())}`;
|
||||
if (element.querySelector(".unit-braa")) (<HTMLElement>element.querySelector(".unit-braa")).innerText = `${BRAA}`;
|
||||
} else if (element.querySelector(".unit-braa")) (<HTMLElement>element.querySelector(".unit-braa")).innerText = ``;
|
||||
|
||||
/* Set operate as */
|
||||
element
|
||||
.querySelector(".unit")
|
||||
?.setAttribute(
|
||||
"data-operate-as",
|
||||
this.getState() === UnitState.MISS_ON_PURPOSE || this.getState() === UnitState.SCENIC_AAA || this.getState() === UnitState.SIMULATE_FIRE_FIGHT
|
||||
? this.#operateAs
|
||||
: "neutral"
|
||||
);
|
||||
}
|
||||
|
||||
/* Set vertical offset for altitude stacking */
|
||||
var pos = getApp().getMap().latLngToLayerPoint(this.getLatLng()).round();
|
||||
this.setZIndexOffset(1000 + Math.floor(this.#position.alt as number) - pos.y + (this.#highlighted || this.#selected ? 5000 : 0));
|
||||
|
||||
/* Get the cluster this unit is in to position the label correctly */
|
||||
let cluster = Object.values(getApp().getUnitsManager().getClusters()).find((cluster) => cluster.includes(this));
|
||||
if (cluster && cluster.length > 1) {
|
||||
let clusterMean = turf.centroid(turf.featureCollection(cluster.map((unit) => turf.point([unit.getPosition().lng, unit.getPosition().lat]))));
|
||||
let bearingFromCluster = bearing(
|
||||
clusterMean.geometry.coordinates[1],
|
||||
clusterMean.geometry.coordinates[0],
|
||||
this.getPosition().lat,
|
||||
this.getPosition().lng,
|
||||
false
|
||||
);
|
||||
|
||||
if (bearingFromCluster < 0) bearingFromCluster += 360;
|
||||
let trackIndex = Math.round(bearingFromCluster / 45);
|
||||
|
||||
for (let idx = 0; idx < bearingStrings.length; idx++) element?.querySelector(".unit-summary")?.classList.remove("cluster-" + bearingStrings[idx]);
|
||||
element?.querySelector(".unit-summary")?.classList.add("cluster-" + bearingStrings[trackIndex]);
|
||||
} else {
|
||||
for (let idx = 0; idx < bearingStrings.length; idx++) element?.querySelector(".unit-summary")?.classList.remove("cluster-" + bearingStrings[idx]);
|
||||
element?.querySelector(".unit-summary")?.classList.add("cluster-north-east");
|
||||
}
|
||||
|
||||
/* Draw the contact trail */
|
||||
if (/*TODO getApp().getMap().getOptions().AWACSMode*/ false) {
|
||||
this.#trailPolylines = this.#trailPositions.map(
|
||||
(latlng, idx) => new Polyline([latlng, latlng], { color: colors.WHITE, opacity: 1 - (idx + 1) / TRAIL_LENGTH })
|
||||
);
|
||||
this.#trailPolylines.forEach((polyline) => polyline.addTo(getApp().getMap()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -241,6 +241,7 @@ export class UnitsManager {
|
||||
if (datumIndex == DataIndexes.category) {
|
||||
const category = dataExtractor.extractString();
|
||||
this.addUnit(ID, category);
|
||||
|
||||
} else {
|
||||
/* Inconsistent data, we need to wait for a refresh */
|
||||
return updateTime;
|
||||
@@ -250,6 +251,7 @@ export class UnitsManager {
|
||||
if (ID in this.#units) {
|
||||
this.#units[ID].setData(dataExtractor);
|
||||
this.#units[ID].getAlive() && updatedUnits.push(this.#units[ID]);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user