Merge pull request #323 from Pax1601/319-add-basic-rts-functions
319 add basic rts functions
@@ -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,7 +1,8 @@
|
||||
var basicAuth = require('express-basic-auth')
|
||||
var enc = new TextEncoder();
|
||||
|
||||
const DEMO_UNIT_DATA = {
|
||||
["1"]:{ alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "KC-135", unitName: "Cool guy 1-1", groupName: "Cool group 1", state: 3, task: "Being cool!",
|
||||
["1"]:{ category: "Aircraft", alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "KC-135", unitName: "Cool guy 1-1 who also has a very long name", groupName: "Cool group 1", state: 3, task: "Being cool!",
|
||||
hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, heading: 45, isTanker: true, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
@@ -14,15 +15,15 @@ const DEMO_UNIT_DATA = {
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [],
|
||||
activePath: [ {lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0} ]
|
||||
contacts: [{ID: 2, detectionMethod: 1}],
|
||||
activePath: [{lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0}]
|
||||
},
|
||||
["2"]:{ alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "KC-135", unitName: "Cool guy 1-2", groupName: "Cool group 2", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 36.9, lng: -116, alt: 1000 }, speed: 200, heading: 0, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
["2"]:{ category: "Aircraft", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "FA-18C_hornet", unitName: "Cool guy 1-2", groupName: "Cool group 2", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 36.9, lng: -116, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 38, lng: -117, alt: 1000 },
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 2,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
@@ -30,15 +31,57 @@ const DEMO_UNIT_DATA = {
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 4}],
|
||||
activePath: [ {lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0} ]
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
}, ["3"]:{ category: "GroundUnit", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "M-60", unitName: "Cool guy 1-3", groupName: "Cool group 3", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 2,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
}, ["4"]:{ category: "Helicopter", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "AH-64D_BLK_II", unitName: "Cool guy 1-4", groupName: "Cool group 3", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.1, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 2,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
}
|
||||
}
|
||||
|
||||
class DemoDataGenerator {
|
||||
constructor()
|
||||
constructor(app)
|
||||
{
|
||||
|
||||
app.get('/demo/units', (req, res) => this.units(req, res));
|
||||
app.get('/demo/logs', (req, res) => this.logs(req, res));
|
||||
app.get('/demo/bullseyes', (req, res) => this.bullseyes(req, res));
|
||||
app.get('/demo/airbases', (req, res) => this.airbases(req, res));
|
||||
app.get('/demo/mission', (req, res) => this.mission(req, res));
|
||||
|
||||
app.use('/demo', basicAuth({
|
||||
users: {
|
||||
'admin': 'socks',
|
||||
'blue': 'bluesocks',
|
||||
'red': 'redsocks'
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
units(req, res){
|
||||
@@ -48,7 +91,7 @@ class DemoDataGenerator {
|
||||
for (let idx in DEMO_UNIT_DATA) {
|
||||
const unit = DEMO_UNIT_DATA[idx];
|
||||
array = this.concat(array, this.uint32ToByteArray(idx));
|
||||
array = this.appendString(array, "Aircraft", 1);
|
||||
array = this.appendString(array, unit.category, 1);
|
||||
array = this.appendUint8(array, unit.alive, 2);
|
||||
array = this.appendUint8(array, unit.human, 3);
|
||||
array = this.appendUint8(array, unit.controlled, 4);
|
||||
@@ -300,8 +343,23 @@ class DemoDataGenerator {
|
||||
};
|
||||
|
||||
mission(req, res){
|
||||
var ret = {mission: {theatre: "Nevada"}};
|
||||
var ret = {mission: {theatre: "Syria"}};
|
||||
ret.time = Date.now();
|
||||
var auth = req.get("Authorization");
|
||||
if (auth) {
|
||||
var username = atob(auth.replace("Basic ", "")).split(":")[0];
|
||||
switch (username) {
|
||||
case "admin":
|
||||
ret.mission.visibilityMode = "Game master";
|
||||
break
|
||||
case "blue":
|
||||
ret.mission.visibilityMode = "Blue commander";
|
||||
break;
|
||||
case "red":
|
||||
ret.mission.visibilityMode = "Red commander";
|
||||
break;
|
||||
}
|
||||
}
|
||||
res.send(JSON.stringify(ret));
|
||||
}
|
||||
|
||||
|
||||
@@ -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,24 +292,24 @@
|
||||
}
|
||||
|
||||
/*** 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -812,16 +812,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!";
|
||||
}
|
||||
@@ -865,6 +865,19 @@ nav.ol-panel> :last-child {
|
||||
translate: 0% -300%;
|
||||
}
|
||||
|
||||
#visibiliy-mode {
|
||||
font-size: 14px;
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
#visibiliy-mode[data-mode="Blue commander"] {
|
||||
color: var(--primary-blue);
|
||||
}
|
||||
|
||||
#visibiliy-mode[data-mode="Red commander"] {
|
||||
color: var(--primary-red);
|
||||
}
|
||||
|
||||
.ol-destination-preview-icon {
|
||||
background-image: url("/resources/theme/images/markers/move.svg");
|
||||
height: 52px;
|
||||
@@ -1182,14 +1195,6 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
background-color: var(--primary-neutral);
|
||||
}
|
||||
|
||||
.ol-context-menu>div:nth-child(2) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.ol-context-menu>ul {
|
||||
max-height: 200px;
|
||||
overflow-x: hidden;
|
||||
@@ -1205,21 +1210,12 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.ol-context-menu>div:nth-child(n+3) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
.ol-context-menu .ol-select-container {
|
||||
align-self: stretch;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.ol-contexmenu-button {
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
|
||||
@@ -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,35 +58,28 @@
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#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,
|
||||
#iads-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;
|
||||
.upper-bar svg>* {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
#ground-ol-contexmenu-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/ground.svg");
|
||||
background-size: 48px;
|
||||
.upper-bar svg {
|
||||
width: 22px;
|
||||
margin: 0px 5px;
|
||||
}
|
||||
|
||||
#smoke-spawn-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/smoke.svg");
|
||||
background-size: 48px;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
[data-coalition="blue"]#active-coalition-label,
|
||||
@@ -117,7 +133,8 @@
|
||||
content: "Create neutral unit";
|
||||
}
|
||||
|
||||
#loadout-preview {
|
||||
#aircraft-loadout-preview,
|
||||
#helicopter-loadout-preview {
|
||||
align-content: space-between;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
@@ -126,14 +143,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;
|
||||
@@ -186,17 +205,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;
|
||||
@@ -350,7 +380,7 @@
|
||||
height: fit-content;
|
||||
position: absolute;
|
||||
row-gap: 5px;
|
||||
width: 250px;
|
||||
width: 300px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
@@ -360,30 +390,14 @@
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#iads-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/sam.svg");
|
||||
background-size: 48px;
|
||||
}
|
||||
|
||||
#cap-button {
|
||||
background-image: url("/resources/theme/images/buttons/spawn/aircraft.svg");
|
||||
background-size: 48px;
|
||||
}
|
||||
|
||||
#coalitionarea-back-button {
|
||||
background-image: url("/resources/theme/images/buttons/other/back.svg");
|
||||
background-size: 48px;
|
||||
}
|
||||
|
||||
#coalitionarea-delete-button {
|
||||
background-image: url("/resources/theme/images/buttons/other/delete.svg");
|
||||
background-size: 48px;
|
||||
}
|
||||
|
||||
#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;
|
||||
@@ -404,3 +418,25 @@
|
||||
#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%;
|
||||
}
|
||||
@@ -23,9 +23,9 @@
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="26.15625"
|
||||
inkscape:cx="20.587814"
|
||||
inkscape:cy="20.109916"
|
||||
inkscape:zoom="13.078125"
|
||||
inkscape:cx="19.918757"
|
||||
inkscape:cy="17.663082"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1912"
|
||||
@@ -34,8 +34,8 @@
|
||||
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 24.337929,21.064066 H 7.766909 c -0.572865,0 -1.035689,0.462823 -1.035689,1.035689 0,0.572864 0.462824,1.035688 1.035689,1.035688 h 16.57102 c 0.572865,0 1.035689,-0.462824 1.035689,-1.035688 0,-0.572866 -0.462824,-1.035689 -1.035689,-1.035689 z M 20.024324,15.15093 c -0.404566,-0.404566 -1.061581,-0.404566 -1.466147,0 l -1.336685,1.339923 V 9.6714892 c 0,-0.572865 -0.462823,-1.035688 -1.035689,-1.035688 -0.572866,0 -1.035688,0.462823 -1.035688,1.035688 V 16.490853 L 13.810192,15.15093 c -0.404566,-0.404566 -1.061581,-0.404566 -1.466147,0 -0.404565,0.404566 -0.404565,1.061581 0,1.466147 l 3.107066,3.107066 c 0.404565,0.404566 1.061582,0.404566 1.466147,0 l 3.107066,-3.107066 c 0.404567,-0.404566 0.404567,-1.061581 0,-1.466147 z"
|
||||
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.0323653"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0541362"
|
||||
sodipodi:nodetypes="ssssssssccssscssccccs" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
@@ -24,8 +24,8 @@
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
showgrid="false"
|
||||
inkscape:zoom="13.078125"
|
||||
inkscape:cx="27.794504"
|
||||
inkscape:cy="19.192354"
|
||||
inkscape:cx="27.870968"
|
||||
inkscape:cy="20.415771"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="1912"
|
||||
@@ -34,7 +34,7 @@
|
||||
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 14.670468,9.3574565 -0.569725,0.8515865 h 4.347895 L 17.878913,9.3574565 C 17.833935,9.2914885 17.758972,9.2495089 17.678011,9.2495089 h -2.809639 c -0.08096,0 -0.155924,0.038981 -0.200903,0.1079476 z m 4.407864,-0.7976134 1.100466,1.6491999 h 0.413801 1.439301 0.239884 c 0.398807,0 0.719652,0.320845 0.719652,0.719652 0,0.398807 -0.320845,0.719651 -0.719652,0.719651 H 22.0319 v 9.115583 c 0,1.325358 -1.073479,2.398837 -2.398837,2.398837 h -6.716745 c -1.325357,0 -2.398838,-1.073479 -2.398838,-2.398837 v -9.115583 h -0.239884 c -0.3988062,0 -0.7196507,-0.320844 -0.7196507,-0.719651 0,-0.398807 0.3208445,-0.719652 0.7196507,-0.719652 h 0.239884 1.439303 0.413799 l 1.100467,-1.6521984 c 0.311849,-0.4647748 0.836595,-0.7466382 1.397323,-0.7466382 h 2.809639 c 0.560728,0 1.085474,0.2818634 1.397322,0.7466382 z m -7.121549,3.0885029 v 9.115583 c 0,0.530743 0.428792,0.959535 0.959535,0.959535 h 6.716745 c 0.530743,0 0.959536,-0.428792 0.959536,-0.959535 v -9.115583 z m 2.398837,1.919071 v 6.236977 c 0,0.263872 -0.215895,0.479767 -0.479767,0.479767 -0.263872,0 -0.479767,-0.215895 -0.479767,-0.479767 v -6.236977 c 0,-0.263873 0.215895,-0.479768 0.479767,-0.479768 0.263872,0 0.479767,0.215895 0.479767,0.479768 z m 2.398838,0 v 6.236977 c 0,0.263872 -0.215896,0.479767 -0.479767,0.479767 -0.263873,0 -0.479768,-0.215895 -0.479768,-0.479767 v -6.236977 c 0,-0.263873 0.215895,-0.479768 0.479768,-0.479768 0.263871,0 0.479767,0.215895 0.479767,0.479768 z m 2.398838,0 v 6.236977 c 0,0.263872 -0.215896,0.479767 -0.479768,0.479767 -0.263871,0 -0.479767,-0.215895 -0.479767,-0.479767 v -6.236977 c 0,-0.263873 0.215896,-0.479768 0.479767,-0.479768 0.263872,0 0.479768,0.215895 0.479768,0.479768 z"
|
||||
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.0299854;stroke-opacity:1" />
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0589579;stroke-opacity:1" />
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB 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 |
|
Before Width: | Height: | Size: 10 KiB 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 |
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;
|
||||
}
|
||||
3
client/src/@types/dom.d.ts
vendored
@@ -17,7 +17,8 @@ interface CustomEventMap {
|
||||
"groupCreation": CustomEvent<Unit[]>,
|
||||
"groupDeletion": CustomEvent<Unit[]>,
|
||||
"mapStateChanged": CustomEvent<string>,
|
||||
"mapContextMenu": CustomEvent<>
|
||||
"mapContextMenu": CustomEvent<>,
|
||||
"visibilityModeChanged": CustomEvent<string>,
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
5
client/src/@types/server.d.ts
vendored
@@ -1,8 +1,3 @@
|
||||
interface UnitsData {
|
||||
units: string,
|
||||
sessionHash: string
|
||||
}
|
||||
|
||||
interface AirbasesData {
|
||||
airbases: {[key: string]: any},
|
||||
}
|
||||
|
||||
3
client/src/@types/unit.d.ts
vendored
@@ -8,7 +8,8 @@ interface UnitIconOptions {
|
||||
showShortLabel: boolean,
|
||||
showFuel: boolean,
|
||||
showAmmo: boolean,
|
||||
showSummary: boolean,
|
||||
showSummary: boolean,
|
||||
showCallsign: boolean,
|
||||
rotateToHeading: boolean
|
||||
}
|
||||
|
||||
|
||||
8
client/src/@types/unitdatabase.d.ts
vendored
@@ -14,11 +14,11 @@ interface LoadoutBlueprint {
|
||||
|
||||
interface UnitBlueprint {
|
||||
name: string;
|
||||
era?: string[];
|
||||
type?: string;
|
||||
era: string[];
|
||||
label: string;
|
||||
shortLabel: string;
|
||||
type?: string;
|
||||
range?: string;
|
||||
loadouts: LoadoutBlueprint[];
|
||||
filename: string;
|
||||
loadouts?: LoadoutBlueprint[];
|
||||
filename?: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
import { LatLng, LatLngBounds, TileLayer, tileLayer } from "leaflet";
|
||||
import { LatLng, LatLngBounds } from "leaflet";
|
||||
|
||||
export const HIDE_ALL = "Hide all";
|
||||
export const GAME_MASTER = "Game master";
|
||||
export const BLUE_COMMANDER = "Blue commander";
|
||||
export const RED_COMMANDER = "Red commander";
|
||||
|
||||
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"];
|
||||
@@ -127,11 +139,11 @@ 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 COALITIONAREA_INTERACT = "Interact with Coalition Areas"
|
||||
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
|
||||
export const visibilityControlsTootlips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"];
|
||||
|
||||
export const IADSRoles: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05};
|
||||
export const IADSTypes = ["AAA", "MANPADS", "SAM Sites", "Radar"];
|
||||
export const IADSDensities: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05};
|
||||
|
||||
export enum DataIndexes {
|
||||
startOfData = 0,
|
||||
|
||||
@@ -50,7 +50,7 @@ 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;
|
||||
}
|
||||
|
||||
enableLandButton(enableLandButton: boolean) {
|
||||
@@ -60,8 +60,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());
|
||||
|
||||
@@ -1,30 +1,38 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getMap, getUnitsManager } from "..";
|
||||
import { IADSRoles } from "../constants/constants";
|
||||
import { GAME_MASTER, IADSTypes } from "../constants/constants";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { Dropdown } from "./dropdown";
|
||||
import { Slider } from "./slider";
|
||||
import { Switch } from "./switch";
|
||||
import { groundUnitDatabase } from "../units/groundunitdatabase";
|
||||
|
||||
export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
#coalitionSwitch: Switch;
|
||||
#coalitionArea: CoalitionArea | null = null;
|
||||
#iadsDensitySlider: Slider;
|
||||
#iadsRoleDropdown: Dropdown;
|
||||
|
||||
//#iadsPeriodDropdown: Dropdown;
|
||||
#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.#iadsRoleDropdown = new Dropdown("iads-units-role-options", () => { });
|
||||
//this.#iadsPeriodDropdown = new Dropdown("iads-period-options", () => {});
|
||||
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)
|
||||
@@ -33,6 +41,12 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
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);
|
||||
@@ -40,41 +54,36 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuCreateIads", (e: any) => {
|
||||
const values: { [key: string]: boolean } = {};
|
||||
const element = this.#iadsRoleDropdown.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;
|
||||
}
|
||||
|
||||
const area = this.getCoalitionArea();
|
||||
if (area)
|
||||
getUnitsManager().createIADS(area, values, this.#iadsDensitySlider.getValue());
|
||||
getUnitsManager().createIADS(area, this.#getCheckboxOptions(this.#iadsTypesDropdown), this.#getCheckboxOptions(this.#iadsErasDropdown), this.#getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue());
|
||||
})
|
||||
|
||||
/* Create the checkboxes to select the unit roles */
|
||||
this.#iadsRoleDropdown.setOptionsElements(Object.keys(IADSRoles).map((unitRole: string) => {
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("ol-checkbox");
|
||||
var label = document.createElement("label");
|
||||
label.title = `Add ${unitRole}s to the IADS`;
|
||||
var input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.checked = true;
|
||||
var span = document.createElement("span");
|
||||
span.innerText = unitRole;
|
||||
label.appendChild(input);
|
||||
label.appendChild(span);
|
||||
div.appendChild(label);
|
||||
return div as HTMLElement;
|
||||
this.#iadsTypesDropdown.setOptionsElements(IADSTypes.map((role: string) => {
|
||||
return this.#createCheckboxOption(role);
|
||||
}));
|
||||
|
||||
|
||||
/* Create the checkboxes to select the unit periods */
|
||||
this.#iadsErasDropdown.setOptionsElements(groundUnitDatabase.getEras().map((era: string) => {
|
||||
return this.#createCheckboxOption(era);
|
||||
}));
|
||||
|
||||
/* Create the checkboxes to select the unit ranges */
|
||||
this.#iadsRangesDropdown.setOptionsElements(groundUnitDatabase.getRanges().map((range: string) => {
|
||||
return this.#createCheckboxOption(range);
|
||||
}));
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
show(x: number, y: number, latlng: LatLng) {
|
||||
super.show(x, y, latlng);
|
||||
if (getUnitsManager().getCommandMode() !== 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");
|
||||
@@ -104,9 +113,40 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
}
|
||||
|
||||
#onSwitchClick(value: boolean) {
|
||||
this.getCoalitionArea()?.setCoalition(value ? "red" : "blue");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
if (getUnitsManager().getCommandMode() == GAME_MASTER) {
|
||||
this.getCoalitionArea()?.setCoalition(value ? "red" : "blue");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#createCheckboxOption(text: string) {
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("ol-checkbox");
|
||||
var label = document.createElement("label");
|
||||
label.title = `Add ${text}s to the IADS`;
|
||||
var input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.checked = true;
|
||||
var span = document.createElement("span");
|
||||
span.innerText = text;
|
||||
label.appendChild(input);
|
||||
label.appendChild(span);
|
||||
div.appendChild(label);
|
||||
return div as HTMLElement;
|
||||
}
|
||||
|
||||
#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,34 +1,38 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getActiveCoalition, getMap, setActiveCoalition } from "..";
|
||||
import { spawnAircraft, spawnExplosion, spawnGroundUnit, spawnSmoke } from "../server/server";
|
||||
import { getActiveCoalition, getMap, getUnitsManager, setActiveCoalition } from "..";
|
||||
import { spawnAircrafts, spawnExplosion, spawnGroundUnits, spawnHelicopters, spawnNavyUnits, spawnSmoke } from "../server/server";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { groundUnitDatabase } from "../units/groundunitdatabase";
|
||||
import { helicopterDatabase } from "../units/helicopterdatabase";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
import { Dropdown } from "./dropdown";
|
||||
import { Switch } from "./switch";
|
||||
import { Slider } from "./slider";
|
||||
import { ftToM } from "../other/utils";
|
||||
|
||||
export interface SpawnOptions {
|
||||
role: string;
|
||||
name: string;
|
||||
latlng: LatLng;
|
||||
coalition: string;
|
||||
loadout?: string | null;
|
||||
airbaseName?: string | null;
|
||||
altitude?: number | null;
|
||||
immediate?: boolean;
|
||||
}
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { navyUnitDatabase } from "../units/navyunitdatabase";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
|
||||
export class MapContextMenu extends ContextMenu {
|
||||
#coalitionSwitch: Switch;
|
||||
#aircraftRoleDropdown: Dropdown;
|
||||
#aircraftTypeDropdown: Dropdown;
|
||||
#aircraftNameDropdown: Dropdown;
|
||||
#aircraftCountDropdown: Dropdown;
|
||||
#aircraftLoadoutDropdown: Dropdown;
|
||||
#aircrafSpawnAltitudeSlider: Slider;
|
||||
#groundUnitRoleDropdown: Dropdown;
|
||||
#aircraftSpawnAltitudeSlider: Slider;
|
||||
#helicopterRoleDropdown: Dropdown;
|
||||
#helicopterNameDropdown: Dropdown;
|
||||
#helicopterCountDropdown: Dropdown;
|
||||
#helicopterLoadoutDropdown: Dropdown;
|
||||
#helicopterSpawnAltitudeSlider: Slider;
|
||||
#groundUnitTypeDropdown: Dropdown;
|
||||
#spawnOptions: SpawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), loadout: null, coalition: "blue", airbaseName: null, altitude: ftToM(20000) };
|
||||
#groundUnitNameDropdown: Dropdown;
|
||||
#groundUnitCountDropdown: Dropdown;
|
||||
#navyUnitTypeDropdown: Dropdown;
|
||||
#navyUnitNameDropdown: Dropdown;
|
||||
#navyUnitCountDropdown: Dropdown;
|
||||
#spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: ftToM(20000), count: 1 };
|
||||
#coalitionArea: CoalitionArea | null = null;
|
||||
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
@@ -36,29 +40,80 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.#coalitionSwitch = new Switch("coalition-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick(e));
|
||||
|
||||
/* Aircraft menu */
|
||||
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));
|
||||
this.#aircraftNameDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftName(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.#helicopterNameDropdown = new Dropdown("helicopter-type-options", (type: string) => this.#setHelicopterName(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.#groundUnitNameDropdown = new Dropdown("groundunit-name-options", (name: string) => this.#setGroundUnitName(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.#navyUnitNameDropdown = new Dropdown("navyunit-name-options", (name: string) => this.#setNavyUnitName(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();
|
||||
this.hideSubMenus(e.detail.type);
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuDeployAircraft", () => {
|
||||
this.hide();
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions);
|
||||
spawnAircraft(this.#spawnOptions);
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||
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);
|
||||
}
|
||||
spawnAircrafts(units, getActiveCoalition(), this.#spawnOptions.airbaseName, false);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuDeployHelicopter", () => {
|
||||
this.hide();
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||
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);
|
||||
}
|
||||
spawnHelicopters(units, getActiveCoalition(), this.#spawnOptions.airbaseName, false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -66,8 +121,29 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.hide();
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions);
|
||||
spawnGroundUnit(this.#spawnOptions);
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||
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;
|
||||
}
|
||||
spawnGroundUnits(units, getActiveCoalition(), false);
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener("contextMenuDeployNavyUnits", () => {
|
||||
this.hide();
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition());
|
||||
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;
|
||||
}
|
||||
spawnNavyUnits(units, getActiveCoalition(), false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -80,63 +156,127 @@ 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);
|
||||
}
|
||||
});
|
||||
|
||||
this.hide();
|
||||
}
|
||||
|
||||
show(x: number, y: number, latlng: LatLng) {
|
||||
this.#spawnOptions.airbaseName = null;
|
||||
this.#spawnOptions.airbaseName = "";
|
||||
super.show(x, y, latlng);
|
||||
this.#spawnOptions.latlng = latlng;
|
||||
this.showUpperBar();
|
||||
|
||||
this.showAltitudeSlider();
|
||||
|
||||
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 (getUnitsManager().getCommandMode() !== 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-ol-contexmenu-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.#resetAircraftRole();
|
||||
this.#resetAircraftType();
|
||||
this.#resetGroundUnitRole();
|
||||
this.#resetAircraftName();
|
||||
this.#resetHelicopterRole();
|
||||
this.#resetHelicopterName();
|
||||
this.#resetGroundUnitType();
|
||||
this.#resetGroundUnitName();
|
||||
this.#resetNavyUnitType();
|
||||
this.#resetNavyUnitName();
|
||||
this.#aircraftCountDropdown.setValue("1");
|
||||
this.#helicopterCountDropdown.setValue("1");
|
||||
this.#groundUnitCountDropdown.setValue("1");
|
||||
this.clip();
|
||||
|
||||
this.setVisibleSubMenu(type);
|
||||
}
|
||||
|
||||
hideSubMenus() {
|
||||
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("#ground-unit-spawn-menu")?.classList.toggle("hide", true);
|
||||
this.getContainer()?.querySelector("#ground-ol-contexmenu-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.#resetAircraftType();
|
||||
this.#resetGroundUnitRole();
|
||||
this.#resetAircraftName();
|
||||
this.#resetHelicopterRole();
|
||||
this.#resetHelicopterName();
|
||||
this.#resetHelicopterRole();
|
||||
this.#resetHelicopterName();
|
||||
this.#resetGroundUnitType();
|
||||
this.#resetGroundUnitName();
|
||||
this.#resetNavyUnitType();
|
||||
this.#resetNavyUnitName();
|
||||
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) {
|
||||
@@ -147,6 +287,11 @@ 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()) });
|
||||
@@ -155,45 +300,51 @@ export class MapContextMenu extends ContextMenu {
|
||||
#onSwitchRightClick(e: any) {
|
||||
this.#coalitionSwitch.setValue(undefined);
|
||||
setActiveCoalition("neutral");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) });
|
||||
}
|
||||
|
||||
/********* 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.#resetAircraftName();
|
||||
this.#aircraftNameDropdown.setOptions(aircraftDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
||||
this.#aircraftNameDropdown.selectValue(0);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#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.#aircraftNameDropdown.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.name = type;
|
||||
this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role));
|
||||
#setAircraftName(label: string) {
|
||||
this.#resetAircraftName();
|
||||
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();
|
||||
}
|
||||
|
||||
#resetAircraftType() {
|
||||
#resetAircraftName() {
|
||||
(<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();
|
||||
}
|
||||
|
||||
@@ -204,7 +355,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
(<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;
|
||||
@@ -215,40 +366,146 @@ 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();
|
||||
|
||||
const types = groundUnitsDatabase.getByRole(role).map((blueprint) => { return blueprint.label });
|
||||
this.#groundUnitTypeDropdown.setOptions(types);
|
||||
this.#groundUnitTypeDropdown.selectValue(0);
|
||||
this.#resetHelicopterName();
|
||||
this.#helicopterNameDropdown.setOptions(helicopterDatabase.getByRole(role).map((blueprint) => { return blueprint.label }));
|
||||
this.#helicopterNameDropdown.selectValue(0);
|
||||
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();
|
||||
|
||||
const roles = groundUnitsDatabase.getRoles();
|
||||
this.#groundUnitRoleDropdown.setOptions(roles);
|
||||
#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.#helicopterNameDropdown.reset();
|
||||
this.#helicopterRoleDropdown.setOptions(helicopterDatabase.getRoles());
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#setGroundUnitType(label: string) {
|
||||
this.#resetGroundUnitType();
|
||||
var type = groundUnitsDatabase.getByLabel(label)?.name || null;
|
||||
if (type != null) {
|
||||
this.#spawnOptions.name = type;
|
||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false;
|
||||
#setHelicopterName(label: string) {
|
||||
this.#resetHelicopterName();
|
||||
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();
|
||||
}
|
||||
|
||||
#resetHelicopterName() {
|
||||
(<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();
|
||||
}
|
||||
|
||||
#setHelicopterCount(count: string) {
|
||||
this.#spawnOptions.count = parseInt(count);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#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.#resetGroundUnitName();
|
||||
|
||||
const types = groundUnitDatabase.getByType(role).map((blueprint) => { return blueprint.label });
|
||||
this.#groundUnitNameDropdown.setOptions(types);
|
||||
this.#groundUnitNameDropdown.selectValue(0);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#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.#groundUnitNameDropdown.reset();
|
||||
|
||||
const types = groundUnitDatabase.getTypes();
|
||||
this.#groundUnitTypeDropdown.setOptions(types);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#setGroundUnitName(label: string) {
|
||||
this.#resetGroundUnitName();
|
||||
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();
|
||||
}
|
||||
|
||||
#resetGroundUnitName() {
|
||||
(<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();
|
||||
}
|
||||
|
||||
/********* Navyunit spawn menu *********/
|
||||
#setNavyUnitType(role: string) {
|
||||
this.#resetNavyUnitName();
|
||||
|
||||
const types = navyUnitDatabase.getByType(role).map((blueprint) => { return blueprint.label });
|
||||
this.#navyUnitNameDropdown.setOptions(types);
|
||||
this.#navyUnitNameDropdown.selectValue(0);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#resetNavyUnitType() {
|
||||
(<HTMLButtonElement>this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true;
|
||||
this.#navyUnitTypeDropdown.reset();
|
||||
this.#navyUnitNameDropdown.reset();
|
||||
|
||||
const types = navyUnitDatabase.getTypes();
|
||||
this.#navyUnitTypeDropdown.setOptions(types);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
#setNavyUnitName(label: string) {
|
||||
this.#resetNavyUnitName();
|
||||
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();
|
||||
}
|
||||
|
||||
#resetNavyUnitName() {
|
||||
(<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();
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ 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";
|
||||
|
||||
var map: Map;
|
||||
|
||||
@@ -44,8 +45,8 @@ function setup() {
|
||||
featureSwitches = new FeatureSwitches();
|
||||
|
||||
/* Initialize base functionalitites */
|
||||
map = new Map('map-container');
|
||||
unitsManager = new UnitsManager();
|
||||
map = new Map('map-container');
|
||||
missionHandler = new MissionHandler();
|
||||
|
||||
/* Panels */
|
||||
@@ -184,12 +185,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,7 +205,6 @@ function setupEvents() {
|
||||
else
|
||||
img.onload = () => SVGInjector(img);
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
export function getMap() {
|
||||
@@ -252,15 +252,25 @@ export function getHotgroupPanel() {
|
||||
}
|
||||
|
||||
export function setActiveCoalition(newActiveCoalition: string) {
|
||||
activeCoalition = newActiveCoalition;
|
||||
if (getUnitsManager().getCommandMode() == GAME_MASTER)
|
||||
activeCoalition = newActiveCoalition;
|
||||
}
|
||||
|
||||
export function getActiveCoalition() {
|
||||
return activeCoalition;
|
||||
if (getUnitsManager().getCommandMode() == GAME_MASTER)
|
||||
return activeCoalition;
|
||||
else {
|
||||
if (getUnitsManager().getCommandMode() == BLUE_COMMANDER)
|
||||
return "blue";
|
||||
else if (getUnitsManager().getCommandMode() == 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;
|
||||
}
|
||||
@@ -269,4 +279,5 @@ export function getInfoPopup() {
|
||||
return infoPopup;
|
||||
}
|
||||
|
||||
window.onload = setup;
|
||||
window.onload = setup;
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { DomUtil, LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions } from "leaflet";
|
||||
import { getMap } from "..";
|
||||
import { getMap, 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";
|
||||
@@ -16,10 +17,17 @@ export class CoalitionArea extends Polygon {
|
||||
options = {};
|
||||
|
||||
options.bubblingMouseEvents = false;
|
||||
options.interactive = false;
|
||||
|
||||
super(latlngs, options);
|
||||
this.#setColors();
|
||||
this.#registerCallbacks();
|
||||
|
||||
|
||||
if (getUnitsManager().getCommandMode() == BLUE_COMMANDER)
|
||||
this.setCoalition("blue");
|
||||
else if (getUnitsManager().getCommandMode() == RED_COMMANDER)
|
||||
this.setCoalition("red");
|
||||
|
||||
}
|
||||
|
||||
setCoalition(coalition: string) {
|
||||
@@ -35,6 +43,7 @@ export class CoalitionArea extends Polygon {
|
||||
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[];
|
||||
@@ -62,16 +71,6 @@ export class CoalitionArea extends Polygon {
|
||||
return this.#editing;
|
||||
}
|
||||
|
||||
setInteractive(interactive: boolean) {
|
||||
this.setOpacity(interactive? 1: 0.5);
|
||||
this.options.interactive = interactive;
|
||||
|
||||
if (interactive)
|
||||
DomUtil.addClass(this.getElement() as HTMLElement, 'leaflet-interactive');
|
||||
else
|
||||
DomUtil.removeClass(this.getElement() as HTMLElement, 'leaflet-interactive');
|
||||
}
|
||||
|
||||
addTemporaryLatLng(latlng: LatLng) {
|
||||
this.#activeIndex++;
|
||||
var latlngs = this.getLatLngs()[0] as LatLng[];
|
||||
@@ -91,6 +90,12 @@ export class CoalitionArea extends Polygon {
|
||||
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 });
|
||||
@@ -156,16 +161,9 @@ export class CoalitionArea extends Polygon {
|
||||
if (!this.getEditing()) {
|
||||
getMap().deselectAllCoalitionAreas();
|
||||
this.setSelected(true);
|
||||
getMap().showCoalitionAreaContextMenu(e, this);
|
||||
}
|
||||
else
|
||||
this.setEditing(false);
|
||||
});
|
||||
}
|
||||
|
||||
onRemove(map: Map): this {
|
||||
super.onRemove(map);
|
||||
this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getMap()));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
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";
|
||||
@@ -12,7 +12,7 @@ import { DestinationPreviewMarker } from "./destinationpreviewmarker";
|
||||
import { TemporaryUnitMarker } from "./temporaryunitmarker";
|
||||
import { ClickableMiniMap } from "./clickableminimap";
|
||||
import { SVGInjector } from '@tanem/svg-injector'
|
||||
import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, FIRE_AT_AREA, MOVE_UNIT, CARPET_BOMBING, BOMBING, COALITIONAREA_INTERACT } from "../constants/constants";
|
||||
import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, FIRE_AT_AREA, MOVE_UNIT, CARPET_BOMBING, BOMBING } from "../constants/constants";
|
||||
import { TargetMarker } from "./targetmarker";
|
||||
import { CoalitionArea } from "./coalitionarea";
|
||||
import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu";
|
||||
@@ -116,16 +116,7 @@ export class Map extends L.Map {
|
||||
getUnitsManager().setHiddenType(ev.detail.type, !el?.classList.contains("off"));
|
||||
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
||||
});
|
||||
|
||||
document.addEventListener("toggleCoalitionAreaInteraction", (ev: CustomEventInit) => {
|
||||
const el = ev.detail._element;
|
||||
/* Add listener to set the button to off if the state changes */
|
||||
document.addEventListener("mapStateChanged", () => el?.classList.toggle("off", !(this.getState() === COALITIONAREA_INTERACT)));
|
||||
if (this.getState() !== COALITIONAREA_INTERACT)
|
||||
this.setState(COALITIONAREA_INTERACT);
|
||||
else
|
||||
this.setState(IDLE);
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener("toggleCoalitionAreaDraw", (ev: CustomEventInit) => {
|
||||
const el = ev.detail._element;
|
||||
@@ -186,22 +177,12 @@ export class Map extends L.Map {
|
||||
this.#updateCursor();
|
||||
|
||||
/* Operations to perform if you are NOT in a state */
|
||||
if (this.#state !== COALITIONAREA_INTERACT) {
|
||||
this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => {
|
||||
coalitionArea.setInteractive(false);
|
||||
});
|
||||
}
|
||||
if (this.#state !== COALITIONAREA_DRAW_POLYGON) {
|
||||
this.#deselectCoalitionAreas();
|
||||
}
|
||||
|
||||
/* Operations to perform if you ARE in a state */
|
||||
if (this.#state === COALITIONAREA_INTERACT) {
|
||||
this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => {
|
||||
coalitionArea.setInteractive(true);
|
||||
});
|
||||
}
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
this.#coalitionAreas.push(new CoalitionArea([]));
|
||||
this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
|
||||
}
|
||||
@@ -231,11 +212,9 @@ export class Map extends L.Map {
|
||||
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"));
|
||||
}
|
||||
|
||||
@@ -248,11 +227,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() {
|
||||
@@ -263,11 +240,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);
|
||||
}
|
||||
|
||||
@@ -279,11 +254,9 @@ export class Map extends L.Map {
|
||||
this.#airbaseContextMenu.hide();
|
||||
}
|
||||
|
||||
showCoalitionAreaContextMenu(e: any, coalitionArea: CoalitionArea) {
|
||||
showCoalitionAreaContextMenu(x: number, y: number, latlng: L.LatLng, coalitionArea: CoalitionArea) {
|
||||
this.hideAllContextMenus();
|
||||
var x = e.originalEvent.x;
|
||||
var y = e.originalEvent.y;
|
||||
this.#coalitionAreaContextMenu.show(x, y, e.latlng);
|
||||
this.#coalitionAreaContextMenu.show(x, y, latlng);
|
||||
this.#coalitionAreaContextMenu.setCoalitionArea(coalitionArea);
|
||||
}
|
||||
|
||||
@@ -390,8 +363,8 @@ export class Map extends L.Map {
|
||||
}
|
||||
}
|
||||
|
||||
addTemporaryMarker(spawnOptions: SpawnOptions) {
|
||||
var marker = new TemporaryUnitMarker(spawnOptions);
|
||||
addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string) {
|
||||
var marker = new TemporaryUnitMarker(latlng, name, coalition);
|
||||
marker.addTo(this);
|
||||
this.#temporaryMarkers.push(marker);
|
||||
}
|
||||
@@ -419,6 +392,12 @@ export class Map extends L.Map {
|
||||
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);
|
||||
}
|
||||
|
||||
/* Event handlers */
|
||||
#onClick(e: any) {
|
||||
if (!this.#preventLeftClick) {
|
||||
@@ -449,7 +428,19 @@ export class Map extends L.Map {
|
||||
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) {
|
||||
@@ -673,13 +664,13 @@ export class Map extends L.Map {
|
||||
this.#showDefaultCursor();
|
||||
} else {
|
||||
/* Hide all the unnecessary cursors depending on the active state */
|
||||
if (this.#state !== IDLE && this.#state !== COALITIONAREA_INTERACT) this.#hideDefaultCursor();
|
||||
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.#state === COALITIONAREA_INTERACT) this.#showDefaultCursor();
|
||||
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();
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
import { CustomMarker } from "./custommarker";
|
||||
import { SpawnOptions } from "../controls/mapcontextmenu";
|
||||
import { DivIcon } from "leaflet";
|
||||
import { DivIcon, LatLng } from "leaflet";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
import { getMarkerCategoryByName, getUnitDatabaseByCategory } from "../other/utils";
|
||||
|
||||
export class TemporaryUnitMarker extends CustomMarker {
|
||||
#spawnOptions: SpawnOptions;
|
||||
#name: string;
|
||||
#coalition: string;
|
||||
|
||||
constructor(spawnOptions: SpawnOptions) {
|
||||
super(spawnOptions.latlng, {interactive: false});
|
||||
this.#spawnOptions = spawnOptions;
|
||||
constructor(latlng: LatLng, name: string, coalition: string) {
|
||||
super(latlng, {interactive: false});
|
||||
this.#name = name;
|
||||
this.#coalition = coalition;
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
const category = getMarkerCategoryByName(this.#spawnOptions.name);
|
||||
const category = getMarkerCategoryByName(this.#name);
|
||||
|
||||
/* Set the icon */
|
||||
var icon = new DivIcon({
|
||||
@@ -26,7 +27,7 @@ export class TemporaryUnitMarker extends CustomMarker {
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("unit");
|
||||
el.setAttribute("data-object", `unit-${category}`);
|
||||
el.setAttribute("data-coalition", this.#spawnOptions.coalition);
|
||||
el.setAttribute("data-coalition", this.#coalition);
|
||||
|
||||
// Main icon
|
||||
var unitIcon = document.createElement("div");
|
||||
@@ -42,7 +43,7 @@ export class TemporaryUnitMarker extends CustomMarker {
|
||||
if (category == "aircraft" || category == "helicopter") {
|
||||
var shortLabel = document.createElement("div");
|
||||
shortLabel.classList.add("unit-short-label");
|
||||
shortLabel.innerText = getUnitDatabaseByCategory(category)?.getByName(this.#spawnOptions.name)?.shortLabel || "";
|
||||
shortLabel.innerText = getUnitDatabaseByCategory(category)?.getByName(this.#name)?.shortLabel || "";
|
||||
el.append(shortLabel);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LatLng } from "leaflet";
|
||||
import { getInfoPopup, getMap } from "..";
|
||||
import { getInfoPopup, getMap, getUnitsManager } from "..";
|
||||
import { Airbase } from "./airbase";
|
||||
import { Bullseye } from "./bullseye";
|
||||
|
||||
@@ -72,6 +72,9 @@ export class MissionHandler {
|
||||
getInfoPopup().setText("Map set to " + this.#theatre);
|
||||
}
|
||||
|
||||
if ("visibilityMode" in data.mission)
|
||||
getUnitsManager().setVisibilityMode(data.mission.visibilityMode);
|
||||
|
||||
if ("date" in data.mission)
|
||||
this.#date = data.mission.date;
|
||||
if ("elapsedTime" in data.mission)
|
||||
@@ -127,6 +130,6 @@ export class MissionHandler {
|
||||
}
|
||||
|
||||
#onAirbaseClick(e: any) {
|
||||
getMap().showAirbaseContextMenu(e, e.sourceTarget);
|
||||
getMap().showAirbaseContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, e.sourceTarget);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import * as turf from "@turf/turf";
|
||||
import { UnitDatabase } from "../units/unitdatabase";
|
||||
import { aircraftDatabase } from "../units/aircraftdatabase";
|
||||
import { helicopterDatabase } from "../units/helicopterdatabase";
|
||||
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
|
||||
import { groundUnitDatabase } from "../units/groundunitdatabase";
|
||||
import { Buffer } from "buffer";
|
||||
import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants";
|
||||
|
||||
@@ -35,6 +35,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);
|
||||
@@ -195,6 +205,11 @@ 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();
|
||||
@@ -216,12 +231,45 @@ export function randomPointInPoly(polygon: Polygon): LatLng {
|
||||
}
|
||||
|
||||
export function polygonArea(polygon: Polygon) {
|
||||
var poly = polygon.toGeoJSON();
|
||||
var poly = polygon.toGeoJSON();
|
||||
return turf.area(poly);
|
||||
}
|
||||
|
||||
export function randomUnitBlueprintByRole(unitDatabse: UnitDatabase, role: string) {
|
||||
const unitBlueprints = unitDatabse.getByRole(role);
|
||||
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 options.eras.reduce((value, era) => {
|
||||
return value? value: unitBlueprint.era.includes(era);
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
var index = Math.floor(Math.random() * unitBlueprints.length);
|
||||
return unitBlueprints[index];
|
||||
}
|
||||
@@ -231,13 +279,13 @@ export function getMarkerCategoryByName(name: string) {
|
||||
return "aircraft";
|
||||
else if (helicopterDatabase.getByName(name) != null)
|
||||
return "helicopter";
|
||||
else if (groundUnitsDatabase.getByName(name) != null){
|
||||
else if (groundUnitDatabase.getByName(name) != null){
|
||||
// TODO this is very messy
|
||||
var role = groundUnitsDatabase.getByName(name)?.loadouts[0].roles[0];
|
||||
return (role?.includes("SAM")) ? "groundunit-sam" : "groundunit-other";
|
||||
var type = groundUnitDatabase.getByName(name)?.type;
|
||||
return (type?.includes("SAM")) ? "groundunit-sam" : "groundunit-other";
|
||||
}
|
||||
else
|
||||
return ""; // TODO add other unit types
|
||||
return "groundunit-other"; // TODO add other unit types
|
||||
}
|
||||
|
||||
export function getUnitDatabaseByCategory(category: string) {
|
||||
@@ -246,7 +294,7 @@ export function getUnitDatabaseByCategory(category: string) {
|
||||
else if (category == "helicopter")
|
||||
return helicopterDatabase;
|
||||
else if (category.includes("groundunit"))
|
||||
return groundUnitsDatabase;
|
||||
return groundUnitDatabase;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export class UnitControlPanel extends Panel {
|
||||
/* Option buttons */
|
||||
// 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[index], () => { getUnitsManager().selectedUnitsSetROE(option); });
|
||||
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) => {
|
||||
@@ -209,7 +209,7 @@ 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.getName())?.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.getRadio().frequency / 1000000);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { LatLng } from 'leaflet';
|
||||
import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setConnectionStatus } from '..';
|
||||
import { SpawnOptions } from '../controls/mapcontextmenu';
|
||||
import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setLoginStatus } from '..';
|
||||
import { GeneralSettings, Radio, TACAN } from '../@types/unit';
|
||||
import { ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants';
|
||||
|
||||
@@ -16,7 +15,7 @@ const BULLSEYE_URI = "bullseyes";
|
||||
const MISSION_URI = "mission";
|
||||
|
||||
var username = "";
|
||||
var credentials = "";
|
||||
var password = "";
|
||||
|
||||
var sessionHash: string | null = null;
|
||||
var lastUpdateTime = 0;
|
||||
@@ -26,12 +25,12 @@ 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 */
|
||||
@@ -39,26 +38,31 @@ 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 (uri === UNITS_URI)
|
||||
xmlHttp.responseType = "arraybuffer";
|
||||
/* 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) {
|
||||
/* Success */
|
||||
setConnected(true);
|
||||
if (xmlHttp.responseType == 'arraybuffer')
|
||||
callback(xmlHttp.response);
|
||||
else {
|
||||
var data = JSON.parse(xmlHttp.responseText);
|
||||
callback(data);
|
||||
}
|
||||
else
|
||||
callback(JSON.parse(xmlHttp.responseText));
|
||||
} else if (xmlHttp.status == 401) {
|
||||
/* Bad credentials */
|
||||
console.error("Incorrect username/password");
|
||||
setConnectionStatus("failed");
|
||||
setLoginStatus("failed");
|
||||
} else {
|
||||
/* Failure, probably disconnected */
|
||||
setConnected(false);
|
||||
}
|
||||
};
|
||||
@@ -73,8 +77,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();
|
||||
};
|
||||
@@ -120,7 +124,7 @@ 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 : lastUpdateTime }, 'arraybuffer');
|
||||
}
|
||||
|
||||
export function addDestination(ID: number, path: any) {
|
||||
@@ -141,15 +145,27 @@ export function spawnExplosion(intensity: number, latlng: LatLng) {
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function spawnGroundUnit(spawnOptions: SpawnOptions) {
|
||||
var command = { "type": spawnOptions.name, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "immediate": spawnOptions.immediate? true: false };
|
||||
var data = { "spawnGround": command }
|
||||
export function spawnAircrafts(units: any, coalition: string, airbaseName: string, immediate: boolean) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate };
|
||||
var data = { "spawnAircrafts": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function spawnAircraft(spawnOptions: SpawnOptions) {
|
||||
var command = { "type": spawnOptions.name, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "altitude": spawnOptions.altitude, "payloadName": spawnOptions.loadout != null ? spawnOptions.loadout : "", "airbaseName": spawnOptions.airbaseName != null ? spawnOptions.airbaseName : "", "immediate": spawnOptions.immediate? true: false };
|
||||
var data = { "spawnAir": command }
|
||||
export function spawnHelicopters(units: any, coalition: string, airbaseName: string, immediate: boolean) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate };
|
||||
var data = { "spawnHelicopter": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function spawnGroundUnits(units: any, coalition: string, immediate: boolean) {
|
||||
var command = { "units": units, "coalition": coalition, "immediate": immediate };
|
||||
var data = { "spawnGroundUnits": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
export function spawnNavyUnits(units: any, coalition: string, immediate: boolean) {
|
||||
var command = { "units": units, "coalition": coalition, "immediate": immediate };
|
||||
var data = { "spawnNavyUnits": command }
|
||||
POST(data, () => { });
|
||||
}
|
||||
|
||||
@@ -319,11 +335,9 @@ export function startUpdate() {
|
||||
|
||||
export function requestUpdate() {
|
||||
/* Main update rate = 250ms is minimum time, equal to server update time. */
|
||||
getUnits((buffer: ArrayBuffer) => {
|
||||
if (!getPaused()) {
|
||||
getUnitsManager()?.update(buffer);
|
||||
}
|
||||
}, false);
|
||||
if (!getPaused()) {
|
||||
getUnits((buffer: ArrayBuffer) => { getUnitsManager()?.update(buffer); }, false);
|
||||
}
|
||||
window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000);
|
||||
|
||||
getConnectionStatusPanel()?.update(getConnected());
|
||||
@@ -331,7 +345,6 @@ export function requestUpdate() {
|
||||
|
||||
export function requestRefresh() {
|
||||
/* Main refresh rate = 5000ms. */
|
||||
|
||||
if (!getPaused()) {
|
||||
getAirbases((data: AirbasesData) => getMissionData()?.update(data));
|
||||
getBullseye((data: BullseyesData) => getMissionData()?.update(data));
|
||||
|
||||
@@ -2123,7 +2123,7 @@ export class AircraftDatabase extends UnitDatabase {
|
||||
"AWACS"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Blue Naval AWACS"
|
||||
"name": "Blue Navy AWACS"
|
||||
}
|
||||
],
|
||||
"filename": "e-2.png"
|
||||
@@ -3484,7 +3484,7 @@ export class AircraftDatabase extends UnitDatabase {
|
||||
},
|
||||
"Su-33": {
|
||||
"name": "Su-33",
|
||||
"label": "Su-33 Naval Flanker",
|
||||
"label": "Su-33 Navy Flanker",
|
||||
"era": ["Late Cold War", "Modern"],
|
||||
"shortLabel": "33",
|
||||
"loadouts": [
|
||||
|
||||
7137
client/src/units/citiesDatabase.ts
Normal file
1542
client/src/units/groundunitdatabase.ts
Normal file
946
client/src/units/navyunitdatabase.ts
Normal file
@@ -0,0 +1,946 @@
|
||||
import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class NavyUnitDatabase extends UnitDatabase {
|
||||
constructor() {
|
||||
super();
|
||||
this.blueprints = {
|
||||
"052B DDG-168 Guangzhou": {
|
||||
"name": "052B DDG-168 Guangzhou",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"Modern"
|
||||
],
|
||||
"label": "052B DDG-168 Guangzhou",
|
||||
"shortLabel": "052B DDG-168 Guangzhou",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"052C DDG-171 Haikou": {
|
||||
"name": "052C DDG-171 Haikou",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"Modern"
|
||||
],
|
||||
"label": "052C DDG-171 Haikou",
|
||||
"shortLabel": "052C DDG-171 Haikou",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"054A FFG-538 Yantai": {
|
||||
"name": "054A FFG-538 Yantai",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Modern"
|
||||
],
|
||||
"label": "054A FFG-538 Yantai",
|
||||
"shortLabel": "054A FFG-538 Yantai",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"Type 071": {
|
||||
"name": "Type 071",
|
||||
"type": "Transport",
|
||||
"era": [
|
||||
"Modern"
|
||||
],
|
||||
"label": "Type 071",
|
||||
"shortLabel": "Type 071",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Type 093": {
|
||||
"name": "Type 093",
|
||||
"type": "Submarine",
|
||||
"era": [
|
||||
"Modern"
|
||||
],
|
||||
"label": "Type 093",
|
||||
"shortLabel": "Type 093",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Akizuki": {
|
||||
"name": "Akizuki",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "Akizuki",
|
||||
"shortLabel": "Akizuki",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"ARA Santa Fe S-21": {
|
||||
"name": "ARA Santa Fe S-21",
|
||||
"type": "Submarine",
|
||||
"era": [
|
||||
"Early Cold War"
|
||||
],
|
||||
"label": "ARA Santa Fe S-21",
|
||||
"shortLabel": "ARA Santa Fe S-21",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"ARA Vienticinco de Mayo": {
|
||||
"name": "ARA Vienticinco de Mayo",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "ARA Vienticinco de Mayo",
|
||||
"shortLabel": "ARA Vienticinco de Mayo",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Admiral Kuznetsov": {
|
||||
"name": "Admiral Kuznetsov",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Admiral Kuznetsov",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"Albatros (Grisha-5)": {
|
||||
"name": "Albatros (Grisha-5)",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Early Cold War"
|
||||
],
|
||||
"label": "Albatros (Grisha-5)",
|
||||
"shortLabel": "Albatros (Grisha-5)",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Almirante Condell PFG-06": {
|
||||
"name": "Almirante Condell PFG-06",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "Almirante Condell PFG-06",
|
||||
"shortLabel": "Almirante Condell PFG-06",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Almirante lynch PFG-07": {
|
||||
"name": "Almirante lynch PFG-07",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "Almirante lynch PFG-07",
|
||||
"shortLabel": "Almirante lynch PFG-07",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Boat Armed Hi-Speed": {
|
||||
"name": "Boat Armed Hi-Speed",
|
||||
"type": "Fast Attack Craft",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "Boat Armed Hi-Speed",
|
||||
"shortLabel": "Boat Armed Hi-Speed",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Boat LCVP Higgins": {
|
||||
"name": "Boat LCVP Higgins",
|
||||
"type": "Landing Craft",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "Boat LCVP Higgins",
|
||||
"shortLabel": "Boat LCVP Higgins",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Boat Schnellboot type S130": {
|
||||
"name": "Boat Schnellboot type S130",
|
||||
"type": "Fast Attack Craft",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "Boat Schnellboot type S130",
|
||||
"shortLabel": "Boat Schnellboot type S130",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Bulker Handy Wind": {
|
||||
"name": "Bulker Handy Wind",
|
||||
"type": "Cargoship",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Bulker Handy Wind",
|
||||
"shortLabel": "Bulker Handy Wind",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"CV Admiral Kuznetsov(2017)": {
|
||||
"name": "CV 1143.5 Admiral Kuznetsov(2017)",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Modern"
|
||||
],
|
||||
"label": "CV Admiral Kuznetsov(2017)",
|
||||
"shortLabel": "Admiral Kuznetsov(2017)",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"CV-59 Forrestal": {
|
||||
"name": "CV-59 Forrestal",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Early Cold War"
|
||||
],
|
||||
"label": "CV-59 Forrestal",
|
||||
"shortLabel": "CV-59 Forrestal",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"CV6 USS Enterprise": {
|
||||
"name": "CV6 USS Enterprise -The Grey Ghost-",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "CV6 USS Enterprise Grey Ghost",
|
||||
"shortLabel": "CV6 USS Enterprise",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"CVN-71 Theodore Roosevelt": {
|
||||
"name": "CVN-71 Theodore Roosevelt",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "CVN-71 Theodore Roosevelt",
|
||||
"shortLabel": "CVN-71 Theodore Roosevelt",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"CVN-72 Abraham Lincoln": {
|
||||
"name": "CVN-72 Abraham Lincoln",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "CVN-72 Abraham Lincoln",
|
||||
"shortLabel": "CVN-72 Abraham Lincoln",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"CVN-73 George Washington": {
|
||||
"name": "CVN-73 George Washington",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "CVN-73 George Washington",
|
||||
"shortLabel": "CVN-73 George Washington",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"CVN-74 John C. Stennis": {
|
||||
"name": "CVN-74 John C. Stennis",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "CVN-74 John C. Stennis",
|
||||
"shortLabel": "CVN-74 John C. Stennis",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"CVN-75 Harry S. Truman": {
|
||||
"name": "CVN-75 Harry S. Truman",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "CVN-75 Harry S. Truman",
|
||||
"shortLabel": "CVN-75 Harry S. Truman",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"HMS Leeds Castle (P-258)": {
|
||||
"name": "Castle Class",
|
||||
"type": "Patrol",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "HMS Leeds Castle (P-258)",
|
||||
"shortLabel": "HMS Leeds Castle (P-258)",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DDG Arleigh Burke lla": {
|
||||
"name": "DDG Arleigh Burke lla",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "DDG Arleigh Burke lla",
|
||||
"shortLabel": "DDG Arleigh Burke",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Admiral Hipper": {
|
||||
"name": "DKM Admiral Hipper",
|
||||
"type": "Cruiser",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Admiral Hipper",
|
||||
"shortLabel": "DKM Admiral Hipper",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Admiral Scheer": {
|
||||
"name": "DKM Admiral Scheer",
|
||||
"type": "Cruiser",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Admiral Scheer",
|
||||
"shortLabel": "DKM Admiral Scheer",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Blucher": {
|
||||
"name": "DKM Blucher",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Blucher",
|
||||
"shortLabel": "DKM Blucher",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Gneisenau": {
|
||||
"name": "DKM Blucher",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Blucher",
|
||||
"shortLabel": "DKM Blucher",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Prinz Eugen": {
|
||||
"name": "DKM Prinz Eugen",
|
||||
"type": "Cruiser",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Prinz Eugen",
|
||||
"shortLabel": "DKM Prinz Eugen",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Scharnhorst": {
|
||||
"name": "Scharnhorst",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Scharnhorst",
|
||||
"shortLabel": "DKM Scharnhorst",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Tirpiz": {
|
||||
"name": "DKM Tirpiz",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Tirpiz",
|
||||
"shortLabel": "DKM Tirpiz",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"DKM Z39": {
|
||||
"name": "DKM Z39",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "DKM Z39",
|
||||
"shortLabel": "DKM Z39",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Dry cargo ship Ivanov": {
|
||||
"name": "Dry cargo ship Ivanov",
|
||||
"type": "Cargoship",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Dry cargo ship Ivanov",
|
||||
"shortLabel": "Dry cargo ship Ivanov",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Dry cargo ship Yakushev": {
|
||||
"name": "Dry cargo ship Yakushev",
|
||||
"type": "Cargoship",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Dry cargo ship Yakushev",
|
||||
"shortLabel": "Dry cargo ship Yakushev",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Elnya tanker": {
|
||||
"name": "Elnya tanker",
|
||||
"type": "Tanker",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Elnya tanker",
|
||||
"shortLabel": "Elnya tanker",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"FAC La Combattante lla": {
|
||||
"name": "FAC La Combattante lla",
|
||||
"type": "Fast Attack Craft",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "FAC La Combattante lla",
|
||||
"shortLabel": "FAC La Combattante",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Fletcher-Class destroyer": {
|
||||
"name": "Fletcher-Class destroyer",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "Fletcher-Class destroyer",
|
||||
"shortLabel": "Fletcher-Class destroyer",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"HMS Achilles (F12)": {
|
||||
"name": "HMS Achilles (F12)",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "HMS Achilles (F12)",
|
||||
"shortLabel": "HMS Achilles",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"HMS Andromeda (F57)": {
|
||||
"name": "HMS Andromeda (F57)",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "HMS Andromeda (F57)",
|
||||
"shortLabel": "HMS Andromeda",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"HMS Ariadne (F72)": {
|
||||
"name": "HMS Ariadne (F72)",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "HMS Ariadne (F72)",
|
||||
"shortLabel": "HMS Ariadne",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"HMS Invincible (R05)": {
|
||||
"name": "HMS Invincible (R05)",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "HMS Invincible (R05)",
|
||||
"shortLabel": "HMS Invincible",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Harbor Tug": {
|
||||
"name": "Harbor Tug",
|
||||
"type": "Tug",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "Harbor Tug",
|
||||
"shortLabel": "Harbor Tug",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Improved Kilo": {
|
||||
"name": "Improved Kilo",
|
||||
"type": "Submarine",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Project 636 Varshavyanka",
|
||||
"shortLabel": "Varshavyanka",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"Kilo": {
|
||||
"name": "Kilo",
|
||||
"type": "Submarine",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Project 636 Varshavyanka Basic",
|
||||
"shortLabel": "Varshavyanka Basic",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"LHA-1 Tarawa": {
|
||||
"name": "LHA-1 Tarawa",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "LHA-1 Tarawa",
|
||||
"shortLabel": "LHA-1 Tarawa",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"LS Ropucha": {
|
||||
"name": "LS Ropucha",
|
||||
"type": "Landing Craft",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "LS Ropucha",
|
||||
"shortLabel": "LS Ropucha",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"LST Mk2": {
|
||||
"name": "LST Mk2",
|
||||
"type": "Transport",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "LST Mk2",
|
||||
"shortLabel": "LST Mk2",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Molniya (Tarantul-3)": {
|
||||
"name": "Molniya (Tarantul-3)",
|
||||
"type": "Fast Attack Craft",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Molniya (Tarantul-3)",
|
||||
"shortLabel": "Molniya (Tarantul-3)",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"Moscow": {
|
||||
"name": "Moscow",
|
||||
"type": "Cruiser",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Moscow",
|
||||
"shortLabel": "Moscow",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"Neustrashimy": {
|
||||
"name": "Neustrashimy",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Neustrashimy",
|
||||
"shortLabel": "Neustrashimy",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"Oliver H. Perry": {
|
||||
"name": "Oliver H. Perry",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Mid Cold War"
|
||||
],
|
||||
"label": "Oliver H. Perry",
|
||||
"shortLabel": "Oliver H. Perry",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"Pyotr Velikiy": {
|
||||
"name": "Pyotr Velikiy",
|
||||
"type": "Cruiser",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Pyotr Velikiy",
|
||||
"shortLabel": "Pyotr Velikiy",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"Rezky (Krivak-2)": {
|
||||
"name": "Rezky (Krivak-2)",
|
||||
"type": "Frigate",
|
||||
"era": [
|
||||
"Early Cold War"
|
||||
],
|
||||
"label": "Rezky (Krivak-2)",
|
||||
"shortLabel": "Rezky",
|
||||
"range": "Short",
|
||||
"filename": ""
|
||||
},
|
||||
"Supply Ship MV Tilde": {
|
||||
"name": "Supply Ship MV Tilde",
|
||||
"type": "Transport",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Supply Ship MV Tilde",
|
||||
"shortLabel": "Supply Ship MV Tilde",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Tanker Seawise Giant": {
|
||||
"name": "Tanker Seawise Giant",
|
||||
"type": "Tanker",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Tanker Seawise Giant",
|
||||
"shortLabel": "Tanker Seawise Giant",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Ticonderoga": {
|
||||
"name": "Ticonderoga",
|
||||
"type": "Cruiser",
|
||||
"era": [
|
||||
"Late Cold War"
|
||||
],
|
||||
"label": "Ticonderoga",
|
||||
"shortLabel": "Ticonderoga",
|
||||
"range": "Medium",
|
||||
"filename": ""
|
||||
},
|
||||
"U-boat VIIC U-flak": {
|
||||
"name": "U-boat VIIC U-flak",
|
||||
"type": "Submarine",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "U-boat VIIC U-flak",
|
||||
"shortLabel": "U-boat VIIC U-flak",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Bell DD-587": {
|
||||
"name": "USS Bell DD-587",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Bell DD-587",
|
||||
"shortLabel": "USS Bell DD-587",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Cassin Young": {
|
||||
"name": "USS Cassin Young",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Cassin Young",
|
||||
"shortLabel": "USS Cassin Young",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Cotten DD-669": {
|
||||
"name": "USS Cotten DD-669",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Cotten DD-669",
|
||||
"shortLabel": "USS Cotten",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Enterprise": {
|
||||
"name": "USS Enterprise",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "CV-6 USS Enterprise",
|
||||
"shortLabel": "USS Enterprise",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Fletcher": {
|
||||
"name": "USS Fletcher",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Fletcher",
|
||||
"shortLabel": "USS Fletcher",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Franklin -Big Ben-": {
|
||||
"name": "USS Franklin -Big Ben-",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Franklin -Big Ben-",
|
||||
"shortLabel": "USS Franklin",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Gregory DD-802": {
|
||||
"name": "USS Gregory DD-802",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Gregory DD-802",
|
||||
"shortLabel": "USS Gregory",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Hopewell DD-681": {
|
||||
"name": "USS Hopewell DD-681",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Hopewell DD-681",
|
||||
"shortLabel": "USS Hopewell",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Hornet (CV-8)": {
|
||||
"name": "USS Hornet (CV-8)",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Hornet (CV-8)",
|
||||
"shortLabel": "USS Hornet (CV-8)",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Illinois BB-65": {
|
||||
"name": "USS Illinois BB-65",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Illinois BB-65",
|
||||
"shortLabel": "USS Illinois",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Iowa": {
|
||||
"name": "USS Iowa",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Iowa",
|
||||
"shortLabel": "USS Iowa",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Johnson DD-557": {
|
||||
"name": "USS Johnson DD-557",
|
||||
"type": "Destoryer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Johnson DD-557",
|
||||
"shortLabel": "USS Johnson",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Kentucky BB-66": {
|
||||
"name": "USS Kentucky BB-66",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Kentucky BB-66",
|
||||
"shortLabel": "USS Kentucky",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS La Vallette DD-448": {
|
||||
"name": "USS La Vallette DD-448",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS La Vallette DD-448",
|
||||
"shortLabel": "USS La Vallette",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Missouri": {
|
||||
"name": "USS Missouri",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Missouri",
|
||||
"shortLabel": "USS Missouri",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS New Jersey": {
|
||||
"name": "USS New Jersey",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS New Jersey",
|
||||
"shortLabel": "USS New Jersey",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Radford DD-446": {
|
||||
"name": "USS Radford DD-446",
|
||||
"type": "Destroyer",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Radford DD-446",
|
||||
"shortLabel": "USS Radford",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Samuel Chase": {
|
||||
"name": "USS Samuel Chase",
|
||||
"type": "Transport",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Samuel Chase",
|
||||
"shortLabel": "USS Samuel Chase",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"USS Winsconsin": {
|
||||
"name": "Winsconsin",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "USS Winsconsin",
|
||||
"shortLabel": "USS Winsconsin",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"WW II USS Intrepid CV-11": {
|
||||
"name": "WW II USS Intrepid CV-11",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "WW II USS Intrepid CV-11",
|
||||
"shortLabel": "USS Intrepid",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"WWII Japanese Battleship Musashi": {
|
||||
"name": "WWII Japanese Battleship Musashi",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "WWII Japanese Battleship Musashi",
|
||||
"shortLabel": "Battleship Musashi",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"WWII Japanese Battleship Yamato": {
|
||||
"name": "WWII Japanese Battleship Yamato",
|
||||
"type": "Battleship",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "WWII Japanese Battleship Yamato",
|
||||
"shortLabel": "Battleship Yamato",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"WWII USS Yorktown CV-5": {
|
||||
"name": "WWII USS Yorktown CV-5",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "WWII USS Yorktown CV-5",
|
||||
"shortLabel": "USS Yorktown",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"WWII USS Hornet CV-8": {
|
||||
"name": "WWII USS Hornet CV-8",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "WWII USS Hornet CV-8",
|
||||
"shortLabel": "USS Hornet",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"ZUIKAKU": {
|
||||
"name": "ZUIKAKU",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": [
|
||||
"WW2"
|
||||
],
|
||||
"label": "ZUIKAKU",
|
||||
"shortLabel": "ZUIKAKU",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
},
|
||||
"Zwezdny": {
|
||||
"name": "Zwezdny",
|
||||
"type": "Civilian Boat",
|
||||
"era": [
|
||||
"Modern"
|
||||
],
|
||||
"label": "Zwezdny",
|
||||
"shortLabel": "Zwezdny",
|
||||
"range": "",
|
||||
"filename": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export var navyUnitDatabase = new NavyUnitDatabase();
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map } from 'leaflet';
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet';
|
||||
import { getMap, getUnitsManager } from '..';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg } from '../other/utils';
|
||||
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad } from '../other/utils';
|
||||
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { UnitDatabase } from './unitdatabase';
|
||||
import { TargetMarker } from '../map/targetmarker';
|
||||
import { BOMBING, CARPET_BOMBING, DataIndexes, FIRE_AT_AREA, IDLE, MOVE_UNIT, ROEs, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants';
|
||||
import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, HIDE_ALL, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants';
|
||||
import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, UnitIconOptions } from '../@types/unit';
|
||||
import { DataExtractor } from './dataextractor';
|
||||
|
||||
@@ -88,6 +88,7 @@ export class Unit extends CustomMarker {
|
||||
#targetPositionPolyline: Polyline;
|
||||
#timer: number = 0;
|
||||
#hotgroup: number | null = null;
|
||||
#detectionMethods: number[] = [];
|
||||
|
||||
getAlive() {return this.#alive};
|
||||
getHuman() {return this.#human};
|
||||
@@ -222,6 +223,10 @@ export class Unit extends CustomMarker {
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
drawLines() {
|
||||
// TODO dont delete the polylines of the detected units
|
||||
this.#clearContacts();
|
||||
if (this.getSelected()) {
|
||||
@@ -233,8 +238,6 @@ export class Unit extends CustomMarker {
|
||||
this.#clearPath();
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
getData() {
|
||||
@@ -296,7 +299,8 @@ export class Unit extends CustomMarker {
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
showSummary: true,
|
||||
showCallsign: true,
|
||||
rotateToHeading: false
|
||||
}
|
||||
}
|
||||
@@ -309,7 +313,7 @@ export class Unit extends CustomMarker {
|
||||
|
||||
setSelected(selected: boolean) {
|
||||
/* Only alive units can be selected. Some units are not selectable (weapons) */
|
||||
if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected) {
|
||||
if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected && this.belongsToCommandedCoalition()) {
|
||||
this.#selected = selected;
|
||||
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-selected", selected);
|
||||
if (selected) {
|
||||
@@ -362,6 +366,16 @@ export class Unit extends CustomMarker {
|
||||
return Object.values(getUnitsManager().getUnits()).filter((unit: Unit) => { return unit != this && unit.#groupName === this.#groupName; });
|
||||
}
|
||||
|
||||
belongsToCommandedCoalition() {
|
||||
if (getUnitsManager().getCommandMode() === HIDE_ALL)
|
||||
return false;
|
||||
if (getUnitsManager().getCommandMode() === BLUE_COMMANDER && this.#coalition !== "blue")
|
||||
return false;
|
||||
if (getUnitsManager().getCommandMode() === RED_COMMANDER && this.#coalition !== "red")
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void {
|
||||
/* Set the icon */
|
||||
@@ -453,7 +467,7 @@ export class Unit extends CustomMarker {
|
||||
altitude.classList.add("unit-altitude");
|
||||
var speed = document.createElement("div");
|
||||
speed.classList.add("unit-speed");
|
||||
summary.appendChild(callsign);
|
||||
if (this.getIconOptions().showCallsign) summary.appendChild(callsign);
|
||||
summary.appendChild(altitude);
|
||||
summary.appendChild(speed);
|
||||
el.appendChild(summary);
|
||||
@@ -468,12 +482,17 @@ export class Unit extends CustomMarker {
|
||||
const hiddenUnits = getUnitsManager().getHiddenTypes();
|
||||
if (this.#human && hiddenUnits.includes("human"))
|
||||
hidden = true;
|
||||
else if (this.#controlled == false && hiddenUnits.includes("dcs"))
|
||||
if (this.#controlled == false && hiddenUnits.includes("dcs"))
|
||||
hidden = true;
|
||||
else if (hiddenUnits.includes(this.getMarkerCategory()))
|
||||
if (hiddenUnits.includes(this.getMarkerCategory()))
|
||||
hidden = true;
|
||||
else if (hiddenUnits.includes(this.#coalition))
|
||||
if (hiddenUnits.includes(this.#coalition))
|
||||
hidden = true;
|
||||
if (getUnitsManager().getCommandMode() === HIDE_ALL)
|
||||
hidden = true;
|
||||
if (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0) {
|
||||
hidden = true;
|
||||
}
|
||||
this.setHidden(hidden || !this.#alive);
|
||||
}
|
||||
|
||||
@@ -495,6 +514,22 @@ export class Unit extends CustomMarker {
|
||||
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;
|
||||
}
|
||||
|
||||
getLeader() {
|
||||
return getUnitsManager().getUnitByID(this.#leaderID);
|
||||
}
|
||||
@@ -503,9 +538,13 @@ export class Unit extends CustomMarker {
|
||||
if (typeof (roles) === "string")
|
||||
roles = [roles];
|
||||
|
||||
return this.getDatabase()?.getByName(this.#name)?.loadouts.some((loadout: LoadoutBlueprint) => {
|
||||
return (roles as string[]).some((role: string) => { return loadout.roles.includes(role) });
|
||||
});
|
||||
var loadouts = this.getDatabase()?.getByName(this.#name)?.loadouts;
|
||||
if (loadouts) {
|
||||
return loadouts.some((loadout: LoadoutBlueprint) => {
|
||||
return (roles as string[]).some((role: string) => { return loadout.roles.includes(role) });
|
||||
});
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************** Unit commands *************************/
|
||||
@@ -696,7 +735,7 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
if (Object.keys(options).length > 0) {
|
||||
getMap().showUnitContextMenu(e);
|
||||
getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||
getMap().getUnitContextMenu().setOptions(options, (option: string) => {
|
||||
getMap().hideUnitContextMenu();
|
||||
this.#executeAction(e, option);
|
||||
@@ -739,7 +778,8 @@ export class Unit extends CustomMarker {
|
||||
getMap().hideUnitContextMenu();
|
||||
this.#applyFollowOptions(option);
|
||||
});
|
||||
getMap().showUnitContextMenu(e);
|
||||
|
||||
getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||
}
|
||||
|
||||
#applyFollowOptions(action: string) {
|
||||
@@ -758,7 +798,8 @@ export class Unit extends CustomMarker {
|
||||
this.updateVisibility();
|
||||
|
||||
/* Draw the minimap marker */
|
||||
if (this.#alive) {
|
||||
var drawMiniMapMarker = (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value)));
|
||||
if (this.#alive && drawMiniMapMarker) {
|
||||
if (this.#miniMapMarker == null) {
|
||||
this.#miniMapMarker = new CircleMarker(new LatLng(this.#position.lat, this.#position.lng), { radius: 0.5 });
|
||||
if (this.#coalition == "neutral")
|
||||
@@ -912,19 +953,28 @@ export class Unit extends CustomMarker {
|
||||
var contactData = this.#contacts[index];
|
||||
var contact = getUnitsManager().getUnitByID(contactData.ID)
|
||||
if (contact != null) {
|
||||
var startLatLng = new LatLng(this.#position.lat, this.#position.lng)
|
||||
var endLatLng = new LatLng(contact.#position.lat, contact.#position.lng)
|
||||
var startLatLng = new LatLng(this.#position.lat, this.#position.lng);
|
||||
var endLatLng: LatLng;
|
||||
if (contactData.detectionMethod === RWR) {
|
||||
var bearingToContact = bearing(this.#position.lat, this.#position.lng, contact.#position.lat, contact.#position.lng);
|
||||
var startXY = getMap().latLngToContainerPoint(startLatLng);
|
||||
var endX = startXY.x + 80 * Math.sin(deg2rad(bearingToContact));
|
||||
var endY = startXY.y - 80 * Math.cos(deg2rad(bearingToContact));
|
||||
endLatLng = getMap().containerPointToLatLng(new Point(endX, endY));
|
||||
}
|
||||
else
|
||||
endLatLng = new LatLng(contact.#position.lat, contact.#position.lng);
|
||||
|
||||
var color;
|
||||
if (contactData.detectionMethod === 1)
|
||||
if (contactData.detectionMethod === VISUAL || contactData.detectionMethod === OPTIC)
|
||||
color = "#FF00FF";
|
||||
else if (contactData.detectionMethod === 4)
|
||||
else if (contactData.detectionMethod === RADAR || contactData.detectionMethod === IRST)
|
||||
color = "#FFFF00";
|
||||
else if (contactData.detectionMethod === 16)
|
||||
else if (contactData.detectionMethod === RWR)
|
||||
color = "#00FF00";
|
||||
else
|
||||
color = "#FFFFFF";
|
||||
var contactPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 0.4, smoothFactor: 1, dashArray: "4, 8" });
|
||||
var contactPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 1, smoothFactor: 1, dashArray: "4, 8" });
|
||||
contactPolyline.addTo(getMap());
|
||||
this.#contactsPolylines.push(contactPolyline)
|
||||
}
|
||||
@@ -941,10 +991,11 @@ export class Unit extends CustomMarker {
|
||||
if (this.#targetPosition.lat != 0 && this.#targetPosition.lng != 0) {
|
||||
this.#drawtargetPosition(this.#targetPosition);
|
||||
}
|
||||
else if (this.#targetID != 0 && getUnitsManager().getUnitByID(this.#targetID)) {
|
||||
const position = getUnitsManager().getUnitByID(this.#targetID)?.getPosition();
|
||||
if (position)
|
||||
this.#drawtargetPosition(position);
|
||||
else if (this.#targetID != 0) {
|
||||
const target = getUnitsManager().getUnitByID(this.#targetID);
|
||||
if (target && (getUnitsManager().getCommandMode() == GAME_MASTER || (this.belongsToCommandedCoalition() && getUnitsManager().getUnitDetectedMethods(target).some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))))) {
|
||||
this.#drawtargetPosition(target.getPosition());
|
||||
}
|
||||
}
|
||||
else
|
||||
this.#clearTarget();
|
||||
@@ -971,14 +1022,15 @@ export class Unit extends CustomMarker {
|
||||
export class AirUnit extends Unit {
|
||||
getIconOptions() {
|
||||
return {
|
||||
showState: true,
|
||||
showVvi: true,
|
||||
showHotgroup: true,
|
||||
showUnitIcon: true,
|
||||
showShortLabel: true,
|
||||
showFuel: true,
|
||||
showAmmo: true,
|
||||
showSummary: true,
|
||||
showState: this.belongsToCommandedCoalition(),
|
||||
showVvi: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showHotgroup: this.belongsToCommandedCoalition(),
|
||||
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value))),
|
||||
showFuel: this.belongsToCommandedCoalition(),
|
||||
showAmmo: this.belongsToCommandedCoalition(),
|
||||
showSummary: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showCallsign: this.belongsToCommandedCoalition(),
|
||||
rotateToHeading: false
|
||||
};
|
||||
}
|
||||
@@ -1011,14 +1063,15 @@ export class GroundUnit extends Unit {
|
||||
|
||||
getIconOptions() {
|
||||
return {
|
||||
showState: true,
|
||||
showState: this.belongsToCommandedCoalition(),
|
||||
showVvi: false,
|
||||
showHotgroup: true,
|
||||
showUnitIcon: true,
|
||||
showHotgroup: this.belongsToCommandedCoalition(),
|
||||
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
showCallsign: this.belongsToCommandedCoalition(),
|
||||
rotateToHeading: false
|
||||
};
|
||||
}
|
||||
@@ -1035,18 +1088,23 @@ export class NavyUnit extends Unit {
|
||||
|
||||
getIconOptions() {
|
||||
return {
|
||||
showState: true,
|
||||
showState: this.belongsToCommandedCoalition(),
|
||||
showVvi: false,
|
||||
showHotgroup: true,
|
||||
showUnitIcon: true,
|
||||
showShortLabel: true,
|
||||
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
showCallsign: this.belongsToCommandedCoalition(),
|
||||
rotateToHeading: false
|
||||
};
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "navyunit";
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "NavyUnit";
|
||||
}
|
||||
@@ -1068,6 +1126,7 @@ export class Weapon extends Unit {
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
showCallsign: false,
|
||||
rotateToHeading: true
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,20 +5,64 @@ export class UnitDatabase {
|
||||
|
||||
}
|
||||
|
||||
getBlueprints() {
|
||||
return this.blueprints;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
var loadouts = this.blueprints[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 types: string[] = [];
|
||||
for (let unit in this.blueprints) {
|
||||
var type = this.blueprints[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 eras: string[] = [];
|
||||
for (let unit in this.blueprints) {
|
||||
var unitEras = this.blueprints[unit].era;
|
||||
if (unitEras) {
|
||||
for (let era of unitEras) {
|
||||
if (era !== "" && !eras.includes(era))
|
||||
eras.push(era);
|
||||
}
|
||||
}
|
||||
}
|
||||
return eras;
|
||||
}
|
||||
|
||||
/* Returns a list of all possible ranges in a database */
|
||||
getRanges() {
|
||||
var ranges: string[] = [];
|
||||
for (let unit in this.blueprints) {
|
||||
var range = this.blueprints[unit].range;
|
||||
if (range && range !== "" && !ranges.includes(range))
|
||||
ranges.push(range);
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/* Gets a specific blueprint by name */
|
||||
getByName(name: string) {
|
||||
if (name in this.blueprints)
|
||||
@@ -35,7 +79,7 @@ export class UnitDatabase {
|
||||
return null;
|
||||
}
|
||||
|
||||
/* Gets a specific blueprint by range */
|
||||
/* Get all blueprints by range */
|
||||
getByRange(range: string) {
|
||||
var unitswithrange = [];
|
||||
for (let unit in this.blueprints) {
|
||||
@@ -46,7 +90,7 @@ export class UnitDatabase {
|
||||
return unitswithrange;
|
||||
}
|
||||
|
||||
/* Gets a specific blueprint by type */
|
||||
/* Get all blueprints by type */
|
||||
getByType(type: string) {
|
||||
var units = [];
|
||||
for (let unit in this.blueprints) {
|
||||
@@ -61,10 +105,13 @@ export class UnitDatabase {
|
||||
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;
|
||||
var loadouts = this.blueprints[unit].loadouts;
|
||||
if (loadouts) {
|
||||
for (let loadout of loadouts) {
|
||||
if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) {
|
||||
units.push(this.blueprints[unit])
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,20 +120,26 @@ export class UnitDatabase {
|
||||
|
||||
/* 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)
|
||||
var loadoutsByRole = [];
|
||||
var loadouts = this.blueprints[name].loadouts;
|
||||
if (loadouts) {
|
||||
for (let loadout of loadouts) {
|
||||
if (loadout.roles.includes(role) || loadout.roles.includes("")) {
|
||||
loadoutsByRole.push(loadout.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return loadouts;
|
||||
return loadoutsByRole;
|
||||
}
|
||||
|
||||
/* Get the loadout content from the unit name and loadout name */
|
||||
getLoadoutByName(name: string, loadoutName: string) {
|
||||
for (let loadout of this.blueprints[name].loadouts) {
|
||||
if (loadout.name === loadoutName)
|
||||
return loadout;
|
||||
var loadouts = this.blueprints[name].loadouts;
|
||||
if (loadouts) {
|
||||
for (let loadout of loadouts) {
|
||||
if (loadout.name === loadoutName)
|
||||
return loadout;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import { LatLng, LatLngBounds } from "leaflet";
|
||||
import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler } from "..";
|
||||
import { Unit } from "./unit";
|
||||
import { cloneUnit, setLastUpdateTime, spawnGroundUnit } from "../server/server";
|
||||
import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils";
|
||||
import { cloneUnit, setLastUpdateTime, spawnGroundUnits } from "../server/server";
|
||||
import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils";
|
||||
import { CoalitionArea } from "../map/coalitionarea";
|
||||
import { Airbase } from "../missionhandler/airbase";
|
||||
import { groundUnitsDatabase } from "./groundunitsdatabase";
|
||||
import { DataIndexes, IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants";
|
||||
import { groundUnitDatabase } from "./groundunitdatabase";
|
||||
import { DataIndexes, HIDE_ALL, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants";
|
||||
import { DataExtractor } from "./dataextractor";
|
||||
import { Contact } from "../@types/unit";
|
||||
import { citiesDatabase } from "./citiesDatabase";
|
||||
|
||||
export class UnitsManager {
|
||||
#units: { [ID: number]: Unit };
|
||||
@@ -15,6 +17,7 @@ export class UnitsManager {
|
||||
#selectionEventDisabled: boolean = false;
|
||||
#pasteDisabled: boolean = false;
|
||||
#hiddenTypes: string[] = [];
|
||||
#commandMode: string = HIDE_ALL;
|
||||
|
||||
constructor() {
|
||||
this.#units = {};
|
||||
@@ -27,6 +30,8 @@ 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());
|
||||
}
|
||||
|
||||
getSelectableAircraft() {
|
||||
@@ -71,7 +76,7 @@ export class UnitsManager {
|
||||
update(buffer: ArrayBuffer) {
|
||||
var dataExtractor = new DataExtractor(buffer);
|
||||
var updateTime = Number(dataExtractor.extractUInt64());
|
||||
|
||||
var requestRefresh = false;
|
||||
while (dataExtractor.getSeekPosition() < buffer.byteLength) {
|
||||
const ID = dataExtractor.extractUInt32();
|
||||
if (!(ID in this.#units)) {
|
||||
@@ -81,13 +86,23 @@ export class UnitsManager {
|
||||
this.addUnit(ID, category);
|
||||
}
|
||||
else {
|
||||
// TODO request a refresh since we must have missed some packets
|
||||
requestRefresh = true;
|
||||
}
|
||||
}
|
||||
this.#units[ID]?.setData(dataExtractor);
|
||||
}
|
||||
|
||||
for (let ID in this.#units) {
|
||||
var unit = this.#units[ID];
|
||||
if (!unit.belongsToCommandedCoalition())
|
||||
unit.setDetectionMethods(this.getUnitDetectedMethods(unit));
|
||||
}
|
||||
|
||||
setLastUpdateTime(updateTime);
|
||||
|
||||
for (let ID in this.#units) {
|
||||
this.#units[ID].drawLines();
|
||||
};
|
||||
}
|
||||
|
||||
setHiddenType(key: string, value: boolean) {
|
||||
@@ -104,6 +119,24 @@ export class UnitsManager {
|
||||
return this.#hiddenTypes;
|
||||
}
|
||||
|
||||
setVisibilityMode(newVisibilityMode: string) {
|
||||
if (newVisibilityMode !== this.#commandMode) {
|
||||
document.dispatchEvent(new CustomEvent("visibilityModeChanged", { detail: this }));
|
||||
const el = document.getElementById("visibiliy-mode");
|
||||
if (el) {
|
||||
el.dataset.mode = newVisibilityMode;
|
||||
el.textContent = newVisibilityMode.toUpperCase();
|
||||
}
|
||||
this.#commandMode = newVisibilityMode;
|
||||
for (let ID in this.#units)
|
||||
this.#units[ID].updateVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
getCommandMode() {
|
||||
return this.#commandMode;
|
||||
}
|
||||
|
||||
selectUnit(ID: number, deselectAllUnits: boolean = true) {
|
||||
if (deselectAllUnits)
|
||||
this.getSelectedUnits().filter((unit: Unit) => unit.ID !== ID).forEach((unit: Unit) => unit.setSelected(false));
|
||||
@@ -479,6 +512,20 @@ export class UnitsManager {
|
||||
this.#showActionMessage(selectedUnits, `unit bombing point`);
|
||||
}
|
||||
|
||||
getUnitDetectedMethods(unit: Unit) {
|
||||
var detectionMethods: number[] = [];
|
||||
for (let idx in this.#units) {
|
||||
if (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;
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
copyUnits() {
|
||||
this.#copiedUnits = this.getSelectedUnits(); /* Can be applied to humans too */
|
||||
@@ -498,29 +545,75 @@ export class UnitsManager {
|
||||
}
|
||||
}
|
||||
|
||||
createIADS(coalitionArea: CoalitionArea, options: {[key: string]: boolean}, density: number) {
|
||||
const activeRoles = Object.keys(options).filter((key: string) => { return options[key]; });
|
||||
const airbases = getMissionHandler().getAirbases();
|
||||
const pointsNumber = polygonArea(coalitionArea) / 1e7 * density / 100;
|
||||
for (let i = 0; i < pointsNumber; i++) {
|
||||
const latlng = randomPointInPoly(coalitionArea);
|
||||
var minDistance: number = Infinity;
|
||||
var maxDistance: number = 0;
|
||||
Object.values(airbases).forEach((airbase: Airbase) => {
|
||||
var distance = airbase.getLatLng().distanceTo(latlng);
|
||||
if (distance < minDistance) minDistance = distance;
|
||||
if (distance > maxDistance) maxDistance = distance;
|
||||
});
|
||||
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]; });
|
||||
|
||||
const role = activeRoles[Math.floor(Math.random() * activeRoles.length)];
|
||||
const probability = Math.pow(1 - minDistance / 50e3, 5) * IADSRoles[role];
|
||||
if (Math.random() < probability){
|
||||
const unitBlueprint = randomUnitBlueprintByRole(groundUnitsDatabase, role);
|
||||
const spawnOptions = {role: role, latlng: latlng, name: unitBlueprint.name, coalition: coalitionArea.getCoalition(), immediate: true};
|
||||
spawnGroundUnit(spawnOptions);
|
||||
getMap().addTemporaryMarker(spawnOptions);
|
||||
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) {
|
||||
spawnGroundUnits([{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();
|
||||
data.category = unit.getCategory();
|
||||
if (unit.getGroupName() in unitsToExport)
|
||||
unitsToExport[unit.getGroupName()].push(data);
|
||||
else
|
||||
unitsToExport[unit.getGroupName()] = [data];
|
||||
}
|
||||
}
|
||||
var a = document.createElement("a");
|
||||
var file = new Blob([JSON.stringify(unitsToExport)], {type: 'text/plain'});
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = 'export.json';
|
||||
a.click();
|
||||
}
|
||||
|
||||
importFromFile() {
|
||||
var input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.addEventListener("change", (e: any) => {
|
||||
var file = e.target.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e: any) {
|
||||
var contents = e.target.result;
|
||||
var groups = JSON.parse(contents);
|
||||
for (let groupName in groups) {
|
||||
if (groupName !== "" && groups[groupName].length > 0 && groups[groupName].every((unit: any) => {return unit.category == "GroundUnit";})) {
|
||||
var units = groups[groupName].map((unit: any) => {return {unitType: unit.name, location: unit.position}});
|
||||
spawnGroundUnits(units, groups[groupName][0].coalition, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
})
|
||||
input.click();
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
|
||||
@@ -1,15 +1,27 @@
|
||||
<div id="map-contextmenu" class="ol-context-menu" oncontextmenu="return false;">
|
||||
<div id="active-coalition-label" data-coalition="blue"></div>
|
||||
<div id="upper-bar" class="ol-panel">
|
||||
<div id="coalition-switch" class="ol-switch ol-coalition-switch"></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"></button>
|
||||
<button data-coalition="blue" id="ground-ol-contexmenu-button" title="Spawn ground unit" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "ground-unit" }' class="ol-contexmenu-button"></button>
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
<!--<button data-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="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"></button>
|
||||
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"></button>
|
||||
data-on-click-params='{ "type": "explosion" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/explosion.svg" inject-svg></button>
|
||||
</div>
|
||||
<div id="aircraft-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div class="ol-select-container">
|
||||
@@ -22,7 +34,7 @@
|
||||
</div>
|
||||
<div class="ol-select-container">
|
||||
<div id="aircraft-type-options" class="ol-select">
|
||||
<div class="ol-select-value">Aircraft type</div>
|
||||
<div class="ol-select-value">Aircraft name</div>
|
||||
<div class="ol-select-options">
|
||||
<div>Select role first</div>
|
||||
<!-- This is where all the aircraft types buttons will be shown-->
|
||||
@@ -30,7 +42,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 +50,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,33 +70,125 @@
|
||||
<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-coalition="blue" data-on-click="contextMenuDeployAircraft" disabled>Deploy unit</button>
|
||||
</div>
|
||||
<div id="ground-unit-spawn-menu" class="ol-panel ol-contexmenu-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-type-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>
|
||||
<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="contextMenuDeployAircraft" disabled>Deploy unit</button>
|
||||
</div>
|
||||
<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-name-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="contextMenuDeployGroundUnit" 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-name-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="contextMenuDeployNavyUnit" 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>
|
||||
@@ -84,10 +197,10 @@
|
||||
<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 ol-contexmenu-panel hide">
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 50 }'>Small explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 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>
|
||||
<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>
|
||||
|
||||
@@ -104,54 +217,67 @@
|
||||
<h4>Parking available:</h4>
|
||||
<div id="airbase-parking"></div>
|
||||
|
||||
<button id="spawn-airbase-aircraft-button" data-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 id="upper-bar" class="ol-panel">
|
||||
<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"></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>
|
||||
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"></button>
|
||||
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"></button>
|
||||
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-role-options" class="ol-select">
|
||||
<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 roles checkboxes will be shown-->
|
||||
<!-- This is where all the iads unit types checkboxes will be shown-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="ol-select-container">
|
||||
<div id="iads-period-options" class="ol-select">
|
||||
<div class="ol-select-value">Units period</div>
|
||||
<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 period buttons will be shown--><!--
|
||||
<!-- 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 type="range" min="0" max="100" value="0" class="ol-slider">
|
||||
<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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -14,12 +14,20 @@
|
||||
<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>
|
||||
|
||||
<span id="visibiliy-mode"></span>
|
||||
|
||||
<div id="map-type" class="ol-select">
|
||||
<div class="ol-select-value map-source-dropdown">
|
||||
<span>ArcGIS Satellite</span>
|
||||
@@ -61,9 +69,6 @@
|
||||
|
||||
<div id="map-tools" class="ol-group-container ol-navbar-buttons-group">
|
||||
<div class="ol-group">
|
||||
<button title="Enable area interaction" data-on-click="toggleCoalitionAreaInteraction" class="off">
|
||||
<img src="resources/theme/images/buttons/tools/pen-solid.svg" inject-svg>
|
||||
</button>
|
||||
<button title="Draw Coalition Areas on the map" data-on-click="toggleCoalitionAreaDraw" data-on-click-params='{"type": "polygon"}' class="off">
|
||||
<img src="resources/theme/images/buttons/tools/draw-polygon-solid.svg" inject-svg>
|
||||
</button>
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
"port": 30000
|
||||
},
|
||||
"authentication": {
|
||||
"password": "password"
|
||||
"gameMasterPassword": "password",
|
||||
"blueCommanderPassword": "bluepassword",
|
||||
"redCommanderPassword": "redpassword"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
local version = "v0.3.0-alpha"
|
||||
|
||||
local debug = FALSE
|
||||
local debug = false
|
||||
|
||||
Olympus.unitCounter = 1
|
||||
Olympus.payloadRegistry = {}
|
||||
@@ -303,105 +303,106 @@ function Olympus.explosion(intensity, lat, lng)
|
||||
trigger.action.explosion(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), intensity)
|
||||
end
|
||||
|
||||
-- Spawns a single ground unit
|
||||
function Olympus.spawnGroundUnit(coalition, unitType, lat, lng)
|
||||
Olympus.debug("Olympus.spawnGroundUnit " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
||||
-- Spawns a new unit or group
|
||||
function Olympus.spawnUnits(spawnTable)
|
||||
Olympus.debug("Olympus.spawnUnits " .. Olympus.serializeTable(spawnTable), 2)
|
||||
|
||||
local unitTable = {}
|
||||
local unitTable = nil
|
||||
local route = nil
|
||||
local category = nil
|
||||
|
||||
if Olympus.hasKey(templates, unitType) then
|
||||
for idx, value in pairs(templates[unitType].units) do
|
||||
unitTable[#unitTable + 1] = {
|
||||
["type"] = value.name,
|
||||
["x"] = spawnLocation.x + value.dx,
|
||||
["y"] = spawnLocation.z + value.dy,
|
||||
["playerCanDrive"] = true,
|
||||
["heading"] = 0,
|
||||
["skill"] = "High"
|
||||
}
|
||||
end
|
||||
else
|
||||
unitTable =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["type"] = unitType,
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
["playerCanDrive"] = true,
|
||||
["heading"] = 0,
|
||||
["skill"] = "High"
|
||||
},
|
||||
}
|
||||
if spawnTable.category == 'Aircraft' then
|
||||
unitTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
||||
route = Olympus.generateAirUnitsRoute(spawnTable)
|
||||
category = 'airplane'
|
||||
elseif spawnTable.category == 'GroundUnit' then
|
||||
unitTable = Olympus.generateGroundUnitsTable(spawnTable.units)
|
||||
category = 'vehicle'
|
||||
end
|
||||
|
||||
local countryID = Olympus.getCountryIDByCoalition(coalition)
|
||||
|
||||
local countryID = Olympus.getCountryIDByCoalition(spawnTable.coalition)
|
||||
local vars =
|
||||
{
|
||||
units = unitTable,
|
||||
country = countryID,
|
||||
category = 'vehicle',
|
||||
name = "Ground-" .. Olympus.unitCounter,
|
||||
category = category,
|
||||
route = route,
|
||||
name = "Olympus-" .. Olympus.unitCounter,
|
||||
task = 'CAP'
|
||||
}
|
||||
mist.dynAdd(vars)
|
||||
|
||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
||||
Olympus.debug("Olympus.spawnGround completed succesfully", 2)
|
||||
end
|
||||
Olympus.debug("Olympus.spawnUnits completed succesfully", 2)
|
||||
end
|
||||
|
||||
-- Spawns a single aircraft. Spawn options are:
|
||||
-- payloadName: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
|
||||
-- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase
|
||||
-- payload: a table, if present the unit will receive this specific payload. Overrides payloadName
|
||||
function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions)
|
||||
local payloadName = spawnOptions["payloadName"]
|
||||
local airbaseName = spawnOptions["airbaseName"]
|
||||
local payload = spawnOptions["payload"]
|
||||
|
||||
Olympus.debug("Olympus.spawnAircraft " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..", " .. alt .. ")", 2)
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
||||
|
||||
if payload == nil then
|
||||
if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] then
|
||||
payload = Olympus.unitPayloads[unitType][payloadName]
|
||||
-- Generates ground units table, either single or from template
|
||||
function Olympus.generateGroundUnitsTable(units)
|
||||
local unitTable = {}
|
||||
for idx, unit in pairs(units) do
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
||||
if Olympus.hasKey(templates, unit.unitType) then
|
||||
for idx, value in pairs(templates[unit.unitType].units) do
|
||||
unitTable[#unitTable + 1] =
|
||||
{
|
||||
["type"] = value.name,
|
||||
["x"] = spawnLocation.x + value.dx,
|
||||
["y"] = spawnLocation.z + value.dy,
|
||||
["heading"] = 0,
|
||||
["skill"] = "High"
|
||||
}
|
||||
end
|
||||
else
|
||||
payload = {}
|
||||
unitTable[#unitTable + 1] =
|
||||
{
|
||||
["type"] = unit.unitType,
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
["heading"] = 0,
|
||||
["skill"] = "High"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
local countryID = Olympus.getCountryIDByCoalition(coalition)
|
||||
|
||||
local unitTable =
|
||||
{
|
||||
[1] =
|
||||
return unitTable
|
||||
end
|
||||
|
||||
-- Generates unit table for a air unit.
|
||||
function Olympus.generateAirUnitsTable(units)
|
||||
local unitTable = {}
|
||||
for idx, unit in pairs(units) do
|
||||
local loadout = unit.loadout -- loadout: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType
|
||||
local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides loadout
|
||||
|
||||
if payload == nil then
|
||||
if loadout and loadout ~= "" and Olympus.unitPayloads[unit.unitType][loadout] then
|
||||
payload = Olympus.unitPayloads[unit.unitType][loadout]
|
||||
else
|
||||
payload = {}
|
||||
end
|
||||
end
|
||||
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0))
|
||||
unitTable[#unitTable + 1] =
|
||||
{
|
||||
["type"] = unitType,
|
||||
["type"] = unit.unitType,
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
["alt"] = alt,
|
||||
["alt_type"] = "BARO",
|
||||
["alt"] = unit.alt,
|
||||
["alt_type"] = "BARO",
|
||||
["skill"] = "Excellent",
|
||||
["payload"] =
|
||||
{
|
||||
["pylons"] = payload,
|
||||
["fuel"] = 999999,
|
||||
["flare"] = 60,
|
||||
["ammo_type"] = 1,
|
||||
["chaff"] = 60,
|
||||
["gun"] = 100,
|
||||
},
|
||||
["payload"] = { ["pylons"] = payload, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100, },
|
||||
["heading"] = 0,
|
||||
["callsign"] =
|
||||
{
|
||||
[1] = 1,
|
||||
[2] = 1,
|
||||
[3] = 1,
|
||||
["name"] = "Olympus" .. Olympus.unitCounter,
|
||||
},
|
||||
["name"] = "Olympus-" .. Olympus.unitCounter
|
||||
},
|
||||
}
|
||||
["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitTable + 1 },
|
||||
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1
|
||||
}
|
||||
end
|
||||
return unitTable
|
||||
end
|
||||
|
||||
function Olympus.generateAirUnitsRoute(spawnTable)
|
||||
local airbaseName = spawnTable.airbaseName -- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(spawnTable.units[1].lat, spawnTable.units[1].lng, 0))
|
||||
|
||||
-- If a airbase is provided the first waypoint is set as a From runway takeoff.
|
||||
local route = {}
|
||||
@@ -416,10 +417,9 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions)
|
||||
[1] =
|
||||
{
|
||||
["action"] = "From Parking Area Hot",
|
||||
["task"] =
|
||||
{
|
||||
["id"] = "ComboTask",
|
||||
["params"] = {["tasks"] = {},},
|
||||
["tasks"] = {
|
||||
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
||||
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
||||
},
|
||||
["type"] = "TakeOffParkingHot",
|
||||
["ETA"] = 0,
|
||||
@@ -442,69 +442,18 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions)
|
||||
{
|
||||
["alt"] = alt,
|
||||
["alt_type"] = "BARO",
|
||||
["task"] =
|
||||
{
|
||||
["id"] = "ComboTask",
|
||||
["params"] =
|
||||
{
|
||||
["tasks"] =
|
||||
{
|
||||
[1] =
|
||||
{
|
||||
["number"] = 1,
|
||||
["auto"] = true,
|
||||
["id"] = "WrappedAction",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["action"] =
|
||||
{
|
||||
["id"] = "EPLRS",
|
||||
["params"] =
|
||||
{
|
||||
["value"] = true
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[2] =
|
||||
{
|
||||
["number"] = 2,
|
||||
["auto"] = false,
|
||||
["id"] = "Orbit",
|
||||
["enabled"] = true,
|
||||
["params"] =
|
||||
{
|
||||
["pattern"] = "Circle"
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
["tasks"] = {
|
||||
[1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, },
|
||||
[2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, },
|
||||
},
|
||||
["type"] = "Turning Point",
|
||||
["x"] = spawnLocation.x,
|
||||
["y"] = spawnLocation.z,
|
||||
}, -- end of [1]
|
||||
}, -- end of ["points"]
|
||||
} -- end of ["route"]
|
||||
},
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
local vars =
|
||||
{
|
||||
units = unitTable,
|
||||
country = countryID,
|
||||
category = 'airplane',
|
||||
name = "Olympus-" .. Olympus.unitCounter,
|
||||
route = route,
|
||||
task = 'CAP',
|
||||
}
|
||||
|
||||
local newGroup = mist.dynAdd(vars)
|
||||
|
||||
-- Save the payload to be reused in case the unit is cloned. TODO: save by ID not by name (it works but I like consistency)
|
||||
Olympus.payloadRegistry[vars.name] = payload
|
||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
||||
Olympus.debug("Olympus.spawnAir completed successfully", 2)
|
||||
return route
|
||||
end
|
||||
|
||||
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units.
|
||||
@@ -513,15 +462,21 @@ function Olympus.clone(ID, lat, lng, category)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
local coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition())
|
||||
|
||||
if category == "Aircraft" then
|
||||
local spawnOptions = {
|
||||
payload = Olympus.payloadRegistry[unit:getName()]
|
||||
-- TODO: understand category in this script
|
||||
local spawnTable = {
|
||||
coalition = coalition,
|
||||
category = category,
|
||||
units = {
|
||||
[1] = {
|
||||
lat = lat,
|
||||
lng = lng,
|
||||
alt = unit:getPoint().y,
|
||||
unitType = unit:getTypeName(),
|
||||
payload = Olympus.payloadRegistry[unit:getName()]
|
||||
}
|
||||
}
|
||||
Olympus.spawnAircraft(coalition, unit:getTypeName(), lat, lng, unit:getPoint().y, spawnOptions)
|
||||
elseif category == "GroundUnit" then
|
||||
Olympus.spawnGroundUnit(coalition, unit:getTypeName(), lat, lng)
|
||||
end
|
||||
}
|
||||
Olympus.spawnUnits(spawnTable)
|
||||
end
|
||||
Olympus.debug("Olympus.clone completed successfully", 2)
|
||||
end
|
||||
@@ -765,4 +720,4 @@ end
|
||||
|
||||
timer.scheduleFunction(Olympus.setMissionData, {}, timer.getTime() + 1)
|
||||
|
||||
Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true)
|
||||
Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true)
|
||||
|
||||
@@ -151,13 +151,13 @@ private:
|
||||
};
|
||||
|
||||
/* Spawn ground unit command */
|
||||
class SpawnGroundUnit : public Command
|
||||
class SpawnGroundUnits : public Command
|
||||
{
|
||||
public:
|
||||
SpawnGroundUnit(string coalition, string unitType, Coords location, bool immediate) :
|
||||
SpawnGroundUnits(string coalition, vector<string> unitTypes, vector<Coords> locations, bool immediate) :
|
||||
coalition(coalition),
|
||||
unitType(unitType),
|
||||
location(location),
|
||||
unitTypes(unitTypes),
|
||||
locations(locations),
|
||||
immediate(immediate)
|
||||
{
|
||||
priority = immediate? CommandPriority::IMMEDIATE: CommandPriority::LOW;
|
||||
@@ -167,20 +167,20 @@ public:
|
||||
|
||||
private:
|
||||
const string coalition;
|
||||
const string unitType;
|
||||
const Coords location;
|
||||
const vector<string> unitTypes;
|
||||
const vector<Coords> locations;
|
||||
const bool immediate;
|
||||
};
|
||||
|
||||
/* Spawn air unit command */
|
||||
class SpawnAircraft : public Command
|
||||
class SpawnAircrafts : public Command
|
||||
{
|
||||
public:
|
||||
SpawnAircraft(string coalition, string unitType, Coords location, string payloadName, string airbaseName, bool immediate) :
|
||||
SpawnAircrafts(string coalition, vector<string> unitTypes, vector<Coords> locations, vector<string> loadouts, string airbaseName, bool immediate) :
|
||||
coalition(coalition),
|
||||
unitType(unitType),
|
||||
location(location),
|
||||
payloadName(payloadName),
|
||||
unitTypes(unitTypes),
|
||||
locations(locations),
|
||||
loadouts(loadouts),
|
||||
airbaseName(airbaseName),
|
||||
immediate(immediate)
|
||||
{
|
||||
@@ -191,9 +191,9 @@ public:
|
||||
|
||||
private:
|
||||
const string coalition;
|
||||
const string unitType;
|
||||
const Coords location;
|
||||
const string payloadName;
|
||||
const vector<string> unitTypes;
|
||||
const vector<Coords> locations;
|
||||
const vector<string> loadouts;
|
||||
const string airbaseName;
|
||||
const bool immediate;
|
||||
};
|
||||
|
||||
@@ -24,10 +24,16 @@ private:
|
||||
void handle_request(http_request request, function<void(json::value const&, json::value&)> action);
|
||||
void handle_put(http_request request);
|
||||
|
||||
string extractPassword(http_request& request);
|
||||
|
||||
void task();
|
||||
|
||||
atomic<bool> runListener;
|
||||
|
||||
string password = "";
|
||||
string gameMasterPassword = "";
|
||||
string blueCommanderPassword = "";
|
||||
string redCommanderPassword = "";
|
||||
string atcPassword = "";
|
||||
string observerPassword = "";
|
||||
};
|
||||
|
||||
|
||||
@@ -37,38 +37,52 @@ string Smoke::getString(lua_State* L)
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Spawn ground command */
|
||||
string SpawnGroundUnit::getString(lua_State* L)
|
||||
/* Spawn ground units command */
|
||||
string SpawnGroundUnits::getString(lua_State* L)
|
||||
{
|
||||
if (unitTypes.size() != locations.size()) return "";
|
||||
|
||||
std::ostringstream unitsSS;
|
||||
unitsSS.precision(10);
|
||||
for (int i = 0; i < unitTypes.size(); i++) {
|
||||
unitsSS << "[" << i + 1 << "] = {"
|
||||
<< "unitType = " << "\"" << unitTypes[i] << "\"" << ", "
|
||||
<< "lat = " << locations[i].lat << ", "
|
||||
<< "lng = " << locations[i].lng << "},";
|
||||
}
|
||||
|
||||
std::ostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.spawnGroundUnit, "
|
||||
<< "\"" << coalition << "\"" << ", "
|
||||
<< "\"" << unitType << "\"" << ", "
|
||||
<< location.lat << ", "
|
||||
<< location.lng;
|
||||
commandSS << "Olympus.spawnUnits, {"
|
||||
<< "category = " << "\"" << "GroundUnit" << "\"" << ", "
|
||||
<< "coalition = " << "\"" << coalition << "\"" << ", "
|
||||
<< "units = " << "{" << unitsSS.str() << "}" << "}";
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Spawn air command */
|
||||
string SpawnAircraft::getString(lua_State* L)
|
||||
/* Spawn aircrafts command */
|
||||
string SpawnAircrafts::getString(lua_State* L)
|
||||
{
|
||||
std::ostringstream optionsSS;
|
||||
optionsSS.precision(10);
|
||||
optionsSS << "{"
|
||||
<< "payloadName = \"" << payloadName << "\", "
|
||||
<< "airbaseName = \"" << airbaseName << "\", "
|
||||
<< "}";
|
||||
if (unitTypes.size() != locations.size() || unitTypes.size() != loadouts.size()) return "";
|
||||
|
||||
std::ostringstream unitsSS;
|
||||
unitsSS.precision(10);
|
||||
for (int i = 0; i < unitTypes.size(); i++) {
|
||||
unitsSS << "[" << i + 1 << "] = {"
|
||||
<< "unitType = " << "\"" << unitTypes[i] << "\"" << ", "
|
||||
<< "lat = " << locations[i].lat << ", "
|
||||
<< "lng = " << locations[i].lng << ", "
|
||||
<< "alt = " << locations[i].alt << ", "
|
||||
<< "loadout = \"" << loadouts[i] << "\"" << "},";
|
||||
}
|
||||
|
||||
std::ostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.spawnAircraft, "
|
||||
<< "\"" << coalition << "\"" << ", "
|
||||
<< "\"" << unitType << "\"" << ", "
|
||||
<< location.lat << ", "
|
||||
<< location.lng << ", "
|
||||
<< location.alt << ", "
|
||||
<< optionsSS.str();
|
||||
commandSS << "Olympus.spawnUnits, {"
|
||||
<< "category = " << "\"" << "Aircraft" << "\"" << ", "
|
||||
<< "coalition = " << "\"" << coalition << "\"" << ", "
|
||||
<< "airbaseName = \"" << airbaseName << "\", "
|
||||
<< "units = " << "{" << unitsSS.str() << "}" << "}";
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -92,30 +92,49 @@ void Scheduler::handleRequest(string key, json::value value)
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
command = dynamic_cast<Command*>(new Smoke(color, loc));
|
||||
}
|
||||
else if (key.compare("spawnGround") == 0)
|
||||
else if (key.compare("spawnGroundUnits") == 0)
|
||||
{
|
||||
bool immediate = value[L"immediate"].as_bool();
|
||||
string coalition = to_string(value[L"coalition"]);
|
||||
string type = to_string(value[L"type"]);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
log("Spawning " + coalition + " ground unit of type " + type + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
command = dynamic_cast<Command*>(new SpawnGroundUnit(coalition, type, loc, immediate));
|
||||
|
||||
vector<string> unitTypes;
|
||||
vector<Coords> locations;
|
||||
for (auto unit : value[L"units"].as_array()) {
|
||||
string unitType = to_string(unit[L"unitType"]);
|
||||
double lat = unit[L"location"][L"lat"].as_double();
|
||||
double lng = unit[L"location"][L"lng"].as_double();
|
||||
Coords location; location.lat = lat; location.lng = lng;
|
||||
log("Spawning " + coalition + " ground unit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
|
||||
unitTypes.push_back(unitType);
|
||||
locations.push_back(location);
|
||||
}
|
||||
|
||||
command = dynamic_cast<Command*>(new SpawnGroundUnits(coalition, unitTypes, locations, immediate));
|
||||
}
|
||||
else if (key.compare("spawnAir") == 0)
|
||||
else if (key.compare("spawnAircrafts") == 0)
|
||||
{
|
||||
bool immediate = value[L"immediate"].as_bool();
|
||||
string coalition = to_string(value[L"coalition"]);
|
||||
string type = to_string(value[L"type"]);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
double altitude = value[L"altitude"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng; loc.alt = altitude;
|
||||
string payloadName = to_string(value[L"payloadName"]);
|
||||
string airbaseName = to_string(value[L"airbaseName"]);
|
||||
log("Spawning " + coalition + " air unit of type " + type + " with payload " + payloadName + " at (" + to_string(lat) + ", " + to_string(lng) + " " + airbaseName + ")");
|
||||
command = dynamic_cast<Command*>(new SpawnAircraft(coalition, type, loc, payloadName, airbaseName, immediate));
|
||||
|
||||
vector<string> unitTypes;
|
||||
vector<Coords> locations;
|
||||
vector<string> loadouts;
|
||||
for (auto unit : value[L"units"].as_array()) {
|
||||
string unitType = to_string(unit[L"unitType"]);
|
||||
double lat = unit[L"location"][L"lat"].as_double();
|
||||
double lng = unit[L"location"][L"lng"].as_double();
|
||||
double alt = unit[L"altitude"].as_double();
|
||||
Coords location; location.lat = lat; location.lng = lng; location.alt = alt;
|
||||
string loadout = to_string(unit[L"loadout"]);
|
||||
|
||||
log("Spawning " + coalition + " air unit unit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
|
||||
unitTypes.push_back(unitType);
|
||||
locations.push_back(location);
|
||||
loadouts.push_back(loadout);
|
||||
}
|
||||
|
||||
command = dynamic_cast<Command*>(new SpawnAircrafts(coalition, unitTypes, locations, loadouts, airbaseName, immediate));
|
||||
}
|
||||
else if (key.compare("attackUnit") == 0)
|
||||
{
|
||||
|
||||
@@ -72,8 +72,9 @@ void Server::handle_get(http_request request)
|
||||
milliseconds ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
|
||||
|
||||
http_response response(status_codes::OK);
|
||||
string authorization = to_base64("admin:" + password);
|
||||
if (password.length() == 0 || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second.compare(L"Basic " + to_wstring(authorization)) == 0))
|
||||
|
||||
string password = extractPassword(request);
|
||||
if (password.compare(gameMasterPassword) == 0 || password.compare(blueCommanderPassword) == 0 || password.compare(redCommanderPassword) == 0)
|
||||
{
|
||||
std::exception_ptr eptr;
|
||||
try {
|
||||
@@ -114,9 +115,15 @@ void Server::handle_get(http_request request)
|
||||
answer[L"airbases"] = airbases;
|
||||
else if (URI.compare(BULLSEYE_URI) == 0)
|
||||
answer[L"bullseyes"] = bullseyes;
|
||||
else if (URI.compare(MISSION_URI) == 0)
|
||||
else if (URI.compare(MISSION_URI) == 0) {
|
||||
if (password.compare(gameMasterPassword) == 0)
|
||||
mission[L"visibilityMode"] = json::value(L"Game master");
|
||||
else if (password.compare(blueCommanderPassword) == 0)
|
||||
mission[L"visibilityMode"] = json::value(L"Blue commander");
|
||||
else if (password.compare(redCommanderPassword) == 0)
|
||||
mission[L"visibilityMode"] = json::value(L"Red commander");
|
||||
answer[L"mission"] = mission;
|
||||
|
||||
}
|
||||
|
||||
answer[L"time"] = json::value::string(to_wstring(ms.count()));
|
||||
answer[L"sessionHash"] = json::value::string(to_wstring(sessionHash));
|
||||
@@ -144,8 +151,10 @@ void Server::handle_get(http_request request)
|
||||
void Server::handle_request(http_request request, function<void(json::value const&, json::value&)> action)
|
||||
{
|
||||
http_response response(status_codes::OK);
|
||||
string authorization = to_base64("admin:" + password);
|
||||
if (password.length() == 0 || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second.compare(L"Basic " + to_wstring(authorization)) == 0))
|
||||
|
||||
//TODO: limit what a user can do depending on the password
|
||||
string password = extractPassword(request);
|
||||
if (password.compare(gameMasterPassword) == 0 || password.compare(blueCommanderPassword) == 0 || password.compare(redCommanderPassword) == 0)
|
||||
{
|
||||
auto answer = json::value::object();
|
||||
request.extract_json().then([&answer, &action](pplx::task<json::value> task)
|
||||
@@ -200,6 +209,30 @@ void Server::handle_put(http_request request)
|
||||
});
|
||||
}
|
||||
|
||||
string Server::extractPassword(http_request& request) {
|
||||
if (request.headers().has(L"Authorization")) {
|
||||
string authorization = to_string(request.headers().find(L"Authorization")->second);
|
||||
string s = "Basic ";
|
||||
string::size_type i = authorization.find(s);
|
||||
|
||||
if (i != std::string::npos)
|
||||
authorization.erase(i, s.length());
|
||||
else
|
||||
return "";
|
||||
|
||||
string decoded = from_base64(authorization);
|
||||
i = decoded.find(":");
|
||||
if (i != string::npos && i+1 < decoded.length())
|
||||
decoded.erase(0, i+1);
|
||||
else
|
||||
return "";
|
||||
|
||||
return decoded;
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
void Server::task()
|
||||
{
|
||||
string address = REST_ADDRESS;
|
||||
@@ -222,10 +255,11 @@ void Server::task()
|
||||
else
|
||||
log("Error reading configuration file. Starting server on " + address);
|
||||
|
||||
if (config.is_object() && config.has_object_field(L"authentication") &&
|
||||
config[L"authentication"].has_string_field(L"password"))
|
||||
if (config.is_object() && config.has_object_field(L"authentication"))
|
||||
{
|
||||
password = to_string(config[L"authentication"][L"password"]);
|
||||
if (config[L"authentication"].has_string_field(L"gameMasterPassword")) gameMasterPassword = to_string(config[L"authentication"][L"gameMasterPassword"]);
|
||||
if (config[L"authentication"].has_string_field(L"blueCommanderPassword")) blueCommanderPassword = to_string(config[L"authentication"][L"blueCommanderPassword"]);
|
||||
if (config[L"authentication"].has_string_field(L"redCommanderPassword")) redCommanderPassword = to_string(config[L"authentication"][L"redCommanderPassword"]);
|
||||
}
|
||||
else
|
||||
log("Error reading configuration file. No password set.");
|
||||
|
||||
@@ -84,7 +84,7 @@ void Unit::runAILoop() {
|
||||
const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this);
|
||||
if (!(isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits)) return;
|
||||
|
||||
if (checkTaskFailed() && state != State::IDLE && State::LAND)
|
||||
if (checkTaskFailed() && state != State::IDLE && state != State::LAND)
|
||||
setState(State::IDLE);
|
||||
|
||||
AIloop();
|
||||
@@ -188,10 +188,10 @@ bool Unit::hasFreshData(unsigned long long time) {
|
||||
void Unit::getData(stringstream& ss, unsigned long long time)
|
||||
{
|
||||
Unit* leader = this;
|
||||
if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this))
|
||||
if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this))
|
||||
leader = unitsManager->getGroupLeader(this);
|
||||
|
||||
if (!leader->hasFreshData(time)) return;
|
||||
if (leader == nullptr || (!leader->hasFreshData(time) && !hasFreshData(time))) return;
|
||||
|
||||
const unsigned char endOfData = DataIndex::endOfData;
|
||||
ss.write((const char*)&ID, sizeof(ID));
|
||||
|
||||
@@ -39,6 +39,7 @@ bool UnitsManager::isUnitInGroup(Unit* unit)
|
||||
{
|
||||
if (unit != nullptr) {
|
||||
string groupName = unit->getGroupName();
|
||||
if (groupName.length() == 0) return false;
|
||||
for (auto const& p : units)
|
||||
{
|
||||
if (p.second->getGroupName().compare(groupName) == 0 && p.second != unit)
|
||||
@@ -50,8 +51,10 @@ bool UnitsManager::isUnitInGroup(Unit* unit)
|
||||
|
||||
bool UnitsManager::isUnitGroupLeader(Unit* unit)
|
||||
{
|
||||
if (unit != nullptr)
|
||||
return unit == getGroupLeader(unit);
|
||||
if (unit != nullptr) {
|
||||
Unit* leader = getGroupLeader(unit);
|
||||
return leader == nullptr? false: unit == getGroupLeader(unit);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
@@ -61,7 +64,7 @@ Unit* UnitsManager::getGroupLeader(Unit* unit)
|
||||
{
|
||||
if (unit != nullptr) {
|
||||
string groupName = unit->getGroupName();
|
||||
|
||||
if (groupName.length() == 0) return nullptr;
|
||||
/* Find the first unit that has the same groupName */
|
||||
for (auto const& p : units)
|
||||
{
|
||||
|
||||