Merge branch 'main' into 343-add-ability-to-select-unit-nation-and-livery

This commit is contained in:
Pax1601 2023-08-10 17:52:43 +02:00
commit 46ca6ac327
33 changed files with 13188 additions and 12707 deletions

View File

@ -1,4 +1,4 @@
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for Q2 2023.
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for Q4 2023.
# DCS Olympus
*A real-time web interface to spawn and control units in DCS World*

View File

@ -8,7 +8,7 @@ const DEMO_UNIT_DATA = {
formationOffset: { x: 0, y: 0, z: 0 },
targetID: 2,
targetPosition: { lat: 0, lng: 0, alt: 0 },
ROE: 2,
ROE: 1,
reactionToThreat: 1,
emissionsCountermeasures: 1,
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
@ -24,7 +24,7 @@ const DEMO_UNIT_DATA = {
formationOffset: { x: 0, y: 0, z: 0 },
targetID: 0,
targetPosition: { lat: 0, lng: 0, alt: 0 },
ROE: 2,
ROE: 1,
reactionToThreat: 1,
emissionsCountermeasures: 1,
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
@ -39,7 +39,7 @@ const DEMO_UNIT_DATA = {
formationOffset: { x: 0, y: 0, z: 0 },
targetID: 0,
targetPosition: { lat: 0, lng: 0, alt: 0 },
ROE: 2,
ROE: 1,
reactionToThreat: 1,
emissionsCountermeasures: 1,
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
@ -54,7 +54,7 @@ const DEMO_UNIT_DATA = {
formationOffset: { x: 0, y: 0, z: 0 },
targetID: 0,
targetPosition: { lat: 0, lng: 0, alt: 0 },
ROE: 2,
ROE: 1,
reactionToThreat: 1,
emissionsCountermeasures: 1,
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
@ -64,13 +64,13 @@ const DEMO_UNIT_DATA = {
contacts: [{ID: 1001, detectionMethod: 16}],
activePath: [ ],
isLeader: true
}, ["5"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "Gepard", unitName: "Cool guy 2-2", groupName: "Cool group 4", state: 1, task: "Being cool",
}, ["5"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "S_75M_Volhov", unitName: "Cool guy 2-2", groupName: "Cool group 4", state: 1, task: "Being cool",
hasTask: false, position: { lat: 37.21, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
formationOffset: { x: 0, y: 0, z: 0 },
targetID: 0,
targetPosition: { lat: 0, lng: 0, alt: 0 },
ROE: 2,
ROE: 1,
reactionToThreat: 1,
emissionsCountermeasures: 1,
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
@ -99,9 +99,9 @@ class DemoDataGenerator {
app.use('/demo', basicAuth({
users: {
'admin': 'socks',
'blue': 'bluesocks',
'red': 'redsocks'
'admin': 'password',
'blue': 'bluepassword',
'red': 'redpassword'
},
}))

View File

@ -1,12 +1,12 @@
{
"name": "DCSOlympus",
"version": "v0.4.1-alpha",
"version": "v0.4.2-alpha",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "DCSOlympus",
"version": "v0.4.1-alpha",
"version": "v0.4.2-alpha",
"dependencies": {
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",

View File

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

View File

@ -691,6 +691,11 @@ nav.ol-panel> :last-child {
width: 30px;
}
#reaction-to-threat-buttons-container button:not(:first-child) svg {
width: 150%;
margin: -5px;
}
#unit-control-panel .ol-option-button button.selected {
background-color: white;
border-color: white;
@ -700,6 +705,28 @@ nav.ol-panel> :last-child {
fill: var(--background-steel);
}
#rapid-controls {
display: flex;
flex-direction: column;
row-gap: 5px;
position: absolute;
height: fit-content;
width: fit-content;
left: calc(100% + 10px);
top: 0px;
}
#rapid-controls button {
padding: 4px;
}
#rapid-controls svg {
height: 20px;
width: 20px;
fill: white;
stroke: white;
}
/****************************************************************************************/
#splash-screen {
background-image: url("/resources/theme/images/splash/1.png");
@ -959,6 +986,10 @@ nav.ol-panel> :last-child {
font-size: 10px;
}
#command-mode-toolbar {
min-width: fit-content ;
}
#command-mode-toolbar .ol-button {
border: 1px solid white;
}

View File

@ -100,6 +100,10 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
margin-top: 15px;
}
#advanced-settings-dialog .ol-text-input input {
height: 40px;
}
#general-settings-grid {
display: grid;
grid-template-columns: 1fr 1fr;

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 640 512"
version="1.1"
id="svg11515"
sodipodi:docname="climb.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="defs11519" />
<sodipodi:namedview
id="namedview11517"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.1559539"
inkscape:cx="184.69595"
inkscape:cy="230.97808"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg11515" />
<!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="m 0,480 c 0,17.7 14.3,32 32,32 h 576 c 17.7,0 32,-14.3 32,-32 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,448 0,462.3 0,480 Z"
id="path12003" />
<path
d="M 381,114.9 186.1,41.8 C 169.4,35.6 150.9,36.5 135,44.5 L 89.1,67.4 C 78,73 77.2,88.5 87.6,95.2 L 234.5,189.7 136,240 77.8,214.1 c -8.7,-3.9 -18.8,-3.7 -27.3,0.6 l -32.2,16.1 c -9.3,4.7 -11.8,16.8 -5,24.7 l 73.1,85.3 c 6.1,7.1 15,11.2 24.3,11.2 h 137.7 c 5,0 9.9,-1.2 14.3,-3.4 L 535.6,212.2 c 46.5,-23.3 82.5,-63.3 100.8,-112 C 645.9,75 627.2,48 600.2,48 h -57.4 c -20.2,0 -40.2,4.8 -58.2,14 z"
id="path11513" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 640 512"
version="1.1"
id="svg11515"
sodipodi:docname="descent.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="defs11519" />
<sodipodi:namedview
id="namedview11517"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.1559539"
inkscape:cx="184.69595"
inkscape:cy="230.97808"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg11515" />
<!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="m 0,480 c 0,17.7 14.3,32 32,32 h 576 c 17.7,0 32,-14.3 32,-32 0,-17.7 -14.3,-32 -32,-32 H 32 C 14.3,448 0,462.3 0,480 Z"
id="path12003" />
<path
d="M 390.60902,242.29848 295.63089,57.072198 C 287.44991,41.248114 273.15967,29.46469 256.01489,24.682806 L 206.65223,10.734745 C 194.67308,7.4076141 183.65093,18.33475 186.83528,30.289244 L 231.90676,199.04475 125.19723,169.9687 99.575041,111.64587 C 95.764001,102.90653 88.158361,96.257906 78.977251,93.718766 L 44.324506,83.959798 C 34.282459,81.178199 24.290763,88.44635 24.004651,98.865954 L 20.676595,211.15409 c -0.265566,9.35678 3.558834,18.37864 10.438121,24.63687 L 132.97255,328.4531 c 3.69854,3.36464 8.13062,5.77434 12.86578,7.10786 l 293.65372,82.74575 c 50.07565,14.05594 103.62226,8.693 149.9305,-15.01621 23.985,-12.24784 28.32151,-44.80371 8.34938,-62.97276 L 555.31268,301.69169 C 540.37057,288.09856 522.34635,278.1906 502.84067,272.88322 Z"
id="path11513" />
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M288 32c0-17.7-14.3-32-32-32s-32 14.3-32 32V240c0 8.8-7.2 16-16 16s-16-7.2-16-16V64c0-17.7-14.3-32-32-32s-32 14.3-32 32V336c0 1.5 0 3.1 .1 4.6L67.6 283c-16-15.2-41.3-14.6-56.6 1.4s-14.6 41.3 1.4 56.6L124.8 448c43.1 41.1 100.4 64 160 64H304c97.2 0 176-78.8 176-176V128c0-17.7-14.3-32-32-32s-32 14.3-32 32V240c0 8.8-7.2 16-16 16s-16-7.2-16-16V64c0-17.7-14.3-32-32-32s-32 14.3-32 32V240c0 8.8-7.2 16-16 16s-16-7.2-16-16V32z"/></svg>

After

Width:  |  Height:  |  Size: 668 B

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 512 512"
version="1.1"
id="svg4"
sodipodi:docname="speed-decrease.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="0.81738281"
inkscape:cx="362.74313"
inkscape:cy="254.47073"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="m 465.11867,284.79404 a 203.05733,202.96808 0 1 0 -406.11467,0 203.05733,202.96808 0 1 0 406.11467,0 z M 236.67916,157.939 a 25.382167,25.371011 0 1 1 50.76433,0 25.382167,25.371011 0 1 1 -50.76433,0 z m 25.38218,253.71009 c -27.99972,0 -50.76435,-22.75462 -50.76435,-50.74202 0,-13.79547 5.47304,-26.24313 14.3568,-35.36086 L 174.81014,210.02885 c -4.20393,-9.59341 0.15866,-20.85181 9.75626,-25.05387 9.59763,-4.2021 20.86097,0.1586 25.06489,9.75199 l 50.92297,115.43808 c 0.47592,0 1.03116,0 1.50708,0 27.99969,0 50.76433,22.75464 50.76433,50.74202 0,27.9874 -22.76464,50.74202 -50.76433,50.74202 z m 63.4554,-215.65359 a 25.38217,25.371013 0 1 1 50.76433,0 25.38217,25.371013 0 1 1 -50.76433,0 z m 63.45542,114.16955 a 25.382167,25.371011 0 1 1 0,-50.74201 25.382167,25.371011 0 1 1 0,50.74201 z M 109.76832,284.79404 a 25.38217,25.371013 0 1 1 50.76433,0 25.38217,25.371013 0 1 1 -50.76433,0 z"
id="path2"
style="stroke-width:0.627239" />
<path
style="fill:none;stroke-width:37.6403;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
id="path1569"
sodipodi:type="arc"
sodipodi:cx="-261.46939"
sodipodi:cy="284.48697"
sodipodi:rx="250.99081"
sodipodi:ry="250.88049"
sodipodi:start="3.7594392"
sodipodi:end="5.6635934"
sodipodi:arc-type="arc"
d="M -466.05898,139.15662 A 250.99081,250.88049 0 0 1 -261.68843,33.606571 250.99081,250.88049 0 0 1 -57.133877,138.79992"
sodipodi:open="true"
transform="scale(-1,1)" />
<path
style="fill:none;stroke-width:30;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 51.337,145.14023 47.06496,70.716358"
id="path2580"
sodipodi:nodetypes="cc" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 512 512"
version="1.1"
id="svg4"
sodipodi:docname="speed-increase.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="0.81738281"
inkscape:cx="362.74313"
inkscape:cy="254.47073"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="svg4" />
<!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="m 51.825435,284.79404 a 203.05733,202.96808 0 1 1 406.114665,0 203.05733,202.96808 0 1 1 -406.114665,0 z M 280.26494,157.939 a 25.382167,25.371011 0 1 0 -50.76433,0 25.382167,25.371011 0 1 0 50.76433,0 z m -25.38218,253.71009 c 27.99972,0 50.76435,-22.75462 50.76435,-50.74202 0,-13.79547 -5.47304,-26.24313 -14.3568,-35.36086 l 50.84365,-115.51736 c 4.20393,-9.59341 -0.15866,-20.85181 -9.75626,-25.05387 -9.59763,-4.2021 -20.86097,0.1586 -25.06489,9.75199 l -50.92297,115.43808 c -0.47592,0 -1.03116,0 -1.50708,0 -27.99969,0 -50.76433,22.75464 -50.76433,50.74202 0,27.9874 22.76464,50.74202 50.76433,50.74202 z M 191.42736,195.9955 a 25.38217,25.371013 0 1 0 -50.76433,0 25.38217,25.371013 0 1 0 50.76433,0 z m -63.45542,114.16955 a 25.382167,25.371011 0 1 0 0,-50.74201 25.382167,25.371011 0 1 0 0,50.74201 z m 279.20384,-25.37101 a 25.38217,25.371013 0 1 0 -50.76433,0 25.38217,25.371013 0 1 0 50.76433,0 z"
id="path2"
style="stroke-width:0.627239" />
<path
style="fill:none;stroke-width:37.6403;stroke-linecap:round;stroke-dasharray:none;stroke-opacity:1"
id="path1569"
sodipodi:type="arc"
sodipodi:cx="255.4747"
sodipodi:cy="284.48697"
sodipodi:rx="250.99081"
sodipodi:ry="250.88049"
sodipodi:start="3.7594392"
sodipodi:end="5.6635934"
sodipodi:arc-type="arc"
d="M 50.885111,139.15662 A 250.99081,250.88049 0 0 1 255.25567,33.606571 250.99081,250.88049 0 0 1 459.81021,138.79992"
sodipodi:open="true" />
<path
style="fill:none;stroke-width:30;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 465.6071,145.14023 4.27204,-74.423872"
id="path2580"
sodipodi:nodetypes="cc" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -13,7 +13,7 @@ export const RWR = 16;
export const DLINK = 32;
export const states: string[] = ["none", "idle", "reach-destination", "attack", "follow", "land", "refuel", "AWACS", "tanker", "bomb-point", "carpet-bomb", "bomb-building", "fire-at-area"];
export const ROEs: string[] = ["free", "designated", "return", "hold"];
export const ROEs: string[] = ["free", "designated", "", "return", "hold"];
export const reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"];
export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"];

View File

@ -205,11 +205,13 @@ function setupEvents() {
document.querySelectorAll("[inject-svg]").forEach((el: Element) => {
var img = el as HTMLImageElement;
var isLoaded = img.complete && img.naturalHeight !== 0;
var isLoaded = img.complete;
if (isLoaded)
SVGInjector(img);
else
img.onload = () => SVGInjector(img);
img.addEventListener("load", () => {
SVGInjector(img);
});
})
}

View File

@ -1,5 +1,5 @@
import * as L from "leaflet"
import { getUnitsManager } from "..";
import { getMissionHandler, getUnitsManager } from "..";
import { BoxSelect } from "./boxselect";
import { MapContextMenu } from "../controls/mapcontextmenu";
import { UnitContextMenu } from "../controls/unitcontextmenu";
@ -117,11 +117,20 @@ export class Map extends L.Map {
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
});
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
document.addEventListener("toggleMarkerVisibility", (ev: CustomEventInit) => {
const el = ev.detail._element;
el?.classList.toggle("off");
ev.detail.types.forEach((type: string) => getUnitsManager().setHiddenType(type, !el?.classList.contains("off")));
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
if (ev.detail.types.includes("airbase")) {
Object.values(getMissionHandler().getAirbases()).forEach((airbase: Airbase) => {
if (el?.classList.contains("off"))
airbase.removeFrom(this);
else
airbase.addTo(this);
})
}
});
@ -150,7 +159,9 @@ export class Map extends L.Map {
/* Option buttons */
this.#optionButtons["visibility"] = visibilityControls.map((option: string, index: number) => {
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, visibilityControlsTootlips[index], "toggleUnitVisibility", `{"types": "${visibilityControlsTypes[index]}"}`);
var typesArrayString = `"${visibilityControlsTypes[index][0]}"`;
visibilityControlsTypes[index].forEach((type: string, idx: number) => {if (idx > 0) typesArrayString = `${typesArrayString}, "${type}"`});
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, visibilityControlsTootlips[index], "toggleMarkerVisibility", `{"types": [${typesArrayString}]}`);
});
document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]);

View File

@ -38,6 +38,7 @@ export class Airbase extends CustomMarker
img.onload = () => SVGInjector(img);
el.appendChild(img);
this.getElement()?.appendChild(el);
el.dataset.coalition = this.#coalition;
}
setCoalition(coalition: string)

View File

@ -1,3 +1,4 @@
import { getMouseInfoPanel } from "..";
import { Panel } from "./panel";
export class LogPanel extends Panel {
@ -23,8 +24,21 @@ export class LogPanel extends Panel {
if (scrollEl) {
scrollEl.addEventListener("scroll", () => {
this.#scrolledDown = Math.abs(scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight) < 1
})
});
}
window.addEventListener("resize", () => {
this.#calculateHeight();
});
const mouseInfoPanel = getMouseInfoPanel();
new ResizeObserver(() => this.#calculateHeight()).observe(mouseInfoPanel.getElement())
}
show() {
super.show();
this.#calculateHeight();
}
appendLogs(logs: {[key: string]: string}) {
@ -68,4 +82,9 @@ export class LogPanel extends Panel {
scrollEl.scrollTop = scrollEl.scrollHeight - scrollEl.clientHeight;
}
}
#calculateHeight() {
const mouseInfoPanel = getMouseInfoPanel();
this.getElement().style.height = `${mouseInfoPanel.getElement().offsetTop - this.getElement().offsetTop - 10}px`;
}
}

View File

@ -39,7 +39,7 @@ export class UnitControlPanel extends Panel {
// Reversing the ROEs so that the least "aggressive" option is always on the left
this.#optionButtons["ROE"] = ROEs.slice(0).reverse().map((option: string, index: number) => {
return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions.slice(0).reverse()[index], () => { getUnitsManager().selectedUnitsSetROE(option); });
});
}).filter((button: HTMLButtonElement, index: number) => {return ROEs[index] !== "";});
this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string, index: number) => {
return this.#createOptionButton(option, `threat/${option.toLowerCase()}.svg`, reactionsToThreatDescriptions[index],() => { getUnitsManager().selectedUnitsSetReactionToThreat(option); });

View File

@ -419,7 +419,7 @@ export function startUpdate() {
getWeapons((buffer: ArrayBuffer) => {
var time = getWeaponsManager()?.update(buffer);
return time;
}, false);
}, true);
}
}, 5000);
}

View File

@ -1,5 +1,5 @@
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet';
import { getMap, getMissionHandler, getUnitsManager } from '..';
import { getMap, getMissionHandler, getUnitsManager, getWeaponsManager } from '..';
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad } from '../other/utils';
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server';
import { CustomMarker } from '../map/custommarker';
@ -11,6 +11,7 @@ import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, ObjectIconOptions
import { DataExtractor } from '../server/dataextractor';
import { groundUnitDatabase } from './groundunitdatabase';
import { navyUnitDatabase } from './navyunitdatabase';
import { Weapon } from '../weapon/weapon';
var pathIcon = new Icon({
iconUrl: '/resources/theme/images/markers/marker-icon.png',
@ -153,7 +154,7 @@ export class Unit extends CustomMarker {
this.on('click', (e) => this.#onClick(e));
this.on('dblclick', (e) => this.#onDoubleClick(e));
this.on('contextmenu', (e) => this.#onContextMenu(e));
this.on('mouseover', () => { this.setHighlighted(true); })
this.on('mouseover', () => { if (this.belongsToCommandedCoalition()) this.setHighlighted(true); })
this.on('mouseout', () => { this.setHighlighted(false); })
getMap().on("zoomend", () => {this.#onZoom();})
@ -540,7 +541,7 @@ export class Unit extends CustomMarker {
/* Force a redraw of the unit to reflect the new status of the detection methods */
this.setHidden(true);
this.#detectionMethods = newDetectionMethods;
this.updateVisibility();
this.#updateMarker();
}
}
}
@ -986,19 +987,25 @@ export class Unit extends CustomMarker {
if (getMap().getVisibilityOptions()[SHOW_CONTACT_LINES]) {
for (let index in this.#contacts) {
var contactData = this.#contacts[index];
var contact = getUnitsManager().getUnitByID(contactData.ID);
var contact: Unit | Weapon | null;
if (contactData.ID in getUnitsManager().getUnits())
contact = getUnitsManager().getUnitByID(contactData.ID);
else
contact = getWeaponsManager().getWeaponByID(contactData.ID);
if (contact != null && contact.getAlive()) {
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.#position.lat, contact.#position.lng);
var bearingToContact = bearing(this.#position.lat, this.#position.lng, contact.getPosition().lat, contact.getPosition().lng);
var startXY = getMap().latLngToContainerPoint(startLatLng);
var endX = startXY.x + 80 * Math.sin(deg2rad(bearingToContact));
var endY = startXY.y - 80 * Math.cos(deg2rad(bearingToContact));
endLatLng = getMap().containerPointToLatLng(new Point(endX, endY));
}
else
endLatLng = new LatLng(contact.#position.lat, contact.#position.lng);
endLatLng = new LatLng(contact.getPosition().lat, contact.getPosition().lng);
var color;
if (contactData.detectionMethod === VISUAL || contactData.detectionMethod === OPTIC)

View File

@ -1,5 +1,5 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager } from "..";
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager, getWeaponsManager } from "..";
import { Unit } from "./unit";
import { cloneUnit, deleteUnit, refreshAll, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server";
import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils";
@ -36,6 +36,8 @@ export class UnitsManager {
document.addEventListener('importFromFile', () => this.importFromFile());
document.addEventListener('contactsUpdated', (e: CustomEvent) => {this.#requestDetectionUpdate = true});
document.addEventListener('commandModeOptionsChanged', () => {Object.values(this.#units).forEach((unit: Unit) => unit.updateVisibility())});
document.addEventListener('selectedUnitsChangeSpeed', (e: any) => {this.selectedUnitsChangeSpeed(e.detail.type)});
document.addEventListener('selectedUnitsChangeAltitude', (e: any) => {this.selectedUnitsChangeAltitude(e.detail.type)});
}
getSelectableAircraft() {
@ -95,11 +97,11 @@ export class UnitsManager {
if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
/* Create a dictionary of empty detection methods arrays */
var detectionMethods: {[key: string]: number[]} = {};
for (let ID in this.#units) {
const unit = this.#units[ID];
for (let ID in this.#units)
detectionMethods[ID] = [];
}
for (let ID in getWeaponsManager().getWeapons())
detectionMethods[ID] = [];
/* Fill the array with the detection methods */
for (let ID in this.#units) {
const unit = this.#units[ID];
@ -107,7 +109,7 @@ export class UnitsManager {
const contacts = unit.getContacts();
contacts.forEach((contact: Contact) => {
const contactID = contact.ID;
if (!(detectionMethods[contactID].includes(contact.detectionMethod)))
if (contactID in detectionMethods && !(detectionMethods[contactID].includes(contact.detectionMethod)))
detectionMethods[contactID]?.push(contact.detectionMethod);
})
}
@ -116,7 +118,11 @@ export class UnitsManager {
/* Set the detection methods for every unit */
for (let ID in this.#units) {
const unit = this.#units[ID];
unit.setDetectionMethods(detectionMethods[ID]);
unit?.setDetectionMethods(detectionMethods[ID]);
}
for (let ID in getWeaponsManager().getWeapons()) {
const weapon = getWeaponsManager().getWeaponByID(parseInt(ID));
weapon?.setDetectionMethods(detectionMethods[ID]);
}
this.#requestDetectionUpdate = false;

View File

@ -202,7 +202,7 @@ export class Weapon extends CustomMarker {
/* Force a redraw of the unit to reflect the new status of the detection methods */
this.setHidden(true);
this.#detectionMethods = newDetectionMethods;
this.updateVisibility();
this.#updateMarker();
}
}
}

View File

@ -6,13 +6,11 @@ import { Contact } from "../@types/unit";
export class WeaponsManager {
#weapons: { [ID: number]: Weapon };
#requestDetectionUpdate: boolean = false;
constructor() {
this.#weapons = {};
document.addEventListener("commandModeOptionsChanged", () => {Object.values(this.#weapons).forEach((weapon: Weapon) => weapon.updateVisibility())});
document.addEventListener('contactsUpdated', (e: CustomEvent) => {this.#requestDetectionUpdate = true});
}
getWeapons() {
@ -54,16 +52,6 @@ export class WeaponsManager {
}
this.#weapons[ID]?.setData(dataExtractor);
}
if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
for (let ID in this.#weapons) {
var weapon = this.#weapons[ID];
if (!weapon.belongsToCommandedCoalition())
weapon.setDetectionMethods(this.getWeaponDetectedMethods(weapon));
}
this.#requestDetectionUpdate = false;
}
return updateTime;
}

View File

@ -3,7 +3,7 @@
<div id="app-summary">
<h2>DCS Olympus</h2>
<h4>Dynamic Unit Command</h4>
<div class="app-version">Version <span class="app-version-number">v0.4.1-alpha</span></div>
<div class="app-version">Version <span class="app-version-number">v0.4.2-alpha</span></div>
</div>
<div id="authentication-form">

View File

@ -79,5 +79,13 @@
<button class="ol-button-warning" data-on-click="deleteSelectedUnits"><img src="/resources/theme/images/icons/trash-can-regular.svg" inject-svg>Delete</button>
<button class="ol-button-warning" data-on-click="explodeSelectedUnits"><img src="/resources/theme/images/icons/explosion-solid.svg" inject-svg></button>
</div>
<div id="rapid-controls" class="ol-panel">
<button title="Increase units altitude" class="ol-button" data-on-click="selectedUnitsChangeAltitude" data-on-click-params='{ "type": "climb" }'><img src="/resources/theme/images/icons/climb.svg" inject-svg></button>
<button title="Descrease units altitude" class="ol-button" data-on-click="selectedUnitsChangeAltitude" data-on-click-params='{ "type": "descend" }'><img src="/resources/theme/images/icons/descent.svg" inject-svg></button>
<button title="Increase units speed" class="ol-button" data-on-click="selectedUnitsChangeSpeed" data-on-click-params='{ "type": "fast" }'><img src="/resources/theme/images/icons/speed-increase.svg" inject-svg></button>
<button title="Decrease units speed" class="ol-button" data-on-click="selectedUnitsChangeSpeed" data-on-click-params='{ "type": "slow" }'><img src="/resources/theme/images/icons/speed-decrease.svg" inject-svg></button>
<button title="Stop unit and go back to idle state" class="ol-button" data-on-click="selectedUnitsChangeSpeed" data-on-click-params='{ "type": "stop" }'><img src="/resources/theme/images/icons/hand-solid.svg" inject-svg></button>
</div>
</div>

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.4.1-alpha</div>
<div class="accent-green app-version-number">version v0.4.2-alpha</div>
</div>
<div>
<a href="https://www.discord.com" target="_blank">Discord</a>
@ -28,9 +28,7 @@
<div class="ol-group">
<div id="map-type" class="ol-select">
<div class="ol-select-value">
<img src="resources/theme/images/icons/map-source.svg" inject-svg> ArcGIS Satellite
</div>
<div class="ol-select-value"><img src="resources/theme/images/icons/map-source.svg" inject-svg>ArcGIS Satellite</div>
<div class="ol-select-options">
<!-- Here the available map sources will be listed-->
</div>

View File

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

View File

@ -15,7 +15,7 @@ declare_plugin(self_ID,
shortName = "Olympus",
fileMenuName = "Olympus",
version = "v0.4.1-alpha",
version = "v0.4.2-alpha",
state = "installed",
developerName= "DCS Refugees 767 squadron",
info = _("DCS Olympus is a mod for DCS World. It allows users to spawn, control, task, group, and remove units from a DCS World server using a real-time map interface, similarly to Real Time Strategy games. The user interface also provides useful informations units, like loadouts, fuel, tasking, and so on. In the future, more features for DCS World GCI and JTAC will be available."),

View File

@ -1,4 +1,4 @@
local version = "v0.4.1-alpha"
local version = "v0.4.2-alpha"
local debug = false
@ -427,6 +427,10 @@ function Olympus.generateAirUnitsTable(units)
local loadout = unit.loadout -- loadout: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides loadout
if unit.heading == nil then
unit.heading = 0
end
if payload == nil then
if loadout and loadout ~= "" and Olympus.unitPayloads[unit.unitType][loadout] then
payload = Olympus.unitPayloads[unit.unitType][loadout]
@ -445,11 +449,14 @@ function Olympus.generateAirUnitsTable(units)
["alt_type"] = "BARO",
["skill"] = "Excellent",
["payload"] = { ["pylons"] = payload, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100, },
["heading"] = 0,
["heading"] = unit.heading,
["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitTable + 1 },
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1,
["livery_id"] = unit.liveryID
}
-- Add the payload to the registry, used for unit cloning
Olympus.payloadRegistry[unitTable[#unitTable].name] = payload
end
return unitTable
end
@ -533,7 +540,7 @@ function Olympus.generateGroundUnitsTable(units)
["type"] = unit.unitType,
["x"] = spawnLocation.x,
["y"] = spawnLocation.z,
["heading"] = 0,
["heading"] = unit.heading,
["skill"] = "High",
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1,
["livery_id"] = unit.liveryID
@ -568,7 +575,7 @@ function Olympus.generateNavyUnitsTable(units)
["type"] = unit.unitType,
["x"] = spawnLocation.x,
["y"] = spawnLocation.z,
["heading"] = 0,
["heading"] = unit.heading,
["skill"] = "High",
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1,
["transportable"] = { ["randomTransportable"] = false },
@ -585,6 +592,9 @@ function Olympus.clone(ID, lat, lng, category)
Olympus.debug("Olympus.clone " .. ID .. ", " .. category, 2)
local unit = Olympus.getUnitByID(ID)
if unit then
local position = unit:getPosition()
local heading = math.atan2( position.x.z, position.x.x )
-- TODO: understand category in this script
local spawnTable = {
coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition()),
@ -594,6 +604,7 @@ function Olympus.clone(ID, lat, lng, category)
lat = lat,
lng = lng,
alt = unit:getPoint().y,
heading = heading,
unitType = unit:getTypeName(),
payload = Olympus.payloadRegistry[unit:getName()]
}

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
#pragma once
#define VERSION "v0.4.1-alpha"
#define VERSION "v0.4.2-alpha"
#define LOG_NAME "Olympus_log.txt"
#define REST_ADDRESS "http://localhost:30000"
#define REST_URI "olympus"