diff --git a/frontend/react/package.json b/frontend/react/package.json index 1f20b338..269cf4f8 100644 --- a/frontend/react/package.json +++ b/frontend/react/package.json @@ -26,6 +26,7 @@ "leaflet": "^1.9.4", "leaflet-control-mini-map": "^0.4.0", "leaflet-path-drag": "^1.9.5", + "magvar": "^1.1.5", "opus-decoder": "^0.7.6", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts index 978b7fed..1f485702 100644 --- a/frontend/react/src/constants/constants.ts +++ b/frontend/react/src/constants/constants.ts @@ -473,7 +473,7 @@ export namespace ContextActions { export const STOP = new ContextAction( "stop", "Stop unit", - "Stops the unit", + "Stops the unit, removing any currently assigned task. Air units will orbin in place, while ground unit will halt.", faHand, ContextActionTarget.NONE, (units: Unit[], _1, _2) => { @@ -488,7 +488,7 @@ export namespace ContextActions { export const MOVE = new ContextAction( "move", "Set destination", - "Click on the map to move the units there", + "Click on the map to directly move the units there, overriding any existing command.", faLocationDot, ContextActionTarget.POINT, (units: Unit[], _, targetPosition, originalEvent) => { @@ -504,7 +504,7 @@ export namespace ContextActions { export const PATH = new ContextAction( "path", "Create route", - "Click on the map to add a destination to the path", + "Click on the map to add a destination add the end of the path. This allows to create a more complex route.", faRoute, ContextActionTarget.POINT, (units: Unit[], _, targetPosition) => { @@ -519,7 +519,7 @@ export namespace ContextActions { export const DELETE = new ContextAction( "delete", "Delete unit", - "Deletes the unit", + "Deletes the unit immediately with no effect.", faTrash, ContextActionTarget.NONE, (units: Unit[], _1, _2) => { @@ -537,7 +537,7 @@ export namespace ContextActions { export const EXPLODE = new ContextAction( "explode", "Explode unit", - "Explodes the unit", + "Explodes the unit using different explosions effects. WARNING: may affect surrounding units too!", faExplosion, ContextActionTarget.NONE, (units: Unit[], _1, _2) => { @@ -556,7 +556,7 @@ export namespace ContextActions { export const CENTER_MAP = new ContextAction( "center-map", "Center map", - "Center the map on the unit and follow it", + "Center the map on the unit and follow it.", faMapLocation, ContextActionTarget.NONE, (units: Unit[]) => { @@ -568,7 +568,7 @@ export namespace ContextActions { export const REFUEL = new ContextAction( "refuel", "Refuel at tanker", - "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB", + "Refuel units at the nearest Air-to-Air refuelling tanker of the appropriate type. If no tanker is available the unit will return to the nearest base.", olButtonsContextRefuel, ContextActionTarget.NONE, (units: Unit[]) => { @@ -580,7 +580,7 @@ export namespace ContextActions { export const FOLLOW = new ContextAction( "follow", "Follow unit", - "Right-click on a unit to follow it in formation", + "Click on a unit to follow it in formation. A menu allows to choose the formation type.", olButtonsContextFollow, ContextActionTarget.UNIT, (units: Unit[], targetUnit: Unit | null, _) => { @@ -598,7 +598,7 @@ export namespace ContextActions { export const BOMB = new ContextAction( "bomb", "Precision bomb location", - "Right-click on a point to execute a precision bombing attack", + "Click on a point to execute a precision bombing attack with the available A/G weapons.", faLocationCrosshairs, ContextActionTarget.POINT, (units: Unit[], _, targetPosition: LatLng | null) => { @@ -611,7 +611,7 @@ export namespace ContextActions { export const CARPET_BOMB = new ContextAction( "carpet-bomb", "Carpet bomb location", - "Right-click on a point to execute a carpet bombing attack", + "Click on a point to execute a carpet bombing attack with the available A/G weapons.", faXmarksLines, ContextActionTarget.POINT, (units: Unit[], _, targetPosition: LatLng | null) => { @@ -624,7 +624,7 @@ export namespace ContextActions { export const LAND = new ContextAction( "land", "Land", - "Right-click on a point to land at the nearest airbase", + "Click on a point to land at the nearest airbase.", faPlaneArrival, ContextActionTarget.POINT, (units: Unit[], _, targetPosition: LatLng | null) => { @@ -636,7 +636,7 @@ export namespace ContextActions { export const LAND_AT_POINT = new ContextAction( "land-at-point", "Land at location", - "Right-click on a point to land there", + "Click on a point to land there. WARNING: if multiple units are selected make sure to choose a different point for each or the units will crash!", olButtonsContextLandAtPoint, ContextActionTarget.POINT, (units: Unit[], _, targetPosition: LatLng | null) => { @@ -649,7 +649,7 @@ export namespace ContextActions { export const GROUP = new ContextAction( "group-ground", "Group ground units", - "Create a group of ground units", + "Create a DCS group of ground units. This is different from hotgroups or formations and is used primarily to create functioning SAM sites. When a group is created, units will no longer be individually controllable.", faPeopleGroup, ContextActionTarget.NONE, (units: Unit[], _1, _2) => { @@ -661,7 +661,7 @@ export namespace ContextActions { export const ATTACK = new ContextAction( "attack", "Attack unit", - "Right-click on a unit to attack it", + "Click on a unit to attack it using A/A or A/G weapons, depending on the target.", olButtonsContextAttack, ContextActionTarget.UNIT, (units: Unit[], targetUnit: Unit | null, _) => { @@ -673,7 +673,7 @@ export namespace ContextActions { export const FIRE_AT_AREA = new ContextAction( "fire-at-area", "Fire at area", - "Right-click on a point to precisely fire at it (if possible)", + "Click on a point to precisely fire at it, if possible. WARNING: this requires the unit to be able to reach the target and not be obstructed by obstacles.", faLocationCrosshairs, ContextActionTarget.POINT, (units: Unit[], _, targetPosition: LatLng | null) => { @@ -686,7 +686,7 @@ export namespace ContextActions { export const SIMULATE_FIRE_FIGHT = new ContextAction( "simulate-fire-fight", "Simulate fire fight", - "Simulate a fire fight by shooting randomly in a certain large area. WARNING: works correctly only on neutral units, blue or red units will aim", + "Click on a point to simulate a fire fight by shooting randomly in that general direction. WARNING: works correctly only on neutral units, blue or red units will aim", olButtonsContextSimulateFireFight, ContextActionTarget.POINT, (units: Unit[], _, targetPosition: LatLng | null) => { @@ -701,7 +701,7 @@ export namespace ContextActions { export const SET_AWACS_REFERENCE = new ContextAction( "set-awacs-reference", "Set AWACS reference", - "Set unit as AWACS reference", + "Set unit as AWACS reference. BRAA indicators will be shown next to Air unit markers.", faWifi, ContextActionTarget.NONE, (units: Unit[], _1, _2) => { @@ -713,7 +713,7 @@ export namespace ContextActions { export const CLONE = new ContextAction( "clone", "Clone unit", - "Clone the unit at the given location", + "Click on a point to clone the units there.", faClone, ContextActionTarget.POINT, (units: Unit[], _1, targetPosition) => { diff --git a/frontend/react/src/controllers/awacs.ts b/frontend/react/src/controllers/awacs.ts index d8ea48c1..af37d588 100644 --- a/frontend/react/src/controllers/awacs.ts +++ b/frontend/react/src/controllers/awacs.ts @@ -2,7 +2,6 @@ import { getApp } from "../olympusapp"; import { Coalition } from "../types/types"; import { Unit } from "../unit/unit"; import { bearing, coalitionToEnum, computeBearingRangeString, mToFt, rad2deg } from "../other/utils"; -import { TextToSpeechSource } from "../audio/texttospeechsource"; const trackStrings = ["North", "North-East", "East", "South-East", "South", "South-West", "West", "North-West", "North"]; const relTrackStrings = ["hot", "flank right", "beam right", "cold", "cold", "cold", "beam left", "flank left", "hot"]; @@ -49,7 +48,7 @@ export class AWACSController { else if (idx == 2) order = "rd"; let trackDegs = - bearing(group[0].getPosition().lat, group[0].getPosition().lng, referenceUnit.getPosition().lat, referenceUnit.getPosition().lng) - + bearing(group[0].getPosition().lat, group[0].getPosition().lng, referenceUnit.getPosition().lat, referenceUnit.getPosition().lng, true) - rad2deg(group[0].getTrack()); if (trackDegs < 0) trackDegs += 360; if (trackDegs > 360) trackDegs -= 360; diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts index e63e6059..e62048ba 100644 --- a/frontend/react/src/map/map.ts +++ b/frontend/react/src/map/map.ts @@ -243,7 +243,7 @@ export class Map extends L.Map { }, 500); // DCS does not always apply the altitude correctly at the first set when changing map type } - if (options.AWACSMode && this.#layerName !== "AWACS") this.setLayerName("AWACS"); + //TODO if (options.AWACSMode && this.#layerName !== "AWACS") this.setLayerName("AWACS"); this.updateMinimap(); }); diff --git a/frontend/react/src/map/markers/stylesheets/airbase.css b/frontend/react/src/map/markers/stylesheets/airbase.css index 1fa991a9..21fb3a4c 100644 --- a/frontend/react/src/map/markers/stylesheets/airbase.css +++ b/frontend/react/src/map/markers/stylesheets/airbase.css @@ -29,7 +29,7 @@ filter: drop-shadow(0px 2px 0px white) drop-shadow(0px -2px 0px white) drop-shadow(2px 0px 0px white) drop-shadow(-2px 0px 0px white); } -[data-awacs-mode] .airbase-icon svg * { +[todo-todo-data-awacs-mode] .airbase-icon svg * { fill: transparent !important; } diff --git a/frontend/react/src/map/markers/stylesheets/bullseye.css b/frontend/react/src/map/markers/stylesheets/bullseye.css index 35833a37..7e3984e6 100644 --- a/frontend/react/src/map/markers/stylesheets/bullseye.css +++ b/frontend/react/src/map/markers/stylesheets/bullseye.css @@ -21,6 +21,6 @@ stroke: var(--unit-background-neutral); } -[data-awacs-mode] .bullseye-icon svg * { +[todo-todo-data-awacs-mode] .bullseye-icon svg * { fill: transparent !important; } diff --git a/frontend/react/src/map/markers/stylesheets/units.css b/frontend/react/src/map/markers/stylesheets/units.css index 0ad82136..01a3234e 100644 --- a/frontend/react/src/map/markers/stylesheets/units.css +++ b/frontend/react/src/map/markers/stylesheets/units.css @@ -34,11 +34,11 @@ cursor: url("/images/cursors/simulate-fire-fight.svg"), auto !important; } -[data-awacs-mode] .unit-short-label { +[todo-data-awacs-mode] .unit-short-label { color: transparent !important; } -[data-awacs-mode] [data-object|="unit"] svg { +[todo-data-awacs-mode] [data-object|="unit"] svg { scale: 0.5; } @@ -54,19 +54,19 @@ width: var(--unit-vvi-width); } -[data-awacs-mode] .unit-vvi { +[todo-data-awacs-mode] .unit-vvi { width: var(--unit-vvi-width-awacs); } -[data-awacs-mode] [data-coalition="blue"] .unit-vvi { +[todo-data-awacs-mode] [data-coalition="blue"] .unit-vvi { background: var(--unit-background-blue) !important; } -[data-awacs-mode] [data-coalition="red"] .unit-vvi { +[todo-data-awacs-mode] [data-coalition="red"] .unit-vvi { background: var(--unit-background-red) !important; } -[data-awacs-mode] [data-coalition="neutral"] .unit-vvi { +[todo-data-awacs-mode] [data-coalition="neutral"] .unit-vvi { background: var(--unit-background-neutral) !important; } @@ -114,7 +114,7 @@ z-index: -1; } -[data-awacs-mode] [data-is-selected] .unit-icon::before { +[todo-data-awacs-mode] [data-is-selected] .unit-icon::before { display: none; } @@ -139,34 +139,34 @@ stroke: white; } -[data-awacs-mode] [data-coalition="blue"] .unit-icon svg { +[todo-data-awacs-mode] [data-coalition="blue"] .unit-icon svg { fill: transparent !important; stroke: var(--unit-background-blue) !important; } -[data-awacs-mode] [data-coalition="red"] .unit-icon svg { +[todo-data-awacs-mode] [data-coalition="red"] .unit-icon svg { fill: transparent !important; stroke: var(--unit-background-red) !important; } -[data-awacs-mode] [data-coalition="neutral"] .unit-icon svg { +[todo-data-awacs-mode] [data-coalition="neutral"] .unit-icon svg { fill: transparent !important; stroke: var(--unit-background-neutral) !important; } -[data-awacs-mode] [data-is-selected] .unit-icon svg { +[todo-data-awacs-mode] [data-is-selected] .unit-icon svg { stroke: #ff0 !important; } -[data-awacs-mode] [data-is-selected] .unit-vvi { +[todo-data-awacs-mode] [data-is-selected] .unit-vvi { background-color: #ff0 !important; } -[data-awacs-mode] [data-is-selected] .unit-summary { +[todo-data-awacs-mode] [data-is-selected] .unit-summary { color: #ff0 !important; } -[data-awacs-mode] [data-is-selected] .unit-summary::after { +[todo-data-awacs-mode] [data-is-selected] .unit-summary::after { background-color: #ff0 !important; } @@ -244,12 +244,11 @@ /*** Unit summary ***/ [data-object|="unit"] .unit-summary { color: white; - column-gap: 6px; display: flex; flex-wrap: wrap; font-size: 11px; font-weight: bold; - justify-content: start; + align-content: center; line-height: 12px; pointer-events: none; position: absolute; @@ -259,43 +258,44 @@ 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; - width: 100px; - translate: 80px 10px; + width: 80px; + height: 45px; + translate: 60px 2px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north { translate: 50px -45px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-east { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-east { translate: 76px -32px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-east { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-east { translate: 95px 7px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-east { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-east { translate: 79px 50px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south { translate: 50px 63px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-west { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-west { translate: -68px 50px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-west { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-west { translate: -80px 7px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-west { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-west { translate: -69px -35px; } -[data-awacs-mode] .unit-summary::after { +[todo-data-awacs-mode] .unit-summary::after { content: " "; background-color: white; width: 40px; @@ -306,40 +306,40 @@ top: 30px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north::after { transform: rotate(90deg); } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-east::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-east::after { transform: rotate(135deg); translate: 2px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-east::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-east::after { transform: rotate(180deg); translate: -5px -12px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-east::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-east::after { transform: rotate(225deg); translate: -2px -28px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south::after { transform: rotate(270deg); translate: -0px -28px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-west::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-south-west::after { transform: rotate(315deg); translate: 90px -28px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-west::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-west::after { translate: 90px -12px; } -[data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-west::after { +[todo-data-awacs-mode] [data-object|="unit"] .unit-summary.cluster-north-west::after { transform: rotate(45deg); translate: 90px; } @@ -355,10 +355,10 @@ [data-object|="unit"] .unit-summary .unit-callsign { color: white; overflow: hidden; - text-align: right; + text-align: left; transform-origin: right; white-space: nowrap; - width: 80px; + width: 100%; text-overflow: ellipsis; } @@ -367,6 +367,14 @@ overflow: visible; } +[data-object|="unit"] .unit-summary .unit-altitude { + width: 50%; +} + +[data-object|="unit"] .unit-summary .unit-speed { + width: 50%; +} + /*** Common ***/ [data-object|="unit"]:hover .unit-ammo, [data-object|="unit"]:hover .unit-health, @@ -552,21 +560,17 @@ .unit-bullseye, .unit-braa { - display: none; -} - -[data-awacs-mode] .unit-bullseye, -[data-awacs-mode] .unit-braa { + width: 50%; display: inline; } -[data-awacs-mode] [data-object|="unit"] .unit-selected-spotlight, -[data-awacs-mode] [data-object|="unit"] .unit-short-label, -[data-awacs-mode] [data-object|="unit"] .unit-state, -[data-awacs-mode] [data-object|="unit"] .unit-fuel, -[data-awacs-mode] [data-object|="unit"] .unit-health, -[data-awacs-mode] [data-object|="unit"] .unit-ammo, -[data-awacs-mode] [data-object|="unit"]:hover .unit-fuel, -[data-awacs-mode] [data-object|="unit"]:hover .unit-ammo { +[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, +[todo-data-awacs-mode] [data-object|="unit"] .unit-fuel, +[todo-data-awacs-mode] [data-object|="unit"] .unit-health, +[todo-data-awacs-mode] [data-object|="unit"] .unit-ammo, +[todo-data-awacs-mode] [data-object|="unit"]:hover .unit-fuel, +[todo-data-awacs-mode] [data-object|="unit"]:hover .unit-ammo { display: none; } diff --git a/frontend/react/src/map/markers/temporaryunitmarker.ts b/frontend/react/src/map/markers/temporaryunitmarker.ts index b08f39de..723c35f7 100644 --- a/frontend/react/src/map/markers/temporaryunitmarker.ts +++ b/frontend/react/src/map/markers/temporaryunitmarker.ts @@ -56,7 +56,7 @@ export class TemporaryUnitMarker extends CustomMarker { var unitIcon = document.createElement("div"); unitIcon.classList.add("unit-icon"); var img = document.createElement("img"); - img.src = `./images/units/map/${getApp().getMap().getOptions().AWACSMode ? "awacs" : "normal"}/${this.#coalition}/${blueprint.markerFile ?? blueprint.category}.svg`; + img.src = `./images/units/map/${/*TODO getApp().getMap().getOptions().AWACSMode ? "awacs" :*/ "normal"}/${this.#coalition}/${blueprint.markerFile ?? blueprint.category}.svg`; img.onload = () => SVGInjector(img); unitIcon.appendChild(img); unitIcon.toggleAttribute("data-rotate-to-heading", false); diff --git a/frontend/react/src/other/utils.ts b/frontend/react/src/other/utils.ts index c77a30f2..e26f2c17 100644 --- a/frontend/react/src/other/utils.ts +++ b/frontend/react/src/other/utils.ts @@ -1,14 +1,13 @@ import { Circle, LatLng, Polygon } from "leaflet"; import * as turf from "@turf/turf"; -import { UnitDatabase } from "../unit/databases/unitdatabase"; import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants"; -import { DateAndTime, UnitBlueprint } from "../interfaces"; +import { DateAndTime } from "../interfaces"; import { Converter } from "usng"; import { MGRS } from "../types/types"; import { featureCollection } from "turf"; -import { getApp } from "../olympusapp"; +import MagVar from "magvar"; -export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) { +export function bearing(lat1: number, lon1: number, lat2: number, lon2: number, magnetic = true) { const φ1 = deg2rad(lat1); // φ, λ in radians const φ2 = deg2rad(lat2); const λ1 = deg2rad(lon1); // φ, λ in radians @@ -16,7 +15,8 @@ export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) const y = Math.sin(λ2 - λ1) * Math.cos(φ2); const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1); const θ = Math.atan2(y, x); - const brng = (rad2deg(θ) + 360) % 360; // in degrees + const magvar = MagVar.get(lat1, lon1); + const brng = (rad2deg(θ) - (magnetic ? magvar : 0) + 360) % 360; // in degrees return brng; } @@ -406,24 +406,19 @@ export function blobToBase64(blob) { }); } -export function mode(array) -{ - if(array.length == 0) - return null; - var modeMap = {}; - var maxEl = array[0], maxCount = 1; - for(var i = 0; i < array.length; i++) - { - var el = array[i]; - if(modeMap[el] == null) - modeMap[el] = 1; - else - modeMap[el]++; - if(modeMap[el] > maxCount) - { - maxEl = el; - maxCount = modeMap[el]; - } +export function mode(array) { + if (array.length == 0) return null; + var modeMap = {}; + var maxEl = array[0], + maxCount = 1; + for (var i = 0; i < array.length; i++) { + var el = array[i]; + if (modeMap[el] == null) modeMap[el] = 1; + else modeMap[el]++; + if (modeMap[el] > maxCount) { + maxEl = el; + maxCount = modeMap[el]; } - return maxEl; -} \ No newline at end of file + } + return maxEl; +} diff --git a/frontend/react/src/server/servermanager.ts b/frontend/react/src/server/servermanager.ts index 6c6e7051..4c03e40c 100644 --- a/frontend/react/src/server/servermanager.ts +++ b/frontend/react/src/server/servermanager.ts @@ -48,13 +48,13 @@ export class ServerManager { }) MapOptionsChangedEvent.on((mapOptions) => { - if (this.#updateMode === "normal" && mapOptions.AWACSMode) { + /* TODO if (this.#updateMode === "normal" && mapOptions.AWACSMode) { this.#updateMode = "awacs"; this.startUpdate(); } else if (this.#updateMode === "awacs" && !mapOptions.AWACSMode) { this.#updateMode = "normal"; this.startUpdate(); - } + } */ }) } diff --git a/frontend/react/src/ui/panels/coordinatespanel.tsx b/frontend/react/src/ui/panels/coordinatespanel.tsx index 8f0d6e78..1ce683d8 100644 --- a/frontend/react/src/ui/panels/coordinatespanel.tsx +++ b/frontend/react/src/ui/panels/coordinatespanel.tsx @@ -3,7 +3,7 @@ import { OlLocation } from "../components/ollocation"; import { LatLng } from "leaflet"; import { FaBullseye, FaChevronDown, FaChevronUp, FaJetFighter, FaMountain } from "react-icons/fa6"; import { BullseyesDataChanged, MouseMovedEvent, SelectedUnitsChangedEvent } from "../../events"; -import { bearing, mToFt } from "../../other/utils"; +import { bearing, computeBearingRangeString, mToFt } from "../../other/utils"; import { Bullseye } from "../../mission/bullseye"; import { Unit } from "../../unit/unit"; @@ -52,8 +52,7 @@ export function CoordinatesPanel(props: {}) { > {" "} - {bearing(bullseyes[2].getLatLng().lat, bullseyes[2].getLatLng().lng, latlng.lat, latlng.lng).toFixed()}° /{" "} - {(bullseyes[2].getLatLng().distanceTo(latlng) / 1852).toFixed(0)} + {computeBearingRangeString(bullseyes[2].getLatLng(), latlng)}
- {bearing(bullseyes[1].getLatLng().lat, bullseyes[1].getLatLng().lng, latlng.lat, latlng.lng).toFixed()}° /{" "} - {(bullseyes[1].getLatLng().distanceTo(latlng) / 1852).toFixed(0)} + {computeBearingRangeString(bullseyes[1].getLatLng(), latlng)}
{selectedUnits.length == 1 && ( @@ -80,8 +78,7 @@ export function CoordinatesPanel(props: {}) {
{" "} - {bearing(selectedUnits[0].getLatLng().lat, selectedUnits[0].getLatLng().lng, latlng.lat, latlng.lng).toFixed()}° / - {(selectedUnits[0].getLatLng().distanceTo(latlng) / 1852).toFixed(0)} + {computeBearingRangeString(selectedUnits[0].getPosition(), latlng)}
)} diff --git a/frontend/react/src/ui/panels/jtacmenu.tsx b/frontend/react/src/ui/panels/jtacmenu.tsx index e524dc2c..3990f6c9 100644 --- a/frontend/react/src/ui/panels/jtacmenu.tsx +++ b/frontend/react/src/ui/panels/jtacmenu.tsx @@ -39,7 +39,7 @@ export function JTACMenu(props: { open: boolean; onClose: () => void; children?: let IPPosition = ""; if (IP && ECHO) { let dist = Math.round(IP.distanceTo(ECHO) / 1852); - let bear = bearing(point([ECHO.lng, ECHO.lat]), point([IP.lng, IP.lat])); + let bear = bearing(point([ECHO.lng, ECHO.lat]), point([IP.lng, IP.lat])); // TODO switch to mag IPPosition = ["A", "AB", "B", "BC", "C", "CD", "D", "DA"][Math.round((bear > 0 ? bear : bear + 360) / 45)] + String(dist); } @@ -50,7 +50,7 @@ export function JTACMenu(props: { open: boolean; onClose: () => void; children?: let location = targetUnit ? targetUnit.getPosition() : targetLocation; if (location) { IPtoTargetDist = Math.round(IP.distanceTo(location) / 1852); - IPtoTargetBear = bearing(point([IP.lng, IP.lat]), point([location.lng, location.lat])); + IPtoTargetBear = bearing(point([IP.lng, IP.lat]), point([location.lng, location.lat])); // TODO switch to mag if (IPtoTargetBear < 0) IPtoTargetBear += 360; IPtoTargetBear = Math.round(IPtoTargetBear); } diff --git a/frontend/react/src/ui/panels/optionsmenu.tsx b/frontend/react/src/ui/panels/optionsmenu.tsx index 1f00b111..535f0c26 100644 --- a/frontend/react/src/ui/panels/optionsmenu.tsx +++ b/frontend/react/src/ui/panels/optionsmenu.tsx @@ -10,6 +10,7 @@ import { OlAccordion } from "../components/olaccordion"; import { Shortcut } from "../../shortcut/shortcut"; import { OlSearchBar } from "../components/olsearchbar"; import { FaTrash, FaXmark } from "react-icons/fa6"; +import { OlCoalitionToggle } from "../components/olcoalitiontoggle"; const enum Accordion { NONE, @@ -84,81 +85,96 @@ export function OptionsMenu(props: { open: boolean; onClose: () => void; childre >
getApp().getMap().setOption("showUnitLabels", !mapOptions.showUnitLabels)} > {}}> - Show Unit Labels + Show Unit Labels
getApp().getMap().setOption("showUnitsEngagementRings", !mapOptions.showUnitsEngagementRings)} > {}}> - Show Threat Rings + Show Threat Rings
getApp().getMap().setOption("showUnitsAcquisitionRings", !mapOptions.showUnitsAcquisitionRings)} > {}}> - Show Detection rings + Show Detection rings
getApp().getMap().setOption("showUnitTargets", !mapOptions.showUnitTargets)} > {}}> - Show Detection lines + Show Detection lines
getApp().getMap().setOption("hideUnitsShortRangeRings", !mapOptions.hideUnitsShortRangeRings)} > {}}> - Hide Short range Rings + Hide Short range Rings
getApp().getMap().setOption("hideGroupMembers", !mapOptions.hideGroupMembers)} > {}}> - Hide Group members + Hide Group members
getApp().getMap().setOption("showMinimap", !mapOptions.showMinimap)} > {}}> - Show minimap + Show minimap +
+
{ + mapOptions.AWACSCoalition === "blue" && getApp().getMap().setOption("AWACSCoalition", "neutral"); + mapOptions.AWACSCoalition === "neutral" && getApp().getMap().setOption("AWACSCoalition", "red"); + mapOptions.AWACSCoalition === "red" && getApp().getMap().setOption("AWACSCoalition", "blue"); + }} + > + {}} coalition={mapOptions.AWACSCoalition} /> + Coalition of unit bullseye info
diff --git a/frontend/react/src/ui/panels/sidebar.tsx b/frontend/react/src/ui/panels/sidebar.tsx index b4b95783..14fcbeec 100644 --- a/frontend/react/src/ui/panels/sidebar.tsx +++ b/frontend/react/src/ui/panels/sidebar.tsx @@ -77,7 +77,7 @@ export function SideBar() { checked={appState === OlympusState.JTAC} icon={faJ} tooltip="Hide/show JTAC menu" - >{*/} + > { getApp().setState(appState !== OlympusState.AWACS ? OlympusState.AWACS : OlympusState.IDLE); @@ -86,7 +86,7 @@ export function SideBar() { icon={faA} tooltip="Hide/show AWACS menu" tooltipPosition="side" - > + >{*/} { getApp().setState(appState !== OlympusState.GAME_MASTER ? OlympusState.GAME_MASTER : OlympusState.IDLE); diff --git a/frontend/react/src/ui/ui.tsx b/frontend/react/src/ui/ui.tsx index 7f382287..2a0e67ea 100644 --- a/frontend/react/src/ui/ui.tsx +++ b/frontend/react/src/ui/ui.tsx @@ -97,8 +97,8 @@ export function UI() { open={appState === OlympusState.UNIT_CONTROL && appSubState === UnitControlSubState.UNIT_EXPLOSION_MENU} onClose={() => getApp().setState(OlympusState.IDLE)} /> - {/*} getApp().setState(OlympusState.IDLE)} />{*/} - getApp().setState(OlympusState.IDLE)} /> + {/*} getApp().setState(OlympusState.IDLE)} /> + getApp().setState(OlympusState.IDLE)} />{*/} diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index cc42c5e2..6f677aac 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -912,7 +912,7 @@ export abstract class Unit extends CustomMarker { if (this.belongsToCommandedCoalition() || this.getDetectionMethods().some((value) => [VISUAL, OPTIC].includes(value))) marker = this.getBlueprint()?.markerFile ?? this.getDefaultMarker(); else marker = "aircraft"; - img.src = `./images/units/map/${getApp().getMap().getOptions().AWACSMode ? "awacs" : "normal"}/${this.getCoalition()}/${marker}.svg`; + img.src = `./images/units/map/${/*TODO getApp().getMap().getOptions().AWACSMode ? "awacs" : */"normal"}/${this.getCoalition()}/${marker}.svg`; img.onload = () => SVGInjector(img); unitIcon.appendChild(img); @@ -1567,7 +1567,8 @@ export abstract class Unit extends CustomMarker { clusterMean.geometry.coordinates[1], clusterMean.geometry.coordinates[0], this.getPosition().lat, - this.getPosition().lng + this.getPosition().lng, + false ); if (bearingFromCluster < 0) bearingFromCluster += 360; @@ -1581,7 +1582,7 @@ export abstract class Unit extends CustomMarker { } /* Draw the contact trail */ - if (getApp().getMap().getOptions().AWACSMode) { + if (/*TODO getApp().getMap().getOptions().AWACSMode*/ false) { this.#trailPolylines = this.#trailPositions.map( (latlng, idx) => new Polyline([latlng, latlng], { color: "#FFFFFF", opacity: 1 - (idx + 1) / TRAIL_LENGTH }) ); @@ -1668,7 +1669,7 @@ export abstract class Unit extends CustomMarker { var startLatLng = new LatLng(this.#position.lat, this.#position.lng); var endLatLng: LatLng; if (contactData.detectionMethod === RWR) { - var bearingToContact = bearing(this.#position.lat, this.#position.lng, contact.getPosition().lat, contact.getPosition().lng); + var bearingToContact = bearing(this.#position.lat, this.#position.lng, contact.getPosition().lat, contact.getPosition().lng, false); var startXY = getApp().getMap().latLngToContainerPoint(startLatLng); var endX = startXY.x + 80 * Math.sin(deg2rad(bearingToContact)); var endY = startXY.y - 80 * Math.cos(deg2rad(bearingToContact)); @@ -1860,7 +1861,7 @@ export abstract class AirUnit extends Unit { showFuel: belongsToCommandedCoalition, showAmmo: belongsToCommandedCoalition, showSummary: belongsToCommandedCoalition || this.getDetectionMethods().some((value) => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value)), - showCallsign: belongsToCommandedCoalition && (!getApp().getMap().getOptions().AWACSMode || this.getHuman()), + showCallsign: belongsToCommandedCoalition && (/*TODO !getApp().getMap().getOptions().AWACSMode || */ this.getHuman()), rotateToHeading: false, } as ObjectIconOptions; } @@ -1947,7 +1948,7 @@ export class GroundUnit extends Unit { showFuel: false, showAmmo: false, showSummary: false, - showCallsign: belongsToCommandedCoalition && (!getApp().getMap().getOptions().AWACSMode || this.getHuman()), + showCallsign: belongsToCommandedCoalition && (/*TODO !getApp().getMap().getOptions().AWACSMode || */ this.getHuman()), rotateToHeading: false, } as ObjectIconOptions; } @@ -2012,7 +2013,7 @@ export class NavyUnit extends Unit { showFuel: false, showAmmo: false, showSummary: false, - showCallsign: belongsToCommandedCoalition && (!getApp().getMap().getOptions().AWACSMode || this.getHuman()), + showCallsign: belongsToCommandedCoalition && (/*TODO !getApp().getMap().getOptions().AWACSMode || */ this.getHuman()), rotateToHeading: false, } as ObjectIconOptions; } diff --git a/frontend/react/src/weapon/weapon.ts b/frontend/react/src/weapon/weapon.ts index 073ac7a8..2aa0ad03 100644 --- a/frontend/react/src/weapon/weapon.ts +++ b/frontend/react/src/weapon/weapon.ts @@ -178,7 +178,7 @@ export class Weapon extends CustomMarker { var unitIcon = document.createElement("div"); unitIcon.classList.add("unit-icon"); var img = document.createElement("img"); - img.src = `./images/units/map/${getApp().getMap().getOptions().AWACSMode ? "awacs" : "normal"}/${this.getCoalition()}/${this.getMarkerCategory()}.svg`; + img.src = `./images/units/map/${/*TODO getApp().getMap().getOptions().AWACSMode ? "awacs" :*/ "normal"}/${this.getCoalition()}/${this.getMarkerCategory()}.svg`; img.onload = () => SVGInjector(img); unitIcon.appendChild(img); unitIcon.toggleAttribute("data-rotate-to-heading", this.getIconOptions().rotateToHeading); diff --git a/frontend/server/src/routes/api/speech.ts b/frontend/server/src/routes/api/speech.ts index 99d55512..2fd91e94 100644 --- a/frontend/server/src/routes/api/speech.ts +++ b/frontend/server/src/routes/api/speech.ts @@ -22,7 +22,7 @@ module.exports = function () { res.send(response[0].audioContent); res.end() } - ).catch((error) => res.sendStatus(400)); + ).catch((error) => res.sendStatus(404)); }); router.put("/recognize", (req, res, next) => { @@ -46,7 +46,7 @@ module.exports = function () { .map((result) => result.alternatives[0].transcript) .join("\n"); res.send(transcription) - }).catch((error) => res.sendStatus(400)); + }).catch((error) => res.sendStatus(404)); }); return router; diff --git a/frontend/server/src/routes/resources.ts b/frontend/server/src/routes/resources.ts index 3efe8afa..564b137c 100644 --- a/frontend/server/src/routes/resources.ts +++ b/frontend/server/src/routes/resources.ts @@ -138,7 +138,7 @@ module.exports = function (configLocation) { sessionData = {}; res.sendStatus(404); } else { - res.send(sessionData[req.params.profileName]); + res.send(sessionData[req.params.profileName] ?? {}); res.end(); } });