Merge branch 'Minor-bugfixes' into Advanced-spawn-and-control-options

This commit is contained in:
Pax1601 2023-06-06 12:52:02 +02:00
commit f84ff1c7b2
22 changed files with 412 additions and 222 deletions

View File

@ -1,12 +1,12 @@
{
"name": "DCSOlympus",
"version": "v0.2.1-alpha",
"version": "v0.3.0-alpha",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "DCSOlympus",
"version": "v0.2.1-alpha",
"version": "v0.3.0-alpha",
"dependencies": {
"@types/geojson": "^7946.0.10",
"@types/leaflet": "^1.9.0",

View File

@ -2,7 +2,7 @@
"name": "DCSOlympus",
"node-main": "./bin/www",
"main": "http://localhost:3000",
"version": "v0.2.1-alpha",
"version": "v0.3.0-alpha",
"private": true,
"scripts": {
"copy": "copy.bat",

View File

@ -14,6 +14,10 @@
z-index: 1000;
}
#app-icon>.ol-select-options {
width: fit-content;
}
#toolbar-summary {
background-image: url("/images/icon-round.png");
background-position: 20px 22px;
@ -25,6 +29,10 @@
text-indent: 60px;
}
#toolbar-summary {
white-space: nowrap;
}
#connection-status-panel {
bottom: 20px;
font-size: 12px;

View File

@ -574,9 +574,9 @@ nav.ol-panel> :last-child {
.ol-measure-box {
background-color: var(--background-steel);
border-radius: 999px;
color: var(--primary-neutral);
color: var(--background-offwhite);
font-size: 12px;
font-weight: var(--font-weight-bolder);
font-weight: bolder;
height: fit-content;
padding-bottom: 0.2em;
padding-left: 0.5em;
@ -586,6 +586,7 @@ nav.ol-panel> :last-child {
text-align: center;
width: fit-content;
z-index: 2000;
pointer-events: none;
}
.ol-sortable .handle {
@ -910,18 +911,6 @@ dl.ol-data-grid dd {
margin-left: auto;
}
.br-info::after {
content: attr(data-bearing) '\00B0 / ' attr(data-distance) attr(data-distance-units);
}
.br-info[data-message]::after {
content: attr(data-message);
}
.coordinates::after {
content: attr(data-dd) "\00b0 " attr(data-mm) "'" attr(data-ss) "." attr(data-sss) '"' attr(data-label);
}
.ol-button-box {
column-gap: 6px;
display: flex;

View File

@ -4,7 +4,7 @@
height: fit-content;
position: absolute;
row-gap: 5px;
width: 230px;
width: 280px;
z-index: 9999;
}
@ -109,6 +109,11 @@
background-size: 48px;
}
#explosion-spawn-button {
background-image: url("/resources/theme/images/buttons/spawn/explosion.svg");
background-size: 48px;
}
.unit-spawn-button {
border: none;
border-radius: 0px;
@ -208,6 +213,7 @@
text-align: center;
}
#explosion-menu>button,
#smoke-spawn-menu>button {
align-items: center;
column-gap: 10px;

View File

@ -6,11 +6,11 @@
#mouse-info-panel dl {
margin-bottom: 4px;
row-gap: 8px;
row-gap: 5px;
}
#mouse-info-panel dt {
height: 20px;
height: fit-content;
width: 30%;
}
@ -30,20 +30,22 @@
width: 16px;
}
#mouse-info-panel dt#ref-unit-position::after {
background-image: url("/resources/theme/images/icons/ruler.svg");
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: 16px 16px;
content: "";
#mouse-info-panel #measuring-tool dt {
height: 24px;
width: 24px;
background-color: var(--background-offwhite);
border-radius: var(--border-radius-sm);
}
#mouse-info-panel dt#ref-measure-position::after {
background-image: url("/resources/theme/images/icons/pin.png");
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: 16px 16px;
content: "";
#mouse-info-panel #measuring-tool svg {
padding: 3px;
height: 100%;
width: 100%;
}
#mouse-info-panel #measuring-tool dt svg>* {
fill: black;
stroke: black;
}
#mouse-info-panel dt[data-label]::after {
@ -72,4 +74,36 @@
#mouse-info-panel dd {
width: 70%;
}
.br-info::after {
content: attr(data-bearing) '\00B0 / ' attr(data-distance) " " attr(data-distance-units);
font-weight: bold;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--background-offwhite);
}
.br-info[data-coalition="blue"]::after {
color: var(--primary-blue)
}
.br-info[data-coalition="red"]::after {
color: var(--primary-red)
}
.br-info[data-message]::after {
content: attr(data-message);
}
.coordinates::after {
content: attr(data-dd) "\00b0 " attr(data-mm) "'" attr(data-ss) "." attr(data-sss) '"' attr(data-label);
font-weight: bold;
font-size: 13px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--background-offwhite);
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 32 31.999584"
version="1.1"
id="svg4"
sodipodi:docname="explosion.svg"
width="32"
height="31.999584"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="13.078125"
inkscape:cx="19.53644"
inkscape:cy="12.157706"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="m 23.013304,7.2180185 c 0.209962,-0.3739974 0.64242,-0.5068189 0.993401,-0.2971008 0.350981,0.2097182 0.495133,0.6815841 0.332178,1.0835439 L 20.042501,18.658144 c 0.06894,0.08039 0.134753,0.164279 0.197427,0.248166 l 3.046013,-1.911931 c 0.329044,-0.206222 0.739567,-0.108354 0.968331,0.2237 0.228764,0.332054 0.197427,0.803919 -0.06894,1.10102 l -2.726369,3.040913 h -2.240637 c -0.413656,-1.303747 -1.52614,-2.236993 -2.836051,-2.236993 -1.309911,0 -2.425529,0.933246 -2.83605,2.236993 H 11.048617 L 8.6826296,19.535465 C 8.3786552,19.30128 8.2721074,18.850386 8.4350628,18.476388 8.5980181,18.102391 8.9803366,17.920635 9.3344512,18.042971 l 3.0460128,1.062571 c 0.09401,-0.136317 0.191159,-0.269138 0.294573,-0.394969 L 10.722706,15.08245 c -0.191159,-0.353027 -0.122217,-0.807416 0.159822,-1.073059 0.282038,-0.265643 0.695694,-0.262148 0.974598,0.0034 l 3.208969,3.072372 c 0.047,-0.01398 0.09401,-0.02796 0.141019,-0.03845 l 0.426191,-4.987745 c 0.0376,-0.429921 0.360382,-0.75848 0.748968,-0.75848 0.388586,0 0.711362,0.328559 0.748967,0.75848 l 0.423058,4.959835 z M 9.362655,22.482004 v 0 H 23.40189 v 0 h 1.002802 c 0.554675,0 1.002803,0.499829 1.002803,1.118498 0,0.618669 -0.448128,1.118496 -1.002803,1.118496 H 8.3598525 c -0.554675,0 -1.0028024,-0.499827 -1.0028024,-1.118496 0,-0.618669 0.4481274,-1.118498 1.0028024,-1.118498 z M 16.382273,6.8230493 c 0.416789,0 0.752101,0.3739974 0.752101,0.8388726 v 1.6777453 c 0,0.4648752 -0.335312,0.8388728 -0.752101,0.8388728 -0.41679,0 -0.752102,-0.3739976 -0.752102,-0.8388728 V 7.6619219 c 0,-0.4648752 0.335312,-0.8388726 0.752102,-0.8388726 z"
fill="#ffffff"
stroke="#ffffff"
id="path2"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0330959" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="35"
height="35"
viewBox="0 0 35 35"
fill="none"
version="1.1"
id="svg4"
sodipodi:docname="pin.svg"
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="23.914286"
inkscape:cx="19.967145"
inkscape:cy="20.573477"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<path
d="m 26.00224,11.876792 c 0,4.375 -3.5547,7.875001 -7.875,7.875001 -4.375,0 -7.875,-3.500001 -7.875,-7.875001 0,-4.32031 3.5,-7.875 7.875,-7.875 4.3203,0 7.875,3.55469 7.875,7.875 z m -9.625,18.375001 v -8.8594 c 0.5469,0.0547 1.1484,0.1094 1.75,0.1094 0.5469,0 1.1484,-0.0547 1.75,-0.1094 v 8.8594 c 0,0.9844 -0.8203,1.75 -1.75,1.75 -0.9844,0 -1.75,-0.7656 -1.75,-1.75 z"
fill="#247BE2"
id="path2-3"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1;stroke-width:4;stroke-dasharray:none"
sodipodi:nodetypes="sssssscscsss" />
<path
d="M26 11.875C26 16.25 22.4453 19.75 18.125 19.75C13.75 19.75 10.25 16.25 10.25 11.875C10.25 7.55469 13.75 4 18.125 4C22.4453 4 26 7.55469 26 11.875ZM18.125 8.375C18.5625 8.375 19 7.99219 19 7.5C19 7.0625 18.5625 6.625 18.125 6.625C15.2266 6.625 12.875 8.97656 12.875 11.875C12.875 12.3672 13.2578 12.75 13.75 12.75C14.1875 12.75 14.625 12.3672 14.625 11.875C14.625 9.96094 16.1562 8.375 18.125 8.375ZM16.375 30.25V21.3906C16.9219 21.4453 17.5234 21.5 18.125 21.5C18.6719 21.5 19.2734 21.4453 19.875 21.3906V30.25C19.875 31.2344 19.0547 32 18.125 32C17.1406 32 16.375 31.2344 16.375 30.25Z"
fill="#247BE2"
id="path2" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,3 @@
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M27.418 14C29.2109 14 32.375 15.5293 32.375 17.375C32.375 19.2734 29.2109 20.75 27.418 20.75H21.248L15.9746 30.0312C15.6582 30.5586 15.0781 30.875 14.498 30.875H11.5449C10.9648 30.875 10.543 30.3477 10.7012 29.8203L13.2852 20.75H7.90625L5.58594 23.8086C5.42773 24.0195 5.2168 24.125 4.95312 24.125H2.73828C2.31641 24.125 2 23.8086 2 23.3867C2 23.334 2 23.2812 2 23.2285L3.6875 17.375L2 11.5742C2 11.5215 2 11.4688 2 11.3633C2 10.9941 2.31641 10.625 2.73828 10.625H4.95312C5.2168 10.625 5.42773 10.7832 5.58594 10.9941L7.90625 14H13.2852L10.7012 4.98242C10.543 4.45508 10.9648 3.875 11.5449 3.875H14.498C15.0781 3.875 15.6582 4.24414 15.9746 4.77148L21.248 14H27.418Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 795 B

View File

@ -1,6 +1,6 @@
import { LatLng } from "leaflet";
import { getActiveCoalition, getMap, setActiveCoalition } from "..";
import { spawnAircraft, spawnGroundUnit, spawnSmoke } from "../server/server";
import { spawnAircraft, spawnExplosion, spawnGroundUnit, spawnSmoke } from "../server/server";
import { aircraftDatabase } from "../units/aircraftdatabase";
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
import { ContextMenu } from "./contextmenu";
@ -39,6 +39,9 @@ export class MapContextMenu extends ContextMenu {
this.#aircraftTypeDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftType(type));
this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout));
this.#aircrafSpawnAltitudeSlider = new Slider("aircraft-spawn-altitude-slider", 0, 50000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);});
this.#aircrafSpawnAltitudeSlider.setIncrement(500);
this.#aircrafSpawnAltitudeSlider.setValue(20000);
this.#aircrafSpawnAltitudeSlider.setActive(true);
this.#groundUnitRoleDropdown = new Dropdown("ground-unit-role-options", (role: string) => this.#setGroundUnitRole(role));
this.#groundUnitTypeDropdown = new Dropdown("ground-unit-type-options", (type: string) => this.#setGroundUnitType(type));
@ -69,9 +72,11 @@ export class MapContextMenu extends ContextMenu {
spawnSmoke(e.detail.color, this.getLatLng());
});
this.#aircrafSpawnAltitudeSlider.setIncrement(500);
this.#aircrafSpawnAltitudeSlider.setValue(20000);
this.#aircrafSpawnAltitudeSlider.setActive(true);
document.addEventListener("contextMenuExplosion", (e: any) => {
this.hide();
spawnExplosion(e.detail.strength, this.getLatLng());
});
this.hide();
}
@ -90,6 +95,8 @@ export class MapContextMenu extends ContextMenu {
this.getContainer()?.querySelector("#ground-unit-spawn-button")?.classList.toggle("is-open", type === "ground-unit");
this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", type !== "smoke");
this.getContainer()?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", type === "smoke");
this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", type !== "explosion");
this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", type === "explosion");
this.#resetAircraftRole();
this.#resetAircraftType();

View File

@ -21,7 +21,7 @@ require("../../public/javascripts/leaflet.nauticscale.js")
/* Map constants */
export const IDLE = "IDLE";
export const MOVE_UNIT = "MOVE_UNIT";
export const UNIT_SELECTED = "MOVE_UNIT";
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
export const visibilityControlsTootlips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"];
@ -159,7 +159,7 @@ export class Map extends L.Map {
this.#computeDestinationRotation = false;
this.#destinationRotationCenter = null;
}
else if (this.#state === MOVE_UNIT) {
else if (this.#state === UNIT_SELECTED) {
/* Remove all the exising destination preview markers */
this.#destinationPreviewMarkers.forEach((marker: L.Marker) => {
this.removeLayer(marker);
@ -363,7 +363,7 @@ export class Map extends L.Map {
if (this.#state === IDLE) {
}
else if (this.#state === MOVE_UNIT) {
else if (this.#state === UNIT_SELECTED) {
this.setState(IDLE);
getUnitsManager().deselectAllUnits();
}
@ -381,17 +381,52 @@ export class Map extends L.Map {
this.showMapContextMenu(e);
}
}
else if (this.#state === MOVE_UNIT) {
if (!e.originalEvent.ctrlKey) {
getUnitsManager().selectedUnitsClearDestinations();
else if (this.#state === UNIT_SELECTED) {
if (e.originalEvent.shiftKey) {
var options: {[key: string]: {text: string, tooltip: string}} = {};
var selectedUnitTypes = getUnitsManager().getSelectedUnitsTypes();
if (selectedUnitTypes.length === 1 && ["Aircraft"].includes(selectedUnitTypes[0]))
{
options["bomb"] = {text: "Bomb here", tooltip: "Precision bombing of this specific point"};
options["carpet-bomb"] = {text: "Carpet bomb", tooltip: "Carpet bombing around this point"};
options["building-bomb"] = {text: "Bomb building", tooltip: "Precision bombing of the building closest to this point"};
}
if (selectedUnitTypes.length === 1 && ["GroundUnit"].includes(selectedUnitTypes[0]))
options["fire-at-area"] = {text: "Fire at area", tooltip: "Fire at this point"};
if (Object.keys(options).length > 0) {
this.showUnitContextMenu(e);
this.getUnitContextMenu().setOptions(options, (option: string) => {
this.hideUnitContextMenu();
this.#executeAction(e, option);
});
}
} else {
if (!e.originalEvent.ctrlKey) {
getUnitsManager().selectedUnitsClearDestinations();
}
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, e.originalEvent.shiftKey, this.#destinationGroupRotation)
this.#destinationGroupRotation = 0;
this.#destinationRotationCenter = null;
this.#computeDestinationRotation = false;
}
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, e.originalEvent.shiftKey, this.#destinationGroupRotation)
this.#destinationGroupRotation = 0;
this.#destinationRotationCenter = null;
this.#computeDestinationRotation = false;
}
}
#executeAction(e: any, action: string) {
if (action === "bomb")
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
else if (action === "carpet-bomb")
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
else if (action === "building-bomb")
getUnitsManager().selectedUnitsBombBuilding(this.getMouseCoordinates());
else if (action === "fire-at-area")
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
}
#onSelectionEnd(e: any) {
clearTimeout(this.#leftClickTimer);
this.#preventLeftClick = true;
@ -404,7 +439,7 @@ export class Map extends L.Map {
#onMouseDown(e: any) {
this.hideAllContextMenus();
if (this.#state == MOVE_UNIT) {
if (this.#state == UNIT_SELECTED) {
this.#destinationGroupRotation = 0;
this.#destinationRotationCenter = null;
this.#computeDestinationRotation = false;

View File

@ -14,8 +14,8 @@ export class MouseInfoPanel extends Panel {
constructor(ID: string) {
super(ID);
this.#measureIcon = new Icon({ iconUrl: 'images/pin.png', iconAnchor: [16, 32]});
this.#measureMarker = new Marker([0, 0], {icon: this.#measureIcon, interactive: false});
this.#measureIcon = new Icon({ iconUrl: 'resources/theme/images/icons/pin.svg', iconAnchor: [16, 32] });
this.#measureMarker = new Marker([0, 0], { icon: this.#measureIcon, interactive: false });
this.#measureBox = document.createElement("div");
this.#measureBox.classList.add("ol-measure-box", "hide");
@ -25,109 +25,36 @@ export class MouseInfoPanel extends Panel {
getMap()?.on('zoom', (e: any) => this.#onZoom(e));
getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e));
document.addEventListener('unitsSelection', (e: CustomEvent<Unit[]>) => this.#onUnitsSelection(e.detail));
document.addEventListener('clearSelection', () => this.#onClearSelection());
document.addEventListener('unitsSelection', (e: CustomEvent<Unit[]>) => this.#update());
document.addEventListener('clearSelection', () => this.#update());
}
#update(mousePosition: LatLng, measurePosition: LatLng | null, unitPosition: LatLng | null) {
#update() {
const mousePosition = getMap().getMouseCoordinates();
var selectedUnitPosition = null;
var selectedUnits = getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1)
selectedUnitPosition = new LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude);
/* Draw measures from selected unit, from pin location, and from bullseyes */
this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition);
this.#drawMeasure("ref-unit-position", "unit-position", selectedUnitPosition, mousePosition);
this.getElement().querySelector(`#measuring-tool`)?.classList.toggle("hide", this.#measurePoint === null && selectedUnitPosition === null);
var bullseyes = getMissionData().getBullseyes();
for (let idx in bullseyes)
{
var el = <HTMLElement>this.getElement().querySelector(`#bullseye-${idx}`);
this.#drawMeasure(null, `bullseye-${idx}`, bullseyes[idx].getLatLng(), mousePosition);
if ( el != null ) {
var dist = distance(bullseyes[idx].getLatLng().lat, bullseyes[idx].getLatLng().lng, mousePosition.lat, mousePosition.lng);
var bear = bearing(bullseyes[idx].getLatLng().lat, bullseyes[idx].getLatLng().lng, mousePosition.lat, mousePosition.lng);
let bng = zeroAppend(Math.floor(bear), 3);
if ( bng === "000" ) {
bng = "360";
}
el.dataset.bearing = bng;
el.dataset.distance = zeroAppend(Math.floor(dist*0.000539957), 3);
el.dataset.distanceUnits = "NM";
}
}
if (measurePosition) {
var el = <HTMLElement>this.getElement().querySelector(`#measure-position`);
if (el != null) {
var bear = bearing(measurePosition.lat, measurePosition.lng, mousePosition.lat, mousePosition.lng);
var dist = distance(measurePosition.lat, measurePosition.lng, mousePosition.lat, mousePosition.lng);
let bng = zeroAppend(Math.floor(bear), 3);
if ( bng === "000" ) {
bng = "360";
}
el.dataset.bearing = bng;
el.dataset.distance = zeroAppend(Math.floor(dist*0.000539957), 3);
el.dataset.distanceUnits = "NM";
}
}
if (unitPosition) {
var el = <HTMLElement>this.getElement().querySelector(`#unit-position`);
if (el != null) {
var dist = distance(unitPosition.lat, unitPosition.lng, mousePosition.lat, mousePosition.lng);
var bear = bearing(unitPosition.lat, unitPosition.lng, mousePosition.lat, mousePosition.lng);
el.dataset.bearing = zeroAppend(Math.floor(bear), 3);
el.dataset.distance = zeroAppend(Math.floor(dist*0.000539957), 3);
el.dataset.distanceUnits = "NM";
}
}
const refMouseLat = <HTMLElement>document.getElementById( "ref-mouse-position-latitude" );
const mouseLat = <HTMLElement>document.getElementById( "mouse-position-latitude" );
if ( refMouseLat && mouseLat ) {
let matches = String( mousePosition.lat ).match( /^\-?(\d+)\.(\d{2})(\d{2})(\d{2})/ );
if ( matches && matches.length ) {
mouseLat.dataset.dd = matches[1];
mouseLat.dataset.mm = matches[2];
mouseLat.dataset.ss = matches[3];
mouseLat.dataset.sss = matches[4];
}
refMouseLat.dataset.label = ( mousePosition.lat < 0 ) ? "S" : "N";
}
const refMouseLng = <HTMLElement>document.getElementById( "ref-mouse-position-longitude" );
const mouseLng = <HTMLElement>document.getElementById( "mouse-position-longitude" );
if ( refMouseLng && mouseLng ) {
let matches = String( mousePosition.lng ).match( /^\-?(\d+)\.(\d{2})(\d{2})(\d{2})/ );
if ( matches && matches.length ) {
mouseLng.dataset.dd = matches[1];
mouseLng.dataset.mm = matches[2];
mouseLng.dataset.ss = matches[3];
mouseLng.dataset.sss = matches[4];
}
refMouseLng.dataset.label = ( mousePosition.lng < 0 ) ? "W" : "E";
}
/* Draw coordinates */
this.#drawCoordinates("ref-mouse-position-latitude", "mouse-position-latitude", mousePosition.lat, ["N", "S"]);
this.#drawCoordinates("ref-mouse-position-longitude", "mouse-position-longitude", mousePosition.lng, ["E", "W"]);
}
#onMapClick(e: any)
{
if (e.originalEvent.ctrlKey)
{
if (!this.#measurePoint)
{
#onMapClick(e: any) {
if (e.originalEvent.ctrlKey) {
if (!this.#measurePoint) {
this.#measureBox.classList.toggle("hide", false);
this.#measurePoint = e.latlng;
this.#measureMarker.setLatLng(e.latlng);
@ -135,8 +62,7 @@ export class MouseInfoPanel extends Panel {
if (!getMap().hasLayer(this.#measureLine))
this.#measureLine.addTo(getMap());
}
else
{
else {
this.#measureBox.classList.toggle("hide", true);
this.#measurePoint = null;
if (getMap().hasLayer(this.#measureMarker))
@ -147,13 +73,13 @@ export class MouseInfoPanel extends Panel {
getMap().removeLayer(this.#measureLine);
}
}
this.#update();
}
#drawMeasureLine()
{
#drawMeasureLine() {
var mouseLatLng = getMap().containerPointToLatLng(getMap().getMousePosition());
if (this.#measurePoint != null)
{
if (this.#measurePoint != null) {
var points = [this.#measurePoint, mouseLatLng];
this.#measureLine.setLatLngs(points);
var dist = distance(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
@ -163,74 +89,79 @@ export class MouseInfoPanel extends Panel {
var dy = (getMap().getMousePosition().y - startXY.y);
var angle = Math.atan2(dy, dx);
if (angle > Math.PI / 2)
if (angle > Math.PI / 2)
angle = angle - Math.PI;
if (angle < -Math.PI / 2)
if (angle < -Math.PI / 2)
angle = angle + Math.PI;
let bng = zeroAppend(Math.floor(bear), 3);
const reciprocal = zeroAppend( reciprocalHeading( parseInt( bng ) ), 3 );
if ( bng === "000" ) {
if (bng === "000")
bng = "360";
}
let data = [ `${bng}°`, `${Math.floor(dist*0.000539957)}NM`, `${reciprocal}°` ];
let data = [`${bng}°`, `${Math.floor(dist * 0.000539957)} NM`];
if ( bear < 180 ) {
data = data.reverse();
}
this.#measureBox.innerText = data.join( " | " );
this.#measureBox.innerText = data.join(" / ");
this.#measureBox.style.left = (getMap().getMousePosition().x + startXY.x) / 2 - this.#measureBox.offsetWidth / 2 + "px";
this.#measureBox.style.top = (getMap().getMousePosition().y + startXY.y) / 2 - this.#measureBox.offsetHeight / 2 + "px";
this.#measureBox.style.rotate = angle + "rad";
}
}
#onMouseMove(e: any)
{
var selectedUnitPosition = null;
var selectedUnits = getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1)
selectedUnitPosition = new LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude);
this.#update(<LatLng>e.latlng, this.#measurePoint, selectedUnitPosition);
this.#drawMeasureLine();
}
#onZoom(e: any)
{
this.#drawMeasureLine();
}
#onUnitsSelection(units: Unit[])
{
const pos = this.getElement().querySelector(`#unit-position`);
#onMouseMove(e: any) {
if ( units.length > 1 ) {
pos?.setAttribute( "data-message", "(multiple units)" );
} else {
pos?.removeAttribute( "data-message" );
this.#update();
this.#drawMeasureLine();
}
#onZoom(e: any) {
this.#drawMeasureLine();
}
#drawMeasure(imgId: string | null, textId: string, value: LatLng | null, mousePosition: LatLng) {
var el = this.getElement().querySelector(`#${textId}`) as HTMLElement;
var img = imgId != null ? this.getElement().querySelector(`#${imgId}`) as HTMLElement : null;
if (value) {
if (el != null) {
el.classList.remove("hide");
var bear = bearing(value.lat, value.lng, mousePosition.lat, mousePosition.lng);
var dist = distance(value.lat, value.lng, mousePosition.lat, mousePosition.lng);
let bng = zeroAppend(Math.floor(bear), 3);
if (bng === "000")
bng = "360";
el.dataset.bearing = bng;
el.dataset.distance = zeroAppend(Math.floor(dist * 0.000539957), 3);
el.dataset.distanceUnits = "NM";
}
if (img != null)
img.classList.remove("hide");
}
else {
if (el != null)
el.classList.add("hide");
if (img != null)
img.classList.add("hide");
}
}
#onClearSelection()
{
this.#measureBox.classList.toggle("hide", true);
const pos = this.getElement().querySelector(`#unit-position`);
if ( pos instanceof HTMLElement ) {
pos?.removeAttribute( "data-message" );
pos.dataset.bearing = "---";
pos.dataset.distance = "---";
pos.dataset.distanceUnits = "NM";
#drawCoordinates(imgId: string, textId: string, value: number, prefixes: string[]) {
const el = this.getElement().querySelector(`#${textId}`) as HTMLElement;
const img = this.getElement().querySelector(`#${imgId}`) as HTMLElement;
if (img && el) {
let matches = String(value).match(/^\-?(\d+)\.(\d{2})(\d{2})(\d{2})/);
if (matches && matches.length) {
el.dataset.dd = matches[1];
el.dataset.mm = matches[2];
el.dataset.ss = matches[3];
el.dataset.sss = matches[4];
}
img.dataset.label = (value < 0) ? prefixes[1] : prefixes[0];
}
}
}

View File

@ -1,4 +1,4 @@
import * as L from 'leaflet'
import { LatLng } from 'leaflet';
import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setConnectionStatus } from '..';
import { SpawnOptions } from '../controls/mapcontextmenu';
@ -121,12 +121,18 @@ export function addDestination(ID: number, path: any) {
POST(data, () => { });
}
export function spawnSmoke(color: string, latlng: L.LatLng) {
export function spawnSmoke(color: string, latlng: LatLng) {
var command = { "color": color, "location": latlng };
var data = { "smoke": command }
POST(data, () => { });
}
export function spawnExplosion(strength: number, latlng: LatLng) {
var command = { "strength": strength, "location": latlng };
var data = { "explosion": command }
POST(data, () => { });
}
export function spawnGroundUnit(spawnOptions: SpawnOptions) {
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition };
var data = { "spawnGround": command }
@ -155,7 +161,7 @@ export function followUnit(ID: number, targetID: number, offset: { "x": number,
POST(data, () => { });
}
export function cloneUnit(ID: number, latlng: L.LatLng) {
export function cloneUnit(ID: number, latlng: LatLng) {
var command = { "ID": ID, "location": latlng };
var data = { "cloneUnit": command }
POST(data, () => { });
@ -167,7 +173,7 @@ export function deleteUnit(ID: number, explosion: boolean) {
POST(data, () => { });
}
export function landAt(ID: number, latlng: L.LatLng) {
export function landAt(ID: number, latlng: LatLng) {
var command = { "ID": ID, "location": latlng };
var data = { "landAt": command }
POST(data, () => { });
@ -251,6 +257,29 @@ export function refuel(ID: number) {
POST(data, () => { });
}
export function bombPoint(ID: number, latlng: LatLng) {
var command = { "ID": ID, "location": latlng }
var data = { "bombPoint": command }
POST(data, () => { });
}
export function carpetBomb(ID: number, latlng: LatLng) {
var command = { "ID": ID, "location": latlng }
var data = { "carpetBomb": command }
POST(data, () => { });
}
export function bombBuilding(ID: number, latlng: LatLng) {
var command = { "ID": ID, "location": latlng }
var data = { "bombBuilding": command }
POST(data, () => { });
}
export function fireAtArea(ID: number, latlng: LatLng) {
var command = { "ID": ID, "location": latlng }
var data = { "fireAtArea": command }
POST(data, () => { });
}
export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings) {
var command = {
"ID": ID,

View File

@ -1,7 +1,7 @@
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map } from 'leaflet';
import { getMap, getUnitsManager } from '..';
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 } from '../server/server';
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';
@ -517,6 +517,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);
@ -557,7 +573,7 @@ export class Unit extends CustomMarker {
}
else if ((getUnitsManager().getSelectedUnits().length > 0 && (getUnitsManager().getSelectedUnits().includes(this))) || getUnitsManager().getSelectedUnits().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
}
}

View File

@ -2,8 +2,8 @@ import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getUnitDataTable } from "..";
import { Unit } from "./unit";
import { cloneUnit } from "../server/server";
import { IDLE, MOVE_UNIT } from "../map/map";
import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots } from "../other/utils";
import { IDLE, UNIT_SELECTED } from "../map/map";
export class UnitsManager {
#units: { [ID: number]: Unit };
@ -306,7 +306,7 @@ export class UnitsManager {
for (let idx in selectedUnits) {
selectedUnits[idx].setOnOff(onOff);
}
this.#showActionMessage(selectedUnits, `unit acitve set to ${onOff}`);
this.#showActionMessage(selectedUnits, `unit active set to ${onOff}`);
}
selectedUnitsSetFollowRoads(followRoads: boolean) {
@ -438,6 +438,38 @@ export class UnitsManager {
return unitDestinations;
}
selectedUnitsBombPoint(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
for (let idx in selectedUnits) {
selectedUnits[idx].bombPoint(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
selectedUnitsCarpetBomb(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
for (let idx in selectedUnits) {
selectedUnits[idx].carpetBomb(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
selectedUnitsBombBuilding(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
for (let idx in selectedUnits) {
selectedUnits[idx].bombBuilding(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
selectedUnitsFireAtArea(mouseCoordinates: LatLng) {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true });
for (let idx in selectedUnits) {
selectedUnits[idx].fireAtArea(mouseCoordinates);
}
this.#showActionMessage(selectedUnits, `unit bombing point`);
}
/***********************************************/
copyUnits() {
this.#copiedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
@ -466,7 +498,7 @@ export class UnitsManager {
#onUnitSelection(unit: Unit) {
if (this.getSelectedUnits().length > 0) {
getMap().setState(MOVE_UNIT);
getMap().setState(UNIT_SELECTED);
/* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */
if (!this.#selectionEventDisabled) {
window.setTimeout(() => {

View File

@ -8,6 +8,8 @@
data-on-click-params='{ "type": "ground-unit" }' class="unit-spawn-button"></button>
<button data-active-coalition="blue" id="smoke-spawn-button" title="Spawn smoke" data-on-click="contextMenuShow"
data-on-click-params='{ "type": "smoke" }' class="unit-spawn-button"></button>
<button data-active-coalition="blue" id="explosion-spawn-button" title="Explosion" data-on-click="contextMenuShow"
data-on-click-params='{ "type": "explosion" }' class="unit-spawn-button"></button>
</div>
<div id="aircraft-spawn-menu" class="ol-panel hide">
<div class="ol-select-container">
@ -81,6 +83,12 @@
<button class="smoke-button" title="" data-smoke-color="green" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "green" }'>Green smoke</button>
<button class="smoke-button" title="" data-smoke-color="orange" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "orange" }'>Orange smoke</button>
</div>
<div id="explosion-menu" class="ol-panel hide">
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 1 }'>Small explosion</button>
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 2 }'>Medium explosion</button>
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 3 }'>Big explosion</button>
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 4 }'>Huge explosion</button>
</div>
</div>
<div id="unit-contextmenu" class="ol-panel" oncontextmenu="return false;">

View File

@ -1,24 +1,28 @@
<div id="mouse-info-panel" class="ol-panel" oncontextmenu="return false;">
<div>
<div id="measuring-tool" class="hide">
<dl class="ol-data-grid">
<dt id="ref-measure-position" data-tooltip="CTRL-click on the map to activate the measuring tool."></dt>
<dt id="ref-measure-position" data-tooltip="CTRL-click on the map to activate the measuring tool">
<img src="/resources/theme/images/icons/pin.svg" inject-svg>
</dt>
<dd id="measure-position" class="br-info" data-bearing="---" data-distance="---" data-distance-units="NM"></dd>
<dt id="ref-unit-position" data-tooltip="Bearing/range from selected unit."></dt>
<dt id="ref-unit-position" data-tooltip="Bearing/range from selected unit">
<img src="/resources/theme/images/icons/plane.svg" inject-svg>
</dt>
<dd id="unit-position" class="br-info" data-bearing="---" data-distance="---" data-distance-units="NM"></dd>
</dl>
</div>
<div>
<div id="bullseyes-tool">
<dl class="ol-data-grid">
<dt id="ref-bullseye-2" data-tooltip="Bearing/range from this bullseye." data-label="BE" data-coalition="blue"></dt>
<dd id="bullseye-2" class="br-info" data-bearing="---" data-distance="---" data-distance-units="NM"></dd>
<dt id="ref-bullseye-1" data-tooltip="Bearing/range from this bullseye." data-label="BE" data-coalition="red"></dt>
<dd id="bullseye-1" class="br-info" data-bearing="---" data-distance="---" data-distance-units="NM"></dd>
<dt id="ref-bullseye-2" data-tooltip="Bearing/range from this bullseye" data-label="BE" data-coalition="blue"></dt>
<dd id="bullseye-2" class="br-info" data-coalition="blue" data-bearing="---" data-distance="---" data-distance-units="NM"></dd>
<dt id="ref-bullseye-1" data-tooltip="Bearing/range from this bullseye" data-label="BE" data-coalition="red"></dt>
<dd id="bullseye-1" class="br-info" data-coalition="red" data-bearing="---" data-distance="---" data-distance-units="NM"></dd>
</dl>
</div>
<div>
<div id="coordinates-tool">
<dl class="ol-data-grid">
<dt id="ref-mouse-position-latitude" data-label="?"></dt>
<dd id="mouse-position-latitude" class="coordinates" data-dd="---" data-mm="---" data-ss="--" data-sss="--"></dd>

View File

@ -6,7 +6,7 @@
<div class="ol-select-options">
<div id="toolbar-summary">
<h3>DCS Olympus</h3>
<div class="accent-green app-version-number">version v0.2.1</div>
<div class="accent-green app-version-number">version v0.3.0</div>
</div>
<div>
<a href="https://www.discord.com" target="_blank">Discord</a>
@ -15,7 +15,7 @@
<a href="https://github.com/Pax1601/DCSOlympus" target="_blank">Github</a>
</div>
<div data-on-click="reloadPage">
<a href="" target="_blank" data-on-click="reloadPage">Restart Olyumpus</a>
<a href="" target="_blank" data-on-click="reloadPage">Restart Olympus</a>
</div>
</div>
</div>

View File

@ -1,5 +1,5 @@
#define nwjsFolder "C:\Users\dpass\Documents\nwjs\"
#define version "v0.2.1-alpha"
#define version "v0.3.0-alpha"
[Setup]
AppName=DCS Olympus

View File

@ -1,4 +1,4 @@
local version = "v0.2.1-alpha"
local version = "v0.3.0-alpha"
local debug = true

View File

@ -1,4 +1,4 @@
local version = 'v0.2.1-alpha'
local version = 'v0.3.0-alpha'
Olympus = {}
Olympus.OlympusDLL = nil