Compare commits
70 Commits
v0.3.0-alp
...
v0.4.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fac8a5663 | ||
|
|
3c11e1c2a1 | ||
|
|
81871b596b | ||
|
|
8ffd5ef972 | ||
|
|
4ae72b7c0b | ||
|
|
5613394a2c | ||
|
|
2e113b468a | ||
|
|
018e37cd2a | ||
|
|
1d5f24dc1a | ||
|
|
cbb8920de7 | ||
|
|
7eb6cadc4e | ||
|
|
a4db569fbd | ||
|
|
e9a3ecb9eb | ||
|
|
785647ad24 | ||
|
|
72cd4de55f | ||
|
|
e882123692 | ||
|
|
a949a9bf22 | ||
|
|
75c897eb5b | ||
|
|
b48f74f879 | ||
|
|
c6eeeac7c1 | ||
|
|
9c1edd2a86 | ||
|
|
133c447d5d | ||
|
|
b78cd27e4e | ||
|
|
5d29c82244 | ||
|
|
f379f6b998 | ||
|
|
439111f7a0 | ||
|
|
55372d5a5b | ||
|
|
13e3b93e93 | ||
|
|
7a6929e34b | ||
|
|
a0763a6450 | ||
|
|
2d4202979f | ||
|
|
327d5c74d9 | ||
|
|
3bad83a02f | ||
|
|
eee97294df | ||
|
|
56427d84ad | ||
|
|
fc1983d554 | ||
|
|
30568e54f7 | ||
|
|
1af98cc54f | ||
|
|
fb3a628ad0 | ||
|
|
79b0eeae9e | ||
|
|
3b2757a022 | ||
|
|
fa9f1e05ea | ||
|
|
fff0c8244e | ||
|
|
5777632114 | ||
|
|
d013337c5e | ||
|
|
ec91376da2 | ||
|
|
807cdfeb5b | ||
|
|
9f6da4845a | ||
|
|
211cf48681 | ||
|
|
4d9dd364b6 | ||
|
|
52dfcebc14 | ||
|
|
2bce51fca6 | ||
|
|
3a2048f3ec | ||
|
|
63e602afb1 | ||
|
|
7a71065b76 | ||
|
|
1989219579 | ||
|
|
dd2e858db4 | ||
|
|
916752301a | ||
|
|
1d62b4c115 | ||
|
|
258f1136e2 | ||
|
|
9d0e2239e4 | ||
|
|
5555c44c4e | ||
|
|
f80a91ca1e | ||
|
|
61d6d9f16c | ||
|
|
635c487c2b | ||
|
|
f9f02c3eb0 | ||
|
|
ad3b1cb167 | ||
|
|
880ce3919d | ||
|
|
b489659ddd | ||
|
|
f8344b1662 |
@@ -3,7 +3,6 @@ var path = require('path');
|
|||||||
var cookieParser = require('cookie-parser');
|
var cookieParser = require('cookie-parser');
|
||||||
var logger = require('morgan');
|
var logger = require('morgan');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var basicAuth = require('express-basic-auth')
|
|
||||||
|
|
||||||
var atcRouter = require('./routes/api/atc');
|
var atcRouter = require('./routes/api/atc');
|
||||||
var airbasesRouter = require('./routes/api/airbases');
|
var airbasesRouter = require('./routes/api/airbases');
|
||||||
@@ -37,15 +36,7 @@ if (config["server"] != undefined)
|
|||||||
module.exports = app;
|
module.exports = app;
|
||||||
|
|
||||||
const DemoDataGenerator = require('./demo.js');
|
const DemoDataGenerator = require('./demo.js');
|
||||||
var demoDataGenerator = new DemoDataGenerator(10);
|
var demoDataGenerator = new DemoDataGenerator(app);
|
||||||
app.get('/demo/units', (req, res) => demoDataGenerator.units(req, res));
|
|
||||||
app.get('/demo/logs', (req, res) => demoDataGenerator.logs(req, res));
|
|
||||||
app.get('/demo/bullseyes', (req, res) => demoDataGenerator.bullseyes(req, res));
|
|
||||||
app.get('/demo/airbases', (req, res) => demoDataGenerator.airbases(req, res));
|
|
||||||
app.get('/demo/mission', (req, res) => demoDataGenerator.mission(req, res));
|
|
||||||
|
|
||||||
app.use('/demo', basicAuth({
|
|
||||||
users: { 'admin': 'socks' }
|
|
||||||
}))
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
copy .\\node_modules\\leaflet\\dist\\leaflet.css .\\public\\stylesheets\\leaflet\\leaflet.css
|
copy .\\node_modules\\leaflet\\dist\\leaflet.css .\\public\\stylesheets\\leaflet\\leaflet.css
|
||||||
copy .\\node_modules\\leaflet.nauticscale\\dist\\leaflet.nauticscale.js .\\public\\javascripts\\leaflet.nauticscale.js
|
copy .\\node_modules\\leaflet.nauticscale\\dist\\leaflet.nauticscale.js .\\public\\javascripts\\leaflet.nauticscale.js
|
||||||
|
copy .\\node_modules\\leaflet-path-drag\\dist\\L.Path.Drag.js .\\public\\javascripts\\L.Path.Drag.js
|
||||||
979
client/demo.js
3377
client/package-lock.json
generated
@@ -2,7 +2,7 @@
|
|||||||
"name": "DCSOlympus",
|
"name": "DCSOlympus",
|
||||||
"node-main": "./bin/www",
|
"node-main": "./bin/www",
|
||||||
"main": "http://localhost:3000",
|
"main": "http://localhost:3000",
|
||||||
"version": "v0.3.0-alpha",
|
"version": "v0.4.0-alpha",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"copy": "copy.bat",
|
"copy": "copy.bat",
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
"watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
|
"watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@turf/turf": "^6.5.0",
|
||||||
"@types/formatcoords": "^1.1.0",
|
"@types/formatcoords": "^1.1.0",
|
||||||
"@types/geojson": "^7946.0.10",
|
"@types/geojson": "^7946.0.10",
|
||||||
"@types/leaflet": "^1.9.0",
|
"@types/leaflet": "^1.9.0",
|
||||||
@@ -21,6 +22,7 @@
|
|||||||
"formatcoords": "^1.1.3",
|
"formatcoords": "^1.1.3",
|
||||||
"leaflet": "^1.9.3",
|
"leaflet": "^1.9.3",
|
||||||
"leaflet-control-mini-map": "^0.4.0",
|
"leaflet-control-mini-map": "^0.4.0",
|
||||||
|
"leaflet-path-drag": "*",
|
||||||
"leaflet.nauticscale": "^1.1.0",
|
"leaflet.nauticscale": "^1.1.0",
|
||||||
"morgan": "~1.9.1",
|
"morgan": "~1.9.1",
|
||||||
"save": "^2.9.0"
|
"save": "^2.9.0"
|
||||||
|
|||||||
6
client/public/javascripts/L.Path.Drag.js
Normal file
@@ -5,13 +5,24 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#primary-toolbar {
|
#toolbar-container {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
left: 10px;
|
left: 10px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
column-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#primary-toolbar {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-toolbar {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
#app-icon>.ol-select-options {
|
#app-icon>.ol-select-options {
|
||||||
@@ -42,6 +53,15 @@
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#server-status-panel {
|
||||||
|
bottom: 20px;
|
||||||
|
font-size: 12px;
|
||||||
|
position: absolute;
|
||||||
|
right: 200px;
|
||||||
|
width: 300px;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
#mouse-info-panel {
|
#mouse-info-panel {
|
||||||
bottom: 60px;
|
bottom: 60px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -81,4 +101,17 @@
|
|||||||
left: 50%;
|
left: 50%;
|
||||||
translate: -50% 0%;
|
translate: -50% 0%;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log-panel {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 220px;
|
||||||
|
width: 310px;
|
||||||
|
height: fit-content;
|
||||||
|
z-index: 9990;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 10px;
|
||||||
}
|
}
|
||||||
@@ -14,11 +14,11 @@
|
|||||||
background: var(--secondary-gunmetal-grey);
|
background: var(--secondary-gunmetal-grey);
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-self: center;
|
justify-self: center;
|
||||||
padding-bottom: calc((var(--unit-aircraft-width) / 2) + var(--unit-stroke-width));
|
padding-bottom: calc((var(--unit-width) / 2) + var(--unit-stroke-width));
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform-origin: bottom;
|
transform-origin: bottom;
|
||||||
translate: 0 -50%;
|
translate: 0 -50%;
|
||||||
width: var(--unit-aircraft-vvi-width);
|
width: var(--unit-vvi-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
.unit-hotgroup {
|
.unit-hotgroup {
|
||||||
@@ -100,13 +100,13 @@
|
|||||||
/*** Fuel indicator ***/
|
/*** Fuel indicator ***/
|
||||||
[data-object|="unit"] .unit-fuel {
|
[data-object|="unit"] .unit-fuel {
|
||||||
background: white;
|
background: white;
|
||||||
border: var(--unit-aircraft-fuel-border-width) solid var(--secondary-dark-steel);
|
border: var(--unit-fuel-border-width) solid var(--secondary-dark-steel);
|
||||||
border-radius: var(--border-radius-sm);
|
border-radius: var(--border-radius-sm);
|
||||||
display: none;
|
display: none;
|
||||||
height: var(--unit-aircraft-fuel-height);
|
height: var(--unit-fuel-height);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
translate: var(--unit-aircraft-fuel-x) var(--unit-aircraft-fuel-y);
|
translate: var(--unit-fuel-x) var(--unit-fuel-y);
|
||||||
width: var(--unit-aircraft-fuel-width);
|
width: var(--unit-fuel-width);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-object|="unit"] .unit-fuel-level {
|
[data-object|="unit"] .unit-fuel-level {
|
||||||
@@ -117,19 +117,19 @@
|
|||||||
|
|
||||||
/*** Ammo indicator ***/
|
/*** Ammo indicator ***/
|
||||||
[data-object|="unit"] .unit-ammo {
|
[data-object|="unit"] .unit-ammo {
|
||||||
column-gap: var(--unit-aircraft-ammo-spacing);
|
column-gap: var(--unit-ammo-spacing);
|
||||||
display: none;
|
display: none;
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
translate: var(--unit-aircraft-ammo-x) var(--unit-aircraft-ammo-y);
|
translate: var(--unit-ammo-x) var(--unit-ammo-y);
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-object|="unit"] .unit-ammo>* {
|
[data-object|="unit"] .unit-ammo>* {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: var(--unit-aircraft-ammo-border-width) solid var(--secondary-dark-steel);
|
border: var(--unit-ammo-border-width) solid var(--secondary-dark-steel);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: var(--unit-aircraft-ammo-radius);
|
padding: var(--unit-ammo-radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Unit summary ***/
|
/*** Unit summary ***/
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
1px -1px 0 #000,
|
1px -1px 0 #000,
|
||||||
-1px 1px 0 #000,
|
-1px 1px 0 #000,
|
||||||
1px 1px 0 #000;
|
1px 1px 0 #000;
|
||||||
translate: -60px 0;
|
right: 100%;
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
width: 80px;
|
width: 80px;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-object|="unit"] .unit-summary .unit-callsign:hover {
|
[data-object|="unit"]:hover .unit-summary .unit-callsign{
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
@@ -292,23 +292,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*** Dead unit ***/
|
/*** Dead unit ***/
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-selected-spotlight,
|
[data-object|="unit"][data-is-dead] .unit-selected-spotlight,
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-short-label,
|
[data-object|="unit"][data-is-dead] .unit-short-label,
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-vvi,
|
[data-object|="unit"][data-is-dead] .unit-vvi,
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup,
|
[data-object|="unit"][data-is-dead] .unit-hotgroup,
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup-id,
|
[data-object|="unit"][data-is-dead] .unit-hotgroup-id,
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-state,
|
[data-object|="unit"][data-is-dead] .unit-state,
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-fuel,
|
[data-object|="unit"][data-is-dead] .unit-fuel,
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-ammo,
|
[data-object|="unit"][data-is-dead] .unit-ammo,
|
||||||
[data-object|="unit-aircraft"][data-is-dead]:hover .unit-fuel,
|
[data-object|="unit"][data-is-dead]:hover .unit-fuel,
|
||||||
[data-object|="unit-aircraft"][data-is-dead]:hover .unit-ammo {
|
[data-object|="unit"][data-is-dead]:hover .unit-ammo {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-summary>* {
|
[data-object|="unit"][data-is-dead] .unit-summary>* {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-object|="unit-aircraft"][data-is-dead] .unit-summary .unit-callsign {
|
[data-object|="unit"][data-is-dead] .unit-summary .unit-callsign {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ol-temporary-marker {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
@@ -3,9 +3,11 @@
|
|||||||
@import url("atc/unitdatatable.css");
|
@import url("atc/unitdatatable.css");
|
||||||
@import url("aic/aic.css");
|
@import url("aic/aic.css");
|
||||||
@import url("panels/connectionstatus.css");
|
@import url("panels/connectionstatus.css");
|
||||||
|
@import url("panels/serverstatus.css");
|
||||||
@import url("panels/mouseinfo.css");
|
@import url("panels/mouseinfo.css");
|
||||||
@import url("panels/unitcontrol.css");
|
@import url("panels/unitcontrol.css");
|
||||||
@import url("panels/unitinfo.css");
|
@import url("panels/unitinfo.css");
|
||||||
|
@import url("panels/logpanel.css");
|
||||||
@import url("other/contextmenus.css");
|
@import url("other/contextmenus.css");
|
||||||
@import url("other/popup.css");
|
@import url("other/popup.css");
|
||||||
@import url("markers/airbase.css");
|
@import url("markers/airbase.css");
|
||||||
@@ -20,6 +22,7 @@
|
|||||||
|
|
||||||
html * {
|
html * {
|
||||||
font-family: 'Open Sans', sans-serif !important;
|
font-family: 'Open Sans', sans-serif !important;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
@@ -38,6 +41,11 @@ body {
|
|||||||
cursor: none !important;
|
cursor: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hidden-cursor * {
|
||||||
|
cursor: none !important;
|
||||||
|
pointer-events: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
@@ -124,7 +132,7 @@ form>div {
|
|||||||
|
|
||||||
.ol-panel {
|
.ol-panel {
|
||||||
background-color: var(--background-steel);
|
background-color: var(--background-steel);
|
||||||
border-radius: 15px;
|
border-radius: var(--border-radius-md);;
|
||||||
box-shadow: 0px 2px 5px #000A;
|
box-shadow: 0px 2px 5px #000A;
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -408,7 +416,6 @@ nav.ol-panel> :last-child {
|
|||||||
|
|
||||||
.ol-panel .ol-group-button-toggle {
|
.ol-panel .ol-group-button-toggle {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 15px;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: nowrap;
|
flex-wrap: nowrap;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -421,7 +428,7 @@ nav.ol-panel> :last-child {
|
|||||||
border: 0;
|
border: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-items: left;
|
justify-items: left;
|
||||||
text-indent: 5px;
|
text-indent: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ol-panel .ol-group-button-toggle button::before {
|
.ol-panel .ol-group-button-toggle button::before {
|
||||||
@@ -624,39 +631,39 @@ nav.ol-panel> :last-child {
|
|||||||
width: 28px;
|
width: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-visibility-control {
|
.ol-navbar-buttons-group {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-visibility-control button {
|
.ol-navbar-buttons-group button {
|
||||||
border: none;
|
border: none;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-visibility-control button svg {
|
.ol-navbar-buttons-group button svg {
|
||||||
height: 16px;
|
height: 16px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-visibility-control button {
|
.ol-navbar-buttons-group button {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-visibility-control button.off {
|
.ol-navbar-buttons-group button.off {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid white;
|
border: 1px solid white;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-visibility-control button.off svg * {
|
.ol-navbar-buttons-group button.off svg * {
|
||||||
fill: white !important;
|
fill: white !important;
|
||||||
stroke: white !important;
|
stroke: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-visibility-control button svg * {
|
.ol-navbar-buttons-group button svg * {
|
||||||
fill: var(--background-steel) !important;
|
fill: var(--background-steel) !important;
|
||||||
stroke: var(--background-steel) !important;
|
stroke: var(--background-steel) !important;
|
||||||
}
|
}
|
||||||
@@ -667,10 +674,9 @@ nav.ol-panel> :last-child {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
#atc-navbar-control button {
|
#atc-navbar-control button svg {
|
||||||
background: #ffffff20;
|
height: 24px;
|
||||||
border-radius: var(--border-radius-sm);
|
width: 24px;
|
||||||
padding: 4px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#roe-buttons-container button,
|
#roe-buttons-container button,
|
||||||
@@ -699,7 +705,7 @@ nav.ol-panel> :last-child {
|
|||||||
background-image: url("/resources/theme/images/splash/1.png");
|
background-image: url("/resources/theme/images/splash/1.png");
|
||||||
background-position: 100% 50%;
|
background-position: 100% 50%;
|
||||||
background-size: 60%;
|
background-size: 60%;
|
||||||
border-radius: var(--border-radius-lg);
|
border-radius: var(--border-radius-md);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
width: 1200px;
|
width: 1200px;
|
||||||
z-index: 99999;
|
z-index: 99999;
|
||||||
@@ -809,16 +815,16 @@ nav.ol-panel> :last-child {
|
|||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#connection-status {
|
#login-status {
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#connection-status[data-status="connecting"]::before {
|
#login-status[data-status="connecting"]::before {
|
||||||
animation: blinker 1s linear infinite;
|
animation: blinker 1s linear infinite;
|
||||||
content: "Connecting...";
|
content: "Connecting...";
|
||||||
}
|
}
|
||||||
|
|
||||||
#connection-status[data-status="failed"]::before {
|
#login-status[data-status="failed"]::before {
|
||||||
color: var(--primary-red);
|
color: var(--primary-red);
|
||||||
content: "Incorrect username/password!";
|
content: "Incorrect username/password!";
|
||||||
}
|
}
|
||||||
@@ -862,6 +868,130 @@ nav.ol-panel> :last-child {
|
|||||||
translate: 0% -300%;
|
translate: 0% -300%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#command-mode {
|
||||||
|
display: flex;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bolder;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-left: -11px;
|
||||||
|
margin-top: -0px;
|
||||||
|
margin-bottom: -0px;
|
||||||
|
height: 58px;
|
||||||
|
padding: 10px;
|
||||||
|
border-top-left-radius: var(--border-radius-md);
|
||||||
|
border-bottom-left-radius: var(--border-radius-md);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode[data-mode="Game master"] {
|
||||||
|
background-color: lightgray;
|
||||||
|
color: var(--secondary-gunmetal-grey);
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode[data-mode="Blue commander"] {
|
||||||
|
background-color: var(--primary-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode[data-mode="Red commander"] {
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
#spawn-points-container {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#spawn-points {
|
||||||
|
background-color: var(--background-grey);
|
||||||
|
padding: 5px 15px;
|
||||||
|
margin-left: 15px;
|
||||||
|
border: 1px white solid;
|
||||||
|
font-size: 14px;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
#spawn-points-container {
|
||||||
|
height: 100%;
|
||||||
|
border-right: 1px solid gray;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-phase::before {
|
||||||
|
content: "Time to start";
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-phase.setup-phase::after {
|
||||||
|
color: orange;
|
||||||
|
border: 1px solid orange;
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background-color: var(--background-grey);
|
||||||
|
margin-left: 15px;
|
||||||
|
content: attr(data-remaining-time);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-phase.game-commenced {
|
||||||
|
background-color: var(--background-grey);
|
||||||
|
color: lightgreen;
|
||||||
|
border: 1px solid lightgreen;
|
||||||
|
padding: 5px 15px;
|
||||||
|
border-radius: var(--border-radius-md);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-phase.game-commenced::before {
|
||||||
|
content: "Game commenced";
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-phase.game-commenced::after {
|
||||||
|
content: "Spawn restrictions are being enforced";
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-phase.no-restrictions::after {
|
||||||
|
content: "No spawn restrictions";
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-toolbar .ol-button {
|
||||||
|
border: 1px solid white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-toolbar .ol-button>svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-settings-dialog {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-settings-dialog>.ol-dialog-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
row-gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-settings-dialog>.ol-dialog-content .ol-group {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#command-mode-settings-dialog h4 {
|
||||||
|
white-space: nowrap;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
.ol-destination-preview-icon {
|
.ol-destination-preview-icon {
|
||||||
background-image: url("/resources/theme/images/markers/move.svg");
|
background-image: url("/resources/theme/images/markers/move.svg");
|
||||||
height: 52px;
|
height: 52px;
|
||||||
@@ -877,6 +1007,33 @@ nav.ol-panel> :last-child {
|
|||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ol-draw-icon {
|
||||||
|
background-image: url("/resources/theme/images/markers/draw.svg");
|
||||||
|
height: 24px;
|
||||||
|
pointer-events: none;
|
||||||
|
width: 24px;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-coalitionarea-handle-icon,
|
||||||
|
.ol-coalitionarea-middle-handle-icon {
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 9999;
|
||||||
|
border-radius: 999px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-coalitionarea-handle-icon {
|
||||||
|
background-color: #FFFFFFEE;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-coalitionarea-middle-handle-icon {
|
||||||
|
background-color: #FFFFFFAA;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
dl.ol-data-grid {
|
dl.ol-data-grid {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -1135,3 +1292,84 @@ input[type=number]::-webkit-outer-spin-button {
|
|||||||
.ol-switch[data-value="undefined"]>.ol-switch-fill::after {
|
.ol-switch[data-value="undefined"]>.ol-switch-fill::after {
|
||||||
transform: translateX(calc((var(--width) - var(--height)) * 0.5));
|
transform: translateX(calc((var(--width) - var(--height)) * 0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ol-contexmenu-panel {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-coalition-switch[data-value="false"]>.ol-switch-fill {
|
||||||
|
background-color: var(--primary-blue);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-coalition-switch[data-value="true"]>.ol-switch-fill {
|
||||||
|
background-color: var(--primary-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-coalition-switch[data-value="undefined"]>.ol-switch-fill {
|
||||||
|
background-color: var(--primary-neutral);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-context-menu>ul {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-context-menu .ol-panel {
|
||||||
|
border-radius: var(--border-radius-sm);
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-context-menu ul {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-context-menu .ol-select-container {
|
||||||
|
align-self: stretch;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-contexmenu-button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 0px;
|
||||||
|
height: 48px;
|
||||||
|
margin-bottom: -10px;
|
||||||
|
margin-top: -10px;
|
||||||
|
width: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-contexmenu-button:last-of-type {
|
||||||
|
border-bottom-right-radius: var(--border-radius-sm);
|
||||||
|
border-top-right-radius: var(--border-radius-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-coalition="blue"].ol-contexmenu-button:hover,
|
||||||
|
[data-coalition="blue"].ol-contexmenu-button.is-open {
|
||||||
|
background-color: var(--primary-blue)
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-coalition="red"].ol-contexmenu-button:hover,
|
||||||
|
[data-coalition="red"].ol-contexmenu-button.is-open {
|
||||||
|
background-color: var(--primary-red)
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-coalition="neutral"].ol-contexmenu-button:hover,
|
||||||
|
[data-coalition="neutral"].ol-contexmenu-button.is-open {
|
||||||
|
background-color: var(--primary-neutral)
|
||||||
|
}
|
||||||
|
|
||||||
|
#map-type svg,
|
||||||
|
#map-visibility-options svg {
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
fill: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-log-entry:first-of-type {
|
||||||
|
border-top: 1px solid #FFFFFF44;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ol-log-entry {
|
||||||
|
border-bottom: 1px solid #FFFFFF44;
|
||||||
|
}
|
||||||
@@ -4,15 +4,38 @@
|
|||||||
height: fit-content;
|
height: fit-content;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
row-gap: 5px;
|
row-gap: 5px;
|
||||||
width: 280px;
|
width: 300px;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
#aircraft-spawn-menu {
|
#map-contextmenu>div:nth-child(2) {
|
||||||
height: fit-content;
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-right: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ground-unit-spawn-menu {
|
#map-contextmenu>div:nth-child(3) {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#map-contextmenu>div:nth-child(n+4) {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
row-gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#aircraft-spawn-menu,
|
||||||
|
#helicopter-spawn-menu,
|
||||||
|
#groundunit-spawn-menu,
|
||||||
|
#navyunit-spawn-menu {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,154 +58,87 @@
|
|||||||
width: 50px;
|
width: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#coalition-switch[data-value="false"]>.ol-switch-fill {
|
#aircraft-spawn-menu .ol-select.is-open .ol-select-options,
|
||||||
background-color: var(--primary-blue);
|
#helicopter-spawn-menu .ol-select.is-open .ol-select-options {
|
||||||
}
|
|
||||||
|
|
||||||
#coalition-switch[data-value="true"]>.ol-switch-fill {
|
|
||||||
background-color: var(--primary-red);
|
|
||||||
}
|
|
||||||
|
|
||||||
#coalition-switch[data-value="undefined"]>.ol-switch-fill {
|
|
||||||
background-color: var(--primary-neutral);
|
|
||||||
}
|
|
||||||
|
|
||||||
#map-contextmenu>div:nth-child(2) {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map-contextmenu>ul {
|
|
||||||
max-height: 200px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map-contextmenu .ol-panel {
|
|
||||||
border-radius: var(--border-radius-sm);
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map-contextmenu ul {
|
|
||||||
margin: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map-contextmenu>div:nth-child(n+3) {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: space-between;
|
|
||||||
row-gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#map-contextmenu .ol-select-container {
|
|
||||||
align-self: stretch;
|
|
||||||
flex: 0 0 auto;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
#aircraft-spawn-menu .ol-select.is-open .ol-select-options {
|
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#aircraft-spawn-menu>button,
|
.deploy-unit-button {
|
||||||
#ground-unit-spawn-menu>button {
|
margin-top: 5px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#aircraft-spawn-button {
|
.deploy-unit-button[data-points]:not([data-points='']):not([data-points='0']):not([data-points='Infinity'])::after {
|
||||||
background-image: url("/resources/theme/images/buttons/spawn/aircraft.svg");
|
content: " (" attr(data-points) " points)";
|
||||||
background-size: 48px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ground-unit-spawn-button {
|
.upper-bar svg>* {
|
||||||
background-image: url("/resources/theme/images/buttons/spawn/ground.svg");
|
fill: white;
|
||||||
background-size: 48px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#smoke-spawn-button {
|
.upper-bar svg {
|
||||||
background-image: url("/resources/theme/images/buttons/spawn/smoke.svg");
|
width: 22px;
|
||||||
background-size: 48px;
|
margin: 0px 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#explosion-spawn-button {
|
.upper-bar button:nth-child(2) {
|
||||||
background-image: url("/resources/theme/images/buttons/spawn/explosion.svg");
|
margin-left: auto;
|
||||||
background-size: 48px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.unit-spawn-button {
|
[data-coalition="blue"]#active-coalition-label,
|
||||||
border: none;
|
[data-coalition="blue"].deploy-unit-button,
|
||||||
border-radius: 0px;
|
[data-coalition="blue"]#spawn-airbase-aircraft-button,
|
||||||
height: 48px;
|
[data-coalition="blue"].create-iads-button {
|
||||||
margin-bottom: -10px;
|
|
||||||
margin-top: -10px;
|
|
||||||
width: 48px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.unit-spawn-button:last-of-type {
|
|
||||||
border-bottom-right-radius: var(--border-radius-sm);
|
|
||||||
border-top-right-radius: var(--border-radius-sm);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-active-coalition="blue"].unit-spawn-button:hover,
|
|
||||||
[data-active-coalition="blue"].unit-spawn-button.is-open,
|
|
||||||
[data-active-coalition="blue"]#active-coalition-label,
|
|
||||||
[data-active-coalition="blue"].deploy-unit-button,
|
|
||||||
[data-active-coalition="blue"]#spawn-airbase-aircraft-button {
|
|
||||||
background-color: var(--primary-blue)
|
background-color: var(--primary-blue)
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="red"].unit-spawn-button:hover,
|
[data-coalition="red"]#active-coalition-label,
|
||||||
[data-active-coalition="red"].unit-spawn-button.is-open,
|
[data-coalition="red"].deploy-unit-button,
|
||||||
[data-active-coalition="red"]#active-coalition-label,
|
[data-coalition="red"]#spawn-airbase-aircraft-button,
|
||||||
[data-active-coalition="red"].deploy-unit-button,
|
[data-coalition="red"].create-iads-button {
|
||||||
[data-active-coalition="red"]#spawn-airbase-aircraft-button {
|
|
||||||
background-color: var(--primary-red)
|
background-color: var(--primary-red)
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="neutral"].unit-spawn-button:hover,
|
[data-coalition="neutral"]#active-coalition-label,
|
||||||
[data-active-coalition="neutral"].unit-spawn-button.is-open,
|
[data-coalition="neutral"].deploy-unit-button,
|
||||||
[data-active-coalition="neutral"]#active-coalition-label,
|
[data-coalition="neutral"]#spawn-airbase-aircraft-button,
|
||||||
[data-active-coalition="neutral"].deploy-unit-button,
|
[data-coalition="neutral"].create-iads-button {
|
||||||
[data-active-coalition="neutral"]#spawn-airbase-aircraft-button {
|
|
||||||
background-color: var(--primary-neutral)
|
background-color: var(--primary-neutral)
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="blue"].deploy-unit-button:disabled {
|
[data-coalition="blue"].deploy-unit-button:disabled {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid var(--primary-blue);
|
border: 1px solid var(--primary-blue);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="red"].deploy-unit-button:disabled {
|
[data-coalition="red"].deploy-unit-button:disabled {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid var(--primary-red);
|
border: 1px solid var(--primary-red);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="neutral"].deploy-unit-button:disabled {
|
[data-coalition="neutral"].deploy-unit-button:disabled {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid var(--primary-neutral);
|
border: 1px solid var(--primary-neutral);
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="blue"]#active-coalition-label::after {
|
[data-coalition="blue"]#active-coalition-label::after {
|
||||||
content: "Create blue unit";
|
content: "Create blue unit";
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="red"]#active-coalition-label::after {
|
[data-coalition="red"]#active-coalition-label::after {
|
||||||
content: "Create red unit";
|
content: "Create red unit";
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-active-coalition="neutral"]#active-coalition-label::after {
|
[data-coalition="neutral"]#active-coalition-label::after {
|
||||||
content: "Create neutral unit";
|
content: "Create neutral unit";
|
||||||
}
|
}
|
||||||
|
|
||||||
#loadout-preview {
|
#aircraft-loadout-preview,
|
||||||
|
#helicopter-loadout-preview {
|
||||||
align-content: space-between;
|
align-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
column-gap: 20px;
|
column-gap: 20px;
|
||||||
@@ -191,14 +147,16 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#loadout-list {
|
#aircaft-loadout-list,
|
||||||
|
#helicopter-loadout-list {
|
||||||
align-content: center;
|
align-content: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#unit-image {
|
#aircraft-unit-image,
|
||||||
|
#helicopter-unit-image {
|
||||||
filter: invert(100%);
|
filter: invert(100%);
|
||||||
height: 100px;
|
height: 100px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
@@ -251,17 +209,28 @@
|
|||||||
background-color: orange;
|
background-color: orange;
|
||||||
}
|
}
|
||||||
|
|
||||||
#aircraft-spawn-menu .ol-slider-value {
|
.ol-context-menu .ol-slider-value {
|
||||||
color: var(--accent-light-blue);
|
color: var(--accent-light-blue);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
#aircraft-spawn-altitude-slider {
|
.ol-context-menu .ol-slider-container {
|
||||||
padding: 0px 10px;
|
padding: 0px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.contextmenu-options-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contextmenu-options-container>*:nth-child(2) {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Unit context menu */
|
/* Unit context menu */
|
||||||
#unit-contextmenu {
|
#unit-contextmenu {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -407,3 +376,71 @@
|
|||||||
width: 180px;
|
width: 180px;
|
||||||
z-index: 9999;
|
z-index: 9999;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Coalition area context menu */
|
||||||
|
#coalition-area-contextmenu {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: fit-content;
|
||||||
|
position: absolute;
|
||||||
|
row-gap: 5px;
|
||||||
|
width: 300px;
|
||||||
|
z-index: 9999;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coalition-area-switch {
|
||||||
|
margin-right: 10px;
|
||||||
|
height: 25px;
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coalition-area-contextmenu .ol-checkbox {
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coalition-units-checkbox {
|
||||||
|
padding: 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iads-menu .ol-select-options>* {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iads-menu .ol-select-options>*:first-child {
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iads-menu .ol-select-options>*:last-child {
|
||||||
|
padding-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iads-menu .ol-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#iads-menu {
|
||||||
|
row-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coalition-area-contextmenu>div:nth-child(2) {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#coalition-area-contextmenu>div:nth-child(n+3) {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
row-gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-iads-button {
|
||||||
|
margin-top: 5px;
|
||||||
|
text-align: center;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
28
client/public/stylesheets/panels/logpanel.css
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#log-panel>div:first-child {
|
||||||
|
width: 100%;
|
||||||
|
height: 38px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log-panel svg {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log-panel>div:nth-child(2) {
|
||||||
|
display: none;
|
||||||
|
margin-top: 5px;
|
||||||
|
width: 100%;
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
background-color: #00000055;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log-panel.open {
|
||||||
|
height: 505px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#log-panel.open>div:nth-child(2) {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
43
client/public/stylesheets/panels/serverstatus.css
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#server-status-panel {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
column-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#server-status-panel .ol-data-grid {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#server-status-panel .ol-data-grid:first-of-type {
|
||||||
|
border-right: 1px solid gray;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#server-status-panel dd {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fps-low {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fps-medium {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fps-high {
|
||||||
|
color: lightgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-low {
|
||||||
|
color: lightgreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-medium {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
||||||
|
.load-high {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
|||||||
|
|
||||||
#unit-control-panel #selected-units-container button {
|
#unit-control-panel #selected-units-container button {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-radius: var(--border-radius-lg);
|
border-radius: var(--border-radius-md);
|
||||||
display: flex;
|
display: flex;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#current-task {
|
#current-task {
|
||||||
border-radius: var(--border-radius-lg);
|
border-radius: var(--border-radius-md);
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
41
client/public/themes/olympus/images/buttons/other/back.svg
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="back.svg"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
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.918757"
|
||||||
|
inkscape:cy="17.663082"
|
||||||
|
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 29.911273,24.547406 H 2.1935652 c -0.9582092,0 -1.73235714,0.774146 -1.73235714,1.732357 0,0.958208 0.77414794,1.732356 1.73235714,1.732356 H 29.911273 c 0.958209,0 1.732357,-0.774148 1.732357,-1.732356 0,-0.958211 -0.774148,-1.732357 -1.732357,-1.732357 z m -7.215201,-9.890675 c -0.676702,-0.676702 -1.775666,-0.676702 -2.452368,0 l -2.235822,2.241238 V 5.4914811 c 0,-0.9582093 -0.774146,-1.7323555 -1.732357,-1.7323555 -0.958211,0 -1.732355,0.7741462 -1.732355,1.7323555 V 16.897969 l -2.241238,-2.241238 c -0.676702,-0.676702 -1.775666,-0.676702 -2.4523675,0 -0.6767003,0.676702 -0.6767003,1.775666 0,2.452368 l 5.1970695,5.197069 c 0.676701,0.676702 1.775668,0.676702 2.452368,0 l 5.19707,-5.197069 c 0.676703,-0.676702 0.676703,-1.775666 0,-2.452368 z"
|
||||||
|
id="path2"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0541362"
|
||||||
|
sodipodi:nodetypes="ssssssssccssscssccccs" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
40
client/public/themes/olympus/images/buttons/other/delete.svg
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
version="1.1"
|
||||||
|
id="svg1940"
|
||||||
|
sodipodi:docname="delete.svg"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
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="defs1944" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="namedview1942"
|
||||||
|
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="27.870968"
|
||||||
|
inkscape:cy="20.415771"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg1940" />
|
||||||
|
<!--! 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 13.120438,4.047182 -1.120204,1.6744053 h 8.548913 L 19.428943,4.047182 C 19.340506,3.9174745 19.193112,3.8349334 19.033925,3.8349334 h -5.524364 c -0.159185,0 -0.306581,0.076645 -0.39502,0.2122486 z m 8.666825,-1.5682824 2.163757,3.2426877 h 0.813623 2.829981 0.471664 c 0.784142,0 1.414993,0.6308515 1.414993,1.4149933 0,0.7841418 -0.630851,1.4149912 -1.414993,1.4149912 H 27.594624 V 26.474805 c 0,2.605944 -2.110695,4.716638 -4.716639,4.716638 H 9.6713954 c -2.6059417,0 -4.7166402,-2.110694 -4.7166402,-4.716638 V 8.5515718 H 4.4830908 c -0.7841402,0 -1.4149907,-0.6308494 -1.4149907,-1.4149912 0,-0.7841418 0.6308505,-1.4149933 1.4149907,-1.4149933 H 4.9547552 7.7847397 8.5983591 L 10.762118,2.4730039 C 11.375281,1.559155 12.407047,1.0049496 13.509561,1.0049496 h 5.524364 c 1.102514,0 2.13428,0.5542053 2.747441,1.4680543 z M 7.7847397,8.5515718 V 26.474805 c 0,1.043557 0.8430989,1.886656 1.8866557,1.886656 H 22.877985 c 1.043557,0 1.886658,-0.843099 1.886658,-1.886656 V 8.5515718 Z m 4.7166383,3.7733162 v 12.263261 c 0,0.51883 -0.424497,0.943327 -0.943327,0.943327 -0.51883,0 -0.943327,-0.424497 -0.943327,-0.943327 V 12.324888 c 0,-0.518832 0.424497,-0.943328 0.943327,-0.943328 0.51883,0 0.943327,0.424496 0.943327,0.943328 z m 4.71664,0 v 12.263261 c 0,0.51883 -0.424499,0.943327 -0.943327,0.943327 -0.518832,0 -0.943328,-0.424497 -0.943328,-0.943327 V 12.324888 c 0,-0.518832 0.424496,-0.943328 0.943328,-0.943328 0.518828,0 0.943327,0.424496 0.943327,0.943328 z m 4.71664,0 v 12.263261 c 0,0.51883 -0.424498,0.943327 -0.943328,0.943327 -0.518829,0 -0.943327,-0.424497 -0.943327,-0.943327 V 12.324888 c 0,-0.518832 0.424498,-0.943328 0.943327,-0.943328 0.51883,0 0.943328,0.424496 0.943328,0.943328 z"
|
||||||
|
id="path1938"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0589579;stroke-opacity:1" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 508 B |
@@ -1,20 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="32"
|
width="32"
|
||||||
height="32"
|
height="32"
|
||||||
viewBox="0 0 32 32"
|
viewBox="0 0 32 32"
|
||||||
fill="none"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg8"
|
id="svg8"
|
||||||
sodipodi:docname="spawn_aircraft.svg"
|
sodipodi:docname="aircraft.svg"
|
||||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)">
|
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"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata14">
|
id="metadata14">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
@@ -44,33 +43,18 @@
|
|||||||
id="namedview10"
|
id="namedview10"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="18.782524"
|
inkscape:zoom="18.782524"
|
||||||
inkscape:cx="26.073424"
|
inkscape:cx="26.114701"
|
||||||
inkscape:cy="15.446316"
|
inkscape:cy="15.493125"
|
||||||
inkscape:window-x="1912"
|
inkscape:window-x="1912"
|
||||||
inkscape:window-y="-8"
|
inkscape:window-y="-8"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg8" />
|
inkscape:current-layer="svg8"
|
||||||
<rect
|
inkscape:showpageshadow="2"
|
||||||
x="0.5"
|
inkscape:pagecheckerboard="0"
|
||||||
y="0.5"
|
inkscape:deskcolor="#d1d1d1" />
|
||||||
width="31"
|
|
||||||
height="31"
|
|
||||||
rx="7.5"
|
|
||||||
fill="white"
|
|
||||||
id="rect2"
|
|
||||||
style="fill:none" />
|
|
||||||
<path
|
<path
|
||||||
d="m 22.011184,13.780381 c 1.003947,0 2.775568,0.85633 2.775568,1.889757 0,1.063016 -1.771621,1.889759 -2.775568,1.889759 h -3.454693 l -2.952773,5.196833 c -0.177205,0.295233 -0.502027,0.472439 -0.826742,0.472439 h -1.653592 c -0.324822,0 -0.560988,-0.295235 -0.472439,-0.590577 l 1.446906,-5.078695 H 11.086009 L 9.786807,19.272448 C 9.698217,19.390585 9.58011,19.449654 9.432472,19.449654 H 8.192315 c -0.236219,0 -0.413381,-0.177206 -0.413381,-0.413371 0,-0.02959 0,-0.05907 0,-0.08855 L 8.723813,15.670138 7.778934,12.422131 c 0,-0.02959 0,-0.05907 0,-0.118137 0,-0.206685 0.177162,-0.413371 0.413381,-0.413371 h 1.240157 c 0.147638,0 0.265743,0.08855 0.354335,0.206686 l 1.299202,1.683072 h 3.011842 L 12.650945,8.7311863 C 12.562399,8.435909 12.798562,8.1111082 13.123384,8.1111082 h 1.653592 c 0.324715,0 0.649537,0.2066963 0.826742,0.5019629 l 2.952773,5.1673099 z"
|
d="m 25.924821,12.489287 c 1.68985,0 4.671853,1.44138 4.671853,3.180851 0,1.789275 -2.982003,3.180853 -4.671853,3.180853 h -5.81496 l -4.970125,8.747341 c -0.298273,0.496938 -0.845015,0.795212 -1.391577,0.795212 h -2.783336 c -0.546743,0 -0.944259,-0.496941 -0.795213,-0.994063 l 2.435441,-8.54849 H 7.5355007 L 5.348676,21.733567 C 5.1995608,21.932416 5.0007624,22.031842 4.7522572,22.031842 H 2.6648175 c -0.3976052,0 -0.6958054,-0.298275 -0.6958054,-0.695789 0,-0.04981 0,-0.09943 0,-0.149048 l 1.590426,-5.516867 -1.590426,-5.467065 c 0,-0.04981 0,-0.09943 0,-0.198849 0,-0.3478937 0.2982002,-0.6957888 0.6958054,-0.6957888 h 2.0874397 c 0.2485052,0 0.4473003,0.1490479 0.5964188,0.3478951 L 7.5355007,12.489287 H 12.605051 L 10.16961,3.9904523 C 10.020569,3.4934397 10.41808,2.9467331 10.964823,2.9467331 h 2.783336 c 0.546562,0 1.093304,0.3479124 1.391577,0.8449069 l 4.970125,8.697647 z"
|
||||||
fill="#202831"
|
fill="#202831"
|
||||||
id="path4"
|
id="path4"
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.07987" />
|
style="fill-opacity:1;stroke-width:1.81764" />
|
||||||
<rect
|
|
||||||
x="0.5"
|
|
||||||
y="0.5"
|
|
||||||
width="31"
|
|
||||||
height="31"
|
|
||||||
rx="7.5"
|
|
||||||
stroke="white"
|
|
||||||
id="rect6"
|
|
||||||
style="fill:none;stroke:none" />
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -24,7 +24,7 @@
|
|||||||
inkscape:deskcolor="#d1d1d1"
|
inkscape:deskcolor="#d1d1d1"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="13.078125"
|
inkscape:zoom="13.078125"
|
||||||
inkscape:cx="19.53644"
|
inkscape:cx="18.389486"
|
||||||
inkscape:cy="12.157706"
|
inkscape:cy="12.157706"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1017"
|
inkscape:window-height="1017"
|
||||||
@@ -34,9 +34,9 @@
|
|||||||
inkscape:current-layer="svg4" />
|
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. -->
|
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||||
<path
|
<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"
|
d="m 26.759564,2.1225723 c 0.340688,-0.6068538 1.042401,-0.822372 1.611908,-0.4820802 0.569507,0.3402919 0.80341,1.1059486 0.538997,1.7581747 L 21.939094,20.685494 c 0.111864,0.130442 0.218653,0.266561 0.320348,0.402678 l 4.942507,-3.102328 c 0.533912,-0.334619 1.200033,-0.175817 1.571229,0.362979 0.371196,0.538795 0.320348,1.304451 -0.111863,1.786531 l -4.423848,4.934232 h -3.635691 c -0.671205,-2.11548 -2.476338,-3.629779 -4.601819,-3.629779 -2.125482,0 -3.9357,1.514299 -4.601818,3.629779 H 7.345485 L 3.506398,22.109048 C 3.013164,21.729056 2.840278,20.997429 3.104692,20.390574 3.369106,19.783721 3.989462,19.488801 4.564053,19.687305 L 9.50656,21.411449 C 9.659102,21.190258 9.8167362,20.974741 9.9845364,20.770566 L 6.816656,14.883519 c -0.310177,-0.572827 -0.198311,-1.310126 0.25933,-1.741162 0.457639,-0.431036 1.128844,-0.425365 1.581397,0.0055 l 5.20692,4.985277 c 0.07626,-0.02268 0.152542,-0.04537 0.22882,-0.06239 l 0.691544,-8.093192 c 0.06101,-0.6975963 0.584761,-1.2307211 1.215286,-1.2307211 0.630526,0 1.154267,0.5331248 1.215285,1.2307211 l 0.686461,8.047904 z M 4.609817,26.890147 v 0 H 27.39009 v 0 h 1.627161 c 0.900024,0 1.627164,0.81103 1.627164,1.814891 0,1.003862 -0.72714,1.814889 -1.627164,1.814889 H 2.982655 c -0.900024,0 -1.627163,-0.811027 -1.627163,-1.814889 0,-1.003861 0.727139,-1.814891 1.627163,-1.814891 z M 15.999953,1.4816894 c 0.676288,0 1.220371,0.6068538 1.220371,1.3611673 v 2.7223348 c 0,0.7543135 -0.544083,1.3611677 -1.220371,1.3611677 -0.676289,0 -1.220372,-0.6068542 -1.220372,-1.3611677 V 2.8428567 c 0,-0.7543135 0.544083,-1.3611673 1.220372,-1.3611673 z"
|
||||||
fill="#ffffff"
|
fill="#ffffff"
|
||||||
stroke="#ffffff"
|
stroke="#ffffff"
|
||||||
id="path2"
|
id="path2"
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0330959" />
|
style="fill-opacity:1;stroke-width:0.0537019" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="helicopter.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"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata14">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs12" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
id="namedview10"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="13.28125"
|
||||||
|
inkscape:cx="11.858824"
|
||||||
|
inkscape:cy="13.101176"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg8"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" />
|
||||||
|
<path
|
||||||
|
d="m 6.3544349,4.6930773 c 0,-0.8721964 0.7046585,-1.5768552 1.5768567,-1.5768552 H 26.853493 c 0.872197,0 1.576856,0.7046588 1.576856,1.5768552 0,0.8721978 -0.704659,1.5768478 -1.576856,1.5768478 h -7.884254 v 3.1537018 h 1.576855 c 4.356045,0 7.884255,3.5282031 7.884255,7.8842541 v 3.153697 c 0,0.872195 -0.704659,1.576855 -1.576856,1.576855 h -7.884254 -3.153695 c -0.990466,0 -1.926715,-0.468129 -2.522966,-1.26148 L 9.7742292,16.085817 c -0.1724691,-0.2316 -0.4089908,-0.408991 -0.67509,-0.517398 l -6.72625,-2.690505 C 1.90476,12.690662 1.5499693,12.286593 1.4267781,11.793826 L 0.29342123,7.25053 C 0.16529133,6.7528385 0.54472759,6.2699251 1.0572018,6.2699251 h 1.3551113 c 0.49769,0 0.9658193,0.2315996 1.2614793,0.6307441 L 5.5660161,9.4236269 H 15.815544 V 6.2699251 H 7.9312916 c -0.8721982,0 -1.5768567,-0.70465 -1.5768567,-1.5768478 z M 18.969239,18.884729 h 6.307407 v -1.576848 c 0,-2.611659 -2.118892,-4.73055 -4.730552,-4.73055 h -1.576855 z m 12.151606,5.19375 c 0.615961,0.615957 0.615961,1.616279 0,2.232229 l -0.192176,0.192183 c -1.182641,1.182634 -2.789057,1.84787 -4.459529,1.84787 h -13.8073 c -0.872195,0 -1.576855,-0.704658 -1.576855,-1.576847 0,-0.872197 0.70466,-1.576855 1.576855,-1.576855 h 13.8073 c 0.837698,0 1.640911,-0.330154 2.232231,-0.921474 l 0.192176,-0.192177 c 0.615959,-0.615959 1.61627,-0.615959 2.232229,0 z"
|
||||||
|
id="path1174"
|
||||||
|
style="fill-opacity:1;stroke-width:0.0492765" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
70
client/public/themes/olympus/images/buttons/spawn/more.svg
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="unit.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"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata14">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs12" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
id="namedview10"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="9.391262"
|
||||||
|
inkscape:cx="25.715394"
|
||||||
|
inkscape:cy="24.171405"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg8"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" />
|
||||||
|
<rect
|
||||||
|
id="rect894"
|
||||||
|
width="5.696785"
|
||||||
|
height="22.699497"
|
||||||
|
x="13.203763"
|
||||||
|
y="4.5254831"
|
||||||
|
ry="1.6436043" />
|
||||||
|
<rect
|
||||||
|
id="rect894-7"
|
||||||
|
width="5.696785"
|
||||||
|
height="22.699497"
|
||||||
|
x="12.751214"
|
||||||
|
y="-27.588247"
|
||||||
|
ry="1.6436043"
|
||||||
|
transform="rotate(90)" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,59 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="navyunit.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"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
|
<metadata
|
||||||
|
id="metadata14">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs12" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
inkscape:document-rotation="0"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1017"
|
||||||
|
id="namedview10"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="18.782524"
|
||||||
|
inkscape:cx="35.857801"
|
||||||
|
inkscape:cy="16.398222"
|
||||||
|
inkscape:window-x="1912"
|
||||||
|
inkscape:window-y="-8"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg8"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" />
|
||||||
|
<path
|
||||||
|
d="m 11.24439,3.5672501 c 0,-0.9200678 0.743331,-1.6634002 1.663401,-1.6634002 h 6.653596 c 0.920068,0 1.663398,0.7433324 1.663398,1.6634002 v 1.6633976 h 2.495098 c 1.377503,0 2.495102,1.1175989 2.495102,2.4951006 v 6.6535967 l 2.307964,0.769323 c 1.200768,0.400255 1.533447,1.949294 0.597785,2.801785 l -5.250104,4.813463 c -0.842097,0.488625 -1.803748,0.784917 -2.645845,0.784917 -1.018831,0 -2.120833,-0.400253 -3.077288,-1.055219 -1.148785,-0.805708 -2.682232,-0.805708 -3.831017,0 -0.88888,0.613379 -1.975286,1.055219 -3.077289,1.055219 -0.842094,0 -1.8037459,-0.296292 -2.6458445,-0.784917 L 3.3432428,17.950453 C 2.4075824,17.092766 2.7402625,15.548923 3.9410271,15.148668 L 6.25419,14.379345 V 7.7257483 c 0,-1.3775017 1.1175986,-2.4951006 2.4951017,-2.4951006 H 11.24439 Z M 9.5809881,13.272145 15.184569,11.406021 c 0.680952,-0.228708 1.419085,-0.228708 2.105238,0 l 5.59838,1.866124 V 8.5574463 H 9.5809881 Z m 7.6152529,10.562584 c 1.169577,0.805711 2.599061,1.356711 4.028544,1.356711 1.398295,0 2.879759,-0.561396 4.023347,-1.356711 v 0 c 0.618575,-0.44184 1.460674,-0.405453 2.037665,0.08836 0.748531,0.618578 1.689389,1.091607 2.630247,1.309929 0.894079,0.207933 1.45028,1.102 1.242353,1.996079 -0.207931,0.894078 -1.102001,1.450276 -1.99608,1.242351 -1.273537,-0.296292 -2.333955,-0.857691 -3.025306,-1.299534 -1.507454,0.81091 -3.196843,1.346319 -4.912226,1.346319 -1.6582,0 -3.150063,-0.514616 -4.17929,-0.982449 -0.301493,-0.140338 -0.576991,-0.275498 -0.810906,-0.400255 -0.233924,0.12477 -0.50422,0.265105 -0.810908,0.400255 -1.02923,0.467833 -2.521091,0.982449 -4.179291,0.982449 -1.7153815,0 -3.404772,-0.535409 -4.9122277,-1.341118 C 5.6356148,27.613758 4.5803957,28.180351 3.306857,28.476646 2.4127783,28.684579 1.5186998,28.128372 1.3107769,27.234294 1.1028445,26.340218 1.6590517,25.44614 2.5531281,25.238215 3.4939891,25.019896 4.4348476,24.546866 5.1833783,23.928289 5.7603695,23.439666 6.6024647,23.403277 7.2210429,23.83993 v 0 c 1.1487833,0.790114 2.6250524,1.35151 4.0233471,1.35151 1.429485,0 2.858967,-0.550998 4.028544,-1.356711 0.576995,-0.410651 1.346314,-0.410651 1.923307,3e-6 z"
|
||||||
|
id="path6267"
|
||||||
|
style="fill-opacity:1;stroke-width:0.0519812" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.7 KiB |
65
client/public/themes/olympus/images/buttons/spawn/sam.svg
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
@@ -1,20 +1,19 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
<svg
|
<svg
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
width="32"
|
width="32"
|
||||||
height="32"
|
height="32"
|
||||||
viewBox="0 0 32 32"
|
viewBox="0 0 32 32"
|
||||||
fill="none"
|
|
||||||
version="1.1"
|
version="1.1"
|
||||||
id="svg15"
|
id="svg15"
|
||||||
sodipodi:docname="spawn_smoke.svg"
|
sodipodi:docname="smoke.svg"
|
||||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)">
|
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"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||||
<metadata
|
<metadata
|
||||||
id="metadata19">
|
id="metadata19">
|
||||||
<rdf:RDF>
|
<rdf:RDF>
|
||||||
@@ -23,7 +22,7 @@
|
|||||||
<dc:format>image/svg+xml</dc:format>
|
<dc:format>image/svg+xml</dc:format>
|
||||||
<dc:type
|
<dc:type
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
<dc:title></dc:title>
|
<dc:title />
|
||||||
</cc:Work>
|
</cc:Work>
|
||||||
</rdf:RDF>
|
</rdf:RDF>
|
||||||
</metadata>
|
</metadata>
|
||||||
@@ -42,25 +41,19 @@
|
|||||||
id="namedview17"
|
id="namedview17"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="18.782524"
|
inkscape:zoom="18.782524"
|
||||||
inkscape:cx="15.515904"
|
inkscape:cx="15.572987"
|
||||||
inkscape:cy="14.452888"
|
inkscape:cy="14.481547"
|
||||||
inkscape:window-x="1912"
|
inkscape:window-x="1912"
|
||||||
inkscape:window-y="-8"
|
inkscape:window-y="-8"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg15" />
|
inkscape:current-layer="svg15"
|
||||||
|
inkscape:showpageshadow="2"
|
||||||
|
inkscape:pagecheckerboard="0"
|
||||||
|
inkscape:deskcolor="#d1d1d1" />
|
||||||
<path
|
<path
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0296135;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
style="fill-opacity:1;stroke:none;stroke-width:0.0484484;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
d="m 7.4791847,12.531786 c 0,2.38189 1.8897313,4.31437 4.2189373,4.31437 h 3.612465 c 0.662138,0.596221 1.529364,0.958747 2.481554,0.958747 0.952191,0 1.819416,-0.362526 2.481555,-0.958747 h 0.799841 c 1.813557,0 3.281394,-1.501042 3.281394,-3.355622 0,-1.85458 -1.467837,-3.355621 -3.281394,-3.355621 -0.313492,0 -0.615263,0.04495 -0.902385,0.128832 -0.626981,-1.2164128 -1.878013,-2.04633 -3.316553,-2.04633 -0.95512,0 -1.828206,0.3655228 -2.493274,0.9677373 C 13.634731,8.5799418 12.708908,8.217415 11.698122,8.217415 c -2.329206,0 -4.2189373,1.932479 -4.2189373,4.314371 z m 17.1101313,6.711242 h -9.844184 c -0.389665,0 -0.703157,0.320582 -0.703157,0.719061 0,0.398479 0.313492,0.719062 0.703157,0.719062 h 9.844184 c 0.389667,0 0.703157,-0.320583 0.703157,-0.719062 0,-0.398479 -0.31349,-0.719061 -0.703157,-0.719061 z m -1.875082,2.876246 H 19.43284 c -0.389666,0 -0.703158,0.320582 -0.703158,0.719063 0,0.398479 0.313492,0.71906 0.703158,0.71906 h 3.281394 c 0.389665,0 0.703156,-0.320581 0.703156,-0.71906 0,-0.398481 -0.313491,-0.719063 -0.703156,-0.719063 z m -5.625249,0 H 7.2447992 c -0.3896663,0 -0.7031566,0.320582 -0.7031566,0.719063 0,0.398479 0.3134903,0.71906 0.7031566,0.71906 h 9.8441858 c 0.389666,0 0.703156,-0.320581 0.703156,-0.71906 0,-0.398481 -0.31349,-0.719063 -0.703156,-0.719063 z m -3.984552,-2.157185 c 0,-0.398479 -0.313489,-0.719061 -0.703155,-0.719061 H 9.354267 c -0.389665,0 -0.703155,0.320582 -0.703155,0.719061 0,0.398479 0.31349,0.719062 0.703155,0.719062 h 3.047011 c 0.389666,0 0.703155,-0.320583 0.703155,-0.719062 z"
|
d="m 2.1124942,10.397531 c 0,3.896829 3.0916459,7.058413 6.902283,7.058413 h 5.9100798 c 1.083274,0.975432 2.502076,1.568533 4.059882,1.568533 1.557807,0 2.976608,-0.593101 4.059884,-1.568533 h 1.308559 c 2.967023,0 5.36844,-2.455741 5.36844,-5.489878 0,-3.0341379 -2.401417,-5.4898771 -5.36844,-5.4898771 -0.512881,0 -1.006585,0.073539 -1.476324,0.2107723 -1.025756,-1.9900807 -3.072474,-3.3478452 -5.42596,-3.3478452 -1.5626,0 -2.990989,0.5980041 -4.079057,1.5832415 C 12.183116,3.9322186 10.668447,3.339116 9.0147772,3.339116 c -3.8106371,0 -6.902283,3.1615822 -6.902283,7.058415 z M 30.105081,21.377284 H 13.999759 c -0.637501,0 -1.150382,0.52448 -1.150382,1.176402 0,0.651921 0.512881,1.176402 1.150382,1.176402 h 16.105322 c 0.637505,0 1.150382,-0.524481 1.150382,-1.176402 0,-0.651922 -0.512877,-1.176402 -1.150382,-1.176402 z m -3.067679,4.705608 h -5.36844 c -0.637503,0 -1.150383,0.52448 -1.150383,1.176405 0,0.651921 0.51288,1.176399 1.150383,1.176399 h 5.36844 c 0.637501,0 1.15038,-0.524478 1.15038,-1.176399 0,-0.651925 -0.512879,-1.176405 -1.15038,-1.176405 z m -9.203043,0 H 1.7290339 c -0.6375035,0 -1.15038115,0.52448 -1.15038115,1.176405 0,0.651921 0.51287765,1.176399 1.15038115,1.176399 H 17.834359 c 0.637503,0 1.15038,-0.524478 1.15038,-1.176399 0,-0.651925 -0.512877,-1.176405 -1.15038,-1.176405 z m -6.518823,-3.529206 c 0,-0.651922 -0.512876,-1.176402 -1.150379,-1.176402 H 5.180174 c -0.6375013,0 -1.1503785,0.52448 -1.1503785,1.176402 0,0.651921 0.5128772,1.176402 1.1503785,1.176402 h 4.984983 c 0.637503,0 1.150379,-0.524481 1.150379,-1.176402 z"
|
||||||
id="path2-7" />
|
id="path2-7" />
|
||||||
<rect
|
|
||||||
x="0.5"
|
|
||||||
y="0.5"
|
|
||||||
width="31"
|
|
||||||
height="31"
|
|
||||||
rx="7.5"
|
|
||||||
stroke="white"
|
|
||||||
id="rect8"
|
|
||||||
style="fill:none;stroke:none" />
|
|
||||||
<defs
|
<defs
|
||||||
id="defs13">
|
id="defs13">
|
||||||
<clipPath
|
<clipPath
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M96 151.4V360.6c9.7 5.6 17.8 13.7 23.4 23.4H328.6c0-.1 .1-.2 .1-.3l-4.5-7.9-32-56 0 0c-1.4 .1-2.8 .1-4.2 .1c-35.3 0-64-28.7-64-64s28.7-64 64-64c1.4 0 2.8 0 4.2 .1l0 0 32-56 4.5-7.9-.1-.3H119.4c-5.6 9.7-13.7 17.8-23.4 23.4zM384.3 352c35.2 .2 63.7 28.7 63.7 64c0 35.3-28.7 64-64 64c-23.7 0-44.4-12.9-55.4-32H119.4c-11.1 19.1-31.7 32-55.4 32c-35.3 0-64-28.7-64-64c0-23.7 12.9-44.4 32-55.4V151.4C12.9 140.4 0 119.7 0 96C0 60.7 28.7 32 64 32c23.7 0 44.4 12.9 55.4 32H328.6c11.1-19.1 31.7-32 55.4-32c35.3 0 64 28.7 64 64c0 35.3-28.5 63.8-63.7 64l-4.5 7.9-32 56-2.3 4c4.2 8.5 6.5 18 6.5 28.1s-2.3 19.6-6.5 28.1l2.3 4 32 56 4.5 7.9z"/></svg>
|
||||||
|
After Width: | Height: | Size: 872 B |
44
client/public/themes/olympus/images/buttons/tools/ground.svg
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="ground.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="1.1559539"
|
||||||
|
inkscape:cx="432.97576"
|
||||||
|
inkscape:cy="301.91516"
|
||||||
|
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 130.61192,122.94993 c 4.83398,-2.4171 10.52734,-2.4171 15.36132,0 l 85.9375,42.97085 c 8.48633,4.24336 11.92383,14.55637 7.68066,23.04312 -3.00781,6.01593 -9.07714,9.5073 -15.36132,9.5073 v 42.97085 c 0,9.50729 -7.68066,17.18833 -17.1875,17.18833 h -2.63184 l 17.1875,103.13004 266.68729,0 c 9.50683,0 17.1875,7.68105 17.1875,17.18835 0,9.5073 -7.68067,17.18833 -17.1875,17.18833 l -280.97441,0 H 206.72031 69.86485 69.27404 17.98008 c -9.50683,0 -17.1875,-7.68103 -17.1875,-17.18833 0,-9.5073 7.68067,-17.18835 17.1875,-17.18835 h 37.00684 l 17.1875,-103.13004 h -2.63184 c -9.50684,0 -17.1875,-7.68104 -17.1875,-17.18833 V 198.4712 c -6.28418,0 -12.35351,-3.49137 -15.36132,-9.5073 -4.24317,-8.48675 -0.80567,-18.79976 7.68066,-23.04312 z m 39.10155,238.81049 -31.42089,-26.21222 -31.4209,26.21222 z m -62.68066,-103.13004 -2.52441,15.20096 33.78418,28.19961 33.78418,-28.19961 -2.52441,-15.20096 z m -7.46582,44.68969 -6.01561,35.98808 24.59961,-20.51857 z m 58.86718,15.46951 24.59961,20.46487 -6.01561,-35.98809 z M 95.32383,189.87702 c -4.72656,0 -8.59374,3.86738 -8.59374,8.59418 0,4.72679 3.86718,8.59417 8.59374,8.59417 h 85.9375 c 4.72656,0 8.59374,-3.86738 8.59374,-8.59417 0,-4.7268 -3.86718,-8.59418 -8.59374,-8.59418 z"
|
||||||
|
id="path2"
|
||||||
|
style="stroke-width:0.537122"
|
||||||
|
sodipodi:nodetypes="cccscssccsssccccsssccsscsccccccccccccccccccccsssssss" />
|
||||||
|
<path
|
||||||
|
d="m 398.88439,290.60444 -56.18308,-59.35032 c -4.82498,-5.06298 -11.50756,-7.93072 -18.4952,-7.84559 l -20.1384,0.17717 c -4.88076,0.0647 -7.83178,5.39609 -5.3158,9.5508 l 35.56198,58.63664 -43.41565,0.78856 -16.07612,-19.15941 c -2.39827,-2.87404 -5.9964,-4.54245 -9.73595,-4.48875 l -14.13363,0.13667 c -4.09068,0.0573 -7.05613,3.89607 -6.017,7.85435 l 11.10611,42.68377 c 0.92991,3.55548 3.36429,6.5345 6.64574,8.13577 l 48.58659,23.70886 c 1.76422,0.86089 3.69975,1.28114 5.63108,1.26246 l 119.77611,-1.14062 c 20.41896,-0.21499 40.00846,-8.13034 54.85055,-22.163 7.69088,-7.25597 5.74152,-20.00247 -3.78528,-24.65128 l -20.25324,-9.883 c -7.12743,-3.47798 -15.01076,-5.22788 -22.94599,-5.08091 z"
|
||||||
|
id="path1903"
|
||||||
|
sodipodi:nodetypes="ccccccccccccsscccsscc"
|
||||||
|
style="stroke-width:0.392612" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 508 B |
44
client/public/themes/olympus/images/buttons/tools/tower.svg
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="tower.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.40869141"
|
||||||
|
inkscape:cx="532.18638"
|
||||||
|
inkscape:cy="298.51374"
|
||||||
|
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 130.61192,122.94993 c 4.83398,-2.4171 10.52734,-2.4171 15.36132,0 l 85.9375,42.97085 c 8.48633,4.24336 11.92383,14.55637 7.68066,23.04312 -3.00781,6.01593 -9.07714,9.5073 -15.36132,9.5073 v 42.97085 c 0,9.50729 -7.68066,17.18833 -17.1875,17.18833 h -2.63184 l 17.1875,103.13004 266.68729,0 c 9.50683,0 17.1875,7.68105 17.1875,17.18835 0,9.5073 -7.68067,17.18833 -17.1875,17.18833 l -280.97441,0 H 206.72031 69.86485 69.27404 17.98008 c -9.50683,0 -17.1875,-7.68103 -17.1875,-17.18833 0,-9.5073 7.68067,-17.18835 17.1875,-17.18835 h 37.00684 l 17.1875,-103.13004 h -2.63184 c -9.50684,0 -17.1875,-7.68104 -17.1875,-17.18833 V 198.4712 c -6.28418,0 -12.35351,-3.49137 -15.36132,-9.5073 -4.24317,-8.48675 -0.80567,-18.79976 7.68066,-23.04312 z m 39.10155,238.81049 -31.42089,-26.21222 -31.4209,26.21222 z m -62.68066,-103.13004 -2.52441,15.20096 33.78418,28.19961 33.78418,-28.19961 -2.52441,-15.20096 z m -7.46582,44.68969 -6.01561,35.98808 24.59961,-20.51857 z m 58.86718,15.46951 24.59961,20.46487 -6.01561,-35.98809 z M 95.32383,189.87702 c -4.72656,0 -8.59374,3.86738 -8.59374,8.59418 0,4.72679 3.86718,8.59417 8.59374,8.59417 h 85.9375 c 4.72656,0 8.59374,-3.86738 8.59374,-8.59417 0,-4.7268 -3.86718,-8.59418 -8.59374,-8.59418 z"
|
||||||
|
id="path2"
|
||||||
|
style="stroke-width:0.537122"
|
||||||
|
sodipodi:nodetypes="cccscssccsssccccsssccsscsccccccccccccccccccccsssssss" />
|
||||||
|
<path
|
||||||
|
d="m 387.14987,87.347348 -58.62532,-21.98825 c -5.0233,-1.86494 -10.58805,-1.59423 -15.37072,0.81215 l -13.80658,6.88825 c -3.33884,1.68446 -3.57948,6.34681 -0.45119,8.36215 l 44.18707,28.425312 -29.6285,15.13009 -17.50638,-7.79064 c -2.61694,-1.17311 -5.65498,-1.11295 -8.21176,0.18048 l -9.68566,4.84283 c -2.79741,1.41374 -3.5494,5.05338 -1.50398,7.42968 l 21.98825,25.65798 c 1.83486,2.13566 4.51196,3.36892 7.30937,3.36892 h 41.41973 c 1.50399,0 2.97789,-0.36095 4.3014,-1.02271 l 82.08748,-41.0287 c 13.98706,-7.00856 24.81575,-19.040442 30.32033,-33.689252 2.85757,-7.58008 -2.76733,-15.7016 -10.88885,-15.7016 h -17.26574 c -6.0761,0 -12.09204,1.44382 -17.50638,4.21116 z"
|
||||||
|
id="path1903"
|
||||||
|
sodipodi:nodetypes="ccccccccccccsscccsscc"
|
||||||
|
style="stroke-width:0.300797" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.3 KiB |
1
client/public/themes/olympus/images/icons/eye-solid.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 820 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
38
client/public/themes/olympus/images/markers/draw.svg
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 512 512"
|
||||||
|
version="1.1"
|
||||||
|
id="svg4"
|
||||||
|
sodipodi:docname="draw.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="159.65591"
|
||||||
|
inkscape:cy="402.50418"
|
||||||
|
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 352.44544,42.011059 -43.76337,43.76337 117.54624,117.546241 43.76337,-43.76337 c 22.60505,-22.60504 22.60505,-59.22522 0,-81.830267 L 434.36613,42.011059 c -22.60505,-22.605047 -59.22523,-22.605047 -81.83027,0 z M 288.2471,106.20939 77.477646,317.06927 c -9.4037,9.4037 -16.275634,21.0679 -20.073282,33.81715 L 25.395618,459.6619 c -2.260505,7.68572 -0.18084,15.91396 5.425211,21.52001 5.606052,5.60605 13.834289,7.68572 21.429585,5.51563 L 161.0259,454.68879 c 12.74925,-3.79764 24.41345,-10.66958 33.81715,-20.07328 l 210.9503,-210.85987 z"
|
||||||
|
id="path2"
|
||||||
|
style="stroke:#ffffff;stroke-width:45.7526;stroke-dasharray:none;stroke-opacity:1;fill:#247be2;fill-opacity:1" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
60
client/public/themes/olympus/images/units/groundunit-ewr.svg
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="50"
|
||||||
|
height="50"
|
||||||
|
viewBox="0 0 50 50"
|
||||||
|
fill="none"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
sodipodi:docname="groundunit-ewr.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
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">
|
||||||
|
<rect
|
||||||
|
x="25"
|
||||||
|
y="2.37241"
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
rx="1.1"
|
||||||
|
transform="rotate(45 25 2.37241)"
|
||||||
|
stroke="none"
|
||||||
|
stroke-width="2"
|
||||||
|
id="rect2" />
|
||||||
|
<rect
|
||||||
|
x="25"
|
||||||
|
y="5.20083"
|
||||||
|
width="28"
|
||||||
|
height="28"
|
||||||
|
transform="rotate(45 25 5.20083)"
|
||||||
|
fill="none"
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="2"
|
||||||
|
id="rect4" />
|
||||||
|
<path
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="m 21.956541,27.690987 5.894515,-3.34903 -3.846924,-2.326589 5.759192,-3.618211"
|
||||||
|
id="path942"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="m 26.663842,17.477657 3.292749,0.82871 -0.740705,2.943076"
|
||||||
|
id="path1230" />
|
||||||
|
<path
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="2"
|
||||||
|
id="path1657"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="27.107115"
|
||||||
|
sodipodi:cy="22.407019"
|
||||||
|
sodipodi:rx="10.069912"
|
||||||
|
sodipodi:ry="10.084956"
|
||||||
|
sodipodi:start="1.0594149"
|
||||||
|
sodipodi:end="3.5325464"
|
||||||
|
sodipodi:arc-type="arc"
|
||||||
|
d="M 32.035153,31.201799 A 10.069912,10.084956 0 0 1 20.42801,29.95437 10.069912,10.084956 0 0 1 17.797018,18.563941"
|
||||||
|
sodipodi:open="true" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,51 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="50"
|
||||||
|
height="50"
|
||||||
|
viewBox="0 0 50 50"
|
||||||
|
fill="none"
|
||||||
|
version="1.1"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="groundunit-sam-launcher.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
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">
|
||||||
|
<path
|
||||||
|
fill="#3BB9FF"
|
||||||
|
stroke="none"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M45.7733 41.3423L25.9481 7.63951C25.5228 6.91648 24.4772 6.91646 24.0519 7.63951L4.22671 41.3423C3.79536 42.0756 4.32409 43 5.17484 43H44.8252C45.6759 43 46.2046 42.0756 45.7733 41.3423Z"
|
||||||
|
id="path2" />
|
||||||
|
<path
|
||||||
|
d="M6.74842 41L25 9.97231L43.2516 41H6.74842Z"
|
||||||
|
fill="none"
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="2"
|
||||||
|
id="path4" />
|
||||||
|
<path
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="2"
|
||||||
|
id="path849"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="25.284166"
|
||||||
|
sodipodi:cy="43.365532"
|
||||||
|
sodipodi:rx="8.2439137"
|
||||||
|
sodipodi:ry="8.2651606"
|
||||||
|
sodipodi:start="3.8397244"
|
||||||
|
sodipodi:end="5.5850536"
|
||||||
|
sodipodi:arc-type="arc"
|
||||||
|
d="m 18.968962,38.052789 a 8.2439137,8.2651606 0 0 1 6.315204,-2.952418 8.2439137,8.2651606 0 0 1 6.315205,2.952418"
|
||||||
|
sodipodi:open="true" />
|
||||||
|
<path
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="M 25.114189,34.887901 V 19.887378"
|
||||||
|
id="path1085" />
|
||||||
|
<path
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="m 23.074458,23.244436 1.981982,-3.432894 1.923784,3.332092"
|
||||||
|
id="path1087" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
width="50"
|
||||||
|
height="50"
|
||||||
|
viewBox="0 0 50 50"
|
||||||
|
fill="none"
|
||||||
|
version="1.1"
|
||||||
|
id="svg6"
|
||||||
|
sodipodi:docname="groundunit-sam-radar.svg"
|
||||||
|
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||||
|
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">
|
||||||
|
<path
|
||||||
|
d="M45.7733 41.3423L25.9481 7.63951C25.5228 6.91648 24.4772 6.91646 24.0519 7.63951L4.22671 41.3423C3.79536 42.0756 4.32409 43 5.17484 43H44.8252C45.6759 43 46.2046 42.0756 45.7733 41.3423Z"
|
||||||
|
fill="#3BB9FF"
|
||||||
|
stroke="none"
|
||||||
|
stroke-width="2"
|
||||||
|
id="path2" />
|
||||||
|
<path
|
||||||
|
d="M6.74842 41L25 9.97231L43.2516 41H6.74842Z"
|
||||||
|
fill="none"
|
||||||
|
stroke="#082E44"
|
||||||
|
stroke-width="2"
|
||||||
|
id="path4"
|
||||||
|
style="fill:none;fill-opacity:1" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#082e44;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
|
||||||
|
d="m 23.148216,33.643859 5.192016,-2.949206 -3.388454,-2.048829 5.07282,-3.186251"
|
||||||
|
id="path942"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="fill:none;stroke:#082e44;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"
|
||||||
|
d="m 27.294508,24.649847 2.900324,0.729775 -0.652429,2.591717"
|
||||||
|
id="path1230" />
|
||||||
|
<path
|
||||||
|
style="fill:none;fill-opacity:1;stroke:#082e44;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:5.6;stroke-opacity:1;paint-order:stroke fill markers"
|
||||||
|
id="path1657"
|
||||||
|
sodipodi:type="arc"
|
||||||
|
sodipodi:cx="27.684952"
|
||||||
|
sodipodi:cy="28.990719"
|
||||||
|
sodipodi:rx="8.8697948"
|
||||||
|
sodipodi:ry="8.8809643"
|
||||||
|
sodipodi:start="1.0594149"
|
||||||
|
sodipodi:end="3.5325464"
|
||||||
|
sodipodi:arc-type="arc"
|
||||||
|
sodipodi:open="true"
|
||||||
|
d="M 32.025673,36.735535 A 8.8697948,8.8809643 0 0 1 21.801853,35.63703 8.8697948,8.8809643 0 0 1 19.484418,25.606446" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
17
client/public/themes/olympus/images/units/helicopter.svg
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<svg width="50" height="50" viewBox="0 0 50 50" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle
|
||||||
|
fill="white"
|
||||||
|
stroke="none"
|
||||||
|
stroke-width="2"
|
||||||
|
id="path2358"
|
||||||
|
cx="25"
|
||||||
|
cy="25"
|
||||||
|
r="22" />
|
||||||
|
<circle
|
||||||
|
cx="25"
|
||||||
|
cy="25"
|
||||||
|
r="20"
|
||||||
|
stroke="#082e44"
|
||||||
|
stroke-width="2"
|
||||||
|
id="circle6" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 338 B |
@@ -65,18 +65,17 @@
|
|||||||
--unit-width: 50px;
|
--unit-width: 50px;
|
||||||
|
|
||||||
/*** Air units ***/
|
/*** Air units ***/
|
||||||
--unit-aircraft-ammo-gap: calc(2px + var(--unit-stroke-width));
|
--unit-ammo-gap: calc(2px + var(--unit-stroke-width));
|
||||||
--unit-aircraft-ammo-border-radius: 50%;
|
--unit-ammo-border-radius: 50%;
|
||||||
--unit-aircraft-ammo-border-width: 2px;
|
--unit-ammo-border-width: 2px;
|
||||||
--unit-aircraft-ammo-radius: 2px;
|
--unit-ammo-radius: 2px;
|
||||||
--unit-aircraft-ammo-spacing: 2px;
|
--unit-ammo-spacing: 2px;
|
||||||
--unit-aircraft-ammo-x: 0px;
|
--unit-ammo-x: 0px;
|
||||||
--unit-aircraft-ammo-y: 30px;
|
--unit-ammo-y: 30px;
|
||||||
--unit-aircraft-fuel-border-width: 2px;
|
--unit-fuel-border-width: 2px;
|
||||||
--unit-aircraft-fuel-height: 6px;
|
--unit-fuel-height: 6px;
|
||||||
--unit-aircraft-fuel-width: 36px;
|
--unit-fuel-width: 36px;
|
||||||
--unit-aircraft-fuel-x: 0px;
|
--unit-fuel-x: 0px;
|
||||||
--unit-aircraft-fuel-y: 22px;
|
--unit-fuel-y: 22px;
|
||||||
--unit-aircraft-height: 28px;
|
--unit-vvi-width: 4px;
|
||||||
--unit-aircraft-vvi-width: 4px;
|
|
||||||
}
|
}
|
||||||
5
client/src/@types/dom.d.ts
vendored
@@ -17,7 +17,10 @@ interface CustomEventMap {
|
|||||||
"groupCreation": CustomEvent<Unit[]>,
|
"groupCreation": CustomEvent<Unit[]>,
|
||||||
"groupDeletion": CustomEvent<Unit[]>,
|
"groupDeletion": CustomEvent<Unit[]>,
|
||||||
"mapStateChanged": CustomEvent<string>,
|
"mapStateChanged": CustomEvent<string>,
|
||||||
"mapContextMenu": CustomEvent<>
|
"mapContextMenu": CustomEvent<>,
|
||||||
|
"mapVisibilityOptionsChanged": CustomEvent<>,
|
||||||
|
"commandModeOptionsChanged": CustomEvent<>,
|
||||||
|
"contactsUpdated": CustomEvent<Unit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
|||||||
40
client/src/@types/server.d.ts
vendored
@@ -1,16 +1,46 @@
|
|||||||
interface UnitsData {
|
|
||||||
units: {[key: string]: UnitData},
|
|
||||||
sessionHash: string
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AirbasesData {
|
interface AirbasesData {
|
||||||
airbases: {[key: string]: any},
|
airbases: {[key: string]: any},
|
||||||
|
sessionHash: string;
|
||||||
|
time: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BullseyesData {
|
interface BullseyesData {
|
||||||
bullseyes: {[key: string]: {latitude: number, longitude: number, coalition: string}},
|
bullseyes: {[key: string]: {latitude: number, longitude: number, coalition: string}},
|
||||||
|
sessionHash: string;
|
||||||
|
time: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MissionData {
|
||||||
|
mission: {
|
||||||
|
theatre: string,
|
||||||
|
dateAndTime: DateAndTime;
|
||||||
|
commandModeOptions: CommandModeOptions;
|
||||||
|
}
|
||||||
|
time: number;
|
||||||
|
sessionHash: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CommandModeOptions {
|
||||||
|
commandMode: string;
|
||||||
|
restrictSpawns: boolean;
|
||||||
|
restrictToCoalition: boolean;
|
||||||
|
setupTime: number;
|
||||||
|
spawnPoints: {
|
||||||
|
red: number,
|
||||||
|
blue: number
|
||||||
|
},
|
||||||
|
eras: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DateAndTime {
|
||||||
|
date: {Year: number, Month: number, Day: number};
|
||||||
|
time: {h: number, m: number, s: number};
|
||||||
|
elapsedTime: number;
|
||||||
|
startTime: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LogData {
|
interface LogData {
|
||||||
logs: {[key: string]: string},
|
logs: {[key: string]: string},
|
||||||
|
sessionHash: string;
|
||||||
|
time: number;
|
||||||
}
|
}
|
||||||
110
client/src/@types/unit.d.ts
vendored
@@ -1,60 +1,24 @@
|
|||||||
interface UpdateData {
|
import { LatLng } from "leaflet"
|
||||||
[key: string]: any
|
|
||||||
|
interface UnitIconOptions {
|
||||||
|
showState: boolean,
|
||||||
|
showVvi: boolean,
|
||||||
|
showHotgroup: boolean,
|
||||||
|
showUnitIcon: boolean,
|
||||||
|
showShortLabel: boolean,
|
||||||
|
showFuel: boolean,
|
||||||
|
showAmmo: boolean,
|
||||||
|
showSummary: boolean,
|
||||||
|
showCallsign: boolean,
|
||||||
|
rotateToHeading: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BaseData {
|
interface GeneralSettings {
|
||||||
controlled: boolean;
|
prohibitJettison: boolean;
|
||||||
name: string;
|
prohibitAA: boolean;
|
||||||
unitName: string;
|
prohibitAG: boolean;
|
||||||
groupName: string;
|
prohibitAfterburner: boolean;
|
||||||
alive: boolean;
|
prohibitAirWpn: boolean;
|
||||||
category: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FlightData {
|
|
||||||
latitude: number;
|
|
||||||
longitude: number;
|
|
||||||
altitude: number;
|
|
||||||
heading: number;
|
|
||||||
speed: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MissionData {
|
|
||||||
fuel: number;
|
|
||||||
flags: any;
|
|
||||||
ammo: any;
|
|
||||||
contacts: any;
|
|
||||||
hasTask: boolean;
|
|
||||||
coalition: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FormationData {
|
|
||||||
leaderID: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TaskData {
|
|
||||||
currentState: string;
|
|
||||||
currentTask: string;
|
|
||||||
activePath: any;
|
|
||||||
desiredSpeed: number;
|
|
||||||
desiredSpeedType: string;
|
|
||||||
desiredAltitude: number;
|
|
||||||
desiredAltitudeType: string;
|
|
||||||
targetLocation: any;
|
|
||||||
isTanker: boolean;
|
|
||||||
isAWACS: boolean;
|
|
||||||
onOff: boolean;
|
|
||||||
followRoads: boolean;
|
|
||||||
targetID: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OptionsData {
|
|
||||||
ROE: string;
|
|
||||||
reactionToThreat: string;
|
|
||||||
emissionsCountermeasures: string;
|
|
||||||
TACAN: TACAN;
|
|
||||||
radio: Radio;
|
|
||||||
generalSettings: GeneralSettings;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TACAN {
|
interface TACAN {
|
||||||
@@ -70,31 +34,21 @@ interface Radio {
|
|||||||
callsignNumber: number;
|
callsignNumber: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GeneralSettings {
|
interface Ammo {
|
||||||
prohibitJettison: boolean;
|
quantity: number,
|
||||||
prohibitAA: boolean;
|
name: string,
|
||||||
prohibitAG: boolean;
|
guidance: number,
|
||||||
prohibitAfterburner: boolean;
|
category: number,
|
||||||
prohibitAirWpn: boolean;
|
missileCategory: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UnitIconOptions {
|
interface Contact {
|
||||||
showState: boolean,
|
ID: number,
|
||||||
showVvi: boolean,
|
detectionMethod: number
|
||||||
showHotgroup: boolean,
|
|
||||||
showUnitIcon: boolean,
|
|
||||||
showShortLabel: boolean,
|
|
||||||
showFuel: boolean,
|
|
||||||
showAmmo: boolean,
|
|
||||||
showSummary: boolean,
|
|
||||||
rotateToHeading: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UnitData {
|
interface Offset {
|
||||||
baseData: BaseData;
|
x: number,
|
||||||
flightData: FlightData;
|
y: number,
|
||||||
missionData: MissionData;
|
z: number
|
||||||
formationData: FormationData;
|
|
||||||
taskData: TaskData;
|
|
||||||
optionsData: OptionsData;
|
|
||||||
}
|
}
|
||||||
9
client/src/@types/unitdatabase.d.ts
vendored
@@ -14,9 +14,12 @@ interface LoadoutBlueprint {
|
|||||||
|
|
||||||
interface UnitBlueprint {
|
interface UnitBlueprint {
|
||||||
name: string;
|
name: string;
|
||||||
era?: string[];
|
coalition: string;
|
||||||
|
era: string;
|
||||||
label: string;
|
label: string;
|
||||||
shortLabel: string;
|
shortLabel: string;
|
||||||
loadouts: LoadoutBlueprint[];
|
type?: string;
|
||||||
filename: string;
|
range?: string;
|
||||||
|
loadouts?: LoadoutBlueprint[];
|
||||||
|
filename?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getMissionData } from "..";
|
import { getMissionHandler } from "..";
|
||||||
|
import { convertDateAndTimeToDate } from "../other/utils";
|
||||||
import { getConnected } from "../server/server";
|
import { getConnected } from "../server/server";
|
||||||
import { ATCBoard } from "./atcboard";
|
import { ATCBoard } from "./atcboard";
|
||||||
import { ATCBoardGround } from "./board/ground";
|
import { ATCBoardGround } from "./board/ground";
|
||||||
@@ -140,11 +141,10 @@ export class ATC {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
getMissionDateTime() : Date {
|
getMissionDate() : Date {
|
||||||
return new Date( getMissionData().getNowDate() );
|
return convertDateAndTimeToDate(getMissionHandler().getDateAndTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
lookForBoards() {
|
lookForBoards() {
|
||||||
|
|
||||||
document.querySelectorAll( ".ol-strip-board" ).forEach( board => {
|
document.querySelectorAll( ".ol-strip-board" ).forEach( board => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { Dropdown } from "../controls/dropdown";
|
|||||||
import { zeroAppend } from "../other/utils";
|
import { zeroAppend } from "../other/utils";
|
||||||
import { ATC } from "./atc";
|
import { ATC } from "./atc";
|
||||||
import { Unit } from "../units/unit";
|
import { Unit } from "../units/unit";
|
||||||
import { getMissionData, getUnitsManager } from "..";
|
import { getMissionHandler, getUnitsManager } from "..";
|
||||||
import Sortable from "sortablejs";
|
import Sortable from "sortablejs";
|
||||||
import { FlightInterface } from "./atc";
|
import { FlightInterface } from "./atc";
|
||||||
import { getConnected } from "../server/server";
|
import { getConnected } from "../server/server";
|
||||||
@@ -115,11 +115,11 @@ export abstract class ATCBoard {
|
|||||||
|
|
||||||
addFlight( unit:Unit ) {
|
addFlight( unit:Unit ) {
|
||||||
|
|
||||||
const baseData = unit.getBaseData();
|
const baseData = unit.getData();
|
||||||
|
|
||||||
const unitCanBeAdded = () => {
|
const unitCanBeAdded = () => {
|
||||||
|
|
||||||
if ( baseData.category !== "Aircraft" ) {
|
if ( unit.getCategory() !== "Aircraft" ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,7 +345,7 @@ export abstract class ATCBoard {
|
|||||||
const results = Object.keys( units ).reduce( ( acc:Unit[], unitId:any ) => {
|
const results = Object.keys( units ).reduce( ( acc:Unit[], unitId:any ) => {
|
||||||
|
|
||||||
const unit = units[ unitId ];
|
const unit = units[ unitId ];
|
||||||
const baseData = unit.getBaseData();
|
const baseData = unit.getData();
|
||||||
|
|
||||||
if ( !unitIdsBeingMonitored.includes( parseInt( unitId ) ) && baseData.unitName.toLowerCase().indexOf( searchString ) > -1 ) {
|
if ( !unitIdsBeingMonitored.includes( parseInt( unitId ) ) && baseData.unitName.toLowerCase().indexOf( searchString ) > -1 ) {
|
||||||
acc.push( unit );
|
acc.push( unit );
|
||||||
@@ -359,7 +359,7 @@ export abstract class ATCBoard {
|
|||||||
|
|
||||||
results.forEach( unit => {
|
results.forEach( unit => {
|
||||||
|
|
||||||
const baseData = unit.getBaseData();
|
const baseData = unit.getData();
|
||||||
|
|
||||||
const a = document.createElement( "a" );
|
const a = document.createElement( "a" );
|
||||||
a.innerText = baseData.unitName;
|
a.innerText = baseData.unitName;
|
||||||
@@ -442,7 +442,7 @@ export abstract class ATCBoard {
|
|||||||
|
|
||||||
timeToGo( timestamp:number ) {
|
timeToGo( timestamp:number ) {
|
||||||
|
|
||||||
const timeData = this.calculateTimeToGo( this.getATC().getMissionDateTime().getTime(), timestamp );
|
const timeData = this.calculateTimeToGo( this.getATC().getMissionDate().getTime(), timestamp );
|
||||||
|
|
||||||
return ( timestamp === -1 ) ? "-" : timeData.elapsedMarker + timeData.time;
|
return ( timestamp === -1 ) ? "-" : timeData.elapsedMarker + timeData.time;
|
||||||
|
|
||||||
@@ -455,8 +455,8 @@ export abstract class ATCBoard {
|
|||||||
|
|
||||||
updateClock() {
|
updateClock() {
|
||||||
|
|
||||||
const missionTime = this.#atc.getMissionDateTime().getTime();
|
const missionTime = this.#atc.getMissionDate().getTime();
|
||||||
const timeDiff = new Date().getTime() - getMissionData().getUpdateTime();
|
const timeDiff = new Date().getTime() - getMissionHandler().getDateAndTime().elapsedTime;
|
||||||
|
|
||||||
const nowDate = new Date( missionTime + timeDiff );
|
const nowDate = new Date( missionTime + timeDiff );
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class ATCBoardGround extends ATCBoard {
|
|||||||
const flights = this.sortFlights( Object.values( this.getATC().getDataHandler().getFlights( this.getBoardId() ) ) );
|
const flights = this.sortFlights( Object.values( this.getATC().getDataHandler().getFlights( this.getBoardId() ) ) );
|
||||||
const stripBoard = this.getStripBoardElement();
|
const stripBoard = this.getStripBoardElement();
|
||||||
|
|
||||||
const missionTime = this.getATC().getMissionDateTime().getTime();
|
const missionTime = this.getATC().getMissionDate().getTime();
|
||||||
|
|
||||||
for( const strip of stripBoard.children ) {
|
for( const strip of stripBoard.children ) {
|
||||||
strip.toggleAttribute( "data-updating", true );
|
strip.toggleAttribute( "data-updating", true );
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class ATCBoardTower extends ATCBoard {
|
|||||||
update() {
|
update() {
|
||||||
|
|
||||||
const flights = this.sortFlights( Object.values( this.getATC().getDataHandler().getFlights( this.getBoardId() ) ) );
|
const flights = this.sortFlights( Object.values( this.getATC().getDataHandler().getFlights( this.getBoardId() ) ) );
|
||||||
const missionTime = this.getATC().getMissionDateTime().getTime();
|
const missionTime = this.getATC().getMissionDate().getTime();
|
||||||
const selectableUnits = getUnitsManager().getSelectableAircraft();
|
const selectableUnits = getUnitsManager().getSelectableAircraft();
|
||||||
const stripBoard = this.getStripBoardElement();
|
const stripBoard = this.getStripBoardElement();
|
||||||
|
|
||||||
@@ -34,13 +34,13 @@ export class ATCBoardTower extends ATCBoard {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const flightData:FlightData = {
|
const flightData = {
|
||||||
latitude: -1,
|
latitude: -1,
|
||||||
longitude: -1,
|
longitude: -1,
|
||||||
altitude: -1,
|
altitude: -1,
|
||||||
heading: -1,
|
heading: -1,
|
||||||
speed: -1,
|
speed: -1,
|
||||||
...( selectableUnits.hasOwnProperty( flight.unitId ) ? selectableUnits[flight.unitId].getFlightData() : {} )
|
...( selectableUnits.hasOwnProperty( flight.unitId ) ? selectableUnits[flight.unitId].getData() : {} )
|
||||||
};
|
};
|
||||||
|
|
||||||
if ( !strip ) {
|
if ( !strip ) {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ export class UnitDataTable extends Panel {
|
|||||||
var units = getUnitsManager().getUnits();
|
var units = getUnitsManager().getUnits();
|
||||||
|
|
||||||
const unitsArray = Object.values(units).sort((a: Unit, b: Unit) => {
|
const unitsArray = Object.values(units).sort((a: Unit, b: Unit) => {
|
||||||
const aVal = a.getBaseData().unitName?.toLowerCase();
|
const aVal = a.getUnitName()?.toLowerCase();
|
||||||
const bVal = b.getBaseData().unitName?.toLowerCase();
|
const bVal = b.getUnitName()?.toLowerCase();
|
||||||
|
|
||||||
if (aVal > bVal) {
|
if (aVal > bVal) {
|
||||||
return 1;
|
return 1;
|
||||||
@@ -48,7 +48,7 @@ export class UnitDataTable extends Panel {
|
|||||||
|
|
||||||
for (const unit of unitsArray) {
|
for (const unit of unitsArray) {
|
||||||
|
|
||||||
const dataset = [unit.getBaseData().unitName, unit.getBaseData().name, unit.getBaseData().category, (unit.getBaseData().controlled) ? "AI" : "Human"];
|
const dataset = [unit.getUnitName(), unit.getName(), unit.getCategory(), (unit.getControlled()) ? "AI" : "Human"];
|
||||||
|
|
||||||
addRow(el, dataset);
|
addRow(el, dataset);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,43 @@
|
|||||||
import { LatLng, LatLngBounds, TileLayer, tileLayer } from "leaflet";
|
import { LatLng, LatLngBounds } from "leaflet";
|
||||||
|
|
||||||
export const ROEs: string[] = ["Hold", "Return", "Designated", "Free"];
|
export const NONE = "None";
|
||||||
export const reactionsToThreat: string[] = ["None", "Manoeuvre", "Passive", "Evade"];
|
export const GAME_MASTER = "Game master";
|
||||||
export const emissionsCountermeasures: string[] = ["Silent", "Attack", "Defend", "Free"];
|
export const BLUE_COMMANDER = "Blue commander";
|
||||||
|
export const RED_COMMANDER = "Red commander";
|
||||||
|
|
||||||
export const ROEDescriptions: string[] = ["Hold (Never fire)", "Return (Only fire if fired upon)", "Designated (Attack the designated target only)", "Free (Attack anyone)"];
|
export const VISUAL = 1;
|
||||||
export const reactionsToThreatDescriptions: string[] = ["None (No reaction)", "Manoeuvre (no countermeasures)", "Passive (Countermeasures only, no manoeuvre)", "Evade (Countermeasures and manoeuvers)"];
|
export const OPTIC = 2;
|
||||||
export const emissionsCountermeasuresDescriptions: string[] = ["Silent (Radar OFF, no ECM)", "Attack (Radar only for targeting, ECM only if locked)", "Defend (Radar for searching, ECM if locked)", "Always on (Radar and ECM always on)"];
|
export const RADAR = 4;
|
||||||
|
export const IRST = 8;
|
||||||
|
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 reactionsToThreat: string[] = ["none", "manoeuvre", "passive", "evade"];
|
||||||
|
export const emissionsCountermeasures: string[] = ["silent", "attack", "defend", "free"];
|
||||||
|
|
||||||
|
export const ROEDescriptions: string[] = [
|
||||||
|
"Free (Attack anyone)",
|
||||||
|
"Designated (Attack the designated target only)",
|
||||||
|
"",
|
||||||
|
"Return (Only fire if fired upon)",
|
||||||
|
"Hold (Never fire)"
|
||||||
|
];
|
||||||
|
|
||||||
|
export const reactionsToThreatDescriptions: string[] = [
|
||||||
|
"None (No reaction)",
|
||||||
|
"Manoeuvre (no countermeasures)",
|
||||||
|
"Passive (Countermeasures only, no manoeuvre)",
|
||||||
|
"Evade (Countermeasures and manoeuvers)"
|
||||||
|
];
|
||||||
|
|
||||||
|
export const emissionsCountermeasuresDescriptions: string[] = [
|
||||||
|
"Silent (Radar OFF, no ECM)",
|
||||||
|
"Attack (Radar only for targeting, ECM only if locked)",
|
||||||
|
"Defend (Radar for searching, ECM if locked)",
|
||||||
|
"Always on (Radar and ECM always on)"
|
||||||
|
];
|
||||||
|
|
||||||
export const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 };
|
export const minSpeedValues: { [key: string]: number } = { Aircraft: 100, Helicopter: 0, NavyUnit: 0, GroundUnit: 0 };
|
||||||
export const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 };
|
export const maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 };
|
||||||
@@ -100,3 +131,64 @@ export const layers = {
|
|||||||
attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
attribution: '<a href="https://github.com/cyclosm/cyclosm-cartocss-style/releases" title="CyclOSM - Open Bicycle render">CyclOSM</a> | Map data: © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Map constants */
|
||||||
|
export const IDLE = "Idle";
|
||||||
|
export const MOVE_UNIT = "Move unit";
|
||||||
|
export const BOMBING = "Bombing";
|
||||||
|
export const CARPET_BOMBING = "Carpet bombing";
|
||||||
|
export const FIRE_AT_AREA = "Fire at area";
|
||||||
|
export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area";
|
||||||
|
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"];
|
||||||
|
|
||||||
|
export const IADSTypes = ["AAA", "MANPADS", "SAM Site", "Radar"];
|
||||||
|
export const IADSDensities: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Site": 0.1, "Radar": 0.05};
|
||||||
|
|
||||||
|
export const SHOW_CONTACT_LINES = "Show unit contact lines";
|
||||||
|
export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out";
|
||||||
|
export const SHOW_UNIT_PATHS = "Show unit paths";
|
||||||
|
export const SHOW_UNIT_TARGETS = "Show unit targets";
|
||||||
|
|
||||||
|
export enum DataIndexes {
|
||||||
|
startOfData = 0,
|
||||||
|
category,
|
||||||
|
alive,
|
||||||
|
human,
|
||||||
|
controlled,
|
||||||
|
coalition,
|
||||||
|
country,
|
||||||
|
name,
|
||||||
|
unitName,
|
||||||
|
groupName,
|
||||||
|
state,
|
||||||
|
task,
|
||||||
|
hasTask,
|
||||||
|
position,
|
||||||
|
speed,
|
||||||
|
heading,
|
||||||
|
isTanker,
|
||||||
|
isAWACS,
|
||||||
|
onOff,
|
||||||
|
followRoads,
|
||||||
|
fuel,
|
||||||
|
desiredSpeed,
|
||||||
|
desiredSpeedType,
|
||||||
|
desiredAltitude,
|
||||||
|
desiredAltitudeType,
|
||||||
|
leaderID,
|
||||||
|
formationOffset,
|
||||||
|
targetID,
|
||||||
|
targetPosition,
|
||||||
|
ROE,
|
||||||
|
reactionToThreat,
|
||||||
|
emissionsCountermeasures,
|
||||||
|
TACAN,
|
||||||
|
radio,
|
||||||
|
generalSettings,
|
||||||
|
ammo,
|
||||||
|
contacts,
|
||||||
|
activePath,
|
||||||
|
isLeader,
|
||||||
|
endOfData = 255
|
||||||
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { getMap, getUnitsManager, setActiveCoalition } from "..";
|
import { getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
|
||||||
import { Airbase } from "../missionhandler/airbase";
|
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "../constants/constants";
|
||||||
|
import { Airbase } from "../mission/airbase";
|
||||||
import { ContextMenu } from "./contextmenu";
|
import { ContextMenu } from "./contextmenu";
|
||||||
|
|
||||||
export class AirbaseContextMenu extends ContextMenu {
|
export class AirbaseContextMenu extends ContextMenu {
|
||||||
@@ -24,7 +25,8 @@ export class AirbaseContextMenu extends ContextMenu {
|
|||||||
this.setProperties(airbase.getProperties());
|
this.setProperties(airbase.getProperties());
|
||||||
this.setParkings(airbase.getParkings());
|
this.setParkings(airbase.getParkings());
|
||||||
this.setCoalition(airbase.getCoalition());
|
this.setCoalition(airbase.getCoalition());
|
||||||
this.enableLandButton(getUnitsManager().getSelectedUnitsTypes().length == 1 && getUnitsManager().getSelectedUnitsTypes()[0] === "Aircraft" && (getUnitsManager().getSelectedUnitsCoalition() === airbase.getCoalition() || airbase.getCoalition() === "neutral"))
|
this.enableLandButton(getUnitsManager().getSelectedUnitsTypes().length == 1 && ["Aircraft", "Helicopter"].includes(getUnitsManager().getSelectedUnitsTypes()[0]) && (getUnitsManager().getSelectedUnitsCoalition() === airbase.getCoalition() || airbase.getCoalition() === "neutral"))
|
||||||
|
this.enableSpawnButton(getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || this.#airbase.getCoalition() == getMissionHandler().getCommandedCoalition());
|
||||||
}
|
}
|
||||||
|
|
||||||
setName(airbaseName: string) {
|
setName(airbaseName: string) {
|
||||||
@@ -50,7 +52,11 @@ export class AirbaseContextMenu extends ContextMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCoalition(coalition: string) {
|
setCoalition(coalition: string) {
|
||||||
(<HTMLElement>this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")).dataset.activeCoalition = coalition;
|
(<HTMLElement>this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")).dataset.coalition = coalition;
|
||||||
|
}
|
||||||
|
|
||||||
|
enableSpawnButton(enableSpawnButton: boolean) {
|
||||||
|
this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")?.classList.toggle("hide", !enableSpawnButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
enableLandButton(enableLandButton: boolean) {
|
enableLandButton(enableLandButton: boolean) {
|
||||||
@@ -60,8 +66,10 @@ export class AirbaseContextMenu extends ContextMenu {
|
|||||||
showSpawnMenu() {
|
showSpawnMenu() {
|
||||||
if (this.#airbase != null) {
|
if (this.#airbase != null) {
|
||||||
setActiveCoalition(this.#airbase.getCoalition());
|
setActiveCoalition(this.#airbase.getCoalition());
|
||||||
getMap().showMapContextMenu({ originalEvent: { x: this.getX(), y: this.getY(), latlng: this.getLatLng() } });
|
getMap().showMapContextMenu(this.getX(), this.getY(), this.getLatLng());
|
||||||
getMap().getMapContextMenu().hideUpperBar();
|
getMap().getMapContextMenu().hideUpperBar();
|
||||||
|
getMap().getMapContextMenu().hideLowerBar();
|
||||||
|
getMap().getMapContextMenu().hideAltitudeSlider();
|
||||||
getMap().getMapContextMenu().showSubMenu("aircraft");
|
getMap().getMapContextMenu().showSubMenu("aircraft");
|
||||||
getMap().getMapContextMenu().setAirbaseName(this.#airbase.getName());
|
getMap().getMapContextMenu().setAirbaseName(this.#airbase.getName());
|
||||||
getMap().getMapContextMenu().setLatLng(this.#airbase.getLatLng());
|
getMap().getMapContextMenu().setLatLng(this.#airbase.getLatLng());
|
||||||
|
|||||||
124
client/src/controls/coalitionareacontextmenu.ts
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
import { LatLng } from "leaflet";
|
||||||
|
import { getMap, getMissionHandler, getUnitsManager } from "..";
|
||||||
|
import { GAME_MASTER, IADSTypes } from "../constants/constants";
|
||||||
|
import { CoalitionArea } from "../map/coalitionarea";
|
||||||
|
import { ContextMenu } from "./contextmenu";
|
||||||
|
import { Dropdown } from "./dropdown";
|
||||||
|
import { Slider } from "./slider";
|
||||||
|
import { Switch } from "./switch";
|
||||||
|
import { groundUnitDatabase } from "../units/groundunitdatabase";
|
||||||
|
import { createCheckboxOption, getCheckboxOptions } from "../other/utils";
|
||||||
|
|
||||||
|
export class CoalitionAreaContextMenu extends ContextMenu {
|
||||||
|
#coalitionSwitch: Switch;
|
||||||
|
#coalitionArea: CoalitionArea | null = null;
|
||||||
|
#iadsDensitySlider: Slider;
|
||||||
|
#iadsDistributionSlider: Slider;
|
||||||
|
#iadsTypesDropdown: Dropdown;
|
||||||
|
#iadsErasDropdown: Dropdown;
|
||||||
|
#iadsRangesDropdown: Dropdown;
|
||||||
|
|
||||||
|
constructor(id: string) {
|
||||||
|
super(id);
|
||||||
|
|
||||||
|
this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||||
|
this.#coalitionSwitch.setValue(false);
|
||||||
|
this.#iadsTypesDropdown = new Dropdown("iads-units-type-options", () => { });
|
||||||
|
this.#iadsErasDropdown = new Dropdown("iads-era-options", () => {});
|
||||||
|
this.#iadsRangesDropdown = new Dropdown("iads-range-options", () => {});
|
||||||
|
this.#iadsDensitySlider = new Slider("iads-density-slider", 5, 100, "%", (value: number) => { });
|
||||||
|
this.#iadsDistributionSlider = new Slider("iads-distribution-slider", 5, 100, "%", (value: number) => { });
|
||||||
|
this.#iadsDensitySlider.setIncrement(5);
|
||||||
|
this.#iadsDensitySlider.setValue(50);
|
||||||
|
this.#iadsDensitySlider.setActive(true);
|
||||||
|
this.#iadsDistributionSlider.setIncrement(5);
|
||||||
|
this.#iadsDistributionSlider.setValue(50);
|
||||||
|
this.#iadsDistributionSlider.setActive(true);
|
||||||
|
|
||||||
|
document.addEventListener("coalitionAreaContextMenuShow", (e: any) => {
|
||||||
|
if (this.getVisibleSubMenu() !== e.detail.type)
|
||||||
|
this.showSubMenu(e.detail.type);
|
||||||
|
else
|
||||||
|
this.hideSubMenus();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("coalitionAreaBringToBack", (e: any) => {
|
||||||
|
if (this.#coalitionArea)
|
||||||
|
getMap().bringCoalitionAreaToBack(this.#coalitionArea);
|
||||||
|
getMap().hideCoalitionAreaContextMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("coalitionAreaDelete", (e: any) => {
|
||||||
|
if (this.#coalitionArea)
|
||||||
|
getMap().deleteCoalitionArea(this.#coalitionArea);
|
||||||
|
getMap().hideCoalitionAreaContextMenu();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("contextMenuCreateIads", (e: any) => {
|
||||||
|
const area = this.getCoalitionArea();
|
||||||
|
if (area)
|
||||||
|
getUnitsManager().createIADS(area, getCheckboxOptions(this.#iadsTypesDropdown), getCheckboxOptions(this.#iadsErasDropdown), getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue());
|
||||||
|
})
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
show(x: number, y: number, latlng: LatLng) {
|
||||||
|
super.show(x, y, latlng);
|
||||||
|
|
||||||
|
/* Create the checkboxes to select the unit roles */
|
||||||
|
this.#iadsTypesDropdown.setOptionsElements(IADSTypes.map((role: string) => {
|
||||||
|
return createCheckboxOption(role, `Add ${role}s to the IADS` );
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
/* Create the checkboxes to select the unit periods */
|
||||||
|
this.#iadsErasDropdown.setOptionsElements(groundUnitDatabase.getEras().map((era: string) => {
|
||||||
|
return createCheckboxOption(era, `Add ${era} era units to the IADS`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* Create the checkboxes to select the unit ranges */
|
||||||
|
this.#iadsRangesDropdown.setOptionsElements(groundUnitDatabase.getRanges().map((range: string) => {
|
||||||
|
return createCheckboxOption(range, `Add ${range} units to the IADS`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER)
|
||||||
|
this.#coalitionSwitch.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
showSubMenu(type: string) {
|
||||||
|
this.getContainer()?.querySelector("#iads-menu")?.classList.toggle("hide", type !== "iads");
|
||||||
|
this.getContainer()?.querySelector("#iads-button")?.classList.toggle("is-open", type === "iads");
|
||||||
|
this.clip();
|
||||||
|
|
||||||
|
this.setVisibleSubMenu(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideSubMenus() {
|
||||||
|
this.getContainer()?.querySelector("#iads-menu")?.classList.toggle("hide", true);
|
||||||
|
this.getContainer()?.querySelector("#iads-button")?.classList.toggle("is-open", false);
|
||||||
|
this.clip();
|
||||||
|
|
||||||
|
this.setVisibleSubMenu(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCoalitionArea() {
|
||||||
|
return this.#coalitionArea;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCoalitionArea(coalitionArea: CoalitionArea) {
|
||||||
|
this.#coalitionArea = coalitionArea;
|
||||||
|
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||||
|
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||||
|
});
|
||||||
|
this.#coalitionSwitch.setValue(this.getCoalitionArea()?.getCoalition() === "red");
|
||||||
|
}
|
||||||
|
|
||||||
|
#onSwitchClick(value: boolean) {
|
||||||
|
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER) {
|
||||||
|
this.getCoalitionArea()?.setCoalition(value ? "red" : "blue");
|
||||||
|
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||||
|
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ export class ContextMenu {
|
|||||||
#latlng: LatLng = new LatLng(0, 0);
|
#latlng: LatLng = new LatLng(0, 0);
|
||||||
#x: number = 0;
|
#x: number = 0;
|
||||||
#y: number = 0;
|
#y: number = 0;
|
||||||
|
#visibleSubMenu: string | null = null;
|
||||||
|
#hidden: boolean = true;
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
this.#container = document.getElementById(id);
|
this.#container = document.getElementById(id);
|
||||||
@@ -17,10 +19,12 @@ export class ContextMenu {
|
|||||||
this.#x = x;
|
this.#x = x;
|
||||||
this.#y = y;
|
this.#y = y;
|
||||||
this.clip();
|
this.clip();
|
||||||
|
this.#hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hide() {
|
hide() {
|
||||||
this.#container?.classList.toggle("hide", true);
|
this.#container?.classList.toggle("hide", true);
|
||||||
|
this.#hidden = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
getContainer() {
|
getContainer() {
|
||||||
@@ -39,6 +43,10 @@ export class ContextMenu {
|
|||||||
return this.#y;
|
return this.#y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getHidden() {
|
||||||
|
return this.#hidden;
|
||||||
|
}
|
||||||
|
|
||||||
clip() {
|
clip() {
|
||||||
if (this.#container != null) {
|
if (this.#container != null) {
|
||||||
if (this.#x + this.#container.offsetWidth < window.innerWidth)
|
if (this.#x + this.#container.offsetWidth < window.innerWidth)
|
||||||
@@ -52,4 +60,12 @@ export class ContextMenu {
|
|||||||
this.#container.style.top = window.innerHeight - this.#container.offsetHeight - 10 + "px";
|
this.#container.style.top = window.innerHeight - this.#container.offsetHeight - 10 + "px";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setVisibleSubMenu(menu: string | null) {
|
||||||
|
this.#visibleSubMenu = menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisibleSubMenu() {
|
||||||
|
return this.#visibleSubMenu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,10 @@ export class Dropdown {
|
|||||||
|
|
||||||
setOptions(optionsList: string[], sortAlphabetically: boolean = true) {
|
setOptions(optionsList: string[], sortAlphabetically: boolean = true) {
|
||||||
this.#optionsList = optionsList.sort();
|
this.#optionsList = optionsList.sort();
|
||||||
|
if (this.#optionsList.length == 0) {
|
||||||
|
optionsList = ["No options available"]
|
||||||
|
this.#value.innerText = "No options available";
|
||||||
|
}
|
||||||
this.#options.replaceChildren(...optionsList.map((option: string, idx: number) => {
|
this.#options.replaceChildren(...optionsList.map((option: string, idx: number) => {
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
var button = document.createElement("button");
|
var button = document.createElement("button");
|
||||||
@@ -50,6 +54,15 @@ export class Dropdown {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setOptionsElements(optionsElements: HTMLElement[]) {
|
||||||
|
this.#optionsList = [];
|
||||||
|
this.#options.replaceChildren(...optionsElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
getOptionElements() {
|
||||||
|
return this.#options.children;
|
||||||
|
}
|
||||||
|
|
||||||
selectText(text: string) {
|
selectText(text: string) {
|
||||||
const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text);
|
const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
|
|||||||
@@ -1,69 +1,153 @@
|
|||||||
import { LatLng } from "leaflet";
|
import { LatLng } from "leaflet";
|
||||||
import { getActiveCoalition, getMap, setActiveCoalition } from "..";
|
import { getActiveCoalition, getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
|
||||||
import { spawnAircraft, spawnExplosion, spawnGroundUnit, spawnSmoke } from "../server/server";
|
import { spawnExplosion, spawnSmoke } from "../server/server";
|
||||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
import { groundUnitDatabase } from "../units/groundunitdatabase";
|
||||||
|
import { helicopterDatabase } from "../units/helicopterdatabase";
|
||||||
import { ContextMenu } from "./contextmenu";
|
import { ContextMenu } from "./contextmenu";
|
||||||
import { Dropdown } from "./dropdown";
|
import { Dropdown } from "./dropdown";
|
||||||
import { Switch } from "./switch";
|
import { Switch } from "./switch";
|
||||||
import { Slider } from "./slider";
|
import { Slider } from "./slider";
|
||||||
import { ftToM } from "../other/utils";
|
import { ftToM } from "../other/utils";
|
||||||
|
import { GAME_MASTER } from "../constants/constants";
|
||||||
export interface SpawnOptions {
|
import { navyUnitDatabase } from "../units/navyunitdatabase";
|
||||||
role: string;
|
import { CoalitionArea } from "../map/coalitionarea";
|
||||||
type: string;
|
|
||||||
latlng: LatLng;
|
|
||||||
coalition: string;
|
|
||||||
loadout: string | null;
|
|
||||||
airbaseName: string | null;
|
|
||||||
altitude: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MapContextMenu extends ContextMenu {
|
export class MapContextMenu extends ContextMenu {
|
||||||
#coalitionSwitch: Switch;
|
#coalitionSwitch: Switch;
|
||||||
#aircraftRoleDropdown: Dropdown;
|
#aircraftRoleDropdown: Dropdown;
|
||||||
#aircraftTypeDropdown: Dropdown;
|
#aircraftLabelDropdown: Dropdown;
|
||||||
|
#aircraftCountDropdown: Dropdown;
|
||||||
#aircraftLoadoutDropdown: Dropdown;
|
#aircraftLoadoutDropdown: Dropdown;
|
||||||
#aircrafSpawnAltitudeSlider: Slider;
|
#aircraftSpawnAltitudeSlider: Slider;
|
||||||
#groundUnitRoleDropdown: Dropdown;
|
#helicopterRoleDropdown: Dropdown;
|
||||||
|
#helicopterLabelDropdown: Dropdown;
|
||||||
|
#helicopterCountDropdown: Dropdown;
|
||||||
|
#helicopterLoadoutDropdown: Dropdown;
|
||||||
|
#helicopterSpawnAltitudeSlider: Slider;
|
||||||
#groundUnitTypeDropdown: Dropdown;
|
#groundUnitTypeDropdown: Dropdown;
|
||||||
#spawnOptions: SpawnOptions = { role: "", type: "", latlng: new LatLng(0, 0), loadout: null, coalition: "blue", airbaseName: null, altitude: ftToM(20000) };
|
#groundUnitLabelDropdown: Dropdown;
|
||||||
|
#groundUnitCountDropdown: Dropdown;
|
||||||
|
#navyUnitTypeDropdown: Dropdown;
|
||||||
|
#navyUnitLabelDropdown: Dropdown;
|
||||||
|
#navyUnitCountDropdown: Dropdown;
|
||||||
|
#spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: 0, count: 1 };
|
||||||
|
#coalitionArea: CoalitionArea | null = null;
|
||||||
|
|
||||||
constructor(id: string) {
|
constructor(id: string) {
|
||||||
super(id);
|
super(id);
|
||||||
|
|
||||||
this.#coalitionSwitch = new Switch("coalition-switch", this.#onSwitchClick);
|
this.#coalitionSwitch = new Switch("coalition-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||||
this.#coalitionSwitch.setValue(false);
|
this.#coalitionSwitch.setValue(false);
|
||||||
this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick(e));
|
this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick(e));
|
||||||
this.#aircraftRoleDropdown = new Dropdown("aircraft-role-options", (role: string) => this.#setAircraftRole(role));
|
|
||||||
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));
|
|
||||||
|
|
||||||
document.addEventListener("contextMenuShow", (e: any) => {
|
/* Aircraft menu */
|
||||||
this.showSubMenu(e.detail.type);
|
this.#aircraftRoleDropdown = new Dropdown("aircraft-role-options", (role: string) => this.#setAircraftRole(role));
|
||||||
|
this.#aircraftLabelDropdown = new Dropdown("aircraft-label-options", (type: string) => this.#setAircraftLabel(type));
|
||||||
|
this.#aircraftCountDropdown = new Dropdown("aircraft-count-options", (type: string) => this.#setAircraftCount(type));
|
||||||
|
this.#aircraftCountDropdown.setOptions(["1", "2", "3", "4"]);
|
||||||
|
this.#aircraftCountDropdown.setValue("1");
|
||||||
|
this.#aircraftLoadoutDropdown = new Dropdown("aircraft-loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout));
|
||||||
|
this.#aircraftSpawnAltitudeSlider = new Slider("aircraft-spawn-altitude-slider", 0, 50000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);});
|
||||||
|
this.#aircraftSpawnAltitudeSlider.setIncrement(500);
|
||||||
|
this.#aircraftSpawnAltitudeSlider.setValue(20000);
|
||||||
|
this.#aircraftSpawnAltitudeSlider.setActive(true);
|
||||||
|
|
||||||
|
/* Helicopter menu */
|
||||||
|
this.#helicopterRoleDropdown = new Dropdown("helicopter-role-options", (role: string) => this.#setHelicopterRole(role));
|
||||||
|
this.#helicopterLabelDropdown = new Dropdown("helicopter-label-options", (type: string) => this.#setHelicopterLabel(type));
|
||||||
|
this.#helicopterCountDropdown = new Dropdown("helicopter-count-options", (type: string) => this.#setHelicopterCount(type));
|
||||||
|
this.#helicopterCountDropdown.setOptions(["1", "2", "3", "4"]);
|
||||||
|
this.#helicopterCountDropdown.setValue("1");
|
||||||
|
this.#helicopterLoadoutDropdown = new Dropdown("helicopter-loadout-options", (loadout: string) => this.#setHelicopterLoadout(loadout));
|
||||||
|
this.#helicopterSpawnAltitudeSlider = new Slider("helicopter-spawn-altitude-slider", 0, 10000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);});
|
||||||
|
this.#helicopterSpawnAltitudeSlider.setIncrement(50);
|
||||||
|
this.#helicopterSpawnAltitudeSlider.setValue(5000);
|
||||||
|
this.#helicopterSpawnAltitudeSlider.setActive(true);
|
||||||
|
|
||||||
|
var count = [];
|
||||||
|
for (let i = 1; i < 10; i++) count.push(String(i));
|
||||||
|
|
||||||
|
/* Ground unit menu */
|
||||||
|
this.#groundUnitTypeDropdown = new Dropdown("groundunit-type-options", (type: string) => this.#setGroundUnitType(type));
|
||||||
|
this.#groundUnitLabelDropdown = new Dropdown("groundunit-label-options", (name: string) => this.#setGroundUnitLabel(name));
|
||||||
|
this.#groundUnitCountDropdown = new Dropdown("groundunit-count-options", (count: string) => this.#setGroundUnitCount(count));
|
||||||
|
this.#groundUnitCountDropdown.setOptions(count);
|
||||||
|
this.#groundUnitCountDropdown.setValue("1");
|
||||||
|
|
||||||
|
/* Navy unit menu */
|
||||||
|
this.#navyUnitTypeDropdown = new Dropdown("navyunit-type-options", (type: string) => this.#setNavyUnitType(type));
|
||||||
|
this.#navyUnitLabelDropdown = new Dropdown("navyunit-label-options", (name: string) => this.#setNavyUnitLabel(name));
|
||||||
|
this.#navyUnitCountDropdown = new Dropdown("navyunit-count-options", (count: string) => this.#setNavyUnitCount(count));
|
||||||
|
this.#navyUnitCountDropdown.setOptions(count);
|
||||||
|
this.#navyUnitCountDropdown.setValue("1");
|
||||||
|
|
||||||
|
document.addEventListener("mapContextMenuShow", (e: any) => {
|
||||||
|
if (this.getVisibleSubMenu() !== e.detail.type)
|
||||||
|
this.showSubMenu(e.detail.type);
|
||||||
|
else
|
||||||
|
this.hideSubMenus(e.detail.type);
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("contextMenuDeployAircraft", () => {
|
document.addEventListener("contextMenuDeployAircrafts", () => {
|
||||||
this.hide();
|
|
||||||
this.#spawnOptions.coalition = getActiveCoalition();
|
this.#spawnOptions.coalition = getActiveCoalition();
|
||||||
if (this.#spawnOptions) {
|
if (this.#spawnOptions) {
|
||||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng);
|
var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng, altitude: this.#spawnOptions.altitude, loadout: this.#spawnOptions.loadout};
|
||||||
spawnAircraft(this.#spawnOptions);
|
var units = [];
|
||||||
|
for (let i = 1; i < parseInt(this.#aircraftCountDropdown.getValue()) + 1; i++) {
|
||||||
|
units.push(unitTable);
|
||||||
|
}
|
||||||
|
if (getUnitsManager().spawnUnits("Aircraft", units, getActiveCoalition(), false, this.#spawnOptions.airbaseName)) {
|
||||||
|
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.addEventListener("contextMenuDeployGroundUnit", () => {
|
document.addEventListener("contextMenuDeployHelicopters", () => {
|
||||||
this.hide();
|
|
||||||
this.#spawnOptions.coalition = getActiveCoalition();
|
this.#spawnOptions.coalition = getActiveCoalition();
|
||||||
if (this.#spawnOptions) {
|
if (this.#spawnOptions) {
|
||||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng);
|
var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng, altitude: this.#spawnOptions.altitude, loadout: this.#spawnOptions.loadout};
|
||||||
spawnGroundUnit(this.#spawnOptions);
|
var units = [];
|
||||||
|
for (let i = 1; i < parseInt(this.#helicopterCountDropdown.getValue()) + 1; i++) {
|
||||||
|
units.push(unitTable);
|
||||||
|
}
|
||||||
|
if (getUnitsManager().spawnUnits("Helicopter", units, getActiveCoalition(), false, this.#spawnOptions.airbaseName)) {
|
||||||
|
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("contextMenuDeployGroundUnits", () => {
|
||||||
|
this.#spawnOptions.coalition = getActiveCoalition();
|
||||||
|
if (this.#spawnOptions) {
|
||||||
|
var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng};
|
||||||
|
var units = [];
|
||||||
|
for (let i = 1; i < parseInt(this.#groundUnitCountDropdown.getValue()) + 1; i++) {
|
||||||
|
units.push(JSON.parse(JSON.stringify(unitTable)));
|
||||||
|
unitTable.location.lat += 0.0001;
|
||||||
|
}
|
||||||
|
if (getUnitsManager().spawnUnits("GroundUnit", units, getActiveCoalition(), false)) {
|
||||||
|
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("contextMenuDeployNavyUnits", () => {
|
||||||
|
this.#spawnOptions.coalition = getActiveCoalition();
|
||||||
|
if (this.#spawnOptions) {
|
||||||
|
var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng};
|
||||||
|
var units = [];
|
||||||
|
for (let i = 1; i < parseInt(this.#navyUnitCountDropdown.getValue()) + 1; i++) {
|
||||||
|
units.push(JSON.parse(JSON.stringify(unitTable)));
|
||||||
|
unitTable.location.lat += 0.0001;
|
||||||
|
}
|
||||||
|
if (getUnitsManager().spawnUnits("NavyUnit", units, getActiveCoalition(), false)) {
|
||||||
|
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||||
|
this.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -77,40 +161,140 @@ export class MapContextMenu extends ContextMenu {
|
|||||||
spawnExplosion(e.detail.strength, this.getLatLng());
|
spawnExplosion(e.detail.strength, this.getLatLng());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.addEventListener("editCoalitionArea", (e: any) => {
|
||||||
|
this.hide();
|
||||||
|
if (this.#coalitionArea) {
|
||||||
|
getMap().deselectAllCoalitionAreas();
|
||||||
|
this.#coalitionArea.setSelected(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener("commandModeOptionsChanged", (e: any) => {
|
||||||
|
this.#refreshOptions();
|
||||||
|
});
|
||||||
|
|
||||||
this.hide();
|
this.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
show(x: number, y: number, latlng: LatLng) {
|
show(x: number, y: number, latlng: LatLng) {
|
||||||
this.#spawnOptions.airbaseName = null;
|
|
||||||
super.show(x, y, latlng);
|
super.show(x, y, latlng);
|
||||||
this.#spawnOptions.latlng = latlng;
|
|
||||||
this.showUpperBar();
|
this.showUpperBar();
|
||||||
|
|
||||||
|
this.showAltitudeSlider();
|
||||||
|
|
||||||
|
this.#spawnOptions.airbaseName = "";
|
||||||
|
this.#spawnOptions.latlng = latlng;
|
||||||
|
|
||||||
|
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||||
|
if (getActiveCoalition() == "blue")
|
||||||
|
this.#coalitionSwitch.setValue(false);
|
||||||
|
else if (getActiveCoalition() == "red")
|
||||||
|
this.#coalitionSwitch.setValue(true);
|
||||||
|
else
|
||||||
|
this.#coalitionSwitch.setValue(undefined);
|
||||||
|
|
||||||
|
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER)
|
||||||
|
this.#coalitionSwitch.hide()
|
||||||
|
|
||||||
|
this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
showSubMenu(type: string) {
|
showSubMenu(type: string) {
|
||||||
|
if (type === "more")
|
||||||
|
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide");
|
||||||
|
else if (["aircraft", "groundunit"].includes(type))
|
||||||
|
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", true);
|
||||||
|
|
||||||
this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", type !== "aircraft");
|
this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", type !== "aircraft");
|
||||||
this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", type === "aircraft");
|
this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", type === "aircraft");
|
||||||
this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.classList.toggle("hide", type !== "ground-unit");
|
this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", type !== "helicopter");
|
||||||
this.getContainer()?.querySelector("#ground-unit-spawn-button")?.classList.toggle("is-open", type === "ground-unit");
|
this.getContainer()?.querySelector("#helicopter-spawn-button")?.classList.toggle("is-open", type === "helicopter");
|
||||||
|
this.getContainer()?.querySelector("#groundunit-spawn-menu")?.classList.toggle("hide", type !== "groundunit");
|
||||||
|
this.getContainer()?.querySelector("#groundunit-spawn-button")?.classList.toggle("is-open", type === "groundunit");
|
||||||
|
this.getContainer()?.querySelector("#navyunit-spawn-menu")?.classList.toggle("hide", type !== "navyunit");
|
||||||
|
this.getContainer()?.querySelector("#navyunit-spawn-button")?.classList.toggle("is-open", type === "navyunit");
|
||||||
this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", type !== "smoke");
|
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("#smoke-spawn-button")?.classList.toggle("is-open", type === "smoke");
|
||||||
this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", type !== "explosion");
|
this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", type !== "explosion");
|
||||||
this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", type === "explosion");
|
this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", type === "explosion");
|
||||||
|
|
||||||
|
(this.getContainer()?.querySelectorAll(".deploy-unit-button"))?.forEach((element: Node) => {(element as HTMLButtonElement).disabled = true;})
|
||||||
|
|
||||||
this.#resetAircraftRole();
|
this.#resetAircraftRole();
|
||||||
this.#resetAircraftType();
|
this.#resetAircraftLabel();
|
||||||
this.#resetGroundUnitRole();
|
this.#resetHelicopterRole();
|
||||||
|
this.#resetHelicopterLabel();
|
||||||
this.#resetGroundUnitType();
|
this.#resetGroundUnitType();
|
||||||
|
this.#resetGroundUnitLabel();
|
||||||
|
this.#resetNavyUnitType();
|
||||||
|
this.#resetNavyUnitLabel();
|
||||||
|
this.#aircraftCountDropdown.setValue("1");
|
||||||
|
this.#helicopterCountDropdown.setValue("1");
|
||||||
|
this.#groundUnitCountDropdown.setValue("1");
|
||||||
this.clip();
|
this.clip();
|
||||||
|
|
||||||
|
if (type === "aircraft") {
|
||||||
|
this.#spawnOptions.altitude = ftToM(this.#aircraftSpawnAltitudeSlider.getValue());
|
||||||
|
}
|
||||||
|
else if (type === "helicopter") {
|
||||||
|
this.#spawnOptions.altitude = ftToM(this.#helicopterSpawnAltitudeSlider.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setVisibleSubMenu(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideSubMenus(type: string) {
|
||||||
|
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", ["aircraft", "groundunit"].includes(type));
|
||||||
|
this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", true);
|
||||||
|
this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", false);
|
||||||
|
this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", true);
|
||||||
|
this.getContainer()?.querySelector("#helicopter-spawn-button")?.classList.toggle("is-open", false);
|
||||||
|
this.getContainer()?.querySelector("#groundunit-spawn-menu")?.classList.toggle("hide", true);
|
||||||
|
this.getContainer()?.querySelector("#groundunit-spawn-button")?.classList.toggle("is-open", false);
|
||||||
|
this.getContainer()?.querySelector("#navyunit-spawn-menu")?.classList.toggle("hide", true);
|
||||||
|
this.getContainer()?.querySelector("#navyunit-spawn-button")?.classList.toggle("is-open", false);
|
||||||
|
this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", true);
|
||||||
|
this.getContainer()?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", false);
|
||||||
|
this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", true);
|
||||||
|
this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", false);
|
||||||
|
|
||||||
|
this.#resetAircraftRole();
|
||||||
|
this.#resetAircraftLabel();
|
||||||
|
this.#resetHelicopterRole();
|
||||||
|
this.#resetHelicopterLabel();
|
||||||
|
this.#resetHelicopterRole();
|
||||||
|
this.#resetHelicopterLabel();
|
||||||
|
this.#resetGroundUnitType();
|
||||||
|
this.#resetGroundUnitLabel();
|
||||||
|
this.#resetNavyUnitType();
|
||||||
|
this.#resetNavyUnitLabel();
|
||||||
|
this.clip();
|
||||||
|
|
||||||
|
this.setVisibleSubMenu(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
showUpperBar() {
|
showUpperBar() {
|
||||||
this.getContainer()?.querySelector("#upper-bar")?.classList.toggle("hide", false);
|
this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
hideUpperBar() {
|
hideUpperBar() {
|
||||||
this.getContainer()?.querySelector("#upper-bar")?.classList.toggle("hide", true);
|
this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
showLowerBar() {
|
||||||
|
this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideLowerBar() {
|
||||||
|
this.getContainer()?.querySelector("#more-optionsbutton-bar")?.classList.toggle("hide", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
showAltitudeSlider() {
|
||||||
|
this.getContainer()?.querySelector("#aircraft-spawn-altitude-slider")?.classList.toggle("hide", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
hideAltitudeSlider() {
|
||||||
|
this.getContainer()?.querySelector("#aircraft-spawn-altitude-slider")?.classList.toggle("hide", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
setAirbaseName(airbaseName: string) {
|
setAirbaseName(airbaseName: string) {
|
||||||
@@ -121,63 +305,100 @@ export class MapContextMenu extends ContextMenu {
|
|||||||
this.#spawnOptions.latlng = latlng;
|
this.#spawnOptions.latlng = latlng;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCoalitionArea(coalitionArea: CoalitionArea) {
|
||||||
|
this.#coalitionArea = coalitionArea;
|
||||||
|
this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", false);
|
||||||
|
}
|
||||||
|
|
||||||
#onSwitchClick(value: boolean) {
|
#onSwitchClick(value: boolean) {
|
||||||
value? setActiveCoalition("red"): setActiveCoalition("blue");
|
value? setActiveCoalition("red"): setActiveCoalition("blue");
|
||||||
|
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
#onSwitchRightClick(e: any) {
|
#onSwitchRightClick(e: any) {
|
||||||
this.#coalitionSwitch.setValue(undefined);
|
this.#coalitionSwitch.setValue(undefined);
|
||||||
setActiveCoalition("neutral");
|
setActiveCoalition("neutral");
|
||||||
|
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#refreshOptions() {
|
||||||
|
if (!aircraftDatabase.getRoles().includes(this.#aircraftRoleDropdown.getValue()))
|
||||||
|
this.#resetAircraftRole();
|
||||||
|
if (!aircraftDatabase.getByRole(this.#aircraftRoleDropdown.getValue()).map((blueprint) => { return blueprint.label }).includes(this.#aircraftLabelDropdown.getValue()))
|
||||||
|
this.#resetAircraftLabel();
|
||||||
|
|
||||||
|
if (!helicopterDatabase.getRoles().includes(this.#helicopterRoleDropdown.getValue()))
|
||||||
|
this.#resetHelicopterRole();
|
||||||
|
if (!helicopterDatabase.getByRole(this.#helicopterRoleDropdown.getValue()).map((blueprint) => { return blueprint.label }).includes(this.#helicopterLabelDropdown.getValue()))
|
||||||
|
this.#resetHelicopterLabel();
|
||||||
|
|
||||||
|
if (!groundUnitDatabase.getRoles().includes(this.#groundUnitTypeDropdown.getValue()))
|
||||||
|
this.#resetGroundUnitType();
|
||||||
|
if (!groundUnitDatabase.getByType(this.#groundUnitTypeDropdown.getValue()).map((blueprint) => { return blueprint.label }).includes(this.#groundUnitLabelDropdown.getValue()))
|
||||||
|
this.#resetGroundUnitLabel();
|
||||||
|
|
||||||
|
if (!navyUnitDatabase.getRoles().includes(this.#navyUnitTypeDropdown.getValue()))
|
||||||
|
this.#resetNavyUnitType();
|
||||||
|
if (!navyUnitDatabase.getByType(this.#navyUnitTypeDropdown.getValue()).map((blueprint) => { return blueprint.label }).includes(this.#aircraftLabelDropdown.getValue()))
|
||||||
|
this.#resetNavyUnitLabel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/********* Aircraft spawn menu *********/
|
/********* Aircraft spawn menu *********/
|
||||||
#setAircraftRole(role: string) {
|
#setAircraftRole(role: string) {
|
||||||
this.#spawnOptions.role = role;
|
this.#spawnOptions.role = role;
|
||||||
this.#resetAircraftType();
|
this.#resetAircraftLabel();
|
||||||
this.#aircraftTypeDropdown.setOptions(aircraftDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
this.#aircraftLabelDropdown.setOptions(aircraftDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
||||||
this.#aircraftTypeDropdown.selectValue(0);
|
this.#aircraftLabelDropdown.selectValue(0);
|
||||||
this.clip();
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
#resetAircraftRole() {
|
#resetAircraftRole() {
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren();
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-loadout-list")).replaceChildren();
|
||||||
this.#aircraftRoleDropdown.reset();
|
this.#aircraftRoleDropdown.reset();
|
||||||
this.#aircraftTypeDropdown.reset();
|
this.#aircraftLabelDropdown.reset();
|
||||||
this.#aircraftRoleDropdown.setOptions(aircraftDatabase.getRoles());
|
this.#aircraftRoleDropdown.setOptions(aircraftDatabase.getRoles());
|
||||||
this.clip();
|
this.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
#setAircraftType(label: string) {
|
#setAircraftLabel(label: string) {
|
||||||
this.#resetAircraftType();
|
this.#resetAircraftLabel();
|
||||||
var type = aircraftDatabase.getByLabel(label)?.name || null;
|
var name = aircraftDatabase.getByLabel(label)?.name || null;
|
||||||
if (type != null) {
|
if (name != null) {
|
||||||
this.#spawnOptions.type = type;
|
this.#spawnOptions.name = name;
|
||||||
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role));
|
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(name, this.#spawnOptions.role));
|
||||||
this.#aircraftLoadoutDropdown.selectValue(0);
|
this.#aircraftLoadoutDropdown.selectValue(0);
|
||||||
var image = (<HTMLImageElement>this.getContainer()?.querySelector("#unit-image"));
|
var image = (<HTMLImageElement>this.getContainer()?.querySelector("#aircraft-unit-image"));
|
||||||
image.src = `images/units/${aircraftDatabase.getByLabel(label)?.filename}`;
|
image.src = `images/units/${aircraftDatabase.getByLabel(label)?.filename}`;
|
||||||
image.classList.toggle("hide", false);
|
image.classList.toggle("hide", false);
|
||||||
}
|
}
|
||||||
this.clip();
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
}
|
}
|
||||||
|
|
||||||
#resetAircraftType() {
|
#resetAircraftLabel() {
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren();
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-loadout-list")).replaceChildren();
|
||||||
this.#aircraftLoadoutDropdown.reset();
|
this.#aircraftLoadoutDropdown.reset();
|
||||||
(<HTMLImageElement>this.getContainer()?.querySelector("#unit-image")).classList.toggle("hide", true);
|
(<HTMLImageElement>this.getContainer()?.querySelector("#aircraft-unit-image")).classList.toggle("hide", true);
|
||||||
this.clip();
|
this.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setAircraftCount(count: string) {
|
||||||
|
this.#spawnOptions.count = parseInt(count);
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
#setAircraftLoadout(loadoutName: string) {
|
#setAircraftLoadout(loadoutName: string) {
|
||||||
var loadout = aircraftDatabase.getLoadoutByName(this.#spawnOptions.type, loadoutName);
|
var loadout = aircraftDatabase.getLoadoutByName(this.#spawnOptions.name, loadoutName);
|
||||||
if (loadout) {
|
if (loadout) {
|
||||||
this.#spawnOptions.loadout = loadout.code;
|
this.#spawnOptions.loadout = loadout.code;
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
||||||
var items = loadout.items.map((item: any) => { return `${item.quantity}x ${item.name}`; });
|
var items = loadout.items.map((item: any) => { return `${item.quantity}x ${item.name}`; });
|
||||||
items.length == 0 ? items.push("Empty loadout") : "";
|
items.length == 0 ? items.push("Empty loadout") : "";
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren(
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-loadout-list")).replaceChildren(
|
||||||
...items.map((item: any) => {
|
...items.map((item: any) => {
|
||||||
var div = document.createElement('div');
|
var div = document.createElement('div');
|
||||||
div.innerText = item;
|
div.innerText = item;
|
||||||
@@ -188,40 +409,179 @@ export class MapContextMenu extends ContextMenu {
|
|||||||
this.clip();
|
this.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
/********* Ground unit spawn menu *********/
|
/********* Helicopter spawn menu *********/
|
||||||
#setGroundUnitRole(role: string) {
|
#setHelicopterRole(role: string) {
|
||||||
this.#spawnOptions.role = role;
|
this.#spawnOptions.role = role;
|
||||||
this.#resetGroundUnitType();
|
this.#resetHelicopterLabel();
|
||||||
|
this.#helicopterLabelDropdown.setOptions(helicopterDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
||||||
|
this.#helicopterLabelDropdown.selectValue(0);
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
const types = groundUnitsDatabase.getByRole(role).map((blueprint) => { return blueprint.label });
|
#resetHelicopterRole() {
|
||||||
this.#groundUnitTypeDropdown.setOptions(types);
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
this.#groundUnitTypeDropdown.selectValue(0);
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-loadout-list")).replaceChildren();
|
||||||
|
this.#helicopterRoleDropdown.reset();
|
||||||
|
this.#helicopterLabelDropdown.reset();
|
||||||
|
this.#helicopterRoleDropdown.setOptions(helicopterDatabase.getRoles());
|
||||||
this.clip();
|
this.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
#resetGroundUnitRole() {
|
#setHelicopterLabel(label: string) {
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
this.#resetHelicopterLabel();
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren();
|
var name = helicopterDatabase.getByLabel(label)?.name || null;
|
||||||
this.#groundUnitRoleDropdown.reset();
|
if (name != null) {
|
||||||
this.#groundUnitTypeDropdown.reset();
|
this.#spawnOptions.name = name;
|
||||||
|
this.#helicopterLoadoutDropdown.setOptions(helicopterDatabase.getLoadoutNamesByRole(name, this.#spawnOptions.role));
|
||||||
|
this.#helicopterLoadoutDropdown.selectValue(0);
|
||||||
|
var image = (<HTMLImageElement>this.getContainer()?.querySelector("#helicopter-unit-image"));
|
||||||
|
image.src = `images/units/${helicopterDatabase.getByLabel(label)?.filename}`;
|
||||||
|
image.classList.toggle("hide", false);
|
||||||
|
}
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
const roles = groundUnitsDatabase.getRoles();
|
#resetHelicopterLabel() {
|
||||||
this.#groundUnitRoleDropdown.setOptions(roles);
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-loadout-list")).replaceChildren();
|
||||||
|
this.#helicopterLoadoutDropdown.reset();
|
||||||
|
(<HTMLImageElement>this.getContainer()?.querySelector("#helicopter-unit-image")).classList.toggle("hide", true);
|
||||||
this.clip();
|
this.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
#setGroundUnitType(label: string) {
|
#setHelicopterCount(count: string) {
|
||||||
this.#resetGroundUnitType();
|
this.#spawnOptions.count = parseInt(count);
|
||||||
var type = groundUnitsDatabase.getByLabel(label)?.name || null;
|
this.clip();
|
||||||
if (type != null) {
|
this.#computeSpawnPoints();
|
||||||
this.#spawnOptions.type = type;
|
}
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
|
||||||
|
#setHelicopterLoadout(loadoutName: string) {
|
||||||
|
var loadout = helicopterDatabase.getLoadoutByName(this.#spawnOptions.name, loadoutName);
|
||||||
|
if (loadout) {
|
||||||
|
this.#spawnOptions.loadout = loadout.code;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
||||||
|
var items = loadout.items.map((item: any) => { return `${item.quantity}x ${item.name}`; });
|
||||||
|
items.length == 0 ? items.push("Empty loadout") : "";
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-loadout-list")).replaceChildren(
|
||||||
|
...items.map((item: any) => {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
div.innerText = item;
|
||||||
|
return div;
|
||||||
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
this.clip();
|
this.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/********* Groundunit spawn menu *********/
|
||||||
|
#setGroundUnitType(role: string) {
|
||||||
|
this.#resetGroundUnitLabel();
|
||||||
|
|
||||||
|
const types = groundUnitDatabase.getByType(role).map((blueprint) => { return blueprint.label });
|
||||||
|
this.#groundUnitLabelDropdown.setOptions(types);
|
||||||
|
this.#groundUnitLabelDropdown.selectValue(0);
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
#resetGroundUnitType() {
|
#resetGroundUnitType() {
|
||||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
|
this.#groundUnitTypeDropdown.reset();
|
||||||
|
this.#groundUnitLabelDropdown.reset();
|
||||||
|
|
||||||
|
const types = groundUnitDatabase.getTypes();
|
||||||
|
this.#groundUnitTypeDropdown.setOptions(types);
|
||||||
this.clip();
|
this.clip();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#setGroundUnitLabel(label: string) {
|
||||||
|
this.#resetGroundUnitLabel();
|
||||||
|
var type = groundUnitDatabase.getByLabel(label)?.name || null;
|
||||||
|
if (type != null) {
|
||||||
|
this.#spawnOptions.name = type;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
||||||
|
}
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
#resetGroundUnitLabel() {
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
|
this.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
#setGroundUnitCount(count: string) {
|
||||||
|
this.#spawnOptions.count = parseInt(count);
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
/********* Navyunit spawn menu *********/
|
||||||
|
#setNavyUnitType(role: string) {
|
||||||
|
this.#resetNavyUnitLabel();
|
||||||
|
|
||||||
|
const types = navyUnitDatabase.getByType(role).map((blueprint) => { return blueprint.label });
|
||||||
|
this.#navyUnitLabelDropdown.setOptions(types);
|
||||||
|
this.#navyUnitLabelDropdown.selectValue(0);
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
#resetNavyUnitType() {
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
|
this.#navyUnitTypeDropdown.reset();
|
||||||
|
this.#navyUnitLabelDropdown.reset();
|
||||||
|
|
||||||
|
const types = navyUnitDatabase.getTypes();
|
||||||
|
this.#navyUnitTypeDropdown.setOptions(types);
|
||||||
|
this.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
#setNavyUnitLabel(label: string) {
|
||||||
|
this.#resetNavyUnitLabel();
|
||||||
|
var type = navyUnitDatabase.getByLabel(label)?.name || null;
|
||||||
|
if (type != null) {
|
||||||
|
this.#spawnOptions.name = type;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
||||||
|
}
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
#resetNavyUnitLabel() {
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||||
|
this.clip();
|
||||||
|
}
|
||||||
|
|
||||||
|
#setNavyUnitCount(count: string) {
|
||||||
|
this.#spawnOptions.count = parseInt(count);
|
||||||
|
this.clip();
|
||||||
|
this.#computeSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
#computeSpawnPoints() {
|
||||||
|
if (getMissionHandler() && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER){
|
||||||
|
var aircraftCount = parseInt(this.#aircraftCountDropdown.getValue());
|
||||||
|
var aircraftSpawnPoints = aircraftCount * aircraftDatabase.getSpawnPointsByLabel(this.#aircraftLabelDropdown.getValue());
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).dataset.points = `${aircraftSpawnPoints}`;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = aircraftSpawnPoints >= getMissionHandler().getAvailableSpawnPoints();
|
||||||
|
|
||||||
|
var helicopterCount = parseInt(this.#helicopterCountDropdown.getValue());
|
||||||
|
var helicopterSpawnPoints = helicopterCount * helicopterDatabase.getSpawnPointsByLabel(this.#helicopterLabelDropdown.getValue());
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).dataset.points = `${helicopterSpawnPoints}`;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = helicopterSpawnPoints >= getMissionHandler().getAvailableSpawnPoints();
|
||||||
|
|
||||||
|
var groundUnitCount = parseInt(this.#groundUnitCountDropdown.getValue());
|
||||||
|
var groundUnitSpawnPoints = groundUnitCount * groundUnitDatabase.getSpawnPointsByLabel(this.#groundUnitLabelDropdown.getValue());
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).dataset.points = `${groundUnitSpawnPoints}`;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = groundUnitSpawnPoints >= getMissionHandler().getAvailableSpawnPoints();
|
||||||
|
|
||||||
|
var navyUnitCount = parseInt(this.#navyUnitCountDropdown.getValue());
|
||||||
|
var navyUnitSpawnPoints = navyUnitCount * navyUnitDatabase.getSpawnPointsByLabel(this.#navyUnitLabelDropdown.getValue());
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).dataset.points = `${navyUnitSpawnPoints}`;
|
||||||
|
(<HTMLButtonElement>this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = navyUnitSpawnPoints >= getMissionHandler().getAvailableSpawnPoints();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,7 @@ import { Map } from "./map/map"
|
|||||||
import { UnitsManager } from "./units/unitsmanager";
|
import { UnitsManager } from "./units/unitsmanager";
|
||||||
import { UnitInfoPanel } from "./panels/unitinfopanel";
|
import { UnitInfoPanel } from "./panels/unitinfopanel";
|
||||||
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
|
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
|
||||||
import { MissionHandler } from "./missionhandler/missionhandler";
|
import { MissionHandler } from "./mission/missionhandler";
|
||||||
import { UnitControlPanel } from "./panels/unitcontrolpanel";
|
import { UnitControlPanel } from "./panels/unitcontrolpanel";
|
||||||
import { MouseInfoPanel } from "./panels/mouseinfopanel";
|
import { MouseInfoPanel } from "./panels/mouseinfopanel";
|
||||||
import { AIC } from "./aic/aic";
|
import { AIC } from "./aic/aic";
|
||||||
@@ -16,6 +16,8 @@ import { Popup } from "./popups/popup";
|
|||||||
import { Dropdown } from "./controls/dropdown";
|
import { Dropdown } from "./controls/dropdown";
|
||||||
import { HotgroupPanel } from "./panels/hotgrouppanel";
|
import { HotgroupPanel } from "./panels/hotgrouppanel";
|
||||||
import { SVGInjector } from "@tanem/svg-injector";
|
import { SVGInjector } from "@tanem/svg-injector";
|
||||||
|
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants";
|
||||||
|
import { ServerStatusPanel } from "./panels/serverstatuspanel";
|
||||||
|
|
||||||
var map: Map;
|
var map: Map;
|
||||||
|
|
||||||
@@ -27,6 +29,7 @@ var atc: ATC;
|
|||||||
|
|
||||||
var unitInfoPanel: UnitInfoPanel;
|
var unitInfoPanel: UnitInfoPanel;
|
||||||
var connectionStatusPanel: ConnectionStatusPanel;
|
var connectionStatusPanel: ConnectionStatusPanel;
|
||||||
|
var serverStatusPanel: ServerStatusPanel;
|
||||||
var unitControlPanel: UnitControlPanel;
|
var unitControlPanel: UnitControlPanel;
|
||||||
var mouseInfoPanel: MouseInfoPanel;
|
var mouseInfoPanel: MouseInfoPanel;
|
||||||
var logPanel: LogPanel;
|
var logPanel: LogPanel;
|
||||||
@@ -44,17 +47,18 @@ function setup() {
|
|||||||
featureSwitches = new FeatureSwitches();
|
featureSwitches = new FeatureSwitches();
|
||||||
|
|
||||||
/* Initialize base functionalitites */
|
/* Initialize base functionalitites */
|
||||||
map = new Map('map-container');
|
|
||||||
unitsManager = new UnitsManager();
|
unitsManager = new UnitsManager();
|
||||||
|
map = new Map('map-container');
|
||||||
missionHandler = new MissionHandler();
|
missionHandler = new MissionHandler();
|
||||||
|
|
||||||
/* Panels */
|
/* Panels */
|
||||||
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
|
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
|
||||||
unitControlPanel = new UnitControlPanel("unit-control-panel");
|
unitControlPanel = new UnitControlPanel("unit-control-panel");
|
||||||
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
|
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
|
||||||
|
serverStatusPanel = new ServerStatusPanel("server-status-panel");
|
||||||
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
|
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
|
||||||
hotgroupPanel = new HotgroupPanel("hotgroup-panel");
|
hotgroupPanel = new HotgroupPanel("hotgroup-panel");
|
||||||
//logPanel = new LogPanel("log-panel");
|
logPanel = new LogPanel("log-panel");
|
||||||
|
|
||||||
/* Popups */
|
/* Popups */
|
||||||
infoPopup = new Popup("info-popup");
|
infoPopup = new Popup("info-popup");
|
||||||
@@ -184,12 +188,12 @@ function setupEvents() {
|
|||||||
const form = document.querySelector("#splash-content")?.querySelector("#authentication-form");
|
const form = document.querySelector("#splash-content")?.querySelector("#authentication-form");
|
||||||
const username = (<HTMLInputElement>(form?.querySelector("#username"))).value;
|
const username = (<HTMLInputElement>(form?.querySelector("#username"))).value;
|
||||||
const password = (<HTMLInputElement>(form?.querySelector("#password"))).value;
|
const password = (<HTMLInputElement>(form?.querySelector("#password"))).value;
|
||||||
setCredentials(username, btoa("admin" + ":" + password));
|
setCredentials(username, password);
|
||||||
|
|
||||||
/* Start periodically requesting updates */
|
/* Start periodically requesting updates */
|
||||||
startUpdate();
|
startUpdate();
|
||||||
|
|
||||||
setConnectionStatus("connecting");
|
setLoginStatus("connecting");
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener("reloadPage", () => {
|
document.addEventListener("reloadPage", () => {
|
||||||
@@ -204,17 +208,12 @@ function setupEvents() {
|
|||||||
else
|
else
|
||||||
img.onload = () => SVGInjector(img);
|
img.onload = () => SVGInjector(img);
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMap() {
|
export function getMap() {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMissionData() {
|
|
||||||
return missionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUnitDataTable() {
|
export function getUnitDataTable() {
|
||||||
return unitDataTable;
|
return unitDataTable;
|
||||||
}
|
}
|
||||||
@@ -223,6 +222,10 @@ export function getUnitsManager() {
|
|||||||
return unitsManager;
|
return unitsManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMissionHandler() {
|
||||||
|
return missionHandler;
|
||||||
|
}
|
||||||
|
|
||||||
export function getUnitInfoPanel() {
|
export function getUnitInfoPanel() {
|
||||||
return unitInfoPanel;
|
return unitInfoPanel;
|
||||||
}
|
}
|
||||||
@@ -243,21 +246,34 @@ export function getConnectionStatusPanel() {
|
|||||||
return connectionStatusPanel;
|
return connectionStatusPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getServerStatusPanel() {
|
||||||
|
return serverStatusPanel;
|
||||||
|
}
|
||||||
|
|
||||||
export function getHotgroupPanel() {
|
export function getHotgroupPanel() {
|
||||||
return hotgroupPanel;
|
return hotgroupPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setActiveCoalition(newActiveCoalition: string) {
|
export function setActiveCoalition(newActiveCoalition: string) {
|
||||||
activeCoalition = newActiveCoalition;
|
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER)
|
||||||
document.querySelectorAll('[data-active-coalition]').forEach((element: any) => { element.setAttribute("data-active-coalition", activeCoalition) });
|
activeCoalition = newActiveCoalition;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getActiveCoalition() {
|
export function getActiveCoalition() {
|
||||||
return activeCoalition;
|
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER)
|
||||||
|
return activeCoalition;
|
||||||
|
else {
|
||||||
|
if (getMissionHandler().getCommandModeOptions().commandMode == BLUE_COMMANDER)
|
||||||
|
return "blue";
|
||||||
|
else if (getMissionHandler().getCommandModeOptions().commandMode == RED_COMMANDER)
|
||||||
|
return "red";
|
||||||
|
else
|
||||||
|
return "neutral";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setConnectionStatus(status: string) {
|
export function setLoginStatus(status: string) {
|
||||||
const el = document.querySelector("#connection-status") as HTMLElement;
|
const el = document.querySelector("#login-status") as HTMLElement;
|
||||||
if (el)
|
if (el)
|
||||||
el.dataset["status"] = status;
|
el.dataset["status"] = status;
|
||||||
}
|
}
|
||||||
@@ -267,3 +283,4 @@ export function getInfoPopup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.onload = setup;
|
window.onload = setup;
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export var BoxSelect = Handler.extend({
|
|||||||
|
|
||||||
_onMouseDown: function (e: any) {
|
_onMouseDown: function (e: any) {
|
||||||
if ((e.which == 1 && e.button == 0 && e.shiftKey)) {
|
if ((e.which == 1 && e.button == 0 && e.shiftKey)) {
|
||||||
|
this._map.fire('selectionstart');
|
||||||
// Clear the deferred resetState if it hasn't executed yet, otherwise it
|
// Clear the deferred resetState if it hasn't executed yet, otherwise it
|
||||||
// will interrupt the interaction and orphan a box element in the container.
|
// will interrupt the interaction and orphan a box element in the container.
|
||||||
this._clearDeferredResetState();
|
this._clearDeferredResetState();
|
||||||
|
|||||||
166
client/src/map/coalitionarea.ts
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
import { DomUtil, LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions } from "leaflet";
|
||||||
|
import { getMap, getMissionHandler, getUnitsManager } from "..";
|
||||||
|
import { CoalitionAreaHandle } from "./coalitionareahandle";
|
||||||
|
import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle";
|
||||||
|
import { BLUE_COMMANDER, RED_COMMANDER } from "../constants/constants";
|
||||||
|
|
||||||
|
export class CoalitionArea extends Polygon {
|
||||||
|
#coalition: string = "blue";
|
||||||
|
#selected: boolean = true;
|
||||||
|
#editing: boolean = true;
|
||||||
|
#handles: CoalitionAreaHandle[] = [];
|
||||||
|
#middleHandles: CoalitionAreaMiddleHandle[] = [];
|
||||||
|
#activeIndex: number = 0;
|
||||||
|
|
||||||
|
constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: PolylineOptions) {
|
||||||
|
if (options === undefined)
|
||||||
|
options = {};
|
||||||
|
|
||||||
|
options.bubblingMouseEvents = false;
|
||||||
|
options.interactive = false;
|
||||||
|
|
||||||
|
super(latlngs, options);
|
||||||
|
this.#setColors();
|
||||||
|
this.#registerCallbacks();
|
||||||
|
|
||||||
|
if ([BLUE_COMMANDER, RED_COMMANDER].includes(getMissionHandler().getCommandModeOptions().commandMode))
|
||||||
|
this.setCoalition(getMissionHandler().getCommandedCoalition());
|
||||||
|
}
|
||||||
|
|
||||||
|
setCoalition(coalition: string) {
|
||||||
|
this.#coalition = coalition;
|
||||||
|
this.#setColors();
|
||||||
|
}
|
||||||
|
|
||||||
|
getCoalition() {
|
||||||
|
return this.#coalition;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelected(selected: boolean) {
|
||||||
|
this.#selected = selected;
|
||||||
|
this.#setColors();
|
||||||
|
this.#setHandles();
|
||||||
|
this.setOpacity(selected? 1: 0.5);
|
||||||
|
if (!this.getSelected() && this.getEditing()) {
|
||||||
|
/* Remove the vertex we were working on */
|
||||||
|
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||||
|
latlngs.splice(this.#activeIndex, 1);
|
||||||
|
this.setLatLngs(latlngs);
|
||||||
|
this.setEditing(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelected() {
|
||||||
|
return this.#selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
setEditing(editing: boolean) {
|
||||||
|
this.#editing = editing;
|
||||||
|
this.#setHandles();
|
||||||
|
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||||
|
|
||||||
|
/* Remove areas with less than 2 vertexes */
|
||||||
|
if (latlngs.length <= 2)
|
||||||
|
getMap().deleteCoalitionArea(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEditing() {
|
||||||
|
return this.#editing;
|
||||||
|
}
|
||||||
|
|
||||||
|
addTemporaryLatLng(latlng: LatLng) {
|
||||||
|
this.#activeIndex++;
|
||||||
|
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||||
|
latlngs.splice(this.#activeIndex, 0, latlng);
|
||||||
|
this.setLatLngs(latlngs);
|
||||||
|
this.#setHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
moveActiveVertex(latlng: LatLng) {
|
||||||
|
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||||
|
latlngs[this.#activeIndex] = latlng;
|
||||||
|
this.setLatLngs(latlngs);
|
||||||
|
this.#setHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
setOpacity(opacity: number) {
|
||||||
|
this.setStyle({opacity: opacity, fillOpacity: opacity * 0.25});
|
||||||
|
}
|
||||||
|
|
||||||
|
onRemove(map: Map): this {
|
||||||
|
super.onRemove(map);
|
||||||
|
this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getMap()));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setColors() {
|
||||||
|
const coalitionColor = this.getCoalition() === "blue" ? "#247be2" : "#ff5858";
|
||||||
|
this.setStyle({ color: this.getSelected() ? "white" : coalitionColor, fillColor: coalitionColor });
|
||||||
|
}
|
||||||
|
|
||||||
|
#setHandles() {
|
||||||
|
this.#handles.forEach((handle: CoalitionAreaHandle) => handle.removeFrom(getMap()));
|
||||||
|
this.#handles = [];
|
||||||
|
if (this.getSelected()) {
|
||||||
|
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||||
|
latlngs.forEach((latlng: LatLng, idx: number) => {
|
||||||
|
/* Add the polygon vertex handle (for moving the vertex) */
|
||||||
|
const handle = new CoalitionAreaHandle(latlng);
|
||||||
|
handle.addTo(getMap());
|
||||||
|
handle.on("drag", (e: any) => {
|
||||||
|
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||||
|
latlngs[idx] = e.target.getLatLng();
|
||||||
|
this.setLatLngs(latlngs);
|
||||||
|
this.#setMiddleHandles();
|
||||||
|
});
|
||||||
|
this.#handles.push(handle);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.#setMiddleHandles();
|
||||||
|
}
|
||||||
|
|
||||||
|
#setMiddleHandles() {
|
||||||
|
this.#middleHandles.forEach((handle: CoalitionAreaMiddleHandle) => handle.removeFrom(getMap()));
|
||||||
|
this.#middleHandles = [];
|
||||||
|
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||||
|
if (this.getSelected() && latlngs.length >= 2) {
|
||||||
|
var lastLatLng: LatLng | null = null;
|
||||||
|
latlngs.concat([latlngs[0]]).forEach((latlng: LatLng, idx: number) => {
|
||||||
|
/* Add the polygon middle point handle (for adding new vertexes) */
|
||||||
|
if (lastLatLng != null) {
|
||||||
|
const handle1Point = getMap().latLngToLayerPoint(latlng);
|
||||||
|
const handle2Point = getMap().latLngToLayerPoint(lastLatLng);
|
||||||
|
const middlePoint = new Point((handle1Point.x + handle2Point.x) / 2, (handle1Point.y + handle2Point.y) / 2);
|
||||||
|
const middleLatLng = getMap().layerPointToLatLng(middlePoint);
|
||||||
|
|
||||||
|
const middleHandle = new CoalitionAreaMiddleHandle(middleLatLng);
|
||||||
|
middleHandle.addTo(getMap());
|
||||||
|
middleHandle.on("click", (e: any) => {
|
||||||
|
this.#activeIndex = idx - 1;
|
||||||
|
this.addTemporaryLatLng(middleLatLng);
|
||||||
|
});
|
||||||
|
this.#middleHandles.push(middleHandle);
|
||||||
|
}
|
||||||
|
lastLatLng = latlng;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#registerCallbacks() {
|
||||||
|
this.on("click", (e: any) => {
|
||||||
|
getMap().deselectAllCoalitionAreas();
|
||||||
|
if (!this.getSelected()) {
|
||||||
|
this.setSelected(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on("contextmenu", (e: any) => {
|
||||||
|
if (!this.getEditing()) {
|
||||||
|
getMap().deselectAllCoalitionAreas();
|
||||||
|
this.setSelected(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
this.setEditing(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
19
client/src/map/coalitionareahandle.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { DivIcon, LatLng } from "leaflet";
|
||||||
|
import { CustomMarker } from "./custommarker";
|
||||||
|
|
||||||
|
export class CoalitionAreaHandle extends CustomMarker {
|
||||||
|
constructor(latlng: LatLng) {
|
||||||
|
super(latlng, {interactive: true, draggable: true});
|
||||||
|
}
|
||||||
|
|
||||||
|
createIcon() {
|
||||||
|
this.setIcon(new DivIcon({
|
||||||
|
iconSize: [24, 24],
|
||||||
|
iconAnchor: [12, 12],
|
||||||
|
className: "leaflet-coalitionarea-handle-marker",
|
||||||
|
}));
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.classList.add("ol-coalitionarea-handle-icon");
|
||||||
|
this.getElement()?.appendChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
19
client/src/map/coalitionareamiddlehandle.ts
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
import { DivIcon, LatLng } from "leaflet";
|
||||||
|
import { CustomMarker } from "./custommarker";
|
||||||
|
|
||||||
|
export class CoalitionAreaMiddleHandle extends CustomMarker {
|
||||||
|
constructor(latlng: LatLng) {
|
||||||
|
super(latlng, {interactive: true, draggable: false});
|
||||||
|
}
|
||||||
|
|
||||||
|
createIcon() {
|
||||||
|
this.setIcon(new DivIcon({
|
||||||
|
iconSize: [16, 16],
|
||||||
|
iconAnchor: [8, 8],
|
||||||
|
className: "leaflet-coalitionarea-middle-handle-marker",
|
||||||
|
}));
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.classList.add("ol-coalitionarea-middle-handle-icon");
|
||||||
|
this.getElement()?.appendChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
import { DivIcon } from "leaflet";
|
import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet";
|
||||||
import { CustomMarker } from "./custommarker";
|
import { CustomMarker } from "./custommarker";
|
||||||
|
|
||||||
export class DestinationPreviewMarker extends CustomMarker {
|
export class DestinationPreviewMarker extends CustomMarker {
|
||||||
|
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||||
|
super(latlng, options);
|
||||||
|
this.setZIndexOffset(9999);
|
||||||
|
}
|
||||||
|
|
||||||
createIcon() {
|
createIcon() {
|
||||||
this.setIcon(new DivIcon({
|
this.setIcon(new DivIcon({
|
||||||
iconSize: [52, 52],
|
iconSize: [52, 52],
|
||||||
|
|||||||
20
client/src/map/drawingcursor.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { DivIcon, LatLng } from "leaflet";
|
||||||
|
import { CustomMarker } from "./custommarker";
|
||||||
|
|
||||||
|
export class DrawingCursor extends CustomMarker {
|
||||||
|
constructor() {
|
||||||
|
super(new LatLng(0, 0), {interactive: false})
|
||||||
|
this.setZIndexOffset(9999);
|
||||||
|
}
|
||||||
|
|
||||||
|
createIcon() {
|
||||||
|
this.setIcon(new DivIcon({
|
||||||
|
iconSize: [24, 24],
|
||||||
|
iconAnchor: [0, 24],
|
||||||
|
className: "leaflet-draw-marker",
|
||||||
|
}));
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.classList.add("ol-draw-icon");
|
||||||
|
this.getElement()?.appendChild(el);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +1,28 @@
|
|||||||
import * as L from "leaflet"
|
import * as L from "leaflet"
|
||||||
import { getUnitsManager } from "..";
|
import { getUnitsManager } from "..";
|
||||||
import { BoxSelect } from "./boxselect";
|
import { BoxSelect } from "./boxselect";
|
||||||
import { MapContextMenu, SpawnOptions } from "../controls/mapcontextmenu";
|
import { MapContextMenu } from "../controls/mapcontextmenu";
|
||||||
import { UnitContextMenu } from "../controls/unitcontextmenu";
|
import { UnitContextMenu } from "../controls/unitcontextmenu";
|
||||||
import { AirbaseContextMenu } from "../controls/airbasecontextmenu";
|
import { AirbaseContextMenu } from "../controls/airbasecontextmenu";
|
||||||
import { Dropdown } from "../controls/dropdown";
|
import { Dropdown } from "../controls/dropdown";
|
||||||
import { Airbase } from "../missionhandler/airbase";
|
import { Airbase } from "../mission/airbase";
|
||||||
import { Unit } from "../units/unit";
|
import { Unit } from "../units/unit";
|
||||||
import { bearing } from "../other/utils";
|
import { bearing, createCheckboxOption } from "../other/utils";
|
||||||
import { DestinationPreviewMarker } from "./destinationpreviewmarker";
|
import { DestinationPreviewMarker } from "./destinationpreviewmarker";
|
||||||
import { TemporaryUnitMarker } from "./temporaryunitmarker";
|
import { TemporaryUnitMarker } from "./temporaryunitmarker";
|
||||||
import { ClickableMiniMap } from "./clickableminimap";
|
import { ClickableMiniMap } from "./clickableminimap";
|
||||||
import { SVGInjector } from '@tanem/svg-injector'
|
import { SVGInjector } from '@tanem/svg-injector'
|
||||||
import { layers as mapLayers, mapBounds, minimapBoundaries } from "../constants/constants";
|
import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, FIRE_AT_AREA, MOVE_UNIT, CARPET_BOMBING, BOMBING, SHOW_CONTACT_LINES, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS } from "../constants/constants";
|
||||||
import { TargetMarker } from "./targetmarker";
|
import { TargetMarker } from "./targetmarker";
|
||||||
|
import { CoalitionArea } from "./coalitionarea";
|
||||||
|
import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu";
|
||||||
|
import { DrawingCursor } from "./drawingcursor";
|
||||||
|
|
||||||
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
||||||
|
|
||||||
// TODO would be nice to convert to ts
|
// TODO would be nice to convert to ts
|
||||||
require("../../public/javascripts/leaflet.nauticscale.js")
|
require("../../public/javascripts/leaflet.nauticscale.js")
|
||||||
|
require("../../public/javascripts/L.Path.Drag.js")
|
||||||
/* Map constants */
|
|
||||||
export const IDLE = "Idle";
|
|
||||||
export const MOVE_UNIT = "Move unit";
|
|
||||||
export const BOMBING = "Bombing";
|
|
||||||
export const CARPET_BOMBING = "Carpet bombing";
|
|
||||||
export const FIRE_AT_AREA = "Fire at area";
|
|
||||||
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"];
|
|
||||||
|
|
||||||
export class Map extends L.Map {
|
export class Map extends L.Map {
|
||||||
#ID: string;
|
#ID: string;
|
||||||
@@ -42,28 +37,38 @@ export class Map extends L.Map {
|
|||||||
#panUp: boolean = false;
|
#panUp: boolean = false;
|
||||||
#panDown: boolean = false;
|
#panDown: boolean = false;
|
||||||
#lastMousePosition: L.Point = new L.Point(0, 0);
|
#lastMousePosition: L.Point = new L.Point(0, 0);
|
||||||
|
#shiftKey: boolean = false;
|
||||||
|
#ctrlKey: boolean = false;
|
||||||
#centerUnit: Unit | null = null;
|
#centerUnit: Unit | null = null;
|
||||||
#miniMap: ClickableMiniMap | null = null;
|
#miniMap: ClickableMiniMap | null = null;
|
||||||
#miniMapLayerGroup: L.LayerGroup;
|
#miniMapLayerGroup: L.LayerGroup;
|
||||||
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
||||||
#destinationPreviewMarkers: DestinationPreviewMarker[] = [];
|
#selecting: boolean = false;
|
||||||
#targetMarker: TargetMarker;
|
#isZooming: boolean = false;
|
||||||
|
|
||||||
#destinationGroupRotation: number = 0;
|
#destinationGroupRotation: number = 0;
|
||||||
#computeDestinationRotation: boolean = false;
|
#computeDestinationRotation: boolean = false;
|
||||||
#destinationRotationCenter: L.LatLng | null = null;
|
#destinationRotationCenter: L.LatLng | null = null;
|
||||||
|
#coalitionAreas: CoalitionArea[] = [];
|
||||||
|
|
||||||
|
#targetCursor: TargetMarker = new TargetMarker(new L.LatLng(0, 0), { interactive: false });
|
||||||
|
#destinationPreviewCursors: DestinationPreviewMarker[] = [];
|
||||||
|
#drawingCursor: DrawingCursor = new DrawingCursor();
|
||||||
|
|
||||||
#mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu");
|
#mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu");
|
||||||
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
|
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
|
||||||
#airbaseContextMenu: AirbaseContextMenu = new AirbaseContextMenu("airbase-contextmenu");
|
#airbaseContextMenu: AirbaseContextMenu = new AirbaseContextMenu("airbase-contextmenu");
|
||||||
|
#coalitionAreaContextMenu: CoalitionAreaContextMenu = new CoalitionAreaContextMenu("coalition-area-contextmenu");
|
||||||
|
|
||||||
#mapSourceDropdown: Dropdown;
|
#mapSourceDropdown: Dropdown;
|
||||||
|
#mapVisibilityOptionsDropdown: Dropdown;
|
||||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||||
|
#visibiityOptions: { [key: string]: boolean } = {}
|
||||||
|
|
||||||
constructor(ID: string) {
|
constructor(ID: string) {
|
||||||
/* Init the leaflet map */
|
/* Init the leaflet map */
|
||||||
|
//@ts-ignore Needed because the boxSelect option is non-standard
|
||||||
//@ts-ignore
|
super(ID, { zoomSnap: 0, zoomDelta: 0.25, preferCanvas: true, doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 });
|
||||||
super(ID, { doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 });
|
|
||||||
this.setView([37.23, -115.8], 10);
|
this.setView([37.23, -115.8], 10);
|
||||||
|
|
||||||
this.#ID = ID;
|
this.#ID = ID;
|
||||||
@@ -81,7 +86,10 @@ export class Map extends L.Map {
|
|||||||
L.control.scalenautic({ position: "topright", maxWidth: 300, nautic: true, metric: true, imperial: false }).addTo(this);
|
L.control.scalenautic({ position: "topright", maxWidth: 300, nautic: true, metric: true, imperial: false }).addTo(this);
|
||||||
|
|
||||||
/* Map source dropdown */
|
/* Map source dropdown */
|
||||||
this.#mapSourceDropdown = new Dropdown("map-type", (layerName: string) => this.setLayer(layerName), this.getLayers())
|
this.#mapSourceDropdown = new Dropdown("map-type", (layerName: string) => this.setLayer(layerName), this.getLayers());
|
||||||
|
|
||||||
|
/* Visibility options dropdown */
|
||||||
|
this.#mapVisibilityOptionsDropdown = new Dropdown("map-visibility-options", () => {});
|
||||||
|
|
||||||
/* Init the state machine */
|
/* Init the state machine */
|
||||||
this.#state = IDLE;
|
this.#state = IDLE;
|
||||||
@@ -89,21 +97,23 @@ export class Map extends L.Map {
|
|||||||
/* Register event handles */
|
/* Register event handles */
|
||||||
this.on("click", (e: any) => this.#onClick(e));
|
this.on("click", (e: any) => this.#onClick(e));
|
||||||
this.on("dblclick", (e: any) => this.#onDoubleClick(e));
|
this.on("dblclick", (e: any) => this.#onDoubleClick(e));
|
||||||
this.on("zoomstart", (e: any) => this.#onZoom(e));
|
this.on("zoomstart", (e: any) => this.#onZoomStart(e));
|
||||||
|
this.on("zoomend", (e: any) => this.#onZoomEnd(e));
|
||||||
this.on("drag", (e: any) => this.centerOnUnit(null));
|
this.on("drag", (e: any) => this.centerOnUnit(null));
|
||||||
this.on("contextmenu", (e: any) => this.#onContextMenu(e));
|
this.on("contextmenu", (e: any) => this.#onContextMenu(e));
|
||||||
|
this.on('selectionstart', (e: any) => this.#onSelectionStart(e));
|
||||||
this.on('selectionend', (e: any) => this.#onSelectionEnd(e));
|
this.on('selectionend', (e: any) => this.#onSelectionEnd(e));
|
||||||
this.on('mousedown', (e: any) => this.#onMouseDown(e));
|
this.on('mousedown', (e: any) => this.#onMouseDown(e));
|
||||||
this.on('mouseup', (e: any) => this.#onMouseUp(e));
|
this.on('mouseup', (e: any) => this.#onMouseUp(e));
|
||||||
this.on('mousemove', (e: any) => this.#onMouseMove(e));
|
this.on('mousemove', (e: any) => this.#onMouseMove(e));
|
||||||
this.on('keydown', (e: any) => this.#updateDestinationPreview(e));
|
this.on('keydown', (e: any) => this.#onKeyDown(e));
|
||||||
this.on('keyup', (e: any) => this.#updateDestinationPreview(e));
|
this.on('keyup', (e: any) => this.#onKeyUp(e));
|
||||||
|
|
||||||
/* Event listeners */
|
/* Event listeners */
|
||||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||||
const el = ev.detail._element;
|
const el = ev.detail._element;
|
||||||
el?.classList.toggle("off");
|
el?.classList.toggle("off");
|
||||||
getUnitsManager().setHiddenType(ev.detail.coalition, (el?.currentTarget as HTMLElement)?.classList.contains("off"));
|
getUnitsManager().setHiddenType(ev.detail.coalition, !el?.classList.contains("off"));
|
||||||
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -114,6 +124,18 @@ export class Map extends L.Map {
|
|||||||
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener("toggleCoalitionAreaDraw", (ev: CustomEventInit) => {
|
||||||
|
this.getMapContextMenu().hide();
|
||||||
|
this.deselectAllCoalitionAreas();
|
||||||
|
if (ev.detail?.type == "polygon") {
|
||||||
|
if (this.getState() !== COALITIONAREA_DRAW_POLYGON)
|
||||||
|
this.setState(COALITIONAREA_DRAW_POLYGON);
|
||||||
|
else
|
||||||
|
this.setState(IDLE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
document.addEventListener("unitUpdated", (ev: CustomEvent) => {
|
document.addEventListener("unitUpdated", (ev: CustomEvent) => {
|
||||||
if (this.#centerUnit != null && ev.detail == this.#centerUnit)
|
if (this.#centerUnit != null && ev.detail == this.#centerUnit)
|
||||||
this.#panToUnit(this.#centerUnit);
|
this.#panToUnit(this.#centerUnit);
|
||||||
@@ -122,7 +144,7 @@ export class Map extends L.Map {
|
|||||||
/* Pan interval */
|
/* Pan interval */
|
||||||
this.#panInterval = window.setInterval(() => {
|
this.#panInterval = window.setInterval(() => {
|
||||||
if (this.#panLeft || this.#panDown || this.#panRight || this.#panLeft)
|
if (this.#panLeft || this.#panDown || this.#panRight || this.#panLeft)
|
||||||
this.panBy(new L.Point(((this.#panLeft? -1 : 0) + (this.#panRight ? 1 : 0)) * this.#deafultPanDelta,
|
this.panBy(new L.Point(((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) * this.#deafultPanDelta,
|
||||||
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) * this.#deafultPanDelta));
|
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) * this.#deafultPanDelta));
|
||||||
}, 20);
|
}, 20);
|
||||||
|
|
||||||
@@ -132,15 +154,24 @@ export class Map extends L.Map {
|
|||||||
});
|
});
|
||||||
document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]);
|
document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]);
|
||||||
|
|
||||||
/* Markers */
|
/* Create the checkboxes to select the advanced visibility options */
|
||||||
this.#targetMarker = new TargetMarker(new L.LatLng(0, 0), {interactive: false});
|
|
||||||
|
this.#visibiityOptions[SHOW_CONTACT_LINES] = false;
|
||||||
|
this.#visibiityOptions[HIDE_GROUP_MEMBERS] = true;
|
||||||
|
this.#visibiityOptions[SHOW_UNIT_PATHS] = true;
|
||||||
|
this.#visibiityOptions[SHOW_UNIT_TARGETS] = true;
|
||||||
|
this.#mapVisibilityOptionsDropdown.setOptionsElements(Object.keys(this.#visibiityOptions).map((option: string) => {
|
||||||
|
return createCheckboxOption(option, option, this.#visibiityOptions[option], (ev: any) => {
|
||||||
|
this.#setVisibilityOption(option, ev);
|
||||||
|
});
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
setLayer(layerName: string) {
|
setLayer(layerName: string) {
|
||||||
if (this.#layer != null)
|
if (this.#layer != null)
|
||||||
this.removeLayer(this.#layer)
|
this.removeLayer(this.#layer)
|
||||||
|
|
||||||
if (layerName in mapLayers){
|
if (layerName in mapLayers) {
|
||||||
const layerData = mapLayers[layerName as keyof typeof mapLayers];
|
const layerData = mapLayers[layerName as keyof typeof mapLayers];
|
||||||
var options: L.TileLayerOptions = {
|
var options: L.TileLayerOptions = {
|
||||||
attribution: layerData.attribution,
|
attribution: layerData.attribution,
|
||||||
@@ -160,21 +191,17 @@ export class Map extends L.Map {
|
|||||||
/* State machine */
|
/* State machine */
|
||||||
setState(state: string) {
|
setState(state: string) {
|
||||||
this.#state = state;
|
this.#state = state;
|
||||||
if (this.#state === IDLE) {
|
this.#updateCursor();
|
||||||
this.#resetDestinationMarkers();
|
|
||||||
this.#resetTargetMarker();
|
/* Operations to perform if you are NOT in a state */
|
||||||
this.#showCursor();
|
if (this.#state !== COALITIONAREA_DRAW_POLYGON) {
|
||||||
|
this.#deselectCoalitionAreas();
|
||||||
}
|
}
|
||||||
else if (this.#state === MOVE_UNIT) {
|
|
||||||
this.#resetTargetMarker();
|
/* Operations to perform if you ARE in a state */
|
||||||
this.#createDestinationMarkers();
|
if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||||
if (this.#destinationPreviewMarkers.length > 0)
|
this.#coalitionAreas.push(new CoalitionArea([]));
|
||||||
this.#hideCursor();
|
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
||||||
}
|
|
||||||
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
|
|
||||||
this.#resetDestinationMarkers();
|
|
||||||
this.#createTargetMarker();
|
|
||||||
this.#hideCursor();
|
|
||||||
}
|
}
|
||||||
document.dispatchEvent(new CustomEvent("mapStateChanged"));
|
document.dispatchEvent(new CustomEvent("mapStateChanged"));
|
||||||
}
|
}
|
||||||
@@ -183,18 +210,28 @@ export class Map extends L.Map {
|
|||||||
return this.#state;
|
return this.#state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deselectAllCoalitionAreas() {
|
||||||
|
this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => coalitionArea.setSelected(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCoalitionArea(coalitionArea: CoalitionArea) {
|
||||||
|
if (this.#coalitionAreas.includes(coalitionArea))
|
||||||
|
this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1);
|
||||||
|
if (this.hasLayer(coalitionArea))
|
||||||
|
this.removeLayer(coalitionArea);
|
||||||
|
}
|
||||||
|
|
||||||
/* Context Menus */
|
/* Context Menus */
|
||||||
hideAllContextMenus() {
|
hideAllContextMenus() {
|
||||||
this.hideMapContextMenu();
|
this.hideMapContextMenu();
|
||||||
this.hideUnitContextMenu();
|
this.hideUnitContextMenu();
|
||||||
this.hideAirbaseContextMenu();
|
this.hideAirbaseContextMenu();
|
||||||
|
this.hideCoalitionAreaContextMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
showMapContextMenu(e: any) {
|
showMapContextMenu(x: number, y: number, latlng: L.LatLng) {
|
||||||
this.hideAllContextMenus();
|
this.hideAllContextMenus();
|
||||||
var x = e.originalEvent.x;
|
this.#mapContextMenu.show(x, y, latlng);
|
||||||
var y = e.originalEvent.y;
|
|
||||||
this.#mapContextMenu.show(x, y, e.latlng);
|
|
||||||
document.dispatchEvent(new CustomEvent("mapContextMenu"));
|
document.dispatchEvent(new CustomEvent("mapContextMenu"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,11 +244,9 @@ export class Map extends L.Map {
|
|||||||
return this.#mapContextMenu;
|
return this.#mapContextMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
showUnitContextMenu(e: any) {
|
showUnitContextMenu(x: number, y: number, latlng: L.LatLng) {
|
||||||
this.hideAllContextMenus();
|
this.hideAllContextMenus();
|
||||||
var x = e.originalEvent.x;
|
this.#unitContextMenu.show(x, y, latlng);
|
||||||
var y = e.originalEvent.y;
|
|
||||||
this.#unitContextMenu.show(x, y, e.latlng);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getUnitContextMenu() {
|
getUnitContextMenu() {
|
||||||
@@ -222,11 +257,9 @@ export class Map extends L.Map {
|
|||||||
this.#unitContextMenu.hide();
|
this.#unitContextMenu.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
showAirbaseContextMenu(e: any, airbase: Airbase) {
|
showAirbaseContextMenu(x: number, y: number, latlng: L.LatLng, airbase: Airbase) {
|
||||||
this.hideAllContextMenus();
|
this.hideAllContextMenus();
|
||||||
var x = e.originalEvent.x;
|
this.#airbaseContextMenu.show(x, y, latlng);
|
||||||
var y = e.originalEvent.y;
|
|
||||||
this.#airbaseContextMenu.show(x, y, e.latlng);
|
|
||||||
this.#airbaseContextMenu.setAirbase(airbase);
|
this.#airbaseContextMenu.setAirbase(airbase);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,6 +271,24 @@ export class Map extends L.Map {
|
|||||||
this.#airbaseContextMenu.hide();
|
this.#airbaseContextMenu.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showCoalitionAreaContextMenu(x: number, y: number, latlng: L.LatLng, coalitionArea: CoalitionArea) {
|
||||||
|
this.hideAllContextMenus();
|
||||||
|
this.#coalitionAreaContextMenu.show(x, y, latlng);
|
||||||
|
this.#coalitionAreaContextMenu.setCoalitionArea(coalitionArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCoalitionAreaContextMenu() {
|
||||||
|
return this.#coalitionAreaContextMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
hideCoalitionAreaContextMenu() {
|
||||||
|
this.#coalitionAreaContextMenu.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
isZooming() {
|
||||||
|
return this.#isZooming;
|
||||||
|
}
|
||||||
|
|
||||||
/* Mouse coordinates */
|
/* Mouse coordinates */
|
||||||
getMousePosition() {
|
getMousePosition() {
|
||||||
return this.#lastMousePosition;
|
return this.#lastMousePosition;
|
||||||
@@ -263,6 +314,10 @@ export class Map extends L.Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getCenterUnit() {
|
||||||
|
return this.#centerUnit;
|
||||||
|
}
|
||||||
|
|
||||||
setTheatre(theatre: string) {
|
setTheatre(theatre: string) {
|
||||||
var bounds = new L.LatLngBounds([-90, -180], [90, 180]);
|
var bounds = new L.LatLngBounds([-90, -180], [90, 180]);
|
||||||
var miniMapZoom = 5;
|
var miniMapZoom = 5;
|
||||||
@@ -333,36 +388,59 @@ export class Map extends L.Map {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addTemporaryMarker(latlng: L.LatLng) {
|
addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string) {
|
||||||
var marker = new TemporaryUnitMarker(latlng);
|
var marker = new TemporaryUnitMarker(latlng, name, coalition);
|
||||||
marker.addTo(this);
|
marker.addTo(this);
|
||||||
this.#temporaryMarkers.push(marker);
|
this.#temporaryMarkers.push(marker);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTemporaryMarker(latlng: L.LatLng) {
|
removeTemporaryMarker(latlng: L.LatLng) {
|
||||||
var d: number | null = null;
|
// TODO something more refined than this
|
||||||
|
var dist: number | null = null;
|
||||||
var closest: L.Marker | null = null;
|
var closest: L.Marker | null = null;
|
||||||
var i: number = 0;
|
var i: number = 0;
|
||||||
this.#temporaryMarkers.forEach((marker: L.Marker, idx: number) => {
|
this.#temporaryMarkers.forEach((marker: L.Marker, idx: number) => {
|
||||||
var t = latlng.distanceTo(marker.getLatLng());
|
var t = latlng.distanceTo(marker.getLatLng());
|
||||||
if (d == null || t < d) {
|
if (dist == null || t < dist) {
|
||||||
d = t;
|
dist = t;
|
||||||
closest = marker;
|
closest = marker;
|
||||||
i = idx;
|
i = idx;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (closest) {
|
if (closest && dist != null && dist < 100) {
|
||||||
this.removeLayer(closest);
|
this.removeLayer(closest);
|
||||||
delete this.#temporaryMarkers[i];
|
this.#temporaryMarkers.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getSelectedCoalitionArea() {
|
||||||
|
return this.#coalitionAreas.find((area: CoalitionArea) => { return area.getSelected() });
|
||||||
|
}
|
||||||
|
|
||||||
|
bringCoalitionAreaToBack(coalitionArea: CoalitionArea) {
|
||||||
|
coalitionArea.bringToBack();
|
||||||
|
this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1);
|
||||||
|
this.#coalitionAreas.unshift(coalitionArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
getVisibilityOptions() {
|
||||||
|
return this.#visibiityOptions;
|
||||||
|
}
|
||||||
|
|
||||||
/* Event handlers */
|
/* Event handlers */
|
||||||
#onClick(e: any) {
|
#onClick(e: any) {
|
||||||
if (!this.#preventLeftClick) {
|
if (!this.#preventLeftClick) {
|
||||||
this.hideAllContextMenus();
|
this.hideAllContextMenus();
|
||||||
if (this.#state === IDLE) {
|
if (this.#state === IDLE) {
|
||||||
|
this.deselectAllCoalitionAreas();
|
||||||
|
}
|
||||||
|
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||||
|
if (this.getSelectedCoalitionArea()?.getEditing()) {
|
||||||
|
this.getSelectedCoalitionArea()?.addTemporaryLatLng(e.latlng);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.deselectAllCoalitionAreas();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.setState(IDLE);
|
this.setState(IDLE);
|
||||||
@@ -372,57 +450,68 @@ export class Map extends L.Map {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#onDoubleClick(e: any) {
|
#onDoubleClick(e: any) {
|
||||||
|
this.deselectAllCoalitionAreas();
|
||||||
}
|
}
|
||||||
|
|
||||||
#onContextMenu(e: any) {
|
#onContextMenu(e: any) {
|
||||||
this.hideMapContextMenu();
|
this.hideMapContextMenu();
|
||||||
if (this.#state === IDLE) {
|
if (this.#state === IDLE) {
|
||||||
if (this.#state == IDLE) {
|
if (this.#state == IDLE) {
|
||||||
this.showMapContextMenu(e);
|
this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||||
|
var clickedCoalitionArea = null;
|
||||||
|
/* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */
|
||||||
|
for (let coalitionArea of this.#coalitionAreas) {
|
||||||
|
if (coalitionArea.getBounds().contains(e.latlng)) {
|
||||||
|
if (coalitionArea.getSelected())
|
||||||
|
clickedCoalitionArea = coalitionArea;
|
||||||
|
else
|
||||||
|
this.getMapContextMenu().setCoalitionArea(coalitionArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clickedCoalitionArea)
|
||||||
|
this.showCoalitionAreaContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, clickedCoalitionArea);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (this.#state === MOVE_UNIT) {
|
else if (this.#state === MOVE_UNIT) {
|
||||||
if (!e.originalEvent.ctrlKey) {
|
if (!e.originalEvent.ctrlKey) {
|
||||||
getUnitsManager().selectedUnitsClearDestinations();
|
getUnitsManager().selectedUnitsClearDestinations();
|
||||||
}
|
}
|
||||||
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, e.originalEvent.shiftKey, this.#destinationGroupRotation)
|
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
||||||
this.#destinationGroupRotation = 0;
|
this.#destinationGroupRotation = 0;
|
||||||
this.#destinationRotationCenter = null;
|
this.#destinationRotationCenter = null;
|
||||||
this.#computeDestinationRotation = false;
|
this.#computeDestinationRotation = false;
|
||||||
}
|
}
|
||||||
else if (this.#state === BOMBING) {
|
else if (this.#state === BOMBING) {
|
||||||
getUnitsManager().getSelectedUnits().length > 0? this.setState(MOVE_UNIT): this.setState(IDLE);
|
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
||||||
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
|
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
|
||||||
}
|
}
|
||||||
else if (this.#state === CARPET_BOMBING) {
|
else if (this.#state === CARPET_BOMBING) {
|
||||||
getUnitsManager().getSelectedUnits().length > 0? this.setState(MOVE_UNIT): this.setState(IDLE);
|
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
||||||
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
|
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
|
||||||
}
|
}
|
||||||
else if (this.#state === FIRE_AT_AREA) {
|
else if (this.#state === FIRE_AT_AREA) {
|
||||||
getUnitsManager().getSelectedUnits().length > 0? this.setState(MOVE_UNIT): this.setState(IDLE);
|
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
||||||
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
|
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
this.setState(IDLE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#executeAction(e: any, action: string) {
|
#onSelectionStart(e: any) {
|
||||||
if (action === "bomb")
|
this.#selecting = true;
|
||||||
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
|
this.#updateCursor();
|
||||||
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) {
|
#onSelectionEnd(e: any) {
|
||||||
|
this.#selecting = false;
|
||||||
clearTimeout(this.#leftClickTimer);
|
clearTimeout(this.#leftClickTimer);
|
||||||
this.#preventLeftClick = true;
|
this.#preventLeftClick = true;
|
||||||
this.#leftClickTimer = window.setTimeout(() => {
|
this.#leftClickTimer = window.setTimeout(() => {
|
||||||
this.#preventLeftClick = false;
|
this.#preventLeftClick = false;
|
||||||
}, 200);
|
}, 200);
|
||||||
getUnitsManager().selectFromBounds(e.selectionBounds);
|
getUnitsManager().selectFromBounds(e.selectionBounds);
|
||||||
|
this.#updateCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
#onMouseDown(e: any) {
|
#onMouseDown(e: any) {
|
||||||
@@ -446,23 +535,51 @@ export class Map extends L.Map {
|
|||||||
this.#lastMousePosition.x = e.originalEvent.x;
|
this.#lastMousePosition.x = e.originalEvent.x;
|
||||||
this.#lastMousePosition.y = e.originalEvent.y;
|
this.#lastMousePosition.y = e.originalEvent.y;
|
||||||
|
|
||||||
if (this.#state === MOVE_UNIT){
|
this.#updateCursor();
|
||||||
|
|
||||||
|
if (this.#state === MOVE_UNIT) {
|
||||||
|
/* Update the position of the destination cursors depeding on mouse rotation */
|
||||||
if (this.#computeDestinationRotation && this.#destinationRotationCenter != null)
|
if (this.#computeDestinationRotation && this.#destinationRotationCenter != null)
|
||||||
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
|
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
|
||||||
this.#updateDestinationPreview(e);
|
this.#updateDestinationCursors();
|
||||||
}
|
}
|
||||||
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
|
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
|
||||||
this.#targetMarker.setLatLng(this.getMouseCoordinates());
|
this.#targetCursor.setLatLng(this.getMouseCoordinates());
|
||||||
|
}
|
||||||
|
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||||
|
this.#drawingCursor.setLatLng(e.latlng);
|
||||||
|
/* Update the polygon being drawn with the current position of the mouse cursor */
|
||||||
|
this.getSelectedCoalitionArea()?.moveActiveVertex(e.latlng);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#onZoom(e: any) {
|
#onKeyDown(e: any) {
|
||||||
if (this.#centerUnit != null)
|
this.#shiftKey = e.originalEvent.shiftKey;
|
||||||
this.#panToUnit(this.#centerUnit);
|
this.#ctrlKey = e.originalEvent.ctrlKey;
|
||||||
|
this.#updateCursor();
|
||||||
|
this.#updateDestinationCursors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#onKeyUp(e: any) {
|
||||||
|
this.#shiftKey = e.originalEvent.shiftKey;
|
||||||
|
this.#ctrlKey = e.originalEvent.ctrlKey;
|
||||||
|
this.#updateCursor();
|
||||||
|
this.#updateDestinationCursors();
|
||||||
|
}
|
||||||
|
|
||||||
|
#onZoomStart(e: any) {
|
||||||
|
if (this.#centerUnit != null)
|
||||||
|
this.#panToUnit(this.#centerUnit);
|
||||||
|
this.#isZooming = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#onZoomEnd(e: any) {
|
||||||
|
this.#isZooming = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#panToUnit(unit: Unit) {
|
#panToUnit(unit: Unit) {
|
||||||
var unitPosition = new L.LatLng(unit.getFlightData().latitude, unit.getFlightData().longitude);
|
var unitPosition = new L.LatLng(unit.getPosition().lat, unit.getPosition().lng);
|
||||||
this.setView(unitPosition, this.getZoom(), { animate: false });
|
this.setView(unitPosition, this.getZoom(), { animate: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,13 +588,6 @@ export class Map extends L.Map {
|
|||||||
return minimapBoundaries;
|
return minimapBoundaries;
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateDestinationPreview(e: any) {
|
|
||||||
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates(), this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
|
||||||
if (idx < this.#destinationPreviewMarkers.length)
|
|
||||||
this.#destinationPreviewMarkers[idx].setLatLng(e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#createOptionButton(value: string, url: string, title: string, callback: string, argument: string) {
|
#createOptionButton(value: string, url: string, title: string, callback: string, argument: string) {
|
||||||
var button = document.createElement("button");
|
var button = document.createElement("button");
|
||||||
const img = document.createElement("img");
|
const img = document.createElement("img");
|
||||||
@@ -491,47 +601,120 @@ export class Map extends L.Map {
|
|||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
#createDestinationMarkers() {
|
#deselectCoalitionAreas() {
|
||||||
this.#resetDestinationMarkers();
|
this.getSelectedCoalitionArea()?.setSelected(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (getUnitsManager().getSelectedUnits({ excludeHumans: true }).length > 0) {
|
/* Cursors */
|
||||||
/* Create the unit destination preview markers */
|
#showDefaultCursor() {
|
||||||
this.#destinationPreviewMarkers = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).map((unit: Unit) => {
|
document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
|
||||||
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), {interactive: false});
|
}
|
||||||
|
|
||||||
|
#hideDefaultCursor() {
|
||||||
|
document.getElementById(this.#ID)?.classList.add("hidden-cursor");
|
||||||
|
}
|
||||||
|
|
||||||
|
#showDestinationCursors() {
|
||||||
|
const singleCursor = !this.#shiftKey;
|
||||||
|
const selectedUnitsCount = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length;
|
||||||
|
if (selectedUnitsCount > 0) {
|
||||||
|
if (singleCursor && this.#destinationPreviewCursors.length != 1) {
|
||||||
|
this.#hideDestinationCursors();
|
||||||
|
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
||||||
marker.addTo(this);
|
marker.addTo(this);
|
||||||
return marker;
|
this.#destinationPreviewCursors = [marker];
|
||||||
})
|
}
|
||||||
|
else if (!singleCursor) {
|
||||||
|
while (this.#destinationPreviewCursors.length > selectedUnitsCount) {
|
||||||
|
this.removeLayer(this.#destinationPreviewCursors[0]);
|
||||||
|
this.#destinationPreviewCursors.splice(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this.#destinationPreviewCursors.length < selectedUnitsCount) {
|
||||||
|
var cursor = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
||||||
|
cursor.addTo(this);
|
||||||
|
this.#destinationPreviewCursors.push(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#updateDestinationCursors();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#resetDestinationMarkers() {
|
#updateDestinationCursors() {
|
||||||
/* Remove all the destination preview markers */
|
const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates();
|
||||||
this.#destinationPreviewMarkers.forEach((marker: L.Marker) => {
|
if (this.#destinationPreviewCursors.length == 1)
|
||||||
|
this.#destinationPreviewCursors[0].setLatLng(this.getMouseCoordinates());
|
||||||
|
else {
|
||||||
|
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
||||||
|
if (idx < this.#destinationPreviewCursors.length)
|
||||||
|
this.#destinationPreviewCursors[idx].setLatLng(this.#shiftKey? latlng : this.getMouseCoordinates());
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#hideDestinationCursors() {
|
||||||
|
/* Remove all the destination cursors */
|
||||||
|
this.#destinationPreviewCursors.forEach((marker: L.Marker) => {
|
||||||
this.removeLayer(marker);
|
this.removeLayer(marker);
|
||||||
})
|
})
|
||||||
this.#destinationPreviewMarkers = [];
|
this.#destinationPreviewCursors = [];
|
||||||
|
|
||||||
|
/* Reset the variables used to compute the rotation of the group cursors */
|
||||||
this.#destinationGroupRotation = 0;
|
this.#destinationGroupRotation = 0;
|
||||||
this.#computeDestinationRotation = false;
|
this.#computeDestinationRotation = false;
|
||||||
this.#destinationRotationCenter = null;
|
this.#destinationRotationCenter = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
#createTargetMarker(){
|
#showTargetCursor() {
|
||||||
this.#resetTargetMarker();
|
this.#hideTargetCursor();
|
||||||
this.#targetMarker.addTo(this);
|
this.#targetCursor.addTo(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#resetTargetMarker() {
|
#hideTargetCursor() {
|
||||||
this.#targetMarker.setLatLng(new L.LatLng(0, 0));
|
this.#targetCursor.setLatLng(new L.LatLng(0, 0));
|
||||||
this.removeLayer(this.#targetMarker);
|
this.removeLayer(this.#targetCursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
#showCursor() {
|
#showDrawingCursor() {
|
||||||
document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
|
this.#hideDefaultCursor();
|
||||||
|
if (!this.hasLayer(this.#drawingCursor))
|
||||||
|
this.#drawingCursor.addTo(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#hideCursor() {
|
#hideDrawingCursor() {
|
||||||
document.getElementById(this.#ID)?.classList.add("hidden-cursor");
|
this.#drawingCursor.setLatLng(new L.LatLng(0, 0));
|
||||||
|
if (this.hasLayer(this.#drawingCursor))
|
||||||
|
this.#drawingCursor.removeFrom(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateCursor() {
|
||||||
|
/* If the ctrl key is being pressed or we are performing an area selection, show the default cursor */
|
||||||
|
if (this.#ctrlKey || this.#selecting) {
|
||||||
|
/* Hide all non default cursors */
|
||||||
|
this.#hideDestinationCursors();
|
||||||
|
this.#hideTargetCursor();
|
||||||
|
this.#hideDrawingCursor();
|
||||||
|
|
||||||
|
this.#showDefaultCursor();
|
||||||
|
} else {
|
||||||
|
/* Hide all the unnecessary cursors depending on the active state */
|
||||||
|
if (this.#state !== IDLE) this.#hideDefaultCursor();
|
||||||
|
if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors();
|
||||||
|
if (![BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#hideTargetCursor();
|
||||||
|
if (this.#state !== COALITIONAREA_DRAW_POLYGON) this.#hideDrawingCursor();
|
||||||
|
|
||||||
|
/* Show the active cursor depending on the active state */
|
||||||
|
if (this.#state === IDLE) this.#showDefaultCursor();
|
||||||
|
else if (this.#state === MOVE_UNIT) this.#showDestinationCursors();
|
||||||
|
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor();
|
||||||
|
else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#setVisibilityOption(option: string, ev: any) {
|
||||||
|
this.#visibiityOptions[option] = ev.currentTarget.checked;
|
||||||
|
document.dispatchEvent(new CustomEvent("mapVisibilityOptionsChanged"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
import { DivIcon } from "leaflet";
|
import { DivIcon, LatLngExpression, MarkerOptions } from "leaflet";
|
||||||
import { CustomMarker } from "./custommarker";
|
import { CustomMarker } from "./custommarker";
|
||||||
|
|
||||||
export class TargetMarker extends CustomMarker {
|
export class TargetMarker extends CustomMarker {
|
||||||
#interactive: boolean = false;
|
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||||
|
super(latlng, options);
|
||||||
|
this.setZIndexOffset(9999);
|
||||||
|
}
|
||||||
|
|
||||||
createIcon() {
|
createIcon() {
|
||||||
this.setIcon(new DivIcon({
|
this.setIcon(new DivIcon({
|
||||||
@@ -12,7 +15,6 @@ export class TargetMarker extends CustomMarker {
|
|||||||
}));
|
}));
|
||||||
var el = document.createElement("div");
|
var el = document.createElement("div");
|
||||||
el.classList.add("ol-target-icon");
|
el.classList.add("ol-target-icon");
|
||||||
el.classList.toggle("ol-target-icon-interactive", this.#interactive)
|
|
||||||
this.getElement()?.appendChild(el);
|
this.getElement()?.appendChild(el);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,53 @@
|
|||||||
import { Icon } from "leaflet";
|
|
||||||
import { CustomMarker } from "./custommarker";
|
import { CustomMarker } from "./custommarker";
|
||||||
|
import { DivIcon, LatLng } from "leaflet";
|
||||||
|
import { SVGInjector } from "@tanem/svg-injector";
|
||||||
|
import { getMarkerCategoryByName, getUnitDatabaseByCategory } from "../other/utils";
|
||||||
|
|
||||||
export class TemporaryUnitMarker extends CustomMarker {
|
export class TemporaryUnitMarker extends CustomMarker {
|
||||||
|
#name: string;
|
||||||
|
#coalition: string;
|
||||||
|
|
||||||
|
constructor(latlng: LatLng, name: string, coalition: string) {
|
||||||
|
super(latlng, {interactive: false});
|
||||||
|
this.#name = name;
|
||||||
|
this.#coalition = coalition;
|
||||||
|
}
|
||||||
|
|
||||||
createIcon() {
|
createIcon() {
|
||||||
var icon = new Icon({
|
const category = getMarkerCategoryByName(this.#name);
|
||||||
iconUrl: '/resources/theme/images/markers/temporary-icon.png',
|
|
||||||
iconSize: [52, 52],
|
/* Set the icon */
|
||||||
iconAnchor: [26, 26]
|
var icon = new DivIcon({
|
||||||
|
className: 'leaflet-unit-icon',
|
||||||
|
iconAnchor: [25, 25],
|
||||||
|
iconSize: [50, 50],
|
||||||
});
|
});
|
||||||
this.setIcon(icon);
|
this.setIcon(icon);
|
||||||
|
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.classList.add("unit");
|
||||||
|
el.setAttribute("data-object", `unit-${category}`);
|
||||||
|
el.setAttribute("data-coalition", this.#coalition);
|
||||||
|
|
||||||
|
// Main icon
|
||||||
|
var unitIcon = document.createElement("div");
|
||||||
|
unitIcon.classList.add("unit-icon");
|
||||||
|
var img = document.createElement("img");
|
||||||
|
img.src = `/resources/theme/images/units/${category}.svg`;
|
||||||
|
img.onload = () => SVGInjector(img);
|
||||||
|
unitIcon.appendChild(img);
|
||||||
|
unitIcon.toggleAttribute("data-rotate-to-heading", false);
|
||||||
|
el.append(unitIcon);
|
||||||
|
|
||||||
|
// Short label
|
||||||
|
if (category == "aircraft" || category == "helicopter") {
|
||||||
|
var shortLabel = document.createElement("div");
|
||||||
|
shortLabel.classList.add("unit-short-label");
|
||||||
|
shortLabel.innerText = getUnitDatabaseByCategory(category)?.getByName(this.#name)?.shortLabel || "";
|
||||||
|
el.append(shortLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getElement()?.appendChild(el);
|
||||||
|
this.getElement()?.classList.add("ol-temporary-marker");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
214
client/src/mission/missionhandler.ts
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
import { LatLng } from "leaflet";
|
||||||
|
import { getInfoPopup, getMap, getUnitsManager } from "..";
|
||||||
|
import { Airbase } from "./airbase";
|
||||||
|
import { Bullseye } from "./bullseye";
|
||||||
|
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "../constants/constants";
|
||||||
|
import { setCommandModeOptions } from "../server/server";
|
||||||
|
import { Dropdown } from "../controls/dropdown";
|
||||||
|
import { groundUnitDatabase } from "../units/groundunitdatabase";
|
||||||
|
import { createCheckboxOption, getCheckboxOptions } from "../other/utils";
|
||||||
|
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||||
|
import { helicopterDatabase } from "../units/helicopterdatabase";
|
||||||
|
import { navyUnitDatabase } from "../units/navyunitdatabase";
|
||||||
|
|
||||||
|
export class MissionHandler {
|
||||||
|
#bullseyes: { [name: string]: Bullseye } = {};
|
||||||
|
#airbases: { [name: string]: Airbase } = {};
|
||||||
|
#theatre: string = "";
|
||||||
|
#dateAndTime: DateAndTime = {date: {Year: 0, Month: 0, Day: 0}, time: {h: 0, m: 0, s: 0}, startTime: 0, elapsedTime: 0};
|
||||||
|
#commandModeOptions: CommandModeOptions = {commandMode: "Hide all", restrictSpawns: false, restrictToCoalition: false, setupTime: Infinity, spawnPoints: {red: Infinity, blue: Infinity}, eras: []};
|
||||||
|
#remainingSetupTime: number = 0;
|
||||||
|
#spentSpawnPoint: number = 0;
|
||||||
|
#commandModeDialog: HTMLElement;
|
||||||
|
#commandModeErasDropdown: Dropdown;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
document.addEventListener("showCommandModeDialog", () => this.showCommandModeDialog());
|
||||||
|
document.addEventListener("applycommandModeOptions", () => this.#applycommandModeOptions());
|
||||||
|
|
||||||
|
/* command-mode settings dialog */
|
||||||
|
this.#commandModeDialog = <HTMLElement> document.querySelector("#command-mode-settings-dialog");
|
||||||
|
|
||||||
|
this.#commandModeErasDropdown = new Dropdown("command-mode-era-options", () => {});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateBullseyes(data: BullseyesData) {
|
||||||
|
for (let idx in data.bullseyes) {
|
||||||
|
const bullseye = data.bullseyes[idx];
|
||||||
|
if (!(idx in this.#bullseyes))
|
||||||
|
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getMap());
|
||||||
|
|
||||||
|
if (bullseye.latitude && bullseye.longitude && bullseye.coalition) {
|
||||||
|
this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
|
||||||
|
this.#bullseyes[idx].setCoalition(bullseye.coalition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateAirbases(data: AirbasesData) {
|
||||||
|
for (let idx in data.airbases) {
|
||||||
|
var airbase = data.airbases[idx]
|
||||||
|
if (this.#airbases[idx] === undefined && airbase.callsign != '') {
|
||||||
|
this.#airbases[idx] = new Airbase({
|
||||||
|
position: new LatLng(airbase.latitude, airbase.longitude),
|
||||||
|
name: airbase.callsign
|
||||||
|
}).addTo(getMap());
|
||||||
|
this.#airbases[idx].on('contextmenu', (e) => this.#onAirbaseClick(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#airbases[idx] != undefined && airbase.latitude && airbase.longitude && airbase.coalition) {
|
||||||
|
this.#airbases[idx].setLatLng(new LatLng(airbase.latitude, airbase.longitude));
|
||||||
|
this.#airbases[idx].setCoalition(airbase.coalition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateMission(data: MissionData) {
|
||||||
|
if (data.mission) {
|
||||||
|
/* Set the mission theatre */
|
||||||
|
if (data.mission.theatre != this.#theatre) {
|
||||||
|
this.#theatre = data.mission.theatre;
|
||||||
|
getMap().setTheatre(this.#theatre);
|
||||||
|
getInfoPopup().setText("Map set to " + this.#theatre);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the date and time data */
|
||||||
|
this.#dateAndTime = data.mission.dateAndTime;
|
||||||
|
|
||||||
|
/* Set the command mode options */
|
||||||
|
this.#setcommandModeOptions(data.mission.commandModeOptions);
|
||||||
|
this.#remainingSetupTime = this.getCommandModeOptions().setupTime - this.getDateAndTime().elapsedTime;
|
||||||
|
var commandModePhaseEl = document.querySelector("#command-mode-phase") as HTMLElement;
|
||||||
|
if (commandModePhaseEl) {
|
||||||
|
if (this.#remainingSetupTime > 0) {
|
||||||
|
var remainingTime = `-${new Date(this.#remainingSetupTime * 1000).toISOString().substring(14, 19)}`;
|
||||||
|
commandModePhaseEl.dataset.remainingTime = remainingTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
commandModePhaseEl.classList.toggle("setup-phase", this.#remainingSetupTime > 0 && this.getCommandModeOptions().restrictSpawns);
|
||||||
|
commandModePhaseEl.classList.toggle("game-commenced", this.#remainingSetupTime <= 0 || !this.getCommandModeOptions().restrictSpawns);
|
||||||
|
commandModePhaseEl.classList.toggle("no-restrictions", !this.getCommandModeOptions().restrictSpawns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getBullseyes() {
|
||||||
|
return this.#bullseyes;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAirbases() {
|
||||||
|
return this.#airbases;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommandModeOptions() {
|
||||||
|
return this.#commandModeOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDateAndTime() {
|
||||||
|
return this.#dateAndTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
getRemainingSetupTime() {
|
||||||
|
return this.#remainingSetupTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAvailableSpawnPoints() {
|
||||||
|
if (this.getCommandModeOptions().commandMode === GAME_MASTER)
|
||||||
|
return Infinity;
|
||||||
|
else if (this.getCommandModeOptions().commandMode === BLUE_COMMANDER)
|
||||||
|
return this.getCommandModeOptions().spawnPoints.blue - this.#spentSpawnPoint;
|
||||||
|
else if (this.getCommandModeOptions().commandMode === RED_COMMANDER)
|
||||||
|
return this.getCommandModeOptions().spawnPoints.red - this.#spentSpawnPoint;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCommandedCoalition() {
|
||||||
|
if (this.getCommandModeOptions().commandMode === BLUE_COMMANDER)
|
||||||
|
return "blue";
|
||||||
|
else if (this.getCommandModeOptions().commandMode === RED_COMMANDER)
|
||||||
|
return "red";
|
||||||
|
else
|
||||||
|
return "all";
|
||||||
|
}
|
||||||
|
|
||||||
|
refreshSpawnPoints() {
|
||||||
|
var spawnPointsEl = document.querySelector("#spawn-points");
|
||||||
|
if (spawnPointsEl) {
|
||||||
|
spawnPointsEl.textContent = `${this.getAvailableSpawnPoints()}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSpentSpawnPoints(spawnPoints: number) {
|
||||||
|
this.#spentSpawnPoint = spawnPoints;
|
||||||
|
this.refreshSpawnPoints();
|
||||||
|
}
|
||||||
|
|
||||||
|
showCommandModeDialog() {
|
||||||
|
/* Create the checkboxes to select the unit eras */
|
||||||
|
var eras = aircraftDatabase.getEras().concat(helicopterDatabase.getEras()).concat(groundUnitDatabase.getEras()).concat(navyUnitDatabase.getEras());
|
||||||
|
eras = eras.filter((item: string, index: number) => eras.indexOf(item) === index).sort();
|
||||||
|
this.#commandModeErasDropdown.setOptionsElements(eras.map((era: string) => {
|
||||||
|
return createCheckboxOption(era, `Enable ${era} units spawns`, this.getCommandModeOptions().eras.includes(era));
|
||||||
|
}));
|
||||||
|
|
||||||
|
this.#commandModeDialog.classList.remove("hide");
|
||||||
|
|
||||||
|
const restrictSpawnsCheckbox = this.#commandModeDialog.querySelector("#restrict-spawns")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const restrictToCoalitionCheckbox = this.#commandModeDialog.querySelector("#restrict-to-coalition")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const blueSpawnPointsInput = this.#commandModeDialog.querySelector("#blue-spawn-points")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const redSpawnPointsInput = this.#commandModeDialog.querySelector("#red-spawn-points")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const setupTimeInput = this.#commandModeDialog.querySelector("#setup-time")?.querySelector("input") as HTMLInputElement;
|
||||||
|
|
||||||
|
restrictSpawnsCheckbox.checked = this.getCommandModeOptions().restrictSpawns;
|
||||||
|
restrictToCoalitionCheckbox.checked = this.getCommandModeOptions().restrictToCoalition;
|
||||||
|
blueSpawnPointsInput.value = String(this.getCommandModeOptions().spawnPoints.blue);
|
||||||
|
redSpawnPointsInput.value = String(this.getCommandModeOptions().spawnPoints.red);
|
||||||
|
setupTimeInput.value = String(Math.floor(this.getCommandModeOptions().setupTime / 60.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#applycommandModeOptions() {
|
||||||
|
this.#commandModeDialog.classList.add("hide");
|
||||||
|
|
||||||
|
const restrictSpawnsCheckbox = this.#commandModeDialog.querySelector("#restrict-spawns")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const restrictToCoalitionCheckbox = this.#commandModeDialog.querySelector("#restrict-to-coalition")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const blueSpawnPointsInput = this.#commandModeDialog.querySelector("#blue-spawn-points")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const redSpawnPointsInput = this.#commandModeDialog.querySelector("#red-spawn-points")?.querySelector("input") as HTMLInputElement;
|
||||||
|
const setupTimeInput = this.#commandModeDialog.querySelector("#setup-time")?.querySelector("input") as HTMLInputElement;
|
||||||
|
|
||||||
|
var eras: string[] = [];
|
||||||
|
const enabledEras = getCheckboxOptions(this.#commandModeErasDropdown);
|
||||||
|
Object.keys(enabledEras).forEach((key: string) => {if (enabledEras[key]) eras.push(key)});
|
||||||
|
setCommandModeOptions(restrictSpawnsCheckbox.checked, restrictToCoalitionCheckbox.checked, {blue: parseInt(blueSpawnPointsInput.value), red: parseInt(redSpawnPointsInput.value)}, eras, parseInt(setupTimeInput.value) * 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
#setcommandModeOptions(commandModeOptions: CommandModeOptions) {
|
||||||
|
var commandModeOptionsChanged = (!commandModeOptions.eras.every((value: string, idx: number) => {return value === this.getCommandModeOptions().eras[idx]}) ||
|
||||||
|
commandModeOptions.spawnPoints.red !== this.getCommandModeOptions().spawnPoints.red ||
|
||||||
|
commandModeOptions.spawnPoints.blue !== this.getCommandModeOptions().spawnPoints.blue ||
|
||||||
|
commandModeOptions.restrictSpawns !== this.getCommandModeOptions().restrictSpawns ||
|
||||||
|
commandModeOptions.restrictToCoalition !== this.getCommandModeOptions().restrictToCoalition);
|
||||||
|
|
||||||
|
this.#commandModeOptions = commandModeOptions;
|
||||||
|
this.setSpentSpawnPoints(0);
|
||||||
|
this.refreshSpawnPoints();
|
||||||
|
|
||||||
|
if (commandModeOptionsChanged) {
|
||||||
|
document.dispatchEvent(new CustomEvent("commandModeOptionsChanged", { detail: this }));
|
||||||
|
|
||||||
|
document.getElementById("command-mode-toolbar")?.classList.remove("hide");
|
||||||
|
const el = document.getElementById("command-mode");
|
||||||
|
if (el) {
|
||||||
|
el.dataset.mode = commandModeOptions.commandMode;
|
||||||
|
el.textContent = commandModeOptions.commandMode.toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector("#spawn-points-container")?.classList.toggle("hide", this.getCommandModeOptions().commandMode === GAME_MASTER || !this.getCommandModeOptions().restrictSpawns);
|
||||||
|
document.querySelector("#command-mode-settings-button")?.classList.toggle("hide", this.getCommandModeOptions().commandMode !== GAME_MASTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
#onAirbaseClick(e: any) {
|
||||||
|
getMap().showAirbaseContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, e.sourceTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
import { LatLng } from "leaflet";
|
|
||||||
import { getInfoPopup, getMap } from "..";
|
|
||||||
import { Airbase } from "./airbase";
|
|
||||||
import { Bullseye } from "./bullseye";
|
|
||||||
|
|
||||||
export class MissionHandler
|
|
||||||
{
|
|
||||||
#bullseyes : {[name: string]: Bullseye} = {};
|
|
||||||
#airbases : {[name: string]: Airbase} = {};
|
|
||||||
#theatre : string = "";
|
|
||||||
|
|
||||||
#airbaseData : { [name: string]: object } = {};
|
|
||||||
|
|
||||||
// Time
|
|
||||||
#date : any;
|
|
||||||
#elapsedTime : any;
|
|
||||||
#startTime : any;
|
|
||||||
#time : any;
|
|
||||||
|
|
||||||
#updateTime : any;
|
|
||||||
|
|
||||||
constructor()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
update(data: BullseyesData | AirbasesData | any)
|
|
||||||
{
|
|
||||||
if ("bullseyes" in data)
|
|
||||||
{
|
|
||||||
for (let idx in data.bullseyes)
|
|
||||||
{
|
|
||||||
const bullseye = data.bullseyes[idx];
|
|
||||||
if (!(idx in this.#bullseyes))
|
|
||||||
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getMap());
|
|
||||||
|
|
||||||
if (bullseye.latitude && bullseye.longitude && bullseye.coalition)
|
|
||||||
{
|
|
||||||
this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
|
|
||||||
this.#bullseyes[idx].setCoalition(bullseye.coalition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ("mission" in data)
|
|
||||||
{
|
|
||||||
if (data.mission != null && data.mission.theatre != this.#theatre)
|
|
||||||
{
|
|
||||||
this.#theatre = data.mission.theatre;
|
|
||||||
getMap().setTheatre(this.#theatre);
|
|
||||||
|
|
||||||
getInfoPopup().setText("Map set to " + this.#theatre);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ("airbases" in data)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
console.log( Object.values( data.airbases ).sort( ( a:any, b:any ) => {
|
|
||||||
const aVal = a.callsign.toLowerCase();
|
|
||||||
const bVal = b.callsign.toLowerCase();
|
|
||||||
|
|
||||||
return aVal > bVal ? 1 : -1;
|
|
||||||
}) );
|
|
||||||
//*/
|
|
||||||
for (let idx in data.airbases)
|
|
||||||
{
|
|
||||||
var airbase = data.airbases[idx]
|
|
||||||
if (this.#airbases[idx] === undefined && airbase.callsign != '')
|
|
||||||
{
|
|
||||||
this.#airbases[idx] = new Airbase({
|
|
||||||
position: new LatLng(airbase.latitude, airbase.longitude),
|
|
||||||
name: airbase.callsign
|
|
||||||
}).addTo(getMap());
|
|
||||||
this.#airbases[idx].on('contextmenu', (e) => this.#onAirbaseClick(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#airbases[idx] != undefined && airbase.latitude && airbase.longitude && airbase.coalition)
|
|
||||||
{
|
|
||||||
this.#airbases[idx].setLatLng(new LatLng(airbase.latitude, airbase.longitude));
|
|
||||||
this.#airbases[idx].setCoalition(airbase.coalition);
|
|
||||||
}
|
|
||||||
//this.#airbases[idx].setProperties(["Runway 1: 31L / 13R", "Runway 2: 31R / 13L", "TCN: 17X", "ILS: ---" ]);
|
|
||||||
//this.#airbases[idx].setParkings(["2x big", "5x small"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ("mission" in data && data.mission != null)
|
|
||||||
{
|
|
||||||
if (data.mission != null && data.mission.theatre != this.#theatre)
|
|
||||||
{
|
|
||||||
this.#theatre = data.mission.theatre;
|
|
||||||
getMap().setTheatre(this.#theatre);
|
|
||||||
|
|
||||||
getInfoPopup().setText("Map set to " + this.#theatre);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( "date" in data.mission ) {
|
|
||||||
this.#date = data.mission.date;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( "elapsedTime" in data.mission ) {
|
|
||||||
this.#elapsedTime = data.mission.elapsedTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( "startTime" in data.mission ) {
|
|
||||||
this.#startTime = data.mission.startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( "time" in data.mission ) {
|
|
||||||
this.#time = data.mission.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ( "time" in data ) {
|
|
||||||
this.#updateTime = data.time;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
getBullseyes()
|
|
||||||
{
|
|
||||||
return this.#bullseyes;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDate() {
|
|
||||||
return this.#date;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getNowDate() {
|
|
||||||
|
|
||||||
const date = this.getDate();
|
|
||||||
const time = this.getTime();
|
|
||||||
|
|
||||||
if ( !date ) {
|
|
||||||
return new Date();
|
|
||||||
}
|
|
||||||
|
|
||||||
let year = date.Year;
|
|
||||||
let month = date.Month - 1;
|
|
||||||
|
|
||||||
if ( month < 0 ) {
|
|
||||||
month = 11;
|
|
||||||
year--;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Date( year, month, date.Day, time.h, time.m, time.s );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getTime() {
|
|
||||||
return this.#time;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getUpdateTime() {
|
|
||||||
return this.#updateTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
#onAirbaseClick(e: any)
|
|
||||||
{
|
|
||||||
getMap().showAirbaseContextMenu(e, e.sourceTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,13 @@
|
|||||||
|
import { LatLng, Point, Polygon } from "leaflet";
|
||||||
|
import * as turf from "@turf/turf";
|
||||||
|
import { UnitDatabase } from "../units/unitdatabase";
|
||||||
|
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||||
|
import { helicopterDatabase } from "../units/helicopterdatabase";
|
||||||
|
import { groundUnitDatabase } from "../units/groundunitdatabase";
|
||||||
|
import { Buffer } from "buffer";
|
||||||
|
import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants";
|
||||||
|
import { Dropdown } from "../controls/dropdown";
|
||||||
|
|
||||||
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {
|
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {
|
||||||
const φ1 = deg2rad(lat1); // φ, λ in radians
|
const φ1 = deg2rad(lat1); // φ, λ in radians
|
||||||
const φ2 = deg2rad(lat2);
|
const φ2 = deg2rad(lat2);
|
||||||
@@ -26,6 +36,16 @@ export function distance(lat1: number, lon1: number, lat2: number, lon2: number)
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function bearingAndDistanceToLatLng(lat: number, lon: number, brng: number, dist: number) {
|
||||||
|
const R = 6371e3; // metres
|
||||||
|
const φ1 = deg2rad(lat); // φ, λ in radians
|
||||||
|
const λ1 = deg2rad(lon);
|
||||||
|
const φ2 = Math.asin( Math.sin(φ1)*Math.cos(dist/R) + Math.cos(φ1)*Math.sin(dist/R)*Math.cos(brng) );
|
||||||
|
const λ2 = λ1 + Math.atan2(Math.sin(brng)*Math.sin(dist/R)*Math.cos(φ1), Math.cos(dist/R)-Math.sin(φ1)*Math.sin(φ2));
|
||||||
|
|
||||||
|
return new LatLng(rad2deg(φ2), rad2deg(λ2));
|
||||||
|
}
|
||||||
|
|
||||||
export function ConvertDDToDMS(D: number, lng: boolean) {
|
export function ConvertDDToDMS(D: number, lng: boolean) {
|
||||||
var dir = D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N";
|
var dir = D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N";
|
||||||
var deg = 0 | (D < 0 ? (D = -D) : D);
|
var deg = 0 | (D < 0 ? (D = -D) : D);
|
||||||
@@ -185,3 +205,193 @@ export function mToNm(m: number) {
|
|||||||
export function nmToFt(nm: number) {
|
export function nmToFt(nm: number) {
|
||||||
return nm * 6076.12;
|
return nm * 6076.12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function polyContains(latlng: LatLng, polygon: Polygon) {
|
||||||
|
var poly = polygon.toGeoJSON();
|
||||||
|
return turf.inside(turf.point([latlng.lng, latlng.lat]), poly);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function randomPointInPoly(polygon: Polygon): LatLng {
|
||||||
|
var bounds = polygon.getBounds();
|
||||||
|
var x_min = bounds.getEast();
|
||||||
|
var x_max = bounds.getWest();
|
||||||
|
var y_min = bounds.getSouth();
|
||||||
|
var y_max = bounds.getNorth();
|
||||||
|
|
||||||
|
var lat = y_min + (Math.random() * (y_max - y_min));
|
||||||
|
var lng = x_min + (Math.random() * (x_max - x_min));
|
||||||
|
|
||||||
|
var poly = polygon.toGeoJSON();
|
||||||
|
var inside = turf.inside(turf.point([lng, lat]), poly);
|
||||||
|
|
||||||
|
if (inside) {
|
||||||
|
return new LatLng(lat, lng);
|
||||||
|
} else {
|
||||||
|
return randomPointInPoly(polygon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function polygonArea(polygon: Polygon) {
|
||||||
|
var poly = polygon.toGeoJSON();
|
||||||
|
return turf.area(poly);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function randomUnitBlueprint(unitDatabase: UnitDatabase, options: {type?: string, role?: string, ranges?: string[], eras?: string[]} ) {
|
||||||
|
/* Start from all the unit blueprints in the database */
|
||||||
|
var unitBlueprints = Object.values(unitDatabase.getBlueprints());
|
||||||
|
|
||||||
|
/* If a specific type or role is provided, use only the blueprints of that type or role */
|
||||||
|
if (options.type && options.role) {
|
||||||
|
console.error("Can't create random unit if both type and role are provided. Either create by type or by role.")
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.type) {
|
||||||
|
unitBlueprints = unitDatabase.getByType(options.type);
|
||||||
|
}
|
||||||
|
else if (options.role) {
|
||||||
|
unitBlueprints = unitDatabase.getByType(options.role);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep only the units that have a range included in the requested values */
|
||||||
|
if (options.ranges) {
|
||||||
|
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
|
||||||
|
//@ts-ignore
|
||||||
|
return unitBlueprint.range? options.ranges.includes(unitBlueprint.range): true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Keep only the units that have an era included in the requested values */
|
||||||
|
if (options.eras) {
|
||||||
|
unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => {
|
||||||
|
//@ts-ignore
|
||||||
|
return unitBlueprint.era? options.eras.includes(unitBlueprint.era): true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = Math.floor(Math.random() * unitBlueprints.length);
|
||||||
|
return unitBlueprints[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMarkerCategoryByName(name: string) {
|
||||||
|
if (aircraftDatabase.getByName(name) != null)
|
||||||
|
return "aircraft";
|
||||||
|
else if (helicopterDatabase.getByName(name) != null)
|
||||||
|
return "helicopter";
|
||||||
|
else if (groundUnitDatabase.getByName(name) != null){
|
||||||
|
var type = groundUnitDatabase.getByName(name)?.type;
|
||||||
|
if (type === "SAM")
|
||||||
|
return "groundunit-sam";
|
||||||
|
else if (type === "SAM Search radar" || type === "SAM Track radar" || type === "SAM Search/Track radar")
|
||||||
|
return "groundunit-sam-radar";
|
||||||
|
else if (type === "SAM Launcher")
|
||||||
|
return "groundunit-sam-launcher";
|
||||||
|
else if (type === "Radar")
|
||||||
|
return "groundunit-ewr";
|
||||||
|
else
|
||||||
|
return "groundunit-other";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return "groundunit-other"; // TODO add other unit types
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUnitDatabaseByCategory(category: string) {
|
||||||
|
if (category == "aircraft")
|
||||||
|
return aircraftDatabase;
|
||||||
|
else if (category == "helicopter")
|
||||||
|
return helicopterDatabase;
|
||||||
|
else if (category.includes("groundunit"))
|
||||||
|
return groundUnitDatabase;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function base64ToBytes(base64: string) {
|
||||||
|
return Buffer.from(base64, 'base64').buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enumToState(state: number) {
|
||||||
|
if (state < states.length)
|
||||||
|
return states[state];
|
||||||
|
else
|
||||||
|
return states[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enumToROE(ROE: number) {
|
||||||
|
if (ROE < ROEs.length)
|
||||||
|
return ROEs[ROE];
|
||||||
|
else
|
||||||
|
return ROEs[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enumToReactionToThreat(reactionToThreat: number) {
|
||||||
|
if (reactionToThreat < reactionsToThreat.length)
|
||||||
|
return reactionsToThreat[reactionToThreat];
|
||||||
|
else
|
||||||
|
return reactionsToThreat[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enumToEmissioNCountermeasure(emissionCountermeasure: number) {
|
||||||
|
if (emissionCountermeasure < emissionsCountermeasures.length)
|
||||||
|
return emissionsCountermeasures[emissionCountermeasure];
|
||||||
|
else
|
||||||
|
return emissionsCountermeasures[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enumToCoalition(coalitionID: number) {
|
||||||
|
switch (coalitionID){
|
||||||
|
case 0: return "neutral";
|
||||||
|
case 1: return "red";
|
||||||
|
case 2: return "blue";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function convertDateAndTimeToDate(dateAndTime: DateAndTime) {
|
||||||
|
const date = dateAndTime.date;
|
||||||
|
const time = dateAndTime.time;
|
||||||
|
|
||||||
|
if (!date) {
|
||||||
|
return new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
let year = date.Year;
|
||||||
|
let month = date.Month - 1;
|
||||||
|
|
||||||
|
if (month < 0) {
|
||||||
|
month = 11;
|
||||||
|
year--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Date(year, month, date.Day, time.h, time.m, time.s);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCheckboxOption(value: string, text: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}) {
|
||||||
|
var div = document.createElement("div");
|
||||||
|
div.classList.add("ol-checkbox");
|
||||||
|
var label = document.createElement("label");
|
||||||
|
label.title = text;
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.type = "checkbox";
|
||||||
|
input.checked = checked;
|
||||||
|
var span = document.createElement("span");
|
||||||
|
span.innerText = value;
|
||||||
|
label.appendChild(input);
|
||||||
|
label.appendChild(span);
|
||||||
|
div.appendChild(label);
|
||||||
|
input.onclick = (ev: any) => callback(ev);
|
||||||
|
return div as HTMLElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCheckboxOptions(dropdown: Dropdown) {
|
||||||
|
var values: { [key: string]: boolean } = {};
|
||||||
|
const element = dropdown.getOptionElements();
|
||||||
|
for (let idx = 0; idx < element.length; idx++) {
|
||||||
|
const option = element.item(idx) as HTMLElement;
|
||||||
|
const key = option.querySelector("span")?.innerText;
|
||||||
|
const value = option.querySelector("input")?.checked;
|
||||||
|
if (key !== undefined && value !== undefined)
|
||||||
|
values[key] = value;
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
@@ -1,28 +1,71 @@
|
|||||||
import { Panel } from "./panel";
|
import { Panel } from "./panel";
|
||||||
|
|
||||||
export class LogPanel extends Panel
|
export class LogPanel extends Panel {
|
||||||
{
|
#open: boolean = false;
|
||||||
#logs: String[];
|
#queuedMessages: number = 0;
|
||||||
|
#scrolledDown: boolean = true;
|
||||||
|
#logs: {[key: string]: string} = {};
|
||||||
|
|
||||||
constructor(ID: string)
|
constructor(ID: string) {
|
||||||
{
|
|
||||||
super(ID);
|
super(ID);
|
||||||
this.#logs = [];
|
|
||||||
|
document.addEventListener("toggleLogPanel", () => {
|
||||||
|
this.getElement().classList.toggle("open");
|
||||||
|
this.#open = !this.#open;
|
||||||
|
this.#queuedMessages = 0;
|
||||||
|
this.#updateHeader();
|
||||||
|
|
||||||
|
if (this.#scrolledDown)
|
||||||
|
this.#scrollDown();
|
||||||
|
});
|
||||||
|
|
||||||
|
const scrollEl = this.getElement().querySelector(".ol-scrollable");
|
||||||
|
if (scrollEl) {
|
||||||
|
scrollEl.addEventListener("scroll", () => {
|
||||||
|
this.#scrolledDown = Math.abs(scrollEl.scrollHeight - scrollEl.scrollTop - scrollEl.clientHeight) < 1
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update(data: any)
|
appendLogs(logs: {[key: string]: string}) {
|
||||||
{
|
Object.keys(logs).forEach((key: string) => {
|
||||||
var logs = data["logs"];
|
if (!(key in this.#logs)) {
|
||||||
for (let idx in logs)
|
this.#logs[key] = logs[key];
|
||||||
{
|
this.appendLog(logs[key]);
|
||||||
if (parseInt(idx) >= this.#logs.length) {
|
|
||||||
this.#logs.push(logs[idx]);
|
|
||||||
var el = document.createElement("div");
|
|
||||||
el.innerText = logs[idx];
|
|
||||||
el.classList.add("js-log-element", "ol-log-element");
|
|
||||||
this.getElement().appendChild(el);
|
|
||||||
this.getElement().scrollTop = this.getElement().scrollHeight;
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
appendLog(log: string) {
|
||||||
|
var el = document.createElement("div");
|
||||||
|
el.classList.add("ol-log-entry");
|
||||||
|
el.textContent = log;
|
||||||
|
this.getElement().querySelector(".ol-scrollable")?.appendChild(el);
|
||||||
|
console.log(log);
|
||||||
|
|
||||||
|
if (!this.#open)
|
||||||
|
this.#queuedMessages++;
|
||||||
|
|
||||||
|
this.#updateHeader();
|
||||||
|
|
||||||
|
if (this.#scrolledDown)
|
||||||
|
this.#scrollDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
#updateHeader() {
|
||||||
|
const headerEl = this.getElement().querySelector("#log-panel-header") as HTMLElement;
|
||||||
|
if (headerEl) {
|
||||||
|
if (this.#queuedMessages)
|
||||||
|
headerEl.innerText = `Server log (${this.#queuedMessages})`;
|
||||||
|
else
|
||||||
|
headerEl.innerText = `Server log`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#scrollDown() {
|
||||||
|
const scrollEl = this.getElement().querySelector(".ol-scrollable");
|
||||||
|
if (scrollEl) {
|
||||||
|
scrollEl.scrollTop = scrollEl.scrollHeight - scrollEl.clientHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Icon, LatLng, Marker, Polyline } from "leaflet";
|
import { Icon, LatLng, Marker, Polyline } from "leaflet";
|
||||||
import { getMap, getMissionData, getUnitsManager } from "..";
|
import { getMap, getMissionHandler, getUnitsManager } from "..";
|
||||||
import { distance, bearing, zeroAppend, mToNm, nmToFt } from "../other/utils";
|
import { distance, bearing, zeroAppend, mToNm, nmToFt } from "../other/utils";
|
||||||
import { Unit } from "../units/unit";
|
import { Unit } from "../units/unit";
|
||||||
import { Panel } from "./panel";
|
import { Panel } from "./panel";
|
||||||
@@ -36,7 +36,7 @@ export class MouseInfoPanel extends Panel {
|
|||||||
var selectedUnitPosition = null;
|
var selectedUnitPosition = null;
|
||||||
var selectedUnits = getUnitsManager().getSelectedUnits();
|
var selectedUnits = getUnitsManager().getSelectedUnits();
|
||||||
if (selectedUnits && selectedUnits.length == 1)
|
if (selectedUnits && selectedUnits.length == 1)
|
||||||
selectedUnitPosition = new LatLng(selectedUnits[0].getFlightData().latitude, selectedUnits[0].getFlightData().longitude);
|
selectedUnitPosition = new LatLng(selectedUnits[0].getPosition().lat, selectedUnits[0].getPosition().lng);
|
||||||
|
|
||||||
/* Draw measures from selected unit, from pin location, and from bullseyes */
|
/* Draw measures from selected unit, from pin location, and from bullseyes */
|
||||||
this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition);
|
this.#drawMeasure("ref-measure-position", "measure-position", this.#measurePoint, mousePosition);
|
||||||
@@ -44,7 +44,7 @@ export class MouseInfoPanel extends Panel {
|
|||||||
|
|
||||||
this.getElement().querySelector(`#measuring-tool`)?.classList.toggle("hide", this.#measurePoint === null && selectedUnitPosition === null);
|
this.getElement().querySelector(`#measuring-tool`)?.classList.toggle("hide", this.#measurePoint === null && selectedUnitPosition === null);
|
||||||
|
|
||||||
var bullseyes = getMissionData().getBullseyes();
|
var bullseyes = getMissionHandler().getBullseyes();
|
||||||
for (let idx in bullseyes)
|
for (let idx in bullseyes)
|
||||||
this.#drawMeasure(null, `bullseye-${idx}`, bullseyes[idx].getLatLng(), mousePosition);
|
this.#drawMeasure(null, `bullseye-${idx}`, bullseyes[idx].getLatLng(), mousePosition);
|
||||||
|
|
||||||
|
|||||||
26
client/src/panels/serverstatuspanel.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { Panel } from "./panel";
|
||||||
|
|
||||||
|
export class ServerStatusPanel extends Panel {
|
||||||
|
constructor(ID: string) {
|
||||||
|
super(ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
update(frameRate: number, load: number) {
|
||||||
|
const frameRateEl = this.getElement().querySelector("#server-frame-rate");
|
||||||
|
if (frameRateEl) {
|
||||||
|
frameRateEl.textContent = `${frameRate}`;
|
||||||
|
frameRateEl.classList.toggle("fps-high", frameRate >= 60)
|
||||||
|
frameRateEl.classList.toggle("fps-medium", frameRate >= 30 && frameRate < 60)
|
||||||
|
frameRateEl.classList.toggle("fps-low", frameRate <= 30)
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadEl = this.getElement().querySelector("#server-load");
|
||||||
|
if (loadEl) {
|
||||||
|
loadEl.textContent = `${load}`;
|
||||||
|
loadEl.classList.toggle("load-high", load >= 1000)
|
||||||
|
loadEl.classList.toggle("load-medium", load >= 100 && load < 1000)
|
||||||
|
loadEl.classList.toggle("load-low", load <= 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import { Panel } from "./panel";
|
|||||||
import { Switch } from "../controls/switch";
|
import { Switch } from "../controls/switch";
|
||||||
import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, speedIncrements } from "../constants/constants";
|
import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, speedIncrements } from "../constants/constants";
|
||||||
import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils";
|
import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils";
|
||||||
|
import { GeneralSettings, Radio, TACAN } from "../@types/unit";
|
||||||
|
|
||||||
export class UnitControlPanel extends Panel {
|
export class UnitControlPanel extends Panel {
|
||||||
#altitudeSlider: Slider;
|
#altitudeSlider: Slider;
|
||||||
@@ -33,8 +34,9 @@ export class UnitControlPanel extends Panel {
|
|||||||
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetSpeedType(value? "GS": "CAS"); });
|
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetSpeedType(value? "GS": "CAS"); });
|
||||||
|
|
||||||
/* Option buttons */
|
/* Option buttons */
|
||||||
this.#optionButtons["ROE"] = ROEs.map((option: string, index: number) => {
|
// Reversing the ROEs so that the least "aggressive" option is always on the left
|
||||||
return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions[index], () => { getUnitsManager().selectedUnitsSetROE(option); });
|
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); });
|
||||||
});
|
});
|
||||||
|
|
||||||
this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string, index: number) => {
|
this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string, index: number) => {
|
||||||
@@ -98,13 +100,13 @@ export class UnitControlPanel extends Panel {
|
|||||||
if (units.length < 20) {
|
if (units.length < 20) {
|
||||||
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => {
|
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => {
|
||||||
var button = document.createElement("button");
|
var button = document.createElement("button");
|
||||||
var callsign = unit.getBaseData().unitName || "";
|
var callsign = unit.getUnitName() || "";
|
||||||
var label = unit.getDatabase()?.getByName(unit.getBaseData().name)?.label || unit.getBaseData().name;
|
var label = unit.getDatabase()?.getByName(unit.getName())?.label || unit.getName();
|
||||||
|
|
||||||
button.setAttribute("data-label", label);
|
button.setAttribute("data-label", label);
|
||||||
button.setAttribute("data-callsign", callsign);
|
button.setAttribute("data-callsign", callsign);
|
||||||
|
|
||||||
button.setAttribute("data-coalition", unit.getMissionData().coalition);
|
button.setAttribute("data-coalition", unit.getCoalition());
|
||||||
button.classList.add("pill", "highlight-coalition")
|
button.classList.add("pill", "highlight-coalition")
|
||||||
|
|
||||||
button.addEventListener("click", () => {
|
button.addEventListener("click", () => {
|
||||||
@@ -139,12 +141,12 @@ export class UnitControlPanel extends Panel {
|
|||||||
element.toggleAttribute("data-show-advanced-settings-button", units.length == 1);
|
element.toggleAttribute("data-show-advanced-settings-button", units.length == 1);
|
||||||
|
|
||||||
/* Flight controls */
|
/* Flight controls */
|
||||||
var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitude});
|
var desiredAltitude = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()});
|
||||||
var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitudeType});
|
var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitudeType()});
|
||||||
var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeed});
|
var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeed()});
|
||||||
var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeedType});
|
var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeedType()});
|
||||||
var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().onOff});
|
var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getOnOff()});
|
||||||
var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().followRoads});
|
var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getFollowRoads()});
|
||||||
|
|
||||||
if (selectedUnitsTypes.length == 1) {
|
if (selectedUnitsTypes.length == 1) {
|
||||||
this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "AGL": undefined, false);
|
this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "AGL": undefined, false);
|
||||||
@@ -170,15 +172,15 @@ export class UnitControlPanel extends Panel {
|
|||||||
|
|
||||||
/* Option buttons */
|
/* Option buttons */
|
||||||
this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => {
|
this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => {
|
||||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().ROE === button.value))
|
button.classList.toggle("selected", units.every((unit: Unit) => unit.getROE() === button.value))
|
||||||
});
|
});
|
||||||
|
|
||||||
this.#optionButtons["reactionToThreat"].forEach((button: HTMLButtonElement) => {
|
this.#optionButtons["reactionToThreat"].forEach((button: HTMLButtonElement) => {
|
||||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().reactionToThreat === button.value))
|
button.classList.toggle("selected", units.every((unit: Unit) => unit.getReactionToThreat() === button.value))
|
||||||
});
|
});
|
||||||
|
|
||||||
this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => {
|
this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => {
|
||||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().emissionsCountermeasures === button.value))
|
button.classList.toggle("selected", units.every((unit: Unit) => unit.getEmissionsCountermeasures() === button.value))
|
||||||
});
|
});
|
||||||
|
|
||||||
this.#onOffSwitch.setValue(onOff, false);
|
this.#onOffSwitch.setValue(onOff, false);
|
||||||
@@ -207,11 +209,11 @@ export class UnitControlPanel extends Panel {
|
|||||||
const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement;
|
const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement;
|
||||||
|
|
||||||
const unit = units[0];
|
const unit = units[0];
|
||||||
const roles = aircraftDatabase.getByName(unit.getBaseData().name)?.loadouts.map((loadout) => {return loadout.roles})
|
const roles = aircraftDatabase.getByName(unit.getName())?.loadouts?.map((loadout) => {return loadout.roles})
|
||||||
const tanker = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker");
|
const tanker = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker");
|
||||||
const AWACS = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS");
|
const AWACS = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS");
|
||||||
const radioMHz = Math.floor(unit.getOptionsData().radio.frequency / 1000000);
|
const radioMHz = Math.floor(unit.getRadio().frequency / 1000000);
|
||||||
const radioDecimals = (unit.getOptionsData().radio.frequency / 1000000 - radioMHz) * 1000;
|
const radioDecimals = (unit.getRadio().frequency / 1000000 - radioMHz) * 1000;
|
||||||
|
|
||||||
/* Activate the correct options depending on unit type */
|
/* Activate the correct options depending on unit type */
|
||||||
this.#advancedSettingsDialog.toggleAttribute("data-show-settings", !tanker && !AWACS);
|
this.#advancedSettingsDialog.toggleAttribute("data-show-settings", !tanker && !AWACS);
|
||||||
@@ -223,28 +225,28 @@ export class UnitControlPanel extends Panel {
|
|||||||
|
|
||||||
/* Set common properties */
|
/* Set common properties */
|
||||||
// Name
|
// Name
|
||||||
unitNameEl.innerText = unit.getBaseData().unitName;
|
unitNameEl.innerText = unit.getUnitName();
|
||||||
|
|
||||||
// General settings
|
// General settings
|
||||||
prohibitJettisonCheckbox.checked = unit.getOptionsData().generalSettings.prohibitJettison;
|
prohibitJettisonCheckbox.checked = unit.getGeneralSettings().prohibitJettison;
|
||||||
prohibitAfterburnerCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAfterburner;
|
prohibitAfterburnerCheckbox.checked = unit.getGeneralSettings().prohibitAfterburner;
|
||||||
prohibitAACheckbox.checked = unit.getOptionsData().generalSettings.prohibitAA;
|
prohibitAACheckbox.checked = unit.getGeneralSettings().prohibitAA;
|
||||||
prohibitAGCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAG;
|
prohibitAGCheckbox.checked = unit.getGeneralSettings().prohibitAG;
|
||||||
prohibitAirWpnCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAirWpn;
|
prohibitAirWpnCheckbox.checked = unit.getGeneralSettings().prohibitAirWpn;
|
||||||
|
|
||||||
// Tasking
|
// Tasking
|
||||||
tankerCheckbox.checked = unit.getTaskData().isTanker;
|
tankerCheckbox.checked = unit.getIsTanker();
|
||||||
AWACSCheckbox.checked = unit.getTaskData().isAWACS;
|
AWACSCheckbox.checked = unit.getIsAWACS();
|
||||||
|
|
||||||
// TACAN
|
// TACAN
|
||||||
TACANCheckbox.checked = unit.getOptionsData().TACAN.isOn;
|
TACANCheckbox.checked = unit.getTACAN().isOn;
|
||||||
TACANChannelInput.value = String(unit.getOptionsData().TACAN.channel);
|
TACANChannelInput.value = String(unit.getTACAN().channel);
|
||||||
TACANCallsignInput.value = String(unit.getOptionsData().TACAN.callsign);
|
TACANCallsignInput.value = String(unit.getTACAN().callsign);
|
||||||
this.#TACANXYDropdown.setValue(unit.getOptionsData().TACAN.XY);
|
this.#TACANXYDropdown.setValue(unit.getTACAN().XY);
|
||||||
|
|
||||||
// Radio
|
// Radio
|
||||||
radioMhzInput.value = String(radioMHz);
|
radioMhzInput.value = String(radioMHz);
|
||||||
radioCallsignNumberInput.value = String(unit.getOptionsData().radio.callsignNumber);
|
radioCallsignNumberInput.value = String(unit.getRadio().callsignNumber);
|
||||||
this.#radioDecimalsDropdown.setValue("." + radioDecimals);
|
this.#radioDecimalsDropdown.setValue("." + radioDecimals);
|
||||||
|
|
||||||
if (tanker) /* Set tanker specific options */
|
if (tanker) /* Set tanker specific options */
|
||||||
@@ -255,7 +257,7 @@ export class UnitControlPanel extends Panel {
|
|||||||
this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]);
|
this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]);
|
||||||
|
|
||||||
// This must be done after setting the options
|
// This must be done after setting the options
|
||||||
if (!this.#radioCallsignDropdown.selectValue(unit.getOptionsData().radio.callsign - 1)) // Ensure the selected value is in the acceptable range
|
if (!this.#radioCallsignDropdown.selectValue(unit.getRadio().callsign - 1)) // Ensure the selected value is in the acceptable range
|
||||||
this.#radioCallsignDropdown.selectValue(0);
|
this.#radioCallsignDropdown.selectValue(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getUnitsManager } from "..";
|
import { getUnitsManager } from "..";
|
||||||
|
import { Ammo } from "../@types/unit";
|
||||||
import { ConvertDDToDMS, rad2deg } from "../other/utils";
|
import { ConvertDDToDMS, rad2deg } from "../other/utils";
|
||||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||||
import { Unit } from "../units/unit";
|
import { Unit } from "../units/unit";
|
||||||
@@ -51,21 +52,21 @@ export class UnitInfoPanel extends Panel {
|
|||||||
#onUnitUpdate(unit: Unit) {
|
#onUnitUpdate(unit: Unit) {
|
||||||
if (this.getElement() != null && this.getVisible() && unit.getSelected()) {
|
if (this.getElement() != null && this.getVisible() && unit.getSelected()) {
|
||||||
|
|
||||||
const baseData = unit.getBaseData();
|
const baseData = unit.getData();
|
||||||
|
|
||||||
/* Set the unit info */
|
/* Set the unit info */
|
||||||
this.#unitLabel.innerText = aircraftDatabase.getByName(baseData.name)?.label || baseData.name;
|
this.#unitLabel.innerText = aircraftDatabase.getByName(baseData.name)?.label || baseData.name;
|
||||||
this.#unitName.innerText = baseData.unitName;
|
this.#unitName.innerText = baseData.unitName;
|
||||||
if (unit.getMissionData().flags.Human)
|
if (unit.getHuman())
|
||||||
this.#unitControl.innerText = "Human";
|
this.#unitControl.innerText = "Human";
|
||||||
else if (baseData.controlled)
|
else if (baseData.controlled)
|
||||||
this.#unitControl.innerText = "Olympus controlled";
|
this.#unitControl.innerText = "Olympus controlled";
|
||||||
else
|
else
|
||||||
this.#unitControl.innerText = "DCS Controlled";
|
this.#unitControl.innerText = "DCS Controlled";
|
||||||
this.#fuelBar.style.width = String(unit.getMissionData().fuel + "%");
|
this.#fuelBar.style.width = String(unit.getFuel() + "%");
|
||||||
this.#fuelPercentage.dataset.percentage = "" + unit.getMissionData().fuel;
|
this.#fuelPercentage.dataset.percentage = "" + unit.getFuel();
|
||||||
this.#currentTask.dataset.currentTask = unit.getTaskData().currentTask !== "" ? unit.getTaskData().currentTask : "No task";
|
this.#currentTask.dataset.currentTask = unit.getTask() !== "" ? unit.getTask() : "No task";
|
||||||
this.#currentTask.dataset.coalition = unit.getMissionData().coalition;
|
this.#currentTask.dataset.coalition = unit.getCoalition();
|
||||||
|
|
||||||
this.#silhouette.src = `/images/units/${unit.getDatabase()?.getByName(baseData.name)?.filename}`;
|
this.#silhouette.src = `/images/units/${unit.getDatabase()?.getByName(baseData.name)?.filename}`;
|
||||||
this.#silhouette.classList.toggle("hide", unit.getDatabase()?.getByName(baseData.name)?.filename == undefined || unit.getDatabase()?.getByName(baseData.name)?.filename == '');
|
this.#silhouette.classList.toggle("hide", unit.getDatabase()?.getByName(baseData.name)?.filename == undefined || unit.getDatabase()?.getByName(baseData.name)?.filename == '');
|
||||||
@@ -74,13 +75,13 @@ export class UnitInfoPanel extends Panel {
|
|||||||
const items = <HTMLElement>this.#loadoutContainer.querySelector("#loadout-items");
|
const items = <HTMLElement>this.#loadoutContainer.querySelector("#loadout-items");
|
||||||
|
|
||||||
if (items) {
|
if (items) {
|
||||||
const ammo = Object.values(unit.getMissionData().ammo);
|
const ammo = Object.values(unit.getAmmo());
|
||||||
if (ammo.length > 0) {
|
if (ammo.length > 0) {
|
||||||
items.replaceChildren(...Object.values(unit.getMissionData().ammo).map(
|
items.replaceChildren(...Object.values(unit.getAmmo()).map(
|
||||||
(ammo: any) => {
|
(ammo: Ammo) => {
|
||||||
var el = document.createElement("div");
|
var el = document.createElement("div");
|
||||||
el.dataset.qty = ammo.count;
|
el.dataset.qty = `${ammo.quantity}`;
|
||||||
el.dataset.item = ammo.desc.displayName;
|
el.dataset.item = ammo.name;
|
||||||
return el;
|
return el;
|
||||||
}
|
}
|
||||||
));
|
));
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { LatLng } from 'leaflet';
|
import { LatLng } from 'leaflet';
|
||||||
import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setConnectionStatus } from '..';
|
import { getConnectionStatusPanel, getInfoPopup, getLogPanel, getMissionHandler, getServerStatusPanel, getUnitDataTable, getUnitsManager, setLoginStatus } from '..';
|
||||||
import { SpawnOptions } from '../controls/mapcontextmenu';
|
import { GeneralSettings, Radio, TACAN } from '../@types/unit';
|
||||||
|
import { ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants';
|
||||||
|
|
||||||
var connected: boolean = false;
|
var connected: boolean = false;
|
||||||
var paused: boolean = false;
|
var paused: boolean = false;
|
||||||
@@ -14,22 +15,22 @@ const BULLSEYE_URI = "bullseyes";
|
|||||||
const MISSION_URI = "mission";
|
const MISSION_URI = "mission";
|
||||||
|
|
||||||
var username = "";
|
var username = "";
|
||||||
var credentials = "";
|
var password = "";
|
||||||
|
|
||||||
var sessionHash: string | null = null;
|
var sessionHash: string | null = null;
|
||||||
var lastUpdateTime = 0;
|
var lastUpdateTimes: {[key: string]: number} = {}
|
||||||
var demoEnabled = false;
|
var demoEnabled = false;
|
||||||
|
|
||||||
export function toggleDemoEnabled() {
|
export function toggleDemoEnabled() {
|
||||||
demoEnabled = !demoEnabled;
|
demoEnabled = !demoEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setCredentials(newUsername: string, newCredentials: string) {
|
export function setCredentials(newUsername: string, newPassword: string) {
|
||||||
username = newUsername;
|
username = newUsername;
|
||||||
credentials = newCredentials;
|
password = newPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function GET(callback: CallableFunction, uri: string, options?: { time?: number }) {
|
export function GET(callback: CallableFunction, uri: string, options?: { time?: number }, responseType?: string) {
|
||||||
var xmlHttp = new XMLHttpRequest();
|
var xmlHttp = new XMLHttpRequest();
|
||||||
|
|
||||||
/* Assemble the request options string */
|
/* Assemble the request options string */
|
||||||
@@ -37,24 +38,36 @@ export function GET(callback: CallableFunction, uri: string, options?: { time?:
|
|||||||
if (options?.time != undefined)
|
if (options?.time != undefined)
|
||||||
optionsString = `time=${options.time}`;
|
optionsString = `time=${options.time}`;
|
||||||
|
|
||||||
|
/* On the connection */
|
||||||
xmlHttp.open("GET", `${demoEnabled ? DEMO_ADDRESS : REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ''}`, true);
|
xmlHttp.open("GET", `${demoEnabled ? DEMO_ADDRESS : REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ''}`, true);
|
||||||
if (credentials)
|
|
||||||
xmlHttp.setRequestHeader("Authorization", "Basic " + credentials);
|
/* If provided, set the credentials */
|
||||||
|
if (username && password)
|
||||||
|
xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${username}:${password}`));
|
||||||
|
|
||||||
|
/* If specified, set the response type */
|
||||||
|
if (responseType)
|
||||||
|
xmlHttp.responseType = responseType as XMLHttpRequestResponseType;
|
||||||
|
|
||||||
xmlHttp.onload = function (e) {
|
xmlHttp.onload = function (e) {
|
||||||
if (xmlHttp.status == 200) {
|
if (xmlHttp.status == 200) {
|
||||||
var data = JSON.parse(xmlHttp.responseText);
|
/* Success */
|
||||||
if (uri !== UNITS_URI || (options?.time == 0) || parseInt(data.time) > lastUpdateTime) {
|
setConnected(true);
|
||||||
callback(data);
|
if (xmlHttp.responseType == 'arraybuffer')
|
||||||
lastUpdateTime = parseInt(data.time);
|
lastUpdateTimes[uri] = callback(xmlHttp.response);
|
||||||
if (isNaN(lastUpdateTime))
|
else {
|
||||||
lastUpdateTime = 0;
|
const result = JSON.parse(xmlHttp.responseText);
|
||||||
setConnected(true);
|
lastUpdateTimes[uri] = callback(result);
|
||||||
|
|
||||||
|
if (result.frameRate !== undefined && result.load !== undefined)
|
||||||
|
getServerStatusPanel().update(result.frameRate, result.load);
|
||||||
}
|
}
|
||||||
} else if (xmlHttp.status == 401) {
|
} else if (xmlHttp.status == 401) {
|
||||||
|
/* Bad credentials */
|
||||||
console.error("Incorrect username/password");
|
console.error("Incorrect username/password");
|
||||||
setConnectionStatus("failed");
|
setLoginStatus("failed");
|
||||||
} else {
|
} else {
|
||||||
|
/* Failure, probably disconnected */
|
||||||
setConnected(false);
|
setConnected(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -69,8 +82,8 @@ export function POST(request: object, callback: CallableFunction) {
|
|||||||
var xmlHttp = new XMLHttpRequest();
|
var xmlHttp = new XMLHttpRequest();
|
||||||
xmlHttp.open("PUT", demoEnabled ? DEMO_ADDRESS : REST_ADDRESS);
|
xmlHttp.open("PUT", demoEnabled ? DEMO_ADDRESS : REST_ADDRESS);
|
||||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||||
if (credentials)
|
if (username && password)
|
||||||
xmlHttp.setRequestHeader("Authorization", "Basic " + credentials);
|
xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${username}:${password}`));
|
||||||
xmlHttp.onreadystatechange = () => {
|
xmlHttp.onreadystatechange = () => {
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
@@ -103,8 +116,8 @@ export function getBullseye(callback: CallableFunction) {
|
|||||||
GET(callback, BULLSEYE_URI);
|
GET(callback, BULLSEYE_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLogs(callback: CallableFunction) {
|
export function getLogs(callback: CallableFunction, refresh: boolean = false) {
|
||||||
GET(callback, LOGS_URI);
|
GET(callback, LOGS_URI, { time: refresh ? 0 : lastUpdateTimes[LOGS_URI]});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMission(callback: CallableFunction) {
|
export function getMission(callback: CallableFunction) {
|
||||||
@@ -112,7 +125,7 @@ export function getMission(callback: CallableFunction) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getUnits(callback: CallableFunction, refresh: boolean = false) {
|
export function getUnits(callback: CallableFunction, refresh: boolean = false) {
|
||||||
GET(callback, `${UNITS_URI}`, { time: refresh ? 0 : lastUpdateTime });
|
GET(callback, UNITS_URI, { time: refresh ? 0 : lastUpdateTimes[UNITS_URI] }, 'arraybuffer');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addDestination(ID: number, path: any) {
|
export function addDestination(ID: number, path: any) {
|
||||||
@@ -133,15 +146,27 @@ export function spawnExplosion(intensity: number, latlng: LatLng) {
|
|||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spawnGroundUnit(spawnOptions: SpawnOptions) {
|
export function spawnAircrafts(units: any, coalition: string, airbaseName: string, immediate: boolean, spawnPoints: number) {
|
||||||
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition };
|
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||||
var data = { "spawnGround": command }
|
var data = { "spawnAircrafts": command }
|
||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function spawnAircraft(spawnOptions: SpawnOptions) {
|
export function spawnHelicopters(units: any, coalition: string, airbaseName: string, immediate: boolean, spawnPoints: number) {
|
||||||
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "altitude": spawnOptions.altitude, "payloadName": spawnOptions.loadout != null ? spawnOptions.loadout : "", "airbaseName": spawnOptions.airbaseName != null ? spawnOptions.airbaseName : "" };
|
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||||
var data = { "spawnAir": command }
|
var data = { "spawnHelicopters": command }
|
||||||
|
POST(data, () => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function spawnGroundUnits(units: any, coalition: string, immediate: boolean, spawnPoints: number) {
|
||||||
|
var command = { "units": units, "coalition": coalition, "immediate": immediate, "spawnPoints": spawnPoints };;
|
||||||
|
var data = { "spawnGroundUnits": command }
|
||||||
|
POST(data, () => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function spawnNavyUnits(units: any, coalition: string, immediate: boolean, spawnPoints: number) {
|
||||||
|
var command = { "units": units, "coalition": coalition, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||||
|
var data = { "spawnNavyUnits": command }
|
||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,8 +192,8 @@ export function cloneUnit(ID: number, latlng: LatLng) {
|
|||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteUnit(ID: number, explosion: boolean) {
|
export function deleteUnit(ID: number, explosion: boolean, immediate: boolean) {
|
||||||
var command = { "ID": ID, "explosion": explosion };
|
var command = { "ID": ID, "explosion": explosion, "immediate": immediate };
|
||||||
var data = { "deleteUnit": command }
|
var data = { "deleteUnit": command }
|
||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
@@ -222,19 +247,19 @@ export function createFormation(ID: number, isLeader: boolean, wingmenIDs: numbe
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function setROE(ID: number, ROE: string) {
|
export function setROE(ID: number, ROE: string) {
|
||||||
var command = { "ID": ID, "ROE": ROE }
|
var command = { "ID": ID, "ROE": ROEs.indexOf(ROE) }
|
||||||
var data = { "setROE": command }
|
var data = { "setROE": command }
|
||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setReactionToThreat(ID: number, reactionToThreat: string) {
|
export function setReactionToThreat(ID: number, reactionToThreat: string) {
|
||||||
var command = { "ID": ID, "reactionToThreat": reactionToThreat }
|
var command = { "ID": ID, "reactionToThreat": reactionsToThreat.indexOf(reactionToThreat) }
|
||||||
var data = { "setReactionToThreat": command }
|
var data = { "setReactionToThreat": command }
|
||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setEmissionsCountermeasures(ID: number, emissionCountermeasure: string) {
|
export function setEmissionsCountermeasures(ID: number, emissionCountermeasure: string) {
|
||||||
var command = { "ID": ID, "emissionsCountermeasures": emissionCountermeasure }
|
var command = { "ID": ID, "emissionsCountermeasures": emissionsCountermeasures.indexOf(emissionCountermeasure) }
|
||||||
var data = { "setEmissionsCountermeasures": command }
|
var data = { "setEmissionsCountermeasures": command }
|
||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
@@ -295,48 +320,86 @@ export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolea
|
|||||||
POST(data, () => { });
|
POST(data, () => { });
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startUpdate() {
|
export function setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: {blue: number, red: number}, eras: string[], setupTime: number) {
|
||||||
/* On the first connection, force request of full data */
|
var command = {
|
||||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
"restrictSpawns": restrictSpawns,
|
||||||
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
|
"restrictToCoalition": restrictToCoalition,
|
||||||
getMission((data: any) => { getMissionData()?.update(data) });
|
"spawnPoints": spawnPoints,
|
||||||
getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */);
|
"eras": eras,
|
||||||
|
"setupTime": setupTime
|
||||||
|
};
|
||||||
|
|
||||||
requestUpdate();
|
var data = { "setCommandModeOptions": command };
|
||||||
requestRefresh();
|
POST(data, () => { });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function startUpdate() {
|
||||||
|
window.setInterval(() => {
|
||||||
|
if (!getPaused()) {
|
||||||
|
getAirbases((data: AirbasesData) => {
|
||||||
|
checkSessionHash(data.sessionHash);
|
||||||
|
getMissionHandler()?.updateAirbases(data);
|
||||||
|
return data.time;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
if (!getPaused()){
|
||||||
|
getBullseye((data: BullseyesData) => {
|
||||||
|
checkSessionHash(data.sessionHash);
|
||||||
|
getMissionHandler()?.updateBullseyes(data);
|
||||||
|
return data.time;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
if (!getPaused()) {
|
||||||
|
getMission((data: MissionData) => {
|
||||||
|
checkSessionHash(data.sessionHash);
|
||||||
|
getMissionHandler()?.updateMission(data);
|
||||||
|
return data.time;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
if (!getPaused()) {
|
||||||
|
getLogs((data: any) => {
|
||||||
|
checkSessionHash(data.sessionHash);
|
||||||
|
getLogPanel().appendLogs(data.logs)
|
||||||
|
return data.time;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
if (!getPaused()) {
|
||||||
|
getUnits((buffer: ArrayBuffer) => {
|
||||||
|
var time = getUnitsManager()?.update(buffer);
|
||||||
|
return time;
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
window.setInterval(() => {
|
||||||
|
if (!getPaused()) {
|
||||||
|
getUnits((buffer: ArrayBuffer) => {
|
||||||
|
var time = getUnitsManager()?.update(buffer);
|
||||||
|
return time;
|
||||||
|
}, true);
|
||||||
|
getConnectionStatusPanel()?.update(getConnected());
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function requestUpdate() {
|
export function requestUpdate() {
|
||||||
/* Main update rate = 250ms is minimum time, equal to server update time. */
|
/* Main update rate = 250ms is minimum time, equal to server update time. */
|
||||||
getUnits((data: UnitsData) => {
|
if (!getPaused()) {
|
||||||
if (!getPaused()) {
|
getUnits((buffer: ArrayBuffer) => { return getUnitsManager()?.update(buffer); }, false);
|
||||||
getUnitsManager()?.update(data);
|
}
|
||||||
checkSessionHash(data.sessionHash);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000);
|
window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000);
|
||||||
|
|
||||||
getConnectionStatusPanel()?.update(getConnected());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function requestRefresh() {
|
|
||||||
/* Main refresh rate = 5000ms. */
|
|
||||||
getUnits((data: UnitsData) => {
|
|
||||||
if (!getPaused()) {
|
|
||||||
getUnitsManager()?.update(data);
|
|
||||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
|
||||||
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
|
|
||||||
getMission((data: any) => {
|
|
||||||
getMissionData()?.update(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the list of existing units
|
|
||||||
getUnitDataTable()?.update();
|
|
||||||
|
|
||||||
checkSessionHash(data.sessionHash);
|
|
||||||
}
|
|
||||||
}, true);
|
|
||||||
window.setTimeout(() => requestRefresh(), 5000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkSessionHash(newSessionHash: string) {
|
export function checkSessionHash(newSessionHash: string) {
|
||||||
|
|||||||
7137
client/src/units/citiesDatabase.ts
Normal file
154
client/src/units/dataextractor.ts
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import { LatLng } from "leaflet";
|
||||||
|
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN } from "../@types/unit";
|
||||||
|
|
||||||
|
export class DataExtractor {
|
||||||
|
#seekPosition = 0;
|
||||||
|
#dataview: DataView;
|
||||||
|
#decoder: TextDecoder;
|
||||||
|
#buffer: ArrayBuffer;
|
||||||
|
|
||||||
|
constructor(buffer: ArrayBuffer) {
|
||||||
|
this.#buffer = buffer;
|
||||||
|
this.#dataview = new DataView(this.#buffer);
|
||||||
|
this.#decoder = new TextDecoder("utf-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
getSeekPosition() {
|
||||||
|
return this.#seekPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractBool() {
|
||||||
|
const value = this.#dataview.getUint8(this.#seekPosition);
|
||||||
|
this.#seekPosition += 1;
|
||||||
|
return value > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractUInt8() {
|
||||||
|
const value = this.#dataview.getUint8(this.#seekPosition);
|
||||||
|
this.#seekPosition += 1;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractUInt16() {
|
||||||
|
const value = this.#dataview.getUint16(this.#seekPosition, true);
|
||||||
|
this.#seekPosition += 2;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractUInt32() {
|
||||||
|
const value = this.#dataview.getUint32(this.#seekPosition, true);
|
||||||
|
this.#seekPosition += 4;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractUInt64() {
|
||||||
|
const value = this.#dataview.getBigUint64(this.#seekPosition, true);
|
||||||
|
this.#seekPosition += 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractFloat64() {
|
||||||
|
const value = this.#dataview.getFloat64(this.#seekPosition, true);
|
||||||
|
this.#seekPosition += 8;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractLatLng() {
|
||||||
|
return new LatLng(this.extractFloat64(), this.extractFloat64(), this.extractFloat64())
|
||||||
|
}
|
||||||
|
|
||||||
|
extractFromBitmask(bitmask: number, position: number) {
|
||||||
|
return ((bitmask >> position) & 1) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractString(length?: number) {
|
||||||
|
if (length === undefined)
|
||||||
|
length = this.extractUInt16()
|
||||||
|
var stringBuffer = this.#buffer.slice(this.#seekPosition, this.#seekPosition + length);
|
||||||
|
var view = new Int8Array(stringBuffer);
|
||||||
|
var stringLength = length;
|
||||||
|
view.forEach((value: number, idx: number) => { if (value === 0) stringLength = idx; });
|
||||||
|
const value = this.#decoder.decode(stringBuffer);
|
||||||
|
this.#seekPosition += length;
|
||||||
|
return value.substring(0, stringLength).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
extractChar() {
|
||||||
|
return this.extractString(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
extractTACAN() {
|
||||||
|
const value: TACAN = {
|
||||||
|
isOn: this.extractBool(),
|
||||||
|
channel: this.extractUInt8(),
|
||||||
|
XY: this.extractChar(),
|
||||||
|
callsign: this.extractString(4)
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractRadio() {
|
||||||
|
const value: Radio = {
|
||||||
|
frequency: this.extractUInt32(),
|
||||||
|
callsign: this.extractUInt8(),
|
||||||
|
callsignNumber: this.extractUInt8()
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractGeneralSettings() {
|
||||||
|
const value: GeneralSettings = {
|
||||||
|
prohibitJettison: this.extractBool(),
|
||||||
|
prohibitAA: this.extractBool(),
|
||||||
|
prohibitAG: this.extractBool(),
|
||||||
|
prohibitAfterburner: this.extractBool(),
|
||||||
|
prohibitAirWpn: this.extractBool(),
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractAmmo() {
|
||||||
|
const value: Ammo[] = [];
|
||||||
|
const size = this.extractUInt16();
|
||||||
|
for (let idx = 0; idx < size; idx++) {
|
||||||
|
value.push({
|
||||||
|
quantity: this.extractUInt16(),
|
||||||
|
name: this.extractString(33),
|
||||||
|
guidance: this.extractUInt8(),
|
||||||
|
category: this.extractUInt8(),
|
||||||
|
missileCategory: this.extractUInt8()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractContacts(){
|
||||||
|
const value: Contact[] = [];
|
||||||
|
const size = this.extractUInt16();
|
||||||
|
for (let idx = 0; idx < size; idx++) {
|
||||||
|
value.push({
|
||||||
|
ID: this.extractUInt32(),
|
||||||
|
detectionMethod: this.extractUInt8()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractActivePath() {
|
||||||
|
const value: LatLng[] = [];
|
||||||
|
const size = this.extractUInt16();
|
||||||
|
for (let idx = 0; idx < size; idx++) {
|
||||||
|
value.push(this.extractLatLng());
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractOffset() {
|
||||||
|
const value: Offset = {
|
||||||
|
x: this.extractFloat64(),
|
||||||
|
y: this.extractFloat64(),
|
||||||
|
z: this.extractFloat64(),
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
1488
client/src/units/groundunitdatabase.ts
Normal file
486
client/src/units/navyunitdatabase.ts
Normal file
@@ -0,0 +1,486 @@
|
|||||||
|
import { getMissionHandler } from "..";
|
||||||
|
import { GAME_MASTER } from "../constants/constants";
|
||||||
|
import { UnitDatabase } from "./unitdatabase"
|
||||||
|
|
||||||
|
export class NavyUnitDatabase extends UnitDatabase {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.blueprints = {
|
||||||
|
"Type_052B": {
|
||||||
|
"name": "Type_052B",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Destroyer",
|
||||||
|
"era": "Modern",
|
||||||
|
"label": "052B DDG-168 Guangzhou",
|
||||||
|
"shortLabel": "Type 52B",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Type_052C": {
|
||||||
|
"name": "Type_052C",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Destroyer",
|
||||||
|
"era": "Modern",
|
||||||
|
"label": "052C DDG-171 Haikou",
|
||||||
|
"shortLabel": "Type 52C",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Type_054A": {
|
||||||
|
"name": "",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Modern",
|
||||||
|
"label": "054A FFG-538 Yantai",
|
||||||
|
"shortLabel": "Type 54A",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Type_071": {
|
||||||
|
"name": "Type_071",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Transport",
|
||||||
|
"era": "Modern",
|
||||||
|
"label": "Type 071",
|
||||||
|
"shortLabel": "Type 071",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Type_093": {
|
||||||
|
"name": "Type_093",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Submarine",
|
||||||
|
"era": "Modern",
|
||||||
|
"label": "Type 093",
|
||||||
|
"shortLabel": "Type 093",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"santafe": {
|
||||||
|
"name": "santafe",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Submarine",
|
||||||
|
"era": "Early Cold War",
|
||||||
|
"label": "ARA Santa Fe S-21",
|
||||||
|
"shortLabel": "ARA Santa",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"ara_vdm": {
|
||||||
|
"name": "ara_vdm",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "ARA Vienticinco de Mayo",
|
||||||
|
"shortLabel": "ARA Vienticinco de Mayo",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"kuznecow": {
|
||||||
|
"name": "kuznecow",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Admiral Kuznetsov",
|
||||||
|
"shortLabel": "Admiral Kuznetsov",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"albatros": {
|
||||||
|
"name": "albatros",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Early Cold War",
|
||||||
|
"label": "Albatros (Grisha-5)",
|
||||||
|
"shortLabel": "Albatros",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"leander-gun-condell": {
|
||||||
|
"name": "leander-gun-condell",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "Almirante Condell PFG-06",
|
||||||
|
"shortLabel": "Almirante Condell",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Boat Armed Hi-Speed": {
|
||||||
|
"name": "Boat Armed Hi-Speed",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Fast Attack Craft",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "Boat Armed Hi-Speed",
|
||||||
|
"shortLabel": "Boat Armed Hi-Speed",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"HandyWind": {
|
||||||
|
"name": "HandyWind",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Cargoship",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Bulker Handy Wind",
|
||||||
|
"shortLabel": "Bulker Handy Wind",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"CV_1143_5": {
|
||||||
|
"name": "CV_1143_5",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Modern",
|
||||||
|
"label": "CV Admiral Kuznetsov(2017)",
|
||||||
|
"shortLabel": "Admiral Kuznetsov(2017)",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"CV_59": {
|
||||||
|
"name": "CV_59",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Early Cold War",
|
||||||
|
"label": "CV-59 Forrestal",
|
||||||
|
"shortLabel": "CV-59",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"CVN_71": {
|
||||||
|
"name": "CVN_71",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Super Aircraft Carrier",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "CVN-71 Theodore Roosevelt",
|
||||||
|
"shortLabel": "CVN-71",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"CVN_72": {
|
||||||
|
"name": "CVN_72",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Super Aircraft Carrier",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "CVN-72 Abraham Lincoln",
|
||||||
|
"shortLabel": "CVN-72",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"CVN_73": {
|
||||||
|
"name": "CVN_73",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Super Aircraft Carrier",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "CVN-73 George Washington",
|
||||||
|
"shortLabel": "CVN-73",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Stennis": {
|
||||||
|
"name": "Stennis",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "CVN-74 John C. Stennis",
|
||||||
|
"shortLabel": "CVN-74",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"CVN_75": {
|
||||||
|
"name": "CVN_75",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "CVN-75 Harry S. Truman",
|
||||||
|
"shortLabel": "CVN-75",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"CastleClass_01": {
|
||||||
|
"name": "CastleClass_01",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Patrol",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "HMS Leeds Castle (P-258)",
|
||||||
|
"shortLabel": "HMS Leeds Castle (P-258)",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"USS_Arleigh_Burke_IIa": {
|
||||||
|
"name": "USS_Arleigh_Burke_IIa",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Destroyer",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "DDG Arleigh Burke lla",
|
||||||
|
"shortLabel": "DDG Arleigh Burke",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"barge-1": {
|
||||||
|
"name": "barge-1",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Cargoship",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Dry cargo ship Ivanov",
|
||||||
|
"shortLabel": "Dry cargo ship Ivanov",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"barge-2": {
|
||||||
|
"name": "barge-2",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Cargoship",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Dry cargo ship Yakushev",
|
||||||
|
"shortLabel": "Dry cargo ship Yakushev",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"elnya": {
|
||||||
|
"name": "elnya",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Tanker",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Elnya tanker",
|
||||||
|
"shortLabel": "Elnya tanker",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"La_Combattante_II": {
|
||||||
|
"name": "La_Combattante_II",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Fast Attack Craft",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "FAC La Combattante lla",
|
||||||
|
"shortLabel": "FAC La Combattante",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"leander-gun-achilles": {
|
||||||
|
"name": "leander-gun-achilles",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "HMS Achilles (F12)",
|
||||||
|
"shortLabel": "HMS Achilles",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"leander-gun-andromeda": {
|
||||||
|
"name": "leander-gun-andromeda",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "HMS Andromeda (F57)",
|
||||||
|
"shortLabel": "HMS Andromeda",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"leander-gun-ariadne": {
|
||||||
|
"name": "leander-gun-ariadne",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "HMS Ariadne (F72)",
|
||||||
|
"shortLabel": "HMS Ariadne",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"leander-gun-lynch": {
|
||||||
|
"name": "leander-gun-lynch",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "CNS Almirante Lynch (PFG-07)",
|
||||||
|
"shortLabel": "CNS Almirante Lynch",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"hms_invincible": {
|
||||||
|
"name": "hms_invincible",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "HMS Invincible (R05)",
|
||||||
|
"shortLabel": "HMS Invincible",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"HarborTug": {
|
||||||
|
"name": "HarborTug",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Tug",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "Harbor Tug",
|
||||||
|
"shortLabel": "Harbor Tug",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"kilo_636": {
|
||||||
|
"name": "kilo_636",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Submarine",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Project 636 Varshavyanka Improved",
|
||||||
|
"shortLabel": "Varshavyanka Improved",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"kilo": {
|
||||||
|
"name": "kilo",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Submarine",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Project 636 Varshavyanka Basic",
|
||||||
|
"shortLabel": "Varshavyanka Basic",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"LHA_Tarawa": {
|
||||||
|
"name": "LHA_Tarawa",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Aircraft Carrier",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "LHA-1 Tarawa",
|
||||||
|
"shortLabel": "LHA-1 Tarawa",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"BDK-775": {
|
||||||
|
"name": "BDK-775",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Landing Craft",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "LS Ropucha",
|
||||||
|
"shortLabel": "LS Ropucha",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"molniya": {
|
||||||
|
"name": "molniya",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Fast Attack Craft",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Molniya (Tarantul-3)",
|
||||||
|
"shortLabel": "Molniya",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"moscow": {
|
||||||
|
"name": "moscow",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Cruiser",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Moscow",
|
||||||
|
"shortLabel": "Moscow",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"neustrash": {
|
||||||
|
"name": "neustrash",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Neustrashimy",
|
||||||
|
"shortLabel": "Neustrashimy",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"perry": {
|
||||||
|
"name": "perry",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Mid Cold War",
|
||||||
|
"label": "Oliver H. Perry",
|
||||||
|
"shortLabel": "Oliver H. Perry",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"piotr_velikiy": {
|
||||||
|
"name": "piotr_velikiy",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Cruiser",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Pyotr Velikiy",
|
||||||
|
"shortLabel": "Pyotr Velikiy",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"rezky": {
|
||||||
|
"name": "Rezky (Krivak-2)",
|
||||||
|
"coalition": "red",
|
||||||
|
"type": "Frigate",
|
||||||
|
"era": "Early Cold War",
|
||||||
|
"label": "Rezky (Krivak-2)",
|
||||||
|
"shortLabel": "Rezky",
|
||||||
|
"range": "Short",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Ship_Tilde_Supply": {
|
||||||
|
"name": "Ship_Tilde_Supply",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Transport",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Supply Ship MV Tilde",
|
||||||
|
"shortLabel": "Supply Ship Tilde",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"Seawise_Giant": {
|
||||||
|
"name": "Seawise_Giant",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Tanker",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Tanker Seawise Giant",
|
||||||
|
"shortLabel": "Seawise Giant",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"TICONDEROG": {
|
||||||
|
"name": "TICONDEROG",
|
||||||
|
"coalition": "blue",
|
||||||
|
"type": "Cruiser",
|
||||||
|
"era": "Late Cold War",
|
||||||
|
"label": "Ticonderoga",
|
||||||
|
"shortLabel": "Ticonderoga",
|
||||||
|
"range": "Medium",
|
||||||
|
"filename": ""
|
||||||
|
},
|
||||||
|
"zwezdny": {
|
||||||
|
"name": "zwezdny",
|
||||||
|
"coalition": "",
|
||||||
|
"type": "Civilian Boat",
|
||||||
|
"era": "Modern",
|
||||||
|
"label": "Zwezdny",
|
||||||
|
"shortLabel": "Zwezdny",
|
||||||
|
"range": "",
|
||||||
|
"filename": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpawnPointsByName(name: string) {
|
||||||
|
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
const blueprint = this.getByName(name);
|
||||||
|
if (blueprint?.era == "WW2")
|
||||||
|
return 20;
|
||||||
|
else if (blueprint?.era == "Early Cold War")
|
||||||
|
return 50;
|
||||||
|
else if (blueprint?.era == "Mid Cold War")
|
||||||
|
return 100;
|
||||||
|
else if (blueprint?.era == "Late Cold War")
|
||||||
|
return 200;
|
||||||
|
else if (blueprint?.era == "Modern")
|
||||||
|
return 400;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCategory() {
|
||||||
|
return "NavyUnit";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export var navyUnitDatabase = new NavyUnitDatabase();
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import { LatLng } from "leaflet";
|
||||||
|
import { getMissionHandler, getUnitsManager } from "..";
|
||||||
|
import { GAME_MASTER } from "../constants/constants";
|
||||||
|
|
||||||
export class UnitDatabase {
|
export class UnitDatabase {
|
||||||
blueprints: { [key: string]: UnitBlueprint } = {};
|
blueprints: { [key: string]: UnitBlueprint } = {};
|
||||||
|
|
||||||
@@ -5,18 +9,8 @@ export class UnitDatabase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Returns a list of all possible roles in a database */
|
getCategory() {
|
||||||
getRoles() {
|
return "";
|
||||||
var roles: string[] = [];
|
|
||||||
for (let unit in this.blueprints) {
|
|
||||||
for (let loadout of this.blueprints[unit].loadouts) {
|
|
||||||
for (let role of loadout.roles) {
|
|
||||||
if (role !== "" && !roles.includes(role))
|
|
||||||
roles.push(role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return roles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Gets a specific blueprint by name */
|
/* Gets a specific blueprint by name */
|
||||||
@@ -35,14 +29,113 @@ export class UnitDatabase {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getBlueprints() {
|
||||||
|
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER || !getMissionHandler().getCommandModeOptions().restrictSpawns)
|
||||||
|
return this.blueprints;
|
||||||
|
else {
|
||||||
|
var filteredBlueprints: { [key: string]: UnitBlueprint } = {};
|
||||||
|
for (let unit in this.blueprints) {
|
||||||
|
const blueprint = this.blueprints[unit];
|
||||||
|
if (this.getSpawnPointsByName(blueprint.name) <= getMissionHandler().getAvailableSpawnPoints() &&
|
||||||
|
getMissionHandler().getCommandModeOptions().eras.includes(blueprint.era) &&
|
||||||
|
(!getMissionHandler().getCommandModeOptions().restrictToCoalition || blueprint.coalition === getMissionHandler().getCommandedCoalition())) {
|
||||||
|
filteredBlueprints[unit] = blueprint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredBlueprints;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a list of all possible roles in a database */
|
||||||
|
getRoles() {
|
||||||
|
var roles: string[] = [];
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
|
for (let unit in filteredBlueprints) {
|
||||||
|
var loadouts = filteredBlueprints[unit].loadouts;
|
||||||
|
if (loadouts) {
|
||||||
|
for (let loadout of loadouts) {
|
||||||
|
for (let role of loadout.roles) {
|
||||||
|
if (role !== "" && !roles.includes(role))
|
||||||
|
roles.push(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a list of all possible types in a database */
|
||||||
|
getTypes() {
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
|
var types: string[] = [];
|
||||||
|
for (let unit in filteredBlueprints) {
|
||||||
|
var type = filteredBlueprints[unit].type;
|
||||||
|
if (type && type !== "" && !types.includes(type))
|
||||||
|
types.push(type);
|
||||||
|
}
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a list of all possible periods in a database */
|
||||||
|
getEras() {
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
|
var eras: string[] = [];
|
||||||
|
for (let unit in filteredBlueprints) {
|
||||||
|
var era = filteredBlueprints[unit].era;
|
||||||
|
if (era && era !== "" && !eras.includes(era))
|
||||||
|
eras.push(era);
|
||||||
|
}
|
||||||
|
return eras;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a list of all possible ranges in a database */
|
||||||
|
getRanges() {
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
|
var ranges: string[] = [];
|
||||||
|
for (let unit in filteredBlueprints) {
|
||||||
|
var range = filteredBlueprints[unit].range;
|
||||||
|
if (range && range !== "" && !ranges.includes(range))
|
||||||
|
ranges.push(range);
|
||||||
|
}
|
||||||
|
return ranges;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get all blueprints by range */
|
||||||
|
getByRange(range: string) {
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
|
var unitswithrange = [];
|
||||||
|
for (let unit in filteredBlueprints) {
|
||||||
|
if (filteredBlueprints[unit].range === range) {
|
||||||
|
unitswithrange.push(filteredBlueprints[unit]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return unitswithrange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get all blueprints by type */
|
||||||
|
getByType(type: string) {
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
|
var units = [];
|
||||||
|
for (let unit in filteredBlueprints) {
|
||||||
|
if (filteredBlueprints[unit].type === type) {
|
||||||
|
units.push(filteredBlueprints[unit]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return units;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get all blueprints by role */
|
/* Get all blueprints by role */
|
||||||
getByRole(role: string) {
|
getByRole(role: string) {
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
var units = [];
|
var units = [];
|
||||||
for (let unit in this.blueprints) {
|
for (let unit in filteredBlueprints) {
|
||||||
for (let loadout of this.blueprints[unit].loadouts) {
|
var loadouts = filteredBlueprints[unit].loadouts;
|
||||||
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) {
|
if (loadouts) {
|
||||||
units.push(this.blueprints[unit])
|
for (let loadout of loadouts) {
|
||||||
break;
|
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) {
|
||||||
|
units.push(filteredBlueprints[unit])
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,21 +144,53 @@ export class UnitDatabase {
|
|||||||
|
|
||||||
/* Get the names of all the loadouts for a specific unit and for a specific role */
|
/* Get the names of all the loadouts for a specific unit and for a specific role */
|
||||||
getLoadoutNamesByRole(name: string, role: string) {
|
getLoadoutNamesByRole(name: string, role: string) {
|
||||||
var loadouts = [];
|
var filteredBlueprints = this.getBlueprints();
|
||||||
for (let loadout of this.blueprints[name].loadouts) {
|
var loadoutsByRole = [];
|
||||||
if (loadout.roles.includes(role) || loadout.roles.includes("")) {
|
var loadouts = filteredBlueprints[name].loadouts;
|
||||||
loadouts.push(loadout.name)
|
if (loadouts) {
|
||||||
|
for (let loadout of loadouts) {
|
||||||
|
if (loadout.roles.includes(role) || loadout.roles.includes("")) {
|
||||||
|
loadoutsByRole.push(loadout.name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return loadouts;
|
return loadoutsByRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the loadout content from the unit name and loadout name */
|
/* Get the loadout content from the unit name and loadout name */
|
||||||
getLoadoutByName(name: string, loadoutName: string) {
|
getLoadoutByName(name: string, loadoutName: string) {
|
||||||
for (let loadout of this.blueprints[name].loadouts) {
|
var loadouts = this.blueprints[name].loadouts;
|
||||||
if (loadout.name === loadoutName)
|
if (loadouts) {
|
||||||
return loadout;
|
for (let loadout of loadouts) {
|
||||||
|
if (loadout.name === loadoutName)
|
||||||
|
return loadout;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateTestGrid(initialPosition: LatLng) {
|
||||||
|
var filteredBlueprints = this.getBlueprints();
|
||||||
|
const step = 0.01;
|
||||||
|
var nUnits = Object.values(filteredBlueprints).length;
|
||||||
|
var gridSize = Math.ceil(Math.sqrt(nUnits));
|
||||||
|
Object.values(filteredBlueprints).forEach((unitBlueprint: UnitBlueprint, idx: number) => {
|
||||||
|
var row = Math.floor(idx / gridSize);
|
||||||
|
var col = idx - row * gridSize;
|
||||||
|
var location = new LatLng(initialPosition.lat + col * step, initialPosition.lng + row * step)
|
||||||
|
getUnitsManager().spawnUnits(this.getCategory(), [{unitType: unitBlueprint.name, location: location, altitude: 1000, loadout: ""}]);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpawnPointsByLabel(label: string) {
|
||||||
|
var blueprint = this.getByLabel(label);
|
||||||
|
if (blueprint)
|
||||||
|
return this.getSpawnPointsByName(blueprint.name);
|
||||||
|
else
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSpawnPointsByName(name: string) {
|
||||||
|
return Infinity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
import { LatLng, LatLngBounds } from "leaflet";
|
import { LatLng, LatLngBounds } from "leaflet";
|
||||||
import { getHotgroupPanel, getInfoPopup, getMap, getUnitDataTable } from "..";
|
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager } from "..";
|
||||||
import { Unit } from "./unit";
|
import { Unit } from "./unit";
|
||||||
import { cloneUnit } from "../server/server";
|
import { cloneUnit, deleteUnit, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server";
|
||||||
import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots } from "../other/utils";
|
import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils";
|
||||||
import { IDLE, MOVE_UNIT } from "../map/map";
|
import { CoalitionArea } from "../map/coalitionarea";
|
||||||
|
import { groundUnitDatabase } from "./groundunitdatabase";
|
||||||
|
import { DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT, NONE } from "../constants/constants";
|
||||||
|
import { DataExtractor } from "./dataextractor";
|
||||||
|
import { Contact } from "../@types/unit";
|
||||||
|
import { citiesDatabase } from "./citiesdatabase";
|
||||||
|
import { aircraftDatabase } from "./aircraftdatabase";
|
||||||
|
import { helicopterDatabase } from "./helicopterdatabase";
|
||||||
|
import { navyUnitDatabase } from "./navyunitdatabase";
|
||||||
|
|
||||||
export class UnitsManager {
|
export class UnitsManager {
|
||||||
#units: { [ID: number]: Unit };
|
#units: { [ID: number]: Unit };
|
||||||
#copiedUnits: Unit[];
|
#copiedUnits: any[];
|
||||||
#selectionEventDisabled: boolean = false;
|
#selectionEventDisabled: boolean = false;
|
||||||
#pasteDisabled: boolean = false;
|
#pasteDisabled: boolean = false;
|
||||||
#hiddenTypes: string[] = [];
|
#hiddenTypes: string[] = [];
|
||||||
|
#requestDetectionUpdate: boolean = false;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.#units = {};
|
this.#units = {};
|
||||||
@@ -23,13 +32,16 @@ export class UnitsManager {
|
|||||||
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete());
|
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete());
|
||||||
document.addEventListener('explodeSelectedUnits', () => this.selectedUnitsDelete(true));
|
document.addEventListener('explodeSelectedUnits', () => this.selectedUnitsDelete(true));
|
||||||
document.addEventListener('keyup', (event) => this.#onKeyUp(event));
|
document.addEventListener('keyup', (event) => this.#onKeyUp(event));
|
||||||
|
document.addEventListener('exportToFile', () => this.exportToFile());
|
||||||
|
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())});
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectableAircraft() {
|
getSelectableAircraft() {
|
||||||
const units = this.getUnits();
|
const units = this.getUnits();
|
||||||
return Object.keys(units).reduce((acc: { [key: number]: Unit }, unitId: any) => {
|
return Object.keys(units).reduce((acc: { [key: number]: Unit }, unitId: any) => {
|
||||||
const baseData = units[unitId].getBaseData();
|
if (units[unitId].getCategory() === "Aircraft" && units[unitId].getAlive() === true) {
|
||||||
if (baseData.category === "Aircraft" && baseData.alive === true) {
|
|
||||||
acc[unitId] = units[unitId];
|
acc[unitId] = units[unitId];
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
@@ -48,15 +60,15 @@ export class UnitsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getUnitsByHotgroup(hotgroup: number) {
|
getUnitsByHotgroup(hotgroup: number) {
|
||||||
return Object.values(this.#units).filter((unit: Unit) => { return unit.getBaseData().alive && unit.getHotgroup() == hotgroup });
|
return Object.values(this.#units).filter((unit: Unit) => { return unit.getAlive() && unit.getHotgroup() == hotgroup });
|
||||||
}
|
}
|
||||||
|
|
||||||
addUnit(ID: number, data: UnitData) {
|
addUnit(ID: number, category: string) {
|
||||||
if (data.baseData && data.baseData.category){
|
if (category){
|
||||||
/* The name of the unit category is exactly the same as the constructor name */
|
/* The name of the unit category is exactly the same as the constructor name */
|
||||||
var constructor = Unit.getConstructor(data.baseData.category);
|
var constructor = Unit.getConstructor(category);
|
||||||
if (constructor != undefined) {
|
if (constructor != undefined) {
|
||||||
this.#units[ID] = new constructor(ID, data);
|
this.#units[ID] = new constructor(ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,30 +77,40 @@ export class UnitsManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update(data: UnitsData) {
|
update(buffer: ArrayBuffer) {
|
||||||
var updatedUnits: Unit[] = [];
|
var dataExtractor = new DataExtractor(buffer);
|
||||||
Object.keys(data.units)
|
var updateTime = Number(dataExtractor.extractUInt64());
|
||||||
.filter((ID: string) => !(ID in this.#units))
|
var requestRefresh = false;
|
||||||
.reduce((timeout: number, ID: string) => {
|
while (dataExtractor.getSeekPosition() < buffer.byteLength) {
|
||||||
window.setTimeout(() => {
|
const ID = dataExtractor.extractUInt32();
|
||||||
if (!(ID in this.#units))
|
if (!(ID in this.#units)) {
|
||||||
this.addUnit(parseInt(ID), data.units[ID]);
|
const datumIndex = dataExtractor.extractUInt8();
|
||||||
this.#units[parseInt(ID)]?.setData(data.units[ID]);
|
if (datumIndex == DataIndexes.category) {
|
||||||
}, timeout);
|
const category = dataExtractor.extractString();
|
||||||
return timeout + 10;
|
this.addUnit(ID, category);
|
||||||
}, 10);
|
}
|
||||||
|
else {
|
||||||
|
requestRefresh = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.#units[ID]?.setData(dataExtractor);
|
||||||
|
}
|
||||||
|
|
||||||
Object.keys(data.units)
|
if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
|
||||||
.filter((ID: string) => ID in this.#units)
|
for (let ID in this.#units) {
|
||||||
.forEach((ID: string) => {
|
var unit = this.#units[ID];
|
||||||
updatedUnits.push(this.#units[parseInt(ID)]);
|
if (!unit.belongsToCommandedCoalition())
|
||||||
this.#units[parseInt(ID)]?.setData(data.units[ID])
|
unit.setDetectionMethods(this.getUnitDetectedMethods(unit));
|
||||||
});
|
}
|
||||||
|
this.#requestDetectionUpdate = false;
|
||||||
|
}
|
||||||
|
|
||||||
this.getSelectedUnits().forEach((unit: Unit) => {
|
for (let ID in this.#units) {
|
||||||
if (!updatedUnits.includes(unit))
|
if (this.#units[ID].getSelected())
|
||||||
unit.setData({})
|
this.#units[ID].drawLines();
|
||||||
});
|
};
|
||||||
|
|
||||||
|
return updateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
setHiddenType(key: string, value: boolean) {
|
setHiddenType(key: string, value: boolean) {
|
||||||
@@ -115,7 +137,7 @@ export class UnitsManager {
|
|||||||
this.deselectAllUnits();
|
this.deselectAllUnits();
|
||||||
for (let ID in this.#units) {
|
for (let ID in this.#units) {
|
||||||
if (this.#units[ID].getHidden() == false) {
|
if (this.#units[ID].getHidden() == false) {
|
||||||
var latlng = new LatLng(this.#units[ID].getFlightData().latitude, this.#units[ID].getFlightData().longitude);
|
var latlng = new LatLng(this.#units[ID].getPosition().lat, this.#units[ID].getPosition().lng);
|
||||||
if (bounds.contains(latlng)) {
|
if (bounds.contains(latlng)) {
|
||||||
this.#units[ID].setSelected(true);
|
this.#units[ID].setSelected(true);
|
||||||
}
|
}
|
||||||
@@ -132,11 +154,11 @@ export class UnitsManager {
|
|||||||
}
|
}
|
||||||
if (options) {
|
if (options) {
|
||||||
if (options.excludeHumans)
|
if (options.excludeHumans)
|
||||||
selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getMissionData().flags.Human });
|
selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getHuman() });
|
||||||
if (options.onlyOnePerGroup) {
|
if (options.onlyOnePerGroup) {
|
||||||
var temp: Unit[] = [];
|
var temp: Unit[] = [];
|
||||||
for (let unit of selectedUnits) {
|
for (let unit of selectedUnits) {
|
||||||
if (!temp.some((otherUnit: Unit) => unit.getBaseData().groupName == otherUnit.getBaseData().groupName))
|
if (!temp.some((otherUnit: Unit) => unit.getGroupName() == otherUnit.getGroupName()))
|
||||||
temp.push(unit);
|
temp.push(unit);
|
||||||
}
|
}
|
||||||
selectedUnits = temp;
|
selectedUnits = temp;
|
||||||
@@ -157,36 +179,59 @@ export class UnitsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getSelectedUnitsTypes() {
|
getSelectedUnitsTypes() {
|
||||||
if (this.getSelectedUnits().length == 0)
|
const selectedUnits = this.getSelectedUnits();
|
||||||
|
if (selectedUnits.length == 0)
|
||||||
return [];
|
return [];
|
||||||
return this.getSelectedUnits().map((unit: Unit) => {
|
return selectedUnits.map((unit: Unit) => {
|
||||||
return unit.constructor.name
|
return unit.getCategory();
|
||||||
})?.filter((value: any, index: any, array: string[]) => {
|
})?.filter((value: any, index: any, array: string[]) => {
|
||||||
return array.indexOf(value) === index;
|
return array.indexOf(value) === index;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Gets the value of a variable from the selected units. If all the units have the same value, returns the value, else returns undefined */
|
||||||
getSelectedUnitsVariable(variableGetter: CallableFunction) {
|
getSelectedUnitsVariable(variableGetter: CallableFunction) {
|
||||||
if (this.getSelectedUnits().length == 0)
|
const selectedUnits = this.getSelectedUnits();
|
||||||
|
if (selectedUnits.length == 0)
|
||||||
return undefined;
|
return undefined;
|
||||||
return this.getSelectedUnits().map((unit: Unit) => {
|
return selectedUnits.map((unit: Unit) => {
|
||||||
return variableGetter(unit);
|
return variableGetter(unit);
|
||||||
})?.reduce((a: any, b: any) => {
|
})?.reduce((a: any, b: any) => {
|
||||||
return a == b ? a : undefined
|
return a === b ? a : undefined
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
getSelectedUnitsCoalition() {
|
getSelectedUnitsCoalition() {
|
||||||
if (this.getSelectedUnits().length == 0)
|
const selectedUnits = this.getSelectedUnits();
|
||||||
|
if (selectedUnits.length == 0)
|
||||||
return undefined;
|
return undefined;
|
||||||
return this.getSelectedUnits().map((unit: Unit) => {
|
return selectedUnits.map((unit: Unit) => {
|
||||||
return unit.getMissionData().coalition
|
return unit.getCoalition()
|
||||||
})?.reduce((a: any, b: any) => {
|
})?.reduce((a: any, b: any) => {
|
||||||
return a == b ? a : undefined
|
return a == b ? a : undefined
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getByType(type: string) {
|
||||||
|
Object.values(this.getUnits()).filter((unit: Unit) => {
|
||||||
|
return unit.getType() === type;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnitDetectedMethods(unit: Unit) {
|
||||||
|
var detectionMethods: number[] = [];
|
||||||
|
for (let idx in this.#units) {
|
||||||
|
if (this.#units[idx].getAlive() && this.#units[idx].getIsLeader() && this.#units[idx].getCoalition() !== "neutral" && this.#units[idx].getCoalition() != unit.getCoalition())
|
||||||
|
{
|
||||||
|
this.#units[idx].getContacts().forEach((contact: Contact) => {
|
||||||
|
if (contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod))
|
||||||
|
detectionMethods.push(contact.detectionMethod);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return detectionMethods;
|
||||||
|
}
|
||||||
|
|
||||||
/*********************** Actions on selected units ************************/
|
/*********************** Actions on selected units ************************/
|
||||||
selectedUnitsAddDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number) {
|
selectedUnitsAddDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number) {
|
||||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||||
@@ -196,13 +241,13 @@ export class UnitsManager {
|
|||||||
if (mantainRelativePosition)
|
if (mantainRelativePosition)
|
||||||
unitDestinations = this.selectedUnitsComputeGroupDestination(latlng, rotation);
|
unitDestinations = this.selectedUnitsComputeGroupDestination(latlng, rotation);
|
||||||
else
|
else
|
||||||
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng });
|
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng; });
|
||||||
|
|
||||||
for (let idx in selectedUnits) {
|
for (let idx in selectedUnits) {
|
||||||
const unit = selectedUnits[idx];
|
const unit = selectedUnits[idx];
|
||||||
/* If a unit is following another unit, and that unit is also selected, send the command to the followed unit */
|
/* If a unit is following another unit, and that unit is also selected, send the command to the followed unit */
|
||||||
if (unit.getTaskData().currentState === "Follow") {
|
if (unit.getState() === "Follow") {
|
||||||
const leader = this.getUnitByID(unit.getFormationData().leaderID)
|
const leader = this.getUnitByID(unit.getLeaderID())
|
||||||
if (leader && leader.getSelected())
|
if (leader && leader.getSelected())
|
||||||
leader.addDestination(latlng);
|
leader.addDestination(latlng);
|
||||||
else
|
else
|
||||||
@@ -221,8 +266,8 @@ export class UnitsManager {
|
|||||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||||
for (let idx in selectedUnits) {
|
for (let idx in selectedUnits) {
|
||||||
const unit = selectedUnits[idx];
|
const unit = selectedUnits[idx];
|
||||||
if (unit.getTaskData().currentState === "Follow") {
|
if (unit.getState() === "Follow") {
|
||||||
const leader = this.getUnitByID(unit.getFormationData().leaderID)
|
const leader = this.getUnitByID(unit.getLeaderID())
|
||||||
if (leader && leader.getSelected())
|
if (leader && leader.getSelected())
|
||||||
leader.clearDestinations();
|
leader.clearDestinations();
|
||||||
else
|
else
|
||||||
@@ -333,21 +378,25 @@ export class UnitsManager {
|
|||||||
for (let idx in selectedUnits) {
|
for (let idx in selectedUnits) {
|
||||||
selectedUnits[idx].attackUnit(ID);
|
selectedUnits[idx].attackUnit(ID);
|
||||||
}
|
}
|
||||||
this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getBaseData().unitName}`);
|
this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedUnitsDelete(explosion: boolean = false) {
|
selectedUnitsDelete(explosion: boolean = false) {
|
||||||
var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
|
var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
|
||||||
const selectionContainsAHuman = selectedUnits.some( ( unit:Unit ) => {
|
const selectionContainsAHuman = selectedUnits.some( ( unit:Unit ) => {
|
||||||
return unit.getMissionData().flags.Human === true;
|
return unit.getHuman() === true;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (selectionContainsAHuman && !confirm( "Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?" ) ) {
|
if (selectionContainsAHuman && !confirm( "Your selection includes a human player. Deleting humans causes their vehicle to crash.\n\nAre you sure you want to do this?" ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var immediate = false;
|
||||||
|
if (selectedUnits.length > 20)
|
||||||
|
immediate = confirm(`You are trying to delete ${selectedUnits.length} units, do you want to delete them immediately? This may cause lag for players.`)
|
||||||
|
|
||||||
for (let idx in selectedUnits) {
|
for (let idx in selectedUnits) {
|
||||||
selectedUnits[idx].delete(explosion);
|
selectedUnits[idx].delete(explosion, immediate);
|
||||||
}
|
}
|
||||||
this.#showActionMessage(selectedUnits, `deleted`);
|
this.#showActionMessage(selectedUnits, `deleted`);
|
||||||
}
|
}
|
||||||
@@ -400,7 +449,7 @@ export class UnitsManager {
|
|||||||
}
|
}
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getBaseData().unitName}`);
|
this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedUnitsSetHotgroup(hotgroup: number) {
|
selectedUnitsSetHotgroup(hotgroup: number) {
|
||||||
@@ -422,7 +471,7 @@ export class UnitsManager {
|
|||||||
/* Compute the center of the group */
|
/* Compute the center of the group */
|
||||||
var center = { x: 0, y: 0 };
|
var center = { x: 0, y: 0 };
|
||||||
selectedUnits.forEach((unit: Unit) => {
|
selectedUnits.forEach((unit: Unit) => {
|
||||||
var mercator = latLngToMercator(unit.getFlightData().latitude, unit.getFlightData().longitude);
|
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
|
||||||
center.x += mercator.x / selectedUnits.length;
|
center.x += mercator.x / selectedUnits.length;
|
||||||
center.y += mercator.y / selectedUnits.length;
|
center.y += mercator.y / selectedUnits.length;
|
||||||
});
|
});
|
||||||
@@ -430,7 +479,7 @@ export class UnitsManager {
|
|||||||
/* Compute the distances from the center of the group */
|
/* Compute the distances from the center of the group */
|
||||||
var unitDestinations: { [key: number]: LatLng } = {};
|
var unitDestinations: { [key: number]: LatLng } = {};
|
||||||
selectedUnits.forEach((unit: Unit) => {
|
selectedUnits.forEach((unit: Unit) => {
|
||||||
var mercator = latLngToMercator(unit.getFlightData().latitude, unit.getFlightData().longitude);
|
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
|
||||||
var distancesFromCenter = { dx: mercator.x - center.x, dy: mercator.y - center.y };
|
var distancesFromCenter = { dx: mercator.x - center.x, dy: mercator.y - center.y };
|
||||||
|
|
||||||
/* Rotate the distance according to the group rotation */
|
/* Rotate the distance according to the group rotation */
|
||||||
@@ -480,29 +529,190 @@ export class UnitsManager {
|
|||||||
this.#showActionMessage(selectedUnits, `unit bombing point`);
|
this.#showActionMessage(selectedUnits, `unit bombing point`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO add undo group
|
||||||
|
selectedUnitsCreateGroup() {
|
||||||
|
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: false });
|
||||||
|
var units = [];
|
||||||
|
var coalition = "neutral";
|
||||||
|
for (let idx in selectedUnits) {
|
||||||
|
var unit = selectedUnits[idx];
|
||||||
|
coalition = unit.getCoalition();
|
||||||
|
deleteUnit(unit.ID, false, true);
|
||||||
|
units.push({unitType: unit.getName(), location: unit.getPosition()});
|
||||||
|
}
|
||||||
|
const category = this.getSelectedUnitsTypes()[0];
|
||||||
|
this.spawnUnits(category, units, coalition, true);
|
||||||
|
}
|
||||||
|
|
||||||
/***********************************************/
|
/***********************************************/
|
||||||
copyUnits() {
|
copyUnits() {
|
||||||
this.#copiedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
|
this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => {return unit.getData()}))); /* Can be applied to humans too */
|
||||||
this.#showActionMessage(this.#copiedUnits, `copied`);
|
getInfoPopup().setText(`${this.#copiedUnits.length} units copied`);
|
||||||
}
|
}
|
||||||
|
|
||||||
pasteUnits() {
|
pasteUnits() {
|
||||||
if (!this.#pasteDisabled) {
|
if (!this.#pasteDisabled && getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER) {
|
||||||
|
/* Compute the position of the center of the copied units */
|
||||||
|
var nUnits = this.#copiedUnits.length;
|
||||||
|
var avgLat = 0;
|
||||||
|
var avgLng = 0;
|
||||||
for (let idx in this.#copiedUnits) {
|
for (let idx in this.#copiedUnits) {
|
||||||
var unit = this.#copiedUnits[idx];
|
var unit = this.#copiedUnits[idx];
|
||||||
getMap().addTemporaryMarker(getMap().getMouseCoordinates());
|
avgLat += unit.position.lat / nUnits;
|
||||||
cloneUnit(unit.ID, getMap().getMouseCoordinates());
|
avgLng += unit.position.lng / nUnits;
|
||||||
this.#showActionMessage(this.#copiedUnits, `pasted`);
|
|
||||||
}
|
}
|
||||||
this.#pasteDisabled = true;
|
|
||||||
window.setTimeout(() => this.#pasteDisabled = false, 250);
|
/* Organize the copied units in groups */
|
||||||
|
var groups: {[key: string]: any} = {};
|
||||||
|
this.#copiedUnits.forEach((unit: any) => {
|
||||||
|
if (!(unit.groupName in groups))
|
||||||
|
groups[unit.groupName] = [];
|
||||||
|
groups[unit.groupName].push(unit);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let groupName in groups) {
|
||||||
|
/* Paste the units as groups. Only for ground and navy units because of loadouts, TODO: find a better solution so it works for them too*/
|
||||||
|
if (!["Aircraft", "Helicopter"].includes(groups[groupName][0].category)) {
|
||||||
|
var units = groups[groupName].map((unit: any) => {
|
||||||
|
var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng);
|
||||||
|
getMap().addTemporaryMarker(position, unit.name, unit.coalition);
|
||||||
|
return {unitType: unit.name, location: position};
|
||||||
|
});
|
||||||
|
this.spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
groups[groupName].forEach((unit: any) => {
|
||||||
|
var position = new LatLng(getMap().getMouseCoordinates().lat + unit.position.lat - avgLat, getMap().getMouseCoordinates().lng + unit.position.lng - avgLng);
|
||||||
|
getMap().addTemporaryMarker(position, unit.name, unit.coalition);
|
||||||
|
cloneUnit(unit.ID, position);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getInfoPopup().setText(`${this.#copiedUnits.length - 1} units pasted`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
getInfoPopup().setText(`Unit cloning is disabled in ${getMissionHandler().getCommandModeOptions().commandMode} mode`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createIADS(coalitionArea: CoalitionArea, types: {[key: string]: boolean}, eras: {[key: string]: boolean}, ranges: {[key: string]: boolean}, density: number, distribution: number) {
|
||||||
|
const activeTypes = Object.keys(types).filter((key: string) => { return types[key]; });
|
||||||
|
const activeEras = Object.keys(eras).filter((key: string) => { return eras[key]; });
|
||||||
|
const activeRanges = Object.keys(ranges).filter((key: string) => { return ranges[key]; });
|
||||||
|
|
||||||
|
citiesDatabase.forEach((city: {lat: number, lng: number, pop: number}) => {
|
||||||
|
if (polyContains(new LatLng(city.lat, city.lng), coalitionArea)) {
|
||||||
|
var pointsNumber = 2 + Math.pow(city.pop, 0.2) * density / 100;
|
||||||
|
for (let i = 0; i < pointsNumber; i++) {
|
||||||
|
var bearing = Math.random() * 360;
|
||||||
|
var distance = Math.random() * distribution * 100;
|
||||||
|
const latlng = bearingAndDistanceToLatLng(city.lat, city.lng, bearing, distance);
|
||||||
|
if (polyContains(latlng, coalitionArea)) {
|
||||||
|
const type = activeTypes[Math.floor(Math.random() * activeTypes.length)];
|
||||||
|
if (Math.random() < IADSDensities[type]) {
|
||||||
|
const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {type: type, eras: activeEras, ranges: activeRanges});
|
||||||
|
if (unitBlueprint) {
|
||||||
|
this.spawnUnits("GroundUnit", [{unitType: unitBlueprint.name, location: latlng}], coalitionArea.getCoalition(), true);
|
||||||
|
getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exportToFile() {
|
||||||
|
var unitsToExport: {[key: string]: any} = {};
|
||||||
|
for (let ID in this.#units) {
|
||||||
|
var unit = this.#units[ID];
|
||||||
|
if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) {
|
||||||
|
var data: any = unit.getData();
|
||||||
|
if (unit.getGroupName() in unitsToExport)
|
||||||
|
unitsToExport[unit.getGroupName()].push(data);
|
||||||
|
else
|
||||||
|
unitsToExport[unit.getGroupName()] = [data];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var a = document.createElement("a");
|
||||||
|
var file = new Blob([JSON.stringify(unitsToExport)], {type: 'text/plain'});
|
||||||
|
a.href = URL.createObjectURL(file);
|
||||||
|
a.download = 'export.json';
|
||||||
|
a.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
importFromFile() {
|
||||||
|
var input = document.createElement("input");
|
||||||
|
input.type = "file";
|
||||||
|
input.addEventListener("change", (e: any) => {
|
||||||
|
var file = e.target.files[0];
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var reader = new FileReader();
|
||||||
|
reader.onload = function(e: any) {
|
||||||
|
var contents = e.target.result;
|
||||||
|
var groups = JSON.parse(contents);
|
||||||
|
for (let groupName in groups) {
|
||||||
|
if (groupName !== "" && groups[groupName].length > 0 && groups[groupName].every((unit: any) => {return unit.category == "GroundUnit";})) {
|
||||||
|
var units = groups[groupName].map((unit: any) => {return {unitType: unit.name, location: unit.position}});
|
||||||
|
getUnitsManager().spawnUnits("GroundUnit", units, groups[groupName][0].coalition, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
})
|
||||||
|
input.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
spawnUnits(category: string, units: any, coalition: string = "blue", immediate: boolean = true, airbase: string = "") {
|
||||||
|
var spawnPoints = 0;
|
||||||
|
if (category === "Aircraft") {
|
||||||
|
if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
|
||||||
|
getInfoPopup().setText("Aircrafts can be air spawned during the SETUP phase only");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spawnPoints = units.reduce((points: number, unit: any) => {return points + aircraftDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||||
|
spawnAircrafts(units, coalition, airbase, immediate, spawnPoints);
|
||||||
|
} else if (category === "Helicopter") {
|
||||||
|
if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
|
||||||
|
getInfoPopup().setText("Helicopters can be air spawned during the SETUP phase only");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spawnPoints = units.reduce((points: number, unit: any) => {return points + helicopterDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||||
|
spawnHelicopters(units, coalition, airbase, immediate, spawnPoints);
|
||||||
|
} else if (category === "GroundUnit") {
|
||||||
|
if (getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
|
||||||
|
getInfoPopup().setText("Ground units can be spawned during the SETUP phase only");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spawnPoints = units.reduce((points: number, unit: any) => {return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||||
|
spawnGroundUnits(units, coalition, immediate, spawnPoints);
|
||||||
|
} else if (category === "NavyUnit") {
|
||||||
|
if (getMissionHandler().getRemainingSetupTime() < 0 && getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER) {
|
||||||
|
getInfoPopup().setText("Navy units can be spawned during the SETUP phase only");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spawnPoints = units.reduce((points: number, unit: any) => {return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||||
|
spawnNavyUnits(units, coalition, immediate, spawnPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spawnPoints <= getMissionHandler().getAvailableSpawnPoints()) {
|
||||||
|
getMissionHandler().setSpentSpawnPoints(spawnPoints);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
getInfoPopup().setText("Not enough spawn points available!");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********************************************/
|
/***********************************************/
|
||||||
#onKeyUp(event: KeyboardEvent) {
|
#onKeyUp(event: KeyboardEvent) {
|
||||||
if (!keyEventWasInInput(event) && event.key === "Delete" ) {
|
if (!keyEventWasInInput(event)) {
|
||||||
this.selectedUnitsDelete();
|
if (event.key === "Delete")
|
||||||
|
this.selectedUnitsDelete();
|
||||||
|
else if (event.key === "a" && event.ctrlKey)
|
||||||
|
Object.values(this.getUnits()).filter((unit: Unit) => {return !unit.getHidden()}).forEach((unit: Unit) => unit.setSelected(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,10 +743,10 @@ export class UnitsManager {
|
|||||||
document.dispatchEvent(new CustomEvent("unitsDeselection", { detail: this.getSelectedUnits() }));
|
document.dispatchEvent(new CustomEvent("unitsDeselection", { detail: this.getSelectedUnits() }));
|
||||||
}
|
}
|
||||||
|
|
||||||
#showActionMessage(units: Unit[], message: string) {
|
#showActionMessage(units: any[], message: string) {
|
||||||
if (units.length == 1)
|
if (units.length == 1)
|
||||||
getInfoPopup().setText(`${units[0].getBaseData().unitName} ${message}`);
|
getInfoPopup().setText(`${units[0].getUnitName()} ${message}`);
|
||||||
else if (units.length > 1)
|
else if (units.length > 1)
|
||||||
getInfoPopup().setText(`${units[0].getBaseData().unitName} and ${units.length - 1} other units ${message}`);
|
getInfoPopup().setText(`${units[0].getUnitName()} and ${units.length - 1} other units ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24,8 +24,14 @@
|
|||||||
<%- include('panels/unitinfo.ejs') %>
|
<%- include('panels/unitinfo.ejs') %>
|
||||||
<%- include('panels/mouseinfo.ejs') %>
|
<%- include('panels/mouseinfo.ejs') %>
|
||||||
<%- include('panels/connectionstatus.ejs') %>
|
<%- include('panels/connectionstatus.ejs') %>
|
||||||
|
<%- include('panels/serverstatus.ejs') %>
|
||||||
<%- include('panels/hotgroup.ejs') %>
|
<%- include('panels/hotgroup.ejs') %>
|
||||||
<%- include('panels/navbar.ejs') %>
|
<%- include('panels/logpanel.ejs') %>
|
||||||
|
|
||||||
|
<div id="toolbar-container">
|
||||||
|
<%- include('toolbars/primary.ejs') %>
|
||||||
|
<%- include('toolbars/commandmode.ejs') %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<%- include('other/dialogs.ejs') %>
|
<%- include('other/dialogs.ejs') %>
|
||||||
<%- include('other/popups.ejs') %>
|
<%- include('other/popups.ejs') %>
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<div id="log-panel" class="ol-panel">
|
|
||||||
<!-- Log entries go here -->
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +1,30 @@
|
|||||||
<div id="map-contextmenu" oncontextmenu="return false;">
|
<div id="map-contextmenu" class="ol-context-menu" oncontextmenu="return false;">
|
||||||
<div id="active-coalition-label" data-active-coalition="blue"></div>
|
<div id="active-coalition-label" data-coalition="blue"></div>
|
||||||
<div id="upper-bar" class="ol-panel">
|
<div class="upper-bar ol-panel">
|
||||||
<div id="coalition-switch" class="ol-switch"></div>
|
<div id="coalition-switch" class="ol-switch ol-coalition-switch"></div>
|
||||||
<button data-active-coalition="blue" id="aircraft-spawn-button" title="Spawn aircraft" data-on-click="contextMenuShow"
|
<button data-coalition="blue" id="aircraft-spawn-button" title="Spawn aircraft" data-on-click="mapContextMenuShow"
|
||||||
data-on-click-params='{ "type": "aircraft" }' class="unit-spawn-button"></button>
|
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||||
<button data-active-coalition="blue" id="ground-unit-spawn-button" title="Spawn ground unit" data-on-click="contextMenuShow"
|
<button data-coalition="blue" id="helicopter-spawn-button" title="Spawn helicopter" data-on-click="mapContextMenuShow"
|
||||||
data-on-click-params='{ "type": "ground-unit" }' class="unit-spawn-button"></button>
|
data-on-click-params='{ "type": "helicopter" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||||
<button data-active-coalition="blue" id="smoke-spawn-button" title="Spawn smoke" data-on-click="contextMenuShow"
|
<button data-coalition="blue" id="groundunit-spawn-button" title="Spawn ground unit" data-on-click="mapContextMenuShow"
|
||||||
data-on-click-params='{ "type": "smoke" }' class="unit-spawn-button"></button>
|
data-on-click-params='{ "type": "groundunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/groundunit.svg" inject-svg></button>
|
||||||
<button data-active-coalition="blue" id="explosion-spawn-button" title="Explosion" data-on-click="contextMenuShow"
|
<button data-coalition="blue" id="coalition-area-button" title="Edit coalition area" data-on-click="editCoalitionArea"
|
||||||
data-on-click-params='{ "type": "explosion" }' class="unit-spawn-button"></button>
|
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/edit.svg" inject-svg></button>
|
||||||
|
<button data-coalition="blue" id="more-options-button" title="More options" data-on-click="mapContextMenuShow"
|
||||||
|
data-on-click-params='{ "type": "more" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/more.svg" inject-svg></button>
|
||||||
</div>
|
</div>
|
||||||
<div id="aircraft-spawn-menu" class="ol-panel hide">
|
<div id="more-options-button-bar" class="upper-bar ol-panel hide">
|
||||||
|
<div id="coalition-switch" class="ol-switch ol-coalition-switch"></div>
|
||||||
|
<button data-coalition="blue" id="navyunit-spawn-button" title="Spawn navy unit" data-on-click="mapContextMenuShow"
|
||||||
|
data-on-click-params='{ "type": "navyunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/navyunit.svg" inject-svg></button>
|
||||||
|
<button data-coalition="blue" id="smoke-spawn-button" title="Spawn smoke" data-on-click="mapContextMenuShow"
|
||||||
|
data-on-click-params='{ "type": "smoke" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/smoke.svg" inject-svg></button>
|
||||||
|
<button data-coalition="blue" id="explosion-spawn-button" title="Explosion" data-on-click="mapContextMenuShow"
|
||||||
|
data-on-click-params='{ "type": "explosion" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/explosion.svg" inject-svg></button>
|
||||||
|
<button data-coalition="blue" id="polygon-draw-button" title="Enter polygon draw mode" data-on-click="toggleCoalitionAreaDraw"
|
||||||
|
data-on-click-params='{"type": "polygon"}' class="ol-contexmenu-button"><img src="resources/theme/images/buttons/tools/draw-polygon-solid.svg" inject-svg></button>
|
||||||
|
</div>
|
||||||
|
<div id="aircraft-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||||
<div class="ol-select-container">
|
<div class="ol-select-container">
|
||||||
<div id="aircraft-role-options" class="ol-select">
|
<div id="aircraft-role-options" class="ol-select">
|
||||||
<div class="ol-select-value">Aircraft role</div>
|
<div class="ol-select-value">Aircraft role</div>
|
||||||
@@ -21,8 +34,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ol-select-container">
|
<div class="ol-select-container">
|
||||||
<div id="aircraft-type-options" class="ol-select">
|
<div id="aircraft-label-options" class="ol-select">
|
||||||
<div class="ol-select-value">Aircraft type</div>
|
<div class="ol-select-value">Aircraft name</div>
|
||||||
<div class="ol-select-options">
|
<div class="ol-select-options">
|
||||||
<div>Select role first</div>
|
<div>Select role first</div>
|
||||||
<!-- This is where all the aircraft types buttons will be shown-->
|
<!-- This is where all the aircraft types buttons will be shown-->
|
||||||
@@ -30,7 +43,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ol-select-container">
|
<div class="ol-select-container">
|
||||||
<div id="loadout-options" class="ol-select">
|
<div id="aircraft-loadout-options" class="ol-select">
|
||||||
<div class="ol-select-value">Loadout</div>
|
<div class="ol-select-value">Loadout</div>
|
||||||
<div class="ol-select-options">
|
<div class="ol-select-options">
|
||||||
<div>Select type first</div>
|
<div>Select type first</div>
|
||||||
@@ -38,6 +51,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="ol-select-container contextmenu-options-container">
|
||||||
|
<div>Group members</div>
|
||||||
|
<div id="aircraft-count-options" class="ol-select">
|
||||||
|
<div class="ol-select-value"></div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the aircraft count buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="aircraft-spawn-altitude-slider" class="ol-slider-container flight-control-ol-slider">
|
<div id="aircraft-spawn-altitude-slider" class="ol-slider-container flight-control-ol-slider">
|
||||||
<dl class="ol-data-grid">
|
<dl class="ol-data-grid">
|
||||||
<dt> Spawn altitude
|
<dt> Spawn altitude
|
||||||
@@ -49,45 +71,137 @@
|
|||||||
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div id="loadout-preview">
|
<div id="aircraft-loadout-preview">
|
||||||
<img id="unit-image" class="hide">
|
<img id="aircraft-unit-image" class="hide">
|
||||||
<div id="loadout-list">
|
<div id="aircraft-loadout-list">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="deploy-unit-button" title="" data-active-coalition="blue" data-on-click="contextMenuDeployAircraft" disabled>Deploy unit</button>
|
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployAircrafts" disabled>Deploy unit</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="ground-unit-spawn-menu" class="ol-panel hide">
|
<div id="helicopter-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||||
<div class="ol-select-container">
|
<div class="ol-select-container">
|
||||||
<div id="ground-unit-role-options" class="ol-select">
|
<div id="helicopter-role-options" class="ol-select">
|
||||||
<div class="ol-select-value">Ground unit role</div>
|
<div class="ol-select-value">Helicopter role</div>
|
||||||
<div class="ol-select-options">
|
<div class="ol-select-options">
|
||||||
<!-- This is where all the ground unit roles buttons will be shown-->
|
<!-- This is where all the helicopter roles buttons will be shown-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ol-select-container">
|
<div class="ol-select-container">
|
||||||
<div id="ground-unit-type-options" class="ol-select">
|
<div id="helicopter-label-options" class="ol-select">
|
||||||
<div class="ol-select-value">Ground unit type</div>
|
<div class="ol-select-value">Helicopter name</div>
|
||||||
<div class="ol-select-options">
|
<div class="ol-select-options">
|
||||||
<div>Select role first</div>
|
<div>Select role first</div>
|
||||||
<!-- This is where all the ground unit types buttons will be shown-->
|
<!-- This is where all the helicopter types buttons will be shown-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button class="deploy-unit-button" title="" data-active-coalition="blue" data-on-click="contextMenuDeployGroundUnit" disabled>Deploy unit</button>
|
<div class="ol-select-container">
|
||||||
|
<div id="helicopter-loadout-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Loadout</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<div>Select type first</div>
|
||||||
|
<!-- This is where all the helicopter loadouts buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ol-select-container contextmenu-options-container">
|
||||||
|
<div>Group members</div>
|
||||||
|
<div id="helicopter-count-options" class="ol-select">
|
||||||
|
<div class="ol-select-value"></div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the helicopter count buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="helicopter-spawn-altitude-slider" class="ol-slider-container flight-control-ol-slider">
|
||||||
|
<dl class="ol-data-grid">
|
||||||
|
<dt> Spawn altitude
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
<div class="ol-slider-value"></div>
|
||||||
|
</dd>
|
||||||
|
</dl>
|
||||||
|
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div id="helicopter-loadout-preview">
|
||||||
|
<img id="helicopter-unit-image" class="hide">
|
||||||
|
<div id="helicopter-loadout-list">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployHelicopters" disabled>Deploy unit</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="smoke-spawn-menu" class="ol-panel hide">
|
<div id="groundunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||||
|
<div class="ol-select-container">
|
||||||
|
<div id="groundunit-type-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Ground unit type</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the groundunit roles buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ol-select-container">
|
||||||
|
<div id="groundunit-label-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Ground unit name</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<div>Select role first</div>
|
||||||
|
<!-- This is where all the groundunit types buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ol-select-container contextmenu-options-container">
|
||||||
|
<div>Group members</div>
|
||||||
|
<div id="groundunit-count-options" class="ol-select">
|
||||||
|
<div class="ol-select-value"></div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the groundunit count buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployGroundUnits" disabled>Deploy unit</button>
|
||||||
|
</div>
|
||||||
|
<div id="navyunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||||
|
<div class="ol-select-container">
|
||||||
|
<div id="navyunit-type-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Navy unit type</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the navyunit roles buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ol-select-container">
|
||||||
|
<div id="navyunit-label-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Navy unit name</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<div>Select role first</div>
|
||||||
|
<!-- This is where all the navyunit types buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ol-select-container contextmenu-options-container">
|
||||||
|
<div>Group members</div>
|
||||||
|
<div id="navyunit-count-options" class="ol-select">
|
||||||
|
<div class="ol-select-value"></div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the navyunit count buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="deploy-unit-button" title="" data-coalition="blue" data-on-click="contextMenuDeployNavyUnits" disabled>Deploy unit</button>
|
||||||
|
</div>
|
||||||
|
<div id="smoke-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||||
<button class="smoke-button" title="" data-smoke-color="white" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "white" }'>White smoke</button>
|
<button class="smoke-button" title="" data-smoke-color="white" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "white" }'>White smoke</button>
|
||||||
<button class="smoke-button" title="" data-smoke-color="blue" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "blue" }'>Blue smoke</button>
|
<button class="smoke-button" title="" data-smoke-color="blue" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "blue" }'>Blue smoke</button>
|
||||||
<button class="smoke-button" title="" data-smoke-color="red" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "red" }'>Red smoke</button>
|
<button class="smoke-button" title="" data-smoke-color="red" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "red" }'>Red smoke</button>
|
||||||
<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="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>
|
<button class="smoke-button" title="" data-smoke-color="orange" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "orange" }'>Orange smoke</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="explosion-menu" class="ol-panel hide">
|
<div id="explosion-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 50 }'>Small explosion</button>
|
<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": 100 }'>Medium 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": 200 }'>Big 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": 400 }'>Huge explosion</button>
|
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 4 }'>Huge explosion</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -96,9 +210,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="airbase-contextmenu" class="ol-panel" oncontextmenu="return false;">
|
<div id="airbase-contextmenu" class="ol-panel" oncontextmenu="return false;">
|
||||||
|
|
||||||
<h3 id="airbase-name"></h3>
|
<h3 id="airbase-name"></h3>
|
||||||
|
|
||||||
<div id="airbase-properties"></div>
|
<div id="airbase-properties"></div>
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
@@ -106,7 +218,68 @@
|
|||||||
<h4>Parking available:</h4>
|
<h4>Parking available:</h4>
|
||||||
<div id="airbase-parking"></div>
|
<div id="airbase-parking"></div>
|
||||||
|
|
||||||
<button id="spawn-airbase-aircraft-button" data-active-coalition="red" title="Spawn aircraft" data-on-click="contextMenuSpawnAirbase" class="deploy-unit-button">Spawn</button>
|
<button id="spawn-airbase-aircraft-button" data-coalition="blue" title="Spawn aircraft" data-on-click="contextMenuSpawnAirbase" class="deploy-unit-button">Spawn</button>
|
||||||
<button id="land-here-button" title="Land here" data-on-click="contextMenuLandAirbase" class="hide">Land here</button>
|
<button id="land-here-button" title="Land here" data-on-click="contextMenuLandAirbase" class="hide">Land here</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="coalition-area-contextmenu" class="ol-context-menu" oncontextmenu="return false;">
|
||||||
|
<div id="area-coalition-label" data-coalition="blue"></div>
|
||||||
|
<div class="upper-bar ol-panel">
|
||||||
|
<div id="coalition-area-switch" class="ol-switch ol-coalition-switch"></div>
|
||||||
|
<button data-coalition="blue" id="iads-button" title="Create Integrated Air Defense System" data-on-click="coalitionAreaContextMenuShow"
|
||||||
|
data-on-click-params='{ "type": "iads" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||||
|
<!--<button data-coalition="blue" id="cap-button" title="Create Combat Air Patrols" data-on-click="coalitionAreaContextMenuShow"
|
||||||
|
data-on-click-params='{ "type": "cap" }' class="ol-contexmenu-button"></button>-->
|
||||||
|
<button data-coalition="blue" id="coalitionarea-back-button" title="Bring area to back" data-on-click="coalitionAreaBringToBack"
|
||||||
|
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/back.svg" inject-svg></button>
|
||||||
|
<button data-coalition="blue" id="coalitionarea-delete-button" title="Delete area" data-on-click="coalitionAreaDelete"
|
||||||
|
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/delete.svg" inject-svg></button>
|
||||||
|
</div>
|
||||||
|
<div id="iads-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||||
|
<div id="iads-units-type-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Unit types</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the iads unit types checkboxes will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ol-select-container">
|
||||||
|
<div id="iads-era-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Units eras</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the iads unit era buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="ol-select-container">
|
||||||
|
<div id="iads-range-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Units ranges</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the iads unit range buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<div id="coalition-units-checkbox" class="ol-checkbox">
|
||||||
|
<label title="Use coalition specific units only (e.g. Patriot sites for blue coalition, SA-2s for red coalition)">
|
||||||
|
<input type="checkbox"/>
|
||||||
|
Coalition specific units
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<div id="iads-density-slider" class="ol-slider-container">
|
||||||
|
<dl class="ol-data-grid">
|
||||||
|
<dt> IADS density </dt> <dd> <div class="ol-slider-value"></div> </dd>
|
||||||
|
</dl>
|
||||||
|
<input title="An high density value will cause more units to be deployed" type="range" min="0" max="100" value="0" class="ol-slider">
|
||||||
|
</div>
|
||||||
|
<div id="iads-distribution-slider" class="ol-slider-container">
|
||||||
|
<dl class="ol-data-grid">
|
||||||
|
<dt> IADS distribution </dt> <dd> <div class="ol-slider-value"></div> </dd>
|
||||||
|
</dl>
|
||||||
|
<input title="If distrubution is low units will be concentrated around towns, otherwise they will spread around the map more evenly" type="range" min="0" max="100" value="0" class="ol-slider">
|
||||||
|
</div>
|
||||||
|
<button class="create-iads-button" title="" data-coalition="blue" data-on-click="contextMenuCreateIads">Add units to IADS</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<div id="app-summary">
|
<div id="app-summary">
|
||||||
<h2>DCS Olympus</h2>
|
<h2>DCS Olympus</h2>
|
||||||
<h4>Dynamic Unit Command</h4>
|
<h4>Dynamic Unit Command</h4>
|
||||||
<div class="app-version">Version <span class="app-version-number">v0.3.0</span></div>
|
<div class="app-version">Version <span class="app-version-number">v0.4.0-alpha</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="authentication-form">
|
<div id="authentication-form">
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
<button id="connection-button" class="ol-button-apply" data-on-click="tryConnection">Connect</button>
|
<button id="connection-button" class="ol-button-apply" data-on-click="tryConnection">Connect</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h5 id="connection-status"><br></h5>
|
<h5 id="login-status"><br></h5>
|
||||||
|
|
||||||
<div id="legal-stuff">
|
<div id="legal-stuff">
|
||||||
<h5>DISCLAIMER</h5>
|
<h5>DISCLAIMER</h5>
|
||||||
@@ -183,7 +183,6 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div id="custom-formation-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
<div id="custom-formation-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||||
|
|
||||||
@@ -232,3 +231,75 @@
|
|||||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="command-mode-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||||
|
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||||
|
|
||||||
|
<div class="ol-dialog-header">
|
||||||
|
<h3 id="unit-name">Command mode settings</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-dialog-content">
|
||||||
|
<div id="restrict-spawns" class="ol-checkbox">
|
||||||
|
<label title="If false, no spawn restrictions will be applied">
|
||||||
|
<input type="checkbox"/>
|
||||||
|
Restrict spawns
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="restrict-to-coalition" class="ol-checkbox">
|
||||||
|
<label title="If true, commanders will be allowed to only spawn units that belong to their coalition. E.g. blue commanders will be able to spawn F/A-18 Hornets, but not MiG-29s.">
|
||||||
|
<input type="checkbox"/>
|
||||||
|
Restrict units to coalition
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-group">
|
||||||
|
<label>Setup time: </label>
|
||||||
|
|
||||||
|
<div class="ol-group">
|
||||||
|
<div id="setup-time" class="ol-text-input">
|
||||||
|
<input type="number" min="-99999" max="99999" step="1" value="10">
|
||||||
|
</div>
|
||||||
|
<label>minutes</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-group">
|
||||||
|
<label>Available eras: </label>
|
||||||
|
|
||||||
|
<div id="command-mode-era-options" class="ol-select">
|
||||||
|
<div class="ol-select-value">Select eras</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where all the available era buttons will be shown-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-group">
|
||||||
|
<h4>Spawn points</h4>
|
||||||
|
<hr>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-group">
|
||||||
|
<label>Blue spawn points: </label>
|
||||||
|
<div id="blue-spawn-points" class="ol-text-input">
|
||||||
|
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-group">
|
||||||
|
<label>Red spawn points: </label>
|
||||||
|
<div id="red-spawn-points" class="ol-text-input">
|
||||||
|
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-dialog-footer ol-group">
|
||||||
|
<button class="ol-button-apply" data-on-click="applycommandModeOptions">Apply</button>
|
||||||
|
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
5
client/views/panels/logpanel.ejs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<div id="log-panel" oncontextmenu="return false;">
|
||||||
|
<div class="ol-panel" data-on-click="toggleLogPanel"><div id="log-panel-header">Server log</div><img src="/resources/theme/images/icons/chevron-down.svg" inject-svg></div>
|
||||||
|
<div class="ol-panel ol-scrollable closed">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
10
client/views/panels/serverstatus.ejs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<div id="server-status-panel" class="ol-panel" oncontextmenu="return false;">
|
||||||
|
<dl class="ol-data-grid">
|
||||||
|
<dt>Server frame rate:</dt>
|
||||||
|
<dd id="server-frame-rate"></dd>
|
||||||
|
</dl>
|
||||||
|
<dl class="ol-data-grid">
|
||||||
|
<dt>Olympus load:</dt>
|
||||||
|
<dd id="server-load"></dd>
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
6
client/views/toolbars/commandmode.ejs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<nav id="command-mode-toolbar" class="ol-panel hide" oncontextmenu="return false;">
|
||||||
|
<span id="command-mode"></span>
|
||||||
|
<div id="spawn-points-container">Spawn points<span id="spawn-points"></span></div>
|
||||||
|
<span id="command-mode-phase"></span>
|
||||||
|
<button id="command-mode-settings-button" class="ol-button" data-on-click="showCommandModeDialog"><img src="/resources/theme/images/icons/gears-solid.svg" inject-svg>Settings</button>
|
||||||
|
</nav>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<div class="ol-select-options">
|
<div class="ol-select-options">
|
||||||
<div id="toolbar-summary">
|
<div id="toolbar-summary">
|
||||||
<h3>DCS Olympus</h3>
|
<h3>DCS Olympus</h3>
|
||||||
<div class="accent-green app-version-number">version v0.3.0</div>
|
<div class="accent-green app-version-number">version v0.4.0-alpha</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<a href="https://www.discord.com" target="_blank">Discord</a>
|
<a href="https://www.discord.com" target="_blank">Discord</a>
|
||||||
@@ -14,53 +14,64 @@
|
|||||||
<div>
|
<div>
|
||||||
<a href="https://github.com/Pax1601/DCSOlympus" target="_blank">Github</a>
|
<a href="https://github.com/Pax1601/DCSOlympus" target="_blank">Github</a>
|
||||||
</div>
|
</div>
|
||||||
|
<div data-on-click="exportToFile">
|
||||||
|
<button>Export to file</button>
|
||||||
|
</div>
|
||||||
|
<div data-on-click="importFromFile">
|
||||||
|
<button>Import from file</button>
|
||||||
|
</div>
|
||||||
<div data-on-click="reloadPage">
|
<div data-on-click="reloadPage">
|
||||||
<a href="" target="_blank" data-on-click="reloadPage">Restart Olympus</a>
|
<a href="" target="_blank" data-on-click="reloadPage">Restart Olympus</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="map-type" class="ol-select">
|
<div class="ol-group">
|
||||||
<div class="ol-select-value map-source-dropdown">
|
<div id="map-type" class="ol-select">
|
||||||
<span>ArcGIS Satellite</span>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
<div class="ol-select-options">
|
|
||||||
<!-- Here the available map sources will be listed-->
|
<div id="map-visibility-options" class="ol-select">
|
||||||
|
<div class="ol-select-value"><img src="resources/theme/images/icons/eye-solid.svg" inject-svg>Options</div>
|
||||||
|
<div class="ol-select-options">
|
||||||
|
<!-- This is where the advanced visibility options will be listed -->
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="unit-visibility-control" class="ol-group">
|
<div id="unit-visibility-control" class="ol-group ol-navbar-buttons-group">
|
||||||
<!-- Here the available visibility controls will be listed -->
|
<!-- Here the available visibility controls will be listed -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="coalition-visibility-control" class="ol-group ol-group-button-toggle">
|
<div id="coalition-visibility-control" class="ol-group ol-group-button-toggle">
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="coalition-visibility-control-blue" data-on-click="toggleCoalitionVisibility"
|
<button id="coalition-visibility-control-blue" data-on-click="toggleCoalitionVisibility"
|
||||||
data-on-click-params='{ "coalition": "blue" }'>View <span class="accent-bluefor">BLUEFOR</span></button>
|
data-on-click-params='{ "coalition": "blue" }'><span class="accent-bluefor">BLUEFOR</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="coalition-visibility-control-red" data-on-click="toggleCoalitionVisibility"
|
<button id="coalition-visibility-control-red" data-on-click="toggleCoalitionVisibility"
|
||||||
data-on-click-params='{ "coalition": "red" }'>View <span class="accent-redfor">REDFOR</span></button>
|
data-on-click-params='{ "coalition": "red" }'><span class="accent-redfor">REDFOR</span></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button id="coalition-visibility-control-neutral" data-on-click="toggleCoalitionVisibility"
|
<button id="coalition-visibility-control-neutral" data-on-click="toggleCoalitionVisibility"
|
||||||
data-on-click-params='{ "coalition": "neutral" }'>View <span
|
data-on-click-params='{ "coalition": "neutral" }'><span class="accent-neutral">NEUTRAL</span></button>
|
||||||
class="accent-neutral">NEUTRAL</span></button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!--
|
||||||
<div id="atc-navbar-control" class="ol-group-container" data-feature-switch="atc">
|
<div id="atc-navbar-control" class="ol-group-container ol-navbar-buttons-group" data-feature-switch="atc">
|
||||||
<div class="ol-group-header">ATC</div>
|
|
||||||
<div class="ol-group">
|
<div class="ol-group">
|
||||||
<button data-on-click="toggleElements"
|
<button data-on-click="toggleElements"
|
||||||
data-on-click-params='{"selector": "#strip-board-ground"}'>GND</button>
|
data-on-click-params='{"selector": "#strip-board-ground"}' class="off"><img src="resources/theme/images/buttons/tools/ground.svg" inject-svg></button>
|
||||||
<button data-on-click="toggleElements"
|
<button data-on-click="toggleElements"
|
||||||
data-on-click-params='{"selector": "#strip-board-tower"}'>TWR</button>
|
data-on-click-params='{"selector": "#strip-board-tower"}' class="off"><img src="resources/theme/images/buttons/tools/tower.svg" inject-svg></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
</nav>
|
</nav>
|
||||||
@@ -1047,7 +1047,7 @@
|
|||||||
<dt>Open air</dt>
|
<dt>Open air</dt>
|
||||||
<dd>5</dd>
|
<dd>5</dd>
|
||||||
</dl>
|
</dl>
|
||||||
<button id="spawn-airbase-aircraft-button" data-active-coalition="red" title="Spawn aircraft" data-on-click="contextMenuSpawnAirbase" class="deploy-unit-button">Spawn</button>
|
<button id="spawn-airbase-aircraft-button" data-coalition="red" title="Spawn aircraft" data-on-click="contextMenuSpawnAirbase" class="deploy-unit-button">Spawn</button>
|
||||||
<button id="land-here-button" title="Land here" data-on-click="contextMenuLandAirbase" class="hide">Land here</button>
|
<button id="land-here-button" title="Land here" data-on-click="contextMenuLandAirbase" class="hide">Land here</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#define nwjsFolder "C:\Users\dpass\Documents\nwjs\"
|
#define nwjsFolder "C:\Users\dpass\Documents\nwjs\"
|
||||||
#define version "v0.3.0-alpha"
|
#define version "v0.4.0-alpha"
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppName=DCS Olympus
|
AppName=DCS Olympus
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ declare_plugin(self_ID,
|
|||||||
shortName = "Olympus",
|
shortName = "Olympus",
|
||||||
fileMenuName = "Olympus",
|
fileMenuName = "Olympus",
|
||||||
|
|
||||||
version = "0.1.1-alpha",
|
version = "v0.4.0-alpha",
|
||||||
state = "installed",
|
state = "installed",
|
||||||
developerName= "DCS Refugees 767 squadron",
|
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."),
|
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."),
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
"port": 30000
|
"port": 30000
|
||||||
},
|
},
|
||||||
"authentication": {
|
"authentication": {
|
||||||
"password": "password"
|
"gameMasterPassword": "password",
|
||||||
|
"blueCommanderPassword": "bluepassword",
|
||||||
|
"redCommanderPassword": "redpassword"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
local version = "v0.3.0-alpha"
|
local version = "v0.4.0-alpha"
|
||||||
|
|
||||||
local debug = false
|
local debug = false
|
||||||
|
|
||||||
Olympus.unitCounter = 1
|
Olympus.unitCounter = 1
|
||||||
Olympus.payloadRegistry = {}
|
Olympus.payloadRegistry = {}
|
||||||
Olympus.groupIndex = 0
|
Olympus.groupIndex = 0
|
||||||
Olympus.groupStep = 40
|
Olympus.groupStep = 5
|
||||||
|
|
||||||
Olympus.OlympusDLL = nil
|
Olympus.OlympusDLL = nil
|
||||||
Olympus.DLLsloaded = false
|
Olympus.DLLsloaded = false
|
||||||
Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\'
|
Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\'
|
||||||
|
Olympus.log = mist.Logger:new("Olympus", 'info')
|
||||||
|
|
||||||
|
Olympus.missionData = {}
|
||||||
|
Olympus.unitsData = {}
|
||||||
|
Olympus.unitNames = {}
|
||||||
|
|
||||||
|
Olympus.missionStartTime = DCS.getRealTime()
|
||||||
|
|
||||||
function Olympus.debug(message, displayFor)
|
function Olympus.debug(message, displayFor)
|
||||||
if debug == true then
|
if debug == true then
|
||||||
@@ -252,7 +259,51 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
|
|||||||
if groupCon then
|
if groupCon then
|
||||||
groupCon:setTask(missionTask)
|
groupCon:setTask(missionTask)
|
||||||
end
|
end
|
||||||
Olympus.debug("Olympus.move executed successfully on a Aircraft", 2)
|
Olympus.debug("Olympus.move executed successfully on Aircraft", 2)
|
||||||
|
elseif category == "Helicopter" then
|
||||||
|
local startPoint = mist.getLeadPos(group)
|
||||||
|
local endPoint = coord.LLtoLO(lat, lng, 0)
|
||||||
|
|
||||||
|
if altitudeType == "AGL" then
|
||||||
|
altitude = land.getHeight({x = endPoint.x, y = endPoint.z}) + altitude
|
||||||
|
end
|
||||||
|
|
||||||
|
local path = {}
|
||||||
|
if taskOptions and taskOptions['id'] == 'Land' then
|
||||||
|
path = {
|
||||||
|
[1] = mist.heli.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
|
||||||
|
[2] = mist.heli.buildWP(endPoint, landing, speed, 0, 'AGL')
|
||||||
|
}
|
||||||
|
else
|
||||||
|
path = {
|
||||||
|
[1] = mist.heli.buildWP(startPoint, turningPoint, speed, altitude, 'BARO'),
|
||||||
|
[2] = mist.heli.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If a task exists assign it to the controller
|
||||||
|
if taskOptions then
|
||||||
|
local task = Olympus.buildEnrouteTask(taskOptions)
|
||||||
|
if task then
|
||||||
|
path[1].task = task
|
||||||
|
path[2].task = task
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Assign the mission task to the controller
|
||||||
|
local missionTask = {
|
||||||
|
id = 'Mission',
|
||||||
|
params = {
|
||||||
|
route = {
|
||||||
|
points = mist.utils.deepCopy(path),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
local groupCon = group:getController()
|
||||||
|
if groupCon then
|
||||||
|
groupCon:setTask(missionTask)
|
||||||
|
end
|
||||||
|
Olympus.debug("Olympus.move executed successfully on Helicopter", 2)
|
||||||
elseif category == "GroundUnit" then
|
elseif category == "GroundUnit" then
|
||||||
vars =
|
vars =
|
||||||
{
|
{
|
||||||
@@ -270,7 +321,17 @@ function Olympus.move(groupName, lat, lng, altitude, altitudeType, speed, speedT
|
|||||||
end
|
end
|
||||||
|
|
||||||
mist.groupToRandomPoint(vars)
|
mist.groupToRandomPoint(vars)
|
||||||
Olympus.debug("Olympus.move executed succesfully on a ground unit", 2)
|
Olympus.debug("Olympus.move executed succesfully on GroundUnit", 2)
|
||||||
|
elseif category == "NavyUnit" then
|
||||||
|
vars =
|
||||||
|
{
|
||||||
|
group = group,
|
||||||
|
point = coord.LLtoLO(lat, lng, 0),
|
||||||
|
heading = 0,
|
||||||
|
speed = speed
|
||||||
|
}
|
||||||
|
mist.groupToRandomPoint(vars)
|
||||||
|
Olympus.debug("Olympus.move executed succesfully on NavyUnit", 2)
|
||||||
else
|
else
|
||||||
Olympus.debug("Olympus.move not implemented yet for " .. category, 2)
|
Olympus.debug("Olympus.move not implemented yet for " .. category, 2)
|
||||||
end
|
end
|
||||||
@@ -303,105 +364,84 @@ function Olympus.explosion(intensity, lat, lng)
|
|||||||
trigger.action.explosion(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), intensity)
|
trigger.action.explosion(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), intensity)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Spawns a single ground unit
|
-- Spawns a new unit or group
|
||||||
function Olympus.spawnGroundUnit(coalition, unitType, lat, lng)
|
function Olympus.spawnUnits(spawnTable)
|
||||||
Olympus.debug("Olympus.spawnGroundUnit " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..")", 2)
|
Olympus.debug("Olympus.spawnUnits " .. Olympus.serializeTable(spawnTable), 2)
|
||||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
|
||||||
|
|
||||||
local unitTable = {}
|
local unitTable = nil
|
||||||
|
local route = nil
|
||||||
|
local category = nil
|
||||||
|
|
||||||
if Olympus.hasKey(templates, unitType) then
|
if spawnTable.category == 'Aircraft' then
|
||||||
for idx, value in pairs(templates[unitType].units) do
|
unitTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
||||||
unitTable[#unitTable + 1] = {
|
route = Olympus.generateAirUnitsRoute(spawnTable)
|
||||||
["type"] = value.name,
|
category = 'plane'
|
||||||
["x"] = spawnLocation.x + value.dx,
|
elseif spawnTable.category == 'Helicopter' then
|
||||||
["y"] = spawnLocation.z + value.dy,
|
unitTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
||||||
["playerCanDrive"] = true,
|
route = Olympus.generateAirUnitsRoute(spawnTable)
|
||||||
["heading"] = 0,
|
category = 'helicopter'
|
||||||
["skill"] = "High"
|
elseif spawnTable.category == 'GroundUnit' then
|
||||||
}
|
unitTable = Olympus.generateGroundUnitsTable(spawnTable.units)
|
||||||
end
|
category = 'vehicle'
|
||||||
else
|
elseif spawnTable.category == 'NavyUnit' then
|
||||||
unitTable =
|
unitTable = Olympus.generateNavyUnitsTable(spawnTable.units)
|
||||||
{
|
category = 'ship'
|
||||||
[1] =
|
|
||||||
{
|
|
||||||
["type"] = unitType,
|
|
||||||
["x"] = spawnLocation.x,
|
|
||||||
["y"] = spawnLocation.z,
|
|
||||||
["playerCanDrive"] = true,
|
|
||||||
["heading"] = 0,
|
|
||||||
["skill"] = "High"
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local countryID = Olympus.getCountryIDByCoalition(coalition)
|
Olympus.debug(Olympus.serializeTable(unitTable), 5)
|
||||||
|
|
||||||
|
local countryID = Olympus.getCountryIDByCoalition(spawnTable.coalition)
|
||||||
local vars =
|
local vars =
|
||||||
{
|
{
|
||||||
units = unitTable,
|
units = unitTable,
|
||||||
country = countryID,
|
country = countryID,
|
||||||
category = 'vehicle',
|
category = category,
|
||||||
|
route = route,
|
||||||
name = "Olympus-" .. Olympus.unitCounter,
|
name = "Olympus-" .. Olympus.unitCounter,
|
||||||
|
task = 'CAP'
|
||||||
}
|
}
|
||||||
mist.dynAdd(vars)
|
mist.dynAdd(vars)
|
||||||
|
|
||||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
Olympus.unitCounter = Olympus.unitCounter + 1
|
||||||
Olympus.debug("Olympus.spawnGround completed succesfully", 2)
|
Olympus.debug("Olympus.spawnUnits completed succesfully", 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Spawns a single aircraft. Spawn options are:
|
-- Generates unit table for a air unit.
|
||||||
-- payloadName: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
|
function Olympus.generateAirUnitsTable(units)
|
||||||
-- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase
|
local unitTable = {}
|
||||||
-- payload: a table, if present the unit will receive this specific payload. Overrides payloadName
|
for idx, unit in pairs(units) do
|
||||||
function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions)
|
local loadout = unit.loadout -- loadout: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
|
||||||
local payloadName = spawnOptions["payloadName"]
|
local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides loadout
|
||||||
local airbaseName = spawnOptions["airbaseName"]
|
|
||||||
local payload = spawnOptions["payload"]
|
|
||||||
|
|
||||||
Olympus.debug("Olympus.spawnAircraft " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..", " .. alt .. ")", 2)
|
if payload == nil then
|
||||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
if loadout and loadout ~= "" and Olympus.unitPayloads[unit.unitType][loadout] then
|
||||||
|
payload = Olympus.unitPayloads[unit.unitType][loadout]
|
||||||
if payload == nil then
|
else
|
||||||
if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] then
|
payload = {}
|
||||||
payload = Olympus.unitPayloads[unitType][payloadName]
|
end
|
||||||
else
|
|
||||||
payload = {}
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
local countryID = Olympus.getCountryIDByCoalition(coalition)
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
||||||
|
unitTable[#unitTable + 1] =
|
||||||
local unitTable =
|
|
||||||
{
|
|
||||||
[1] =
|
|
||||||
{
|
{
|
||||||
["type"] = unitType,
|
["type"] = unit.unitType,
|
||||||
["x"] = spawnLocation.x,
|
["x"] = spawnLocation.x,
|
||||||
["y"] = spawnLocation.z,
|
["y"] = spawnLocation.z,
|
||||||
["alt"] = alt,
|
["alt"] = unit.alt,
|
||||||
["alt_type"] = "BARO",
|
["alt_type"] = "BARO",
|
||||||
["skill"] = "Excellent",
|
["skill"] = "Excellent",
|
||||||
["payload"] =
|
["payload"] = { ["pylons"] = payload, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100, },
|
||||||
{
|
|
||||||
["pylons"] = payload,
|
|
||||||
["fuel"] = 999999,
|
|
||||||
["flare"] = 60,
|
|
||||||
["ammo_type"] = 1,
|
|
||||||
["chaff"] = 60,
|
|
||||||
["gun"] = 100,
|
|
||||||
},
|
|
||||||
["heading"] = 0,
|
["heading"] = 0,
|
||||||
["callsign"] =
|
["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitTable + 1 },
|
||||||
{
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1
|
||||||
[1] = 1,
|
}
|
||||||
[2] = 1,
|
end
|
||||||
[3] = 1,
|
return unitTable
|
||||||
["name"] = "Olympus" .. Olympus.unitCounter,
|
end
|
||||||
},
|
|
||||||
["name"] = "Olympus-" .. Olympus.unitCounter
|
function Olympus.generateAirUnitsRoute(spawnTable)
|
||||||
},
|
local airbaseName = spawnTable.airbaseName -- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase
|
||||||
}
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(spawnTable.units[1].lat, spawnTable.units[1].lng, 0))
|
||||||
|
|
||||||
-- If a airbase is provided the first waypoint is set as a From runway takeoff.
|
-- If a airbase is provided the first waypoint is set as a From runway takeoff.
|
||||||
local route = {}
|
local route = {}
|
||||||
@@ -416,10 +456,9 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions)
|
|||||||
[1] =
|
[1] =
|
||||||
{
|
{
|
||||||
["action"] = "From Parking Area Hot",
|
["action"] = "From Parking Area Hot",
|
||||||
["task"] =
|
["tasks"] = {
|
||||||
{
|
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
||||||
["id"] = "ComboTask",
|
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
||||||
["params"] = {["tasks"] = {},},
|
|
||||||
},
|
},
|
||||||
["type"] = "TakeOffParkingHot",
|
["type"] = "TakeOffParkingHot",
|
||||||
["ETA"] = 0,
|
["ETA"] = 0,
|
||||||
@@ -442,69 +481,86 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions)
|
|||||||
{
|
{
|
||||||
["alt"] = alt,
|
["alt"] = alt,
|
||||||
["alt_type"] = "BARO",
|
["alt_type"] = "BARO",
|
||||||
["task"] =
|
["tasks"] = {
|
||||||
{
|
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
||||||
["id"] = "ComboTask",
|
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
||||||
["params"] =
|
|
||||||
{
|
|
||||||
["tasks"] =
|
|
||||||
{
|
|
||||||
[1] =
|
|
||||||
{
|
|
||||||
["number"] = 1,
|
|
||||||
["auto"] = true,
|
|
||||||
["id"] = "WrappedAction",
|
|
||||||
["enabled"] = true,
|
|
||||||
["params"] =
|
|
||||||
{
|
|
||||||
["action"] =
|
|
||||||
{
|
|
||||||
["id"] = "EPLRS",
|
|
||||||
["params"] =
|
|
||||||
{
|
|
||||||
["value"] = true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[2] =
|
|
||||||
{
|
|
||||||
["number"] = 2,
|
|
||||||
["auto"] = false,
|
|
||||||
["id"] = "Orbit",
|
|
||||||
["enabled"] = true,
|
|
||||||
["params"] =
|
|
||||||
{
|
|
||||||
["pattern"] = "Circle"
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
["type"] = "Turning Point",
|
["type"] = "Turning Point",
|
||||||
["x"] = spawnLocation.x,
|
["x"] = spawnLocation.x,
|
||||||
["y"] = spawnLocation.z,
|
["y"] = spawnLocation.z,
|
||||||
}, -- end of [1]
|
},
|
||||||
}, -- end of ["points"]
|
},
|
||||||
} -- end of ["route"]
|
}
|
||||||
|
end
|
||||||
|
return route
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Generates ground units table, either single or from template
|
||||||
|
function Olympus.generateGroundUnitsTable(units)
|
||||||
|
local unitTable = {}
|
||||||
|
for idx, unit in pairs(units) do
|
||||||
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
||||||
|
if Olympus.hasKey(templates, unit.unitType) then
|
||||||
|
for idx, value in pairs(templates[unit.unitType].units) do
|
||||||
|
unitTable[#unitTable + 1] =
|
||||||
|
{
|
||||||
|
["type"] = value.name,
|
||||||
|
["x"] = spawnLocation.x + value.dx,
|
||||||
|
["y"] = spawnLocation.z + value.dy,
|
||||||
|
["heading"] = 0,
|
||||||
|
["skill"] = "High",
|
||||||
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1
|
||||||
|
}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
unitTable[#unitTable + 1] =
|
||||||
|
{
|
||||||
|
["type"] = unit.unitType,
|
||||||
|
["x"] = spawnLocation.x,
|
||||||
|
["y"] = spawnLocation.z,
|
||||||
|
["heading"] = 0,
|
||||||
|
["skill"] = "High",
|
||||||
|
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1
|
||||||
|
}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local vars =
|
return unitTable
|
||||||
{
|
end
|
||||||
units = unitTable,
|
|
||||||
country = countryID,
|
|
||||||
category = 'airplane',
|
|
||||||
name = "Olympus-" .. Olympus.unitCounter,
|
|
||||||
route = route,
|
|
||||||
task = 'CAP',
|
|
||||||
}
|
|
||||||
|
|
||||||
local newGroup = mist.dynAdd(vars)
|
-- Generates navy units table, either single or from template
|
||||||
|
function Olympus.generateNavyUnitsTable(units)
|
||||||
|
local unitTable = {}
|
||||||
|
for idx, unit in pairs(units) do
|
||||||
|
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
||||||
|
if Olympus.hasKey(templates, unit.unitType) then
|
||||||
|
for idx, value in pairs(templates[unit.unitType].units) do
|
||||||
|
unitTable[#unitTable + 1] =
|
||||||
|
{
|
||||||
|
["type"] = value.name,
|
||||||
|
["x"] = spawnLocation.x + value.dx,
|
||||||
|
["y"] = spawnLocation.z + value.dy,
|
||||||
|
["heading"] = 0,
|
||||||
|
["skill"] = "High",
|
||||||
|
["name"] = "NavyUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1,
|
||||||
|
["transportable"] = { ["randomTransportable"] = false }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
else
|
||||||
|
unitTable[#unitTable + 1] =
|
||||||
|
{
|
||||||
|
["type"] = unit.unitType,
|
||||||
|
["x"] = spawnLocation.x,
|
||||||
|
["y"] = spawnLocation.z,
|
||||||
|
["heading"] = 0,
|
||||||
|
["skill"] = "High",
|
||||||
|
["name"] = "NavyUnit-" .. Olympus.unitCounter .. "-" .. #unitTable + 1,
|
||||||
|
["transportable"] = { ["randomTransportable"] = false }
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Save the payload to be reused in case the unit is cloned. TODO: save by ID not by name (it works but I like consistency)
|
return unitTable
|
||||||
Olympus.payloadRegistry[vars.name] = payload
|
|
||||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
|
||||||
Olympus.debug("Olympus.spawnAir completed successfully", 2)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units.
|
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units.
|
||||||
@@ -513,15 +569,21 @@ function Olympus.clone(ID, lat, lng, category)
|
|||||||
local unit = Olympus.getUnitByID(ID)
|
local unit = Olympus.getUnitByID(ID)
|
||||||
if unit then
|
if unit then
|
||||||
local coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition())
|
local coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition())
|
||||||
|
-- TODO: understand category in this script
|
||||||
if category == "Aircraft" then
|
local spawnTable = {
|
||||||
local spawnOptions = {
|
coalition = coalition,
|
||||||
payload = Olympus.payloadRegistry[unit:getName()]
|
category = category,
|
||||||
|
units = {
|
||||||
|
[1] = {
|
||||||
|
lat = lat,
|
||||||
|
lng = lng,
|
||||||
|
alt = unit:getPoint().y,
|
||||||
|
unitType = unit:getTypeName(),
|
||||||
|
payload = Olympus.payloadRegistry[unit:getName()]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Olympus.spawnAircraft(coalition, unit:getTypeName(), lat, lng, unit:getPoint().y, spawnOptions)
|
}
|
||||||
elseif category == "GroundUnit" then
|
Olympus.spawnUnits(spawnTable)
|
||||||
Olympus.spawnGroundUnit(coalition, unit:getTypeName(), lat, lng)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
Olympus.debug("Olympus.clone completed successfully", 2)
|
Olympus.debug("Olympus.clone completed successfully", 2)
|
||||||
end
|
end
|
||||||
@@ -650,22 +712,9 @@ function Olympus.hasKey(tab, key)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
function Olympus.setMissionData(arg, time)
|
function Olympus.setUnitsData(arg, time)
|
||||||
local missionData = {}
|
-- Units data
|
||||||
|
local units = {}
|
||||||
-- Bullseye data
|
|
||||||
local bullseyes = {}
|
|
||||||
for i = 0, 2 do
|
|
||||||
local bullseyeVec3 = coalition.getMainRefPoint(i)
|
|
||||||
local bullseyeLatitude, bullseyeLongitude, bullseyeAltitude = coord.LOtoLL(bullseyeVec3)
|
|
||||||
bullseyes[i] = {}
|
|
||||||
bullseyes[i]["latitude"] = bullseyeLatitude
|
|
||||||
bullseyes[i]["longitude"] = bullseyeLongitude
|
|
||||||
bullseyes[i]["coalition"] = Olympus.getCoalitionByCoalitionID(i)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Units tactical data
|
|
||||||
local unitsData = {}
|
|
||||||
|
|
||||||
local startIndex = Olympus.groupIndex
|
local startIndex = Olympus.groupIndex
|
||||||
local endIndex = startIndex + Olympus.groupStep
|
local endIndex = startIndex + Olympus.groupStep
|
||||||
@@ -680,31 +729,74 @@ function Olympus.setMissionData(arg, time)
|
|||||||
-- Get the targets detected by the group controller
|
-- Get the targets detected by the group controller
|
||||||
local controller = group:getController()
|
local controller = group:getController()
|
||||||
local controllerTargets = controller:getDetectedTargets()
|
local controllerTargets = controller:getDetectedTargets()
|
||||||
|
local contacts = {}
|
||||||
|
for i, target in ipairs(controllerTargets) do
|
||||||
|
for det, enum in pairs(Controller.Detection) do
|
||||||
|
if target.object ~= nil then
|
||||||
|
target["detectionMethod"] = det
|
||||||
|
contacts[#contacts + 1] = target
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Update the units position
|
||||||
for index, unit in pairs(group:getUnits()) do
|
for index, unit in pairs(group:getUnits()) do
|
||||||
local unitController = unit:getController()
|
local unitController = unit:getController()
|
||||||
local table = {}
|
local table = {}
|
||||||
table["contacts"] = {}
|
table["category"] = "None"
|
||||||
|
|
||||||
for i, target in ipairs(controllerTargets) do
|
-- Get the object category in Olympus name
|
||||||
for det, enum in pairs(Controller.Detection) do
|
local objectCategory = unit:getCategory()
|
||||||
if target.object ~= nil then
|
if objectCategory == Object.Category.UNIT then
|
||||||
local detected = unitController:isTargetDetected(target.object, enum)
|
if unit:getDesc().category == Unit.Category.AIRPLANE then
|
||||||
|
table["category"] = "Aircraft"
|
||||||
if detected then
|
elseif unit:getDesc().category == Unit.Category.HELICOPTER then
|
||||||
target["detectionMethod"] = det
|
table["category"] = "Helicopter"
|
||||||
table["contacts"][#table["contacts"] + 1] = target
|
elseif unit:getDesc().category == Unit.Category.GROUND_UNIT then
|
||||||
end
|
table["category"] = "GroundUnit"
|
||||||
end
|
elseif unit:getDesc().category == Unit.Category.SHIP then
|
||||||
|
table["category"] = "NavyUnit"
|
||||||
|
end
|
||||||
|
elseif objectCategory == Object.Category.WEAPON then
|
||||||
|
if unit:getDesc().category == Weapon.Category.MISSILE then
|
||||||
|
table["category"] = "Missile"
|
||||||
|
elseif unit:getDesc().category == Weapon.Category.ROCKET then
|
||||||
|
table["category"] = "Missile"
|
||||||
|
elseif unit:getDesc().category == Unit.Category.BOMB then
|
||||||
|
table["category"] = "Bomb"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table["hasTask"] = controller:hasTask()
|
-- If the category is handled by Olympus, get the data
|
||||||
|
if table["category"] ~= "None" then
|
||||||
|
-- Compute unit position and heading
|
||||||
|
local lat, lng, alt = coord.LOtoLL(unit:getPoint())
|
||||||
|
local position = unit:getPosition()
|
||||||
|
local heading = math.atan2( position.x.z, position.x.x )
|
||||||
|
|
||||||
table["ammo"] = unit:getAmmo()
|
-- Fill the data table
|
||||||
table["fuel"] = unit:getFuel()
|
table["name"] = unit:getTypeName()
|
||||||
table["life"] = unit:getLife() / unit:getLife0()
|
table["unitName"] = unit:getName()
|
||||||
unitsData[unit:getObjectID()] = table
|
table["groupName"] = group:getName()
|
||||||
|
table["isHuman"] = (unit:getPlayerName() ~= nil)
|
||||||
|
table["coalitionID"] = unit:getCoalition()
|
||||||
|
table["hasTask"] = controller:hasTask()
|
||||||
|
table["ammo"] = unit:getAmmo() --TODO remove a lot of stuff we don't really need
|
||||||
|
table["fuel"] = unit:getFuel()
|
||||||
|
table["life"] = unit:getLife() / unit:getLife0()
|
||||||
|
table["contacts"] = contacts
|
||||||
|
table["position"] = {}
|
||||||
|
table["position"]["lat"] = lat
|
||||||
|
table["position"]["lng"] = lng
|
||||||
|
table["position"]["alt"] = alt
|
||||||
|
table["speed"] = mist.vec.mag(unit:getVelocity())
|
||||||
|
table["heading"] = heading
|
||||||
|
table["country"] = unit:getCountry()
|
||||||
|
table["isAlive"] = (unit:getLife() > 1) and unit:isExist()
|
||||||
|
|
||||||
|
units[unit:getObjectID()] = table
|
||||||
|
Olympus.unitNames[unit:getObjectID()] = unit:getName() -- Used to find what units are dead, since they will not be in mist.DBs.groupsByName
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -720,6 +812,34 @@ function Olympus.setMissionData(arg, time)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- All the units that can't be retrieved by getByName are dead
|
||||||
|
for ID, name in pairs(Olympus.unitNames) do
|
||||||
|
local unit = Unit.getByName(name)
|
||||||
|
if unit == nil then
|
||||||
|
units[ID] = {isAlive = false}
|
||||||
|
Olympus.unitNames[ID] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Assemble unitsData table
|
||||||
|
Olympus.unitsData["units"] = units
|
||||||
|
|
||||||
|
Olympus.OlympusDLL.setUnitsData()
|
||||||
|
return time + 0.05
|
||||||
|
end
|
||||||
|
|
||||||
|
function Olympus.setMissionData(arg, time)
|
||||||
|
-- Bullseye data
|
||||||
|
local bullseyes = {}
|
||||||
|
for i = 0, 2 do
|
||||||
|
local bullseyeVec3 = coalition.getMainRefPoint(i)
|
||||||
|
local bullseyeLatitude, bullseyeLongitude, bullseyeAltitude = coord.LOtoLL(bullseyeVec3)
|
||||||
|
bullseyes[i] = {}
|
||||||
|
bullseyes[i]["latitude"] = bullseyeLatitude
|
||||||
|
bullseyes[i]["longitude"] = bullseyeLongitude
|
||||||
|
bullseyes[i]["coalition"] = Olympus.getCoalitionByCoalitionID(i)
|
||||||
|
end
|
||||||
|
|
||||||
-- Airbases data
|
-- Airbases data
|
||||||
local base = world.getAirbases()
|
local base = world.getAirbases()
|
||||||
local airbases = {}
|
local airbases = {}
|
||||||
@@ -736,20 +856,21 @@ function Olympus.setMissionData(arg, time)
|
|||||||
airbases[i] = info
|
airbases[i] = info
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Mission
|
||||||
local mission = {}
|
local mission = {}
|
||||||
mission.theatre = env.mission.theatre
|
mission.theatre = env.mission.theatre
|
||||||
mission.elapsedTime = DCS.getRealTime()
|
mission.dateAndTime = {
|
||||||
mission.time = mist.time.getDHMS(timer.getAbsTime())
|
["elapsedTime"] = DCS.getRealTime() - Olympus.missionStartTime,
|
||||||
mission.startTime = env.mission.start_time
|
["time"] = mist.time.getDHMS(timer.getAbsTime()),
|
||||||
mission.date = env.mission.date
|
["startTime"] = env.mission.start_time,
|
||||||
|
["date"] = env.mission.date
|
||||||
|
}
|
||||||
|
|
||||||
-- Assemble missionData table
|
-- Assemble table
|
||||||
missionData["bullseyes"] = bullseyes
|
Olympus.missionData["bullseyes"] = bullseyes
|
||||||
missionData["unitsData"] = unitsData
|
Olympus.missionData["airbases"] = airbases
|
||||||
missionData["airbases"] = airbases
|
Olympus.missionData["mission"] = mission
|
||||||
missionData["mission"] = mission
|
|
||||||
|
|
||||||
Olympus.missionData = missionData
|
|
||||||
Olympus.OlympusDLL.setMissionData()
|
Olympus.OlympusDLL.setMissionData()
|
||||||
return time + 1
|
return time + 1
|
||||||
end
|
end
|
||||||
@@ -763,6 +884,7 @@ else
|
|||||||
Olympus.notify('Failed to load '..OlympusName, 20)
|
Olympus.notify('Failed to load '..OlympusName, 20)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
timer.scheduleFunction(Olympus.setUnitsData, {}, timer.getTime() + 0.05)
|
||||||
timer.scheduleFunction(Olympus.setMissionData, {}, timer.getTime() + 1)
|
timer.scheduleFunction(Olympus.setMissionData, {}, timer.getTime() + 1)
|
||||||
|
|
||||||
Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true)
|
Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
local version = 'v0.3.0-alpha'
|
local version = 'v0.4.0-alpha'
|
||||||
|
|
||||||
Olympus = {}
|
Olympus = {}
|
||||||
Olympus.OlympusDLL = nil
|
Olympus.OlympusDLL = nil
|
||||||
|
|||||||
@@ -1351,4 +1351,141 @@ templates =
|
|||||||
}, -- end of [18]
|
}, -- end of [18]
|
||||||
}, -- end of ["units"]
|
}, -- end of ["units"]
|
||||||
}, -- end of ["SA-10 SAM Battery"]
|
}, -- end of ["SA-10 SAM Battery"]
|
||||||
|
["SA-5 SAM Battery"] =
|
||||||
|
{
|
||||||
|
["type"] = "vehicle",
|
||||||
|
["name"] = "SA-5 SAM Battery",
|
||||||
|
["country"] = 0,
|
||||||
|
["units"] =
|
||||||
|
{
|
||||||
|
[1] =
|
||||||
|
{
|
||||||
|
["dx"] = 0,
|
||||||
|
["dy"] = 0,
|
||||||
|
["name"] = "RPC_5N62V",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 4.7123889803847,
|
||||||
|
}, -- end of [1]
|
||||||
|
[2] =
|
||||||
|
{
|
||||||
|
["dx"] = 0.69314285699511,
|
||||||
|
["dy"] = 127.97571428004,
|
||||||
|
["name"] = "RLS_19J6",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 0,
|
||||||
|
},
|
||||||
|
[3] =
|
||||||
|
{
|
||||||
|
["dx"] = 83.349983285123,
|
||||||
|
["dy"] = -1.3806866992963,
|
||||||
|
["name"] = "S-200_Launcher",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 3.1415926535898,
|
||||||
|
}, -- end of [5]
|
||||||
|
[4] =
|
||||||
|
{
|
||||||
|
["dx"] = 82.498640577192,
|
||||||
|
["dy"] = 16.104647497996,
|
||||||
|
["name"] = "S-200_Launcher",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 3.3161255787892,
|
||||||
|
}, -- end of [6]
|
||||||
|
[5] =
|
||||||
|
{
|
||||||
|
["dx"] = 82.547616217693,
|
||||||
|
["dy"] = -18.227276489837,
|
||||||
|
["name"] = "S-200_Launcher",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 2.9670597283904,
|
||||||
|
}, -- end of [7]
|
||||||
|
[6] =
|
||||||
|
{
|
||||||
|
["dx"] = -82.640406328603,
|
||||||
|
["dy"] = -0.41562629467808,
|
||||||
|
["name"] = "S-200_Launcher",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 0,
|
||||||
|
}, -- end of [8]
|
||||||
|
[7] =
|
||||||
|
{
|
||||||
|
["dx"] = -81.939684967569,
|
||||||
|
["dy"] = 17.115632734494,
|
||||||
|
["name"] = "S-200_Launcher",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 6.1086523819802,
|
||||||
|
}, -- end of [9]
|
||||||
|
[8] =
|
||||||
|
{
|
||||||
|
["dx"] = -81.939684967569,
|
||||||
|
["dy"] = -17.99454369233,
|
||||||
|
["name"] = "S-200_Launcher",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 0.17453292519943,
|
||||||
|
}, -- end of [10]
|
||||||
|
[9] =
|
||||||
|
{
|
||||||
|
["dx"] = -9.0858776818495,
|
||||||
|
["dy"] = 187.67713509151,
|
||||||
|
["name"] = "generator_5i57",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 1.5707963267949,
|
||||||
|
}, -- end of [11]
|
||||||
|
[10] =
|
||||||
|
{
|
||||||
|
["dx"] = 0.83760223048739,
|
||||||
|
["dy"] = 187.51811292395,
|
||||||
|
["name"] = "generator_5i57",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 1.5707963267949,
|
||||||
|
}, -- end of [12]
|
||||||
|
[11] =
|
||||||
|
{
|
||||||
|
["dx"] = -59.823818980018,
|
||||||
|
["dy"] = 168.63468487991,
|
||||||
|
["name"] = "ATZ-5",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 0,
|
||||||
|
}, -- end of [13]
|
||||||
|
[12] =
|
||||||
|
{
|
||||||
|
["dx"] = -59.823818980018,
|
||||||
|
["dy"] = 179.2654343833,
|
||||||
|
["name"] = "ATZ-5",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 0,
|
||||||
|
}, -- end of [14]
|
||||||
|
[13] =
|
||||||
|
{
|
||||||
|
["dx"] = 20.947679329896,
|
||||||
|
["dy"] = -62.811427216162,
|
||||||
|
["name"] = "GAZ-66",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 1.5707963267949,
|
||||||
|
}, -- end of [15]
|
||||||
|
[14] =
|
||||||
|
{
|
||||||
|
["dx"] = 66.751355714747,
|
||||||
|
["dy"] = 151.35592090525,
|
||||||
|
["name"] = "ATZ-60_Maz",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 3.9269908169872,
|
||||||
|
}, -- end of [16]
|
||||||
|
[15] =
|
||||||
|
{
|
||||||
|
["dx"] = 59.63926918729,
|
||||||
|
["dy"] = 158.46800743265,
|
||||||
|
["name"] = "ATZ-60_Maz",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 3.9269908169872,
|
||||||
|
}, -- end of [17]
|
||||||
|
[16] =
|
||||||
|
{
|
||||||
|
["dx"] = -16.327227612433,
|
||||||
|
["dy"] = -62.472874663305,
|
||||||
|
["name"] = "KAMAZ Truck",
|
||||||
|
["skill"] = "High",
|
||||||
|
["heading"] = 1.5707963267949,
|
||||||
|
}, -- end of [18]
|
||||||
|
}, -- end of ["units"]
|
||||||
|
} -- end of ["SA-5 SAM Battery"]
|
||||||
} -- end of templates
|
} -- end of templates
|
||||||
|
|||||||
8
src/.vscode/settings.json
vendored
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"*.ejs": "html",
|
|
||||||
"xstring": "cpp",
|
|
||||||
"vector": "cpp",
|
|
||||||
"list": "cpp"
|
|
||||||
}
|
|
||||||
}
|
|
||||||