Merge pull request #323 from Pax1601/319-add-basic-rts-functions

319 add basic rts functions
This commit is contained in:
Pax1601
2023-07-11 18:56:57 +02:00
committed by GitHub
56 changed files with 11535 additions and 6307 deletions

View File

@@ -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);

View File

@@ -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));
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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%;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 9.6 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -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

View 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

View File

@@ -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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -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

View 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

View File

@@ -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;
}

View File

@@ -17,7 +17,8 @@ interface CustomEventMap {
"groupCreation": CustomEvent<Unit[]>,
"groupDeletion": CustomEvent<Unit[]>,
"mapStateChanged": CustomEvent<string>,
"mapContextMenu": CustomEvent<>
"mapContextMenu": CustomEvent<>,
"visibilityModeChanged": CustomEvent<string>,
}
declare global {

View File

@@ -1,8 +1,3 @@
interface UnitsData {
units: string,
sessionHash: string
}
interface AirbasesData {
airbases: {[key: string]: any},
}

View File

@@ -8,7 +8,8 @@ interface UnitIconOptions {
showShortLabel: boolean,
showFuel: boolean,
showAmmo: boolean,
showSummary: boolean,
showSummary: boolean,
showCallsign: boolean,
rotateToHeading: boolean
}

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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());

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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();

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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));

View File

@@ -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": [

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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();

View File

@@ -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
};
}

View File

@@ -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;
}

View File

@@ -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();
}
/***********************************************/

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -4,6 +4,8 @@
"port": 30000
},
"authentication": {
"password": "password"
"gameMasterPassword": "password",
"blueCommanderPassword": "bluepassword",
"redCommanderPassword": "redpassword"
}
}

View File

@@ -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)

View File

@@ -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;
};

View File

@@ -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 = "";
};

View File

@@ -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();
}

View File

@@ -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)
{

View File

@@ -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.");

View File

@@ -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));

View File

@@ -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)
{