feat(alarm state): added unit control panel buttons (WIP)

This commit is contained in:
MarcoJayUsai 2025-03-21 12:33:15 +01:00
parent 18960ca51e
commit 2a00bab149
6 changed files with 121 additions and 24 deletions

View File

@ -96,13 +96,8 @@ 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 alarmStates: string[] = ["green", "auto", "red"];
export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"];
export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"];

View File

@ -213,6 +213,7 @@ export interface UnitData {
markerCategory: string;
ID: number;
alive: boolean;
alarmState: AlarmState | undefined;
human: boolean;
controlled: boolean;
coalition: string;
@ -395,4 +396,10 @@ export interface Drawing {
hiddenOnPlanner?: boolean;
file?: string;
scale?: number;
}
export enum AlarmState {
AUTO = 'auto',
GREEN = 'green',
RED = 'red'
}

View File

@ -631,12 +631,12 @@
left: 35px;
bottom: 8px;
}
.unit[data-radar-state="GREEN"] .unit-radar-state {
.unit[data-radar-state="green"] .unit-radar-state {
border: 1px solid white;
background: rgb(0, 226, 0);
}
.unit[data-radar-state="RED"] .unit-radar-state {
.unit[data-radar-state="red"] .unit-radar-state {
border: 1px solid white;
background: red;
}

View File

@ -547,7 +547,7 @@ export function roundToNearestFive(number) {
return Math.round(number / 5) * 5;
}
export function toDCSFormationOffset(offset: {x: number, y: number, z: number}) {
export function toDCSFormationOffset(offset: { x: number, y: number, z: number }) {
// X: front-rear, positive front
// Y: top-bottom, positive top
// Z: left-right, positive right
@ -555,7 +555,7 @@ export function toDCSFormationOffset(offset: {x: number, y: number, z: number})
return { x: -offset.y, y: offset.z, z: offset.x };
}
export function fromDCSFormationOffset(offset: {x: number, y: number, z: number}) {
export function fromDCSFormationOffset(offset: { x: number, y: number, z: number }) {
return { x: offset.z, y: -offset.x, z: offset.y };
}
@ -649,10 +649,10 @@ export function normalizeAngle(angle: number): number {
}
export function decimalToRGBA(decimal: number): string {
const r = (decimal >>> 24) & 0xff;
const g = (decimal >>> 16) & 0xff;
const b = (decimal >>> 8) & 0xff;
const a = (decimal & 0xff) / 255;
const r = (decimal >>> 24) & 0xff;
const g = (decimal >>> 16) & 0xff;
const b = (decimal >>> 8) & 0xff;
const a = (decimal & 0xff) / 255;
return `rgba(${r}, ${g}, ${b}, ${a.toFixed(2)})`;
}

View File

@ -8,6 +8,7 @@ import { OlButtonGroup, OlButtonGroupItem } from "../components/olbuttongroup";
import { OlCheckbox } from "../components/olcheckbox";
import {
ROEs,
alarmStates,
altitudeIncrements,
emissionsCountermeasures,
maxAltitudeValues,
@ -53,7 +54,7 @@ import { OlSearchBar } from "../components/olsearchbar";
import { OlDropdown, OlDropdownItem } from "../components/oldropdown";
import { FaRadio, FaVolumeHigh } from "react-icons/fa6";
import { OlNumberInput } from "../components/olnumberinput";
import { GeneralSettings, Radio, TACAN } from "../../interfaces";
import { AlarmState, GeneralSettings, Radio, TACAN } from "../../interfaces";
import { OlStringInput } from "../components/olstringinput";
import { OlFrequencyInput } from "../components/olfrequencyinput";
import { UnitSink } from "../../audio/unitsink";
@ -85,6 +86,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
radio: undefined as undefined | Radio,
TACAN: undefined as undefined | TACAN,
generalSettings: undefined as undefined | GeneralSettings,
alarmState: undefined as undefined | AlarmState
};
}
@ -165,6 +167,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
onOff: (unit: Unit) => unit.getOnOff(),
radio: (unit: Unit) => unit.getRadio(),
TACAN: (unit: Unit) => unit.getTACAN(),
alarmState: (unit: Unit) => unit.getAlarmState(),
generalSettings: (unit: Unit) => unit.getGeneralSettings(),
isAudioSink: (unit: Unit) => {
return (
@ -818,6 +821,82 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
</div>
)}
{/* ============== Rules of Engagement END ============== */}
{/* ============== Alarm state selector START ============== */}
{selectedUnitsData.alarmState &&
<div className="flex flex-col gap-2">
<span
className={`
my-auto font-normal
dark:text-white
`}
>
Alarm State
</span>
<OlButtonGroup
tooltip={() => (
<OlExpandingTooltip
title="Alarm State"
content={
<div className="flex flex-col gap-2">
<div>Sets the alarm state of the unit, in order:</div>
<div className="flex flex-col gap-2 px-2">
<div className="flex content-center gap-2">
{" "}
<FontAwesomeIcon icon={olButtonsRoeReturn} className={`
my-auto min-w-8 text-white
`} /> Green: The unit will not engage with its sensors in any circumstances. The unit will be able to move.
</div>
<div className="flex content-center gap-2">
{" "}
<FontAwesomeIcon icon={olButtonsRoeDesignated} className={`
my-auto min-w-8 text-white
`} />{" "}
<div>
{" "}
Auto: The unit will use its sensors to engage based on its ROE.
</div>
</div>
<div className="flex content-center gap-2">
{" "}
<FontAwesomeIcon icon={olButtonsRoeHold} className={`
my-auto min-w-8 text-white
`} /> Red: The unit will be actively searching for target with its sensors. For some units, this will deploy
the radar and make the unit not able to move.
</div>
</div>
</div>
}
/>
)}
tooltipRelativeToParent={true}
>
{[olButtonsRoeHold, olButtonsRoeReturn, olButtonsRoeDesignated].map((icon, idx) => {
return (
<OlButtonGroupItem
key={idx}
onClick={() => {
// TODO: set alarm state
// getApp()
// .getUnitsManager()
// .setROE(ROEs[convertROE(idx)], null, () =>
// setForcedUnitsData({
// ...forcedUnitsData,
// ROE: ROEs[convertROE(idx)],
// })
// );
}}
active={selectedUnitsData.alarmState === alarmStates[idx]}
icon={icon}
/>
);
})}
</OlButtonGroup>
</div>
}
{/* ============== Alarm state selector END ============== */}
{selectedCategories.every((category) => {
return ["Aircraft", "Helicopter"].includes(category);
}) && (

View File

@ -51,7 +51,7 @@ import {
} from "../constants/constants";
import { DataExtractor } from "../server/dataextractor";
import { Weapon } from "../weapon/weapon";
import { Ammo, Contact, GeneralSettings, LoadoutBlueprint, ObjectIconOptions, Offset, Radio, TACAN, UnitBlueprint, UnitData } from "../interfaces";
import { AlarmState, Ammo, Contact, GeneralSettings, LoadoutBlueprint, ObjectIconOptions, Offset, Radio, TACAN, UnitBlueprint, UnitData } from "../interfaces";
import { RangeCircle } from "../map/rangecircle";
import { Group } from "./group";
import { ContextActionSet } from "./contextactionset";
@ -83,7 +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 = '';
#alarmState: AlarmState | undefined = undefined;
#human: boolean = false;
#controlled: boolean = false;
#coalition: string = "neutral";
@ -333,6 +333,9 @@ export abstract class Unit extends CustomMarker {
getRaceTrackBearing() {
return this.#racetrackBearing;
}
getAlarmState() {
return this.#alarmState;
}
static getConstructor(type: string) {
if (type === "GroundUnit") return GroundUnit;
@ -490,7 +493,19 @@ export abstract class Unit extends CustomMarker {
updateMarker = true;
break;
case DataIndexes.radarState:
this.setRadarState(dataExtractor.extractString());
let stringAlarmState = dataExtractor.extractString();
switch (stringAlarmState) {
case 'RED':
this.setRadarState(AlarmState.RED);
break;
case 'GREEN':
this.setRadarState(AlarmState.GREEN);
break;
case '':
this.setRadarState(AlarmState.AUTO);
default:
break;
}
updateMarker = true;
break;
case DataIndexes.human:
@ -755,6 +770,7 @@ export abstract class Unit extends CustomMarker {
racetrackLength: this.#racetrackLength,
racetrackAnchor: this.#racetrackAnchor,
racetrackBearing: this.#racetrackBearing,
alarmState: this.#alarmState
};
}
@ -769,11 +785,11 @@ export abstract class Unit extends CustomMarker {
}
}
setRadarState(newRadarState: string) {
if (newRadarState != this.#radarState) {
this.#radarState = newRadarState;
setRadarState(newRadarState: AlarmState) {
if (newRadarState != this.#alarmState) {
this.#alarmState = newRadarState;
// TODO: check if an event is needed -- surely yes to update the UI
console.log('----radar state updated: ', this.#radarState);
console.log('----radar state updated: ', this.#alarmState);
this.#updateMarker();
}
}
@ -1053,7 +1069,7 @@ export abstract class Unit extends CustomMarker {
}
/* Radar state indicator */
if (this.#radarState !== '') {
if (this.#alarmState) {
var radarStateIcon = document.createElement("div");
radarStateIcon.classList.add("unit-radar-state");
el.append(radarStateIcon);
@ -1587,7 +1603,7 @@ export abstract class Unit extends CustomMarker {
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
/* Set RED/GREEN state*/
if (this.#radarState !== '') element.querySelector(".unit")?.setAttribute("data-radar-state", this.#radarState);
if (this.#alarmState) element.querySelector(".unit")?.setAttribute("data-radar-state", this.#alarmState);
/* Set current unit state */
if (this.#human) {