mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
fix: Complete implementation of alarm state
This commit is contained in:
@@ -173,6 +173,7 @@ export interface ObjectIconOptions {
|
||||
showCallsign: boolean;
|
||||
rotateToHeading: boolean;
|
||||
showCluster: boolean;
|
||||
showAlarmState: boolean;
|
||||
}
|
||||
|
||||
export interface GeneralSettings {
|
||||
|
||||
@@ -661,25 +661,31 @@
|
||||
|
||||
/* Unit Radar State */
|
||||
.unit-alarm-state {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
background-repeat: no-repeat;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: transparent;
|
||||
left: 35px;
|
||||
bottom: 8px;
|
||||
bottom: 0px;
|
||||
}
|
||||
.unit[data-alarm-state="green"] .unit-alarm-state {
|
||||
border: 1px solid white;
|
||||
background: rgb(0, 226, 0);
|
||||
background-image: url("/images/states/alarm-state-green.svg");
|
||||
}
|
||||
|
||||
.unit[data-alarm-state="red"] .unit-alarm-state {
|
||||
border: 1px solid white;
|
||||
background: red;
|
||||
background-image: url("/images/states/alarm-state-red.svg");
|
||||
}
|
||||
|
||||
.unit[data-alarm-state="auto"] .unit-alarm-state {
|
||||
border: 0px solid transpare;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[todo-data-awacs-mode] [data-object|="unit"] .unit-selected-spotlight,
|
||||
[todo-data-awacs-mode] [data-object|="unit"] .unit-short-label,
|
||||
[todo-data-awacs-mode] [data-object|="unit"] .unit-state,
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Converter } from "usng";
|
||||
import { MGRS } from "../types/types";
|
||||
import { featureCollection } from "turf";
|
||||
import MagVar from "magvar";
|
||||
import axios from 'axios';
|
||||
import axios from "axios";
|
||||
|
||||
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number, magnetic = true) {
|
||||
const φ1 = deg2rad(lat1); // φ, λ in radians
|
||||
@@ -54,19 +54,19 @@ export function midpoint(lat1: number, lon1: number, lat2: number, lon2: number,
|
||||
const λ2 = deg2rad(lon2); // Convert longitude of point 2 from degrees to radians
|
||||
|
||||
// Convert point 1 to Mercator projection coordinates
|
||||
const x1 = 1 / (2 * Math.PI) * Math.pow(2, zoom) * (Math.PI + λ1);
|
||||
const y1 = 1 / (2 * Math.PI) * Math.pow(2, zoom) * (Math.PI - Math.log(Math.tan(Math.PI / 4 + φ1 / 2)));
|
||||
const x1 = (1 / (2 * Math.PI)) * Math.pow(2, zoom) * (Math.PI + λ1);
|
||||
const y1 = (1 / (2 * Math.PI)) * Math.pow(2, zoom) * (Math.PI - Math.log(Math.tan(Math.PI / 4 + φ1 / 2)));
|
||||
|
||||
// Convert point 2 to Mercator projection coordinates
|
||||
const x2 = 1 / (2 * Math.PI) * Math.pow(2, zoom) * (Math.PI + λ2);
|
||||
const y2 = 1 / (2 * Math.PI) * Math.pow(2, zoom) * (Math.PI - Math.log(Math.tan(Math.PI / 4 + φ2 / 2)));
|
||||
const x2 = (1 / (2 * Math.PI)) * Math.pow(2, zoom) * (Math.PI + λ2);
|
||||
const y2 = (1 / (2 * Math.PI)) * Math.pow(2, zoom) * (Math.PI - Math.log(Math.tan(Math.PI / 4 + φ2 / 2)));
|
||||
|
||||
// Calculate the midpoint in Mercator projection coordinates
|
||||
const mx = (x1 + x2) / 2;
|
||||
const my = (y1 + y2) / 2;
|
||||
|
||||
// Convert the midpoint back to latitude and longitude
|
||||
const λ = (2 * Math.PI * mx / Math.pow(2, zoom)) - Math.PI;
|
||||
const λ = (2 * Math.PI * mx) / Math.pow(2, zoom) - Math.PI;
|
||||
const φ = 2 * Math.atan(Math.exp(Math.PI - (2 * Math.PI * my) / Math.pow(2, zoom))) - Math.PI / 2;
|
||||
|
||||
// Return the midpoint as a LatLng object
|
||||
@@ -277,11 +277,11 @@ export function enumToROE(ROE: number) {
|
||||
|
||||
export function enumToAlarmState(alarmState: number) {
|
||||
switch (alarmState) {
|
||||
case 0:
|
||||
case 2:
|
||||
return AlarmState.RED;
|
||||
case 1:
|
||||
return AlarmState.GREEN;
|
||||
case 2:
|
||||
case 0:
|
||||
return AlarmState.AUTO;
|
||||
default:
|
||||
return AlarmState.AUTO;
|
||||
@@ -524,7 +524,7 @@ export function computeStandardFormationOffset(formation, idx) {
|
||||
var xl = xr * Math.cos(Math.PI / 4) - yr * Math.sin(Math.PI / 4);
|
||||
var yl = xr * Math.sin(Math.PI / 4) + yr * Math.cos(Math.PI / 4);
|
||||
offset = { x: xl * 50, y: yl * 50, z: 0 };
|
||||
offset.z = -Math.sqrt(offset.x * offset.x + offset.y * offset.y) * 0.1
|
||||
offset.z = -Math.sqrt(offset.x * offset.x + offset.y * offset.y) * 0.1;
|
||||
if (yr == 0) {
|
||||
layer++;
|
||||
xr = 0;
|
||||
@@ -560,7 +560,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
|
||||
@@ -568,7 +568,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 };
|
||||
}
|
||||
|
||||
@@ -581,7 +581,7 @@ export function fromDCSFormationOffset(offset: { x: number, y: number, z: number
|
||||
export function adjustBrightness(color, percent) {
|
||||
// Ensure the color is in the correct format
|
||||
if (!/^#[0-9A-F]{6}$/i.test(color)) {
|
||||
throw new Error('Invalid color format. Use #RRGGBB.');
|
||||
throw new Error("Invalid color format. Use #RRGGBB.");
|
||||
}
|
||||
|
||||
// Parse the color components
|
||||
@@ -607,12 +607,12 @@ export function adjustBrightness(color, percent) {
|
||||
export function setOpacity(color, opacity) {
|
||||
// Ensure the color is in the correct format
|
||||
if (!/^#[0-9A-F]{6}$/i.test(color)) {
|
||||
throw new Error('Invalid color format. Use #RRGGBB.');
|
||||
throw new Error("Invalid color format. Use #RRGGBB.");
|
||||
}
|
||||
|
||||
// Ensure the opacity is within the valid range
|
||||
if (opacity < 0 || opacity > 1) {
|
||||
throw new Error('Opacity must be between 0 and 1.');
|
||||
throw new Error("Opacity must be between 0 and 1.");
|
||||
}
|
||||
|
||||
// Parse the color components
|
||||
@@ -632,7 +632,7 @@ export function setOpacity(color, opacity) {
|
||||
export function computeBrightness(color) {
|
||||
// Ensure the color is in the correct format
|
||||
if (!/^#[0-9A-F]{6}$/i.test(color)) {
|
||||
throw new Error('Invalid color format. Use #RRGGBB.');
|
||||
throw new Error("Invalid color format. Use #RRGGBB.");
|
||||
}
|
||||
|
||||
// Parse the color components
|
||||
@@ -673,18 +673,18 @@ export function decimalToRGBA(decimal: number): string {
|
||||
export async function getWikipediaImage(unitName: string): Promise<string | null> {
|
||||
try {
|
||||
// Search for the unit name on Wikipedia
|
||||
const searchResponse = await axios.get('https://en.wikipedia.org/w/api.php', {
|
||||
const searchResponse = await axios.get("https://en.wikipedia.org/w/api.php", {
|
||||
params: {
|
||||
action: 'query',
|
||||
list: 'search',
|
||||
action: "query",
|
||||
list: "search",
|
||||
srsearch: unitName,
|
||||
format: 'json',
|
||||
origin: '*'
|
||||
}
|
||||
format: "json",
|
||||
origin: "*",
|
||||
},
|
||||
});
|
||||
|
||||
if (searchResponse.data.query.search.length === 0) {
|
||||
console.error('No search results found for the unit name.');
|
||||
console.error("No search results found for the unit name.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -692,15 +692,15 @@ export async function getWikipediaImage(unitName: string): Promise<string | null
|
||||
const pageTitle = searchResponse.data.query.search[0].title;
|
||||
|
||||
// Get the page content to find the image
|
||||
const pageResponse = await axios.get('https://en.wikipedia.org/w/api.php', {
|
||||
const pageResponse = await axios.get("https://en.wikipedia.org/w/api.php", {
|
||||
params: {
|
||||
action: 'query',
|
||||
action: "query",
|
||||
titles: pageTitle,
|
||||
prop: 'pageimages',
|
||||
prop: "pageimages",
|
||||
pithumbsize: 500,
|
||||
format: 'json',
|
||||
origin: '*'
|
||||
}
|
||||
format: "json",
|
||||
origin: "*",
|
||||
},
|
||||
});
|
||||
|
||||
const pages = pageResponse.data.query.pages;
|
||||
@@ -710,11 +710,11 @@ export async function getWikipediaImage(unitName: string): Promise<string | null
|
||||
if (page.thumbnail && page.thumbnail.source) {
|
||||
return page.thumbnail.source;
|
||||
} else {
|
||||
console.error('No image found for the unit name.');
|
||||
console.error("No image found for the unit name.");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching data from Wikipedia:', error);
|
||||
console.error("Error fetching data from Wikipedia:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -722,18 +722,18 @@ export async function getWikipediaImage(unitName: string): Promise<string | null
|
||||
export async function getWikipediaSummary(unitName: string): Promise<string | null> {
|
||||
try {
|
||||
// Search for the unit name on Wikipedia
|
||||
const searchResponse = await axios.get('https://en.wikipedia.org/w/api.php', {
|
||||
const searchResponse = await axios.get("https://en.wikipedia.org/w/api.php", {
|
||||
params: {
|
||||
action: 'query',
|
||||
list: 'search',
|
||||
action: "query",
|
||||
list: "search",
|
||||
srsearch: unitName,
|
||||
format: 'json',
|
||||
origin: '*'
|
||||
}
|
||||
format: "json",
|
||||
origin: "*",
|
||||
},
|
||||
});
|
||||
|
||||
if (searchResponse.data.query.search.length === 0) {
|
||||
console.error('No search results found for the unit name.');
|
||||
console.error("No search results found for the unit name.");
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -741,16 +741,16 @@ export async function getWikipediaSummary(unitName: string): Promise<string | nu
|
||||
const pageTitle = searchResponse.data.query.search[0].title;
|
||||
|
||||
// Get the page content to find the summary
|
||||
const pageResponse = await axios.get('https://en.wikipedia.org/w/api.php', {
|
||||
const pageResponse = await axios.get("https://en.wikipedia.org/w/api.php", {
|
||||
params: {
|
||||
action: 'query',
|
||||
prop: 'extracts',
|
||||
action: "query",
|
||||
prop: "extracts",
|
||||
exintro: true,
|
||||
explaintext: true,
|
||||
titles: pageTitle,
|
||||
format: 'json',
|
||||
origin: '*'
|
||||
}
|
||||
format: "json",
|
||||
origin: "*",
|
||||
},
|
||||
});
|
||||
|
||||
const pages = pageResponse.data.query.pages;
|
||||
@@ -760,11 +760,11 @@ export async function getWikipediaSummary(unitName: string): Promise<string | nu
|
||||
if (page.extract) {
|
||||
return page.extract;
|
||||
} else {
|
||||
console.error('No summary found for the unit name.');
|
||||
console.error("No summary found for the unit name.");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching data from Wikipedia:', error);
|
||||
console.error("Error fetching data from Wikipedia:", error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -775,4 +775,4 @@ export function secondsToTimeString(seconds: number) {
|
||||
const secs = Math.floor(seconds % 60);
|
||||
|
||||
return `${zeroPad(hours, 2)}:${zeroPad(minutes, 2)}:${zeroPad(secs, 2)}`;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -8,7 +8,6 @@ import { OlButtonGroup, OlButtonGroupItem } from "../components/olbuttongroup";
|
||||
import { OlCheckbox } from "../components/olcheckbox";
|
||||
import {
|
||||
ROEs,
|
||||
alarmStates,
|
||||
UnitState,
|
||||
altitudeIncrements,
|
||||
emissionsCountermeasures,
|
||||
@@ -20,6 +19,9 @@ import {
|
||||
import { OlToggle } from "../components/oltoggle";
|
||||
import { OlCoalitionToggle } from "../components/olcoalitiontoggle";
|
||||
import {
|
||||
olButtonsAlarmstateAuto,
|
||||
olButtonsAlarmstateGreen,
|
||||
olButtonsAlarmstateRed,
|
||||
olButtonsEmissionsAttack,
|
||||
olButtonsEmissionsDefend,
|
||||
olButtonsEmissionsFree,
|
||||
@@ -890,7 +892,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
{/* ============== Rules of Engagement END ============== */}
|
||||
|
||||
{/* ============== Alarm state selector START ============== */}
|
||||
{selectedUnitsData.alarmState &&
|
||||
{
|
||||
<div className="flex flex-col gap-2">
|
||||
<span
|
||||
className={`
|
||||
@@ -908,9 +910,15 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
<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={olButtonsAlarmstateGreen} 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={`
|
||||
<FontAwesomeIcon icon={olButtonsAlarmstateAuto} className={`
|
||||
my-auto min-w-8 text-white
|
||||
`} />{" "}
|
||||
<div>
|
||||
@@ -918,15 +926,10 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
Auto: The unit will use its sensors to engage based on its ROE.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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={olButtonsRoeHold} className={`
|
||||
<FontAwesomeIcon icon={olButtonsAlarmstateRed} 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.
|
||||
@@ -938,33 +941,21 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
)}
|
||||
tooltipRelativeToParent={true}
|
||||
>
|
||||
{[olButtonsRoeHold, olButtonsRoeReturn, olButtonsRoeDesignated].map((icon, idx) => {
|
||||
|
||||
const getAlarmStateByIdx = (idx) => {
|
||||
switch (idx) {
|
||||
case 0:
|
||||
return AlarmState.AUTO;
|
||||
case 1:
|
||||
return AlarmState.GREEN;
|
||||
case 2:
|
||||
return AlarmState.RED;
|
||||
}
|
||||
}
|
||||
|
||||
{[olButtonsAlarmstateGreen, olButtonsAlarmstateAuto, olButtonsAlarmstateRed].map((icon, idx) => {
|
||||
return (
|
||||
<OlButtonGroupItem
|
||||
key={idx}
|
||||
onClick={() => {
|
||||
getApp()
|
||||
.getUnitsManager()
|
||||
.setAlarmState(idx, null, () =>
|
||||
.setAlarmState([1, 0, 2][idx], null, () =>
|
||||
setForcedUnitsData({
|
||||
...forcedUnitsData,
|
||||
alarmState: getAlarmStateByIdx(idx),
|
||||
alarmState: [AlarmState.GREEN, AlarmState.AUTO, AlarmState.RED][idx],
|
||||
})
|
||||
);
|
||||
}}
|
||||
active={selectedUnitsData.alarmState === getAlarmStateByIdx(idx)}
|
||||
active={selectedUnitsData.alarmState === [AlarmState.GREEN, AlarmState.AUTO, AlarmState.RED][idx]}
|
||||
icon={icon}
|
||||
/>
|
||||
);
|
||||
@@ -1430,7 +1421,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
{/* ============== Miss on purpose toggle END ============== */}
|
||||
<div className="flex gap-4">
|
||||
{/* ============== Shots scatter START ============== */}
|
||||
<div className={`flex flex-col gap-2`}>
|
||||
<div className={`flex w-full justify-between gap-2`}>
|
||||
<span
|
||||
className={`
|
||||
my-auto font-normal
|
||||
@@ -1463,7 +1454,7 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
</div>
|
||||
{/* ============== Shots scatter END ============== */}
|
||||
{/* ============== Shots intensity START ============== */}
|
||||
<div className="flex flex-col gap-2">
|
||||
{/*<div className="flex flex-col gap-2">
|
||||
<span
|
||||
className={`
|
||||
my-auto font-normal
|
||||
@@ -1495,12 +1486,13 @@ export function UnitControlMenu(props: { open: boolean; onClose: () => void }) {
|
||||
</OlButtonGroup>
|
||||
</div>
|
||||
{/* ============== Shots intensity END ============== */}
|
||||
<OlStateButton
|
||||
{/*<OlStateButton
|
||||
className="mt-auto"
|
||||
checked={showEngagementSettings}
|
||||
onClick={() => setShowEngagementSettings(!showEngagementSettings)}
|
||||
icon={faCog}
|
||||
></OlStateButton>
|
||||
*/}
|
||||
</div>
|
||||
{/* ============== Operate as toggle START ============== */}
|
||||
{selectedUnits.every((unit) => unit.getCoalition() === "neutral") && (
|
||||
|
||||
@@ -1190,10 +1190,10 @@ export abstract class Unit extends CustomMarker {
|
||||
el.append(healthIndicator);
|
||||
}
|
||||
|
||||
/* Radar state indicator */
|
||||
if (this.#alarmState) {
|
||||
/* Alarm state indicator */
|
||||
if (iconOptions.showAlarmState) {
|
||||
var alarmStateIcon = document.createElement("div");
|
||||
alarmStateIcon.classList.add("unit-radar-state");
|
||||
alarmStateIcon.classList.add("unit-alarm-state");
|
||||
el.append(alarmStateIcon);
|
||||
}
|
||||
|
||||
@@ -1774,8 +1774,7 @@ export abstract class Unit extends CustomMarker {
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
|
||||
|
||||
/* Set radar state*/
|
||||
// if (this.#radarState !== undefined) element.querySelector(".unit")?.setAttribute("data-radar-state", (this.#radarState === true ? 'on' : 'off'));
|
||||
if (this.#alarmState !== AlarmState.AUTO) element.querySelector(".unit")?.setAttribute("data-alarm-state", (this.#alarmState === AlarmState.RED ? 'red' : 'green'));
|
||||
element.querySelector(".unit")?.setAttribute("data-alarm-state", this.#alarmState);
|
||||
|
||||
/* Set current unit state */
|
||||
if (this.#human) {
|
||||
@@ -2423,6 +2422,7 @@ export abstract class AirUnit extends Unit {
|
||||
showCallsign: belongsToCommandedCoalition && /*TODO !getApp().getMap().getOptions().AWACSMode || */ this.getHuman(),
|
||||
rotateToHeading: false,
|
||||
showCluster: false,
|
||||
showAlarmState: false
|
||||
} as ObjectIconOptions;
|
||||
}
|
||||
|
||||
@@ -2511,6 +2511,7 @@ export class GroundUnit extends Unit {
|
||||
showCallsign: belongsToCommandedCoalition && /*TODO !getApp().getMap().getOptions().AWACSMode || */ this.getHuman(),
|
||||
rotateToHeading: false,
|
||||
showCluster: true,
|
||||
showAlarmState: true,
|
||||
} as ObjectIconOptions;
|
||||
}
|
||||
|
||||
@@ -2578,6 +2579,7 @@ export class NavyUnit extends Unit {
|
||||
showCallsign: belongsToCommandedCoalition && /*TODO !getApp().getMap().getOptions().AWACSMode || */ this.getHuman(),
|
||||
rotateToHeading: false,
|
||||
showCluster: false,
|
||||
showAlarmState: true
|
||||
} as ObjectIconOptions;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user