Compare commits
153 Commits
v0.4.7-alp
...
v0.4.10-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d03c6a767 | ||
|
|
e29fdfd8c7 | ||
|
|
af619c3687 | ||
|
|
6afb6682ea | ||
|
|
9c2c7f45c6 | ||
|
|
7fbc19e90a | ||
|
|
8f2f73dc0e | ||
|
|
24a1681337 | ||
|
|
304e8ba3a0 | ||
|
|
45b840fa72 | ||
|
|
82704f042b | ||
|
|
87d71da55e | ||
|
|
71cf7c67f7 | ||
|
|
62142ed976 | ||
|
|
f56bd514dc | ||
|
|
e30f161d1d | ||
|
|
720bfb3118 | ||
|
|
4100a3cc67 | ||
|
|
942993ff6d | ||
|
|
fbf435c799 | ||
|
|
8fdb0d82e8 | ||
|
|
dcbebab61b | ||
|
|
7b10afae43 | ||
|
|
81d8a88abd | ||
|
|
cb14158a0f | ||
|
|
38fc60352a | ||
|
|
3748f0d6e3 | ||
|
|
edd626d5e5 | ||
|
|
f785e75686 | ||
|
|
9a46ed70b7 | ||
|
|
532c09a815 | ||
|
|
e430d38c63 | ||
|
|
289b36de6d | ||
|
|
7e01f40e5c | ||
|
|
cac4bb5f13 | ||
|
|
c8bb041887 | ||
|
|
9302c5c6d1 | ||
|
|
84a3313f2c | ||
|
|
1eb3beeb2e | ||
|
|
d0e6ef8c6c | ||
|
|
5da1df7e20 | ||
|
|
16fa6d9805 | ||
|
|
0ca7766689 | ||
|
|
4342575418 | ||
|
|
4cdb73a60d | ||
|
|
a23c53bcb8 | ||
|
|
19ac4f92e0 | ||
|
|
0b8f48b4fd | ||
|
|
d2fa94a6cb | ||
|
|
da1c674911 | ||
|
|
7184bc1eb2 | ||
|
|
37f30a0f1a | ||
|
|
04f4b3e78a | ||
|
|
dbdc162fae | ||
|
|
563f673fb3 | ||
|
|
423e799a82 | ||
|
|
4734b89414 | ||
|
|
6f708e7733 | ||
|
|
f0c3f95189 | ||
|
|
93ca0e3f22 | ||
|
|
d94432636f | ||
|
|
60fca35d80 | ||
|
|
db71462d1c | ||
|
|
86f522176a | ||
|
|
94ee71c48f | ||
|
|
fda0b21fb0 | ||
|
|
775148cec8 | ||
|
|
f2dc4a1a00 | ||
|
|
eda5723a3c | ||
|
|
b044d9a6c0 | ||
|
|
125d5396b9 | ||
|
|
17b8b35a43 | ||
|
|
4bd017e3c7 | ||
|
|
37447a7fd3 | ||
|
|
1ed1dec65e | ||
|
|
5e2e465813 | ||
|
|
335655406e | ||
|
|
bf12d6330c | ||
|
|
3cca3187ad | ||
|
|
f4388a2cff | ||
|
|
7cf5d324eb | ||
|
|
ec9ef2b0fb | ||
|
|
6c852d93a9 | ||
|
|
290067932e | ||
|
|
4aa3a43604 | ||
|
|
3c33696452 | ||
|
|
3ea1ab8f30 | ||
|
|
76801f773d | ||
|
|
de7eeec94a | ||
|
|
0723a3ab95 | ||
|
|
8ef48ad977 | ||
|
|
ed24d1af60 | ||
|
|
e3dffb8245 | ||
|
|
74310a5ad3 | ||
|
|
a2223165e8 | ||
|
|
a879761dc2 | ||
|
|
124b909aa4 | ||
|
|
858e9eb066 | ||
|
|
7415e0cb97 | ||
|
|
915020ddc3 | ||
|
|
95915489b2 | ||
|
|
3e39c3351a | ||
|
|
244d32e7d2 | ||
|
|
16a79827b7 | ||
|
|
bd6a36e9ea | ||
|
|
771695af59 | ||
|
|
e811c7bd46 | ||
|
|
20f99c287f | ||
|
|
f464c9ae76 | ||
|
|
15ccb3dc04 | ||
|
|
defe40d48f | ||
|
|
0f2622e821 | ||
|
|
b442d238cd | ||
|
|
00f2a3c19d | ||
|
|
dcbfdf8666 | ||
|
|
6e84423032 | ||
|
|
912ab75a96 | ||
|
|
1022fc2f0c | ||
|
|
65edce855b | ||
|
|
0ba05d5795 | ||
|
|
1b17e17290 | ||
|
|
87957df1fb | ||
|
|
d3f8d4eff7 | ||
|
|
3ffeed3b39 | ||
|
|
4197e4402c | ||
|
|
1cfe6f5583 | ||
|
|
ad5cb83abf | ||
|
|
f4a4882dbc | ||
|
|
ecb2cf182a | ||
|
|
6aea839e04 | ||
|
|
e20d2fa95a | ||
|
|
806d0cc52f | ||
|
|
2a2baaf324 | ||
|
|
74989ec015 | ||
|
|
31af3a53ce | ||
|
|
361d51b55d | ||
|
|
a12c09eba5 | ||
|
|
bcfe0653bd | ||
|
|
c43bc763f3 | ||
|
|
85325c17ac | ||
|
|
178706f8de | ||
|
|
51defbb8b2 | ||
|
|
84cf9aa27a | ||
|
|
a66098e080 | ||
|
|
f00df5ca3f | ||
|
|
210c1fbecf | ||
|
|
b89c5142b6 | ||
|
|
0421f6b8fe | ||
|
|
9ef6efa3e0 | ||
|
|
b43afd4e9c | ||
|
|
7700aa2030 | ||
|
|
e68683acb7 | ||
|
|
8dc48c10c3 |
4
.gitignore
vendored
@@ -1,5 +1,8 @@
|
||||
bin
|
||||
/scripts/old
|
||||
/scripts/python/configurator/dist
|
||||
/scripts/python/configurator/build
|
||||
/scripts/python/configurator/venv
|
||||
.vs
|
||||
x64
|
||||
core.user
|
||||
@@ -15,3 +18,4 @@ node_modules
|
||||
hgt
|
||||
/client/public/databases/units/old
|
||||
/client/plugins/databasemanager/index.js
|
||||
|
||||
|
||||
40
LEGAL
@@ -2,13 +2,15 @@ DCS Olympus
|
||||
|
||||
A real-time AI unit control mod for DCS World
|
||||
|
||||
Copyright (C) 2023 Veltro & Gang
|
||||
Copyright (C) 2023 Veltro & Gang (the "DCS Olympus Team" or the
|
||||
"Rightsholders")
|
||||
|
||||
DCS Olympus (the "MATERIAL" or "Software") is provided completely free
|
||||
to users subject to the it under both the terms of version 3 of the GNU
|
||||
General Public License as published by the Free Software Foundation, and
|
||||
the additional terms set out below; except where such terms conflict with this
|
||||
disclaimer, in which case, the terms of this disclaimer shall prevail.
|
||||
General Public License ("GPLv3") as published by the Free Software Foundation,
|
||||
and the additional terms set out below (the "Additional Terms"). In the event
|
||||
that the terms of GPLv3 conflict with the Additional Terms, the
|
||||
Additional Terms shall prevail.
|
||||
|
||||
The authors and/or copyright holders of the Software have not received any
|
||||
financial benefit in connection with the Software. In any event, the
|
||||
@@ -17,15 +19,14 @@ implied, including but not limited to the warranties of merchantability,
|
||||
fitness for a particular purpose and non-infringement. In no event shall
|
||||
the authors and/or copyright holders be liable for any claim, damages or
|
||||
other liability, whether in an action of contract, tort or otherwise,
|
||||
arising from, out of or in connection with the Software or the use or o
|
||||
ther dealings in the Software.
|
||||
|
||||
Any party making use of the Software in any manner agrees to be
|
||||
bound by the terms set out in this disclaimer, version 3 of the GNU
|
||||
General Public Licence, and the Additional Terms below.
|
||||
arising from, out of or in connection with the Software or the use or
|
||||
other dealings in the Software.
|
||||
|
||||
THIS MATERIAL IS NOT MADE OR SUPPORTED BY EAGLE DYNAMICS SA.
|
||||
|
||||
Any party making use of the Software in any manner agrees to be bound by
|
||||
the entirety of this document.
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
@@ -657,4 +658,21 @@ copy of the Program in return for a fee.
|
||||
GNU General Public Licence Version 3 above shall be governed by and
|
||||
interpreted in accordance with English Law and the parties submit to the
|
||||
exclusive jurisdiction of the English Courts.
|
||||
|
||||
|
||||
2. Entire Agreement
|
||||
|
||||
The text of this document contains the entire understanding between
|
||||
you, the licensee, and the authors and/or copyright holders of the
|
||||
Software with respect to the subject matter to which it pertains.
|
||||
It supersedes all prior agreements and understandings (if applicable),
|
||||
oral or written, with respect to such matters.
|
||||
|
||||
3. Unilateral Modification
|
||||
|
||||
The parties agree that the DCS Olympus Team shall have the right to
|
||||
unilaterally modify these terms (i.e. the agreement between you and the
|
||||
DCS Olympus Team for the use of the Software), and that parties shall
|
||||
be bound by such terms as modified from time to time. The DCS Olympus Team
|
||||
shall not have an obligation to inform you of such modification, save that
|
||||
such changes will be published on the DCS Olympus Github Repository located at
|
||||
https://github.com/Pax1601/DCSOlympus.
|
||||
|
||||
31
README.md
@@ -1,4 +1,4 @@
|
||||
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for Q4 2023.
|
||||
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for mid december 2023.
|
||||
|
||||
# DCS Olympus
|
||||
*A real-time web interface to spawn and control units in DCS World*
|
||||
@@ -6,32 +6,19 @@
|
||||

|
||||
|
||||
### What is this?
|
||||
DCS Olympus is a mod for DCS World. It allows users to spawn, control, task, group, and remove units from a DCS World server using a real-time map interface, similarly to Real Time Strategy games. The user interface also provides useful informations units, like loadouts, fuel, tasking, and so on. In the future, more features for DCS World GCI and JTAC will be available.
|
||||
DCS: Olympus is a free and open-source mod for DCS that enables dynamic real-time control through a map interface. The user is able to spawn units/groups, deploy a variety of effects such as smoke, flares, or explosions, and waypoints/tasks can be given to AI units in real-time in a way similar to a classic RTS game.
|
||||
|
||||
### Features and how to use it
|
||||
- Spawn air and ground units, with preset loadouts
|
||||
- Double click on the map to spawn a blue and red units, both in the air and in the ground, with preset loadouts for air-to-air or air-to-ground tasks;
|
||||
- Control units
|
||||
- Select one ore more units to move them around. Hold down ctrl and click to create a route for the unit to follow;
|
||||
- Attack other units
|
||||
- After selecting one ore more units, double click on another unit and select "Attack" to attack it, depending on the available weapons.
|
||||
Additionally Olympus is able to run several effects and unit behaviours beyond the core DCS offerings. This includes such things as napalm and white phosphosous explosions, or setting up AA units to fire at players and miss, and more.
|
||||
|
||||
It even includes Red and Blue modes which limit your view and powers to just seeing what your coalition sees, with a spawning budget you could play against your friends even with no-one in the game piloting, or have a Red commander working against a squadron of blue pilots, and/or a blue commander working with them.
|
||||
|
||||
Even better it requires no client mods be installed if used on a server
|
||||
|
||||
The full feature list is simply too long to enumerate in a short summary but needless to say Olympus offers up a lot of unique gameplay that has previously not existed, and enhances many other elements of DCS in exciting ways
|
||||
|
||||
### Installing DCS Olympus
|
||||
A prebuilt installer will soon be released and available here
|
||||
|
||||
### Building DCS Olympus
|
||||
DCS Olympus is comprised of two modules:
|
||||
|
||||
A "core" c++ .dll module, which is run by DCS and exposes all the necessary data, and provides endpoints for commands from a REST server. A Visual Studio 2017/2019/2022 solution is provided, and requires no additional configuration. The core dll solution has two dependencies, both can be installed using vcpkg (https://vcpkg.io/en/getting-started.html):
|
||||
- cpprestsdk: `vcpkg install cpprestsdk:x64-windows`
|
||||
- geographiclib: `vcpkg install geographiclib:x64-windows`
|
||||
|
||||
|
||||
A "client" node.js typescript web app, which can be hosted on the server using express.js. A Visual Studio Code configuration is provided for debugging. The client requires node.js to be installed for building (https://nodejs.org/en/). After installing node.js, move in the client folder and run the following commands:
|
||||
- `npm install`
|
||||
- `npm -g install`
|
||||
|
||||
After installing all the necessary dependencies you can start a development server executing the *client/debug.bat* batch file, and visiting http:\\localhost:3000 with any modern browser (tested with updated Chrome, Firefox and Edge). However, it is highly suggested to simply run the `Launch Chrome against localhost` debug configuration in Visual Studio Code.
|
||||
|
||||
|
||||
|
||||
|
||||
28
build.bat
Normal file
@@ -0,0 +1,28 @@
|
||||
call git clean -fx
|
||||
|
||||
cd src
|
||||
msbuild olympus.sln /t:Rebuild /p:Configuration=Release
|
||||
cd ..
|
||||
|
||||
cd scripts/python/configurator
|
||||
call build_configurator.bat
|
||||
cd ../../..
|
||||
|
||||
cd client
|
||||
rmdir /s /q "hgt"
|
||||
call npm install
|
||||
call npm run emit-declarations
|
||||
call npm run build-release
|
||||
|
||||
cd "plugins\controltips"
|
||||
call npm install
|
||||
call npm run build-release
|
||||
cd "..\.."
|
||||
|
||||
cd "plugins\databasemanager"
|
||||
call npm install
|
||||
call npm run build-release
|
||||
cd "..\.."
|
||||
|
||||
call npm prune --production
|
||||
cd ..
|
||||
@@ -1,17 +1,2 @@
|
||||
cd src
|
||||
msbuild olympus.sln /t:Rebuild /p:Configuration=Release
|
||||
cd ..
|
||||
cd client
|
||||
call npm install
|
||||
rmdir /s /q "hgt"
|
||||
call npm run emit-declarations
|
||||
call npm run build
|
||||
cd "plugins\controltips"
|
||||
call npm run build
|
||||
cd "..\.."
|
||||
cd "plugins\databasemanager"
|
||||
call npm run build
|
||||
cd "..\.."
|
||||
call npm prune --production
|
||||
cd ..
|
||||
call build.bat
|
||||
call "C:\Program Files (x86)\Inno Setup 6\iscc.exe" "installer\olympus.iss"
|
||||
|
||||
790
client/@types/olympus/index.d.ts
vendored
@@ -43,7 +43,7 @@ if (config["server"] != undefined)
|
||||
module.exports = app;
|
||||
|
||||
const DemoDataGenerator = require('./demo.js');
|
||||
var demoDataGenerator = new DemoDataGenerator(app);
|
||||
var demoDataGenerator = new DemoDataGenerator(app, config);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
console.log('\x1b[36m%s\x1b[0m', "*********************************************************************");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* _____ _____ _____ ____ _ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | __ \\ / ____|/ ____| / __ \\| | *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | | | | | | (___ | | | | |_ _ _ __ ___ _ __ _ _ ___ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | | | | | \\___ \\ | | | | | | | | '_ ` _ \\| '_ \\| | | / __| *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* | |__| | |____ ____) | | |__| | | |_| | | | | | | |_) | |_| \\__ \\ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* |_____/ \\_____|_____/ \\____/|_|\\__, |_| |_| |_| .__/ \\__,_|___/ *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* __/ | | | *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "* |___/ |_| *");
|
||||
console.log('\x1b[36m%s\x1b[0m', "*********************************************************************");
|
||||
console.log('\x1b[36m%s\x1b[0m', "");
|
||||
console.log("Please wait while DCS Olympus Server starts up...");
|
||||
|
||||
var fs = require('fs');
|
||||
let rawdata = fs.readFileSync('../olympus.json');
|
||||
let config = JSON.parse(rawdata);
|
||||
@@ -98,3 +111,6 @@ function onListening() {
|
||||
: 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
|
||||
console.log("DCS Olympus server v0.4.9-alpha-rc1 started correctly!")
|
||||
console.log("Waiting for connections...")
|
||||
|
||||
111
client/demo.js
@@ -14,7 +14,7 @@ const DEMO_WEAPONS_DATA = {
|
||||
}
|
||||
|
||||
class DemoDataGenerator {
|
||||
constructor(app)
|
||||
constructor(app, config)
|
||||
{
|
||||
app.get('/demo/units', (req, res) => this.units(req, res));
|
||||
app.get('/demo/weapons', (req, res) => this.weapons(req, res));
|
||||
@@ -27,15 +27,15 @@ class DemoDataGenerator {
|
||||
|
||||
app.use('/demo', basicAuth({
|
||||
users: {
|
||||
'admin': 'password',
|
||||
'blue': 'bluepassword',
|
||||
'red': 'redpassword'
|
||||
'admin': config["authentication"]["gameMasterPassword"],
|
||||
'blue': config["authentication"]["blueCommanderPassword"],
|
||||
'red': config["authentication"]["redCommanderPassword"]
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
let baseData = { alive: true, human: false, controlled: true, coalition: 2, country: 0, unitName: "Cool guy", groupName: "Cool group 1", state: 1, task: "Being cool!",
|
||||
hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 45, isActiveTanker: false, isActiveAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
let baseData = { alive: true, human: false, controlled: true, coalition: 2, country: 0, unitName: "Cool guy", groupName: "Cool group 1", state: 13, task: "Being cool!",
|
||||
hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 45, track: 45, isActiveTanker: false, isActiveAWACS: 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,
|
||||
@@ -52,10 +52,10 @@ class DemoDataGenerator {
|
||||
isLeader: true
|
||||
}
|
||||
|
||||
|
||||
|
||||
// UNCOMMENT TO TEST ALL UNITS ****************
|
||||
/*
|
||||
|
||||
***************** UNCOMMENT TO TEST ALL UNITS ****************
|
||||
|
||||
var databases = Object.assign({}, aircraftDatabase, helicopterDatabase, groundUnitDatabase, navyUnitDatabase);
|
||||
var t = Object.keys(databases).length;
|
||||
var l = Math.floor(Math.sqrt(t));
|
||||
@@ -70,6 +70,7 @@ class DemoDataGenerator {
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-${idx}`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += latIdx / 5;
|
||||
DEMO_UNIT_DATA[idx].position.lng += lngIdx / 5;
|
||||
DEMO_UNIT_DATA[idx].coalition = Math.floor(Math.random() * 3)
|
||||
|
||||
latIdx += 1;
|
||||
if (latIdx === l) {
|
||||
@@ -89,8 +90,9 @@ class DemoDataGenerator {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
*/
|
||||
let idx = 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "S_75M_Volhov";
|
||||
@@ -114,6 +116,7 @@ class DemoDataGenerator {
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = false;
|
||||
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
@@ -130,6 +133,8 @@ class DemoDataGenerator {
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = true;
|
||||
DEMO_UNIT_DATA[idx].coalition = 0;
|
||||
DEMO_UNIT_DATA[idx].operateAs = 2;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
@@ -138,6 +143,8 @@ class DemoDataGenerator {
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = true;
|
||||
DEMO_UNIT_DATA[idx].coalition = 0;
|
||||
DEMO_UNIT_DATA[idx].operateAs = 1;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
@@ -158,48 +165,50 @@ class DemoDataGenerator {
|
||||
if (req.query["time"] == 0){
|
||||
for (let idx in DEMO_UNIT_DATA) {
|
||||
const unit = DEMO_UNIT_DATA[idx];
|
||||
var dataIndex = 1;
|
||||
array = this.concat(array, this.uint32ToByteArray(idx));
|
||||
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);
|
||||
array = this.appendUint16(array, unit.coalition, 5);
|
||||
array = this.appendUint8(array, unit.country, 6);
|
||||
array = this.appendString(array, unit.name, 7);
|
||||
array = this.appendString(array, unit.unitName, 8);
|
||||
array = this.appendString(array, unit.groupName, 9);
|
||||
array = this.appendUint8(array, unit.state, 10);
|
||||
array = this.appendString(array, unit.task, 11);
|
||||
array = this.appendUint8(array, unit.hasTask, 12);
|
||||
array = this.appendCoordinates(array, unit.position, 13);
|
||||
array = this.appendDouble(array, unit.speed, 14);
|
||||
array = this.appendDouble(array, unit.horizontalVelocity, 15);
|
||||
array = this.appendDouble(array, unit.verticalVelicity, 16);
|
||||
array = this.appendDouble(array, unit.heading, 17);
|
||||
array = this.appendUint8(array, unit.isActiveTanker, 18);
|
||||
array = this.appendUint8(array, unit.isActiveAWACS, 19);
|
||||
array = this.appendUint8(array, unit.onOff, 20);
|
||||
array = this.appendUint8(array, unit.followRoads, 21);
|
||||
array = this.appendUint16(array, unit.fuel, 22);
|
||||
array = this.appendDouble(array, unit.desiredSpeed, 23);
|
||||
array = this.appendUint8(array, unit.desiredSpeedType, 24);
|
||||
array = this.appendDouble(array, unit.desiredAltitude, 25);
|
||||
array = this.appendUint8(array, unit.desiredAltitudeType, 26);
|
||||
array = this.appendUint32(array, unit.leaderID, 27);
|
||||
array = this.appendOffset(array, unit.formationOffset, 28);
|
||||
array = this.appendUint32(array, unit.targetID, 29);
|
||||
array = this.appendCoordinates(array, unit.targetPosition, 30);
|
||||
array = this.appendUint8(array, unit.ROE, 31);
|
||||
array = this.appendUint8(array, unit.reactionToThreat, 32);
|
||||
array = this.appendUint8(array, unit.emissionsCountermeasures, 33);
|
||||
array = this.appendTACAN(array, unit.TACAN, 34);
|
||||
array = this.appendRadio(array, unit.radio, 35);
|
||||
array = this.appendRadio(array, unit.generalSettings, 36);
|
||||
array = this.appendAmmo(array, unit.ammo, 37);
|
||||
array = this.appendContacts(array, unit.contacts, 38);
|
||||
array = this.appendActivePath(array, unit.activePath, 39);
|
||||
array = this.appendUint8(array, unit.isLeader, 40);
|
||||
array = this.appendUint8(array, unit.operateAs, 41);
|
||||
array = this.appendString(array, unit.category, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.alive, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.human, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.controlled, dataIndex); dataIndex++;
|
||||
array = this.appendUint16(array, unit.coalition, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.country, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.name, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.unitName, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.groupName, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.state, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.task, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.hasTask, dataIndex); dataIndex++;
|
||||
array = this.appendCoordinates(array, unit.position, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.speed, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.horizontalVelocity, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.verticalVelicity, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.heading, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.track, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.isActiveTanker, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.isActiveAWACS, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.onOff, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.followRoads, dataIndex); dataIndex++;
|
||||
array = this.appendUint16(array, unit.fuel, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.desiredSpeed, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.desiredSpeedType, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.desiredAltitude, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.desiredAltitudeType, dataIndex); dataIndex++;
|
||||
array = this.appendUint32(array, unit.leaderID, dataIndex); dataIndex++;
|
||||
array = this.appendOffset(array, unit.formationOffset, dataIndex); dataIndex++;
|
||||
array = this.appendUint32(array, unit.targetID, dataIndex); dataIndex++;
|
||||
array = this.appendCoordinates(array, unit.targetPosition, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.ROE, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.reactionToThreat, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.emissionsCountermeasures, dataIndex); dataIndex++;
|
||||
array = this.appendTACAN(array, unit.TACAN, dataIndex); dataIndex++;
|
||||
array = this.appendRadio(array, unit.radio, dataIndex); dataIndex++;
|
||||
array = this.appendRadio(array, unit.generalSettings, dataIndex); dataIndex++;
|
||||
array = this.appendAmmo(array, unit.ammo, dataIndex); dataIndex++;
|
||||
array = this.appendContacts(array, unit.contacts, dataIndex); dataIndex++;
|
||||
array = this.appendActivePath(array, unit.activePath, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.isLeader, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.operateAs, dataIndex); dataIndex++;
|
||||
array = this.concat(array, this.uint8ToByteArray(255));
|
||||
}
|
||||
}
|
||||
|
||||
1297
client/package-lock.json
generated
@@ -2,10 +2,11 @@
|
||||
"name": "DCSOlympus",
|
||||
"node-main": "./bin/www",
|
||||
"main": "http://localhost:3000",
|
||||
"version": "v0.4.7-alpha",
|
||||
"version": "v0.4.9-alpha-rc1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ] && copy.bat",
|
||||
"build": "browserify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ] && copy.bat",
|
||||
"build-release": "browserify .\\src\\index.ts -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ] && copy.bat",
|
||||
"emit-declarations": "tsc --project tsconfig.json --declaration --emitDeclarationOnly --outfile ./@types/olympus/index.d.ts",
|
||||
"copy": "copy.bat",
|
||||
"start": "node ./bin/www",
|
||||
@@ -20,14 +21,16 @@
|
||||
"ejs": "^3.1.8",
|
||||
"express": "~4.16.1",
|
||||
"express-basic-auth": "^1.2.1",
|
||||
"js-sha256": "^0.10.1",
|
||||
"leaflet-gesture-handling": "^1.2.2",
|
||||
"morgan": "~1.9.1",
|
||||
"save": "^2.9.0",
|
||||
"srtm-elevation": "^2.1.2"
|
||||
"srtm-elevation": "^2.1.2",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@tanem/svg-injector": "^10.1.55",
|
||||
"@tanem/svg-injector": "^10.1.68",
|
||||
"@types/formatcoords": "^1.1.0",
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
@@ -48,10 +51,9 @@
|
||||
"nodemon": "^2.0.20",
|
||||
"requirejs": "^2.3.6",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tinyify": "^4.0.0",
|
||||
"tsify": "^5.0.4",
|
||||
"tslib": "latest",
|
||||
"typedoc": "^0.24.8",
|
||||
"typedoc-umlclass": "^0.7.1",
|
||||
"typescript": "^4.9.4",
|
||||
"usng.js": "^0.4.5",
|
||||
"watchify": "^4.0.0"
|
||||
|
||||
8
client/plugins/_boilerplate/copy.bat
Normal file
@@ -0,0 +1,8 @@
|
||||
mkdir .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin
|
||||
|
||||
copy .\\index.js .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\index.js
|
||||
copy .\\plugin.json .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\plugin.json
|
||||
copy .\\style.css .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\style.css
|
||||
|
||||
mkdir .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\images
|
||||
copy .\\images\\*.* .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\images\\
|
||||
BIN
client/plugins/_boilerplate/images/placeholder1x1.png
Normal file
|
After Width: | Height: | Size: 928 B |
14
client/plugins/_boilerplate/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "boilerplateplugin",
|
||||
"version": "v0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/sortablejs": "^1.15.4",
|
||||
"browserify": "^17.0.0",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tsify": "^5.0.4"
|
||||
}
|
||||
}
|
||||
7
client/plugins/_boilerplate/plugin.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Boilerplate",
|
||||
"version": "0.0.1",
|
||||
"description": "Base plugin starter",
|
||||
"authorName": "",
|
||||
"authorContact": ""
|
||||
}
|
||||
3
client/plugins/_boilerplate/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Boilerplate plugin
|
||||
|
||||
See: https://github.com/Pax1601/DCSOlympus/wiki/Developer-Guide
|
||||
27
client/plugins/_boilerplate/src/boilerplate.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { OlympusPlugin } from "interfaces";
|
||||
import { OlympusApp } from "olympusapp";
|
||||
|
||||
|
||||
export class BoilerplatePlugin implements OlympusPlugin {
|
||||
#app!: OlympusApp;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param app <OlympusApp>
|
||||
*
|
||||
* @returns boolean on success/fail
|
||||
*/
|
||||
|
||||
initialize(app: OlympusApp) : boolean {
|
||||
this.#app = app;
|
||||
|
||||
return true; // Return true on success
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "Boilerplate";
|
||||
}
|
||||
|
||||
}
|
||||
5
client/plugins/_boilerplate/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { BoilerplatePlugin } from "./boilerplate";
|
||||
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new BoilerplatePlugin();
|
||||
}
|
||||
0
client/plugins/_boilerplate/style.css
Normal file
104
client/plugins/_boilerplate/tsconfig.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
/* Language and Environment */
|
||||
"target": "ES2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDirs": ["./src", "./@types"], /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"@types"
|
||||
], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"types": [
|
||||
"olympus"
|
||||
], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
/* JavaScript Support */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src/*.ts",
|
||||
"../DCSOlympus/client/@types/olympus/index.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,384 +0,0 @@
|
||||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
|
||||
"use strict";
|
||||
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
||||
if (kind === "m") throw new TypeError("Private method is not writable");
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
||||
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
||||
};
|
||||
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
||||
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
||||
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
||||
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
||||
};
|
||||
var _ControlTipsPlugin_instances, _ControlTipsPlugin_element, _ControlTipsPlugin_app, _ControlTipsPlugin_shortcutManager, _ControlTipsPlugin_cursorIsHoveringOverUnit, _ControlTipsPlugin_cursorIsHoveringOverAirbase, _ControlTipsPlugin_mouseoverElement, _ControlTipsPlugin_updateTips;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ControlTipsPlugin = void 0;
|
||||
const SHOW_CONTROL_TIPS = "Show control tips";
|
||||
class ControlTipsPlugin {
|
||||
constructor() {
|
||||
_ControlTipsPlugin_instances.add(this);
|
||||
_ControlTipsPlugin_element.set(this, void 0);
|
||||
_ControlTipsPlugin_app.set(this, void 0);
|
||||
_ControlTipsPlugin_shortcutManager.set(this, void 0);
|
||||
_ControlTipsPlugin_cursorIsHoveringOverUnit.set(this, false);
|
||||
_ControlTipsPlugin_cursorIsHoveringOverAirbase.set(this, false);
|
||||
_ControlTipsPlugin_mouseoverElement.set(this, void 0);
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_element, document.createElement("div"), "f");
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_element, "f").id = "control-tips-panel";
|
||||
document.body.appendChild(__classPrivateFieldGet(this, _ControlTipsPlugin_element, "f"));
|
||||
}
|
||||
getName() {
|
||||
return "Control Tips Plugin";
|
||||
}
|
||||
initialize(app) {
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_app, app, "f");
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_shortcutManager, __classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getShortcutManager(), "f");
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_shortcutManager, "f").onKeyDown(() => {
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_shortcutManager, "f").onKeyUp(() => {
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("airbaseMouseover", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverAirbase, true, "f");
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("airbaseMouseout", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverAirbase, false, "f");
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("unitDeselection", (ev) => {
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("unitMouseover", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverUnit, true, "f");
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("unitMouseout", (ev) => {
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_cursorIsHoveringOverUnit, false, "f");
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("unitsSelection", (ev) => {
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("mapVisibilityOptionsChanged", () => {
|
||||
this.toggle(!__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getMap().getVisibilityOptions()[SHOW_CONTROL_TIPS]);
|
||||
});
|
||||
document.addEventListener("mouseover", (ev) => {
|
||||
if (ev.target instanceof HTMLElement) {
|
||||
__classPrivateFieldSet(this, _ControlTipsPlugin_mouseoverElement, ev.target, "f");
|
||||
}
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
document.addEventListener("mouseup", (ev) => {
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
});
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_instances, "m", _ControlTipsPlugin_updateTips).call(this);
|
||||
__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getMap().addVisibilityOption(SHOW_CONTROL_TIPS, true);
|
||||
return true;
|
||||
}
|
||||
getElement() {
|
||||
return __classPrivateFieldGet(this, _ControlTipsPlugin_element, "f");
|
||||
}
|
||||
toggle(bool) {
|
||||
this.getElement().classList.toggle("hide", bool);
|
||||
}
|
||||
}
|
||||
exports.ControlTipsPlugin = ControlTipsPlugin;
|
||||
_ControlTipsPlugin_element = new WeakMap(), _ControlTipsPlugin_app = new WeakMap(), _ControlTipsPlugin_shortcutManager = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverUnit = new WeakMap(), _ControlTipsPlugin_cursorIsHoveringOverAirbase = new WeakMap(), _ControlTipsPlugin_mouseoverElement = new WeakMap(), _ControlTipsPlugin_instances = new WeakSet(), _ControlTipsPlugin_updateTips = function _ControlTipsPlugin_updateTips() {
|
||||
const combos = [
|
||||
{
|
||||
"keys": [],
|
||||
"tips": [
|
||||
{
|
||||
"key": `SHIFT`,
|
||||
"action": `Box select`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"showIfUnitSelected": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": `Deselect`,
|
||||
"showIfUnitSelected": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse1+drag`,
|
||||
"action": `Move map`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"showIfUnitSelected": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Spawn menu`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Quick options`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Airbase menu`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Set first waypoint`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2 (hold)`,
|
||||
"action": `Interact (ground)`,
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Shift`,
|
||||
"action": "<em> in formation...</em>",
|
||||
"showIfUnitSelected": true,
|
||||
"minSelectedUnits": 2
|
||||
},
|
||||
{
|
||||
"key": "CTRL",
|
||||
"action": "<em> ... more</em>",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL",
|
||||
"action": " Pin tool",
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL+Mouse2",
|
||||
"action": " Airbase menu",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle Blue/Red",
|
||||
"mouseoverSelector": "#coalition-switch .ol-switch-fill"
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": "Set Neutral",
|
||||
"mouseoverSelector": "#coalition-switch .ol-switch-fill"
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle time display",
|
||||
"mouseoverSelector": "#connection-status-panel[data-is-connected] #connection-status-message abbr"
|
||||
},
|
||||
{
|
||||
"key": `Mouse1 or Z`,
|
||||
"action": "Change location system",
|
||||
"mouseoverSelector": "#coordinates-tool, #coordinates-tool *"
|
||||
},
|
||||
{
|
||||
"key": `Comma`,
|
||||
"action": "Decrease precision",
|
||||
"mouseoverSelector": `#coordinates-tool[data-location-system="MGRS"], #coordinates-tool[data-location-system="MGRS"] *`
|
||||
},
|
||||
{
|
||||
"key": `Period`,
|
||||
"action": "Increase precision",
|
||||
"mouseoverSelector": `#coordinates-tool[data-location-system="MGRS"], #coordinates-tool[data-location-system="MGRS"] *`
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": ["ControlLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle pin",
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse1`,
|
||||
"action": "Toggle selection",
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Add waypoint`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Interact (airbase)`,
|
||||
"showIfHoveringOverAirbase": true,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Interact (unit)`,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true,
|
||||
"showIfUnitSelected": true,
|
||||
"unitsMustBeControlled": true
|
||||
},
|
||||
{
|
||||
"key": `Shift`,
|
||||
"action": "<em> in formation...</em>",
|
||||
"showIfUnitSelected": true,
|
||||
"minSelectedUnits": 2
|
||||
},
|
||||
{
|
||||
"key": `[Num 1-9]`,
|
||||
"action": "Set hotgroup",
|
||||
"showIfUnitSelected": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": ["ShiftLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `Mouse1+drag`,
|
||||
"action": "Box select",
|
||||
"showIfUnitSelected": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": "Set first formation waypoint",
|
||||
"showIfUnitSelected": true,
|
||||
"minSelectedUnits": 2
|
||||
},
|
||||
{
|
||||
"key": `[Num 1-9]`,
|
||||
"action": "Add to hotgroup",
|
||||
"showIfUnitSelected": true
|
||||
},
|
||||
{
|
||||
"key": "CTRL",
|
||||
"action": "<em> ... more</em>",
|
||||
"minSelectedUnits": 2,
|
||||
"showIfUnitSelected": true,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"unitsMustBeControlled": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"keys": ["ControlLeft", "ShiftLeft"],
|
||||
"tips": [
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": "Add formation waypoint",
|
||||
"showIfUnitSelected": true,
|
||||
"minSelectedUnits": 2,
|
||||
"unitsMustBeControlled": true
|
||||
}, {
|
||||
"key": `[Num 1-9]`,
|
||||
"action": "Add hotgroup to selection",
|
||||
"callback": (tip) => {
|
||||
return (Object.values(__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getUnitsManager().getUnits()).some((unit) => {
|
||||
return unit.getHotgroup();
|
||||
}));
|
||||
},
|
||||
"showIfUnitSelected": true,
|
||||
"minSelectedUnits": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
const currentCombo = combos.find((combo) => __classPrivateFieldGet(this, _ControlTipsPlugin_shortcutManager, "f").keyComboMatches(combo.keys)) || combos[0];
|
||||
const element = this.getElement();
|
||||
element.innerHTML = "";
|
||||
let numSelectedUnits = 0;
|
||||
let numSelectedControlledUnits = 0;
|
||||
let unitSelectionContainsControlled = false;
|
||||
if (__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getUnitsManager()) {
|
||||
let selectedUnits = Object.values(__classPrivateFieldGet(this, _ControlTipsPlugin_app, "f").getUnitsManager().getSelectedUnits());
|
||||
numSelectedUnits = selectedUnits.length;
|
||||
numSelectedControlledUnits = selectedUnits.filter((unit) => unit.getControlled()).length;
|
||||
unitSelectionContainsControlled = numSelectedControlledUnits > 0;
|
||||
}
|
||||
const tipsIncludesActiveMouseover = (currentCombo.tips.some((tip) => {
|
||||
if (!tip.mouseoverSelector) {
|
||||
return false;
|
||||
}
|
||||
if (__classPrivateFieldGet(this, _ControlTipsPlugin_mouseoverElement, "f") instanceof HTMLElement === false) {
|
||||
return false;
|
||||
}
|
||||
if (!__classPrivateFieldGet(this, _ControlTipsPlugin_mouseoverElement, "f").matches(tip.mouseoverSelector)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}));
|
||||
currentCombo.tips.filter((tip) => {
|
||||
if (numSelectedUnits > 0) {
|
||||
if (tip.showIfUnitSelected === false) {
|
||||
return false;
|
||||
}
|
||||
if (tip.unitsMustBeControlled === true && unitSelectionContainsControlled === false) {
|
||||
return false;
|
||||
}
|
||||
if (typeof tip.minSelectedUnits === "number" && numSelectedControlledUnits < tip.minSelectedUnits) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (numSelectedUnits === 0 && tip.showIfUnitSelected === true) {
|
||||
return false;
|
||||
}
|
||||
if (typeof tip.showIfHoveringOverAirbase === "boolean") {
|
||||
if (tip.showIfHoveringOverAirbase !== __classPrivateFieldGet(this, _ControlTipsPlugin_cursorIsHoveringOverAirbase, "f")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (typeof tip.showIfHoveringOverUnit === "boolean") {
|
||||
if (tip.showIfHoveringOverUnit !== __classPrivateFieldGet(this, _ControlTipsPlugin_cursorIsHoveringOverUnit, "f")) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (tipsIncludesActiveMouseover && (typeof tip.mouseoverSelector !== "string" || !__classPrivateFieldGet(this, _ControlTipsPlugin_mouseoverElement, "f").matches(tip.mouseoverSelector))) {
|
||||
return false;
|
||||
}
|
||||
if (!tipsIncludesActiveMouseover && typeof tip.mouseoverSelector === "string") {
|
||||
return false;
|
||||
}
|
||||
if (typeof tip.callback === "function" && !tip.callback(tip)) {
|
||||
return false;
|
||||
}
|
||||
element.innerHTML += `<div><span class="key">${tip.key}</span><span class="action">${tip.action}</span></div>`;
|
||||
});
|
||||
};
|
||||
|
||||
},{}],2:[function(require,module,exports){
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const controltipsplugin_1 = require("./controltipsplugin");
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new controltipsplugin_1.ControlTipsPlugin();
|
||||
};
|
||||
|
||||
},{"./controltipsplugin":1}]},{},[2]);
|
||||
6251
client/plugins/controltips/package-lock.json
generated
@@ -3,8 +3,27 @@
|
||||
"version": "v0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat"
|
||||
"build": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat",
|
||||
"build-release": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {}
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@types/node": "^18.16.1",
|
||||
"@types/sortablejs": "^1.15.0",
|
||||
"babelify": "^10.0.0",
|
||||
"browserify": "^17.0.0",
|
||||
"concurrently": "^7.6.0",
|
||||
"cp": "^0.2.0",
|
||||
"esmify": "^2.1.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"requirejs": "^2.3.6",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tinyify": "^4.0.0",
|
||||
"tsify": "^5.0.4",
|
||||
"tslib": "latest",
|
||||
"typescript": "^4.9.4",
|
||||
"usng.js": "^0.4.5",
|
||||
"watchify": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ export class ControlTipsPlugin implements OlympusPlugin {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("mapVisibilityOptionsChanged", () => {
|
||||
document.addEventListener("mapOptionsChanged", () => {
|
||||
this.toggle( !this.#app.getMap().getVisibilityOptions()[SHOW_CONTROL_TIPS] );
|
||||
});
|
||||
|
||||
|
||||
6203
client/plugins/databasemanager/package-lock.json
generated
@@ -4,10 +4,29 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat",
|
||||
"build-release": "browserify ./src/index.ts -p [ tsify --noImplicitAny] -p [ tinyify ] > index.js && copy.bat",
|
||||
"start": "npm run copy & concurrently --kill-others \"npm run watch\"",
|
||||
"copy": "copy.bat",
|
||||
"watch": "watchify ./src/index.ts --debug -o ../../public/plugins/databasemanager/index.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
|
||||
"watch": "watchify ./src/index.ts --debug -o ../../public/plugins/databasemanager/index.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js']"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {}
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@types/node": "^18.16.1",
|
||||
"@types/sortablejs": "^1.15.0",
|
||||
"babelify": "^10.0.0",
|
||||
"browserify": "^17.0.0",
|
||||
"concurrently": "^7.6.0",
|
||||
"cp": "^0.2.0",
|
||||
"esmify": "^2.1.1",
|
||||
"nodemon": "^2.0.20",
|
||||
"requirejs": "^2.3.6",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tinyify": "^4.0.0",
|
||||
"tsify": "^5.0.4",
|
||||
"tslib": "latest",
|
||||
"typescript": "^4.9.4",
|
||||
"usng.js": "^0.4.5",
|
||||
"watchify": "^4.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { NavyUnitEditor } from "./navyuniteditor";
|
||||
*/
|
||||
|
||||
export class DatabaseManagerPlugin implements OlympusPlugin {
|
||||
#app: OlympusApp | null = null;
|
||||
#app!: OlympusApp;
|
||||
|
||||
#element: HTMLElement;
|
||||
#mainContentContainer: HTMLElement;
|
||||
@@ -157,6 +157,15 @@ export class DatabaseManagerPlugin implements OlympusPlugin {
|
||||
*/
|
||||
initialize(app: any) {
|
||||
this.#app = app;
|
||||
|
||||
const contextManager = this.#app.getContextManager();
|
||||
contextManager.add( "databaseManager", {
|
||||
"allowUnitCopying": false,
|
||||
"allowUnitPasting": false,
|
||||
"useSpawnMenu": false,
|
||||
"useUnitControlPanel": false,
|
||||
"useUnitInfoPanel": false
|
||||
});
|
||||
|
||||
/* Load the databases and initialize the editors */
|
||||
this.#loadDatabases();
|
||||
@@ -169,7 +178,7 @@ export class DatabaseManagerPlugin implements OlympusPlugin {
|
||||
var toolbar: PrimaryToolbar = this.#app?.getToolbarsManager().get("primaryToolbar") as PrimaryToolbar;
|
||||
var elements = toolbar.getMainDropdown().getOptionElements();
|
||||
var arr = Array.prototype.slice.call(elements);
|
||||
arr.splice(arr.length - 1, 0, mainButtonDiv);
|
||||
arr.splice(arr.length - 3, 0, mainButtonDiv);
|
||||
toolbar.getMainDropdown().setOptionsElements(arr);
|
||||
mainButton.onclick = () => {
|
||||
toolbar.getMainDropdown().close();
|
||||
@@ -197,6 +206,9 @@ export class DatabaseManagerPlugin implements OlympusPlugin {
|
||||
this.getElement().classList.toggle("hide", !bool);
|
||||
else
|
||||
this.getElement().classList.toggle("hide");
|
||||
|
||||
if ( this.#app )
|
||||
this.#app.getContextManager().setContext( this.getElement().classList.contains("hide") ? "olympus" : "databaseManager" );
|
||||
}
|
||||
|
||||
/** Hide all the editors
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
{
|
||||
"BDK-775": {
|
||||
"name": "BDK-775",
|
||||
"coalition": "blue",
|
||||
"type": "Landing Ship",
|
||||
"era": "Mid Cold War",
|
||||
"label": "LS Ropucha",
|
||||
"shortLabel": "LS Ropucha",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 6000,
|
||||
"description": "Landing ship Ropucha",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"CVN_71": {
|
||||
"name": "CVN_71",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-71 Theodore Roosevelt",
|
||||
"shortLabel": "CVN-71",
|
||||
@@ -28,7 +11,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -36,7 +20,7 @@
|
||||
"CVN_72": {
|
||||
"name": "CVN_72",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-72 Abraham Lincoln",
|
||||
"shortLabel": "CVN-72",
|
||||
@@ -45,7 +29,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -53,7 +38,7 @@
|
||||
"CVN_73": {
|
||||
"name": "CVN_73",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-73 George Washington",
|
||||
"shortLabel": "CVN-73",
|
||||
@@ -62,7 +47,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -101,7 +87,8 @@
|
||||
},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -111,13 +98,14 @@
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Modern",
|
||||
"label": "CV Admiral Kuznetsov(2017)",
|
||||
"shortLabel": "Admiral Kuznetsov(2017)",
|
||||
"label": "Admiral Kuznetsov (2017)",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "Admiral Kuznetsov. Conventional STOBAR carrier",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -126,8 +114,8 @@
|
||||
"CastleClass_01": {
|
||||
"name": "CastleClass_01",
|
||||
"coalition": "blue",
|
||||
"type": "Patrol",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Leeds Castle (P-258)",
|
||||
"shortLabel": "HMS Leeds Castle (P-258)",
|
||||
"range": "",
|
||||
@@ -141,7 +129,8 @@
|
||||
},
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 3000,
|
||||
"description": "HMS Leeds Castle. Smaller. Patrol craft",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -149,7 +138,7 @@
|
||||
"HandyWind": {
|
||||
"name": "HandyWind",
|
||||
"coalition": "blue",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Bulker Handy Wind",
|
||||
"shortLabel": "Bulker Handy Wind",
|
||||
@@ -176,6 +165,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -184,7 +174,7 @@
|
||||
"HarborTug": {
|
||||
"name": "HarborTug",
|
||||
"coalition": "",
|
||||
"type": "Tug",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Harbor Tug",
|
||||
"shortLabel": "Harbor Tug",
|
||||
@@ -211,6 +201,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -228,7 +219,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 20000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -481,6 +473,7 @@
|
||||
},
|
||||
"acquisitionRange": 19000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -489,15 +482,16 @@
|
||||
"Seawise_Giant": {
|
||||
"name": "Seawise_Giant",
|
||||
"coalition": "blue",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Tanker Seawise Giant",
|
||||
"label": "Seawise Giant (Tanker)",
|
||||
"shortLabel": "Seawise Giant",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -506,7 +500,7 @@
|
||||
"Ship_Tilde_Supply": {
|
||||
"name": "Ship_Tilde_Supply",
|
||||
"coalition": "blue",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Supply Ship MV Tilde",
|
||||
"shortLabel": "Supply Ship Tilde",
|
||||
@@ -515,6 +509,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -532,6 +527,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -540,9 +536,9 @@
|
||||
"TICONDEROG": {
|
||||
"name": "TICONDEROG",
|
||||
"coalition": "blue",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Ticonderoga",
|
||||
"label": "Ticonderoga Class (Cruiser)",
|
||||
"shortLabel": "Ticonderoga",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -611,7 +607,8 @@
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -619,9 +616,9 @@
|
||||
"Type_052B": {
|
||||
"name": "Type_052B",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052B DDG-168 Guangzhou",
|
||||
"label": "Type 52B Guangzhou",
|
||||
"shortLabel": "Type 52B",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -646,7 +643,8 @@
|
||||
},
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 30000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -654,9 +652,9 @@
|
||||
"Type_052C": {
|
||||
"name": "Type_052C",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052C DDG-171 Haikou",
|
||||
"label": "Type 52C Haikou (Destroyer)",
|
||||
"shortLabel": "Type 52C",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -705,17 +703,18 @@
|
||||
},
|
||||
"acquisitionRange": 260000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"Type_054A": {
|
||||
"name": "",
|
||||
"name": "Type_054A",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "054A FFG-538 Yantai",
|
||||
"label": "Type 54A Yantai (Frigate)",
|
||||
"shortLabel": "Type 54A",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -872,15 +871,16 @@
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 45000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "35nm >50,000ft range"
|
||||
},
|
||||
"Type_071": {
|
||||
"name": "Type_071",
|
||||
"coalition": "red",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Type 071",
|
||||
"label": "Type 071 (Transport dock)",
|
||||
"shortLabel": "Type 071",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -925,7 +925,8 @@
|
||||
},
|
||||
"acquisitionRange": 300000,
|
||||
"engagementRange": 150000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -935,7 +936,7 @@
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Modern",
|
||||
"label": "Type 093",
|
||||
"label": "Type 093 (Shang)",
|
||||
"shortLabel": "Type 093",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -948,6 +949,7 @@
|
||||
},
|
||||
"acquisitionRange": 40000,
|
||||
"engagementRange": 40000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -956,10 +958,10 @@
|
||||
"USS_Arleigh_Burke_IIa": {
|
||||
"name": "USS_Arleigh_Burke_IIa",
|
||||
"coalition": "blue",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "DDG Arleigh Burke lla",
|
||||
"shortLabel": "DDG Arleigh Burke",
|
||||
"label": "Arleigh Burke Class (Destroyer)",
|
||||
"shortLabel": "Arleigh Burke Class (Destroyer)",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
@@ -1064,6 +1066,7 @@
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"abilities": "",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1080,7 +1083,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 18000,
|
||||
"engagementRange": 5000,
|
||||
"description": "ARA Vienticinco de Mayo. Conventional CATOBAR carrier",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1089,15 +1093,16 @@
|
||||
"name": "hms_invincible",
|
||||
"coalition": "blue",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Mid Cold War",
|
||||
"label": "HMS Invincible (R05)",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Invincible",
|
||||
"shortLabel": "HMS Invincible",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 74000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1105,8 +1110,8 @@
|
||||
"leander-gun-achilles": {
|
||||
"name": "leander-gun-achilles",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Achilles (F12)",
|
||||
"shortLabel": "HMS Achilles",
|
||||
"range": "",
|
||||
@@ -1114,7 +1119,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 8000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1122,8 +1128,8 @@
|
||||
"leander-gun-andromeda": {
|
||||
"name": "leander-gun-andromeda",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Andromeda (F57)",
|
||||
"shortLabel": "HMS Andromeda",
|
||||
"range": "",
|
||||
@@ -1131,7 +1137,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1139,8 +1146,8 @@
|
||||
"leander-gun-ariadne": {
|
||||
"name": "leander-gun-ariadne",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Ariadne (F72)",
|
||||
"shortLabel": "HMS Ariadne",
|
||||
"range": "",
|
||||
@@ -1148,7 +1155,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1156,8 +1164,8 @@
|
||||
"leander-gun-condell": {
|
||||
"name": "leander-gun-condell",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Almirante Condell PFG-06",
|
||||
"shortLabel": "Almirante Condell",
|
||||
"range": "",
|
||||
@@ -1165,7 +1173,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1173,8 +1182,8 @@
|
||||
"leander-gun-lynch": {
|
||||
"name": "leander-gun-lynch",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "CNS Almirante Lynch (PFG-07)",
|
||||
"shortLabel": "CNS Almirante Lynch",
|
||||
"range": "",
|
||||
@@ -1182,7 +1191,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1191,7 +1201,7 @@
|
||||
"name": "santafe",
|
||||
"coalition": "",
|
||||
"type": "Submarine",
|
||||
"era": "Early Cold War",
|
||||
"era": "WW2",
|
||||
"label": "ARA Santa Fe S-21",
|
||||
"shortLabel": "ARA Santa",
|
||||
"range": "",
|
||||
@@ -1199,6 +1209,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1210,36 +1221,21 @@
|
||||
"era": "",
|
||||
"label": "Boat Armed Hi-speed",
|
||||
"shortLabel": "Boat Armed Hi-speed",
|
||||
"type": "Speedboat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 5000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"VINSON": {
|
||||
"name": "VINSON",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "CVN-70 Carl Vinson",
|
||||
"shortLabel": "CVN-70 Carl Vinson",
|
||||
"type": "Aircraft Carrier",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 15000,
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PERRY": {
|
||||
"name": "perry",
|
||||
"name": "PERRY",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Oliver H. Perry",
|
||||
"shortLabel": "Oliver H. Perry",
|
||||
@@ -1289,13 +1285,18 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000
|
||||
"engagementRange": 100000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ALBATROS": {
|
||||
"name": "albatros",
|
||||
"name": "ALBATROS",
|
||||
"coalition": "red",
|
||||
"type": "Frigade",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "Albatros (Grisha-5)",
|
||||
"shortLabel": "Albatros",
|
||||
"range": "",
|
||||
@@ -1340,25 +1341,35 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KUZNECOW": {
|
||||
"name": "kuznecow",
|
||||
"name": "KUZNECOW",
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "Admiral Kuznetsov",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"shortLabel": "AK",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOLNIYA": {
|
||||
"name": "molniya",
|
||||
"name": "MOLNIYA",
|
||||
"coalition": "",
|
||||
"type": "Corvette",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Molniya (Tarantul-3)",
|
||||
"shortLabel": "Molniya",
|
||||
@@ -1380,12 +1391,17 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 21000,
|
||||
"engagementRange": 2000
|
||||
"engagementRange": 2000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOSCOW": {
|
||||
"name": "moscow",
|
||||
"name": "MOSCOW",
|
||||
"coalition": "red",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Moscow",
|
||||
"shortLabel": "Moscow",
|
||||
@@ -1411,54 +1427,70 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 75000
|
||||
"engagementRange": 75000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"NEUSTRASH": {
|
||||
"name": "neustrash",
|
||||
"name": "NEUSTRASH",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Neustrashimy",
|
||||
"shortLabel": "Neustrashimy",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 27000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PIOTR": {
|
||||
"name": "PIOTR",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"shortLabel": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"type": "Cruiser",
|
||||
"coalition": "red",
|
||||
"era": "Late Cold War",
|
||||
"label": "Pyotr Velikiy (Battlecruiser)",
|
||||
"shortLabel": "Pyotr Velikiy",
|
||||
"type": "Combatants",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 250000,
|
||||
"engagementRange": 190000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"REZKY": {
|
||||
"name": "Rezky (Krivak-2)",
|
||||
"name": "REZKY",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "WW2",
|
||||
"label": "Rezky (Krivak-2)",
|
||||
"shortLabel": "Rezky",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ELNYA": {
|
||||
"name": "elnya",
|
||||
"name": "ELNYA",
|
||||
"coalition": "red",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Elnya tanker",
|
||||
"shortLabel": "Elnya tanker",
|
||||
@@ -1480,7 +1512,12 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"Dry-cargo ship-2": {
|
||||
"name": "Dry-cargo ship-2",
|
||||
@@ -1488,11 +1525,12 @@
|
||||
"era": "",
|
||||
"label": "Cargo Ivanov",
|
||||
"shortLabel": "Cargo Ivanov",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1504,20 +1542,21 @@
|
||||
"era": "",
|
||||
"label": "Bulker Yakushev",
|
||||
"shortLabel": "Bulker Yakushev",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ZWEZDNY": {
|
||||
"name": "zwezdny",
|
||||
"name": "ZWEZDNY",
|
||||
"coalition": "",
|
||||
"type": "Civilian Boat",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Zwezdny",
|
||||
"shortLabel": "Zwezdny",
|
||||
@@ -1525,32 +1564,43 @@
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KILO": {
|
||||
"name": "kilo",
|
||||
"name": "KILO",
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Late Cold War",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Project 636 Varshavyanka Basic",
|
||||
"shortLabel": "Varshavyanka Basic",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"IMPROVED_KILO": {
|
||||
"name": "IMPROVED_KILO",
|
||||
"coalition": "",
|
||||
"coalition": "red",
|
||||
"era": "",
|
||||
"label": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "Kilo",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1561,12 +1611,13 @@
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "SSK 641B Tango",
|
||||
"shortLabel": "SSK 641B Tango",
|
||||
"shortLabel": "Tango",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1574,8 +1625,8 @@
|
||||
},
|
||||
"Forrestal": {
|
||||
"name": "Forrestal",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "CV-59 Forrestal",
|
||||
"shortLabel": "CV-59 Forrestal",
|
||||
"type": "Aircraft Carrier",
|
||||
@@ -1583,6 +1634,7 @@
|
||||
"liveries": {},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1590,15 +1642,16 @@
|
||||
},
|
||||
"LST_Mk2": {
|
||||
"name": "LST_Mk2",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LST Mk.II",
|
||||
"shortLabel": "LST Mk.II",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1606,15 +1659,16 @@
|
||||
},
|
||||
"USS_Samuel_Chase": {
|
||||
"name": "USS_Samuel_Chase",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LS Samuel Chase",
|
||||
"shortLabel": "LS Samuel Chase",
|
||||
"type": "Landing SHip",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 7000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1622,15 +1676,16 @@
|
||||
},
|
||||
"Higgins_boat": {
|
||||
"name": "Higgins_boat",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "Boat LCVP Higgins",
|
||||
"shortLabel": "Boat LCVP Higgins",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 3000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1638,15 +1693,16 @@
|
||||
},
|
||||
"Uboat_VIIC": {
|
||||
"name": "Uboat_VIIC",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "U-boat VIIC U-flak",
|
||||
"shortLabel": "U-boat VIIC U-flak",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "U-boat VIIC",
|
||||
"shortLabel": "U-boat",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 20000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1654,15 +1710,16 @@
|
||||
},
|
||||
"Schnellboot_type_S130": {
|
||||
"name": "Schnellboot_type_S130",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "Boat Schnellboot type S130",
|
||||
"shortLabel": "Boat Schnellboot type S130",
|
||||
"type": "Torpedo Boat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 10000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
{
|
||||
"BDK-775": {
|
||||
"name": "BDK-775",
|
||||
"coalition": "blue",
|
||||
"type": "Landing Ship",
|
||||
"era": "Mid Cold War",
|
||||
"label": "LS Ropucha",
|
||||
"shortLabel": "LS Ropucha",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 6000,
|
||||
"description": "Landing ship Ropucha",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"CVN_71": {
|
||||
"name": "CVN_71",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-71 Theodore Roosevelt",
|
||||
"shortLabel": "CVN-71",
|
||||
@@ -28,7 +11,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -36,7 +20,7 @@
|
||||
"CVN_72": {
|
||||
"name": "CVN_72",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-72 Abraham Lincoln",
|
||||
"shortLabel": "CVN-72",
|
||||
@@ -45,7 +29,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -53,7 +38,7 @@
|
||||
"CVN_73": {
|
||||
"name": "CVN_73",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-73 George Washington",
|
||||
"shortLabel": "CVN-73",
|
||||
@@ -62,7 +47,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -101,7 +87,8 @@
|
||||
},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -111,13 +98,14 @@
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Modern",
|
||||
"label": "CV Admiral Kuznetsov(2017)",
|
||||
"shortLabel": "Admiral Kuznetsov(2017)",
|
||||
"label": "Admiral Kuznetsov (2017)",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "Admiral Kuznetsov. Conventional STOBAR carrier",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -126,8 +114,8 @@
|
||||
"CastleClass_01": {
|
||||
"name": "CastleClass_01",
|
||||
"coalition": "blue",
|
||||
"type": "Patrol",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Leeds Castle (P-258)",
|
||||
"shortLabel": "HMS Leeds Castle (P-258)",
|
||||
"range": "",
|
||||
@@ -141,7 +129,8 @@
|
||||
},
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 3000,
|
||||
"description": "HMS Leeds Castle. Smaller. Patrol craft",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -149,7 +138,7 @@
|
||||
"HandyWind": {
|
||||
"name": "HandyWind",
|
||||
"coalition": "blue",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Bulker Handy Wind",
|
||||
"shortLabel": "Bulker Handy Wind",
|
||||
@@ -176,6 +165,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -184,7 +174,7 @@
|
||||
"HarborTug": {
|
||||
"name": "HarborTug",
|
||||
"coalition": "",
|
||||
"type": "Tug",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Harbor Tug",
|
||||
"shortLabel": "Harbor Tug",
|
||||
@@ -211,6 +201,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -228,7 +219,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 20000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -481,6 +473,7 @@
|
||||
},
|
||||
"acquisitionRange": 19000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -489,15 +482,16 @@
|
||||
"Seawise_Giant": {
|
||||
"name": "Seawise_Giant",
|
||||
"coalition": "blue",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Tanker Seawise Giant",
|
||||
"label": "Seawise Giant (Tanker)",
|
||||
"shortLabel": "Seawise Giant",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -506,7 +500,7 @@
|
||||
"Ship_Tilde_Supply": {
|
||||
"name": "Ship_Tilde_Supply",
|
||||
"coalition": "blue",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Supply Ship MV Tilde",
|
||||
"shortLabel": "Supply Ship Tilde",
|
||||
@@ -515,6 +509,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -532,6 +527,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -540,9 +536,9 @@
|
||||
"TICONDEROG": {
|
||||
"name": "TICONDEROG",
|
||||
"coalition": "blue",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Ticonderoga",
|
||||
"label": "Ticonderoga Class (Cruiser)",
|
||||
"shortLabel": "Ticonderoga",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -611,7 +607,8 @@
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -619,9 +616,9 @@
|
||||
"Type_052B": {
|
||||
"name": "Type_052B",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052B DDG-168 Guangzhou",
|
||||
"label": "Type 52B Guangzhou",
|
||||
"shortLabel": "Type 52B",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -646,7 +643,8 @@
|
||||
},
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 30000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -654,9 +652,9 @@
|
||||
"Type_052C": {
|
||||
"name": "Type_052C",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052C DDG-171 Haikou",
|
||||
"label": "Type 52C Haikou (Destroyer)",
|
||||
"shortLabel": "Type 52C",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -705,17 +703,18 @@
|
||||
},
|
||||
"acquisitionRange": 260000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"Type_054A": {
|
||||
"name": "",
|
||||
"name": "Type_054A",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "054A FFG-538 Yantai",
|
||||
"label": "Type 54A Yantai (Frigate)",
|
||||
"shortLabel": "Type 54A",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -872,15 +871,16 @@
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 45000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "35nm >50,000ft range"
|
||||
},
|
||||
"Type_071": {
|
||||
"name": "Type_071",
|
||||
"coalition": "red",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Type 071",
|
||||
"label": "Type 071 (Transport dock)",
|
||||
"shortLabel": "Type 071",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -925,7 +925,8 @@
|
||||
},
|
||||
"acquisitionRange": 300000,
|
||||
"engagementRange": 150000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -935,7 +936,7 @@
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Modern",
|
||||
"label": "Type 093",
|
||||
"label": "Type 093 (Shang)",
|
||||
"shortLabel": "Type 093",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -948,6 +949,7 @@
|
||||
},
|
||||
"acquisitionRange": 40000,
|
||||
"engagementRange": 40000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -956,10 +958,10 @@
|
||||
"USS_Arleigh_Burke_IIa": {
|
||||
"name": "USS_Arleigh_Burke_IIa",
|
||||
"coalition": "blue",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "DDG Arleigh Burke lla",
|
||||
"shortLabel": "DDG Arleigh Burke",
|
||||
"label": "Arleigh Burke Class (Destroyer)",
|
||||
"shortLabel": "Arleigh Burke Class (Destroyer)",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
@@ -1064,6 +1066,7 @@
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"abilities": "",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1080,7 +1083,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 18000,
|
||||
"engagementRange": 5000,
|
||||
"description": "ARA Vienticinco de Mayo. Conventional CATOBAR carrier",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1089,15 +1093,16 @@
|
||||
"name": "hms_invincible",
|
||||
"coalition": "blue",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Mid Cold War",
|
||||
"label": "HMS Invincible (R05)",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Invincible",
|
||||
"shortLabel": "HMS Invincible",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 74000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1105,8 +1110,8 @@
|
||||
"leander-gun-achilles": {
|
||||
"name": "leander-gun-achilles",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Achilles (F12)",
|
||||
"shortLabel": "HMS Achilles",
|
||||
"range": "",
|
||||
@@ -1114,7 +1119,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 8000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1122,8 +1128,8 @@
|
||||
"leander-gun-andromeda": {
|
||||
"name": "leander-gun-andromeda",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Andromeda (F57)",
|
||||
"shortLabel": "HMS Andromeda",
|
||||
"range": "",
|
||||
@@ -1131,7 +1137,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1139,8 +1146,8 @@
|
||||
"leander-gun-ariadne": {
|
||||
"name": "leander-gun-ariadne",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Ariadne (F72)",
|
||||
"shortLabel": "HMS Ariadne",
|
||||
"range": "",
|
||||
@@ -1148,7 +1155,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1156,8 +1164,8 @@
|
||||
"leander-gun-condell": {
|
||||
"name": "leander-gun-condell",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Almirante Condell PFG-06",
|
||||
"shortLabel": "Almirante Condell",
|
||||
"range": "",
|
||||
@@ -1165,7 +1173,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1173,8 +1182,8 @@
|
||||
"leander-gun-lynch": {
|
||||
"name": "leander-gun-lynch",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "CNS Almirante Lynch (PFG-07)",
|
||||
"shortLabel": "CNS Almirante Lynch",
|
||||
"range": "",
|
||||
@@ -1182,7 +1191,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1191,7 +1201,7 @@
|
||||
"name": "santafe",
|
||||
"coalition": "",
|
||||
"type": "Submarine",
|
||||
"era": "Early Cold War",
|
||||
"era": "WW2",
|
||||
"label": "ARA Santa Fe S-21",
|
||||
"shortLabel": "ARA Santa",
|
||||
"range": "",
|
||||
@@ -1199,6 +1209,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1210,36 +1221,21 @@
|
||||
"era": "",
|
||||
"label": "Boat Armed Hi-speed",
|
||||
"shortLabel": "Boat Armed Hi-speed",
|
||||
"type": "Speedboat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 5000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"VINSON": {
|
||||
"name": "VINSON",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "CVN-70 Carl Vinson",
|
||||
"shortLabel": "CVN-70 Carl Vinson",
|
||||
"type": "Aircraft Carrier",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 15000,
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PERRY": {
|
||||
"name": "perry",
|
||||
"name": "PERRY",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Oliver H. Perry",
|
||||
"shortLabel": "Oliver H. Perry",
|
||||
@@ -1289,13 +1285,18 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000
|
||||
"engagementRange": 100000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ALBATROS": {
|
||||
"name": "albatros",
|
||||
"name": "ALBATROS",
|
||||
"coalition": "red",
|
||||
"type": "Frigade",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "Albatros (Grisha-5)",
|
||||
"shortLabel": "Albatros",
|
||||
"range": "",
|
||||
@@ -1340,25 +1341,35 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KUZNECOW": {
|
||||
"name": "kuznecow",
|
||||
"name": "KUZNECOW",
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "Admiral Kuznetsov",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"shortLabel": "AK",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOLNIYA": {
|
||||
"name": "molniya",
|
||||
"name": "MOLNIYA",
|
||||
"coalition": "",
|
||||
"type": "Corvette",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Molniya (Tarantul-3)",
|
||||
"shortLabel": "Molniya",
|
||||
@@ -1380,12 +1391,17 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 21000,
|
||||
"engagementRange": 2000
|
||||
"engagementRange": 2000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOSCOW": {
|
||||
"name": "moscow",
|
||||
"name": "MOSCOW",
|
||||
"coalition": "red",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Moscow",
|
||||
"shortLabel": "Moscow",
|
||||
@@ -1411,54 +1427,70 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 75000
|
||||
"engagementRange": 75000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"NEUSTRASH": {
|
||||
"name": "neustrash",
|
||||
"name": "NEUSTRASH",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Neustrashimy",
|
||||
"shortLabel": "Neustrashimy",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 27000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PIOTR": {
|
||||
"name": "PIOTR",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"shortLabel": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"type": "Cruiser",
|
||||
"coalition": "red",
|
||||
"era": "Late Cold War",
|
||||
"label": "Pyotr Velikiy (Battlecruiser)",
|
||||
"shortLabel": "Pyotr Velikiy",
|
||||
"type": "Combatants",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 250000,
|
||||
"engagementRange": 190000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"REZKY": {
|
||||
"name": "Rezky (Krivak-2)",
|
||||
"name": "REZKY",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "WW2",
|
||||
"label": "Rezky (Krivak-2)",
|
||||
"shortLabel": "Rezky",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ELNYA": {
|
||||
"name": "elnya",
|
||||
"name": "ELNYA",
|
||||
"coalition": "red",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Elnya tanker",
|
||||
"shortLabel": "Elnya tanker",
|
||||
@@ -1480,7 +1512,12 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"Dry-cargo ship-2": {
|
||||
"name": "Dry-cargo ship-2",
|
||||
@@ -1488,11 +1525,12 @@
|
||||
"era": "",
|
||||
"label": "Cargo Ivanov",
|
||||
"shortLabel": "Cargo Ivanov",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1504,20 +1542,21 @@
|
||||
"era": "",
|
||||
"label": "Bulker Yakushev",
|
||||
"shortLabel": "Bulker Yakushev",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ZWEZDNY": {
|
||||
"name": "zwezdny",
|
||||
"name": "ZWEZDNY",
|
||||
"coalition": "",
|
||||
"type": "Civilian Boat",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Zwezdny",
|
||||
"shortLabel": "Zwezdny",
|
||||
@@ -1525,32 +1564,43 @@
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KILO": {
|
||||
"name": "kilo",
|
||||
"name": "KILO",
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Late Cold War",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Project 636 Varshavyanka Basic",
|
||||
"shortLabel": "Varshavyanka Basic",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"IMPROVED_KILO": {
|
||||
"name": "IMPROVED_KILO",
|
||||
"coalition": "",
|
||||
"coalition": "red",
|
||||
"era": "",
|
||||
"label": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "Kilo",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1561,12 +1611,13 @@
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "SSK 641B Tango",
|
||||
"shortLabel": "SSK 641B Tango",
|
||||
"shortLabel": "Tango",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1574,8 +1625,8 @@
|
||||
},
|
||||
"Forrestal": {
|
||||
"name": "Forrestal",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "CV-59 Forrestal",
|
||||
"shortLabel": "CV-59 Forrestal",
|
||||
"type": "Aircraft Carrier",
|
||||
@@ -1583,6 +1634,7 @@
|
||||
"liveries": {},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1590,15 +1642,16 @@
|
||||
},
|
||||
"LST_Mk2": {
|
||||
"name": "LST_Mk2",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LST Mk.II",
|
||||
"shortLabel": "LST Mk.II",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1606,15 +1659,16 @@
|
||||
},
|
||||
"USS_Samuel_Chase": {
|
||||
"name": "USS_Samuel_Chase",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LS Samuel Chase",
|
||||
"shortLabel": "LS Samuel Chase",
|
||||
"type": "Landing SHip",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 7000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1622,15 +1676,16 @@
|
||||
},
|
||||
"Higgins_boat": {
|
||||
"name": "Higgins_boat",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "Boat LCVP Higgins",
|
||||
"shortLabel": "Boat LCVP Higgins",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 3000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1638,15 +1693,16 @@
|
||||
},
|
||||
"Uboat_VIIC": {
|
||||
"name": "Uboat_VIIC",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "U-boat VIIC U-flak",
|
||||
"shortLabel": "U-boat VIIC U-flak",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "U-boat VIIC",
|
||||
"shortLabel": "U-boat",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 20000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1654,15 +1710,16 @@
|
||||
},
|
||||
"Schnellboot_type_S130": {
|
||||
"name": "Schnellboot_type_S130",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "Boat Schnellboot type S130",
|
||||
"shortLabel": "Boat Schnellboot type S130",
|
||||
"type": "Torpedo Boat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 10000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
|
||||
BIN
client/public/images/units/f-1.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
client/public/images/units/mb-339.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -62,6 +62,16 @@
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
#hotgroup-panel {
|
||||
bottom: 40px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
translate: -50%;
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
#info-popup {
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.airbase-icon[data-coalition="red"] svg * {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bullseye-icon[data-coalition="red"] svg * {
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
width: var(--unit-width);
|
||||
}
|
||||
|
||||
.unit-icon svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-is-selected] .unit-icon::before {
|
||||
background-color: var(--unit-spotlight-fill);
|
||||
border-radius: 50%;
|
||||
@@ -200,12 +205,6 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-has-low-fuel] .unit-fuel, [data-object|="unit"][data-has-low-health] .unit-health {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
@@ -329,6 +328,19 @@
|
||||
background-image: url("/resources/theme/images/states/awacs.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="miss-on-purpose"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/miss-on-purpose.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="scenic-aaa"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/scenic-aaa.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="simulate-fire-fight"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/simulate-fire-fight.svg");
|
||||
}
|
||||
|
||||
|
||||
[data-object|="unit"] .unit-health::before {
|
||||
background-image: url("/resources/theme/images/icons/health.svg");
|
||||
background-repeat: no-repeat;
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle,
|
||||
@@ -60,8 +61,18 @@
|
||||
|
||||
.contextmenu-advanced-options-toggle:after,
|
||||
.contextmenu-metadata-toggle:after {
|
||||
content: url(/resources/theme/images/icons/chevron-down.svg);
|
||||
margin: auto;
|
||||
content: "";
|
||||
margin-left: auto;
|
||||
margin-top: auto;
|
||||
background-image: url(/resources/theme/images/icons/chevron-down.svg);
|
||||
background-size: 100% 100%;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle.is-open:after,
|
||||
.contextmenu-metadata-toggle.is-open:after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle div:first-child,
|
||||
@@ -241,12 +252,16 @@
|
||||
}
|
||||
|
||||
.unit-label-count-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
display: grid;
|
||||
grid-template-columns: 187px 1fr 1fr;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
}
|
||||
|
||||
.unit-label-count-container>*:first-child {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.unit-label-count-container button {
|
||||
display: flex !important;
|
||||
flex-direction: row;
|
||||
@@ -396,84 +411,124 @@
|
||||
|
||||
/* Buttons */
|
||||
#center-map::before {
|
||||
content: url("/resources/theme/images/icons/arrows-to-eye-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/arrows-to-eye-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#refuel::before {
|
||||
content: url("/resources/theme/images/icons/fuel.svg");
|
||||
background-image: url("/resources/theme/images/icons/fuel.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#attack::before {
|
||||
content: url("/resources/theme/images/icons/sword.svg");
|
||||
background-image: url("/resources/theme/images/icons/sword.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#bomb::before {
|
||||
content: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#carpet-bomb::before {
|
||||
content: url("/resources/theme/images/icons/explosion-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/explosion-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#fire-at-area::before {
|
||||
content: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#simulate-fire-fight::before {
|
||||
content: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#follow::before {
|
||||
content: url("/resources/theme/images/icons/follow.svg");
|
||||
background-image: url("/resources/theme/images/icons/follow.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#scenic-aaa::before {
|
||||
content: url("/resources/theme/images/icons/scenic.svg");
|
||||
background-image: url("/resources/theme/images/icons/scenic.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#miss-aaa::before {
|
||||
content: url("/resources/theme/images/icons/miss.svg");
|
||||
background-image: url("/resources/theme/images/icons/miss.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#group-ground::before {
|
||||
content: url("/resources/theme/images/icons/group-ground.svg");
|
||||
background-image: url("/resources/theme/images/icons/group-ground.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#group-navy::before {
|
||||
content: url("/resources/theme/images/icons/group-navy.svg");
|
||||
background-image: url("/resources/theme/images/icons/group-navy.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#land-at-point::before {
|
||||
content: url("/resources/theme/images/icons/land-at-point.svg");
|
||||
background-image: url("/resources/theme/images/icons/land-at-point.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#trail::before {
|
||||
content: url("/resources/theme/images/icons/trail.svg");
|
||||
background-image: url("/resources/theme/images/icons/trail.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#echelon-lh::before {
|
||||
content: url("/resources/theme/images/icons/echelon-lh.svg");
|
||||
background-image: url("/resources/theme/images/icons/echelon-lh.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#echelon-rh::before {
|
||||
content: url("/resources/theme/images/icons/echelon-rh.svg");
|
||||
background-image: url("/resources/theme/images/icons/echelon-rh.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#line-abreast-rh::before,
|
||||
#line-abreast-lh::before {
|
||||
content: url("/resources/theme/images/icons/line-abreast.svg");
|
||||
background-image: url("/resources/theme/images/icons/line-abreast.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#front::before {
|
||||
content: url("/resources/theme/images/icons/front.svg");
|
||||
background-image: url("/resources/theme/images/icons/front.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#diamond::before {
|
||||
content: url("/resources/theme/images/icons/diamond.svg");
|
||||
background-image: url("/resources/theme/images/icons/diamond.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#custom::before {
|
||||
content: url("/resources/theme/images/icons/custom.svg");
|
||||
background-image: url("/resources/theme/images/icons/custom.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#custom-formation-dialog {
|
||||
@@ -576,6 +631,7 @@
|
||||
|
||||
#iads-menu {
|
||||
row-gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#coalition-area-contextmenu>div:nth-child(2) {
|
||||
@@ -592,6 +648,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.create-iads-button {
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#app-icon>.ol-select-value {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
background-image: url("/images/icon-round.png");
|
||||
background-position: 20px 22px;
|
||||
@@ -29,22 +33,69 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(2)>svg {
|
||||
display: none;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
.ol-panel-tab {
|
||||
align-items: center;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
margin-right:6px;
|
||||
}
|
||||
|
||||
.ol-panel-tab svg {
|
||||
height:24;
|
||||
width:24px;
|
||||
}
|
||||
|
||||
.ol-panel-tab svg * {
|
||||
fill:white;
|
||||
}
|
||||
|
||||
.ol-panel-tab span {
|
||||
font-size:13px;
|
||||
font-weight:400;
|
||||
padding:0 6px;
|
||||
}
|
||||
|
||||
#view-label {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#view-label svg {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(3)>svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-visibility-control > div:nth-child(4) {
|
||||
border-left: 2px solid white;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
#unit-visibility-control > div:last-child {
|
||||
border-right: 2px solid white;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 1145px) {
|
||||
#toolbar-container {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
#toolbar-container .ol-panel .ol-panel-tab {
|
||||
margin-right:0;
|
||||
}
|
||||
|
||||
#toolbar-container .ol-panel:hover .ol-panel-tab {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#toolbar-container .ol-panel-tab span {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(1):not(:hover) {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
@@ -62,10 +113,10 @@
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>svg {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: block;
|
||||
filter: invert();
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>*:not(:first-child) {
|
||||
|
||||
@@ -50,15 +50,6 @@
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] #connection-status-light {
|
||||
background: var(--accent-amber);
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
#log-panel-header-right {
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
@@ -35,6 +34,11 @@
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
#log-panel-header-right svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
#server-status-panel abbr {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,75 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
|
||||
#roe-buttons-container button,
|
||||
#reaction-to-threat-buttons-container button,
|
||||
#emissions-countermeasures-buttons-container button,
|
||||
#shots-scatter-buttons-container button #shots-intensity-buttons-container button {
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--accent-light-blue);
|
||||
display: flex;
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button:not(:first-child) svg {
|
||||
width: 150%;
|
||||
margin: -5px;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button button.selected {
|
||||
background-color: white;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button button.selected svg * {
|
||||
fill: var(--background-steel);
|
||||
stroke: var(--background-steel);
|
||||
}
|
||||
|
||||
#rapid-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#rapid-controls button {
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#rapid-controls button.pulse {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
#rapid-controls svg {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
fill: white;
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
#rapid-controls button:before {
|
||||
display: inline-block;
|
||||
filter: invert(100%);
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#unit-control-panel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -49,16 +118,12 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#unit-control-panel:not(:hover)>*:nth-child(2),
|
||||
#unit-control-panel:not(:hover)>*:nth-child(2),
|
||||
#unit-control-panel:not(:hover)>*:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#unit-control-panel h3 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#unit-control-panel #selected-units-container {
|
||||
align-items: left;
|
||||
border-radius: var(--border-radius-md);
|
||||
@@ -72,9 +137,9 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
|
||||
#unit-control-panel #selected-units-container button {
|
||||
align-items: center;
|
||||
border-radius: var(--border-radius-md);
|
||||
border-radius: 20px;
|
||||
display: flex;
|
||||
font-size: 11px;
|
||||
font-size: 13px;
|
||||
height: 32px;
|
||||
justify-content: space-between;
|
||||
margin-right: 5px;
|
||||
@@ -88,6 +153,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
content: attr(data-label);
|
||||
font-size: 10px;
|
||||
padding: 4px 6px;
|
||||
padding-right: 7px;
|
||||
white-space: nowrap;
|
||||
width: fit-content;
|
||||
}
|
||||
@@ -103,7 +169,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
padding: 4px;
|
||||
padding-left: 0;
|
||||
padding-left: 7px;
|
||||
text-align: left;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
@@ -139,7 +205,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
}
|
||||
|
||||
#advanced-settings-dialog>.ol-dialog-content>div input[type="number"] {
|
||||
width: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
#advanced-settings-dialog hr {
|
||||
@@ -204,27 +270,34 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
content: "GS";
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-slider-value {
|
||||
.switch-control .ol-switch {
|
||||
height: 23px;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.ol-slider-value {
|
||||
color: var(--accent-light-blue);
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control {
|
||||
.switch-control {
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control h4 {
|
||||
margin: 0px;
|
||||
.switch-control h4 {
|
||||
margin: 0px !important;
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control h4 img {
|
||||
.switch-control h4 img {
|
||||
height: 15px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
@@ -232,56 +305,60 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
opacity: 80%;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control .ol-switch {
|
||||
height: 25px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control .ol-switch-fill {
|
||||
background-color: var(--accent-light-blue);
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control .ol-switch-fill::after {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control .ol-switch[data-value="true"]>.ol-switch-fill::before {
|
||||
content: "YES";
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control .ol-switch[data-value="false"]>.ol-switch-fill::before {
|
||||
content: "NO";
|
||||
}
|
||||
|
||||
#operate-as-switch[data-value="true"] .ol-switch-fill {
|
||||
background-color: var(--accent-light-blue);
|
||||
}
|
||||
|
||||
#operate-as-switch[data-value="false"] .ol-switch-fill {
|
||||
background-color: var(--primary-red);
|
||||
}
|
||||
|
||||
#operate-as-switch[data-value="true"]>.ol-switch-fill::before {
|
||||
content: "BLUE" !important;
|
||||
}
|
||||
|
||||
#operate-as-switch[data-value="false"]>.ol-switch-fill::before {
|
||||
content: "RED" !important;
|
||||
}
|
||||
|
||||
#advanced-settings-div {
|
||||
position: relative;
|
||||
column-gap: 5px;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
#advanced-settings-div>*:nth-child(2) {
|
||||
margin-left: auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#advanced-settings-div>button {
|
||||
background-color: var(--background-grey);
|
||||
box-shadow: 0px 2px 5px #000A;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
#delete-options {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#delete-options.ol-select>.ol-select-value:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
#delete-options.ol-select>.ol-select-value svg {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
right: 2px;
|
||||
translate: 0 1px;
|
||||
}
|
||||
|
||||
#delete-options.ol-select>.ol-select-value svg * {
|
||||
fill: var(--primary-red);
|
||||
}
|
||||
|
||||
#delete-options * {
|
||||
background-color: var(--background-steel);
|
||||
}
|
||||
|
||||
#delete-options.ol-select>.ol-select-value:hover,
|
||||
#delete-options .ol-select-options>div:not(.hr):hover,
|
||||
#delete-options .ol-select-options>div:not(.hr):hover button,
|
||||
#delete-options .ol-select-options>div hr {
|
||||
background-color: var(--background-grey);
|
||||
}
|
||||
|
||||
#delete-options .ol-select-options>div:first-of-type {
|
||||
margin-top: 12px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#delete-options .ol-select-options>div:last-of-type {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#delete-options button {
|
||||
@@ -291,11 +368,16 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
}
|
||||
|
||||
#delete-options button svg {
|
||||
background-color: transparent;
|
||||
margin-right: 10px;
|
||||
width: 18px;
|
||||
max-height: 18px;
|
||||
}
|
||||
|
||||
#delete-options button svg * {
|
||||
stroke: red;
|
||||
}
|
||||
|
||||
/* Element visibility control */
|
||||
#unit-control-panel:not([data-show-categories-tooltip]) #categories-tooltip,
|
||||
#unit-control-panel:not([data-show-speed-slider]) #speed-slider,
|
||||
@@ -317,4 +399,4 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
#advanced-settings-dialog:not([data-show-radio]) #radio-options,
|
||||
#advanced-settings-dialog:not([data-show-air-unit-checkboxes]) .air-unit-checkbox {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -146,12 +146,12 @@
|
||||
}
|
||||
|
||||
#fuel-percentage::before {
|
||||
content: url("/resources/theme/images/icons/fuel.svg");
|
||||
content: "";
|
||||
background-image: url("/resources/theme/images/icons/fuel.svg");
|
||||
background-size: 16px 16px;
|
||||
display: inline-block;
|
||||
filter: invert(100%);
|
||||
height: 16px;
|
||||
margin-right: 6px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#fuel-percentage::after {
|
||||
|
||||
@@ -23,7 +23,7 @@ body {
|
||||
}
|
||||
|
||||
.hidden-cursor {
|
||||
cursor: none !important;
|
||||
/*cursor: none !important;*/
|
||||
}
|
||||
|
||||
.hidden-cursor * {
|
||||
@@ -72,6 +72,18 @@ form {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button svg.fill-coalition * {
|
||||
fill: var(--primary-neutral) !important;
|
||||
}
|
||||
|
||||
button svg.fill-coalition[data-coalition="blue"] * {
|
||||
fill: var(--primary-blue) !important;
|
||||
}
|
||||
|
||||
button svg.fill-coalition[data-coalition="red"] * {
|
||||
fill: var(--primary-red) !important;
|
||||
}
|
||||
|
||||
.pill {
|
||||
background-color: var(--background-steel);
|
||||
border-radius: 999px;
|
||||
@@ -194,9 +206,21 @@ form {
|
||||
}
|
||||
|
||||
.ol-select:not(.ol-select-image)>.ol-select-value:after {
|
||||
content: url("/resources/theme/images/icons/chevron-down.svg");
|
||||
background-image: url("/resources/theme/images/icons/chevron-down.svg");
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.ol-select:not(.ol-select-image)>.ol-select-value.ol-select-warning:after {
|
||||
background-image: url("/resources/theme/images/icons/chevron-down-warning.svg") !important;
|
||||
}
|
||||
|
||||
.ol-select.is-open:not(.ol-select-image)>.ol-select-value:after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.ol-select>.ol-select-options {
|
||||
@@ -232,7 +256,6 @@ form {
|
||||
|
||||
.ol-select>.ol-select-options>div {
|
||||
background-color: var(--background-grey);
|
||||
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
|
||||
display: flex;
|
||||
justify-content: left;
|
||||
padding: 2px 15px;
|
||||
@@ -373,7 +396,7 @@ button.ol-button-warning>svg:first-child {
|
||||
}
|
||||
|
||||
nav.ol-panel {
|
||||
column-gap: 20px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 58px;
|
||||
@@ -385,8 +408,7 @@ nav.ol-panel> :last-child {
|
||||
|
||||
.ol-panel .ol-group {
|
||||
align-items: center;
|
||||
border-radius: var(--border-radius-sm);
|
||||
column-gap: 10px;
|
||||
column-gap: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
@@ -427,6 +449,7 @@ nav.ol-panel> :last-child {
|
||||
-webkit-filter: invert(100%);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.ol-panel .ol-group-button-toggle button.off::before {
|
||||
@@ -653,13 +676,13 @@ nav.ol-panel> :last-child {
|
||||
}
|
||||
|
||||
.ol-navbar-buttons-group button.off svg * {
|
||||
fill: white !important;
|
||||
stroke: white !important;
|
||||
fill: white;
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
.ol-navbar-buttons-group button svg * {
|
||||
fill: var(--background-steel) !important;
|
||||
stroke: var(--background-steel) !important;
|
||||
fill: var(--background-steel);
|
||||
stroke: var(--background-steel);
|
||||
}
|
||||
|
||||
.ol-navbar-buttons-group .protectable button:first-of-type {
|
||||
@@ -778,6 +801,7 @@ nav.ol-panel> :last-child {
|
||||
width: 70%;
|
||||
max-width: 1200px;
|
||||
z-index: 999999;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
@media (min-width: 1700px) {
|
||||
@@ -828,11 +852,11 @@ nav.ol-panel> :last-child {
|
||||
justify-content: space-between;
|
||||
min-height: 75px;
|
||||
text-indent: 85px;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#splash-content #app-summary>* {
|
||||
height: fit-content;
|
||||
line-height: 25px;
|
||||
padding: 2px;
|
||||
white-space: nowrap;
|
||||
width: fit-content;
|
||||
@@ -840,10 +864,23 @@ nav.ol-panel> :last-child {
|
||||
|
||||
#splash-content .app-version {
|
||||
font-size: 11px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#splash-content .new-version {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
#splash-content .app-version:first-of-type {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
#splash-content #legal-stuff {
|
||||
width: 100%;
|
||||
width: 120%;
|
||||
text-wrap: wrap;
|
||||
max-height: 250px;
|
||||
overflow-x: hidden;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#splash-content #legal-stuff h5 {
|
||||
@@ -853,11 +890,11 @@ nav.ol-panel> :last-child {
|
||||
#splash-content #legal-stuff p {
|
||||
color: #FFF7;
|
||||
font-size: 10px;
|
||||
width: 120%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1700px) {
|
||||
#splash-content #legal-stuff p {
|
||||
#splash-content #legal-stuff {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -876,6 +913,37 @@ nav.ol-panel> :last-child {
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
#loading-screen {
|
||||
display: flex;
|
||||
background-image: linear-gradient(var(--background-steel), var(--background-grey));
|
||||
height: 100%;
|
||||
left: 0px;
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
z-index: 999999;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
row-gap: 20px;
|
||||
}
|
||||
|
||||
#loading-screen img {
|
||||
height: 300px;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#loading-screen div {
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
animation: pulse 3s linear infinite;
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
opacity: 0%;
|
||||
transition: opacity 1s;
|
||||
}
|
||||
|
||||
#authentication-form {
|
||||
align-items: end;
|
||||
column-gap: 10px;
|
||||
@@ -926,16 +994,6 @@ nav.ol-panel> :last-child {
|
||||
}
|
||||
}
|
||||
|
||||
#hotgroup-panel {
|
||||
bottom: 40px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
translate: -50%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#hotgroup-panel>div {
|
||||
align-items: center;
|
||||
background-color: var(--background-steel);
|
||||
@@ -1254,11 +1312,22 @@ dl.ol-data-grid dd {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.ol-dialog-content table th {
|
||||
background-color: var(--background-grey);
|
||||
color:white;
|
||||
font-size:14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.ol-dialog-content table tbody th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ol-dialog-footer {
|
||||
align-content: center;
|
||||
border-top: 1px solid var(--background-grey);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
padding-top: 15px;
|
||||
row-gap: 10px;
|
||||
}
|
||||
@@ -1292,6 +1361,11 @@ dl.ol-data-grid dd {
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
width: 16px;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.ol-checkbox input[type="checkbox"]:disabled:before {
|
||||
opacity: 10%;
|
||||
}
|
||||
|
||||
.ol-checkbox input[type="checkbox"]:checked::before {
|
||||
@@ -1424,20 +1498,54 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
transform: translateX(calc((var(--width) - var(--height)) * 0.5));
|
||||
}
|
||||
|
||||
.ol-contexmenu-panel {
|
||||
padding: 20px;
|
||||
.switch-control.yes-no .ol-switch[data-value="true"] .ol-switch-fill {
|
||||
background-color: var(--accent-light-blue);
|
||||
}
|
||||
|
||||
.ol-coalition-switch[data-value="false"]>.ol-switch-fill {
|
||||
background-color: var(--primary-blue);
|
||||
.switch-control.yes-no .ol-switch[data-value="false"] .ol-switch-fill {
|
||||
background-color: var(--ol-switch-off);
|
||||
}
|
||||
|
||||
.ol-coalition-switch[data-value="true"]>.ol-switch-fill {
|
||||
background-color: var(--primary-red);
|
||||
.switch-control.yes-no .ol-switch[data-value="undefined"] .ol-switch-fill {
|
||||
background-color: var(--ol-switch-undefined);
|
||||
}
|
||||
|
||||
.ol-coalition-switch[data-value="undefined"]>.ol-switch-fill {
|
||||
background-color: var(--primary-neutral);
|
||||
.switch-control.coalition .ol-switch>.ol-switch-fill::before,
|
||||
.switch-control.yes-no .ol-switch>.ol-switch-fill::before {
|
||||
translate:-100% 0;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.switch-control.yes-no .ol-switch[data-value="true"]>.ol-switch-fill::before {
|
||||
content: "YES";
|
||||
}
|
||||
|
||||
.switch-control.yes-no .ol-switch[data-value="false"]>.ol-switch-fill::before {
|
||||
content: "NO";
|
||||
}
|
||||
|
||||
.switch-control.coalition [data-value="true"] .ol-switch-fill {
|
||||
background-color: var(--primary-blue);
|
||||
}
|
||||
|
||||
.switch-control.coalition [data-value="false"] .ol-switch-fill {
|
||||
background-color: var(--primary-red);
|
||||
}
|
||||
|
||||
.switch-control.coalition [data-value="undefined"] .ol-switch-fill {
|
||||
background-color: var(--primary-neutral);
|
||||
}
|
||||
|
||||
.switch-control.coalition [data-value="true"] .ol-switch-fill::before {
|
||||
content: "BLUE";
|
||||
}
|
||||
|
||||
.switch-control.coalition [data-value="false"] .ol-switch-fill::before {
|
||||
content: "RED";
|
||||
}
|
||||
|
||||
.switch-control.no-label [data-value] .ol-switch-fill::before {
|
||||
content:"";
|
||||
}
|
||||
|
||||
.ol-context-menu>ul {
|
||||
@@ -1461,7 +1569,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ol-contexmenu-button {
|
||||
.ol-context-menu-button {
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
height: 48px;
|
||||
@@ -1470,23 +1578,23 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.ol-contexmenu-button:last-of-type {
|
||||
.ol-context-menu-button:last-of-type {
|
||||
border-bottom-right-radius: var(--border-radius-sm);
|
||||
border-top-right-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
[data-coalition="blue"].ol-contexmenu-button:hover,
|
||||
[data-coalition="blue"].ol-contexmenu-button.is-open {
|
||||
[data-coalition="blue"].ol-context-menu-button:hover,
|
||||
[data-coalition="blue"].ol-context-menu-button.is-open {
|
||||
background-color: var(--primary-blue)
|
||||
}
|
||||
|
||||
[data-coalition="red"].ol-contexmenu-button:hover,
|
||||
[data-coalition="red"].ol-contexmenu-button.is-open {
|
||||
[data-coalition="red"].ol-context-menu-button:hover,
|
||||
[data-coalition="red"].ol-context-menu-button.is-open {
|
||||
background-color: var(--primary-red)
|
||||
}
|
||||
|
||||
[data-coalition="neutral"].ol-contexmenu-button:hover,
|
||||
[data-coalition="neutral"].ol-contexmenu-button.is-open {
|
||||
[data-coalition="neutral"].ol-context-menu-button:hover,
|
||||
[data-coalition="neutral"].ol-context-menu-button.is-open {
|
||||
background-color: var(--primary-neutral)
|
||||
}
|
||||
|
||||
@@ -1497,6 +1605,24 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
fill: lightgray;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox {
|
||||
font-size:13px;
|
||||
font-weight:400;
|
||||
padding:6px 15px;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox:first-of-type {
|
||||
padding-top:12px;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox:last-of-type {
|
||||
padding-bottom:12px;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox label:hover span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.ol-log-entry:first-of-type {
|
||||
border-top: 1px solid #FFFFFF44;
|
||||
}
|
||||
@@ -1504,3 +1630,93 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
.ol-log-entry {
|
||||
border-bottom: 1px solid #FFFFFF44;
|
||||
}
|
||||
|
||||
.file-import-export {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.file-import-export .ol-dialog-content {
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-import-export p {
|
||||
background-color: var(--background-grey);
|
||||
border-left:6px solid var(--secondary-blue-text);
|
||||
padding:12px;
|
||||
}
|
||||
|
||||
.file-import-export th {
|
||||
padding:4px 6px;
|
||||
}
|
||||
|
||||
.file-import-export tr td:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.file-import-export td {
|
||||
color:white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file-import-export .ol-checkbox {
|
||||
display:flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-import-export .ol-checkbox input::before {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.file-import-export .ol-checkbox span {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.file-import-export button.start-transfer {
|
||||
background-color: var(--secondary-blue-text);
|
||||
border-color: var(--secondary-blue-text);
|
||||
}
|
||||
|
||||
.file-import-export .export-filename-container {
|
||||
display: flex;
|
||||
column-gap: 15px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 10px 0px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.file-import-export .export-filename-container input {
|
||||
width: 100%;
|
||||
background-color: var(--background-grey);
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border-style: solid;
|
||||
border: 1px solid var(--background-steel);
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.file-import-export .export-filename-container img {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
filter: invert(100%);
|
||||
margin-left: -31px;
|
||||
transform: translateX(-15px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.file-import-export .ol-dialog-footer button:first-of-type{
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-352a96 96 0 1 1 0 192 96 96 0 1 1 0-192z"/></svg>
|
||||
|
After Width: | Height: | Size: 346 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 32C64 14.3 49.7 0 32 0S0 14.3 0 32V64 368 480c0 17.7 14.3 32 32 32s32-14.3 32-32V352l64.3-16.1c41.1-10.3 84.6-5.5 122.5 13.4c44.2 22.1 95.5 24.8 141.7 7.4l34.7-13c12.5-4.7 20.8-16.6 20.8-30V66.1c0-23-24.2-38-44.8-27.7l-9.6 4.8c-46.3 23.2-100.8 23.2-147.1 0c-35.1-17.6-75.4-22-113.5-12.5L64 48V32z"/></svg>
|
||||
|
After Width: | Height: | Size: 554 B |
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.8 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M0 224.2C0 100.6 100.2 0 224 0h24c95.2 0 181.2 69.3 197.3 160.2c2.3 13 6.8 25.7 15.1 36l42 52.6c6.2 7.8 9.6 17.4 9.6 27.4c0 24.2-19.6 43.8-43.8 43.8H448v64c0 35.3-28.7 64-64 64H320v32c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V407.3c0-16.7-6.9-32.5-17.1-45.8C16.6 322.4 0 274.1 0 224.2zM224 64c-8.8 0-16 7.2-16 16c0 33-39.9 49.5-63.2 26.2c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6C145.5 152.1 129 192 96 192c-8.8 0-16 7.2-16 16s7.2 16 16 16c33 0 49.5 39.9 26.2 63.2c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0C168.1 286.5 208 303 208 336c0 8.8 7.2 16 16 16s16-7.2 16-16c0-33 39.9-49.5 63.2-26.2c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6C302.5 263.9 319 224 352 224c8.8 0 16-7.2 16-16s-7.2-16-16-16c-33 0-49.5-39.9-26.2-63.2c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0C279.9 129.5 240 113 240 80c0-8.8-7.2-16-16-16zm-24 96a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm40 80a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M256 0c4.6 0 9.2 1 13.4 2.9L457.7 82.8c22 9.3 38.4 31 38.3 57.2c-.5 99.2-41.3 280.7-213.6 363.2c-16.7 8-36.1 8-52.8 0C57.3 420.7 16.5 239.2 16 140c-.1-26.2 16.3-47.9 38.3-57.2L242.7 2.9C246.8 1 251.4 0 256 0zm0 66.8V444.8C394 378 431.1 230.1 432 141.4L256 66.8l0 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 519 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.37109 7.37891C6.69922 7.73438 7.27344 7.73438 7.60156 7.37891L12.8516 2.12891C13.207 1.80078 13.207 1.22656 12.8516 0.898438C12.5234 0.542969 11.9492 0.542969 11.6211 0.898438L7 5.51953L2.35156 0.898438C2.02344 0.542969 1.44922 0.542969 1.12109 0.898438C0.765625 1.22656 0.765625 1.80078 1.12109 2.12891L6.37109 7.37891Z" fill="#ff5858"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 452 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="18" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/></svg>
|
||||
|
Before Width: | Height: | Size: 820 B After Width: | Height: | Size: 826 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M64 64C28.7 64 0 92.7 0 128V384c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V128c0-35.3-28.7-64-64-64H64zm16 64h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM64 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V240zm16 80h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V336c0-8.8 7.2-16 16-16zm80-176c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V144zm16 80h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V240c0-8.8 7.2-16 16-16zM160 336c0-8.8 7.2-16 16-16H400c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V336zM272 128h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H272c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM256 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H272c-8.8 0-16-7.2-16-16V240zM368 128h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H368c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM352 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H368c-8.8 0-16-7.2-16-16V240zM464 128h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H464c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM448 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H464c-8.8 0-16-7.2-16-16V240zm16 80h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H464c-8.8 0-16-7.2-16-16V336c0-8.8 7.2-16 16-16z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
client/public/themes/olympus/images/parrot/parrot.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="19"
|
||||
height="15"
|
||||
viewBox="0 0 19 15"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="miss-on-purpose.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs10" /><sodipodi:namedview
|
||||
id="namedview8"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="39.220856"
|
||||
inkscape:cx="17.37596"
|
||||
inkscape:cy="10.759582"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1" /><g
|
||||
id="g139-7"
|
||||
transform="matrix(0.02244338,0,0,0.02244338,3.6322523,1.8724426)"
|
||||
style="fill:none;fill-opacity:1;stroke:#262626;stroke-width:180.346;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"><path
|
||||
d="m 376.91839,231.41639 c -1.31238,-3.09721 -4.35709,-5.09202 -7.71677,-5.09202 -3.35968,0 -6.40439,1.99481 -7.71677,5.09202 l -23.1503,54.01737 c -1.78483,4.1996 -2.72974,8.66167 -2.72974,13.22874 v 40.42116 l -75.59281,44.0958 v -9.86906 c 0,-6.98183 -5.61697,-12.5988 -12.5988,-12.5988 -6.98184,0 -12.59881,5.61697 -12.59881,12.5988 v 29.39721 16.7984 12.5988 c 0,6.98184 5.61697,12.59881 12.59881,12.59881 6.98183,0 12.5988,-5.61697 12.5988,-12.59881 v -4.1996 h 75.59281 v 17.16587 l -30.70958,26.92994 c -1.83732,1.57485 -2.88722,3.88463 -2.88722,6.2994 v 8.3992 c 0,4.61956 3.77964,8.39921 8.3992,8.39921 h 50.39521 v -33.59681 c 0,-4.61956 3.77964,-8.3992 8.3992,-8.3992 4.61956,0 8.3992,3.77964 8.3992,8.3992 v 33.59681 h 50.39521 c 4.61956,0 8.3992,-3.77965 8.3992,-8.39921 v -8.3992 c 0,-2.41477 -1.0499,-4.72455 -2.88722,-6.2994 l -30.70958,-26.92994 v -17.16587 h 75.59281 v 4.1996 c 0,6.98184 5.61697,12.59881 12.5988,12.59881 6.98184,0 12.59881,-5.61697 12.59881,-12.59881 v -12.5988 -16.7984 -29.39721 c 0,-6.98183 -5.61697,-12.5988 -12.59881,-12.5988 -6.98183,0 -12.5988,5.61697 -12.5988,12.5988 v 9.86906 l -75.59281,-44.0958 V 298.6625 c 0,-4.56707 -0.94491,-9.02914 -2.72974,-13.22874 z"
|
||||
id="path1154-3"
|
||||
style="fill:none;fill-opacity:1;stroke:#262626;stroke-width:180.346;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
d="m 184.66044,8.5185895 c 12.17907,0 22.01865,9.8395825 22.01865,22.0186445 v 7.15606 C 271.15243,47.257643 322.07055,98.244571 331.6349,162.6491 h 7.15606 c 12.17906,0 22.01864,9.83958 22.01864,22.01864 0,12.17907 -9.83958,22.01865 -22.01864,22.01865 h -7.15606 c -9.56435,64.47334 -60.55128,115.39146 -124.95581,124.95581 v 7.15606 c 0,12.17906 -9.83958,22.01864 -22.01865,22.01864 -12.17906,0 -22.01864,-9.83958 -22.01864,-22.01864 V 331.6422 C 98.168455,322.07785 47.250339,271.15973 37.68599,206.68639 h -7.156059 c -12.179063,0 -22.0186444,-9.83958 -22.0186444,-22.01865 0,-12.17906 9.8395814,-22.01864 22.0186444,-22.01864 H 37.68599 C 47.250339,98.175761 98.168455,47.257643 162.6418,37.693294 v -7.15606 c 0,-12.179062 9.83958,-22.0186445 22.01864,-22.0186445 z M 82.411363,206.68639 c 8.601033,40.11522 40.184027,71.6294 80.230437,80.23043 v -14.1745 c 0,-12.17906 9.83958,-22.01864 22.01864,-22.01864 12.17907,0 22.01865,9.83958 22.01865,22.01864 v 14.1745 c 40.11522,-8.60103 71.6294,-40.18402 80.23044,-80.23043 h -14.17451 c -12.17906,0 -22.01864,-9.83958 -22.01864,-22.01865 0,-12.17906 9.83958,-22.01864 22.01864,-22.01864 h 14.17451 C 278.30849,122.53388 246.79431,91.019701 206.67909,82.418665 v 14.174506 c 0,12.179059 -9.83958,22.018639 -22.01865,22.018639 -12.17906,0 -22.01864,-9.83958 -22.01864,-22.018639 V 82.418665 C 122.52658,91.019701 91.012396,122.53388 82.411363,162.6491 h 14.174502 c 12.179065,0 22.018645,9.83958 22.018645,22.01864 0,12.17907 -9.83958,22.01865 -22.018645,22.01865 z M 184.66044,162.6491 a 22.018645,22.018645 0 1 1 0,44.03729 22.018645,22.018645 0 1 1 0,-44.03729 z"
|
||||
id="path2-1"
|
||||
style="fill:none;fill-opacity:1;stroke:#262626;stroke-width:180.346;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" /></g><g
|
||||
id="g139"
|
||||
transform="matrix(0.02244338,0,0,0.02244338,3.6322523,1.8724426)"
|
||||
style="fill:#ffffff;fill-opacity:1"><path
|
||||
d="m 376.91839,231.41639 c -1.31238,-3.09721 -4.35709,-5.09202 -7.71677,-5.09202 -3.35968,0 -6.40439,1.99481 -7.71677,5.09202 l -23.1503,54.01737 c -1.78483,4.1996 -2.72974,8.66167 -2.72974,13.22874 v 40.42116 l -75.59281,44.0958 v -9.86906 c 0,-6.98183 -5.61697,-12.5988 -12.5988,-12.5988 -6.98184,0 -12.59881,5.61697 -12.59881,12.5988 v 29.39721 16.7984 12.5988 c 0,6.98184 5.61697,12.59881 12.59881,12.59881 6.98183,0 12.5988,-5.61697 12.5988,-12.59881 v -4.1996 h 75.59281 v 17.16587 l -30.70958,26.92994 c -1.83732,1.57485 -2.88722,3.88463 -2.88722,6.2994 v 8.3992 c 0,4.61956 3.77964,8.39921 8.3992,8.39921 h 50.39521 v -33.59681 c 0,-4.61956 3.77964,-8.3992 8.3992,-8.3992 4.61956,0 8.3992,3.77964 8.3992,8.3992 v 33.59681 h 50.39521 c 4.61956,0 8.3992,-3.77965 8.3992,-8.39921 v -8.3992 c 0,-2.41477 -1.0499,-4.72455 -2.88722,-6.2994 l -30.70958,-26.92994 v -17.16587 h 75.59281 v 4.1996 c 0,6.98184 5.61697,12.59881 12.5988,12.59881 6.98184,0 12.59881,-5.61697 12.59881,-12.59881 v -12.5988 -16.7984 -29.39721 c 0,-6.98183 -5.61697,-12.5988 -12.59881,-12.5988 -6.98183,0 -12.5988,5.61697 -12.5988,12.5988 v 9.86906 l -75.59281,-44.0958 V 298.6625 c 0,-4.56707 -0.94491,-9.02914 -2.72974,-13.22874 z"
|
||||
id="path1154"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.52495;stroke-opacity:1" /><path
|
||||
d="m 184.66044,8.5185895 c 12.17907,0 22.01865,9.8395825 22.01865,22.0186445 v 7.15606 C 271.15243,47.257643 322.07055,98.244571 331.6349,162.6491 h 7.15606 c 12.17906,0 22.01864,9.83958 22.01864,22.01864 0,12.17907 -9.83958,22.01865 -22.01864,22.01865 h -7.15606 c -9.56435,64.47334 -60.55128,115.39146 -124.95581,124.95581 v 7.15606 c 0,12.17906 -9.83958,22.01864 -22.01865,22.01864 -12.17906,0 -22.01864,-9.83958 -22.01864,-22.01864 V 331.6422 C 98.168455,322.07785 47.250339,271.15973 37.68599,206.68639 h -7.156059 c -12.179063,0 -22.0186444,-9.83958 -22.0186444,-22.01865 0,-12.17906 9.8395814,-22.01864 22.0186444,-22.01864 H 37.68599 C 47.250339,98.175761 98.168455,47.257643 162.6418,37.693294 v -7.15606 c 0,-12.179062 9.83958,-22.0186445 22.01864,-22.0186445 z M 82.411363,206.68639 c 8.601033,40.11522 40.184027,71.6294 80.230437,80.23043 v -14.1745 c 0,-12.17906 9.83958,-22.01864 22.01864,-22.01864 12.17907,0 22.01865,9.83958 22.01865,22.01864 v 14.1745 c 40.11522,-8.60103 71.6294,-40.18402 80.23044,-80.23043 h -14.17451 c -12.17906,0 -22.01864,-9.83958 -22.01864,-22.01865 0,-12.17906 9.83958,-22.01864 22.01864,-22.01864 h 14.17451 C 278.30849,122.53388 246.79431,91.019701 206.67909,82.418665 v 14.174506 c 0,12.179059 -9.83958,22.018639 -22.01865,22.018639 -12.17906,0 -22.01864,-9.83958 -22.01864,-22.018639 V 82.418665 C 122.52658,91.019701 91.012396,122.53388 82.411363,162.6491 h 14.174502 c 12.179065,0 22.018645,9.83958 22.018645,22.01864 0,12.17907 -9.83958,22.01865 -22.018645,22.01865 z M 184.66044,162.6491 a 22.018645,22.018645 0 1 1 0,44.03729 22.018645,22.018645 0 1 1 0,-44.03729 z"
|
||||
id="path2"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.688083" /></g></svg>
|
||||
|
After Width: | Height: | Size: 7.6 KiB |
77
client/public/themes/olympus/images/states/scenic-aaa.svg
Normal file
@@ -0,0 +1,77 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="19"
|
||||
height="15"
|
||||
viewBox="0 0 19 15"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="scenic-aaa.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs10" /><sodipodi:namedview
|
||||
id="namedview8"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="55.466666"
|
||||
inkscape:cx="7.7253607"
|
||||
inkscape:cy="7.3737982"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1" /><g
|
||||
id="g6370-3"
|
||||
transform="matrix(0.02459887,0,0,0.02459887,3.3277353,0.85717408)"
|
||||
style="stroke:#262626;stroke-width:139.23403799;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"><path
|
||||
d="m 376.91839,231.41639 c -1.31238,-3.09721 -4.35709,-5.09202 -7.71677,-5.09202 -3.35968,0 -6.40439,1.99481 -7.71677,5.09202 l -23.1503,54.01737 c -1.78483,4.1996 -2.72974,8.66167 -2.72974,13.22874 v 40.42116 l -75.59281,44.0958 v -9.86906 c 0,-6.98183 -5.61697,-12.5988 -12.5988,-12.5988 -6.98184,0 -12.59881,5.61697 -12.59881,12.5988 v 29.39721 16.7984 12.5988 c 0,6.98184 5.61697,12.59881 12.59881,12.59881 6.98183,0 12.5988,-5.61697 12.5988,-12.59881 v -4.1996 h 75.59281 v 17.16587 l -30.70958,26.92994 c -1.83732,1.57485 -2.88722,3.88463 -2.88722,6.2994 v 8.3992 c 0,4.61956 3.77964,8.39921 8.3992,8.39921 h 50.39521 v -33.59681 c 0,-4.61956 3.77964,-8.3992 8.3992,-8.3992 4.61956,0 8.3992,3.77964 8.3992,8.3992 v 33.59681 h 50.39521 c 4.61956,0 8.3992,-3.77965 8.3992,-8.39921 v -8.3992 c 0,-2.41477 -1.0499,-4.72455 -2.88722,-6.2994 l -30.70958,-26.92994 v -17.16587 h 75.59281 v 4.1996 c 0,6.98184 5.61697,12.59881 12.5988,12.59881 6.98184,0 12.59881,-5.61697 12.59881,-12.59881 v -12.5988 -16.7984 -29.39721 c 0,-6.98183 -5.61697,-12.5988 -12.59881,-12.5988 -6.98183,0 -12.5988,5.61697 -12.5988,12.5988 v 9.86906 l -75.59281,-44.0958 V 298.6625 c 0,-4.56707 -0.94491,-9.02914 -2.72974,-13.22874 z"
|
||||
id="path1154-7-0"
|
||||
style="fill:#000000;fill-opacity:1;stroke:#262626;stroke-width:139.23403799;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" /><path
|
||||
style="fill:none;stroke:#262626;stroke-width:139.23403799;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 90.329008,471.87874 294.83388,138.74536"
|
||||
id="path9928-4"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="fill:none;stroke:#262626;stroke-width:139.23403799;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 37.215553,461.40058 33.602384,51.668198"
|
||||
id="path9928-3-3"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="fill:none;stroke:#262626;stroke-width:139.23403799;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 61.785043,466.45901 134.77088,205.58883"
|
||||
id="path9928-31-1"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
d="m 97.776883,46.496287 c -2.69052,-1.821707 -6.27788,-1.485392 -8.576032,0.784735 -2.298159,2.270126 -2.634474,5.885513 -0.784735,8.548007 l 31.389404,45.766871 -27.914144,9.08052 c -2.774607,0.89684 -4.652369,3.47525 -4.652369,6.38998 0,2.91473 1.877762,5.49315 4.652369,6.38999 l 28.895064,9.36076 -14.82589,28.19441 c -1.37329,2.60645 -0.89684,5.80143 1.20513,7.87538 2.10197,2.07394 5.26893,2.57842 7.87538,1.20513 l 28.19442,-14.82589 9.36076,28.89507 c 0.89684,2.7746 3.47525,4.65236 6.38998,4.65236 2.91474,0 5.49316,-1.87776 6.39,-4.65236 l 9.36076,-28.89507 28.19441,14.82589 c 2.60645,1.37329 5.80144,0.89684 7.87539,-1.20513 2.07393,-2.10197 2.57841,-5.26893 1.20512,-7.87538 l -14.82589,-28.19441 28.89507,-9.36076 c 2.7746,-0.89684 4.65236,-3.47526 4.65236,-6.38999 0,-2.91473 -1.87776,-5.49314 -4.65236,-6.38998 l -29.84797,-9.66907 7.20276,-19.730481 c 0.89684,-2.466311 0.28026,-5.212883 -1.56947,-7.062617 -1.84974,-1.849732 -4.59631,-2.46631 -7.06262,-1.56947 l -19.73049,7.202748 -9.69707,-29.875988 c -0.89684,-2.7746 -3.47526,-4.652359 -6.39,-4.652359 -2.91473,0 -5.49314,1.877759 -6.38998,4.652359 l -9.05248,27.91415 z"
|
||||
id="path10424-8"
|
||||
style="stroke:#262626;stroke-width:139.23403799;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1" /></g><g
|
||||
id="g6370"
|
||||
transform="matrix(0.02459887,0,0,0.02459887,3.3277353,0.85717408)"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-opacity:1"><path
|
||||
d="m 376.91839,231.41639 c -1.31238,-3.09721 -4.35709,-5.09202 -7.71677,-5.09202 -3.35968,0 -6.40439,1.99481 -7.71677,5.09202 l -23.1503,54.01737 c -1.78483,4.1996 -2.72974,8.66167 -2.72974,13.22874 v 40.42116 l -75.59281,44.0958 v -9.86906 c 0,-6.98183 -5.61697,-12.5988 -12.5988,-12.5988 -6.98184,0 -12.59881,5.61697 -12.59881,12.5988 v 29.39721 16.7984 12.5988 c 0,6.98184 5.61697,12.59881 12.59881,12.59881 6.98183,0 12.5988,-5.61697 12.5988,-12.59881 v -4.1996 h 75.59281 v 17.16587 l -30.70958,26.92994 c -1.83732,1.57485 -2.88722,3.88463 -2.88722,6.2994 v 8.3992 c 0,4.61956 3.77964,8.39921 8.3992,8.39921 h 50.39521 v -33.59681 c 0,-4.61956 3.77964,-8.3992 8.3992,-8.3992 4.61956,0 8.3992,3.77964 8.3992,8.3992 v 33.59681 h 50.39521 c 4.61956,0 8.3992,-3.77965 8.3992,-8.39921 v -8.3992 c 0,-2.41477 -1.0499,-4.72455 -2.88722,-6.2994 l -30.70958,-26.92994 v -17.16587 h 75.59281 v 4.1996 c 0,6.98184 5.61697,12.59881 12.5988,12.59881 6.98184,0 12.59881,-5.61697 12.59881,-12.59881 v -12.5988 -16.7984 -29.39721 c 0,-6.98183 -5.61697,-12.5988 -12.59881,-12.5988 -6.98183,0 -12.5988,5.61697 -12.5988,12.5988 v 9.86906 l -75.59281,-44.0958 V 298.6625 c 0,-4.56707 -0.94491,-9.02914 -2.72974,-13.22874 z"
|
||||
id="path1154-7"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.52495;stroke-opacity:1" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 90.329008,471.87874 294.83388,138.74536"
|
||||
id="path9928"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 37.215553,461.40058 33.602384,51.668198"
|
||||
id="path9928-3"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:10;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 61.785043,466.45901 134.77088,205.58883"
|
||||
id="path9928-31"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
d="m 97.776883,46.496287 c -2.69052,-1.821707 -6.27788,-1.485392 -8.576032,0.784735 -2.298159,2.270126 -2.634474,5.885513 -0.784735,8.548007 l 31.389404,45.766871 -27.914144,9.08052 c -2.774607,0.89684 -4.652369,3.47525 -4.652369,6.38998 0,2.91473 1.877762,5.49315 4.652369,6.38999 l 28.895064,9.36076 -14.82589,28.19441 c -1.37329,2.60645 -0.89684,5.80143 1.20513,7.87538 2.10197,2.07394 5.26893,2.57842 7.87538,1.20513 l 28.19442,-14.82589 9.36076,28.89507 c 0.89684,2.7746 3.47525,4.65236 6.38998,4.65236 2.91474,0 5.49316,-1.87776 6.39,-4.65236 l 9.36076,-28.89507 28.19441,14.82589 c 2.60645,1.37329 5.80144,0.89684 7.87539,-1.20513 2.07393,-2.10197 2.57841,-5.26893 1.20512,-7.87538 l -14.82589,-28.19441 28.89507,-9.36076 c 2.7746,-0.89684 4.65236,-3.47526 4.65236,-6.38999 0,-2.91473 -1.87776,-5.49314 -4.65236,-6.38998 l -29.84797,-9.66907 7.20276,-19.730481 c 0.89684,-2.466311 0.28026,-5.212883 -1.56947,-7.062617 -1.84974,-1.849732 -4.59631,-2.46631 -7.06262,-1.56947 l -19.73049,7.202748 -9.69707,-29.875988 c -0.89684,-2.7746 -3.47526,-4.652359 -6.39,-4.652359 -2.91473,0 -5.49314,1.877759 -6.38998,4.652359 l -9.05248,27.91415 z"
|
||||
id="path10424"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.280262;stroke-opacity:1" /></g></svg>
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
width="19"
|
||||
height="15"
|
||||
viewBox="0 0 19 15"
|
||||
fill="none"
|
||||
version="1.1"
|
||||
id="svg6"
|
||||
sodipodi:docname="simulate-fire-fight.svg"
|
||||
inkscape:version="1.2.2 (732a01da63, 2022-12-09)"
|
||||
xml:space="preserve"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs10" /><sodipodi:namedview
|
||||
id="namedview8"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="39.220856"
|
||||
inkscape:cx="4.2961837"
|
||||
inkscape:cy="7.419522"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="1912"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg6"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:deskcolor="#d1d1d1" /><path
|
||||
d="m 16.282245,9.6013771 c 0,0.1740419 -0.142397,0.3164399 -0.316438,0.3164399 h -2.300125 c -0.108773,0.189864 -0.314463,0.316441 -0.547837,0.316441 h -2.161682 l 0.104821,0.316439 h 1.10754 c 0.174043,0 0.316441,0.142398 0.316441,0.316441 v 0.316439 c 0,0.174043 -0.142398,0.316442 -0.316441,0.316442 h -1.671199 c -0.136465,0 -0.257107,-0.08702 -0.300618,-0.215576 L 9.8486235,10.234258 H 9.3205639 v 0.949319 c 0,0.174043 -0.142398,0.316442 -0.3164399,0.316442 H 8.6876841 c -0.1740424,0 -0.3164404,-0.142399 -0.3164404,-0.316442 v -0.87021 L 6.5497356,10.76825 C 6.349983,10.817694 6.1561633,10.667385 6.1561633,10.461699 V 9.2849368 c 0,-0.174042 0.1423981,-0.31644 0.3164399,-0.31644 H 8.3712437 V 8.6520569 c 0,-0.350062 0.2828182,-0.6328802 0.6328803,-0.6328802 h 2.847961 c 0.350061,0 0.63288,0.2828182 0.63288,0.6328802 h 0.63288 c 0.233374,0 0.43906,0.1265758 0.547837,0.3164399 h 1.983684 c 0,-0.1740418 0.142398,-0.3164399 0.316441,-0.3164399 0.174041,0 0.316438,0.1423981 0.316438,0.3164399 v 0.31644 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#262626;stroke-width:3.88754;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path14144" /><path
|
||||
d="m 7.2231639,6.192428 c 0.5023486,0 0.9849197,0.1404203 1.4002473,0.3935723 V 12.521231 H 4.8261305 V 9.0740104 L 3.7660562,10.865854 C 3.5445481,11.241626 3.0580216,11.366224 2.6822489,11.144715 2.3064765,10.923208 2.181878,10.436681 2.4033861,10.060908 L 3.9104319,7.5135653 C 4.394981,6.6947765 5.2750798,6.192428 6.2263778,6.192428 Z M 5.1425706,3.9773475 c -3.695e-4,-2.1099689 3.1640311,-2.1099689 3.1644005,0 3.697e-4,2.109969 -3.164031,2.109969 -3.1644005,0 z"
|
||||
id="path10805"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#262626;stroke-width:3.88754;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
sodipodi:nodetypes="scccccsccsssss" /><path
|
||||
d="m 16.290263,9.5997326 c 0,0.174042 -0.142396,0.31644 -0.316438,0.31644 h -2.300124 c -0.108774,0.1898644 -0.314463,0.3164404 -0.547838,0.3164404 h -2.161681 l 0.104821,0.316439 h 1.10754 c 0.174042,0 0.31644,0.142399 0.31644,0.316443 v 0.316439 c 0,0.174043 -0.142398,0.316442 -0.31644,0.316442 h -1.671198 c -0.136466,0 -0.257108,-0.08702 -0.300618,-0.215576 L 9.8566424,10.232613 H 9.3285828 v 0.949321 c 0,0.174043 -0.1423981,0.316442 -0.31644,0.316442 H 8.6957029 c -0.1740423,0 -0.3164404,-0.142399 -0.3164404,-0.316442 v -0.870211 l -1.821508,0.454883 C 6.3580019,10.816049 6.1641822,10.66574 6.1641822,10.460054 V 9.2832924 c 0,-0.174042 0.142398,-0.31644 0.3164398,-0.31644 H 8.3792625 V 8.6504125 c 0,-0.350062 0.2828182,-0.6328802 0.6328803,-0.6328802 h 2.8479602 c 0.350062,0 0.63288,0.2828182 0.63288,0.6328802 h 0.63288 c 0.233375,0 0.439061,0.1265757 0.547838,0.3164399 h 1.983684 c 0,-0.1740419 0.142398,-0.3164399 0.31644,-0.3164399 0.174042,0 0.316438,0.142398 0.316438,0.3164399 v 0.31644 z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#262626;stroke-width:1.31528;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path14144-2" /></svg>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
@@ -45,6 +45,8 @@
|
||||
--nav-text: #ECECEC;
|
||||
|
||||
--ol-select-secondary: #545F6C;
|
||||
--ol-switch-off:#686868;
|
||||
--ol-switch-undefined:#383838;
|
||||
|
||||
/*** General border radii **/
|
||||
--border-radius-xs: 2px;
|
||||
@@ -88,3 +90,7 @@
|
||||
--unit-fuel-y: 22px;
|
||||
--unit-vvi-width: 4px;
|
||||
}
|
||||
|
||||
* {
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
54
client/public/themes/parrot/images/parrot.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 460 460" xml:space="preserve">
|
||||
<g id="XMLID_2582_">
|
||||
<path id="XMLID_4_" style="fill:#871A27;" d="M353.16,459.97L353.16,459.97c-32.138-3.383-61.944-18.336-83.849-42.066
|
||||
L65.784,206.343c-6.885-7.157-3.266-19.083,6.439-21.217l26.729-5.878l258.429,272.378
|
||||
C360.487,455.023,357.74,460.452,353.16,459.97z"/>
|
||||
<path id="XMLID_2108_" style="fill:#A52531;" d="M403.222,459.97L403.222,459.97c-32.138-3.383-61.944-18.336-83.849-42.066
|
||||
L100.124,190l48.891-10.752l258.429,272.378C410.549,455.023,407.802,460.452,403.222,459.97z"/>
|
||||
<path id="XMLID_2018_" style="fill:#CC3248;" d="M454.455,459.97L454.455,459.97c-32.138-3.383-61.944-18.336-83.849-42.066
|
||||
L151.357,190l48.891-10.752l258.429,272.378C461.782,455.023,459.035,460.452,454.455,459.97z"/>
|
||||
<path id="XMLID_2391_" style="fill:#C1A991;" d="M280.347,380h-80.099l-9.48,37.873C188.984,425,182.572,430,175.217,430
|
||||
s-13.767-5-15.551-12.127L150.186,380h-0.277c-40.008,0-73.881-29.485-79.348-69.069L44.112,119.414
|
||||
C30.376,116.668,20.025,104.544,20.025,90H0c0-20.678,15.692-37.69,35.817-39.781C35.079,22.989,56.998,0,84.812,0l0,0
|
||||
c21.102,0,39.837,13.487,46.51,33.481l0.617,1.848c8.632,25.864,26.455,47.418,49.597,61.877
|
||||
c8.184,5.113,15.785,13.683,19.63,27.974L280.347,380z"/>
|
||||
<path id="XMLID_2087_" style="fill:#473C3F;" d="M180.223,420v10v10v5c0,8.284-6.724,15-15.019,15h-50.062
|
||||
c-2.765,0-5.006-2.239-5.006-5l0,0c0-8.284,6.724-15,15.019-15h35.043c5.53,0,10.012-4.477,10.012-10v-10H180.223z"/>
|
||||
<path id="XMLID_1701_" style="fill:#9B1C2B;" d="M280.347,380h-80.099l-9.48,37.873C188.984,425,182.572,430,175.217,430
|
||||
s-13.767-5-15.551-12.127L150.186,380h-0.277c-40.008,0-73.881-29.485-79.348-69.069L44.112,119.414L30.382,90H52.77
|
||||
c4.803,0,9.541-1.117,13.837-3.262l16.413-8.197c10.482-5.234,17.103-15.935,17.103-27.639C100.124,33.835,86.272,20,69.184,20
|
||||
H45.307C54.179,7.937,68.475,0,84.812,0l0,0c21.102,0,39.837,13.487,46.51,33.481l0.617,1.848
|
||||
c8.632,25.864,26.455,47.418,49.597,61.877c8.184,5.113,15.785,13.683,19.63,27.974L280.347,380z"/>
|
||||
<path id="XMLID_2389_" style="fill:#8C735D;" d="M40.05,90l4.062,29.414C30.376,116.668,20.025,104.544,20.025,90H0
|
||||
c0-3.454,0.447-6.803,1.27-10h37.667L40.05,90z"/>
|
||||
<path id="XMLID_1631_" style="fill:#AF8F6D;" d="M40.05,90H0c0-20.676,15.688-37.686,35.81-39.781
|
||||
c0.037,1.375,0.121,2.759,0.276,4.154L40.05,90z"/>
|
||||
<path id="XMLID_2088_" style="fill:#66313A;" d="M70.087,45c0,2.761-2.241,5-5.006,5s-5.006-2.239-5.006-5s2.241-5,5.006-5
|
||||
C67.845,40,70.087,42.239,70.087,45z"/>
|
||||
<path id="XMLID_1864_" style="fill:#720C1F;" d="M150.186,380h50.062l-5.006,20h-40.049L150.186,380z"/>
|
||||
<path id="XMLID_1836_" style="fill:#720C1F;" d="M331.261,397.796L246.772,194.5c-19.25-46.319-62.419-73.992-106.431-68.225
|
||||
l-33.801,4.429c-6.283,0.823-10.071,7.378-7.641,13.223l82.08,197.501c19.25,46.32,62.419,73.992,106.431,68.226l40.03-5.245
|
||||
C330.582,403.996,332.476,400.719,331.261,397.796z"/>
|
||||
<path id="XMLID_1924_" style="fill:#871A27;" d="M349.271,380.344l-39.554,8.084c-43.488,8.888-88.523-15.638-111.033-60.467
|
||||
l-95.979-191.149c-2.841-5.657,0.469-12.465,6.677-13.734l33.398-6.826c43.488-8.888,88.523,15.638,111.033,60.468l98.796,196.758
|
||||
C354.03,376.305,352.375,379.709,349.271,380.344z"/>
|
||||
<path id="XMLID_1905_" style="fill:#A52531;" d="M351.863,378.865c-39.327,2.272-78.009-21.643-98.331-62.115l-73.749-146.618
|
||||
c-1.957-3.897-0.994-8.339,1.823-11.141c37.969-0.535,74.788,23.122,94.439,62.258l76.565,152.227
|
||||
C353.548,375.345,353.139,377.459,351.863,378.865z"/>
|
||||
<path id="XMLID_1810_" style="fill:#82542E;" d="M349.27,380.344L310.841,385c-43.488,8.888-89.648-12.21-112.158-57.04
|
||||
l-66.414-132.268c34.538,30.088,78.926,43.67,122.276,34.81l23.828-4.87l74.236,147.845
|
||||
C354.029,376.305,352.374,379.709,349.27,380.344z"/>
|
||||
<path id="XMLID_1705_" style="fill:#684627;" d="M351.864,378.865c-0.656,0.723-1.538,1.263-2.592,1.479l-39.307,6.781
|
||||
c-43.488,8.888-88.77-14.335-111.28-59.165l-66.414-132.268c22.958,20,50.271,32.696,78.737,36.365l42.526,84.693
|
||||
C273.856,357.223,312.537,381.137,351.864,378.865z"/>
|
||||
<path id="XMLID_1636_" style="fill:#385056;" d="M349.27,380.344l-39.554,8.084c-43.488,8.888-88.523-15.638-111.033-60.467
|
||||
l-21.17-42.161c28.416,19.045,62.146,26.827,95.205,20.07l41.667-8.516l38.223,76.123
|
||||
C354.029,376.305,352.374,379.709,349.27,380.344z"/>
|
||||
<path id="XMLID_1807_" style="fill:#446772;" d="M249.716,308.257c7.802-0.018,15.644-0.792,23.45-2.388l41.667-8.516
|
||||
l38.223,76.123c0.594,1.184,0.644,2.466,0.281,3.606c-0.209,0.65-0.552,1.254-1.012,1.765c-0.005,0.006-0.01,0.012-0.016,0.018
|
||||
c-39.326,2.271-78.007-21.643-98.329-62.115L249.716,308.257z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
109
client/public/themes/parrot/theme.css
Normal file
@@ -0,0 +1,109 @@
|
||||
:root {
|
||||
/** Colours **/
|
||||
|
||||
/*** Coalition: neutral ***/
|
||||
--primary-neutral: #949ba7;
|
||||
--secondary-neutral-outline: #111111;
|
||||
--secondary-neutral-text: #111111;
|
||||
--unit-background-neutral: #CFD9E8;
|
||||
|
||||
/*** Coalition: blue ***/
|
||||
--primary-blue: #247be2;
|
||||
--secondary-blue-outline: #082e44;
|
||||
--secondary-blue-text: #017DC1;
|
||||
--unit-background-blue: #3BB9FF;
|
||||
|
||||
/*** Coalition: red ***/
|
||||
--primary-red: #ff5858;
|
||||
--secondary-red-outline: #262222;
|
||||
--secondary-red-text: #D42121;
|
||||
--unit-background-red: #FF5858;
|
||||
|
||||
/*** UI Colours **/
|
||||
--accent-amber: #ffd828;
|
||||
--accent-green: #8bff63;
|
||||
--accent-light-blue: #5ca7ff;
|
||||
--accent-dark-blue: #017DC1;
|
||||
--transparent-accent-light-blue: rgba(92, 167, 255, .33);
|
||||
--accent-light-red: #F5B6B6;
|
||||
|
||||
--background-grey: #3d4651;
|
||||
--background-slate-blue: #363c43;
|
||||
--background-offwhite: #f2f2f3;
|
||||
--background-steel: #202831;
|
||||
|
||||
--secondary-dark-steel: #181e25;
|
||||
--secondary-gunmetal-grey: #2f2f2f;
|
||||
--secondary-lighter-grey: #949ba7;
|
||||
--secondary-light-grey: #797e83;
|
||||
--secondary-semitransparent-white: #FFFFFFAA;
|
||||
--secondary-transparent-white: #FFFFFF30;
|
||||
--secondary-yellow: #ffd46893;
|
||||
|
||||
--background-hover: #f2f2f333;
|
||||
|
||||
--nav-text: #ECECEC;
|
||||
|
||||
--ol-select-secondary: #545F6C;
|
||||
--ol-switch-off:#686868;
|
||||
--ol-switch-undefined:#383838;
|
||||
|
||||
/*** General border radii **/
|
||||
--border-radius-xs: 2px;
|
||||
--border-radius-sm: 5px;
|
||||
--border-radius-md: 10px;
|
||||
--border-radius-lg: 15px;
|
||||
|
||||
/*** Fonts **/
|
||||
--font-weight-bolder: 600;
|
||||
|
||||
/*** Unit marker settings ***/
|
||||
/*** All markers **/
|
||||
--unit-border-radius: var(--border-radius-xs);
|
||||
--unit-font-size: 14px;
|
||||
--unit-font-weight: bolder;
|
||||
--unit-label-border-width: 2px;
|
||||
--unit-spotlight-fill: var(--secondary-yellow);
|
||||
--unit-spotlight-radius: 26px;
|
||||
--unit-stroke-width: 3px;
|
||||
--unit-height: 50px;
|
||||
--unit-width: 50px;
|
||||
|
||||
--unit-health-border-width: 2px;
|
||||
--unit-health-height: 6px;
|
||||
--unit-health-width: 36px;
|
||||
--unit-health-x: 0px;
|
||||
--unit-health-y: 26px;
|
||||
|
||||
/*** Air units ***/
|
||||
--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;
|
||||
}
|
||||
|
||||
* {
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
svg {
|
||||
animation: spin linear infinite 1s;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform:rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,42 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
// TODO should be user selectable or at least configurable from configuration file
|
||||
var theme = "olympus";
|
||||
var themesMap = {};
|
||||
|
||||
router.get('/theme/*', function (req, res, next) {
|
||||
res.redirect(req.url.replace("theme", "themes/" + theme));
|
||||
/* If this is the first time this session makes a request, create a uuid and save it to the map. Default theme is the olympus theme */
|
||||
if (!req.cookies.id) {
|
||||
const id = uuidv4();
|
||||
res.cookie('id', id, { httpOnly: true });
|
||||
themesMap[id] = "olympus";
|
||||
reqTheme = "olympus";
|
||||
}
|
||||
else {
|
||||
/* If it is present, recover the session theme from the map */
|
||||
if (!(req.cookies.id in themesMap))
|
||||
themesMap[req.cookies.id] = "olympus";
|
||||
reqTheme = themesMap[req.cookies.id];
|
||||
}
|
||||
|
||||
/* Yes, this in an easter egg! :D Feel free to ignore it, or activate the parrot theme to check what it does. Why parrots? The story is a bit long, come to the Discord and ask :D */
|
||||
if (reqTheme === "parrot" && !req.url.includes(".css"))
|
||||
res.redirect('/themes/parrot/images/parrot.svg');
|
||||
else
|
||||
res.redirect(req.url.replace("theme", "themes/" + reqTheme));
|
||||
});
|
||||
|
||||
router.put('/theme/:newTheme', function (req, res, next) {
|
||||
/* Add the theme to the map, if this session already has an id */
|
||||
const newTheme = req.params.newTheme;
|
||||
if (req.cookies.id) {
|
||||
themesMap[req.cookies.id] = newTheme;
|
||||
console.log("Theme set to " + newTheme + " for session " + req.cookies.id);
|
||||
} else {
|
||||
console.log("Failed to set theme to " + newTheme + ", no session id");
|
||||
}
|
||||
|
||||
res.end("Ok");
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 7.7 MiB |
@@ -1,5 +1,5 @@
|
||||
import { LatLng, LatLngBounds } from "leaflet";
|
||||
import { MapMarkerControl } from "../map/map";
|
||||
import { MapMarkerVisibilityControl } from "../map/map";
|
||||
|
||||
export const UNITS_URI = "units";
|
||||
export const WEAPONS_URI = "weapons";
|
||||
@@ -28,9 +28,9 @@ export const emissionsCountermeasures: string[] = ["silent", "attack", "defend",
|
||||
|
||||
export const ROEDescriptions: string[] = [
|
||||
"Free (Attack anyone)",
|
||||
"Designated (Attack the designated target only)",
|
||||
"Designated (Attack the designated target only) \nWARNING: Ground and Navy units don't respect this ROE, it will be equivalent to weapons FREE.",
|
||||
"",
|
||||
"Return (Only fire if fired upon)",
|
||||
"Return (Only fire if fired upon) \nWARNING: Ground and Navy units don't respect this ROE, it will be equivalent to weapons FREE.",
|
||||
"Hold (Never fire)"
|
||||
];
|
||||
|
||||
@@ -102,6 +102,27 @@ export const minimapBoundaries = [
|
||||
new LatLng(10.7725, 149.3918333),
|
||||
new LatLng(22.5127778, 149.5427778),
|
||||
new LatLng(22.09, 135.0572222)
|
||||
],
|
||||
[ // South Atlantic
|
||||
new LatLng(-49.097217, -79.418267),
|
||||
new LatLng(-56.874517,-79.418267),
|
||||
new LatLng(-56.874517, -43.316433),
|
||||
new LatLng(-49.097217, -43.316433),
|
||||
new LatLng(-49.097217, -79.418267)
|
||||
],
|
||||
[ // Normandy
|
||||
new LatLng(50.44, -3.29),
|
||||
new LatLng(48.12,-3.29),
|
||||
new LatLng(48.12, 3.70),
|
||||
new LatLng(50.44, 3.70),
|
||||
new LatLng(50.44, -3.29)
|
||||
],
|
||||
[ // Sinai
|
||||
new LatLng(34.312222, 28.523333),
|
||||
new LatLng(25.946944, 28.523333),
|
||||
new LatLng(25.946944, 36.897778),
|
||||
new LatLng(34.312222, 36.897778),
|
||||
new LatLng(34.312222, 28.523333)
|
||||
]
|
||||
];
|
||||
|
||||
@@ -111,7 +132,9 @@ export const mapBounds = {
|
||||
"Nevada": { bounds: new LatLngBounds([34.4037128, -119.7806729], [39.7372411, -112.1130805]), zoom: 5 },
|
||||
"PersianGulf": { bounds: new LatLngBounds([21.729393, 47.572675], [33.131584, 64.7313594]), zoom: 5 },
|
||||
"Caucasus": { bounds: new LatLngBounds([39.6170191, 27.634935], [47.3907982, 49.3101946]), zoom: 4 },
|
||||
// TODO "Falklands"
|
||||
"Falklands": { bounds: new LatLngBounds([-49.097217, -79.418267], [-56.874517, -43.316433]), zoom: 3 },
|
||||
"Normandy": { bounds: new LatLngBounds([50.44, -3.29], [48.12, 3.70]), zoom: 5 },
|
||||
"SinaiMap": { bounds: new LatLngBounds([34.312222, 28.523333], [25.946944, 36.897778]), zoom: 4 },
|
||||
}
|
||||
|
||||
export const mapLayers = {
|
||||
@@ -119,7 +142,7 @@ export const mapLayers = {
|
||||
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
||||
minZoom: 1,
|
||||
maxZoom: 19,
|
||||
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, GetApp().getMap()ping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Mapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
},
|
||||
"USGS Topo": {
|
||||
urlTemplate: 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
|
||||
@@ -157,14 +180,22 @@ export const mapLayers = {
|
||||
export const IDLE = "Idle";
|
||||
export const MOVE_UNIT = "Move unit";
|
||||
export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area";
|
||||
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "helicopter", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
|
||||
export const visibilityControlsTypes: string[][] = [["human"], ["dcs"], ["aircraft"], ["helicopter"], ["groundunit-sam", "groundunit-sam-radar", "groundunit-sam-launcher"], ["groundunit-other", "groundunit-ewr"], ["navyunit"], ["airbase"]];
|
||||
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "helicopter", "groundunit-sam", "groundunit", "navyunit", "airbase"];
|
||||
export const visibilityControlsTypes: string[][] = [["human"], ["dcs"], ["aircraft"], ["helicopter"], ["groundunit-sam"], ["groundunit"], ["navyunit"], ["airbase"]];
|
||||
export const visibilityControlsTooltips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle helicopter visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"];
|
||||
export const MAP_MARKER_CONTROLS: MapMarkerControl[] = [{
|
||||
|
||||
export const MAP_MARKER_CONTROLS: MapMarkerVisibilityControl[] = [{
|
||||
"name": "Human",
|
||||
"image": "visibility/human.svg",
|
||||
"toggles": ["human"],
|
||||
"tooltip": "Toggle human players' visibility"
|
||||
}, {
|
||||
"image": "visibility/head-side-virus-solid.svg",
|
||||
"isProtected": false,
|
||||
"name": "Olympus",
|
||||
"protectable": false,
|
||||
"toggles": ["olympus"],
|
||||
"tooltip": "Toggle Olympus-controlled units' visibility"
|
||||
}, {
|
||||
"image": "visibility/dcs.svg",
|
||||
"isProtected": true,
|
||||
@@ -188,9 +219,9 @@ export const MAP_MARKER_CONTROLS: MapMarkerControl[] = [{
|
||||
"toggles": ["groundunit-sam"],
|
||||
"tooltip": "Toggle air defence units' visibility"
|
||||
}, {
|
||||
"image": "visibility/groundunit-other.svg",
|
||||
"image": "visibility/groundunit.svg",
|
||||
"name": "Ground units",
|
||||
"toggles": ["groundunit-other"],
|
||||
"toggles": ["groundunit"],
|
||||
"tooltip": "Toggle ground units' visibility"
|
||||
}, {
|
||||
"image": "visibility/navyunit.svg",
|
||||
@@ -204,9 +235,9 @@ export const MAP_MARKER_CONTROLS: MapMarkerControl[] = [{
|
||||
"tooltip": "Toggle airbase' visibility"
|
||||
}];
|
||||
|
||||
export const IADSTypes = ["AAA", "MANPADS", "SAM Site", "Radar"];
|
||||
export const IADSDensities: { [key: string]: number } = { "AAA": 0.8, "MANPADS": 0.3, "SAM Site": 0.1, "Radar": 0.05 };
|
||||
|
||||
export const IADSTypes = ["AAA", "SAM Site", "Radar (EWR)"];
|
||||
export const IADSDensities: { [key: string]: number } = { "AAA": 0.8, "SAM Site": 0.1, "Radar (EWR)": 0.05 };
|
||||
export const GROUND_UNIT_AIR_DEFENCE_REGEX:RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
|
||||
export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out";
|
||||
export const SHOW_UNIT_LABELS = "Show unit labels (L)";
|
||||
export const SHOW_UNITS_ENGAGEMENT_RINGS = "Show units threat range rings (Q)";
|
||||
@@ -236,6 +267,7 @@ export enum DataIndexes {
|
||||
horizontalVelocity,
|
||||
verticalVelocity,
|
||||
heading,
|
||||
track,
|
||||
isActiveTanker,
|
||||
isActiveAWACS,
|
||||
onOff,
|
||||
@@ -275,4 +307,8 @@ export const MGRS_PRECISION_1M = 6;
|
||||
export const DELETE_CYCLE_TIME = 0.05;
|
||||
export const DELETE_SLOW_THRESHOLD = 50;
|
||||
|
||||
export const GROUPING_ZOOM_TRANSITION = 13;
|
||||
export const GROUPING_ZOOM_TRANSITION = 13;
|
||||
|
||||
export const MAX_SHOTS_SCATTER = 3;
|
||||
export const MAX_SHOTS_INTENSITY = 3;
|
||||
export const SHOTS_SCATTER_DEGREES = 10;
|
||||
@@ -1,4 +1,6 @@
|
||||
export interface ContextInterface {
|
||||
allowUnitCopying?: boolean;
|
||||
allowUnitPasting?: boolean;
|
||||
useSpawnMenu?: boolean;
|
||||
useUnitControlPanel?: boolean;
|
||||
useUnitInfoPanel?: boolean;
|
||||
@@ -6,16 +8,28 @@ export interface ContextInterface {
|
||||
|
||||
export class Context {
|
||||
|
||||
#allowUnitCopying:boolean;
|
||||
#allowUnitPasting:boolean;
|
||||
#useSpawnMenu:boolean;
|
||||
#useUnitControlPanel:boolean;
|
||||
#useUnitInfoPanel:boolean;
|
||||
|
||||
constructor( config:ContextInterface ) {
|
||||
this.#allowUnitCopying = ( config.allowUnitCopying !== false );
|
||||
this.#allowUnitPasting = ( config.allowUnitPasting !== false );
|
||||
this.#useSpawnMenu = ( config.useSpawnMenu !== false );
|
||||
this.#useUnitControlPanel = ( config.useUnitControlPanel !== false );
|
||||
this.#useUnitInfoPanel = ( config.useUnitInfoPanel !== false );
|
||||
}
|
||||
|
||||
getAllowUnitCopying() {
|
||||
return this.#allowUnitCopying;
|
||||
}
|
||||
|
||||
getAllowUnitPasting() {
|
||||
return this.#allowUnitPasting;
|
||||
}
|
||||
|
||||
getUseSpawnMenu() {
|
||||
return this.#useSpawnMenu;
|
||||
}
|
||||
|
||||
@@ -27,8 +27,8 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
super(ID);
|
||||
|
||||
/* Create the coalition switch */
|
||||
this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value), true);
|
||||
this.#coalitionSwitch.setValue(true);
|
||||
|
||||
/* Create the controls of the IADS creation submenu */
|
||||
this.#iadsTypesDropdown = new Dropdown("iads-units-type-options", () => { });
|
||||
@@ -68,7 +68,8 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
const area = this.getCoalitionArea();
|
||||
if (area)
|
||||
getApp().getUnitsManager().createIADS(area, getCheckboxOptions(this.#iadsTypesDropdown), getCheckboxOptions(this.#iadsErasDropdown), getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue());
|
||||
})
|
||||
this.hide();
|
||||
});
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
this.#coalitionSwitch.setValue(this.getCoalitionArea()?.getCoalition() === "red");
|
||||
this.#coalitionSwitch.setValue(this.getCoalitionArea()?.getCoalition() === "blue");
|
||||
}
|
||||
|
||||
/** Get the CoalitionArea object the contextmenu is editing
|
||||
@@ -146,11 +147,11 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
|
||||
/** Callback event called when the coalition switch is clicked to change the coalition of the CoalitionArea
|
||||
*
|
||||
* @param value Switch position (false: blue, true: red)
|
||||
* @param value Switch position (false: red, true: blue)
|
||||
*/
|
||||
#onSwitchClick(value: boolean) {
|
||||
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) {
|
||||
this.getCoalitionArea()?.setCoalition(value ? "red" : "blue");
|
||||
this.getCoalitionArea()?.setCoalition(value ? "blue" : "red");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
|
||||
/* Create the coalition switch */
|
||||
this.#coalitionSwitch = new Switch("coalition-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
this.#coalitionSwitch.setValue(true);
|
||||
this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick());
|
||||
|
||||
/* Create the spawn menus for the different unit types */
|
||||
@@ -128,9 +128,9 @@ export class MapContextMenu extends ContextMenu {
|
||||
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getApp().getActiveCoalition()) });
|
||||
if (getApp().getActiveCoalition() == "blue")
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
else if (getApp().getActiveCoalition() == "red")
|
||||
this.#coalitionSwitch.setValue(true);
|
||||
else if (getApp().getActiveCoalition() == "red")
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
else
|
||||
this.#coalitionSwitch.setValue(undefined);
|
||||
|
||||
@@ -232,10 +232,10 @@ export class MapContextMenu extends ContextMenu {
|
||||
|
||||
/** Callback called when the user left clicks on the coalition switch
|
||||
*
|
||||
* @param value Switch position (false: "blue", true: "red")
|
||||
* @param value Switch position (true: "blue", false: "red")
|
||||
*/
|
||||
#onSwitchClick(value: boolean) {
|
||||
value ? getApp().setActiveCoalition("red") : getApp().setActiveCoalition("blue");
|
||||
value ? getApp().setActiveCoalition("blue") : getApp().setActiveCoalition("red");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getApp().getActiveCoalition()) });
|
||||
this.#aircraftSpawnMenu.setCountries();
|
||||
this.#helicopterSpawnMenu.setCountries();
|
||||
|
||||
@@ -5,8 +5,10 @@ export class Dropdown {
|
||||
#callback: CallableFunction;
|
||||
#defaultValue: string;
|
||||
#optionsList: string[] = [];
|
||||
#labelsList: string[] | undefined = [];
|
||||
#index: number = 0;
|
||||
#hidden: boolean = false;
|
||||
#text!: HTMLElement;
|
||||
|
||||
constructor(ID: string | null, callback: CallableFunction, options: string[] | null = null, defaultText?: string) {
|
||||
if (ID === null)
|
||||
@@ -15,13 +17,16 @@ export class Dropdown {
|
||||
this.#container = document.getElementById(ID) as HTMLElement;
|
||||
|
||||
this.#options = this.#container.querySelector(".ol-select-options") as HTMLElement;
|
||||
this.#value = this.#container.querySelector(".ol-select-value") as HTMLElement;
|
||||
|
||||
const text = this.#container.querySelector(".ol-select-value-text");
|
||||
this.#value = (text instanceof HTMLElement) ? text : this.#container.querySelector(".ol-select-value") as HTMLElement;
|
||||
|
||||
this.#defaultValue = this.#value.innerText;
|
||||
this.#callback = callback;
|
||||
|
||||
if (options != null) this.setOptions(options);
|
||||
|
||||
this.#value.addEventListener("click", (ev) => { this.#toggle(); });
|
||||
(this.#container.querySelector(".ol-select-value") as HTMLElement)?.addEventListener("click", (ev) => { this.#toggle(); });
|
||||
|
||||
document.addEventListener("click", (ev) => {
|
||||
if (!(this.#value.contains(ev.target as Node) || this.#options.contains(ev.target as Node) || this.#container.contains(ev.target as Node))) {
|
||||
@@ -36,48 +41,37 @@ export class Dropdown {
|
||||
return this.#container;
|
||||
}
|
||||
|
||||
setOptions(optionsList: string[], sort: "" | "string" | "number" | "string+number" = "string") {
|
||||
if (sort === "number") {
|
||||
this.#optionsList = optionsList.sort((optionA: string, optionB: string) => {
|
||||
const a = parseInt(optionA);
|
||||
const b = parseInt(optionB);
|
||||
if (a > b)
|
||||
return 1;
|
||||
else
|
||||
return (b > a) ? -1 : 0;
|
||||
});
|
||||
} else if (sort === "string+number") {
|
||||
this.#optionsList = optionsList.sort((optionA: string, optionB: string) => {
|
||||
var regex = /\d+/g;
|
||||
var matchesA = optionA.match(regex);
|
||||
var matchesB = optionB.match(regex);
|
||||
if ((matchesA != null && matchesA?.length > 0) && (matchesB != null && matchesB?.length > 0) && optionA[0] == optionB[0]) {
|
||||
const a = parseInt(matchesA[0] ?? 0);
|
||||
const b = parseInt(matchesB[0] ?? 0);
|
||||
if (a > b)
|
||||
return 1;
|
||||
else
|
||||
return (b > a) ? -1 : 0;
|
||||
} else {
|
||||
if (optionA > optionB)
|
||||
return 1;
|
||||
else
|
||||
return (optionB > optionA) ? -1 : 0;
|
||||
}
|
||||
|
||||
});
|
||||
} else if (sort === "string") {
|
||||
this.#optionsList = optionsList.sort();
|
||||
}
|
||||
/** Set the dropdown options strings
|
||||
*
|
||||
* @param optionsList List of options. These are the keys that will always be returned on selection
|
||||
* @param sort Sort method. "string" performs js default sort. "number" sorts purely by numeric value.
|
||||
* "string+number" sorts by string, unless two elements are lexicographically identical up to a numeric value (e.g. "SA-2" and "SA-3"), in which case it sorts by number.
|
||||
* @param labelsList (Optional) List of labels to be shown instead of the keys directly. If provided, the options will be sorted by label.
|
||||
*/
|
||||
setOptions(optionsList: string[], sort: "" | "string" | "number" | "string+number" = "string", labelsList: string[] | undefined = undefined) {
|
||||
/* If labels are provided, sort by labels, else by options */
|
||||
if (labelsList && labelsList.length == optionsList.length)
|
||||
this.#sortByLabels(optionsList, sort, labelsList);
|
||||
else
|
||||
this.#sortByOptions(optionsList, sort);
|
||||
|
||||
/* If no options are provided, return */
|
||||
if (this.#optionsList.length == 0) {
|
||||
optionsList = ["No options available"]
|
||||
this.#value.innerText = "No options available";
|
||||
return;
|
||||
}
|
||||
this.#options.replaceChildren(...optionsList.map((option: string, idx: number) => {
|
||||
|
||||
/* Create the buttons containing the options or the labels */
|
||||
this.#options.replaceChildren(...this.#optionsList.map((option: string, idx: number) => {
|
||||
var div = document.createElement("div");
|
||||
var button = document.createElement("button");
|
||||
button.textContent = option;
|
||||
|
||||
/* If the labels are provided use them for the options */
|
||||
if (this.#labelsList && this.#labelsList.length === optionsList.length)
|
||||
button.textContent = this.#labelsList[idx];
|
||||
else
|
||||
button.textContent = option;
|
||||
div.appendChild(button);
|
||||
|
||||
if (option === this.#defaultValue)
|
||||
@@ -91,8 +85,21 @@ export class Dropdown {
|
||||
}));
|
||||
}
|
||||
|
||||
getOptionsList() {
|
||||
return this.#optionsList;
|
||||
}
|
||||
|
||||
getLabelsList() {
|
||||
return this.#labelsList;
|
||||
}
|
||||
|
||||
/** Manually set the HTMLElements of the dropdown values. Handling of the selection must be performed externally.
|
||||
*
|
||||
* @param optionsElements List of elements to be added to the dropdown
|
||||
*/
|
||||
setOptionsElements(optionsElements: HTMLElement[]) {
|
||||
this.#optionsList = [];
|
||||
this.#labelsList = [];
|
||||
this.#options.replaceChildren(...optionsElements);
|
||||
}
|
||||
|
||||
@@ -104,19 +111,22 @@ export class Dropdown {
|
||||
this.#options.appendChild(optionElement);
|
||||
}
|
||||
|
||||
selectText(text: string) {
|
||||
const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text);
|
||||
if (index > -1) {
|
||||
this.selectValue(index);
|
||||
}
|
||||
}
|
||||
|
||||
/** Select the active value of the dropdown
|
||||
*
|
||||
* @param idx The index of the element to select
|
||||
* @returns True if the index is valid, false otherwise
|
||||
*/
|
||||
selectValue(idx: number) {
|
||||
if (idx < this.#optionsList.length) {
|
||||
var option = this.#optionsList[idx];
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-ellipsed");
|
||||
el.innerText = option;
|
||||
|
||||
if (this.#labelsList && this.#labelsList.length == this.#optionsList.length)
|
||||
el.innerText = this.#labelsList[idx];
|
||||
else
|
||||
el.innerText = option;
|
||||
|
||||
this.#value.replaceChildren();
|
||||
this.#value.appendChild(el);
|
||||
this.#index = idx;
|
||||
@@ -133,16 +143,24 @@ export class Dropdown {
|
||||
this.#value.innerText = this.#defaultValue;
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.#value.innerText;
|
||||
}
|
||||
|
||||
/** Manually set the selected value of the dropdown
|
||||
*
|
||||
* @param value The value to select. Must be one of the valid options
|
||||
*/
|
||||
setValue(value: string) {
|
||||
var index = this.#optionsList.findIndex((option) => { return option === value });
|
||||
if (index > -1)
|
||||
this.selectValue(index);
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.#value.innerText;
|
||||
}
|
||||
|
||||
/** Force the selected value of the dropdown.
|
||||
*
|
||||
* @param value Any string. Will be shown as selected value even if not one of the options.
|
||||
*/
|
||||
forceValue(value: string) {
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-ellipsed");
|
||||
@@ -205,4 +223,110 @@ export class Dropdown {
|
||||
div.append(value, options);
|
||||
return div;
|
||||
}
|
||||
|
||||
/** Sort the elements by their option keys
|
||||
*
|
||||
* @param optionsList The unsorted list of options
|
||||
* @param sort The sorting method
|
||||
*/
|
||||
#sortByOptions(optionsList: string[], sort: string) {
|
||||
if (sort === "number") {
|
||||
this.#optionsList = JSON.parse(JSON.stringify(this.#numberSort(optionsList)));
|
||||
} else if (sort === "string+number") {
|
||||
this.#optionsList = JSON.parse(JSON.stringify(this.#stringNumberSort(optionsList)));
|
||||
} else if (sort === "string") {
|
||||
this.#optionsList = JSON.parse(JSON.stringify(this.#stringSort(optionsList)));
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort the elements by their labels
|
||||
*
|
||||
* @param optionsList The unsorted list of options
|
||||
* @param sort The sorting method
|
||||
* @param labelsList The unsorted list of labels. The elements will be sorted according to these values
|
||||
*/
|
||||
#sortByLabels(optionsList: string[], sort: string, labelsList: string[]) {
|
||||
/* Create a temporary deepcopied list. This is necessary because unlike options, labels can be repeated.
|
||||
Once matched, labels are removed from the temporary array to avoid repeating the same key multiple times */
|
||||
var tempLabelsList: (string | undefined)[] = JSON.parse(JSON.stringify(labelsList));
|
||||
|
||||
if (sort === "number") {
|
||||
this.#labelsList = JSON.parse(JSON.stringify(this.#numberSort(labelsList)));
|
||||
} else if (sort === "string+number") {
|
||||
this.#labelsList = JSON.parse(JSON.stringify(this.#stringNumberSort(labelsList)));
|
||||
} else if (sort === "string") {
|
||||
this.#labelsList = JSON.parse(JSON.stringify(this.#stringSort(labelsList)));
|
||||
}
|
||||
|
||||
/* Remap the options list to match their labels */
|
||||
this.#optionsList = optionsList?.map((option: string, idx: number) => {
|
||||
let originalIdx = tempLabelsList.indexOf(this.#labelsList? this.#labelsList[idx]: "");
|
||||
/* After a match has been completed, set the label to undefined so it won't be matched again. This allows to have repeated labels */
|
||||
tempLabelsList[originalIdx] = undefined;
|
||||
return optionsList[originalIdx];
|
||||
})
|
||||
}
|
||||
|
||||
/** Sort elements by number. All elements must be parsable as numbers.
|
||||
*
|
||||
* @param elements List of strings
|
||||
* @returns Sorted list
|
||||
*/
|
||||
#numberSort(elements: string[]) {
|
||||
return elements.sort((elementA: string, elementB: string) => {
|
||||
const a = parseFloat(elementA);
|
||||
const b = parseFloat(elementB);
|
||||
if (a > b)
|
||||
return 1;
|
||||
else
|
||||
return (b > a) ? -1 : 0;
|
||||
});
|
||||
}
|
||||
|
||||
/** Sort elements by string, unless two elements are lexicographically identical up to a numeric value (e.g. "SA-2" and "SA-3"), in which case sort by number
|
||||
*
|
||||
* @param elements List of strings
|
||||
* @returns Sorted list
|
||||
*/
|
||||
#stringNumberSort(elements: string[]) {
|
||||
return elements.sort((elementA: string, elementB: string) => {
|
||||
/* Check if there is a number in both strings */
|
||||
var regex = /\d+/g;
|
||||
var matchesA = elementA.match(regex);
|
||||
var matchesB = elementB.match(regex);
|
||||
|
||||
/* Get the position of the number in the string */
|
||||
var indexA = -1;
|
||||
var indexB = -1;
|
||||
if (matchesA != null && matchesA?.length > 0)
|
||||
indexA = elementA.search(matchesA[0]);
|
||||
|
||||
if (matchesB != null && matchesB?.length > 0)
|
||||
indexB = elementB.search(matchesB[0]);
|
||||
|
||||
/* If the two strings are the same up to the number, sort them using the number value, else sort them according to the string */
|
||||
if ((matchesA != null && matchesA?.length > 0) && (matchesB != null && matchesB?.length > 0) && elementA.substring(0, indexA) === elementB.substring(0, indexB)) {
|
||||
const a = parseInt(matchesA[0] ?? 0);
|
||||
const b = parseInt(matchesB[0] ?? 0);
|
||||
if (a > b)
|
||||
return 1;
|
||||
else
|
||||
return (b > a) ? -1 : 0;
|
||||
} else {
|
||||
if (elementA > elementB)
|
||||
return 1;
|
||||
else
|
||||
return (elementB > elementA) ? -1 : 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Sort by string. Just a wrapper for consistency.
|
||||
*
|
||||
* @param elements List of strings
|
||||
* @returns Sorted list
|
||||
*/
|
||||
#stringSort(elements: string[]) {
|
||||
return elements.sort();
|
||||
}
|
||||
}
|
||||
@@ -3,17 +3,23 @@ import { Dropdown } from "./dropdown";
|
||||
import { Slider } from "./slider";
|
||||
import { UnitDatabase } from "../unit/databases/unitdatabase";
|
||||
import { getApp } from "..";
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { GAME_MASTER, GROUND_UNIT_AIR_DEFENCE_REGEX } from "../constants/constants";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { ftToM } from "../other/utils";
|
||||
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
|
||||
import { helicopterDatabase } from "../unit/databases/helicopterdatabase";
|
||||
import { groundUnitDatabase } from "../unit/databases/groundunitdatabase";
|
||||
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
|
||||
import { UnitSpawnOptions, UnitSpawnTable } from "../interfaces";
|
||||
import { UnitBlueprint, UnitSpawnOptions, UnitSpawnTable } from "../interfaces";
|
||||
|
||||
export class UnitSpawnMenu {
|
||||
/** This is the common code for all the unit spawn menus. It is shown both when right clicking on the map and when spawning from airbase.
|
||||
*
|
||||
*/
|
||||
|
||||
export abstract class UnitSpawnMenu {
|
||||
protected showRangeCircles: boolean = false;
|
||||
protected unitTypeFilter = (unit:any) => { return true; };
|
||||
/* Default options */
|
||||
protected spawnOptions: UnitSpawnOptions = {
|
||||
roleType: "",
|
||||
name: "",
|
||||
@@ -31,8 +37,9 @@ export class UnitSpawnMenu {
|
||||
#unitDatabase: UnitDatabase;
|
||||
#countryCodes: any;
|
||||
#orderByRole: boolean;
|
||||
protected unitTypeFilter = (unit:any) => { return true; };
|
||||
|
||||
#showLoadout: boolean = true;
|
||||
#showAltitudeSlider: boolean = true;
|
||||
|
||||
/* Controls */
|
||||
#unitRoleTypeDropdown: Dropdown;
|
||||
#unitLabelDropdown: Dropdown;
|
||||
@@ -44,11 +51,18 @@ export class UnitSpawnMenu {
|
||||
|
||||
/* HTML Elements */
|
||||
#deployUnitButtonEl: HTMLButtonElement;
|
||||
#unitCountDivider: HTMLDivElement;
|
||||
#unitLoadoutPreviewEl: HTMLDivElement;
|
||||
#unitImageEl: HTMLImageElement;
|
||||
#unitLoadoutListEl: HTMLDivElement;
|
||||
#descriptionDiv: HTMLDivElement;
|
||||
#abilitiesDiv: HTMLDivElement;
|
||||
#advancedOptionsDiv: HTMLDivElement;
|
||||
#unitInfoDiv: HTMLDivElement;
|
||||
#advancedOptionsToggle: HTMLDivElement;
|
||||
#advancedOptionsText: HTMLDivElement;
|
||||
#unitInfoToggle: HTMLDivElement;
|
||||
#unitInfoText: HTMLDivElement;
|
||||
|
||||
/* Range circle previews */
|
||||
#engagementCircle: Circle;
|
||||
@@ -60,20 +74,20 @@ export class UnitSpawnMenu {
|
||||
this.#orderByRole = orderByRole;
|
||||
|
||||
/* Create the dropdowns and the altitude slider */
|
||||
this.#unitRoleTypeDropdown = new Dropdown(null, (roleType: string) => this.#setUnitRoleType(roleType), undefined, "Unit type");
|
||||
this.#unitLabelDropdown = new Dropdown(null, (label: string) => this.#setUnitLabel(label), undefined, "Unit label");
|
||||
this.#unitLoadoutDropdown = new Dropdown(null, (loadout: string) => this.#setUnitLoadout(loadout), undefined, "Unit loadout");
|
||||
this.#unitCountDropdown = new Dropdown(null, (count: string) => this.#setUnitCount(count), undefined, "Unit count");
|
||||
this.#unitCountryDropdown = new Dropdown(null, () => { /* Custom button implementation */ }, undefined, "Unit country");
|
||||
this.#unitLiveryDropdown = new Dropdown(null, (livery: string) => this.#setUnitLivery(livery), undefined, "Unit livery");
|
||||
this.#unitRoleTypeDropdown = new Dropdown(null, (roleType: string) => this.#setUnitRoleType(roleType), undefined, "Role");
|
||||
this.#unitLabelDropdown = new Dropdown(null, (name: string) => this.#setUnitName(name), undefined, "Type");
|
||||
this.#unitLoadoutDropdown = new Dropdown(null, (loadout: string) => this.#setUnitLoadout(loadout), undefined, "Loadout");
|
||||
this.#unitCountDropdown = new Dropdown(null, (count: string) => this.#setUnitCount(count), undefined, "Count");
|
||||
this.#unitCountryDropdown = new Dropdown(null, () => { /* Custom button implementation */ }, undefined, "Country");
|
||||
this.#unitLiveryDropdown = new Dropdown(null, (livery: string) => this.#setUnitLivery(livery), undefined, "Livery");
|
||||
this.#unitSpawnAltitudeSlider = new Slider(null, 0, 1000, "ft", (value: number) => { this.spawnOptions.altitude = ftToM(value); }, { title: "Spawn altitude" });
|
||||
|
||||
/* The unit label and unit count are in the same "row" for clarity and compactness */
|
||||
var unitLabelCountContainerEl = document.createElement("div");
|
||||
unitLabelCountContainerEl.classList.add("unit-label-count-container");
|
||||
var divider = document.createElement("div");
|
||||
divider.innerText = "x";
|
||||
unitLabelCountContainerEl.append(this.#unitLabelDropdown.getContainer(), divider, this.#unitCountDropdown.getContainer());
|
||||
this.#unitCountDivider = document.createElement("div");
|
||||
this.#unitCountDivider.innerText = "x";
|
||||
unitLabelCountContainerEl.append(this.#unitLabelDropdown.getContainer(), this.#unitCountDivider, this.#unitCountDropdown.getContainer());
|
||||
|
||||
/* Create the unit image and loadout elements */
|
||||
this.#unitImageEl = document.createElement("img");
|
||||
@@ -84,38 +98,37 @@ export class UnitSpawnMenu {
|
||||
this.#unitLoadoutListEl.classList.add("unit-loadout-list");
|
||||
this.#unitLoadoutPreviewEl.append(this.#unitImageEl, this.#unitLoadoutListEl);
|
||||
|
||||
/* Create the divider and the advanced options collapsible div */
|
||||
var advancedOptionsDiv = document.createElement("div");
|
||||
advancedOptionsDiv.classList.add("contextmenu-advanced-options", "hide");
|
||||
var advancedOptionsToggle = document.createElement("div");
|
||||
advancedOptionsToggle.classList.add("contextmenu-advanced-options-toggle");
|
||||
var advancedOptionsText = document.createElement("div");
|
||||
advancedOptionsText.innerText = "Advanced options";
|
||||
var advancedOptionsHr = document.createElement("hr");
|
||||
advancedOptionsToggle.append(advancedOptionsText, advancedOptionsHr);
|
||||
advancedOptionsToggle.addEventListener("click", () => {
|
||||
advancedOptionsDiv.classList.toggle("hide");
|
||||
/* Create the advanced options collapsible div */
|
||||
this.#advancedOptionsDiv = document.createElement("div");
|
||||
this.#advancedOptionsDiv.classList.add("contextmenu-advanced-options", "hide");
|
||||
this.#advancedOptionsToggle = document.createElement("div");
|
||||
this.#advancedOptionsToggle.classList.add("contextmenu-advanced-options-toggle");
|
||||
this.#advancedOptionsText = document.createElement("div");
|
||||
this.#advancedOptionsText.innerText = "Faction / Liveries";
|
||||
this.#advancedOptionsToggle.append(this.#advancedOptionsText);
|
||||
this.#advancedOptionsToggle.addEventListener("click", () => {
|
||||
this.#advancedOptionsToggle.classList.toggle("is-open");
|
||||
this.#advancedOptionsDiv.classList.toggle("hide");
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
});
|
||||
advancedOptionsDiv.append(this.#unitCountryDropdown.getContainer(), this.#unitLiveryDropdown.getContainer(),
|
||||
this.#unitSpawnAltitudeSlider.getContainer() as HTMLElement);
|
||||
this.#advancedOptionsDiv.append(this.#unitCountryDropdown.getContainer(), this.#unitLiveryDropdown.getContainer());
|
||||
|
||||
/* Create the divider and the metadata collapsible div */
|
||||
var metadataDiv = document.createElement("div");
|
||||
metadataDiv.classList.add("contextmenu-metadata", "hide");
|
||||
var metadataToggle = document.createElement("div");
|
||||
metadataToggle.classList.add("contextmenu-metadata-toggle");
|
||||
var metadataText = document.createElement("div");
|
||||
metadataText.innerText = "Info";
|
||||
var metadataHr = document.createElement("hr");
|
||||
metadataToggle.append(metadataText, metadataHr);
|
||||
metadataToggle.addEventListener("click", () => {
|
||||
metadataDiv.classList.toggle("hide");
|
||||
/* Create the unit info collapsible div */
|
||||
this.#unitInfoDiv = document.createElement("div");
|
||||
this.#unitInfoDiv.classList.add("contextmenu-metadata", "hide");
|
||||
this.#unitInfoToggle = document.createElement("div");
|
||||
this.#unitInfoToggle.classList.add("contextmenu-metadata-toggle");
|
||||
this.#unitInfoText = document.createElement("div");
|
||||
this.#unitInfoText.innerText = "Unit information";
|
||||
this.#unitInfoToggle.append(this.#unitInfoText);
|
||||
this.#unitInfoToggle.addEventListener("click", () => {
|
||||
this.#unitInfoToggle.classList.toggle("is-open");
|
||||
this.#unitInfoDiv.classList.toggle("hide");
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
});
|
||||
this.#descriptionDiv = document.createElement("div");
|
||||
this.#abilitiesDiv = document.createElement("div");
|
||||
metadataDiv.append(this.#descriptionDiv, this.#abilitiesDiv);
|
||||
this.#unitInfoDiv.append(this.#descriptionDiv, this.#abilitiesDiv);
|
||||
|
||||
/* Create the unit deploy button */
|
||||
this.#deployUnitButtonEl = document.createElement("button");
|
||||
@@ -128,8 +141,8 @@ export class UnitSpawnMenu {
|
||||
});
|
||||
|
||||
/* Assemble all components */
|
||||
this.#container.append(this.#unitRoleTypeDropdown.getContainer(), unitLabelCountContainerEl, this.#unitLoadoutDropdown.getContainer(),
|
||||
this.#unitLoadoutPreviewEl, advancedOptionsToggle, advancedOptionsDiv, metadataToggle, metadataDiv, this.#deployUnitButtonEl);
|
||||
this.#container.append(this.#unitRoleTypeDropdown.getContainer(), unitLabelCountContainerEl, this.#unitLoadoutDropdown.getContainer(), this.#unitSpawnAltitudeSlider.getContainer() as HTMLElement,
|
||||
this.#unitLoadoutPreviewEl, this.#advancedOptionsToggle, this.#advancedOptionsDiv, this.#unitInfoToggle, this.#unitInfoDiv, this.#deployUnitButtonEl);
|
||||
|
||||
/* Load the country codes from the public folder */
|
||||
var xhr = new XMLHttpRequest();
|
||||
@@ -144,8 +157,29 @@ export class UnitSpawnMenu {
|
||||
};
|
||||
xhr.send();
|
||||
|
||||
/* Create the range circle previews */
|
||||
this.#engagementCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 4, opacity: 0.8, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
|
||||
this.#acquisitionCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 2, opacity: 0.8, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
|
||||
|
||||
/* Event listeners */
|
||||
this.#container.addEventListener("unitRoleTypeChanged", () => {
|
||||
/* Shown the unit label and the unit count dropdowns */
|
||||
this.#unitLabelDropdown.show();
|
||||
this.#unitCountDivider.classList.remove("hide");
|
||||
this.#unitCountDropdown.show();
|
||||
|
||||
/* Hide all the other components */
|
||||
this.#unitLoadoutDropdown.hide();
|
||||
this.#unitSpawnAltitudeSlider.hide();
|
||||
this.#unitLoadoutPreviewEl.classList.add("hide");
|
||||
this.#advancedOptionsDiv.classList.add("hide");
|
||||
this.#unitInfoDiv.classList.add("hide");
|
||||
this.#advancedOptionsText.classList.add("hide");
|
||||
this.#advancedOptionsToggle.classList.add("hide");
|
||||
this.#unitInfoText.classList.add("hide");
|
||||
this.#unitInfoToggle.classList.add("hide");
|
||||
|
||||
/* Disable the spawn button */
|
||||
this.#deployUnitButtonEl.disabled = true;
|
||||
this.#unitLabelDropdown.reset();
|
||||
this.#unitLoadoutListEl.replaceChildren();
|
||||
@@ -153,17 +187,30 @@ export class UnitSpawnMenu {
|
||||
this.#unitImageEl.classList.toggle("hide", true);
|
||||
this.#unitLiveryDropdown.reset();
|
||||
|
||||
/* Populate the labels dropdown from the database */
|
||||
var blueprints: UnitBlueprint[] = [];
|
||||
if (this.#orderByRole)
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByRole(this.spawnOptions.roleType).map((blueprint) => { return blueprint.label }), "string+number");
|
||||
blueprints = this.#unitDatabase.getByRole(this.spawnOptions.roleType);
|
||||
else
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByType(this.spawnOptions.roleType).map((blueprint) => { return blueprint.label }), "string+number");
|
||||
blueprints = this.#unitDatabase.getByType(this.spawnOptions.roleType);
|
||||
|
||||
/* Presort the elements by name in case any have equal labels */
|
||||
blueprints = blueprints.sort((blueprintA: UnitBlueprint, blueprintB: UnitBlueprint) => {
|
||||
if (blueprintA.name > blueprintA.name)
|
||||
return 1;
|
||||
else
|
||||
return (blueprintB.name > blueprintA.name) ? -1 : 0;
|
||||
});
|
||||
|
||||
this.#unitLabelDropdown.setOptions(blueprints.map((blueprint) => { return blueprint.name }), "string+number", blueprints.map((blueprint) => { return blueprint.label }));
|
||||
|
||||
/* Add the tags to the options */
|
||||
var elements: HTMLElement[] = [];
|
||||
for (let idx = 0; idx < this.#unitLabelDropdown.getOptionElements().length; idx++) {
|
||||
let name = this.#unitLabelDropdown.getOptionsList()[idx];
|
||||
let element = this.#unitLabelDropdown.getOptionElements()[idx] as HTMLElement;
|
||||
let entry = this.#unitDatabase.getByLabel(element.textContent ?? "");
|
||||
if (entry) {
|
||||
let entry = this.#unitDatabase.getByName(name);
|
||||
if (entry && entry.tags?.trim() !== "") {
|
||||
element.querySelectorAll("button")[0]?.append(...(entry.tags?.split(",").map((tag: string) => {
|
||||
tag = tag.trim();
|
||||
let el = document.createElement("div");
|
||||
@@ -176,8 +223,10 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/* Request resizing */
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
|
||||
/* Reset the spawn options */
|
||||
this.spawnOptions.name = "";
|
||||
this.spawnOptions.loadout = undefined;
|
||||
this.spawnOptions.liveryID = undefined;
|
||||
@@ -186,23 +235,43 @@ export class UnitSpawnMenu {
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitLabelChanged", () => {
|
||||
/* If enabled, show the altitude slideer and loadouts section */
|
||||
if (this.#showAltitudeSlider)
|
||||
this.#unitSpawnAltitudeSlider.show();
|
||||
|
||||
if (this.#showLoadout) {
|
||||
this.#unitLoadoutDropdown.show();
|
||||
this.#unitLoadoutPreviewEl.classList.remove("hide");
|
||||
}
|
||||
|
||||
/* Show the advanced options and unit info sections */
|
||||
this.#advancedOptionsText.classList.remove("hide");
|
||||
this.#advancedOptionsToggle.classList.remove("hide");
|
||||
this.#advancedOptionsToggle.classList.remove("is-open");
|
||||
this.#unitInfoText.classList.remove("hide");
|
||||
this.#unitInfoToggle.classList.remove("hide");
|
||||
this.#unitInfoToggle.classList.remove("is-open");
|
||||
|
||||
/* Enable the spawn button */
|
||||
this.#deployUnitButtonEl.disabled = false;
|
||||
|
||||
/* If enabled, populate the loadout dropdown */
|
||||
if (!this.#unitLoadoutDropdown.isHidden()) {
|
||||
this.#unitLoadoutDropdown.setOptions(this.#unitDatabase.getLoadoutNamesByRole(this.spawnOptions.name, this.spawnOptions.roleType));
|
||||
this.#unitLoadoutDropdown.selectValue(0);
|
||||
}
|
||||
|
||||
/* Get the unit data from the db */
|
||||
var blueprint = this.#unitDatabase.getByName(this.spawnOptions.name);
|
||||
|
||||
|
||||
/* Shown the unit silhouette */
|
||||
this.#unitImageEl.src = `images/units/${blueprint?.filename}`;
|
||||
this.#unitImageEl.classList.toggle("hide", !(blueprint?.filename !== undefined));
|
||||
this.#unitImageEl.classList.toggle("hide", !(blueprint?.filename !== undefined && blueprint?.filename !== ''));
|
||||
|
||||
/* Set the livery options */
|
||||
this.#setUnitLiveryOptions();
|
||||
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#computeSpawnPoints();
|
||||
|
||||
/* Populate the description and abilities sections */
|
||||
this.#descriptionDiv.replaceChildren();
|
||||
this.#abilitiesDiv.replaceChildren();
|
||||
|
||||
@@ -223,10 +292,16 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/* Show the range circles */
|
||||
this.showCirclesPreviews();
|
||||
|
||||
/* Request resizing */
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#computeSpawnPoints();
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitLoadoutChanged", () => {
|
||||
/* Update the loadout information */
|
||||
var items = this.spawnOptions.loadout?.items.map((item: any) => { return `${item.quantity}x ${item.name}`; });
|
||||
if (items != undefined) {
|
||||
items.length == 0 ? items.push("Empty loadout") : "";
|
||||
@@ -243,10 +318,12 @@ export class UnitSpawnMenu {
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitCountChanged", () => {
|
||||
/* Recompute the spawn points */
|
||||
this.#computeSpawnPoints();
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitCountryChanged", () => {
|
||||
/* Get the unit liveries by country */
|
||||
this.#setUnitLiveryOptions();
|
||||
})
|
||||
|
||||
@@ -255,13 +332,13 @@ export class UnitSpawnMenu {
|
||||
})
|
||||
|
||||
document.addEventListener('activeCoalitionChanged', () => {
|
||||
/* If the coalition changed, update the circle previews to set the colours */
|
||||
this.showCirclesPreviews();
|
||||
});
|
||||
|
||||
this.#engagementCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 4, opacity: 0.8, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
|
||||
this.#acquisitionCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 2, opacity: 0.8, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
|
||||
}
|
||||
|
||||
abstract deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number): void;
|
||||
|
||||
getContainer() {
|
||||
return this.#container;
|
||||
}
|
||||
@@ -271,7 +348,10 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
|
||||
reset() {
|
||||
/* Disable the spawn button */
|
||||
this.#deployUnitButtonEl.disabled = true;
|
||||
|
||||
/* Reset all the dropdowns */
|
||||
this.#unitRoleTypeDropdown.reset();
|
||||
this.#unitLabelDropdown.reset();
|
||||
this.#unitLiveryDropdown.reset();
|
||||
@@ -280,19 +360,37 @@ export class UnitSpawnMenu {
|
||||
else
|
||||
this.#unitRoleTypeDropdown.setOptions(this.#unitDatabase.getTypes(this.unitTypeFilter));
|
||||
|
||||
/* Reset the contents of the div elements */
|
||||
this.#unitLoadoutListEl.replaceChildren();
|
||||
this.#unitLoadoutDropdown.reset();
|
||||
this.#unitImageEl.classList.toggle("hide", true);
|
||||
this.#descriptionDiv.replaceChildren();
|
||||
this.#abilitiesDiv.replaceChildren();
|
||||
|
||||
this.setCountries();
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
/* Hide everything but the unit type dropdown */
|
||||
this.#unitLabelDropdown.hide();
|
||||
this.#unitCountDivider.classList.add("hide");
|
||||
this.#unitCountDropdown.hide();
|
||||
this.#unitLoadoutDropdown.hide();
|
||||
this.#unitSpawnAltitudeSlider.hide();
|
||||
this.#unitLoadoutPreviewEl.classList.add("hide");
|
||||
this.#advancedOptionsDiv.classList.add("hide");
|
||||
this.#unitInfoDiv.classList.add("hide");
|
||||
this.#advancedOptionsText.classList.add("hide");
|
||||
this.#advancedOptionsToggle.classList.add("hide");
|
||||
this.#unitInfoText.classList.add("hide");
|
||||
this.#unitInfoToggle.classList.add("hide");
|
||||
|
||||
/* Get the countries and clear the circle previews */
|
||||
this.setCountries();
|
||||
this.clearCirclesPreviews();
|
||||
|
||||
/* Request resizing */
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
}
|
||||
|
||||
setCountries() {
|
||||
/* Create the countries dropdown elements (with the little flags) */
|
||||
var coalitions = getApp().getMissionManager().getCoalitions();
|
||||
var countries = Object.values(coalitions[getApp().getActiveCoalition() as keyof typeof coalitions]);
|
||||
this.#unitCountryDropdown.setOptionsElements(this.#createCountryButtons(this.#unitCountryDropdown, countries, (country: string) => { this.#setUnitCountry(country) }));
|
||||
@@ -303,13 +401,6 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
refreshOptions() {
|
||||
//if (!this.#unitDatabase.getTypes().includes(this.#unitTypeDropdown.getValue()))
|
||||
// this.reset();
|
||||
//if (!this.#unitDatabase.getByType(this.#unitTypeDropdown.getValue()).map((blueprint) => { return blueprint.label }).includes(this.#unitLabelDropdown.getValue()))
|
||||
// this.resetUnitLabel();
|
||||
}
|
||||
|
||||
showCirclesPreviews() {
|
||||
this.clearCirclesPreviews();
|
||||
|
||||
@@ -413,13 +504,20 @@ export class UnitSpawnMenu {
|
||||
return this.#unitSpawnAltitudeSlider;
|
||||
}
|
||||
|
||||
setShowLoadout(showLoadout: boolean) {
|
||||
this.#showLoadout = showLoadout;
|
||||
}
|
||||
|
||||
setShowAltitudeSlider(showAltitudeSlider: boolean) {
|
||||
this.#showAltitudeSlider = showAltitudeSlider;
|
||||
}
|
||||
|
||||
#setUnitRoleType(roleType: string) {
|
||||
this.spawnOptions.roleType = roleType;
|
||||
this.#container.dispatchEvent(new Event("unitRoleTypeChanged"));
|
||||
}
|
||||
|
||||
#setUnitLabel(label: string) {
|
||||
var name = this.#unitDatabase.getByLabel(label)?.name || null;
|
||||
#setUnitName(name: string) {
|
||||
if (name != null)
|
||||
this.spawnOptions.name = name;
|
||||
this.#container.dispatchEvent(new Event("unitLabelChanged"));
|
||||
@@ -471,10 +569,6 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
|
||||
/* Virtual function must be overloaded by inheriting classes */
|
||||
}
|
||||
|
||||
#createCountryButtons(parent: Dropdown, countries: string[], callback: CallableFunction) {
|
||||
return Object.values(countries).map((country: string) => {
|
||||
var el = document.createElement("div");
|
||||
@@ -603,9 +697,8 @@ export class HelicopterSpawnMenu extends UnitSpawnMenu {
|
||||
}
|
||||
|
||||
export class GroundUnitSpawnMenu extends UnitSpawnMenu {
|
||||
|
||||
protected showRangeCircles: boolean = true;
|
||||
protected unitTypeFilter = (unit:any) => {return !(/\bAAA|SAM\b/.test(unit.type) || /\bmanpad|stinger\b/i.test(unit.type))};
|
||||
protected unitTypeFilter = (unit:any) => {return !(GROUND_UNIT_AIR_DEFENCE_REGEX.test(unit.type))};
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -614,8 +707,8 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu {
|
||||
constructor(ID: string){
|
||||
super(ID, groundUnitDatabase, false);
|
||||
this.setMaxUnitCount(20);
|
||||
this.getAltitudeSlider().hide();
|
||||
this.getLoadoutDropdown().hide();
|
||||
this.setShowAltitudeSlider(false);
|
||||
this.setShowLoadout(false);
|
||||
this.getLoadoutPreview().classList.add("hide");
|
||||
}
|
||||
|
||||
@@ -645,8 +738,7 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu {
|
||||
}
|
||||
|
||||
export class AirDefenceUnitSpawnMenu extends GroundUnitSpawnMenu {
|
||||
|
||||
protected unitTypeFilter = (unit:any) => {return /\bAAA|SAM\b/.test(unit.type) || /\bmanpad|stinger\b/i.test(unit.type)};
|
||||
protected unitTypeFilter = (unit:any) => {return GROUND_UNIT_AIR_DEFENCE_REGEX.test(unit.type)};
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -666,9 +758,8 @@ export class NavyUnitSpawnMenu extends UnitSpawnMenu {
|
||||
constructor(ID: string){
|
||||
super(ID, navyUnitDatabase, false);
|
||||
this.setMaxUnitCount(4);
|
||||
this.getAltitudeSlider().hide();
|
||||
this.getLoadoutDropdown().hide();
|
||||
this.getLoadoutPreview().classList.add("hide");
|
||||
this.setShowAltitudeSlider(false);
|
||||
this.setShowLoadout(false);
|
||||
}
|
||||
|
||||
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
|
||||
|
||||
2
client/src/dom.d.ts
vendored
@@ -18,7 +18,7 @@ interface CustomEventMap {
|
||||
"groupDeletion": CustomEvent<Unit[]>,
|
||||
"mapStateChanged": CustomEvent<string>,
|
||||
"mapContextMenu": CustomEvent<>,
|
||||
"mapVisibilityOptionsChanged": CustomEvent<>,
|
||||
"mapOptionsChanged": CustomEvent<>,
|
||||
"commandModeOptionsChanged": CustomEvent<>,
|
||||
"contactsUpdated": CustomEvent<Unit>,
|
||||
"activeCoalitionChanged": CustomEvent<>
|
||||
|
||||
@@ -139,6 +139,7 @@ export interface Offset {
|
||||
|
||||
export interface UnitData {
|
||||
category: string,
|
||||
categoryDisplayName: string,
|
||||
ID: number;
|
||||
alive: boolean;
|
||||
human: boolean;
|
||||
@@ -156,6 +157,7 @@ export interface UnitData {
|
||||
horizontalVelocity: number;
|
||||
verticalVelocity: number;
|
||||
heading: number;
|
||||
track: number;
|
||||
isActiveTanker: boolean;
|
||||
isActiveAWACS: boolean;
|
||||
onOff: boolean;
|
||||
|
||||
@@ -7,13 +7,12 @@ import { AirbaseContextMenu } from "../contextmenus/airbasecontextmenu";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { bearing, createCheckboxOption } from "../other/utils";
|
||||
import { bearing, createCheckboxOption, polyContains } from "../other/utils";
|
||||
import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
|
||||
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
|
||||
import { ClickableMiniMap } from "./clickableminimap";
|
||||
import { SVGInjector } from '@tanem/svg-injector'
|
||||
import { mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, MOVE_UNIT, SHOW_UNIT_CONTACTS, HIDE_GROUP_MEMBERS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, SHOW_UNIT_LABELS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, MAP_MARKER_CONTROLS } from "../constants/constants";
|
||||
import { TargetMarker } from "./markers/targetmarker";
|
||||
import { CoalitionArea } from "./coalitionarea/coalitionarea";
|
||||
import { CoalitionAreaContextMenu } from "../contextmenus/coalitionareacontextmenu";
|
||||
import { DrawingCursor } from "./coalitionarea/drawingcursor";
|
||||
@@ -27,9 +26,9 @@ var hasTouchScreen = false;
|
||||
//if ("maxTouchPoints" in navigator)
|
||||
// hasTouchScreen = navigator.maxTouchPoints > 0;
|
||||
|
||||
if (hasTouchScreen)
|
||||
if (hasTouchScreen)
|
||||
L.Map.addInitHook('addHandler', 'boxSelect', TouchBoxSelect);
|
||||
else
|
||||
else
|
||||
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
||||
|
||||
L.Map.addInitHook("addHandler", "gestureHandling", GestureHandling);
|
||||
@@ -38,10 +37,10 @@ L.Map.addInitHook("addHandler", "gestureHandling", GestureHandling);
|
||||
require("../../public/javascripts/leaflet.nauticscale.js")
|
||||
require("../../public/javascripts/L.Path.Drag.js")
|
||||
|
||||
export type MapMarkerControl = {
|
||||
export type MapMarkerVisibilityControl = {
|
||||
"image": string;
|
||||
"isProtected"?: boolean,
|
||||
"name":string,
|
||||
"name": string,
|
||||
"protectable"?: boolean,
|
||||
"toggles": string[],
|
||||
"tooltip": string
|
||||
@@ -75,7 +74,6 @@ export class Map extends L.Map {
|
||||
#destinationRotationCenter: L.LatLng | null = null;
|
||||
#coalitionAreas: CoalitionArea[] = [];
|
||||
|
||||
#targetCursor: TargetMarker = new TargetMarker(new L.LatLng(0, 0), { interactive: false });
|
||||
#destinationPreviewCursors: DestinationPreviewMarker[] = [];
|
||||
#drawingCursor: DrawingCursor = new DrawingCursor();
|
||||
#destinationPreviewHandle: DestinationPreviewHandle = new DestinationPreviewHandle(new L.LatLng(0, 0));
|
||||
@@ -90,7 +88,7 @@ export class Map extends L.Map {
|
||||
#coalitionAreaContextMenu: CoalitionAreaContextMenu = new CoalitionAreaContextMenu("coalition-area-contextmenu");
|
||||
|
||||
#mapSourceDropdown: Dropdown;
|
||||
#mapMarkerControls:MapMarkerControl[] = MAP_MARKER_CONTROLS;
|
||||
#mapMarkerVisibilityControls: MapMarkerVisibilityControl[] = MAP_MARKER_CONTROLS;
|
||||
#mapVisibilityOptionsDropdown: Dropdown;
|
||||
#optionButtons: { [key: string]: HTMLButtonElement[] } = {}
|
||||
#visibilityOptions: { [key: string]: boolean } = {}
|
||||
@@ -100,21 +98,21 @@ export class Map extends L.Map {
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
constructor(ID: string) {
|
||||
/* Init the leaflet map */
|
||||
super(ID, {
|
||||
preferCanvas: true,
|
||||
doubleClickZoom: false,
|
||||
zoomControl: false,
|
||||
boxZoom: false,
|
||||
super(ID, {
|
||||
preferCanvas: true,
|
||||
doubleClickZoom: false,
|
||||
zoomControl: false,
|
||||
boxZoom: false,
|
||||
//@ts-ignore Needed because the boxSelect option is non-standard
|
||||
boxSelect: true,
|
||||
zoomAnimation: true,
|
||||
boxSelect: true,
|
||||
zoomAnimation: true,
|
||||
maxBoundsViscosity: 1.0,
|
||||
minZoom: 7,
|
||||
minZoom: 7,
|
||||
keyboard: true,
|
||||
keyboardPanDelta: 0,
|
||||
gestureHandling: hasTouchScreen
|
||||
gestureHandling: hasTouchScreen
|
||||
});
|
||||
this.setView([37.23, -115.8], 10);
|
||||
|
||||
@@ -198,15 +196,15 @@ export class Map extends L.Map {
|
||||
this.#panToUnit(this.#centerUnit);
|
||||
});
|
||||
|
||||
document.addEventListener("mapVisibilityOptionsChanged", () => {
|
||||
document.addEventListener("mapOptionsChanged", () => {
|
||||
this.getContainer().toggleAttribute("data-hide-labels", !this.getVisibilityOptions()[SHOW_UNIT_LABELS]);
|
||||
});
|
||||
|
||||
/* Pan interval */
|
||||
this.#panInterval = window.setInterval(() => {
|
||||
if (this.#panUp || this.#panDown || this.#panRight || this.#panLeft)
|
||||
this.panBy(new L.Point(((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) * this.#deafultPanDelta,
|
||||
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) * this.#deafultPanDelta));
|
||||
this.panBy(new L.Point(((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) * this.#deafultPanDelta * (this.#shiftKey ? 3 : 1),
|
||||
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) * this.#deafultPanDelta * (this.#shiftKey ? 3 : 1)));
|
||||
}, 20);
|
||||
|
||||
/* Option buttons */
|
||||
@@ -257,7 +255,7 @@ export class Map extends L.Map {
|
||||
|
||||
/* Operations to perform if you are NOT in a state */
|
||||
if (this.#state !== COALITIONAREA_DRAW_POLYGON) {
|
||||
this.#deselectCoalitionAreas();
|
||||
this.#deselectSelectedCoalitionArea();
|
||||
}
|
||||
|
||||
/* Operations to perform if you ARE in a state */
|
||||
@@ -291,7 +289,6 @@ export class Map extends L.Map {
|
||||
else {
|
||||
this.#hiddenTypes.push(key);
|
||||
}
|
||||
Object.values(getApp().getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
||||
}
|
||||
|
||||
getHiddenTypes() {
|
||||
@@ -377,11 +374,6 @@ export class Map extends L.Map {
|
||||
this.#coalitionAreaContextMenu.hide();
|
||||
}
|
||||
|
||||
isZooming() {
|
||||
return this.#isZooming;
|
||||
}
|
||||
|
||||
/* Mouse coordinates */
|
||||
getMousePosition() {
|
||||
return this.#lastMousePosition;
|
||||
}
|
||||
@@ -390,11 +382,6 @@ export class Map extends L.Map {
|
||||
return this.containerPointToLatLng(this.#lastMousePosition);
|
||||
}
|
||||
|
||||
/* Spawn from air base */
|
||||
spawnFromAirbase(e: any) {
|
||||
//this.#aircraftSpawnMenu(e);
|
||||
}
|
||||
|
||||
centerOnUnit(ID: number | null) {
|
||||
if (ID != null) {
|
||||
this.options.scrollWheelZoom = 'center';
|
||||
@@ -407,7 +394,7 @@ export class Map extends L.Map {
|
||||
this.#updateCursor();
|
||||
}
|
||||
|
||||
getCenterUnit() {
|
||||
getCenteredOnUnit() {
|
||||
return this.#centerUnit;
|
||||
}
|
||||
|
||||
@@ -420,7 +407,6 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
this.setView(bounds.getCenter(), 8);
|
||||
//this.setMaxBounds(bounds);
|
||||
|
||||
if (this.#miniMap)
|
||||
this.#miniMap.remove();
|
||||
@@ -502,10 +488,35 @@ export class Map extends L.Map {
|
||||
return this.#visibilityOptions;
|
||||
}
|
||||
|
||||
isZooming() {
|
||||
return this.#isZooming;
|
||||
}
|
||||
|
||||
getPreviousZoom() {
|
||||
return this.#previousZoom;
|
||||
}
|
||||
|
||||
getIsUnitProtected(unit: Unit) {
|
||||
const toggles = this.#mapMarkerVisibilityControls.reduce((list, control: MapMarkerVisibilityControl) => {
|
||||
if (control.isProtected) {
|
||||
list = list.concat(control.toggles);
|
||||
}
|
||||
return list;
|
||||
}, [] as string[]);
|
||||
|
||||
if (toggles.length === 0)
|
||||
return false;
|
||||
|
||||
return toggles.some((toggle: string) => {
|
||||
// Specific coding for robots - extend later if needed
|
||||
return (toggle === "dcs" && !unit.getControlled() && !unit.getHuman());
|
||||
});
|
||||
}
|
||||
|
||||
getMapMarkerVisibilityControls() {
|
||||
return this.#mapMarkerVisibilityControls;
|
||||
}
|
||||
|
||||
/* Event handlers */
|
||||
#onClick(e: any) {
|
||||
if (!this.#preventLeftClick) {
|
||||
@@ -548,7 +559,7 @@ export class Map extends L.Map {
|
||||
|
||||
/* 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 (polyContains(e.latlng, coalitionArea)) {
|
||||
if (coalitionArea.getSelected())
|
||||
clickedCoalitionArea = coalitionArea;
|
||||
else
|
||||
@@ -560,19 +571,20 @@ export class Map extends L.Map {
|
||||
}
|
||||
}
|
||||
else if (this.#state === MOVE_UNIT) {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getApp().getUnitsManager().clearDestinations();
|
||||
if (!e.originalEvent.shiftKey) {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getApp().getUnitsManager().clearDestinations();
|
||||
}
|
||||
getApp().getUnitsManager().addDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
||||
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
}
|
||||
getApp().getUnitsManager().addDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
||||
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
}
|
||||
else {
|
||||
this.setState(IDLE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#onSelectionStart(e: any) {
|
||||
@@ -616,7 +628,7 @@ export class Map extends L.Map {
|
||||
units.forEach((unit: Unit) => {
|
||||
unit.appendContextActions(contextActionSet, null, e.latlng);
|
||||
})
|
||||
|
||||
|
||||
if (Object.keys(contextActionSet.getContextActions()).length > 0) {
|
||||
getApp().getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||
getApp().getMap().getUnitContextMenu().setContextActions(contextActionSet);
|
||||
@@ -626,6 +638,16 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
#onMouseUp(e: any) {
|
||||
if (this.#state === MOVE_UNIT && e.originalEvent.button == 2 && e.originalEvent.shiftKey) {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getApp().getUnitsManager().clearDestinations();
|
||||
}
|
||||
getApp().getUnitsManager().addDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
||||
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
}
|
||||
}
|
||||
|
||||
#onMouseMove(e: any) {
|
||||
@@ -640,7 +662,7 @@ export class Map extends L.Map {
|
||||
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
|
||||
this.#updateDestinationCursors();
|
||||
}
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON && e.latlng !== undefined) {
|
||||
this.#drawingCursor.setLatLng(e.latlng);
|
||||
/* Update the polygon being drawn with the current position of the mouse cursor */
|
||||
this.getSelectedCoalitionArea()?.moveActiveVertex(e.latlng);
|
||||
@@ -677,6 +699,7 @@ export class Map extends L.Map {
|
||||
this.#isZooming = false;
|
||||
}
|
||||
|
||||
/* */
|
||||
#panToUnit(unit: Unit) {
|
||||
var unitPosition = new L.LatLng(unit.getPosition().lat, unit.getPosition().lng);
|
||||
this.setView(unitPosition, this.getZoom(), { animate: false });
|
||||
@@ -691,10 +714,10 @@ export class Map extends L.Map {
|
||||
|
||||
#createUnitMarkerControlButtons() {
|
||||
const unitVisibilityControls = <HTMLElement>document.getElementById("unit-visibility-control");
|
||||
const makeTitle = (isProtected:boolean) => {
|
||||
return ( isProtected ) ? "Unit type is protected and will ignore orders" : "Unit is NOT protected and will respond to orders";
|
||||
const makeTitle = (isProtected: boolean) => {
|
||||
return (isProtected) ? "Unit type is protected and will ignore orders" : "Unit is NOT protected and will respond to orders";
|
||||
}
|
||||
this.getMapMarkerControls().forEach( (control:MapMarkerControl) => {
|
||||
this.getMapMarkerVisibilityControls().forEach((control: MapMarkerVisibilityControl) => {
|
||||
const toggles = `["${control.toggles.join('","')}"]`;
|
||||
const div = document.createElement("div");
|
||||
div.className = control.protectable === true ? "protectable" : "";
|
||||
@@ -707,7 +730,7 @@ export class Map extends L.Map {
|
||||
`;
|
||||
unitVisibilityControls.appendChild(div);
|
||||
|
||||
if ( control.protectable ) {
|
||||
if (control.protectable) {
|
||||
div.innerHTML += `
|
||||
<button class="lock" ${control.isProtected ? "data-protected" : ""} title="${makeTitle(control.isProtected || false)}">
|
||||
<img src="/resources/theme/images/buttons/other/lock-solid.svg" class="locked" />
|
||||
@@ -715,10 +738,10 @@ export class Map extends L.Map {
|
||||
</button>`;
|
||||
|
||||
const btn = <HTMLButtonElement>div.querySelector("button.lock");
|
||||
btn.addEventListener("click", (ev:MouseEventInit) => {
|
||||
btn.addEventListener("click", (ev: MouseEventInit) => {
|
||||
control.isProtected = !control.isProtected;
|
||||
btn.toggleAttribute("data-protected", control.isProtected);
|
||||
btn.title = makeTitle( control.isProtected );
|
||||
btn.title = makeTitle(control.isProtected);
|
||||
document.dispatchEvent(new CustomEvent("toggleMarkerProtection", {
|
||||
detail: {
|
||||
"_element": btn,
|
||||
@@ -732,28 +755,32 @@ export class Map extends L.Map {
|
||||
unitVisibilityControls.querySelectorAll(`img[src$=".svg"]`).forEach(img => SVGInjector(img));
|
||||
}
|
||||
|
||||
unitIsProtected(unit:Unit) {
|
||||
const toggles = this.#mapMarkerControls.reduce((list, control:MapMarkerControl) => {
|
||||
if (control.isProtected) {
|
||||
list = list.concat(control.toggles);
|
||||
}
|
||||
return list;
|
||||
}, [] as string[]);
|
||||
|
||||
if (toggles.length === 0)
|
||||
return false;
|
||||
|
||||
return toggles.some((toggle:string) => {
|
||||
// Specific coding for robots - extend later if needed
|
||||
return (toggle === "dcs" && !unit.getControlled() && !unit.getHuman());
|
||||
});
|
||||
}
|
||||
|
||||
#deselectCoalitionAreas() {
|
||||
#deselectSelectedCoalitionArea() {
|
||||
this.getSelectedCoalitionArea()?.setSelected(false);
|
||||
}
|
||||
|
||||
/* Cursors */
|
||||
#updateCursor() {
|
||||
/* If the ctrl key is being pressed or we are performing an area selection, show the default cursor */
|
||||
if (this.#ctrlKey || this.#selecting) {
|
||||
/* Hide all non default cursors */
|
||||
this.#hideDestinationCursors();
|
||||
this.#hideDrawingCursor();
|
||||
|
||||
this.#showDefaultCursor();
|
||||
} else {
|
||||
/* Hide all the unnecessary cursors depending on the active state */
|
||||
if (this.#state !== IDLE) this.#hideDefaultCursor();
|
||||
if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors();
|
||||
if (this.#state !== COALITIONAREA_DRAW_POLYGON) this.#hideDrawingCursor();
|
||||
|
||||
/* Show the active cursor depending on the active state */
|
||||
if (this.#state === IDLE) this.#showDefaultCursor();
|
||||
else if (this.#state === MOVE_UNIT) this.#showDestinationCursors();
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor();
|
||||
}
|
||||
}
|
||||
|
||||
#showDefaultCursor() {
|
||||
document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
|
||||
}
|
||||
@@ -766,48 +793,47 @@ export class Map extends L.Map {
|
||||
const singleCursor = !this.#shiftKey;
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length;
|
||||
if (singleCursor) {
|
||||
if ( this.#destinationPreviewCursors.length != 1) {
|
||||
this.#hideDestinationCursors();
|
||||
var marker = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
||||
marker.addTo(this);
|
||||
this.#destinationPreviewCursors = [marker];
|
||||
}
|
||||
|
||||
this.#destinationPreviewHandleLine.removeFrom(this);
|
||||
this.#destinationPreviewHandle.removeFrom(this);
|
||||
this.#hideDestinationCursors();
|
||||
}
|
||||
else if (!singleCursor) {
|
||||
while (this.#destinationPreviewCursors.length > selectedUnitsCount) {
|
||||
this.removeLayer(this.#destinationPreviewCursors[0]);
|
||||
this.#destinationPreviewCursors.splice(0, 1);
|
||||
if (selectedUnitsCount > 1) {
|
||||
while (this.#destinationPreviewCursors.length > selectedUnitsCount) {
|
||||
this.removeLayer(this.#destinationPreviewCursors[0]);
|
||||
this.#destinationPreviewCursors.splice(0, 1);
|
||||
}
|
||||
|
||||
this.#destinationPreviewHandleLine.addTo(this);
|
||||
this.#destinationPreviewHandle.addTo(this);
|
||||
|
||||
while (this.#destinationPreviewCursors.length < selectedUnitsCount) {
|
||||
var cursor = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
||||
cursor.addTo(this);
|
||||
this.#destinationPreviewCursors.push(cursor);
|
||||
}
|
||||
|
||||
this.#updateDestinationCursors();
|
||||
}
|
||||
|
||||
this.#destinationPreviewHandleLine.addTo(this);
|
||||
this.#destinationPreviewHandle.addTo(this);
|
||||
|
||||
while (this.#destinationPreviewCursors.length < selectedUnitsCount) {
|
||||
var cursor = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
||||
cursor.addTo(this);
|
||||
this.#destinationPreviewCursors.push(cursor);
|
||||
}
|
||||
|
||||
this.#updateDestinationCursors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#updateDestinationCursors() {
|
||||
const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates();
|
||||
if (this.#destinationPreviewCursors.length == 1)
|
||||
this.#destinationPreviewCursors[0].setLatLng(this.getMouseCoordinates());
|
||||
else {
|
||||
Object.values(getApp().getUnitsManager().computeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
||||
if (idx < this.#destinationPreviewCursors.length)
|
||||
this.#destinationPreviewCursors[idx].setLatLng(this.#shiftKey ? latlng : this.getMouseCoordinates());
|
||||
})
|
||||
};
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length;
|
||||
if (selectedUnitsCount > 1) {
|
||||
const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates();
|
||||
if (this.#destinationPreviewCursors.length == 1)
|
||||
this.#destinationPreviewCursors[0].setLatLng(this.getMouseCoordinates());
|
||||
else {
|
||||
Object.values(getApp().getUnitsManager().computeGroupDestination(groupLatLng, this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
||||
if (idx < this.#destinationPreviewCursors.length)
|
||||
this.#destinationPreviewCursors[idx].setLatLng(this.#shiftKey ? latlng : this.getMouseCoordinates());
|
||||
})
|
||||
};
|
||||
|
||||
this.#destinationPreviewHandleLine.setLatLngs([groupLatLng, this.getMouseCoordinates()]);
|
||||
this.#destinationPreviewHandle.setLatLng(this.getMouseCoordinates());
|
||||
this.#destinationPreviewHandleLine.setLatLngs([groupLatLng, this.getMouseCoordinates()]);
|
||||
this.#destinationPreviewHandle.setLatLng(this.getMouseCoordinates());
|
||||
} else {
|
||||
this.#hideDestinationCursors();
|
||||
}
|
||||
}
|
||||
|
||||
#hideDestinationCursors() {
|
||||
@@ -838,34 +864,9 @@ export class Map extends L.Map {
|
||||
this.#drawingCursor.removeFrom(this);
|
||||
}
|
||||
|
||||
#updateCursor() {
|
||||
/* If the ctrl key is being pressed or we are performing an area selection, show the default cursor */
|
||||
if (this.#ctrlKey || this.#selecting) {
|
||||
/* Hide all non default cursors */
|
||||
this.#hideDestinationCursors();
|
||||
this.#hideDrawingCursor();
|
||||
|
||||
this.#showDefaultCursor();
|
||||
} else {
|
||||
/* Hide all the unnecessary cursors depending on the active state */
|
||||
if (this.#state !== IDLE) this.#hideDefaultCursor();
|
||||
if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors();
|
||||
if (this.#state !== COALITIONAREA_DRAW_POLYGON) this.#hideDrawingCursor();
|
||||
|
||||
/* Show the active cursor depending on the active state */
|
||||
if (this.#state === IDLE) this.#showDefaultCursor();
|
||||
else if (this.#state === MOVE_UNIT) this.#showDestinationCursors();
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor();
|
||||
}
|
||||
}
|
||||
|
||||
#setVisibilityOption(option: string, ev: any) {
|
||||
this.#visibilityOptions[option] = ev.currentTarget.checked;
|
||||
document.dispatchEvent(new CustomEvent("mapVisibilityOptionsChanged"));
|
||||
}
|
||||
|
||||
getMapMarkerControls() {
|
||||
return this.#mapMarkerControls;
|
||||
document.dispatchEvent(new CustomEvent("mapOptionsChanged"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ export class MissionManager {
|
||||
this.#commandModeErasDropdown = new Dropdown("command-mode-era-options", () => {});
|
||||
}
|
||||
|
||||
/** Update location of bullseyes
|
||||
*
|
||||
* @param object <BulleyesData>
|
||||
*/
|
||||
updateBullseyes(data: BullseyesData) {
|
||||
const commandMode = getApp().getMissionManager().getCommandModeOptions().commandMode;
|
||||
for (let idx in data.bullseyes) {
|
||||
@@ -56,6 +60,10 @@ export class MissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Update airbase information
|
||||
*
|
||||
* @param object <AirbasesData>
|
||||
*/
|
||||
updateAirbases(data: AirbasesData) {
|
||||
for (let idx in data.airbases) {
|
||||
var airbase = data.airbases[idx]
|
||||
@@ -75,6 +83,10 @@ export class MissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Update mission information
|
||||
*
|
||||
* @param object <MissionData>
|
||||
*/
|
||||
updateMission(data: MissionData) {
|
||||
if (data.mission) {
|
||||
|
||||
@@ -109,30 +121,63 @@ export class MissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the bullseyes set in this theatre
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getBullseyes() {
|
||||
return this.#bullseyes;
|
||||
}
|
||||
|
||||
/** Get the airbases in this theatre
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getAirbases() {
|
||||
return this.#airbases;
|
||||
}
|
||||
|
||||
/** Get the options/settings as set in the command mode
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getCommandModeOptions() {
|
||||
return this.#commandModeOptions;
|
||||
}
|
||||
|
||||
/** Get the current date and time of the mission (based on local time)
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getDateAndTime() {
|
||||
return this.#dateAndTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds left of setup time
|
||||
* @returns number
|
||||
*/
|
||||
getRemainingSetupTime() {
|
||||
return this.#remainingSetupTime;
|
||||
}
|
||||
|
||||
/** Get an object with the coalitions in it
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getCoalitions() {
|
||||
return this.#coalitions;
|
||||
}
|
||||
|
||||
/** Get the current theatre (map) name
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
getTheatre() {
|
||||
return this.#theatre;
|
||||
}
|
||||
|
||||
|
||||
getAvailableSpawnPoints() {
|
||||
if (this.getCommandModeOptions().commandMode === GAME_MASTER)
|
||||
return Infinity;
|
||||
|
||||
@@ -17,6 +17,7 @@ import { WeaponsManager } from "./weapon/weaponsmanager";
|
||||
import { Manager } from "./other/manager";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
import { ServerManager } from "./server/servermanager";
|
||||
import { sha256 } from 'js-sha256';
|
||||
|
||||
import { BLUE_COMMANDER, FILL_SELECTED_RING, GAME_MASTER, HIDE_UNITS_SHORT_RANGE_RINGS, RED_COMMANDER, SHOW_UNITS_ACQUISITION_RINGS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_LABELS } from "./constants/constants";
|
||||
import { aircraftDatabase } from "./unit/databases/aircraftdatabase";
|
||||
@@ -28,9 +29,13 @@ import { UnitListPanel } from "./panels/unitlistpanel";
|
||||
import { ContextManager } from "./context/contextmanager";
|
||||
import { Context } from "./context/context";
|
||||
|
||||
var VERSION = "v0.4.9-alpha-rc1";
|
||||
var DEBUG = false;
|
||||
|
||||
export class OlympusApp {
|
||||
/* Global data */
|
||||
#activeCoalition: string = "blue";
|
||||
#latestVersion: string|undefined = undefined;
|
||||
|
||||
/* Main leaflet map, extended by custom methods */
|
||||
#map: Map | null = null;
|
||||
@@ -49,7 +54,6 @@ export class OlympusApp {
|
||||
#weaponsManager: WeaponsManager | null = null;
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
// TODO add checks on null
|
||||
@@ -177,7 +181,6 @@ export class OlympusApp {
|
||||
|
||||
start() {
|
||||
/* Initialize base functionalitites */
|
||||
|
||||
this.#contextManager = new ContextManager();
|
||||
this.#contextManager.add( "olympus", {} );
|
||||
|
||||
@@ -231,11 +234,39 @@ export class OlympusApp {
|
||||
this.#setupEvents();
|
||||
|
||||
/* Set the splash background image to a random image */
|
||||
var splashScreen = document.getElementById("splash-screen");
|
||||
if (splashScreen) {
|
||||
let i = Math.round(Math.random() * 7 + 1);
|
||||
let splashScreen = document.getElementById("splash-screen") as HTMLElement;
|
||||
let i = Math.round(Math.random() * 7 + 1);
|
||||
|
||||
new Promise((resolve, reject) => {
|
||||
const image = new Image();
|
||||
image.addEventListener('load', resolve);
|
||||
image.addEventListener('error', resolve);
|
||||
image.src = `/resources/theme/images/splash/${i}.jpg`;
|
||||
}).then(() => {
|
||||
splashScreen.style.backgroundImage = `url('/resources/theme/images/splash/${i}.jpg')`;
|
||||
}
|
||||
let loadingScreen = document.getElementById("loading-screen") as HTMLElement;
|
||||
loadingScreen.classList.add("fade-out");
|
||||
window.setInterval(() => { loadingScreen.classList.add("hide"); }, 1000);
|
||||
})
|
||||
|
||||
/* Check if we are running the latest version */
|
||||
const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json");
|
||||
fetch(request).then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw new Error("Error connecting to Github to retrieve latest version");
|
||||
}
|
||||
}).then((res) => {
|
||||
this.#latestVersion = res["version"];
|
||||
const latestVersionSpan = document.getElementById("latest-version") as HTMLElement;
|
||||
if (latestVersionSpan) {
|
||||
latestVersionSpan.innerHTML = this.#latestVersion ?? "Unknown";
|
||||
if (this.#latestVersion !== VERSION) {
|
||||
latestVersionSpan.classList.add("new-version");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#setupEvents() {
|
||||
@@ -268,7 +299,7 @@ export class OlympusApp {
|
||||
shortcutManager.addKeyboardShortcut("toggleDemo", {
|
||||
"altKey": false,
|
||||
"callback": () => {
|
||||
this.getServerManager().toggleDemoEnabled();
|
||||
if (DEBUG === true) this.getServerManager().toggleDemoEnabled();
|
||||
},
|
||||
"code": "KeyT",
|
||||
"context": "olympus",
|
||||
@@ -400,6 +431,7 @@ export class OlympusApp {
|
||||
// TODO: move from here in dedicated class
|
||||
document.addEventListener("closeDialog", (ev: CustomEventInit) => {
|
||||
ev.detail._element.closest(".ol-dialog").classList.add("hide");
|
||||
document.getElementById("gray-out")?.classList.toggle("hide", true);
|
||||
});
|
||||
|
||||
/* Try and connect with the Olympus REST server */
|
||||
@@ -408,8 +440,9 @@ export class OlympusApp {
|
||||
loginForm.addEventListener("submit", (ev:SubmitEvent) => {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
var hash = sha256.create();
|
||||
const username = (loginForm.querySelector("#username") as HTMLInputElement).value;
|
||||
const password = (loginForm.querySelector("#password") as HTMLInputElement).value;
|
||||
const password = hash.update((loginForm.querySelector("#password") as HTMLInputElement).value).hex();
|
||||
|
||||
// Update the user credentials
|
||||
this.getServerManager().setCredentials(username, password);
|
||||
@@ -423,7 +456,6 @@ export class OlympusApp {
|
||||
console.error("Unable to find login form.");
|
||||
}
|
||||
|
||||
|
||||
/* Reload the page, used to mimic a restart of the app */
|
||||
document.addEventListener("reloadPage", () => {
|
||||
location.reload();
|
||||
|
||||
@@ -348,7 +348,7 @@ export function getMarkerCategoryByName(name: string) {
|
||||
else if (navyUnitDatabase.getByName(name) != null)
|
||||
return "navyunit";
|
||||
else
|
||||
return "groundunit-other"; // TODO add other unit types
|
||||
return "aircraft"; // TODO add other unit types
|
||||
}
|
||||
|
||||
export function getUnitDatabaseByCategory(category: string) {
|
||||
@@ -434,14 +434,24 @@ export function convertDateAndTimeToDate(dateAndTime: DateAndTime) {
|
||||
return new Date(year, month, date.Day, time.h, time.m, time.s);
|
||||
}
|
||||
|
||||
export function createCheckboxOption(value: string, text: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}) {
|
||||
export function createCheckboxOption(value: string, text: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}, options?:any) {
|
||||
options = {
|
||||
"disabled": false,
|
||||
"name": "",
|
||||
"readOnly": false,
|
||||
...options
|
||||
};
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("ol-checkbox");
|
||||
var label = document.createElement("label");
|
||||
label.title = text;
|
||||
var input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.checked = checked;
|
||||
input.checked = checked;
|
||||
input.name = options.name;
|
||||
input.disabled = options.disabled;
|
||||
input.readOnly = options.readOnly;
|
||||
input.value = value;
|
||||
var span = document.createElement("span");
|
||||
span.innerText = value;
|
||||
label.appendChild(input);
|
||||
|
||||
@@ -2,16 +2,13 @@ import { SVGInjector } from "@tanem/svg-injector";
|
||||
import { getApp } from "..";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Slider } from "../controls/slider";
|
||||
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
import { Switch } from "../controls/switch";
|
||||
import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, shotsIntensityDescriptions, shotsScatterDescriptions, speedIncrements } from "../constants/constants";
|
||||
import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils";
|
||||
import { GeneralSettings, Radio, TACAN } from "../interfaces";
|
||||
import { PrimaryToolbar } from "../toolbars/primarytoolbar";
|
||||
import { ContextActionSet } from "../unit/contextactionset";
|
||||
import { ContextAction } from "../unit/contextaction";
|
||||
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
@@ -36,36 +33,36 @@ export class UnitControlPanel extends Panel {
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string){
|
||||
constructor(ID: string) {
|
||||
super(ID);
|
||||
|
||||
/* Unit control sliders */
|
||||
this.#altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => { getApp().getUnitsManager().setAltitude(ftToM(value)); });
|
||||
this.#altitudeTypeSwitch = new Switch("altitude-type-switch", (value: boolean) => { getApp().getUnitsManager().setAltitudeType(value? "ASL": "AGL"); });
|
||||
this.#altitudeTypeSwitch = new Switch("altitude-type-switch", (value: boolean) => { getApp().getUnitsManager().setAltitudeType(value ? "ASL" : "AGL"); });
|
||||
|
||||
this.#speedSlider = new Slider("speed-slider", 0, 100, "kts", (value: number) => { getApp().getUnitsManager().setSpeed(knotsToMs(value)); });
|
||||
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getApp().getUnitsManager().setSpeedType(value? "CAS": "GS"); });
|
||||
this.#speedTypeSwitch = new Switch("speed-type-switch", (value: boolean) => { getApp().getUnitsManager().setSpeedType(value ? "CAS" : "GS"); });
|
||||
|
||||
/* 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.slice(0).reverse()[index], () => { getApp().getUnitsManager().setROE(option); });
|
||||
}).filter((button: HTMLButtonElement, index: number) => {return ROEs[index] !== "";});
|
||||
}).filter((button: HTMLButtonElement, index: number) => { return ROEs[index] !== ""; });
|
||||
|
||||
this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string, index: number) => {
|
||||
return this.#createOptionButton(option, `threat/${option.toLowerCase()}.svg`, reactionsToThreatDescriptions[index],() => { getApp().getUnitsManager().setReactionToThreat(option); });
|
||||
return this.#createOptionButton(option, `threat/${option.toLowerCase()}.svg`, reactionsToThreatDescriptions[index], () => { getApp().getUnitsManager().setReactionToThreat(option); });
|
||||
});
|
||||
|
||||
this.#optionButtons["emissionsCountermeasures"] = emissionsCountermeasures.map((option: string, index: number) => {
|
||||
return this.#createOptionButton(option, `emissions/${option.toLowerCase()}.svg`, emissionsCountermeasuresDescriptions[index],() => { getApp().getUnitsManager().setEmissionsCountermeasures(option); });
|
||||
return this.#createOptionButton(option, `emissions/${option.toLowerCase()}.svg`, emissionsCountermeasuresDescriptions[index], () => { getApp().getUnitsManager().setEmissionsCountermeasures(option); });
|
||||
});
|
||||
|
||||
this.#optionButtons["shotsScatter"] = [1, 2, 3].map((option: number, index: number) => {
|
||||
return this.#createOptionButton(option.toString(), `scatter/${option.toString().toLowerCase()}.svg`, shotsScatterDescriptions[index],() => { getApp().getUnitsManager().setShotsScatter(option); });
|
||||
return this.#createOptionButton(option.toString(), `scatter/${option.toString().toLowerCase()}.svg`, shotsScatterDescriptions[index], () => { getApp().getUnitsManager().setShotsScatter(option); });
|
||||
});
|
||||
|
||||
this.#optionButtons["shotsIntensity"] = [1, 2, 3].map((option: number, index: number) => {
|
||||
return this.#createOptionButton(option.toString(), `intensity/${option.toString().toLowerCase()}.svg`, shotsIntensityDescriptions[index],() => { getApp().getUnitsManager().setShotsIntensity(option); });
|
||||
return this.#createOptionButton(option.toString(), `intensity/${option.toString().toLowerCase()}.svg`, shotsIntensityDescriptions[index], () => { getApp().getUnitsManager().setShotsIntensity(option); });
|
||||
});
|
||||
|
||||
this.getElement().querySelector("#roe-buttons-container")?.append(...this.#optionButtons["ROE"]);
|
||||
@@ -107,38 +104,58 @@ export class UnitControlPanel extends Panel {
|
||||
getApp().getUnitsManager().setOperateAs(value);
|
||||
});
|
||||
|
||||
/* Mouseover of (?) highlights activation buttons */
|
||||
const operateAsQuestionMark = <HTMLElement>this.getElement().querySelector("#operate-as h4 img");
|
||||
operateAsQuestionMark.addEventListener("mouseover", () => {
|
||||
document.querySelectorAll(`#rapid-controls button.scenic-action`).forEach((btn: Element) => {
|
||||
btn.classList.add(`pulse`);
|
||||
});
|
||||
});
|
||||
operateAsQuestionMark.addEventListener("mouseout", () => {
|
||||
document.querySelectorAll(`#rapid-controls button.scenic-action.pulse`).forEach((btn: Element) => {
|
||||
btn.classList.remove(`pulse`);
|
||||
});
|
||||
});
|
||||
|
||||
/* Advanced settings dialog */
|
||||
this.#advancedSettingsDialog = <HTMLElement> document.querySelector("#advanced-settings-dialog");
|
||||
this.#advancedSettingsDialog = <HTMLElement>document.querySelector("#advanced-settings-dialog");
|
||||
|
||||
/* Advanced settings dropdowns */
|
||||
this.#TACANXYDropdown = new Dropdown("TACAN-XY", () => {});
|
||||
this.#TACANXYDropdown = new Dropdown("TACAN-XY", () => { });
|
||||
this.#TACANXYDropdown.setOptions(["X", "Y"]);
|
||||
this.#radioDecimalsDropdown = new Dropdown("radio-decimals", () => {});
|
||||
this.#radioDecimalsDropdown = new Dropdown("radio-decimals", () => { });
|
||||
this.#radioDecimalsDropdown.setOptions([".000", ".250", ".500", ".750"]);
|
||||
this.#radioCallsignDropdown = new Dropdown("radio-callsign", () => {});
|
||||
this.#radioCallsignDropdown = new Dropdown("radio-callsign", () => { });
|
||||
this.#deleteDropdown = new Dropdown("delete-options", () => { });
|
||||
|
||||
/* Events and timer */
|
||||
window.setInterval(() => {this.update();}, 25);
|
||||
window.setInterval(() => { this.update(); }, 25);
|
||||
|
||||
document.addEventListener("unitsSelection", (e: CustomEvent<Unit[]>) => {
|
||||
this.show();
|
||||
document.addEventListener("unitsSelection", (e: CustomEvent<Unit[]>) => {
|
||||
this.show();
|
||||
this.addButtons();
|
||||
this.#updateRapidControls();
|
||||
this.#updateRapidControls();
|
||||
});
|
||||
document.addEventListener("clearSelection", () => {
|
||||
document.addEventListener("clearSelection", () => {
|
||||
this.hide();
|
||||
this.#updateRapidControls();
|
||||
this.#updateRapidControls();
|
||||
});
|
||||
document.addEventListener("applyAdvancedSettings", () => {this.#applyAdvancedSettings();})
|
||||
document.addEventListener("applyAdvancedSettings", () => { this.#applyAdvancedSettings(); })
|
||||
document.addEventListener("showAdvancedSettings", () => {
|
||||
this.#updateAdvancedSettingsDialog(getApp().getUnitsManager().getSelectedUnits());
|
||||
this.#advancedSettingsDialog.classList.remove("hide");
|
||||
});
|
||||
|
||||
/* This is for when a ctrl-click happens on the map for deselection and we need to remove the selected unit from the panel */
|
||||
document.addEventListener( "unitsDeselection", ( ev:CustomEventInit ) => {
|
||||
this.getElement().querySelector( `button[data-unit-id="${ev.detail.ID}"]` )?.remove();
|
||||
this.#updateRapidControls();
|
||||
document.addEventListener("unitsDeselection", (ev: CustomEventInit) => {
|
||||
if (ev.detail.length > 0) {
|
||||
this.show();
|
||||
this.addButtons();
|
||||
this.#updateRapidControls();
|
||||
} else {
|
||||
this.hide();
|
||||
this.#updateRapidControls();
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener("resize", (e: any) => this.#calculateMaxHeight());
|
||||
@@ -146,16 +163,16 @@ export class UnitControlPanel extends Panel {
|
||||
const element = document.getElementById("toolbar-container");
|
||||
if (element)
|
||||
new ResizeObserver(() => this.#calculateTop()).observe(element);
|
||||
|
||||
|
||||
this.#calculateMaxHeight()
|
||||
this.hide();
|
||||
}
|
||||
|
||||
show() {
|
||||
const context = getApp().getCurrentContext();
|
||||
if ( !context.getUseUnitControlPanel() )
|
||||
if (!context.getUseUnitControlPanel())
|
||||
return;
|
||||
|
||||
|
||||
super.show();
|
||||
this.#speedTypeSwitch.resetExpectedValue();
|
||||
this.#altitudeTypeSwitch.resetExpectedValue();
|
||||
@@ -163,6 +180,7 @@ export class UnitControlPanel extends Panel {
|
||||
this.#AWACSSwitch.resetExpectedValue();
|
||||
this.#onOffSwitch.resetExpectedValue();
|
||||
this.#followRoadsSwitch.resetExpectedValue();
|
||||
this.#operateAsSwitch.resetExpectedValue();
|
||||
this.#altitudeSlider.resetExpectedValue();
|
||||
this.#speedSlider.resetExpectedValue();
|
||||
this.#calculateMaxHeight();
|
||||
@@ -171,26 +189,26 @@ export class UnitControlPanel extends Panel {
|
||||
addButtons() {
|
||||
this.#units = getApp().getUnitsManager().getSelectedUnits();
|
||||
this.#selectedUnitsTypes = getApp().getUnitsManager().getSelectedUnitsCategories();
|
||||
|
||||
|
||||
if (this.#units.length < 20) {
|
||||
this.getElement().querySelector("#selected-units-container")?.replaceChildren(...this.#units.map((unit: Unit, index: number) => {
|
||||
var button = document.createElement("button");
|
||||
var callsign = unit.getUnitName() || "";
|
||||
var label = unit.getDatabase()?.getByName(unit.getName())?.label || unit.getName();
|
||||
|
||||
button.setAttribute("data-unit-id", "" + unit.ID );
|
||||
button.setAttribute("data-unit-id", "" + unit.ID);
|
||||
button.setAttribute("data-label", label);
|
||||
button.setAttribute("data-callsign", callsign);
|
||||
|
||||
button.setAttribute("data-coalition", unit.getCoalition());
|
||||
button.classList.add("pill", "highlight-coalition")
|
||||
|
||||
button.addEventListener("click", ( ev:MouseEventInit ) => {
|
||||
button.addEventListener("click", (ev: MouseEventInit) => {
|
||||
// Ctrl-click deselection
|
||||
if ( ev.ctrlKey === true && ev.shiftKey === false && ev.altKey === false ) {
|
||||
getApp().getUnitsManager().deselectUnit( unit.ID );
|
||||
if (ev.ctrlKey === true && ev.shiftKey === false && ev.altKey === false) {
|
||||
getApp().getUnitsManager().deselectUnit(unit.ID);
|
||||
button.remove();
|
||||
// Deselect all
|
||||
// Deselect all
|
||||
} else {
|
||||
getApp().getUnitsManager().deselectAllUnits();
|
||||
getApp().getUnitsManager().selectUnit(unit.ID, true);
|
||||
@@ -206,14 +224,14 @@ export class UnitControlPanel extends Panel {
|
||||
}
|
||||
|
||||
update() {
|
||||
if (this.getVisible()){
|
||||
if (this.getVisible()) {
|
||||
const element = this.getElement();
|
||||
if (element != null && this.#units.length > 0) {
|
||||
/* Toggle visibility of control elements */
|
||||
var isTanker = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.isTanker();});
|
||||
var isAWACS = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.isAWACS();});
|
||||
var isActiveTanker = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getIsActiveTanker()});
|
||||
var isActiveAWACAS = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getIsActiveAWACS()});
|
||||
var isTanker = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.isTanker(); });
|
||||
var isAWACS = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.isAWACS(); });
|
||||
var isActiveTanker = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getIsActiveTanker() });
|
||||
var isActiveAWACAS = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getIsActiveAWACS() });
|
||||
|
||||
element.toggleAttribute("data-show-categories-tooltip", this.#selectedUnitsTypes.length > 1);
|
||||
element.toggleAttribute("data-show-speed-slider", this.#selectedUnitsTypes.length == 1);
|
||||
@@ -221,37 +239,37 @@ export class UnitControlPanel extends Panel {
|
||||
element.toggleAttribute("data-show-roe", !isTanker && !isAWACS);
|
||||
element.toggleAttribute("data-show-threat", (this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")) && !(this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-emissions-countermeasures", (this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")) && !(this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit")));
|
||||
element.toggleAttribute("data-show-shots-scatter", this.#selectedUnitsTypes.includes("GroundUnit")); //TODO: more refined
|
||||
element.toggleAttribute("data-show-shots-intensity", this.#selectedUnitsTypes.includes("GroundUnit")); //TODO: more refined
|
||||
element.toggleAttribute("data-show-tanker-button", getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.isTanker();}) === true);
|
||||
element.toggleAttribute("data-show-AWACS-button", getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.isAWACS();}) === true);
|
||||
element.toggleAttribute("data-show-shots-scatter", this.#selectedUnitsTypes.length == 1 && this.#selectedUnitsTypes.includes("GroundUnit")); //TODO: more refined
|
||||
element.toggleAttribute("data-show-shots-intensity", this.#selectedUnitsTypes.length == 1 && this.#selectedUnitsTypes.includes("GroundUnit")); //TODO: more refined
|
||||
element.toggleAttribute("data-show-tanker-button", getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.isTanker(); }) === true);
|
||||
element.toggleAttribute("data-show-AWACS-button", getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.isAWACS(); }) === true);
|
||||
element.toggleAttribute("data-show-on-off", (this.#selectedUnitsTypes.includes("GroundUnit") || this.#selectedUnitsTypes.includes("NavyUnit")) && !(this.#selectedUnitsTypes.includes("Aircraft") || this.#selectedUnitsTypes.includes("Helicopter")));
|
||||
element.toggleAttribute("data-show-follow-roads", (this.#selectedUnitsTypes.length == 1 && this.#selectedUnitsTypes.includes("GroundUnit")));
|
||||
element.toggleAttribute("data-show-operate-as", getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) === "neutral");
|
||||
element.toggleAttribute("data-show-operate-as", this.#selectedUnitsTypes.length == 1 && this.#selectedUnitsTypes.includes("GroundUnit") && getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getCoalition() }) === "neutral");
|
||||
|
||||
if (this.#units.length == 1) {
|
||||
if (isAWACS)
|
||||
if (isAWACS)
|
||||
element.toggleAttribute("data-show-advanced-settings-button", isActiveAWACAS);
|
||||
else if (isTanker)
|
||||
else if (isTanker)
|
||||
element.toggleAttribute("data-show-advanced-settings-button", isActiveTanker);
|
||||
else
|
||||
else
|
||||
element.toggleAttribute("data-show-advanced-settings-button", true);
|
||||
}
|
||||
|
||||
|
||||
if (this.#selectedUnitsTypes.length == 1) {
|
||||
/* Flight controls */
|
||||
var desiredAltitude = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitude()});
|
||||
var desiredAltitudeType = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredAltitudeType()});
|
||||
var desiredSpeed = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeed()});
|
||||
var desiredSpeedType = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getDesiredSpeedType()});
|
||||
var isActiveTanker = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getIsActiveTanker()});
|
||||
var isActiveAWACAS = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getIsActiveAWACS()});
|
||||
var onOff = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getOnOff()});
|
||||
var followRoads = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getFollowRoads()});
|
||||
var operateAs = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getOperateAs()});
|
||||
|
||||
this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined? desiredAltitudeType == "ASL": undefined, false);
|
||||
this.#speedTypeSwitch.setValue(desiredSpeedType != undefined? desiredSpeedType == "CAS": undefined, false);
|
||||
var desiredAltitude = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getDesiredAltitude() });
|
||||
var desiredAltitudeType = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getDesiredAltitudeType() });
|
||||
var desiredSpeed = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getDesiredSpeed() });
|
||||
var desiredSpeedType = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getDesiredSpeedType() });
|
||||
var isActiveTanker = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getIsActiveTanker() });
|
||||
var isActiveAWACAS = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getIsActiveAWACS() });
|
||||
var onOff = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getOnOff() });
|
||||
var followRoads = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getFollowRoads() });
|
||||
var operateAs = getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => { return unit.getOperateAs() });
|
||||
|
||||
this.#altitudeTypeSwitch.setValue(desiredAltitudeType != undefined ? desiredAltitudeType == "ASL" : undefined, false);
|
||||
this.#speedTypeSwitch.setValue(desiredSpeedType != undefined ? desiredSpeedType == "CAS" : undefined, false);
|
||||
|
||||
this.#speedSlider.setMinMax(minSpeedValues[this.#selectedUnitsTypes[0]], maxSpeedValues[this.#selectedUnitsTypes[0]]);
|
||||
this.#altitudeSlider.setMinMax(minAltitudeValues[this.#selectedUnitsTypes[0]], maxAltitudeValues[this.#selectedUnitsTypes[0]]);
|
||||
@@ -296,7 +314,7 @@ export class UnitControlPanel extends Panel {
|
||||
this.#AWACSSwitch.setValue(isActiveAWACAS, false);
|
||||
this.#onOffSwitch.setValue(onOff, false);
|
||||
this.#followRoadsSwitch.setValue(followRoads, false);
|
||||
this.#operateAsSwitch.setValue(operateAs? operateAs === "blue": undefined, false);
|
||||
this.#operateAsSwitch.setValue(operateAs ? operateAs === "blue" : undefined, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,8 +323,8 @@ export class UnitControlPanel extends Panel {
|
||||
var contextActionSet = new ContextActionSet();
|
||||
var units = getApp().getUnitsManager().getSelectedUnits();
|
||||
|
||||
var showAltitudeChange = units.some((unit: Unit) => {return ["Aircraft", "Helicopter"].includes(unit.getCategory());});
|
||||
this.getElement().querySelector("#climb")?.classList.toggle("hide", !showAltitudeChange);
|
||||
var showAltitudeChange = units.some((unit: Unit) => { return ["Aircraft", "Helicopter"].includes(unit.getCategory()); });
|
||||
this.getElement().querySelector("#climb")?.classList.toggle("hide", !showAltitudeChange);
|
||||
this.getElement().querySelector("#descend")?.classList.toggle("hide", !showAltitudeChange);
|
||||
|
||||
units.forEach((unit: Unit) => {
|
||||
@@ -324,6 +342,8 @@ export class UnitControlPanel extends Panel {
|
||||
let button = document.createElement("button");
|
||||
button.title = contextAction.getDescription();
|
||||
button.classList.add("ol-button", "unit-action-button");
|
||||
if (contextAction.getOptions().isScenic)
|
||||
button.classList.add("scenic-action");
|
||||
button.id = key;
|
||||
rapidControlsContainer.appendChild(button);
|
||||
button.onclick = () => {
|
||||
@@ -332,10 +352,8 @@ export class UnitControlPanel extends Panel {
|
||||
}
|
||||
}
|
||||
|
||||
#updateAdvancedSettingsDialog(units: Unit[])
|
||||
{
|
||||
if (units.length == 1)
|
||||
{
|
||||
#updateAdvancedSettingsDialog(units: Unit[]) {
|
||||
if (units.length == 1) {
|
||||
/* HTML Elements */
|
||||
const unitNameEl = this.#advancedSettingsDialog.querySelector("#unit-name") as HTMLElement;
|
||||
const prohibitJettisonCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-jettison-checkbox")?.querySelector("input") as HTMLInputElement;
|
||||
@@ -348,7 +366,7 @@ export class UnitControlPanel extends Panel {
|
||||
const TACANCallsignInput = this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input") as HTMLInputElement;
|
||||
const radioMhzInput = this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input") as HTMLInputElement;
|
||||
const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement;
|
||||
|
||||
|
||||
const unit = units[0];
|
||||
const isTanker = unit.isTanker();
|
||||
const isAWACS = unit.isAWACS();
|
||||
@@ -385,7 +403,7 @@ export class UnitControlPanel extends Panel {
|
||||
radioMhzInput.value = String(radioMHz);
|
||||
radioCallsignNumberInput.value = String(unit.getRadio().callsignNumber);
|
||||
this.#radioDecimalsDropdown.setValue("." + radioDecimals);
|
||||
|
||||
|
||||
if (isTanker) /* Set tanker specific options */
|
||||
this.#radioCallsignDropdown.setOptions(["Texaco", "Arco", "Shell"]);
|
||||
else if (isAWACS) /* Set AWACS specific options */
|
||||
@@ -399,8 +417,7 @@ export class UnitControlPanel extends Panel {
|
||||
}
|
||||
}
|
||||
|
||||
#applyAdvancedSettings()
|
||||
{
|
||||
#applyAdvancedSettings() {
|
||||
/* HTML Elements */
|
||||
const prohibitJettisonCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-jettison-checkbox")?.querySelector("input") as HTMLInputElement;
|
||||
const prohibitAfterburnerCheckbox = this.#advancedSettingsDialog.querySelector("#prohibit-afterburner-checkbox")?.querySelector("input") as HTMLInputElement;
|
||||
@@ -416,7 +433,7 @@ export class UnitControlPanel extends Panel {
|
||||
|
||||
/* TACAN */
|
||||
const TACAN: TACAN = {
|
||||
isOn: TACANCheckbox.checked? true: false,
|
||||
isOn: TACANCheckbox.checked ? true : false,
|
||||
channel: Number(TACANChannelInput.value),
|
||||
XY: this.#TACANXYDropdown.getValue(),
|
||||
callsign: TACANCallsignInput.value as string
|
||||
@@ -428,18 +445,18 @@ export class UnitControlPanel extends Panel {
|
||||
const radio: Radio = {
|
||||
frequency: (radioMHz * 1000 + Number(radioDecimals.substring(1))) * 1000,
|
||||
callsign: this.#radioCallsignDropdown.getIndex() + 1,
|
||||
callsignNumber: Number(radioCallsignNumberInput.value)
|
||||
callsignNumber: Number(radioCallsignNumberInput.value)
|
||||
}
|
||||
|
||||
/* General settings */
|
||||
const generalSettings: GeneralSettings = {
|
||||
prohibitJettison: prohibitJettisonCheckbox.checked? true: false,
|
||||
prohibitAfterburner: prohibitAfterburnerCheckbox.checked? true: false,
|
||||
prohibitAA: prohibitAACheckbox.checked? true: false,
|
||||
prohibitAG: prohibitAGCheckbox.checked? true: false,
|
||||
prohibitAirWpn: prohibitAirWpnCheckbox.checked? true: false
|
||||
prohibitJettison: prohibitJettisonCheckbox.checked ? true : false,
|
||||
prohibitAfterburner: prohibitAfterburnerCheckbox.checked ? true : false,
|
||||
prohibitAA: prohibitAACheckbox.checked ? true : false,
|
||||
prohibitAG: prohibitAGCheckbox.checked ? true : false,
|
||||
prohibitAirWpn: prohibitAirWpnCheckbox.checked ? true : false
|
||||
}
|
||||
|
||||
|
||||
/* Send command and close */
|
||||
var units = getApp().getUnitsManager().getSelectedUnits();
|
||||
// TODO: split setAdvancedOptions into setIsTanker, setIsAWACS, setAdvancedOptions
|
||||
|
||||
@@ -12,14 +12,16 @@ export class ServerManager {
|
||||
#connected: boolean = false;
|
||||
#paused: boolean = false;
|
||||
#REST_ADDRESS = "http://localhost:30000/olympus";
|
||||
#DEMO_ADDRESS = window.location.href + "demo";
|
||||
#DEMO_ADDRESS = window.location.href.split('?')[0] + "demo"; /* Remove query parameters */
|
||||
#username = "";
|
||||
#password = "";
|
||||
#sessionHash: string | null = null;
|
||||
#lastUpdateTimes: {[key: string]: number} = {}
|
||||
#lastUpdateTimes: { [key: string]: number } = {}
|
||||
#demoEnabled = false;
|
||||
#previousMissionElapsedTime:number = 0; // Track if mission elapsed time is increasing (i.e. is the server paused)
|
||||
#previousMissionElapsedTime: number = 0; // Track if mission elapsed time is increasing (i.e. is the server paused)
|
||||
#serverIsPaused: boolean = false;
|
||||
#intervals: number[] = [];
|
||||
#requests: { [key: string]: XMLHttpRequest } = {};
|
||||
|
||||
constructor() {
|
||||
this.#lastUpdateTimes[UNITS_URI] = Date.now();
|
||||
@@ -39,9 +41,20 @@ export class ServerManager {
|
||||
this.#password = newPassword;
|
||||
}
|
||||
|
||||
GET(callback: CallableFunction, uri: string, options?: ServerRequestOptions, responseType?: string) {
|
||||
GET(callback: CallableFunction, uri: string, options?: ServerRequestOptions, responseType: string = 'text', force: boolean = false) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
|
||||
/* If a request on this uri is still pending (meaning it's not done or did not yet fail), skip the request, to avoid clogging the TCP workers */
|
||||
/* If we are forcing the request we don't care if one already exists, just send it. CAREFUL: this makes sense only for low frequency requests, like refreshes, when we
|
||||
are reasonably confident any previous request will be done before we make a new one on the same URI. */
|
||||
if (uri in this.#requests && this.#requests[uri].readyState !== 4 && !force) {
|
||||
console.warn(`GET request on ${uri} URI still pending, skipping...`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force)
|
||||
this.#requests[uri] = xmlHttp;
|
||||
|
||||
/* Assemble the request options string */
|
||||
var optionsString = '';
|
||||
if (options?.time != undefined)
|
||||
@@ -82,9 +95,11 @@ export class ServerManager {
|
||||
this.setConnected(false);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res) => {
|
||||
console.error("An error occurred during the XMLHttpRequest");
|
||||
this.setConnected(false);
|
||||
xmlHttp.onreadystatechange = (res) => {
|
||||
if (xmlHttp.readyState == 4 && xmlHttp.status === 0) {
|
||||
console.error("An error occurred during the XMLHttpRequest");
|
||||
this.setConnected(false);
|
||||
}
|
||||
};
|
||||
xmlHttp.send(null);
|
||||
}
|
||||
@@ -104,7 +119,7 @@ export class ServerManager {
|
||||
|
||||
getConfig(callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("GET", window.location.href + "config", true);
|
||||
xmlHttp.open("GET", window.location.href.split('?')[0] + "config", true);
|
||||
xmlHttp.onload = function (e) {
|
||||
var data = JSON.parse(xmlHttp.responseText);
|
||||
callback(data);
|
||||
@@ -129,7 +144,7 @@ export class ServerManager {
|
||||
}
|
||||
|
||||
getLogs(callback: CallableFunction, refresh: boolean = false) {
|
||||
this.GET(callback, LOGS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[LOGS_URI]});
|
||||
this.GET(callback, LOGS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[LOGS_URI] }, 'text', refresh);
|
||||
}
|
||||
|
||||
getMission(callback: CallableFunction) {
|
||||
@@ -137,66 +152,66 @@ export class ServerManager {
|
||||
}
|
||||
|
||||
getUnits(callback: CallableFunction, refresh: boolean = false) {
|
||||
this.GET(callback, UNITS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[UNITS_URI] }, 'arraybuffer');
|
||||
this.GET(callback, UNITS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[UNITS_URI] }, 'arraybuffer', refresh);
|
||||
}
|
||||
|
||||
getWeapons(callback: CallableFunction, refresh: boolean = false) {
|
||||
this.GET(callback, WEAPONS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[WEAPONS_URI] }, 'arraybuffer');
|
||||
this.GET(callback, WEAPONS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[WEAPONS_URI] }, 'arraybuffer', refresh);
|
||||
}
|
||||
|
||||
isCommandExecuted(callback: CallableFunction, commandHash: string) {
|
||||
this.GET(callback, COMMANDS_URI, { commandHash: commandHash});
|
||||
this.GET(callback, COMMANDS_URI, { commandHash: commandHash });
|
||||
}
|
||||
|
||||
addDestination(ID: number, path: any, callback: CallableFunction = () => {}) {
|
||||
addDestination(ID: number, path: any, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "path": path }
|
||||
var data = { "setPath": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnSmoke(color: string, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
spawnSmoke(color: string, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "color": color, "location": latlng };
|
||||
var data = { "smoke": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnExplosion(intensity: number, explosionType: string, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
spawnExplosion(intensity: number, explosionType: string, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "explosionType": explosionType, "intensity": intensity, "location": latlng };
|
||||
var data = { "explosion": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnAircrafts(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnAircrafts(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
var data = { "spawnAircrafts": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnHelicopters(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnHelicopters(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
var data = { "spawnHelicopters": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };;
|
||||
var data = { "spawnGroundUnits": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnNavyUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnNavyUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
var data = { "spawnNavyUnits": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
attackUnit(ID: number, targetID: number, callback: CallableFunction = () => {}) {
|
||||
attackUnit(ID: number, targetID: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "targetID": targetID };
|
||||
var data = { "attackUnit": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
followUnit(ID: number, targetID: number, offset: { "x": number, "y": number, "z": number }, callback: CallableFunction = () => {}) {
|
||||
followUnit(ID: number, targetID: number, offset: { "x": number, "y": number, "z": number }, callback: CallableFunction = () => { }) {
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive bottom
|
||||
// Z: left-right, positive right
|
||||
@@ -206,170 +221,172 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
cloneUnits(units: {ID: number, location: LatLng}[], deleteOriginal: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
cloneUnits(units: { ID: number, location: LatLng }[], deleteOriginal: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "deleteOriginal": deleteOriginal, "spawnPoints": spawnPoints };
|
||||
var data = { "cloneUnits": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
deleteUnit(ID: number, explosion: boolean, explosionType: string, immediate: boolean, callback: CallableFunction = () => {}) {
|
||||
deleteUnit(ID: number, explosion: boolean, explosionType: string, immediate: boolean, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "explosion": explosion, "explosionType": explosionType, "immediate": immediate };
|
||||
var data = { "deleteUnit": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
landAt(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
landAt(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng };
|
||||
var data = { "landAt": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
changeSpeed(ID: number, speedChange: string, callback: CallableFunction = () => {}) {
|
||||
changeSpeed(ID: number, speedChange: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "change": speedChange }
|
||||
var data = { "changeSpeed": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setSpeed(ID: number, speed: number, callback: CallableFunction = () => {}) {
|
||||
setSpeed(ID: number, speed: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "speed": speed }
|
||||
var data = { "setSpeed": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setSpeedType(ID: number, speedType: string, callback: CallableFunction = () => {}) {
|
||||
setSpeedType(ID: number, speedType: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "speedType": speedType }
|
||||
var data = { "setSpeedType": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
changeAltitude(ID: number, altitudeChange: string, callback: CallableFunction = () => {}) {
|
||||
changeAltitude(ID: number, altitudeChange: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "change": altitudeChange }
|
||||
var data = { "changeAltitude": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setAltitudeType(ID: number, altitudeType: string, callback: CallableFunction = () => {}) {
|
||||
setAltitudeType(ID: number, altitudeType: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "altitudeType": altitudeType }
|
||||
var data = { "setAltitudeType": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setAltitude(ID: number, altitude: number, callback: CallableFunction = () => {}) {
|
||||
setAltitude(ID: number, altitude: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "altitude": altitude }
|
||||
var data = { "setAltitude": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
createFormation(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => {}) {
|
||||
createFormation(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader }
|
||||
var data = { "setLeader": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setROE(ID: number, ROE: string, callback: CallableFunction = () => {}) {
|
||||
setROE(ID: number, ROE: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "ROE": ROEs.indexOf(ROE) }
|
||||
var data = { "setROE": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setReactionToThreat(ID: number, reactionToThreat: string, callback: CallableFunction = () => {}) {
|
||||
setReactionToThreat(ID: number, reactionToThreat: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "reactionToThreat": reactionsToThreat.indexOf(reactionToThreat) }
|
||||
var data = { "setReactionToThreat": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setEmissionsCountermeasures(ID: number, emissionCountermeasure: string, callback: CallableFunction = () => {}) {
|
||||
setEmissionsCountermeasures(ID: number, emissionCountermeasure: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "emissionsCountermeasures": emissionsCountermeasures.indexOf(emissionCountermeasure) }
|
||||
var data = { "setEmissionsCountermeasures": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setOnOff(ID: number, onOff: boolean, callback: CallableFunction = () => {}) {
|
||||
setOnOff(ID: number, onOff: boolean, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "onOff": onOff }
|
||||
var data = { "setOnOff": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setFollowRoads(ID: number, followRoads: boolean, callback: CallableFunction = () => {}) {
|
||||
setFollowRoads(ID: number, followRoads: boolean, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "followRoads": followRoads }
|
||||
var data = { "setFollowRoads": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setOperateAs(ID: number, operateAs: number, callback: CallableFunction = () => {}) {
|
||||
setOperateAs(ID: number, operateAs: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "operateAs": operateAs }
|
||||
var data = { "setOperateAs": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
|
||||
refuel(ID: number, callback: CallableFunction = () => {}) {
|
||||
refuel(ID: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID };
|
||||
var data = { "refuel": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
bombPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
bombPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "bombPoint": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
carpetBomb(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
carpetBomb(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "carpetBomb": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
bombBuilding(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
bombBuilding(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "bombBuilding": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
fireAtArea(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
fireAtArea(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "fireAtArea": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
simulateFireFight(ID: number, latlng: LatLng, altitude: number, callback: CallableFunction = () => {}) {
|
||||
simulateFireFight(ID: number, latlng: LatLng, altitude: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng, "altitude": altitude }
|
||||
var data = { "simulateFireFight": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
scenicAAA(ID: number, coalition: string, callback: CallableFunction = () => {}) {
|
||||
// TODO: Remove coalition
|
||||
scenicAAA(ID: number, coalition: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "coalition": coalition }
|
||||
var data = { "scenicAAA": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
missOnPurpose(ID: number, coalition: string, callback: CallableFunction = () => {}) {
|
||||
// TODO: Remove coalition
|
||||
missOnPurpose(ID: number, coalition: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "coalition": coalition }
|
||||
var data = { "missOnPurpose": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
landAtPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
landAtPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "landAtPoint": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setShotsScatter(ID: number, shotsScatter: number, callback: CallableFunction = () => {}) {
|
||||
setShotsScatter(ID: number, shotsScatter: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "shotsScatter": shotsScatter }
|
||||
var data = { "setShotsScatter": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setShotsIntensity(ID: number, shotsIntensity: number, callback: CallableFunction = () => {}) {
|
||||
setShotsIntensity(ID: number, shotsIntensity: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "shotsIntensity": shotsIntensity }
|
||||
var data = { "setShotsIntensity": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setAdvacedOptions(ID: number, isActiveTanker: boolean, isActiveAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings, callback: CallableFunction = () => {}) {
|
||||
setAdvacedOptions(ID: number, isActiveTanker: boolean, isActiveAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings, callback: CallableFunction = () => { }) {
|
||||
var command = {
|
||||
"ID": ID,
|
||||
"isActiveTanker": isActiveTanker,
|
||||
@@ -383,7 +400,7 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: {blue: number, red: number}, eras: string[], setupTime: number, callback: CallableFunction = () => {}) {
|
||||
setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: { blue: number, red: number }, eras: string[], setupTime: number, callback: CallableFunction = () => { }) {
|
||||
var command = {
|
||||
"restrictSpawns": restrictSpawns,
|
||||
"restrictToCoalition": restrictToCoalition,
|
||||
@@ -396,13 +413,17 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
reloadDatabases(callback: CallableFunction = () => {}) {
|
||||
reloadDatabases(callback: CallableFunction = () => { }) {
|
||||
var data = { "reloadDatabases": {} };
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
startUpdate() {
|
||||
window.setInterval(() => {
|
||||
/* Clear any existing interval */
|
||||
this.#intervals.forEach((interval: number) => { window.clearInterval(interval); });
|
||||
this.#intervals = [];
|
||||
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused()) {
|
||||
this.getMission((data: MissionData) => {
|
||||
this.checkSessionHash(data.sessionHash);
|
||||
@@ -410,9 +431,9 @@ export class ServerManager {
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}, 1000));
|
||||
|
||||
window.setInterval(() => {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getAirbases((data: AirbasesData) => {
|
||||
this.checkSessionHash(data.sessionHash);
|
||||
@@ -420,19 +441,19 @@ export class ServerManager {
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
}, 10000));
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE){
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getBullseye((data: BullseyesData) => {
|
||||
this.checkSessionHash(data.sessionHash);
|
||||
getApp().getMissionManager()?.updateBullseyes(data);
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
}, 10000);
|
||||
}, 10000));
|
||||
|
||||
window.setInterval(() => {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getLogs((data: any) => {
|
||||
this.checkSessionHash(data.sessionHash);
|
||||
@@ -440,41 +461,41 @@ export class ServerManager {
|
||||
return data.time;
|
||||
});
|
||||
}
|
||||
}, 1000);
|
||||
}, 1000));
|
||||
|
||||
window.setInterval(() => {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, false);
|
||||
}
|
||||
}, 250);
|
||||
}, 250));
|
||||
|
||||
window.setInterval(() => {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, false);
|
||||
}
|
||||
}, 250);
|
||||
}, 250));
|
||||
|
||||
window.setInterval(() => {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
this.#serverIsPaused = ( elapsedMissionTime === this.#previousMissionElapsedTime );
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
this.#serverIsPaused = (elapsedMissionTime === this.#previousMissionElapsedTime);
|
||||
this.#previousMissionElapsedTime = elapsedMissionTime;
|
||||
|
||||
const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
|
||||
if ( this.getConnected() ) {
|
||||
if ( this.getServerIsPaused() ) {
|
||||
if (this.getConnected()) {
|
||||
if (this.getServerIsPaused()) {
|
||||
csp.showServerPaused();
|
||||
} else {
|
||||
csp.showConnected();
|
||||
@@ -484,33 +505,33 @@ export class ServerManager {
|
||||
}
|
||||
|
||||
}
|
||||
}, ( this.getServerIsPaused() ? 500 : 5000 ));
|
||||
}, (this.getServerIsPaused() ? 500 : 5000)));
|
||||
|
||||
// Mission clock and elapsed time
|
||||
window.setInterval( () => {
|
||||
|
||||
if ( !this.getConnected() || this.#serverIsPaused ) {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
|
||||
if (!this.getConnected() || this.#serverIsPaused) {
|
||||
return;
|
||||
}
|
||||
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
|
||||
const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
const mt = getApp().getMissionManager().getDateAndTime().time;
|
||||
const mt = getApp().getMissionManager().getDateAndTime().time;
|
||||
|
||||
csp.setMissionTime( [ mt.h, mt.m, mt.s ].map( n => zeroAppend( n, 2 )).join( ":" ) );
|
||||
csp.setElapsedTime( new Date( elapsedMissionTime * 1000 ).toISOString().substring( 11, 19 ) );
|
||||
csp.setMissionTime([mt.h, mt.m, mt.s].map(n => zeroAppend(n, 2)).join(":"));
|
||||
csp.setElapsedTime(new Date(elapsedMissionTime * 1000).toISOString().substring(11, 19));
|
||||
|
||||
}, 1000 );
|
||||
}, 1000));
|
||||
|
||||
window.setInterval(() => {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
}
|
||||
}, 5000);
|
||||
}, 5000));
|
||||
}
|
||||
|
||||
refreshAll() {
|
||||
@@ -533,12 +554,12 @@ export class ServerManager {
|
||||
});
|
||||
|
||||
this.getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
|
||||
this.getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
}
|
||||
@@ -580,4 +601,8 @@ export class ServerManager {
|
||||
getServerIsPaused() {
|
||||
return this.#serverIsPaused;
|
||||
}
|
||||
|
||||
getRequests() {
|
||||
return this.#requests;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import { Unit } from "./unit";
|
||||
|
||||
export interface ContextActionOptions {
|
||||
isScenic?: boolean
|
||||
}
|
||||
|
||||
export class ContextAction {
|
||||
#id: string = "";
|
||||
#label: string = "";
|
||||
@@ -7,13 +11,18 @@ export class ContextAction {
|
||||
#callback: CallableFunction | null = null;
|
||||
#units: Unit[] = [];
|
||||
#hideContextAfterExecution: boolean = true
|
||||
#options: ContextActionOptions;
|
||||
|
||||
constructor(id: string, label: string, description: string, callback: CallableFunction, hideContextAfterExecution: boolean = true) {
|
||||
this.#id = id;
|
||||
constructor(id: string, label: string, description: string, callback: CallableFunction, hideContextAfterExecution: boolean = true, options: ContextActionOptions) {
|
||||
this.#id = id;
|
||||
this.#label = label;
|
||||
this.#description = description;
|
||||
this.#callback = callback;
|
||||
this.#hideContextAfterExecution = hideContextAfterExecution;
|
||||
this.#options = {
|
||||
"isScenic": false,
|
||||
...options
|
||||
}
|
||||
}
|
||||
|
||||
addUnit(unit: Unit) {
|
||||
@@ -28,6 +37,10 @@ export class ContextAction {
|
||||
return this.#label;
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return this.#options;
|
||||
}
|
||||
|
||||
getDescription() {
|
||||
return this.#description;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ContextAction } from "./contextaction";
|
||||
import { ContextAction, ContextActionOptions } from "./contextaction";
|
||||
import { Unit } from "./unit";
|
||||
|
||||
export class ContextActionSet {
|
||||
@@ -8,9 +8,11 @@ export class ContextActionSet {
|
||||
|
||||
}
|
||||
|
||||
addContextAction(unit: Unit, id: string, label: string, description: string, callback: CallableFunction, hideContextAfterExecution: boolean = true) {
|
||||
addContextAction(unit: Unit, id: string, label: string, description: string, callback: CallableFunction, hideContextAfterExecution: boolean = true, options?:ContextActionOptions) {
|
||||
options = options || {};
|
||||
|
||||
if (!(id in this.#contextActions)) {
|
||||
this.#contextActions[id] = new ContextAction(id, label, description, callback, hideContextAfterExecution);
|
||||
this.#contextActions[id] = new ContextAction(id, label, description, callback, hideContextAfterExecution, options);
|
||||
}
|
||||
this.#contextActions[id].addUnit(unit);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getApp } from "../..";
|
||||
import { GAME_MASTER } from "../../constants/constants";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class AircraftDatabase extends UnitDatabase {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getApp } from "../..";
|
||||
import { GAME_MASTER } from "../../constants/constants";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
|
||||
export class UnitDatabase {
|
||||
export abstract class UnitDatabase {
|
||||
blueprints: { [key: string]: UnitBlueprint } = {};
|
||||
#url: string;
|
||||
|
||||
@@ -31,9 +31,7 @@ export class UnitDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "";
|
||||
}
|
||||
abstract getCategory(): string;
|
||||
|
||||
/* Gets a specific blueprint by name */
|
||||
getByName(name: string) {
|
||||
@@ -240,4 +238,15 @@ export class UnitDatabase {
|
||||
getSpawnPointsByName(name: string) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
getUnkownUnit(name: string): UnitBlueprint {
|
||||
return {
|
||||
name: name,
|
||||
enabled: true,
|
||||
coalition: 'neutral',
|
||||
era: 'N/A',
|
||||
label: name,
|
||||
shortLabel: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
65
client/src/unit/importexport/unitdatafile.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { Dialog } from "../../dialog/dialog";
|
||||
import { createCheckboxOption } from "../../other/utils";
|
||||
|
||||
var categoryMap = {
|
||||
"Aircraft": "Aircraft",
|
||||
"Helicopter": "Helicopter",
|
||||
"GroundUnit": "Ground units",
|
||||
"NavyUnit": "Naval units"
|
||||
}
|
||||
|
||||
export abstract class UnitDataFile {
|
||||
|
||||
protected data: any;
|
||||
protected dialog!: Dialog;
|
||||
|
||||
constructor() { }
|
||||
|
||||
buildCategoryCoalitionTable() {
|
||||
|
||||
const categories = this.#getCategoriesFromData();
|
||||
const coalitions = ["blue", "neutral", "red"];
|
||||
|
||||
let headersHTML: string = ``;
|
||||
let matrixHTML: string = ``;
|
||||
|
||||
categories.forEach((category: string, index) => {
|
||||
matrixHTML += `<tr><td>${categoryMap[category as keyof typeof categoryMap]}</td>`;
|
||||
|
||||
coalitions.forEach((coalition: string) => {
|
||||
if (index === 0)
|
||||
headersHTML += `<th data-coalition="${coalition}">${coalition[0].toUpperCase() + coalition.substring(1)}</th>`;
|
||||
|
||||
const optionIsValid = this.data[category].hasOwnProperty(coalition);
|
||||
let checkboxHTML = createCheckboxOption(`${category}:${coalition}`, category, optionIsValid, () => { }, {
|
||||
"disabled": !optionIsValid,
|
||||
"name": "category-coalition-selection",
|
||||
"readOnly": !optionIsValid
|
||||
}).outerHTML;
|
||||
|
||||
if (optionIsValid)
|
||||
checkboxHTML = checkboxHTML.replace(`"checkbox"`, `"checkbox" checked`); // inner and outerHTML screw default checked up
|
||||
|
||||
matrixHTML += `<td data-coalition="${coalition}">${checkboxHTML}</td>`;
|
||||
|
||||
});
|
||||
matrixHTML += "</tr>";
|
||||
});
|
||||
|
||||
const table = <HTMLTableElement>this.dialog.getElement().querySelector("table.categories-coalitions");
|
||||
|
||||
(<HTMLElement>table.tHead).innerHTML = `<tr><td> </td>${headersHTML}</tr>`;
|
||||
(<HTMLElement>table.querySelector(`tbody`)).innerHTML = matrixHTML;
|
||||
|
||||
}
|
||||
|
||||
#getCategoriesFromData() {
|
||||
const categories = Object.keys(this.data);
|
||||
categories.sort();
|
||||
return categories;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
97
client/src/unit/importexport/unitdatafileexport.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { getApp } from "../..";
|
||||
import { Dialog } from "../../dialog/dialog";
|
||||
import { zeroAppend } from "../../other/utils";
|
||||
import { Unit } from "../unit";
|
||||
import { UnitDataFile } from "./unitdatafile";
|
||||
|
||||
export class UnitDataFileExport extends UnitDataFile {
|
||||
|
||||
protected data!: any;
|
||||
protected dialog: Dialog;
|
||||
#element!: HTMLElement;
|
||||
#filename: string = "export.json";
|
||||
|
||||
constructor(elementId: string) {
|
||||
super();
|
||||
this.dialog = new Dialog(elementId);
|
||||
this.#element = this.dialog.getElement();
|
||||
|
||||
this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => {
|
||||
this.#doExport();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form to start the export journey
|
||||
*/
|
||||
showForm(units: Unit[]) {
|
||||
const data: any = {};
|
||||
const unitCanBeExported = (unit: Unit) => !["Aircraft", "Helicopter"].includes(unit.getCategory());
|
||||
|
||||
units.filter((unit: Unit) => unit.getAlive() && unitCanBeExported(unit)).forEach((unit: Unit) => {
|
||||
const category = unit.getCategory();
|
||||
const coalition = unit.getCoalition();
|
||||
|
||||
if (!data.hasOwnProperty(category)) {
|
||||
data[category] = {};
|
||||
}
|
||||
|
||||
if (!data[category].hasOwnProperty(coalition))
|
||||
data[category][coalition] = [];
|
||||
|
||||
data[category][coalition].push(unit);
|
||||
});
|
||||
|
||||
this.data = data;
|
||||
this.buildCategoryCoalitionTable();
|
||||
this.dialog.show();
|
||||
|
||||
const date = new Date();
|
||||
this.#filename = `olympus_${getApp().getMissionManager().getTheatre().replace(/[^\w]/gi, "").toLowerCase()}_${date.getFullYear()}${zeroAppend(date.getMonth() + 1, 2)}${zeroAppend(date.getDate(), 2)}_${zeroAppend(date.getHours(), 2)}${zeroAppend(date.getMinutes(), 2)}${zeroAppend(date.getSeconds(), 2)}.json`;
|
||||
var input = this.#element.querySelector("#export-filename") as HTMLInputElement;
|
||||
input.onchange = (ev: Event) => {
|
||||
this.#filename = (ev.currentTarget as HTMLInputElement).value;
|
||||
}
|
||||
if (input)
|
||||
input.value = this.#filename;
|
||||
}
|
||||
|
||||
#doExport() {
|
||||
|
||||
let selectedUnits: Unit[] = [];
|
||||
|
||||
this.#element.querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach(<HTMLInputElement>(checkbox: HTMLInputElement) => {
|
||||
if (checkbox instanceof HTMLInputElement) {
|
||||
const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
|
||||
selectedUnits = selectedUnits.concat(this.data[category][coalition]);
|
||||
}
|
||||
});
|
||||
|
||||
if (selectedUnits.length === 0) {
|
||||
alert("Please select at least one option for export.");
|
||||
return;
|
||||
}
|
||||
|
||||
var unitsToExport: { [key: string]: any } = {};
|
||||
selectedUnits.forEach((unit: Unit) => {
|
||||
var data: any = unit.getData();
|
||||
if (unit.getGroupName() in unitsToExport)
|
||||
unitsToExport[unit.getGroupName()].push(data);
|
||||
else
|
||||
unitsToExport[unit.getGroupName()] = [data];
|
||||
});
|
||||
|
||||
|
||||
const a = document.createElement("a");
|
||||
const file = new Blob([JSON.stringify(unitsToExport)], { type: 'text/plain' });
|
||||
a.href = URL.createObjectURL(file);
|
||||
|
||||
var filename = this.#filename;
|
||||
if (!this.#filename.toLowerCase().endsWith(".json"))
|
||||
filename += ".json";
|
||||
a.download = filename;
|
||||
a.click();
|
||||
this.dialog.hide();
|
||||
}
|
||||
|
||||
}
|
||||
138
client/src/unit/importexport/unitdatafileimport.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { getApp } from "../..";
|
||||
import { Dialog } from "../../dialog/dialog";
|
||||
import { UnitData } from "../../interfaces";
|
||||
import { UnitDataFile } from "./unitdatafile";
|
||||
|
||||
export class UnitDataFileImport extends UnitDataFile {
|
||||
|
||||
protected data!: any;
|
||||
protected dialog: Dialog;
|
||||
#fileData!: { [key: string]: UnitData[] };
|
||||
|
||||
constructor(elementId: string) {
|
||||
super();
|
||||
this.dialog = new Dialog(elementId);
|
||||
this.dialog.getElement().querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => {
|
||||
this.#doImport();
|
||||
this.dialog.hide();
|
||||
});
|
||||
}
|
||||
|
||||
#doImport() {
|
||||
|
||||
let selectedCategories: any = {};
|
||||
const unitsManager = getApp().getUnitsManager();
|
||||
|
||||
this.dialog.getElement().querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach(<HTMLInputElement>(checkbox: HTMLInputElement) => {
|
||||
if (checkbox instanceof HTMLInputElement) {
|
||||
const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
|
||||
selectedCategories[category] = selectedCategories[category] || {};
|
||||
selectedCategories[category][coalition] = true;
|
||||
}
|
||||
});
|
||||
|
||||
for (const [groupName, groupData] of Object.entries(this.#fileData)) {
|
||||
if (groupName === "" || groupData.length === 0 || !this.#unitGroupDataCanBeImported(groupData))
|
||||
continue;
|
||||
|
||||
let { category, coalition } = groupData[0];
|
||||
|
||||
if (!selectedCategories.hasOwnProperty(category)
|
||||
|| !selectedCategories[category].hasOwnProperty(coalition)
|
||||
|| selectedCategories[category][coalition] !== true)
|
||||
continue;
|
||||
|
||||
let unitsToSpawn = groupData.filter((unitData: UnitData) => this.#unitDataCanBeImported(unitData)).map((unitData: UnitData) => {
|
||||
return { unitType: unitData.name, location: unitData.position, liveryID: "" }
|
||||
});
|
||||
|
||||
unitsManager.spawnUnits(category, unitsToSpawn, coalition, false);
|
||||
}
|
||||
|
||||
/*
|
||||
for (let groupName in groups) {
|
||||
if (groupName !== "" && groups[groupName].length > 0 && (groups[groupName].every((unit: UnitData) => { return unit.category == "GroundUnit"; }) || groups[groupName].every((unit: any) => { return unit.category == "NavyUnit"; }))) {
|
||||
var aliveUnits = groups[groupName].filter((unit: UnitData) => { return unit.alive });
|
||||
var units = aliveUnits.map((unit: UnitData) => {
|
||||
return { unitType: unit.name, location: unit.position, liveryID: "" }
|
||||
});
|
||||
getApp().getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
|
||||
}
|
||||
}
|
||||
//*/
|
||||
}
|
||||
|
||||
#showForm() {
|
||||
const data: any = {};
|
||||
|
||||
for (const [group, units] of Object.entries(this.#fileData)) {
|
||||
if (group === "" || units.length === 0)
|
||||
continue;
|
||||
|
||||
if (units.some((unit: UnitData) => !this.#unitDataCanBeImported(unit)))
|
||||
continue;
|
||||
|
||||
const category = units[0].category;
|
||||
|
||||
if (!data.hasOwnProperty(category)) {
|
||||
data[category] = {};
|
||||
}
|
||||
|
||||
units.forEach((unit: UnitData) => {
|
||||
if (!data[category].hasOwnProperty(unit.coalition))
|
||||
data[category][unit.coalition] = [];
|
||||
|
||||
data[category][unit.coalition].push(unit);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
groups.filter((unit:Unit) => unitCanBeImported(unit)).forEach((unit:Unit) => {
|
||||
const category = unit.getCategoryLabel();
|
||||
const coalition = unit.getCoalition();
|
||||
|
||||
if (!data.hasOwnProperty(category)) {
|
||||
data[category] = {};
|
||||
}
|
||||
|
||||
if (!data[category].hasOwnProperty(coalition))
|
||||
data[category][coalition] = [];
|
||||
|
||||
data[category][coalition].push(unit);
|
||||
});
|
||||
//*/
|
||||
this.data = data;
|
||||
this.buildCategoryCoalitionTable();
|
||||
this.dialog.show();
|
||||
}
|
||||
|
||||
selectFile() {
|
||||
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 = (e: any) => {
|
||||
this.#fileData = JSON.parse(e.target.result);
|
||||
this.#showForm();
|
||||
};
|
||||
reader.readAsText(file);
|
||||
})
|
||||
input.click();
|
||||
}
|
||||
|
||||
#unitDataCanBeImported(unitData: UnitData) {
|
||||
return unitData.alive && this.#unitGroupDataCanBeImported([unitData]);
|
||||
}
|
||||
|
||||
#unitGroupDataCanBeImported(unitGroupData: UnitData[]) {
|
||||
return unitGroupData.every((unitData: UnitData) => {
|
||||
return !["Aircraft", "Helicopter"].includes(unitData.category);
|
||||
}) && unitGroupData.some((unitData: UnitData) => unitData.alive);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point, Circle } from 'leaflet';
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet';
|
||||
import { getApp } from '..';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad, ftToM, getGroundElevation, coalitionToEnum, nmToFt, nmToM } from '../other/utils';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad, ftToM, getGroundElevation, coalitionToEnum, nmToFt, nmToM } from '../other/utils';
|
||||
import { CustomMarker } from '../map/markers/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { UnitDatabase } from './databases/unitdatabase';
|
||||
import { TargetMarker } from '../map/markers/targetmarker';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION } from '../constants/constants';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants';
|
||||
import { DataExtractor } from '../server/dataextractor';
|
||||
import { groundUnitDatabase } from './databases/groundunitdatabase';
|
||||
import { navyUnitDatabase } from './databases/navyunitdatabase';
|
||||
@@ -14,7 +14,7 @@ import { Ammo, Contact, GeneralSettings, LoadoutBlueprint, ObjectIconOptions, Of
|
||||
import { RangeCircle } from "../map/rangecircle";
|
||||
import { Group } from './group';
|
||||
import { ContextActionSet } from './contextactionset';
|
||||
import { ContextAction } from './contextaction';
|
||||
import * as turf from "@turf/turf";
|
||||
|
||||
var pathIcon = new Icon({
|
||||
iconUrl: '/resources/theme/images/markers/marker-icon.png',
|
||||
@@ -45,6 +45,7 @@ export abstract class Unit extends CustomMarker {
|
||||
#horizontalVelocity: number = 0;
|
||||
#verticalVelocity: number = 0;
|
||||
#heading: number = 0;
|
||||
#track: number = 0;
|
||||
#isActiveTanker: boolean = false;
|
||||
#isActiveAWACS: boolean = false;
|
||||
#onOff: boolean = true;
|
||||
@@ -127,6 +128,7 @@ export abstract class Unit extends CustomMarker {
|
||||
getHorizontalVelocity() { return this.#horizontalVelocity };
|
||||
getVerticalVelocity() { return this.#verticalVelocity };
|
||||
getHeading() { return this.#heading };
|
||||
getTrack() { return this.#track };
|
||||
getIsActiveAWACS() { return this.#isActiveAWACS };
|
||||
getIsActiveTanker() { return this.#isActiveTanker };
|
||||
getOnOff() { return this.#onOff };
|
||||
@@ -192,15 +194,17 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
/* Deselect units if they are hidden */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
|
||||
this.#updateMarker();
|
||||
this.setSelected(this.getSelected() && !this.getHidden());
|
||||
});
|
||||
|
||||
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
|
||||
document.addEventListener("toggleMarkerVisibility", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
this.setSelected(this.getSelected() && !this.getHidden());
|
||||
});
|
||||
|
||||
/* Update the marker when the visibility options change */
|
||||
document.addEventListener("mapVisibilityOptionsChanged", (ev: CustomEventInit) => {
|
||||
/* Update the marker when the options change */
|
||||
document.addEventListener("mapOptionsChanged", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
@@ -233,6 +237,26 @@ export abstract class Unit extends CustomMarker {
|
||||
*/
|
||||
abstract appendContextActions(contextActionSet: ContextActionSet, targetUnit: Unit | null, targetPosition: LatLng | null): void;
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns string containing the marker category
|
||||
*/
|
||||
abstract getMarkerCategory(): string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns string containing the default marker
|
||||
*/
|
||||
abstract getDefaultMarker(): string;
|
||||
|
||||
/** Get the category but for display use - for the user. (i.e. has spaces in it)
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
getCategoryLabel() {
|
||||
return ((GROUND_UNIT_AIR_DEFENCE_REGEX.test(this.getType())) ? "Air Defence" : this.getCategory()).replace(/([a-z])([A-Z])/g, "$1 $2");
|
||||
}
|
||||
|
||||
/********************** Unit data *************************/
|
||||
/** This function is called by the units manager to update all the data coming from the backend. It reads the binary raw data using a DataExtractor
|
||||
*
|
||||
@@ -264,6 +288,7 @@ export abstract class Unit extends CustomMarker {
|
||||
case DataIndexes.horizontalVelocity: this.#horizontalVelocity = dataExtractor.extractFloat64(); break;
|
||||
case DataIndexes.verticalVelocity: this.#verticalVelocity = dataExtractor.extractFloat64(); break;
|
||||
case DataIndexes.heading: this.#heading = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
case DataIndexes.track: this.#track = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
case DataIndexes.isActiveTanker: this.#isActiveTanker = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.isActiveAWACS: this.#isActiveAWACS = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.onOff: this.#onOff = dataExtractor.extractBool(); break;
|
||||
@@ -313,7 +338,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
/* If the unit is selected or if the view is centered on this unit, sent the update signal so that other elements like the UnitControlPanel can be updated. */
|
||||
if (this.getSelected() || getApp().getMap().getCenterUnit() === this)
|
||||
if (this.getSelected() || getApp().getMap().getCenteredOnUnit() === this)
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
@@ -324,6 +349,7 @@ export abstract class Unit extends CustomMarker {
|
||||
getData(): UnitData {
|
||||
return {
|
||||
category: this.getCategory(),
|
||||
categoryDisplayName: this.getCategoryLabel(),
|
||||
ID: this.ID,
|
||||
alive: this.#alive,
|
||||
human: this.#human,
|
||||
@@ -341,6 +367,7 @@ export abstract class Unit extends CustomMarker {
|
||||
horizontalVelocity: this.#horizontalVelocity,
|
||||
verticalVelocity: this.#verticalVelocity,
|
||||
heading: this.#heading,
|
||||
track: this.#track,
|
||||
isActiveTanker: this.#isActiveTanker,
|
||||
isActiveAWACS: this.#isActiveAWACS,
|
||||
onOff: this.#onOff,
|
||||
@@ -371,14 +398,6 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns string containing the marker category
|
||||
*/
|
||||
getMarkerCategory(): string {
|
||||
return getMarkerCategoryByName(this.getName());
|
||||
}
|
||||
|
||||
/** Get a database of information also in this unit's category
|
||||
*
|
||||
* @returns UnitDatabase
|
||||
@@ -413,7 +432,7 @@ export abstract class Unit extends CustomMarker {
|
||||
else {
|
||||
this.#clearContacts();
|
||||
this.#clearPath();
|
||||
this.#clearTarget();
|
||||
this.#clearTargetPosition();
|
||||
}
|
||||
|
||||
/* When the group leader is selected, if grouping is active, all the other group members are also selected */
|
||||
@@ -448,7 +467,7 @@ export abstract class Unit extends CustomMarker {
|
||||
return this.#selected;
|
||||
}
|
||||
|
||||
/** Set the number of the hotgroup to which the unit belongs
|
||||
/** Set the number of the hotgroup to which the unit belongss
|
||||
*
|
||||
* @param hotgroup (number)
|
||||
*/
|
||||
@@ -522,7 +541,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
getDatabaseEntry() {
|
||||
return this.getDatabase()?.getByName(this.#name);
|
||||
return this.getDatabase()?.getByName(this.#name) ?? this.getDatabase()?.getUnkownUnit(this.getName());
|
||||
}
|
||||
|
||||
getGroup() {
|
||||
@@ -546,6 +565,14 @@ export abstract class Unit extends CustomMarker {
|
||||
return false;
|
||||
}
|
||||
|
||||
isControlledByDCS() {
|
||||
return this.getControlled() === false && this.getHuman() === false;
|
||||
}
|
||||
|
||||
isControlledByOlympus() {
|
||||
return this.getControlled() === true;
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void {
|
||||
/* Set the icon */
|
||||
@@ -592,7 +619,7 @@ export abstract class Unit extends CustomMarker {
|
||||
/* If a unit does not belong to the commanded coalition or it is not visually detected, show it with the generic aircraft square */
|
||||
var marker;
|
||||
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)))
|
||||
marker = this.getDatabaseEntry()?.markerFile ?? this.getMarkerCategory();
|
||||
marker = this.getDatabaseEntry()?.markerFile ?? this.getDefaultMarker();
|
||||
else
|
||||
marker = "aircraft";
|
||||
img.src = `/resources/theme/images/units/${marker}.svg`;
|
||||
@@ -672,9 +699,11 @@ export abstract class Unit extends CustomMarker {
|
||||
const hiddenTypes = getApp().getMap().getHiddenTypes();
|
||||
var hidden = (
|
||||
/* Hide the unit if it is a human and humans are hidden */
|
||||
(this.#human && hiddenTypes.includes("human")) ||
|
||||
/* Hide the unit if it is DCS controlled and DCS controlled units are hidden */
|
||||
(this.#controlled == false && hiddenTypes.includes("dcs")) ||
|
||||
(this.getHuman() && hiddenTypes.includes("human")) ||
|
||||
/* Hide the unit if it is DCS-controlled and DCS controlled units are hidden */
|
||||
(this.isControlledByDCS() && hiddenTypes.includes("dcs")) ||
|
||||
/* Hide the unit if it is Olympus-controlled and Olympus-controlled units are hidden */
|
||||
(this.isControlledByOlympus() && hiddenTypes.includes("olympus")) ||
|
||||
/* Hide the unit if this specific category is hidden */
|
||||
(hiddenTypes.includes(this.getMarkerCategory())) ||
|
||||
/* Hide the unit if this coalition is hidden */
|
||||
@@ -682,14 +711,11 @@ export abstract class Unit extends CustomMarker {
|
||||
/* Hide the unit if it does not belong to the commanded coalition and it is not detected by a method that can pinpoint its location (RWR does not count) */
|
||||
(!this.belongsToCommandedCoalition() && (this.#detectionMethods.length == 0 || (this.#detectionMethods.length == 1 && this.#detectionMethods[0] === RWR))) ||
|
||||
/* Hide the unit if grouping is activated, the unit is not the group leader, it is not selected, and the zoom is higher than the grouping threshold */
|
||||
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION &&
|
||||
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && !this.getSelected() && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION &&
|
||||
(this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0))));
|
||||
|
||||
/* Force dead units to be hidden */
|
||||
this.setHidden(hidden || !this.getAlive());
|
||||
|
||||
/* Force hidden units to be unselected */
|
||||
this.setSelected(this.getSelected() && !this.getHidden());
|
||||
}
|
||||
|
||||
setHidden(hidden: boolean) {
|
||||
@@ -773,7 +799,7 @@ export abstract class Unit extends CustomMarker {
|
||||
return this.getDatabaseEntry()?.canAAA === true;
|
||||
}
|
||||
|
||||
indirectFire() {
|
||||
isIndirectFire() {
|
||||
return this.getDatabaseEntry()?.indirectFire === true;
|
||||
}
|
||||
|
||||
@@ -930,6 +956,7 @@ export abstract class Unit extends CustomMarker {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Remove coalition
|
||||
scenicAAA() {
|
||||
var coalition = "neutral";
|
||||
if (this.getCoalition() === "red")
|
||||
@@ -939,6 +966,7 @@ export abstract class Unit extends CustomMarker {
|
||||
getApp().getServerManager().scenicAAA(this.ID, coalition);
|
||||
}
|
||||
|
||||
// TODO: Remove coalition
|
||||
missOnPurpose() {
|
||||
var coalition = "neutral";
|
||||
if (this.getCoalition() === "red")
|
||||
@@ -1162,7 +1190,7 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
/* Rotate elements according to heading */
|
||||
element.querySelectorAll("[data-rotate-to-heading]").forEach(el => {
|
||||
const headingDeg = rad2deg(this.#heading);
|
||||
const headingDeg = rad2deg(this.#track);
|
||||
let currentStyle = el.getAttribute("style") || "";
|
||||
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
|
||||
});
|
||||
@@ -1252,11 +1280,13 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
#clearPath() {
|
||||
for (let WP in this.#pathMarkers) {
|
||||
getApp().getMap().removeLayer(this.#pathMarkers[WP]);
|
||||
if (this.#pathPolyline.getLatLngs().length != 0) {
|
||||
for (let WP in this.#pathMarkers) {
|
||||
getApp().getMap().removeLayer(this.#pathMarkers[WP]);
|
||||
}
|
||||
this.#pathMarkers = [];
|
||||
this.#pathPolyline.setLatLngs([]);
|
||||
}
|
||||
this.#pathMarkers = [];
|
||||
this.#pathPolyline.setLatLngs([]);
|
||||
}
|
||||
|
||||
#drawContacts() {
|
||||
@@ -1410,7 +1440,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
else
|
||||
this.#clearTarget();
|
||||
this.#clearTargetPosition();
|
||||
}
|
||||
|
||||
#drawTargetPosition(targetPosition: LatLng) {
|
||||
@@ -1419,10 +1449,25 @@ export abstract class Unit extends CustomMarker {
|
||||
if (!getApp().getMap().hasLayer(this.#targetPositionPolyline))
|
||||
this.#targetPositionPolyline.addTo(getApp().getMap());
|
||||
this.#targetPositionMarker.setLatLng(new LatLng(targetPosition.lat, targetPosition.lng));
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(this.#position.lat, this.#position.lng), new LatLng(targetPosition.lat, targetPosition.lng)])
|
||||
|
||||
if (this.getState() === 'simulate-fire-fight' && this.getShotsScatter() != MAX_SHOTS_SCATTER) {
|
||||
let turfUnitPosition = turf.point([this.getPosition().lng, this.getPosition().lat]);
|
||||
let turfTargetPosition = turf.point([targetPosition.lng, targetPosition.lat]);
|
||||
|
||||
let bearing = turf.bearing(turfUnitPosition, turfTargetPosition);
|
||||
let scatterDistance = turf.distance(turfUnitPosition, turfTargetPosition) * Math.tan((MAX_SHOTS_SCATTER - this.getShotsScatter()) * deg2rad(SHOTS_SCATTER_DEGREES));
|
||||
let destination1 = turf.destination(turfTargetPosition, scatterDistance, bearing + 90);
|
||||
let destination2 = turf.destination(turfTargetPosition, scatterDistance, bearing - 90);
|
||||
|
||||
this.#targetPositionPolyline.setStyle({dashArray: "4, 8"});
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(destination1.geometry.coordinates[1], destination1.geometry.coordinates[0]), new LatLng(this.#position.lat, this.#position.lng), new LatLng(destination2.geometry.coordinates[1], destination2.geometry.coordinates[0])])
|
||||
} else {
|
||||
this.#targetPositionPolyline.setStyle({dashArray: ""});
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(this.#position.lat, this.#position.lng), new LatLng(targetPosition.lat, targetPosition.lng)])
|
||||
}
|
||||
}
|
||||
|
||||
#clearTarget() {
|
||||
#clearTargetPosition() {
|
||||
if (getApp().getMap().hasLayer(this.#targetPositionMarker))
|
||||
this.#targetPositionMarker.removeFrom(getApp().getMap());
|
||||
|
||||
@@ -1492,6 +1537,14 @@ export class Aircraft extends AirUnit {
|
||||
contextActionSet.addContextAction(this, "refuel", "Refuel", "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB", (units: Unit[]) => { getApp().getUnitsManager().refuel(units) });
|
||||
}
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "aircraft";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "aircraft";
|
||||
}
|
||||
}
|
||||
|
||||
export class Helicopter extends AirUnit {
|
||||
@@ -1509,6 +1562,14 @@ export class Helicopter extends AirUnit {
|
||||
if (targetPosition !== null)
|
||||
contextActionSet.addContextAction(this, "land-at-point", "Land here", "land at this precise location", (units: Unit[]) => { getApp().getUnitsManager().landAtPoint(targetPosition, units) });
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "helicopter";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "helicopter";
|
||||
}
|
||||
}
|
||||
|
||||
export class GroundUnit extends Unit {
|
||||
@@ -1549,13 +1610,17 @@ export class GroundUnit extends Unit {
|
||||
if (targetPosition !== null) {
|
||||
if (this.canTargetPoint()) {
|
||||
contextActionSet.addContextAction(this, "fire-at-area", "Fire at area", "Fire at a specific area on the ground", (units: Unit[]) => { getApp().getUnitsManager().fireAtArea(targetPosition, units) });
|
||||
contextActionSet.addContextAction(this, "simulate-fire-fight", "Simulate fire fight", "Simulate a fire fight by shooting randomly in a certain large area. WARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().fireAtArea(targetPosition, units) });
|
||||
contextActionSet.addContextAction(this, "simulate-fire-fight", "Simulate fire fight", "Simulate a fire fight by shooting randomly in a certain large area.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().simulateFireFight(targetPosition, units) });
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.canAAA()) {
|
||||
contextActionSet.addContextAction(this, "scenic-aaa", "Scenic AAA", "Shoot AAA in the air without aiming at any target, when a enemy unit gets close enough. WARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().scenicAAA(units) });
|
||||
contextActionSet.addContextAction(this, "miss-aaa", "Misson on purpose", "Shoot AAA towards the closest enemy unit, but don't aim precisely. WARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().missOnPurpose(units) });
|
||||
contextActionSet.addContextAction(this, "scenic-aaa", "Scenic AAA", "Shoot AAA in the air without aiming at any target, when an enemy unit gets close enough.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().scenicAAA(units) }, undefined, {
|
||||
"isScenic": true
|
||||
});
|
||||
contextActionSet.addContextAction(this, "miss-aaa", "Dynamic accuracy AAA", "Shoot AAA towards the closest enemy unit, but don't aim precisely.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().missOnPurpose(units) }, undefined, {
|
||||
"isScenic": true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1582,9 +1647,9 @@ export class GroundUnit extends Unit {
|
||||
unitWhenGrouped = (member !== null ? member?.getDatabaseEntry()?.unitWhenGrouped : unitWhenGrouped);
|
||||
}
|
||||
if (unitWhenGrouped)
|
||||
return this.getDatabase()?.getByName(unitWhenGrouped);
|
||||
return this.getDatabase()?.getByName(unitWhenGrouped) ?? this.getDatabase()?.getUnkownUnit(this.getName());
|
||||
else
|
||||
return this.getDatabase()?.getByName(this.getName());
|
||||
return this.getDatabase()?.getByName(this.getName()) ?? this.getDatabase()?.getUnkownUnit(this.getName());
|
||||
}
|
||||
|
||||
/* When we zoom past the grouping limit, grouping is enabled and the unit is a leader, we redraw the unit to apply any possible grouped marker */
|
||||
@@ -1593,6 +1658,17 @@ export class GroundUnit extends Unit {
|
||||
(getApp().getMap().getZoom() >= GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() < GROUPING_ZOOM_TRANSITION ||
|
||||
getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() >= GROUPING_ZOOM_TRANSITION))
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
if (/\bAAA|SAM\b/.test(this.getType()) || /\bmanpad|stinger\b/i.test(this.getType()))
|
||||
return "groundunit-sam";
|
||||
else
|
||||
return "groundunit";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "groundunit";
|
||||
}
|
||||
}
|
||||
|
||||
export class NavyUnit extends Unit {
|
||||
@@ -1634,10 +1710,6 @@ export class NavyUnit extends Unit {
|
||||
}
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "navyunit";
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "NavyUnit";
|
||||
}
|
||||
@@ -1646,4 +1718,12 @@ export class NavyUnit extends Unit {
|
||||
var blueprint = navyUnitDatabase.getByName(this.getName());
|
||||
return blueprint?.type ? blueprint.type : "";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "navyunit";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "navyunit";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CoalitionArea } from "../map/coalitionarea/coalitionarea";
|
||||
import { groundUnitDatabase } from "./databases/groundunitdatabase";
|
||||
import { DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants";
|
||||
import { DataExtractor } from "../server/dataextractor";
|
||||
import { citiesDatabase } from "./citiesDatabase";
|
||||
import { citiesDatabase } from "./databases/citiesdatabase";
|
||||
import { aircraftDatabase } from "./databases/aircraftdatabase";
|
||||
import { helicopterDatabase } from "./databases/helicopterdatabase";
|
||||
import { navyUnitDatabase } from "./databases/navyunitdatabase";
|
||||
@@ -16,6 +16,8 @@ import { HotgroupPanel } from "../panels/hotgrouppanel";
|
||||
import { Contact, UnitData, UnitSpawnTable } from "../interfaces";
|
||||
import { Dialog } from "../dialog/dialog";
|
||||
import { Group } from "./group";
|
||||
import { UnitDataFileExport } from "./importexport/unitdatafileexport";
|
||||
import { UnitDataFileImport } from "./importexport/unitdatafileimport";
|
||||
|
||||
/** The UnitsManager handles the creation, update, and control of units. Data is strictly updated by the server ONLY. This means that any interaction from the user will always and only
|
||||
* result in a command to the server, executed by means of a REST PUT request. Any subsequent change in data will be reflected only when the new data is sent back by the server. This strategy allows
|
||||
@@ -29,6 +31,8 @@ export class UnitsManager {
|
||||
#slowDeleteDialog!: Dialog;
|
||||
#units: { [ID: number]: Unit };
|
||||
#groups: { [groupName: string]: Group } = {};
|
||||
#unitDataExport!:UnitDataFileExport;
|
||||
#unitDataImport!:UnitDataFileImport;
|
||||
|
||||
constructor() {
|
||||
this.#copiedUnits = [];
|
||||
@@ -321,11 +325,13 @@ export class UnitsManager {
|
||||
getUnitsVariable(variableGetter: CallableFunction, units: Unit[]) {
|
||||
if (units.length == 0)
|
||||
return undefined;
|
||||
return units.map((unit: Unit) => {
|
||||
return variableGetter(unit);
|
||||
})?.reduce((a: any, b: any) => {
|
||||
return a === b ? a : undefined
|
||||
|
||||
var value: any = variableGetter(units[0]);
|
||||
units.forEach((unit: Unit) => {
|
||||
if (variableGetter(unit) !== value)
|
||||
value = undefined;
|
||||
});
|
||||
return value;
|
||||
};
|
||||
|
||||
/** For a given unit, it returns if and how it is being detected by other units. NOTE: this function will return how a unit is being detected, i.e. how other units are detecting it. It will not return
|
||||
@@ -353,6 +359,7 @@ export class UnitsManager {
|
||||
* @param latlng Position of the new destination
|
||||
* @param mantainRelativePosition If true, the selected units will mantain their relative positions when reaching the target. This is useful to maintain a formation for groun/navy units
|
||||
* @param rotation Rotation in radians by which the formation will be rigidly rotated. E.g. a ( V ) formation will look like this ( < ) if rotated pi/4 radians (90 degrees)
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
addDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -412,6 +419,7 @@ export class UnitsManager {
|
||||
/** Instruct all the selected units to land at a specific location
|
||||
*
|
||||
* @param latlng Location where to land at
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
landAt(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -428,6 +436,7 @@ export class UnitsManager {
|
||||
/** Instruct all the selected units to change their speed
|
||||
*
|
||||
* @param speedChange Speed change, either "stop", "slow", or "fast". The specific value depends on the unit category
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
changeSpeed(speedChange: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -442,6 +451,7 @@ export class UnitsManager {
|
||||
/** Instruct all the selected units to change their altitude
|
||||
*
|
||||
* @param altitudeChange Altitude change, either "climb" or "descend". The specific value depends on the unit category
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
changeAltitude(altitudeChange: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -456,6 +466,7 @@ export class UnitsManager {
|
||||
/** Set a specific speed to all the selected units
|
||||
*
|
||||
* @param speed Value to set, in m/s
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setSpeed(speed: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -471,6 +482,7 @@ export class UnitsManager {
|
||||
/** Set a specific speed type to all the selected units
|
||||
*
|
||||
* @param speedType Value to set, either "CAS" or "GS". If "CAS" is selected, the unit will try to maintain the selected Calibrated Air Speed, but DCS will still only maintain a Ground Speed value so errors may arise depending on wind.
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setSpeedType(speedType: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -486,6 +498,7 @@ export class UnitsManager {
|
||||
/** Set a specific altitude to all the selected units
|
||||
*
|
||||
* @param altitude Value to set, in m
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setAltitude(altitude: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -501,6 +514,7 @@ export class UnitsManager {
|
||||
/** Set a specific altitude type to all the selected units
|
||||
*
|
||||
* @param altitudeType Value to set, either "ASL" or "AGL". If "AGL" is selected, the unit will try to maintain the selected Above Ground Level altitude. Due to a DCS bug, this will only be true at the final position.
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setAltitudeType(altitudeType: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -516,6 +530,7 @@ export class UnitsManager {
|
||||
/** Set a specific ROE to all the selected units
|
||||
*
|
||||
* @param ROE Value to set, see constants for acceptable values
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setROE(ROE: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -531,6 +546,7 @@ export class UnitsManager {
|
||||
/** Set a specific reaction to threat to all the selected units
|
||||
*
|
||||
* @param reactionToThreat Value to set, see constants for acceptable values
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setReactionToThreat(reactionToThreat: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -546,6 +562,7 @@ export class UnitsManager {
|
||||
/** Set a specific emissions & countermeasures to all the selected units
|
||||
*
|
||||
* @param emissionCountermeasure Value to set, see constants for acceptable values
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setEmissionsCountermeasures(emissionCountermeasure: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -561,6 +578,7 @@ export class UnitsManager {
|
||||
/** Turn selected units on or off, only works on ground and navy units
|
||||
*
|
||||
* @param onOff If true, the unit will be turned on
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setOnOff(onOff: boolean, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -576,6 +594,7 @@ export class UnitsManager {
|
||||
/** Instruct the selected units to follow roads, only works on ground units
|
||||
*
|
||||
* @param followRoads If true, units will follow roads
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setFollowRoads(followRoads: boolean, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -591,6 +610,7 @@ export class UnitsManager {
|
||||
/** Instruct selected units to operate as a certain coalition
|
||||
*
|
||||
* @param operateAsBool If true, units will operate as blue
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setOperateAs(operateAsBool: boolean, units: Unit[] | null = null) {
|
||||
var operateAs = operateAsBool ? "blue" : "red";
|
||||
@@ -607,6 +627,7 @@ export class UnitsManager {
|
||||
/** Instruct units to attack a specific unit
|
||||
*
|
||||
* @param ID ID of the unit to attack
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
attackUnit(ID: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -620,7 +641,7 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
/** Instruct units to refuel at the nearest tanker, if possible. Else units will RTB
|
||||
*
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
refuel(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -638,6 +659,7 @@ export class UnitsManager {
|
||||
* @param ID ID of the unit to follow
|
||||
* @param offset Optional parameter, defines a static offset. X: front-rear, positive front, Y: top-bottom, positive top, Z: left-right, positive right
|
||||
* @param formation Optional parameter, defines a predefined formation type. Values are: "trail", "echelon-lh", "echelon-rh", "line-abreast-lh", "line-abreast-rh", "front", "diamond"
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
followUnit(ID: number, offset?: { "x": number, "y": number, "z": number }, formation?: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -690,6 +712,7 @@ export class UnitsManager {
|
||||
/** Instruct the selected units to perform precision bombing of specific coordinates
|
||||
*
|
||||
* @param latlng Location to bomb
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
bombPoint(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -705,6 +728,7 @@ export class UnitsManager {
|
||||
/** Instruct the selected units to perform carpet bombing of specific coordinates
|
||||
*
|
||||
* @param latlng Location to bomb
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
carpetBomb(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -720,6 +744,7 @@ export class UnitsManager {
|
||||
/** Instruct the selected units to fire at specific coordinates
|
||||
*
|
||||
* @param latlng Location to fire at
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
fireAtArea(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -735,6 +760,7 @@ export class UnitsManager {
|
||||
/** Instruct the selected units to simulate a fire fight at specific coordinates
|
||||
*
|
||||
* @param latlng Location to fire at
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
simulateFireFight(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -756,7 +782,7 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
/** Instruct units to enter into scenic AAA mode. Units will shoot in the air without aiming
|
||||
*
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
scenicAAA(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -769,8 +795,8 @@ export class UnitsManager {
|
||||
this.#showActionMessage(units, `unit set to perform scenic AAA`);
|
||||
}
|
||||
|
||||
/** Instruct units to enter into miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
*
|
||||
/** Instruct units to enter into dynamic accuracy/miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
missOnPurpose(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -786,6 +812,7 @@ export class UnitsManager {
|
||||
/** Instruct units to land at specific point
|
||||
*
|
||||
* @param latlng Point where to land
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
landAtPoint(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -802,6 +829,7 @@ export class UnitsManager {
|
||||
/** Set a specific shots scatter to all the selected units
|
||||
*
|
||||
* @param shotsScatter Value to set
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setShotsScatter(shotsScatter: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -817,6 +845,7 @@ export class UnitsManager {
|
||||
/** Set a specific shots intensity to all the selected units
|
||||
*
|
||||
* @param shotsScatter Value to set
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setShotsIntensity(shotsIntensity: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -870,6 +899,7 @@ export class UnitsManager {
|
||||
/** Set the hotgroup for the selected units. It will be the only hotgroup of the unit
|
||||
*
|
||||
* @param hotgroup Hotgroup number
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
setHotgroup(hotgroup: number, units: Unit[] | null = null) {
|
||||
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHotgroup(null));
|
||||
@@ -879,6 +909,7 @@ export class UnitsManager {
|
||||
/** Add the selected units to a hotgroup. Units can be in multiple hotgroups at the same type
|
||||
*
|
||||
* @param hotgroup Hotgroup number
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
addToHotgroup(hotgroup: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
@@ -891,6 +922,7 @@ export class UnitsManager {
|
||||
/** Delete the selected units
|
||||
*
|
||||
* @param explosion If true, the unit will be deleted using an explosion
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
* @returns
|
||||
*/
|
||||
delete(explosion: boolean = false, explosionType: string = "", units: Unit[] | null = null) {
|
||||
@@ -929,6 +961,7 @@ export class UnitsManager {
|
||||
*
|
||||
* @param latlng Center of the group after the translation
|
||||
* @param rotation Rotation of the group, in radians
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
* @returns Array of positions for each unit, in order
|
||||
*/
|
||||
computeGroupDestination(latlng: LatLng, rotation: number, units: Unit[] | null = null) {
|
||||
@@ -972,6 +1005,9 @@ export class UnitsManager {
|
||||
*
|
||||
*/
|
||||
copy(units: Unit[] | null = null) {
|
||||
if ( !getApp().getContextManager().getCurrentContext().getAllowUnitCopying() )
|
||||
return;
|
||||
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true });
|
||||
|
||||
@@ -989,6 +1025,9 @@ export class UnitsManager {
|
||||
* @returns True if units were pasted successfully
|
||||
*/
|
||||
paste() {
|
||||
if ( !getApp().getContextManager().getCurrentContext().getAllowUnitPasting() )
|
||||
return;
|
||||
|
||||
let spawnPoints = 0;
|
||||
|
||||
/* If spawns are restricted, check that the user has the necessary spawn points */
|
||||
@@ -1069,11 +1108,38 @@ export class UnitsManager {
|
||||
const activeEras = Object.keys(eras).filter((key: string) => { return eras[key]; });
|
||||
const activeRanges = Object.keys(ranges).filter((key: string) => { return ranges[key]; });
|
||||
|
||||
var airbases = getApp().getMissionManager().getAirbases();
|
||||
Object.keys(airbases).forEach((airbaseName: string) => {
|
||||
var airbase = airbases[airbaseName];
|
||||
/* Check if the city is inside the coalition area */
|
||||
if (polyContains(new LatLng(airbase.getLatLng().lat, airbase.getLatLng().lng), coalitionArea)) {
|
||||
/* Arbitrary formula to obtain a number of units */
|
||||
var pointsNumber = 2 + 10 * density / 100;
|
||||
for (let i = 0; i < pointsNumber; i++) {
|
||||
/* Place the unit nearby the airbase, depending on the distribution parameter */
|
||||
var bearing = Math.random() * 360;
|
||||
var distance = Math.random() * distribution * 100;
|
||||
const latlng = bearingAndDistanceToLatLng(airbase.getLatLng().lat, airbase.getLatLng().lng, bearing, distance);
|
||||
|
||||
/* Make sure the unit is still inside the coalition area */
|
||||
if (polyContains(latlng, coalitionArea)) {
|
||||
const type = activeTypes[Math.floor(Math.random() * activeTypes.length)];
|
||||
if (Math.random() < IADSDensities[type]) {
|
||||
/* Get a random blueprint depending on the selected parameters and spawn the unit */
|
||||
const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, { type: type, eras: activeEras, ranges: activeRanges });
|
||||
if (unitBlueprint)
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), false, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
citiesDatabase.forEach((city: { lat: number, lng: number, pop: number }) => {
|
||||
/* Check if the city is inside the coalition area */
|
||||
if (polyContains(new LatLng(city.lat, city.lng), coalitionArea)) {
|
||||
/* Arbitrary formula to obtain a number of units depending on the city population */
|
||||
var pointsNumber = 2 + Math.pow(city.pop, 0.2) * density / 100;
|
||||
var pointsNumber = 2 + Math.pow(city.pop, 0.15) * density / 100;
|
||||
for (let i = 0; i < pointsNumber; i++) {
|
||||
/* Place the unit nearby the city, depending on the distribution parameter */
|
||||
var bearing = Math.random() * 360;
|
||||
@@ -1086,8 +1152,8 @@ export class UnitsManager {
|
||||
if (Math.random() < IADSDensities[type]) {
|
||||
/* Get a random blueprint depending on the selected parameters and spawn the unit */
|
||||
const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, { type: type, eras: activeEras, ranges: activeRanges });
|
||||
if (unitBlueprint)
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), true);
|
||||
if (unitBlueprint)
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), false, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1099,52 +1165,18 @@ export class UnitsManager {
|
||||
* TODO: Extend to aircraft and helicopters
|
||||
*/
|
||||
exportToFile() {
|
||||
var unitsToExport: { [key: string]: any } = {};
|
||||
for (let ID in this.#units) {
|
||||
var unit = this.#units[ID];
|
||||
if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) {
|
||||
var data: any = unit.getData();
|
||||
if (unit.getGroupName() in unitsToExport)
|
||||
unitsToExport[unit.getGroupName()].push(data);
|
||||
else
|
||||
unitsToExport[unit.getGroupName()] = [data];
|
||||
}
|
||||
}
|
||||
var a = document.createElement("a");
|
||||
var file = new Blob([JSON.stringify(unitsToExport)], { type: 'text/plain' });
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = 'export.json';
|
||||
a.click();
|
||||
if (!this.#unitDataExport)
|
||||
this.#unitDataExport = new UnitDataFileExport("unit-export-dialog");
|
||||
this.#unitDataExport.showForm(Object.values(this.#units));
|
||||
}
|
||||
|
||||
|
||||
/** Import ground and navy units from file
|
||||
* TODO: extend to support aircraft and helicopters
|
||||
*/
|
||||
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: UnitData) => { return unit.category == "GroundUnit"; }) || groups[groupName].every((unit: any) => { return unit.category == "NavyUnit"; }))) {
|
||||
var aliveUnits = groups[groupName].filter((unit: UnitData) => { return unit.alive });
|
||||
var units = aliveUnits.map((unit: UnitData) => {
|
||||
return { unitType: unit.name, location: unit.position, liveryID: "" }
|
||||
});
|
||||
getApp().getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
})
|
||||
input.click();
|
||||
if (!this.#unitDataImport)
|
||||
this.#unitDataImport = new UnitDataFileImport("unit-import-dialog");
|
||||
this.#unitDataImport.selectFile();
|
||||
}
|
||||
|
||||
/** Spawn a new group of units
|
||||
@@ -1290,7 +1322,7 @@ export class UnitsManager {
|
||||
const map = getApp().getMap();
|
||||
const units = this.getSelectedUnits();
|
||||
const numSelectedUnits = units.length;
|
||||
const numProtectedUnits = units.filter((unit: Unit) => map.unitIsProtected(unit)).length;
|
||||
const numProtectedUnits = units.filter((unit: Unit) => map.getIsUnitProtected(unit)).length;
|
||||
|
||||
if (numProtectedUnits === 1 && numSelectedUnits === numProtectedUnits)
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: unit is protected`);
|
||||
@@ -1300,6 +1332,6 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
#unitIsProtected(unit: Unit) {
|
||||
return getApp().getMap().unitIsProtected(unit)
|
||||
return getApp().getMap().getIsUnitProtected(unit)
|
||||
}
|
||||
}
|
||||
@@ -36,17 +36,9 @@ export class Weapon extends CustomMarker {
|
||||
super(new LatLng(0, 0), { riseOnHover: true, keyboard: false });
|
||||
|
||||
this.ID = ID;
|
||||
|
||||
/* Deselect units if they are hidden */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { !this.getHidden() }, 300);
|
||||
});
|
||||
|
||||
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { !this.getHidden() }, 300);
|
||||
});
|
||||
|
||||
document.addEventListener("mapVisibilityOptionsChanged", (ev: CustomEventInit) => {
|
||||
/* Update the marker when the options change */
|
||||
document.addEventListener("mapOptionsChanged", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<div id="airbase-active-coalition-label" data-coalition="blue"></div>
|
||||
<div class="upper-bar ol-panel">
|
||||
<button data-coalition="blue" id="airbase-aircraft-spawn-button" title="Spawn aircraft" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="airbase-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>
|
||||
data-on-click-params='{ "type": "helicopter" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||
</div>
|
||||
<div id="airbase-aircraft-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="airbase-aircraft-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the aircraft spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="airbase-helicopter-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="airbase-helicopter-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the helicopter spawn menu will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,17 +1,17 @@
|
||||
<div id="coalition-area-contextmenu" class="ol-context-menu" oncontextmenu="return false;">
|
||||
<div id="area-coalition-label" data-coalition="blue"></div>
|
||||
<div class="upper-bar ol-panel">
|
||||
<div id="coalition-area-switch" class="ol-switch ol-coalition-switch"></div>
|
||||
<div class="switch-control coalition no-label"><div id="coalition-area-switch" class="ol-switch"></div></div>
|
||||
<button data-coalition="blue" id="iads-button" title="Create Integrated Air Defense System" data-on-click="coalitionAreaContextMenuShow"
|
||||
data-on-click-params='{ "type": "iads" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "iads" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||
<!--<button data-coalition="blue" id="cap-button" title="Create Combat Air Patrols" data-on-click="coalitionAreaContextMenuShow"
|
||||
data-on-click-params='{ "type": "cap" }' class="ol-contexmenu-button"></button>-->
|
||||
<button data-coalition="blue" id="coalitionarea-back-button" title="Bring area to back" data-on-click="coalitionAreaBringToBack"
|
||||
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/back.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "cap" }' class="ol-context-menu-button"></button>-->
|
||||
<button data-coalition="blue" id="coalitionarea-back-button" title="Send to back" data-on-click="coalitionAreaBringToBack"
|
||||
class="ol-context-menu-button"><img src="/resources/theme/images/buttons/other/back.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="coalitionarea-delete-button" title="Delete area" data-on-click="coalitionAreaDelete"
|
||||
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/delete.svg" inject-svg></button>
|
||||
class="ol-context-menu-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-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<div id="iads-units-type-options" class="ol-select">
|
||||
<div class="ol-select-value">Unit types</div>
|
||||
<div class="ol-select-options">
|
||||
|
||||
@@ -1,60 +1,58 @@
|
||||
<div id="map-contextmenu" class="ol-context-menu" oncontextmenu="return false;">
|
||||
<div id="active-coalition-label" data-coalition="blue"></div>
|
||||
<div class="upper-bar ol-panel">
|
||||
<div id="coalition-switch" class="ol-switch ol-coalition-switch"></div>
|
||||
<div class="switch-control coalition no-label"><div id="coalition-switch" class="ol-switch"></div></div>
|
||||
<button data-coalition="blue" id="aircraft-spawn-button" title="Spawn aircraft" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-context-menu-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>
|
||||
data-on-click-params='{ "type": "helicopter" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="air-defence-spawn-button" title="Spawn air defence unit" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "air-defence" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "air-defence" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/sam.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>
|
||||
data-on-click-params='{ "type": "groundunit" }' class="ol-context-menu-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>
|
||||
class="ol-context-menu-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>
|
||||
data-on-click-params='{ "type": "more" }' class="ol-context-menu-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>
|
||||
data-on-click-params='{ "type": "navyunit" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/navyunit.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="smoke-spawn-button" title="Spawn smoke" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "smoke" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/smoke.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "smoke" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/smoke.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="explosion-spawn-button" title="Explosion" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "explosion" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/explosion.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "explosion" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/explosion.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="polygon-draw-button" title="Enter polygon draw mode" data-on-click="toggleCoalitionAreaDraw"
|
||||
data-on-click-params='{"type": "polygon"}' class="ol-contexmenu-button"><img src="resources/theme/images/buttons/tools/draw-polygon-solid.svg" inject-svg></button>
|
||||
data-on-click-params='{"type": "polygon"}' class="ol-context-menu-button"><img src="resources/theme/images/buttons/tools/draw-polygon-solid.svg" inject-svg></button>
|
||||
</div>
|
||||
<div id="aircraft-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="aircraft-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the aircraft spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="helicopter-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="helicopter-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the helicopter spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="air-defence-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="air-defence-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<!-- Here the air defence units' spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="groundunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="groundunit-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<!-- Here the ground units' spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="navyunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="navyunit-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<!-- Here the navy units' spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="smoke-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="smoke-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<button class="smoke-button" title="" data-smoke-color="white" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "white" }'>White smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="blue" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "blue" }'>Blue smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="red" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "red" }'>Red smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="green" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "green" }'>Green smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="orange" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "orange" }'>Orange smoke</button>
|
||||
</div>
|
||||
<div id="explosion-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="explosion-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "normal", "strength": 1 }'>Small explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "normal", "strength": 10 }'>Big explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "phosphorous"}'>White phosphorous</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "napalm"}'>Napalm</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "secondary"}'>Explosion with secondaries</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "secondary"}'>Explosion with debries</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "fire"}'>Static fire</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "depthCharge"}'>Depth charge</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,5 +1,17 @@
|
||||
<html>
|
||||
|
||||
<script>
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
const theme = urlParams.get('theme');
|
||||
if (theme != undefined) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/resources/theme/" + theme);
|
||||
xmlHttp.send("");
|
||||
console.log("Setting theme to " + theme)
|
||||
}
|
||||
</script>
|
||||
|
||||
<head>
|
||||
<title>Olympus client</title>
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/olympus.css" />
|
||||
@@ -50,6 +62,12 @@
|
||||
|
||||
<!-- Grayout effect of the background when login prompt is visible -->
|
||||
<div id="gray-out"></div>
|
||||
|
||||
<!-- Loading screen -->
|
||||
<div id="loading-screen">
|
||||
<img src="images/olympus-500x500.png">
|
||||
<div>Loading DCS Olympus...</div>
|
||||
</div>
|
||||
|
||||
<script src="javascripts/bundle.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,333 +1,19 @@
|
||||
<div id="splash-screen" class="ol-dialog" oncontextmenu="return false;">
|
||||
<div id="splash-content" class="ol-dialog-content">
|
||||
<div id="app-summary">
|
||||
<h2>DCS Olympus</h2>
|
||||
<h4>Dynamic Unit Command</h4>
|
||||
<div class="app-version">Version <span class="app-version-number">v0.4.7-alpha</span></div>
|
||||
</div>
|
||||
|
||||
<form id="authentication-form">
|
||||
<div><h5>Name</h5> <input type="text" id="username" name="username" required autocomplete="username" placeholder="Enter username..."></div>
|
||||
<div><h5>Server password</h5> <input type="password" id="password" name="password" minlength="8" required autocomplete="current-password" placeholder="Enter password..."></div>
|
||||
<button type="submit" id="connection-button" class="ol-button-apply">Connect</button>
|
||||
</form>
|
||||
|
||||
<h5 id="login-status"><br></h5>
|
||||
|
||||
<div id="legal-stuff">
|
||||
<h5>DISCLAIMER</h5>
|
||||
<p>
|
||||
Copyright (C) 2023 Veltro & Gang
|
||||
</p>
|
||||
<p>
|
||||
DCS Olympus (the "MATERIAL" or "Software") is provided completely free
|
||||
to users subject to the it under both the terms of version 3 of the GNU
|
||||
General Public License as published by the Free Software Foundation, and
|
||||
the additional terms set out below; except where such terms conflict with this
|
||||
disclaimer, in which case, the terms of this disclaimer shall prevail.
|
||||
</p>
|
||||
<p>
|
||||
The authors and/or copyright holders of the Software have not received any
|
||||
financial benefit in connection with the Software. In any event, the
|
||||
Software is provided “as is”, without warranty of any kind, express or
|
||||
implied, including but not limited to the warranties of merchantability,
|
||||
fitness for a particular purpose and non-infringement. In no event shall
|
||||
the authors and/or copyright holders be liable for any claim, damages or
|
||||
other liability, whether in an action of contract, tort or otherwise,
|
||||
arising from, out of or in connection with the Software or the use or o
|
||||
ther dealings in the Software.
|
||||
</p>
|
||||
<p>
|
||||
Any party making use of the Software in any manner agrees to be
|
||||
bound by the terms set out in this disclaimer, version 3 of the GNU
|
||||
General Public Licence, and the Additional Terms below.
|
||||
</p>
|
||||
<p>
|
||||
THIS MATERIAL IS NOT MADE OR SUPPORTED BY EAGLE DYNAMICS SA.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="advanced-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Olympus 1-1</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
|
||||
<!-- General settings -->
|
||||
<div id="general-settings">
|
||||
<div class="ol-group">
|
||||
<h4>General settings</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="general-settings-grid">
|
||||
<div id="prohibit-jettison-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not jettison external stores">
|
||||
<input type="checkbox"/>
|
||||
Prohibit jettison
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-afterburner-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage the afterburner">
|
||||
<input type="checkbox" />
|
||||
Prohibit afterburner
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AA-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage airborne targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/A
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AG-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage ground targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/G
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-air-wpn-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will not engage air weapons (e.g. SAM sites will not engage HARMs)">
|
||||
<input type="checkbox" />
|
||||
Prohibit air wpn engage
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TACAN options -->
|
||||
<div id="TACAN-options">
|
||||
<div class="ol-group">
|
||||
<h4>TACAN options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="TACAN-checkbox" class="ol-checkbox">
|
||||
<label title="Turn ON the TACAN">
|
||||
<input type="checkbox" />
|
||||
Activate TACAN
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>TACAN: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="TACAN-channel" class="ol-text-input">
|
||||
<input type="number" onkeypress='return event.charCode >= 48 && event.charCode <= 57' onkeyup="if (value > 126) value = 126;">
|
||||
</div>
|
||||
|
||||
<div id="TACAN-XY" class="ol-select">
|
||||
<div class="ol-select-value">X</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="TACAN-callsign" class="ol-text-input">
|
||||
<input type="text" maxlength="3" value="TKR" style="width: 50px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Radio options -->
|
||||
<div id="radio-options">
|
||||
<div class="ol-group">
|
||||
<h4>Radio options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio frequency: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-mhz" class="ol-text-input">
|
||||
<input type="number" onkeypress='return event.charCode >= 48 && event.charCode <= 57' onkeyup="if (value > 999) value = 999;">
|
||||
</div>
|
||||
|
||||
<div id="radio-decimals" class="ol-select">
|
||||
<div class="ol-select-value">.000</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio callsign: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-callsign" class="ol-select">
|
||||
<div class="ol-select-value"></div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
-
|
||||
</label>
|
||||
|
||||
<div id="radio-callsign-number" class="ol-text-input">
|
||||
<input type="number" min="1" max="999" step="1" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyAdvancedSettings">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="custom-formation-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Custom formation</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div class="formation-position-clock">
|
||||
<div class="clock-hand" style="top: 0px; left: 50px;"><input type="radio" id="formation-1" name="formation-position" value="1"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 14px;"><input type="radio" id="formation-2" name="formation-position" value="2"></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 0px; "><input type="radio" id="formation-3" name="formation-position" value="3"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 14px;"><input type="radio" id="formation-4" name="formation-position" value="4"></div>
|
||||
<div class="clock-hand" style="top: 100px; left: 50px;"><input type="radio" id="formation-5" name="formation-position" value="5"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 86px;"><input type="radio" id="formation-6" name="formation-position" value="6" checked></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 100px"><input type="radio" id="formation-7" name="formation-position" value="7"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 86px;"><input type="radio" id="formation-8" name="formation-position" value="8"></div>
|
||||
<div style="top: 50px; left: 50px;" id="reference-system"></div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Distance: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="distance" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="150">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Up-down: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="up-down" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="30">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyCustomFormation">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="command-mode-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Command mode settings</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div id="restrict-spawns" class="ol-checkbox">
|
||||
<label title="If false, no spawn restrictions will be applied">
|
||||
<input type="checkbox"/>
|
||||
Restrict spawns
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="restrict-to-coalition" class="ol-checkbox">
|
||||
<label title="If true, commanders will be allowed to only spawn units that belong to their coalition. E.g. blue commanders will be able to spawn F/A-18 Hornets, but not MiG-29s.">
|
||||
<input type="checkbox"/>
|
||||
Restrict units to coalition
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Setup time: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="setup-time" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="10">
|
||||
</div>
|
||||
<label>minutes</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Available eras: </label>
|
||||
|
||||
<div id="command-mode-era-options" class="ol-select">
|
||||
<div class="ol-select-value">Select eras</div>
|
||||
<div class="ol-select-options">
|
||||
<!-- This is where all the available era buttons will be shown-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<h4>Spawn points</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Blue spawn points: </label>
|
||||
<div id="blue-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Red spawn points: </label>
|
||||
<div id="red-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applycommandModeOptions">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="slow-delete-dialog" class="ol-panel ol-dialog hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Confirm deletion method</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<p>You are trying to delete a large amount of units (<span class="deletion-count"></span>), which can cause the server to lag for players.</p>
|
||||
<p>You may:
|
||||
<ul>
|
||||
<li>delete in batches (less lag but Olympus cannot process any additional orders until<br /> all units have been deleted);</li>
|
||||
<li>delete immediately (you can continue to give Olympus orders but players may<br />experience lag while this happens);</li>
|
||||
<li>cancel this instruction.</li>
|
||||
</ul></p>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button data-action="delete-slow">Delete in batches (~<span class="deletion-time"></span>s)</button>
|
||||
<button data-action="delete-immediate">Delete immediately</button>
|
||||
<button data-action="delete-cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
<%- include('dialogs/advancedsettings.ejs') %>
|
||||
<%- include('dialogs/commandmodesettings.ejs') %>
|
||||
<%- include('dialogs/customformation.ejs') %>
|
||||
<%- include('dialogs/importexport.ejs', {
|
||||
"dialogId": "unit-export-dialog",
|
||||
"submitButtonText": "Export units to file",
|
||||
"textContent": "Select the unit categories you would like to export. Note: only ground and naval units can be exported at this time.",
|
||||
"title": "Export",
|
||||
"showFilenameInput": true
|
||||
}) %>
|
||||
<%- include('dialogs/importexport.ejs', {
|
||||
"dialogId": "unit-import-dialog",
|
||||
"submitButtonText": "Import units into mission",
|
||||
"textContent": "Select the unit categories you would like to import.",
|
||||
"title": "Import",
|
||||
"showFilenameInput": false
|
||||
}) %>
|
||||
<%- include('dialogs/slowdelete.ejs') %>
|
||||
<%- include('dialogs/splash.ejs') %>
|
||||
162
client/views/other/dialogs/advancedsettings.ejs
Normal file
@@ -0,0 +1,162 @@
|
||||
<div id="advanced-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Olympus 1-1</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
|
||||
<!-- General settings -->
|
||||
<div id="general-settings">
|
||||
<div class="ol-group">
|
||||
<h4>General settings</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="general-settings-grid">
|
||||
<div id="prohibit-jettison-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not jettison external stores">
|
||||
<input type="checkbox"/>
|
||||
Prohibit jettison
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-afterburner-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage the afterburner">
|
||||
<input type="checkbox" />
|
||||
Prohibit afterburner
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AA-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage airborne targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/A
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AG-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage ground targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/G
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-air-wpn-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will not engage air weapons (e.g. SAM sites will not engage HARMs)">
|
||||
<input type="checkbox" />
|
||||
Prohibit air wpn engage
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tasking -->
|
||||
<!--
|
||||
<div id="tasking">
|
||||
<div class="ol-group">
|
||||
<h4>Tasking</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div id="tanker-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will operate as Air to Air Refuelling tanker for airplanes that have a compatible refuelling system">
|
||||
<input type="checkbox" />
|
||||
Operate as AAR tanker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="AWACS-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will operate as AWACS on datalink">
|
||||
<input type="checkbox" />
|
||||
Operate as AWACS
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- TACAN options -->
|
||||
<div id="TACAN-options">
|
||||
<div class="ol-group">
|
||||
<h4>TACAN options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="TACAN-checkbox" class="ol-checkbox">
|
||||
<label title="Turn ON the TACAN">
|
||||
<input type="checkbox" />
|
||||
Activate TACAN
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>TACAN: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="TACAN-channel" class="ol-text-input">
|
||||
<input type="number" min="1" max="126" step="1" value="40">
|
||||
</div>
|
||||
|
||||
<div id="TACAN-XY" class="ol-select">
|
||||
<div class="ol-select-value">X</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="TACAN-callsign" class="ol-text-input">
|
||||
<input type="text" maxlength="3" value="TKR" style="width: 50px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Radio options -->
|
||||
<div id="radio-options">
|
||||
<div class="ol-group">
|
||||
<h4>Radio options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio frequency: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-mhz" class="ol-text-input">
|
||||
<input type="number" min="1" max="999" step="1" value="260">
|
||||
</div>
|
||||
|
||||
<div id="radio-decimals" class="ol-select">
|
||||
<div class="ol-select-value">.000</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio callsign: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-callsign" class="ol-select">
|
||||
<div class="ol-select-value"></div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
-
|
||||
</label>
|
||||
|
||||
<div id="radio-callsign-number" class="ol-text-input">
|
||||
<input type="number" min="1" max="999" step="1" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyAdvancedSettings">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
70
client/views/other/dialogs/commandmodesettings.ejs
Normal file
@@ -0,0 +1,70 @@
|
||||
<div id="command-mode-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Command mode settings</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div id="restrict-spawns" class="ol-checkbox">
|
||||
<label title="If false, no spawn restrictions will be applied">
|
||||
<input type="checkbox"/>
|
||||
Restrict spawns
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="restrict-to-coalition" class="ol-checkbox">
|
||||
<label title="If true, commanders will be allowed to only spawn units that belong to their coalition. E.g. blue commanders will be able to spawn F/A-18 Hornets, but not MiG-29s.">
|
||||
<input type="checkbox"/>
|
||||
Restrict units to coalition
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Setup time: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="setup-time" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="10">
|
||||
</div>
|
||||
<label>minutes</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Available eras: </label>
|
||||
|
||||
<div id="command-mode-era-options" class="ol-select">
|
||||
<div class="ol-select-value">Select eras</div>
|
||||
<div class="ol-select-options">
|
||||
<!-- This is where all the available era buttons will be shown-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<h4>Spawn points</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Blue spawn points: </label>
|
||||
<div id="blue-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Red spawn points: </label>
|
||||
<div id="red-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applycommandModeOptions">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
48
client/views/other/dialogs/customformation.ejs
Normal file
@@ -0,0 +1,48 @@
|
||||
<div id="custom-formation-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Custom formation</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div class="formation-position-clock">
|
||||
<div class="clock-hand" style="top: 0px; left: 50px;"><input type="radio" id="formation-1" name="formation-position" value="1"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 14px;"><input type="radio" id="formation-2" name="formation-position" value="2"></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 0px; "><input type="radio" id="formation-3" name="formation-position" value="3"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 14px;"><input type="radio" id="formation-4" name="formation-position" value="4"></div>
|
||||
<div class="clock-hand" style="top: 100px; left: 50px;"><input type="radio" id="formation-5" name="formation-position" value="5"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 86px;"><input type="radio" id="formation-6" name="formation-position" value="6" checked></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 100px"><input type="radio" id="formation-7" name="formation-position" value="7"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 86px;"><input type="radio" id="formation-8" name="formation-position" value="8"></div>
|
||||
<div style="top: 50px; left: 50px;" id="reference-system"></div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Distance: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="distance" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="150">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Up-down: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="up-down" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="30">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyCustomFormation">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
29
client/views/other/dialogs/importexport.ejs
Normal file
@@ -0,0 +1,29 @@
|
||||
<div id="<%= dialogId %>" class="ol-panel ol-dialog file-import-export hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-header">
|
||||
<h3><%= title %></h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<p><%= textContent %></p>
|
||||
|
||||
<% if (showFilenameInput) { %>
|
||||
<div class="export-filename-container">
|
||||
<label>Filename:</label>
|
||||
<input id="export-filename">
|
||||
<img src="resources/theme/images/icons/keyboard-solid.svg">
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<table class="categories-coalitions">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="start-transfer"><%= submitButtonText %></button>
|
||||
<button data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||