Fixed conflicts, ordered maps.
1
.gitignore
vendored
@ -10,3 +10,4 @@ server
|
||||
node_modules
|
||||
/client/TODO.txt
|
||||
/client/public/javascripts/bundle.js
|
||||
!client/bin
|
||||
@ -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
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
BIN
client/public/images/BEBlue.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/public/images/BERed.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
client/public/images/bullseye.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/public/images/bullseye.xcf
Normal file
BIN
client/public/images/bullseye0.png
Normal file
|
After Width: | Height: | Size: 4.4 KiB |
BIN
client/public/images/bullseye1.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
client/public/images/bullseye2.png
Normal file
|
After Width: | Height: | Size: 4.3 KiB |
1
client/public/images/buttons/create.svg
Normal 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 |
1
client/public/images/buttons/erase.svg
Normal 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 |
54
client/public/images/icons/formation-end.svg
Normal 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 |
54
client/public/images/icons/formation-middle.svg
Normal 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 |
BIN
client/public/images/icons/formation.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
client/public/images/icons/leader.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
1
client/public/images/icons/ruler.svg
Normal 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 |
BIN
client/public/images/icons/singleton.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
client/public/images/pin.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
1
client/public/images/pin.svg
Normal 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 |
BIN
client/public/images/unit.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
0
client/public/javascripts/placeholder
Normal 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;
|
||||
|
||||
31
client/public/stylesheets/mouseinfopanel.css
Normal 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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
47
client/public/stylesheets/slider.css
Normal 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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
83
client/src/controls/slider.ts
Normal 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()
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
@ -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;
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
|
||||
73
client/src/panels/mouseInfoPanel.ts
Normal 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';
|
||||
}
|
||||
}
|
||||
}
|
||||
363
client/src/panels/unitcontrolpanel.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
@ -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");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -30,6 +30,7 @@
|
||||
<%- include('unitcontrolpanel.ejs') %>
|
||||
<%- include('visibilitycontrolpanel.ejs') %>
|
||||
<%- include('connectionstatuspanel.ejs') %>
|
||||
<%- include('mouseinfopanel.ejs') %>
|
||||
<script src="javascripts/bundle.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
6
client/views/mouseinfopanel.ejs
Normal 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>
|
||||
@ -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>
|
||||
@ -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>
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
[Setup]
|
||||
AppName=DCS Olympus
|
||||
AppVerName=DCS Olympus Alpha v0.0.2
|
||||
AppVerName=DCS Olympus Alpha v0.0.5
|
||||
DefaultDirName={usersavedgames}\DCS.openbeta
|
||||
DefaultGroupName=DCSOlympus
|
||||
OutputBaseFilename=DCSOlympus
|
||||
|
||||
@ -37,10 +37,10 @@ function Olympus.getCoalitionByCoalitionID(coalitionID)
|
||||
end
|
||||
|
||||
-- Builds a valid task depending on the provided options
|
||||
function Olympus.buildTask(options)
|
||||
function Olympus.buildEnrouteTask(options)
|
||||
local task = nil
|
||||
-- Engage specific target by ID. Checks if target exists.
|
||||
if options['id'] == 'EngageUnit' and options['targetID'] ~= nil then
|
||||
if options['id'] == 'EngageUnit' and options['targetID'] then
|
||||
local target = Olympus.getUnitByID(options['targetID'])
|
||||
if target and target:isExist() then
|
||||
task = {
|
||||
@ -54,25 +54,56 @@ function Olympus.buildTask(options)
|
||||
return task
|
||||
end
|
||||
|
||||
-- Move a unit. Since most tasks in DCS are Enroute tasks, this function is the main way to control the unit AI
|
||||
-- Builds a valid task depending on the provided options
|
||||
function Olympus.buildTask(options)
|
||||
local task = nil
|
||||
-- Engage specific target by ID. Checks if target exists.
|
||||
if options['id'] == 'FollowUnit' and options['leaderID'] and options['offset'] then
|
||||
local leader = Olympus.getUnitByID(options['leaderID'])
|
||||
if leader and leader:isExist() then
|
||||
task = {
|
||||
id = 'Follow',
|
||||
params = {
|
||||
groupId = leader:getGroup():getID(),
|
||||
pos = options['offset'],
|
||||
lastWptIndexFlag = false,
|
||||
lastWptIndex = 1
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
return task
|
||||
end
|
||||
|
||||
-- Move a unit. Since many tasks in DCS are Enroute tasks, this function is an important way to control the unit AI
|
||||
function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions)
|
||||
Olympus.notify("Olympus.move " .. ID .. " (" .. lat .. ", " .. lng ..") " .. altitude .. "m " .. speed .. "m/s " .. category, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
if category == "Aircraft" then
|
||||
local startPoint = mist.getLeadPos(unit:getGroup())
|
||||
local endPoint = coord.LLtoLO(lat, lng, 0)
|
||||
local endPoint = coord.LLtoLO(lat, lng, 0)
|
||||
|
||||
local path = {
|
||||
[1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'),
|
||||
[2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
||||
}
|
||||
local path = {}
|
||||
if taskOptions and taskOptions['id'] == 'Land' then
|
||||
path = {
|
||||
[1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'),
|
||||
[2] = mist.fixedWing.buildWP(endPoint, landing, speed, 0, 'AGL')
|
||||
}
|
||||
else
|
||||
path = {
|
||||
[1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'),
|
||||
[2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO')
|
||||
}
|
||||
end
|
||||
|
||||
-- If a task exists assign it to the controller
|
||||
local task = Olympus.buildTask(taskOptions)
|
||||
if task then
|
||||
path[1].task = task
|
||||
path[2].task = task
|
||||
if taskOptions then
|
||||
local task = Olympus.buildEnrouteTask(taskOptions)
|
||||
if task then
|
||||
path[1].task = task
|
||||
path[2].task = task
|
||||
end
|
||||
end
|
||||
|
||||
-- Assign the mission task to the controller
|
||||
@ -171,7 +202,7 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions)
|
||||
local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0))
|
||||
|
||||
if payload == nil then
|
||||
if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] ~= nil then
|
||||
if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] then
|
||||
payload = Olympus.unitPayloads[unitType][payloadName]
|
||||
else
|
||||
payload = {}
|
||||
@ -250,7 +281,7 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions)
|
||||
task = 'CAP',
|
||||
}
|
||||
|
||||
mist.dynAdd(vars)
|
||||
local newGroup = mist.dynAdd(vars)
|
||||
|
||||
-- Save the payload to be reused in case the unit is cloned. TODO: save by ID not by name (it works but I like consistency)
|
||||
Olympus.payloadRegistry[vars.name] = payload
|
||||
@ -259,40 +290,99 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions)
|
||||
end
|
||||
|
||||
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units.
|
||||
function Olympus.clone(ID)
|
||||
function Olympus.clone(ID, lat, lng)
|
||||
Olympus.notify("Olympus.clone " .. ID, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
local coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition())
|
||||
local lat, lng, alt = coord.LOtoLL(unit:getPoint())
|
||||
|
||||
-- TODO: only works on Aircraft
|
||||
local spawnOptions = {
|
||||
payload = Olympus.payloadRegistry[unitName]
|
||||
payload = Olympus.payloadRegistry[unit:getName()]
|
||||
}
|
||||
Olympus.spawnAircraft(coalition, unit:getTypeName(), lat + 0.001, lng + 0.001, spawnOptions)
|
||||
Olympus.spawnAircraft(coalition, unit:getTypeName(), lat, lng, spawnOptions)
|
||||
end
|
||||
Olympus.notify("Olympus.clone completed successfully", 2)
|
||||
end
|
||||
|
||||
function Olympus.follow(leaderID, ID)
|
||||
Olympus.notify("Olympus.follow " .. ID .. " " .. leaderID, 2)
|
||||
local leader = Olympus.getUnitByID(leaderID)
|
||||
function Olympus.delete(ID, lat, lng)
|
||||
Olympus.notify("Olympus.delete " .. ID, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
local followTask = {
|
||||
id = 'Follow',
|
||||
params = {
|
||||
groupId = leader:getGroup():getID(),
|
||||
pos = {x = 0 , y = 0, z = 20} ,
|
||||
lastWptIndexFlag = false,
|
||||
lastWptIndex = 1
|
||||
}
|
||||
}
|
||||
Olympus.notify("Olympus.follow group ID" .. unit:getGroup():getID(), 2)
|
||||
unit:getGroup():getController():pushTask(followTask)
|
||||
Olympus.notify("Olympus.follow completed successfully", 2)
|
||||
if unit then
|
||||
unit:destroy();
|
||||
Olympus.notify("Olympus.delete completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.setTask(ID, taskOptions)
|
||||
Olympus.notify("Olympus.setTask " .. ID .. " " .. Olympus.serializeTable(taskOptions), 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
local task = Olympus.buildTask(taskOptions);
|
||||
if task then
|
||||
unit:getGroup():getController():setTask(task)
|
||||
Olympus.notify("Olympus.setTask completed successfully", 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.resetTask(ID)
|
||||
Olympus.notify("Olympus.resetTask " .. ID, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
unit:getGroup():getController():resetTask()
|
||||
Olympus.notify("Olympus.resetTask completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.setCommand(ID, command)
|
||||
Olympus.notify("Olympus.setCommand " .. ID .. " " .. Olympus.serializeTable(command), 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
unit:getGroup():getController():setCommand(command)
|
||||
Olympus.notify("Olympus.setCommand completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.setOption(ID, optionID, optionValue)
|
||||
Olympus.notify("Olympus.setCommand " .. ID .. " " .. optionID .. " " .. optionValue, 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
unit:getGroup():getController():setOption(optionID, optionValue)
|
||||
Olympus.notify("Olympus.setOption completed successfully", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.serializeTable(val, name, skipnewlines, depth)
|
||||
skipnewlines = skipnewlines or false
|
||||
depth = depth or 0
|
||||
|
||||
local tmp = string.rep(" ", depth)
|
||||
if name then
|
||||
if type(name) == "number" then
|
||||
tmp = tmp .. "[" .. name .. "]" .. " = "
|
||||
else
|
||||
tmp = tmp .. name .. " = "
|
||||
end
|
||||
end
|
||||
|
||||
if type(val) == "table" then
|
||||
tmp = tmp .. "{" .. (not skipnewlines and "\n" or "")
|
||||
for k, v in pairs(val) do
|
||||
tmp = tmp .. Olympus.serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "")
|
||||
end
|
||||
tmp = tmp .. string.rep(" ", depth) .. "}"
|
||||
elseif type(val) == "number" then
|
||||
tmp = tmp .. tostring(val)
|
||||
elseif type(val) == "string" then
|
||||
tmp = tmp .. string.format("%q", val)
|
||||
elseif type(val) == "boolean" then
|
||||
tmp = tmp .. (val and "true" or "false")
|
||||
else
|
||||
tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\""
|
||||
end
|
||||
|
||||
return tmp
|
||||
end
|
||||
|
||||
Olympus.notify("OlympusCommand script loaded successfully", 2)
|
||||
@ -8,11 +8,14 @@ function Olympus.setMissionData(arg, time)
|
||||
local missionData = {}
|
||||
|
||||
-- Bullseye data
|
||||
local bullseyeVec3 = coalition.getMainRefPoint(0)
|
||||
local bullseyeLatitude, bullseyeLongitude, bullseyeAltitude = coord.LOtoLL(bullseyeVec3)
|
||||
local bullseye = {}
|
||||
bullseye["lat"] = bullseyeLatitude
|
||||
bullseye["lng"] = bullseyeLongitude
|
||||
for i = 0, 2 do
|
||||
local bullseyeVec3 = coalition.getMainRefPoint(i)
|
||||
local bullseyeLatitude, bullseyeLongitude, bullseyeAltitude = coord.LOtoLL(bullseyeVec3)
|
||||
bullseye[i] = {}
|
||||
bullseye[i]["lat"] = bullseyeLatitude
|
||||
bullseye[i]["lng"] = bullseyeLongitude
|
||||
end
|
||||
|
||||
-- Units tactical data
|
||||
-- TODO find some way to spread the load of getting this data (split)
|
||||
@ -29,6 +32,8 @@ function Olympus.setMissionData(arg, time)
|
||||
table["targets"]["radar"] = controller:getDetectedTargets(4)
|
||||
table["targets"]["rwr"] = controller:getDetectedTargets(16)
|
||||
table["targets"]["other"] = controller:getDetectedTargets(2, 8, 32)
|
||||
|
||||
table["hasTask"] = controller:hasTask()
|
||||
|
||||
table["ammo"] = unit:getAmmo()
|
||||
table["fuel"] = unit:getFuel()
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
try:
|
||||
# Python 2
|
||||
from SimpleHTTPServer import test, SimpleHTTPRequestHandler
|
||||
except ImportError:
|
||||
# Python 3
|
||||
from http.server import test, SimpleHTTPRequestHandler
|
||||
|
||||
test(SimpleHTTPRequestHandler)
|
||||
@ -1,44 +0,0 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['server.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='server',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
runtime_tmpdir=None,
|
||||
console=True,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
@ -18,23 +18,6 @@
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\commands.h" />
|
||||
<ClInclude Include="include\scriptLoader.h" />
|
||||
<ClInclude Include="include\server.h" />
|
||||
<ClInclude Include="include\scheduler.h" />
|
||||
<ClInclude Include="include\unit.h" />
|
||||
<ClInclude Include="include\unitsFactory.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\commands.cpp" />
|
||||
<ClCompile Include="src\scriptLoader.cpp" />
|
||||
<ClCompile Include="src\core.cpp" />
|
||||
<ClCompile Include="src\server.cpp" />
|
||||
<ClCompile Include="src\scheduler.cpp" />
|
||||
<ClCompile Include="src\unit.cpp" />
|
||||
<ClCompile Include="src\unitsFactory.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\dcstools\dcstools.vcxproj">
|
||||
<Project>{2b255368-39a0-431a-a6de-cc739ac70dc1}</Project>
|
||||
@ -49,6 +32,35 @@
|
||||
<Project>{b85009ce-4a5c-4a5a-b85d-001b3a2651b2}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\aircraft.h" />
|
||||
<ClInclude Include="include\airunit.h" />
|
||||
<ClInclude Include="include\commands.h" />
|
||||
<ClInclude Include="include\groundunit.h" />
|
||||
<ClInclude Include="include\helicopter.h" />
|
||||
<ClInclude Include="include\navyunit.h" />
|
||||
<ClInclude Include="include\scheduler.h" />
|
||||
<ClInclude Include="include\scriptloader.h" />
|
||||
<ClInclude Include="include\server.h" />
|
||||
<ClInclude Include="include\unit.h" />
|
||||
<ClInclude Include="include\unitsfactory.h" />
|
||||
<ClInclude Include="include\weapon.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\aircraft.cpp" />
|
||||
<ClCompile Include="src\airunit.cpp" />
|
||||
<ClCompile Include="src\commands.cpp" />
|
||||
<ClCompile Include="src\core.cpp" />
|
||||
<ClCompile Include="src\groundunit.cpp" />
|
||||
<ClCompile Include="src\helicopter.cpp" />
|
||||
<ClCompile Include="src\navyunit.cpp" />
|
||||
<ClCompile Include="src\scheduler.cpp" />
|
||||
<ClCompile Include="src\scriptloader.cpp" />
|
||||
<ClCompile Include="src\server.cpp" />
|
||||
<ClCompile Include="src\unit.cpp" />
|
||||
<ClCompile Include="src\unitsfactory.cpp" />
|
||||
<ClCompile Include="src\weapon.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
|
||||
@ -9,45 +9,81 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\aircraft.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\airunit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\commands.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\server.h">
|
||||
<ClInclude Include="include\groundunit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\helicopter.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\navyunit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\scheduler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\scriptloader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\server.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\unit.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\unitsFactory.h">
|
||||
<ClInclude Include="include\unitsfactory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\scriptLoader.h">
|
||||
<ClInclude Include="include\weapon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\aircraft.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\airunit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\commands.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\core.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\server.cpp">
|
||||
<ClCompile Include="src\groundunit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\helicopter.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\navyunit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\scheduler.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\scriptloader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\server.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\unit.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\unitsFactory.cpp">
|
||||
<ClCompile Include="src\unitsfactory.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\scriptLoader.cpp">
|
||||
<ClCompile Include="src\weapon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
||||
@ -8,9 +8,57 @@ namespace CommandPriority {
|
||||
};
|
||||
|
||||
namespace CommandType {
|
||||
enum CommandTypes { NO_TYPE, MOVE, SMOKE, LASE, EXPLODE, SPAWN_AIR, SPAWN_GROUND, CLONE, LAND, REFUEL, FOLLOW };
|
||||
enum CommandTypes { NO_TYPE, MOVE, SMOKE, SPAWN_AIR, SPAWN_GROUND, CLONE, FOLLOW, RESET_TASK, SET_OPTION, SET_COMMAND, SET_TASK };
|
||||
};
|
||||
|
||||
namespace SetCommandType {
|
||||
enum SetCommandTypes {
|
||||
ROE = 0,
|
||||
REACTION_ON_THREAT = 1,
|
||||
RADAR_USING = 3,
|
||||
FLARE_USING = 4,
|
||||
Formation = 5,
|
||||
RTB_ON_BINGO = 6,
|
||||
SILENCE = 7,
|
||||
RTB_ON_OUT_OF_AMMO = 10,
|
||||
ECM_USING = 13,
|
||||
PROHIBIT_AA = 14,
|
||||
PROHIBIT_JETT = 15,
|
||||
PROHIBIT_AB = 16,
|
||||
PROHIBIT_AG = 17,
|
||||
MISSILE_ATTACK = 18,
|
||||
PROHIBIT_WP_PASS_REPORT = 19,
|
||||
OPTION_RADIO_USAGE_CONTACT = 21,
|
||||
OPTION_RADIO_USAGE_ENGAGE = 22,
|
||||
OPTION_RADIO_USAGE_KILL = 23,
|
||||
JETT_TANKS_IF_EMPTY = 25,
|
||||
FORCED_ATTACK = 26
|
||||
};
|
||||
}
|
||||
|
||||
namespace ROE {
|
||||
enum ROEs {
|
||||
WEAPON_FREE = 0,
|
||||
OPEN_FIRE_WEAPON_FREE = 1,
|
||||
OPEN_FIRE = 2,
|
||||
RETURN_FIRE = 3,
|
||||
WEAPON_HOLD = 4,
|
||||
};
|
||||
}
|
||||
|
||||
namespace ReactionToThreat {
|
||||
enum ReactionToThreats {
|
||||
NO_REACTION = 0,
|
||||
PASSIVE_DEFENCE = 1,
|
||||
EVADE_FIRE = 2,
|
||||
BYPASS_AND_ESCAPE = 3,
|
||||
ALLOW_ABORT_MISSION = 4
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Base command class */
|
||||
class Command
|
||||
{
|
||||
@ -25,10 +73,10 @@ protected:
|
||||
};
|
||||
|
||||
/* Simple low priority move command (from user click) */
|
||||
class MoveCommand : public Command
|
||||
class Move : public Command
|
||||
{
|
||||
public:
|
||||
MoveCommand(int ID, Coords destination, double speed, double altitude, wstring unitCategory, wstring taskOptions):
|
||||
Move(int ID, Coords destination, double speed, double altitude, wstring unitCategory, wstring taskOptions):
|
||||
ID(ID),
|
||||
destination(destination),
|
||||
speed(speed),
|
||||
@ -51,10 +99,10 @@ private:
|
||||
};
|
||||
|
||||
/* Smoke command */
|
||||
class SmokeCommand : public Command
|
||||
class Smoke : public Command
|
||||
{
|
||||
public:
|
||||
SmokeCommand(wstring color, Coords location) :
|
||||
Smoke(wstring color, Coords location) :
|
||||
color(color),
|
||||
location(location)
|
||||
{
|
||||
@ -69,10 +117,10 @@ private:
|
||||
};
|
||||
|
||||
/* Spawn ground unit command */
|
||||
class SpawnGroundUnitCommand : public Command
|
||||
class SpawnGroundUnit : public Command
|
||||
{
|
||||
public:
|
||||
SpawnGroundUnitCommand(wstring coalition, wstring unitType, Coords location) :
|
||||
SpawnGroundUnit(wstring coalition, wstring unitType, Coords location) :
|
||||
coalition(coalition),
|
||||
unitType(unitType),
|
||||
location(location)
|
||||
@ -89,10 +137,10 @@ private:
|
||||
};
|
||||
|
||||
/* Spawn air unit command */
|
||||
class SpawnAircraftCommand : public Command
|
||||
class SpawnAircraft : public Command
|
||||
{
|
||||
public:
|
||||
SpawnAircraftCommand(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName) :
|
||||
SpawnAircraft(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName) :
|
||||
coalition(coalition),
|
||||
unitType(unitType),
|
||||
location(location),
|
||||
@ -114,35 +162,107 @@ private:
|
||||
};
|
||||
|
||||
/* Clone unit command */
|
||||
class CloneCommand : public Command
|
||||
class Clone : public Command
|
||||
{
|
||||
public:
|
||||
CloneCommand(int ID) :
|
||||
ID(ID)
|
||||
Clone(int ID, Coords location) :
|
||||
ID(ID),
|
||||
location(location)
|
||||
{
|
||||
priority = CommandPriority::LOW;
|
||||
type = CommandType::CLONE;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int ID;
|
||||
const Coords location;
|
||||
};
|
||||
|
||||
/* Delete unit command */
|
||||
class Delete : public Command
|
||||
{
|
||||
public:
|
||||
Delete(int ID) :
|
||||
ID(ID)
|
||||
{
|
||||
priority = CommandPriority::HIGH;
|
||||
type = CommandType::CLONE;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int ID;
|
||||
};
|
||||
|
||||
/* Follow command */
|
||||
class FollowCommand : public Command
|
||||
class SetTask : public Command
|
||||
{
|
||||
public:
|
||||
FollowCommand(int leaderID, int ID) :
|
||||
leaderID(leaderID),
|
||||
ID(ID)
|
||||
SetTask(int ID, wstring task) :
|
||||
ID(ID),
|
||||
task(task)
|
||||
{
|
||||
priority = CommandPriority::LOW;
|
||||
priority = CommandPriority::MEDIUM;
|
||||
type = CommandType::FOLLOW;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int leaderID;
|
||||
const int ID;
|
||||
const wstring task;
|
||||
};
|
||||
|
||||
/* Reset task command */
|
||||
class ResetTask : public Command
|
||||
{
|
||||
public:
|
||||
ResetTask(int ID) :
|
||||
ID(ID)
|
||||
{
|
||||
priority = CommandPriority::HIGH;
|
||||
type = CommandType::RESET_TASK;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int ID;
|
||||
};
|
||||
|
||||
/* Set command */
|
||||
class SetCommand : public Command
|
||||
{
|
||||
public:
|
||||
SetCommand(int ID, wstring command) :
|
||||
ID(ID),
|
||||
command(command)
|
||||
{
|
||||
priority = CommandPriority::HIGH;
|
||||
type = CommandType::RESET_TASK;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int ID;
|
||||
const wstring command;
|
||||
};
|
||||
|
||||
/* Set option command */
|
||||
class SetOption : public Command
|
||||
{
|
||||
public:
|
||||
SetOption(int ID, int optionID, int optionValue) :
|
||||
ID(ID),
|
||||
optionID(optionID),
|
||||
optionValue(optionValue)
|
||||
{
|
||||
priority = CommandPriority::HIGH;
|
||||
type = CommandType::RESET_TASK;
|
||||
};
|
||||
virtual wstring getString(lua_State* L);
|
||||
|
||||
private:
|
||||
const int ID;
|
||||
const int optionID;
|
||||
const int optionValue;
|
||||
};
|
||||
@ -4,8 +4,9 @@
|
||||
#include "dcstools.h"
|
||||
#include "luatools.h"
|
||||
|
||||
#define GROUND_DEST_DIST_THR 100
|
||||
#define AIR_DEST_DIST_THR 2000
|
||||
namespace State {
|
||||
enum States { IDLE, REACH_DESTINATION, ATTACK, WINGMAN, FOLLOW, LAND, REFUEL, AWACS, EWR, TANKER, RUN_AWAY };
|
||||
};
|
||||
|
||||
class Unit
|
||||
{
|
||||
@ -17,19 +18,22 @@ public:
|
||||
void updateMissionData(json::value json);
|
||||
json::value json();
|
||||
|
||||
virtual void setState(int newState) { state = newState; };
|
||||
void resetTask();
|
||||
|
||||
void setPath(list<Coords> path);
|
||||
void setActiveDestination(Coords newActiveDestination) { activeDestination = newActiveDestination; }
|
||||
void setAlive(bool newAlive) { alive = newAlive; }
|
||||
void setTarget(int targetID);
|
||||
void setLeader(bool newLeader) { leader = newLeader; }
|
||||
void setWingman(bool newWingman) { wingman = newWingman; }
|
||||
void setIsLeader(bool newIsLeader);
|
||||
void setIsWingman(bool newIsWingman);
|
||||
void setLeader(Unit* newLeader) { leader = newLeader; }
|
||||
void setWingmen(vector<Unit*> newWingmen) { wingmen = newWingmen; }
|
||||
void setFormation(wstring newFormation) { formation = newFormation; }
|
||||
|
||||
virtual void changeSpeed(wstring change) {};
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
|
||||
void resetActiveDestination();
|
||||
void setFormationOffset(Offset formationOffset);
|
||||
void setROE(wstring newROE);
|
||||
void setReactionToThreat(wstring newReactionToThreat);
|
||||
void landAt(Coords loc);
|
||||
|
||||
int getID() { return ID; }
|
||||
wstring getName() { return name; }
|
||||
@ -46,38 +50,53 @@ public:
|
||||
Coords getActiveDestination() { return activeDestination; }
|
||||
virtual wstring getCategory() { return L"No category"; };
|
||||
wstring getTarget();
|
||||
bool isTargetAlive();
|
||||
wstring getCurrentTask() { return currentTask; }
|
||||
bool getAlive() { return alive; }
|
||||
bool getIsLeader() { return isLeader; }
|
||||
bool getIsWingman() { return isWingman; }
|
||||
wstring getFormation() { return formation; }
|
||||
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
virtual void setTargetSpeed(double newSpeed) { targetSpeed = newSpeed; }
|
||||
virtual void setTargetAltitude(double newAltitude) { targetAltitude = newAltitude; }
|
||||
virtual void changeSpeed(wstring change) {};
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
|
||||
void resetActiveDestination();
|
||||
|
||||
protected:
|
||||
int ID;
|
||||
bool AI = false;
|
||||
bool alive = true;
|
||||
wstring name = L"undefined";
|
||||
wstring unitName = L"undefined";
|
||||
wstring groupName = L"undefined";
|
||||
json::value type = json::value::null();
|
||||
int country = NULL;
|
||||
int coalitionID = NULL;
|
||||
double latitude = NULL;
|
||||
double longitude = NULL;
|
||||
double altitude = NULL;
|
||||
double heading = NULL;
|
||||
double speed = NULL;
|
||||
json::value flags = json::value::null();
|
||||
int targetID = NULL;
|
||||
bool holding = false;
|
||||
bool looping = false;
|
||||
wstring taskOptions = L"{}";
|
||||
wstring currentTask = L"";
|
||||
bool leader = false;
|
||||
bool wingman = false;
|
||||
wstring formation = L"";
|
||||
int state = State::IDLE;
|
||||
bool hasTask = false;
|
||||
bool AI = false;
|
||||
bool alive = true;
|
||||
wstring name = L"undefined";
|
||||
wstring unitName = L"undefined";
|
||||
wstring groupName = L"undefined";
|
||||
json::value type = json::value::null();
|
||||
int country = NULL;
|
||||
int coalitionID = NULL;
|
||||
double latitude = NULL;
|
||||
double longitude = NULL;
|
||||
double altitude = NULL;
|
||||
double heading = NULL;
|
||||
double speed = NULL;
|
||||
json::value flags = json::value::null();
|
||||
int targetID = NULL;
|
||||
wstring currentTask = L"";
|
||||
bool isLeader = false;
|
||||
bool isWingman = false;
|
||||
Offset formationOffset = Offset(NULL);
|
||||
wstring formation = L"";
|
||||
Unit* leader = nullptr;
|
||||
wstring ROE = L"";
|
||||
wstring reactionToThreat = L"";
|
||||
vector<Unit*> wingmen;
|
||||
double targetSpeed = 0;
|
||||
double targetAltitude = 0;
|
||||
double fuel = 0;
|
||||
double targetSpeed = 0;
|
||||
double targetAltitude = 0;
|
||||
double fuel = 0;
|
||||
json::value ammo;
|
||||
json::value targets;
|
||||
|
||||
@ -85,112 +104,16 @@ protected:
|
||||
Coords activeDestination = Coords(0);
|
||||
Coords oldPosition = Coords(0); // Used to approximate speed
|
||||
|
||||
virtual void AIloop();
|
||||
virtual void AIloop() = 0;
|
||||
|
||||
private:
|
||||
mutex mutexLock;
|
||||
};
|
||||
|
||||
class AirUnit : public Unit
|
||||
{
|
||||
public:
|
||||
AirUnit(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() = 0;
|
||||
|
||||
protected:
|
||||
virtual void AIloop();
|
||||
};
|
||||
|
||||
class Aircraft : public AirUnit
|
||||
{
|
||||
public:
|
||||
Aircraft(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Aircraft"; };
|
||||
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change);
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
|
||||
protected:
|
||||
double targetSpeed = 150;
|
||||
double targetAltitude = 5000;
|
||||
};
|
||||
|
||||
class Helicopter : public AirUnit
|
||||
{
|
||||
public:
|
||||
Helicopter(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Helicopter"; };
|
||||
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change);
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
|
||||
protected:
|
||||
double targetSpeed = 50;
|
||||
double targetAltitude = 1000;
|
||||
};
|
||||
|
||||
class GroundUnit : public Unit
|
||||
{
|
||||
public:
|
||||
GroundUnit(json::value json, int ID);
|
||||
virtual void AIloop();
|
||||
|
||||
virtual wstring getCategory() { return L"GroundUnit"; };
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
|
||||
protected:
|
||||
double targetSpeed = 10;
|
||||
};
|
||||
|
||||
class NavyUnit : public Unit
|
||||
{
|
||||
public:
|
||||
NavyUnit(json::value json, int ID);
|
||||
virtual void AIloop();
|
||||
|
||||
virtual wstring getCategory() { return L"NavyUnit"; };
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
|
||||
protected:
|
||||
double targetSpeed = 10;
|
||||
};
|
||||
|
||||
class Weapon : public Unit
|
||||
{
|
||||
public:
|
||||
Weapon(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() = 0;
|
||||
|
||||
protected:
|
||||
/* Weapons are not controllable and have no AIloop */
|
||||
virtual void AIloop() {};
|
||||
};
|
||||
|
||||
class Missile : public Weapon
|
||||
{
|
||||
public:
|
||||
Missile(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Missile"; };
|
||||
};
|
||||
|
||||
class Bomb : public Weapon
|
||||
{
|
||||
public:
|
||||
Bomb(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Bomb"; };
|
||||
};
|
||||
|
||||
|
||||
21
src/core/include/aircraft.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "airunit.h"
|
||||
|
||||
class Aircraft : public AirUnit
|
||||
{
|
||||
public:
|
||||
Aircraft(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Aircraft"; };
|
||||
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change);
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
virtual void setTargetSpeed(double newTargetSpeed);
|
||||
virtual void setTargetAltitude(double newTargetAltitude);
|
||||
|
||||
protected:
|
||||
double targetSpeed = 300 / 1.94384;
|
||||
double targetAltitude = 20000 * 0.3048;
|
||||
};
|
||||
30
src/core/include/airunit.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "framework.h"
|
||||
#include "utils.h"
|
||||
#include "dcstools.h"
|
||||
#include "luatools.h"
|
||||
#include "Unit.h"
|
||||
|
||||
#define AIR_DEST_DIST_THR 2000
|
||||
|
||||
class AirUnit : public Unit
|
||||
{
|
||||
public:
|
||||
AirUnit(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() = 0;
|
||||
virtual void changeSpeed(wstring change) {};
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
virtual void setTargetSpeed(double newTargetSpeed) {};
|
||||
virtual void setTargetAltitude(double newTargetAltitude) {};
|
||||
|
||||
protected:
|
||||
virtual void AIloop();
|
||||
virtual void setState(int newState);
|
||||
bool isDestinationReached();
|
||||
bool setActiveDestination();
|
||||
void createHoldingPattern();
|
||||
bool updateActivePath(bool looping);
|
||||
void goToDestination(wstring enrouteTask = L"nil");
|
||||
void taskWingmen();
|
||||
};
|
||||
19
src/core/include/groundunit.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include "unit.h"
|
||||
|
||||
#define GROUND_DEST_DIST_THR 100
|
||||
|
||||
class GroundUnit : public Unit
|
||||
{
|
||||
public:
|
||||
GroundUnit(json::value json, int ID);
|
||||
virtual void AIloop();
|
||||
|
||||
virtual wstring getCategory() { return L"GroundUnit"; };
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
|
||||
protected:
|
||||
double targetSpeed = 10;
|
||||
};
|
||||
21
src/core/include/helicopter.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
#include "airunit.h"
|
||||
|
||||
class Helicopter : public AirUnit
|
||||
{
|
||||
public:
|
||||
Helicopter(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Helicopter"; };
|
||||
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change);
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
virtual double getTargetAltitude() { return targetAltitude; };
|
||||
virtual void setTargetSpeed(double newTargetSpeed);
|
||||
virtual void setTargetAltitude(double newTargetAltitude);
|
||||
|
||||
protected:
|
||||
double targetSpeed = 100 / 1.94384;
|
||||
double targetAltitude = 5000 * 0.3048;
|
||||
};
|
||||
17
src/core/include/navyunit.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "unit.h"
|
||||
|
||||
class NavyUnit : public Unit
|
||||
{
|
||||
public:
|
||||
NavyUnit(json::value json, int ID);
|
||||
virtual void AIloop();
|
||||
|
||||
virtual wstring getCategory() { return L"NavyUnit"; };
|
||||
virtual void changeSpeed(wstring change);
|
||||
virtual void changeAltitude(wstring change) {};
|
||||
virtual double getTargetSpeed() { return targetSpeed; };
|
||||
|
||||
protected:
|
||||
double targetSpeed = 10;
|
||||
};
|
||||
@ -14,6 +14,7 @@ public:
|
||||
void updateExportData(lua_State* L);
|
||||
void updateMissionData(json::value missionData);
|
||||
void updateAnswer(json::value& answer);
|
||||
void deleteUnit(int ID);
|
||||
|
||||
private:
|
||||
map<int, Unit*> units;
|
||||
|
||||
30
src/core/include/weapon.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
#include "unit.h"
|
||||
|
||||
class Weapon : public Unit
|
||||
{
|
||||
public:
|
||||
Weapon(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() = 0;
|
||||
|
||||
protected:
|
||||
/* Weapons are not controllable and have no AIloop */
|
||||
virtual void AIloop() {};
|
||||
};
|
||||
|
||||
class Missile : public Weapon
|
||||
{
|
||||
public:
|
||||
Missile(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Missile"; };
|
||||
};
|
||||
|
||||
class Bomb : public Weapon
|
||||
{
|
||||
public:
|
||||
Bomb(json::value json, int ID);
|
||||
|
||||
virtual wstring getCategory() { return L"Bomb"; };
|
||||
};
|
||||
@ -3,7 +3,7 @@
|
||||
#include "dcstools.h"
|
||||
|
||||
/* Move command */
|
||||
wstring MoveCommand::getString(lua_State* L)
|
||||
wstring Move::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
@ -19,7 +19,7 @@ wstring MoveCommand::getString(lua_State* L)
|
||||
}
|
||||
|
||||
/* Smoke command */
|
||||
wstring SmokeCommand::getString(lua_State* L)
|
||||
wstring Smoke::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
@ -31,7 +31,7 @@ wstring SmokeCommand::getString(lua_State* L)
|
||||
}
|
||||
|
||||
/* Spawn ground command */
|
||||
wstring SpawnGroundUnitCommand::getString(lua_State* L)
|
||||
wstring SpawnGroundUnit::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
@ -44,7 +44,7 @@ wstring SpawnGroundUnitCommand::getString(lua_State* L)
|
||||
}
|
||||
|
||||
/* Spawn air command */
|
||||
wstring SpawnAircraftCommand::getString(lua_State* L)
|
||||
wstring SpawnAircraft::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream optionsSS;
|
||||
optionsSS.precision(10);
|
||||
@ -65,23 +65,71 @@ wstring SpawnAircraftCommand::getString(lua_State* L)
|
||||
}
|
||||
|
||||
/* Clone unit command */
|
||||
wstring CloneCommand::getString(lua_State* L)
|
||||
wstring Clone::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.clone, "
|
||||
commandSS << "Olympus.clone, "
|
||||
<< ID << ", "
|
||||
<< location.lat << ", "
|
||||
<< location.lng;
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Delete unit command */
|
||||
wstring Delete::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.delete, "
|
||||
<< ID;
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Follow unit command */
|
||||
wstring FollowCommand::getString(lua_State* L)
|
||||
/* Set task command */
|
||||
wstring SetTask::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.follow, "
|
||||
<< leaderID << ","
|
||||
commandSS << "Olympus.setTask, "
|
||||
<< ID << ","
|
||||
<< task;
|
||||
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Reset task command */
|
||||
wstring ResetTask::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.resetTask, "
|
||||
<< ID;
|
||||
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Set command command */
|
||||
wstring SetCommand::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.setCommand, "
|
||||
<< ID << ","
|
||||
<< command;
|
||||
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
/* Set option command */
|
||||
wstring SetOption::getString(lua_State* L)
|
||||
{
|
||||
std::wostringstream commandSS;
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.setOption, "
|
||||
<< ID << ","
|
||||
<< optionID << ","
|
||||
<< optionValue;
|
||||
|
||||
return commandSS.str();
|
||||
}
|
||||
@ -73,17 +73,17 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
log(unitName + L" set path destination " + WP + L" (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
|
||||
Coords dest; dest.lat = lat; dest.lng = lng;
|
||||
newPath.push_back(dest);
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
{
|
||||
unit->setPath(newPath);
|
||||
log(unitName + L" new path set successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
log(unitName + L" not found, request will be discarded");
|
||||
}
|
||||
}
|
||||
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
{
|
||||
unit->setPath(newPath);
|
||||
unit->setState(State::REACH_DESTINATION);
|
||||
log(unitName + L" new path set successfully");
|
||||
}
|
||||
else
|
||||
log(unitName + L" not found, request will be discarded");
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"smoke") == 0)
|
||||
@ -93,7 +93,7 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
log(L"Adding " + color + L" smoke at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
command = dynamic_cast<Command*>(new SmokeCommand(color, loc));
|
||||
command = dynamic_cast<Command*>(new Smoke(color, loc));
|
||||
}
|
||||
else if (key.compare(L"spawnGround") == 0)
|
||||
{
|
||||
@ -103,7 +103,7 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
log(L"Spawning " + coalition + L" ground unit of type " + type + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
command = dynamic_cast<Command*>(new SpawnGroundUnitCommand(coalition, type, loc));
|
||||
command = dynamic_cast<Command*>(new SpawnGroundUnit(coalition, type, loc));
|
||||
}
|
||||
else if (key.compare(L"spawnAir") == 0)
|
||||
{
|
||||
@ -115,7 +115,7 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
wstring payloadName = value[L"payloadName"].as_string();
|
||||
wstring airbaseName = value[L"airbaseName"].as_string();
|
||||
log(L"Spawning " + coalition + L" air unit of type " + type + L" with payload " + payloadName + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L" " + airbaseName + L")");
|
||||
command = dynamic_cast<Command*>(new SpawnAircraftCommand(coalition, type, loc, payloadName, airbaseName));
|
||||
command = dynamic_cast<Command*>(new SpawnAircraft(coalition, type, loc, payloadName, airbaseName));
|
||||
}
|
||||
else if (key.compare(L"attackUnit") == 0)
|
||||
{
|
||||
@ -129,72 +129,90 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
wstring targetName;
|
||||
|
||||
if (unit != nullptr)
|
||||
{
|
||||
unitName = unit->getUnitName();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (target != nullptr)
|
||||
{
|
||||
targetName = target->getUnitName();
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
log(L"Unit " + unitName + L" attacking unit " + targetName);
|
||||
unit->setTarget(targetID);
|
||||
unit->setState(State::ATTACK);
|
||||
}
|
||||
else if (key.compare(L"stopAttack") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
unit->setState(State::REACH_DESTINATION);
|
||||
else
|
||||
return;
|
||||
}
|
||||
else if (key.compare(L"changeSpeed") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
{
|
||||
unit->changeSpeed(value[L"change"].as_string());
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"changeAltitude") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
{
|
||||
unit->changeAltitude(value[L"change"].as_string());
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"setSpeed") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
unit->setTargetSpeed(value[L"speed"].as_double());
|
||||
}
|
||||
else if (key.compare(L"setAltitude") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
if (unit != nullptr)
|
||||
unit->setTargetAltitude(value[L"altitude"].as_double());
|
||||
}
|
||||
else if (key.compare(L"cloneUnit") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
command = dynamic_cast<Command*>(new CloneCommand(ID));
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
command = dynamic_cast<Command*>(new Clone(ID, loc));
|
||||
log(L"Cloning unit " + to_wstring(ID));
|
||||
}
|
||||
else if (key.compare(L"setLeader") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
json::value wingmenIDs = value[L"wingmenIDs"];
|
||||
vector<Unit*> wingmen;
|
||||
if (unit != nullptr)
|
||||
bool isLeader = value[L"isLeader"].as_bool();
|
||||
if (isLeader)
|
||||
{
|
||||
for (auto itr = wingmenIDs.as_array().begin(); itr != wingmenIDs.as_array().end(); itr++)
|
||||
json::value wingmenIDs = value[L"wingmenIDs"];
|
||||
vector<Unit*> wingmen;
|
||||
if (unit != nullptr)
|
||||
{
|
||||
Unit* wingman = unitsFactory->getUnit(itr->as_integer());
|
||||
if (wingman != nullptr)
|
||||
for (auto itr = wingmenIDs.as_array().begin(); itr != wingmenIDs.as_array().end(); itr++)
|
||||
{
|
||||
wingman->setWingman(true);
|
||||
wingmen.push_back(wingman);
|
||||
log(L"Setting " + wingman->getName() + L" as wingman leader");
|
||||
Unit* wingman = unitsFactory->getUnit(itr->as_integer());
|
||||
if (wingman != nullptr)
|
||||
wingmen.push_back(wingman);
|
||||
}
|
||||
unit->setFormation(L"Line abreast");
|
||||
unit->setIsLeader(true);
|
||||
unit->setWingmen(wingmen);
|
||||
log(L"Setting " + unit->getName() + L" as formation leader");
|
||||
}
|
||||
unit->setWingmen(wingmen);
|
||||
unit->setLeader(true);
|
||||
unit->resetActiveDestination();
|
||||
log(L"Setting " + unit->getName() + L" as formation leader");
|
||||
}
|
||||
else {
|
||||
unit->setIsLeader(false);
|
||||
}
|
||||
}
|
||||
else if (key.compare(L"setFormation") == 0)
|
||||
@ -204,6 +222,34 @@ void Scheduler::handleRequest(wstring key, json::value value)
|
||||
wstring formation = value[L"formation"].as_string();
|
||||
unit->setFormation(formation);
|
||||
}
|
||||
else if (key.compare(L"setROE") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
wstring ROE = value[L"ROE"].as_string();
|
||||
unit->setROE(ROE);
|
||||
}
|
||||
else if (key.compare(L"setReactionToThreat") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
wstring reactionToThreat = value[L"reactionToThreat"].as_string();
|
||||
unit->setReactionToThreat(reactionToThreat);
|
||||
}
|
||||
else if (key.compare(L"landAt") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
Unit* unit = unitsFactory->getUnit(ID);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
unit->landAt(loc);
|
||||
}
|
||||
else if (key.compare(L"deleteUnit") == 0)
|
||||
{
|
||||
int ID = value[L"ID"].as_integer();
|
||||
unitsFactory->deleteUnit(ID);
|
||||
}
|
||||
else
|
||||
{
|
||||
log(L"Unknown command: " + key);
|
||||
|
||||
@ -12,8 +12,6 @@ using namespace GeographicLib;
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsFactory* unitsFactory;
|
||||
|
||||
const Geodesic& geod = Geodesic::WGS84();
|
||||
|
||||
Unit::Unit(json::value json, int ID) :
|
||||
ID(ID)
|
||||
{
|
||||
@ -34,7 +32,7 @@ void Unit::updateExportData(json::value json)
|
||||
if (oldPosition != NULL)
|
||||
{
|
||||
double dist = 0;
|
||||
geod.Inverse(latitude, longitude, oldPosition.lat, oldPosition.lng, dist);
|
||||
Geodesic::WGS84().Inverse(latitude, longitude, oldPosition.lat, oldPosition.lng, dist);
|
||||
speed = speed * 0.95 + (dist / UPDATE_TIME_INTERVAL) * 0.05;
|
||||
}
|
||||
oldPosition = Coords(latitude, longitude, altitude);
|
||||
@ -79,97 +77,17 @@ void Unit::updateExportData(json::value json)
|
||||
|
||||
void Unit::updateMissionData(json::value json)
|
||||
{
|
||||
/* Lock for thread safety */
|
||||
lock_guard<mutex> guard(mutexLock);
|
||||
|
||||
if (json.has_number_field(L"fuel"))
|
||||
fuel = json[L"fuel"].as_number().to_int32();
|
||||
if (json.has_object_field(L"ammo"))
|
||||
ammo = json[L"ammo"];
|
||||
if (json.has_object_field(L"targets"))
|
||||
targets = json[L"targets"];
|
||||
}
|
||||
|
||||
void Unit::setPath(list<Coords> path)
|
||||
{
|
||||
activePath = path;
|
||||
holding = false;
|
||||
}
|
||||
|
||||
void Unit::setTarget(int newTargetID)
|
||||
{
|
||||
targetID = newTargetID;
|
||||
resetActiveDestination();
|
||||
}
|
||||
|
||||
wstring Unit::getTarget()
|
||||
{
|
||||
if (targetID == NULL)
|
||||
{
|
||||
return L"";
|
||||
}
|
||||
|
||||
Unit* target = unitsFactory->getUnit(targetID);
|
||||
if (target != nullptr)
|
||||
{
|
||||
if (target->alive)
|
||||
{
|
||||
return target->getUnitName();
|
||||
}
|
||||
else
|
||||
{
|
||||
targetID = NULL;
|
||||
return L"";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
targetID = NULL;
|
||||
return L"";
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::AIloop()
|
||||
{
|
||||
// For wingman units, the leader decides the active destination
|
||||
if (!wingman)
|
||||
{
|
||||
/* Set the active destination to be always equal to the first point of the active path. This is in common with all AI units */
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
if (activeDestination != activePath.front())
|
||||
{
|
||||
activeDestination = activePath.front();
|
||||
Command* command = dynamic_cast<Command*>(new MoveCommand(ID, activeDestination, getTargetSpeed(), getTargetAltitude(), getCategory(), taskOptions));
|
||||
scheduler->appendCommand(command);
|
||||
|
||||
if (leader)
|
||||
{
|
||||
for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++)
|
||||
{
|
||||
// Manually set the path and the active destination of the wingmen
|
||||
(*itr)->setPath(activePath);
|
||||
(*itr)->setActiveDestination(activeDestination);
|
||||
Command* command = dynamic_cast<Command*>(new FollowCommand(ID, (*itr)->getID()));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
log(unitName + L" no more points in active path");
|
||||
activeDestination = Coords(0); // Set the active path to NULL
|
||||
currentTask = L"Idle";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This function reset the activation so that the AI lopp will call again the MoveCommand. This is useful to change speed and altitude, for example */
|
||||
void Unit::resetActiveDestination()
|
||||
{
|
||||
log(unitName + L" resetting active destination");
|
||||
activeDestination = Coords(0);
|
||||
if (json.has_boolean_field(L"hasTask"))
|
||||
hasTask = json[L"hasTask"].as_bool();
|
||||
}
|
||||
|
||||
json::value Unit::json()
|
||||
@ -194,18 +112,24 @@ json::value Unit::json()
|
||||
json[L"flags"] = flags;
|
||||
json[L"category"] = json::value::string(getCategory());
|
||||
json[L"currentTask"] = json::value::string(getCurrentTask());
|
||||
json[L"leader"] = leader;
|
||||
json[L"wingman"] = wingman;
|
||||
json[L"isLeader"] = isLeader;
|
||||
json[L"isWingman"] = isWingman;
|
||||
json[L"formation"] = json::value::string(formation);
|
||||
json[L"fuel"] = fuel;
|
||||
json[L"ammo"] = ammo;
|
||||
json[L"targets"] = targets;
|
||||
json[L"targetSpeed"] = getTargetSpeed();
|
||||
json[L"targetAltitude"] = getTargetAltitude();
|
||||
json[L"hasTask"] = hasTask;
|
||||
json[L"ROE"] = json::value::string(ROE);
|
||||
json[L"reactionToThreat"] = json::value::string(reactionToThreat);
|
||||
|
||||
int i = 0;
|
||||
for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++)
|
||||
{
|
||||
json[L"wingmenIDs"][i++] = (*itr)->getID();
|
||||
}
|
||||
|
||||
if (leader != nullptr)
|
||||
json[L"leaderID"] = leader->getID();
|
||||
|
||||
/* Send the active path as a json object */
|
||||
if (activePath.size() > 0) {
|
||||
@ -225,239 +149,122 @@ json::value Unit::json()
|
||||
return json;
|
||||
}
|
||||
|
||||
/* Air unit */
|
||||
AirUnit::AirUnit(json::value json, int ID) : Unit(json, ID)
|
||||
void Unit::setPath(list<Coords> path)
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void AirUnit::AIloop()
|
||||
{
|
||||
if (targetID != 0)
|
||||
if (state != State::WINGMAN && state != State::FOLLOW)
|
||||
{
|
||||
std::wostringstream taskOptionsSS;
|
||||
taskOptionsSS << "{"
|
||||
<< "id = 'EngageUnit'" << ","
|
||||
<< "targetID = " << targetID << ","
|
||||
<< "}";
|
||||
taskOptions = taskOptionsSS.str();
|
||||
currentTask = L"Attacking " + getTarget();
|
||||
activePath = path;
|
||||
resetActiveDestination();
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setTarget(int newTargetID)
|
||||
{
|
||||
targetID = newTargetID;
|
||||
}
|
||||
|
||||
wstring Unit::getTarget()
|
||||
{
|
||||
if (isTargetAlive())
|
||||
{
|
||||
Unit* target = unitsFactory->getUnit(targetID);
|
||||
if (target != nullptr)
|
||||
return target->getUnitName();
|
||||
}
|
||||
return L"";
|
||||
}
|
||||
|
||||
bool Unit::isTargetAlive()
|
||||
{
|
||||
if (targetID == NULL)
|
||||
return false;
|
||||
|
||||
Unit* target = unitsFactory->getUnit(targetID);
|
||||
if (target != nullptr)
|
||||
return target->alive;
|
||||
else
|
||||
{
|
||||
currentTask = L"Reaching destination";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Call the common AI loop */
|
||||
Unit::AIloop();
|
||||
/* This function reset the activation so that the AI lopp will call again the MoveCommand. This is useful to change speed and altitude, for example */
|
||||
void Unit::resetActiveDestination()
|
||||
{
|
||||
activeDestination = Coords(NULL);
|
||||
}
|
||||
|
||||
/* Air unit AI Loop */
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
double newDist = 0;
|
||||
geod.Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, newDist);
|
||||
if (newDist < AIR_DEST_DIST_THR)
|
||||
void Unit::resetTask()
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new ResetTask(ID));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
|
||||
void Unit::setIsLeader(bool newIsLeader) {
|
||||
isLeader = newIsLeader;
|
||||
if (!isLeader) {
|
||||
for (auto wingman : wingmen)
|
||||
{
|
||||
/* Destination reached */
|
||||
if (holding || looping)
|
||||
{
|
||||
activePath.push_back(activePath.front());
|
||||
}
|
||||
activePath.pop_front();
|
||||
log(name + L" destination reached");
|
||||
wingman->setFormation(L"");
|
||||
wingman->setIsWingman(false);
|
||||
wingman->setLeader(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::setIsWingman(bool newIsWingman)
|
||||
{
|
||||
isWingman = newIsWingman;
|
||||
if (isWingman)
|
||||
setState(State::WINGMAN);
|
||||
else
|
||||
{
|
||||
/* Air units must ALWAYS have a destination or they will RTB and may become uncontrollable */
|
||||
Coords point1;
|
||||
Coords point2;
|
||||
Coords point3;
|
||||
geod.Direct(latitude, longitude, 45, 10000, point1.lat, point1.lng);
|
||||
geod.Direct(point1.lat, point1.lng, 135, 10000, point2.lat, point2.lng);
|
||||
geod.Direct(point2.lat, point2.lng, 225, 10000, point3.lat, point3.lng);
|
||||
activePath.push_back(point1);
|
||||
activePath.push_back(point2);
|
||||
activePath.push_back(point3);
|
||||
activePath.push_back(Coords(latitude, longitude));
|
||||
holding = true;
|
||||
currentTask = L"Holding";
|
||||
}
|
||||
setState(State::IDLE);
|
||||
}
|
||||
|
||||
/* Aircraft */
|
||||
Aircraft::Aircraft(json::value json, int ID) : AirUnit(json, ID)
|
||||
void Unit::setFormationOffset(Offset newFormationOffset)
|
||||
{
|
||||
log("New Aircraft created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void Aircraft::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
/* Air units can't hold a position, so we can only set them to hold. At the moment, this will erase any other command. TODO: helicopters should be able to hover in place */
|
||||
activePath.clear();
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
{
|
||||
targetSpeed *= 0.9;
|
||||
resetActiveDestination();
|
||||
}
|
||||
else if (change.compare(L"fast") == 0)
|
||||
{
|
||||
targetSpeed *= 1.1;
|
||||
resetActiveDestination();
|
||||
}
|
||||
formationOffset = newFormationOffset;
|
||||
resetTask();
|
||||
}
|
||||
|
||||
void Aircraft::changeAltitude(wstring change)
|
||||
{
|
||||
if (change.compare(L"descend") == 0)
|
||||
{
|
||||
targetAltitude *= 0.9;
|
||||
}
|
||||
else if (change.compare(L"climb") == 0)
|
||||
{
|
||||
targetAltitude *= 1.1;
|
||||
}
|
||||
resetActiveDestination();
|
||||
void Unit::setROE(wstring newROE) {
|
||||
ROE = newROE;
|
||||
int ROEEnum;
|
||||
if (newROE.compare(L"Free") == 0)
|
||||
ROEEnum = ROE::WEAPON_FREE;
|
||||
else if (newROE.compare(L"Designated free") == 0)
|
||||
ROEEnum = ROE::OPEN_FIRE_WEAPON_FREE;
|
||||
else if (newROE.compare(L"Designated") == 0)
|
||||
ROEEnum = ROE::OPEN_FIRE;
|
||||
else if (newROE.compare(L"Return") == 0)
|
||||
ROEEnum = ROE::RETURN_FIRE;
|
||||
else if (newROE.compare(L"Hold") == 0)
|
||||
ROEEnum = ROE::WEAPON_HOLD;
|
||||
else
|
||||
return;
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::ROE, ROEEnum));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
|
||||
/* Helicopter */
|
||||
Helicopter::Helicopter(json::value json, int ID) : AirUnit(json, ID)
|
||||
{
|
||||
log("New Helicopter created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void Helicopter::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
/* Air units can't hold a position, so we can only set them to hold. At the moment, this will erase any other command. TODO: helicopters should be able to hover in place */
|
||||
activePath.clear();
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
{
|
||||
targetSpeed *= 0.9;
|
||||
resetActiveDestination();
|
||||
}
|
||||
else if (change.compare(L"fast") == 0)
|
||||
{
|
||||
targetSpeed *= 1.1;
|
||||
resetActiveDestination();
|
||||
}
|
||||
void Unit::setReactionToThreat(wstring newReactionToThreat) {
|
||||
reactionToThreat = newReactionToThreat;
|
||||
int reactionToThreatEnum;
|
||||
if (newReactionToThreat.compare(L"None") == 0)
|
||||
reactionToThreatEnum = ReactionToThreat::NO_REACTION;
|
||||
else if (newReactionToThreat.compare(L"Passive") == 0)
|
||||
reactionToThreatEnum = ReactionToThreat::PASSIVE_DEFENCE;
|
||||
else if (newReactionToThreat.compare(L"Evade") == 0)
|
||||
reactionToThreatEnum = ReactionToThreat::EVADE_FIRE;
|
||||
else if (newReactionToThreat.compare(L"Escape") == 0)
|
||||
reactionToThreatEnum = ReactionToThreat::BYPASS_AND_ESCAPE;
|
||||
else if (newReactionToThreat.compare(L"Abort") == 0)
|
||||
reactionToThreatEnum = ReactionToThreat::ALLOW_ABORT_MISSION;
|
||||
else
|
||||
return;
|
||||
Command* command = dynamic_cast<Command*>(new SetOption(ID, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
|
||||
void Helicopter::changeAltitude(wstring change)
|
||||
{
|
||||
if (change.compare(L"descend") == 0)
|
||||
{
|
||||
targetAltitude *= 0.9;
|
||||
}
|
||||
else if (change.compare(L"climb") == 0)
|
||||
{
|
||||
targetAltitude *= 1.1;
|
||||
}
|
||||
resetActiveDestination();
|
||||
}
|
||||
|
||||
|
||||
/* Ground unit */
|
||||
GroundUnit::GroundUnit(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Ground Unit created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void GroundUnit::AIloop()
|
||||
{
|
||||
/* Call the common AI loop */
|
||||
Unit::AIloop();
|
||||
|
||||
/* Ground unit AI Loop */
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
double newDist = 0;
|
||||
geod.Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, newDist);
|
||||
if (newDist < GROUND_DEST_DIST_THR)
|
||||
{
|
||||
/* Destination reached */
|
||||
activePath.pop_front();
|
||||
log(unitName + L" destination reached");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroundUnit::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"fast") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Navy Unit */
|
||||
NavyUnit::NavyUnit(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Navy Unit created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void NavyUnit::AIloop()
|
||||
{
|
||||
/* Call the common AI loop */
|
||||
Unit::AIloop();
|
||||
|
||||
/* Navy unit AI Loop */
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
double newDist = 0;
|
||||
geod.Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, newDist);
|
||||
if (newDist < GROUND_DEST_DIST_THR)
|
||||
{
|
||||
/* Destination reached */
|
||||
activePath.pop_front();
|
||||
log(unitName + L" destination reached");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NavyUnit::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"fast") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/* Weapon */
|
||||
Weapon::Weapon(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
/* Missile */
|
||||
Missile::Missile(json::value json, int ID) : Weapon(json, ID)
|
||||
{
|
||||
log("New Missile created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
/* Bomb */
|
||||
Bomb::Bomb(json::value json, int ID) : Weapon(json, ID)
|
||||
{
|
||||
log("New Bomb created with ID: " + to_string(ID));
|
||||
};
|
||||
void Unit::landAt(Coords loc) {
|
||||
activePath.clear();
|
||||
activePath.push_back(loc);
|
||||
setState(State::LAND);
|
||||
}
|
||||
68
src/core/src/aircraft.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
#include "aircraft.h"
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "scheduler.h"
|
||||
#include "defines.h"
|
||||
#include "unitsFactory.h"
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
using namespace GeographicLib;
|
||||
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsFactory* unitsFactory;
|
||||
|
||||
/* Aircraft */
|
||||
Aircraft::Aircraft(json::value json, int ID) : AirUnit(json, ID)
|
||||
{
|
||||
log("New Aircraft created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void Aircraft::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
setState(State::IDLE);
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
targetSpeed -= 25 / 1.94384;
|
||||
else if (change.compare(L"fast") == 0)
|
||||
targetSpeed += 25 / 1.94384;
|
||||
|
||||
if (targetSpeed < 50 / 1.94384)
|
||||
targetSpeed = 50 / 1.94384;
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Aircraft::changeAltitude(wstring change)
|
||||
{
|
||||
if (change.compare(L"descend") == 0)
|
||||
{
|
||||
if (targetAltitude > 5000)
|
||||
targetAltitude -= 2500 / 3.28084;
|
||||
else if (targetAltitude > 0)
|
||||
targetAltitude -= 500 / 3.28084;
|
||||
}
|
||||
else if (change.compare(L"climb") == 0)
|
||||
{
|
||||
if (targetAltitude > 5000)
|
||||
targetAltitude += 2500 / 3.28084;
|
||||
else if (targetAltitude >= 0)
|
||||
targetAltitude += 500 / 3.28084;
|
||||
}
|
||||
if (targetAltitude < 0)
|
||||
targetAltitude = 0;
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Aircraft::setTargetSpeed(double newTargetSpeed) {
|
||||
targetSpeed = newTargetSpeed;
|
||||
goToDestination();
|
||||
}
|
||||
|
||||
void Aircraft::setTargetAltitude(double newTargetAltitude) {
|
||||
targetAltitude = newTargetAltitude;
|
||||
goToDestination();
|
||||
}
|
||||
344
src/core/src/airunit.cpp
Normal file
@ -0,0 +1,344 @@
|
||||
#include "airunit.h"
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "scheduler.h"
|
||||
#include "defines.h"
|
||||
#include "unitsFactory.h"
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
using namespace GeographicLib;
|
||||
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsFactory* unitsFactory;
|
||||
|
||||
/* Air unit */
|
||||
AirUnit::AirUnit(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
void AirUnit::setState(int newState)
|
||||
{
|
||||
if (state != newState)
|
||||
{
|
||||
switch (state) {
|
||||
case State::IDLE: {
|
||||
break;
|
||||
}
|
||||
case State::REACH_DESTINATION: {
|
||||
break;
|
||||
}
|
||||
case State::ATTACK: {
|
||||
setTarget(NULL);
|
||||
break;
|
||||
}
|
||||
case State::FOLLOW: {
|
||||
break;
|
||||
}
|
||||
case State::WINGMAN: {
|
||||
if (isWingman)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
case State::LAND: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (newState) {
|
||||
case State::IDLE: {
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
case State::REACH_DESTINATION: {
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
case State::ATTACK: {
|
||||
if (isTargetAlive()) {
|
||||
Unit* target = unitsFactory->getUnit(targetID);
|
||||
Coords targetPosition = Coords(target->getLatitude(), target->getLongitude(), 0);
|
||||
activePath.clear();
|
||||
activePath.push_front(targetPosition);
|
||||
resetActiveDestination();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case State::FOLLOW: {
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
case State::WINGMAN: {
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
case State::LAND: {
|
||||
resetActiveDestination();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
resetTask();
|
||||
|
||||
log(unitName + L" setting state from " + to_wstring(state) + L" to " + to_wstring(newState));
|
||||
state = newState;
|
||||
}
|
||||
}
|
||||
|
||||
bool AirUnit::isDestinationReached()
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
double dist = 0;
|
||||
Geodesic::WGS84().Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, dist);
|
||||
if (dist < AIR_DEST_DIST_THR)
|
||||
{
|
||||
log(unitName + L" destination reached");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AirUnit::setActiveDestination()
|
||||
{
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
activeDestination = activePath.front();
|
||||
log(unitName + L" active destination set to queue front");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeDestination = Coords(0);
|
||||
log(unitName + L" active destination set to NULL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AirUnit::createHoldingPattern()
|
||||
{
|
||||
/* Air units must ALWAYS have a destination or they will RTB and become uncontrollable */
|
||||
activePath.clear();
|
||||
Coords point1;
|
||||
Coords point2;
|
||||
Coords point3;
|
||||
Geodesic::WGS84().Direct(latitude, longitude, 45, 10000, point1.lat, point1.lng);
|
||||
Geodesic::WGS84().Direct(point1.lat, point1.lng, 135, 10000, point2.lat, point2.lng);
|
||||
Geodesic::WGS84().Direct(point2.lat, point2.lng, 225, 10000, point3.lat, point3.lng);
|
||||
activePath.push_back(point1);
|
||||
activePath.push_back(point2);
|
||||
activePath.push_back(point3);
|
||||
activePath.push_back(Coords(latitude, longitude));
|
||||
log(unitName + L" holding pattern created");
|
||||
}
|
||||
|
||||
bool AirUnit::updateActivePath(bool looping)
|
||||
{
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
/* Push the next destination in the queue to the front */
|
||||
if (looping)
|
||||
activePath.push_back(activePath.front());
|
||||
activePath.pop_front();
|
||||
log(unitName + L" active path front popped");
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void AirUnit::goToDestination(wstring enrouteTask)
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new Move(ID, activeDestination, getTargetSpeed(), getTargetAltitude(), getCategory(), enrouteTask));
|
||||
scheduler->appendCommand(command);
|
||||
hasTask = true;
|
||||
}
|
||||
else
|
||||
log(unitName + L" error, no active destination!");
|
||||
}
|
||||
|
||||
void AirUnit::taskWingmen()
|
||||
{
|
||||
switch (state) {
|
||||
case State::IDLE:
|
||||
case State::REACH_DESTINATION:
|
||||
case State::ATTACK:{
|
||||
int idx = 1;
|
||||
for (auto const& wingman : wingmen)
|
||||
{
|
||||
if (!wingman->getIsWingman())
|
||||
{
|
||||
wingman->setIsWingman(true);
|
||||
wingman->setLeader(this);
|
||||
}
|
||||
|
||||
if (wingman->getFormation().compare(formation) != 0)
|
||||
{
|
||||
wingman->resetTask();
|
||||
wingman->setFormation(formation);
|
||||
if (formation.compare(L"Line abreast") == 0)
|
||||
wingman->setFormationOffset(Offset(0 * idx, 0 * idx, 1852 * idx));
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void AirUnit::AIloop()
|
||||
{
|
||||
/* State machine */
|
||||
switch (state) {
|
||||
case State::IDLE: {
|
||||
wstring enrouteTask = L"nil";
|
||||
currentTask = L"Idle";
|
||||
|
||||
if (activeDestination == NULL || !hasTask)
|
||||
{
|
||||
createHoldingPattern();
|
||||
setActiveDestination();
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
else {
|
||||
if (isDestinationReached() && updateActivePath(true) && setActiveDestination())
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
|
||||
if (isLeader)
|
||||
taskWingmen();
|
||||
break;
|
||||
}
|
||||
case State::REACH_DESTINATION: {
|
||||
wstring enrouteTask = L"nil";
|
||||
currentTask = L"Reaching destination";
|
||||
|
||||
if (activeDestination == NULL || !hasTask)
|
||||
{
|
||||
setActiveDestination();
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
else {
|
||||
if (isDestinationReached()) {
|
||||
if (updateActivePath(false) && setActiveDestination())
|
||||
goToDestination(enrouteTask);
|
||||
else {
|
||||
setState(State::IDLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isLeader)
|
||||
taskWingmen();
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case State::LAND: {
|
||||
wstring enrouteTask = L"{" "id = 'land' }";
|
||||
currentTask = L"Landing";
|
||||
|
||||
if (activeDestination == NULL)
|
||||
{
|
||||
setActiveDestination();
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
|
||||
if (isLeader)
|
||||
taskWingmen();
|
||||
|
||||
break;
|
||||
}
|
||||
case State::ATTACK: {
|
||||
/* If the target is not alive (either not set or was succesfully destroyed) go back to REACH_DESTINATION */
|
||||
if (!isTargetAlive()) {
|
||||
setState(State::REACH_DESTINATION);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Attack state is an "enroute" task, meaning the unit will keep trying to attack even if a new destination is set. This is useful to
|
||||
manoeuvre the unit so that it can detect and engage the target. */
|
||||
std::wostringstream enrouteTaskSS;
|
||||
enrouteTaskSS << "{"
|
||||
<< "id = 'EngageUnit'" << ","
|
||||
<< "targetID = " << targetID << ","
|
||||
<< "}";
|
||||
wstring enrouteTask = enrouteTaskSS.str();
|
||||
currentTask = L"Attacking " + getTarget();
|
||||
|
||||
if (activeDestination == NULL || !hasTask)
|
||||
{
|
||||
setActiveDestination();
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
else {
|
||||
if (isDestinationReached()) {
|
||||
if (updateActivePath(false) && setActiveDestination())
|
||||
goToDestination(enrouteTask);
|
||||
else {
|
||||
setState(State::IDLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isLeader)
|
||||
taskWingmen();
|
||||
|
||||
break;
|
||||
}
|
||||
case State::FOLLOW: {
|
||||
/* TODO */
|
||||
setState(State::IDLE);
|
||||
break;
|
||||
}
|
||||
case State::WINGMAN: {
|
||||
/* In the WINGMAN state, the unit relinquishes control to the leader */
|
||||
activePath.clear();
|
||||
activeDestination = Coords(NULL);
|
||||
if (leader == nullptr || !leader->getAlive())
|
||||
{
|
||||
this->setFormation(L"");
|
||||
this->setIsWingman(false);
|
||||
break;
|
||||
}
|
||||
if (!hasTask) {
|
||||
if (leader != nullptr && leader->getAlive() && formationOffset != NULL)
|
||||
{
|
||||
std::wostringstream taskSS;
|
||||
taskSS << "{"
|
||||
<< "id = 'FollowUnit'" << ", "
|
||||
<< "leaderID = " << leader->getID() << ","
|
||||
<< "offset = {"
|
||||
<< "x = " << formationOffset.x << ","
|
||||
<< "y = " << formationOffset.y << ","
|
||||
<< "z = " << formationOffset.z
|
||||
<< "},"
|
||||
<< "}";
|
||||
Command* command = dynamic_cast<Command*>(new SetTask(ID, taskSS.str()));
|
||||
scheduler->appendCommand(command);
|
||||
hasTask = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -12,6 +12,8 @@ UnitsFactory* unitsFactory = nullptr;
|
||||
Server* server = nullptr;
|
||||
Scheduler* scheduler = nullptr;
|
||||
json::value airbasesData;
|
||||
json::value bullseyeData;
|
||||
|
||||
|
||||
/* Called when DCS simulation stops. All singleton instances are deleted. */
|
||||
extern "C" DllExport int coreDeinit(lua_State* L)
|
||||
@ -71,40 +73,8 @@ extern "C" DllExport int coreMissionData(lua_State * L)
|
||||
unitsFactory->updateMissionData(missionData[L"unitsData"]);
|
||||
if (missionData.has_object_field(L"airbases"))
|
||||
airbasesData = missionData[L"airbases"];
|
||||
if (missionData.has_object_field(L"bullseye"))
|
||||
bullseyeData = missionData[L"bullseye"];
|
||||
|
||||
return(0);
|
||||
}
|
||||
|
||||
BOOL WINAPI DllMain(
|
||||
HINSTANCE hinstDLL, // handle to DLL module
|
||||
DWORD fdwReason, // reason for calling function
|
||||
LPVOID lpvReserved) // reserved
|
||||
{
|
||||
// Perform actions based on the reason for calling.
|
||||
switch (fdwReason)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
// Initialize once for each new process.
|
||||
// Return FALSE to fail DLL load.
|
||||
break;
|
||||
|
||||
case DLL_THREAD_ATTACH:
|
||||
// Do thread-specific initialization.
|
||||
break;
|
||||
|
||||
case DLL_THREAD_DETACH:
|
||||
// Do thread-specific cleanup.
|
||||
break;
|
||||
|
||||
case DLL_PROCESS_DETACH:
|
||||
|
||||
if (lpvReserved != nullptr)
|
||||
{
|
||||
break; // do not do cleanup if process termination scenario
|
||||
}
|
||||
|
||||
// Perform any necessary cleanup.
|
||||
break;
|
||||
}
|
||||
return TRUE; // Successful DLL_PROCESS_ATTACH.
|
||||
}
|
||||
71
src/core/src/groundunit.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "groundunit.h"
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "scheduler.h"
|
||||
#include "defines.h"
|
||||
#include "unitsFactory.h"
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
using namespace GeographicLib;
|
||||
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsFactory* unitsFactory;
|
||||
|
||||
/* Ground unit */
|
||||
GroundUnit::GroundUnit(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Ground Unit created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void GroundUnit::AIloop()
|
||||
{
|
||||
/* Set the active destination to be always equal to the first point of the active path. This is in common with all AI units */
|
||||
if (activePath.size() > 0)
|
||||
{
|
||||
if (activeDestination != activePath.front())
|
||||
{
|
||||
activeDestination = activePath.front();
|
||||
Command* command = dynamic_cast<Command*>(new Move(ID, activeDestination, getTargetSpeed(), getTargetAltitude(), getCategory(), L"nil"));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
log(unitName + L" no more points in active path");
|
||||
activeDestination = Coords(0); // Set the active path to NULL
|
||||
currentTask = L"Idle";
|
||||
}
|
||||
}
|
||||
|
||||
/* Ground unit AI Loop */
|
||||
if (activeDestination != NULL)
|
||||
{
|
||||
double newDist = 0;
|
||||
Geodesic::WGS84().Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, newDist);
|
||||
if (newDist < GROUND_DEST_DIST_THR)
|
||||
{
|
||||
/* Destination reached */
|
||||
activePath.pop_front();
|
||||
log(unitName + L" destination reached");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GroundUnit::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"fast") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
69
src/core/src/helicopter.cpp
Normal file
@ -0,0 +1,69 @@
|
||||
#include "helicopter.h"
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "scheduler.h"
|
||||
#include "defines.h"
|
||||
#include "unitsFactory.h"
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
using namespace GeographicLib;
|
||||
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsFactory* unitsFactory;
|
||||
|
||||
/* Helicopter */
|
||||
Helicopter::Helicopter(json::value json, int ID) : AirUnit(json, ID)
|
||||
{
|
||||
log("New Helicopter created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void Helicopter::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
/* Air units can't hold a position, so we can only set them to hold. At the moment, this will erase any other command. TODO: helicopters should be able to hover in place */
|
||||
activePath.clear();
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
targetSpeed -= 10 / 1.94384;
|
||||
else if (change.compare(L"fast") == 0)
|
||||
targetSpeed += 10 / 1.94384;
|
||||
if (targetSpeed < 0)
|
||||
targetSpeed = 0;
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Helicopter::changeAltitude(wstring change)
|
||||
{
|
||||
if (change.compare(L"descend") == 0)
|
||||
{
|
||||
if (targetAltitude > 100)
|
||||
targetAltitude -= 100 / 3.28084;
|
||||
else if (targetAltitude > 0)
|
||||
targetAltitude -= 10 / 3.28084;
|
||||
}
|
||||
else if (change.compare(L"climb") == 0)
|
||||
{
|
||||
if (targetAltitude > 100)
|
||||
targetAltitude += 100 / 3.28084;
|
||||
else if (targetAltitude >= 0)
|
||||
targetAltitude += 10 / 3.28084;
|
||||
}
|
||||
if (targetAltitude < 0)
|
||||
targetAltitude = 0;
|
||||
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
|
||||
void Helicopter::setTargetSpeed(double newTargetSpeed) {
|
||||
targetSpeed = newTargetSpeed;
|
||||
goToDestination();
|
||||
}
|
||||
|
||||
void Helicopter::setTargetAltitude(double newTargetAltitude) {
|
||||
targetAltitude = newTargetAltitude;
|
||||
goToDestination();
|
||||
}
|
||||
40
src/core/src/navyunit.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#include "navyunit.h"
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "scheduler.h"
|
||||
#include "defines.h"
|
||||
#include "unitsFactory.h"
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
using namespace GeographicLib;
|
||||
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsFactory* unitsFactory;
|
||||
|
||||
/* Navy Unit */
|
||||
NavyUnit::NavyUnit(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
log("New Navy Unit created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
void NavyUnit::AIloop()
|
||||
{
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void NavyUnit::changeSpeed(wstring change)
|
||||
{
|
||||
if (change.compare(L"stop") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"slow") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
else if (change.compare(L"fast") == 0)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@ -10,6 +10,7 @@
|
||||
extern UnitsFactory* unitsFactory;
|
||||
extern Scheduler* scheduler;
|
||||
extern json::value airbasesData;
|
||||
extern json::value bullseyeData;
|
||||
|
||||
void handle_eptr(std::exception_ptr eptr)
|
||||
{
|
||||
@ -59,6 +60,7 @@ void Server::handle_get(http_request request)
|
||||
try {
|
||||
unitsFactory->updateAnswer(answer);
|
||||
answer[L"airbases"] = airbasesData;
|
||||
answer[L"bullseye"] = bullseyeData;
|
||||
response.set_body(answer);
|
||||
}
|
||||
catch (...) {
|
||||
|
||||
@ -2,9 +2,15 @@
|
||||
#include "unitsFactory.h"
|
||||
#include "logger.h"
|
||||
#include "unit.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include "aircraft.h"
|
||||
#include "helicopter.h"
|
||||
#include "groundunit.h"
|
||||
#include "navyunit.h"
|
||||
#include "weapon.h"
|
||||
#include "commands.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
extern Scheduler* scheduler;
|
||||
|
||||
UnitsFactory::UnitsFactory(lua_State* L)
|
||||
{
|
||||
@ -114,3 +120,12 @@ void UnitsFactory::updateAnswer(json::value& answer)
|
||||
answer[L"units"] = unitsJson;
|
||||
}
|
||||
|
||||
void UnitsFactory::deleteUnit(int ID)
|
||||
{
|
||||
if (getUnit(ID) != nullptr)
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new Delete(ID));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
31
src/core/src/weapon.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "weapon.h"
|
||||
#include "utils.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "scheduler.h"
|
||||
#include "defines.h"
|
||||
#include "unitsFactory.h"
|
||||
|
||||
#include <GeographicLib/Geodesic.hpp>
|
||||
using namespace GeographicLib;
|
||||
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsFactory* unitsFactory;
|
||||
|
||||
/* Weapon */
|
||||
Weapon::Weapon(json::value json, int ID) : Unit(json, ID)
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
/* Missile */
|
||||
Missile::Missile(json::value json, int ID) : Weapon(json, ID)
|
||||
{
|
||||
log("New Missile created with ID: " + to_string(ID));
|
||||
};
|
||||
|
||||
/* Bomb */
|
||||
Bomb::Bomb(json::value json, int ID) : Weapon(json, ID)
|
||||
{
|
||||
log("New Bomb created with ID: " + to_string(ID));
|
||||
};
|
||||
@ -7,6 +7,12 @@ struct Coords {
|
||||
double alt = 0;
|
||||
};
|
||||
|
||||
struct Offset {
|
||||
double x = 0;
|
||||
double y = 0;
|
||||
double z = 0;
|
||||
};
|
||||
|
||||
// Get current date/time, format is YYYY-MM-DD.HH:mm:ss
|
||||
const DllExport std::string CurrentDateTime();
|
||||
std::wstring DllExport to_wstring(const std::string& str);
|
||||
@ -16,3 +22,8 @@ bool DllExport operator== (const Coords& a, const Coords& b);
|
||||
bool DllExport operator!= (const Coords& a, const Coords& b);
|
||||
bool DllExport operator== (const Coords& a, const int& b);
|
||||
bool DllExport operator!= (const Coords& a, const int& b);
|
||||
|
||||
bool DllExport operator== (const Offset& a, const Offset& b);
|
||||
bool DllExport operator!= (const Offset& a, const Offset& b);
|
||||
bool DllExport operator== (const Offset& a, const int& b);
|
||||
bool DllExport operator!= (const Offset& a, const int& b);
|
||||
|
||||
@ -42,3 +42,8 @@ bool operator== (const Coords& a, const Coords& b) { return a.lat == b.lat && a.
|
||||
bool operator!= (const Coords& a, const Coords& b) { return !(a == b); }
|
||||
bool operator== (const Coords& a, const int& b) { return a.lat == b && a.lng == b && a.alt == b; }
|
||||
bool operator!= (const Coords& a, const int& b) { return !(a == b); }
|
||||
|
||||
bool operator== (const Offset& a, const Offset& b) { return a.x == b.x && a.y == b.y && a.z == b.z; }
|
||||
bool operator!= (const Offset& a, const Offset& b) { return !(a == b); }
|
||||
bool operator== (const Offset& a, const int& b) { return a.x == b && a.y == b && a.z == b; }
|
||||
bool operator!= (const Offset& a, const int& b) { return !(a == b); }
|
||||
|
||||