Compare commits
495 Commits
v0.4.3-alp
...
v0.4.12-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
564a650403 | ||
|
|
0e403e8b74 | ||
|
|
840cdd9049 | ||
|
|
e5e7e9be14 | ||
|
|
e677968ba7 | ||
|
|
d24b955d52 | ||
|
|
48860ff514 | ||
|
|
0e78b7559b | ||
|
|
25edfc45e5 | ||
|
|
885825e5cc | ||
|
|
344413ae74 | ||
|
|
fa0643987b | ||
|
|
dcff462b32 | ||
|
|
fa04e5f8bb | ||
|
|
c8d5f9ce0e | ||
|
|
55642a89b1 | ||
|
|
f305aa3929 | ||
|
|
47ee88c339 | ||
|
|
022e041f68 | ||
|
|
0cc53890c1 | ||
|
|
e37f7a4977 | ||
|
|
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 | ||
|
|
283b9e682e | ||
|
|
8b5956e76b | ||
|
|
017a89b945 | ||
|
|
5bc685182b | ||
|
|
f47fc2fb19 | ||
|
|
11d6f25606 | ||
|
|
331692e3d3 | ||
|
|
7483225e0d | ||
|
|
f00df5ca3f | ||
|
|
22ae882032 | ||
|
|
fbab82e4de | ||
|
|
ca81d1c4ce | ||
|
|
a1884ab19f | ||
|
|
8905cd5e85 | ||
|
|
8d5ed33ad8 | ||
|
|
b747752b76 | ||
|
|
4e13daa270 | ||
|
|
c00096bd9a | ||
|
|
210c1fbecf | ||
|
|
4a54011aac | ||
|
|
b89c5142b6 | ||
|
|
0421f6b8fe | ||
|
|
0c50141be6 | ||
|
|
a71b8806e7 | ||
|
|
e0a27c574f | ||
|
|
8d7a49a31f | ||
|
|
25e2c50438 | ||
|
|
e3423ea9a3 | ||
|
|
812112858c | ||
|
|
8db1357904 | ||
|
|
bba0aa9f52 | ||
|
|
1cc549b24a | ||
|
|
7cafeb73ca | ||
|
|
772f082913 | ||
|
|
9f7efe3018 | ||
|
|
a28584b08b | ||
|
|
5cba46a482 | ||
|
|
a15b7620eb | ||
|
|
9ef6efa3e0 | ||
|
|
49bff88c4e | ||
|
|
80af34fa3e | ||
|
|
1d6f2644aa | ||
|
|
dc61d364d1 | ||
|
|
78de9dd538 | ||
|
|
ccaea5b9d1 | ||
|
|
e02e9de55d | ||
|
|
40da192dbb | ||
|
|
c8254238c7 | ||
|
|
9caee0c77c | ||
|
|
b9830f0190 | ||
|
|
02a0a9b783 | ||
|
|
ea1758a1a9 | ||
|
|
b43afd4e9c | ||
|
|
7700aa2030 | ||
|
|
d5d8391f63 | ||
|
|
b508d2301b | ||
|
|
01b3336709 | ||
|
|
6ba37aefd6 | ||
|
|
e68683acb7 | ||
|
|
a804904ff5 | ||
|
|
eacb89176c | ||
|
|
ec971aa822 | ||
|
|
e1f404c647 | ||
|
|
5f1e32d610 | ||
|
|
732ee2bbb9 | ||
|
|
1e461250d5 | ||
|
|
f727174044 | ||
|
|
7edc687f7b | ||
|
|
596fbf0b87 | ||
|
|
8dc48c10c3 | ||
|
|
5db90e5896 | ||
|
|
dd811def07 | ||
|
|
5273291e9a | ||
|
|
130dbda499 | ||
|
|
769aea7e4e | ||
|
|
fa3d65bde6 | ||
|
|
073281135c | ||
|
|
4c7f979e56 | ||
|
|
a5bfb4f8d2 | ||
|
|
a04780f311 | ||
|
|
aad0fd22ad | ||
|
|
bc1c3994d3 | ||
|
|
840033aa6a | ||
|
|
9cc6e9e790 | ||
|
|
b1ad2f409e | ||
|
|
7106646042 | ||
|
|
31405119f0 | ||
|
|
2e5ee93361 | ||
|
|
f981a661d5 | ||
|
|
7e05bbf5e3 | ||
|
|
4465f4a3bb | ||
|
|
ade4f183cd | ||
|
|
99dc61799f | ||
|
|
a213b94606 | ||
|
|
6600ed8cbb | ||
|
|
826bf12bd6 | ||
|
|
528e0db79c | ||
|
|
11a5fec195 | ||
|
|
c0f3f3a40a | ||
|
|
88844db23a | ||
|
|
31710b4e28 | ||
|
|
42d2f6400b | ||
|
|
2701d6adc0 | ||
|
|
5a4b203937 | ||
|
|
74cb7877cb | ||
|
|
f954338c27 | ||
|
|
cc729b1d25 | ||
|
|
4a4b1d6b00 | ||
|
|
d14741f3b1 | ||
|
|
94684fff9f | ||
|
|
44deef4a5a | ||
|
|
67079e1c0b | ||
|
|
005ffc55ce | ||
|
|
1691b0b30c | ||
|
|
e4e55430ca | ||
|
|
4d31d9d748 | ||
|
|
1413f596fb | ||
|
|
c0aee94da5 | ||
|
|
8e545346e6 | ||
|
|
2b1272c70d | ||
|
|
121f9b6ec3 | ||
|
|
6f9722143e | ||
|
|
5bf62f4b93 | ||
|
|
79319dd006 | ||
|
|
fdcff53697 | ||
|
|
24cd2729db | ||
|
|
9ca2703f16 | ||
|
|
1a579c5755 | ||
|
|
7344c761fe | ||
|
|
203a981fed | ||
|
|
d3faf65900 | ||
|
|
5efbf81090 | ||
|
|
9ffab7d531 | ||
|
|
0886ff1be1 | ||
|
|
2893182713 | ||
|
|
478bb18ab2 | ||
|
|
eae07ca6cc | ||
|
|
2264faabfc | ||
|
|
4cdf82cb14 | ||
|
|
5a7e511dac | ||
|
|
a29eb3d343 | ||
|
|
3c65aefe61 | ||
|
|
867d7697d2 | ||
|
|
ebee6610a3 | ||
|
|
c26691c2fc | ||
|
|
a038d6e999 | ||
|
|
aa1550ca01 | ||
|
|
80eaa643c9 | ||
|
|
d67d9c1afd | ||
|
|
af15fc123d | ||
|
|
0ce56e6529 | ||
|
|
258164c285 | ||
|
|
746617deb0 | ||
|
|
c51d00673d | ||
|
|
bedd21522a | ||
|
|
382013d0ca | ||
|
|
3794207d97 | ||
|
|
2805323cfa | ||
|
|
0db2e56e32 | ||
|
|
a33ad18322 | ||
|
|
885ef6b357 | ||
|
|
2435072b07 | ||
|
|
d462bd16b5 | ||
|
|
b08a3835dc | ||
|
|
ace0908d84 | ||
|
|
5186fffb5e | ||
|
|
c5160c9baa | ||
|
|
63acc94558 | ||
|
|
fc82407d9a | ||
|
|
a0ac9eb285 | ||
|
|
fc835eaba7 | ||
|
|
f7b3e5bf77 | ||
|
|
e41484186b | ||
|
|
e0bfe6a96c | ||
|
|
228f184548 | ||
|
|
892ce4c453 | ||
|
|
569d45d58a | ||
|
|
ce5f00e075 | ||
|
|
4da407008e | ||
|
|
96e79cc8ae | ||
|
|
ee93806e19 | ||
|
|
fcb02602a0 | ||
|
|
5d4fdf1e76 | ||
|
|
9af59fb51c | ||
|
|
6493d37b7c | ||
|
|
a82c033da0 | ||
|
|
91484e1c98 | ||
|
|
3db5202cdf | ||
|
|
5a28974027 | ||
|
|
4d694f1f74 | ||
|
|
5035c408e7 | ||
|
|
d209f98265 | ||
|
|
85b1404321 | ||
|
|
72c6e4e94e | ||
|
|
eb46ed2768 | ||
|
|
7f4f01c9e8 | ||
|
|
691d2746e6 | ||
|
|
d41dfc1e0d | ||
|
|
493898bd4a | ||
|
|
8ebe427269 | ||
|
|
48d4ff97a0 | ||
|
|
83769bf539 | ||
|
|
0954bf2923 | ||
|
|
a2f5f82e28 | ||
|
|
a82af04fca | ||
|
|
a1a876429c | ||
|
|
58830fa53e | ||
|
|
15c8e32fef | ||
|
|
59c915d8bc | ||
|
|
968ff61979 | ||
|
|
94901849e6 | ||
|
|
ff42126b0e | ||
|
|
39ddf10ca7 | ||
|
|
7a24e5d39d | ||
|
|
e9100504a0 | ||
|
|
099cbfdf75 | ||
|
|
28afef8847 | ||
|
|
010b1e1cce | ||
|
|
274ce76c2b | ||
|
|
f3155e618b | ||
|
|
18307caab3 | ||
|
|
2e279b8876 | ||
|
|
0da878f50f | ||
|
|
ecfb3644be | ||
|
|
c87d18fd8e | ||
|
|
5810f9232a | ||
|
|
fb8eb6f357 | ||
|
|
7eb76ff32d | ||
|
|
c3d3de9984 | ||
|
|
cc2549dbdf | ||
|
|
7efa8eab18 | ||
|
|
376ae9ec1c | ||
|
|
9882b2999f | ||
|
|
3667a75244 | ||
|
|
4089448b53 | ||
|
|
fec11869a8 | ||
|
|
e7e56d1cdb | ||
|
|
9db01d6060 | ||
|
|
ed38397631 | ||
|
|
b3dddc0570 | ||
|
|
6fa06de245 | ||
|
|
834ddde1d0 | ||
|
|
5d4348f8a7 | ||
|
|
36f2917995 | ||
|
|
eedbec175d | ||
|
|
344ec86722 | ||
|
|
952d1e5895 | ||
|
|
a371484f38 | ||
|
|
1567a6fd27 | ||
|
|
e0f96b5cca | ||
|
|
8f6e48642c | ||
|
|
0c4c10dc1a | ||
|
|
de6b78d762 | ||
|
|
6b77f32548 | ||
|
|
fd2b7a00e1 | ||
|
|
818886a65e | ||
|
|
0ca31afc91 | ||
|
|
024a73da63 | ||
|
|
744b0063d8 | ||
|
|
355923f680 | ||
|
|
44332a2004 | ||
|
|
2f125e3d0e | ||
|
|
588228c050 | ||
|
|
8977ba9b6d | ||
|
|
ad06117b78 | ||
|
|
01b30ccf12 | ||
|
|
7f75905d5d | ||
|
|
798856c649 | ||
|
|
9acd358080 | ||
|
|
31c112157a | ||
|
|
d2e162edbf | ||
|
|
6aaffe20d9 | ||
|
|
6d32b20117 | ||
|
|
bd894704b1 | ||
|
|
7af2162b50 | ||
|
|
c735d108d4 | ||
|
|
6a2ffc936e | ||
|
|
74d5480587 | ||
|
|
89c39c7038 | ||
|
|
61f955cfeb | ||
|
|
744adee94c | ||
|
|
267c0b037d | ||
|
|
860414b7b2 | ||
|
|
09bf361d44 | ||
|
|
e2f80c5788 | ||
|
|
a96a6eb57d | ||
|
|
803f9a7fd6 | ||
|
|
71a141e0b8 | ||
|
|
a99b85e646 | ||
|
|
333169d18c | ||
|
|
a08eb418a6 | ||
|
|
cbb878cf96 | ||
|
|
d684f91a5a | ||
|
|
f2de4cd34c | ||
|
|
3d076a605b | ||
|
|
3607f88e18 | ||
|
|
a0d2bb11ed | ||
|
|
ede245a37d | ||
|
|
aca1e112d2 | ||
|
|
bf5d6dac18 | ||
|
|
182ce4da42 | ||
|
|
41e2e6fa59 | ||
|
|
fe0e964a5a | ||
|
|
bee35f9ee9 | ||
|
|
e66a1bb5b4 | ||
|
|
8490997604 | ||
|
|
6898d1df6d | ||
|
|
4d863bb894 | ||
|
|
a338e5fa26 | ||
|
|
695adc8acb | ||
|
|
fab7d26191 | ||
|
|
3959139dd8 | ||
|
|
1298669f1c | ||
|
|
4fe40f5ff7 | ||
|
|
a10c113c42 | ||
|
|
d5f4b5c711 | ||
|
|
93707af56b | ||
|
|
0ae694c1a8 | ||
|
|
0b53fb19b7 | ||
|
|
cc386e86b9 | ||
|
|
a922d7de31 | ||
|
|
8e7d64f0e4 | ||
|
|
0bc406538b | ||
|
|
9547559e00 | ||
|
|
70783dc828 | ||
|
|
85bdd791b8 | ||
|
|
9d7e61556d | ||
|
|
eb80c39b98 | ||
|
|
07b69fe96f | ||
|
|
e0a3fd1795 |
33
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# ci.yml file for GitHub Actions
|
||||
name: CI
|
||||
|
||||
on: [push]
|
||||
|
||||
# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
|
||||
permissions:
|
||||
contents: write
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
jobs:
|
||||
build_docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout the repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
working-directory: ./client
|
||||
|
||||
- name: Create the docs directory locally in CI
|
||||
run: npx typedoc --out ../docs/client @types/*.d.ts src/**/*.ts
|
||||
working-directory: ./client
|
||||
|
||||
- name: Deploy 🚀
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
with:
|
||||
folder: docs
|
||||
11
.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
|
||||
@@ -9,4 +12,10 @@ Output
|
||||
node_modules
|
||||
/client/TODO.txt
|
||||
/client/public/javascripts/bundle.js
|
||||
!client/bin
|
||||
!client/bin
|
||||
/client/public/plugins
|
||||
/client/plugins/controltips/index.js
|
||||
hgt
|
||||
/client/public/databases/units/old
|
||||
/client/plugins/databasemanager/index.js
|
||||
|
||||
|
||||
100
COPYING → LEGAL
@@ -1,4 +1,34 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
DCS Olympus
|
||||
|
||||
A real-time AI unit control mod for DCS World
|
||||
|
||||
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 ("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
|
||||
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
|
||||
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
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
@@ -618,57 +648,31 @@ an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
END OF GNU GENERAL PUBLIC LICENCE
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
ADDITIONAL TERMS & CONDITIONS
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
1. Governing Law
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
Save where specifically provided for otherwise, the provisions of the
|
||||
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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
2. Entire Agreement
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
3. Unilateral Modification
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
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.
|
||||
78
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,68 @@
|
||||

|
||||
|
||||
### 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:
|
||||
# Frequently Asked Questions
|
||||
### Can I join up and help out with the project? ###
|
||||
We are currently running towards first release in the very near future so we are not looking to add more people to the core team for the moment. However that does not mean we are not open to collaborations and help going forward, if you want to help for now we are committed to the free and open source model so feel free to check out the github, familiarize yourself with the project and maybe even start submitting pull requests for open issues.
|
||||
|
||||
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.
|
||||
Post-release we will be more interested in developing partnerships/collaborations with other teams/projects and potentially bringing in more team members, we will update this after release on how that will be managed!
|
||||
|
||||
### Can I be a beta/alpha-tester? ###
|
||||
With first public release planned for the very-near future we are fully committed to the final sprint, as such we will not be formally recruiting more people to test pre-release.
|
||||
|
||||
Post-release we will be eager to hear feedback of all forms and take in bug-reports, at this time after release we will begin considering bringing in more team members to test in development versions as we go.
|
||||
|
||||
### Do you have a roadmap? ###
|
||||
We do not have a roadmap no, we have a laundry list of things we are hoping to do.
|
||||
|
||||
These include but are not limited to:
|
||||
1) Enhancements to helicopter play
|
||||
2) More features around use of ground units
|
||||
3) More unique effects and behaviours
|
||||
4) ATC/AIC features
|
||||
5) Usability features like unit painters etc
|
||||
|
||||
However we cannot commit to specific features, feature release order, or timelines, please remember this isn't our job and we work on it in our free time because we love DCS
|
||||
|
||||
### Does Olympus support mods? ###
|
||||
Generally OIympus will not have any issues with other mods, however you may need to tell olympus about modded units in order to be able to dynamically spawn them etc
|
||||
|
||||
Keep in mind that any mods you do choose to spawn your players will need to have, some mod unit just appear as a su27 or leo2 etc. when a player is missing them, others can cause client crashes. So be smart about how you use them
|
||||
|
||||
### Is Olympus compatible with mission scripts? ###
|
||||
We have tried hard to keep Olympus from interfering with other scripts, we have tested with a variety of new and old mission scripts and generally expect it will not be an issue.
|
||||
|
||||
However we cannot foresee everything people come up with so we suggest testing with what you have in mind once olympus releases
|
||||
|
||||
### How does it work? ###
|
||||
The quick answer is magic.
|
||||
|
||||
The long answer is well all the code is there for you to read.
|
||||
|
||||
The middle answer is a bit like SRS does. Olympus consists of two parts.
|
||||
|
||||
(A) Olympus back end: A dll, run by DCS, that sends data out and gets commands in via a REST API;
|
||||
(B) Webserver exe: The one you start when starting the server via the desktop shortcut.
|
||||
|
||||
A and B never communicate when you connect the client you download the web page and some other minor stuff from B, and you get the DCS data from and send commands to A.
|
||||
|
||||
### How much does Olympus impact performance? ###
|
||||
Olympus by itself should not have a noticeable impact on server performance, however the ability for the user to spawn arbitrary units and command engagements means Olympus can be used in such a way that brings the game to it's knees.
|
||||
|
||||
Be cognizant of how you play, whether it's done through Olympus or the mission editor 500 MLRS units firing at once is not going to go over well with most servers
|
||||
|
||||
|
||||
|
||||
|
||||
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,4 +1,2 @@
|
||||
cd client
|
||||
call npm prune --production
|
||||
cd ..
|
||||
call build.bat
|
||||
call "C:\Program Files (x86)\Inno Setup 6\iscc.exe" "installer\olympus.iss"
|
||||
14
client/.vscode/launch.json
vendored
@@ -4,17 +4,6 @@
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Chrome",
|
||||
"port": 9222,
|
||||
"urlFilter": "http://localhost:3000/*",
|
||||
"request": "attach",
|
||||
"type": "chrome",
|
||||
"webRoot": "${workspaceFolder}/public/",
|
||||
"sourceMapPathOverrides": {
|
||||
"src/*": "${workspaceFolder}/src/*"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
@@ -24,7 +13,8 @@
|
||||
"sourceMapPathOverrides": {
|
||||
"src/*": "${workspaceFolder}/src/*"
|
||||
},
|
||||
"preLaunchTask": "server"
|
||||
"preLaunchTask": "server",
|
||||
"port": 9222
|
||||
}
|
||||
]
|
||||
}
|
||||
2
client/.vscode/tasks.json
vendored
@@ -6,7 +6,7 @@
|
||||
{
|
||||
"label": "server",
|
||||
"type": "shell",
|
||||
"command": "npm run start",
|
||||
"command": "npm run debug",
|
||||
"isBackground": true
|
||||
}
|
||||
]
|
||||
|
||||
2598
client/@types/olympus/index.d.ts
vendored
Normal file
@@ -3,25 +3,32 @@ var path = require('path');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var logger = require('morgan');
|
||||
var fs = require('fs');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
var atcRouter = require('./routes/api/atc');
|
||||
var airbasesRouter = require('./routes/api/airbases');
|
||||
var elevationRouter = require('./routes/api/elevation');
|
||||
var databasesRouter = require('./routes/api/databases');
|
||||
var indexRouter = require('./routes/index');
|
||||
var uikitRouter = require('./routes/uikit');
|
||||
var usersRouter = require('./routes/users');
|
||||
var resourcesRouter = require('./routes/resources');
|
||||
var pluginsRouter = require('./routes/plugins');
|
||||
|
||||
var app = express();
|
||||
|
||||
app.use(logger('dev'));
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(bodyParser.json({limit: '50mb'}));
|
||||
app.use(bodyParser.urlencoded({limit: '50mb', extended: true}));
|
||||
app.use(cookieParser());
|
||||
app.use(express.static(path.join(__dirname, 'public')));
|
||||
|
||||
app.use('/', indexRouter);
|
||||
app.use('/api/atc', atcRouter);
|
||||
app.use('/api/airbases', airbasesRouter);
|
||||
app.use('/api/elevation', elevationRouter);
|
||||
app.use('/api/databases', databasesRouter);
|
||||
app.use('/plugins', pluginsRouter)
|
||||
app.use('/users', usersRouter);
|
||||
app.use('/uikit', uikitRouter);
|
||||
app.use('/resources', resourcesRouter);
|
||||
@@ -36,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,22 @@
|
||||
#!/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);
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -12,8 +29,14 @@ var http = require('http');
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '3000');
|
||||
var configPort = null;
|
||||
if (config["client"] != undefined && config["client"]["port"] != undefined) {
|
||||
configPort = config["client"]["port"];
|
||||
}
|
||||
|
||||
var port = normalizePort(configPort || '3000');
|
||||
app.set('port', port);
|
||||
console.log("Express server listening on port: " + port)
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
@@ -88,3 +111,6 @@ function onListening() {
|
||||
: 'port ' + addr.port;
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
|
||||
console.log("DCS Olympus server v0.4.12-alpha-rc4 started correctly!")
|
||||
console.log("Waiting for connections...")
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
copy .\\node_modules\\leaflet\\dist\\leaflet.css .\\public\\stylesheets\\leaflet\\leaflet.css
|
||||
copy .\\node_modules\\leaflet-gesture-handling\\dist\\leaflet-gesture-handling.css .\\public\\stylesheets\\leaflet\\leaflet-gesture-handling.css
|
||||
copy .\\node_modules\\leaflet.nauticscale\\dist\\leaflet.nauticscale.js .\\public\\javascripts\\leaflet.nauticscale.js
|
||||
copy .\\node_modules\\leaflet-path-drag\\dist\\L.Path.Drag.js .\\public\\javascripts\\L.Path.Drag.js
|
||||
copy .\\node_modules\\leaflet-path-drag\\dist\\L.Path.Drag.js .\\public\\javascripts\\L.Path.Drag.js
|
||||
|
||||
358
client/demo.js
@@ -1,110 +1,20 @@
|
||||
const { random } = require('@turf/turf');
|
||||
var basicAuth = require('express-basic-auth')
|
||||
var enc = new TextEncoder();
|
||||
|
||||
const DEMO_UNIT_DATA = {
|
||||
["1"]:{ category: "Aircraft", alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "KC-135", unitName: "Cool guy 1-1 who also has a very long name", groupName: "Cool group 1", state: 3, task: "Being cool!",
|
||||
hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, heading: 45, isTanker: true, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 2,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 2, detectionMethod: 1}, {ID: 3, detectionMethod: 4}],
|
||||
activePath: [{lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0}]
|
||||
},
|
||||
["2"]:{ category: "Aircraft", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "FA-18C_hornet", unitName: "Cool guy 1-2", groupName: "Cool group 2", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 36.9, lng: -116, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
}, ["3"]:{ category: "Helicopter", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "AH-64D_BLK_II", unitName: "Cool guy 1-4", groupName: "Cool group 3", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.1, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
}, ["4"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "Gepard", unitName: "Cool guy 2-1", groupName: "Cool group 4", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.2, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1001, detectionMethod: 16}],
|
||||
activePath: [ ],
|
||||
isLeader: true
|
||||
}, ["5"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "S_75M_Volhov", unitName: "Cool guy 2-2", groupName: "Cool group 4", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.21, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [],
|
||||
activePath: [ ],
|
||||
isLeader: false
|
||||
},
|
||||
["6"]:{ category: "Aircraft", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "FA-18C_hornet", unitName: "Bad boi 1-2", groupName: "Bad group 1", state: 1, task: "Being bad",
|
||||
hasTask: false, position: { lat: 36.8, lng: -116, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
},
|
||||
}
|
||||
const aircraftDatabase = require('./public/databases/units/aircraftDatabase.json');
|
||||
const helicopterDatabase = require('./public/databases/units/helicopterDatabase.json');
|
||||
const groundUnitDatabase = require('./public/databases/units/groundUnitDatabase.json');
|
||||
const navyUnitDatabase = require('./public/databases/units/navyUnitDatabase.json');
|
||||
|
||||
const DEMO_UNIT_DATA = {}
|
||||
|
||||
const DEMO_WEAPONS_DATA = {
|
||||
["1001"]:{ category: "Missile", alive: true, coalition: 2, name: "", position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 45 * Math.PI / 180 },
|
||||
/*["1001"]:{ category: "Missile", alive: true, coalition: 2, name: "", position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 45 * Math.PI / 180 }, */
|
||||
}
|
||||
|
||||
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));
|
||||
@@ -112,15 +22,139 @@ class DemoDataGenerator {
|
||||
app.get('/demo/bullseyes', (req, res) => this.bullseyes(req, res));
|
||||
app.get('/demo/airbases', (req, res) => this.airbases(req, res));
|
||||
app.get('/demo/mission', (req, res) => this.mission(req, res));
|
||||
app.get('/demo/commands', (req, res) => this.command(req, res));
|
||||
app.put('/demo', (req, res) => this.put(req, res));
|
||||
|
||||
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: 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,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [],
|
||||
contacts: [],
|
||||
activePath: [],
|
||||
isLeader: true
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 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));
|
||||
let latIdx = 0;
|
||||
let lngIdx = 0;
|
||||
let idx = 1;
|
||||
console.log(l)
|
||||
for (let name in databases) {
|
||||
if (databases[name].enabled) {
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = name;
|
||||
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) {
|
||||
latIdx = 0;
|
||||
lngIdx += 1;
|
||||
}
|
||||
|
||||
if (name in aircraftDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Aircraft";
|
||||
else if (name in helicopterDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Helicopter";
|
||||
else if (name in groundUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
else if (name in navyUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "NavyUnit";
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
let idx = 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "S_75M_Volhov";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = true;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "SNR_75V";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
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));
|
||||
DEMO_UNIT_DATA[idx].name = "Ural-4320 APA-5D";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
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));
|
||||
DEMO_UNIT_DATA[idx].name = "F-14B";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-1`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "Aircraft";
|
||||
DEMO_UNIT_DATA[idx].isLeader = false;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "Infantry AK";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-2`;
|
||||
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));
|
||||
DEMO_UNIT_DATA[idx].name = "Infantry AK";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-3`;
|
||||
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));
|
||||
DEMO_UNIT_DATA[idx].name = "KC-135";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-4`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "Aircraft";
|
||||
DEMO_UNIT_DATA[idx].isLeader = true;
|
||||
|
||||
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
|
||||
@@ -128,48 +162,55 @@ class DemoDataGenerator {
|
||||
var array = new Uint8Array();
|
||||
var time = Date.now();
|
||||
array = this.concat(array, this.uint64ToByteArray(BigInt(time)));
|
||||
for (let idx in DEMO_UNIT_DATA) {
|
||||
const unit = DEMO_UNIT_DATA[idx];
|
||||
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.heading, 15);
|
||||
array = this.appendUint8(array, unit.isTanker, 16);
|
||||
array = this.appendUint8(array, unit.isAWACS, 17);
|
||||
array = this.appendUint8(array, unit.onOff, 18);
|
||||
array = this.appendUint8(array, unit.followRoads, 19);
|
||||
array = this.appendUint16(array, unit.fuel, 20);
|
||||
array = this.appendDouble(array, unit.desiredSpeed, 21);
|
||||
array = this.appendUint8(array, unit.desiredSpeedType, 22);
|
||||
array = this.appendDouble(array, unit.desiredAltitude, 23);
|
||||
array = this.appendUint8(array, unit.desiredAltitudeType, 24);
|
||||
array = this.appendUint32(array, unit.leaderID, 25);
|
||||
array = this.appendOffset(array, unit.formationOffset, 26);
|
||||
array = this.appendUint32(array, unit.targetID, 27);
|
||||
array = this.appendCoordinates(array, unit.targetPosition, 28);
|
||||
array = this.appendUint8(array, unit.ROE, 29);
|
||||
array = this.appendUint8(array, unit.reactionToThreat, 30);
|
||||
array = this.appendUint8(array, unit.emissionsCountermeasures, 31);
|
||||
array = this.appendTACAN(array, unit.TACAN, 32);
|
||||
array = this.appendRadio(array, unit.radio, 33);
|
||||
array = this.appendRadio(array, unit.generalSettings, 34);
|
||||
array = this.appendAmmo(array, unit.ammo, 35);
|
||||
array = this.appendContacts(array, unit.contacts, 36);
|
||||
array = this.appendActivePath(array, unit.activePath, 37);
|
||||
array = this.appendUint8(array, unit.isLeader, 38);
|
||||
array = this.concat(array, this.uint8ToByteArray(255));
|
||||
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, 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));
|
||||
}
|
||||
}
|
||||
res.end(Buffer.from(array, 'binary'));
|
||||
};
|
||||
@@ -409,7 +450,7 @@ class DemoDataGenerator {
|
||||
ret.time = Date.now();
|
||||
|
||||
ret.mission.dateAndTime = {
|
||||
time: Date.now(),
|
||||
time: { h: 10, m: 15, s: 34 },
|
||||
date: "",
|
||||
elapsedTime: (Date.now() - this.startTime) / 1000,
|
||||
startTime: 0
|
||||
@@ -417,21 +458,24 @@ class DemoDataGenerator {
|
||||
|
||||
ret.mission.coalitions = {
|
||||
red: [
|
||||
'Russia',
|
||||
'China'
|
||||
'RUSSIA',
|
||||
'CHINA'
|
||||
],
|
||||
blue: [
|
||||
'United States',
|
||||
'Great Britain'
|
||||
'UK',
|
||||
'USA'
|
||||
],
|
||||
neutral: [
|
||||
'ITALY'
|
||||
]
|
||||
}
|
||||
|
||||
ret.mission.commandModeOptions = {
|
||||
restrictSpawns: false,
|
||||
restrictSpawns: true,
|
||||
restrictToCoalition: true,
|
||||
setupTime: 0,
|
||||
spawnPoints: {
|
||||
red: 1000,
|
||||
red: 400,
|
||||
blue: 400
|
||||
},
|
||||
eras: ["WW2", "Early Cold War", "Late Cold War", "Modern"]
|
||||
@@ -439,7 +483,7 @@ class DemoDataGenerator {
|
||||
|
||||
var auth = req.get("Authorization");
|
||||
if (auth) {
|
||||
var username = atob(auth.replace("Basic ", "")).split(":")[0];
|
||||
var username = Buffer.from(auth.replace("Basic ", ""), 'base64').toString('binary').split(":")[0];
|
||||
switch (username) {
|
||||
case "admin":
|
||||
ret.mission.commandModeOptions.commandMode = "Game master";
|
||||
@@ -454,7 +498,17 @@ class DemoDataGenerator {
|
||||
}
|
||||
res.send(JSON.stringify(ret));
|
||||
}
|
||||
|
||||
command(req, res) {
|
||||
var ret = {commandExecuted: Math.random() > 0.5};
|
||||
res.send(JSON.stringify(ret));
|
||||
}
|
||||
|
||||
put(req, res) {
|
||||
var ret = {commandHash: Math.random().toString(36).slice(2, 19)}
|
||||
res.send(JSON.stringify(ret));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = DemoDataGenerator;
|
||||
7769
client/package-lock.json
generated
@@ -2,29 +2,37 @@
|
||||
"name": "DCSOlympus",
|
||||
"node-main": "./bin/www",
|
||||
"main": "http://localhost:3000",
|
||||
"version": "v0.4.3-alpha",
|
||||
"version": "v0.4.12-alpha-rc4",
|
||||
"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-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": "npm run copy & concurrently --kill-others \"npm run watch\" \"nodemon ./bin/www\"",
|
||||
"start": "node ./bin/www",
|
||||
"debug": "npm run copy & concurrently --kill-others \"npm run watch\" \"nodemon --ignore ./public/databases/ ./bin/www\"",
|
||||
"watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
|
||||
},
|
||||
"dependencies": {
|
||||
"@turf/turf": "^6.5.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"cookie-parser": "~1.4.4",
|
||||
"debug": "~2.6.9",
|
||||
"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",
|
||||
"express-basic-auth": "^1.2.1"
|
||||
"srtm-elevation": "^2.1.2",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@tanem/svg-injector": "^10.1.55",
|
||||
"@turf/turf": "^6.5.0",
|
||||
"@tanem/svg-injector": "^10.1.68",
|
||||
"@types/formatcoords": "^1.1.0",
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/gtag.js": "^0.0.12",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
"@types/node": "^18.16.1",
|
||||
"@types/sortablejs": "^1.15.0",
|
||||
@@ -35,14 +43,19 @@
|
||||
"cp": "^0.2.0",
|
||||
"esmify": "^2.1.1",
|
||||
"formatcoords": "^1.1.3",
|
||||
"geodesy": "^1.1.2",
|
||||
"leaflet": "^1.9.3",
|
||||
"leaflet-control-mini-map": "^0.4.0",
|
||||
"leaflet-path-drag": "*",
|
||||
"leaflet.nauticscale": "^1.1.0",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
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"
|
||||
]
|
||||
}
|
||||
5
client/plugins/controltips/copy.bat
Normal file
@@ -0,0 +1,5 @@
|
||||
mkdir .\\..\\..\\public\\plugins\\controltipsplugin
|
||||
|
||||
copy .\\index.js .\\..\\..\\public\\plugins\\controltipsplugin\\index.js
|
||||
copy .\\plugin.json .\\..\\..\\public\\plugins\\controltipsplugin\\plugin.json
|
||||
copy .\\style.css .\\..\\..\\public\\plugins\\controltipsplugin\\style.css
|
||||
6317
client/plugins/controltips/package-lock.json
generated
Normal file
29
client/plugins/controltips/package.json
Normal file
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"name": "ControlTipsPlugin",
|
||||
"version": "v0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"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": {
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
6
client/plugins/controltips/plugin.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Control Tip Plugin",
|
||||
"version": "0.0.1",
|
||||
"description": "This plugin shows useful control tips on the right side of the screen. The tips change dynamically depending on what the user does",
|
||||
"author": "Peekaboo"
|
||||
}
|
||||
396
client/plugins/controltips/src/controltipsplugin.ts
Normal file
@@ -0,0 +1,396 @@
|
||||
import { OlympusPlugin } from "interfaces";
|
||||
import { OlympusApp } from "olympusapp";
|
||||
import { Unit } from "unit/unit";
|
||||
|
||||
|
||||
const SHOW_CONTROL_TIPS = "Show control tips"
|
||||
|
||||
export class ControlTipsPlugin implements OlympusPlugin {
|
||||
#element: HTMLElement;
|
||||
#app: any;
|
||||
#shortcutManager: any;
|
||||
#cursorIsHoveringOverUnit: boolean = false;
|
||||
#cursorIsHoveringOverAirbase: boolean = false;
|
||||
#mouseoverElement!: HTMLElement;
|
||||
|
||||
constructor() {
|
||||
this.#element = document.createElement("div");
|
||||
this.#element.id = "control-tips-panel";
|
||||
document.body.appendChild(this.#element);
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "Control Tips Plugin"
|
||||
}
|
||||
|
||||
initialize(app: any) {
|
||||
this.#app = app;
|
||||
|
||||
this.#shortcutManager = this.#app.getShortcutManager();
|
||||
|
||||
this.#shortcutManager.onKeyDown(() => {
|
||||
this.#updateTips()
|
||||
});
|
||||
|
||||
this.#shortcutManager.onKeyUp(() => {
|
||||
this.#updateTips()
|
||||
});
|
||||
|
||||
document.addEventListener("airbaseMouseover", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverAirbase = true;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("airbaseMouseout", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverAirbase = false;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("unitDeselection", (ev: CustomEventInit ) => {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("unitMouseover", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverUnit = true;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("unitMouseout", (ev: CustomEventInit) => {
|
||||
this.#cursorIsHoveringOverUnit = false;
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("unitsSelection", (ev: CustomEventInit ) => {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("mapOptionsChanged", () => {
|
||||
this.toggle( !this.#app.getMap().getVisibilityOptions()[SHOW_CONTROL_TIPS] );
|
||||
});
|
||||
|
||||
document.addEventListener( "mouseover", ( ev: MouseEvent ) => {
|
||||
if ( ev.target instanceof HTMLElement ) {
|
||||
this.#mouseoverElement = <HTMLElement>ev.target;
|
||||
}
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener( "mouseup", ( ev: MouseEvent ) => {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
this.#updateTips();
|
||||
|
||||
this.#app.getMap().addVisibilityOption(SHOW_CONTROL_TIPS, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getElement() {
|
||||
return this.#element;
|
||||
}
|
||||
|
||||
toggle(bool?: boolean) {
|
||||
this.getElement().classList.toggle("hide", bool);
|
||||
}
|
||||
|
||||
#updateTips() {
|
||||
const combos: Array<object> = [
|
||||
{
|
||||
"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": `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:object ) => {
|
||||
return (Object.values<Unit>( this.#app.getUnitsManager().getUnits() ).some( ( unit:Unit ) => {
|
||||
return unit.getAlive() && unit.getControlled() && unit.getHotgroup();
|
||||
}));
|
||||
},
|
||||
"showIfUnitSelected": true,
|
||||
"minSelectedUnits": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const currentCombo: any = combos.find((combo: any) => this.#shortcutManager.keyComboMatches(combo.keys)) || combos[0];
|
||||
|
||||
const element = this.getElement();
|
||||
|
||||
element.innerHTML = "";
|
||||
|
||||
let numSelectedUnits = 0;
|
||||
let numSelectedControlledUnits = 0;
|
||||
let unitSelectionContainsControlled = false;
|
||||
|
||||
if (this.#app.getUnitsManager()) {
|
||||
let selectedUnits = Object.values(this.#app.getUnitsManager().getSelectedUnits());
|
||||
numSelectedUnits = selectedUnits.length;
|
||||
numSelectedControlledUnits = selectedUnits.filter((unit: any) => unit.getControlled()).length;
|
||||
unitSelectionContainsControlled = numSelectedControlledUnits > 0;
|
||||
}
|
||||
|
||||
const tipsIncludesActiveMouseover = ( currentCombo.tips.some( ( tip:any ) => {
|
||||
if ( !tip.mouseoverSelector ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( this.#mouseoverElement instanceof HTMLElement === false ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !this.#mouseoverElement.matches( tip.mouseoverSelector ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}));
|
||||
|
||||
currentCombo.tips.filter((tip: any) => {
|
||||
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 !== this.#cursorIsHoveringOverAirbase) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof tip.showIfHoveringOverUnit === "boolean") {
|
||||
if (tip.showIfHoveringOverUnit !== this.#cursorIsHoveringOverUnit) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ( tipsIncludesActiveMouseover && ( typeof tip.mouseoverSelector !== "string" || !this.#mouseoverElement.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>`;
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
5
client/plugins/controltips/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { ControlTipsPlugin } from "./controltipsplugin";
|
||||
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new ControlTipsPlugin();
|
||||
}
|
||||
33
client/plugins/controltips/style.css
Normal file
@@ -0,0 +1,33 @@
|
||||
#control-tips-panel {
|
||||
align-self: center;
|
||||
display: flex;
|
||||
flex-flow: column wrap;
|
||||
font-size: 13px;
|
||||
justify-self: flex-end;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
row-gap: 20px;
|
||||
text-align: right;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#control-tips-panel>* {
|
||||
align-items: center;
|
||||
align-self: end;
|
||||
background-color: var(--background-steel);
|
||||
border-radius: var(--border-radius-md);
|
||||
color: white;
|
||||
column-gap: 8px;
|
||||
display: flex;
|
||||
justify-items: right;
|
||||
opacity: .9;
|
||||
padding: 5px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#control-tips-panel>*>.key {
|
||||
background-color: var(--background-grey);
|
||||
border-radius: var(--border-radius-sm);
|
||||
color: white;
|
||||
padding: 1px 4px;
|
||||
}
|
||||
104
client/plugins/controltips/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",
|
||||
"../../@types/olympus/*.d.ts"
|
||||
]
|
||||
}
|
||||
20
client/plugins/databasemanager/.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Attach to Chrome",
|
||||
"port": 9222,
|
||||
"urlFilter": "http://localhost:3000/*",
|
||||
"request": "attach",
|
||||
"type": "chrome",
|
||||
"webRoot": "${workspaceFolder}../../public/",
|
||||
"sourceMapPathOverrides": {
|
||||
"src/*": "src/*"
|
||||
},
|
||||
"preLaunchTask": "start"
|
||||
}
|
||||
]
|
||||
}
|
||||
13
client/plugins/databasemanager/.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "start",
|
||||
"type": "shell",
|
||||
"command": "npm run start",
|
||||
"isBackground": true
|
||||
}
|
||||
]
|
||||
}
|
||||
5
client/plugins/databasemanager/copy.bat
Normal file
@@ -0,0 +1,5 @@
|
||||
mkdir .\\..\\..\\public\\plugins\\databasemanager
|
||||
|
||||
copy .\\index.js .\\..\\..\\public\\plugins\\databasemanager\\index.js
|
||||
copy .\\plugin.json .\\..\\..\\public\\plugins\\databasemanager\\plugin.json
|
||||
copy .\\style.css .\\..\\..\\public\\plugins\\databasemanager\\style.css
|
||||
1
client/plugins/databasemanager/index.js
Normal file
6214
client/plugins/databasemanager/package-lock.json
generated
Normal file
32
client/plugins/databasemanager/package.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "DatabaseManagerPlugin",
|
||||
"version": "v0.0.1",
|
||||
"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']"
|
||||
},
|
||||
"dependencies": {},
|
||||
"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"
|
||||
}
|
||||
}
|
||||
6
client/plugins/databasemanager/plugin.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "Control Tip Plugin",
|
||||
"version": "0.0.1",
|
||||
"description": "This plugin shows useful control tips on the right side of the screen. The tips change dynamically depending on what the user does",
|
||||
"author": "Peekaboo"
|
||||
}
|
||||
110
client/plugins/databasemanager/src/airuniteditor.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { LoadoutBlueprint, UnitBlueprint } from "interfaces";
|
||||
import { UnitEditor } from "./uniteditor";
|
||||
import { LoadoutEditor } from "./loadouteditor";
|
||||
import { addCheckboxInput, addDropdownInput, addLoadoutsScroll, addNewElementInput, addStringInput } from "./utils";
|
||||
|
||||
/** Database editor for Air Units, both Aircraft and Helicopter since they are identical in terms of datbase entries.
|
||||
*
|
||||
*/
|
||||
export class AirUnitEditor extends UnitEditor {
|
||||
|
||||
#loadoutEditor: LoadoutEditor | null = null;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
super(contentDiv1, contentDiv2, contentDiv3);
|
||||
|
||||
/* The loadout editor allows to edit the loadout (who could have thought eh?) */
|
||||
this.#loadoutEditor = new LoadoutEditor(this.contentDiv3);
|
||||
|
||||
/* Refresh the loadout editor if needed */
|
||||
this.contentDiv3.addEventListener("refresh", () => {
|
||||
if (this.visible)
|
||||
this.#loadoutEditor?.show();
|
||||
});
|
||||
}
|
||||
|
||||
/** Sets a unit blueprint as the currently active one
|
||||
*
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint: UnitBlueprint) {
|
||||
this.blueprint = blueprint;
|
||||
|
||||
if (this.blueprint !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Unit properties";
|
||||
this.contentDiv2.appendChild(title);
|
||||
|
||||
addStringInput(this.contentDiv2, "Name", blueprint.name, "text", (value: string) => { blueprint.name = value; }, true);
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => { blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => { blueprint.shortLabel = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
addStringInput(this.contentDiv2, "Filename", blueprint.filename ?? "", "text", (value: string) => { blueprint.filename = value; });
|
||||
addStringInput(this.contentDiv2, "Cost", String(blueprint.cost) ?? "", "number", (value: string) => { blueprint.cost = parseFloat(value); });
|
||||
addCheckboxInput(this.contentDiv2, "Can target point", blueprint.canTargetPoint ?? false, (value: boolean) => {blueprint.canTargetPoint = value;})
|
||||
addStringInput(this.contentDiv2, "Description", blueprint.description ?? "", "text", (value: string) => {blueprint.description = value; });
|
||||
addStringInput(this.contentDiv2, "Tags", blueprint.tags ?? "", "text", (value: string) => {blueprint.tags = value; });
|
||||
|
||||
/* Add a scrollable list of loadouts that the user can edit */
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Loadouts";
|
||||
this.contentDiv2.appendChild(title);
|
||||
addLoadoutsScroll(this.contentDiv2, blueprint.loadouts ?? [], (loadout: LoadoutBlueprint) => {
|
||||
this.#loadoutEditor?.setLoadout(loadout);
|
||||
this.#loadoutEditor?.show();
|
||||
});
|
||||
addNewElementInput(this.contentDiv2, (ev: MouseEvent, input: HTMLInputElement) => { this.addLoadout(input.value); });
|
||||
|
||||
this.#loadoutEditor?.hide();
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty blueprint
|
||||
*
|
||||
* @param key Blueprint key
|
||||
*/
|
||||
addBlueprint(key: string) {
|
||||
if (this.database != null) {
|
||||
this.database.blueprints[key] = {
|
||||
name: key,
|
||||
coalition: "",
|
||||
label: "",
|
||||
shortLabel: "",
|
||||
era: "",
|
||||
loadouts: [],
|
||||
enabled: true
|
||||
}
|
||||
this.show();
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty loadout to the currently active blueprint
|
||||
*
|
||||
* @param loadoutName The name of the new loadout
|
||||
*/
|
||||
addLoadout(loadoutName: string) {
|
||||
if (loadoutName && this.blueprint !== null) {
|
||||
this.blueprint.loadouts?.push({
|
||||
name: loadoutName,
|
||||
code: "",
|
||||
fuel: 1,
|
||||
items: [],
|
||||
roles: [],
|
||||
enabled: true
|
||||
})
|
||||
this.setBlueprint(this.blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide the editor
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
super.hide();
|
||||
this.#loadoutEditor?.hide();
|
||||
}
|
||||
}
|
||||
424
client/plugins/databasemanager/src/databasemanagerplugin.ts
Normal file
@@ -0,0 +1,424 @@
|
||||
import { OlympusPlugin, UnitBlueprint } from "interfaces";
|
||||
import { AirUnitEditor } from "./airuniteditor";
|
||||
import { OlympusApp } from "olympusapp";
|
||||
import { GroundUnitEditor } from "./grounduniteditor";
|
||||
import { PrimaryToolbar } from "toolbars/primarytoolbar";
|
||||
import { NavyUnitEditor } from "./navyuniteditor";
|
||||
|
||||
/** Database Manager
|
||||
*
|
||||
* This database provides a user interface to allow easier and convenient unit databases manipulation. It allows to edit all the fields of the units databases, save them
|
||||
* on the server, and restore the defaults.
|
||||
*
|
||||
* TODO:
|
||||
* Add ability to manage liveries
|
||||
*
|
||||
*/
|
||||
|
||||
export class DatabaseManagerPlugin implements OlympusPlugin {
|
||||
#app!: OlympusApp;
|
||||
|
||||
#element: HTMLElement;
|
||||
#mainContentContainer: HTMLElement;
|
||||
#contentDiv1: HTMLElement;
|
||||
#contentDiv2: HTMLElement;
|
||||
#contentDiv3: HTMLElement;
|
||||
|
||||
/* Upper tab buttons */
|
||||
#button1: HTMLButtonElement;
|
||||
#button2: HTMLButtonElement;
|
||||
#button3: HTMLButtonElement;
|
||||
#button4: HTMLButtonElement;
|
||||
|
||||
/* Lower operation buttons */
|
||||
#button5: HTMLButtonElement;
|
||||
#button6: HTMLButtonElement;
|
||||
#button7: HTMLButtonElement;
|
||||
#button8: HTMLButtonElement;
|
||||
#button9: HTMLButtonElement;
|
||||
|
||||
/* Database editors */
|
||||
#aircraftEditor: AirUnitEditor;
|
||||
#helicopterEditor: AirUnitEditor;
|
||||
#groundUnitEditor: GroundUnitEditor;
|
||||
#navyUnitEditor: NavyUnitEditor;
|
||||
|
||||
constructor() {
|
||||
/* Create main HTML element */
|
||||
this.#element = document.createElement("div");
|
||||
this.#element.id = "database-manager-panel";
|
||||
this.#element.oncontextmenu = () => { return false; }
|
||||
this.#element.classList.add("ol-dialog");
|
||||
document.body.appendChild(this.#element);
|
||||
|
||||
/* Start hidden */
|
||||
this.toggle(false);
|
||||
|
||||
/* Create the top tab buttons container and buttons */
|
||||
let topButtonContainer = document.createElement("div");
|
||||
|
||||
this.#button1 = document.createElement("button");
|
||||
this.#button1.classList.add("tab-button");
|
||||
this.#button1.textContent = "Aircraft database";
|
||||
this.#button1.onclick = () => { this.#hideAll(); this.#aircraftEditor.show(); this.#button1.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button1);
|
||||
|
||||
this.#button2 = document.createElement("button");
|
||||
this.#button2.classList.add("tab-button");
|
||||
this.#button2.textContent = "Helicopter database";
|
||||
this.#button2.onclick = () => { this.#hideAll(); this.#helicopterEditor.show(); this.#button2.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button2);
|
||||
|
||||
this.#button3 = document.createElement("button");
|
||||
this.#button3.classList.add("tab-button");
|
||||
this.#button3.textContent = "Ground Unit database";
|
||||
this.#button3.onclick = () => { this.#hideAll(); this.#groundUnitEditor.show(); this.#button3.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button3);
|
||||
|
||||
this.#button4 = document.createElement("button");
|
||||
this.#button4.classList.add("tab-button");
|
||||
this.#button4.textContent = "Navy Unit database";
|
||||
this.#button4.onclick = () => { this.#hideAll(); this.#navyUnitEditor.show(); this.#button4.classList.add("selected"); };
|
||||
topButtonContainer.appendChild(this.#button4);
|
||||
|
||||
this.#element.appendChild(topButtonContainer);
|
||||
|
||||
/* Create the container for the database editor elements and the elements themselves */
|
||||
this.#mainContentContainer = document.createElement("div");
|
||||
this.#mainContentContainer.classList.add("dm-container");
|
||||
this.#element.appendChild(this.#mainContentContainer);
|
||||
|
||||
this.#contentDiv1 = document.createElement("div");
|
||||
this.#contentDiv1.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv1);
|
||||
|
||||
this.#contentDiv2 = document.createElement("div");
|
||||
this.#contentDiv2.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv2);
|
||||
|
||||
this.#contentDiv3 = document.createElement("div");
|
||||
this.#contentDiv3.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv3);
|
||||
|
||||
/* Create the database editors, which use the three divs created before */
|
||||
this.#aircraftEditor = new AirUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
this.#helicopterEditor = new AirUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
this.#groundUnitEditor = new GroundUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
this.#navyUnitEditor = new NavyUnitEditor(this.#contentDiv1, this.#contentDiv2, this.#contentDiv3);
|
||||
|
||||
/* Create the bottom buttons container. These buttons allow to save, restore, reset, and discard the changes */
|
||||
let bottomButtonContainer = document.createElement("div");
|
||||
|
||||
this.#button5 = document.createElement("button");
|
||||
this.#button5.textContent = "Save";
|
||||
this.#button5.title = "Save the changes on the server"
|
||||
this.#button5.onclick = () => { this.#saveDatabases();};
|
||||
bottomButtonContainer.appendChild(this.#button5);
|
||||
|
||||
this.#button6 = document.createElement("button");
|
||||
this.#button6.textContent = "Discard";
|
||||
this.#button6.title = "Discard all changes and reload the database from the server";
|
||||
this.#button6.onclick = () => { this.#loadDatabases(); };
|
||||
bottomButtonContainer.appendChild(this.#button6);
|
||||
|
||||
this.#button7 = document.createElement("button");
|
||||
this.#button7.textContent = "Reset defaults";
|
||||
this.#button7.onclick = () => { this.#resetToDefaultDatabases(); };
|
||||
this.#button7.title = "Reset the databases to the default values";
|
||||
bottomButtonContainer.appendChild(this.#button7);
|
||||
|
||||
this.#button8 = document.createElement("button");
|
||||
this.#button8.textContent = "Restore previous";
|
||||
this.#button8.onclick = () => { this.#restoreToPreviousDatabases(); };
|
||||
this.#button8.title = "Restore the previously saved databases. Use this if you saved a database by mistake.";
|
||||
bottomButtonContainer.appendChild(this.#button8);
|
||||
|
||||
this.#button9 = document.createElement("button");
|
||||
this.#button9.textContent = "Close";
|
||||
this.#button9.title = "Close the Database Manager"
|
||||
this.#button9.onclick = () => { this.toggle(false); };
|
||||
bottomButtonContainer.appendChild(this.#button9);
|
||||
|
||||
this.#element.appendChild(bottomButtonContainer);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The name of the plugin
|
||||
*/
|
||||
getName() {
|
||||
return "Database Control Plugin"
|
||||
}
|
||||
|
||||
/** Initialize the plugin
|
||||
*
|
||||
* @param app The OlympusApp singleton
|
||||
* @returns True if successfull
|
||||
*/
|
||||
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();
|
||||
|
||||
/* Add a button to the main Olympus App to allow the users to open the dialog */
|
||||
var mainButtonDiv = document.createElement("div");
|
||||
var mainButton = document.createElement("button");
|
||||
mainButton.textContent = "Database manager";
|
||||
mainButtonDiv.appendChild(mainButton);
|
||||
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 - 3, 0, mainButtonDiv);
|
||||
toolbar.getMainDropdown().setOptionsElements(arr);
|
||||
mainButton.onclick = () => {
|
||||
toolbar.getMainDropdown().close();
|
||||
if (this.#app?.getMissionManager().getCommandModeOptions().commandMode === "Game master")
|
||||
this.toggle();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The main container element
|
||||
*/
|
||||
getElement() {
|
||||
return this.#element;
|
||||
}
|
||||
|
||||
/** Toggles the visibility of the dialog
|
||||
*
|
||||
* @param bool Force a specific visibility state
|
||||
*/
|
||||
toggle(bool?: boolean) {
|
||||
if (bool)
|
||||
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
|
||||
*
|
||||
*/
|
||||
#hideAll() {
|
||||
this.#aircraftEditor.hide();
|
||||
this.#helicopterEditor.hide();
|
||||
this.#groundUnitEditor.hide();
|
||||
this.#navyUnitEditor.hide();
|
||||
|
||||
this.#button1.classList.remove("selected");
|
||||
this.#button2.classList.remove("selected");
|
||||
this.#button3.classList.remove("selected");
|
||||
this.#button4.classList.remove("selected");
|
||||
}
|
||||
|
||||
/** Load the databases from the app to the editor. Note, this does not reload the databases from the server to the app
|
||||
*
|
||||
*/
|
||||
#loadDatabases() {
|
||||
var aircraftDatabase = this.#app?.getAircraftDatabase();
|
||||
if (aircraftDatabase != null)
|
||||
this.#aircraftEditor.setDatabase(aircraftDatabase);
|
||||
|
||||
var helicopterDatabase = this.#app?.getHelicopterDatabase();
|
||||
if (helicopterDatabase != null)
|
||||
this.#helicopterEditor.setDatabase(helicopterDatabase);
|
||||
|
||||
var groundUnitDatabase = this.#app?.getGroundUnitDatabase();
|
||||
if (groundUnitDatabase != null)
|
||||
this.#groundUnitEditor.setDatabase(groundUnitDatabase);
|
||||
|
||||
var navyUnitDatabase = this.#app?.getNavyUnitDatabase();
|
||||
if (navyUnitDatabase != null)
|
||||
this.#navyUnitEditor.setDatabase(navyUnitDatabase);
|
||||
|
||||
this.#hideAll();
|
||||
this.#aircraftEditor.show();
|
||||
this.#button1.classList.add("selected");
|
||||
}
|
||||
|
||||
/** Save the databases on the server and reloads it to apply the changes
|
||||
*
|
||||
*/
|
||||
#saveDatabases() {
|
||||
var aircraftDatabase = this.#aircraftEditor.getDatabase();
|
||||
if (aircraftDatabase){
|
||||
this.#uploadDatabase(aircraftDatabase, "aircraftdatabase", "Aircraft database", () => {
|
||||
var helicopterDatabase = this.#helicopterEditor.getDatabase();
|
||||
if (helicopterDatabase) {
|
||||
this.#uploadDatabase(helicopterDatabase, "helicopterDatabase", "Helicopter database", () => {
|
||||
var groundUnitDatabase = this.#groundUnitEditor.getDatabase();
|
||||
if (groundUnitDatabase) {
|
||||
this.#uploadDatabase(groundUnitDatabase, "groundUnitDatabase", "Ground Unit database", () => {
|
||||
var navyUnitDatabase = this.#navyUnitEditor.getDatabase();
|
||||
if (navyUnitDatabase) {
|
||||
this.#uploadDatabase(navyUnitDatabase, "navyUnitDatabase", "Navy Unit database", () => {
|
||||
this.#app?.getAircraftDatabase().load(() => {});
|
||||
this.#app?.getHelicopterDatabase().load(() => {});
|
||||
this.#app?.getGroundUnitDatabase().load(() => {});
|
||||
this.#app?.getNavyUnitDatabase().load(() => {});
|
||||
|
||||
this.#app?.getServerManager().reloadDatabases(() => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("Olympus core databases reloaded");
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** Resets the databases to the default values
|
||||
*
|
||||
*/
|
||||
#resetToDefaultDatabases() {
|
||||
this.#resetToDefaultDatabase("aircraftdatabase", "Aircraft database", () => {
|
||||
this.#app?.getAircraftDatabase().load(() => {
|
||||
this.#resetToDefaultDatabase("helicopterdatabase", "Helicopter database", () => {
|
||||
this.#app?.getHelicopterDatabase().load(() => {
|
||||
this.#resetToDefaultDatabase("groundunitdatabase", "Ground Unit database", () => {
|
||||
this.#app?.getGroundUnitDatabase().load(() => {
|
||||
this.#resetToDefaultDatabase("navyunitdatabase", "Navy Unit database", () => {
|
||||
this.#app?.getNavyUnitDatabase().load(() => {
|
||||
this.#loadDatabases();
|
||||
|
||||
this.#app?.getServerManager().reloadDatabases(() => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("Olympus core databases reloaded");
|
||||
})
|
||||
|
||||
this.#hideAll();
|
||||
this.#aircraftEditor.show();
|
||||
this.#button1.classList.add("selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Restores the databases to the previous saved values. This is useful if you saved the databases by mistake and want to undo the error.
|
||||
*
|
||||
*/
|
||||
#restoreToPreviousDatabases() {
|
||||
this.#restoreToPreviousDatabase("aircraftdatabase", "Aircraft database", () => {
|
||||
this.#app?.getAircraftDatabase().load(() => {
|
||||
this.#restoreToPreviousDatabase("helicopterdatabase", "Helicopter database", () => {
|
||||
this.#app?.getHelicopterDatabase().load(() => {
|
||||
this.#restoreToPreviousDatabase("groundunitdatabase", "Ground Unit database", () => {
|
||||
this.#app?.getGroundUnitDatabase().load(() => {
|
||||
this.#restoreToPreviousDatabase("navyunitdatabase", "Navy Unit database", () => {
|
||||
this.#app?.getNavyUnitDatabase().load(() => {
|
||||
this.#loadDatabases();
|
||||
|
||||
this.#app?.getServerManager().reloadDatabases(() => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("Olympus core databases reloaded");
|
||||
})
|
||||
|
||||
this.#hideAll();
|
||||
this.#aircraftEditor.show();
|
||||
this.#button1.classList.add("selected");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Upload a single database to the server
|
||||
*
|
||||
* @param database The database
|
||||
* @param name The name of the database as it will be saved on the server
|
||||
* @param label A label used in the info popup
|
||||
*/
|
||||
#uploadDatabase(database: { blueprints: { [key: string]: UnitBlueprint } }, name: string, label: string, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/api/databases/save/units/" + name);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
xmlHttp.onload = (res: any) => {
|
||||
if (xmlHttp.status == 200) {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText(label + " saved successfully");
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while saving the " + label);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res: any) => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while saving the " + label);
|
||||
}
|
||||
xmlHttp.send(JSON.stringify(database));
|
||||
}
|
||||
|
||||
/** Resets a database to its default values on the server. NOTE: this only resets the database on the server, it will not reload it in the app.
|
||||
*
|
||||
* @param name The name of the database as it is saved on the server
|
||||
* @param label A label used in the info popup
|
||||
* @param callback Called when the operation is completed
|
||||
*/
|
||||
#resetToDefaultDatabase(name: string, label: string, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/api/databases/reset/units/" + name);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
xmlHttp.onload = (res: any) => {
|
||||
if (xmlHttp.status == 200) {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText(label + " reset successfully");
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while resetting the " + label);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res: any) => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while resetting the " + label)
|
||||
}
|
||||
xmlHttp.send("");
|
||||
}
|
||||
|
||||
/** Restores a database to its previously saved values on the server. NOTE: this only restores the database on the server, it will not reload it in the app.
|
||||
*
|
||||
* @param name The name of the database as it is saved on the server
|
||||
* @param label A label used in the info popup
|
||||
* @param callback Called when the operation is completed
|
||||
*/
|
||||
#restoreToPreviousDatabase(name: string, label: string, callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/api/databases/restore/units/" + name);
|
||||
xmlHttp.setRequestHeader("Content-Type", "application/json");
|
||||
xmlHttp.onload = (res: any) => {
|
||||
if (xmlHttp.status == 200) {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText(label + " restored successfully");
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while restoring the " + label);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res: any) => {
|
||||
this.#app?.getPopupsManager().get("infoPopup")?.setText("An error has occurred while restoring the " + label)
|
||||
}
|
||||
xmlHttp.send("");
|
||||
}
|
||||
}
|
||||
77
client/plugins/databasemanager/src/grounduniteditor.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { UnitBlueprint } from "interfaces";
|
||||
import { UnitEditor } from "./uniteditor";
|
||||
import { addCheckboxInput, addDropdownInput, addStringInput } from "./utils";
|
||||
|
||||
/** Database editor for ground units
|
||||
*
|
||||
*/
|
||||
export class GroundUnitEditor extends UnitEditor {
|
||||
#blueprint: UnitBlueprint | null = null;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
super(contentDiv1, contentDiv2, contentDiv3);
|
||||
}
|
||||
|
||||
/** Sets a unit blueprint as the currently active one
|
||||
*
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint: UnitBlueprint) {
|
||||
this.#blueprint = blueprint;
|
||||
|
||||
if (this.#blueprint !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Unit properties";
|
||||
this.contentDiv2.appendChild(title);
|
||||
|
||||
addStringInput(this.contentDiv2, "Name", blueprint.name, "text", (value: string) => {blueprint.name = value; }, true);
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => {blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => {blueprint.shortLabel = value; });
|
||||
addStringInput(this.contentDiv2, "Type", blueprint.type?? "", "text", (value: string) => {blueprint.type = value; });
|
||||
addStringInput(this.contentDiv2, "Unit when grouped", blueprint.unitWhenGrouped?? "", "text", (value: string) => {blueprint.unitWhenGrouped = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
addStringInput(this.contentDiv2, "Cost", String(blueprint.cost)?? "", "number", (value: string) => {blueprint.cost = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Acquisition range [m]", String(blueprint.acquisitionRange)?? "", "number", (value: string) => {blueprint.acquisitionRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Engagement range [m]", String(blueprint.engagementRange)?? "", "number", (value: string) => {blueprint.engagementRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Targeting range [m]", String(blueprint.targetingRange)?? "", "number", (value: string) => {blueprint.targetingRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Aim method range [m]", String(blueprint.aimMethodRange)?? "", "number", (value: string) => {blueprint.aimMethodRange = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Barrel height [m]", String(blueprint.barrelHeight)?? "", "number", (value: string) => {blueprint.barrelHeight = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Muzzle velocity [m/s]", String(blueprint.muzzleVelocity)?? "", "number", (value: string) => {blueprint.muzzleVelocity = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Aim time [s]", String(blueprint.aimTime)?? "", "number", (value: string) => {blueprint.aimTime = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Shots to fire", String(blueprint.shotsToFire)?? "", "number", (value: string) => {blueprint.shotsToFire = Math.round(parseFloat(value)); });
|
||||
addStringInput(this.contentDiv2, "Shots base interval [s]", String(blueprint.shotsBaseInterval)?? "", "number", (value: string) => {blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
|
||||
addStringInput(this.contentDiv2, "Shots base scatter [°]", String(blueprint.shotsBaseScatter)?? "", "number", (value: string) => {blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
|
||||
addStringInput(this.contentDiv2, "Alertness time constant [s]", String(blueprint.alertnessTimeConstant)?? "", "number", (value: string) => {blueprint.alertnessTimeConstant = Math.round(parseFloat(value)); });
|
||||
addCheckboxInput(this.contentDiv2, "Can target point", blueprint.canTargetPoint ?? false, (value: boolean) => {blueprint.canTargetPoint = value;})
|
||||
addCheckboxInput(this.contentDiv2, "Can rearm", blueprint.canRearm ?? false, (value: boolean) => {blueprint.canRearm = value;})
|
||||
addCheckboxInput(this.contentDiv2, "Can operate as AAA", blueprint.canAAA ?? false, (value: boolean) => {blueprint.canAAA = value;})
|
||||
addCheckboxInput(this.contentDiv2, "Indirect fire (e.g. mortar)", blueprint.indirectFire ?? false, (value: boolean) => {blueprint.indirectFire = value;})
|
||||
addStringInput(this.contentDiv2, "Description", blueprint.description ?? "", "text", (value: string) => {blueprint.description = value; });
|
||||
addStringInput(this.contentDiv2, "Tags", blueprint.tags ?? "", "text", (value: string) => {blueprint.tags = value; });
|
||||
addStringInput(this.contentDiv2, "Marker file", blueprint.markerFile ?? "", "text", (value: string) => {blueprint.markerFile = value; });
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty blueprint
|
||||
*
|
||||
* @param key Blueprint key
|
||||
*/
|
||||
addBlueprint(key: string) {
|
||||
if (this.database != null) {
|
||||
this.database.blueprints[key] = {
|
||||
name: key,
|
||||
coalition: "",
|
||||
label: "",
|
||||
shortLabel: "",
|
||||
era: "",
|
||||
enabled: true
|
||||
}
|
||||
this.show();
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
5
client/plugins/databasemanager/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { DatabaseManagerPlugin } from "./databasemanagerplugin";
|
||||
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new DatabaseManagerPlugin();
|
||||
}
|
||||
55
client/plugins/databasemanager/src/loadouteditor.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { LoadoutBlueprint } from "interfaces";
|
||||
import { addLoadoutItemsEditor, addStringInput, arrayToString, stringToArray } from "./utils";
|
||||
|
||||
/** The LoadoutEditor allows the user to edit a loadout
|
||||
*
|
||||
*/
|
||||
export class LoadoutEditor {
|
||||
#contentDiv: HTMLElement;
|
||||
#loadout: LoadoutBlueprint | null = null;
|
||||
#visible: boolean = false;
|
||||
|
||||
constructor(contentDiv: HTMLElement) {
|
||||
this.#contentDiv = contentDiv;
|
||||
this.#contentDiv.addEventListener("refresh", () => {
|
||||
if (this.#visible)
|
||||
this.show();
|
||||
})
|
||||
}
|
||||
|
||||
/** Set the loadout to edit
|
||||
*
|
||||
* @param loadout The loadout to edit
|
||||
*/
|
||||
setLoadout(loadout: LoadoutBlueprint) {
|
||||
this.#loadout = loadout;
|
||||
}
|
||||
|
||||
/** Show the editor
|
||||
*
|
||||
*/
|
||||
show() {
|
||||
this.#visible = true;
|
||||
this.#contentDiv.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Loadout properties";
|
||||
this.#contentDiv.appendChild(title);
|
||||
|
||||
if (this.#loadout) {
|
||||
var loadout = this.#loadout;
|
||||
addStringInput(this.#contentDiv, "Name", loadout.name, "text", (value: string) => {loadout.name = value; this.#contentDiv.dispatchEvent(new Event("refresh"));});
|
||||
addStringInput(this.#contentDiv, "Code", loadout.code, "text", (value: string) => {loadout.code = value; });
|
||||
addStringInput(this.#contentDiv, "Roles", arrayToString(loadout.roles), "text", (value: string) => {loadout.roles = stringToArray(value);});
|
||||
addLoadoutItemsEditor(this.#contentDiv, this.#loadout);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide the editor
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
this.#visible = false;
|
||||
this.#contentDiv.replaceChildren();
|
||||
}
|
||||
}
|
||||
60
client/plugins/databasemanager/src/navyuniteditor.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { UnitBlueprint } from "interfaces";
|
||||
import { UnitEditor } from "./uniteditor";
|
||||
import { addDropdownInput, addStringInput } from "./utils";
|
||||
|
||||
/** Database editor for navy units
|
||||
*
|
||||
*/
|
||||
export class NavyUnitEditor extends UnitEditor {
|
||||
#blueprint: UnitBlueprint | null = null;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
super(contentDiv1, contentDiv2, contentDiv3);
|
||||
}
|
||||
|
||||
/** Sets a unit blueprint as the currently active one
|
||||
*
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint: UnitBlueprint) {
|
||||
this.#blueprint = blueprint;
|
||||
|
||||
if (this.#blueprint !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Unit properties";
|
||||
this.contentDiv2.appendChild(title);
|
||||
|
||||
addStringInput(this.contentDiv2, "Name", blueprint.name, "text", (value: string) => {blueprint.name = value; }, true);
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => {blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => {blueprint.shortLabel = value; });
|
||||
addStringInput(this.contentDiv2, "Type", blueprint.type?? "", "text", (value: string) => {blueprint.type = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
addStringInput(this.contentDiv2, "Cost", String(blueprint.cost)?? "", "number", (value: string) => {blueprint.cost = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Barrel height [m]", String(blueprint.barrelHeight)?? "", "number", (value: string) => {blueprint.barrelHeight = parseFloat(value); });
|
||||
addStringInput(this.contentDiv2, "Muzzle velocity [m/s]", String(blueprint.muzzleVelocity)?? "", "number", (value: string) => {blueprint.muzzleVelocity = parseFloat(value); });
|
||||
}
|
||||
}
|
||||
|
||||
/** Add a new empty blueprint
|
||||
*
|
||||
* @param key Blueprint key
|
||||
*/
|
||||
addBlueprint(key: string) {
|
||||
if (this.database != null) {
|
||||
this.database.blueprints[key] = {
|
||||
name: key,
|
||||
coalition: "",
|
||||
label: "",
|
||||
shortLabel: "",
|
||||
era: "",
|
||||
enabled: true
|
||||
}
|
||||
this.show();
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
117
client/plugins/databasemanager/src/uniteditor.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import { UnitBlueprint } from "interfaces";
|
||||
import { UnitDatabase } from "unit/databases/unitdatabase";
|
||||
import { addBlueprintsScroll, addNewElementInput } from "./utils";
|
||||
|
||||
/** Base abstract class of Unit database editors
|
||||
*
|
||||
*/
|
||||
export abstract class UnitEditor {
|
||||
blueprint: UnitBlueprint | null = null;
|
||||
database: {blueprints: {[key: string]: UnitBlueprint}} | null = null;
|
||||
visible: boolean = false;
|
||||
contentDiv1: HTMLElement;
|
||||
contentDiv2: HTMLElement;
|
||||
contentDiv3: HTMLElement;
|
||||
|
||||
constructor(contentDiv1: HTMLElement, contentDiv2: HTMLElement, contentDiv3: HTMLElement) {
|
||||
this.contentDiv1 = contentDiv1;
|
||||
this.contentDiv2 = contentDiv2;
|
||||
this.contentDiv3 = contentDiv3;
|
||||
|
||||
/* Refresh the list of units if it changes */
|
||||
this.contentDiv1.addEventListener("refresh", () => {
|
||||
if (this.visible)
|
||||
this.show();
|
||||
})
|
||||
|
||||
/* If the unit properties or loadout are edited, reload the editor */
|
||||
this.contentDiv2.addEventListener("refresh", () => {
|
||||
if (this.visible) {
|
||||
if (this.blueprint !== null)
|
||||
this.setBlueprint(this.blueprint);
|
||||
}
|
||||
});
|
||||
|
||||
this.contentDiv3.addEventListener("refresh", () => {
|
||||
if (this.visible) {
|
||||
if (this.blueprint !== null)
|
||||
this.setBlueprint(this.blueprint);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param database The database that the editor will operate on
|
||||
*/
|
||||
setDatabase(database: UnitDatabase) {
|
||||
this.database = JSON.parse(JSON.stringify({blueprints: database.getBlueprints(true)}));
|
||||
}
|
||||
|
||||
/** Show the editor
|
||||
* @param filter String filter
|
||||
*/
|
||||
show(filter: string = "") {
|
||||
this.visible = true;
|
||||
this.contentDiv1.replaceChildren();
|
||||
this.contentDiv2.replaceChildren();
|
||||
this.contentDiv3.replaceChildren();
|
||||
|
||||
/* Create the list of units. Each unit is clickable to activate the editor on it */
|
||||
if (this.database != null) {
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Units list";
|
||||
this.contentDiv1.appendChild(title);
|
||||
|
||||
var filterInput = document.createElement("input");
|
||||
filterInput.value = filter;
|
||||
this.contentDiv1.appendChild(filterInput);
|
||||
|
||||
filterInput.onchange = (e: Event) => {
|
||||
this.show((e.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
this.addBlueprints(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/** Hide the editor
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
this.visible = false;
|
||||
this.contentDiv1.replaceChildren();
|
||||
this.contentDiv2.replaceChildren();
|
||||
this.contentDiv3.replaceChildren();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The edited database
|
||||
*/
|
||||
getDatabase() {
|
||||
return this.database;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param filter String filter
|
||||
*/
|
||||
addBlueprints(filter: string = "") {
|
||||
if (this.database) {
|
||||
addBlueprintsScroll(this.contentDiv1, this.database, filter, (key: string) => {
|
||||
if (this.database != null)
|
||||
this.setBlueprint(this.database.blueprints[key])
|
||||
});
|
||||
|
||||
addNewElementInput(this.contentDiv1, (ev: MouseEvent, input: HTMLInputElement) => {
|
||||
if (input.value != "")
|
||||
this.addBlueprint((input).value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* Abstract methods which will depend on the specific type of units */
|
||||
abstract setBlueprint(blueprint: UnitBlueprint): void;
|
||||
abstract addBlueprint(key: string): void;
|
||||
}
|
||||
297
client/plugins/databasemanager/src/utils.ts
Normal file
@@ -0,0 +1,297 @@
|
||||
import { LoadoutBlueprint, LoadoutItemBlueprint, UnitBlueprint } from "interfaces";
|
||||
|
||||
/** This file contains a set of utility functions that are reused in the various editors and allows to declutter the classes
|
||||
*
|
||||
*/
|
||||
|
||||
/** Add a string input in the form of String: [ value ]
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input
|
||||
* @param key The key of the input, which will be used as label
|
||||
* @param value The initial value of the input
|
||||
* @param type The type of the input, e.g. "Text" or "Number" as per html standard
|
||||
* @param callback Callback called when the user enters a new value
|
||||
* @param disabled If true, the input will be disabled and read only
|
||||
*/
|
||||
export function addStringInput(div: HTMLElement, key: string, value: string, type: string, callback: CallableFunction, disabled?: boolean) {
|
||||
var row = document.createElement("div");
|
||||
var dt = document.createElement("dt");
|
||||
var dd = document.createElement("dd");
|
||||
dt.innerText = key;
|
||||
var input = document.createElement("input");
|
||||
input.value = value;
|
||||
input.textContent = value;
|
||||
input.type = type?? "text";
|
||||
input.disabled = disabled?? false;
|
||||
input.onchange = () => callback(input.value);
|
||||
dd.appendChild(input);
|
||||
row.appendChild(dt);
|
||||
row.appendChild(dd);
|
||||
row.classList.add("input-row");
|
||||
div.appendChild(row);
|
||||
}
|
||||
|
||||
/** Add a dropdown (select) input
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input
|
||||
* @param key The key of the input, which will be used as label
|
||||
* @param value The initial value of the input
|
||||
* @param options The dropdown options
|
||||
*/
|
||||
export function addDropdownInput(div: HTMLElement, key: string, value: string, options: string[], callback: CallableFunction, disabled?: boolean) {
|
||||
var row = document.createElement("div");
|
||||
var dt = document.createElement("dt");
|
||||
var dd = document.createElement("dd");
|
||||
dt.innerText = key;
|
||||
var select = document.createElement("select");
|
||||
options.forEach((option: string) => {
|
||||
var el = document.createElement("option");
|
||||
el.value = option;
|
||||
el.innerText = option;
|
||||
select.appendChild(el);
|
||||
});
|
||||
select.value = value;
|
||||
select.disabled = disabled?? false;
|
||||
select.onchange = () => callback(select.value);
|
||||
dd.appendChild(select);
|
||||
row.appendChild(dt);
|
||||
row.appendChild(dd);
|
||||
row.classList.add("input-row");
|
||||
div.appendChild(row);
|
||||
}
|
||||
|
||||
/** Add a checkbox input in the form of String: [ value ]
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input
|
||||
* @param key The key of the input, which will be used as label
|
||||
* @param value The initial value of the input
|
||||
* @param callback Callback called when the user enters a new value
|
||||
* @param disabled If true, the input will be disabled and read only
|
||||
*/
|
||||
export function addCheckboxInput(div: HTMLElement, key: string, value: boolean, callback: CallableFunction, disabled?: boolean) {
|
||||
var row = document.createElement("div");
|
||||
var dt = document.createElement("dt");
|
||||
var dd = document.createElement("dd");
|
||||
dt.innerText = key;
|
||||
var input = document.createElement("input");
|
||||
input.checked = value;
|
||||
input.type = "checkbox";
|
||||
input.disabled = disabled?? false;
|
||||
input.onchange = () => callback(input.checked);
|
||||
dd.appendChild(input);
|
||||
row.appendChild(dt);
|
||||
row.appendChild(dd);
|
||||
row.classList.add("input-row");
|
||||
div.appendChild(row);
|
||||
}
|
||||
|
||||
/** Create a loadout items editor. This editor allows to add or remove loadout items, as well as changing their name and quantity
|
||||
*
|
||||
* @param div The HTMLElement that will contain the editor
|
||||
* @param loadout The loadout to edit
|
||||
*/
|
||||
export function addLoadoutItemsEditor(div: HTMLElement, loadout: LoadoutBlueprint) {
|
||||
var itemsEl = document.createElement("div");
|
||||
itemsEl.classList.add("dm-scroll-container", "dm-items-container");
|
||||
|
||||
/* Create a row for each loadout item to allow and change the name and quantity of the item itself */
|
||||
loadout.items.sort((a: LoadoutItemBlueprint, b: LoadoutItemBlueprint) => a.name.localeCompare(b.name, undefined, {sensitivity: 'base'}));
|
||||
loadout.items.forEach((item: LoadoutItemBlueprint, index: number) => {
|
||||
var rowDiv = document.createElement("div");
|
||||
|
||||
var nameLabel = document.createElement("label");
|
||||
nameLabel.innerText = "Name"
|
||||
rowDiv.appendChild(nameLabel);
|
||||
|
||||
var nameInput = document.createElement("input");
|
||||
rowDiv.appendChild(nameInput);
|
||||
nameInput.textContent = item.name;
|
||||
nameInput.value = item.name
|
||||
nameInput.onchange = () => { loadout.items[index].name = nameInput.value; }
|
||||
|
||||
var quantityLabel = document.createElement("label");
|
||||
quantityLabel.innerText = "Quantity"
|
||||
rowDiv.appendChild(quantityLabel);
|
||||
|
||||
var quantityInput = document.createElement("input");
|
||||
rowDiv.appendChild(quantityInput);
|
||||
quantityInput.textContent = String(item.quantity);
|
||||
quantityInput.value = String(item.quantity);
|
||||
quantityInput.type = "number";
|
||||
quantityInput.step = "1";
|
||||
quantityInput.onchange = () => { loadout.items[index].quantity = parseInt(quantityInput.value); }
|
||||
|
||||
/* This button allows to remove the item */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
loadout.items.splice(index, 1);
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
}
|
||||
rowDiv.appendChild(button);
|
||||
|
||||
itemsEl.appendChild(rowDiv);
|
||||
})
|
||||
div.appendChild(itemsEl);
|
||||
|
||||
/* Button to add a new item to the loadout */
|
||||
var inputDiv = document.createElement("div");
|
||||
inputDiv.classList.add("dm-new-item-input");
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "Add";
|
||||
inputDiv.appendChild(button);
|
||||
div.appendChild(inputDiv);
|
||||
|
||||
button.addEventListener("click", (ev: MouseEvent) => {
|
||||
loadout?.items.push({
|
||||
name: "",
|
||||
quantity: 1
|
||||
})
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
});
|
||||
}
|
||||
|
||||
/** Add a input and button to create a new element in a list. It uses a generic callback to actually add the element.
|
||||
*
|
||||
* @param div The HTMLElement that will contain the input and button
|
||||
* @param callback Callback called when the user clicks on "Add"
|
||||
*/
|
||||
export function addNewElementInput(div: HTMLElement, callback: CallableFunction) {
|
||||
var inputDiv = document.createElement("div");
|
||||
inputDiv.classList.add("dm-new-element-input");
|
||||
|
||||
var input = document.createElement("input");
|
||||
inputDiv.appendChild(input);
|
||||
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "Add";
|
||||
button.addEventListener("click", (ev: MouseEvent) => callback(ev, input));
|
||||
inputDiv.appendChild(button);
|
||||
div.appendChild(inputDiv);
|
||||
}
|
||||
|
||||
/** Add a scrollable list of blueprints
|
||||
*
|
||||
* @param div The HTMLElement that will contain the list
|
||||
* @param database The database that will be used to fill the list of blueprints
|
||||
* @param filter A string filter that will be executed to filter the blueprints to add
|
||||
* @param callback Callback called when the user clicks on one of the elements
|
||||
*/
|
||||
export function addBlueprintsScroll(div: HTMLElement, database: {blueprints: {[key: string]: UnitBlueprint}}, filter: string, callback: CallableFunction) {
|
||||
var scrollDiv = document.createElement("div");
|
||||
scrollDiv.classList.add("dm-scroll-container");
|
||||
if (database !== null) {
|
||||
var blueprints: {[key: string]: UnitBlueprint} = database.blueprints;
|
||||
|
||||
for (let key of Object.keys(blueprints).sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'}))) {
|
||||
var addKey = true;
|
||||
if (filter !== "") {
|
||||
try {
|
||||
var blueprint = blueprints[key];
|
||||
addKey = eval(filter);
|
||||
} catch {
|
||||
console.error("An error has occurred evaluating the blueprint filter")
|
||||
}
|
||||
}
|
||||
|
||||
if (addKey) {
|
||||
var rowDiv = document.createElement("div");
|
||||
scrollDiv.appendChild(rowDiv);
|
||||
|
||||
let text = document.createElement("div");
|
||||
text.innerHTML = `<div>${key}</div> <div>${blueprints[key].label}</div>`;
|
||||
text.onclick = () => {
|
||||
callback(key);
|
||||
const collection = document.getElementsByClassName("blueprint-selected");
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
collection[i].classList.remove("blueprint-selected");
|
||||
}
|
||||
text.classList.add("blueprint-selected");
|
||||
}
|
||||
rowDiv.appendChild(text);
|
||||
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = blueprints[key].enabled;
|
||||
checkbox.onclick = () => {
|
||||
console.log(checkbox.checked);
|
||||
blueprints[key].enabled = checkbox.checked;
|
||||
}
|
||||
rowDiv.appendChild(checkbox);
|
||||
|
||||
/* This button allows to remove an element from the list. It requires a refresh. */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
delete blueprints[key];
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
}
|
||||
rowDiv.appendChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
div.appendChild(scrollDiv);
|
||||
}
|
||||
|
||||
/** Add a scrollable list of loadouts
|
||||
*
|
||||
* @param div The HTMLElement that will contain the list
|
||||
* @param loadouts The loadouts that will be used to fill the list
|
||||
* @param callback Callback called when the user clicks on one of the elements
|
||||
*/
|
||||
export function addLoadoutsScroll(div: HTMLElement, loadouts: LoadoutBlueprint[], callback: CallableFunction) {
|
||||
var loadoutsEl = document.createElement("div");
|
||||
loadoutsEl.classList.add("dm-scroll-container", "dm-loadout-container")
|
||||
|
||||
loadouts.sort((a: LoadoutBlueprint, b: LoadoutBlueprint) => a.name.localeCompare(b.name, undefined, {sensitivity: 'base'}));
|
||||
loadouts.forEach((loadout: LoadoutBlueprint, index: number) => {
|
||||
var rowDiv = document.createElement("div");
|
||||
loadoutsEl.appendChild(rowDiv);
|
||||
|
||||
var text = document.createElement("label");
|
||||
text.textContent = loadout.name;
|
||||
text.onclick = () => { callback(loadout) };
|
||||
rowDiv.appendChild(text);
|
||||
|
||||
/* The "Empty loadout" can not be removed */
|
||||
if (loadout.name !== "Empty loadout") {
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = loadout.enabled;
|
||||
checkbox.onclick = () => {
|
||||
console.log(checkbox.checked);
|
||||
loadout.enabled = checkbox.checked;
|
||||
}
|
||||
rowDiv.appendChild(checkbox);
|
||||
|
||||
/* This button allows to remove an element from the list. It requires a refresh. */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
loadouts.splice(index, 1);
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
}
|
||||
rowDiv.appendChild(button);
|
||||
}
|
||||
});
|
||||
|
||||
div.appendChild(loadoutsEl);
|
||||
}
|
||||
|
||||
/** Converts an array of string into a single string like [val1, val2, val3]
|
||||
*
|
||||
* @param array The input array of strings
|
||||
* @returns The string
|
||||
*/
|
||||
export function arrayToString(array: string[]) {
|
||||
return "[" + array.join( ", " ) + "]";
|
||||
}
|
||||
|
||||
/** Converts an a single string like [val1, val2, val3] into an array
|
||||
*
|
||||
* @param input The input string
|
||||
* @returns The array
|
||||
*/
|
||||
export function stringToArray(input: string) {
|
||||
return input.match( /(\w)+/g ) ?? [];
|
||||
}
|
||||
295
client/plugins/databasemanager/style.css
Normal file
@@ -0,0 +1,295 @@
|
||||
#database-manager-panel {
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
background-color: var(--background-steel) !important;
|
||||
z-index: 9999999;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.dm-container {
|
||||
flex-direction: row;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.dm-container {
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
|
||||
#database-manager-panel * {
|
||||
font-size: 13;
|
||||
font-family: 'Open Sans', sans-serif !important;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
#database-manager-panel>div:first-child {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#database-manager-panel>div:last-child {
|
||||
display: flex;
|
||||
column-gap: 5px;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
justify-items: end;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
#database-manager-panel>div:last-child>button {
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.dm-container {
|
||||
background-color: var(--background-grey);
|
||||
border: 2px solid #777777;
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
height: calc(100% - 64px - 5px);
|
||||
border-radius: 0px 5px 5px 5px;
|
||||
}
|
||||
|
||||
.dm-content-container {
|
||||
position: relative;
|
||||
margin: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
max-height: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.dm-content-container {
|
||||
height: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(1) {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(2) {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(3) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.dm-content-container {
|
||||
width: calc(100% - 20px);
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(1) {
|
||||
height: 30%;
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(2) {
|
||||
height: 50%;
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(3) {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.dm-content-container>label {
|
||||
font-size: 18px !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dm-scroll-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: scroll;
|
||||
max-height: 100%;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#database-manager-panel input {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div:nth-child(even) {
|
||||
background-color: gainsboro;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div:nth-child(odd) {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div *:nth-child(1) {
|
||||
height: 100%;
|
||||
width: calc(100% - 25px);
|
||||
padding: 2px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div *:nth-child(1):hover {
|
||||
background-color: var(--accent-dark-blue);
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.blueprint-selected {
|
||||
background-color: var(--accent-light-blue) !important;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>button {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div>div:nth-child(1) {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div>div:nth-child(2) {
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.input-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.dm-content-container label {
|
||||
width: 100%;
|
||||
}
|
||||
.input-row {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.input-row>dt {
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.input-row>dd {
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.input-row>dd>* {
|
||||
width: 100%;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.input-row>dd>*[type="checkbox"] {
|
||||
width: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dm-loadout-container {
|
||||
max-height: 100%;
|
||||
max-width: 500px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dm-items-container {
|
||||
max-height: 100%;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.dm-items-container>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
column-gap: 2px;
|
||||
}
|
||||
|
||||
.dm-items-container>div>label {
|
||||
width: 80px !important;
|
||||
}
|
||||
|
||||
.dm-items-container div>input:nth-of-type(1) {
|
||||
flex: 1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dm-items-container div>input:nth-of-type(2) {
|
||||
width: 40px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dm-new-element-input {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
column-gap: 2px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.dm-new-element-input>input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.dm-new-element-input>button {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.dm-new-item-input {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.dm-new-item-input>button {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.tab-button {
|
||||
transform: translateY(+3px);
|
||||
background-color: var(--background-steel);
|
||||
border-radius: 0;
|
||||
border-bottom: 2px solid transparent !important;
|
||||
border-top: 2px solid #777777 !important;
|
||||
border-left: 2px solid #777777 !important;
|
||||
border-right: 0px solid #777777 !important;
|
||||
}
|
||||
|
||||
.tab-button.selected {
|
||||
background-color: var(--background-grey);
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.tab-button:first-of-type {
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
|
||||
.tab-button:last-of-type {
|
||||
border-top-right-radius: 5px;
|
||||
border-right: 2px solid #777777 !important;
|
||||
}
|
||||
|
||||
#database-manager-panel button :not(.dm-scroll-container>div) {
|
||||
border: 1px solid white;
|
||||
}
|
||||
103
client/plugins/databasemanager/tsconfig.json
Normal file
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"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"], /* 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"
|
||||
]
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
copy .\\node_modules\\leaflet\\dist\\leaflet.css .\\public\\stylesheets\\leaflet\\leaflet.css
|
||||
copy .\\node_modules\\leaflet.nauticscale\\dist\\leaflet.nauticscale.js .\\public\\javascripts\\leaflet.nauticscale.js
|
||||
32392
client/public/databases/units/default/aircraftdatabase.json
Normal file
10299
client/public/databases/units/default/groundunitdatabase.json
Normal file
4302
client/public/databases/units/default/helicopterdatabase.json
Normal file
1728
client/public/databases/units/default/navyunitdatabase.json
Normal file
@@ -1,767 +0,0 @@
|
||||
{
|
||||
"An-26B": {
|
||||
"name": "An-26B",
|
||||
"coalition": "red",
|
||||
"label": "An-26B Curl",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "26",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "an-26.png",
|
||||
"cost": 0
|
||||
},
|
||||
"An-30M": {
|
||||
"name": "An-30M",
|
||||
"coalition": "red",
|
||||
"label": "An-30M Clank",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "30",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "a-50.png",
|
||||
"cost": 0
|
||||
},
|
||||
"C-130": {
|
||||
"name": "C-130",
|
||||
"label": "C-130 Hercules",
|
||||
"era": "Early Cold War",
|
||||
"shortLabel": "130",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "c-130.png",
|
||||
"cost": 0
|
||||
},
|
||||
"F-14A-135-GR": {
|
||||
"name": "F-14A-135-GR",
|
||||
"coalition": "blue",
|
||||
"label": "F-14A-135-GR Tomcat",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "14A",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "fuel",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "AIM-54A",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "AIM-7F",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "AIM-9L",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "AIM-54A-MK47*2, AIM-7F*1, AIM-9L*4, XT*2",
|
||||
"name": "Heavy / Fox 3 / Long Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "fuel",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "AIM-7F",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "AIM-9L",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "AIM-7F*4, AIM-9L*4, XT*2",
|
||||
"name": "Heavy / Fox 1 / Long Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "fuel",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "AIM-7M",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "AIM-9M",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "GBU-12",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "LANTIRN",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "AIM-7M*1, AIM-9M*2, XT*2, GBU-12*2, LANTIRN",
|
||||
"name": "Heavy / Fox 3, GBU-12 / Long Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "f-14.png",
|
||||
"cost": 300,
|
||||
"liveryID": "IRIAF Asia Minor"
|
||||
},
|
||||
"F-4E": {
|
||||
"name": "F-4E",
|
||||
"coalition": "blue",
|
||||
"label": "F-4E Phantom II",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "4",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "fuel",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "AIM-7M",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "AIM-9M",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "AIM-9*4,AIM-7*4,Fuel*2",
|
||||
"name": "Heavy / Fox 1 / Long Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "ECM",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "AIM-7M",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "Mk-82",
|
||||
"quantity": 18
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "Mk-82*18,AIM-7*2,ECM",
|
||||
"name": "Heavy / Fox 1, Mk-82 / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "f-4.png",
|
||||
"cost": 100,
|
||||
"liveryID": "IRIAF Asia Minor"
|
||||
},
|
||||
"F-5E-3": {
|
||||
"name": "F-5E-3",
|
||||
"coalition": "blue",
|
||||
"label": "F-5E Tiger",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "5",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Fuel 275",
|
||||
"quantity": 3
|
||||
},
|
||||
{
|
||||
"name": "AIM-9P5",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "AIM-9P5*2, Fuel 275*3",
|
||||
"name": "Heavy / Fox 2 / Long Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Mk-82",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "AIM-9P5",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "Fuel 275",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "Mk-82LD*4,AIM-9P*2,Fuel 275",
|
||||
"name": "Heavy / Fox 2 / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"liveryID": "ir iriaf 43rd tfs",
|
||||
"filename": "f-5.png",
|
||||
"cost": 80
|
||||
},
|
||||
"F-86F Sabre": {
|
||||
"name": "F-86F Sabre",
|
||||
"coalition": "blue",
|
||||
"label": "F-86F Sabre",
|
||||
"era": "Early Cold War",
|
||||
"shortLabel": "86",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "120gal Fuel",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "120gal Fuel*2",
|
||||
"name": "Light / Guns / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "HVAR",
|
||||
"quantity": 16
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "HVAR*16",
|
||||
"name": "Light / HVAR / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AN-M64",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "AN-M64*2",
|
||||
"name": "Light / AN-M64 / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Light / Guns / Short Range"
|
||||
}
|
||||
],
|
||||
"filename": "f-86.png",
|
||||
"cost": 40,
|
||||
"liveryID": "iiaf bare metall"
|
||||
},
|
||||
"IL-76MD": {
|
||||
"name": "IL-76MD",
|
||||
"label": "IL-76MD Candid",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "76",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Default Transport"
|
||||
}
|
||||
],
|
||||
"filename": "il-76.png",
|
||||
"cost": 0
|
||||
},
|
||||
"MiG-15bis": {
|
||||
"name": "MiG-15bis",
|
||||
"coalition": "red",
|
||||
"label": "MiG-15 Fagot",
|
||||
"era": "Early Cold War",
|
||||
"shortLabel": "M15",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "300L Fuel Tanks",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "2*300L",
|
||||
"name": "Medium / Guns / Medium Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "FAB-100M",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "2*FAB-100M",
|
||||
"name": "Medium / FAB-100M / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Light / Guns / Short Range"
|
||||
}
|
||||
],
|
||||
"filename": "mig-15.png",
|
||||
"cost": 30,
|
||||
"liveryID": "Iraqi_Camo"
|
||||
},
|
||||
"MiG-21Bis": {
|
||||
"name": "MiG-21Bis",
|
||||
"coalition": "red",
|
||||
"label": "MiG-21 Fishbed",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "21",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "R-3 Atoll",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "R-60 Aphid",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "130 gal tanks",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "ASO-2 Countermeasures",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "Patrol, short range",
|
||||
"name": "Light / Fox-2 / Short range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "R-3 Atoll",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "R-60 Aphid",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "210 gal tanks",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "ASO-2 Countermeasures",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "Patrol, medium range",
|
||||
"name": "Medium / Fox-2 / Medium range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "R-3R Atoll",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "R-3S Atoll",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "210 gal tanks",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "ASO-2 Countermeasures",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "Patrol, long range",
|
||||
"name": "Medium / Fox-1, Fox-2 / Medium range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "GROM",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "FAB-250",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "210 gal tanks",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "ASO-2 Countermeasures",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "Few big targets, GROM + BOMBS",
|
||||
"name": "Heavy / GROM, FAB250 / Medium range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mig-21.png",
|
||||
"cost": 100,
|
||||
"liveryID": "iran - standard"
|
||||
},
|
||||
"MiG-23MLD": {
|
||||
"name": "MiG-23MLD",
|
||||
"coalition": "red",
|
||||
"label": "MiG-23 Flogger",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "23",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Fuel-800",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "R-60M",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "R-24R",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "R-24R*2,R-60M*4,Fuel-800",
|
||||
"name": "Heavy / Fox 1 / Long Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "Fuel-800",
|
||||
"quantity": 1
|
||||
},
|
||||
{
|
||||
"name": "FAB-500",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "R-60M",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "FAB-500*2,R-60M*2,Fuel-800",
|
||||
"name": "Heavy / FAB-500 / Long Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mig-23.png",
|
||||
"cost": 200
|
||||
},
|
||||
"Mirage-F1EE": {
|
||||
"name": "Mirage-F1EE",
|
||||
"coalition": "red",
|
||||
"label": "Mirage-F1EE",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "F1EE",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AIM-9JULI",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "R530EM",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "1137L Fuel Tank",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "2*AIM9-JULI, 2*R530EM, 1*Fuel Tank",
|
||||
"name": "Medium / Fox 1 / Medium Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AIM-9JULI",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "SAMP 400 LD",
|
||||
"quantity": 8
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "2*AIM-9JULI, 8*SAMP 400 LD",
|
||||
"name": "Heavy / SAMP400 / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mig-25.png",
|
||||
"cost": 150,
|
||||
"liveryID": "iriaf 3-6215 _ 1990-2010s desert (eq variant)"
|
||||
},
|
||||
"Mirage-F1CE": {
|
||||
"name": "Mirage-F1CE",
|
||||
"coalition": "red",
|
||||
"label": "Mirage-F1CE",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "F1CE",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AIM-9JULI",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "R530IR",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "1137L Fuel Tank",
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAP"
|
||||
],
|
||||
"code": "2*AIM9-JULI, 2*R530IR, 1*Fuel Tank",
|
||||
"name": "Medium / Fox 2 / Medium Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "AIM-9JULI",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "SAMP 400 LD",
|
||||
"quantity": 8
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "2*AIM-9JULI, 8*SAMP 400 LD",
|
||||
"name": "Heavy / SAMP400 / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mig-25.png",
|
||||
"cost": 150,
|
||||
"liveryID": "iraq air force (fictional eq version)"
|
||||
},
|
||||
"Su-24M": {
|
||||
"name": "Su-24M",
|
||||
"coalition": "red",
|
||||
"label": "Su-24M Fencer",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "24",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "R-60M",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "FAB-1500",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "FAB-1500*2,R-60M*2",
|
||||
"name": "Heavy / FAB-500 / Short Range"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "su-24.png",
|
||||
"cost": 250,
|
||||
"liveryID": "iran air force"
|
||||
}
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
{
|
||||
"BMP-1": {
|
||||
"name": "BMP-1",
|
||||
"coalition": "red",
|
||||
"era": "Mid Cold War",
|
||||
"label": "BMP-1",
|
||||
"shortLabel": "BMP-1",
|
||||
"filename": "",
|
||||
"type": "IFV",
|
||||
"cost": 10,
|
||||
"liveryID": "desert"
|
||||
},
|
||||
"Hawk SAM Battery": {
|
||||
"name": "Hawk SAM Battery",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "Hawk SAM Battery",
|
||||
"shortLabel": "Hawk SAM Battery",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"type": "SAM Site",
|
||||
"cost": 500
|
||||
},
|
||||
"Infantry AK": {
|
||||
"name": "Infantry AK",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Infantry AK",
|
||||
"shortLabel": "Infantry AK",
|
||||
"filename": "",
|
||||
"type": "Infantry",
|
||||
"cost": 1
|
||||
},
|
||||
"M-113": {
|
||||
"name": "M-113",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "M-113",
|
||||
"shortLabel": "M-113",
|
||||
"filename": "",
|
||||
"type": "APC",
|
||||
"cost": 5
|
||||
},
|
||||
"M-60": {
|
||||
"name": "M-60",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "M-60",
|
||||
"shortLabel": "M-60",
|
||||
"filename": "",
|
||||
"type": "Tank",
|
||||
"cost": 10
|
||||
},
|
||||
"SA-2 SAM Battery": {
|
||||
"name": "SA-2 SAM Battery",
|
||||
"coalition": "red",
|
||||
"era": "Early Cold War",
|
||||
"label": "SA-2 SAM Battery",
|
||||
"shortLabel": "SA-2 SAM Battery",
|
||||
"range": "Long",
|
||||
"filename": "",
|
||||
"type": "SAM Site",
|
||||
"cost": 500
|
||||
},
|
||||
"T-55": {
|
||||
"name": "T-55",
|
||||
"coalition": "red",
|
||||
"era": "Early Cold War",
|
||||
"label": "T-55",
|
||||
"shortLabel": "T-55",
|
||||
"filename": "",
|
||||
"type": "Tank",
|
||||
"cost": 10,
|
||||
"liveryID": "desert"
|
||||
},
|
||||
"T-72B": {
|
||||
"name": "T-72B",
|
||||
"coalition": "red",
|
||||
"era": "Mid Cold War",
|
||||
"label": "T-72B",
|
||||
"shortLabel": "T-72B",
|
||||
"filename": "",
|
||||
"type": "Tank",
|
||||
"cost": 20,
|
||||
"liveryID": "desert"
|
||||
},
|
||||
"ZSU-23-4 Shilka": {
|
||||
"name": "ZSU-23-4 Shilka",
|
||||
"era": "Late Cold War",
|
||||
"label": "ZSU-23-4 Shilka",
|
||||
"shortLabel": "ZSU-23-4 Shilka",
|
||||
"filename": "",
|
||||
"type": "AAA",
|
||||
"cost": 100
|
||||
},
|
||||
"Ural-375 ZU-23": {
|
||||
"name": "Ural-375 ZU-23",
|
||||
"era": "Early Cold War",
|
||||
"label": "Ural-375 ZU-23",
|
||||
"shortLabel": "Ural-375 ZU-23",
|
||||
"filename": "",
|
||||
"type": "AAA",
|
||||
"cost": 50
|
||||
},
|
||||
"SAU Gvozdika": {
|
||||
"name": "SAU Gvozdika",
|
||||
"coalition": "red",
|
||||
"era": "Early Cold War",
|
||||
"label": "SAU Gvozdika",
|
||||
"shortLabel": "SAU Gvozdika",
|
||||
"filename": "",
|
||||
"type": "Gun Artillery",
|
||||
"cost": 10,
|
||||
"liveryID": "desert"
|
||||
},
|
||||
"Grad-URAL": {
|
||||
"name": "Grad-URAL",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "BM-21",
|
||||
"shortLabel": "BM-21",
|
||||
"filename": "",
|
||||
"type": "Rocket Artillery",
|
||||
"cost": 10
|
||||
},
|
||||
"Chieftain_mk3": {
|
||||
"name": "Chieftain_mk3",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "Chieftain Mk3",
|
||||
"shortLabel": "Chieftain Mk3",
|
||||
"filename": "",
|
||||
"type": "Tank",
|
||||
"cost": 20
|
||||
},
|
||||
"Scud_B": {
|
||||
"name": "Scud_B",
|
||||
"era": "Early Cold War",
|
||||
"label": "SCUD",
|
||||
"shortLabel": "SCUD",
|
||||
"filename": "",
|
||||
"type": "Rocket Artillery",
|
||||
"cost": 10
|
||||
},
|
||||
"tt_ZU-23": {
|
||||
"name": "tt_ZU-23",
|
||||
"era": "Early Cold War",
|
||||
"label": "Technical ZSU-23",
|
||||
"shortLabel": "Technical ZSU-23",
|
||||
"filename": "",
|
||||
"type": "AAA",
|
||||
"cost": 25
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"Mi-24P": {
|
||||
"name": "Mi-24P",
|
||||
"coalition": "red",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Mi-24P Hind",
|
||||
"shortLabel": "Mi24",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "S-8KOM",
|
||||
"quantity": 40
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 8
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2xB8V20 (S-8KOM)+8xATGM 9M114",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "S-24B",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"Strike"
|
||||
],
|
||||
"code": "4xS-24B+4xATGM 9M114",
|
||||
"name": "Gun / ATGM / Rockets"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "GUV-1 Grenade Launcher",
|
||||
"quantity": 4
|
||||
},
|
||||
{
|
||||
"name": "9M114 ATGM",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "4xGUV-1 AP30+4xATGM 9M114",
|
||||
"name": "Gun / ATGM / Grenade Launcher"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
""
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-24.png",
|
||||
"cost": 150,
|
||||
"liveryID": "IQAF"
|
||||
},
|
||||
"Mi-8MT": {
|
||||
"name": "Mi-8MT",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Mi-8MT Hip",
|
||||
"shortLabel": "Mi8",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "UPK",
|
||||
"quantity": 2
|
||||
},
|
||||
{
|
||||
"name": "B8",
|
||||
"quantity": 2
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "2 x UPK +2 x B8",
|
||||
"name": "Rockets / Gunpods"
|
||||
},
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [],
|
||||
"roles": [
|
||||
"Transport"
|
||||
],
|
||||
"code": "",
|
||||
"name": "Empty Loadout"
|
||||
}
|
||||
],
|
||||
"filename": "mi-8.png",
|
||||
"cost": 100,
|
||||
"liveryID": "IR Iranian Special Police Forces"
|
||||
},
|
||||
"SA342M": {
|
||||
"name": "SA342M",
|
||||
"coalition": "blue",
|
||||
"era": "Mid Cold War",
|
||||
"label": "SA342M Gazelle",
|
||||
"shortLabel": "342",
|
||||
"loadouts": [
|
||||
{
|
||||
"fuel": 1,
|
||||
"items": [
|
||||
{
|
||||
"name": "HOT3",
|
||||
"quantity": 4
|
||||
}
|
||||
],
|
||||
"roles": [
|
||||
"CAS"
|
||||
],
|
||||
"code": "4x HOT3, IR Deflector, Sand Filter",
|
||||
"name": "ATGM"
|
||||
}
|
||||
],
|
||||
"filename": "sa-342.png",
|
||||
"cost": 80
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
|
||||
}
|
||||
@@ -75,7 +75,7 @@
|
||||
<path d="M232.2 261.4a3.7 3.7 0 0 1 3.7-3 3.7 3.7 0 0 1 3.6 3.7 3.8 3.8 0 0 1-1 2.6"/>
|
||||
<path d="M239.4 261.3a15.5 15.5 0 0 0 6.2-12.4c0-4.1-1.6-8.4-3.6-10"/>
|
||||
<path stroke-linecap="round" d="M244.7 259.4a16.5 16.5 0 0 1-6.3 6"/>
|
||||
<path d="M254.6 273.7c-1-2.2-2.8-3.2-5.8-3.5-3-.3-5.5.5-8.2 1.9a18.6 18.6 0 0 0-10.8 17 25 25 0 0 0 2 9.5c.9 1.6 3 9 15.3 14a86.1 86.1 0 0 0 25.7 3.9c10.4.4 20 .8 25.6 7.6"/>
|
||||
<path d="M254.6 273.7c-1-2.2-2.8-3.2-5.8-3.5-3-.3-5.5.5-8.2 1.9a18.6 18.6 0 0 0-10.8 17 25 25 0 0 0 2 9.5c.9 1.6 3 9 15.3 14a86.1 86.1 0 0 0 25.7 3.9c10.4.5 20 .8 25.6 7.6"/>
|
||||
<path stroke-linecap="round" d="M239.7 275.9c3.3-2.2 9.5-5 15.1-2.2a8 8 0 0 1 4.3 4.4 10 10 0 0 1-1.8 9.5c-.9 1-2.7 2.6-5 2.7-3.8 0-4.7-2.6-4.8-3.2-.4-2.8 1.2-3.9 2-4.2.7-.2 2.8-.1 3.2 1.4.2.5.2 1.3-.2 2"/>
|
||||
<path d="M252.7 285.7c.3-1 .2-2.2-.8-3.3a5.1 5.1 0 0 0-6-1c-.7.5-1.6 1-2.4 2.2-.4.4-1 1.1-1.2 1.6-.7 1.1-1 2-1 2.5-2.5 7 1.5 14.4 6.5 17.6 4.4 2.8 8.8 3.6 14.4 4 2.5.3 4 .3 6.5.5h9.6a70.1 70.1 0 0 1 7.2 0c3 .3 5.1.4 7.6.8 1.6.2 3.5.5 5.4 1 .6 0 1.2.2 1.8.4l1.2.3c3.6 1.1 7 2.4 9.8 4.2.8.5 1.8 1 2.5 1.7l1.3 1.2c2 2 4 4 4.4 6.7v1.6c0 1.8-1.4 4.3-5.3 5"/>
|
||||
<path d="M298.6 328.4c-1 2.2.2 3.2 1.6 3.4 2.2.3 3.3-1.4 3.5-3a4.4 4.4 0 0 0-4.4-4.7 5.5 5.5 0 0 0-5 3.5 6.9 6.9 0 0 0-.5 2.4 5.8 5.8 0 0 0 1.4 4.1 7.5 7.5 0 0 0 5.4 2.5c4.2.1 7.5-3.8 7.5-7.8 0-7.7-11.4-12-16-13a84 84 0 0 0-17.9-2.4c-3.5-.1-6.2 0-10.1-.5-3.5-.3-5.4-.5-9-1.3a27.2 27.2 0 0 1-12.5-6.4 17 17 0 0 1-4.7-22 14.3 14.3 0 0 1 10.3-6.9c3.8-.5 7 1.1 9 4.8 1 1.8.6 4.8-.1 6.2a6 6 0 0 1-4.8 3c-3.8 0-4.7-2.6-4.8-3.2-.4-2.8 1.2-3.9 2-4.2.7-.2 2.8-.1 3.2 1.4.2.5.2 1.3-.2 2"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
57
client/public/images/countries/blue.svg
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
width="640"
|
||||
height="480"
|
||||
id="svg10"
|
||||
sodipodi:docname="blue.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.88166667"
|
||||
inkscape:cx="497.3535"
|
||||
inkscape:cy="301.13421"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs14" />
|
||||
<path
|
||||
fill="#bc0000"
|
||||
fill-opacity="1"
|
||||
d="M 0,0 H 640 V 480 H 0 Z"
|
||||
id="path2"
|
||||
style="fill:#0000cd;fill-opacity:1;stroke-width:0.999996" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -1,84 +1,556 @@
|
||||
{
|
||||
"RED": "AGGRESSORS",
|
||||
"INS": "INSURGENTS",
|
||||
"DZ" : "ALGERIA",
|
||||
"AR" : "ARGENTINA",
|
||||
"AU" : "AUSTRALIA",
|
||||
"AT" : "AUSTRIA",
|
||||
"BH" : "BAHRAIN",
|
||||
"BY" : "BELARUS",
|
||||
"BE" : "BELGIUM",
|
||||
"BO" : "BOLIVIA",
|
||||
"BR" : "BRAZIL",
|
||||
"BG" : "BULGARIA",
|
||||
"CA" : "CANADA",
|
||||
"CL" : "CHILE",
|
||||
"CN" : "CHINA",
|
||||
"HR" : "CROATIA",
|
||||
"CU" : "CUBA",
|
||||
"CY" : "CYPRUS",
|
||||
"CZ" : "CHEZH_REPUBLIC",
|
||||
"DK" : "DENMARK",
|
||||
"EG" : "EGYPT",
|
||||
"ET" : "ETHIOPIA",
|
||||
"FI" : "FINLAND",
|
||||
"FR" : "FRANCE",
|
||||
"GE" : "GEORGIA",
|
||||
"DE" : "GERMANY",
|
||||
"GH" : "GHANA",
|
||||
"GI" : "Gibraltar",
|
||||
"GR" : "GREECE",
|
||||
"HN" : "HONDURAS",
|
||||
"HU" : "HUNGARY",
|
||||
"IS" : "Iceland",
|
||||
"IN" : "INDIA",
|
||||
"ID" : "INDONESIA",
|
||||
"IR" : "IRAN",
|
||||
"IQ" : "IRAQ",
|
||||
"IE" : "Ireland",
|
||||
"IM" : "Isle Of Man",
|
||||
"IL" : "ISRAEL",
|
||||
"IT" : "ITALY",
|
||||
"JP" : "JAPAN",
|
||||
"JO" : "JORDAN",
|
||||
"KZ" : "KAZAKHSTAN",
|
||||
"KR" : "SOUTH_KOREA",
|
||||
"KW" : "KUWAIT",
|
||||
"LB" : "LEBANON",
|
||||
"LY" : "LIBYIA",
|
||||
"MY" : "MALAYSIA",
|
||||
"MX" : "MEXICO",
|
||||
"MA" : "MOROCCO",
|
||||
"NL" : "THE_NETHERLANDS",
|
||||
"NG" : "NIGERIA",
|
||||
"NO" : "NORWAY",
|
||||
"OM" : "OMAN",
|
||||
"PK" : "PAKISTAN",
|
||||
"PE" : "PERU",
|
||||
"PH" : "PHILIPPINES",
|
||||
"PL" : "POLAND",
|
||||
"PT" : "PORTUGAL",
|
||||
"QA" : "QATAR",
|
||||
"RO" : "ROMANIA",
|
||||
"RU" : "RUSSIA",
|
||||
"SA" : "SAUDI ARABIA",
|
||||
"RS" : "SERBIA",
|
||||
"SK" : "SLOVAKIA",
|
||||
"SI" : "SLOVENIA",
|
||||
"ZA" : "SOUTH AFRICA",
|
||||
"ES" : "SPAIN",
|
||||
"SD" : "SUDAN",
|
||||
"SE" : "SWEDEN",
|
||||
"CH" : "SWITZERLAND",
|
||||
"SY" : "SYRIA",
|
||||
"TH" : "THAILAND",
|
||||
"TN" : "TUNISIA",
|
||||
"TR" : "TURKEY",
|
||||
"UA" : "UKRAINE",
|
||||
"AE" : "UNITED ARAB EMIRATES",
|
||||
"GB" : "UK",
|
||||
"US" : "USA",
|
||||
"VE" : "VENEZUELA",
|
||||
"VN" : "VIETNAM",
|
||||
"YE" : "YEMEN"
|
||||
"AGGRESSORS": {
|
||||
"flagCode": "RED",
|
||||
"liveryCodes": [
|
||||
"RSO"
|
||||
]
|
||||
},
|
||||
"INSURGENTS": {
|
||||
"flagCode": "UNK",
|
||||
"liveryCodes": [
|
||||
"INS"
|
||||
]
|
||||
},
|
||||
"ALGERIA": {
|
||||
"flagCode": "DZ",
|
||||
"liveryCodes": [
|
||||
"DZA"
|
||||
]
|
||||
},
|
||||
"ARGENTINA": {
|
||||
"flagCode": "AR",
|
||||
"liveryCodes": [
|
||||
"ARG"
|
||||
]
|
||||
},
|
||||
"AUSTRALIA": {
|
||||
"flagCode": "AU",
|
||||
"liveryCodes": [
|
||||
"AUS",
|
||||
"AUSAF"
|
||||
]
|
||||
},
|
||||
"AUSTRIA": {
|
||||
"flagCode": "AT",
|
||||
"liveryCodes": [
|
||||
"AUT"
|
||||
]
|
||||
},
|
||||
"BAHRAIN": {
|
||||
"flagCode": "BH",
|
||||
"liveryCodes": [
|
||||
"BHR"
|
||||
]
|
||||
},
|
||||
"BELARUS": {
|
||||
"flagCode": "BY",
|
||||
"liveryCodes": [
|
||||
"BLR"
|
||||
]
|
||||
},
|
||||
"BELGIUM": {
|
||||
"flagCode": "BE",
|
||||
"liveryCodes": [
|
||||
"BEL"
|
||||
]
|
||||
},
|
||||
"BOLIVIA": {
|
||||
"flagCode": "BO",
|
||||
"liveryCodes": [
|
||||
"BOL"
|
||||
]
|
||||
},
|
||||
"BRAZIL": {
|
||||
"flagCode": "BR",
|
||||
"liveryCodes": [
|
||||
"BRA"
|
||||
]
|
||||
},
|
||||
"BULGARIA": {
|
||||
"flagCode": "BG",
|
||||
"liveryCodes": [
|
||||
"BGR"
|
||||
]
|
||||
},
|
||||
"CANADA": {
|
||||
"flagCode": "CA",
|
||||
"liveryCodes": [
|
||||
"CAN"
|
||||
]
|
||||
},
|
||||
"CHILE": {
|
||||
"flagCode": "CL",
|
||||
"liveryCodes": [
|
||||
"CHL"
|
||||
]
|
||||
},
|
||||
"CHINA": {
|
||||
"flagCode": "CN",
|
||||
"liveryCodes": [
|
||||
"CHN"
|
||||
]
|
||||
},
|
||||
"CROATIA": {
|
||||
"flagCode": "HR",
|
||||
"liveryCodes": [
|
||||
"HRV"
|
||||
]
|
||||
},
|
||||
"CUBA": {
|
||||
"flagCode": "CU",
|
||||
"liveryCodes": [
|
||||
"CUB"
|
||||
]
|
||||
},
|
||||
"CYPRUS": {
|
||||
"flagCode": "CY",
|
||||
"liveryCodes": [
|
||||
"CYP"
|
||||
]
|
||||
},
|
||||
"CHEZH_REPUBLIC": {
|
||||
"displayName": "Czech Republic",
|
||||
"flagCode": "CZ",
|
||||
"liveryCodes": [
|
||||
"CZE"
|
||||
]
|
||||
},
|
||||
"DENMARK": {
|
||||
"flagCode": "DK",
|
||||
"liveryCodes": [
|
||||
"DEN"
|
||||
]
|
||||
},
|
||||
"EGYPT": {
|
||||
"flagCode": "EG",
|
||||
"liveryCodes": [
|
||||
"EGY",
|
||||
"EGP"
|
||||
]
|
||||
},
|
||||
"ETHIOPIA": {
|
||||
"flagCode": "ET",
|
||||
"liveryCodes": [
|
||||
"ETH"
|
||||
]
|
||||
},
|
||||
"FINLAND": {
|
||||
"flagCode": "FI",
|
||||
"liveryCodes": [
|
||||
"FIN"
|
||||
]
|
||||
},
|
||||
"FRANCE": {
|
||||
"flagCode": "FR",
|
||||
"liveryCodes": [
|
||||
"FRA"
|
||||
]
|
||||
},
|
||||
"GEORGIA": {
|
||||
"flagCode": "GE",
|
||||
"liveryCodes": [
|
||||
"GRG"
|
||||
]
|
||||
},
|
||||
"GERMANY": {
|
||||
"flagCode": "DE",
|
||||
"liveryCodes": [
|
||||
"GER"
|
||||
]
|
||||
},
|
||||
"GHANA": {
|
||||
"flagCode": "GH",
|
||||
"liveryCodes": [
|
||||
"GHA"
|
||||
]
|
||||
},
|
||||
"GREECE": {
|
||||
"flagCode": "GR",
|
||||
"liveryCodes": [
|
||||
"GRC"
|
||||
]
|
||||
},
|
||||
"HONDURAS": {
|
||||
"flagCode": "HN",
|
||||
"liveryCodes": [
|
||||
"HND"
|
||||
]
|
||||
},
|
||||
"HUNGARY": {
|
||||
"flagCode": "HU",
|
||||
"liveryCodes": [
|
||||
"HUN"
|
||||
]
|
||||
},
|
||||
"INDIA": {
|
||||
"flagCode": "IN",
|
||||
"liveryCodes": [
|
||||
"IND"
|
||||
]
|
||||
},
|
||||
"INDONESIA": {
|
||||
"flagCode": "ID",
|
||||
"liveryCodes": [
|
||||
"IDN"
|
||||
]
|
||||
},
|
||||
"IRAN": {
|
||||
"flagCode": "IR",
|
||||
"liveryCodes": [
|
||||
"IRN"
|
||||
]
|
||||
},
|
||||
"IRAQ": {
|
||||
"flagCode": "IQ",
|
||||
"liveryCodes": [
|
||||
"IRQ"
|
||||
]
|
||||
},
|
||||
"ISRAEL": {
|
||||
"flagCode": "IL",
|
||||
"liveryCodes": [
|
||||
"ISR"
|
||||
]
|
||||
},
|
||||
"ITALY": {
|
||||
"flagCode": "IT",
|
||||
"liveryCodes": [
|
||||
"ITA"
|
||||
]
|
||||
},
|
||||
"JAPAN": {
|
||||
"flagCode": "JP",
|
||||
"liveryCodes": [
|
||||
"JPN"
|
||||
]
|
||||
},
|
||||
"JORDAN": {
|
||||
"flagCode": "JO",
|
||||
"liveryCodes": [
|
||||
"JOR"
|
||||
]
|
||||
},
|
||||
"KAZAKHSTAN": {
|
||||
"flagCode": "KZ",
|
||||
"liveryCodes": [
|
||||
"KAZ"
|
||||
]
|
||||
},
|
||||
"SOUTH_KOREA": {
|
||||
"displayName": "South Korea",
|
||||
"flagCode": "KR",
|
||||
"liveryCodes": [
|
||||
"KOR"
|
||||
]
|
||||
},
|
||||
"KUWAIT": {
|
||||
"flagCode": "KW",
|
||||
"liveryCodes": [
|
||||
"KWT"
|
||||
]
|
||||
},
|
||||
"LEBANON": {
|
||||
"flagCode": "LB",
|
||||
"liveryCodes": [
|
||||
"LBN"
|
||||
]
|
||||
},
|
||||
"MALAYSIA": {
|
||||
"flagCode": "MY",
|
||||
"liveryCodes": [
|
||||
"MYS"
|
||||
]
|
||||
},
|
||||
"MEXICO": {
|
||||
"flagCode": "MX",
|
||||
"liveryCodes": [
|
||||
"MEX"
|
||||
]
|
||||
},
|
||||
"MOROCCO": {
|
||||
"flagCode": "MA",
|
||||
"liveryCodes": [
|
||||
"MAR"
|
||||
]
|
||||
},
|
||||
"THE_NETHERLANDS": {
|
||||
"displayName": "The Netherlands",
|
||||
"flagCode": "NL",
|
||||
"liveryCodes": [
|
||||
"NETH"
|
||||
]
|
||||
},
|
||||
"NIGERIA": {
|
||||
"flagCode": "NG",
|
||||
"liveryCodes": [
|
||||
"NGA"
|
||||
]
|
||||
},
|
||||
"NORWAY": {
|
||||
"flagCode": "NO",
|
||||
"liveryCodes": [
|
||||
"NOR"
|
||||
]
|
||||
},
|
||||
"OMAN": {
|
||||
"flagCode": "OM",
|
||||
"liveryCodes": [
|
||||
"OMN"
|
||||
]
|
||||
},
|
||||
"PAKISTAN": {
|
||||
"flagCode": "PK",
|
||||
"liveryCodes": [
|
||||
"PAK"
|
||||
]
|
||||
},
|
||||
"PERU": {
|
||||
"flagCode": "PE",
|
||||
"liveryCodes": [
|
||||
"PER"
|
||||
]
|
||||
},
|
||||
"PHILIPPINES": {
|
||||
"flagCode": "PH",
|
||||
"liveryCodes": [
|
||||
"PHL"
|
||||
]
|
||||
},
|
||||
"POLAND": {
|
||||
"flagCode": "PL",
|
||||
"liveryCodes": [
|
||||
"POL"
|
||||
]
|
||||
},
|
||||
"PORTUGAL": {
|
||||
"flagCode": "PT",
|
||||
"liveryCodes": [
|
||||
"PRT"
|
||||
]
|
||||
},
|
||||
"QATAR": {
|
||||
"flagCode": "QA",
|
||||
"liveryCodes": [
|
||||
"QAT"
|
||||
]
|
||||
},
|
||||
"ROMANIA": {
|
||||
"flagCode": "RO",
|
||||
"liveryCodes": [
|
||||
"ROU"
|
||||
]
|
||||
},
|
||||
"RUSSIA": {
|
||||
"flagCode": "RU",
|
||||
"liveryCodes": [
|
||||
"RUS"
|
||||
]
|
||||
},
|
||||
"SAUDI_ARABIA": {
|
||||
"displayName": "Saudi Arabia",
|
||||
"flagCode": "SA",
|
||||
"liveryCodes": [
|
||||
"SAU"
|
||||
]
|
||||
},
|
||||
"SERBIA": {
|
||||
"flagCode": "RS",
|
||||
"liveryCodes": [
|
||||
"SRB"
|
||||
]
|
||||
},
|
||||
"SLOVAKIA": {
|
||||
"flagCode": "SK",
|
||||
"liveryCodes": [
|
||||
"SVK"
|
||||
]
|
||||
},
|
||||
"SLOVENIA": {
|
||||
"flagCode": "SI",
|
||||
"liveryCodes": [
|
||||
"SVN"
|
||||
]
|
||||
},
|
||||
"SOUTH_AFRICA": {
|
||||
"displayName": "South Africa",
|
||||
"flagCode": "ZA",
|
||||
"liveryCodes": []
|
||||
},
|
||||
"SPAIN": {
|
||||
"flagCode": "ES",
|
||||
"liveryCodes": [
|
||||
"SPN",
|
||||
"SPA"
|
||||
]
|
||||
},
|
||||
"SUDAN": {
|
||||
"flagCode": "SD",
|
||||
"liveryCodes": [
|
||||
"SDN",
|
||||
"SUN"
|
||||
]
|
||||
},
|
||||
"SWEDEN": {
|
||||
"flagCode": "SE",
|
||||
"liveryCodes": [
|
||||
"SWE"
|
||||
]
|
||||
},
|
||||
"SWITZERLAND": {
|
||||
"flagCode": "CH",
|
||||
"liveryCodes": [
|
||||
"SUI"
|
||||
]
|
||||
},
|
||||
"SYRIA": {
|
||||
"flagCode": "SY",
|
||||
"liveryCodes": [
|
||||
"SYR"
|
||||
]
|
||||
},
|
||||
"THAILAND": {
|
||||
"flagCode": "TH",
|
||||
"liveryCodes": [
|
||||
"THA"
|
||||
]
|
||||
},
|
||||
"TUNISIA": {
|
||||
"flagCode": "TN",
|
||||
"liveryCodes": [
|
||||
"TUN"
|
||||
]
|
||||
},
|
||||
"TURKEY": {
|
||||
"flagCode": "TR",
|
||||
"liveryCodes": [
|
||||
"TUR"
|
||||
]
|
||||
},
|
||||
"UKRAINE": {
|
||||
"flagCode": "UA",
|
||||
"liveryCodes": [
|
||||
"UKR"
|
||||
]
|
||||
},
|
||||
"UNITED_ARAB_EMIRATES": {
|
||||
"displayName": "United Arab Emirates",
|
||||
"flagCode": "AE",
|
||||
"liveryCodes": [
|
||||
"ARE"
|
||||
]
|
||||
},
|
||||
"UK": {
|
||||
"displayName": "United Kingdom",
|
||||
"flagCode": "GB",
|
||||
"liveryCodes": [
|
||||
"UK"
|
||||
]
|
||||
},
|
||||
"USA": {
|
||||
"displayName": "United States of America",
|
||||
"flagCode": "US",
|
||||
"liveryCodes": [
|
||||
"USA",
|
||||
"USAF"
|
||||
]
|
||||
},
|
||||
"VENEZUELA": {
|
||||
"flagCode": "VE",
|
||||
"liveryCodes": [
|
||||
"VEN"
|
||||
]
|
||||
},
|
||||
"VIETNAM": {
|
||||
"flagCode": "VN",
|
||||
"liveryCodes": [
|
||||
"VNM"
|
||||
]
|
||||
},
|
||||
"YEMEN": {
|
||||
"flagCode": "YE",
|
||||
"liveryCodes": [
|
||||
"YEM"
|
||||
]
|
||||
},
|
||||
"CJTF_BLUE": {
|
||||
"displayName": "Combined Joint Task Force Blue",
|
||||
"flagCode": "BLUE",
|
||||
"liveryCodes": [
|
||||
"BLUE"
|
||||
]
|
||||
},
|
||||
"SOUTH_OSETIA": {
|
||||
"displayName": "South Ossetia",
|
||||
"flagCode": "UNK",
|
||||
"liveryCodes": []
|
||||
},
|
||||
"NORTH_KOREA": {
|
||||
"displayName": "Democratic People's Republic of Korea",
|
||||
"flagCode": "KP",
|
||||
"liveryCodes": [
|
||||
"PRK"
|
||||
]
|
||||
},
|
||||
"CJTF_RED": {
|
||||
"displayName": "Combined Joint Task Force Red",
|
||||
"flagCode": "RED",
|
||||
"liveryCodes": [
|
||||
"RED"
|
||||
]
|
||||
},
|
||||
"ABKHAZIA": {
|
||||
"flagCode": "UNK",
|
||||
"liveryCodes": [
|
||||
"ABH"
|
||||
]
|
||||
},
|
||||
"ITALIAN_SOCIAL_REPUBLIC": {
|
||||
"displayName": "Italian Social Republic",
|
||||
"flagCode": "SOCIAL",
|
||||
"liveryCodes": [
|
||||
"RSI"
|
||||
]
|
||||
},
|
||||
"USSR": {
|
||||
"displayName": "USSR",
|
||||
"flagCode": "USSR",
|
||||
"liveryCodes": []
|
||||
},
|
||||
"ECUADOR": {
|
||||
"flagCode": "EC",
|
||||
"liveryCodes": [
|
||||
"ECU"
|
||||
]
|
||||
},
|
||||
"LIBYA": {
|
||||
"flagCode": "LY",
|
||||
"liveryCodes": [
|
||||
"LBY",
|
||||
"LIB"
|
||||
]
|
||||
},
|
||||
"UN_PEACEKEEPERS": {
|
||||
"displayName": "United Nations",
|
||||
"flagCode": "UNK",
|
||||
"liveryCodes": [
|
||||
"UN"
|
||||
]
|
||||
},
|
||||
"GDR": {
|
||||
"flagCode": "UNK",
|
||||
"liveryCodes": [
|
||||
"GDR"
|
||||
]
|
||||
},
|
||||
"YUGOSLAVIA": {
|
||||
"flagCode": "YUG",
|
||||
"liveryCodes": [
|
||||
"YUG"
|
||||
]
|
||||
},
|
||||
"THIRDREICH": {
|
||||
"displayName": "Third Reich",
|
||||
"flagCode": "THIRD",
|
||||
"liveryCodes": []
|
||||
}
|
||||
}
|
||||
@@ -216,7 +216,7 @@
|
||||
<g stroke="#d4af37" stroke-width=".9">
|
||||
<path fill="#fff" d="M367.7 432.4c-1.5.5-2.5 1-9 .5-4.6-.3-10.3-1.1-13.2-1.2-5.6 0-5.6.3-15.5 7.1-7 4.8-16 4.4-22.7 3-4-2-5.8-2.3-5.2-1.3 1.1 1.8 9 4.4 13.4 4.4 7 0 12.2-1.8 20.7-7.1 6.6-4.2 9.5-4.5 18.5-2.5 10.6 2.1 12.2 1.2 20.9-2.7-2.6 0-3.2 0-4 .4 1.4-1.6 1.5-3.2 1.8-4.2.4-.6-.8.2-2.1 1.2l-3.6 2.4z" transform="matrix(.59548 0 0 .58466 141.4 62.9)"/>
|
||||
<path fill="#d91023" d="M354 429.8c-4.9-.7-8.2-1.4-11.3-1-3.4.3-5.8 2-9.9 4.5-4 2.7-7.7 4.6-8.1 4.6-.6 0-5.8 1.2-9.8 1.2-1.8 0-5.9-1.3-8.6-2.3-5.7-2.1-7.8 1.4-1.8 3.9a33.4 33.4 0 0 0 15.6 1.8c5-.8 9.6-3 13.4-5.8 7.7-5.6 3.2-3 6.2-4.2 3-1.3 5.9-.9 5.9-.9 4 .2 11.9 1.5 15.9 1.6 7.2-.7 6-.7 8.4-2 .8-.6 3.5-2.3 3.6-2.7.2-.4 1.4-2.7 1.2-2.8-7.5 5.1-11.2 5.1-20.7 4.1z" transform="matrix(.59548 0 0 .58466 141.4 62.9)"/>
|
||||
<path fill="#d91023" d="m341 435.4-7.1 4c-6.2 3.8-12 5.6-18.8 5.6-3 .5-13-3.6-11.5-3.1 1.6 3 4 3.4 9.6 4.5 4 .8 6.6.1 11.1-.8 5-.6 7-3 9-4.3a33.5 33.5 0 0 1 14-5.4c1 0 4.7 2.1 8.9 3 4.1.9 6.1 1.1 10.4.4s8.7-4.2 12.8-6.9c-.6.2-2 .2-4 .3-6 3.5-16 4.8-21.2 2.4-5.4-1.3-10.5-1-13.2.3z" transform="matrix(.59548 0 0 .58466 141.4 62.9)"/>
|
||||
<path fill="#d91023" d="m341 435.4-7.1 4c-6.2 3.8-12 5.6-18.8 5.6-3 .5-13-3.6-11.5-3.1 1.6 3 4 3.4 9.6 4.5 4 .8 6.6.1 11.1-.8 5-.6 7-3 9-4.3a33.5 33.5 0 0 1 14-5.4c1 0 4.7 2.1 8.9 3 4.1.9 6.1 1.1 10.4.5s8.7-4.2 12.8-6.9c-.6.2-2 .2-4 .3-6 3.5-16 4.8-21.2 2.4-5.4-1.3-10.5-1-13.2.3z" transform="matrix(.59548 0 0 .58466 141.4 62.9)"/>
|
||||
</g>
|
||||
<path fill="#00a854" d="M276.4 176.3a37.7 37.7 0 0 0-17.5 13.2c-1 1.2-1.8 2.2-1.9 2.1 0 0 .4-2.7 1.2-5.7.7-3 1.2-6.1 1.2-6.8 0-1.2-.1-1.1-2 1a41 41 0 0 0-7 13c-.7 2.2-1 4.7-1 9.3l.1 6.3-1.2-4.5c-1.3-4.7-2.6-8-4-9.6-.6-1-.8-1-1.3-.3-1 1.5-.3 7.8 1.4 12.4.1.6-.5-.2-1.4-1.7-1-1.6-2-2.7-2.3-2.6-.6.3-.6 5.3 0 7.4.4 1.4.4 1.4-1.3-.5-2.9-3.3-3.5-3.6-3.5-1.3 0 .9.3 2.4.7 3.4l.8 2-1.6.4c-2.3.4-3 1.1-3 3 0 2 1.4 6.8 2 7.2.9.5 1.1 0 1.8-3l.6-2.6 1.9 2.5a76.7 76.7 0 0 1 8.8 17.7c2.2 6.4 2.3 7.7.3 3.7a72 72 0 0 0-3.2-5.2c-2-2.8-12.6-13.9-13.3-13.9-.8 0 0 3.5 1.3 6.4.7 1.4 2.7 4.2 4.4 6.2l2.9 3.6c-.2 0-1.4-.8-2.7-1.7-2.6-2-5.3-3.5-5.6-3.2-.1.1.9 2.3 2.3 4.8l2.4 4.6-1.6-.3a6 6 0 0 0-2.8.3c-1 .6-1 1.1-1 3.5 0 3.3.8 6.7 1.5 6.7.8 0 2-3.2 2-4.9 0-1.3 0-1.3 2.6 1.1 3.7 3.6 8.6 9.6 13.4 16.6l4 6-4.7-3.6c-4.6-3.3-14-8.6-15.5-8.6-.4 0-.8.2-.8.5 0 .2 3 3.3 6.7 6.8l6.7 6.3-3.5-1.1a71 71 0 0 0-5.3-1.5c-1.7-.4-1.7-.3 2.2 3.3a68 68 0 0 0 8.7 6.7c2.6 1.6 4.3 2.8 3.7 2.7a35.1 35.1 0 0 0-11.5-1.9c0 .7 3.5 3.5 6 4.8 1.4.7 5 2 7.8 3 6 2.1 9.6 4 12.6 6.6l2 1.9-3.5-1.6c-4.1-1.8-15.3-5.5-16.5-5.5s3.8 5 8.8 8.7a80.2 80.2 0 0 0 19.5 9.7c5.2 1.7 9.6 2.6 18 3.6 3.6.5 7 1 7.8 1.3.6.3 5 1 10 1.5a99.7 99.7 0 0 1 30.7 7.2c1.7.8 3.5 1.1 7.2 1.3 5.5.3 5.8.1 4.2-2.9-1.3-2.5-3.7-3.9-8.7-4.8l-9.2-1.8a726.1 726.1 0 0 0-29-5c-14-2.3-20.1-4.7-26.7-10.7A43.3 43.3 0 0 1 269 263c.4-3 .6-5.5.5-5.6-.5-.4-3.7 6.3-4.6 9.4l-1 3.3.3-5.4c.3-8.3 3.5-16.1 8.2-20.6 1-1 1.8-2 1.8-2.2 0-.2-1 0-2.1.2-1.6.4-3 1.3-5 3.2l-2.6 2.7 1.3-2.4a27 27 0 0 1 8.3-9.6l2.9-1.9-1.7-.1c-3.4-.4-9.5 4-12.9 9.3-2 3-2 2.1 0-2.6a41.8 41.8 0 0 1 8-12.5c1.9-1.8 2.2-2.5 1.5-2.5-2.7 0-7.2 3.5-11 8.4-1 1.5-1 1.4.2-1.5a40 40 0 0 1 6.1-9.7c1.6-1.9 1.2-2.1-1.5-1a26.4 26.4 0 0 0-8.6 9.7c-1 2-1.8 3-1.7 2.5a61 61 0 0 0 .8-3.3 46.4 46.4 0 0 1 12.8-22.7c2.4-2.2 3-3.2 2.3-3.2-2 0-6.5 2.6-9.5 5.5-3.1 3-3.2 3-2 .9a57 57 0 0 1 18.3-18.6l4.3-3c0-.8-7.8 1.6-11.3 3.3a42.3 42.3 0 0 0-4.4 3.2c-2.5 2-2.7 2.1-1.8.8 1.7-2.4 7.6-7.7 10-8.9 1.7-.9 2-1.2 1-1.3-2.3-.4-8.9 3-14 7.4l-2.2 1.9 1.2-2a48 48 0 0 1 14.7-15.6 60.5 60.5 0 0 1 4.4-2.6c.3-.1.5-.4.4-.5-.2-.2-2 .2-4 1zm77 145a6 6 0 0 1 1.4 1.9c0 .2-1.1.4-2.5.4-2.5 0-2.6 0-2.6-1.4 0-2.6 1.6-2.9 3.8-.8z"/>
|
||||
<path fill="#9eab05" d="M350.3 320.5c-1.3 1.2-1 3 .3 3.4 2.2.6 5 .4 5-.4s-3.3-3.8-4.1-3.8c-.3 0-.8.3-1.2.8z"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
57
client/public/images/countries/red.svg
Normal file
@@ -0,0 +1,57 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
width="640"
|
||||
height="480"
|
||||
id="svg10"
|
||||
sodipodi:docname="red.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.88166667"
|
||||
inkscape:cx="497.3535"
|
||||
inkscape:cy="301.13421"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs14" />
|
||||
<path
|
||||
fill="#bc0000"
|
||||
fill-opacity="1"
|
||||
d="M 0,0 H 640 V 480 H 0 Z"
|
||||
id="path2"
|
||||
style="fill:#cc0000;fill-opacity:1;stroke-width:0.999996" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
289
client/public/images/countries/social.svg
Normal file
|
After Width: | Height: | Size: 50 KiB |
56
client/public/images/countries/third.svg
Normal file
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.0"
|
||||
width="640"
|
||||
height="480"
|
||||
viewBox="0 0 640 480"
|
||||
id="svg8"
|
||||
sodipodi:docname="third.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs12" />
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
width="640px"
|
||||
inkscape:zoom="1.058"
|
||||
inkscape:cx="284.97164"
|
||||
inkscape:cy="274.57467"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<rect
|
||||
width="640"
|
||||
height="480"
|
||||
fill="#dd0000"
|
||||
id="rect2"
|
||||
x="0"
|
||||
y="0"
|
||||
style="stroke-width:0.999997" />
|
||||
<circle
|
||||
cx="320"
|
||||
cy="240"
|
||||
r="180"
|
||||
fill="#ffffff"
|
||||
id="circle4"
|
||||
style="stroke-width:1" />
|
||||
<path
|
||||
d="M 472.73507,256.97056 387.88225,172.11774 252.11775,307.88225 167.26494,223.02943 M 336.97057,87.264932 252.11775,172.11774 387.88225,307.88225 303.02944,392.73506"
|
||||
fill="none"
|
||||
stroke="#000000"
|
||||
stroke-width="48"
|
||||
id="path6" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
68
client/public/images/countries/unk.svg
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
width="640"
|
||||
height="480"
|
||||
id="svg10"
|
||||
sodipodi:docname="unk.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.88166667"
|
||||
inkscape:cx="497.3535"
|
||||
inkscape:cy="300"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0" />
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs14" />
|
||||
<path
|
||||
fill="#bc0000"
|
||||
fill-opacity="1"
|
||||
d="M 0,0 H 640 V 480 H 0 Z"
|
||||
id="path2"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.999996" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-weight:normal;font-size:549.6px;line-height:1.25;font-family:sans-serif;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1"
|
||||
x="179.23065"
|
||||
y="424.19662"
|
||||
id="text2551"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan2549"
|
||||
x="179.23065"
|
||||
y="424.19662"
|
||||
style="stroke-width:1">?</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
74
client/public/images/countries/ussr.svg
Normal file
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
version="1.1"
|
||||
width="640"
|
||||
height="480"
|
||||
id="svg10"
|
||||
sodipodi:docname="ussr.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.88166667"
|
||||
inkscape:cx="497.3535"
|
||||
inkscape:cy="300"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10" />
|
||||
<metadata
|
||||
id="metadata16">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs14" />
|
||||
<path
|
||||
fill="#bc0000"
|
||||
fill-opacity="1"
|
||||
d="M 0,0 H 639.99998 V 480 H 0 Z"
|
||||
id="path2"
|
||||
style="fill:#cc0000;fill-opacity:1;stroke-width:0.999997" />
|
||||
<path
|
||||
id="path11728"
|
||||
d="m 160.0004,30 -6.73546,20.729509 H 131.4688 L 149.10222,63.540898 142.36675,84.2704 160.0004,71.458772 177.63406,84.2704 170.89859,63.540898 188.532,50.729509 h -21.79613 z m 0,10.79999 4.31062,13.266778 h 13.94975 l -11.2856,8.199597 4.31061,13.266777 -11.28538,-8.199363 -11.28538,8.199363 4.31062,-13.266777 -11.28561,-8.199597 h 13.94975 z"
|
||||
style="fill:#ffd700;fill-opacity:1;stroke:none;stroke-width:0.15px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<g
|
||||
style="fill:#ffd700;fill-opacity:1;stroke-width:1.25"
|
||||
id="g2900"
|
||||
transform="matrix(0.79145503,0,0,0.78939049,3.0638126,3.0127518)">
|
||||
<path
|
||||
id="rect4165-6"
|
||||
d="m 137.43744,171.69421 18.86296,18.9937 17.78834,-17.66589 c 27.05847,29.021 55.43807,56.99501 82.28704,86.12782 4.03444,4.06233 10.59815,4.085 14.66056,0.0506 4.06232,-4.03445 4.08499,-10.59815 0.0506,-14.66056 -28.81871,-27.1901 -57.72545,-54.60143 -86.55328,-81.89095 l 23.96499,-23.80003 -33.34026,-4.61605 z"
|
||||
style="fill:#ffd700;fill-opacity:1;stroke:none;stroke-width:0.611489;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
<path
|
||||
id="path4179-3"
|
||||
d="m 198.2887,110.1955 c 15.51743,8.7394 27.29872,21.28122 34.2484,34.3924 7.04394,13.28902 10.13959,27.16218 10.20325,38.25433 0.13054,22.74374 -18.43771,41.18184 -41.18183,41.18184 -12.13597,0 -23.04607,-5.24868 -30.58302,-13.60085 l -4.16863,3.51033 c -0.70999,-0.27231 -1.46387,-0.41221 -2.22429,-0.41276 -1.82948,1.9e-4 -3.56621,0.80531 -4.74859,2.20136 -2.97368,0.38896 -5.46251,2.44529 -6.40534,5.29224 -3.13486,6.28843 -8.63524,11.21997 -15.29104,13.4776 -0.0637,0.0216 -0.11992,0.05 -0.1758,0.0783 -3.07749,1.12758 -6.16259,3.1643 -8.78919,5.80245 -5.19155,5.23656 -7.72858,11.93658 -6.30024,16.63822 -0.14098,0.40857 -0.21361,0.83759 -0.21498,1.26979 1.5e-4,2.17082 1.75991,3.93058 3.93073,3.93073 0.54341,-0.002 1.08053,-0.11639 1.57745,-0.33632 4.69369,1.05881 11.06885,-1.54582 16.05444,-6.55917 2.82624,-2.85072 4.94356,-6.22349 5.98303,-9.53062 2.31696,-6.62278 7.29699,-12.01856 13.62281,-15.05312 0.15105,-0.0725 0.27303,-0.14714 0.38218,-0.22358 2.12082,-1.01408 3.67251,-2.92895 4.225,-5.2139 9.70222,11.44481 24.25255,18.75299 40.51876,19.13577 29.83352,0.70205 52.13299,-21.25802 53.16414,-52.83642 0.51894,-15.89259 -5.62993,-36.3847 -19.6412,-53.19089 -10.70835,-12.84441 -26.40987,-23.50795 -44.18699,-28.20777 z"
|
||||
style="fill:#ffd700;fill-opacity:1;stroke:none;stroke-width:0.625044;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
53
client/public/images/countries/yug.svg
Normal file
@@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 640 480"
|
||||
version="1.1"
|
||||
id="svg8"
|
||||
sodipodi:docname="yug.svg"
|
||||
width="640"
|
||||
height="480"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs12" />
|
||||
<sodipodi:namedview
|
||||
id="namedview10"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:zoom="0.91840278"
|
||||
inkscape:cx="253.1569"
|
||||
inkscape:cy="286.91115"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<path
|
||||
fill="#dd0000"
|
||||
d="M 0,320 H 640 V 480 H 0"
|
||||
id="path2"
|
||||
style="stroke-width:0.999996" />
|
||||
<path
|
||||
fill="#ffffff"
|
||||
d="M 0,160 H 640 V 320 H 0"
|
||||
id="path4"
|
||||
style="stroke-width:0.999996" />
|
||||
<path
|
||||
fill="#003893"
|
||||
d="M 0,0 H 640 V 160 H 0"
|
||||
id="path6"
|
||||
style="stroke-width:0.999996" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
BIN
client/public/images/favicons/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
client/public/images/favicons/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
client/public/images/favicons/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
client/public/images/favicons/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 779 B |
BIN
client/public/images/favicons/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
client/public/images/favicons/favicon.ico
Normal file
|
After Width: | Height: | Size: 15 KiB |
16
client/public/images/favicons/site.webmanifest
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "DCS Olympus",
|
||||
"short_name": "DCS Olympus",
|
||||
"icons": [{
|
||||
"src": "/images/favicons/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}, {
|
||||
"src": "/images/favicons/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
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 |
@@ -1,194 +0,0 @@
|
||||
/**************************************/
|
||||
|
||||
.olympus-dialog {
|
||||
align-self: center;
|
||||
background:white;
|
||||
border-radius: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-self: center;
|
||||
padding:10px;
|
||||
position:absolute;
|
||||
width:fit-content;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.olympus-dialog-close {
|
||||
cursor:pointer;
|
||||
position:absolute;
|
||||
right:10px;
|
||||
top:5px;
|
||||
}
|
||||
|
||||
.olympus-dialog-header {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
/**************************************/
|
||||
|
||||
|
||||
/***** AIC *****/
|
||||
|
||||
.aic-panel {
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#aic-control-panel {
|
||||
bottom:30px;
|
||||
position: absolute;
|
||||
left:30px;
|
||||
}
|
||||
|
||||
|
||||
#aic-control-panel .olympus-button img {
|
||||
max-width: 32px;
|
||||
}
|
||||
|
||||
|
||||
#aic-toolbox, #aic-callsign-panel {
|
||||
align-items: flex-start;
|
||||
align-self: center;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
display:none;
|
||||
position:absolute;
|
||||
}
|
||||
|
||||
.aic-panel {
|
||||
background:#eaeaea;
|
||||
border-bottom-right-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
justify-self: left;
|
||||
padding:5px 10px;
|
||||
}
|
||||
|
||||
.aic-enabled #aic-toolbox, .aic-enabled #aic-callsign-panel {
|
||||
display:flex;
|
||||
}
|
||||
|
||||
.aic-enabled #aic-callsign-panel {
|
||||
align-self: auto;
|
||||
top: 100px;
|
||||
}
|
||||
|
||||
.aic-panel h2 {
|
||||
font-size:90%;
|
||||
margin:0;
|
||||
padding:0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#aic-callsign-display {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#aic-formation-list {
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#aic-formation-list > div {
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-top:10px;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
#aic-formation-list .aic-formation-image img {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 10px;
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
#aic-formation-list .aic-formation-name {
|
||||
font-size:90%;
|
||||
}
|
||||
|
||||
#aic-formation-list .aic-formation-descriptor {
|
||||
background:white;
|
||||
border-radius: 10px;
|
||||
left:100px;
|
||||
padding:5px;
|
||||
position:absolute;
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
#aic-teleprompt {
|
||||
background-color: white;
|
||||
border:2px solid black;
|
||||
border-radius: 10px;
|
||||
bottom: 50px;
|
||||
color: black;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
justify-self: center;
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.aic-enabled #aic-teleprompt {
|
||||
display:flex;
|
||||
}
|
||||
|
||||
#aic-descriptor {
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-section {
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
margin:0 10px;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-section-label {
|
||||
background-color:#eaeaea;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
padding:.25em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-phrase {
|
||||
border-bottom: 1px solid #ccc;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
margin-bottom:5px;
|
||||
padding-bottom:2px;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-phrase:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-components .aic-descriptor-component {
|
||||
margin:0 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-component-label {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-component-value:after {
|
||||
content:",";
|
||||
margin-right:5px;
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-component:last-of-type .aic-descriptor-component-value:after {
|
||||
content:"; ";
|
||||
}
|
||||
|
||||
#aic-descriptor .aic-descriptor-section:last-of-type .aic-descriptor-component:last-of-type .aic-descriptor-component-value:after {
|
||||
content:".";
|
||||
}
|
||||
|
||||
|
||||
/**************************************/
|
||||
@@ -1,205 +0,0 @@
|
||||
.ol-strip-board .ol-dialog-header {
|
||||
align-items: center;
|
||||
display:flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.ol-strip-board-strips {
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip {
|
||||
align-items: center;
|
||||
border-radius: var( --border-radius-sm );
|
||||
column-gap: 4px;
|
||||
display:flex;
|
||||
flex-flow: row nowrap;
|
||||
row-gap:4px;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip[data-flight-status="checkedin"] {
|
||||
background-color: #ffffff2A;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip[data-flight-status="readytotaxi"] {
|
||||
background-color: #ffff0063;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip[data-flight-status="clearedtotaxi"] {
|
||||
background-color: #00ff0030;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip[data-flight-status="halted"] {
|
||||
background-color: #FF000040;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip[data-flight-status="terminated"] {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.ol-strip-board-headers {
|
||||
column-gap: 4px;
|
||||
display:flex;
|
||||
flex-flow:row nowrap;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ol-strip-board-headers > *, .ol-strip-board-strip > [data-point] {
|
||||
padding: 4px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width:80px;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip input[type="text"] {
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
border:1px solid #ffffff30;
|
||||
border-radius: var( --border-radius-sm );
|
||||
color:white;
|
||||
font-size:12px;
|
||||
font-weight:normal;
|
||||
outline:none;
|
||||
padding: 4px 0;
|
||||
text-align: center;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip[data-time-warning="level-1"] [data-point="timeToGo"] {
|
||||
border:1px solid #cc0000;
|
||||
}
|
||||
|
||||
.ol-strip-board-headers :nth-child(1) {
|
||||
width:12px;
|
||||
}
|
||||
|
||||
.ol-strip-board-headers :nth-child(2),
|
||||
.ol-strip-board-strip :nth-child(2),
|
||||
[data-board-type="ground"] .ol-strip-board-headers :nth-child(3),
|
||||
[data-board-type="ground"] .ol-strip-board-strip :nth-child(3) {
|
||||
width:130px;
|
||||
}
|
||||
|
||||
[data-board-type="ground"] .ol-strip-board-strip :nth-child(5) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ol-strip-board-headers :last-child,
|
||||
.ol-strip-board-strip :last-child {
|
||||
width:20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[data-board-type="tower"] .ol-strip-board-strip > * {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
[data-board-type="tower"] .ol-strip-board-strip a {
|
||||
color:white;
|
||||
}
|
||||
|
||||
[data-board-type="tower"] .ol-strip-board-strip > :nth-child(2) {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
[data-board-type="tower"] .ol-strip-board-strip :nth-child(3) input,
|
||||
[data-board-type="tower"] .ol-strip-board-strip :nth-child(5) input {
|
||||
width:30px;
|
||||
}
|
||||
|
||||
[data-board-type="tower"] .ol-strip-board-strip :nth-child(3) {
|
||||
font-size:10px;
|
||||
}
|
||||
|
||||
|
||||
[data-altitude-assigned] [data-point="assignedAltitude"] input,
|
||||
[data-speed-assigned] [data-point="assignedSpeed"] input {
|
||||
background-color:#ffffffbb;
|
||||
color: black;
|
||||
font-weight: var( --font-weight-bolder );
|
||||
}
|
||||
|
||||
[data-warning-altitude] [data-point="altitude"],
|
||||
[data-warning-speed] [data-point="speed"] {
|
||||
background:#cc0000;
|
||||
border-radius: var( --border-radius-sm );
|
||||
}
|
||||
|
||||
|
||||
|
||||
.ol-strip-board-strip > [data-point="name"] {
|
||||
text-overflow: ellipsis;
|
||||
overflow:hidden;
|
||||
}
|
||||
|
||||
.ol-strip-board-strip .ol-select-value {
|
||||
opacity: .85;
|
||||
}
|
||||
|
||||
|
||||
.ol-strip-board-add-flight {
|
||||
display:flex;
|
||||
flex-flow: row nowrap;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
|
||||
.ol-strip-board-add-flight > * {
|
||||
border:none;
|
||||
outline: none;
|
||||
padding:4px 8px;
|
||||
}
|
||||
|
||||
.add-flight-by-click img {
|
||||
filter:invert();
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
.ol-strip-board-add-flight input {
|
||||
border-radius: var( --border-radius-sm );
|
||||
}
|
||||
|
||||
.ol-strip-board-add-flight .ol-auto-suggest {
|
||||
background:white;
|
||||
border-radius: var(--border-radius-sm );
|
||||
color:black;
|
||||
display:none;
|
||||
flex-direction: column;
|
||||
left:0;
|
||||
margin:0;
|
||||
position:absolute;
|
||||
translate:0 -100%;
|
||||
top:0;
|
||||
}
|
||||
|
||||
.ol-strip-board-add-flight .ol-auto-suggest[data-has-suggestions] {
|
||||
display:flex;
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
.ol-strip-board-add-flight .ol-auto-suggest[data-has-suggestions] a {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
[data-board-type="ground"] {
|
||||
bottom:20px;
|
||||
}
|
||||
|
||||
[data-board-type="tower"] {
|
||||
right:10px;
|
||||
top:10px;
|
||||
}
|
||||
|
||||
[data-board-type="tower"] .ol-auto-suggest {
|
||||
top:30px;
|
||||
translate:0;
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#unit-list {
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
font-size:13px;
|
||||
height: 250px;
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
#unit-list > div {
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
#unit-list > div > div {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width:100px;
|
||||
}
|
||||
|
||||
#unit-list > div:first-of-type {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#unit-list > div > div:nth-of-type( 4 ) {
|
||||
text-align: center;
|
||||
}
|
||||
@@ -11,37 +11,12 @@
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
z-index: 9999;
|
||||
z-index: 99999;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
#primary-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#command-mode-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#app-icon>.ol-select-options {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
background-image: url("/images/icon-round.png");
|
||||
background-position: 20px 22px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 45px 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
text-indent: 60px;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
white-space: nowrap;
|
||||
row-gap: 10px;
|
||||
margin-right: 320px;
|
||||
height: fit-content;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#connection-status-panel {
|
||||
@@ -49,16 +24,7 @@
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
width: 180px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#server-status-panel {
|
||||
bottom: 20px;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
right: 200px;
|
||||
width: 300px;
|
||||
width: 190px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
@@ -70,27 +36,40 @@
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
row-gap: 10px;
|
||||
width: 180px;
|
||||
width: 190px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#unit-control-panel {
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
width: 320px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#unit-info-panel {
|
||||
bottom: 20px;
|
||||
font-size: 12px;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
z-index: 9999;
|
||||
padding: 24px 30px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
right: 210px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
#hotgroup-panel {
|
||||
bottom: 40px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
translate: -50%;
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
#info-popup {
|
||||
@@ -100,15 +79,25 @@
|
||||
top: 100px;
|
||||
left: 50%;
|
||||
translate: -50% 0%;
|
||||
z-index: 9999;
|
||||
z-index: 9999999999;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#slow-delete-popup {
|
||||
align-self: center;
|
||||
display:flex;
|
||||
justify-self: center;
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
z-index: 9999999999;
|
||||
}
|
||||
|
||||
#log-panel {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 220px;
|
||||
top: 170px;
|
||||
width: 310px;
|
||||
height: fit-content;
|
||||
z-index: 9990;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
@-webkit-keyframes leaflet-gestures-fadein {
|
||||
0% {
|
||||
opacity: 0; }
|
||||
100% {
|
||||
opacity: 1; } }
|
||||
|
||||
@keyframes leaflet-gestures-fadein {
|
||||
0% {
|
||||
opacity: 0; }
|
||||
100% {
|
||||
opacity: 1; } }
|
||||
|
||||
.leaflet-container:after {
|
||||
-webkit-animation: leaflet-gestures-fadein 0.8s backwards;
|
||||
animation: leaflet-gestures-fadein 0.8s backwards;
|
||||
color: #fff;
|
||||
font-family: "Roboto", Arial, sans-serif;
|
||||
font-size: 22px;
|
||||
-webkit-box-pack: center;
|
||||
-ms-flex-pack: center;
|
||||
justify-content: center;
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
z-index: 461;
|
||||
pointer-events: none; }
|
||||
|
||||
.leaflet-gesture-handling-touch-warning:after,
|
||||
.leaflet-gesture-handling-scroll-warning:after {
|
||||
-webkit-animation: leaflet-gestures-fadein 0.8s forwards;
|
||||
animation: leaflet-gestures-fadein 0.8s forwards; }
|
||||
|
||||
.leaflet-gesture-handling-touch-warning:after {
|
||||
content: attr(data-gesture-handling-touch-content); }
|
||||
|
||||
.leaflet-gesture-handling-scroll-warning:after {
|
||||
content: attr(data-gesture-handling-scroll-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%;
|
||||
@@ -97,6 +102,22 @@
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[data-object|="unit-groundunit"] .unit-short-label {
|
||||
transform: translateY(7px);
|
||||
}
|
||||
|
||||
/*** Health indicator ***/
|
||||
[data-object|="unit"] .unit-health {
|
||||
background: white;
|
||||
border: var(--unit-health-border-width) solid var(--secondary-dark-steel);
|
||||
border-radius: var(--border-radius-sm);
|
||||
display: none;
|
||||
height: var(--unit-health-height);
|
||||
position: absolute;
|
||||
translate: var(--unit-health-x) var(--unit-health-y);
|
||||
width: var(--unit-health-width);
|
||||
}
|
||||
|
||||
/*** Fuel indicator ***/
|
||||
[data-object|="unit"] .unit-fuel {
|
||||
background: white;
|
||||
@@ -109,7 +130,8 @@
|
||||
width: var(--unit-fuel-width);
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-fuel-level {
|
||||
[data-object|="unit"] .unit-fuel-level,
|
||||
[data-object|="unit"] .unit-health-level {
|
||||
background-color: var(--secondary-light-grey);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@@ -178,23 +200,19 @@
|
||||
|
||||
/*** Common ***/
|
||||
[data-object|="unit"]:hover .unit-ammo,
|
||||
[data-object|="unit"]:hover .unit-health ,
|
||||
[data-object|="unit"]:hover .unit-fuel {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-has-low-fuel] .unit-fuel {
|
||||
[data-object|="unit"][data-has-low-fuel] .unit-fuel, [data-object|="unit"][data-has-low-health] .unit-health {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-is-in-hotgroup] .unit-hotgroup,
|
||||
[data-object|="unit"][data-is-selected] .unit-ammo,
|
||||
[data-object|="unit"][data-is-selected] .unit-fuel,
|
||||
[data-object|="unit"][data-is-selected] .unit-health,
|
||||
[data-object|="unit"][data-is-selected] .unit-selected-spotlight {
|
||||
display: flex;
|
||||
}
|
||||
@@ -211,6 +229,7 @@
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-fuel-level,
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-health-level,
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-1] .unit-ammo>div:nth-child(1),
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-2] .unit-ammo>div:nth-child(2),
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-3] .unit-ammo>div:nth-child(3),
|
||||
@@ -227,6 +246,7 @@
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="red"] .unit-fuel-level,
|
||||
[data-object|="unit"][data-coalition="red"] .unit-health-level,
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-1] .unit-ammo>div:nth-child(1),
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-2] .unit-ammo>div:nth-child(2),
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-3] .unit-ammo>div:nth-child(3),
|
||||
@@ -260,14 +280,15 @@
|
||||
background-image: url("/resources/theme/images/states/idle.svg");
|
||||
}
|
||||
|
||||
[data-object*="groundunit"][data-state="idle"] .unit-state {
|
||||
[data-object*="groundunit"][data-state="idle"] .unit-state,
|
||||
[data-object*="navyunit"][data-state="idle"] .unit-state {
|
||||
background-image: url(""); /* To avoid clutter, dont show the idle state for non flying units */
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="attack"] .unit-state,
|
||||
[data-object|="unit"][data-state="bombing point"] .unit-state,
|
||||
[data-object|="unit"][data-state="carpet bombing"] .unit-state,
|
||||
[data-object|="unit"][data-state="firing at area"] .unit-state {
|
||||
[data-object|="unit"][data-state="bomb-point"] .unit-state,
|
||||
[data-object|="unit"][data-state="carpet-bombing"] .unit-state,
|
||||
[data-object|="unit"][data-state="fire-at-area"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/attack.svg");
|
||||
}
|
||||
|
||||
@@ -287,10 +308,53 @@
|
||||
background-image: url("/resources/theme/images/states/dcs.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="land-at-point"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/land-at-point.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="no-task"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/no-task.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="off"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/off.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="tanker"] .unit-state {
|
||||
background-image: url("/resources/theme/images/states/tanker.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-state="AWACS"] .unit-state {
|
||||
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;
|
||||
background-size: contain;
|
||||
content: " ";
|
||||
height: 6px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
translate: -10px -2px;
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
|
||||
/*** Dead unit ***/
|
||||
[data-object|="unit"][data-is-dead] .unit-selected-spotlight,
|
||||
[data-object|="unit"][data-is-dead] .unit-short-label,
|
||||
@@ -299,6 +363,7 @@
|
||||
[data-object|="unit"][data-is-dead] .unit-hotgroup-id,
|
||||
[data-object|="unit"][data-is-dead] .unit-state,
|
||||
[data-object|="unit"][data-is-dead] .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead] .unit-health,
|
||||
[data-object|="unit"][data-is-dead] .unit-ammo,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-ammo {
|
||||
|
||||
@@ -30,12 +30,85 @@
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#aircraft-spawn-menu,
|
||||
#helicopter-spawn-menu,
|
||||
#groundunit-spawn-menu,
|
||||
#navyunit-spawn-menu {
|
||||
#map-contextmenu>div:nth-child(n+4)>div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options,
|
||||
.contextmenu-metadata {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle,
|
||||
.contextmenu-metadata-toggle {
|
||||
display: flex;
|
||||
align-content: center;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
margin: 5px;
|
||||
column-gap: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle:after,
|
||||
.contextmenu-metadata-toggle:after {
|
||||
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,
|
||||
.contextmenu-metadata-toggle div:first-child {
|
||||
width: fit-content;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options>*,
|
||||
.contextmenu-metadata>* {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contextmenu-metadata {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
padding: 0px 10px 10px 10px;
|
||||
}
|
||||
|
||||
.contextmenu-metadata>div:nth-child(1) {
|
||||
margin-bottom: 5px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.contextmenu-metadata>div:nth-child(2) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
row-gap: 5px;
|
||||
column-gap: 5px;
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.unit-spawn-menu {
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
@@ -58,11 +131,52 @@
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
#aircraft-spawn-menu .ol-select.is-open .ol-select-options,
|
||||
#helicopter-spawn-menu .ol-select.is-open .ol-select-options {
|
||||
.unit-spawn-menu .ol-select.is-open .ol-select-options {
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.ol-tag {
|
||||
background-color: #FFFFFF11;
|
||||
color: #FFFFFFDD;
|
||||
border: 1px solid #FFFFFF55;
|
||||
font-weight: normal;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
/*
|
||||
.ol-tag-CA {
|
||||
background-color: #FF000022;
|
||||
}
|
||||
|
||||
.ol-tag-Radar {
|
||||
background-color: #00FF0022;
|
||||
}
|
||||
|
||||
.ol-tag-IR {
|
||||
background-color: #0000FF22;
|
||||
}
|
||||
*/
|
||||
|
||||
.unit-loadout-list {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.unit-loadout-list div {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
background-color: var(--background-steel);
|
||||
padding: 2px 5px 2px 5px;
|
||||
}
|
||||
|
||||
.unit-loadout-list div:hover {
|
||||
overflow: visible;
|
||||
white-space: nowrap;
|
||||
background-color: var(--background-steel);
|
||||
width: fit-content;
|
||||
border-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
.deploy-unit-button {
|
||||
margin-top: 5px;
|
||||
text-align: center;
|
||||
@@ -82,7 +196,7 @@
|
||||
margin: 0px 5px;
|
||||
}
|
||||
|
||||
.upper-bar button:nth-child(2) {
|
||||
.upper-bar button:first-of-type {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@@ -137,31 +251,49 @@
|
||||
content: "Create neutral unit";
|
||||
}
|
||||
|
||||
#aircraft-loadout-preview,
|
||||
#helicopter-loadout-preview {
|
||||
.unit-label-count-container {
|
||||
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;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.unit-label-count-container button>*:nth-child(1) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.unit-loadout-preview {
|
||||
align-content: space-between;
|
||||
align-items: center;
|
||||
column-gap: 20px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#aircaft-loadout-list,
|
||||
#helicopter-loadout-list {
|
||||
.unit-loadout-list {
|
||||
align-content: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#aircraft-unit-image,
|
||||
#helicopter-unit-image {
|
||||
.unit-image {
|
||||
filter: invert(100%);
|
||||
height: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 10px;
|
||||
width: 100px;
|
||||
width: 25%;
|
||||
aspect-ratio: 1/1;
|
||||
margin: 5px 0px;
|
||||
}
|
||||
|
||||
#smoke-spawn-menu {
|
||||
@@ -259,69 +391,144 @@
|
||||
#unit-contextmenu div:before {
|
||||
display: inline-block;
|
||||
filter: invert(100%);
|
||||
height: 16px;
|
||||
height: 20px;
|
||||
margin-right: 15px;
|
||||
width: 16px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.ol-select>.ol-select-options>div button.country-dropdown-element {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
column-gap: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.country-dropdown-element img {
|
||||
height: 20px;
|
||||
aspect-ratio: initial;
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
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 {
|
||||
background-image: url("/resources/theme/images/icons/scenic.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#miss-aaa::before {
|
||||
background-image: url("/resources/theme/images/icons/miss.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#group-ground::before {
|
||||
background-image: url("/resources/theme/images/icons/group-ground.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#group-navy::before {
|
||||
background-image: url("/resources/theme/images/icons/group-navy.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#land-at-point::before {
|
||||
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 {
|
||||
@@ -424,6 +631,7 @@
|
||||
|
||||
#iads-menu {
|
||||
row-gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#coalition-area-contextmenu>div:nth-child(2) {
|
||||
@@ -440,6 +648,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.create-iads-button {
|
||||
@@ -486,4 +695,36 @@
|
||||
|
||||
#airbase-runways>.runway>.heading:last-of-type {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
/* Airbase spawn menu */
|
||||
#airbase-spawn-contextmenu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: fit-content;
|
||||
position: absolute;
|
||||
row-gap: 5px;
|
||||
width: 300px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#airbase-spawn-contextmenu>div:nth-child(2) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#airbase-spawn-contextmenu >div:nth-child(n+3) {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
#airbase-spawn-contextmenu>div:nth-child(n+3)>div {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -1,8 +1,28 @@
|
||||
.ol-popup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
.ol-popup > div {
|
||||
background-color: var(--background-steel);
|
||||
border-radius: var(--border-radius-md);
|
||||
box-shadow: 0px 2px 5px #000A;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.ol-popup-stack {
|
||||
margin-bottom: -20px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
125
client/public/stylesheets/other/toolbar.css
Normal file
@@ -0,0 +1,125 @@
|
||||
|
||||
#primary-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
#command-mode-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#app-icon>.ol-select-options {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#app-icon>.ol-select-value {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
background-image: url("/images/icon-round.png");
|
||||
background-position: 20px 22px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 45px 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
text-indent: 60px;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(1):not(:hover)>*:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover) {
|
||||
height: 52px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>svg {
|
||||
display: block;
|
||||
filter: invert();
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>*:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
#connection-status-panel dt::before {
|
||||
#connection-status-panel {
|
||||
align-items: center;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#connection-status-panel #connection-status-message::before {
|
||||
content: "No connection";
|
||||
}
|
||||
|
||||
#connection-status-panel dd::after {
|
||||
#connection-status-light {
|
||||
border-radius: 50%;
|
||||
background: red;
|
||||
content: " ";
|
||||
@@ -10,10 +17,39 @@
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-connected] dt::before {
|
||||
content: "Connected";
|
||||
#connection-status-panel[data-is-connected] #connection-status-message::before {
|
||||
content: "";
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-connected] dd::after {
|
||||
#connection-status-panel .time-display {
|
||||
cursor:pointer;
|
||||
display:none;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-connected] .mission-elapsed-time,
|
||||
#connection-status-panel[data-is-connected]:not([data-mission-time]) .mission-time {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-connected]:not([data-mission-time]) .mission-elapsed-time,
|
||||
#connection-status-panel[data-is-connected][data-mission-time] .mission-time {
|
||||
display:block;
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-connected] #connection-status-light {
|
||||
background: var(--accent-green);
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] #connection-status-message::before {
|
||||
content: "Server paused";
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] #connection-status-light {
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] #connection-status-light {
|
||||
background: var(--accent-amber);
|
||||
}
|
||||
@@ -26,3 +26,59 @@
|
||||
#log-panel.open>div:nth-child(2) {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#log-panel-header-right {
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
display:flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
#log-panel-header-right svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
#server-status-panel abbr {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#server-status-panel dl {
|
||||
column-gap: 4px;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
#server-status-panel dl > * {
|
||||
margin:0;
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
#server-status-panel dd {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.fps-low {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.fps-medium {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.fps-high {
|
||||
color: lightgreen;
|
||||
}
|
||||
|
||||
.load-low {
|
||||
color: lightgreen;
|
||||
}
|
||||
|
||||
.load-medium {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.load-high {
|
||||
color: red;
|
||||
}
|
||||
@@ -1,20 +1,41 @@
|
||||
#mouse-info-panel>* {
|
||||
#mouse-info-panel .mouse-tool {
|
||||
background-color: var(--background-grey);
|
||||
border-radius: var(--border-radius-sm);
|
||||
display:flex;
|
||||
flex-flow:column wrap;
|
||||
row-gap: 4px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
#mouse-info-panel dl {
|
||||
margin-bottom: 4px;
|
||||
row-gap: 5px;
|
||||
#mouse-info-panel .mouse-tool .mouse-tool-item {
|
||||
align-items: center;
|
||||
display:flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#mouse-info-panel dt {
|
||||
height: fit-content;
|
||||
width: 30%;
|
||||
|
||||
#mouse-info-panel svg {
|
||||
padding: 3px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#mouse-info-panel dt::after {
|
||||
#mouse-info-panel svg > * {
|
||||
fill: black;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
#mouse-info-panel .mouse-tool .mouse-tool-item > * {
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
#mouse-info-panel .mouse-tool .mouse-tool-item > *:last-child {
|
||||
text-align: right;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
#mouse-info-panel .svg-icon, #mouse-info-panel .mouse-tool .mouse-tool-item :first-child {
|
||||
align-items: center;
|
||||
background-color: white;
|
||||
border-radius: var(--border-radius-sm);
|
||||
@@ -22,68 +43,41 @@
|
||||
display: flex;
|
||||
font-size: 15.6px;
|
||||
font-weight: bolder;
|
||||
height: 16px;
|
||||
height: 22px;
|
||||
justify-content: center;
|
||||
line-height: 16px;
|
||||
padding: 4px;
|
||||
text-transform: uppercase;
|
||||
width: 16px;
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
#mouse-info-panel #measuring-tool dt {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-color: var(--background-offwhite);
|
||||
#mouse-info-panel .mouse-tool .mouse-tool-item [data-label]::after {
|
||||
border-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
#mouse-info-panel #measuring-tool svg {
|
||||
padding: 3px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#mouse-info-panel #measuring-tool dt svg>* {
|
||||
fill: black;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
#mouse-info-panel dt[data-label]::after {
|
||||
content: attr(data-label);
|
||||
}
|
||||
|
||||
#mouse-info-panel dt[data-coalition="blue"]::after {
|
||||
background-color: var(--primary-blue);
|
||||
}
|
||||
|
||||
#mouse-info-panel dt[data-coalition="red"]::after {
|
||||
background-color: var(--primary-red);
|
||||
}
|
||||
|
||||
#mouse-info-panel dt[data-tooltip]:hover::before {
|
||||
background-color: var(--background-grey);
|
||||
border-radius: 5px;
|
||||
content: attr(data-tooltip);
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
translate: calc(-100% - 15px) 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#mouse-info-panel dd {
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
.br-info::after {
|
||||
content: attr(data-bearing) '\00B0 / ' attr(data-distance) " " attr(data-distance-units);
|
||||
#mouse-info-panel .mouse-tool .mouse-tool-item :last-child {
|
||||
color: var(--background-offwhite);
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--background-offwhite);
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
|
||||
/* Bullseye info */
|
||||
|
||||
#mouse-info-panel .mouse-tool .mouse-tool-item [data-label][data-coalition="blue"] {
|
||||
background-color: var(--primary-blue);
|
||||
}
|
||||
|
||||
#mouse-info-panel .mouse-tool .mouse-tool-item [data-label][data-coalition="red"] {
|
||||
background-color: var(--primary-red);
|
||||
}
|
||||
|
||||
.br-info::after {
|
||||
content: attr(data-bearing) '\00B0 / ' attr(data-distance) " " attr(data-distance-units);
|
||||
}
|
||||
|
||||
.br-info[data-coalition="blue"]::after {
|
||||
@@ -98,12 +92,19 @@
|
||||
content: attr(data-message);
|
||||
}
|
||||
|
||||
.coordinates::after {
|
||||
content: attr(data-value);
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--background-offwhite);
|
||||
|
||||
/* Coordinates */
|
||||
#coordinates-tool .elevation::after {
|
||||
content: attr(data-value)
|
||||
}
|
||||
|
||||
#coordinates-tool[data-location-system] [data-location-system] {
|
||||
cursor:pointer;
|
||||
display:none;
|
||||
}
|
||||
|
||||
#coordinates-tool[data-location-system="LatLng"] [data-location-system="LatLng"],
|
||||
#coordinates-tool[data-location-system="MGRS"] [data-location-system="MGRS"],
|
||||
#coordinates-tool[data-location-system="UTM"] [data-location-system="UTM"] {
|
||||
display:flex;
|
||||
}
|
||||
@@ -5,13 +5,24 @@
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
#server-status-panel .ol-data-grid {
|
||||
width: 100%;
|
||||
|
||||
#log-panel-header-right {
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
display:flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
#server-status-panel .ol-data-grid:first-of-type {
|
||||
border-right: 1px solid gray;
|
||||
padding-right: 10px;
|
||||
#server-status-panel dl {
|
||||
column-gap: 4px;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
#server-status-panel dl > * {
|
||||
margin:0;
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
#server-status-panel dd {
|
||||
|
||||
@@ -2,14 +2,126 @@ 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;
|
||||
column-gap: 10px;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
#unit-control-panel>div:nth-child(2),
|
||||
#unit-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
#unit-control-panel h3 {
|
||||
margin-bottom: 8px;
|
||||
#unit-controls {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#unit-control-panel>div:nth-child(2) {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
#unit-control-panel>*:nth-child(1) {
|
||||
display: none;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 1145px) {
|
||||
#unit-control-panel>*:nth-child(1) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#unit-control-panel>*:nth-child(1) svg {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
#unit-control-panel:hover>*:nth-child(1) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-control-panel:not(:hover) {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#unit-control-panel:not(:hover)>*:nth-child(2),
|
||||
#unit-control-panel:not(:hover)>*:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#unit-control-panel #selected-units-container {
|
||||
@@ -25,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;
|
||||
@@ -41,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;
|
||||
}
|
||||
@@ -56,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;
|
||||
@@ -91,8 +204,8 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#advanced-settings-dialog h4 {
|
||||
white-space: nowrap;
|
||||
#advanced-settings-dialog>.ol-dialog-content>div input[type="number"] {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
#advanced-settings-dialog hr {
|
||||
@@ -102,6 +215,12 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
|
||||
#advanced-settings-dialog .ol-text-input input {
|
||||
height: 40px;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#advanced-settings-dialog h4 {
|
||||
width: fit-content;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
#general-settings-grid {
|
||||
@@ -136,78 +255,127 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
}
|
||||
|
||||
#altitude-type-switch[data-value="true"]>.ol-switch-fill::before {
|
||||
content: "AGL";
|
||||
}
|
||||
|
||||
#altitude-type-switch[data-value="false"]>.ol-switch-fill::before {
|
||||
content: "ASL";
|
||||
}
|
||||
|
||||
#speed-type-switch[data-value="true"]>.ol-switch-fill::before {
|
||||
content: "GS";
|
||||
#altitude-type-switch[data-value="false"]>.ol-switch-fill::before {
|
||||
content: "AGL";
|
||||
}
|
||||
|
||||
#speed-type-switch[data-value="false"]>.ol-switch-fill::before {
|
||||
#speed-type-switch[data-value="true"]>.ol-switch-fill::before {
|
||||
content: "CAS";
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-slider-value {
|
||||
#speed-type-switch[data-value="false"]>.ol-switch-fill::before {
|
||||
content: "GS";
|
||||
}
|
||||
|
||||
.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;
|
||||
display: grid;
|
||||
grid-template-columns: 1.35fr 0.65fr;
|
||||
align-content: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control>*:nth-child(2) {
|
||||
justify-self: end;
|
||||
.switch-control h4 {
|
||||
margin: 0px !important;
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control>*:nth-child(3) {
|
||||
color: var(--secondary-semitransparent-white);
|
||||
}
|
||||
|
||||
#unit-control-panel .switch-control h4 {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#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";
|
||||
.switch-control h4 img {
|
||||
height: 15px;
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
filter: invert(100%);
|
||||
opacity: 80%;
|
||||
}
|
||||
|
||||
#advanced-settings-div {
|
||||
column-gap: 5px;
|
||||
align-items: center;
|
||||
column-gap: 8px;
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#advanced-settings-div>*:nth-child(2) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#advanced-settings-div button {
|
||||
#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 {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
#delete-options button svg {
|
||||
background-color: transparent;
|
||||
margin-right: 10px;
|
||||
width: 18px;
|
||||
max-height: 18px;
|
||||
}
|
||||
|
||||
#delete-options button svg * {
|
||||
stroke: red;
|
||||
}
|
||||
|
||||
/* Element visibility control */
|
||||
@@ -217,14 +385,18 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
#unit-control-panel:not([data-show-roe]) #roe,
|
||||
#unit-control-panel:not([data-show-threat]) #threat,
|
||||
#unit-control-panel:not([data-show-emissions-countermeasures]) #emissions-countermeasures,
|
||||
#unit-control-panel:not([data-show-shots-scatter]) #shots-scatter,
|
||||
#unit-control-panel:not([data-show-shots-intensity]) #shots-intensity,
|
||||
#unit-control-panel:not([data-show-tanker-button]) #tanker-on,
|
||||
#unit-control-panel:not([data-show-AWACS-button]) #AWACS-on,
|
||||
#unit-control-panel:not([data-show-on-off]) #ai-on-off,
|
||||
#unit-control-panel:not([data-show-follow-roads]) #follow-roads,
|
||||
#unit-control-panel:not([data-show-operate-as]) #operate-as,
|
||||
#unit-control-panel:not([data-show-advanced-settings-button]) #advanced-settings-button,
|
||||
#advanced-settings-dialog:not([data-show-settings]) #general-settings,
|
||||
#advanced-settings-dialog:not([data-show-tasking]) #tasking,
|
||||
#advanced-settings-dialog:not([data-show-tanker]) #tanker-checkbox,
|
||||
#advanced-settings-dialog:not([data-show-AWACS]) #AWACS-checkbox,
|
||||
#advanced-settings-dialog:not([data-show-TACAN]) #TACAN-options,
|
||||
#advanced-settings-dialog:not([data-show-radio]) #radio-options {
|
||||
#advanced-settings-dialog:not([data-show-radio]) #radio-options,
|
||||
#advanced-settings-dialog:not([data-show-air-unit-checkboxes]) .air-unit-checkbox {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,15 +1,55 @@
|
||||
#unit-info-panel>* {
|
||||
position: relative;
|
||||
min-height: 100px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>*:nth-child(1) {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 6px;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
#unit-info-panel:hover>*:nth-child(1) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-info-panel:not(:hover) {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
padding: 10px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel:not(:hover)>*:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section {
|
||||
border-right: 1px solid #555;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:first-of-type {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-of-type{
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-of-type {
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
#general {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 4px;
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#unit-label {
|
||||
@@ -24,6 +64,10 @@
|
||||
#unit-name {
|
||||
margin-bottom: 4px;
|
||||
padding: 0px 0;
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#current-task {
|
||||
@@ -48,20 +92,24 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#loadout-silhouette {
|
||||
filter: invert(100%);
|
||||
height: 100px;
|
||||
height: 75px;
|
||||
margin-right: 25px;
|
||||
width: 100px;
|
||||
width: 75px;
|
||||
}
|
||||
|
||||
#loadout-items {
|
||||
align-self: center;
|
||||
column-gap: 8px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
row-gap: 8px;
|
||||
height: 100px;
|
||||
row-gap: 6px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#loadout-items>* {
|
||||
@@ -79,7 +127,7 @@
|
||||
display: flex;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
padding: 4px 6px;
|
||||
padding: 3px 4px;
|
||||
}
|
||||
|
||||
#loadout-items>*::after {
|
||||
@@ -94,16 +142,16 @@
|
||||
#fuel-percentage {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-top: auto;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
#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 {
|
||||
|
||||