Compare commits
79 Commits
v0.3.0-alp
...
v0.4.1-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3b4c4ae36 | ||
|
|
fd7f4b9772 | ||
|
|
bac2c430ef | ||
|
|
3f1c9942c3 | ||
|
|
80ed675cbc | ||
|
|
6d434e48a1 | ||
|
|
7961870ac4 | ||
|
|
0150ae9df1 | ||
|
|
875f3ebe68 | ||
|
|
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 logger = require('morgan');
|
||||
var fs = require('fs');
|
||||
var basicAuth = require('express-basic-auth')
|
||||
|
||||
var atcRouter = require('./routes/api/atc');
|
||||
var airbasesRouter = require('./routes/api/airbases');
|
||||
@@ -37,15 +36,7 @@ if (config["server"] != undefined)
|
||||
module.exports = app;
|
||||
|
||||
const DemoDataGenerator = require('./demo.js');
|
||||
var demoDataGenerator = new DemoDataGenerator(10);
|
||||
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' }
|
||||
}))
|
||||
var demoDataGenerator = new DemoDataGenerator(app);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
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-path-drag\\dist\\L.Path.Drag.js .\\public\\javascripts\\L.Path.Drag.js
|
||||
988
client/demo.js
3697
client/package-lock.json
generated
@@ -2,7 +2,7 @@
|
||||
"name": "DCSOlympus",
|
||||
"node-main": "./bin/www",
|
||||
"main": "http://localhost:3000",
|
||||
"version": "v0.3.0-alpha",
|
||||
"version": "v0.4.1-alpha",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"copy": "copy.bat",
|
||||
@@ -10,33 +10,35 @@
|
||||
"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": {
|
||||
"@types/formatcoords": "^1.1.0",
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
"@types/svg-injector": "^0.0.29",
|
||||
"cookie-parser": "~1.4.4",
|
||||
"debug": "~2.6.9",
|
||||
"ejs": "^3.1.8",
|
||||
"express": "~4.16.1",
|
||||
"formatcoords": "^1.1.3",
|
||||
"leaflet": "^1.9.3",
|
||||
"leaflet-control-mini-map": "^0.4.0",
|
||||
"leaflet.nauticscale": "^1.1.0",
|
||||
"morgan": "~1.9.1",
|
||||
"save": "^2.9.0"
|
||||
"save": "^2.9.0",
|
||||
"express-basic-auth": "^1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@tanem/svg-injector": "^10.1.55",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@types/formatcoords": "^1.1.0",
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/gtag.js": "^0.0.12",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
"@types/node": "^18.16.1",
|
||||
"@types/sortablejs": "^1.15.0",
|
||||
"@types/svg-injector": "^0.0.29",
|
||||
"babelify": "^10.0.0",
|
||||
"browserify": "^17.0.0",
|
||||
"concurrently": "^7.6.0",
|
||||
"cp": "^0.2.0",
|
||||
"esmify": "^2.1.1",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
"formatcoords": "^1.1.3",
|
||||
"leaflet": "^1.9.3",
|
||||
"leaflet-control-mini-map": "^0.4.0",
|
||||
"leaflet-path-drag": "*",
|
||||
"leaflet.nauticscale": "^1.1.0",
|
||||
"nodemon": "^2.0.20",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tsify": "^5.0.4",
|
||||
|
||||
6
client/public/javascripts/L.Path.Drag.js
Normal file
@@ -5,13 +5,24 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#primary-toolbar {
|
||||
#toolbar-container {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
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 {
|
||||
@@ -42,6 +53,15 @@
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#server-status-panel {
|
||||
bottom: 20px;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
right: 200px;
|
||||
width: 300px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#mouse-info-panel {
|
||||
bottom: 60px;
|
||||
display: flex;
|
||||
@@ -81,4 +101,17 @@
|
||||
left: 50%;
|
||||
translate: -50% 0%;
|
||||
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);
|
||||
display: flex;
|
||||
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;
|
||||
transform-origin: bottom;
|
||||
translate: 0 -50%;
|
||||
width: var(--unit-aircraft-vvi-width);
|
||||
width: var(--unit-vvi-width);
|
||||
}
|
||||
|
||||
.unit-hotgroup {
|
||||
@@ -100,13 +100,13 @@
|
||||
/*** Fuel indicator ***/
|
||||
[data-object|="unit"] .unit-fuel {
|
||||
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);
|
||||
display: none;
|
||||
height: var(--unit-aircraft-fuel-height);
|
||||
height: var(--unit-fuel-height);
|
||||
position: absolute;
|
||||
translate: var(--unit-aircraft-fuel-x) var(--unit-aircraft-fuel-y);
|
||||
width: var(--unit-aircraft-fuel-width);
|
||||
translate: var(--unit-fuel-x) var(--unit-fuel-y);
|
||||
width: var(--unit-fuel-width);
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-fuel-level {
|
||||
@@ -117,19 +117,19 @@
|
||||
|
||||
/*** Ammo indicator ***/
|
||||
[data-object|="unit"] .unit-ammo {
|
||||
column-gap: var(--unit-aircraft-ammo-spacing);
|
||||
column-gap: var(--unit-ammo-spacing);
|
||||
display: none;
|
||||
height: fit-content;
|
||||
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;
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-ammo>* {
|
||||
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%;
|
||||
padding: var(--unit-aircraft-ammo-radius);
|
||||
padding: var(--unit-ammo-radius);
|
||||
}
|
||||
|
||||
/*** Unit summary ***/
|
||||
@@ -150,7 +150,7 @@
|
||||
1px -1px 0 #000,
|
||||
-1px 1px 0 #000,
|
||||
1px 1px 0 #000;
|
||||
translate: -60px 0;
|
||||
right: 100%;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-summary .unit-callsign:hover {
|
||||
[data-object|="unit"]:hover .unit-summary .unit-callsign{
|
||||
direction: rtl;
|
||||
overflow: visible;
|
||||
}
|
||||
@@ -292,23 +292,27 @@
|
||||
}
|
||||
|
||||
/*** Dead unit ***/
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-selected-spotlight,
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-short-label,
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-vvi,
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup,
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup-id,
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-state,
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-fuel,
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-ammo,
|
||||
[data-object|="unit-aircraft"][data-is-dead]:hover .unit-fuel,
|
||||
[data-object|="unit-aircraft"][data-is-dead]:hover .unit-ammo {
|
||||
[data-object|="unit"][data-is-dead] .unit-selected-spotlight,
|
||||
[data-object|="unit"][data-is-dead] .unit-short-label,
|
||||
[data-object|="unit"][data-is-dead] .unit-vvi,
|
||||
[data-object|="unit"][data-is-dead] .unit-hotgroup,
|
||||
[data-object|="unit"][data-is-dead] .unit-hotgroup-id,
|
||||
[data-object|="unit"][data-is-dead] .unit-state,
|
||||
[data-object|="unit"][data-is-dead] .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead] .unit-ammo,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-ammo {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-object|="unit-aircraft"][data-is-dead] .unit-summary>* {
|
||||
[data-object|="unit"][data-is-dead] .unit-summary>* {
|
||||
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;
|
||||
}
|
||||
|
||||
.ol-temporary-marker {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -3,9 +3,11 @@
|
||||
@import url("atc/unitdatatable.css");
|
||||
@import url("aic/aic.css");
|
||||
@import url("panels/connectionstatus.css");
|
||||
@import url("panels/serverstatus.css");
|
||||
@import url("panels/mouseinfo.css");
|
||||
@import url("panels/unitcontrol.css");
|
||||
@import url("panels/unitinfo.css");
|
||||
@import url("panels/logpanel.css");
|
||||
@import url("other/contextmenus.css");
|
||||
@import url("other/popup.css");
|
||||
@import url("markers/airbase.css");
|
||||
@@ -20,6 +22,7 @@
|
||||
|
||||
html * {
|
||||
font-family: 'Open Sans', sans-serif !important;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body {
|
||||
@@ -38,6 +41,11 @@ body {
|
||||
cursor: none !important;
|
||||
}
|
||||
|
||||
.hidden-cursor * {
|
||||
cursor: none !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -124,7 +132,7 @@ form>div {
|
||||
|
||||
.ol-panel {
|
||||
background-color: var(--background-steel);
|
||||
border-radius: 15px;
|
||||
border-radius: var(--border-radius-md);;
|
||||
box-shadow: 0px 2px 5px #000A;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
@@ -408,7 +416,6 @@ nav.ol-panel> :last-child {
|
||||
|
||||
.ol-panel .ol-group-button-toggle {
|
||||
align-items: center;
|
||||
column-gap: 15px;
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
@@ -421,7 +428,7 @@ nav.ol-panel> :last-child {
|
||||
border: 0;
|
||||
display: flex;
|
||||
justify-items: left;
|
||||
text-indent: 5px;
|
||||
text-indent: 2px;
|
||||
}
|
||||
|
||||
.ol-panel .ol-group-button-toggle button::before {
|
||||
@@ -624,39 +631,39 @@ nav.ol-panel> :last-child {
|
||||
width: 28px;
|
||||
}
|
||||
|
||||
#unit-visibility-control {
|
||||
.ol-navbar-buttons-group {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#unit-visibility-control button {
|
||||
.ol-navbar-buttons-group button {
|
||||
border: none;
|
||||
height: 32px;
|
||||
padding: 0px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
#unit-visibility-control button svg {
|
||||
.ol-navbar-buttons-group button svg {
|
||||
height: 16px;
|
||||
pointer-events: none;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#unit-visibility-control button {
|
||||
.ol-navbar-buttons-group button {
|
||||
background-color: white;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
#unit-visibility-control button.off {
|
||||
.ol-navbar-buttons-group button.off {
|
||||
background-color: transparent;
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
#unit-visibility-control button.off svg * {
|
||||
.ol-navbar-buttons-group button.off svg * {
|
||||
fill: white !important;
|
||||
stroke: white !important;
|
||||
}
|
||||
|
||||
#unit-visibility-control button svg * {
|
||||
.ol-navbar-buttons-group button svg * {
|
||||
fill: var(--background-steel) !important;
|
||||
stroke: var(--background-steel) !important;
|
||||
}
|
||||
@@ -667,10 +674,9 @@ nav.ol-panel> :last-child {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#atc-navbar-control button {
|
||||
background: #ffffff20;
|
||||
border-radius: var(--border-radius-sm);
|
||||
padding: 4px;
|
||||
#atc-navbar-control button svg {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
#roe-buttons-container button,
|
||||
@@ -699,7 +705,7 @@ nav.ol-panel> :last-child {
|
||||
background-image: url("/resources/theme/images/splash/1.png");
|
||||
background-position: 100% 50%;
|
||||
background-size: 60%;
|
||||
border-radius: var(--border-radius-lg);
|
||||
border-radius: var(--border-radius-md);
|
||||
overflow: hidden;
|
||||
width: 1200px;
|
||||
z-index: 99999;
|
||||
@@ -809,16 +815,16 @@ nav.ol-panel> :last-child {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#connection-status {
|
||||
#login-status {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
#connection-status[data-status="connecting"]::before {
|
||||
#login-status[data-status="connecting"]::before {
|
||||
animation: blinker 1s linear infinite;
|
||||
content: "Connecting...";
|
||||
}
|
||||
|
||||
#connection-status[data-status="failed"]::before {
|
||||
#login-status[data-status="failed"]::before {
|
||||
color: var(--primary-red);
|
||||
content: "Incorrect username/password!";
|
||||
}
|
||||
@@ -862,6 +868,130 @@ nav.ol-panel> :last-child {
|
||||
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 {
|
||||
background-image: url("/resources/theme/images/markers/move.svg");
|
||||
height: 52px;
|
||||
@@ -877,6 +1007,33 @@ nav.ol-panel> :last-child {
|
||||
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 {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
@@ -1134,4 +1291,85 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
|
||||
.ol-switch[data-value="undefined"]>.ol-switch-fill::after {
|
||||
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;
|
||||
position: absolute;
|
||||
row-gap: 5px;
|
||||
width: 280px;
|
||||
width: 300px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#aircraft-spawn-menu {
|
||||
height: fit-content;
|
||||
#map-contextmenu>div:nth-child(2) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -35,154 +58,87 @@
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#coalition-switch[data-value="false"]>.ol-switch-fill {
|
||||
background-color: var(--primary-blue);
|
||||
}
|
||||
|
||||
#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 {
|
||||
#aircraft-spawn-menu .ol-select.is-open .ol-select-options,
|
||||
#helicopter-spawn-menu .ol-select.is-open .ol-select-options {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
#aircraft-spawn-menu>button,
|
||||
#ground-unit-spawn-menu>button {
|
||||
.deploy-unit-button {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#aircraft-spawn-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/aircraft.svg");
|
||||
background-size: 48px;
|
||||
.deploy-unit-button[data-points]:not([data-points='']):not([data-points='0']):not([data-points='Infinity'])::after {
|
||||
content: " (" attr(data-points) " points)";
|
||||
}
|
||||
|
||||
#ground-unit-spawn-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/ground.svg");
|
||||
background-size: 48px;
|
||||
.upper-bar svg>* {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
#smoke-spawn-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/smoke.svg");
|
||||
background-size: 48px;
|
||||
.upper-bar svg {
|
||||
width: 22px;
|
||||
margin: 0px 5px;
|
||||
}
|
||||
|
||||
#explosion-spawn-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/explosion.svg");
|
||||
background-size: 48px;
|
||||
.upper-bar button:nth-child(2) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.unit-spawn-button {
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
height: 48px;
|
||||
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 {
|
||||
[data-coalition="blue"]#active-coalition-label,
|
||||
[data-coalition="blue"].deploy-unit-button,
|
||||
[data-coalition="blue"]#spawn-airbase-aircraft-button,
|
||||
[data-coalition="blue"].create-iads-button {
|
||||
background-color: var(--primary-blue)
|
||||
}
|
||||
|
||||
[data-active-coalition="red"].unit-spawn-button:hover,
|
||||
[data-active-coalition="red"].unit-spawn-button.is-open,
|
||||
[data-active-coalition="red"]#active-coalition-label,
|
||||
[data-active-coalition="red"].deploy-unit-button,
|
||||
[data-active-coalition="red"]#spawn-airbase-aircraft-button {
|
||||
[data-coalition="red"]#active-coalition-label,
|
||||
[data-coalition="red"].deploy-unit-button,
|
||||
[data-coalition="red"]#spawn-airbase-aircraft-button,
|
||||
[data-coalition="red"].create-iads-button {
|
||||
background-color: var(--primary-red)
|
||||
}
|
||||
|
||||
[data-active-coalition="neutral"].unit-spawn-button:hover,
|
||||
[data-active-coalition="neutral"].unit-spawn-button.is-open,
|
||||
[data-active-coalition="neutral"]#active-coalition-label,
|
||||
[data-active-coalition="neutral"].deploy-unit-button,
|
||||
[data-active-coalition="neutral"]#spawn-airbase-aircraft-button {
|
||||
[data-coalition="neutral"]#active-coalition-label,
|
||||
[data-coalition="neutral"].deploy-unit-button,
|
||||
[data-coalition="neutral"]#spawn-airbase-aircraft-button,
|
||||
[data-coalition="neutral"].create-iads-button {
|
||||
background-color: var(--primary-neutral)
|
||||
}
|
||||
|
||||
[data-active-coalition="blue"].deploy-unit-button:disabled {
|
||||
[data-coalition="blue"].deploy-unit-button:disabled {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--primary-blue);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
[data-active-coalition="red"].deploy-unit-button:disabled {
|
||||
[data-coalition="red"].deploy-unit-button:disabled {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--primary-red);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
[data-active-coalition="neutral"].deploy-unit-button:disabled {
|
||||
[data-coalition="neutral"].deploy-unit-button:disabled {
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--primary-neutral);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
[data-active-coalition="blue"]#active-coalition-label::after {
|
||||
[data-coalition="blue"]#active-coalition-label::after {
|
||||
content: "Create blue unit";
|
||||
}
|
||||
|
||||
[data-active-coalition="red"]#active-coalition-label::after {
|
||||
[data-coalition="red"]#active-coalition-label::after {
|
||||
content: "Create red unit";
|
||||
}
|
||||
|
||||
[data-active-coalition="neutral"]#active-coalition-label::after {
|
||||
[data-coalition="neutral"]#active-coalition-label::after {
|
||||
content: "Create neutral unit";
|
||||
}
|
||||
|
||||
#loadout-preview {
|
||||
#aircraft-loadout-preview,
|
||||
#helicopter-loadout-preview {
|
||||
align-content: space-between;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
@@ -191,14 +147,16 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#loadout-list {
|
||||
#aircaft-loadout-list,
|
||||
#helicopter-loadout-list {
|
||||
align-content: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#unit-image {
|
||||
#aircraft-unit-image,
|
||||
#helicopter-unit-image {
|
||||
filter: invert(100%);
|
||||
height: 100px;
|
||||
margin-bottom: 10px;
|
||||
@@ -251,17 +209,28 @@
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
#aircraft-spawn-menu .ol-slider-value {
|
||||
.ol-context-menu .ol-slider-value {
|
||||
color: var(--accent-light-blue);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#aircraft-spawn-altitude-slider {
|
||||
.ol-context-menu .ol-slider-container {
|
||||
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-contextmenu {
|
||||
display: flex;
|
||||
@@ -407,3 +376,71 @@
|
||||
width: 180px;
|
||||
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 {
|
||||
align-items: center;
|
||||
border-radius: var(--border-radius-lg);
|
||||
border-radius: var(--border-radius-md);
|
||||
display: flex;
|
||||
font-size: 11px;
|
||||
height: 32px;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
}
|
||||
|
||||
#current-task {
|
||||
border-radius: var(--border-radius-lg);
|
||||
border-radius: var(--border-radius-md);
|
||||
margin-top: auto;
|
||||
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"?>
|
||||
<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"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="spawn_aircraft.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)">
|
||||
sodipodi:docname="aircraft.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>
|
||||
@@ -44,33 +43,18 @@
|
||||
id="namedview10"
|
||||
showgrid="false"
|
||||
inkscape:zoom="18.782524"
|
||||
inkscape:cx="26.073424"
|
||||
inkscape:cy="15.446316"
|
||||
inkscape:cx="26.114701"
|
||||
inkscape:cy="15.493125"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="31"
|
||||
height="31"
|
||||
rx="7.5"
|
||||
fill="white"
|
||||
id="rect2"
|
||||
style="fill:none" />
|
||||
inkscape:current-layer="svg8"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<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"
|
||||
id="path4"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.07987" />
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="31"
|
||||
height="31"
|
||||
rx="7.5"
|
||||
stroke="white"
|
||||
id="rect6"
|
||||
style="fill:none;stroke:none" />
|
||||
style="fill-opacity:1;stroke-width:1.81764" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.6 KiB |
@@ -24,7 +24,7 @@
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="13.078125"
|
||||
inkscape:cx="19.53644"
|
||||
inkscape:cx="18.389486"
|
||||
inkscape:cy="12.157706"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
@@ -34,9 +34,9 @@
|
||||
inkscape:current-layer="svg4" />
|
||||
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path
|
||||
d="m 23.013304,7.2180185 c 0.209962,-0.3739974 0.64242,-0.5068189 0.993401,-0.2971008 0.350981,0.2097182 0.495133,0.6815841 0.332178,1.0835439 L 20.042501,18.658144 c 0.06894,0.08039 0.134753,0.164279 0.197427,0.248166 l 3.046013,-1.911931 c 0.329044,-0.206222 0.739567,-0.108354 0.968331,0.2237 0.228764,0.332054 0.197427,0.803919 -0.06894,1.10102 l -2.726369,3.040913 h -2.240637 c -0.413656,-1.303747 -1.52614,-2.236993 -2.836051,-2.236993 -1.309911,0 -2.425529,0.933246 -2.83605,2.236993 H 11.048617 L 8.6826296,19.535465 C 8.3786552,19.30128 8.2721074,18.850386 8.4350628,18.476388 8.5980181,18.102391 8.9803366,17.920635 9.3344512,18.042971 l 3.0460128,1.062571 c 0.09401,-0.136317 0.191159,-0.269138 0.294573,-0.394969 L 10.722706,15.08245 c -0.191159,-0.353027 -0.122217,-0.807416 0.159822,-1.073059 0.282038,-0.265643 0.695694,-0.262148 0.974598,0.0034 l 3.208969,3.072372 c 0.047,-0.01398 0.09401,-0.02796 0.141019,-0.03845 l 0.426191,-4.987745 c 0.0376,-0.429921 0.360382,-0.75848 0.748968,-0.75848 0.388586,0 0.711362,0.328559 0.748967,0.75848 l 0.423058,4.959835 z M 9.362655,22.482004 v 0 H 23.40189 v 0 h 1.002802 c 0.554675,0 1.002803,0.499829 1.002803,1.118498 0,0.618669 -0.448128,1.118496 -1.002803,1.118496 H 8.3598525 c -0.554675,0 -1.0028024,-0.499827 -1.0028024,-1.118496 0,-0.618669 0.4481274,-1.118498 1.0028024,-1.118498 z M 16.382273,6.8230493 c 0.416789,0 0.752101,0.3739974 0.752101,0.8388726 v 1.6777453 c 0,0.4648752 -0.335312,0.8388728 -0.752101,0.8388728 -0.41679,0 -0.752102,-0.3739976 -0.752102,-0.8388728 V 7.6619219 c 0,-0.4648752 0.335312,-0.8388726 0.752102,-0.8388726 z"
|
||||
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"
|
||||
stroke="#ffffff"
|
||||
id="path2"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0330959" />
|
||||
style="fill-opacity:1;stroke-width:0.0537019" />
|
||||
</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"?>
|
||||
<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"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg15"
|
||||
sodipodi:docname="spawn_smoke.svg"
|
||||
inkscape:version="1.0 (4035a4fb49, 2020-05-01)">
|
||||
sodipodi:docname="smoke.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="metadata19">
|
||||
<rdf:RDF>
|
||||
@@ -23,7 +22,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@@ -42,25 +41,19 @@
|
||||
id="namedview17"
|
||||
showgrid="false"
|
||||
inkscape:zoom="18.782524"
|
||||
inkscape:cx="15.515904"
|
||||
inkscape:cy="14.452888"
|
||||
inkscape:cx="15.572987"
|
||||
inkscape:cy="14.481547"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg15" />
|
||||
inkscape:current-layer="svg15"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1" />
|
||||
<path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0296135;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"
|
||||
style="fill-opacity:1;stroke:none;stroke-width:0.0484484;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
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" />
|
||||
<rect
|
||||
x="0.5"
|
||||
y="0.5"
|
||||
width="31"
|
||||
height="31"
|
||||
rx="7.5"
|
||||
stroke="white"
|
||||
id="rect8"
|
||||
style="fill:none;stroke:none" />
|
||||
<defs
|
||||
id="defs13">
|
||||
<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;
|
||||
|
||||
/*** Air units ***/
|
||||
--unit-aircraft-ammo-gap: calc(2px + var(--unit-stroke-width));
|
||||
--unit-aircraft-ammo-border-radius: 50%;
|
||||
--unit-aircraft-ammo-border-width: 2px;
|
||||
--unit-aircraft-ammo-radius: 2px;
|
||||
--unit-aircraft-ammo-spacing: 2px;
|
||||
--unit-aircraft-ammo-x: 0px;
|
||||
--unit-aircraft-ammo-y: 30px;
|
||||
--unit-aircraft-fuel-border-width: 2px;
|
||||
--unit-aircraft-fuel-height: 6px;
|
||||
--unit-aircraft-fuel-width: 36px;
|
||||
--unit-aircraft-fuel-x: 0px;
|
||||
--unit-aircraft-fuel-y: 22px;
|
||||
--unit-aircraft-height: 28px;
|
||||
--unit-aircraft-vvi-width: 4px;
|
||||
--unit-ammo-gap: calc(2px + var(--unit-stroke-width));
|
||||
--unit-ammo-border-radius: 50%;
|
||||
--unit-ammo-border-width: 2px;
|
||||
--unit-ammo-radius: 2px;
|
||||
--unit-ammo-spacing: 2px;
|
||||
--unit-ammo-x: 0px;
|
||||
--unit-ammo-y: 30px;
|
||||
--unit-fuel-border-width: 2px;
|
||||
--unit-fuel-height: 6px;
|
||||
--unit-fuel-width: 36px;
|
||||
--unit-fuel-x: 0px;
|
||||
--unit-fuel-y: 22px;
|
||||
--unit-vvi-width: 4px;
|
||||
}
|
||||
5
client/src/@types/dom.d.ts
vendored
@@ -17,7 +17,10 @@ interface CustomEventMap {
|
||||
"groupCreation": CustomEvent<Unit[]>,
|
||||
"groupDeletion": CustomEvent<Unit[]>,
|
||||
"mapStateChanged": CustomEvent<string>,
|
||||
"mapContextMenu": CustomEvent<>
|
||||
"mapContextMenu": CustomEvent<>,
|
||||
"mapVisibilityOptionsChanged": CustomEvent<>,
|
||||
"commandModeOptionsChanged": CustomEvent<>,
|
||||
"contactsUpdated": CustomEvent<Unit>,
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
40
client/src/@types/server.d.ts
vendored
@@ -1,16 +1,46 @@
|
||||
interface UnitsData {
|
||||
units: {[key: string]: UnitData},
|
||||
sessionHash: string
|
||||
}
|
||||
|
||||
interface AirbasesData {
|
||||
airbases: {[key: string]: any},
|
||||
sessionHash: string;
|
||||
time: number;
|
||||
}
|
||||
|
||||
interface BullseyesData {
|
||||
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 {
|
||||
logs: {[key: string]: string},
|
||||
sessionHash: string;
|
||||
time: number;
|
||||
}
|
||||
110
client/src/@types/unit.d.ts
vendored
@@ -1,60 +1,24 @@
|
||||
interface UpdateData {
|
||||
[key: string]: any
|
||||
import { LatLng } from "leaflet"
|
||||
|
||||
interface ObjectIconOptions {
|
||||
showState: boolean,
|
||||
showVvi: boolean,
|
||||
showHotgroup: boolean,
|
||||
showUnitIcon: boolean,
|
||||
showShortLabel: boolean,
|
||||
showFuel: boolean,
|
||||
showAmmo: boolean,
|
||||
showSummary: boolean,
|
||||
showCallsign: boolean,
|
||||
rotateToHeading: boolean
|
||||
}
|
||||
|
||||
interface BaseData {
|
||||
controlled: boolean;
|
||||
name: string;
|
||||
unitName: string;
|
||||
groupName: string;
|
||||
alive: 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 GeneralSettings {
|
||||
prohibitJettison: boolean;
|
||||
prohibitAA: boolean;
|
||||
prohibitAG: boolean;
|
||||
prohibitAfterburner: boolean;
|
||||
prohibitAirWpn: boolean;
|
||||
}
|
||||
|
||||
interface TACAN {
|
||||
@@ -70,31 +34,21 @@ interface Radio {
|
||||
callsignNumber: number;
|
||||
}
|
||||
|
||||
interface GeneralSettings {
|
||||
prohibitJettison: boolean;
|
||||
prohibitAA: boolean;
|
||||
prohibitAG: boolean;
|
||||
prohibitAfterburner: boolean;
|
||||
prohibitAirWpn: boolean;
|
||||
interface Ammo {
|
||||
quantity: number,
|
||||
name: string,
|
||||
guidance: number,
|
||||
category: number,
|
||||
missileCategory: number
|
||||
}
|
||||
|
||||
interface UnitIconOptions {
|
||||
showState: boolean,
|
||||
showVvi: boolean,
|
||||
showHotgroup: boolean,
|
||||
showUnitIcon: boolean,
|
||||
showShortLabel: boolean,
|
||||
showFuel: boolean,
|
||||
showAmmo: boolean,
|
||||
showSummary: boolean,
|
||||
rotateToHeading: boolean
|
||||
interface Contact {
|
||||
ID: number,
|
||||
detectionMethod: number
|
||||
}
|
||||
|
||||
interface UnitData {
|
||||
baseData: BaseData;
|
||||
flightData: FlightData;
|
||||
missionData: MissionData;
|
||||
formationData: FormationData;
|
||||
taskData: TaskData;
|
||||
optionsData: OptionsData;
|
||||
interface Offset {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number
|
||||
}
|
||||
9
client/src/@types/unitdatabase.d.ts
vendored
@@ -14,9 +14,12 @@ interface LoadoutBlueprint {
|
||||
|
||||
interface UnitBlueprint {
|
||||
name: string;
|
||||
era?: string[];
|
||||
coalition: string;
|
||||
era: string;
|
||||
label: string;
|
||||
shortLabel: string;
|
||||
loadouts: LoadoutBlueprint[];
|
||||
filename: string;
|
||||
type?: 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 { ATCBoard } from "./atcboard";
|
||||
import { ATCBoardGround } from "./board/ground";
|
||||
@@ -140,11 +141,10 @@ export class ATC {
|
||||
}
|
||||
|
||||
|
||||
getMissionDateTime() : Date {
|
||||
return new Date( getMissionData().getNowDate() );
|
||||
getMissionDate() : Date {
|
||||
return convertDateAndTimeToDate(getMissionHandler().getDateAndTime());
|
||||
}
|
||||
|
||||
|
||||
lookForBoards() {
|
||||
|
||||
document.querySelectorAll( ".ol-strip-board" ).forEach( board => {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { zeroAppend } from "../other/utils";
|
||||
import { ATC } from "./atc";
|
||||
import { Unit } from "../units/unit";
|
||||
import { getMissionData, getUnitsManager } from "..";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { getMissionHandler, getUnitsManager } from "..";
|
||||
import Sortable from "sortablejs";
|
||||
import { FlightInterface } from "./atc";
|
||||
import { getConnected } from "../server/server";
|
||||
@@ -115,11 +115,11 @@ export abstract class ATCBoard {
|
||||
|
||||
addFlight( unit:Unit ) {
|
||||
|
||||
const baseData = unit.getBaseData();
|
||||
const baseData = unit.getData();
|
||||
|
||||
const unitCanBeAdded = () => {
|
||||
|
||||
if ( baseData.category !== "Aircraft" ) {
|
||||
if ( unit.getCategory() !== "Aircraft" ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -345,7 +345,7 @@ export abstract class ATCBoard {
|
||||
const results = Object.keys( units ).reduce( ( acc:Unit[], unitId:any ) => {
|
||||
|
||||
const unit = units[ unitId ];
|
||||
const baseData = unit.getBaseData();
|
||||
const baseData = unit.getData();
|
||||
|
||||
if ( !unitIdsBeingMonitored.includes( parseInt( unitId ) ) && baseData.unitName.toLowerCase().indexOf( searchString ) > -1 ) {
|
||||
acc.push( unit );
|
||||
@@ -359,7 +359,7 @@ export abstract class ATCBoard {
|
||||
|
||||
results.forEach( unit => {
|
||||
|
||||
const baseData = unit.getBaseData();
|
||||
const baseData = unit.getData();
|
||||
|
||||
const a = document.createElement( "a" );
|
||||
a.innerText = baseData.unitName;
|
||||
@@ -442,7 +442,7 @@ export abstract class ATCBoard {
|
||||
|
||||
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;
|
||||
|
||||
@@ -455,8 +455,8 @@ export abstract class ATCBoard {
|
||||
|
||||
updateClock() {
|
||||
|
||||
const missionTime = this.#atc.getMissionDateTime().getTime();
|
||||
const timeDiff = new Date().getTime() - getMissionData().getUpdateTime();
|
||||
const missionTime = this.#atc.getMissionDate().getTime();
|
||||
const timeDiff = new Date().getTime() - getMissionHandler().getDateAndTime().elapsedTime;
|
||||
|
||||
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 stripBoard = this.getStripBoardElement();
|
||||
|
||||
const missionTime = this.getATC().getMissionDateTime().getTime();
|
||||
const missionTime = this.getATC().getMissionDate().getTime();
|
||||
|
||||
for( const strip of stripBoard.children ) {
|
||||
strip.toggleAttribute( "data-updating", true );
|
||||
|
||||
@@ -17,7 +17,7 @@ export class ATCBoardTower extends ATCBoard {
|
||||
update() {
|
||||
|
||||
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 stripBoard = this.getStripBoardElement();
|
||||
|
||||
@@ -34,13 +34,13 @@ export class ATCBoardTower extends ATCBoard {
|
||||
return;
|
||||
}
|
||||
|
||||
const flightData:FlightData = {
|
||||
const flightData = {
|
||||
latitude: -1,
|
||||
longitude: -1,
|
||||
altitude: -1,
|
||||
heading: -1,
|
||||
speed: -1,
|
||||
...( selectableUnits.hasOwnProperty( flight.unitId ) ? selectableUnits[flight.unitId].getFlightData() : {} )
|
||||
...( selectableUnits.hasOwnProperty( flight.unitId ) ? selectableUnits[flight.unitId].getData() : {} )
|
||||
};
|
||||
|
||||
if ( !strip ) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getUnitsManager } from "..";
|
||||
import { Panel } from "../panels/panel";
|
||||
import { Unit } from "../units/unit";
|
||||
import { Unit } from "../unit/unit";
|
||||
|
||||
export class UnitDataTable extends Panel {
|
||||
constructor(id: string) {
|
||||
@@ -12,8 +12,8 @@ export class UnitDataTable extends Panel {
|
||||
var units = getUnitsManager().getUnits();
|
||||
|
||||
const unitsArray = Object.values(units).sort((a: Unit, b: Unit) => {
|
||||
const aVal = a.getBaseData().unitName?.toLowerCase();
|
||||
const bVal = b.getBaseData().unitName?.toLowerCase();
|
||||
const aVal = a.getUnitName()?.toLowerCase();
|
||||
const bVal = b.getUnitName()?.toLowerCase();
|
||||
|
||||
if (aVal > bVal) {
|
||||
return 1;
|
||||
@@ -48,7 +48,7 @@ export class UnitDataTable extends Panel {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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 reactionsToThreat: string[] = ["None", "Manoeuvre", "Passive", "Evade"];
|
||||
export const emissionsCountermeasures: string[] = ["Silent", "Attack", "Defend", "Free"];
|
||||
export const NONE = "None";
|
||||
export const GAME_MASTER = "Game master";
|
||||
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 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 VISUAL = 1;
|
||||
export const OPTIC = 2;
|
||||
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 maxSpeedValues: { [key: string]: number } = { Aircraft: 800, Helicopter: 300, NavyUnit: 60, GroundUnit: 60 };
|
||||
@@ -99,4 +130,66 @@ export const layers = {
|
||||
maxZoom: 20,
|
||||
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 visibilityControlsTypes: string[][] = [["human"], ["dcs"], ["aircraft"], ["groundunit-sam", "groundunit-sam-radar", "groundunit-sam-launcher"], ["groundunit-other", "groundunit-ewr"], ["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 { Airbase } from "../missionhandler/airbase";
|
||||
import { getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
|
||||
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "../constants/constants";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
|
||||
export class AirbaseContextMenu extends ContextMenu {
|
||||
@@ -24,7 +25,8 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
this.setProperties(airbase.getProperties());
|
||||
this.setParkings(airbase.getParkings());
|
||||
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) {
|
||||
@@ -50,7 +52,11 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -60,8 +66,10 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
showSpawnMenu() {
|
||||
if (this.#airbase != null) {
|
||||
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().hideLowerBar();
|
||||
getMap().getMapContextMenu().hideAltitudeSlider();
|
||||
getMap().getMapContextMenu().showSubMenu("aircraft");
|
||||
getMap().getMapContextMenu().setAirbaseName(this.#airbase.getName());
|
||||
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 "../unit/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);
|
||||
#x: number = 0;
|
||||
#y: number = 0;
|
||||
#visibleSubMenu: string | null = null;
|
||||
#hidden: boolean = true;
|
||||
|
||||
constructor(id: string) {
|
||||
this.#container = document.getElementById(id);
|
||||
@@ -17,10 +19,12 @@ export class ContextMenu {
|
||||
this.#x = x;
|
||||
this.#y = y;
|
||||
this.clip();
|
||||
this.#hidden = false;
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.#container?.classList.toggle("hide", true);
|
||||
this.#hidden = true;
|
||||
}
|
||||
|
||||
getContainer() {
|
||||
@@ -39,6 +43,10 @@ export class ContextMenu {
|
||||
return this.#y;
|
||||
}
|
||||
|
||||
getHidden() {
|
||||
return this.#hidden;
|
||||
}
|
||||
|
||||
clip() {
|
||||
if (this.#container != null) {
|
||||
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";
|
||||
}
|
||||
}
|
||||
|
||||
setVisibleSubMenu(menu: string | null) {
|
||||
this.#visibleSubMenu = menu;
|
||||
}
|
||||
|
||||
getVisibleSubMenu() {
|
||||
return this.#visibleSubMenu;
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,10 @@ export class Dropdown {
|
||||
|
||||
setOptions(optionsList: string[], sortAlphabetically: boolean = true) {
|
||||
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) => {
|
||||
var div = document.createElement("div");
|
||||
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) {
|
||||
const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text);
|
||||
if (index > -1) {
|
||||
|
||||
@@ -1,69 +1,153 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getActiveCoalition, getMap, setActiveCoalition } from "..";
|
||||
import { spawnAircraft, spawnExplosion, spawnGroundUnit, spawnSmoke } from "../server/server";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { getActiveCoalition, getMap, getMissionHandler, getUnitsManager, setActiveCoalition } from "..";
|
||||
import { spawnExplosion, spawnSmoke } from "../server/server";
|
||||
import { aircraftDatabase } from "../unit/aircraftdatabase";
|
||||
import { groundUnitDatabase } from "../unit/groundunitdatabase";
|
||||
import { helicopterDatabase } from "../unit/helicopterdatabase";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { Dropdown } from "./dropdown";
|
||||
import { Switch } from "./switch";
|
||||
import { Slider } from "./slider";
|
||||
import { ftToM } from "../other/utils";
|
||||
|
||||
export interface SpawnOptions {
|
||||
role: string;
|
||||
type: string;
|
||||
latlng: LatLng;
|
||||
coalition: string;
|
||||
loadout: string | null;
|
||||
airbaseName: string | null;
|
||||
altitude: number | null;
|
||||
}
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { navyUnitDatabase } from "../unit/navyunitdatabase";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
|
||||
export class MapContextMenu extends ContextMenu {
|
||||
#coalitionSwitch: Switch;
|
||||
#aircraftRoleDropdown: Dropdown;
|
||||
#aircraftTypeDropdown: Dropdown;
|
||||
#aircraftLabelDropdown: Dropdown;
|
||||
#aircraftCountDropdown: Dropdown;
|
||||
#aircraftLoadoutDropdown: Dropdown;
|
||||
#aircrafSpawnAltitudeSlider: Slider;
|
||||
#groundUnitRoleDropdown: Dropdown;
|
||||
#aircraftSpawnAltitudeSlider: Slider;
|
||||
#helicopterRoleDropdown: Dropdown;
|
||||
#helicopterLabelDropdown: Dropdown;
|
||||
#helicopterCountDropdown: Dropdown;
|
||||
#helicopterLoadoutDropdown: Dropdown;
|
||||
#helicopterSpawnAltitudeSlider: Slider;
|
||||
#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) {
|
||||
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.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) => {
|
||||
this.showSubMenu(e.detail.type);
|
||||
/* Aircraft menu */
|
||||
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", () => {
|
||||
this.hide();
|
||||
document.addEventListener("contextMenuDeployAircrafts", () => {
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng);
|
||||
spawnAircraft(this.#spawnOptions);
|
||||
var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng, altitude: this.#spawnOptions.altitude, loadout: this.#spawnOptions.loadout};
|
||||
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", () => {
|
||||
this.hide();
|
||||
document.addEventListener("contextMenuDeployHelicopters", () => {
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng);
|
||||
spawnGroundUnit(this.#spawnOptions);
|
||||
var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng, altitude: this.#spawnOptions.altitude, loadout: this.#spawnOptions.loadout};
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -76,41 +160,141 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.hide();
|
||||
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();
|
||||
}
|
||||
|
||||
show(x: number, y: number, latlng: LatLng) {
|
||||
this.#spawnOptions.airbaseName = null;
|
||||
super.show(x, y, latlng);
|
||||
this.#spawnOptions.latlng = latlng;
|
||||
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) {
|
||||
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-button")?.classList.toggle("is-open", type === "aircraft");
|
||||
this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.classList.toggle("hide", type !== "ground-unit");
|
||||
this.getContainer()?.querySelector("#ground-unit-spawn-button")?.classList.toggle("is-open", type === "ground-unit");
|
||||
this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", type !== "helicopter");
|
||||
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-button")?.classList.toggle("is-open", type === "smoke");
|
||||
this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", type !== "explosion");
|
||||
this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", type === "explosion");
|
||||
|
||||
(this.getContainer()?.querySelectorAll(".deploy-unit-button"))?.forEach((element: Node) => {(element as HTMLButtonElement).disabled = true;})
|
||||
|
||||
this.#resetAircraftRole();
|
||||
this.#resetAircraftType();
|
||||
this.#resetGroundUnitRole();
|
||||
this.#resetAircraftLabel();
|
||||
this.#resetHelicopterRole();
|
||||
this.#resetHelicopterLabel();
|
||||
this.#resetGroundUnitType();
|
||||
this.#resetGroundUnitLabel();
|
||||
this.#resetNavyUnitType();
|
||||
this.#resetNavyUnitLabel();
|
||||
this.#aircraftCountDropdown.setValue("1");
|
||||
this.#helicopterCountDropdown.setValue("1");
|
||||
this.#groundUnitCountDropdown.setValue("1");
|
||||
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() {
|
||||
this.getContainer()?.querySelector("#upper-bar")?.classList.toggle("hide", false);
|
||||
this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -121,63 +305,100 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.#spawnOptions.latlng = latlng;
|
||||
}
|
||||
|
||||
setCoalitionArea(coalitionArea: CoalitionArea) {
|
||||
this.#coalitionArea = coalitionArea;
|
||||
this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", false);
|
||||
}
|
||||
|
||||
#onSwitchClick(value: boolean) {
|
||||
value? setActiveCoalition("red"): setActiveCoalition("blue");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
}
|
||||
|
||||
#onSwitchRightClick(e: any) {
|
||||
this.#coalitionSwitch.setValue(undefined);
|
||||
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 *********/
|
||||
#setAircraftRole(role: string) {
|
||||
this.#spawnOptions.role = role;
|
||||
this.#resetAircraftType();
|
||||
this.#aircraftTypeDropdown.setOptions(aircraftDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
||||
this.#aircraftTypeDropdown.selectValue(0);
|
||||
this.#resetAircraftLabel();
|
||||
this.#aircraftLabelDropdown.setOptions(aircraftDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
||||
this.#aircraftLabelDropdown.selectValue(0);
|
||||
this.clip();
|
||||
this.#computeSpawnPoints();
|
||||
}
|
||||
|
||||
#resetAircraftRole() {
|
||||
(<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.#aircraftTypeDropdown.reset();
|
||||
this.#aircraftLabelDropdown.reset();
|
||||
this.#aircraftRoleDropdown.setOptions(aircraftDatabase.getRoles());
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#setAircraftType(label: string) {
|
||||
this.#resetAircraftType();
|
||||
var type = aircraftDatabase.getByLabel(label)?.name || null;
|
||||
if (type != null) {
|
||||
this.#spawnOptions.type = type;
|
||||
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role));
|
||||
#setAircraftLabel(label: string) {
|
||||
this.#resetAircraftLabel();
|
||||
var name = aircraftDatabase.getByLabel(label)?.name || null;
|
||||
if (name != null) {
|
||||
this.#spawnOptions.name = name;
|
||||
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(name, this.#spawnOptions.role));
|
||||
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.classList.toggle("hide", false);
|
||||
}
|
||||
this.clip();
|
||||
this.#computeSpawnPoints();
|
||||
}
|
||||
|
||||
#resetAircraftType() {
|
||||
#resetAircraftLabel() {
|
||||
(<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();
|
||||
(<HTMLImageElement>this.getContainer()?.querySelector("#unit-image")).classList.toggle("hide", true);
|
||||
(<HTMLImageElement>this.getContainer()?.querySelector("#aircraft-unit-image")).classList.toggle("hide", true);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#setAircraftCount(count: string) {
|
||||
this.#spawnOptions.count = parseInt(count);
|
||||
this.clip();
|
||||
this.#computeSpawnPoints();
|
||||
}
|
||||
|
||||
#setAircraftLoadout(loadoutName: string) {
|
||||
var loadout = aircraftDatabase.getLoadoutByName(this.#spawnOptions.type, loadoutName);
|
||||
var loadout = aircraftDatabase.getLoadoutByName(this.#spawnOptions.name, loadoutName);
|
||||
if (loadout) {
|
||||
this.#spawnOptions.loadout = loadout.code;
|
||||
(<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}`; });
|
||||
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) => {
|
||||
var div = document.createElement('div');
|
||||
div.innerText = item;
|
||||
@@ -188,40 +409,179 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.clip();
|
||||
}
|
||||
|
||||
/********* Ground unit spawn menu *********/
|
||||
#setGroundUnitRole(role: string) {
|
||||
/********* Helicopter spawn menu *********/
|
||||
#setHelicopterRole(role: string) {
|
||||
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 });
|
||||
this.#groundUnitTypeDropdown.setOptions(types);
|
||||
this.#groundUnitTypeDropdown.selectValue(0);
|
||||
#resetHelicopterRole() {
|
||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#helicopter-loadout-list")).replaceChildren();
|
||||
this.#helicopterRoleDropdown.reset();
|
||||
this.#helicopterLabelDropdown.reset();
|
||||
this.#helicopterRoleDropdown.setOptions(helicopterDatabase.getRoles());
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#resetGroundUnitRole() {
|
||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#loadout-list")).replaceChildren();
|
||||
this.#groundUnitRoleDropdown.reset();
|
||||
this.#groundUnitTypeDropdown.reset();
|
||||
#setHelicopterLabel(label: string) {
|
||||
this.#resetHelicopterLabel();
|
||||
var name = helicopterDatabase.getByLabel(label)?.name || null;
|
||||
if (name != null) {
|
||||
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();
|
||||
this.#groundUnitRoleDropdown.setOptions(roles);
|
||||
#resetHelicopterLabel() {
|
||||
(<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();
|
||||
}
|
||||
|
||||
#setGroundUnitType(label: string) {
|
||||
this.#resetGroundUnitType();
|
||||
var type = groundUnitsDatabase.getByLabel(label)?.name || null;
|
||||
if (type != null) {
|
||||
this.#spawnOptions.type = type;
|
||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
||||
#setHelicopterCount(count: string) {
|
||||
this.#spawnOptions.count = parseInt(count);
|
||||
this.clip();
|
||||
this.#computeSpawnPoints();
|
||||
}
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
/********* 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() {
|
||||
(<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();
|
||||
}
|
||||
|
||||
#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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Map } from "./map/map"
|
||||
import { UnitsManager } from "./units/unitsmanager";
|
||||
import { UnitsManager } from "./unit/unitsmanager";
|
||||
import { UnitInfoPanel } from "./panels/unitinfopanel";
|
||||
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
|
||||
import { MissionHandler } from "./missionhandler/missionhandler";
|
||||
import { MissionHandler } from "./mission/missionhandler";
|
||||
import { UnitControlPanel } from "./panels/unitcontrolpanel";
|
||||
import { MouseInfoPanel } from "./panels/mouseinfopanel";
|
||||
import { AIC } from "./aic/aic";
|
||||
@@ -16,10 +16,14 @@ import { Popup } from "./popups/popup";
|
||||
import { Dropdown } from "./controls/dropdown";
|
||||
import { HotgroupPanel } from "./panels/hotgrouppanel";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants";
|
||||
import { ServerStatusPanel } from "./panels/serverstatuspanel";
|
||||
import { WeaponsManager } from "./weapon/weaponsmanager";
|
||||
|
||||
var map: Map;
|
||||
|
||||
var unitsManager: UnitsManager;
|
||||
var weaponsManager: WeaponsManager;
|
||||
var missionHandler: MissionHandler;
|
||||
|
||||
var aic: AIC;
|
||||
@@ -27,6 +31,7 @@ var atc: ATC;
|
||||
|
||||
var unitInfoPanel: UnitInfoPanel;
|
||||
var connectionStatusPanel: ConnectionStatusPanel;
|
||||
var serverStatusPanel: ServerStatusPanel;
|
||||
var unitControlPanel: UnitControlPanel;
|
||||
var mouseInfoPanel: MouseInfoPanel;
|
||||
var logPanel: LogPanel;
|
||||
@@ -44,18 +49,20 @@ function setup() {
|
||||
featureSwitches = new FeatureSwitches();
|
||||
|
||||
/* Initialize base functionalitites */
|
||||
map = new Map('map-container');
|
||||
unitsManager = new UnitsManager();
|
||||
weaponsManager = new WeaponsManager();
|
||||
map = new Map('map-container');
|
||||
missionHandler = new MissionHandler();
|
||||
|
||||
/* Panels */
|
||||
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
|
||||
unitControlPanel = new UnitControlPanel("unit-control-panel");
|
||||
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
|
||||
serverStatusPanel = new ServerStatusPanel("server-status-panel");
|
||||
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
|
||||
hotgroupPanel = new HotgroupPanel("hotgroup-panel");
|
||||
//logPanel = new LogPanel("log-panel");
|
||||
|
||||
logPanel = new LogPanel("log-panel");
|
||||
|
||||
/* Popups */
|
||||
infoPopup = new Popup("info-popup");
|
||||
|
||||
@@ -184,12 +191,12 @@ function setupEvents() {
|
||||
const form = document.querySelector("#splash-content")?.querySelector("#authentication-form");
|
||||
const username = (<HTMLInputElement>(form?.querySelector("#username"))).value;
|
||||
const password = (<HTMLInputElement>(form?.querySelector("#password"))).value;
|
||||
setCredentials(username, btoa("admin" + ":" + password));
|
||||
setCredentials(username, password);
|
||||
|
||||
/* Start periodically requesting updates */
|
||||
startUpdate();
|
||||
|
||||
setConnectionStatus("connecting");
|
||||
setLoginStatus("connecting");
|
||||
})
|
||||
|
||||
document.addEventListener("reloadPage", () => {
|
||||
@@ -204,17 +211,12 @@ function setupEvents() {
|
||||
else
|
||||
img.onload = () => SVGInjector(img);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
export function getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
export function getMissionData() {
|
||||
return missionHandler;
|
||||
}
|
||||
|
||||
export function getUnitDataTable() {
|
||||
return unitDataTable;
|
||||
}
|
||||
@@ -223,6 +225,15 @@ export function getUnitsManager() {
|
||||
return unitsManager;
|
||||
}
|
||||
|
||||
export function getWeaponsManager() {
|
||||
return weaponsManager;
|
||||
}
|
||||
|
||||
|
||||
export function getMissionHandler() {
|
||||
return missionHandler;
|
||||
}
|
||||
|
||||
export function getUnitInfoPanel() {
|
||||
return unitInfoPanel;
|
||||
}
|
||||
@@ -243,21 +254,34 @@ export function getConnectionStatusPanel() {
|
||||
return connectionStatusPanel;
|
||||
}
|
||||
|
||||
export function getServerStatusPanel() {
|
||||
return serverStatusPanel;
|
||||
}
|
||||
|
||||
export function getHotgroupPanel() {
|
||||
return hotgroupPanel;
|
||||
}
|
||||
|
||||
export function setActiveCoalition(newActiveCoalition: string) {
|
||||
activeCoalition = newActiveCoalition;
|
||||
document.querySelectorAll('[data-active-coalition]').forEach((element: any) => { element.setAttribute("data-active-coalition", activeCoalition) });
|
||||
if (getMissionHandler().getCommandModeOptions().commandMode == GAME_MASTER)
|
||||
activeCoalition = newActiveCoalition;
|
||||
}
|
||||
|
||||
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) {
|
||||
const el = document.querySelector("#connection-status") as HTMLElement;
|
||||
export function setLoginStatus(status: string) {
|
||||
const el = document.querySelector("#login-status") as HTMLElement;
|
||||
if (el)
|
||||
el.dataset["status"] = status;
|
||||
}
|
||||
@@ -266,4 +290,5 @@ export function getInfoPopup() {
|
||||
return infoPopup;
|
||||
}
|
||||
|
||||
window.onload = setup;
|
||||
window.onload = setup;
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ export var BoxSelect = Handler.extend({
|
||||
|
||||
_onMouseDown: function (e: any) {
|
||||
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
|
||||
// will interrupt the interaction and orphan a box element in the container.
|
||||
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";
|
||||
|
||||
export class DestinationPreviewMarker extends CustomMarker {
|
||||
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||
super(latlng, options);
|
||||
this.setZIndexOffset(9999);
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
this.setIcon(new DivIcon({
|
||||
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 { getUnitsManager } from "..";
|
||||
import { BoxSelect } from "./boxselect";
|
||||
import { MapContextMenu, SpawnOptions } from "../controls/mapcontextmenu";
|
||||
import { MapContextMenu } from "../controls/mapcontextmenu";
|
||||
import { UnitContextMenu } from "../controls/unitcontextmenu";
|
||||
import { AirbaseContextMenu } from "../controls/airbasecontextmenu";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Airbase } from "../missionhandler/airbase";
|
||||
import { Unit } from "../units/unit";
|
||||
import { bearing } from "../other/utils";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { bearing, createCheckboxOption } from "../other/utils";
|
||||
import { DestinationPreviewMarker } from "./destinationpreviewmarker";
|
||||
import { TemporaryUnitMarker } from "./temporaryunitmarker";
|
||||
import { ClickableMiniMap } from "./clickableminimap";
|
||||
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, visibilityControlsTypes } from "../constants/constants";
|
||||
import { TargetMarker } from "./targetmarker";
|
||||
import { CoalitionArea } from "./coalitionarea";
|
||||
import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu";
|
||||
import { DrawingCursor } from "./drawingcursor";
|
||||
|
||||
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
||||
|
||||
// TODO would be nice to convert to ts
|
||||
require("../../public/javascripts/leaflet.nauticscale.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"];
|
||||
require("../../public/javascripts/L.Path.Drag.js")
|
||||
|
||||
export class Map extends L.Map {
|
||||
#ID: string;
|
||||
@@ -42,28 +37,38 @@ export class Map extends L.Map {
|
||||
#panUp: boolean = false;
|
||||
#panDown: boolean = false;
|
||||
#lastMousePosition: L.Point = new L.Point(0, 0);
|
||||
#shiftKey: boolean = false;
|
||||
#ctrlKey: boolean = false;
|
||||
#centerUnit: Unit | null = null;
|
||||
#miniMap: ClickableMiniMap | null = null;
|
||||
#miniMapLayerGroup: L.LayerGroup;
|
||||
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
||||
#destinationPreviewMarkers: DestinationPreviewMarker[] = [];
|
||||
#targetMarker: TargetMarker;
|
||||
#selecting: boolean = false;
|
||||
#isZooming: boolean = false;
|
||||
|
||||
#destinationGroupRotation: number = 0;
|
||||
#computeDestinationRotation: boolean = false;
|
||||
#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");
|
||||
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
|
||||
#airbaseContextMenu: AirbaseContextMenu = new AirbaseContextMenu("airbase-contextmenu");
|
||||
#coalitionAreaContextMenu: CoalitionAreaContextMenu = new CoalitionAreaContextMenu("coalition-area-contextmenu");
|
||||
|
||||
#mapSourceDropdown: Dropdown;
|
||||
#mapVisibilityOptionsDropdown: Dropdown;
|
||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||
#visibiityOptions: { [key: string]: boolean } = {}
|
||||
|
||||
constructor(ID: string) {
|
||||
/* Init the leaflet map */
|
||||
|
||||
//@ts-ignore
|
||||
super(ID, { doubleClickZoom: false, zoomControl: false, boxZoom: false, boxSelect: true, zoomAnimation: true, maxBoundsViscosity: 1.0, minZoom: 7, keyboard: true, keyboardPanDelta: 0 });
|
||||
//@ts-ignore Needed because the boxSelect option is non-standard
|
||||
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 });
|
||||
this.setView([37.23, -115.8], 10);
|
||||
|
||||
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);
|
||||
|
||||
/* 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 */
|
||||
this.#state = IDLE;
|
||||
@@ -89,30 +97,44 @@ export class Map extends L.Map {
|
||||
/* Register event handles */
|
||||
this.on("click", (e: any) => this.#onClick(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("contextmenu", (e: any) => this.#onContextMenu(e));
|
||||
this.on('selectionstart', (e: any) => this.#onSelectionStart(e));
|
||||
this.on('selectionend', (e: any) => this.#onSelectionEnd(e));
|
||||
this.on('mousedown', (e: any) => this.#onMouseDown(e));
|
||||
this.on('mouseup', (e: any) => this.#onMouseUp(e));
|
||||
this.on('mousemove', (e: any) => this.#onMouseMove(e));
|
||||
this.on('keydown', (e: any) => this.#updateDestinationPreview(e));
|
||||
this.on('keyup', (e: any) => this.#updateDestinationPreview(e));
|
||||
this.on('keydown', (e: any) => this.#onKeyDown(e));
|
||||
this.on('keyup', (e: any) => this.#onKeyUp(e));
|
||||
|
||||
/* Event listeners */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
const el = ev.detail._element;
|
||||
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());
|
||||
});
|
||||
|
||||
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
|
||||
const el = ev.detail._element;
|
||||
el?.classList.toggle("off");
|
||||
getUnitsManager().setHiddenType(ev.detail.type, !el?.classList.contains("off"));
|
||||
ev.detail.types.forEach((type: string) => getUnitsManager().setHiddenType(type, !el?.classList.contains("off")));
|
||||
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) => {
|
||||
if (this.#centerUnit != null && ev.detail == this.#centerUnit)
|
||||
@@ -122,25 +144,34 @@ export class Map extends L.Map {
|
||||
/* Pan interval */
|
||||
this.#panInterval = window.setInterval(() => {
|
||||
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));
|
||||
}, 20);
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["visibility"] = visibilityControls.map((option: string, index: number) => {
|
||||
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, visibilityControlsTootlips[index], "toggleUnitVisibility", `{"type": "${option}"}`);
|
||||
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, visibilityControlsTootlips[index], "toggleUnitVisibility", `{"types": "${visibilityControlsTypes[index]}"}`);
|
||||
});
|
||||
document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]);
|
||||
|
||||
/* Markers */
|
||||
this.#targetMarker = new TargetMarker(new L.LatLng(0, 0), {interactive: false});
|
||||
/* Create the checkboxes to select the advanced visibility options */
|
||||
|
||||
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) {
|
||||
if (this.#layer != null)
|
||||
if (this.#layer != null)
|
||||
this.removeLayer(this.#layer)
|
||||
|
||||
if (layerName in mapLayers){
|
||||
|
||||
if (layerName in mapLayers) {
|
||||
const layerData = mapLayers[layerName as keyof typeof mapLayers];
|
||||
var options: L.TileLayerOptions = {
|
||||
attribution: layerData.attribution,
|
||||
@@ -160,21 +191,17 @@ export class Map extends L.Map {
|
||||
/* State machine */
|
||||
setState(state: string) {
|
||||
this.#state = state;
|
||||
if (this.#state === IDLE) {
|
||||
this.#resetDestinationMarkers();
|
||||
this.#resetTargetMarker();
|
||||
this.#showCursor();
|
||||
this.#updateCursor();
|
||||
|
||||
/* Operations to perform if you are NOT in a state */
|
||||
if (this.#state !== COALITIONAREA_DRAW_POLYGON) {
|
||||
this.#deselectCoalitionAreas();
|
||||
}
|
||||
else if (this.#state === MOVE_UNIT) {
|
||||
this.#resetTargetMarker();
|
||||
this.#createDestinationMarkers();
|
||||
if (this.#destinationPreviewMarkers.length > 0)
|
||||
this.#hideCursor();
|
||||
}
|
||||
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
|
||||
this.#resetDestinationMarkers();
|
||||
this.#createTargetMarker();
|
||||
this.#hideCursor();
|
||||
|
||||
/* Operations to perform if you ARE in a state */
|
||||
if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
this.#coalitionAreas.push(new CoalitionArea([]));
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
||||
}
|
||||
document.dispatchEvent(new CustomEvent("mapStateChanged"));
|
||||
}
|
||||
@@ -183,18 +210,28 @@ export class Map extends L.Map {
|
||||
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 */
|
||||
hideAllContextMenus() {
|
||||
this.hideMapContextMenu();
|
||||
this.hideUnitContextMenu();
|
||||
this.hideAirbaseContextMenu();
|
||||
this.hideCoalitionAreaContextMenu();
|
||||
}
|
||||
|
||||
showMapContextMenu(e: any) {
|
||||
showMapContextMenu(x: number, y: number, latlng: L.LatLng) {
|
||||
this.hideAllContextMenus();
|
||||
var x = e.originalEvent.x;
|
||||
var y = e.originalEvent.y;
|
||||
this.#mapContextMenu.show(x, y, e.latlng);
|
||||
this.#mapContextMenu.show(x, y, latlng);
|
||||
document.dispatchEvent(new CustomEvent("mapContextMenu"));
|
||||
}
|
||||
|
||||
@@ -207,11 +244,9 @@ export class Map extends L.Map {
|
||||
return this.#mapContextMenu;
|
||||
}
|
||||
|
||||
showUnitContextMenu(e: any) {
|
||||
showUnitContextMenu(x: number, y: number, latlng: L.LatLng) {
|
||||
this.hideAllContextMenus();
|
||||
var x = e.originalEvent.x;
|
||||
var y = e.originalEvent.y;
|
||||
this.#unitContextMenu.show(x, y, e.latlng);
|
||||
this.#unitContextMenu.show(x, y, latlng);
|
||||
}
|
||||
|
||||
getUnitContextMenu() {
|
||||
@@ -222,11 +257,9 @@ export class Map extends L.Map {
|
||||
this.#unitContextMenu.hide();
|
||||
}
|
||||
|
||||
showAirbaseContextMenu(e: any, airbase: Airbase) {
|
||||
showAirbaseContextMenu(x: number, y: number, latlng: L.LatLng, airbase: Airbase) {
|
||||
this.hideAllContextMenus();
|
||||
var x = e.originalEvent.x;
|
||||
var y = e.originalEvent.y;
|
||||
this.#airbaseContextMenu.show(x, y, e.latlng);
|
||||
this.#airbaseContextMenu.show(x, y, latlng);
|
||||
this.#airbaseContextMenu.setAirbase(airbase);
|
||||
}
|
||||
|
||||
@@ -238,6 +271,24 @@ export class Map extends L.Map {
|
||||
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 */
|
||||
getMousePosition() {
|
||||
return this.#lastMousePosition;
|
||||
@@ -263,6 +314,10 @@ export class Map extends L.Map {
|
||||
}
|
||||
}
|
||||
|
||||
getCenterUnit() {
|
||||
return this.#centerUnit;
|
||||
}
|
||||
|
||||
setTheatre(theatre: string) {
|
||||
var bounds = new L.LatLngBounds([-90, -180], [90, 180]);
|
||||
var miniMapZoom = 5;
|
||||
@@ -333,36 +388,59 @@ export class Map extends L.Map {
|
||||
}
|
||||
}
|
||||
|
||||
addTemporaryMarker(latlng: L.LatLng) {
|
||||
var marker = new TemporaryUnitMarker(latlng);
|
||||
addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string) {
|
||||
var marker = new TemporaryUnitMarker(latlng, name, coalition);
|
||||
marker.addTo(this);
|
||||
this.#temporaryMarkers.push(marker);
|
||||
}
|
||||
|
||||
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 i: number = 0;
|
||||
this.#temporaryMarkers.forEach((marker: L.Marker, idx: number) => {
|
||||
var t = latlng.distanceTo(marker.getLatLng());
|
||||
if (d == null || t < d) {
|
||||
d = t;
|
||||
if (dist == null || t < dist) {
|
||||
dist = t;
|
||||
closest = marker;
|
||||
i = idx;
|
||||
}
|
||||
});
|
||||
if (closest) {
|
||||
if (closest && dist != null && dist < 100) {
|
||||
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 */
|
||||
#onClick(e: any) {
|
||||
if (!this.#preventLeftClick) {
|
||||
this.hideAllContextMenus();
|
||||
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 {
|
||||
this.setState(IDLE);
|
||||
@@ -372,64 +450,75 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
#onDoubleClick(e: any) {
|
||||
|
||||
this.deselectAllCoalitionAreas();
|
||||
}
|
||||
|
||||
#onContextMenu(e: any) {
|
||||
this.hideMapContextMenu();
|
||||
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) {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getUnitsManager().selectedUnitsClearDestinations();
|
||||
}
|
||||
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, e.originalEvent.shiftKey, this.#destinationGroupRotation)
|
||||
this.#destinationGroupRotation = 0;
|
||||
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
}
|
||||
else if (this.#state === BOMBING) {
|
||||
getUnitsManager().getSelectedUnits().length > 0? this.setState(MOVE_UNIT): this.setState(IDLE);
|
||||
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
|
||||
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
||||
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
|
||||
}
|
||||
else if (this.#state === CARPET_BOMBING) {
|
||||
getUnitsManager().getSelectedUnits().length > 0? this.setState(MOVE_UNIT): this.setState(IDLE);
|
||||
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
|
||||
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
||||
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
|
||||
}
|
||||
else if (this.#state === FIRE_AT_AREA) {
|
||||
getUnitsManager().getSelectedUnits().length > 0? this.setState(MOVE_UNIT): this.setState(IDLE);
|
||||
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
|
||||
getUnitsManager().getSelectedUnits().length > 0 ? this.setState(MOVE_UNIT) : this.setState(IDLE);
|
||||
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
|
||||
}
|
||||
else {
|
||||
this.setState(IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
#executeAction(e: any, action: string) {
|
||||
if (action === "bomb")
|
||||
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
|
||||
else if (action === "carpet-bomb")
|
||||
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
|
||||
else if (action === "building-bomb")
|
||||
getUnitsManager().selectedUnitsBombBuilding(this.getMouseCoordinates());
|
||||
else if (action === "fire-at-area")
|
||||
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
|
||||
#onSelectionStart(e: any) {
|
||||
this.#selecting = true;
|
||||
this.#updateCursor();
|
||||
}
|
||||
|
||||
#onSelectionEnd(e: any) {
|
||||
this.#selecting = false;
|
||||
clearTimeout(this.#leftClickTimer);
|
||||
this.#preventLeftClick = true;
|
||||
this.#leftClickTimer = window.setTimeout(() => {
|
||||
this.#preventLeftClick = false;
|
||||
}, 200);
|
||||
getUnitsManager().selectFromBounds(e.selectionBounds);
|
||||
this.#updateCursor();
|
||||
}
|
||||
|
||||
#onMouseDown(e: any) {
|
||||
this.hideAllContextMenus();
|
||||
|
||||
if (this.#state == MOVE_UNIT) {
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
if (e.originalEvent.button == 2) {
|
||||
@@ -446,23 +535,51 @@ export class Map extends L.Map {
|
||||
this.#lastMousePosition.x = e.originalEvent.x;
|
||||
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)
|
||||
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)) {
|
||||
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) {
|
||||
if (this.#centerUnit != null)
|
||||
this.#panToUnit(this.#centerUnit);
|
||||
#onKeyDown(e: any) {
|
||||
this.#shiftKey = e.originalEvent.shiftKey;
|
||||
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) {
|
||||
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 });
|
||||
}
|
||||
|
||||
@@ -471,13 +588,6 @@ export class Map extends L.Map {
|
||||
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) {
|
||||
var button = document.createElement("button");
|
||||
const img = document.createElement("img");
|
||||
@@ -491,47 +601,120 @@ export class Map extends L.Map {
|
||||
return button;
|
||||
}
|
||||
|
||||
#createDestinationMarkers() {
|
||||
this.#resetDestinationMarkers();
|
||||
#deselectCoalitionAreas() {
|
||||
this.getSelectedCoalitionArea()?.setSelected(false);
|
||||
}
|
||||
|
||||
if (getUnitsManager().getSelectedUnits({ excludeHumans: true }).length > 0) {
|
||||
/* Create the unit destination preview markers */
|
||||
this.#destinationPreviewMarkers = getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).map((unit: Unit) => {
|
||||
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), {interactive: false});
|
||||
/* Cursors */
|
||||
#showDefaultCursor() {
|
||||
document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
|
||||
}
|
||||
|
||||
#hideDefaultCursor() {
|
||||
document.getElementById(this.#ID)?.classList.add("hidden-cursor");
|
||||
}
|
||||
|
||||
#showDestinationCursors() {
|
||||
const singleCursor = !this.#shiftKey;
|
||||
const selectedUnitsCount = getUnitsManager().getSelectedUnits({ excludeHumans: false, 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);
|
||||
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() {
|
||||
/* Remove all the destination preview markers */
|
||||
this.#destinationPreviewMarkers.forEach((marker: L.Marker) => {
|
||||
#updateDestinationCursors() {
|
||||
const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates();
|
||||
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.#destinationPreviewMarkers = [];
|
||||
this.#destinationPreviewCursors = [];
|
||||
|
||||
/* Reset the variables used to compute the rotation of the group cursors */
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#computeDestinationRotation = false;
|
||||
this.#destinationRotationCenter = null;
|
||||
}
|
||||
|
||||
#createTargetMarker(){
|
||||
this.#resetTargetMarker();
|
||||
this.#targetMarker.addTo(this);
|
||||
#showTargetCursor() {
|
||||
this.#hideTargetCursor();
|
||||
this.#targetCursor.addTo(this);
|
||||
}
|
||||
|
||||
#resetTargetMarker() {
|
||||
this.#targetMarker.setLatLng(new L.LatLng(0, 0));
|
||||
this.removeLayer(this.#targetMarker);
|
||||
#hideTargetCursor() {
|
||||
this.#targetCursor.setLatLng(new L.LatLng(0, 0));
|
||||
this.removeLayer(this.#targetCursor);
|
||||
}
|
||||
|
||||
#showCursor() {
|
||||
document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
|
||||
#showDrawingCursor() {
|
||||
this.#hideDefaultCursor();
|
||||
if (!this.hasLayer(this.#drawingCursor))
|
||||
this.#drawingCursor.addTo(this);
|
||||
}
|
||||
|
||||
#hideCursor() {
|
||||
document.getElementById(this.#ID)?.classList.add("hidden-cursor");
|
||||
#hideDrawingCursor() {
|
||||
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";
|
||||
|
||||
export class TargetMarker extends CustomMarker {
|
||||
#interactive: boolean = false;
|
||||
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||
super(latlng, options);
|
||||
this.setZIndexOffset(9999);
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
this.setIcon(new DivIcon({
|
||||
@@ -12,7 +15,6 @@ export class TargetMarker extends CustomMarker {
|
||||
}));
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-target-icon");
|
||||
el.classList.toggle("ol-target-icon-interactive", this.#interactive)
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,53 @@
|
||||
import { Icon } from "leaflet";
|
||||
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 {
|
||||
#name: string;
|
||||
#coalition: string;
|
||||
|
||||
constructor(latlng: LatLng, name: string, coalition: string) {
|
||||
super(latlng, {interactive: false});
|
||||
this.#name = name;
|
||||
this.#coalition = coalition;
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
var icon = new Icon({
|
||||
iconUrl: '/resources/theme/images/markers/temporary-icon.png',
|
||||
iconSize: [52, 52],
|
||||
iconAnchor: [26, 26]
|
||||
const category = getMarkerCategoryByName(this.#name);
|
||||
|
||||
/* Set the icon */
|
||||
var icon = new DivIcon({
|
||||
className: 'leaflet-unit-icon',
|
||||
iconAnchor: [25, 25],
|
||||
iconSize: [50, 50],
|
||||
});
|
||||
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");
|
||||
}
|
||||
}
|
||||
226
client/src/mission/missionhandler.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getInfoPopup, getMap } from "..";
|
||||
import { Airbase } from "./airbase";
|
||||
import { Bullseye } from "./bullseye";
|
||||
import { BLUE_COMMANDER, GAME_MASTER, NONE, RED_COMMANDER } from "../constants/constants";
|
||||
import { refreshAll, setCommandModeOptions } from "../server/server";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { groundUnitDatabase } from "../unit/groundunitdatabase";
|
||||
import { createCheckboxOption, getCheckboxOptions } from "../other/utils";
|
||||
import { aircraftDatabase } from "../unit/aircraftdatabase";
|
||||
import { helicopterDatabase } from "../unit/helicopterdatabase";
|
||||
import { navyUnitDatabase } from "../unit/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: NONE, 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) {
|
||||
/* Refresh all the data if we have exited the NONE state */
|
||||
var requestRefresh = false;
|
||||
if (this.#commandModeOptions.commandMode === NONE && commandModeOptions.commandMode !== NONE)
|
||||
requestRefresh = true;
|
||||
|
||||
/* Refresh the page if we have lost Game Master priviledges */
|
||||
if (this.#commandModeOptions.commandMode === GAME_MASTER && commandModeOptions.commandMode !== GAME_MASTER)
|
||||
location.reload();
|
||||
|
||||
/* Check if any option has changed */
|
||||
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);
|
||||
|
||||
if (requestRefresh)
|
||||
refreshAll();
|
||||
}
|
||||
|
||||
#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 "../unit/unitdatabase";
|
||||
import { aircraftDatabase } from "../unit/aircraftdatabase";
|
||||
import { helicopterDatabase } from "../unit/helicopterdatabase";
|
||||
import { groundUnitDatabase } from "../unit/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) {
|
||||
const φ1 = deg2rad(lat1); // φ, λ in radians
|
||||
const φ2 = deg2rad(lat2);
|
||||
@@ -26,6 +36,16 @@ export function distance(lat1: number, lon1: number, lat2: number, lon2: number)
|
||||
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) {
|
||||
var dir = D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N";
|
||||
var deg = 0 | (D < 0 ? (D = -D) : D);
|
||||
@@ -184,4 +204,194 @@ export function mToNm(m: number) {
|
||||
|
||||
export function nmToFt(nm: number) {
|
||||
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,5 +1,5 @@
|
||||
import { getUnitsManager } from "..";
|
||||
import { Unit } from "../units/unit";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class HotgroupPanel extends Panel {
|
||||
|
||||
@@ -1,28 +1,71 @@
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class LogPanel extends Panel
|
||||
{
|
||||
#logs: String[];
|
||||
export class LogPanel extends Panel {
|
||||
#open: boolean = false;
|
||||
#queuedMessages: number = 0;
|
||||
#scrolledDown: boolean = true;
|
||||
#logs: {[key: string]: string} = {};
|
||||
|
||||
constructor(ID: string)
|
||||
{
|
||||
constructor(ID: string) {
|
||||
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)
|
||||
{
|
||||
var logs = data["logs"];
|
||||
for (let idx in logs)
|
||||
{
|
||||
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;
|
||||
appendLogs(logs: {[key: string]: string}) {
|
||||
Object.keys(logs).forEach((key: string) => {
|
||||
if (!(key in this.#logs)) {
|
||||
this.#logs[key] = logs[key];
|
||||
this.appendLog(logs[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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,7 +1,7 @@
|
||||
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 { Unit } from "../units/unit";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
import formatcoords from "formatcoords";
|
||||
|
||||
@@ -36,7 +36,7 @@ export class MouseInfoPanel extends Panel {
|
||||
var selectedUnitPosition = null;
|
||||
var selectedUnits = getUnitsManager().getSelectedUnits();
|
||||
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 */
|
||||
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);
|
||||
|
||||
var bullseyes = getMissionData().getBullseyes();
|
||||
var bullseyes = getMissionHandler().getBullseyes();
|
||||
for (let idx in bullseyes)
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,12 +2,13 @@ import { SVGInjector } from "@tanem/svg-injector";
|
||||
import { getUnitsManager } from "..";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Slider } from "../controls/slider";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { Unit } from "../units/unit";
|
||||
import { aircraftDatabase } from "../unit/aircraftdatabase";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
import { Switch } from "../controls/switch";
|
||||
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 { GeneralSettings, Radio, TACAN } from "../@types/unit";
|
||||
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
@@ -21,6 +22,8 @@ export class UnitControlPanel extends Panel {
|
||||
#radioCallsignDropdown: Dropdown;
|
||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||
#advancedSettingsDialog: HTMLElement;
|
||||
#units: Unit[] = [];
|
||||
#selectedUnitsTypes: string[] = [];
|
||||
|
||||
constructor(ID: string) {
|
||||
super(ID);
|
||||
@@ -33,8 +36,9 @@ export class UnitControlPanel extends Panel {
|
||||
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getUnitsManager().selectedUnitsSetSpeedType(value? "GS": "CAS"); });
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["ROE"] = ROEs.map((option: string, index: number) => {
|
||||
return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions[index], () => { getUnitsManager().selectedUnitsSetROE(option); });
|
||||
// Reversing the ROEs so that the least "aggressive" option is always on the left
|
||||
this.#optionButtons["ROE"] = ROEs.slice(0).reverse().map((option: string, index: number) => {
|
||||
return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions.slice(0).reverse()[index], () => { getUnitsManager().selectedUnitsSetROE(option); });
|
||||
});
|
||||
|
||||
this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string, index: number) => {
|
||||
@@ -94,17 +98,19 @@ export class UnitControlPanel extends Panel {
|
||||
}
|
||||
|
||||
addButtons() {
|
||||
var units = getUnitsManager().getSelectedUnits();
|
||||
if (units.length < 20) {
|
||||
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...units.map((unit: Unit, index: number) => {
|
||||
this.#units = getUnitsManager().getSelectedUnits();
|
||||
this.#selectedUnitsTypes = getUnitsManager().getSelectedUnitsTypes();
|
||||
|
||||
if (this.#units.length < 20) {
|
||||
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...this.#units.map((unit: Unit, index: number) => {
|
||||
var button = document.createElement("button");
|
||||
var callsign = unit.getBaseData().unitName || "";
|
||||
var label = unit.getDatabase()?.getByName(unit.getBaseData().name)?.label || unit.getBaseData().name;
|
||||
var callsign = unit.getUnitName() || "";
|
||||
var label = unit.getDatabase()?.getByName(unit.getName())?.label || unit.getName();
|
||||
|
||||
button.setAttribute("data-label", label);
|
||||
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.addEventListener("click", () => {
|
||||
@@ -123,37 +129,34 @@ export class UnitControlPanel extends Panel {
|
||||
update() {
|
||||
if (this.getVisible()){
|
||||
const element = this.getElement();
|
||||
const units = getUnitsManager().getSelectedUnits();
|
||||
const selectedUnitsTypes = getUnitsManager().getSelectedUnitsTypes();
|
||||
|
||||
if (element != null && units.length > 0) {
|
||||
if (element != null && this.#units.length > 0) {
|
||||
/* Toggle visibility of control elements */
|
||||
element.toggleAttribute("data-show-categories-tooltip", selectedUnitsTypes.length > 1);
|
||||
element.toggleAttribute("data-show-speed-slider", selectedUnitsTypes.length == 1);
|
||||
element.toggleAttribute("data-show-altitude-slider", selectedUnitsTypes.length == 1 && (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-categories-tooltip", this.#selectedUnitsTypes.length > 1);
|
||||
element.toggleAttribute("data-show-speed-slider", this.#selectedUnitsTypes.length == 1);
|
||||
element.toggleAttribute("data-show-altitude-slider", this.#selectedUnitsTypes.length == 1 && (this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-roe", true);
|
||||
element.toggleAttribute("data-show-threat", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-emissions-countermeasures", (selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")) && !(selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-on-off", (selectedUnitsTypes.includes("GroundUnit") || selectedUnitsTypes.includes("NavyUnit")) && !(selectedUnitsTypes.includes("Aircraft") || selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-follow-roads", (selectedUnitsTypes.length == 1 && selectedUnitsTypes.includes("GroundUnit")));
|
||||
element.toggleAttribute("data-show-advanced-settings-button", units.length == 1);
|
||||
element.toggleAttribute("data-show-threat", (this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")) && !(this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-emissions-countermeasures", (this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")) && !(this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-on-off", (this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit")) && !(this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-follow-roads", (this.#selectedUnitsTypes.length == 1 && this.#selectedUnitsTypes.includes("GroundUnit")));
|
||||
element.toggleAttribute("data-show-advanced-settings-button", this.#units.length == 1);
|
||||
|
||||
/* Flight controls */
|
||||
var desiredAltitude: number | undefined = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitude});
|
||||
var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredAltitudeType});
|
||||
var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeed});
|
||||
var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().desiredSpeedType});
|
||||
var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().onOff});
|
||||
var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getTaskData().followRoads});
|
||||
|
||||
if (selectedUnitsTypes.length == 1) {
|
||||
if (this.#selectedUnitsTypes.length == 1) {
|
||||
/* Flight controls */
|
||||
var desiredAltitude = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()});
|
||||
var desiredAltitudeType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitudeType()});
|
||||
var desiredSpeed = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeed()});
|
||||
var desiredSpeedType = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeedType()});
|
||||
var onOff = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getOnOff()});
|
||||
var followRoads = getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getFollowRoads()});
|
||||
|
||||
this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "AGL": undefined, false);
|
||||
this.#speedTypeSwitch.setValue(desiredSpeedType != undefined? desiredSpeedType == "GS": undefined, false);
|
||||
|
||||
this.#speedSlider.setMinMax(minSpeedValues[selectedUnitsTypes[0]], maxSpeedValues[selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setMinMax(minAltitudeValues[selectedUnitsTypes[0]], maxAltitudeValues[selectedUnitsTypes[0]]);
|
||||
this.#speedSlider.setIncrement(speedIncrements[selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setIncrement(altitudeIncrements[selectedUnitsTypes[0]]);
|
||||
this.#speedSlider.setMinMax(minSpeedValues[this.#selectedUnitsTypes[0]], maxSpeedValues[this.#selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setMinMax(minAltitudeValues[this.#selectedUnitsTypes[0]], maxAltitudeValues[this.#selectedUnitsTypes[0]]);
|
||||
this.#speedSlider.setIncrement(speedIncrements[this.#selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setIncrement(altitudeIncrements[this.#selectedUnitsTypes[0]]);
|
||||
|
||||
this.#speedSlider.setActive(desiredSpeed != undefined);
|
||||
if (desiredSpeed != undefined)
|
||||
@@ -170,15 +173,15 @@ export class UnitControlPanel extends Panel {
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["ROE"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().ROE === button.value))
|
||||
button.classList.toggle("selected", this.#units.every((unit: Unit) => unit.getROE() === button.value))
|
||||
});
|
||||
|
||||
this.#optionButtons["reactionToThreat"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().reactionToThreat === button.value))
|
||||
button.classList.toggle("selected", this.#units.every((unit: Unit) => unit.getReactionToThreat() === button.value))
|
||||
});
|
||||
|
||||
this.#optionButtons["emissionsCountermeasures"].forEach((button: HTMLButtonElement) => {
|
||||
button.classList.toggle("selected", units.every((unit: Unit) => unit.getOptionsData().emissionsCountermeasures === button.value))
|
||||
button.classList.toggle("selected", this.#units.every((unit: Unit) => unit.getEmissionsCountermeasures() === button.value))
|
||||
});
|
||||
|
||||
this.#onOffSwitch.setValue(onOff, false);
|
||||
@@ -207,11 +210,11 @@ export class UnitControlPanel extends Panel {
|
||||
const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement;
|
||||
|
||||
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 AWACS = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS");
|
||||
const radioMHz = Math.floor(unit.getOptionsData().radio.frequency / 1000000);
|
||||
const radioDecimals = (unit.getOptionsData().radio.frequency / 1000000 - radioMHz) * 1000;
|
||||
const radioMHz = Math.floor(unit.getRadio().frequency / 1000000);
|
||||
const radioDecimals = (unit.getRadio().frequency / 1000000 - radioMHz) * 1000;
|
||||
|
||||
/* Activate the correct options depending on unit type */
|
||||
this.#advancedSettingsDialog.toggleAttribute("data-show-settings", !tanker && !AWACS);
|
||||
@@ -223,28 +226,28 @@ export class UnitControlPanel extends Panel {
|
||||
|
||||
/* Set common properties */
|
||||
// Name
|
||||
unitNameEl.innerText = unit.getBaseData().unitName;
|
||||
unitNameEl.innerText = unit.getUnitName();
|
||||
|
||||
// General settings
|
||||
prohibitJettisonCheckbox.checked = unit.getOptionsData().generalSettings.prohibitJettison;
|
||||
prohibitAfterburnerCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAfterburner;
|
||||
prohibitAACheckbox.checked = unit.getOptionsData().generalSettings.prohibitAA;
|
||||
prohibitAGCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAG;
|
||||
prohibitAirWpnCheckbox.checked = unit.getOptionsData().generalSettings.prohibitAirWpn;
|
||||
prohibitJettisonCheckbox.checked = unit.getGeneralSettings().prohibitJettison;
|
||||
prohibitAfterburnerCheckbox.checked = unit.getGeneralSettings().prohibitAfterburner;
|
||||
prohibitAACheckbox.checked = unit.getGeneralSettings().prohibitAA;
|
||||
prohibitAGCheckbox.checked = unit.getGeneralSettings().prohibitAG;
|
||||
prohibitAirWpnCheckbox.checked = unit.getGeneralSettings().prohibitAirWpn;
|
||||
|
||||
// Tasking
|
||||
tankerCheckbox.checked = unit.getTaskData().isTanker;
|
||||
AWACSCheckbox.checked = unit.getTaskData().isAWACS;
|
||||
tankerCheckbox.checked = unit.getIsTanker();
|
||||
AWACSCheckbox.checked = unit.getIsAWACS();
|
||||
|
||||
// TACAN
|
||||
TACANCheckbox.checked = unit.getOptionsData().TACAN.isOn;
|
||||
TACANChannelInput.value = String(unit.getOptionsData().TACAN.channel);
|
||||
TACANCallsignInput.value = String(unit.getOptionsData().TACAN.callsign);
|
||||
this.#TACANXYDropdown.setValue(unit.getOptionsData().TACAN.XY);
|
||||
TACANCheckbox.checked = unit.getTACAN().isOn;
|
||||
TACANChannelInput.value = String(unit.getTACAN().channel);
|
||||
TACANCallsignInput.value = String(unit.getTACAN().callsign);
|
||||
this.#TACANXYDropdown.setValue(unit.getTACAN().XY);
|
||||
|
||||
// Radio
|
||||
radioMhzInput.value = String(radioMHz);
|
||||
radioCallsignNumberInput.value = String(unit.getOptionsData().radio.callsignNumber);
|
||||
radioCallsignNumberInput.value = String(unit.getRadio().callsignNumber);
|
||||
this.#radioDecimalsDropdown.setValue("." + radioDecimals);
|
||||
|
||||
if (tanker) /* Set tanker specific options */
|
||||
@@ -255,7 +258,7 @@ export class UnitControlPanel extends Panel {
|
||||
this.#radioCallsignDropdown.setOptions(["Enfield", "Springfield", "Uzi", "Colt", "Dodge", "Ford", "Chevy", "Pontiac"]);
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { getUnitsManager } from "..";
|
||||
import { Ammo } from "../@types/unit";
|
||||
import { ConvertDDToDMS, rad2deg } from "../other/utils";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { Unit } from "../units/unit";
|
||||
import { aircraftDatabase } from "../unit/aircraftdatabase";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class UnitInfoPanel extends Panel {
|
||||
@@ -51,21 +52,21 @@ export class UnitInfoPanel extends Panel {
|
||||
#onUnitUpdate(unit: Unit) {
|
||||
if (this.getElement() != null && this.getVisible() && unit.getSelected()) {
|
||||
|
||||
const baseData = unit.getBaseData();
|
||||
const baseData = unit.getData();
|
||||
|
||||
/* Set the unit info */
|
||||
this.#unitLabel.innerText = aircraftDatabase.getByName(baseData.name)?.label || baseData.name;
|
||||
this.#unitName.innerText = baseData.unitName;
|
||||
if (unit.getMissionData().flags.Human)
|
||||
if (unit.getHuman())
|
||||
this.#unitControl.innerText = "Human";
|
||||
else if (baseData.controlled)
|
||||
this.#unitControl.innerText = "Olympus controlled";
|
||||
else
|
||||
this.#unitControl.innerText = "DCS Controlled";
|
||||
this.#fuelBar.style.width = String(unit.getMissionData().fuel + "%");
|
||||
this.#fuelPercentage.dataset.percentage = "" + unit.getMissionData().fuel;
|
||||
this.#currentTask.dataset.currentTask = unit.getTaskData().currentTask !== "" ? unit.getTaskData().currentTask : "No task";
|
||||
this.#currentTask.dataset.coalition = unit.getMissionData().coalition;
|
||||
this.#fuelBar.style.width = String(unit.getFuel() + "%");
|
||||
this.#fuelPercentage.dataset.percentage = "" + unit.getFuel();
|
||||
this.#currentTask.dataset.currentTask = unit.getTask() !== "" ? unit.getTask() : "No task";
|
||||
this.#currentTask.dataset.coalition = unit.getCoalition();
|
||||
|
||||
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 == '');
|
||||
@@ -74,13 +75,13 @@ export class UnitInfoPanel extends Panel {
|
||||
const items = <HTMLElement>this.#loadoutContainer.querySelector("#loadout-items");
|
||||
|
||||
if (items) {
|
||||
const ammo = Object.values(unit.getMissionData().ammo);
|
||||
const ammo = Object.values(unit.getAmmo());
|
||||
if (ammo.length > 0) {
|
||||
items.replaceChildren(...Object.values(unit.getMissionData().ammo).map(
|
||||
(ammo: any) => {
|
||||
items.replaceChildren(...Object.values(unit.getAmmo()).map(
|
||||
(ammo: Ammo) => {
|
||||
var el = document.createElement("div");
|
||||
el.dataset.qty = ammo.count;
|
||||
el.dataset.item = ammo.desc.displayName;
|
||||
el.dataset.qty = `${ammo.quantity}`;
|
||||
el.dataset.item = ammo.name;
|
||||
return el;
|
||||
}
|
||||
));
|
||||
|
||||
164
client/src/server/dataextractor.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
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");
|
||||
}
|
||||
|
||||
setSeekPosition(seekPosition: number) {
|
||||
this.#seekPosition = seekPosition;
|
||||
}
|
||||
|
||||
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.every((value: number, idx: number) => {
|
||||
if (value === 0) {
|
||||
stringLength = idx;
|
||||
return false;
|
||||
} else
|
||||
return true;
|
||||
});
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LatLng } from 'leaflet';
|
||||
import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setConnectionStatus } from '..';
|
||||
import { SpawnOptions } from '../controls/mapcontextmenu';
|
||||
import { getConnectionStatusPanel, getInfoPopup, getLogPanel, getMissionHandler, getServerStatusPanel, getUnitsManager, getWeaponsManager, setLoginStatus } from '..';
|
||||
import { GeneralSettings, Radio, TACAN } from '../@types/unit';
|
||||
import { NONE, ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants';
|
||||
|
||||
var connected: boolean = false;
|
||||
var paused: boolean = false;
|
||||
@@ -8,28 +9,36 @@ var paused: boolean = false;
|
||||
var REST_ADDRESS = "http://localhost:30000/olympus";
|
||||
var DEMO_ADDRESS = window.location.href + "demo";
|
||||
const UNITS_URI = "units";
|
||||
const WEAPONS_URI = "weapons";
|
||||
const LOGS_URI = "logs";
|
||||
const AIRBASES_URI = "airbases";
|
||||
const BULLSEYE_URI = "bullseyes";
|
||||
const MISSION_URI = "mission";
|
||||
|
||||
var username = "";
|
||||
var credentials = "";
|
||||
var password = "";
|
||||
|
||||
var sessionHash: string | null = null;
|
||||
var lastUpdateTime = 0;
|
||||
var lastUpdateTimes: {[key: string]: number} = {}
|
||||
lastUpdateTimes[UNITS_URI] = Date.now();
|
||||
lastUpdateTimes[WEAPONS_URI] = Date.now();
|
||||
lastUpdateTimes[LOGS_URI] = Date.now();
|
||||
lastUpdateTimes[AIRBASES_URI] = Date.now();
|
||||
lastUpdateTimes[BULLSEYE_URI] = Date.now();
|
||||
lastUpdateTimes[MISSION_URI] = Date.now();
|
||||
|
||||
var demoEnabled = false;
|
||||
|
||||
export function toggleDemoEnabled() {
|
||||
demoEnabled = !demoEnabled;
|
||||
}
|
||||
|
||||
export function setCredentials(newUsername: string, newCredentials: string) {
|
||||
export function setCredentials(newUsername: string, newPassword: string) {
|
||||
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();
|
||||
|
||||
/* Assemble the request options string */
|
||||
@@ -37,24 +46,36 @@ export function GET(callback: CallableFunction, uri: string, options?: { time?:
|
||||
if (options?.time != undefined)
|
||||
optionsString = `time=${options.time}`;
|
||||
|
||||
|
||||
/* On the connection */
|
||||
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) {
|
||||
if (xmlHttp.status == 200) {
|
||||
var data = JSON.parse(xmlHttp.responseText);
|
||||
if (uri !== UNITS_URI || (options?.time == 0) || parseInt(data.time) > lastUpdateTime) {
|
||||
callback(data);
|
||||
lastUpdateTime = parseInt(data.time);
|
||||
if (isNaN(lastUpdateTime))
|
||||
lastUpdateTime = 0;
|
||||
setConnected(true);
|
||||
/* Success */
|
||||
setConnected(true);
|
||||
if (xmlHttp.responseType == 'arraybuffer')
|
||||
lastUpdateTimes[uri] = callback(xmlHttp.response);
|
||||
else {
|
||||
const result = JSON.parse(xmlHttp.responseText);
|
||||
lastUpdateTimes[uri] = callback(result);
|
||||
|
||||
if (result.frameRate !== undefined && result.load !== undefined)
|
||||
getServerStatusPanel().update(result.frameRate, result.load);
|
||||
}
|
||||
} else if (xmlHttp.status == 401) {
|
||||
/* Bad credentials */
|
||||
console.error("Incorrect username/password");
|
||||
setConnectionStatus("failed");
|
||||
setLoginStatus("failed");
|
||||
} else {
|
||||
/* Failure, probably disconnected */
|
||||
setConnected(false);
|
||||
}
|
||||
};
|
||||
@@ -69,8 +90,8 @@ export function POST(request: object, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", demoEnabled ? DEMO_ADDRESS : REST_ADDRESS);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
if (credentials)
|
||||
xmlHttp.setRequestHeader("Authorization", "Basic " + credentials);
|
||||
if (username && password)
|
||||
xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${username}:${password}`));
|
||||
xmlHttp.onreadystatechange = () => {
|
||||
callback();
|
||||
};
|
||||
@@ -103,8 +124,8 @@ export function getBullseye(callback: CallableFunction) {
|
||||
GET(callback, BULLSEYE_URI);
|
||||
}
|
||||
|
||||
export function getLogs(callback: CallableFunction) {
|
||||
GET(callback, LOGS_URI);
|
||||
export function getLogs(callback: CallableFunction, refresh: boolean = false) {
|
||||
GET(callback, LOGS_URI, { time: refresh ? 0 : lastUpdateTimes[LOGS_URI]});
|
||||
}
|
||||
|
||||
export function getMission(callback: CallableFunction) {
|
||||
@@ -112,7 +133,11 @@ export function getMission(callback: CallableFunction) {
|
||||
}
|
||||
|
||||
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 getWeapons(callback: CallableFunction, refresh: boolean = false) {
|
||||
GET(callback, WEAPONS_URI, { time: refresh ? 0 : lastUpdateTimes[WEAPONS_URI] }, 'arraybuffer');
|
||||
}
|
||||
|
||||
export function addDestination(ID: number, path: any) {
|
||||
@@ -133,15 +158,27 @@ export function spawnExplosion(intensity: number, latlng: LatLng) {
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function spawnGroundUnit(spawnOptions: SpawnOptions) {
|
||||
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition };
|
||||
var data = { "spawnGround": command }
|
||||
export function spawnAircrafts(units: any, coalition: string, airbaseName: string, immediate: boolean, spawnPoints: number) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
var data = { "spawnAircrafts": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function spawnAircraft(spawnOptions: SpawnOptions) {
|
||||
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 data = { "spawnAir": command }
|
||||
export function spawnHelicopters(units: any, coalition: string, airbaseName: string, immediate: boolean, spawnPoints: number) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
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, () => { });
|
||||
}
|
||||
|
||||
@@ -167,8 +204,8 @@ export function cloneUnit(ID: number, latlng: LatLng) {
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function deleteUnit(ID: number, explosion: boolean) {
|
||||
var command = { "ID": ID, "explosion": explosion };
|
||||
export function deleteUnit(ID: number, explosion: boolean, immediate: boolean) {
|
||||
var command = { "ID": ID, "explosion": explosion, "immediate": immediate };
|
||||
var data = { "deleteUnit": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
@@ -222,19 +259,19 @@ export function createFormation(ID: number, isLeader: boolean, wingmenIDs: numbe
|
||||
}
|
||||
|
||||
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 }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
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 }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
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 }
|
||||
POST(data, () => { });
|
||||
}
|
||||
@@ -295,53 +332,131 @@ export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolea
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: {blue: number, red: number}, eras: string[], setupTime: number) {
|
||||
var command = {
|
||||
"restrictSpawns": restrictSpawns,
|
||||
"restrictToCoalition": restrictToCoalition,
|
||||
"spawnPoints": spawnPoints,
|
||||
"eras": eras,
|
||||
"setupTime": setupTime
|
||||
};
|
||||
|
||||
var data = { "setCommandModeOptions": command };
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function startUpdate() {
|
||||
/* On the first connection, force request of full data */
|
||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
||||
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
|
||||
getMission((data: any) => { getMissionData()?.update(data) });
|
||||
getUnits((data: UnitsData) => getUnitsManager()?.update(data), true /* Does a full refresh */);
|
||||
|
||||
requestUpdate();
|
||||
requestRefresh();
|
||||
}
|
||||
|
||||
export function requestUpdate() {
|
||||
/* Main update rate = 250ms is minimum time, equal to server update time. */
|
||||
getUnits((data: UnitsData) => {
|
||||
window.setInterval(() => {
|
||||
if (!getPaused()) {
|
||||
getUnitsManager()?.update(data);
|
||||
checkSessionHash(data.sessionHash);
|
||||
}
|
||||
}, false);
|
||||
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)
|
||||
getMission((data: MissionData) => {
|
||||
checkSessionHash(data.sessionHash);
|
||||
getMissionHandler()?.updateMission(data);
|
||||
return data.time;
|
||||
});
|
||||
|
||||
// Update the list of existing units
|
||||
getUnitDataTable()?.update();
|
||||
|
||||
checkSessionHash(data.sessionHash);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
|
||||
getAirbases((data: AirbasesData) => {
|
||||
checkSessionHash(data.sessionHash);
|
||||
getMissionHandler()?.updateAirbases(data);
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE){
|
||||
getBullseye((data: BullseyesData) => {
|
||||
checkSessionHash(data.sessionHash);
|
||||
getMissionHandler()?.updateBullseyes(data);
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
|
||||
getLogs((data: any) => {
|
||||
checkSessionHash(data.sessionHash);
|
||||
getLogPanel().appendLogs(data.logs)
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
|
||||
getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, false);
|
||||
}
|
||||
}, 250);
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
|
||||
getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, false);
|
||||
}
|
||||
}, 250);
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
|
||||
getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
getConnectionStatusPanel()?.update(getConnected());
|
||||
}
|
||||
}, 5000);
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!getPaused() && getMissionHandler().getCommandModeOptions().commandMode != NONE) {
|
||||
getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, false);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
export function refreshAll() {
|
||||
getAirbases((data: AirbasesData) => {
|
||||
checkSessionHash(data.sessionHash);
|
||||
getMissionHandler()?.updateAirbases(data);
|
||||
return data.time;
|
||||
});
|
||||
|
||||
getBullseye((data: BullseyesData) => {
|
||||
checkSessionHash(data.sessionHash);
|
||||
getMissionHandler()?.updateBullseyes(data);
|
||||
return data.time;
|
||||
});
|
||||
|
||||
getLogs((data: any) => {
|
||||
checkSessionHash(data.sessionHash);
|
||||
getLogPanel().appendLogs(data.logs)
|
||||
return data.time;
|
||||
});
|
||||
|
||||
getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
|
||||
getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
window.setTimeout(() => requestRefresh(), 5000);
|
||||
}
|
||||
|
||||
export function checkSessionHash(newSessionHash: string) {
|
||||
if (sessionHash != null) {
|
||||
if (newSessionHash != sessionHash)
|
||||
if (newSessionHash !== sessionHash)
|
||||
location.reload();
|
||||
}
|
||||
else
|
||||
|
||||
4004
client/src/unit/aircraftdatabase.ts
Normal file
7137
client/src/unit/citiesDatabase.ts
Normal file
1488
client/src/unit/groundunitdatabase.ts
Normal file
621
client/src/unit/helicopterdatabase.ts
Normal file
@@ -0,0 +1,621 @@
|
||||
import { getMissionHandler } from "..";
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class HelicopterDatabase extends UnitDatabase {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.blueprints = {
|
||||
"AH-64D_BLK_II": {
|
||||
"name": "AH-64D_BLK_II",
|
||||
"coalition": "blue",
|
||||
"era": "Modern",
|
||||
"label": "AH-64D Apache",
|
||||
"shortLabel": "AH64",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AGM-114k Hellfire",
|
||||
"quantity": 8
|
||||
},
|
||||
{
|
||||
"name": "M151 Rocket Pod",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2 * M261: M151 (6PD), 2 * Hellfire station: 4*AGM-114K",
|
||||
"name": "Gun / ATGM / Rocket"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AGM-114K Hellfire",
|
||||
"quantity": 16
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "4 * Hellfire station: 4*AGM-114K",
|
||||
"name": "Gun / ATGM"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "ah-64.png"
|
||||
},
|
||||
"Ka-50_3": {
|
||||
"name": "Ka-50_3",
|
||||
"coalition": "red",
|
||||
"era": "Late Cold War",
|
||||
"label": "Ka-50 Hokum A",
|
||||
"shortLabel": "K50",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "4xIgla",
|
||||
"name": "Gun / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "S-13",
|
||||
"quantity": 10
|
||||
},
|
||||
{
|
||||
"name": "Kh-25ML",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Anti-Ship"
|
||||
],
|
||||
"code": "2xKh-25ML, 10xS-13, 4xIgla",
|
||||
"name": "Gun / ASM / Rockets / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "S-80FP",
|
||||
"quantity": 40
|
||||
},
|
||||
{
|
||||
"name": "Vikhr-M",
|
||||
"quantity": 12
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "12x9A4172, 40xS-8OFP, 4xIgla",
|
||||
"name": "Gun / ATGM / Rockets / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "S-80FP",
|
||||
"quantity": 40
|
||||
},
|
||||
{
|
||||
"name": "Vikhr-M",
|
||||
"quantity": 12
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "12x9A4172, 40xS-8OFP, 4xIgla",
|
||||
"name": "Gun / ATGM"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "FAB-500",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "S-13",
|
||||
"quantity": 10
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "10xS-13, 2xFAB-500, 4xIgla",
|
||||
"name": "Gun / Bombs / Rockets / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "ka-50.png"
|
||||
},
|
||||
"Mi-24P": {
|
||||
"name": "Mi-24P",
|
||||
"coalition": "red",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Mi-24P Hind",
|
||||
"shortLabel": "Mi24",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "S-8KOM",
|
||||
"quantity": 40
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 8
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2xB8V20 (S-8KOM)+8xATGM 9M114",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "S-24B",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "4xS-24B+4xATGM 9M114",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "GUV-1 Grenade Launcher",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "4xGUV-1 AP30+4xATGM 9M114",
|
||||
"name": "Gun / ATGM / Grenade Launcher"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-24.png"
|
||||
},
|
||||
"SA342L": {
|
||||
"name": "SA342L",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "SA342L Gazelle",
|
||||
"shortLabel": "342",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "20mm Cannon",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "SNEB68",
|
||||
"quantity": 8
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Recon"
|
||||
],
|
||||
"code": "M621, 8xSNEB68 EAP",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
}
|
||||
],
|
||||
"filename": "sa-342.png"
|
||||
},
|
||||
"SA342M": {
|
||||
"name": "SA342M",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "SA342M Gazelle",
|
||||
"shortLabel": "342",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "HOT3",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "HOT3x4",
|
||||
"name": "ATGM"
|
||||
}
|
||||
],
|
||||
"filename": "sa-342.png"
|
||||
},
|
||||
"SA342Mistral": {
|
||||
"name": "SA342Mistral",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "SA342Mistral Gazelle",
|
||||
"shortLabel": "342",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Mistral",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "Mistral x 4",
|
||||
"name": "Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "sa-342.png"
|
||||
},
|
||||
"AH-1W": {
|
||||
"name": "AH-1W",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "AH-1W Cobra",
|
||||
"shortLabel": "AH1",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "BGM-71 TOW",
|
||||
"quantity": 8
|
||||
},
|
||||
{
|
||||
"name": "Hydra-70 WP",
|
||||
"quantity": 38
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "8xBGM-71, 38xHYDRA-70 WP",
|
||||
"name": "TOW / Hydra"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Hydra-70",
|
||||
"quantity": 76
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "76xHYDRA-70",
|
||||
"name": "Hydra"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "ah-1.png"
|
||||
},
|
||||
"Mi-26": {
|
||||
"name": "Mi-26",
|
||||
"coalition": "red",
|
||||
"era": "Late Cold War",
|
||||
"label": "Mi-26 Halo",
|
||||
"shortLabel": "M26",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-26.png"
|
||||
},
|
||||
"Mi-28N": {
|
||||
"name": "Mi-28N",
|
||||
"coalition": "red",
|
||||
"era": "Modern",
|
||||
"label": "Mi-28N Havoc",
|
||||
"shortLabel": "M28",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "9M114 Shturm",
|
||||
"quantity": 16
|
||||
},
|
||||
{
|
||||
"name": "S-8",
|
||||
"quantity": 40
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "16x9M114, 40xS-8",
|
||||
"name": "ATGM / S-8"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-28.png"
|
||||
},
|
||||
"Mi-8MT": {
|
||||
"name": "Mi-8MT",
|
||||
"coalition": "red",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Mi-8MT Hip",
|
||||
"shortLabel": "Mi8",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "UPK",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "B8",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2 x UPK +2 x B8",
|
||||
"name": "Rockets / Gunpods"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-8.png"
|
||||
},
|
||||
"SH-60B": {
|
||||
"name": "SH-60B",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "SH-60B Seahawk",
|
||||
"shortLabel": "S60",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AGM-119 ASM",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "AGM-119",
|
||||
"name": "ASM"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "uh-60.png"
|
||||
},
|
||||
"UH-60A": {
|
||||
"name": "UH-60A",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "UH-60A Blackhawk",
|
||||
"shortLabel": "U60",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "uh-60.png"
|
||||
},
|
||||
"UH-1H": {
|
||||
"name": "UH-1H",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "UH-1H Huey",
|
||||
"shortLabel": "UH1",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "M134 Minigun",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "XM-158",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "M134 Minigun*2, XM158*2",
|
||||
"name": "Miniguns / XM158"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "uh-1.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 "Helicopter";
|
||||
}
|
||||
}
|
||||
|
||||
export var helicopterDatabase = new HelicopterDatabase();
|
||||
|
||||
486
client/src/unit/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();
|
||||
1156
client/src/unit/unit.ts
Normal file
196
client/src/unit/unitdatabase.ts
Normal file
@@ -0,0 +1,196 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getMissionHandler, getUnitsManager } from "..";
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
|
||||
export class UnitDatabase {
|
||||
blueprints: { [key: string]: UnitBlueprint } = {};
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Gets a specific blueprint by name */
|
||||
getByName(name: string) {
|
||||
if (name in this.blueprints)
|
||||
return this.blueprints[name];
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Gets a specific blueprint by label */
|
||||
getByLabel(label: string) {
|
||||
for (let unit in this.blueprints) {
|
||||
if (this.blueprints[unit].label === label)
|
||||
return this.blueprints[unit];
|
||||
}
|
||||
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 */
|
||||
getByRole(role: string) {
|
||||
var filteredBlueprints = this.getBlueprints();
|
||||
var units = [];
|
||||
for (let unit in filteredBlueprints) {
|
||||
var loadouts = filteredBlueprints[unit].loadouts;
|
||||
if (loadouts) {
|
||||
for (let loadout of loadouts) {
|
||||
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) {
|
||||
units.push(filteredBlueprints[unit])
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return units;
|
||||
}
|
||||
|
||||
/* Get the names of all the loadouts for a specific unit and for a specific role */
|
||||
getLoadoutNamesByRole(name: string, role: string) {
|
||||
var filteredBlueprints = this.getBlueprints();
|
||||
var loadoutsByRole = [];
|
||||
var loadouts = filteredBlueprints[name].loadouts;
|
||||
if (loadouts) {
|
||||
for (let loadout of loadouts) {
|
||||
if (loadout.roles.includes(role) || loadout.roles.includes("")) {
|
||||
loadoutsByRole.push(loadout.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return loadoutsByRole;
|
||||
}
|
||||
|
||||
/* Get the loadout content from the unit name and loadout name */
|
||||
getLoadoutByName(name: string, loadoutName: string) {
|
||||
var loadouts = this.blueprints[name].loadouts;
|
||||
if (loadouts) {
|
||||
for (let loadout of loadouts) {
|
||||
if (loadout.name === loadoutName)
|
||||
return loadout;
|
||||
}
|
||||
}
|
||||
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 { getHotgroupPanel, getInfoPopup, getMap, getUnitDataTable } from "..";
|
||||
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitsManager } from "..";
|
||||
import { Unit } from "./unit";
|
||||
import { cloneUnit } from "../server/server";
|
||||
import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots } from "../other/utils";
|
||||
import { IDLE, MOVE_UNIT } from "../map/map";
|
||||
import { cloneUnit, deleteUnit, refreshAll, spawnAircrafts, spawnGroundUnits, spawnHelicopters, spawnNavyUnits } from "../server/server";
|
||||
import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
import { groundUnitDatabase } from "./groundunitdatabase";
|
||||
import { DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT, NONE } from "../constants/constants";
|
||||
import { DataExtractor } from "../server/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 {
|
||||
#units: { [ID: number]: Unit };
|
||||
#copiedUnits: Unit[];
|
||||
#copiedUnits: any[];
|
||||
#selectionEventDisabled: boolean = false;
|
||||
#pasteDisabled: boolean = false;
|
||||
#hiddenTypes: string[] = [];
|
||||
#requestDetectionUpdate: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.#units = {};
|
||||
@@ -23,13 +32,16 @@ export class UnitsManager {
|
||||
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete());
|
||||
document.addEventListener('explodeSelectedUnits', () => this.selectedUnitsDelete(true));
|
||||
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() {
|
||||
const units = this.getUnits();
|
||||
return Object.keys(units).reduce((acc: { [key: number]: Unit }, unitId: any) => {
|
||||
const baseData = units[unitId].getBaseData();
|
||||
if (baseData.category === "Aircraft" && baseData.alive === true) {
|
||||
if (units[unitId].getCategory() === "Aircraft" && units[unitId].getAlive() === true) {
|
||||
acc[unitId] = units[unitId];
|
||||
}
|
||||
return acc;
|
||||
@@ -48,47 +60,74 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
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) {
|
||||
if (data.baseData && data.baseData.category){
|
||||
/* The name of the unit category is exactly the same as the constructor name */
|
||||
var constructor = Unit.getConstructor(data.baseData.category);
|
||||
addUnit(ID: number, category: string) {
|
||||
if (category){
|
||||
/* The name of the unit category is exactly the same as the constructor name */
|
||||
var constructor = Unit.getConstructor(category);
|
||||
if (constructor != undefined) {
|
||||
this.#units[ID] = new constructor(ID, data);
|
||||
this.#units[ID] = new constructor(ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeUnit(ID: number) {
|
||||
update(buffer: ArrayBuffer) {
|
||||
var dataExtractor = new DataExtractor(buffer);
|
||||
var updateTime = Number(dataExtractor.extractUInt64());
|
||||
while (dataExtractor.getSeekPosition() < buffer.byteLength) {
|
||||
const ID = dataExtractor.extractUInt32();
|
||||
if (!(ID in this.#units)) {
|
||||
const datumIndex = dataExtractor.extractUInt8();
|
||||
if (datumIndex == DataIndexes.category) {
|
||||
const category = dataExtractor.extractString();
|
||||
this.addUnit(ID, category);
|
||||
}
|
||||
else {
|
||||
/* Inconsistent data, we need to wait for a refresh */
|
||||
return updateTime;
|
||||
}
|
||||
}
|
||||
this.#units[ID]?.setData(dataExtractor);
|
||||
}
|
||||
|
||||
}
|
||||
if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
|
||||
/* Create a dictionary of empty detection methods arrays */
|
||||
var detectionMethods: {[key: string]: number[]} = {};
|
||||
for (let ID in this.#units) {
|
||||
const unit = this.#units[ID];
|
||||
detectionMethods[ID] = [];
|
||||
}
|
||||
|
||||
update(data: UnitsData) {
|
||||
var updatedUnits: Unit[] = [];
|
||||
Object.keys(data.units)
|
||||
.filter((ID: string) => !(ID in this.#units))
|
||||
.reduce((timeout: number, ID: string) => {
|
||||
window.setTimeout(() => {
|
||||
if (!(ID in this.#units))
|
||||
this.addUnit(parseInt(ID), data.units[ID]);
|
||||
this.#units[parseInt(ID)]?.setData(data.units[ID]);
|
||||
}, timeout);
|
||||
return timeout + 10;
|
||||
}, 10);
|
||||
/* Fill the array with the detection methods */
|
||||
for (let ID in this.#units) {
|
||||
const unit = this.#units[ID];
|
||||
if (unit.getAlive() && unit.belongsToCommandedCoalition()){
|
||||
const contacts = unit.getContacts();
|
||||
contacts.forEach((contact: Contact) => {
|
||||
const contactID = contact.ID;
|
||||
if (!(detectionMethods[contactID].includes(contact.detectionMethod)))
|
||||
detectionMethods[contactID]?.push(contact.detectionMethod);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(data.units)
|
||||
.filter((ID: string) => ID in this.#units)
|
||||
.forEach((ID: string) => {
|
||||
updatedUnits.push(this.#units[parseInt(ID)]);
|
||||
this.#units[parseInt(ID)]?.setData(data.units[ID])
|
||||
});
|
||||
/* Set the detection methods for every unit */
|
||||
for (let ID in this.#units) {
|
||||
const unit = this.#units[ID];
|
||||
unit.setDetectionMethods(detectionMethods[ID]);
|
||||
}
|
||||
|
||||
this.getSelectedUnits().forEach((unit: Unit) => {
|
||||
if (!updatedUnits.includes(unit))
|
||||
unit.setData({})
|
||||
});
|
||||
this.#requestDetectionUpdate = false;
|
||||
}
|
||||
|
||||
for (let ID in this.#units) {
|
||||
if (this.#units[ID].getSelected())
|
||||
this.#units[ID].drawLines();
|
||||
};
|
||||
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
setHiddenType(key: string, value: boolean) {
|
||||
@@ -115,7 +154,7 @@ export class UnitsManager {
|
||||
this.deselectAllUnits();
|
||||
for (let ID in this.#units) {
|
||||
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)) {
|
||||
this.#units[ID].setSelected(true);
|
||||
}
|
||||
@@ -132,11 +171,11 @@ export class UnitsManager {
|
||||
}
|
||||
if (options) {
|
||||
if (options.excludeHumans)
|
||||
selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getMissionData().flags.Human });
|
||||
selectedUnits = selectedUnits.filter((unit: Unit) => { return !unit.getHuman() });
|
||||
if (options.onlyOnePerGroup) {
|
||||
var temp: Unit[] = [];
|
||||
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);
|
||||
}
|
||||
selectedUnits = temp;
|
||||
@@ -157,36 +196,59 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
getSelectedUnitsTypes() {
|
||||
if (this.getSelectedUnits().length == 0)
|
||||
const selectedUnits = this.getSelectedUnits();
|
||||
if (selectedUnits.length == 0)
|
||||
return [];
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return unit.constructor.name
|
||||
return selectedUnits.map((unit: Unit) => {
|
||||
return unit.getCategory();
|
||||
})?.filter((value: any, index: any, array: string[]) => {
|
||||
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) {
|
||||
if (this.getSelectedUnits().length == 0)
|
||||
const selectedUnits = this.getSelectedUnits();
|
||||
if (selectedUnits.length == 0)
|
||||
return undefined;
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return selectedUnits.map((unit: Unit) => {
|
||||
return variableGetter(unit);
|
||||
})?.reduce((a: any, b: any) => {
|
||||
return a == b ? a : undefined
|
||||
return a === b ? a : undefined
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
getSelectedUnitsCoalition() {
|
||||
if (this.getSelectedUnits().length == 0)
|
||||
const selectedUnits = this.getSelectedUnits();
|
||||
if (selectedUnits.length == 0)
|
||||
return undefined;
|
||||
return this.getSelectedUnits().map((unit: Unit) => {
|
||||
return unit.getMissionData().coalition
|
||||
return selectedUnits.map((unit: Unit) => {
|
||||
return unit.getCoalition()
|
||||
})?.reduce((a: any, b: any) => {
|
||||
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 ************************/
|
||||
selectedUnitsAddDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
@@ -196,13 +258,13 @@ export class UnitsManager {
|
||||
if (mantainRelativePosition)
|
||||
unitDestinations = this.selectedUnitsComputeGroupDestination(latlng, rotation);
|
||||
else
|
||||
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng });
|
||||
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng; });
|
||||
|
||||
for (let idx in selectedUnits) {
|
||||
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 (unit.getTaskData().currentState === "Follow") {
|
||||
const leader = this.getUnitByID(unit.getFormationData().leaderID)
|
||||
if (unit.getState() === "Follow") {
|
||||
const leader = this.getUnitByID(unit.getLeaderID())
|
||||
if (leader && leader.getSelected())
|
||||
leader.addDestination(latlng);
|
||||
else
|
||||
@@ -221,8 +283,8 @@ export class UnitsManager {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
for (let idx in selectedUnits) {
|
||||
const unit = selectedUnits[idx];
|
||||
if (unit.getTaskData().currentState === "Follow") {
|
||||
const leader = this.getUnitByID(unit.getFormationData().leaderID)
|
||||
if (unit.getState() === "Follow") {
|
||||
const leader = this.getUnitByID(unit.getLeaderID())
|
||||
if (leader && leader.getSelected())
|
||||
leader.clearDestinations();
|
||||
else
|
||||
@@ -333,21 +395,25 @@ export class UnitsManager {
|
||||
for (let idx in selectedUnits) {
|
||||
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) {
|
||||
var selectedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
|
||||
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?" ) ) {
|
||||
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) {
|
||||
selectedUnits[idx].delete(explosion);
|
||||
selectedUnits[idx].delete(explosion, immediate);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `deleted`);
|
||||
}
|
||||
@@ -400,7 +466,7 @@ export class UnitsManager {
|
||||
}
|
||||
count++;
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getBaseData().unitName}`);
|
||||
this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||
}
|
||||
|
||||
selectedUnitsSetHotgroup(hotgroup: number) {
|
||||
@@ -422,7 +488,7 @@ export class UnitsManager {
|
||||
/* Compute the center of the group */
|
||||
var center = { x: 0, y: 0 };
|
||||
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.y += mercator.y / selectedUnits.length;
|
||||
});
|
||||
@@ -430,7 +496,7 @@ export class UnitsManager {
|
||||
/* Compute the distances from the center of the group */
|
||||
var unitDestinations: { [key: number]: LatLng } = {};
|
||||
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 };
|
||||
|
||||
/* Rotate the distance according to the group rotation */
|
||||
@@ -480,37 +546,199 @@ export class UnitsManager {
|
||||
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() {
|
||||
this.#copiedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
|
||||
this.#showActionMessage(this.#copiedUnits, `copied`);
|
||||
this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => {return unit.getData()}))); /* Can be applied to humans too */
|
||||
getInfoPopup().setText(`${this.#copiedUnits.length} units copied`);
|
||||
}
|
||||
|
||||
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) {
|
||||
var unit = this.#copiedUnits[idx];
|
||||
getMap().addTemporaryMarker(getMap().getMouseCoordinates());
|
||||
cloneUnit(unit.ID, getMap().getMouseCoordinates());
|
||||
this.#showActionMessage(this.#copiedUnits, `pasted`);
|
||||
avgLat += unit.position.lat / nUnits;
|
||||
avgLng += unit.position.lng / nUnits;
|
||||
}
|
||||
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";}) || groups[groupName].every((unit: any) => {return unit.category == "NavyUnit";}))) {
|
||||
var aliveUnits = groups[groupName].filter((unit: any) => {return unit.alive});
|
||||
var units = aliveUnits.map((unit: any) => {return {unitType: unit.name, location: unit.position}});
|
||||
getUnitsManager().spawnUnits(groups[groupName][0].category, 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().getCommandModeOptions().restrictSpawns && 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().getCommandModeOptions().restrictSpawns && 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().getCommandModeOptions().restrictSpawns && 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().getCommandModeOptions().restrictSpawns && 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) {
|
||||
if (!keyEventWasInInput(event) && event.key === "Delete" ) {
|
||||
this.selectedUnitsDelete();
|
||||
if (!keyEventWasInInput(event)) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
#onUnitSelection(unit: Unit) {
|
||||
if (this.getSelectedUnits().length > 0) {
|
||||
getMap().setState(MOVE_UNIT);
|
||||
/* Disable the firing of the selection event for a certain amount of time. This avoids firing many events if many units are selected */
|
||||
if (!this.#selectionEventDisabled) {
|
||||
getMap().setState(MOVE_UNIT);
|
||||
window.setTimeout(() => {
|
||||
document.dispatchEvent(new CustomEvent("unitsSelection", { detail: this.getSelectedUnits() }));
|
||||
this.#selectionEventDisabled = false;
|
||||
@@ -533,10 +761,10 @@ export class UnitsManager {
|
||||
document.dispatchEvent(new CustomEvent("unitsDeselection", { detail: this.getSelectedUnits() }));
|
||||
}
|
||||
|
||||
#showActionMessage(units: Unit[], message: string) {
|
||||
#showActionMessage(units: any[], message: string) {
|
||||
if (units.length == 1)
|
||||
getInfoPopup().setText(`${units[0].getBaseData().unitName} ${message}`);
|
||||
getInfoPopup().setText(`${units[0].getUnitName()} ${message}`);
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
@@ -1,571 +0,0 @@
|
||||
import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class HelicopterDatabase extends UnitDatabase {
|
||||
constructor() {
|
||||
super();
|
||||
this.blueprints = {
|
||||
"AH-64D_BLK_II": {
|
||||
"name": "AH-64D_BLK_II",
|
||||
"label": "AH-64D Apache",
|
||||
"shortLabel": "AH64",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AGM-114k Hellfire",
|
||||
"quantity": 8
|
||||
},
|
||||
{
|
||||
"name": "M151 Rocket Pod",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2 * M261: M151 (6PD), 2 * Hellfire station: 4*AGM-114K",
|
||||
"name": "Gun / ATGM / Rocket"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AGM-114K Hellfire",
|
||||
"quantity": 16
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "4 * Hellfire station: 4*AGM-114K",
|
||||
"name": "Gun / ATGM"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "ah-64.png"
|
||||
},
|
||||
"Ka-50_3": {
|
||||
"name": "Ka-50_3",
|
||||
"label": "Ka-50 Hokum A",
|
||||
"shortLabel": "K50",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "4xIgla",
|
||||
"name": "Gun / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "S-13",
|
||||
"quantity": 10
|
||||
},
|
||||
{
|
||||
"name": "Kh-25ML",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Anti-Ship"
|
||||
],
|
||||
"code": "2xKh-25ML, 10xS-13, 4xIgla",
|
||||
"name": "Gun / ASM / Rockets / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "S-80FP",
|
||||
"quantity": 40
|
||||
},
|
||||
{
|
||||
"name": "Vikhr-M",
|
||||
"quantity": 12
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "12x9A4172, 40xS-8OFP, 4xIgla",
|
||||
"name": "Gun / ATGM / Rockets / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "S-80FP",
|
||||
"quantity": 40
|
||||
},
|
||||
{
|
||||
"name": "Vikhr-M",
|
||||
"quantity": 12
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "12x9A4172, 40xS-8OFP, 4xIgla",
|
||||
"name": "Gun / ATGM"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Igla",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "FAB-500",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "S-13",
|
||||
"quantity": 10
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "10xS-13, 2xFAB-500, 4xIgla",
|
||||
"name": "Gun / Bombs / Rockets / Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "ka-50.png"
|
||||
},
|
||||
"Mi-24P": {
|
||||
"name": "Mi-24P",
|
||||
"label": "Mi-24P Hind",
|
||||
"shortLabel": "Mi24",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "S-8KOM",
|
||||
"quantity": 40
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 8
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2xB8V20 (S-8KOM)+8xATGM 9M114",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "S-24B",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "4xS-24B+4xATGM 9M114",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "GUV-1 Grenade Launcher",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "4xGUV-1 AP30+4xATGM 9M114",
|
||||
"name": "Gun / ATGM / Grenade Launcher"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-24.png"
|
||||
},
|
||||
"SA342L": {
|
||||
"name": "SA342L",
|
||||
"label": "SA342L Gazelle",
|
||||
"shortLabel": "342",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "20mm Cannon",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "SNEB68",
|
||||
"quantity": 8
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Recon"
|
||||
],
|
||||
"code": "M621, 8xSNEB68 EAP",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
}
|
||||
],
|
||||
"filename": "sa-342.png"
|
||||
},
|
||||
"SA342M": {
|
||||
"name": "SA342M",
|
||||
"label": "SA342M Gazelle",
|
||||
"shortLabel": "342",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "HOT3",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "HOT3x4",
|
||||
"name": "ATGM"
|
||||
}
|
||||
],
|
||||
"filename": "sa-342.png"
|
||||
},
|
||||
"SA342Mistral": {
|
||||
"name": "SA342Mistral",
|
||||
"label": "SA342Mistral Gazelle",
|
||||
"shortLabel": "342",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Mistral",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "Mistral x 4",
|
||||
"name": "Fox 2"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "sa-342.png"
|
||||
},
|
||||
"AH-1W": {
|
||||
"name": "AH-1W",
|
||||
"label": "AH-1W Cobra",
|
||||
"shortLabel": "AH1",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "BGM-71 TOW",
|
||||
"quantity": 8
|
||||
},
|
||||
{
|
||||
"name": "Hydra-70 WP",
|
||||
"quantity": 38
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "8xBGM-71, 38xHYDRA-70 WP",
|
||||
"name": "TOW / Hydra"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Hydra-70",
|
||||
"quantity": 76
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "76xHYDRA-70",
|
||||
"name": "Hydra"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "ah-1.png"
|
||||
},
|
||||
"Mi-26": {
|
||||
"name": "Mi-26",
|
||||
"label": "Mi-26 Halo",
|
||||
"shortLabel": "M26",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-26.png"
|
||||
},
|
||||
"Mi-28N": {
|
||||
"name": "Mi-28N",
|
||||
"label": "Mi-28N Havoc",
|
||||
"shortLabel": "M28",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "9M114 Shturm",
|
||||
"quantity": 16
|
||||
},
|
||||
{
|
||||
"name": "S-8",
|
||||
"quantity": 40
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "16x9M114, 40xS-8",
|
||||
"name": "ATGM / S-8"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-28.png"
|
||||
},
|
||||
"Mi-8MT": {
|
||||
"name": "Mi-8MT",
|
||||
"label": "Mi-8MT Hip",
|
||||
"shortLabel": "Mi8",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "UPK",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "B8",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2 x UPK +2 x B8",
|
||||
"name": "Rockets / Gunpods"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-8.png"
|
||||
},
|
||||
"SH-60B": {
|
||||
"name": "SH-60B",
|
||||
"label": "SH-60B Blackhawk",
|
||||
"shortLabel": "S60",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AGM-119 ASM",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "AGM-119",
|
||||
"name": "ASM"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "uh-60.png"
|
||||
},
|
||||
"UH-60A": {
|
||||
"name": "UH-60A",
|
||||
"label": "UH-60A Blackhawk",
|
||||
"shortLabel": "U60",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "uh-60.png"
|
||||
},
|
||||
"UH-1H": {
|
||||
"name": "UH-1H",
|
||||
"label": "UH-1H Huey",
|
||||
"shortLabel": "UH1",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "M134 Minigun",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "XM-158",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "M134 Minigun*2, XM158*2",
|
||||
"name": "Miniguns / XM158"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
|
||||
],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "uh-1.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export var helicopterDatabase = new HelicopterDatabase();
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
export class UnitDatabase {
|
||||
blueprints: { [key: string]: UnitBlueprint } = {};
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
/* Returns a list of all possible roles in a database */
|
||||
getRoles() {
|
||||
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 */
|
||||
getByName(name: string) {
|
||||
if (name in this.blueprints)
|
||||
return this.blueprints[name];
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Gets a specific blueprint by label */
|
||||
getByLabel(label: string) {
|
||||
for (let unit in this.blueprints) {
|
||||
if (this.blueprints[unit].label === label)
|
||||
return this.blueprints[unit];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Get all blueprints by role */
|
||||
getByRole(role: string) {
|
||||
var units = [];
|
||||
for (let unit in this.blueprints) {
|
||||
for (let loadout of this.blueprints[unit].loadouts) {
|
||||
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) {
|
||||
units.push(this.blueprints[unit])
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return units;
|
||||
}
|
||||
|
||||
/* Get the names of all the loadouts for a specific unit and for a specific role */
|
||||
getLoadoutNamesByRole(name: string, role: string) {
|
||||
var loadouts = [];
|
||||
for (let loadout of this.blueprints[name].loadouts) {
|
||||
if (loadout.roles.includes(role) || loadout.roles.includes("")) {
|
||||
loadouts.push(loadout.name)
|
||||
}
|
||||
}
|
||||
return loadouts;
|
||||
}
|
||||
|
||||
/* Get the loadout content from the unit name and loadout name */
|
||||
getLoadoutByName(name: string, loadoutName: string) {
|
||||
for (let loadout of this.blueprints[name].loadouts) {
|
||||
if (loadout.name === loadoutName)
|
||||
return loadout;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
323
client/src/weapon/weapon.ts
Normal file
@@ -0,0 +1,323 @@
|
||||
import { LatLng, DivIcon, Map } from 'leaflet';
|
||||
import { getMap, getMissionHandler, getUnitsManager } from '..';
|
||||
import { enumToCoalition, mToFt, msToKnots, rad2deg } from '../other/utils';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, IRST, OPTIC, RADAR, VISUAL } from '../constants/constants';
|
||||
import { ObjectIconOptions } from '../@types/unit';
|
||||
import { DataExtractor } from '../server/dataextractor';
|
||||
|
||||
export class Weapon extends CustomMarker {
|
||||
ID: number;
|
||||
|
||||
#alive: boolean = false;
|
||||
#coalition: string = "neutral";
|
||||
#name: string = "";
|
||||
#position: LatLng = new LatLng(0, 0, 0);
|
||||
#speed: number = 0;
|
||||
#heading: number = 0;
|
||||
|
||||
#hidden: boolean = false;
|
||||
#detectionMethods: number[] = [];
|
||||
|
||||
getAlive() {return this.#alive};
|
||||
getCoalition() {return this.#coalition};
|
||||
getName() {return this.#name};
|
||||
getPosition() {return this.#position};
|
||||
getSpeed() {return this.#speed};
|
||||
getHeading() {return this.#heading};
|
||||
|
||||
static getConstructor(type: string) {
|
||||
if (type === "Missile") return Missile;
|
||||
if (type === "Bomb") return Bomb;
|
||||
}
|
||||
|
||||
constructor(ID: number) {
|
||||
super(new LatLng(0, 0), { riseOnHover: true, keyboard: false });
|
||||
|
||||
this.ID = ID;
|
||||
|
||||
/* Deselect units if they are hidden */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { !this.getHidden() }, 300);
|
||||
});
|
||||
|
||||
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { !this.getHidden() }, 300);
|
||||
});
|
||||
|
||||
document.addEventListener("mapVisibilityOptionsChanged", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
});
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
// Overloaded by child classes
|
||||
return "";
|
||||
}
|
||||
|
||||
/********************** Unit data *************************/
|
||||
setData(dataExtractor: DataExtractor) {
|
||||
var updateMarker = !getMap().hasLayer(this);
|
||||
|
||||
var datumIndex = 0;
|
||||
while (datumIndex != DataIndexes.endOfData) {
|
||||
datumIndex = dataExtractor.extractUInt8();
|
||||
switch (datumIndex) {
|
||||
case DataIndexes.category: dataExtractor.extractString(); break;
|
||||
case DataIndexes.alive: this.setAlive(dataExtractor.extractBool()); updateMarker = true; break;
|
||||
case DataIndexes.coalition: this.#coalition = enumToCoalition(dataExtractor.extractUInt8()); break;
|
||||
case DataIndexes.name: this.#name = dataExtractor.extractString(); break;
|
||||
case DataIndexes.position: this.#position = dataExtractor.extractLatLng(); updateMarker = true; break;
|
||||
case DataIndexes.speed: this.#speed = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
case DataIndexes.heading: this.#heading = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
}
|
||||
|
||||
getData() {
|
||||
return {
|
||||
category: this.getCategory(),
|
||||
ID: this.ID,
|
||||
alive: this.#alive,
|
||||
coalition: this.#coalition,
|
||||
name: this.#name,
|
||||
position: this.#position,
|
||||
speed: this.#speed,
|
||||
heading: this.#heading
|
||||
}
|
||||
}
|
||||
|
||||
getMarkerCategory(): string {
|
||||
return "";
|
||||
}
|
||||
|
||||
getIconOptions(): ObjectIconOptions {
|
||||
// Default values, overloaded by child classes if needed
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: false,
|
||||
showHotgroup: false,
|
||||
showUnitIcon: true,
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: true,
|
||||
showCallsign: true,
|
||||
rotateToHeading: false
|
||||
}
|
||||
}
|
||||
|
||||
setAlive(newAlive: boolean) {
|
||||
this.#alive = newAlive;
|
||||
}
|
||||
|
||||
belongsToCommandedCoalition() {
|
||||
if (getMissionHandler().getCommandModeOptions().commandMode !== GAME_MASTER && getMissionHandler().getCommandedCoalition() !== this.#coalition)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
getType() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void {
|
||||
/* Set the icon */
|
||||
var icon = new DivIcon({
|
||||
className: 'leaflet-unit-icon',
|
||||
iconAnchor: [25, 25],
|
||||
iconSize: [50, 50],
|
||||
});
|
||||
this.setIcon(icon);
|
||||
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("unit");
|
||||
el.setAttribute("data-object", `unit-${this.getMarkerCategory()}`);
|
||||
el.setAttribute("data-coalition", this.#coalition);
|
||||
|
||||
// Generate and append elements depending on active options
|
||||
// Velocity vector
|
||||
if (this.getIconOptions().showVvi) {
|
||||
var vvi = document.createElement("div");
|
||||
vvi.classList.add("unit-vvi");
|
||||
vvi.toggleAttribute("data-rotate-to-heading");
|
||||
el.append(vvi);
|
||||
}
|
||||
|
||||
// Main icon
|
||||
if (this.getIconOptions().showUnitIcon) {
|
||||
var unitIcon = document.createElement("div");
|
||||
unitIcon.classList.add("unit-icon");
|
||||
var img = document.createElement("img");
|
||||
img.src = `/resources/theme/images/units/${this.getMarkerCategory()}.svg`;
|
||||
img.onload = () => SVGInjector(img);
|
||||
unitIcon.appendChild(img);
|
||||
unitIcon.toggleAttribute("data-rotate-to-heading", this.getIconOptions().rotateToHeading);
|
||||
el.append(unitIcon);
|
||||
}
|
||||
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
|
||||
/********************** Visibility *************************/
|
||||
updateVisibility() {
|
||||
const hiddenUnits = getUnitsManager().getHiddenTypes();
|
||||
var hidden = (hiddenUnits.includes(this.getMarkerCategory())) ||
|
||||
(hiddenUnits.includes(this.#coalition)) ||
|
||||
(!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0);
|
||||
|
||||
this.setHidden(hidden || !this.#alive);
|
||||
}
|
||||
|
||||
setHidden(hidden: boolean) {
|
||||
this.#hidden = hidden;
|
||||
|
||||
/* Add the marker if not present */
|
||||
if (!getMap().hasLayer(this) && !this.getHidden()) {
|
||||
if (getMap().isZooming())
|
||||
this.once("zoomend", () => {this.addTo(getMap())})
|
||||
else
|
||||
this.addTo(getMap());
|
||||
}
|
||||
|
||||
/* Hide the marker if necessary*/
|
||||
if (getMap().hasLayer(this) && this.getHidden()) {
|
||||
getMap().removeLayer(this);
|
||||
}
|
||||
}
|
||||
|
||||
getHidden() {
|
||||
return this.#hidden;
|
||||
}
|
||||
|
||||
setDetectionMethods(newDetectionMethods: number[]) {
|
||||
if (!this.belongsToCommandedCoalition()) {
|
||||
/* Check if the detection methods of this unit have changed */
|
||||
if (this.#detectionMethods.length !== newDetectionMethods.length || this.getDetectionMethods().some(value => !newDetectionMethods.includes(value))) {
|
||||
/* Force a redraw of the unit to reflect the new status of the detection methods */
|
||||
this.setHidden(true);
|
||||
this.#detectionMethods = newDetectionMethods;
|
||||
this.updateVisibility();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getDetectionMethods() {
|
||||
return this.#detectionMethods;
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
onAdd(map: Map): this {
|
||||
super.onAdd(map);
|
||||
/* If this is the first time adding this unit to the map, remove the temporary marker */
|
||||
getMap().removeTemporaryMarker(new LatLng(this.#position.lat, this.#position.lng));
|
||||
return this;
|
||||
}
|
||||
|
||||
#updateMarker() {
|
||||
this.updateVisibility();
|
||||
|
||||
/* Draw the marker */
|
||||
if (!this.getHidden()) {
|
||||
if (this.getLatLng().lat !== this.#position.lat || this.getLatLng().lng !== this.#position.lng) {
|
||||
this.setLatLng(new LatLng(this.#position.lat, this.#position.lng));
|
||||
}
|
||||
|
||||
var element = this.getElement();
|
||||
if (element != null) {
|
||||
/* Draw the velocity vector */
|
||||
element.querySelector(".unit-vvi")?.setAttribute("style", `height: ${15 + this.#speed / 5}px;`);
|
||||
|
||||
/* Set dead/alive flag */
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
|
||||
|
||||
|
||||
/* Set altitude and speed */
|
||||
if (element.querySelector(".unit-altitude"))
|
||||
(<HTMLElement>element.querySelector(".unit-altitude")).innerText = "FL" + String(Math.floor(mToFt(this.#position.alt as number) / 100));
|
||||
if (element.querySelector(".unit-speed"))
|
||||
(<HTMLElement>element.querySelector(".unit-speed")).innerText = String(Math.floor(msToKnots(this.#speed))) + "GS";
|
||||
|
||||
/* Rotate elements according to heading */
|
||||
element.querySelectorAll("[data-rotate-to-heading]").forEach(el => {
|
||||
const headingDeg = rad2deg(this.#heading);
|
||||
let currentStyle = el.getAttribute("style") || "";
|
||||
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
|
||||
});
|
||||
}
|
||||
|
||||
/* Set vertical offset for altitude stacking */
|
||||
var pos = getMap().latLngToLayerPoint(this.getLatLng()).round();
|
||||
this.setZIndexOffset(1000 + Math.floor(this.#position.alt as number) - pos.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Missile extends Weapon {
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "Missile";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC))
|
||||
return "missile";
|
||||
else
|
||||
return "aircraft";
|
||||
}
|
||||
|
||||
getIconOptions() {
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
|
||||
showHotgroup: false,
|
||||
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
|
||||
showCallsign: false,
|
||||
rotateToHeading: this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class Bomb extends Weapon {
|
||||
constructor(ID: number) {
|
||||
super(ID);
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "Bomb";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC))
|
||||
return "bomb";
|
||||
else
|
||||
return "aircraft";
|
||||
}
|
||||
|
||||
getIconOptions() {
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
|
||||
showHotgroup: false,
|
||||
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
|
||||
showCallsign: false,
|
||||
rotateToHeading: this.belongsToCommandedCoalition() || this.getDetectionMethods().includes(VISUAL) || this.getDetectionMethods().includes(OPTIC)
|
||||
};
|
||||
}
|
||||
}
|
||||
84
client/src/weapon/weaponsmanager.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { getMissionHandler, getUnitsManager } from "..";
|
||||
import { Weapon } from "./weapon";
|
||||
import { DataIndexes, GAME_MASTER } from "../constants/constants";
|
||||
import { DataExtractor } from "../server/dataextractor";
|
||||
import { Contact } from "../@types/unit";
|
||||
|
||||
export class WeaponsManager {
|
||||
#weapons: { [ID: number]: Weapon };
|
||||
#requestDetectionUpdate: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.#weapons = {};
|
||||
|
||||
document.addEventListener("commandModeOptionsChanged", () => {Object.values(this.#weapons).forEach((weapon: Weapon) => weapon.updateVisibility())});
|
||||
document.addEventListener('contactsUpdated', (e: CustomEvent) => {this.#requestDetectionUpdate = true});
|
||||
}
|
||||
|
||||
getWeapons() {
|
||||
return this.#weapons;
|
||||
}
|
||||
|
||||
getWeaponByID(ID: number) {
|
||||
if (ID in this.#weapons)
|
||||
return this.#weapons[ID];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
addWeapon(ID: number, category: string) {
|
||||
if (category){
|
||||
/* The name of the weapon category is exactly the same as the constructor name */
|
||||
var constructor = Weapon.getConstructor(category);
|
||||
if (constructor != undefined) {
|
||||
this.#weapons[ID] = new constructor(ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update(buffer: ArrayBuffer) {
|
||||
var dataExtractor = new DataExtractor(buffer);
|
||||
var updateTime = Number(dataExtractor.extractUInt64());
|
||||
while (dataExtractor.getSeekPosition() < buffer.byteLength) {
|
||||
const ID = dataExtractor.extractUInt32();
|
||||
if (!(ID in this.#weapons)) {
|
||||
const datumIndex = dataExtractor.extractUInt8();
|
||||
if (datumIndex == DataIndexes.category) {
|
||||
const category = dataExtractor.extractString();
|
||||
this.addWeapon(ID, category);
|
||||
}
|
||||
else {
|
||||
/* Inconsistent data, we need to wait for a refresh */
|
||||
return updateTime;
|
||||
}
|
||||
}
|
||||
this.#weapons[ID]?.setData(dataExtractor);
|
||||
}
|
||||
|
||||
if (this.#requestDetectionUpdate && getMissionHandler().getCommandModeOptions().commandMode != GAME_MASTER) {
|
||||
for (let ID in this.#weapons) {
|
||||
var weapon = this.#weapons[ID];
|
||||
if (!weapon.belongsToCommandedCoalition())
|
||||
weapon.setDetectionMethods(this.getWeaponDetectedMethods(weapon));
|
||||
}
|
||||
this.#requestDetectionUpdate = false;
|
||||
}
|
||||
|
||||
return updateTime;
|
||||
}
|
||||
|
||||
getWeaponDetectedMethods(weapon: Weapon) {
|
||||
var detectionMethods: number[] = [];
|
||||
var units = getUnitsManager().getUnits();
|
||||
for (let idx in units) {
|
||||
if (units[idx].getAlive() && units[idx].getIsLeader() && units[idx].getCoalition() !== "neutral" && units[idx].getCoalition() != weapon.getCoalition())
|
||||
{
|
||||
units[idx].getContacts().forEach((contact: Contact) => {
|
||||
if (contact.ID == weapon.ID && !detectionMethods.includes(contact.detectionMethod))
|
||||
detectionMethods.push(contact.detectionMethod);
|
||||
});
|
||||
}
|
||||
}
|
||||
return detectionMethods;
|
||||
}
|
||||
}
|
||||
@@ -24,8 +24,14 @@
|
||||
<%- include('panels/unitinfo.ejs') %>
|
||||
<%- include('panels/mouseinfo.ejs') %>
|
||||
<%- include('panels/connectionstatus.ejs') %>
|
||||
<%- include('panels/serverstatus.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/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="active-coalition-label" data-active-coalition="blue"></div>
|
||||
<div id="upper-bar" class="ol-panel">
|
||||
<div id="coalition-switch" class="ol-switch"></div>
|
||||
<button data-active-coalition="blue" id="aircraft-spawn-button" title="Spawn aircraft" data-on-click="contextMenuShow"
|
||||
data-on-click-params='{ "type": "aircraft" }' class="unit-spawn-button"></button>
|
||||
<button data-active-coalition="blue" id="ground-unit-spawn-button" title="Spawn ground unit" data-on-click="contextMenuShow"
|
||||
data-on-click-params='{ "type": "ground-unit" }' class="unit-spawn-button"></button>
|
||||
<button data-active-coalition="blue" id="smoke-spawn-button" title="Spawn smoke" data-on-click="contextMenuShow"
|
||||
data-on-click-params='{ "type": "smoke" }' class="unit-spawn-button"></button>
|
||||
<button data-active-coalition="blue" id="explosion-spawn-button" title="Explosion" data-on-click="contextMenuShow"
|
||||
data-on-click-params='{ "type": "explosion" }' class="unit-spawn-button"></button>
|
||||
<div id="map-contextmenu" class="ol-context-menu" oncontextmenu="return false;">
|
||||
<div id="active-coalition-label" data-coalition="blue"></div>
|
||||
<div class="upper-bar ol-panel">
|
||||
<div id="coalition-switch" class="ol-switch ol-coalition-switch"></div>
|
||||
<button data-coalition="blue" id="aircraft-spawn-button" title="Spawn aircraft" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="helicopter-spawn-button" title="Spawn helicopter" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "helicopter" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="groundunit-spawn-button" title="Spawn ground unit" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "groundunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/groundunit.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="coalition-area-button" title="Edit coalition area" data-on-click="editCoalitionArea"
|
||||
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 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 id="aircraft-role-options" class="ol-select">
|
||||
<div class="ol-select-value">Aircraft role</div>
|
||||
@@ -21,8 +34,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ol-select-container">
|
||||
<div id="aircraft-type-options" class="ol-select">
|
||||
<div class="ol-select-value">Aircraft type</div>
|
||||
<div id="aircraft-label-options" class="ol-select">
|
||||
<div class="ol-select-value">Aircraft name</div>
|
||||
<div class="ol-select-options">
|
||||
<div>Select role first</div>
|
||||
<!-- This is where all the aircraft types buttons will be shown-->
|
||||
@@ -30,7 +43,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<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-options">
|
||||
<div>Select type first</div>
|
||||
@@ -38,6 +51,15 @@
|
||||
</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">
|
||||
<dl class="ol-data-grid">
|
||||
<dt> Spawn altitude
|
||||
@@ -49,45 +71,137 @@
|
||||
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
||||
|
||||
</div>
|
||||
<div id="loadout-preview">
|
||||
<img id="unit-image" class="hide">
|
||||
<div id="loadout-list">
|
||||
<div id="aircraft-loadout-preview">
|
||||
<img id="aircraft-unit-image" class="hide">
|
||||
<div id="aircraft-loadout-list">
|
||||
</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 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 id="ground-unit-role-options" class="ol-select">
|
||||
<div class="ol-select-value">Ground unit role</div>
|
||||
<div id="helicopter-role-options" class="ol-select">
|
||||
<div class="ol-select-value">Helicopter role</div>
|
||||
<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 class="ol-select-container">
|
||||
<div id="ground-unit-type-options" class="ol-select">
|
||||
<div class="ol-select-value">Ground unit type</div>
|
||||
<div id="helicopter-label-options" class="ol-select">
|
||||
<div class="ol-select-value">Helicopter name</div>
|
||||
<div class="ol-select-options">
|
||||
<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>
|
||||
<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 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="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="green" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "green" }'>Green smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="orange" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "orange" }'>Orange smoke</button>
|
||||
</div>
|
||||
<div id="explosion-menu" class="ol-panel hide">
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 50 }'>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": 200 }'>Big explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 400 }'>Huge explosion</button>
|
||||
<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": 1 }'>Small explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 2 }'>Medium explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 3 }'>Big explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 4 }'>Huge explosion</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,17 +210,76 @@
|
||||
</div>
|
||||
|
||||
<div id="airbase-contextmenu" class="ol-panel" oncontextmenu="return false;">
|
||||
|
||||
<h3 id="airbase-name"></h3>
|
||||
|
||||
<div id="airbase-properties"></div>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<h4>Parking available:</h4>
|
||||
<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>
|
||||
</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>
|
||||
@@ -3,7 +3,7 @@
|
||||
<div id="app-summary">
|
||||
<h2>DCS Olympus</h2>
|
||||
<h4>Dynamic Unit Command</h4>
|
||||
<div class="app-version">Version <span class="app-version-number">v0.3.0</span></div>
|
||||
<div class="app-version">Version <span class="app-version-number">v0.4.1-alpha</span></div>
|
||||
</div>
|
||||
|
||||
<div id="authentication-form">
|
||||
@@ -12,7 +12,7 @@
|
||||
<button id="connection-button" class="ol-button-apply" data-on-click="tryConnection">Connect</button>
|
||||
</div>
|
||||
|
||||
<h5 id="connection-status"><br></h5>
|
||||
<h5 id="login-status"><br></h5>
|
||||
|
||||
<div id="legal-stuff">
|
||||
<h5>DISCLAIMER</h5>
|
||||
@@ -183,7 +183,6 @@
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<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>
|
||||
|
||||
@@ -231,4 +230,76 @@
|
||||
<button class="ol-button-apply" data-on-click="applyCustomFormation">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</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 id="toolbar-summary">
|
||||
<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.1-alpha</div>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.discord.com" target="_blank">Discord</a>
|
||||
@@ -14,53 +14,64 @@
|
||||
<div>
|
||||
<a href="https://github.com/Pax1601/DCSOlympus" target="_blank">Github</a>
|
||||
</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">
|
||||
<a href="" target="_blank" data-on-click="reloadPage">Restart Olympus</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map-type" class="ol-select">
|
||||
<div class="ol-select-value map-source-dropdown">
|
||||
<span>ArcGIS Satellite</span>
|
||||
<div class="ol-group">
|
||||
<div id="map-type" class="ol-select">
|
||||
<div class="ol-select-value">
|
||||
<img src="resources/theme/images/icons/map-source.svg" inject-svg> ArcGIS Satellite
|
||||
</div>
|
||||
<div class="ol-select-options">
|
||||
<!-- Here the available map sources will be listed-->
|
||||
</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 id="unit-visibility-control" class="ol-group">
|
||||
<!-- Here the available visibility controls will be listed -->
|
||||
<div id="unit-visibility-control" class="ol-group ol-navbar-buttons-group">
|
||||
<!-- Here the available visibility controls will be listed -->
|
||||
</div>
|
||||
|
||||
<div id="coalition-visibility-control" class="ol-group ol-group-button-toggle">
|
||||
|
||||
<div>
|
||||
<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>
|
||||
<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>
|
||||
<button id="coalition-visibility-control-neutral" data-on-click="toggleCoalitionVisibility"
|
||||
data-on-click-params='{ "coalition": "neutral" }'>View <span
|
||||
class="accent-neutral">NEUTRAL</span></button>
|
||||
data-on-click-params='{ "coalition": "neutral" }'><span class="accent-neutral">NEUTRAL</span></button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div id="atc-navbar-control" class="ol-group-container" data-feature-switch="atc">
|
||||
<div class="ol-group-header">ATC</div>
|
||||
<!--
|
||||
<div id="atc-navbar-control" class="ol-group-container ol-navbar-buttons-group" data-feature-switch="atc">
|
||||
<div class="ol-group">
|
||||
<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"
|
||||
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> -->
|
||||
</nav>
|
||||
@@ -1047,7 +1047,7 @@
|
||||
<dt>Open air</dt>
|
||||
<dd>5</dd>
|
||||
</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>
|
||||
</div>
|
||||
|
||||
|
||||