Fixed conflicts, ordered maps.

This commit is contained in:
PeekabooSteam
2023-02-10 14:07:13 +00:00
77 changed files with 5700 additions and 940 deletions

View File

@@ -1,11 +1,9 @@
Change cursor when in moving mode
Show airfields and enable airfield spawn
RTB
tanker
scenario dropdown
explosion
wrong name for ground units
ground units don't move
improve map zIndex
fuel is wrong (either 0 or 1, its is casting it to int somewhere)
weapons should not be selected
weapons should not be selectable
human symbol if user

265
client/package-lock.json generated
View File

@@ -1,11 +1,11 @@
{
"name": "DCS Olympus",
"name": "DCSOlympus",
"version": "0.0.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "DCS Olympus",
"name": "DCSOlympus",
"version": "0.0.0",
"dependencies": {
"@types/geojson": "^7946.0.10",
@@ -23,6 +23,7 @@
"@types/gtag.js": "^0.0.12",
"browserify": "^17.0.0",
"concurrently": "^7.6.0",
"nodemon": "^2.0.20",
"tsify": "^5.0.4",
"typescript": "^4.9.4",
"watchify": "^4.0.0"
@@ -47,6 +48,12 @@
"@types/geojson": "*"
}
},
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -1515,6 +1522,12 @@
}
]
},
"node_modules/ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true
},
"node_modules/inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -1979,6 +1992,94 @@
"node": ">= 0.6"
}
},
"node_modules/nodemon": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
"integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
"debug": "^3.2.7",
"ignore-by-default": "^1.0.1",
"minimatch": "^3.1.2",
"pstree.remy": "^1.1.8",
"semver": "^5.7.1",
"simple-update-notifier": "^1.0.7",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.5"
},
"bin": {
"nodemon": "bin/nodemon.js"
},
"engines": {
"node": ">=8.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/nodemon"
}
},
"node_modules/nodemon/node_modules/debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"dependencies": {
"ms": "^2.1.1"
}
},
"node_modules/nodemon/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/nodemon/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"node_modules/nodemon/node_modules/semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true,
"bin": {
"semver": "bin/semver"
}
},
"node_modules/nodemon/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
"integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
"dev": true,
"dependencies": {
"abbrev": "1"
},
"bin": {
"nopt": "bin/nopt.js"
},
"engines": {
"node": "*"
}
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -2186,6 +2287,12 @@
"node": ">= 0.10"
}
},
"node_modules/pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
"node_modules/public-encrypt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
@@ -2493,6 +2600,27 @@
}
]
},
"node_modules/simple-update-notifier": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
"integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
"dev": true,
"dependencies": {
"semver": "~7.0.0"
},
"engines": {
"node": ">=8.10.0"
}
},
"node_modules/simple-update-notifier/node_modules/semver": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
"dev": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -2774,6 +2902,18 @@
"node": ">=8.0"
}
},
"node_modules/touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
"dev": true,
"dependencies": {
"nopt": "~1.0.10"
},
"bin": {
"nodetouch": "bin/nodetouch.js"
}
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -2884,6 +3024,12 @@
"undeclared-identifiers": "bin.js"
}
},
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -3102,6 +3248,12 @@
"@types/geojson": "*"
}
},
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
"accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -4294,6 +4446,12 @@
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
"dev": true
},
"ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
"dev": true
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
@@ -4661,6 +4819,71 @@
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="
},
"nodemon": {
"version": "2.0.20",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz",
"integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==",
"dev": true,
"requires": {
"chokidar": "^3.5.2",
"debug": "^3.2.7",
"ignore-by-default": "^1.0.1",
"minimatch": "^3.1.2",
"pstree.remy": "^1.1.8",
"semver": "^5.7.1",
"simple-update-notifier": "^1.0.7",
"supports-color": "^5.5.0",
"touch": "^3.1.0",
"undefsafe": "^2.0.5"
},
"dependencies": {
"debug": {
"version": "3.2.7",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"requires": {
"ms": "^2.1.1"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"dev": true
},
"ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"dev": true
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
},
"supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dev": true,
"requires": {
"has-flag": "^3.0.0"
}
}
}
},
"nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
"integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
"dev": true,
"requires": {
"abbrev": "1"
}
},
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@@ -4829,6 +5052,12 @@
"ipaddr.js": "1.9.1"
}
},
"pstree.remy": {
"version": "1.1.8",
"resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
"integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
"dev": true
},
"public-encrypt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
@@ -5083,6 +5312,23 @@
"integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==",
"dev": true
},
"simple-update-notifier": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz",
"integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==",
"dev": true,
"requires": {
"semver": "~7.0.0"
},
"dependencies": {
"semver": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz",
"integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==",
"dev": true
}
}
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -5314,6 +5560,15 @@
"is-number": "^7.0.0"
}
},
"touch": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
"integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
"dev": true,
"requires": {
"nopt": "~1.0.10"
}
},
"tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
@@ -5398,6 +5653,12 @@
"xtend": "^4.0.1"
}
},
"undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
"dev": true
},
"unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",

View File

@@ -1,5 +1,5 @@
{
"name": "DCS Olympus",
"name": "DCSOlympus",
"node-main": "./bin/www",
"main": "http://localhost:3000",
"version": "0.0.0",
@@ -25,6 +25,7 @@
"@types/gtag.js": "^0.0.12",
"browserify": "^17.0.0",
"concurrently": "^7.6.0",
"nodemon": "^2.0.20",
"tsify": "^5.0.4",
"typescript": "^4.9.4",
"watchify": "^4.0.0"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M256 8C119 8 8 119 8 256s111 248 248 248 248-111 248-248S393 8 256 8zm144 276c0 6.6-5.4 12-12 12h-92v92c0 6.6-5.4 12-12 12h-56c-6.6 0-12-5.4-12-12v-92h-92c-6.6 0-12-5.4-12-12v-56c0-6.6 5.4-12 12-12h92v-92c0-6.6 5.4-12 12-12h56c6.6 0 12 5.4 12 12v92h92c6.6 0 12 5.4 12 12v56z"/></svg>

After

Width:  |  Height:  |  Size: 491 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M497.941 273.941c18.745-18.745 18.745-49.137 0-67.882l-160-160c-18.745-18.745-49.136-18.746-67.883 0l-256 256c-18.745 18.745-18.745 49.137 0 67.882l96 96A48.004 48.004 0 0 0 144 480h356c6.627 0 12-5.373 12-12v-40c0-6.627-5.373-12-12-12H355.883l142.058-142.059zm-302.627-62.627l137.373 137.373L265.373 416H150.628l-80-80 124.686-124.686z"/></svg>

After

Width:  |  Height:  |  Size: 553 B

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="36"
height="36"
viewBox="0 0 9.5249998 9.5250003"
version="1.1"
id="svg5"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
sodipodi:docname="formation-end.svg"
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">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
width="36px"
inkscape:zoom="16.000001"
inkscape:cx="14.406249"
inkscape:cy="20.906249"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:1.48167;stroke-opacity:1"
d="M 7.509551,4.8476603 H 4.2913461"
id="path1369"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:1.48167;stroke-opacity:1"
d="M 4.2913461,0.24555387 V 4.8476603"
id="path2640"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="36"
height="36"
viewBox="0 0 9.5249998 9.5250003"
version="1.1"
id="svg5"
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
sodipodi:docname="formation-middle.svg"
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">
<sodipodi:namedview
id="namedview7"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:document-units="mm"
showgrid="false"
units="px"
width="36px"
inkscape:zoom="16.000001"
inkscape:cx="14.406249"
inkscape:cy="20.906249"
inkscape:window-width="1920"
inkscape:window-height="1017"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs2" />
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:1.48167;stroke-opacity:1"
d="M 7.509551,4.8476603 H 4.2913461"
id="path1369"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.05833, 0.529167;stroke-dashoffset:1.48167;stroke-opacity:1"
d="m 4.2913461,0.24555387 0,9.06695013"
id="path2640"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M635.7 167.2L556.1 31.7c-8.8-15-28.3-20.1-43.5-11.5l-69 39.1L503.3 161c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L416 75l-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L333.2 122 278 153.3 337.8 255c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-59.7-101.7-55.2 31.3 27.9 47.4c2.2 3.8.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9l-27.9-47.5-55.2 31.3 59.7 101.7c2.2 3.7.9 8.5-2.9 10.7l-13.8 7.8c-3.8 2.2-8.7.9-10.9-2.9L84.9 262.9l-69 39.1C.7 310.7-4.6 329.8 4.2 344.8l79.6 135.6c8.8 15 28.3 20.1 43.5 11.5L624.1 210c15.2-8.6 20.4-27.8 11.6-42.8z"/></svg>

After

Width:  |  Height:  |  Size: 821 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 288 512"><!-- Font Awesome Pro 5.15.4 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) --><path d="M112 316.94v156.69l22.02 33.02c4.75 7.12 15.22 7.12 19.97 0L176 473.63V316.94c-10.39 1.92-21.06 3.06-32 3.06s-21.61-1.14-32-3.06zM144 0C64.47 0 0 64.47 0 144s64.47 144 144 144 144-64.47 144-144S223.53 0 144 0zm0 76c-37.5 0-68 30.5-68 68 0 6.62-5.38 12-12 12s-12-5.38-12-12c0-50.73 41.28-92 92-92 6.62 0 12 5.38 12 12s-5.38 12-12 12z"/></svg>

After

Width:  |  Height:  |  Size: 549 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

View File

@@ -56,16 +56,15 @@ body {
}
#unit-control-buttons {
position: absolute;
position: fixed;
top: 10px;
height: fit-content;
width: fit-content;
right: 10px;
right: 270px;
z-index: 1000;
}
#unit-control-panel {
display: none;
position: absolute;
top: 10px;
height: fit-content;
@@ -77,12 +76,21 @@ body {
#connection-status-panel {
position: absolute;
height: 30px;
width: 140px;
width: 160px;
bottom: 10px;
right: 10px;
z-index: 1000;
}
#mouse-info-panel {
position: absolute;
height: fit-content;
width: 160px;
bottom: 50px;
right: 10px;
z-index: 1000;
}
@media only screen and (max-width: 1000px) {
#unit-control-buttons {
top: 50px;

View File

@@ -0,0 +1,31 @@
#mouse-info-panel {
display: flex;
flex-direction: column;
padding: 10px;
row-gap: 5px;
}
#mouse-info-panel .rectangular-container{
width: 100%;
font-weight: 600;
font-size: 12px;
display: flex;
align-items: center;
justify-content: space-between;
padding-left: 10px;
padding-right: 10px;
border-radius: 10px;
background-color: #FFF3;
}
#mouse-info-panel img {
height: 24px;
}
#measure-position-container{
display: none;
}
#unit-position-container{
display: none;
}

View File

@@ -1,20 +1,40 @@
.olympus-selection-scroll-container {
position: fixed;
background-color: var(--background-color-dark);
position: absolute;
font-size: 12px;
transition: bottom 0.2s;
border-radius: 15px;
box-shadow: 0px 2px 5px #000A;
width: 180px;
border-radius: 5px;
width: 220px;
height: fit-content;
z-index: 1000;
z-index: 2000;
max-height: 400px;
padding: 10px;
padding: 8px;
display: flex;
flex-direction: column;
row-gap: 5px;
align-items: center;
}
#olympus-selection-scroll-top-bar {
color: white;
font-size: 14px;
opacity: 1;
border-radius: 5px;
padding: 5px;
background-color: #333D;
width: 100%;
text-align: center;
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding-left: 15px;
padding-right: 15px;
}
.olympus-selection-scroll {
overflow-x: hidden;
overflow-y: auto;
max-height: calc(400px - 60px);
height: 100%;
width: 100%;
}
.olympus-selection-scroll::-webkit-scrollbar {
@@ -34,48 +54,51 @@
}
.olympus-selection-scroll-element {
margin: 2px;
border-bottom: 1px solid #FFF5;
color: white;
cursor: pointer;
font-size: 14px;
font-size: 13px;
opacity: 1;
border-radius: 5px;
margin: 5px;
padding: 5px;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 15px;
background-color: var(--background-color-dark);
font-weight: 600;
}
.olympus-selection-scroll-element:hover {
background-color: var(--highlight-color);
.olympus-selection-scroll:last-child {
border-radius: 5px;
border-bottom: 0px transparent !important;
}
.olympus-selection-scroll-container label {
width: 0;
height: 0;
display: inline-block;
width: 40px;
height: 24px;
}
.olympus-selection-scroll-container input {
display: inline-block;
width: 0;
height: 0;
margin: 0px;
}
.olympus-selection-scroll-switch {
position: relative;
display: inline-block;
left: calc(50% - 10px);
width: 60px;
height: 34px;
width: 40px;
height: 24px;
background-color: var(--active-coalition-color);
border-radius: 999px;
cursor: pointer;
margin-left: -30px;
}
.olympus-selection-scroll-switch:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: white;
@@ -85,7 +108,12 @@
}
input:checked+.olympus-selection-scroll-switch:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
-webkit-transform: translateX(16px);
-ms-transform: translateX(16px);
transform: translateX(16px);
}
.olympus-selection-scroll-title {
font-size: 11px;
font-weight: 600;
}

View File

@@ -0,0 +1,47 @@
.slider-container {
width: 100%;
}
.slider {
width: 100%;
-webkit-appearance: none;
appearance: none;
height: 2px;
background: #d3d3d3;
outline: none;
opacity: 0.7;
-webkit-transition: .2s;
transition: opacity .2s;
margin-top: 10px;
margin-bottom: 10px;
}
.slider:hover {
opacity: 1;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: gray;
cursor: pointer;
border-radius: 999px;
}
.active .slider::-webkit-slider-thumb {
background: #5ca7ff;
}
.slider::-moz-range-thumb {
width: 20px;
height: 20px;
background: gray;
cursor: pointer;
border-radius: 999px;
}
.active .slider::-moz-range-thumb {
background: #5ca7ff;
}

View File

@@ -1,4 +1,5 @@
@import url("button.css");
@import url("slider.css");
@import url("dropdown.css");
@import url("selectionwheel.css");
@@ -12,6 +13,7 @@
@import url("unitcontrolpanel.css");
@import url("visibilitycontrolpanel.css");
@import url("unitinfopanel.css");
@import url("mouseinfopanel.css");
@import url("layout.css");
@@ -19,14 +21,14 @@
/* Variables definitions */
:root {
--background-color-dark: #202831;
--background-color-light: #aaaaaa;
--background-color-light: #AAA;
--title-color: #d3e9ff;
--text-color: white;
--blue-coalition-color: #2196F3;
--blue-coalition-color: #247be2;
--red-coalition-color: #f32121;
--neutral-coalition-color: #AAAAAA;
--neutral-coalition-color: #202831;
--active-coalition-color: var(--blue-coalition-color);
--highlight-color: #FFFFFFAA;
--highlight-color: #FFF5;
}
* {
@@ -69,15 +71,90 @@ html {
}
.rounded-container {
position: relative;
padding: 0.5em;
background-color: #247be2;
border-radius: 10px;
width: fit-content;
height: fit-content;
text-align: center;
color: white;
font-size: 12px;
border-radius: 9999px;
background-color: gray;
}
.rounded-container.blue {
background-color: var(--blue-coalition-color);
border: 1px solid var(--blue-coalition-color);
}
.rounded-container.red {
background-color: var(--red-coalition-color);
border: 1px solid var(--red-coalition-color);
}
.rounded-container.neutral {
background-color: var(--neutral-coalition-color);
}
.rounded-container-small {
padding: 0.2em;
width: fit-content;
height: fit-content;
text-align: center;
color: black;
font-weight: 600;
background-color: #FFFA;
font-size: 11px;
border-radius: 9999px;
padding-left: 5px;
padding-right: 5px;
}
.rectangular-button {
position: relative;
padding: 0.5em;
width: fit-content;
height: fit-content;
text-align: center;
color: var(--highlight-color);
font-size: 12px;
border-radius: 5px;
background-color: transparent;
border: 1px solid var(--highlight-color);
cursor: pointer;
font-weight: 600;
display: flex;
align-items: center;
column-gap: 5px;
}
.rectangular-button.blue {
border: 1px solid var(--blue-coalition-color);
color: var(--blue-coalition-color);
}
.rectangular-button.red {
border: 1px solid var(--red-coalition-color);
color: var(--red-coalition-color);
}
.rectangular-button.white {
border: 1px solid white;
color: white;
}
.rectangular-button.white>img {
filter: invert(100%);
}
.rectangular-button>img {
display: inline-block;
height: 18px;
width: 18px;
}
.rectangular-button.red {
border: 1px solid var(--red-coalition-color);
}
.vl {
@@ -85,3 +162,26 @@ html {
width: 1px !important;
display: inline-block;
}
.hl {
border-top: 1px solid #555;
height: 1px !important;
display: inline-block;
}
.measure-box {
position: absolute;
padding-left: 0.5em;
padding-right: 0.5em;
padding-top: 0.2em;
padding-bottom: 0.2em;
background-color: #151b20;
border-radius: 5px;
width: fit-content;
height: fit-content;
text-align: center;
color: white;
font-size: 12px;
z-index: 2000;
font-weight: 600;
}

View File

@@ -13,24 +13,86 @@
#unit-control-panel {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: space-between;
align-content: flex-start;
row-gap: 5px;
row-gap: 10px;
padding-left: 30px;
padding-right: 30px;
padding-top: 20px;
padding-bottom: 20px;
}
/* Common */
#unit-info-panel>div {
height: 100%;
#selected-units-container {
display: flex;
flex-direction: column;
row-gap: 5px;
width: 100%;
height: 100%;
}
#unit-control-panel .rounded-container {
width: 100%;
#formation-buttons-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
row-gap: 5px;
column-gap: 5px;
width: 100%;
height: 100%;
}
#roe-buttons-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
row-gap: 5px;
column-gap: 5px;
width: 100%;
height: 100%;
}
#reaction-to-threat-buttons-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
row-gap: 5px;
column-gap: 5px;
width: 100%;
height: 100%;
}
#selected-units-container .rounded-container {
width: calc(100% - 25px);
cursor: pointer;
margin-left: 25px;
}
#selected-units-container .rounded-container.not-selected {
background-color: transparent;
}
#selected-units-container .rounded-container .rounded-container-small {
display: inline-block;
position: absolute;
left: 5px;
top: 5px;
}
#selected-units-container img {
height: calc(100% + 6px);
display: inline-block;
position: absolute;
left: -32px;
top: -3px;
}
#selected-units-container img.blue {
filter: invert(81%) sepia(6%) saturate(1685%) hue-rotate(181deg) brightness(103%) contrast(92%);
}
#selected-units-container img.red {
filter: invert(93%) sepia(97%) saturate(1174%) hue-rotate(291deg) brightness(105%) contrast(97%);
}
#unit-control-panel #title-label {
@@ -38,4 +100,31 @@
font-size: 14px;
width: 100%;
font-weight: 600;
}
}
#unit-control-panel #section-label {
color: white;
font-size: 13px;
width: 100%;
}
.flight-control-slider {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
.flight-control-title {
font-size: 13px;
color: white;
}
.flight-control-value {
font-size: 14px;
font-weight: 600;
color: gray;
}
.active .flight-control-value {
color: #5ca7ff;
}

View File

@@ -1,5 +1,5 @@
import { LatLng } from "leaflet";
import { setActiveCoalition } from "..";
import { getActiveCoalition, setActiveCoalition } from "..";
export class SelectionScroll {
#container: HTMLElement | null;
@@ -15,14 +15,17 @@ export class SelectionScroll {
}
}
show(x: number, y: number, options: any, callback: CallableFunction, showCoalition: boolean) {
show(x: number, y: number, title: string, options: any, callback: CallableFunction, showCoalition: boolean) {
/* Hide to remove buttons, if present */
this.hide();
if (this.#container != null && options.length >= 1) {
var titleDiv = this.#container.querySelector("#olympus-selection-scroll-top-bar")?.querySelector(".olympus-selection-scroll-title");
if (titleDiv)
titleDiv.innerHTML = title;
this.#container.style.display = this.#display;
this.#container.style.left = x - 110 + "px";
this.#container.style.top = y - 110 + "px";
this.#container.style.left = x - this.#container.offsetWidth / 2 + "px";
this.#container.style.top = y - 20 + "px";
var scroll = this.#container.querySelector(".olympus-selection-scroll");
if (scroll != null)
{
@@ -40,6 +43,20 @@ export class SelectionScroll {
scroll.appendChild(node);
}
}
/* Hide the coalition switch if required */
var switchContainer = <HTMLElement>this.#container.querySelector("#olympus-selection-scroll-top-bar")?.querySelector("#coalition-switch-container");
if (showCoalition == false) {
switchContainer.style.display = "none";
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--neutral-coalition-color"));
}
else {
switchContainer.style.display = "block";
if (getActiveCoalition() == "blue")
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--blue-coalition-color"));
else
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--red-coalition-color"));
}
}
}

View File

@@ -0,0 +1,83 @@
export class Slider {
#container: HTMLElement | null;
#callback: CallableFunction;
#slider: HTMLInputElement | null = null;
#value: HTMLElement | null = null;
#minValue: number;
#maxValue: number;
#minValueDiv: HTMLElement | null = null;
#maxValueDiv: HTMLElement | null = null;
#unit: string;
#display: string = "";
constructor(ID: string, minValue: number, maxValue: number, unit: string, callback: CallableFunction) {
this.#container = document.getElementById(ID);
this.#callback = callback;
this.#minValue = minValue;
this.#maxValue = maxValue;
this.#unit = unit;
if (this.#container != null) {
this.#display = this.#container.style.display;
this.#slider = <HTMLInputElement>this.#container.querySelector("input");
if (this.#slider != null)
{
this.#slider.addEventListener("input", (e: any) => this.#onInput());
this.#slider.addEventListener("mouseup", (e: any) => this.#onFinalize());
}
this.#value = <HTMLElement>this.#container.querySelector("#value");
}
}
#onValue()
{
if (this.#value != null && this.#slider != null)
this.#value.innerHTML = this.#minValue + Math.round(parseFloat(this.#slider.value) / 100 * (this.#maxValue - this.#minValue)) + this.#unit
this.setActive(true);
}
#onInput()
{
this.#onValue();
}
#onFinalize()
{
if (this.#slider != null)
this.#callback(this.#minValue + parseFloat(this.#slider.value) / 100 * (this.#maxValue - this.#minValue));
}
show()
{
if (this.#container != null)
this.#container.style.display = this.#display;
}
hide()
{
if (this.#container != null)
this.#container.style.display = 'none';
}
setActive(newActive: boolean)
{
if (this.#container)
{
this.#container.classList.toggle("active", newActive);
if (!newActive && this.#value != null)
this.#value.innerHTML = "Mixed values"
}
}
setMinMax(newMinValue: number, newMaxValue: number)
{
this.#minValue = newMinValue;
this.#maxValue = newMaxValue;
}
setValue(newValue: number)
{
if (this.#slider != null)
this.#slider.value = String((newValue - this.#minValue) / (this.#maxValue - this.#minValue) * 100);
this.#onValue()
}
}

View File

@@ -89,7 +89,7 @@ export function attackUnit(ID: number, targetID: number) {
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " attack " + getUnitsManager().getUnitByID(targetID).unitName);
//console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " attack " + getUnitsManager().getUnitByID(targetID).unitName);
}
};
@@ -99,29 +99,45 @@ export function attackUnit(ID: number, targetID: number) {
xhr.send(JSON.stringify(data));
}
export function cloneUnit(ID: number) {
export function cloneUnit(ID: number, latlng: L.LatLng) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned");
//console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned");
}
};
var command = { "ID": ID };
var command = { "ID": ID, "location": latlng };
var data = { "cloneUnit": command }
xhr.send(JSON.stringify(data));
}
export function landAt(ID: number, latlng: L.LatLng) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned");
}
};
var command = { "ID": ID, "location": latlng };
var data = { "landAt": command }
xhr.send(JSON.stringify(data));
}
export function changeSpeed(ID: number, speedChange: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
@@ -131,18 +147,98 @@ export function changeSpeed(ID: number, speedChange: string) {
xhr.send(JSON.stringify(data));
}
export function setSpeed(ID: number, speed: number) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "speed": speed}
var data = {"setSpeed": command}
xhr.send(JSON.stringify(data));
}
export function changeAltitude(ID: number, altitudeChange: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log(getUnitsManager().getUnitByID(ID).unitName + " altitude change request: " + altitudeChange);
//console.log(getUnitsManager().getUnitByID(ID).unitName + " altitude change request: " + altitudeChange);
}
};
var command = {"ID": ID, "change": altitudeChange}
var data = {"changeAltitude": command}
xhr.send(JSON.stringify(data));
}
export function setAltitude(ID: number, altitude: number) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "altitude": altitude}
var data = {"setAltitude": command}
xhr.send(JSON.stringify(data));
}
export function createFormation(ID: number, isLeader: boolean, wingmenIDs: number[]) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " created formation with: " + wingmenIDs);
}
};
var command = {"ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader}
var data = {"setLeader": command}
xhr.send(JSON.stringify(data));
}
export function setROE(ID: number, ROE: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "ROE": ROE}
var data = {"setROE": command}
xhr.send(JSON.stringify(data));
}
export function setReactionToThreat(ID: number, reactionToThreat: string) {
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
//console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange);
}
};
var command = {"ID": ID, "reactionToThreat": reactionToThreat}
var data = {"setReactionToThreat": command}
xhr.send(JSON.stringify(data));
}

View File

@@ -8,19 +8,25 @@ import { Dropdown } from "./controls/dropdown";
import { ConnectionStatusPanel } from "./panels/connectionstatuspanel";
import { Button } from "./controls/button";
import { MissionData } from "./missiondata/missiondata";
import { UnitControlPanel } from "./panels/unitcontrolpanel";
import { MouseInfoPanel } from "./panels/mouseInfoPanel";
import { Slider } from "./controls/slider";
/* TODO: should this be a class? */
var map: Map;
var selectionWheel: SelectionWheel;
var selectionScroll: SelectionScroll;
var unitsManager: UnitsManager;
var missionData: MissionData;
var unitInfoPanel: UnitInfoPanel;
var activeCoalition: string;
var connectionStatusPanel: ConnectionStatusPanel;
var unitControlPanel: UnitControlPanel;
var mouseInfoPanel: MouseInfoPanel;
var scenarioDropdown: Dropdown;
var mapSourceDropdown: Dropdown;
var connected: boolean;
var connectionStatusPanel: ConnectionStatusPanel;
var missionData: MissionData;
var slowButton: Button;
var fastButton: Button;
@@ -31,6 +37,12 @@ var aiVisibilityButton: Button;
var weaponVisibilityButton: Button;
var deadVisibilityButton: Button;
var altitudeSlider: Slider;
var airspeedSlider: Slider;
var connected: boolean;
var activeCoalition: string;
function setup() {
/* Initialize */
map = new Map('map-container');
@@ -38,9 +50,11 @@ function setup() {
selectionScroll = new SelectionScroll("selection-scroll");
unitsManager = new UnitsManager();
unitInfoPanel = new UnitInfoPanel("unit-info-panel");
unitControlPanel = new UnitControlPanel("unit-control-panel");
scenarioDropdown = new Dropdown("scenario-dropdown", ["Caucasus", "Marianas", "Nevada", "South Atlantic", "Syria", "The Channel"], () => { });
mapSourceDropdown = new Dropdown("map-source-dropdown", map.getLayers(), (option: string) => map.setLayer(option));
connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel");
mouseInfoPanel = new MouseInfoPanel("mouse-info-panel");
missionData = new MissionData();
/* Unit control buttons */
@@ -49,6 +63,10 @@ function setup() {
climbButton = new Button("climb-button", ["images/buttons/climb.svg"], () => { getUnitsManager().selectedUnitsChangeAltitude("climb"); });
descendButton = new Button("descend-button", ["images/buttons/descend.svg"], () => { getUnitsManager().selectedUnitsChangeAltitude("descend"); });
/* Unit control sliders */
altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => getUnitsManager().selectedUnitsSetAltitude(value * 0.3048));
airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => getUnitsManager().selectedUnitsSetSpeed(value / 1.94384));
/* Visibility buttons */
userVisibilityButton = new Button("user-visibility-button", ["images/buttons/user-full.svg", "images/buttons/user-partial.svg", "images/buttons/user-none.svg", "images/buttons/user-hidden.svg"], () => { });
aiVisibilityButton = new Button("ai-visibility-button", ["images/buttons/ai-full.svg", "images/buttons/ai-partial.svg", "images/buttons/ai-none.svg", "images/buttons/ai-hidden.svg"], () => { });
@@ -93,6 +111,10 @@ export function getMap() {
return map;
}
export function getMissionData() {
return missionData;
}
export function getSelectionWheel() {
return selectionWheel;
}
@@ -109,6 +131,14 @@ export function getUnitInfoPanel() {
return unitInfoPanel;
}
export function getUnitControlPanel() {
return unitControlPanel;
}
export function getMouseInfoPanel() {
return mouseInfoPanel;
}
export function setActiveCoalition(newActiveCoalition: string) {
activeCoalition = newActiveCoalition;
}
@@ -173,4 +203,12 @@ export function getVisibilitySettings() {
return visibility;
}
export function getVisibilityButtons() {
return {user: userVisibilityButton, ai: aiVisibilityButton, weapon: weaponVisibilityButton, dead: deadVisibilityButton}
}
export function getUnitControlSliders() {
return {altitude: altitudeSlider, airspeed: airspeedSlider}
}
window.onload = setup;

View File

@@ -45,7 +45,7 @@ export var BoxSelect = Handler.extend({
},
_onMouseDown: function (e: any) {
if (((e.which !== 3) && (e.button !== 2))) { return false; }
if (((e.which !== 1) && (e.button !== 0))) { return false; }
// Clear the deferred resetState if it hasn't executed yet, otherwise it
// will interrupt the interaction and orphan a box element in the container.
@@ -107,7 +107,7 @@ export var BoxSelect = Handler.extend({
},
_onMouseUp: function (e: any) {
if ((e.which !== 3) && (e.button !== 2)) { return; }
if ((e.which !== 1) && (e.button !== 0)) { return; }
this._finish();

View File

@@ -1,6 +1,7 @@
import * as L from "leaflet"
import { getSelectionWheel, getSelectionScroll, getUnitsManager, getActiveCoalition } from "..";
import { getSelectionWheel, getSelectionScroll, getUnitsManager, getActiveCoalition, getMouseInfoPanel } from "..";
import { spawnAircraft, spawnGroundUnit, spawnSmoke } from "../dcs/dcs";
import { bearing, distance, zeroAppend } from "../other/utils";
import { payloadNames } from "../units/payloadNames";
import { unitTypes } from "../units/unitTypes";
import { BoxSelect } from "./boxselect";
@@ -21,8 +22,14 @@ export interface SpawnEvent extends ClickEvent{
export class Map extends L.Map {
#state: string;
#layer?: L.TileLayer;
#preventRightClick: boolean = false;
#rightClickTimer: number = 0;
#preventLeftClick: boolean = false;
#leftClickTimer: number = 0;
#measurePoint: L.LatLng | null;
#measureIcon: L.Icon;
#measureMarker: L.Marker;
#measureLine: L.Polyline = new L.Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false });
#measureLineDiv: HTMLElement;
#lastMousePosition: L.Point = new L.Point(0, 0);
constructor(ID: string) {
/* Init the leaflet map */
@@ -34,12 +41,25 @@ export class Map extends L.Map {
/* Init the state machine */
this.#state = "IDLE";
this.#measurePoint = null;
this.#measureIcon = new L.Icon({ iconUrl: 'images/pin.png', iconAnchor: [16, 32]});
this.#measureMarker = new L.Marker([0, 0], {icon: this.#measureIcon, interactive: false});
this.#measureLineDiv = document.createElement("div");
this.#measureLineDiv.classList.add("measure-box");
this.#measureLineDiv.style.display = 'none';
document.body.appendChild(this.#measureLineDiv);
/* Register event handles */
this.on("click", (e: any) => this.#onClick(e));
this.on("dblclick", (e: any) => this.#onDoubleClick(e));
this.on("contextmenu", (e: any) => this.#onContextMenu(e));
this.on('selectionend', (e: any) => this.#onSelectionEnd(e));
this.on('mousedown', (e: any) => this.#onMouseDown(e));
this.on('mouseup', (e: any) => this.#onMouseUp(e));
this.on('mousemove', (e: any) => this.#onMouseMove(e));
this.on('zoom', (e: any) => this.#onZoom(e));
}
setLayer(layerName: string) {
@@ -124,63 +144,133 @@ export class Map extends L.Map {
}
/* Selection scroll */
showSelectionScroll(e: ClickEvent | SpawnEvent, options: any, callback: CallableFunction, showCoalition: boolean = false) {
showSelectionScroll(e: ClickEvent | SpawnEvent, title: string, options: any, callback: CallableFunction, showCoalition: boolean = false) {
var x = e.x;
var y = e.y;
getSelectionScroll().show(x, y, options, callback, showCoalition);
getSelectionScroll().show(x, y, title, options, callback, showCoalition);
}
hideSelectionScroll() {
getSelectionScroll().hide();
}
getMousePosition() {
return this.#lastMousePosition;
}
getMouseCoordinates() {
return this.containerPointToLatLng(this.#lastMousePosition);
}
/* Event handlers */
#onClick(e: any) {
this.hideSelectionWheel();
this.hideSelectionScroll();
if (this.#state === "IDLE") {
}
else if (this.#state === "MOVE_UNIT") {
if (!e.originalEvent.ctrlKey) {
getUnitsManager().clearDestinations();
if (!this.#preventLeftClick) {
this.hideSelectionWheel();
this.hideSelectionScroll();
if (this.#state === "IDLE") {
if (e.originalEvent.ctrlKey)
if (!this.#measurePoint)
{
this.#measurePoint = e.latlng;
this.#measureMarker.setLatLng(e.latlng);
this.#measureMarker.addTo(this);
}
else
{
this.#measurePoint = null;
if (this.hasLayer(this.#measureMarker))
this.removeLayer(this.#measureMarker);
}
}
getUnitsManager().addDestination(e.latlng)
}
}
#onDoubleClick(e: any) {
var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: null, coalitionID: null};
if (this.#state == "IDLE") {
var options = [
{ "tooltip": "Spawn air unit", "src": "spawnAir.png", "callback": () => this.#aircraftSpawnMenu(spawnEvent) },
{ "tooltip": "Spawn ground unit", "src": "spawnGround.png", "callback": () => this.#groundUnitSpawnMenu(spawnEvent) },
{ "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) },
//{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) }
]
this.showSelectionScroll(spawnEvent, options, () => {}, true);
}
}
#onContextMenu(e: any) {
this.#rightClickTimer = setTimeout(() => {
if (!this.#preventRightClick) {
else if (this.#state === "MOVE_UNIT") {
this.setState("IDLE");
getUnitsManager().deselectAllUnits();
this.hideSelectionWheel();
this.hideSelectionScroll();
}
this.#preventRightClick = false;
}, 200);
}
}
#onDoubleClick(e: any) {
}
#onContextMenu(e: any) {
this.hideSelectionWheel();
this.hideSelectionScroll();
if (this.#state === "IDLE") {
var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: null, coalitionID: null};
if (this.#state == "IDLE") {
var options = [
{ "tooltip": "Spawn air unit", "src": "spawnAir.png", "callback": () => this.#aircraftSpawnMenu(spawnEvent) },
{ "tooltip": "Spawn ground unit", "src": "spawnGround.png", "callback": () => this.#groundUnitSpawnMenu(spawnEvent) },
{ "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) },
//{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) }
]
this.showSelectionScroll(spawnEvent, "Action", options, () => {}, false);
}
}
else if (this.#state === "MOVE_UNIT") {
if (!e.originalEvent.ctrlKey) {
getUnitsManager().selectedUnitsClearDestinations();
}
getUnitsManager().selectedUnitsAddDestination(e.latlng)
}
}
#onSelectionEnd(e: any)
{
clearTimeout(this.#rightClickTimer);
this.#preventRightClick = true;
clearTimeout(this.#leftClickTimer);
this.#preventLeftClick = true;
this.#leftClickTimer = setTimeout(() => {
this.#preventLeftClick = false;
}, 200);
getUnitsManager().selectFromBounds(e.selectionBounds);
}
#onMouseDown(e: any)
{
if ((e.originalEvent.which == 1) && (e.originalEvent.button == 0))
{
this.dragging.disable();
}
}
#onMouseUp(e: any)
{
if ((e.originalEvent.which == 1) && (e.originalEvent.button == 0))
{
this.dragging.enable();
}
}
#onMouseMove(e: any)
{
var selectedUnitPosition = null;
var selectedUnits = getUnitsManager().getSelectedUnits();
if (selectedUnits && selectedUnits.length == 1)
{
selectedUnitPosition = new L.LatLng(selectedUnits[0].latitude, selectedUnits[0].longitude);
}
getMouseInfoPanel().update(<L.LatLng>e.latlng, this.#measurePoint, selectedUnitPosition);
this.#lastMousePosition.x = e.originalEvent.x;
this.#lastMousePosition.y = e.originalEvent.y;
if ( this.#measurePoint)
this.#drawMeasureLine();
else
this.#hideMeasureLine();
}
#onZoom(e: any)
{
if (this.#measurePoint)
this.#drawMeasureLine();
else
this.#hideMeasureLine();
}
/* Spawn from air base */
spawnFromAirbase(e: SpawnEvent)
{
@@ -198,7 +288,7 @@ export class Map extends L.Map {
{'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this.#selectGroundUnit(e, "Radar")},
{'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this.#selectGroundUnit(e, "Unarmed")}
]
this.showSelectionScroll(e, options, () => {}, true);
this.showSelectionScroll(e, "Spawn ground unit", options, () => {}, true);
}
#smokeSpawnMenu(e: SpawnEvent) {
@@ -211,7 +301,7 @@ export class Map extends L.Map {
{'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('green', e.latlng)}, 'tint': 'green'},
{'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'},
]
this.showSelectionScroll(e, options, () => {}, true);
this.showSelectionScroll(e, "Spawn smoke", options, () => {}, false);
}
#explosionSpawnMenu(e: SpawnEvent) {
@@ -228,7 +318,10 @@ export class Map extends L.Map {
{ 'coalition': true, 'tooltip': 'Drone', 'src': 'spawnDrone.png', 'callback': () => this.#selectAircraft(e, "drone") },
{ 'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png', 'callback': () => this.#selectAircraft(e, "transport") },
]
this.showSelectionScroll(e, options, () => {}, true);
if (e.airbaseName != null)
this.showSelectionScroll(e, "Spawn at " + e.airbaseName, options, () => {}, true);
else
this.showSelectionScroll(e, "Spawn air unit", options, () => {}, true);
}
/* Show unit selection for air units */
@@ -240,11 +333,11 @@ export class Map extends L.Map {
options.sort();
else
options = [];
this.showSelectionScroll(e, options, (unitType: string) => {
this.showSelectionScroll(e, "Select aircraft", options, (unitType: string) => {
this.hideSelectionWheel();
this.hideSelectionScroll();
this.#unitSelectPayload(e, unitType);
});
}, true);
}
/* Show weapon selection for air units */
@@ -255,11 +348,11 @@ export class Map extends L.Map {
options = payloadNames[unitType]
if (options != undefined && options.length > 0) {
options.sort();
this.showSelectionScroll({x: e.x, y: e.y, latlng: e.latlng}, options, (payloadName: string) => {
this.showSelectionScroll({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout", options, (payloadName: string) => {
this.hideSelectionWheel();
this.hideSelectionScroll();
spawnAircraft(unitType, e.latlng, getActiveCoalition(), payloadName, e.airbaseName);
});
}, true);
}
else {
spawnAircraft(unitType, e.latlng, getActiveCoalition());
@@ -273,10 +366,49 @@ export class Map extends L.Map {
this.hideSelectionScroll();
var options = unitTypes.vehicles[group];
options.sort();
this.showSelectionScroll(e, options, (unitType: string) => {
this.showSelectionScroll(e, "Select ground unit", options, (unitType: string) => {
this.hideSelectionWheel();
this.hideSelectionScroll();
spawnGroundUnit(unitType, e.latlng, getActiveCoalition());
});
}, true);
}
#drawMeasureLine()
{
var mouseLatLng = this.containerPointToLatLng(this.#lastMousePosition);
if (this.#measurePoint != null)
{
var points = [this.#measurePoint, mouseLatLng];
this.#measureLine.setLatLngs(points);
var dist = distance(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var bear = bearing(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
var startXY = this.latLngToContainerPoint(this.#measurePoint);
var dx = (this.#lastMousePosition.x - startXY.x);
var dy = (this.#lastMousePosition.y - startXY.y);
var angle = Math.atan2(dy, dx);
if (angle > Math.PI / 2)
angle = angle - Math.PI;
if (angle < -Math.PI / 2)
angle = angle + Math.PI;
this.#measureLineDiv.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
this.#measureLineDiv.style.left = (this.#lastMousePosition.x + startXY.x) / 2 - this.#measureLineDiv.offsetWidth / 2 + "px";
this.#measureLineDiv.style.top = (this.#lastMousePosition.y + startXY.y) / 2 - this.#measureLineDiv.offsetHeight / 2 + "px";
this.#measureLineDiv.style.rotate = angle + "rad";
this.#measureLineDiv.style.display = "";
}
if (!this.hasLayer(this.#measureLine))
this.#measureLine.addTo(this);
}
#hideMeasureLine()
{
this.#measureLineDiv.style.display = "none";
if (this.hasLayer(this.#measureLine))
this.removeLayer(this.#measureLine)
}
}

View File

@@ -1,41 +1,56 @@
import { Marker, LatLng } from "leaflet";
import { getMap } from "..";
import { Marker, LatLng, Icon } from "leaflet";
import { getMap, getUnitsManager } from "..";
import { SpawnEvent } from "../map/map";
import { AirbaseMarker } from "./airbasemarker";
var bullseyeIcons = [
new Icon({ iconUrl: 'images/bullseye0.png', iconAnchor: [30, 30]}),
new Icon({ iconUrl: 'images/bullseye1.png', iconAnchor: [30, 30]}),
new Icon({ iconUrl: 'images/bullseye2.png', iconAnchor: [30, 30]})
]
export class MissionData
{
//#bullseye : any; //TODO declare interface
//#bullseyeMarker : Marker;
#bullseyes : any; //TODO declare interface
#bullseyeMarkers: any;
#airbases : any; //TODO declare interface
#airbasesMarkers: {[name: string]: AirbaseMarker};
constructor()
{
//this.#bullseye = undefined;
//this.#bullseyeMarker = undefined;
this.#bullseyes = undefined;
this.#bullseyeMarkers = [
new Marker([0, 0], {icon: bullseyeIcons[0]}).addTo(getMap()),
new Marker([0, 0], {icon: bullseyeIcons[1]}).addTo(getMap()),
new Marker([0, 0], {icon: bullseyeIcons[2]}).addTo(getMap())
]
this.#airbasesMarkers = {};
}
update(data: any)
{
//this.#bullseye = data.missionData.bullseye;
this.#bullseyes = data.bullseye;
this.#airbases = data.airbases;
//this.#drawBullseye();
this.#drawAirbases();
if (this.#bullseyes != null && this.#airbases != null)
{
this.#drawBullseye();
this.#drawAirbases();
}
}
//#drawBullseye()
//{
// if (this.#bullseyeMarker === undefined)
// {
// this.#bullseyeMarker = new Marker([this.#bullseye.lat, this.#bullseye.lng]).addTo(map.getMap());
// }
// else
// {
// this.#bullseyeMarker.setLatLng(new LatLng(this.#bullseye.lat, this.#bullseye.lng));
// }
//}
getBullseyes()
{
return this.#bullseyes;
}
#drawBullseye()
{
for (let idx in this.#bullseyes)
{
var bullseye = this.#bullseyes[idx];
this.#bullseyeMarkers[idx].setLatLng(new LatLng(bullseye.lat, bullseye.lng));
}
}
#drawAirbases()
{
@@ -48,7 +63,7 @@ export class MissionData
position: new LatLng(airbase.lat, airbase.lng),
name: airbase.callsign,
src: "images/airbase.png"}).addTo(getMap());
this.#airbasesMarkers[idx].on('click', (e) => this.#onAirbaseClick(e));
this.#airbasesMarkers[idx].on('contextmenu', (e) => this.#onAirbaseClick(e));
}
else
{
@@ -59,7 +74,25 @@ export class MissionData
#onAirbaseClick(e: any)
{
var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: e.sourceTarget.getName(), coalitionID: e.sourceTarget.getCoalitionID()};
getMap().spawnFromAirbase(spawnEvent);
var options = [];
if (getUnitsManager().getSelectedUnits().length > 0)
options = ["Spawn unit", "Land here"];
else
options = ["Spawn unit"];
getMap().showSelectionScroll(e.originalEvent, e.sourceTarget.getName(), options, (option: string) => this.#onAirbaseOptionSelection(e, option), false);
}
#onAirbaseOptionSelection(e: any, option: string) {
if (option === "Spawn unit") {
var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: e.sourceTarget.getName(), coalitionID: e.sourceTarget.getCoalitionID()};
getMap().spawnFromAirbase(spawnEvent);
}
else if (option === "Land here")
{
getMap().hideSelectionWheel();
getMap().hideSelectionScroll();
getUnitsManager().selectedUnitsLandAt(e.latlng);
}
}
}

View File

@@ -26,7 +26,7 @@ export function bearing(lat1: number, lon1: number, lat2: number, lon2: number)
return brng;
}
const zeroPad = function (num: number, places: number) {
export const zeroPad = function (num: number, places: number) {
var string = String(num);
while (string.length < places) {
string += "0";
@@ -34,6 +34,14 @@ const zeroPad = function (num: number, places: number) {
return string;
}
export const zeroAppend = function (num: number, places: number) {
var string = String(num);
while (string.length < places) {
string = "0" + string;
}
return string;
}
export function ConvertDDToDMS(D: number, lng: boolean) {
var dir = D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N";
var deg = 0 | (D < 0 ? (D = -D) : D);

View File

@@ -0,0 +1,73 @@
import { LatLng } from "leaflet";
import { getMissionData } from "..";
import { distance, bearing, zeroPad, zeroAppend } from "../other/utils";
import { Unit } from "../units/unit";
export class MouseInfoPanel {
#element: HTMLElement
#display: string;
constructor(ID: string) {
this.#element = <HTMLElement>document.getElementById(ID);
this.#display = '';
if (this.#element != null) {
this.#display = this.#element.style.display;
var el = <HTMLElement>this.#element.querySelector(`#measure-position`);
this.show();
}
}
show() {
this.#element.style.display = this.#display;
}
hide() {
this.#element.style.display = "none";
}
update(mousePosition: LatLng, measurePosition: LatLng | null, unitPosition: LatLng | null) {
var bullseyes = getMissionData().getBullseyes();
for (let idx in bullseyes)
{
var dist = distance(bullseyes[idx].lat, bullseyes[idx].lng, mousePosition.lat, mousePosition.lng);
var bear = bearing(bullseyes[idx].lat, bullseyes[idx].lng, mousePosition.lat, mousePosition.lng);
var el = <HTMLElement>this.#element.querySelector(`#bullseye-${idx}`);
if (el != null)
el.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
}
if (measurePosition) {
var dist = distance(measurePosition.lat, measurePosition.lng, mousePosition.lat, mousePosition.lng);
var bear = bearing(measurePosition.lat, measurePosition.lng, mousePosition.lat, mousePosition.lng);
var el = <HTMLElement>this.#element.querySelector(`#measure-position`);
if (el != null)
{
el.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
if (el.parentElement != null)
el.parentElement.style.display = 'flex'; //TODO: don't like that its hardcoded
}
}
else {
var el = <HTMLElement>this.#element.querySelector(`#measure-position`);
if (el != null && el.parentElement != null)
el.parentElement.style.display = 'none';
}
if (unitPosition) {
var dist = distance(unitPosition.lat, unitPosition.lng, mousePosition.lat, mousePosition.lng);
var bear = bearing(unitPosition.lat, unitPosition.lng, mousePosition.lat, mousePosition.lng);
var el = <HTMLElement>this.#element.querySelector(`#unit-position`);
if (el != null)
{
el.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM`
if (el.parentElement != null)
el.parentElement.style.display = 'flex'; //TODO: don't like that its hardcoded
}
}
else {
var el = <HTMLElement>this.#element.querySelector(`#unit-position`);
if (el != null && el.parentElement != null)
el.parentElement.style.display = 'none';
}
}
}

View File

@@ -0,0 +1,363 @@
import { imageOverlay } from "leaflet";
import { getUnitControlSliders, getUnitsManager } from "..";
import { ConvertDDToDMS, rad2deg } from "../other/utils";
import { Aircraft, AirUnit, GroundUnit, Helicopter, NavyUnit, Unit } from "../units/unit";
export class UnitControlPanel {
#element: HTMLElement
#display: string;
constructor(ID: string) {
this.#element = <HTMLElement>document.getElementById(ID);
this.#display = '';
if (this.#element != null) {
this.#display = this.#element.style.display;
var formationCreationContainer = <HTMLElement>(this.#element.querySelector("#formation-creation-container"));
if (formationCreationContainer != null)
{
var createButton = <HTMLElement>formationCreationContainer.querySelector("#create-formation");
createButton?.addEventListener("click", () => getUnitsManager().selectedUnitsCreateFormation());
var undoButton = <HTMLElement>formationCreationContainer.querySelector("#undo-formation");
undoButton?.addEventListener("click", () => getUnitsManager().selectedUnitsUndoFormation());
}
var ROEButtonsContainer = <HTMLElement>(this.#element.querySelector("#roe-buttons-container"));
if (ROEButtonsContainer != null)
{
(<HTMLElement>ROEButtonsContainer.querySelector("#free"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Free"));
(<HTMLElement>ROEButtonsContainer.querySelector("#designated-free"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Designated free"));
(<HTMLElement>ROEButtonsContainer.querySelector("#designated"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Designated"));
(<HTMLElement>ROEButtonsContainer.querySelector("#return"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Return"));
(<HTMLElement>ROEButtonsContainer.querySelector("#hold"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Hold"));
}
var reactionToThreatButtonsContainer = <HTMLElement>(this.#element.querySelector("#reaction-to-threat-buttons-container"));
if (reactionToThreatButtonsContainer != null)
{
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#none"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("None"));
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#passive"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Passive"));
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#evade"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Evade"));
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#escape"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Escape"));
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#abort"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Abort"));
}
this.hide();
}
}
show() {
this.#element.style.display = this.#display;
}
hide() {
this.#element.style.display = "none";
}
update(units: Unit[]) {
if (this.#element != null)
{
var selectedUnitsContainer = <HTMLElement>(this.#element.querySelector("#selected-units-container"));
var formationCreationContainer = <HTMLElement>(this.#element.querySelector("#formation-creation-container"));
if (selectedUnitsContainer != null && formationCreationContainer != null)
{
this.#addUnitsButtons(units, selectedUnitsContainer);
this.#showFlightControlSliders(units);
this.#showFormationButtons(units, formationCreationContainer);
}
var ROEButtonsContainer = <HTMLElement>(this.#element.querySelector("#roe-buttons-container"));
if (ROEButtonsContainer != null)
{
(<HTMLElement>ROEButtonsContainer.querySelector("#free"))?.classList.toggle("white", this.#getROE(units) === "Free");
(<HTMLElement>ROEButtonsContainer.querySelector("#designated-free"))?.classList.toggle("white", this.#getROE(units) === "Designated free");
(<HTMLElement>ROEButtonsContainer.querySelector("#designated"))?.classList.toggle("white", this.#getROE(units) === "Designated");
(<HTMLElement>ROEButtonsContainer.querySelector("#return"))?.classList.toggle("white", this.#getROE(units) === "Return");
(<HTMLElement>ROEButtonsContainer.querySelector("#hold"))?.classList.toggle("white", this.#getROE(units) === "Hold");
}
var reactionToThreatButtonsContainer = <HTMLElement>(this.#element.querySelector("#reaction-to-threat-buttons-container"));
if (reactionToThreatButtonsContainer != null)
{
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#none"))?.classList.toggle("white", this.#getReactionToThreat(units) === "None");
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#passive"))?.classList.toggle("white", this.#getReactionToThreat(units) === "Passive");
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#evade"))?.classList.toggle("white", this.#getReactionToThreat(units) === "Evade");
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#escape"))?.classList.toggle("white", this.#getReactionToThreat(units) === "Escape");
(<HTMLElement>reactionToThreatButtonsContainer.querySelector("#abort"))?.classList.toggle("white", this.#getReactionToThreat(units) === "Abort");
}
}
}
#showFlightControlSliders(units: Unit[])
{
var sliders = getUnitControlSliders();
sliders.airspeed.show();
sliders.altitude.show();
if (this.#checkAllUnitsAircraft(units))
{
sliders.airspeed.setMinMax(100, 600);
sliders.altitude.setMinMax(0, 50000);
}
else if (this.#checkAllUnitsHelicopter(units))
{
sliders.airspeed.setMinMax(0, 200);
sliders.altitude.setMinMax(0, 10000);
}
else if (this.#checkAllUnitsGroundUnit(units))
{
sliders.airspeed.setMinMax(0, 60);
sliders.altitude.hide();
}
else if (this.#checkAllUnitsNavyUnit(units))
{
sliders.airspeed.setMinMax(0, 60);
sliders.altitude.hide();
}
else {
sliders.airspeed.hide();
sliders.altitude.hide();
}
var targetSpeed = this.#getTargetAirspeed(units);
if (targetSpeed != null)
{
sliders.airspeed.setActive(true);
sliders.airspeed.setValue(targetSpeed * 1.94384);
}
else
{
sliders.airspeed.setActive(false);
}
var targetAltitude = this.#getTargetAltitude(units);
if (targetAltitude != null)
{
sliders.altitude.setActive(true);
sliders.altitude.setValue(targetAltitude / 0.3048);
}
else
{
sliders.altitude.setActive(false);
}
}
#addUnitsButtons(units: Unit[], selectedUnitsContainer: HTMLElement)
{
/* Remove any pre-existing unit button */
var elements = selectedUnitsContainer.getElementsByClassName("js-unit-container");
while (elements.length > 0)
selectedUnitsContainer.removeChild(elements[0])
/* Create all the units buttons */
for (let unit of units)
{
this.#addUnitButton(unit, selectedUnitsContainer);
if (unit.isLeader)
for (let wingman of unit.getWingmen())
this.#addUnitButton(wingman, selectedUnitsContainer);
}
}
#addUnitButton(unit: Unit, container: HTMLElement)
{
var el = document.createElement("div");
/* Unit name (actually type, but DCS calls it name for some reason) */
var nameDiv = document.createElement("div");
nameDiv.classList.add("rounded-container-small");
if (unit.name.length >= 7)
nameDiv.innerHTML = `${unit.name.substring(0, 4)} ...`;
else
nameDiv.innerHTML = `${unit.name}`;
/* Unit icon */
var icon = document.createElement("img");
if (unit.isLeader)
icon.src = "images/icons/formation.png"
else if (unit.isWingman)
{
var wingmen = unit.getLeader()?.getWingmen();
if (wingmen && wingmen.lastIndexOf(unit) == wingmen.length - 1)
icon.src = "images/icons/formation-end.svg"
else
icon.src = "images/icons/formation-middle.svg"
}
else
icon.src = "images/icons/singleton.png"
el.innerHTML = unit.unitName;
el.prepend(nameDiv);
/* Show the icon only for air units */
if ((unit instanceof AirUnit))
el.append(icon);
el.classList.add("rounded-container", "js-unit-container");
if (!unit.getSelected())
el.classList.add("not-selected")
/* Set background color */
if (unit.coalitionID == 1)
{
el.classList.add("red");
icon.classList.add("red");
}
else if (unit.coalitionID == 2)
{
el.classList.add("blue");
icon.classList.add("blue");
}
else
{
el.classList.add("neutral");
icon.classList.add("neutral");
}
el.addEventListener("click", () => getUnitsManager().selectUnit(unit.ID));
container.appendChild(el);
}
#showFormationButtons(units: Unit[], formationCreationContainer: HTMLElement)
{
var createButton = <HTMLElement>formationCreationContainer.querySelector("#create-formation");
var undoButton = <HTMLElement>formationCreationContainer.querySelector("#undo-formation");
if (createButton && undoButton && this.#checkAllUnitsAir(units))
{
if (!this.#checkUnitsAlreadyInFormation(units))
{
createButton.style.display = '';
undoButton.style.display = 'none';
}
else if (this.#checkUnitsAlreadyInFormation(units) && this.#checkAllUnitsSameFormation(units))
{
createButton.style.display = 'none';
undoButton.style.display = '';
}
else
{
createButton.style.display = 'none';
undoButton.style.display = 'none';
}
}
}
#checkAllUnitsAir(units: Unit[])
{
for (let unit of units)
if (!(unit instanceof AirUnit))
return false
return true
}
#checkAllUnitsAircraft(units: Unit[])
{
for (let unit of units)
if (!(unit instanceof Aircraft))
return false
return true
}
#checkAllUnitsHelicopter(units: Unit[])
{
for (let unit of units)
if (!(unit instanceof Helicopter))
return false
return true
}
#checkAllUnitsGroundUnit(units: Unit[])
{
for (let unit of units)
if (!(unit instanceof GroundUnit))
return false
return true
}
#checkAllUnitsNavyUnit(units: Unit[])
{
for (let unit of units)
if (!(unit instanceof NavyUnit))
return false
return true
}
#checkAllUnitsSameFormation(units: Unit[])
{
var leaderFound = false;
for (let unit of units)
{
if (unit.isLeader)
{
if (leaderFound)
return false
else
leaderFound = true;
}
if (!unit.isLeader)
return false
}
return true
}
#checkUnitsAlreadyInFormation(units: Unit[])
{
for (let unit of units)
if (unit.isLeader)
return true
return false
}
#getTargetAirspeed(units: Unit[])
{
var airspeed = null;
for (let unit of units)
{
if (unit.targetSpeed != airspeed && airspeed != null)
return null
else
airspeed = unit.targetSpeed;
}
return airspeed;
}
#getTargetAltitude(units: Unit[])
{
var altitude = null;
for (let unit of units)
{
if (unit.targetAltitude != altitude && altitude != null)
return null
else
altitude = unit.targetAltitude;
}
return altitude;
}
#getROE(units: Unit[])
{
var ROE = null;
for (let unit of units)
{
if (unit.ROE !== ROE && ROE != null)
return null
else
ROE = unit.ROE;
}
return ROE;
}
#getReactionToThreat(units: Unit[])
{
var reactionToThreat = null;
for (let unit of units)
{
if (unit.reactionToThreat !== reactionToThreat && reactionToThreat != null)
return null
else
reactionToThreat = unit.reactionToThreat;
}
return reactionToThreat;
}
}

View File

@@ -48,6 +48,15 @@ export class UnitInfoPanel {
this.#element.querySelector("#latitude")!.innerHTML = ConvertDDToDMS(unit.latitude, false);
this.#element.querySelector("#longitude")!.innerHTML = ConvertDDToDMS(unit.longitude, true);
this.#element.querySelector("#task")!.innerHTML = unit.currentTask !== ""? unit.currentTask: "Not controlled";
this.#element.querySelector("#task")!.classList.remove("red", "blue", "neutral");
if (unit.coalitionID == 1)
this.#element.querySelector("#task")!.classList.add("red");
else if (unit.coalitionID == 2)
this.#element.querySelector("#task")!.classList.add("blue");
else
this.#element.querySelector("#task")!.classList.add("neutral");
}
}
}

View File

@@ -2,7 +2,7 @@ import { Marker, LatLng, Polyline, Icon } from 'leaflet';
import { ConvertDDToDMS } from '../other/utils';
import { getMap, getUnitsManager, getVisibilitySettings } from '..';
import { UnitMarker, MarkerOptions, AircraftMarker, HelicopterMarker, GroundUnitMarker, NavyUnitMarker, WeaponMarker } from './unitmarker';
import { addDestination, attackUnit, changeAltitude, changeSpeed } from '../dcs/dcs';
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, landAt, setAltitude, setReactionToThreat, setROE, setSpeed } from '../dcs/dcs';
var pathIcon = new Icon({
iconUrl: 'images/marker-icon.png',
@@ -12,9 +12,6 @@ var pathIcon = new Icon({
export class Unit {
ID: number = -1;
leader: boolean = false;
wingman: boolean = false;
wingmen: Unit[] = [];
formation: string = "";
name: string = "";
unitName: string = "";
@@ -33,7 +30,17 @@ export class Unit {
activePath: any = null;
ammo: any = null;
targets: any = null;
hasTask: boolean = false;
isLeader: boolean = false;
isWingman: boolean = false;
leaderID: number = 0;
wingmen: Unit[] = [];
wingmenIDs: number[] = [];
targetSpeed: number = 0;
targetAltitude: number = 0;
ROE: string = "";
reactionToThreat: string = "";
#selectable: boolean;
#selected: boolean = false;
#preventClick: boolean = false;
@@ -61,42 +68,36 @@ export class Unit {
this.#marker = marker;
this.#marker.on('click', (e) => this.#onClick(e));
this.#marker.on('dblclick', (e) => this.#onDoubleClick(e));
this.#marker.on('contextmenu', (e) => this.#onContextMenu(e));
this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 });
this.#pathPolyline.addTo(getMap());
this.#targetsPolylines = [];
}
update(response: JSON) {
update(response: any) {
for (let entry in response) {
// @ts-ignore
// @ts-ignore TODO handle better
this[entry] = response[entry];
}
// TODO handle better
if (response['activePath'] == undefined)
this.activePath = null
/* Dead units can't be selected */
this.setSelected(this.getSelected() && this.alive)
this.#updateMarker();
this.#clearTargets();
if (this.getSelected())
if (this.getSelected() && this.activePath != null)
{
this.#drawPath();
this.#drawTargets();
}
else
this.#clearPath();
/*
this.wingmen = [];
if (response["wingmenIDs"] != null)
{
for (let ID of response["wingmenIDs"])
{
this.wingmen.push(unitsManager.getUnitByID(ID));
}
}
*/
this.#clearPath();
}
setSelected(selected: boolean) {
@@ -105,6 +106,7 @@ export class Unit {
this.#selected = selected;
this.#marker.setSelected(selected);
getUnitsManager().onUnitSelection();
}
}
@@ -140,6 +142,28 @@ export class Unit {
return false;
}
getLeader() {
return getUnitsManager().getUnitByID(this.leaderID);
}
getFormation() {
return [<Unit>this].concat(this.getWingmen())
}
getWingmen() {
var wingmen: Unit[] = [];
if (this.wingmenIDs != null)
{
for (let ID of this.wingmenIDs)
{
var unit = getUnitsManager().getUnitByID(ID)
if (unit)
wingmen.push(unit);
}
}
return wingmen;
}
#onClick(e: any) {
this.#timer = setTimeout(() => {
if (!this.#preventClick) {
@@ -157,22 +181,21 @@ export class Unit {
#onDoubleClick(e: any) {
clearTimeout(this.#timer);
this.#preventClick = true;
}
#onContextMenu(e: any) {
var options = [
'Attack',
'Follow'
]
//if (!this.leader && !this.wingman) {
// options.push({ 'tooltip': 'Create formation', 'src': 'formation.png', 'callback': () => { getMap().hideSelectionWheel(); /*unitsManager.createFormation(this.ID);*/ } });
//}
getMap().showSelectionScroll(e.originalEvent, options, (action: string) => this.#executeAction(action));
getMap().showSelectionScroll(e.originalEvent, "Action: " + this.unitName, options, (action: string) => this.#executeAction(action));
}
#executeAction(action: string) {
getMap().hideSelectionScroll();
if (action === "Attack")
getUnitsManager().attackUnit(this.ID);
getUnitsManager().selectedUnitsAttackUnit(this.ID);
}
#updateMarker() {
@@ -199,8 +222,8 @@ export class Unit {
#drawPath() {
if (this.activePath != null) {
var _points = [];
_points.push(new LatLng(this.latitude, this.longitude));
var points = [];
points.push(new LatLng(this.latitude, this.longitude));
/* Add markers if missing */
while (this.#pathMarkers.length < Object.keys(this.activePath).length) {
@@ -218,8 +241,8 @@ export class Unit {
for (let WP in this.activePath) {
var destination = this.activePath[WP];
this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]);
_points.push(new LatLng(destination.lat, destination.lng));
this.#pathPolyline.setLatLngs(_points);
points.push(new LatLng(destination.lat, destination.lng));
this.#pathPolyline.setLatLngs(points);
}
}
}
@@ -232,7 +255,6 @@ export class Unit {
this.#pathPolyline.setLatLngs([]);
}
#drawTargets()
{
for (let typeIndex in this.targets)
@@ -278,7 +300,6 @@ export class Unit {
}
}
attackUnit(targetID: number) {
/* Call DCS attackUnit function */
if (this.ID != targetID) {
@@ -289,7 +310,11 @@ export class Unit {
}
}
landAt(latlng: LatLng)
{
landAt(this.ID, latlng);
}
changeSpeed(speedChange: string)
{
changeSpeed(this.ID, speedChange);
@@ -300,6 +325,26 @@ export class Unit {
changeAltitude(this.ID, altitudeChange);
}
setSpeed(speed: number)
{
setSpeed(this.ID, speed);
}
setAltitude(altitude: number)
{
setAltitude(this.ID, altitude);
}
setROE(ROE: string)
{
setROE(this.ID, ROE);
}
setReactionToThreat(reactionToThreat: string)
{
setReactionToThreat(this.ID, reactionToThreat);
}
/*
setformation(formation)
{
@@ -318,25 +363,13 @@ export class Unit {
xhr.send(JSON.stringify(data));
}
setLeader(wingmenIDs)
{
// TODO move in dedicated file
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log(this.unitName + " created formation with: " + wingmenIDs);
}
};
var command = {"ID": this.ID, "wingmenIDs": wingmenIDs}
var data = {"setLeader": command}
xhr.send(JSON.stringify(data));
}
*/
setLeader(isLeader: boolean, wingmenIDs: number[] = [])
{
setLeader(this.ID, isLeader, wingmenIDs);
}
}
export class AirUnit extends Unit {

View File

@@ -1,6 +1,7 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getMap, getUnitInfoPanel } from "..";
import { getMap, getUnitControlPanel, getUnitInfoPanel } from "..";
import { Unit, GroundUnit } from "./unit";
import { cloneUnit } from "../dcs/dcs";
export class UnitsManager {
#units: { [ID: number]: Unit };
@@ -9,6 +10,24 @@ export class UnitsManager {
constructor() {
this.#units = {};
this.#copiedUnits = [];
document.addEventListener('copy', () => this.copyUnits());
document.addEventListener('paste', () => this.pasteUnits());
}
#updateUnitControlPanel() {
/* Update the unit control panel */
if (this.getSelectedUnits().length > 0) {
getUnitControlPanel().show();
getUnitControlPanel().update(this.getSelectedLeaders().concat(this.getSelectedSingletons()));
}
else {
getUnitControlPanel().hide();
}
}
getUnits() {
return this.#units;
}
addUnit(ID: number, data: any) {
@@ -27,7 +46,10 @@ export class UnitsManager {
}
getUnitByID(ID: number) {
return this.#units[ID];
if (ID in this.#units)
return this.#units[ID];
else
return null;
}
removeUnit(ID: number) {
@@ -40,6 +62,13 @@ export class UnitsManager {
}
}
selectUnit(ID: number, deselectAllUnits: boolean = true)
{
if (deselectAllUnits)
this.deselectAllUnits();
this.#units[ID]?.setSelected(true);
}
update(data: any) {
for (let ID in data["units"]) {
/* Create the unit if missing from the local array, then update the data. Drawing is handled by leaflet. */
@@ -49,6 +78,7 @@ export class UnitsManager {
this.#units[parseInt(ID)].update(data["units"][ID]);
}
/* Update the unit info panel */
if (this.getSelectedUnits().length == 1) {
getUnitInfoPanel().show();
getUnitInfoPanel().update(this.getSelectedUnits()[0]);
@@ -67,6 +97,8 @@ export class UnitsManager {
getMap().setState("IDLE");
//unitControlPanel.setEnabled(false);
}
this.#updateUnitControlPanel();
}
selectFromBounds(bounds: LatLngBounds)
@@ -92,7 +124,35 @@ export class UnitsManager {
return selectedUnits;
}
addDestination(latlng: L.LatLng) {
getSelectedLeaders() {
var leaders: Unit[] = [];
for (let idx in this.getSelectedUnits())
{
var unit = this.getSelectedUnits()[idx];
if (unit.isLeader)
leaders.push(unit);
else if (unit.isWingman)
{
var leader = unit.getLeader();
if (leader && !leaders.includes(leader))
leaders.push(leader);
}
}
return leaders;
}
getSelectedSingletons() {
var singletons: Unit[] = [];
for (let idx in this.getSelectedUnits())
{
var unit = this.getSelectedUnits()[idx];
if (!unit.isLeader && !unit.isWingman)
singletons.push(unit);
}
return singletons;
}
selectedUnitsAddDestination(latlng: L.LatLng) {
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits) {
var commandedUnit = selectedUnits[idx];
@@ -104,7 +164,7 @@ export class UnitsManager {
}
}
clearDestinations() {
selectedUnitsClearDestinations() {
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits) {
var commandedUnit = selectedUnits[idx];
@@ -116,6 +176,15 @@ export class UnitsManager {
}
}
selectedUnitsLandAt(latlng: LatLng)
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].landAt(latlng);
}
}
selectedUnitsChangeSpeed(speedChange: string)
{
var selectedUnits = this.getSelectedUnits();
@@ -123,6 +192,8 @@ export class UnitsManager {
{
selectedUnits[idx].changeSpeed(speedChange);
}
setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail
}
selectedUnitsChangeAltitude(altitudeChange: string)
@@ -132,35 +203,66 @@ export class UnitsManager {
{
selectedUnits[idx].changeAltitude(altitudeChange);
}
setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail
}
// handleKeyEvent(e)
// {
// if (e.originalEvent.code === 'KeyC' && e.originalEvent.ctrlKey)
// {
// this.copyUnits();
// }
// else if (e.originalEvent.code === 'KeyV' && e.originalEvent.ctrlKey)
// {
// this.pasteUnits();
// }
// }
selectedUnitsSetSpeed(speed: number)
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].setSpeed(speed);
}
}
// copyUnits()
// {
// this.#copiedUnits = this.getSelectedUnits();
// }
selectedUnitsSetAltitude(altitude: number)
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].setAltitude(altitude);
}
}
// pasteUnits()
// {
// for (let idx in this.#copiedUnits)
// {
// var unit = this.#copiedUnits[idx];
// cloneUnit(unit.ID);
// }
// }
selectedUnitsSetROE(ROE: string)
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].setROE(ROE);
}
attackUnit(ID: number) {
setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail
}
selectedUnitsSetReactionToThreat(reactionToThreat: string)
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
selectedUnits[idx].setReactionToThreat(reactionToThreat);
}
setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail
}
copyUnits()
{
this.#copiedUnits = this.getSelectedUnits();
}
pasteUnits()
{
for (let idx in this.#copiedUnits)
{
var unit = this.#copiedUnits[idx];
cloneUnit(unit.ID, getMap().getMouseCoordinates());
}
}
selectedUnitsAttackUnit(ID: number) {
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits) {
/* If a unit is a wingman, send the command to its leader */
@@ -173,59 +275,59 @@ export class UnitsManager {
}
}
// createFormation(ID)
// {
// var selectedUnits = this.getSelectedUnits();
// var wingmenIDs = [];
// for (let idx in selectedUnits)
// {
// if (selectedUnits[idx].wingman)
// {
// showMessage(selectedUnits[idx].unitName + " is already in a formation.");
// return;
// }
// else if (selectedUnits[idx].leader)
// {
// showMessage(selectedUnits[idx].unitName + " is already in a formation.");
// return;
// }
// else
// {
// /* TODO
// if (selectedUnits[idx].category !== this.getUnitByID(ID).category)
// {
// showMessage("All units must be of the same category to create a formation.");
// }
// */
// if (selectedUnits[idx].ID != ID)
// {
// wingmenIDs.push(selectedUnits[idx].ID);
// }
// }
// }
// if (wingmenIDs.length > 0)
// {
// this.getUnitByID(ID).setLeader(wingmenIDs);
// }
// else
// {
// showMessage("At least 2 units must be selected to create a formation.");
// }
// }
selectedUnitsCreateFormation(ID: number | null = null)
{
var selectedUnits = this.getSelectedUnits();
if (selectedUnits.length >= 2)
{
if (ID == null)
ID = selectedUnits[0].ID
var wingmenIDs = [];
for (let idx in selectedUnits)
{
if (selectedUnits[idx].isWingman)
{
console.log(selectedUnits[idx].unitName + " is already in a formation.");
return;
}
else if (selectedUnits[idx].isLeader)
{
console.log(selectedUnits[idx].unitName + " is already in a formation.");
return;
}
else
{
/* TODO
if (selectedUnits[idx].category !== this.getUnitByID(ID).category)
{
showMessage("All units must be of the same category to create a formation.");
}
*/
if (selectedUnits[idx].ID != ID)
{
wingmenIDs.push(selectedUnits[idx].ID);
}
}
}
if (wingmenIDs.length > 0)
{
this.getUnitByID(ID)?.setLeader(true, wingmenIDs);
}
else
{
console.log("At least 2 units must be selected to create a formation.");
}
}
setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail
}
// getLeader(ID)
// {
// for (let idx in this.#units)
// {
// var unit = this.#units[idx];
// if (unit.leader)
// {
// if (unit.wingmen.includes(this.getUnitByID(ID)))
// {
// return unit;
// }
// }
// }
// showMessage("Error: no leader found for this unit")
// }
selectedUnitsUndoFormation(ID: number | null = null)
{
for (let leader of this.getSelectedLeaders())
{
leader.setLeader(false);
}
setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail
}
}

View File

@@ -30,6 +30,7 @@
<%- include('unitcontrolpanel.ejs') %>
<%- include('visibilitycontrolpanel.ejs') %>
<%- include('connectionstatuspanel.ejs') %>
<%- include('mouseinfopanel.ejs') %>
<script src="javascripts/bundle.js"></script>
</body>

View File

@@ -0,0 +1,6 @@
<div class="olympus-panel" id="mouse-info-panel">
<div id="measure-position-container" class="rectangular-container"><img src="images/pin.png"><div id="measure-position">---° / --- NM</div></div>
<div id="unit-position-container" class="rectangular-container"><img src="images/unit.png"><div id="unit-position">---° / --- NM</div></div>
<div class="rectangular-container"><img src="images/BEBlue.png"><div id="bullseye-2">---° / --- NM</div></div>
<div class="rectangular-container"><img src="images/BERed.png"><div id="bullseye-1">---° / --- NM</div></div>
</div>

View File

@@ -1,8 +1,11 @@
<div id="selection-scroll" class="olympus-selection-scroll-container">
<label id="coalition-switch-container">
<input type="checkbox" id="coalition-switch"> <span
class="olympus-selection-scroll-slider olympus-selection-scroll-switch"></span>
</label>
<div id="olympus-selection-scroll-top-bar">
<div class="olympus-selection-scroll-title"></div>
<label id="coalition-switch-container">
<input type="checkbox" id="coalition-switch"> <span
class="olympus-selection-scroll-slider olympus-selection-scroll-switch"></span>
</label>
</div>
<div class="olympus-selection-scroll">
</div>
</div>

View File

@@ -1,12 +1,65 @@
<div class="olympus-panel" id="unit-control-buttons">
<div class="olympus-button" id="slow-button"></div>
<div class="olympus-button" id="fast-button"></div>
<div class="olympus-button" id="descend-button"></div>
<div class="olympus-button" id="climb-button"></div>
</div>
<div class="olympus-panel" id="unit-control-panel">
<div class="olympus-panel" id="unit-control-buttons">
<div class="olympus-button" id="slow-button"></div>
<div class="olympus-button" id="fast-button"></div>
<div class="olympus-button" id="descend-button"></div>
<div class="olympus-button" id="climb-button"></div>
</div>
<div id="title-label">Selected units</div>
<div class="rounded-container">Olympus-1</div>
<div class="rounded-container">Olympus-2</div>
<div id="selected-units-container">
<!-- This is where all the unit selection buttons will be shown-->
</div>
<div id="formation-creation-container">
<div class="rectangular-button white" id="create-formation"><img src="images\buttons\create.svg">Create formation</div>
<div class="rectangular-button white" id="undo-formation"><img src="images\buttons\erase.svg">Undo formation</div>
</div>
<div class="hl"></div>
<div id="section-label">Controls</div>
<div id="flight-controls-buttons-container">
<div class="slider-container flight-control-slider" id="altitude-slider">
<div class="flight-control-title">Altitude</div>
<div class="flight-control-value" id="value"></div>
<input type="range" min="1" max="100" value="50" class="slider">
</div>
<div class="slider-container flight-control-slider" id="airspeed-slider">
<div class="flight-control-title">Speed</div>
<div class="flight-control-value" id="value"></div>
<input type="range" min="1" max="100" value="50" class="slider">
</div>
</div>
<div id="section-label">Formation</div>
<div id="formation-buttons-container">
<div class="rectangular-button">Echelon</div>
<div class="rectangular-button">Fingertip</div>
<div class="rectangular-button">Trail</div>
<div class="rectangular-button">Line abreast</div>
</div>
<div class="hl"></div>
<div id="section-label">Rules of engagement</div>
<div id="roe-buttons-container">
<div class="rectangular-button" id="free">Free</div>
<div class="rectangular-button" id="designated-free">Designated free</div>
<div class="rectangular-button" id="designated">Designated</div>
<div class="rectangular-button" id="return">Return</div>
<div class="rectangular-button" id="hold">Hold</div>
</div>
<div class="hl"></div>
<div id="section-label">Reaction to threat</div>
<div id="reaction-to-threat-buttons-container">
<div class="rectangular-button" id="none">None</div>
<div class="rectangular-button" id="passive">Passive</div>
<div class="rectangular-button" id="evade">Evade</div>
<div class="rectangular-button" id="escape">Escape</div>
<div class="rectangular-button" id="abort">Abort</div>
</div>
</div>