From 3c1db67733d26f8c0ee266f66bc98df1174b5225 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Wed, 1 Feb 2023 21:38:36 +0100 Subject: [PATCH 1/3] Reworked unit as as state machine, added formations, added measuring on map, added copy-paste --- .gitignore | 1 + client/TODO.txt | 5 +- client/package-lock.json | 266 +- client/package.json | 5 +- client/public/images/BEBlue.png | Bin 0 -> 1375 bytes client/public/images/BERed.png | Bin 0 -> 2782 bytes client/public/images/bullseye.png | Bin 0 -> 1382 bytes client/public/images/bullseye.xcf | Bin 0 -> 263593 bytes client/public/images/bullseye0.png | Bin 0 -> 4552 bytes client/public/images/bullseye1.png | Bin 0 -> 3800 bytes client/public/images/bullseye2.png | Bin 0 -> 4357 bytes client/public/images/buttons/create.svg | 1 + client/public/images/buttons/erase.svg | 1 + client/public/images/icons/formation-end.svg | 54 + .../public/images/icons/formation-middle.svg | 54 + client/public/images/icons/formation.png | Bin 0 -> 19556 bytes client/public/images/icons/leader.png | Bin 0 -> 22070 bytes client/public/images/icons/ruler.svg | 1 + client/public/images/icons/singleton.png | Bin 0 -> 14026 bytes client/public/images/pin.png | Bin 0 -> 1393 bytes client/public/images/pin.svg | 1 + client/public/images/unit.png | Bin 0 -> 22307 bytes client/public/javascripts/placeholder | 0 client/public/stylesheets/layout.css | 16 +- client/public/stylesheets/mouseinfopanel.css | 31 + client/public/stylesheets/selectionscroll.css | 82 +- client/public/stylesheets/style.css | 95 +- .../public/stylesheets/unitcontrolpanel.css | 80 +- client/src/controls/selectionscroll.ts | 25 +- client/src/dcs/dcs.ts | 24 +- client/src/index.ts | 30 +- client/src/map/boxselect.ts | 4 +- client/src/map/map.ts | 151 +- client/src/missiondata/missiondata.ts | 79 +- client/src/other/utils.ts | 10 +- client/src/panels/mouseInfoPanel.ts | 73 + client/src/panels/unitcontrolpanel.ts | 185 ++ client/src/panels/unitinfopanel.ts | 9 + client/src/units/unit.ts | 95 +- client/src/units/unitsmanager.ts | 219 +- client/views/index.ejs | 1 + client/views/mouseinfopanel.ejs | 6 + client/views/selectionscroll.ejs | 11 +- client/views/unitcontrolpanel.ejs | 58 +- installer/DCSOlympus.iss | 2 +- scripts/OlympusCommand.lua | 122 +- scripts/OlympusMission.lua | 13 +- scripts/payloadNames.js | 2236 ++++++++++++++++- scripts/server.py | 8 - scripts/server.spec | 44 - src/core/core.vcxproj | 46 +- src/core/core.vcxproj.filters | 48 +- src/core/include/Commands.h | 88 +- src/core/include/Unit.h | 140 +- src/core/include/aircraft.h | 19 + src/core/include/airunit.h | 30 + src/core/include/groundunit.h | 19 + src/core/include/helicopter.h | 19 + src/core/include/navyunit.h | 17 + src/core/include/weapon.h | 30 + src/core/src/Commands.cpp | 54 +- src/core/src/Scheduler.cpp | 93 +- src/core/src/Unit.cpp | 391 +-- src/core/src/aircraft.cpp | 58 + src/core/src/airunit.cpp | 331 +++ src/core/src/core.cpp | 38 +- src/core/src/groundunit.cpp | 71 + src/core/src/helicopter.cpp | 58 + src/core/src/navyunit.cpp | 40 + src/core/src/server.cpp | 2 + src/core/src/unitsFactory.cpp | 8 +- src/core/src/weapon.cpp | 31 + src/utils/include/Utils.h | 11 + src/utils/src/Utils.cpp | 5 + 74 files changed, 4838 insertions(+), 907 deletions(-) create mode 100644 client/public/images/BEBlue.png create mode 100644 client/public/images/BERed.png create mode 100644 client/public/images/bullseye.png create mode 100644 client/public/images/bullseye.xcf create mode 100644 client/public/images/bullseye0.png create mode 100644 client/public/images/bullseye1.png create mode 100644 client/public/images/bullseye2.png create mode 100644 client/public/images/buttons/create.svg create mode 100644 client/public/images/buttons/erase.svg create mode 100644 client/public/images/icons/formation-end.svg create mode 100644 client/public/images/icons/formation-middle.svg create mode 100644 client/public/images/icons/formation.png create mode 100644 client/public/images/icons/leader.png create mode 100644 client/public/images/icons/ruler.svg create mode 100644 client/public/images/icons/singleton.png create mode 100644 client/public/images/pin.png create mode 100644 client/public/images/pin.svg create mode 100644 client/public/images/unit.png create mode 100644 client/public/javascripts/placeholder create mode 100644 client/public/stylesheets/mouseinfopanel.css create mode 100644 client/src/panels/mouseInfoPanel.ts create mode 100644 client/src/panels/unitcontrolpanel.ts create mode 100644 client/views/mouseinfopanel.ejs delete mode 100644 scripts/server.py delete mode 100644 scripts/server.spec create mode 100644 src/core/include/aircraft.h create mode 100644 src/core/include/airunit.h create mode 100644 src/core/include/groundunit.h create mode 100644 src/core/include/helicopter.h create mode 100644 src/core/include/navyunit.h create mode 100644 src/core/include/weapon.h create mode 100644 src/core/src/aircraft.cpp create mode 100644 src/core/src/airunit.cpp create mode 100644 src/core/src/groundunit.cpp create mode 100644 src/core/src/helicopter.cpp create mode 100644 src/core/src/navyunit.cpp create mode 100644 src/core/src/weapon.cpp diff --git a/.gitignore b/.gitignore index 047617ad..5ded2391 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ server node_modules /client/TODO.txt /client/public/javascripts/bundle.js +!client/bin \ No newline at end of file diff --git a/client/TODO.txt b/client/TODO.txt index 343d85e2..cb925eba 100644 --- a/client/TODO.txt +++ b/client/TODO.txt @@ -1,11 +1,8 @@ -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 \ No newline at end of file +weapons should not be selectable \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index 1a17cba1..df336cd1 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,10 +1,11 @@ { - "name": "client", + "name": "DCSOlympus", "version": "0.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { + "name": "DCSOlympus", "version": "0.0.0", "dependencies": { "@types/geojson": "^7946.0.10", @@ -21,6 +22,7 @@ "devDependencies": { "browserify": "^17.0.0", "concurrently": "^7.6.0", + "nodemon": "^2.0.20", "tsify": "^5.0.4", "typescript": "^4.9.4", "watchify": "^4.0.0" @@ -39,6 +41,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", @@ -1507,6 +1515,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", @@ -1971,6 +1985,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", @@ -2178,6 +2280,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", @@ -2485,6 +2593,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", @@ -2766,6 +2895,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", @@ -2876,6 +3017,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", @@ -3088,6 +3235,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", @@ -4280,6 +4433,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", @@ -4647,6 +4806,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", @@ -4815,6 +5039,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", @@ -5069,6 +5299,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", @@ -5300,6 +5547,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", @@ -5384,6 +5640,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", @@ -5544,4 +5806,4 @@ "dev": true } } -} \ No newline at end of file +} diff --git a/client/package.json b/client/package.json index 3a7711c2..8ef758d9 100644 --- a/client/package.json +++ b/client/package.json @@ -1,5 +1,5 @@ { - "name": "DCS Olympus", + "name": "DCSOlympus", "node-main": "./bin/www", "main": "http://localhost:3000", "version": "0.0.0", @@ -24,8 +24,9 @@ "devDependencies": { "browserify": "^17.0.0", "concurrently": "^7.6.0", + "nodemon": "^2.0.20", "tsify": "^5.0.4", "typescript": "^4.9.4", "watchify": "^4.0.0" } -} \ No newline at end of file +} diff --git a/client/public/images/BEBlue.png b/client/public/images/BEBlue.png new file mode 100644 index 0000000000000000000000000000000000000000..93338e8a2756400ea19a03ed0a810021c55bc7d9 GIT binary patch literal 1375 zcmV-l1)%zgP)EX>4Tx04R}tkv&MmP!xqvQ>7vmhjtKg$WRM{1yK=4sbUcq3h_DdxIq^re&o9B@*C%p!vfC?8`<UyLAHipMfi_C-6b3k8o}nuM00Ub|L_t(|+U?vwP8&fK$MM;f z&wxeaiUUx)PEV2JP#_8)hav|^OBcxpa7AO;7f6E^szf33pIv)q_u2dTb_fL8=dXWn z-i(DkxR;o zjEE#=b%uy0XJnVMI#EWch$2!}WJE!>tHo?uOh_`Kw3HPY5lPCLjOc;Z!*4}KL>hGJ zBQkE35xHm+ZpT`-eMa;uWvx|X04X<}gd`(|8ggp2L8R7QOGiXPLLyX3*3uCY64(=h zwRD7p1oosPArUVjwQFeEz>c;}NW@A=?HXD(u!l-QA`n{%iAdZK64sCaAwfu>E0ZDr z`1{Mlun+(J$7r8DYF#E{emCxtV#pT*BNy_Rq~x@Q3@2yg)=EgJ2|~t^vRN+`f{<}& zUY|%w$aqqMkk*uh#KEN0x(TUu6B6i^i$AGpbDGkumqilo{pI26PIKz{Ga-u?`Sw25 z|A=!fL&-HBZH}^KJEJBfKu9S`NPv(aB+~F(B!tA*;@#dW0wMF@0}_NZrGt>tk&t0& z*2^q1O2>koN7`NPWS#rjBfQb9Ax+j2@*+i?Ka-J=CTc=LB3eVnV?I$DLI&d3d!q=1 zjKlBOCmZO5jE5W02uVyrqKlXyBq>LNivrG&(uO%9Piq5(E(&PY%Rx8KT@-LF@4`$k zqzk7ADP0IjN&^ldK}dj*03kt0fRG?0K*&?Ehe${=f{-O;bVp=fCImcF$N@j?Q|h^f zJm+_}547y7X)iu8L`V=4(vgs4BqYd4NHT(uWQ<#9B_JXpi3maxk&pyLN2Rw)psWv>>qsWQMC5+Ec3uOF+wLjL&zkdOc& z5wTr=5mMWRgoJebF9_>H-vlHiMw^a1g_M$rn2^r5(R(_kl+xZb^v_@DTgHQHNUa+Y zhZ?OLy`|&9maTQ8Zy8$&NlNr3V{0Kvi9Td(vxYR};9P!o`QgrzNlKIqv6E7dlql6v z&uYWOMDFF?+3P}5BIm4;Wl$j{at*y%Cud@gbBbOqX5VuCvLahK#FUDmge0a!3@Icz hrD0eh$telj@CjgpN!||n-75e9002ovPDHLkV1m~lQ&|82 literal 0 HcmV?d00001 diff --git a/client/public/images/BERed.png b/client/public/images/BERed.png new file mode 100644 index 0000000000000000000000000000000000000000..367984f9f678163c11f1dda76670a945d0f38702 GIT binary patch literal 2782 zcmV<43L*80P)EX>4Tx04R}tkv&MmP!xqvQ>7vmhjtKg$WRM{1yK=4sbUcq3h_DdxIq^re&o9B@*C%p!vfC?8`<UyLAHipMfi_C-6b2W|Pn>c900`PiL_t(|+U=b^P#b3&$N&4T zBoHjpM1+ipIok!fnNT@ZRM@3GgNe^)#K*XV6sZhuQXHOCnZre{IGg&Q%-BdIvClAL zcjty{4p*drx!3_8n)6vX$!i3XR&NRc0rRDgSGx=Re~kxhvDUjko{!ykA8Bxqk*=t0 z_h@Jp8rpRMLjYl+f!Kp7B@$Ng&jm{&QIJ^77jSoa%CVDR>w$;5qVl{~;DS$3JeTt< zSHRO;0nhS<)$!@6#$zMoP*+rT`UKAV1PqN59z_xbiHVGbk%uwu$O<`nrAKzw37qo? zp3!5(%({ik-%V;qM##}CJ#xUWGbK|@XN4T42ssk1c`a{5}Y?5v}V zq;^u4e2r%ODrF?)VQ3oe2E1nmI=i1M6_Pb0sYc4tChr+#LT=u9A*52mLc>a5zf>aR zjRwj{)JQpftyfkhEE|Qv@{zcJ zk2d-kv1ukDPO`ZXT(s+~ABj5{=RV|DoJ z@Ug!hmx2a5{B<=y8TP;TqfCQ%sv+OLGYEWD3_8moWb77cK^cbO+1X9KL?}UwrY?OQfv=7lcB(gU2Zo z*B6&4C53cT1GI)OetOA;ppb5AdRAFeQb>1kC-Iu8n?kyWv}Ix9Rgw!qA>BhPndTx; zNcXUu&0#s4<3cFp$ls|6=mG&<-#4w+y3U(5SJ4v+(j*F5ojKpw=s3KC^}= zDHD%Vrt*m9WQ6SS*W=?F>{ii zZ9QgBDCmmln6?T^f63gi;g}V91*!V0Qy*|4D5Udr`0Md)`|sFUGi5u^U`q(Y;bzK6 zA)O^+G+;Q~%sVm^k`a+Yk}YD%|6?he#igLZ>y2z`oF{D+@FZp8%bydtF#m65w;KtC zteWL)ZcEIAF2WNEX}g%$7nf`uMNJ{ep{RaPj8I4=QZjv0NLTUY&j}8kQp}OR>=O+I zOBxg~pIJjZnZ|r(U1?1jOQtZ`@}48_UG<;Q@E3Ot!&~%+(98Z zDBu{-6ACKbAhG$8B7{O#jOs09Y;Gu|l1QmCN)ifLHJ>Wl!SiEgjV4h@H4Tpncs#Y~l1pq&~m_k<0thtKoi%UxD z$Br5do?Opz;8IcsyRnd_XEhwZb27| zkgkz&z@f($C?we>BU5$l;IOGOYDPvz3h4?WMgzV%9cIl)A)Teo=f!ZindA5?2{-?y`V)88)*jg2=^&&;}o%iryNRcGJH72~T+-J~|DdOsNt6ms}s zjH#R0(#VvQvYEPxYVNOD=>PuU8!(3sn;J3mTuGuNRUal}Nf8nN{&uxj4*L0NRittm zKb*W*l4~XBVoFJBEn`V3dy~(9nAFC3(VUdQ8!2(i%$4=bR#^i}DJ3Z;j*#2=PjA0$)p?zg0OPB9eEuL- zl?+u6z5j6X9_vb`Xbh#ZRm-R(LT=|D-F@0jHN`xng|I1fBq~g2mBr| zjUR6g+Ogv*A-9X!S+C<04uF&h)ijdCnskHIH!T{4{4__(8npC(u?yZgfTE z1+Tz4kHAMB%IW4v_$*hzT)tq7gpLWhm!R!B&?*GlJsJ#XFa*%bndi1hXG*{<0E<=u krUVwO0&YEwJG%J40t|#q;`jn2P5=M^07*qoM6N<$f|sx+asU7T literal 0 HcmV?d00001 diff --git a/client/public/images/bullseye.png b/client/public/images/bullseye.png new file mode 100644 index 0000000000000000000000000000000000000000..f70249546be7042ad275a9ff5d40cb7b0186ddcd GIT binary patch literal 1382 zcmV-s1)2JZP)EX>4Tx04R}tkv&MmP!xqvQ>7vm2Rn##$WWauh>EC_RIvyaN?V~-2a}inL6e3g z#l=x@EjakISaoo5*44pP5CnffTwR{K$3L|nWrS; zby?xO#aXS?SnHnrg~7bGlIA+CFydH30!fIFQ9~IOScuZDkzyi6=P?iekmFC1OD0zt zj2sK7LWSh`!T;d*Y|X;NxSJG=0o^aQ{V@y#c7bNyw!e>UyLkfmpMfi_?XNa~=}*$@ zZ7p&H^lt+f*KJMS11@)f!6#iZBuDbo5()+2{fxdT2MpW-y=(5=TIV=@05UYIy{D4^000SaNLh0L z04^f{04^f|c%?sf00007bV*G`2j>AC2RkYPd~jv}000?uMObu0Z*6U5Zgc=ca%Ew3 zWn>_CX>@2HM@dakSAh-}0009$NklE=>@?WZIN8kQbo(NUeIRmER!(Y(IgT)RJtdOJ&0dDNu%h+Z0+-7y4)_l;R)* zEO6M}+1Z)>=bt(Fk3=u4sFZpPWWc*+^v$w{^F05~f1drTp=mU}*btFEaICd{1317L zW8MJABGS*YY~#TURw|XYh#ZMX4mhRM2{2Gfz0_L2^xr3d6Omj*j-n`P-^*wihFxGJ zB13EKp0&1FSnm080~W2DaUB1ulo|q~D2jes)@~1QK@hwE+H=->CG%~sTxI24ZsYGpHi}S_-4l_#)oQJO%G*BR;`kQ5KD?JjqtBlGBEUYd zzq`9DGsZ8y4gmfv=g*!i$37$3zBhT)Vz!Z7@L9)WnHlYJi%NVC~o zUr3q0v`r~Mc3rPRXKM=903 z0n$zD6UTA)UQClD$;O;!@0C*ORavuFtyUkd+w}Fc5B#RJJ}&_4+>=9pVahy^^p<|Z o`~1R)$e+fTi#*TY@qd!P0POehO~k`~g8%>k07*qoM6N<$g0tU+Z~y=R literal 0 HcmV?d00001 diff --git a/client/public/images/bullseye.xcf b/client/public/images/bullseye.xcf new file mode 100644 index 0000000000000000000000000000000000000000..097722f6875070d185e944d4580c54f5eab676ef GIT binary patch literal 263593 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�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

&cU@WqEP}0pxAU_lzzUQW0PTlI3>uc9v=VN-- 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 \ No newline at end of file diff --git a/client/public/images/buttons/erase.svg b/client/public/images/buttons/erase.svg new file mode 100644 index 00000000..f6588839 --- /dev/null +++ b/client/public/images/buttons/erase.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/images/icons/formation-end.svg b/client/public/images/icons/formation-end.svg new file mode 100644 index 00000000..f8235368 --- /dev/null +++ b/client/public/images/icons/formation-end.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + diff --git a/client/public/images/icons/formation-middle.svg b/client/public/images/icons/formation-middle.svg new file mode 100644 index 00000000..edf78e19 --- /dev/null +++ b/client/public/images/icons/formation-middle.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + diff --git a/client/public/images/icons/formation.png b/client/public/images/icons/formation.png new file mode 100644 index 0000000000000000000000000000000000000000..b140e9f63e07a795d4a22ccbb257607eb1c6dee6 GIT binary patch literal 19556 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 z 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 \ No newline at end of file diff --git a/client/public/images/icons/singleton.png b/client/public/images/icons/singleton.png new file mode 100644 index 0000000000000000000000000000000000000000..0664436164b0b847cc588a88125f3494fc4f8554 GIT binary patch literal 14026 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 E0%|ysq5uE@ literal 0 HcmV?d00001 diff --git a/client/public/images/pin.png b/client/public/images/pin.png new file mode 100644 index 0000000000000000000000000000000000000000..c6222cd29b40e039065e19a5256b3b9617abd6df GIT binary patch literal 1393 zcmV-%1&;cOP)EX>4Tx04R}tkv&MmP!xqvQ>7vmhjtKg$WRM{1yK=4sbUcq3h_DdxIq^re&o9B@*C%p!vfC?8`<UyLAHipMfi_C-6bKR}rbTlA00V7FL_t(o!|j;QZxm%1$3O4R zP8YXZ7Pf6#YLiWRGmQ`tG$ zJPW)I?6Y&XfiHkL;LcWqB!II?csm`5>^U5Zk&4C8A}oa=-2U`Jh z2)Oj7>!y$H-c8bVF`*$4@I?p!Un%Z-9@7sW@|`ii1MdJcn+fs^aQVYTqW6VFf*=eD zfIy@M5s0h}$6>Zq;xo@H051W*Yz9aI`00a0V&ui{ZnPD44j!cM$Poti?0k=Nt@9*8G6h5%`_;KDieVW_Xuk-b}bKJ^i@yq3=P-%O97x=tX zDgaLdUb`L{9gRklDWz&cFP}QaN}<5iv13$j+#sMa9P7RE6wvE9$$~MXzz_c>6lnpH zPJx3JN{x@x)7QsWlam$#0%;Vk9W7883NaA}NOzJWZp6M&ix3+aKnDTK`FvxgK+O}n zX z)QgW3aW(+5BJf8(2;LfY99*T=Dxy9y*FhAuX3qC9@ILTuD-madi{5M*O1~O&H08RH zE?Wt~KUGZ_@)t1c`^LaTtJdD`e&iM4lcZAV=aNa)1MX8A7fp>v# zwsKe34ZH^21fpxkvO%Me*LqkKv z<8c61S63h9Tw5O(;R&%FkR6a6knI8yK-=C?-$!(-o!DlBXy9Tlm%|u?F@{_&2XN6| z+x9;RS2CH5WHK26u5_?ubZmpb$94?zf3Uv+rZ!t=0C|Rj00000NkvXXu0mjfy{mSH literal 0 HcmV?d00001 diff --git a/client/public/images/pin.svg b/client/public/images/pin.svg new file mode 100644 index 00000000..19e92ec2 --- /dev/null +++ b/client/public/images/pin.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/images/unit.png b/client/public/images/unit.png new file mode 100644 index 0000000000000000000000000000000000000000..ae72bc19198e8cd98442381e7463d1d3a2504822 GIT binary patch literal 22307 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 { + 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 +161,10 @@ html { width: 1px !important; display: inline-block; } + +.hl { + border-top: 1px solid #555; + height: 1px !important; + display: inline-block; +} + diff --git a/client/public/stylesheets/unitcontrolpanel.css b/client/public/stylesheets/unitcontrolpanel.css index 20a2a45c..9bcd3ba9 100644 --- a/client/public/stylesheets/unitcontrolpanel.css +++ b/client/public/stylesheets/unitcontrolpanel.css @@ -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%; +} + +#threat-reaction-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,10 @@ font-size: 14px; width: 100%; font-weight: 600; +} + +#unit-control-panel #section-label { + color: white; + font-size: 13px; + width: 100%; } \ No newline at end of file diff --git a/client/src/controls/selectionscroll.ts b/client/src/controls/selectionscroll.ts index 3a341605..7a518371 100644 --- a/client/src/controls/selectionscroll.ts +++ b/client/src/controls/selectionscroll.ts @@ -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 = 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")); + } } } diff --git a/client/src/dcs/dcs.ts b/client/src/dcs/dcs.ts index 581e1a0f..adf8bf17 100644 --- a/client/src/dcs/dcs.ts +++ b/client/src/dcs/dcs.ts @@ -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); } }; @@ -105,7 +105,7 @@ export function cloneUnit(ID: number) { 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"); } }; @@ -121,7 +121,7 @@ export function changeSpeed(ID: number, speedChange: string) { 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); } }; @@ -137,12 +137,28 @@ export function changeAltitude(ID: number, altitudeChange: string) { 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 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)); } \ No newline at end of file diff --git a/client/src/index.ts b/client/src/index.ts index 5d2f8c5a..10fbba6f 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -8,19 +8,24 @@ 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"; /* 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 +36,9 @@ var aiVisibilityButton: Button; var weaponVisibilityButton: Button; var deadVisibilityButton: Button; +var connected: boolean; +var activeCoalition: string; + function setup() { /* Initialize */ map = new Map('map-container'); @@ -38,9 +46,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", "Syria", "Marianas", "Nevada", "South Atlantic", "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 */ @@ -82,6 +92,10 @@ export function getMap() { return map; } +export function getMissionData() { + return missionData; +} + export function getSelectionWheel() { return selectionWheel; } @@ -98,6 +112,14 @@ export function getUnitInfoPanel() { return unitInfoPanel; } +export function getUnitControlPanel() { + return unitControlPanel; +} + +export function getMouseInfoPanel() { + return mouseInfoPanel; +} + export function setActiveCoalition(newActiveCoalition: string) { activeCoalition = newActiveCoalition; } diff --git a/client/src/map/boxselect.ts b/client/src/map/boxselect.ts index 2f44853e..5a742bea 100644 --- a/client/src/map/boxselect.ts +++ b/client/src/map/boxselect.ts @@ -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(); diff --git a/client/src/map/map.ts b/client/src/map/map.ts index febe7e67..536e5470 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -1,5 +1,5 @@ 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 { payloadNames } from "../units/payloadNames"; import { unitTypes } from "../units/unitTypes"; @@ -21,8 +21,11 @@ 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; constructor(ID: string) { /* Init the leaflet map */ @@ -34,12 +37,19 @@ 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}); /* 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)); } setLayer(layerName: string) { @@ -124,10 +134,10 @@ 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() { @@ -136,51 +146,97 @@ export class Map extends L.Map { /* 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(e.latlng, this.#measurePoint, selectedUnitPosition); + } + /* Spawn from air base */ spawnFromAirbase(e: SpawnEvent) { @@ -198,7 +254,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 +267,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 +284,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 +299,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 +314,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 +332,10 @@ 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); } } diff --git a/client/src/missiondata/missiondata.ts b/client/src/missiondata/missiondata.ts index 392b3600..f5ccedad 100644 --- a/client/src/missiondata/missiondata.ts +++ b/client/src/missiondata/missiondata.ts @@ -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); + } } } \ No newline at end of file diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index 182ba265..4b169360 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -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); diff --git a/client/src/panels/mouseInfoPanel.ts b/client/src/panels/mouseInfoPanel.ts new file mode 100644 index 00000000..74ed0a72 --- /dev/null +++ b/client/src/panels/mouseInfoPanel.ts @@ -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 = document.getElementById(ID); + this.#display = ''; + if (this.#element != null) { + this.#display = this.#element.style.display; + var el = 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 = 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 = 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 = 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 = 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 = this.#element.querySelector(`#unit-position`); + if (el != null && el.parentElement != null) + el.parentElement.style.display = 'none'; + } + } +} \ No newline at end of file diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts new file mode 100644 index 00000000..e4851602 --- /dev/null +++ b/client/src/panels/unitcontrolpanel.ts @@ -0,0 +1,185 @@ +import { imageOverlay } from "leaflet"; +import { getUnitsManager } from ".."; +import { ConvertDDToDMS, rad2deg } from "../other/utils"; +import { AirUnit, Unit } from "../units/unit"; + +export class UnitControlPanel { + #element: HTMLElement + #display: string; + + constructor(ID: string) { + this.#element = document.getElementById(ID); + this.#display = ''; + if (this.#element != null) { + this.#display = this.#element.style.display; + var formationCreationContainer = (this.#element.querySelector("#formation-creation-container")); + if (formationCreationContainer != null) + { + var createButton = formationCreationContainer.querySelector("#create-formation"); + createButton?.addEventListener("click", () => getUnitsManager().selectedUnitsCreateFormation()); + + var undoButton = formationCreationContainer.querySelector("#undo-formation"); + undoButton?.addEventListener("click", () => getUnitsManager().selectedUnitsUndoFormation()); + } + this.hide(); + } + } + + show() { + this.#element.style.display = this.#display; + } + + hide() { + this.#element.style.display = "none"; + } + + update(units: Unit[]) { + if (this.#element != null) + { + var selectedUnitsContainer = (this.#element.querySelector("#selected-units-container")); + var formationCreationContainer = (this.#element.querySelector("#formation-creation-container")); + if (selectedUnitsContainer != null && formationCreationContainer != null) + { + this.#addUnitsButtons(units, selectedUnitsContainer); + this.#showFormationButtons(units, formationCreationContainer); + } + } + } + + #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 = formationCreationContainer.querySelector("#create-formation"); + var undoButton = 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 + } + + #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 + } +} \ No newline at end of file diff --git a/client/src/panels/unitinfopanel.ts b/client/src/panels/unitinfopanel.ts index b4741024..070d1dda 100644 --- a/client/src/panels/unitinfopanel.ts +++ b/client/src/panels/unitinfopanel.ts @@ -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"); + } } } \ No newline at end of file diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 856b2749..19a94a2a 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -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 } 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,13 @@ 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[] = []; + #selectable: boolean; #selected: boolean = false; #preventClick: boolean = false; @@ -61,42 +64,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 +102,7 @@ export class Unit { this.#selected = selected; this.#marker.setSelected(selected); getUnitsManager().onUnitSelection(); + } } @@ -140,6 +138,28 @@ export class Unit { return false; } + getLeader() { + return getUnitsManager().getUnitByID(this.leaderID); + } + + getFormation() { + return [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 +177,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() { @@ -318,25 +337,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 { diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 4533d988..b85aeaa5 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -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,13 @@ export class UnitsManager { constructor() { this.#units = {}; this.#copiedUnits = []; + + document.addEventListener('copy', () => this.copyUnits()); + document.addEventListener('paste', () => this.pasteUnits()); + } + + getUnits() { + return this.#units; } addUnit(ID: number, data: any) { @@ -27,7 +35,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 +51,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 +67,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 +86,8 @@ export class UnitsManager { getMap().setState("IDLE"); //unitControlPanel.setEnabled(false); } + + this.#updateUnitControlPanel(); } selectFromBounds(bounds: LatLngBounds) @@ -92,7 +113,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 +153,7 @@ export class UnitsManager { } } - clearDestinations() { + selectedUnitsClearDestinations() { var selectedUnits = this.getSelectedUnits(); for (let idx in selectedUnits) { var commandedUnit = selectedUnits[idx]; @@ -116,6 +165,11 @@ export class UnitsManager { } } + selectedUnitsLandAt(latlng: LatLng) + { + + } + selectedUnitsChangeSpeed(speedChange: string) { var selectedUnits = this.getSelectedUnits(); @@ -134,33 +188,21 @@ export class UnitsManager { } } - // handleKeyEvent(e) - // { - // if (e.originalEvent.code === 'KeyC' && e.originalEvent.ctrlKey) - // { - // this.copyUnits(); - // } - // else if (e.originalEvent.code === 'KeyV' && e.originalEvent.ctrlKey) - // { - // this.pasteUnits(); - // } - // } + copyUnits() + { + this.#copiedUnits = this.getSelectedUnits(); + } - // copyUnits() - // { - // this.#copiedUnits = this.getSelectedUnits(); - // } + pasteUnits() + { + for (let idx in this.#copiedUnits) + { + var unit = this.#copiedUnits[idx]; + cloneUnit(unit.ID); + } + } - // pasteUnits() - // { - // for (let idx in this.#copiedUnits) - // { - // var unit = this.#copiedUnits[idx]; - // cloneUnit(unit.ID); - // } - // } - - attackUnit(ID: number) { + 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 +215,70 @@ 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(), 1000); // 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(), 1000); // TODO find better method, may fail + } + + #updateUnitControlPanel() { + /* Update the unit control panel */ + if (this.getSelectedUnits().length > 0) { + getUnitControlPanel().show(); + getUnitControlPanel().update(this.getSelectedLeaders().concat(this.getSelectedSingletons())); + } + else { + getUnitControlPanel().hide(); + } + } } \ No newline at end of file diff --git a/client/views/index.ejs b/client/views/index.ejs index f53a4638..dd19f256 100644 --- a/client/views/index.ejs +++ b/client/views/index.ejs @@ -19,6 +19,7 @@ <%- include('unitcontrolpanel.ejs') %> <%- include('visibilitycontrolpanel.ejs') %> <%- include('connectionstatuspanel.ejs') %> + <%- include('mouseinfopanel.ejs') %> diff --git a/client/views/mouseinfopanel.ejs b/client/views/mouseinfopanel.ejs new file mode 100644 index 00000000..4e6bac2c --- /dev/null +++ b/client/views/mouseinfopanel.ejs @@ -0,0 +1,6 @@ +

+
---° / --- NM
+
---° / --- NM
+
---° / --- NM
+
---° / --- NM
+
\ No newline at end of file diff --git a/client/views/selectionscroll.ejs b/client/views/selectionscroll.ejs index 4827bd8c..3a78b420 100644 --- a/client/views/selectionscroll.ejs +++ b/client/views/selectionscroll.ejs @@ -1,8 +1,11 @@
- +
+
+ +
\ No newline at end of file diff --git a/client/views/unitcontrolpanel.ejs b/client/views/unitcontrolpanel.ejs index 9aa44f35..ab0f7820 100644 --- a/client/views/unitcontrolpanel.ejs +++ b/client/views/unitcontrolpanel.ejs @@ -1,12 +1,52 @@ -
-
-
-
-
-
-
+
+
+
+
+
+
+
Selected units
-
Olympus-1
-
Olympus-2
+ +
+ +
+ +
+
Create formation
+
Undo formation
+
+
+ +
\ No newline at end of file diff --git a/installer/DCSOlympus.iss b/installer/DCSOlympus.iss index d10a00c2..f123559f 100644 --- a/installer/DCSOlympus.iss +++ b/installer/DCSOlympus.iss @@ -2,7 +2,7 @@ [Setup] AppName=DCS Olympus -AppVerName=DCS Olympus Alpha v0.0.2 +AppVerName=DCS Olympus Alpha v0.0.3 DefaultDirName={usersavedgames}\DCS.openbeta DefaultGroupName=DCSOlympus OutputBaseFilename=DCSOlympus diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index dea45dc1..790bd1a5 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -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,7 +54,28 @@ 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) @@ -69,10 +90,12 @@ function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions) } -- 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 +194,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 = {} @@ -275,24 +298,77 @@ function Olympus.clone(ID) 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.setTask(ID, taskOptions) + Olympus.notify("Olympus.setTask " .. ID .. " " .. Olympus.serializeTable(taskOptions), 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 + 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) \ No newline at end of file diff --git a/scripts/OlympusMission.lua b/scripts/OlympusMission.lua index 30533cb4..87bd6b90 100644 --- a/scripts/OlympusMission.lua +++ b/scripts/OlympusMission.lua @@ -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() diff --git a/scripts/payloadNames.js b/scripts/payloadNames.js index 37990c29..2f0cf869 100644 --- a/scripts/payloadNames.js +++ b/scripts/payloadNames.js @@ -1 +1,2235 @@ -payloadNames = {"A-10A": ["MK-84*2 , LAU-68*2 , AGM-65K*2", "LAU-68-MK5*6", "AGM-65K*2,Mk20*6,AIM-9*2,ECM", "Mk-82*6,AIM-9*2,ECM", "Mk20*6,AIM-9*2,ECM", "AGM-65D*4,AIM-9*2,ECM", "AGM-65K, AGM-65D", "ECM", "Mk-82*6, Mk-84*2", "Mk20*8", "AGM-65K*2,Mk-84*2,AIM-9*2,ECM", "AGM-65K*2,Mk-82*6,AIM-9*2,ECM", "Mk-84*2,AIM-9*2,ECM", "AGM-65K*2,Mk84*2,Mk82*4,AIM-9M*2,ECM", "AGM-65H*6,Mk82*10,AIM-9M*2,ECM"], "A-10C": ["LAU-68 42 rkt M156 WP, AIM-9*2, ECM", "AGM-65D*4, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", "LAU-131 98 rkt M156 WP, AIM-9*2,ECM", "SUU-25*9,AIM-9*2,ECM", "AGM-65D*4, CBU-97*4,TGP, ECM, AIM-9*2", "Mk-82AIR*8,AIM-9*2,ECM", "MK-84*2,LAU-68*2,AGM-65K*2", "BDU-33*6, TGM-65H, TGM-65D, TGP, BDU-50LGB*2, CAP-9*1", "Mk-82*6,Mk-84*2,AIM-9*2,ECM", "Mk-84*4,AIM-9*2,ECM", "Mk-82*8,AIM-9*2,ECM", "BDU-33*12, TGP, CAP-9*1", "AGM-65D*4,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", "AGM-65D*4,GBU-12*2,GBU-38,Mk-82,AIM-9,TGP,ECM", "AGM-65D*2, AGM-65H*2, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", "BDU-50HD*6,Mk1*7,TGP, CAP-9*1", "AGM-65H*4, CBU-97*4,TGP, ECM, AIM-9*2", "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", "AGM-65K*2,GBU-38*4,AIM-9*2,TGP,ECM", "BDU-33*6, TGP, CAP-9*1", "AGM-65D*2,AGM-65H*2,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK151*7", "TGP", "BDU-33*6, TGP, CAP-9*1, BDU-50LD*2", "GBU-12*6,GBU-10*2,TGP, AIM-9*2", "TGP, CBU-87*3, M151*28, AIM-9*2, ECM", "AGM-65D*4,Mk-82*6,CBU-87*2,TGP,AIM-9*2,Mk151*7", "PGM- GBU-10*2,GBU-12*4,AIM-9*2,TGP,ECM", "AGM-65D*4,TGP, ECM, AIM-9*2", "TGP, CAP-9*1, CATM-65K*1, TGM-65G*1", "AGM-65G*2,GBU-31*2,AIM-9*2,TGP,ECM", "TGP, M151*14, Mk-82*2, Mk-82AIR*2, AIM-9*2, ECM", "PGM- GBU-10*4, AGM-65K*2,AIM-9*2,TGP,ECM", "AGM-65D*2,AGM-65H*2,Mk-82AIR*6,CBU-87*2,Mk151*7,AIM-9*2,TGP,ECM", "GBU-31*2,GBU-38*2, AGM-65H*2, AIM-9*2,TGP, ECM", "CBU-103*4, M151*14, AIM-9*2, ECM", "CBU-87*4, M151*42, AIM-9*2, ECM", "AGM-65D*6, CBU-97*4,TGP, ECM, AIM-9*2", "CBU-87*2, M151*14, MK-82AIR*6, AIM-9*2,ECM", "AGM-65D*4, CBU-105*4,TGP, ECM, AIM-9*2", "BDU-50HD*2,BDU-50LGB*2,TGP, CAP-9*1", "CBU-87*4, M151*28, AIM-9*2,ECM", "M151*98, Mk-82*2,AIM-9*2,ECM", "AGM-65D*2,AGM-65H*2,GBU-12,GBU-38,MK82*3,MK82AIR*3,MK5*7,TGP,AM-9*2", "TGP, M151*42, Mk-82*6, Mk-82AIR*6, AIM-9*2, ECM", "TGP, M151*84, Mk-82*2,AIM-9*2, ECM", "BDU-50LD*2, BDU-50HD*2,CATM-65K, TGM-65G, TGP, CAP-9*1", "TGP, M151*49, Mk-82*2, CBU-87*2, AIM-9*2, ECM", "TGP, CAP-9*1, BDU-50LGB*4", "GBU-12*14,TGP, AIM-9*2", "AGM-65D*3, AGM-65H*3, CBU-97*4,TGP, ECM, AIM-9*2", "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,TGP,ECM", "AGM-65D*4, CBU-105*2,CBU-97*2, TGP, ECM, AIM-9*2", "AGM-65D*2,Mk-82*6,AIM-9*2,ECM", "AGM-65D*2,AGM-65H*2,TGP, ECM, AIM-9*2", "GBU-38*4,GBU-31*2,TGP, AIM-9*2", "AGM-65D*4,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK5*7", "AGM-65G,AGM-65K,GBU-10*2,AIM-9*2,TGP,ECM", "AGM-65G,AGM-65D,Mk-82*7,AIM-9*2,ECM", "GBU-31*2,GBU-38*4,AIM-9*2,TGP,ECM, AIM-9*2", "AGM-65K*2,GBU-12*8,AIM-9M*2.ECM,TGP", "AGM-65D*6,GBU-12*4,AIM-9M*2,ECM,TGP"], "A-10C_2": ["LAU-68 42 rkt M156 WP, AIM-9*2, ECM", "AGM-65D*4, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", "LAU-131 98 rkt M156 WP, AIM-9*2,ECM", "SUU-25*9,AIM-9*2,ECM", "AGM-65D*4, CBU-97*4,TGP, ECM, AIM-9*2", "Mk-82AIR*8,AIM-9*2,ECM", "MK-84*2,LAU-68*2,AGM-65K*2", "BDU-33*6, TGM-65H, TGM-65D, TGP, BDU-50LGB*2, CAP-9*1", "Mk-82*6,Mk-84*2,AIM-9*2,ECM", "Mk-84*4,AIM-9*2,ECM", "Mk-82*8,AIM-9*2,ECM", "BDU-33*12, TGP, CAP-9*1", "AGM-65D*4,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", "AGM-65D*4,GBU-12*2,GBU-38,Mk-82,AIM-9,TGP,ECM", "AGM-65D*2, AGM-65H*2, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", "BDU-50HD*6,Mk1*7,TGP, CAP-9*1", "AGM-65H*4, CBU-97*4,TGP, ECM, AIM-9*2", "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", "AGM-65K*2,GBU-38*4,AIM-9*2,TGP,ECM", "BDU-33*6, TGP, CAP-9*1", "AGM-65D*2,AGM-65H*2,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK151*7", "TGP", "BDU-33*6, TGP, CAP-9*1, BDU-50LD*2", "GBU-12*6,GBU-10*2,TGP, AIM-9*2", "TGP, CBU-87*3, M151*28, AIM-9*2, ECM", "AGM-65D*4,Mk-82*6,CBU-87*2,TGP,AIM-9*2,Mk151*7", "PGM- GBU-10*2,GBU-12*4,AIM-9*2,TGP,ECM", "AGM-65D*4,TGP, ECM, AIM-9*2", "TGP, CAP-9*1, CATM-65K*1, TGM-65G*1", "AGM-65G*2,GBU-31*2,AIM-9*2,TGP,ECM", "TGP, M151*14, Mk-82*2, Mk-82AIR*2, AIM-9*2, ECM", "PGM- GBU-10*4, AGM-65K*2,AIM-9*2,TGP,ECM", "AGM-65D*2,AGM-65H*2,Mk-82AIR*6,CBU-87*2,Mk151*7,AIM-9*2,TGP,ECM", "GBU-31*2,GBU-38*2, AGM-65H*2, AIM-9*2,TGP, ECM", "CBU-103*4, M151*14, AIM-9*2, ECM", "CBU-87*4, M151*42, AIM-9*2, ECM", "AGM-65D*6, CBU-97*4,TGP, ECM, AIM-9*2", "CBU-87*2, M151*14, MK-82AIR*6, AIM-9*2,ECM", "AGM-65D*4, CBU-105*4,TGP, ECM, AIM-9*2", "BDU-50HD*2,BDU-50LGB*2,TGP, CAP-9*1", "CBU-87*4, M151*28, AIM-9*2,ECM", "M151*98, Mk-82*2,AIM-9*2,ECM", "AGM-65D*2,AGM-65H*2,GBU-12,GBU-38,MK82*3,MK82AIR*3,MK5*7,TGP,AM-9*2", "TGP, M151*42, Mk-82*6, Mk-82AIR*6, AIM-9*2, ECM", "TGP, M151*84, Mk-82*2,AIM-9*2, ECM", "BDU-50LD*2, BDU-50HD*2,CATM-65K, TGM-65G, TGP, CAP-9*1", "TGP, M151*49, Mk-82*2, CBU-87*2, AIM-9*2, ECM", "TGP, CAP-9*1, BDU-50LGB*4", "GBU-12*14,TGP, AIM-9*2", "AGM-65D*3, AGM-65H*3, CBU-97*4,TGP, ECM, AIM-9*2", "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,TGP,ECM", "AGM-65D*4, CBU-105*2,CBU-97*2, TGP, ECM, AIM-9*2", "AGM-65D*2,Mk-82*6,AIM-9*2,ECM", "AGM-65D*2,AGM-65H*2,TGP, ECM, AIM-9*2", "GBU-38*4,GBU-31*2,TGP, AIM-9*2", "AGM-65D*4,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK5*7", "AGM-65G,AGM-65K,GBU-10*2,AIM-9*2,TGP,ECM", "AGM-65G,AGM-65D,Mk-82*7,AIM-9*2,ECM", "GBU-31*2,GBU-38*4,AIM-9*2,TGP,ECM, AIM-9*2", "AGM-65K*2,GBU-12*8,AIM-9M*2.ECM,TGP", "AGM-65D*6,GBU-12*4,AIM-9M*2,ECM,TGP", "AGM-65E*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,ECM,TGP", "AGM-65E*2,CBU-97*4,AIM-9M*2,ECM,TGP", "AGM-65E*2,CBU-97*4,AIM-9M*2,ECM,M151 APKWS*7,TGP", "AGM-65E*2,CBU-105*4,AIM-9M*2,ECM,M151 APKWS*7,TGP", "Mk-82*4,Mk-8AIR*4,AIM-9*2,ECM", "Mk-82*20,AIM-9*2,ECM", "Mk-82*6,AIM-9*2,TGP,ECM", "Mk-84*6,AIM-9*2,TGP,ECM", "Mk-82AIR*6,Mk-8AIR*4,M151*1,TGP,AIM-9*2,ECM", "GBU-38*4,M151 APKWS*7,AGM-65D*1,AGM-65H*1,TGP,AIM-9*2,ECM", "GBU-38*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", "GBU-12*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", "GBU-12*2,GBU-38*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", "GBU-10*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", "GBU-31*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", "GBU-54*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", "GBU-54*4,M151 APKWS*7,AGM-65D*4,TGP,AIM-9*2,ECM"], "AH-64D_BLK_II": ["4 * Fuel Tank 230 gal", "2 * M261: M151 (6PD), 2 * Hellfire station: 4*AGM-114K", "4 * Hellfire station: 4*AGM-114K", "4 * M261: M151 (6PD)", "2 * M261: M151 (6PD), 2 * Fuel Tank 230 gal", "2 * Fuel Tank 230 gal, 2 * Hellfire station: 4*AGM-114K", "2 * M261: A/B - M151 (6PD), E - M274 (6SK), 2 * Hellfire station: 4*AGM-114K", "2 * M261: A/B - M151 (6PD), E - M257 (6IL), 2 * Hellfire station: 4*AGM-114K", "2 * M261: C - M257 (6IL), D/E - M151 (6PD), 2 * Hellfire station: 4*AGM-114K", "2 * M261: C - M274 (6SK), D/E - M151 (6PD), 2 * Hellfire station: 4*AGM-114K"], "AJS37": ["Battlefield Air Interdiction: RB-75*4, RB-24J*2, XT", "Anti-ship: RB-04E*2, RB-74*2, XT", "Anti-ship (Heavy Mav): RB-75T*4, XT", "Hard Target (Countermeasures): RB-05, XT, KB, U22", "Hard Target (MAV): RB-75T*2, RB-74*2, XT", "Ferry Flight: XT", "CAS (75 GUN): RB-75*2, AKAN", "CAP: RB-74*4, XT", "Countermeasures Escort: U/22A, KB", "Strike: BK90 (MJ1)*2, RB-74*2, XT", "CAS: AKAN, RB-05A", "CAP (6 AAM): RB-74*4, RB-24J*2, XT", "Rocket Half Load HE: ARAK HE*2, RB-74*2, XT", "CAP / Intecept: RB-05A*2, RB-74*2, XT", "Bombs Low-drag: SB71LD*16, RB-24J*2, XT", "SEAD: RB-75T*2, U22/A, KB, XT", "Anti-Ship (Modern): RB-15F*2, RB-74*2, XT", "New Payload", "CAP (AJ37): RB-24J*2", "ECM Escort Anti-ship: RB-04E, KB, RB-74*2, XT", "Bombs High-drag: SB71HD*16, XT, RB-24J", "Anti-ship (Light Mav): RB-75*4, XT", "Rocket Full Load HE: ARAK HE*4, RB-24J, XT", "Illumination: LYSB*8, XT", "Anti-ship (RB05): RB-05A*2, RB-74*2, XT", "CAP (Gun): AKAN*2, RB-74*2, XT", "Hard Target: RB-05A*2, RB-74*2, XT", "RB-05*2, XT", "CAS: ARAK M70 HE*4, XT", "Runway Strike: SB71HD*16, RB-24J, XT"], "AV8BNA": ["H-L-H 2", "AFAC 1", "AS 1", "H-M-H 3", "Interdiction 1 (H-H-H-H)", "Interdiction 2 (H-H-H-H)", "AFAC 3", "Interdiction 3 (H-L-L-H)", "H-M-H 1", "H-L-H 1", "PGM 2 (H-H-H-H)", "L-L-L 1", "AFAC 2", "H-M-H 2", "Area Suppression", "Rockets 1", "AS 2", "Helo Escort 1", "PGM 1 (H-H-H-H)", "Helo Escort 2", "PGM 3 (H-H-H-H)", "Anti Armor", "RA 1 (H-M-M-H)", "Stand Off 1", "Stand Off 2", "Stand Off 3", "Iron Hand 1", "Iron Hand 2"], "C-101CC": ["2*AIM-9P, DEFA 553 CANNON (I)", "2*AIM-9M, DEFA 553 CANNON (I)", "2*AIM-9P, DEFA 533 CANNON (II)", "2*AIM-9P, AN-M3 CANNON (IV)", "2*R.550 MAGIC, DEFA 553 CANNON", "2*AIM-9M, AN-M3 CANNON (III)", "2*AIM-9P, DEFA 553 CANNON", "2*R.550 MAGIC, DEFA 553 CANNON (III)", "2*AIM-9P, 2*BELOUGA, DEFA 553 CANNON", "2*AIM9-P, 2*SEA EAGLE, DEFA-553 CANNON", "2*AIM-9M 2*SEA EAGLE, AN-M3 CANNON", "2*AIM-9M, AN-M3 CANNON", "2*BELOUGA,2*BDU-33, DEFA-553 CANNON", "2* SEA EAGLE, DEFA-553 CANNON", "2*AIM-9P, 2*BR-250,2*MK-82, DEFA 553 CANNON", "2*R.550 MAGIC, 2*SEA EAGLE , DEFA-553 CANNON", "2*R.550 MAGIC, DEFA 553 CANNON (IV)", "2*BELOUGA, 2*BR-500, DEFA 553 CANNON", "2*AIM-9M, DEFA 553 CANNON (IV)", "2*R.550 MAGIC, AN-M3 CANNON (II)", "2*R550 Magic, DEFA 553 CANNON (I)", "2*AIM-9M ,2*BELOUGA,2*BIN-200, AN-M3 CANNON", "2*AIM-9M, 2*LAU 68, 2*MK-82, DEFA 553 CANNON", "2*AIM-9P, AN-M3 CANNON (III)", "2*AIM-9M, DEFA 533 CANNON (II)", "2*R.550 MAGIC, 2*BR-250, 2*BDU-33, DEFA 553 CANNON"], "C-101EB": ["Smoke System: White Smoke", "Smoke System: White Smoke+Red Colorant", "Smoke System: White Smoke+Yellow Colorant"], "H-6J": ["YJ-12 x 2", "YJ-12 x 4", "YJ-83K x 6", "250-2 HD Bomb x 12 in Bay", "250-2 HD Bomb x 24 in Bay", "250-3 LD Bomb x 36", "KD-63 x 4", "KD-20 x 6", "KD-20 x 4", "KD-63 x 2, KD-20 x 4", "KD-63 x 2, KD-20 x 2"], "J-11A": ["FAB-100x36,R-73x2,ECM", "FAB-250x8,R-73x2,ECM", "FAB-500x8,R-73x2,ECM", "S-8KOMx80,FAB-250x4,R-73x2,ECM", "S-13x20,FAB-250x4,R-73x2,ECM", "S-25x4,FAB-500x4,R-73x2,ECM", "R-27ERx4,R-27ETx2,R-73x2,ECM", "R-77x6,R-73x2,ECM", "R-27ERx6,R-73x2,ECM", "R-77x4,R-27ETx2,R-73x2,ECM", "R-77x4,R-27ERx2,R-73x2,ECM", "BetAB-500ShPx6,R-73x2,ECM", "R-73x4,ECM", "R-77x2,R-27ETx2,R-73x2,ECM", "R-77x6,R-73x4", "R-77x2,R-27ETx2,R-27ERx2,R-73x2,ECM", "R-27ETx2,R-27ERx4,R-73x2,ECM", "S-8TsMx80,FAB-250x4,R-73x2,ECM", "S-8OFP2x80,FAB-250x4,R-73x2,ECM", "FAB-250x18,R-73x2,ECM", "2*S8-KOMx2, R-73x2, ECM", "2*S8-OFP2x2, R-73x2, ECM", "FAB-250x4, 2*FAB-500x2, R-73x2", "FAB-250x4, 2*FAB-250x2, R-73x2", "RBK-250 HEAT/APx2, RBK-250 HE/Fragx2, R-73x2"], "JF-17": ["PL-5Ex2, C802AKx2, 800L Tank", "PL-5Ex2, C-701 IRx2, 1100L Tank, 800L Tank", "PL-5Ex2, SD-10x2, 1100L Tankx2, WMD7", "PL-5Ex2, LD-10x2, 1100L Tankx2, WMD7", "PL-5Ex2, 800L Tank, WMD7", "PL-5Ex2, GBU-10x2, WMD7", "PL-5Ex2, 2*GBU-12x2, 800L Tank, WMD7", "PL-5Ex2, 2*Mk-82x2, Mk-83x2, 800L Tank", "PL-5Ex2, 1100L Tankx2, 800L Tank", "PL-5Ex2, WMD7, CM802AKGx2, 800L Tank, DL", "PL-5Ex2, C-701 CCDx2, 1100L Tank, 800L Tank", "PL-5Ex2, GBU-12x2, 1100L Tank, WMD7", "PL-5Ex2, 2*GBU-12x2, GBU-16x2, WMD7", "PL-5Ex2, 1100L Tankx2, WMD7", "PL-5Ex2, WMD7, 800L Tankx2, SPJ, 2*LD-10", "PL-5Ex2, LS-6x2, 1100L Tankx2, WMD7", "PL-5Ex2, C-701 IRx2, 1100L Tankx2, WMD7", "PL-5Ex2, GBU-12x2, 1100L Tankx2, WMD7", "PL-5Ex2, 2*LD-10x2, 1100L Tankx2, SPJ", "PL-5Ex2, LD-10x2, 1100L Tankx2, SPJ", "PL-5Ex2, 2*LD-10x2, LS-6x2, SPJ", "PL-5Ex2, 2*LD-10x2, GB-6-HEx2, SPJ", "PL-5Ex2, C-701 IRx2, 800L Tankx2, WMD7", "PL-5Ex2, C-701 CCDx2, 1100L Tankx2, WMD7", "PL-5Ex2, C-701 CCDx2, 800L Tankx2, WMD7", "PL-5Ex2, 2*GBU-12x2, 1100L Tank, WMD7", "PL-5Ex2, C-701 IRx2, 1100L Tank, WMD7", "PL-5Ex2, C-701 IRx2, 800L Tank, WMD7", "PL-5Ex2, C-701 CCDx2, 1100L Tank, WMD7", "PL-5Ex2, C-701 CCDx2, 800L Tank, WMD7", "PL-5Ex2, C-701 IRx2, LS-6x2, 800L Tank", "PL-5Ex2, C-701 IR/CCD, GB-6-HEx2, 800L Tank", "PL-5Ex2, C-701 IR/CCD, GB-6-SFWx2, 800L Tank", "PL-5Ex2, WMD7, GB-6-SFWx2, 800L Tank, BRM1", "PL-5Ex2, WMD7, GB-6-SFWx2, 800L Tank, GBU-12", "PL-5Ex2, 2*Mk-82SEx2, Mk-83x3", "PL-5Ex2, Mk-84x3", "PL-5Ex2, 2*Mk5x2, 800L Tank", "PL-5Ex2, Unguided 90mmx2, 800L Tank", "PL-5Ex2, 2*Mk5x2, Mk-83x3", "PL-5Ex2, BRM1x2, 1100L Tank, WMD7", "PL-5Ex2, 2x1100L Tank", "PL-5Ex2, SD-10x2, 2x1100L Tank", "PL-5Ex2, 2*SD-10x2, 2x1100L Tank", "PL-5Ex2, 800L Tank", "PL-5Ex2, SD-10x2, 800L Tank", "PL-5Ex2, 2*SD-10x2, 800L Tank", "PL-5Ex2, SD-10x2, SPJ", "PL-5Ex2, SPJ", "PL-5Ex2, 2*SD-10x2, SPJ", "PL-5Ex2", "PL-5Ex2, SD-10x2", "PL-5Ex2, 2*SD-10", "PL-5Ex2, SD-10x2, SPJ, 1100L Tankx2", "PL-5Ex2, 2*SD-10x2, 1100L Tankx2, 800L Tank", "PL-5Ex2, SD-10x2, 1100L Tankx2, 800L Tank", "PL-5Ex2, GBU-16x2, BRM1x2, WMD7", "PL-5Ex2, WMD7", "PL-5Ex2, 2*LD-10, GB-6x2, 2*SD-10, SPJ", "PL-5Ex2, C-701 CCDx2, SPJ", "PL-5Ex2, 2*LD-10, CM802AKGx2, 2*SD-10, DL", "PL-5Ex2, 2*MK-82x2, MK-83x2, MK-84", "PL-5Ex2, LS-6x2, GB-6x2, 800L Tank", "PL-5Ex2, 2*GBU-12x2, LS-6x2, WMD7", "PL-5Ex2, 2*GBU-12x2, GB-6x2, WMD7", "PL-5Ex2, 2*Type-200Ax2", "PL-5Ex2, Type-200Ax2", "PL-5Ex2, 2*LS6-250x2, 800L Tankx2, WMD7", "PL-5Ex2, 2*LS6-250x2, 800L Tank, 1100L Tankx2", "PL-5Ex2, 2*LS6-100x2, 1100L Tankx2, WMD7", "PL-5Ex2, 2*LS6-100x2, 800L Tankx2, WMD7"], "WingLoong-I": ["AKD-10 x 2"], "Christen Eagle II": ["Smoke - white"], "F-16C_50": ["AIM-120B*2, AIM-9M*4, FUEL*3", "AIM-120B*4, AIM-9M*2, FUEL*3", "AIM-120B*6, FUEL*3", "AIM-120C*2, AIM-9X*4, FUEL*2", "AIM-120C*4, AIM-9X*2, FUEL*3", "AIM-120C*4, AIM-9X*2, FUEL*3, TGP", "AIM-120C*4, AIM-9X*2, FUEL*2", "AIM-120C*6, FUEL*3", "AIM-120C*4, AIM-9X*2, FUEL*2, ECM", "AIM-120C*4, AIM-9X*2, FUEL*2, ECM, TGP", "AIM-120C*6, FUEL*2, ECM", "AIM-120C*6, FUEL*2, ECM, TGP", "AIM-120C*6, FUEL*2", "AIM-120C*6, FUEL*3, TGP", "AIM-120C*2, AIM-9X*2, AGM-65D*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, AGM-65H*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, AGM-65H*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, AGM-65D*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, CBU-97*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, MK-82*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, CBU-87*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, MK-82HD*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, CBU-103*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, CBU-105*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, MK-82*6, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, MK-82HD*6, FUEL*2, ECM, TGP", "AIM-120*2, AIM-9X*2, MK-82SE*4, FUEL*2, ECM, TGP", "AIM-120*2, AIM-9X*2, MK-82SE*6, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, MK-84*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, MK-82P*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-12*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-12*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-10*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-24*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-31-1B*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-31-3B*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-38*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, GBU-38*4, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, AGM-65K*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, AGM-65G*2, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, AGM-65G, AGM-65K, FUEL*2, ECM, TGP", "AIM-120C*2, AIM-9X*2, AGM-88C*2, FUEL*3, TGP, HTS", "AIM-120C*2, AIM-9X*2, AGM-88C*2, FUEL*2, ECM, TGP, HTS", "AIM-120C*2, AIM-9X*2, AGM-88C*4, ECM, TGP, HTS", "AIM-120C*4, AGM-88C*2, FUEL*3, TGP, HTS", "AIM-120C*4, AGM-88C*2, FUEL*2, ECM, TGP, HTS", "AIM-120C*4, AGM-88C*4, ECM, TGP, HTS", "AIM-120C*2, AIM-9X*2, MK-61*2, FUEL*2, ECM, TGP"], "F-5E-3": ["Mk-82LD*4,AIM-9P*2,Fuel 275", "AIM-9P*2, Fuel 275*3", "AIM-9P5*2, Fuel 275*3", "AIM-9P*2, Fuel 150*3", "AIM-9P5*2, Fuel 150*3", "Mk-82SE*4,AIM-9P*2,Fuel 275", "CBU-52B*4,AIM-9P*2,Fuel 275", "LAU-3 HE*4,AIM-9P*2,Fuel 275", "LAU-3 HEAT*4,AIM-9P*2,Fuel 275", "LAU-68 HE*4,AIM-9P*2,Fuel 275", "LAU-68 HEAT*4,AIM-9P*2,Fuel 275", "M-117*4,AIM-9P*2,Fuel 275", "GBU-12*4,AIM-9P*2,Fuel 275", "CBU-52B*5,AIM-9*2", "Mk-82LD*5,AIM-9*2", "Mk-82SE*5,AIM-9*2", "Mk-82LD*7,AIM-9P*2, Fuel 275*2", "Mk-82SE*7,AIM-9P*2, Fuel 275*2", "LAU-3 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", "LAU-68 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", "M-117*5,AIM-9*2", "AIM-9P*2, Fuel 275", "AIM-9P*2, Fuel 150", "AIM-9P5*2, Fuel 275", "AIM-9P5*2, Fuel 150", "AIM-9B*2, Fuel 275", "AIM-9B*2, Fuel 150", "AIM-9B*2, Fuel 275*3", "AIM-9B*2, Fuel 150*3", "AN/ASQ-T50, AIM-9P, Fuel 150", "AIM-9B*2", "AIM-9P*2", "AIM-9P5*2", "Antiship Mk82"], "F-5E": ["Mk-82LD*4,AIM-9P*2,Fuel 275", "AIM-9P*2, Fuel 275*3", "AIM-9P5*2, Fuel 275*3", "AIM-9P*2, Fuel 150*3", "AIM-9P5*2, Fuel 150*3", "Mk-82SE*4,AIM-9P*2,Fuel 275", "CBU-52B*4,AIM-9P*2,Fuel 275", "LAU-3 HE*4,AIM-9P*2,Fuel 275", "LAU-3 HEAT*4,AIM-9P*2,Fuel 275", "LAU-68 HE*4,AIM-9P*2,Fuel 275", "LAU-68 HEAT*4,AIM-9P*2,Fuel 275", "M-117*4,AIM-9P*2,Fuel 275", "GBU-12*4,AIM-9P*2,Fuel 275", "CBU-52B*5,AIM-9*2", "Mk-82LD*5,AIM-9*2", "Mk-82SE*5,AIM-9*2", "Mk-82LD*7,AIM-9P*2, Fuel 275*2", "Mk-82SE*7,AIM-9P*2, Fuel 275*2", "LAU-3 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", "LAU-68 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", "M-117*5,AIM-9*2", "AIM-9P*2, Fuel 275", "AIM-9P*2, Fuel 150", "AIM-9P5*2, Fuel 275", "AIM-9P5*2, Fuel 150", "AIM-9B*2, Fuel 275", "AIM-9B*2, Fuel 150", "AIM-9B*2, Fuel 275*3", "AIM-9B*2, Fuel 150*3", "AN/ASQ-T50, AIM-9P, Fuel 150", "AIM-9B*2", "AIM-9P*2", "AIM-9P5*2"], "F-86F Sabre": ["120gal Fuel*2", "200gal Fuel*2", "120gal Fuel*2, 200gal Fuel*2", "GAR-8*2", "120gal Fuel*2, GAR-8*2", "HVAR*16", "200gal Fuel*2, HVARx2*4", "AN-M64*2", "200gal Fuel*2, AN-M64*2", "M117*2"], "F-14A-135-GR": ["XT*2", "AIM-54A-MK47*6, AIM-9L*2, XT*2", "AIM-7F*6, AIM-9L*2, XT*2", "AIM-54A-MK47*4, AIM-7F*2, AIM-9L*2, XT*2", "AIM-54A-MK47*2, AIM-7F*1, AIM-9L*4, XT*2", "AIM-54A-MK47*4, AIM-9L*4, XT*2", "AIM-54A-MK47*4, AIM-9M*4, XT*2", "AIM-54A-MK60*4, AIM-9M*4, XT*2", "AIM-7F*4, AIM-9L*4, XT*2", "AIM-7F*4, AIM-9L*4, XT*2", "BDU-33*14", "BDU-33*12", "GBU-10*2", "GBU-12*4", "GBU-16*4", "GBU-24*2", "Mk-84*4", "Mk-83*4", "Mk-82*4", "Mk-82*14", "Mk-81*14", "Mk-20*4", "Mk-82AIR*4", "Zuni*12", "Zuni*28", "LUU-2*24", "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, XT*2, Mk-82*2", "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, XT*2, Mk-82*1", "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, XT*2, Mk-20*2", "AIM-7M*1, AIM-9M*2, XT*2, GBU-12*2, LANTIRN", "AIM-7M*1, AIM-9M*2, XT*2, GBU-24*1, LANTIRN", "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-20*2, LANTIRN"], "F-14A-95-GR": ["AIM-54A-MK47*6, AIM-9L*2", "AIM-54A-MK47*6, AIM-9L*2", "AIM-54A-MK60*6, AIM-9L*2", "AIM-7F*6, AIM-9L*2", "AIM-7F*6, AIM-9L*2", "AIM-54A-MK47*4, AIM-7F*2, AIM-9L*2", "AIM-54A-MK47*4, AIM-7F*2, AIM-9L*2", "AIM-54A-MK60*4, AIM-7F*2, AIM-9L*2", "AIM-54A-MK47*2, AIM-7F*1, AIM-9L*4", "AIM-54A-MK47*2, AIM-7F*1, AIM-9L*4", "AIM-54A-MK60*2, AIM-7F*1, AIM-9L*4", "AIM-54A-MK47*4, AIM-9L*4", "AIM-54A-MK47*4, AIM-9L*4", "AIM-54A-MK60*4, AIM-9L*4", "AIM-7F*4, AIM-9L*4", "AIM-7F*4, AIM-9L*4", "AIM-54A-MK47*2, AIM-7F*3, AIM-9L*2", "AIM-54A-MK47*2, AIM-7F*3, AIM-9L*2", "AIM-54A-MK60*2, AIM-7F*3, AIM-9L*2", "BDU-33*14", "BDU-33*12", "GBU-10*2", "GBU-12*4", "GBU-16*4", "GBU-24*2", "Mk-84*4", "Mk-83*4", "Mk-82*4", "Mk-82*14", "Mk-81*14", "Mk-20*4", "Mk-82AIR*4", "Zuni*12", "Zuni*28", "LUU-2*24", "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, Mk-82*2", "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, Mk-82*1", "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, Mk-20*2"], "F-14B": ["XT*2", "AIM-54A-MK47*6, AIM-9M*2, XT*2", "AIM-54A-MK47*6, AIM-9L*2, XT*2", "AIM-54A-MK60*6, AIM-9M*2, XT*2", "AIM-54C-MK47*6, AIM-9M*2, XT*2", "AIM-7M*6, AIM-9M*2, XT*2", "AIM-7M*6, AIM-9L*2, XT*2", "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", "AIM-54A-MK47*4, AIM-7M*2, AIM-9L*2, XT*2", "AIM-54A-MK60*4, AIM-7M*2, AIM-9M*2, XT*2", "AIM-54C-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", "AIM-54A-MK47*2, AIM-7M*1, AIM-9M*2, AIM-9L*2, XT*2", "AIM-54A-MK47*2, AIM-7M*1, AIM-9M*4, XT*2", "AIM-54A-MK60*2, AIM-7M*1, AIM-9M*4, XT*2", "AIM-54C-MK47*2, AIM-7M*1, AIM-9M*4, XT*2", "AIM-54A-MK47*4, AIM-9M*2, AIM-9L*2, XT*2", "AIM-54A-MK47*4, AIM-9M*4, XT*2", "AIM-54A-MK60*4, AIM-9M*4, XT*2", "AIM-54C-MK47*4, AIM-9M*4, XT*2", "AIM-7M*4, AIM-9M*2, AIM-9L*2, XT*2", "AIM-7M*4, AIM-9L*4, XT*2", "AIM-54A-MK47*2, AIM-7M*3, AIM-9M*2, XT*2", "AIM-54A-MK47*2, AIM-7M*3, AIM-9M*2, XT*2", "AIM-54A-MK60*2, AIM-7M*3, AIM-9M*2, XT*2", "AIM-54C-MK47*2, AIM-7M*3, AIM-9M*2, XT*2", "BDU-33*14", "BDU-33*12", "GBU-10*2", "GBU-12*4", "GBU-16*4", "GBU-24*2", "Mk-84*4", "Mk-83*4", "Mk-82*4", "Mk-82*14", "Mk-81*14", "Mk-20*4", "Mk-82AIR*4", "Zuni*12", "Zuni*28", "LUU-2*24", "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2", "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*1", "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-20*2", "AIM-7M*1, AIM-9M*2, XT*2, GBU-12*2, LANTIRN", "AIM-7M*1, AIM-9M*2, XT*2, GBU-24*1, LANTIRN", "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-20*2, LANTIRN"], "FA-18C_hornet": ["AIM-9M*6, AIM-7M*2, FUEL*3", "AIM-9M*6, AIM-7M*2, FUEL*2", "AIM-9M*2, MK-84*2, FUEL*2", "AIM-9M*2, MK-83*4, FUEL*2", "Carrier Landing", "AIM-9M*2, AIM-7M*4, FUEL*3", "AIM-9M*2, CBU-99*4, FUEL*2", "AIM-9M*2, MK-82SE*4, FUEL*2", "AIM-9M*2, MK-20*4, FUEL*2", "AIM-9M*2, MK-82*4, FUEL*2", "AIM-9M*2, AIM-7M*2, FUEL*2", "AIM-9M*2, MK-83*2, FUEL*2", "AIM-9M*2, ZUNI*4, FUEL*2", "AIM-9M*2, LAU-61*4, FUEL*2", "AIM-9M*2, LAU-68*4, FUEL*2", "AIM-9M*2, AIM-7M*2, FUEL*1", "AIM-9X*2, AIM-120C-5*1, GBU-31*4, ATFLIR, FUEL", "AIM-9X*2, AIM-120C-5*6, FUEL*3", "AIM-9X*2, AIM-120C-5*1, AGM-65D*4, ATFLIR, FUEL", "AIM-9X*2, AIM-120C-5*2, AGM-88C*2, FUEL", "AIM-9X*2, AIM-120C-5*1, GBU-38*4, GBU-12*4, ATFLIR, FUEL", "AIM-9X*2, AIM-120C-5*1, AGM-84E*2, DATALINK, ATFLIR, FUEL*2", "AIM-9M*2, AIM-120C-5*1, AGM-84D*4, ATFLIR, FUEL", "AIM-9M*2, ATFLIR, FUEL", "AIM-9M*2, ATFLIR, FUEL*2"], "I-16": ["6xRS-82", "2xFAB-100", "6xRS-82, 2xFAB-100", "6xRS-82, 2xDropTank-93L", "2xDropTank-93L"], "Ka-50": ["4xFAB-500", "2xFuel tank, 2xKMGU AP", "80xS-8 TsM", "2xFuel tank, 40xS-8", "80xS-8", "4xKMGU AT", "2xKMGU AP", "2xFuel tank, 12x9A4172", "2xFuel tank, 2xUPK-23", "12x9A4172, 40xS-8", "12x9A4172, 2xKMGU AT", "4xKMGU AP", "2xFAB-250, 2xFuel tank", "12x9A4172, 2xFAB-500", "20xS-13", "40xS-8", "12x9A4172, 2xUPK-23", "2xKMGU AT", "6x9A4172", "2xFuel tank, 2xKMGU AT", "4xFuel tank", "2xKMGU AP, 12x9A4172", "12x9A4172", "2xFuel tank", "40xS-8 TsM, 12x9A4172", "2xFAB-500", "40xS-8 TsM, 2xFuel tank", "4xUPK-23", "10xS-13", "40xS-8 TsM", "12x9A4172, 10xS-13", "2xFuel tank, 2xFAB-500", "2xFAB-250", "4xFAB-250", "2xFuel tank, 10xS-13", "2xFAB-250, 12x9A4172", "2xUPK-23"], "Ka-50_3": ["4xIgla", "2xKh-25ML, 10xS-13, 4xIgla", "12x9A4172, 40xS-8KOM, 4xIgla", "12x9A4172, 40xS-8OFP, 4xIgla", "12x9A4172, 40xS-13, 4xIgla", "80xS-8KOM, 4xIgla", "80xS-8OFP, 4xIgla", "20xS-20, 4xIgla", "4xUPK-23, 4xIgla", "10xS-13, 2xFAB-500, 4xIgla", "10xS-13, 2xFAB-250, 4xIgla", "80xS-8OM, 4xIgla", "80xS-8TsM, 4xIgla", "40xS-8OFP, 2xFuel, 4xIgla", "12x9A4172, 2xFuel, 4xIgla"], "L-39C": ["SAB-100x2", "R-3Sx2", "Smokes", "Central Smoke", "FAB-100x2", "S-5KOx32"], "L-39ZA": ["S-5KOx32", "S-5KOx64", "S-5KOx32, PTB-150x2", "S-5KOx32, PTB-350x2", "S-5KOx32, FAB-100x2", "OFAB-100 Jupiter x4, FAB-100x2", "FAB-100x2", "FAB-100x4", "OFAB-100 Jupiter x8", "FAB-100x2, PTB-150x2", "FAB-100x2, PTB-350x2", "PK-3x4", "PK-3x2, PTB-150x2", "R-60Mx2", "SAB-100x4", "R-3Sx2", "R-3Sx2, PK-3x2", "R-60Mx2, PK-3x2"], "M-2000C": ["Fox", "Fox / Magic (QRA)", "Alpha / S530D", "Fox / S530D / Magic", "Fox / S530D / Magic / Eclair", "Bravo", "Bravo / Magic", "Kilo", "Kilo / Magic", "Bravo / 4xMk-82 / Magic", "Bravo / GBU-12 / Magic", "Bravo / 2xGBU-12 / Magic", "Bravo / GBU-16 / Magic", "Bravo / GBU-24 / Magic", "Bravo / BAP-100 / Magic", "Bravo / 4xSnakeEye / Magic", "Fox / 4xMk-82 / Magic", "Kilo / 4xMk-82 / Magic"], "MB-339A": ["A - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*Mk.83 + 2*Mk.81 ", "A - 2*320L TipTanks [Clean]", "Recon", "Training", "AA - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*LAU-10(Zuni Rockets) [ARMADA]", "AM - 2*320L TipTanks + 2*AN/M3 GunPods + 2*330L Tanks + 2*LAU-3 (Hydra rockets)", "A - 2*500L TipTanks + 2*330L Tanks + Luggage Container [Ferry Long Range]", "A - 2*500L TipTanks + 4*Mk.82HD + 2*LR-25 (API Rockets)", "A - 2*320L TipTanks + 2*330L Tanks [Ferry Medium Range]", "A - 2*500L TipTanks + 2*AN/M3 GunPods + 2*Matra 155 + 2* Belouga", "Runway Interdiction", "A - 2*500L TipTanks + 2*DEFA-553 GunPods + 2*Mk.82LD + 2*LR-25 (API Rockets)", "A - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*Mk.82LD Bombs + 2*LR-25(API Rockets)", "A - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*Mk.82LD + 2*LR-25 (HEI Rockets)", "A - 2*320L TipTanks + 6*Mk.82LD", "Runway Interdiction (36*BAP-100)", "Anti - Light Armoured Vehicle (36*BAT-120 ABL)", "AP - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*330L Tanks + 2*Matra 155 (SNEB rockets)"], "MB-339APAN": ["PAN - Smoke White", "PAN - Smoke White and Color Red", "PAN - Fuel External [RED SMOKE]", "PAN - Full Payload [RED SMOKE] + 2*320L TipTanks", "PAN - Full Payload [GREEN SMOKE] + 2*320L TipTanks", "PAN - Fuel External [GREEN SMOKE]", "PAN - Full Payload [RED SMOKE] + 2*500L TipTanks", "PAN - Smoke White and Color Green", "PAN - Full Payload [GREEN SMOKE] + 2*500L TipTanks"], "Mi-24P": ["2xB8V20 (S-8KOM)+8xATGM 9M114", "2xB8V20 ( S-8KOM)+4xATGM 9M114", "4xB8V20 (S-8KOM)+4xATGM 9M114", "2xB8V20 (S-8KOM)+2xBombs-250+4xATGM 9M114", "2xB8V20 (S-8OFP2)+4xATGM 9M114", "4xUB-32A (S-5KO)+4xATGM 9M114", "4xGUV-1 AP30+4xATGM 9M114", "2xGUV-1 AP30+4xATGM 9M114", "2xGUV-1 (GUN 12.7+2x7.62) +4xATGM 9M114", "2xKMGU (96 AO 2.5RT)+8xATGM 9M114", "2xB-13L+4xATGM 9M114", "2xS-24B+4xATGM 9M114", "4xS-24B+4xATGM 9M114", "2xBombs-500+4xATGM 9M114", "4xBombs-250+4ATGM 9M114", "2xRBK-500 (PTAB-1M)+4xATGM 9M114", "2xRBK-500U (OAB 2.5RT)+4xATGM 9M114", "4xRBK-250 (42 PTAB 2.5M) +4ATGM 9M114", "4xRBK-250-275 (150 AO-1SCh)+4ATGM 9M114", "4xPTB-450 Fuel tank"], "MiG-19P": ["PTB-760 x 2", "K-13A x 2, PTB-760 x 2", "K-13A x 2", "K-13A x 2, ORO-57K x 2, PTB-760 x 2", "ORO-57K x 2, PTB-760 x 2", "ORO-57K x 4", "ORO-57K x 2", "FAB-100M x 2, ORO-57K x 2", "FAB-250 x 2, ORO-57K x 2", "FAB-100M x 2", "FAB-250 x 2"], "MiG-21Bis": ["Patrol, long range", "Patrol, medium range", "Patrol, short range", "Hard targets, BOMBS", "Unknown or mixed targets, BOMBS + ROCKETS", "Soft targets, CLUSTERS + ROCKETS", "Soft targets, CLUSTERS", "Soft targets, scattered", "Few big targets, GROM + BOMBS", "Very hard target, PENETRATION", "Aerial attack, hard targets, CLUSTERS", "Hard targets, ROCKETS, PENETRATION", "Soft targets, ROCKETS, BLAST-FRAGMENTS", "Long range, MIX", "Long range, RADAR GUIDED MISSILES", "Long range, INFRA RED MISSILES", "Escort", "Escort, JAMMER", "Night, ILLUMINATOR", "Long range, JAMMER", "Soft targets, UPK + ROCKETS", "Soft targets, UPK + CLUSTERS", "Patrol, JAMMER", "NUCLEAR A", "NUCLEAR B", "Short range", "AEROBATIC"], "Mirage-F1B": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1BD": ["2*R550 Magic I, 2*S530, 1*Fuel Tank", "2*R550 Magic I", "2*R550 Magic I, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1BE": ["2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*AIM9-JULI, R530IR", "2*R550 Magic I, R530EM", "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*AIM-9JULI, 8*SAMP 250 HD", "2*AIM-9JULI, 8*SAMP 400 LD", "2*AIM-9JULI, 8*BLU107 Durandal"], "Mirage-F1BQ": ["2*R550 Magic I, 2*S530, 1*Fuel Tank", "2*R550 Magic I", "2*R550 Magic I, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1C-200": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1C": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1CE": ["2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*AIM9-JULI, R530IR", "2*R550 Magic I, R530EM", "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*AIM-9JULI, 8*SAMP 250 HD", "2*AIM-9JULI, 8*SAMP 400 LD", "2*AIM-9JULI, 8*BLU107 Durandal"], "Mirage-F1CG": ["2*AIM-9 JULI, 2*R530IR, 1*Fuel Tank", "4*AIM-9 JULI, 2*R530IR, 1*Fuel Tank", "2*AIM-9 JULI, 2*R530EM, 1*Fuel Tank", "2*AIM-9 JULI, R530IR", "2*AIM-9 JULI, 1*R530IR, 2*Fuel Tank", "2*AIM-9 JULI, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*AIM-9 JULI, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*AIM-9 JULI, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*AIM-9 JULI, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*AIM-9 JULI, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*AIM-9 JULI, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1CH": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1CJ": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1CK": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1CR": ["2*R550 Magic I, 2*S530, 1*Fuel Tank", "2*R550 Magic I", "2*R550 Magic I, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1CT": ["2*R550 Magic I, 2*S530, 1*Fuel Tank", "2*R550 Magic I", "2*R550 Magic I, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1CZ": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1DDA": ["2*R550 Magic I, 2*S530, 1*Fuel Tank", "2*R550 Magic I", "2*R550 Magic I, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1ED": ["2*R550 Magic II, 2*S530, 1*Fuel Tank", "2*R550 Magic II", "2*R550 Magic II, 2*Fuel Tank", "2*R550 Magic II, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic II, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic II, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic II, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic II, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic II, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1EDA": ["2*R550 Magic I, 2*S530, 1*Fuel Tank", "2*R550 Magic I", "2*R550 Magic I, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1EE": ["2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", "2*AIM9-JULI, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*AIM9-JULI, R530EM", "2*R550 Magic I, R530IR", "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*AIM-9JULI, 8*SAMP 250 HD", "2*AIM-9JULI, 8*SAMP 400 LD", "2*AIM-9JULI, 8*BLU107 Durandal"], "Mirage-F1EH": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1EQ": ["2*R550 Magic I, 2*S530, 1*Fuel Tank", "2*R550 Magic I", "2*R550 Magic I, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1JA": ["2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", "2*R550 Magic I, R530IR", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550_Magic_1, 8*SAMP 250 HD", "2*R550_Magic_1, 8*SAMP 400 LD", "2*R550_Magic_1, 8*BLU107 Durandal"], "Mirage-F1M-CE": ["2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*AIM9-JULI, R530IR", "2*R550 Magic I, R530IR", "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*AIM-9JULI, 8*SAMP 250 HD", "2*AIM-9JULI, 8*SAMP 400 LD", "2*AIM-9JULI, 8*BLU107 Durandal"], "Mirage-F1M-EE": ["2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", "2*AIM9-JULI, R530IR", "2*R550 Magic I, R530IR", "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", "2*AIM-9JULI, 8*SAMP 250 HD", "2*AIM-9JULI, 8*SAMP 400 LD", "2*AIM-9JULI, 8*BLU107 Durandal"], "SA342L": ["M621, 8xSNEB68 EAP", "M621, 8xSNEB68 EAP, IR Deflector", "M621, 8xSNEB68 EAP, IR Deflector, Sand Filter"], "SA342M": ["HOT3x4", "IR Deflector", "Hot3x4, FAS, IR Deflector", "HOT3x2", "Hot3x4, IR Deflector", "Hot3x2, IR Deflector"], "SA342Minigun": ["IR Deflector", "IR Deflector, Sand Filter"], "SA342Mistral": ["Mistral x 4", "Mistral x 4, IR Deflector", "Mistral x 4, IR Deflector, Sand Filter"], "A-20G": ["500 lb GP bomb LD*4"], "Bf-109K-4": ["Fuel Tank", "SC250", "SC500"], "FW-190A8": ["Without pylon", "SC 50 * 4", "AB 250 (w/ SD 10A)", "AB 250 (w/ SD 2)", "AB 500 (w/ SD 10A)", "SC 250 L2", "SC 250 J", "SC 500 J", "SC 500 L2", "SD 250 Stg", "SD 500 A", "Fuel Tank 300 liters", "BR 21"], "FW-190D9": ["SC500", "Fuel Tank", "R4M", "BR 21"], "MosquitoFBMkVI": ["250 lb S.A.P*2; 500 lb S.A.P.*2", "500 lb GP Mk.V*2, 500 lb GP Short tail*2", "100 gal Drop tank*2, 500 lb MC Short tail*2", "RP-3 60lb SAP No2 Mk.I*8, 250 lb A.A.P.*2", "100 gal. Drop tank*2, 250 lb MC Mk.II, RP-3 60lb F No1 Mk.I*4", "500 lb GP Short tail*4"], "P-47D-30": ["AN-M65*2", "Fuel150*2", "AN-M64*2, Fuel110", "AN-M57*3"], "P-47D-30bl1": ["AN-M57*2", "AN-M64*2, Fuel110"], "P-47D-40": ["AN-M65*2", "Fuel150*2", "AN-M57*3", "AN-M64*2, Fuel110", "M8*6, AN-M57*2, Fuel110", "HVAR*10, Fuel110"], "P-51D-30-NA": ["Fuel75*2", "HVAR*6,Fuel75*2", "HVAR*6,M64*2", "HVAR*6", "M64*2", "HVAR*10", "Smokes"], "P-51D": ["Fuel75*2", "HVAR*6,Fuel75*2", "HVAR*6,M64*2", "HVAR*6", "M64*2", "HVAR*10", "Smokes"], "A-50": [], "AH-1W": ["14xHYDRA-70 WP", "38xHYDRA-70 WP", "8xBGM-71, 14xHYDRA-70", "8xBGM-71, 14xHYDRA-70 WP", "8xBGM-71, 38xHYDRA-70 WP", "14xHYDRA-70", "38xHYDRA-70", "8xAGM-114", "28xHYDRA-70", "8xBGM-71, 38xHYDRA-70", "8xAGM-114, 38xHYDRA-70 WP", "8xBGM-71", "8xAGM-114, 14xHYDRA-70 WP", "76xHYDRA-70", "8xAGM-114, 38xHYDRA-70", "8xAGM-114, 14xHYDRA-70"], "AH-64A": ["8xAGM-114", "38xHYDRA-70 WP", "76xHYDRA-70", "8xAGM-114, 38xHYDRA-70 WP", "38xHYDRA-70", "8xAGM-114, 38xHYDRA-70", "AGM-114K*16"], "AH-64D": ["76xHYDRA-70", "38xHYDRA-70", "38xHYDRA-70 WP", "8xAGM-114", "8xAGM-114, 38xHYDRA-70 WP", "8xAGM-114, 38xHYDRA-70", "AGM-114K*16"], "An-26B": [], "An-30M": [], "B-1B": ["Mk-82*84", "AGM-154*12", "GBU-38*48", "CBU-87*30", "CBU-97*30", "GBU-38*16, CBU-97*20", "Mk-84*24", "GBU-31*24", "GBU-31(V)3/B*24", "GBU-31*8, GBU-38*32"], "B-52H": ["Mk-84*18", "Mk 82*51", "Mk20*18", "AGM-86C*20", "AGM-84A*8"], "C-130": [], "C-17A": [], "CH-47D": [], "CH-53E": [], "E-2C": [], "E-3A": [], "F-117A": ["GBU-12*2", "GBU-10*2", "GBU-27*2"], "F-14A": ["AIM-9*2", "AIM-54C*6,AIM-9*2", "AIM-54C*4,AIM-9*2,AIM-7*2"], "F-15C": ["AIM-120B*4, AIM-7M*2, AIM-9M*2, Fuel*3", "AIM-9*2,AIM-120*6,Fuel", "AIM-9*4,AIM-120*4,Fuel*3", "AIM-9*4,AIM-120*4,Fuel", "AIM-9*2,AIM-120*2,AIM-7*4,Fuel*3", "AIM-9*2,AIM-120*6,Fuel*3", "AIM-9*4,AIM-7*4,Fuel", "AIM-120*8,Fuel", "AIM-9*4,AIM-7*4,Fuel*3", "AIM-120*8,Fuel*3", "AIM-9*2,AIM-120*2,AIM-7*4,Fuel"], "F-15E": ["AIM-120B*2,AIM-9M*2,FUEL*3,CBU-87*6,Mk-82AIR*6", "AIM-120B*2,AIM-9M*2,FUEL*3,CBU-97*12", "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-82*12", "AIM-120B*4,AIM-120C*4,FUEL*3", "AIM-120B*2,AIM-9M*2,FUEL,GBU-31*4,AGM-65H,AGM-65D", "AIM-120B*2,AIM-9M*2,FUEL,CBU-103*2,GBU-12,GBU-38,AGM-154C*2", "AIM-120B*4,AIM-9M*4,FUEL*3", "AIM-120B*2,AIM-9M*2,AIM-120C*2,AIM-7M*2,FUEL*3", "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*4,GBU-38*4,AGM-154C*2", "AIM-120B*2,AIM-9M*2,FUEL,GBU-31*4,AGM-154C*2", "AIM-120B*2,AIM-9M*2,AIM-7M*4,FUEL*3", "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-84*8", "AIM-120B*2,AIM-9M*2,FUEL,AGM-154C*2", "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*4,GBU-38*4,AGM-65D*2", "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*4,GBU-38*4,AGM-65K*2", "AIM-120C*2,AIM-9M*4,AIM-7M*2,FUEL*3", "AIM-9M*4,AIM-7M*4,FUEL*3", "AIM-120C*2,AIM-9M*2,FUEL*3", "AIM-120B*2,AIM-9M*2,FUEL,Mk-84*8,AGM-154C*2", "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-82AIR*12", "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*2,GBU-38*2,AGM-154C*2", "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*2,GBU-38*2,AGM-65H,AGM-65D", "AIM-120B*2,AIM-120C*4,AIM-9M*2,FUEL*3", "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-82*6,Mk-82AIR*6", "AIM-120B*2,AIM-9M*2,FUEL,CBU-103*2,GBU-12,GBU-38,AGM-65H*2", "AIM-120B*2,AIM-9M*2,FUEL,AGM-65D*2", "AIM-120B*2,AIM-9M*2,FUEL*2,SUU-25*2,GBU-12,GBU-38", "AIM-120B*6,AIM-9M*2,FUEL*3", "AIM-120B*2,AIM-9M*2,FUEL,Mk-82AIR*12,AGM-154C*2", "AIM-120C*6,AIM-9M*2,FUEL*3", "GBU-31(V)3/B*5, AIM-120C*2, AIM-9M*2"], "F-16A MLU": ["Fuel*3", "AGM-88*2, AGM-65D*2, AIM-120B*2, ECM", "Mk-82*6,AIM-120*2,ECM,Fuel*2", "Mk-82*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", "AGM88*2,AGM-65D*6,AIM-120*2,AIM-9*2,ECM", "AIM-120*2,GBU-10*2,ECM,Lantirn ,Fuel*2", "AIM-9*4,ECM", "Mk20*4,AIM-9*2,ECM,Fuel*2", "AIM-120*6,ECM", "AIM-120*4,AIM-9*2,ECM", "AGM88, AGM-65D, AIM-120*2,Fuel*2,ECM", "AGM-65D*2,AIM-120*2,ECM,Lantirn ,Fuel*2", "AIM-9*4,ECM,Fuel*2", "AIM-120*4,AIM-9*2,ECM,Fuel*2", "AGM-88*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", "Mk-84*2,AIM-9*2,ECM,Fuel*2", "AIM-120*6,ECM,Fuel*2", "AGM-154*2,AIM-120*2,ECM,Lantirn ,Fuel*2", "AGM-88*2,AGM-65D*2,AIM-120B*2,AIM-9M*2,ECM", "Mk20*2,AIM-120*2,ECM,Fuel*2", "AIM-120*2,AIM-9*2,GBU-12*2,ECM,Lantirn ,Fuel*2", "AGM-65K*2,AIM-120*2,ECM,Lantirn ,Fuel*2", "AIM-120B*2_AIM-9M*2_AGM-119*4_ALQ-131"], "F-16A": ["AGM-88*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", "Mk20*4,AIM-9*2,ECM,Fuel*2", "AIM-9*4,ECM,Fuel*2", "AGM-65D*2,AIM-120*2,ECM,Lantirn ,Fuel*2", "AIM-120*4,AIM-9*2,ECM,Fuel*2", "AGM88*2_AGM-65D*6_AIM-120*2_AIM-9*2_ECM", "AGM-65K*2,AIM-120*2,ECM,Lantirn ,Fuel*2", "AIM-120*2,AIM-9*2,GBU-12*2,ECM,Lantirn ,Fuel*2", "AIM-120*2,GBU-10*2,ECM,Lantirn ,Fuel*2", "AIM-120*6,ECM,Fuel*2", "AIM-120*6,ECM", "AIM-9*4,ECM", "Fuel*3", "Mk-84*2,AIM-9*2,ECM,Fuel*2", "AIM-120*4,AIM-9*2,ECM", "Mk-82*6,AIM-120*2,ECM,Fuel*2", "Mk-82*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", "AGM-154*2,AIM-120*2,ECM,Lantirn ,Fuel*2", "Mk20*2,AIM-120*2,ECM,Fuel*2", "AGM-119*2,AIM-120B*2,AIM-9M*2,ALQ-184,Fuel*2"], "F-16C bl.50": ["AIM-120B*4, AIM-9M*2, Fuel", "AIM-120C*4, AIM-9M*2, Fuel", "AGM-65G*2, AIM-120C*2, AIM-9M*2, Fuel, ECM", "AGM-65D*6, AIM-120B*2, AIM-9M*2, ECM, Fuel", "Mk86*6, AIM-9M*4, ECM, Fuel", "CBU-97*4, AIM-9M*4, Fuel", "CBU-87*4, AIM-9M*4, Fuel", "AGM-65D*2, CBU-87*2, AIM-9M*4, Fuel", "AGM-65D*2, CBU-97*2, AIM-9M*4, Fuel", "Mk84*2, AIM-9M*4, ECM, Fuel", "GBU-31v1*2, AIM-9M*4, ECM, Fuel", "GBU-31v3*2, AIM-9M*4, ECM, Fuel", "GBU-31v1*2, Mk82*6, AIM-9M*4, Fuel", "Mk84*4, AIM-9M*4, Fuel", "Mk82*12, AIM-9M*4, Fuel", "AGM-154C*2, AIM-9M*4, ECM, Fuel"], "F-16C bl.52d": ["AGM-88*2, AGM-65D*2, AIM-120B*2, ECM,LIGHTNING", "Mk-82*6,AIM-120*2,ECM,Fuel*2,LIGHTNING", "AGM88*2,AGM-65D*6,AIM-120*2,AIM-9*2,ECM,LIGHTNING", "AGM-65D*4,AIM-120*2,ECM,Fuel*2,LIGHTNING", "AIM-120*2,GBU-31*2,ECM,Fuel*2,LIGHTNING", "AIM-120*2,GBU-31(v)*2,ECM,Fuel*2,LIGHTNING", "AGM-88*2,AIM-120*2,AIM-9*2,ECM,Fuel*2,LIGHTNING", "Copy AIM-120*2,GBU-38*2,ECM,Fuel*2,LIGHTNING", "AIM-120*2,GBU-10*2,ECM,Fuel*2,LIGHTNING", "Mk-84*2,AIM-120*2,ECM,Fuel*2,LIGHTNING", "AGM-154*2,AIM-120*2,ECM,Fuel*2,LIGHTNING", "Mk-82AIR*6,AIM-120*2,ECM,Fuel*2,LITENING", "CBU97*4,AIM120*2,ECM,Fuel*2,LITENING", "AGM-88*2,AGM-65D*2,AIM-120B*2,AIM-9M*2,ECM,LITENING", "CBU87*2,AIM-120*2,ECM,Fuel*2,LITENING", "AGM88, AGM-65D, AIM-120*2,Fuel*2,ECM,LITENING", "AIM-120*2,GBU-12*2,ECM,Fuel*2,LITENING", "AGM-65K*2,AIM-120*2,ECM,Lantirn ,Fuel*2,LITENING", "AGM-65G*4,AIM-120C*2,AIM-9M*2,ECM", "AIM-120C*4,AIM-9M*2,ECM,Fuel*2", "AIM-120B*4,AIM-9M*2,ECM,Fuel*2", "AIM-9M*4,Fuel*2", "AIM-120C*2,AIM-120B*2,AIM-9M*2,Fuel*3", "AIM-9P*4", "AIM-9M*2,ECM"], "F-4E": ["AIM-9*4,AIM-7*4", "AGM45*2_AGM-65D*4_AIM7*2_ECM", "AGM-45*2,AIM-7*2,Fuel*2,ECM", "Mk-82*18,AIM-7*2,ECM", "GBU-12*2,AIM-7*2,Fuel*2,ECM", "Mk20*12,AIM-7*2,ECM", "Mk-82*6,AIM-7*2,Fuel*2,ECM", "GBU-10*2,AIM-7*2,Fuel*2,ECM", "Mk20*6,AIM-7*2,Fuel*2,ECM", "AGM-45*4,AIM-7*2,ECM", "AGM-65K*4,AIM-7*2,Fuel*2,ECM", "Fuel*3", "AIM-9*4,AIM-7*4,Fuel*2", "Mk-84*2,AIM-7*2,ECM", "AGM-65K*4,AIM-7M*4,Fuel*3"], "F/A-18A": ["GBU-16*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AIM-9*6,Fuel", "Mk-84*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM-65D*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM88*4_AIM9*2_AIM7_FLIR Pod_Fuel", "AIM-9*4,Fuel*3", "LAU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM-88*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel", "MK-82*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AIM-9*4,AIM-7*2,Fuel*3", "AGM-65K*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "Fuel*3", "AGM88*2_AGM65D*2_AIM9*2_AIM7_FLIR Pod_Fuel", "GBU-12*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AIM-9*4,AIM-7*4,Fuel", "Mk20*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "GBU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM-84A*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3"], "F/A-18C": ["AGM-62*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "GBU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "GBU-12*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "Fuel*3", "Mk-84*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "GBU-16*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM-154*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AIM-120*4,AIM-9*2,Fuel*3", "AGM-65D*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM88*2_AGM65D*2_AIM9*2_AIM7_FLIR Pod_Fuel", "AGM88*4_AIM9*2_AIM7_FLIR Pod_Fuel", "AIM-9*2,AIM-7*6,Fuel", "Mk20*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AIM-120*6,AIM-9*2,Fuel", "LAU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AIM-9*2,AIM-7*4,Fuel*3", "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel", "AIM-120*2,AIM-9*2,AIM-7*2,Fuel*3", "AGM-88*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM-84E*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "MK-82*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AGM-84A*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", "AIM-9M*2,AGM-65D*2,Mk-82*4,FLIR Pod,Fuel", "AIM-120*4,AIM-9*2,AIM-7*2,Fuel", "AGM-65K*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3"], "IL-76MD": [], "IL-78M": [], "Ka-27": [], "Ka-52": ["APU-6 Vikhr-M*2, Kh-25ML*2", "APU-6 Vikhr-M*2", "B-8*4", "KMGU-2 (AO-2.5RT)*4", "B-8*2, APU-6 Vikhr-M*2", "FAB-500*2", "UB-13*2", "FAB-250*4", "Kh-25ML*2, R-73*2"], "KC-10A": [], "KC-135": [], "Mi-24V": ["2xFAB-500", "10xS-13", "2xFAB-250", "4x9M114, 2xFuel tank", "128xS-5", "80xS-8 TsM", "4x9M114, 40xS-8 TsM", "64xS-5", "8x9M114", "4x9M114, 80xS-8", "4x9M114, 128xS-5", "2xKMGU AP", "4xFuel tank", "4xUPK-23", "4x9M114, 10xS-13", "4x9M114, 80xS-8 TsM", "4x9M114", "80xS-8", "40xS-8 TsM", "8x9M114, 40xS-8 TsM", "8x9M114, 10xS-13", "2xFuel tank", "4x9M114, 4xUPK-23", "2xUPK-23", "8x9M114, 64xS-5", "8x9M114, 40xS-8", "20xS-13", "40xS-8", "8x9M114, 2xUPK-23"], "Mi-26": [], "Mi-28N": ["2xFAB-250", "4xFuel tank", "80xS-8", "4xKMGU AP", "4xUPK-23", "16x9M114, 10xS-13", "4xFAB-500", "16x9M114, 2xFAB-500", "40xS-8", "40xS-8 TsM", "2xKMGU AP", "2xUPK-23", "16x9M114, 2xUPK-23", "2xFAB-500", "16x9M114, 40xS-8", "16x9M114", "20xS-13", "16x9M114, 2xKMGU AP", "4xFAB-250", "4xKMGU AT", "16x9M114, 40xS-8 TsM", "80xS-8 TsM", "2xKMGU AT", "9x9M114", "2xFuel tank", "10xS-13", "2xFAB-250, 16x9M114", "16x9M114, 2xKMGU AT"], "Mi-8MT": ["4 x B8", "4 x B8 + 2GUV_AP-30 (GrL 30mm)", "2 x UPK +2 x B8", "2 xB8 + 2GUV_YaKB (MG-12.7+7.62)+ 2GUV_AP-30 (GrL 30mm)", "6 x FAB-100", "2 x B8 + 2 x UPK-23-250", "2 x UPK--23-250"], "MiG-15bis": ["2*FAB-50", "2*FAB-100M", "2*300L", "2*400L", "2*600L", "Fuel tank 300", "Fuel tank 400"], "MiG-23MLD": ["R-60M*4", "B-8*2,R-60M*2,Fuel-800", "UB-32*2,R-60M*2,Fuel-800", "R-24R*2,R-60M*4,Fuel-800", "R-24R,R-24T,R-60M*4,Fuel-800", "R-60M*4,Fuel-800", "FAB-500*2,R-60M*2,Fuel-800", "R-24R*2,R-60M*4", "FAB-250*2,R-60M*2,Fuel-800", "RBK-250*2,R-60M*2,Fuel-800", "RBK-500*2,R-60M*2,Fuel-800", "R-24R,R-24T,R-60M*4"], "MiG-25PD": ["R-40R*2,R-40T*2", "R-40R*4", "R-40R*2,R-60M*2"], "MiG-25RBT": ["FAB-500x2_60x2", "R-60M*2"], "MiG-27K": ["FAB-250*6,R-60M*2,Fuel", "BetAB-500ShP*2,FAB-250*2,R-60*2", "Kh-25MR*2,R-60M*2,Fuel", "Kh-29L*2,R-60M*2,Fuel", "B-8*4", "BetAB-500*2,FAB-500*2,R-60*2", "Kh-25MPU*2,R-60M*2,Fuel", "Kh-29T*2,R-60M*2,Fuel", "FAB-500*2,FAB-250*2,R-60M*2,Fuel", "Kh-25ML*2,R-60M*2,Fuel", "KAB-500*2,R-60M*2,Fuel", "RBK-500AO*2,RBK-250*2,R-60M*2", "UB-32*4", "Kh-29L*2,R-60*2,Fuel"], "MiG-29A": ["Fuel-1150*2,Fuel-1500", "RBK-500AO*4,R-73*2,Fuel", "FAB-250*4,R-73*2,Fuel", "B-8*4,R-73*2,Fuel", "R-60M*4,R-27R*2", "R-73*4,R-27R*2,Fuel-1500", "R-73*6,Fuel-1500", "R-60M*6,Fuel-1500", "S-24*4,R-73*2,Fuel", "FAB-500*4,R-73*2,Fuel", "R-60M*6", "BetAB-500*4,R-73*2,Fuel", "R-73*6", "R-73*2,R-60M*2,R-27R*2,Fuel-1500", "R-60M*4,R-27R*2,Fuel-1500", "RBK-250*4,R-73*2,Fuel", "R-73*4,R-27R*2", "R-73*2,R-60M*2,R-27R*2", "S-24*2,FAB-500*2,R-73*2,Fuel"], "MiG-29G": ["R-73*6,Fuel-1500", "R-73*4,R-27R*2,Fuel-1500", "R-73*2,R-60M*2,R-27R*2", "R-60M*4,R-27R*2,Fuel-1500", "R-73*6", "R-60M*4,R-27R*2", "R-73*4,R-27R*2", "Fuel-1150*2,Fuel-1500", "R-60M*6", "R-60M*6,Fuel-1500", "R-73*2,R-60M*2,R-27R*2,Fuel-1500"], "MiG-29S": ["R-73*2,R-60M*2,R-27R*2", "R-73*4,R-27R*2,Fuel-1500", "R-73*6,Fuel-1500", "R-60M*6,Fuel-1500", "S-24*4,R-73*2,Fuel", "FAB-500*4,R-73*2,Fuel", "BetAB-500*4,R-73*2,Fuel", "RBK-500AO*4,R-73*2,Fuel", "R-73*2,R-60M*2,R-27R*2,Fuel-1500", "R-77*2,R-73*2,Fuel-1500,Fuel-1150*2", "B-8*4,R-73*2,Fuel", "RBK-250*4,R-73*2,Fuel", "R-73*6", "Fuel-1150*2,Fuel-1500", "R-60M*6", "R-60M*4,R-27R*2", "R-73*4,R-27R*2", "R-77*4,R-73*2", "FAB-250*4,R-73*2,Fuel", "R-60M*4,R-27R*2,Fuel-1500", "R-77*4,R-73*2,Fuel-1500", "S-24*2,FAB-500*2,R-73*2,Fuel"], "MiG-31": ["R-40T*2,R-33*4", "R-40T,R-33*4,R-40R", "R-40R*2,R-33*4", "R-60M*4,R-33*4"], "Mirage 2000-5": ["R 550*2,MICA IR*4", "R 550*2,MICA IR*2,MICA AR*2,Fuel*3", "R 550*2,MICA AR*4,Fuel*3", "R 550*2,SUPER 530F*2,Fuel", "Fuel*3", "R 550*2,MICA IR*4,Fuel*3", "R 550*2,MICA AR*4", "R 550*2,SUPER 530F*2"], "MQ-9 Reaper": ["GBU-12*4", "GBU-38*4", "AGM-114K*8,GBU-38*2", "AGM-114K*12"], "OH-58D": ["2xAGM-114, 7xHYDRA-70", "4xAGM-114", "M-3, 7xHYDRA-70", "2xAGM-114, M-3", "14xHYDRA-70", "14xHYDRA-70 WP"], "RQ-1A Predator": ["AGM-114K*2"], "S-3B": ["AGM-84A*2, Mk-82*2", "AGM-84E*2", "AGM-65D, AGM-65K, Mk20*4", "AGM-65D, AGM-65K, Mk82*4", "Mk82*10", "Mk84*2, Mk82*4", "ZUNI Mk71*8, Mk20*4"], "SH-3W": [], "SH-60B": ["AGM-119"], "Su-17M4": ["UB-32*4,R-60M*2,FAB-250*4", "FAB-100*24,R-60M*2", "UB-32*4,R-60M*2,Fuel*2", "B-8*4,R-60M*2,FAB-250*4", "Kh-29L*2,R-60M*2,Fuel*2", "B-8*4,R-60M*2,Fuel*2", "Kh-29T*2,R-60M*2,Fuel*2", "BetAB-500*6,R-60M*2", "Kh-25MR*4,R-60M*2,Fuel*2", "S-24*4,R-60M*2,Fuel*2", "Kh25MPU*2_Kh25ML*2_,R60M*2_Fuel*2", "Kh58*2_Kh25MPU*2_R60M*2_Fuel*2", "FAB-250*16,R-60M*2", "Kh-25ML*4,R-60M*2,Fuel*2", "RBK-500AO*4,SPPU-22*2,R-60M*2", "S-24*4,R-60M*2,FAB-250*4", "Fuel*4", "FAB-500*6,R-60M*2", "Kh-25ML*2,Kh-29L*2,R-60*2"], "Su-24M": ["UB-13*4,FAB-500*2", "Kh-31A*2,R-60M*2,Fuel", "UB-13*4", "KAB-500*4,R-60M*2", "S-25*2,Fuel*3", "Kh31P*2_Kh25ML*2_L-081", "B-8*2,Fuel*3", "FAB-1500*2,R-60M*2", "S-24*4", "BetAB-500*4,R-60M*2", "Kh-25ML*4", "Kh-25MR*4", "FAB-100*24", "Kh-31A*2,R-60M*2", "UB-13*2,Fuel*3", "B-8*2,Fuel*2", "Kh58*2_Kh25ML*2_L-081", "RBK-250*8", "UB-32*4", "Kh-29L*2,R-60M*2", "S-24*2,Fuel*3", "Kh25MPU*2_Kh25ML*2_L-081", "FAB-500*4,R-60M*2", "FAB-250*8", "Fuel*3", "RBK-500AO*4,R-60M*2", "KAB-1500*2,R-60M*2,Fuel", "UB-32*4,FAB-250*4", "Kh-29T*2,R-60M*2", "UB-32*2,Fuel*3", "Kh-59M*2,R-60M*2,Fuel", "S-25*4", "B-8*6"], "Su-24MR": ["SHPIL,ETHER,R-60M*2", "Fuel*2", "TANGAZH,ETHER,R-60M*2,Fuel*2", "TANGAZH,ETHER,R-60M*2", "SHPIL,ETHER,R-60M*2,Fuel*2"], "Su-25": ["RBK-250*2,S-8KOM*80,R-60M*2,Fuel*2", "FAB-250*4,UB-13*2,R-60M*2,SPPU-22*2", "S-25L*6,UB-13*2,R-60M*2", "S-25*6,SPPU-22*2,R-60M*2", "2-25L*2, KH-25ML*2, RBK-500*2, B-8MI*2, R-60M*2", "S-8KOM*120,R-60M*2,Fuel*2", "FAB-250*4,S-25*2,R-60M*2,Fuel*2", "RBK-500AO*4,S-8KOM*40,R-60M*2,Fuel*2", "FAB-250*2,SPPU-22*2,SAB-100*4,R-60M*2", "RBK-500AO*6,R-60M*2,Fuel*2", "RBK-250*8,R-60M*2", "Kh-29L*2,Kh-25ML*4,R-60M*2", "RBK-250*4,S-8KOM*80,R-60M*2", "FAB-250*4,UB-13*2,R-60M*2,Fuel*2", "S-8TsM*160,R-60*2", "Kh-25ML*4,R-60M*2,Fuel*2", "BetAB-500ShP*8,R-60M*2", "SAB-100*8,R-60*2", "Kh-29L*2,Kh-25ML*4,S-25L*2,R-60M*2", "FAB-500*6,R-60M*2,Fuel*2", "Kh-29L*2,Kh-25ML*2,R-60M*2,Fuel*2", "Kh-29L*2,R-60M*2,Fuel*2", "FAB-100*32,R-60M*2", "FAB-100*16,R-60M*2,Fuel*2", "FAB-250*6,R-60M*2,Fuel*2", "BetAB-500*6,R-60M*2,Fuel*2", "S-25*6,R-60M*2,Fuel*2", "UB-13*6,R-60M*2,Fuel*2", "Kh-25*4,Kh-29T*2,R-60*2", "S-25L*6,R-60*2,Fuel*2"], "Su-25T": ["FAB-250*4,SPPU-22*2,SAB-100*2,R-60M*2", "Kh-29L*2,Kh-25ML*4,R-73*2,Mercury LLTV Pod,MPS-410", "KAB-500Kr*2,Kh-25ML*2,R-73*2,MPS-410,Fuel*2", "RBK-500AO*4,UB-32*2,R-60M*2,Fuel*2", "BetAB-500ShP*8,R-60M*2", "UB-13*6,R-60M*2,Fuel*2", "Kh-29T*2,R-73*2,Fuel*2,MPS-410", "Kh58*2_Kh25ML*4_R73*2_L-081_MPS-410", "FAB-250*4,UB-13*2,R-60M*2,Fuel*2", "KH-29T*2, VIKHR*2, ECM", "Kh-29T*2,Kh-25ML*4,R-73*2,MPS-410", "FAB-250*4,UB-13*2,R-60M*2,SPPU-22*2", "FAB-500*6,R-60M*2,Fuel*2", "Fuel*4", "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410", "S-8KOM*120,R-60M*2,Fuel*2", "KMGU-2 (PTAB-2.5KO)*8,R-60M*2", "FAB-250*6,R-60M*2,Fuel*2", "Kh-29L*2,R-73*2,Fuel*2,Mercury LLTV Pod,MPS-410", "FAB-100*32,R-60M*2", "RBK-250*8,R-60M*2", "S-25L*6,UB-13*2,R-60M*2", "FAB-250*4,S-25*2,R-60M*2,Fuel*2", "S-25*2,SPPU-22*4,R-60M*2,R-73*2", "KMGU-2 (AO-2.5RT)*8,R-60M*2", "APU-8 Vikhr-M*2,S-25L*2,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410", "S-25*6,R-60M*2,Fuel*2", "RBK-500AO*6,R-60M*2,Fuel*2", "Kh58*2_Kh25MPU*2_Kh25ML*2_R73*2_L-081_MPS-410", "RBK-250*4,UB-32*4,R-60M*2", "Kh25MPU*4_R73*2_Fuel*2_L-081_MPS-410", "BetAB-500*6,R-60M*2,Fuel*2", "RBK-250*2,UB-32*4,R-60M*2,Fuel*2", "FAB-100*16,R-60M*2,Fuel*2", "Kh-29L*2,Kh-25ML*4,R-73*2,ECM"], "Su-25TM": ["BetAB-500ShP*6,R-73*2,ECM", "APU-8 Vikhr-M*2,R-60M*2,R-73*2,SPPU-22*2,Mercury LLTV Pod", "Kh-29T*2,Kh-25ML*2,R-73*2,Fuel*2,ECM", "Kh-58*2_Kh-25MPU*4_R-73*2_L-081_MPS410", "S-25L*6,UB-13*2,R-60M*2", "Kh-35*2_R-73*2_Fuel*2_MPS410_Kopyo-25", "Kh-31A*2_R-73*2_Fuel*2_MPS410_Kopyo-25", "Fuel*4", "BetAB-500*6,R-60M*2,Fuel*2", "KAB-500Kr*2,Kh-25ML*2,R-73*2,Fuel*2,ECM", "Kh-29L*2,R-73*2,Fuel*2,Mercury LLTV Pod,ECM", "UB-13*6,R-60M*2,Fuel*2", "S-25*2,SPPU-22*4,R-60M*2,R-73*2", "Kh-31P*2_Kh-25ML*4_R-73*2_L-081_MPS410", "UB-32*6,R-60M*2,Fuel*2", "FAB-100*16,R-60M*2,Fuel*2", "FAB-250*6,R-60M*2,Fuel*2", "FAB-500*6,R-60M*2,Fuel*2", "Kh-25MPU*4_R-73*2_Fuel*2_L-081_MPS410", "S-25*6,R-60M*2,Fuel*2", "RBK-500AO*4,UB-32*2,R-60M*2,Fuel*2", "FAB-250*4,UB-13*2,R-60M*2,SPPU-22*2", "Kh-31P*2_Kh-25MPU*4_R-73*2_L-081_MPS410", "RBK-500AO*6,R-60M*2,Fuel*2", "FAB-100*32,R-60M*2", "RBK-250*2,UB-32*4,R-60M*2,Fuel*2", "RBK-250*8,R-60M*2", "APU-8 Vikhr-M*2,R-73*2,SPPU-22*2,Mercury LLTV Pod,ECM", "RBK-250*4,UB-32*4,R-60M*2", "FAB-250*4,SPPU-22*2,SAB-100*2,R-60M*2", "FAB-250*4,S-25*2,R-60M*2,Fuel*2", "FAB-250*4,UB-13*2,R-60M*2,Fuel*2"], "Su-27": ["R-73*4,R-27ER*4,R-27ET*2", "KMGU-2 (AO-2.5RT)*5,R-73*2,ECM", "BetAB-500ShP*6,R-73*2,ECM", "KMGU-2 (PTAB-2.5KO)*5,R-73*2,ECM", "R-73*2,R-27ER*6,ECM", "R-73*6", "S-13*10,RBK-500AO*2,FAB-500*2,R-73*2,ECM", "R-73*4,R-27ER*6", "R-73*2,R-27ER*4,R-27ET*2,ECM", "R-73*4,ECM", "ECM", "FAB-500*6,R-73*2,ECM", "S-25*2,FAB-500*4,R-73*4", "S-25*4, FAB-500*4, R-73*2, ECM", "CAS S-8KOM Rockets + RBK-500 PTAB1", "CAS S-8OFP Rockets + FAB-500 Bombs", "CAS S-8OFP Rockets", "CAS S-8OFP Rockets + FAB-100 Bombs", "CAS S-8KOM Rockets + FAB-100 Bombs", "CAS S-13 Rockets", "CAS S-8KOM Rockets + FAB-250 Bombs", "CAS S-8KOM Rockets + RBK-250 PTAB2.5", "CAS S-8KOM Rockets", "CAS S-8KOM Rockets + FAB-500 Bombs", "CAS S-8KOM Rockets + RBK-500 PTAB10", "CAS S-8KOM Rockets + KMGU PTAB", " CAS S-25 Rockets", "CAS S-25 Rockets + FAB-500 Bombs"], "Su-30": ["R-73*2,R-77*6,ECM", "R-73*2,R-27T*2,R-27R*4", "RBK-500AO*6,R-73*2,ECM", "Kh-31P*2,Kh-31A*2,R-73*2,R-77*2,ECM", "R-73*4,R-27T*2,R-27R*4", "R-73*2,R-77*2,Kh-35*2,ECM", "Kh-35*2,Kh-31P*2,R-73*2,R-77*2,ECM", "FAB-250*4,B-8*2,R-73*2,ECM", "ECM", "KAB-1500*2,R-73*2,R-77*2,ECM", "RBK-250*6,R-73*2,ECM", "R-73*4,R-77*6", "FAB-250*4,S-25*2,R-73*2,ECM", "R-73*2,R-27R*2,R-27ER*4,ECM", "R-73*2,R-27T*2,R-27ER*2,R-77*2,ECM", "FAB-1500*2,R-73*2,R-77*2,ECM", "R-73*4,R-27T*2,R-27ER*2,R-77*2", "Kh-59M*2,R-73*2,R-77*2,ECM", "FAB-500*6,R-73*2,ECM", "R-73*4,R-27R*2,R-27ER*4", "Kh-29L*4,R-73*2,R-77*2,ECM", "BetAB-500*6,R-73*2,ECM", "R-73*4", "FAB-250*4,UB-13*2,R-73*2,ECM", "R-73*2,R-77*4,R-27ER*2,ECM", "KAB-500*4,R-73*2,R-77*2,ECM", "FAB-250*6,R-73*2,ECM", "R-73*4,R-77*4,R-27ER*2", "Kh-29T*4,R-73*2,R-77*2,ECM", "Kh-31P*2,Kh-31A*2,R-73*2,R-77*2,ECM", "Kh-31P*4,R-73*2,R-77*2,ECM"], "Su-33": ["RBK-250*6,R-73*2,R-27R*2,ECM", "R-73*4", "R-73*4,R-27R*2,R-27ER*6", "R-73*2,R-27ET*2,R-27ER*6,ECM", "R-73*4,R-27ET*2,R-27ER*6", "FAB-250*6,R-73*2,R-27R*2,ECM", "R-73*2,R-27R*2,R-27ER*6,ECM", "ECM", "BetAB-500*6,R-73*2,R-27R*2,ECM", "RBK-500AO*6,R-73*2,R-27R*2,ECM", "UB-13*4,FAB-250*4,R-73*2,ECM", "S-25*4,FAB-250*4,R-73*2,ECM", "FAB-500*6,R-73*2,R-27R*2,ECM", "B-8*4,FAB-250*4,R-73*2,ECM", "S-25*4,FAB-500*4,R-73*4", "CAS S-8KOM rockets + FAB500", "CAS S-8OFP rockets + FAB500", "CAS S-13 Rockets + FAB500", "CAS S-13 Rockets + FAB100", "CAS S-8KOM rockets + FAB250", "CAS S-25 Rockets + FAB500", "CAS S-8KOM rockets + RBK500 PTAB10", "CAS S-8KOM rockets + RBK500 PTAB1"], "Su-34": ["UB-13*4,FAB-250*4,R-73*2,ECM", "FAB-100*28,R-73*2,ECM", "BetAB-500*8,R-73*2,ECM", "Kh-29L*4,R-73*2,R-77*2,ECM", "KAB-500*4,R-73*2,R-77*2,ECM", "RBK-250 PTAB-2.5M*8,R-73*2,ECM", "FAB-250*8,R-73*2,ECM", "ECM", "Kh-29T*4,R-73*2,R-77*2,ECM", "RBK-500 PTAB-10-5*8,R-73*2,ECM", "FAB-1500*3,R-73*2,R-77*2,ECM", "Kh-59M*2,R-73*2,R-77*2,ECM", "B-8*6,R-73*2,R-27R*2,ECM", "FAB-500*8,R-73*2,ECM", "KAB-1500*2,R-73*2,R-77*2,ECM", "Kh-29T*4,R-73*2,R-27R*2,ECM", "Kh-31A*4,Kh-31P*2,R-73*2,R-27R*2,ECM", "Kh-31A*6,R-73*2,R-27R*2,ECM", "Kh-31P*4,R-73*2,R-27R*2,ECM", "Kh-29L*4,R-73*2,R-27R*2,ECM"], "Tornado GR4": ["AIM-9M*2, Fuel*2, ECM", "ALARM*4, Fuel*2, ECM", "GBU-16*2, AIM-9M*2, Fuel*2, ECM", "BL755*4, AIM-9M*2, Fuel*2, ECM", "Sea Eagle*2, AIM-9M*2, Fuel*2, ECM"], "Tornado IDS": ["Kormoran*2,AIM-9*2,Fuel*2", "GBU-16*2,AIM-9*2,Fuel*2", "Fuel*2", "AGM-88*4,AIM-9*2,ECM", "AGM-88*2,AIM-9*2,Fuel*2,ECM", "Kormoran*4,AIM-9*2", "Kormoran*2,AIM-9*2,AGM-88*2", "Mk-82*4,AIM-9*2,Fuel*2"], "Tu-142": ["Kh-35*6"], "Tu-160": ["Kh-65*12"], "Tu-22M3": ["Kh-22N", "Kh-22N*2", "FAB-250*69", "FAB-500*33", "FAB-500*33, FAB-250*36", "FAB-250*33"], "Tu-95MS": ["Kh-65*6"], "UH-1H": ["M134 Minigun*2, XM158*2"], "UH-60A": []} \ No newline at end of file +payloadNames = { + "A-10A":[ + "MK-84*2 , LAU-68*2 , AGM-65K*2", + "LAU-68-MK5*6", + "AGM-65K*2,Mk20*6,AIM-9*2,ECM", + "Mk-82*6,AIM-9*2,ECM", + "Mk20*6,AIM-9*2,ECM", + "AGM-65D*4,AIM-9*2,ECM", + "AGM-65K, AGM-65D", + "ECM", + "Mk-82*6, Mk-84*2", + "Mk20*8", + "AGM-65K*2,Mk-84*2,AIM-9*2,ECM", + "AGM-65K*2,Mk-82*6,AIM-9*2,ECM", + "Mk-84*2,AIM-9*2,ECM", + "AGM-65K*2,Mk84*2,Mk82*4,AIM-9M*2,ECM", + "AGM-65H*6,Mk82*10,AIM-9M*2,ECM" + ], + "A-10C":[ + "LAU-68 42 rkt M156 WP, AIM-9*2, ECM", + "AGM-65D*4, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", + "LAU-131 98 rkt M156 WP, AIM-9*2,ECM", + "SUU-25*9,AIM-9*2,ECM", + "AGM-65D*4, CBU-97*4,TGP, ECM, AIM-9*2", + "Mk-82AIR*8,AIM-9*2,ECM", + "MK-84*2,LAU-68*2,AGM-65K*2", + "BDU-33*6, TGM-65H, TGM-65D, TGP, BDU-50LGB*2, CAP-9*1", + "Mk-82*6,Mk-84*2,AIM-9*2,ECM", + "Mk-84*4,AIM-9*2,ECM", + "Mk-82*8,AIM-9*2,ECM", + "BDU-33*12, TGP, CAP-9*1", + "AGM-65D*4,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", + "AGM-65D*4,GBU-12*2,GBU-38,Mk-82,AIM-9,TGP,ECM", + "AGM-65D*2, AGM-65H*2, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", + "BDU-50HD*6,Mk1*7,TGP, CAP-9*1", + "AGM-65H*4, CBU-97*4,TGP, ECM, AIM-9*2", + "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", + "AGM-65K*2,GBU-38*4,AIM-9*2,TGP,ECM", + "BDU-33*6, TGP, CAP-9*1", + "AGM-65D*2,AGM-65H*2,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK151*7", + "TGP", + "BDU-33*6, TGP, CAP-9*1, BDU-50LD*2", + "GBU-12*6,GBU-10*2,TGP, AIM-9*2", + "TGP, CBU-87*3, M151*28, AIM-9*2, ECM", + "AGM-65D*4,Mk-82*6,CBU-87*2,TGP,AIM-9*2,Mk151*7", + "PGM- GBU-10*2,GBU-12*4,AIM-9*2,TGP,ECM", + "AGM-65D*4,TGP, ECM, AIM-9*2", + "TGP, CAP-9*1, CATM-65K*1, TGM-65G*1", + "AGM-65G*2,GBU-31*2,AIM-9*2,TGP,ECM", + "TGP, M151*14, Mk-82*2, Mk-82AIR*2, AIM-9*2, ECM", + "PGM- GBU-10*4, AGM-65K*2,AIM-9*2,TGP,ECM", + "AGM-65D*2,AGM-65H*2,Mk-82AIR*6,CBU-87*2,Mk151*7,AIM-9*2,TGP,ECM", + "GBU-31*2,GBU-38*2, AGM-65H*2, AIM-9*2,TGP, ECM", + "CBU-103*4, M151*14, AIM-9*2, ECM", + "CBU-87*4, M151*42, AIM-9*2, ECM", + "AGM-65D*6, CBU-97*4,TGP, ECM, AIM-9*2", + "CBU-87*2, M151*14, MK-82AIR*6, AIM-9*2,ECM", + "AGM-65D*4, CBU-105*4,TGP, ECM, AIM-9*2", + "BDU-50HD*2,BDU-50LGB*2,TGP, CAP-9*1", + "CBU-87*4, M151*28, AIM-9*2,ECM", + "M151*98, Mk-82*2,AIM-9*2,ECM", + "AGM-65D*2,AGM-65H*2,GBU-12,GBU-38,MK82*3,MK82AIR*3,MK5*7,TGP,AM-9*2", + "TGP, M151*42, Mk-82*6, Mk-82AIR*6, AIM-9*2, ECM", + "TGP, M151*84, Mk-82*2,AIM-9*2, ECM", + "BDU-50LD*2, BDU-50HD*2,CATM-65K, TGM-65G, TGP, CAP-9*1", + "TGP, M151*49, Mk-82*2, CBU-87*2, AIM-9*2, ECM", + "TGP, CAP-9*1, BDU-50LGB*4", + "GBU-12*14,TGP, AIM-9*2", + "AGM-65D*3, AGM-65H*3, CBU-97*4,TGP, ECM, AIM-9*2", + "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,TGP,ECM", + "AGM-65D*4, CBU-105*2,CBU-97*2, TGP, ECM, AIM-9*2", + "AGM-65D*2,Mk-82*6,AIM-9*2,ECM", + "AGM-65D*2,AGM-65H*2,TGP, ECM, AIM-9*2", + "GBU-38*4,GBU-31*2,TGP, AIM-9*2", + "AGM-65D*4,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK5*7", + "AGM-65G,AGM-65K,GBU-10*2,AIM-9*2,TGP,ECM", + "AGM-65G,AGM-65D,Mk-82*7,AIM-9*2,ECM", + "GBU-31*2,GBU-38*4,AIM-9*2,TGP,ECM, AIM-9*2", + "AGM-65K*2,GBU-12*8,AIM-9M*2.ECM,TGP", + "AGM-65D*6,GBU-12*4,AIM-9M*2,ECM,TGP" + ], + "A-10C_2":[ + "LAU-68 42 rkt M156 WP, AIM-9*2, ECM", + "AGM-65D*4, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", + "LAU-131 98 rkt M156 WP, AIM-9*2,ECM", + "SUU-25*9,AIM-9*2,ECM", + "AGM-65D*4, CBU-97*4,TGP, ECM, AIM-9*2", + "Mk-82AIR*8,AIM-9*2,ECM", + "MK-84*2,LAU-68*2,AGM-65K*2", + "BDU-33*6, TGM-65H, TGM-65D, TGP, BDU-50LGB*2, CAP-9*1", + "Mk-82*6,Mk-84*2,AIM-9*2,ECM", + "Mk-84*4,AIM-9*2,ECM", + "Mk-82*8,AIM-9*2,ECM", + "BDU-33*12, TGP, CAP-9*1", + "AGM-65D*4,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", + "AGM-65D*4,GBU-12*2,GBU-38,Mk-82,AIM-9,TGP,ECM", + "AGM-65D*2, AGM-65H*2, CBU-97*2, CBU-87*2, TGP, ECM, AIM-9*2", + "BDU-50HD*6,Mk1*7,TGP, CAP-9*1", + "AGM-65H*4, CBU-97*4,TGP, ECM, AIM-9*2", + "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-87*2,AIM-9M*2,ECM,TGP", + "AGM-65K*2,GBU-38*4,AIM-9*2,TGP,ECM", + "BDU-33*6, TGP, CAP-9*1", + "AGM-65D*2,AGM-65H*2,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK151*7", + "TGP", + "BDU-33*6, TGP, CAP-9*1, BDU-50LD*2", + "GBU-12*6,GBU-10*2,TGP, AIM-9*2", + "TGP, CBU-87*3, M151*28, AIM-9*2, ECM", + "AGM-65D*4,Mk-82*6,CBU-87*2,TGP,AIM-9*2,Mk151*7", + "PGM- GBU-10*2,GBU-12*4,AIM-9*2,TGP,ECM", + "AGM-65D*4,TGP, ECM, AIM-9*2", + "TGP, CAP-9*1, CATM-65K*1, TGM-65G*1", + "AGM-65G*2,GBU-31*2,AIM-9*2,TGP,ECM", + "TGP, M151*14, Mk-82*2, Mk-82AIR*2, AIM-9*2, ECM", + "PGM- GBU-10*4, AGM-65K*2,AIM-9*2,TGP,ECM", + "AGM-65D*2,AGM-65H*2,Mk-82AIR*6,CBU-87*2,Mk151*7,AIM-9*2,TGP,ECM", + "GBU-31*2,GBU-38*2, AGM-65H*2, AIM-9*2,TGP, ECM", + "CBU-103*4, M151*14, AIM-9*2, ECM", + "CBU-87*4, M151*42, AIM-9*2, ECM", + "AGM-65D*6, CBU-97*4,TGP, ECM, AIM-9*2", + "CBU-87*2, M151*14, MK-82AIR*6, AIM-9*2,ECM", + "AGM-65D*4, CBU-105*4,TGP, ECM, AIM-9*2", + "BDU-50HD*2,BDU-50LGB*2,TGP, CAP-9*1", + "CBU-87*4, M151*28, AIM-9*2,ECM", + "M151*98, Mk-82*2,AIM-9*2,ECM", + "AGM-65D*2,AGM-65H*2,GBU-12,GBU-38,MK82*3,MK82AIR*3,MK5*7,TGP,AM-9*2", + "TGP, M151*42, Mk-82*6, Mk-82AIR*6, AIM-9*2, ECM", + "TGP, M151*84, Mk-82*2,AIM-9*2, ECM", + "BDU-50LD*2, BDU-50HD*2,CATM-65K, TGM-65G, TGP, CAP-9*1", + "TGP, M151*49, Mk-82*2, CBU-87*2, AIM-9*2, ECM", + "TGP, CAP-9*1, BDU-50LGB*4", + "GBU-12*14,TGP, AIM-9*2", + "AGM-65D*3, AGM-65H*3, CBU-97*4,TGP, ECM, AIM-9*2", + "AGM-65D*2,AGM-65H*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,TGP,ECM", + "AGM-65D*4, CBU-105*2,CBU-97*2, TGP, ECM, AIM-9*2", + "AGM-65D*2,Mk-82*6,AIM-9*2,ECM", + "AGM-65D*2,AGM-65H*2,TGP, ECM, AIM-9*2", + "GBU-38*4,GBU-31*2,TGP, AIM-9*2", + "AGM-65D*4,GBU-12*2,GBU-38*2,AIM-9*2,TGP,ECM,MK5*7", + "AGM-65G,AGM-65K,GBU-10*2,AIM-9*2,TGP,ECM", + "AGM-65G,AGM-65D,Mk-82*7,AIM-9*2,ECM", + "GBU-31*2,GBU-38*4,AIM-9*2,TGP,ECM, AIM-9*2", + "AGM-65K*2,GBU-12*8,AIM-9M*2.ECM,TGP", + "AGM-65D*6,GBU-12*4,AIM-9M*2,ECM,TGP", + "AGM-65E*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,ECM,TGP", + "AGM-65E*2,CBU-97*4,AIM-9M*2,ECM,TGP", + "AGM-65E*2,CBU-97*4,AIM-9M*2,ECM,M151 APKWS*7,TGP", + "AGM-65E*2,CBU-105*4,AIM-9M*2,ECM,M151 APKWS*7,TGP", + "Mk-82*4,Mk-8AIR*4,AIM-9*2,ECM", + "Mk-82*20,AIM-9*2,ECM", + "Mk-82*6,AIM-9*2,TGP,ECM", + "Mk-84*6,AIM-9*2,TGP,ECM", + "Mk-82AIR*6,Mk-8AIR*4,M151*1,TGP,AIM-9*2,ECM", + "GBU-38*4,M151 APKWS*7,AGM-65D*1,AGM-65H*1,TGP,AIM-9*2,ECM", + "GBU-38*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", + "GBU-12*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", + "GBU-12*2,GBU-38*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", + "GBU-10*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", + "GBU-31*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", + "GBU-54*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM", + "GBU-54*4,M151 APKWS*7,AGM-65D*4,TGP,AIM-9*2,ECM" + ], + "AH-64D_BLK_II":[ + "4 * Fuel Tank 230 gal", + "2 * M261: M151 (6PD), 2 * Hellfire station: 4*AGM-114K", + "4 * Hellfire station: 4*AGM-114K", + "4 * M261: M151 (6PD)", + "2 * M261: M151 (6PD), 2 * Fuel Tank 230 gal", + "2 * Fuel Tank 230 gal, 2 * Hellfire station: 4*AGM-114K", + "2 * M261: A/B - M151 (6PD), E - M274 (6SK), 2 * Hellfire station: 4*AGM-114K", + "2 * M261: A/B - M151 (6PD), E - M257 (6IL), 2 * Hellfire station: 4*AGM-114K", + "2 * M261: C - M257 (6IL), D/E - M151 (6PD), 2 * Hellfire station: 4*AGM-114K", + "2 * M261: C - M274 (6SK), D/E - M151 (6PD), 2 * Hellfire station: 4*AGM-114K" + ], + "AJS37":[ + "Battlefield Air Interdiction: RB-75*4, RB-24J*2, XT", + "Anti-ship: RB-04E*2, RB-74*2, XT", + "Anti-ship (Heavy Mav): RB-75T*4, XT", + "Hard Target (Countermeasures): RB-05, XT, KB, U22", + "Hard Target (MAV): RB-75T*2, RB-74*2, XT", + "Ferry Flight: XT", + "CAS (75 GUN): RB-75*2, AKAN", + "CAP: RB-74*4, XT", + "Countermeasures Escort: U/22A, KB", + "Strike: BK90 (MJ1)*2, RB-74*2, XT", + "CAS: AKAN, RB-05A", + "CAP (6 AAM): RB-74*4, RB-24J*2, XT", + "Rocket Half Load HE: ARAK HE*2, RB-74*2, XT", + "CAP / Intecept: RB-05A*2, RB-74*2, XT", + "Bombs Low-drag: SB71LD*16, RB-24J*2, XT", + "SEAD: RB-75T*2, U22/A, KB, XT", + "Anti-Ship (Modern): RB-15F*2, RB-74*2, XT", + "New Payload", + "CAP (AJ37): RB-24J*2", + "ECM Escort Anti-ship: RB-04E, KB, RB-74*2, XT", + "Bombs High-drag: SB71HD*16, XT, RB-24J", + "Anti-ship (Light Mav): RB-75*4, XT", + "Rocket Full Load HE: ARAK HE*4, RB-24J, XT", + "Illumination: LYSB*8, XT", + "Anti-ship (RB05): RB-05A*2, RB-74*2, XT", + "CAP (Gun): AKAN*2, RB-74*2, XT", + "Hard Target: RB-05A*2, RB-74*2, XT", + "RB-05*2, XT", + "CAS: ARAK M70 HE*4, XT", + "Runway Strike: SB71HD*16, RB-24J, XT" + ], + "AV8BNA":[ + "H-L-H 2", + "AFAC 1", + "AS 1", + "H-M-H 3", + "Interdiction 1 (H-H-H-H)", + "Interdiction 2 (H-H-H-H)", + "AFAC 3", + "Interdiction 3 (H-L-L-H)", + "H-M-H 1", + "H-L-H 1", + "PGM 2 (H-H-H-H)", + "L-L-L 1", + "AFAC 2", + "H-M-H 2", + "Area Suppression", + "Rockets 1", + "AS 2", + "Helo Escort 1", + "PGM 1 (H-H-H-H)", + "Helo Escort 2", + "PGM 3 (H-H-H-H)", + "Anti Armor", + "RA 1 (H-M-M-H)", + "Stand Off 1", + "Stand Off 2", + "Stand Off 3", + "Iron Hand 1", + "Iron Hand 2" + ], + "C-101CC":[ + "2*AIM-9P, DEFA 553 CANNON (I)", + "2*AIM-9M, DEFA 553 CANNON (I)", + "2*AIM-9P, DEFA 533 CANNON (II)", + "2*AIM-9P, AN-M3 CANNON (IV)", + "2*R.550 MAGIC, DEFA 553 CANNON", + "2*AIM-9M, AN-M3 CANNON (III)", + "2*AIM-9P, DEFA 553 CANNON", + "2*R.550 MAGIC, DEFA 553 CANNON (III)", + "2*AIM-9P, 2*BELOUGA, DEFA 553 CANNON", + "2*AIM9-P, 2*SEA EAGLE, DEFA-553 CANNON", + "2*AIM-9M 2*SEA EAGLE, AN-M3 CANNON", + "2*AIM-9M, AN-M3 CANNON", + "2*BELOUGA,2*BDU-33, DEFA-553 CANNON", + "2* SEA EAGLE, DEFA-553 CANNON", + "2*AIM-9P, 2*BR-250,2*MK-82, DEFA 553 CANNON", + "2*R.550 MAGIC, 2*SEA EAGLE , DEFA-553 CANNON", + "2*R.550 MAGIC, DEFA 553 CANNON (IV)", + "2*BELOUGA, 2*BR-500, DEFA 553 CANNON", + "2*AIM-9M, DEFA 553 CANNON (IV)", + "2*R.550 MAGIC, AN-M3 CANNON (II)", + "2*R550 Magic, DEFA 553 CANNON (I)", + "2*AIM-9M ,2*BELOUGA,2*BIN-200, AN-M3 CANNON", + "2*AIM-9M, 2*LAU 68, 2*MK-82, DEFA 553 CANNON", + "2*AIM-9P, AN-M3 CANNON (III)", + "2*AIM-9M, DEFA 533 CANNON (II)", + "2*R.550 MAGIC, 2*BR-250, 2*BDU-33, DEFA 553 CANNON" + ], + "C-101EB":[ + "Smoke System: White Smoke", + "Smoke System: White Smoke+Red Colorant", + "Smoke System: White Smoke+Yellow Colorant" + ], + "H-6J":[ + "YJ-12 x 2", + "YJ-12 x 4", + "YJ-83K x 6", + "250-2 HD Bomb x 12 in Bay", + "250-2 HD Bomb x 24 in Bay", + "250-3 LD Bomb x 36", + "KD-63 x 4", + "KD-20 x 6", + "KD-20 x 4", + "KD-63 x 2, KD-20 x 4", + "KD-63 x 2, KD-20 x 2" + ], + "J-11A":[ + "FAB-100x36,R-73x2,ECM", + "FAB-250x8,R-73x2,ECM", + "FAB-500x8,R-73x2,ECM", + "S-8KOMx80,FAB-250x4,R-73x2,ECM", + "S-13x20,FAB-250x4,R-73x2,ECM", + "S-25x4,FAB-500x4,R-73x2,ECM", + "R-27ERx4,R-27ETx2,R-73x2,ECM", + "R-77x6,R-73x2,ECM", + "R-27ERx6,R-73x2,ECM", + "R-77x4,R-27ETx2,R-73x2,ECM", + "R-77x4,R-27ERx2,R-73x2,ECM", + "BetAB-500ShPx6,R-73x2,ECM", + "R-73x4,ECM", + "R-77x2,R-27ETx2,R-73x2,ECM", + "R-77x6,R-73x4", + "R-77x2,R-27ETx2,R-27ERx2,R-73x2,ECM", + "R-27ETx2,R-27ERx4,R-73x2,ECM", + "S-8TsMx80,FAB-250x4,R-73x2,ECM", + "S-8OFP2x80,FAB-250x4,R-73x2,ECM", + "FAB-250x18,R-73x2,ECM", + "2*S8-KOMx2, R-73x2, ECM", + "2*S8-OFP2x2, R-73x2, ECM", + "FAB-250x4, 2*FAB-500x2, R-73x2", + "FAB-250x4, 2*FAB-250x2, R-73x2", + "RBK-250 HEAT/APx2, RBK-250 HE/Fragx2, R-73x2" + ], + "JF-17":[ + "PL-5Ex2, C802AKx2, 800L Tank", + "PL-5Ex2, C-701 IRx2, 1100L Tank, 800L Tank", + "PL-5Ex2, SD-10x2, 1100L Tankx2, WMD7", + "PL-5Ex2, LD-10x2, 1100L Tankx2, WMD7", + "PL-5Ex2, 800L Tank, WMD7", + "PL-5Ex2, GBU-10x2, WMD7", + "PL-5Ex2, 2*GBU-12x2, 800L Tank, WMD7", + "PL-5Ex2, 2*Mk-82x2, Mk-83x2, 800L Tank", + "PL-5Ex2, 1100L Tankx2, 800L Tank", + "PL-5Ex2, WMD7, CM802AKGx2, 800L Tank, DL", + "PL-5Ex2, C-701 CCDx2, 1100L Tank, 800L Tank", + "PL-5Ex2, GBU-12x2, 1100L Tank, WMD7", + "PL-5Ex2, 2*GBU-12x2, GBU-16x2, WMD7", + "PL-5Ex2, 1100L Tankx2, WMD7", + "PL-5Ex2, WMD7, 800L Tankx2, SPJ, 2*LD-10", + "PL-5Ex2, LS-6x2, 1100L Tankx2, WMD7", + "PL-5Ex2, C-701 IRx2, 1100L Tankx2, WMD7", + "PL-5Ex2, GBU-12x2, 1100L Tankx2, WMD7", + "PL-5Ex2, 2*LD-10x2, 1100L Tankx2, SPJ", + "PL-5Ex2, LD-10x2, 1100L Tankx2, SPJ", + "PL-5Ex2, 2*LD-10x2, LS-6x2, SPJ", + "PL-5Ex2, 2*LD-10x2, GB-6-HEx2, SPJ", + "PL-5Ex2, C-701 IRx2, 800L Tankx2, WMD7", + "PL-5Ex2, C-701 CCDx2, 1100L Tankx2, WMD7", + "PL-5Ex2, C-701 CCDx2, 800L Tankx2, WMD7", + "PL-5Ex2, 2*GBU-12x2, 1100L Tank, WMD7", + "PL-5Ex2, C-701 IRx2, 1100L Tank, WMD7", + "PL-5Ex2, C-701 IRx2, 800L Tank, WMD7", + "PL-5Ex2, C-701 CCDx2, 1100L Tank, WMD7", + "PL-5Ex2, C-701 CCDx2, 800L Tank, WMD7", + "PL-5Ex2, C-701 IRx2, LS-6x2, 800L Tank", + "PL-5Ex2, C-701 IR/CCD, GB-6-HEx2, 800L Tank", + "PL-5Ex2, C-701 IR/CCD, GB-6-SFWx2, 800L Tank", + "PL-5Ex2, WMD7, GB-6-SFWx2, 800L Tank, BRM1", + "PL-5Ex2, WMD7, GB-6-SFWx2, 800L Tank, GBU-12", + "PL-5Ex2, 2*Mk-82SEx2, Mk-83x3", + "PL-5Ex2, Mk-84x3", + "PL-5Ex2, 2*Mk5x2, 800L Tank", + "PL-5Ex2, Unguided 90mmx2, 800L Tank", + "PL-5Ex2, 2*Mk5x2, Mk-83x3", + "PL-5Ex2, BRM1x2, 1100L Tank, WMD7", + "PL-5Ex2, 2x1100L Tank", + "PL-5Ex2, SD-10x2, 2x1100L Tank", + "PL-5Ex2, 2*SD-10x2, 2x1100L Tank", + "PL-5Ex2, 800L Tank", + "PL-5Ex2, SD-10x2, 800L Tank", + "PL-5Ex2, 2*SD-10x2, 800L Tank", + "PL-5Ex2, SD-10x2, SPJ", + "PL-5Ex2, SPJ", + "PL-5Ex2, 2*SD-10x2, SPJ", + "PL-5Ex2", + "PL-5Ex2, SD-10x2", + "PL-5Ex2, 2*SD-10", + "PL-5Ex2, SD-10x2, SPJ, 1100L Tankx2", + "PL-5Ex2, 2*SD-10x2, 1100L Tankx2, 800L Tank", + "PL-5Ex2, SD-10x2, 1100L Tankx2, 800L Tank", + "PL-5Ex2, GBU-16x2, BRM1x2, WMD7", + "PL-5Ex2, WMD7", + "PL-5Ex2, 2*LD-10, GB-6x2, 2*SD-10, SPJ", + "PL-5Ex2, C-701 CCDx2, SPJ", + "PL-5Ex2, 2*LD-10, CM802AKGx2, 2*SD-10, DL", + "PL-5Ex2, 2*MK-82x2, MK-83x2, MK-84", + "PL-5Ex2, LS-6x2, GB-6x2, 800L Tank", + "PL-5Ex2, 2*GBU-12x2, LS-6x2, WMD7", + "PL-5Ex2, 2*GBU-12x2, GB-6x2, WMD7", + "PL-5Ex2, 2*Type-200Ax2", + "PL-5Ex2, Type-200Ax2", + "PL-5Ex2, 2*LS6-250x2, 800L Tankx2, WMD7", + "PL-5Ex2, 2*LS6-250x2, 800L Tank, 1100L Tankx2", + "PL-5Ex2, 2*LS6-100x2, 1100L Tankx2, WMD7", + "PL-5Ex2, 2*LS6-100x2, 800L Tankx2, WMD7" + ], + "WingLoong-I":[ + "AKD-10 x 2" + ], + "Christen Eagle II":[ + "Smoke - white" + ], + "F-16C_50":[ + "AIM-120B*2, AIM-9M*4, FUEL*3", + "AIM-120B*4, AIM-9M*2, FUEL*3", + "AIM-120B*6, FUEL*3", + "AIM-120C*2, AIM-9X*4, FUEL*2", + "AIM-120C*4, AIM-9X*2, FUEL*3", + "AIM-120C*4, AIM-9X*2, FUEL*3, TGP", + "AIM-120C*4, AIM-9X*2, FUEL*2", + "AIM-120C*6, FUEL*3", + "AIM-120C*4, AIM-9X*2, FUEL*2, ECM", + "AIM-120C*4, AIM-9X*2, FUEL*2, ECM, TGP", + "AIM-120C*6, FUEL*2, ECM", + "AIM-120C*6, FUEL*2, ECM, TGP", + "AIM-120C*6, FUEL*2", + "AIM-120C*6, FUEL*3, TGP", + "AIM-120C*2, AIM-9X*2, AGM-65D*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, AGM-65H*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, AGM-65H*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, AGM-65D*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, CBU-97*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, MK-82*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, CBU-87*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, MK-82HD*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, CBU-103*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, CBU-105*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, MK-82*6, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, MK-82HD*6, FUEL*2, ECM, TGP", + "AIM-120*2, AIM-9X*2, MK-82SE*4, FUEL*2, ECM, TGP", + "AIM-120*2, AIM-9X*2, MK-82SE*6, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, MK-84*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, MK-82P*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-12*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-12*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-10*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-24*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-31-1B*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-31-3B*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-38*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, GBU-38*4, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, AGM-65K*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, AGM-65G*2, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, AGM-65G, AGM-65K, FUEL*2, ECM, TGP", + "AIM-120C*2, AIM-9X*2, AGM-88C*2, FUEL*3, TGP, HTS", + "AIM-120C*2, AIM-9X*2, AGM-88C*2, FUEL*2, ECM, TGP, HTS", + "AIM-120C*2, AIM-9X*2, AGM-88C*4, ECM, TGP, HTS", + "AIM-120C*4, AGM-88C*2, FUEL*3, TGP, HTS", + "AIM-120C*4, AGM-88C*2, FUEL*2, ECM, TGP, HTS", + "AIM-120C*4, AGM-88C*4, ECM, TGP, HTS", + "AIM-120C*2, AIM-9X*2, MK-61*2, FUEL*2, ECM, TGP" + ], + "F-5E-3":[ + "Mk-82LD*4,AIM-9P*2,Fuel 275", + "AIM-9P*2, Fuel 275*3", + "AIM-9P5*2, Fuel 275*3", + "AIM-9P*2, Fuel 150*3", + "AIM-9P5*2, Fuel 150*3", + "Mk-82SE*4,AIM-9P*2,Fuel 275", + "CBU-52B*4,AIM-9P*2,Fuel 275", + "LAU-3 HE*4,AIM-9P*2,Fuel 275", + "LAU-3 HEAT*4,AIM-9P*2,Fuel 275", + "LAU-68 HE*4,AIM-9P*2,Fuel 275", + "LAU-68 HEAT*4,AIM-9P*2,Fuel 275", + "M-117*4,AIM-9P*2,Fuel 275", + "GBU-12*4,AIM-9P*2,Fuel 275", + "CBU-52B*5,AIM-9*2", + "Mk-82LD*5,AIM-9*2", + "Mk-82SE*5,AIM-9*2", + "Mk-82LD*7,AIM-9P*2, Fuel 275*2", + "Mk-82SE*7,AIM-9P*2, Fuel 275*2", + "LAU-3 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", + "LAU-68 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", + "M-117*5,AIM-9*2", + "AIM-9P*2, Fuel 275", + "AIM-9P*2, Fuel 150", + "AIM-9P5*2, Fuel 275", + "AIM-9P5*2, Fuel 150", + "AIM-9B*2, Fuel 275", + "AIM-9B*2, Fuel 150", + "AIM-9B*2, Fuel 275*3", + "AIM-9B*2, Fuel 150*3", + "AN/ASQ-T50, AIM-9P, Fuel 150", + "AIM-9B*2", + "AIM-9P*2", + "AIM-9P5*2", + "Antiship Mk82" + ], + "F-5E":[ + "Mk-82LD*4,AIM-9P*2,Fuel 275", + "AIM-9P*2, Fuel 275*3", + "AIM-9P5*2, Fuel 275*3", + "AIM-9P*2, Fuel 150*3", + "AIM-9P5*2, Fuel 150*3", + "Mk-82SE*4,AIM-9P*2,Fuel 275", + "CBU-52B*4,AIM-9P*2,Fuel 275", + "LAU-3 HE*4,AIM-9P*2,Fuel 275", + "LAU-3 HEAT*4,AIM-9P*2,Fuel 275", + "LAU-68 HE*4,AIM-9P*2,Fuel 275", + "LAU-68 HEAT*4,AIM-9P*2,Fuel 275", + "M-117*4,AIM-9P*2,Fuel 275", + "GBU-12*4,AIM-9P*2,Fuel 275", + "CBU-52B*5,AIM-9*2", + "Mk-82LD*5,AIM-9*2", + "Mk-82SE*5,AIM-9*2", + "Mk-82LD*7,AIM-9P*2, Fuel 275*2", + "Mk-82SE*7,AIM-9P*2, Fuel 275*2", + "LAU-3 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", + "LAU-68 HE*2,Mk-82LD,AIM-9P*2,Fuel 275*2", + "M-117*5,AIM-9*2", + "AIM-9P*2, Fuel 275", + "AIM-9P*2, Fuel 150", + "AIM-9P5*2, Fuel 275", + "AIM-9P5*2, Fuel 150", + "AIM-9B*2, Fuel 275", + "AIM-9B*2, Fuel 150", + "AIM-9B*2, Fuel 275*3", + "AIM-9B*2, Fuel 150*3", + "AN/ASQ-T50, AIM-9P, Fuel 150", + "AIM-9B*2", + "AIM-9P*2", + "AIM-9P5*2" + ], + "F-86F Sabre":[ + "120gal Fuel*2", + "200gal Fuel*2", + "120gal Fuel*2, 200gal Fuel*2", + "GAR-8*2", + "120gal Fuel*2, GAR-8*2", + "HVAR*16", + "200gal Fuel*2, HVARx2*4", + "AN-M64*2", + "200gal Fuel*2, AN-M64*2", + "M117*2" + ], + "F-14A-135-GR":[ + "XT*2", + "AIM-54A-MK47*6, AIM-9L*2, XT*2", + "AIM-7F*6, AIM-9L*2, XT*2", + "AIM-54A-MK47*4, AIM-7F*2, AIM-9L*2, XT*2", + "AIM-54A-MK47*2, AIM-7F*1, AIM-9L*4, XT*2", + "AIM-54A-MK47*4, AIM-9L*4, XT*2", + "AIM-54A-MK47*4, AIM-9M*4, XT*2", + "AIM-54A-MK60*4, AIM-9M*4, XT*2", + "AIM-7F*4, AIM-9L*4, XT*2", + "AIM-7F*4, AIM-9L*4, XT*2", + "BDU-33*14", + "BDU-33*12", + "GBU-10*2", + "GBU-12*4", + "GBU-16*4", + "GBU-24*2", + "Mk-84*4", + "Mk-83*4", + "Mk-82*4", + "Mk-82*14", + "Mk-81*14", + "Mk-20*4", + "Mk-82AIR*4", + "Zuni*12", + "Zuni*28", + "LUU-2*24", + "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, XT*2, Mk-82*2", + "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, XT*2, Mk-82*1", + "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, XT*2, Mk-20*2", + "AIM-7M*1, AIM-9M*2, XT*2, GBU-12*2, LANTIRN", + "AIM-7M*1, AIM-9M*2, XT*2, GBU-24*1, LANTIRN", + "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", + "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-20*2, LANTIRN" + ], + "F-14A-95-GR":[ + "AIM-54A-MK47*6, AIM-9L*2", + "AIM-54A-MK47*6, AIM-9L*2", + "AIM-54A-MK60*6, AIM-9L*2", + "AIM-7F*6, AIM-9L*2", + "AIM-7F*6, AIM-9L*2", + "AIM-54A-MK47*4, AIM-7F*2, AIM-9L*2", + "AIM-54A-MK47*4, AIM-7F*2, AIM-9L*2", + "AIM-54A-MK60*4, AIM-7F*2, AIM-9L*2", + "AIM-54A-MK47*2, AIM-7F*1, AIM-9L*4", + "AIM-54A-MK47*2, AIM-7F*1, AIM-9L*4", + "AIM-54A-MK60*2, AIM-7F*1, AIM-9L*4", + "AIM-54A-MK47*4, AIM-9L*4", + "AIM-54A-MK47*4, AIM-9L*4", + "AIM-54A-MK60*4, AIM-9L*4", + "AIM-7F*4, AIM-9L*4", + "AIM-7F*4, AIM-9L*4", + "AIM-54A-MK47*2, AIM-7F*3, AIM-9L*2", + "AIM-54A-MK47*2, AIM-7F*3, AIM-9L*2", + "AIM-54A-MK60*2, AIM-7F*3, AIM-9L*2", + "BDU-33*14", + "BDU-33*12", + "GBU-10*2", + "GBU-12*4", + "GBU-16*4", + "GBU-24*2", + "Mk-84*4", + "Mk-83*4", + "Mk-82*4", + "Mk-82*14", + "Mk-81*14", + "Mk-20*4", + "Mk-82AIR*4", + "Zuni*12", + "Zuni*28", + "LUU-2*24", + "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, Mk-82*2", + "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, Mk-82*1", + "AIM-54A-MK60*1, AIM-7F*1, AIM-9L*2, Mk-20*2" + ], + "F-14B":[ + "XT*2", + "AIM-54A-MK47*6, AIM-9M*2, XT*2", + "AIM-54A-MK47*6, AIM-9L*2, XT*2", + "AIM-54A-MK60*6, AIM-9M*2, XT*2", + "AIM-54C-MK47*6, AIM-9M*2, XT*2", + "AIM-7M*6, AIM-9M*2, XT*2", + "AIM-7M*6, AIM-9L*2, XT*2", + "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", + "AIM-54A-MK47*4, AIM-7M*2, AIM-9L*2, XT*2", + "AIM-54A-MK60*4, AIM-7M*2, AIM-9M*2, XT*2", + "AIM-54C-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", + "AIM-54A-MK47*2, AIM-7M*1, AIM-9M*2, AIM-9L*2, XT*2", + "AIM-54A-MK47*2, AIM-7M*1, AIM-9M*4, XT*2", + "AIM-54A-MK60*2, AIM-7M*1, AIM-9M*4, XT*2", + "AIM-54C-MK47*2, AIM-7M*1, AIM-9M*4, XT*2", + "AIM-54A-MK47*4, AIM-9M*2, AIM-9L*2, XT*2", + "AIM-54A-MK47*4, AIM-9M*4, XT*2", + "AIM-54A-MK60*4, AIM-9M*4, XT*2", + "AIM-54C-MK47*4, AIM-9M*4, XT*2", + "AIM-7M*4, AIM-9M*2, AIM-9L*2, XT*2", + "AIM-7M*4, AIM-9L*4, XT*2", + "AIM-54A-MK47*2, AIM-7M*3, AIM-9M*2, XT*2", + "AIM-54A-MK47*2, AIM-7M*3, AIM-9M*2, XT*2", + "AIM-54A-MK60*2, AIM-7M*3, AIM-9M*2, XT*2", + "AIM-54C-MK47*2, AIM-7M*3, AIM-9M*2, XT*2", + "BDU-33*14", + "BDU-33*12", + "GBU-10*2", + "GBU-12*4", + "GBU-16*4", + "GBU-24*2", + "Mk-84*4", + "Mk-83*4", + "Mk-82*4", + "Mk-82*14", + "Mk-81*14", + "Mk-20*4", + "Mk-82AIR*4", + "Zuni*12", + "Zuni*28", + "LUU-2*24", + "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2", + "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*1", + "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-20*2", + "AIM-7M*1, AIM-9M*2, XT*2, GBU-12*2, LANTIRN", + "AIM-7M*1, AIM-9M*2, XT*2, GBU-24*1, LANTIRN", + "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", + "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-20*2, LANTIRN" + ], + "FA-18C_hornet":[ + "AIM-9M*6, AIM-7M*2, FUEL*3", + "AIM-9M*6, AIM-7M*2, FUEL*2", + "AIM-9M*2, MK-84*2, FUEL*2", + "AIM-9M*2, MK-83*4, FUEL*2", + "Carrier Landing", + "AIM-9M*2, AIM-7M*4, FUEL*3", + "AIM-9M*2, CBU-99*4, FUEL*2", + "AIM-9M*2, MK-82SE*4, FUEL*2", + "AIM-9M*2, MK-20*4, FUEL*2", + "AIM-9M*2, MK-82*4, FUEL*2", + "AIM-9M*2, AIM-7M*2, FUEL*2", + "AIM-9M*2, MK-83*2, FUEL*2", + "AIM-9M*2, ZUNI*4, FUEL*2", + "AIM-9M*2, LAU-61*4, FUEL*2", + "AIM-9M*2, LAU-68*4, FUEL*2", + "AIM-9M*2, AIM-7M*2, FUEL*1", + "AIM-9X*2, AIM-120C-5*1, GBU-31*4, ATFLIR, FUEL", + "AIM-9X*2, AIM-120C-5*6, FUEL*3", + "AIM-9X*2, AIM-120C-5*1, AGM-65D*4, ATFLIR, FUEL", + "AIM-9X*2, AIM-120C-5*2, AGM-88C*2, FUEL", + "AIM-9X*2, AIM-120C-5*1, GBU-38*4, GBU-12*4, ATFLIR, FUEL", + "AIM-9X*2, AIM-120C-5*1, AGM-84E*2, DATALINK, ATFLIR, FUEL*2", + "AIM-9M*2, AIM-120C-5*1, AGM-84D*4, ATFLIR, FUEL", + "AIM-9M*2, ATFLIR, FUEL", + "AIM-9M*2, ATFLIR, FUEL*2" + ], + "I-16":[ + "6xRS-82", + "2xFAB-100", + "6xRS-82, 2xFAB-100", + "6xRS-82, 2xDropTank-93L", + "2xDropTank-93L" + ], + "Ka-50":[ + "4xFAB-500", + "2xFuel tank, 2xKMGU AP", + "80xS-8 TsM", + "2xFuel tank, 40xS-8", + "80xS-8", + "4xKMGU AT", + "2xKMGU AP", + "2xFuel tank, 12x9A4172", + "2xFuel tank, 2xUPK-23", + "12x9A4172, 40xS-8", + "12x9A4172, 2xKMGU AT", + "4xKMGU AP", + "2xFAB-250, 2xFuel tank", + "12x9A4172, 2xFAB-500", + "20xS-13", + "40xS-8", + "12x9A4172, 2xUPK-23", + "2xKMGU AT", + "6x9A4172", + "2xFuel tank, 2xKMGU AT", + "4xFuel tank", + "2xKMGU AP, 12x9A4172", + "12x9A4172", + "2xFuel tank", + "40xS-8 TsM, 12x9A4172", + "2xFAB-500", + "40xS-8 TsM, 2xFuel tank", + "4xUPK-23", + "10xS-13", + "40xS-8 TsM", + "12x9A4172, 10xS-13", + "2xFuel tank, 2xFAB-500", + "2xFAB-250", + "4xFAB-250", + "2xFuel tank, 10xS-13", + "2xFAB-250, 12x9A4172", + "2xUPK-23" + ], + "Ka-50_3":[ + "4xIgla", + "2xKh-25ML, 10xS-13, 4xIgla", + "12x9A4172, 40xS-8KOM, 4xIgla", + "12x9A4172, 40xS-8OFP, 4xIgla", + "12x9A4172, 40xS-13, 4xIgla", + "80xS-8KOM, 4xIgla", + "80xS-8OFP, 4xIgla", + "20xS-20, 4xIgla", + "4xUPK-23, 4xIgla", + "10xS-13, 2xFAB-500, 4xIgla", + "10xS-13, 2xFAB-250, 4xIgla", + "80xS-8OM, 4xIgla", + "80xS-8TsM, 4xIgla", + "40xS-8OFP, 2xFuel, 4xIgla", + "12x9A4172, 2xFuel, 4xIgla" + ], + "L-39C":[ + "SAB-100x2", + "R-3Sx2", + "Smokes", + "Central Smoke", + "FAB-100x2", + "S-5KOx32" + ], + "L-39ZA":[ + "S-5KOx32", + "S-5KOx64", + "S-5KOx32, PTB-150x2", + "S-5KOx32, PTB-350x2", + "S-5KOx32, FAB-100x2", + "OFAB-100 Jupiter x4, FAB-100x2", + "FAB-100x2", + "FAB-100x4", + "OFAB-100 Jupiter x8", + "FAB-100x2, PTB-150x2", + "FAB-100x2, PTB-350x2", + "PK-3x4", + "PK-3x2, PTB-150x2", + "R-60Mx2", + "SAB-100x4", + "R-3Sx2", + "R-3Sx2, PK-3x2", + "R-60Mx2, PK-3x2" + ], + "M-2000C":[ + "Fox", + "Fox / Magic (QRA)", + "Alpha / S530D", + "Fox / S530D / Magic", + "Fox / S530D / Magic / Eclair", + "Bravo", + "Bravo / Magic", + "Kilo", + "Kilo / Magic", + "Bravo / 4xMk-82 / Magic", + "Bravo / GBU-12 / Magic", + "Bravo / 2xGBU-12 / Magic", + "Bravo / GBU-16 / Magic", + "Bravo / GBU-24 / Magic", + "Bravo / BAP-100 / Magic", + "Bravo / 4xSnakeEye / Magic", + "Fox / 4xMk-82 / Magic", + "Kilo / 4xMk-82 / Magic" + ], + "MB-339A":[ + "A - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*Mk.83 + 2*Mk.81 ", + "A - 2*320L TipTanks [Clean]", + "Recon", + "Training", + "AA - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*LAU-10(Zuni Rockets) [ARMADA]", + "AM - 2*320L TipTanks + 2*AN/M3 GunPods + 2*330L Tanks + 2*LAU-3 (Hydra rockets)", + "A - 2*500L TipTanks + 2*330L Tanks + Luggage Container [Ferry Long Range]", + "A - 2*500L TipTanks + 4*Mk.82HD + 2*LR-25 (API Rockets)", + "A - 2*320L TipTanks + 2*330L Tanks [Ferry Medium Range]", + "A - 2*500L TipTanks + 2*AN/M3 GunPods + 2*Matra 155 + 2* Belouga", + "Runway Interdiction", + "A - 2*500L TipTanks + 2*DEFA-553 GunPods + 2*Mk.82LD + 2*LR-25 (API Rockets)", + "A - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*Mk.82LD Bombs + 2*LR-25(API Rockets)", + "A - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*Mk.82LD + 2*LR-25 (HEI Rockets)", + "A - 2*320L TipTanks + 6*Mk.82LD", + "Runway Interdiction (36*BAP-100)", + "Anti - Light Armoured Vehicle (36*BAT-120 ABL)", + "AP - 2*320L TipTanks + 2*DEFA-553 GunPods + 2*330L Tanks + 2*Matra 155 (SNEB rockets)" + ], + "MB-339APAN":[ + "PAN - Smoke White", + "PAN - Smoke White and Color Red", + "PAN - Fuel External [RED SMOKE]", + "PAN - Full Payload [RED SMOKE] + 2*320L TipTanks", + "PAN - Full Payload [GREEN SMOKE] + 2*320L TipTanks", + "PAN - Fuel External [GREEN SMOKE]", + "PAN - Full Payload [RED SMOKE] + 2*500L TipTanks", + "PAN - Smoke White and Color Green", + "PAN - Full Payload [GREEN SMOKE] + 2*500L TipTanks" + ], + "Mi-24P":[ + "2xB8V20 (S-8KOM)+8xATGM 9M114", + "2xB8V20 ( S-8KOM)+4xATGM 9M114", + "4xB8V20 (S-8KOM)+4xATGM 9M114", + "2xB8V20 (S-8KOM)+2xBombs-250+4xATGM 9M114", + "2xB8V20 (S-8OFP2)+4xATGM 9M114", + "4xUB-32A (S-5KO)+4xATGM 9M114", + "4xGUV-1 AP30+4xATGM 9M114", + "2xGUV-1 AP30+4xATGM 9M114", + "2xGUV-1 (GUN 12.7+2x7.62) +4xATGM 9M114", + "2xKMGU (96 AO 2.5RT)+8xATGM 9M114", + "2xB-13L+4xATGM 9M114", + "2xS-24B+4xATGM 9M114", + "4xS-24B+4xATGM 9M114", + "2xBombs-500+4xATGM 9M114", + "4xBombs-250+4ATGM 9M114", + "2xRBK-500 (PTAB-1M)+4xATGM 9M114", + "2xRBK-500U (OAB 2.5RT)+4xATGM 9M114", + "4xRBK-250 (42 PTAB 2.5M) +4ATGM 9M114", + "4xRBK-250-275 (150 AO-1SCh)+4ATGM 9M114", + "4xPTB-450 Fuel tank" + ], + "MiG-19P":[ + "PTB-760 x 2", + "K-13A x 2, PTB-760 x 2", + "K-13A x 2", + "K-13A x 2, ORO-57K x 2, PTB-760 x 2", + "ORO-57K x 2, PTB-760 x 2", + "ORO-57K x 4", + "ORO-57K x 2", + "FAB-100M x 2, ORO-57K x 2", + "FAB-250 x 2, ORO-57K x 2", + "FAB-100M x 2", + "FAB-250 x 2" + ], + "MiG-21Bis":[ + "Patrol, long range", + "Patrol, medium range", + "Patrol, short range", + "Hard targets, BOMBS", + "Unknown or mixed targets, BOMBS + ROCKETS", + "Soft targets, CLUSTERS + ROCKETS", + "Soft targets, CLUSTERS", + "Soft targets, scattered", + "Few big targets, GROM + BOMBS", + "Very hard target, PENETRATION", + "Aerial attack, hard targets, CLUSTERS", + "Hard targets, ROCKETS, PENETRATION", + "Soft targets, ROCKETS, BLAST-FRAGMENTS", + "Long range, MIX", + "Long range, RADAR GUIDED MISSILES", + "Long range, INFRA RED MISSILES", + "Escort", + "Escort, JAMMER", + "Night, ILLUMINATOR", + "Long range, JAMMER", + "Soft targets, UPK + ROCKETS", + "Soft targets, UPK + CLUSTERS", + "Patrol, JAMMER", + "NUCLEAR A", + "NUCLEAR B", + "Short range", + "AEROBATIC" + ], + "Mirage-F1B":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1BD":[ + "2*R550 Magic I, 2*S530, 1*Fuel Tank", + "2*R550 Magic I", + "2*R550 Magic I, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1BE":[ + "2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*AIM9-JULI, R530IR", + "2*R550 Magic I, R530EM", + "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*AIM-9JULI, 8*SAMP 250 HD", + "2*AIM-9JULI, 8*SAMP 400 LD", + "2*AIM-9JULI, 8*BLU107 Durandal" + ], + "Mirage-F1BQ":[ + "2*R550 Magic I, 2*S530, 1*Fuel Tank", + "2*R550 Magic I", + "2*R550 Magic I, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1C-200":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1C":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1CE":[ + "2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*AIM9-JULI, R530IR", + "2*R550 Magic I, R530EM", + "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*AIM-9JULI, 8*SAMP 250 HD", + "2*AIM-9JULI, 8*SAMP 400 LD", + "2*AIM-9JULI, 8*BLU107 Durandal" + ], + "Mirage-F1CG":[ + "2*AIM-9 JULI, 2*R530IR, 1*Fuel Tank", + "4*AIM-9 JULI, 2*R530IR, 1*Fuel Tank", + "2*AIM-9 JULI, 2*R530EM, 1*Fuel Tank", + "2*AIM-9 JULI, R530IR", + "2*AIM-9 JULI, 1*R530IR, 2*Fuel Tank", + "2*AIM-9 JULI, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*AIM-9 JULI, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*AIM-9 JULI, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*AIM-9 JULI, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*AIM-9 JULI, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*AIM-9 JULI, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1CH":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1CJ":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1CK":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1CR":[ + "2*R550 Magic I, 2*S530, 1*Fuel Tank", + "2*R550 Magic I", + "2*R550 Magic I, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1CT":[ + "2*R550 Magic I, 2*S530, 1*Fuel Tank", + "2*R550 Magic I", + "2*R550 Magic I, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1CZ":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1DDA":[ + "2*R550 Magic I, 2*S530, 1*Fuel Tank", + "2*R550 Magic I", + "2*R550 Magic I, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1ED":[ + "2*R550 Magic II, 2*S530, 1*Fuel Tank", + "2*R550 Magic II", + "2*R550 Magic II, 2*Fuel Tank", + "2*R550 Magic II, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic II, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic II, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic II, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic II, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic II, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1EDA":[ + "2*R550 Magic I, 2*S530, 1*Fuel Tank", + "2*R550 Magic I", + "2*R550 Magic I, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1EE":[ + "2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", + "2*AIM9-JULI, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*AIM9-JULI, R530EM", + "2*R550 Magic I, R530IR", + "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*AIM-9JULI, 8*SAMP 250 HD", + "2*AIM-9JULI, 8*SAMP 400 LD", + "2*AIM-9JULI, 8*BLU107 Durandal" + ], + "Mirage-F1EH":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1EQ":[ + "2*R550 Magic I, 2*S530, 1*Fuel Tank", + "2*R550 Magic I", + "2*R550 Magic I, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*S530, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1JA":[ + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530EM, 1*Fuel Tank", + "2*R550 Magic I, R530IR", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 LD", + "2*R550_Magic_1, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550_Magic_1, 8*SAMP 250 HD", + "2*R550_Magic_1, 8*SAMP 400 LD", + "2*R550_Magic_1, 8*BLU107 Durandal" + ], + "Mirage-F1M-CE":[ + "2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*AIM9-JULI, R530IR", + "2*R550 Magic I, R530IR", + "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*AIM-9JULI, 8*SAMP 250 HD", + "2*AIM-9JULI, 8*SAMP 400 LD", + "2*AIM-9JULI, 8*BLU107 Durandal" + ], + "Mirage-F1M-EE":[ + "2*AIM9-JULI, 2*R530IR, 1*Fuel Tank", + "2*R550 Magic I, 2*R530IR, 1*Fuel Tank", + "2*AIM9-JULI, R530IR", + "2*R550 Magic I, R530IR", + "2*AIM9-JULI, 1*R530IR, 2*Fuel Tank", + "2*R550 Magic I, 1*R530IR, 2*Fuel Tank", + "2*AIM9-J, 2*MATRA F4 SNEB251 (HE), 2*R530IR, 1*Fuel Tank", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 LD", + "2*AIM-9J, 2*Fuel Tank, 4*SAMP 400 HD", + "2*R550 Magic I, 4*MATRA F1 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB253 (Shaped Charge), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 4*MATRA F4 SNEB256 (AP), 1*Fuel Tank", + "2*R550 Magic I, 2*SAMP 250 HD, 2 MATRA F1 SNEB256 (AP), 1*Fuel Tank", + "2*AIM-9JULI, 8*SAMP 250 HD", + "2*AIM-9JULI, 8*SAMP 400 LD", + "2*AIM-9JULI, 8*BLU107 Durandal" + ], + "SA342L":[ + "M621, 8xSNEB68 EAP", + "M621, 8xSNEB68 EAP, IR Deflector", + "M621, 8xSNEB68 EAP, IR Deflector, Sand Filter" + ], + "SA342M":[ + "HOT3x4", + "IR Deflector", + "Hot3x4, FAS, IR Deflector", + "HOT3x2", + "Hot3x4, IR Deflector", + "Hot3x2, IR Deflector" + ], + "SA342Minigun":[ + "IR Deflector", + "IR Deflector, Sand Filter" + ], + "SA342Mistral":[ + "Mistral x 4", + "Mistral x 4, IR Deflector", + "Mistral x 4, IR Deflector, Sand Filter" + ], + "A-20G":[ + "500 lb GP bomb LD*4" + ], + "Bf-109K-4":[ + "Fuel Tank", + "SC250", + "SC500" + ], + "FW-190A8":[ + "Without pylon", + "SC 50 * 4", + "AB 250 (w/ SD 10A)", + "AB 250 (w/ SD 2)", + "AB 500 (w/ SD 10A)", + "SC 250 L2", + "SC 250 J", + "SC 500 J", + "SC 500 L2", + "SD 250 Stg", + "SD 500 A", + "Fuel Tank 300 liters", + "BR 21" + ], + "FW-190D9":[ + "SC500", + "Fuel Tank", + "R4M", + "BR 21" + ], + "MosquitoFBMkVI":[ + "250 lb S.A.P*2; 500 lb S.A.P.*2", + "500 lb GP Mk.V*2, 500 lb GP Short tail*2", + "100 gal Drop tank*2, 500 lb MC Short tail*2", + "RP-3 60lb SAP No2 Mk.I*8, 250 lb A.A.P.*2", + "100 gal. Drop tank*2, 250 lb MC Mk.II, RP-3 60lb F No1 Mk.I*4", + "500 lb GP Short tail*4" + ], + "P-47D-30":[ + "AN-M65*2", + "Fuel150*2", + "AN-M64*2, Fuel110", + "AN-M57*3" + ], + "P-47D-30bl1":[ + "AN-M57*2", + "AN-M64*2, Fuel110" + ], + "P-47D-40":[ + "AN-M65*2", + "Fuel150*2", + "AN-M57*3", + "AN-M64*2, Fuel110", + "M8*6, AN-M57*2, Fuel110", + "HVAR*10, Fuel110" + ], + "P-51D-30-NA":[ + "Fuel75*2", + "HVAR*6,Fuel75*2", + "HVAR*6,M64*2", + "HVAR*6", + "M64*2", + "HVAR*10", + "Smokes" + ], + "P-51D":[ + "Fuel75*2", + "HVAR*6,Fuel75*2", + "HVAR*6,M64*2", + "HVAR*6", + "M64*2", + "HVAR*10", + "Smokes" + ], + "A-50":[ + + ], + "AH-1W":[ + "14xHYDRA-70 WP", + "38xHYDRA-70 WP", + "8xBGM-71, 14xHYDRA-70", + "8xBGM-71, 14xHYDRA-70 WP", + "8xBGM-71, 38xHYDRA-70 WP", + "14xHYDRA-70", + "38xHYDRA-70", + "8xAGM-114", + "28xHYDRA-70", + "8xBGM-71, 38xHYDRA-70", + "8xAGM-114, 38xHYDRA-70 WP", + "8xBGM-71", + "8xAGM-114, 14xHYDRA-70 WP", + "76xHYDRA-70", + "8xAGM-114, 38xHYDRA-70", + "8xAGM-114, 14xHYDRA-70" + ], + "AH-64A":[ + "8xAGM-114", + "38xHYDRA-70 WP", + "76xHYDRA-70", + "8xAGM-114, 38xHYDRA-70 WP", + "38xHYDRA-70", + "8xAGM-114, 38xHYDRA-70", + "AGM-114K*16" + ], + "AH-64D":[ + "76xHYDRA-70", + "38xHYDRA-70", + "38xHYDRA-70 WP", + "8xAGM-114", + "8xAGM-114, 38xHYDRA-70 WP", + "8xAGM-114, 38xHYDRA-70", + "AGM-114K*16" + ], + "An-26B":[ + + ], + "An-30M":[ + + ], + "B-1B":[ + "Mk-82*84", + "AGM-154*12", + "GBU-38*48", + "CBU-87*30", + "CBU-97*30", + "GBU-38*16, CBU-97*20", + "Mk-84*24", + "GBU-31*24", + "GBU-31(V)3/B*24", + "GBU-31*8, GBU-38*32" + ], + "B-52H":[ + "Mk-84*18", + "Mk 82*51", + "Mk20*18", + "AGM-86C*20", + "AGM-84A*8" + ], + "C-130":[ + + ], + "C-17A":[ + + ], + "CH-47D":[ + + ], + "CH-53E":[ + + ], + "E-2C":[ + + ], + "E-3A":[ + + ], + "F-117A":[ + "GBU-12*2", + "GBU-10*2", + "GBU-27*2" + ], + "F-14A":[ + "AIM-9*2", + "AIM-54C*6,AIM-9*2", + "AIM-54C*4,AIM-9*2,AIM-7*2" + ], + "F-15C":[ + "AIM-120B*4, AIM-7M*2, AIM-9M*2, Fuel*3", + "AIM-9*2,AIM-120*6,Fuel", + "AIM-9*4,AIM-120*4,Fuel*3", + "AIM-9*4,AIM-120*4,Fuel", + "AIM-9*2,AIM-120*2,AIM-7*4,Fuel*3", + "AIM-9*2,AIM-120*6,Fuel*3", + "AIM-9*4,AIM-7*4,Fuel", + "AIM-120*8,Fuel", + "AIM-9*4,AIM-7*4,Fuel*3", + "AIM-120*8,Fuel*3", + "AIM-9*2,AIM-120*2,AIM-7*4,Fuel" + ], + "F-15E":[ + "AIM-120B*2,AIM-9M*2,FUEL*3,CBU-87*6,Mk-82AIR*6", + "AIM-120B*2,AIM-9M*2,FUEL*3,CBU-97*12", + "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-82*12", + "AIM-120B*4,AIM-120C*4,FUEL*3", + "AIM-120B*2,AIM-9M*2,FUEL,GBU-31*4,AGM-65H,AGM-65D", + "AIM-120B*2,AIM-9M*2,FUEL,CBU-103*2,GBU-12,GBU-38,AGM-154C*2", + "AIM-120B*4,AIM-9M*4,FUEL*3", + "AIM-120B*2,AIM-9M*2,AIM-120C*2,AIM-7M*2,FUEL*3", + "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*4,GBU-38*4,AGM-154C*2", + "AIM-120B*2,AIM-9M*2,FUEL,GBU-31*4,AGM-154C*2", + "AIM-120B*2,AIM-9M*2,AIM-7M*4,FUEL*3", + "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-84*8", + "AIM-120B*2,AIM-9M*2,FUEL,AGM-154C*2", + "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*4,GBU-38*4,AGM-65D*2", + "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*4,GBU-38*4,AGM-65K*2", + "AIM-120C*2,AIM-9M*4,AIM-7M*2,FUEL*3", + "AIM-9M*4,AIM-7M*4,FUEL*3", + "AIM-120C*2,AIM-9M*2,FUEL*3", + "AIM-120B*2,AIM-9M*2,FUEL,Mk-84*8,AGM-154C*2", + "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-82AIR*12", + "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*2,GBU-38*2,AGM-154C*2", + "AIM-120B*2,AIM-9M*2,FUEL,GBU-12*2,GBU-38*2,AGM-65H,AGM-65D", + "AIM-120B*2,AIM-120C*4,AIM-9M*2,FUEL*3", + "AIM-120B*2,AIM-9M*2,FUEL*3,Mk-82*6,Mk-82AIR*6", + "AIM-120B*2,AIM-9M*2,FUEL,CBU-103*2,GBU-12,GBU-38,AGM-65H*2", + "AIM-120B*2,AIM-9M*2,FUEL,AGM-65D*2", + "AIM-120B*2,AIM-9M*2,FUEL*2,SUU-25*2,GBU-12,GBU-38", + "AIM-120B*6,AIM-9M*2,FUEL*3", + "AIM-120B*2,AIM-9M*2,FUEL,Mk-82AIR*12,AGM-154C*2", + "AIM-120C*6,AIM-9M*2,FUEL*3", + "GBU-31(V)3/B*5, AIM-120C*2, AIM-9M*2" + ], + "F-16A MLU":[ + "Fuel*3", + "AGM-88*2, AGM-65D*2, AIM-120B*2, ECM", + "Mk-82*6,AIM-120*2,ECM,Fuel*2", + "Mk-82*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", + "AGM88*2,AGM-65D*6,AIM-120*2,AIM-9*2,ECM", + "AIM-120*2,GBU-10*2,ECM,Lantirn ,Fuel*2", + "AIM-9*4,ECM", + "Mk20*4,AIM-9*2,ECM,Fuel*2", + "AIM-120*6,ECM", + "AIM-120*4,AIM-9*2,ECM", + "AGM88, AGM-65D, AIM-120*2,Fuel*2,ECM", + "AGM-65D*2,AIM-120*2,ECM,Lantirn ,Fuel*2", + "AIM-9*4,ECM,Fuel*2", + "AIM-120*4,AIM-9*2,ECM,Fuel*2", + "AGM-88*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", + "Mk-84*2,AIM-9*2,ECM,Fuel*2", + "AIM-120*6,ECM,Fuel*2", + "AGM-154*2,AIM-120*2,ECM,Lantirn ,Fuel*2", + "AGM-88*2,AGM-65D*2,AIM-120B*2,AIM-9M*2,ECM", + "Mk20*2,AIM-120*2,ECM,Fuel*2", + "AIM-120*2,AIM-9*2,GBU-12*2,ECM,Lantirn ,Fuel*2", + "AGM-65K*2,AIM-120*2,ECM,Lantirn ,Fuel*2", + "AIM-120B*2_AIM-9M*2_AGM-119*4_ALQ-131" + ], + "F-16A":[ + "AGM-88*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", + "Mk20*4,AIM-9*2,ECM,Fuel*2", + "AIM-9*4,ECM,Fuel*2", + "AGM-65D*2,AIM-120*2,ECM,Lantirn ,Fuel*2", + "AIM-120*4,AIM-9*2,ECM,Fuel*2", + "AGM88*2_AGM-65D*6_AIM-120*2_AIM-9*2_ECM", + "AGM-65K*2,AIM-120*2,ECM,Lantirn ,Fuel*2", + "AIM-120*2,AIM-9*2,GBU-12*2,ECM,Lantirn ,Fuel*2", + "AIM-120*2,GBU-10*2,ECM,Lantirn ,Fuel*2", + "AIM-120*6,ECM,Fuel*2", + "AIM-120*6,ECM", + "AIM-9*4,ECM", + "Fuel*3", + "Mk-84*2,AIM-9*2,ECM,Fuel*2", + "AIM-120*4,AIM-9*2,ECM", + "Mk-82*6,AIM-120*2,ECM,Fuel*2", + "Mk-82*2,AIM-120*2,AIM-9*2,ECM,Fuel*2", + "AGM-154*2,AIM-120*2,ECM,Lantirn ,Fuel*2", + "Mk20*2,AIM-120*2,ECM,Fuel*2", + "AGM-119*2,AIM-120B*2,AIM-9M*2,ALQ-184,Fuel*2" + ], + "F-16C bl.50":[ + "AIM-120B*4, AIM-9M*2, Fuel", + "AIM-120C*4, AIM-9M*2, Fuel", + "AGM-65G*2, AIM-120C*2, AIM-9M*2, Fuel, ECM", + "AGM-65D*6, AIM-120B*2, AIM-9M*2, ECM, Fuel", + "Mk86*6, AIM-9M*4, ECM, Fuel", + "CBU-97*4, AIM-9M*4, Fuel", + "CBU-87*4, AIM-9M*4, Fuel", + "AGM-65D*2, CBU-87*2, AIM-9M*4, Fuel", + "AGM-65D*2, CBU-97*2, AIM-9M*4, Fuel", + "Mk84*2, AIM-9M*4, ECM, Fuel", + "GBU-31v1*2, AIM-9M*4, ECM, Fuel", + "GBU-31v3*2, AIM-9M*4, ECM, Fuel", + "GBU-31v1*2, Mk82*6, AIM-9M*4, Fuel", + "Mk84*4, AIM-9M*4, Fuel", + "Mk82*12, AIM-9M*4, Fuel", + "AGM-154C*2, AIM-9M*4, ECM, Fuel" + ], + "F-16C bl.52d":[ + "AGM-88*2, AGM-65D*2, AIM-120B*2, ECM,LIGHTNING", + "Mk-82*6,AIM-120*2,ECM,Fuel*2,LIGHTNING", + "AGM88*2,AGM-65D*6,AIM-120*2,AIM-9*2,ECM,LIGHTNING", + "AGM-65D*4,AIM-120*2,ECM,Fuel*2,LIGHTNING", + "AIM-120*2,GBU-31*2,ECM,Fuel*2,LIGHTNING", + "AIM-120*2,GBU-31(v)*2,ECM,Fuel*2,LIGHTNING", + "AGM-88*2,AIM-120*2,AIM-9*2,ECM,Fuel*2,LIGHTNING", + "Copy AIM-120*2,GBU-38*2,ECM,Fuel*2,LIGHTNING", + "AIM-120*2,GBU-10*2,ECM,Fuel*2,LIGHTNING", + "Mk-84*2,AIM-120*2,ECM,Fuel*2,LIGHTNING", + "AGM-154*2,AIM-120*2,ECM,Fuel*2,LIGHTNING", + "Mk-82AIR*6,AIM-120*2,ECM,Fuel*2,LITENING", + "CBU97*4,AIM120*2,ECM,Fuel*2,LITENING", + "AGM-88*2,AGM-65D*2,AIM-120B*2,AIM-9M*2,ECM,LITENING", + "CBU87*2,AIM-120*2,ECM,Fuel*2,LITENING", + "AGM88, AGM-65D, AIM-120*2,Fuel*2,ECM,LITENING", + "AIM-120*2,GBU-12*2,ECM,Fuel*2,LITENING", + "AGM-65K*2,AIM-120*2,ECM,Lantirn ,Fuel*2,LITENING", + "AGM-65G*4,AIM-120C*2,AIM-9M*2,ECM", + "AIM-120C*4,AIM-9M*2,ECM,Fuel*2", + "AIM-120B*4,AIM-9M*2,ECM,Fuel*2", + "AIM-9M*4,Fuel*2", + "AIM-120C*2,AIM-120B*2,AIM-9M*2,Fuel*3", + "AIM-9P*4", + "AIM-9M*2,ECM" + ], + "F-4E":[ + "AIM-9*4,AIM-7*4", + "AGM45*2_AGM-65D*4_AIM7*2_ECM", + "AGM-45*2,AIM-7*2,Fuel*2,ECM", + "Mk-82*18,AIM-7*2,ECM", + "GBU-12*2,AIM-7*2,Fuel*2,ECM", + "Mk20*12,AIM-7*2,ECM", + "Mk-82*6,AIM-7*2,Fuel*2,ECM", + "GBU-10*2,AIM-7*2,Fuel*2,ECM", + "Mk20*6,AIM-7*2,Fuel*2,ECM", + "AGM-45*4,AIM-7*2,ECM", + "AGM-65K*4,AIM-7*2,Fuel*2,ECM", + "Fuel*3", + "AIM-9*4,AIM-7*4,Fuel*2", + "Mk-84*2,AIM-7*2,ECM", + "AGM-65K*4,AIM-7M*4,Fuel*3" + ], + "F/A-18A":[ + "GBU-16*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AIM-9*6,Fuel", + "Mk-84*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM-65D*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM88*4_AIM9*2_AIM7_FLIR Pod_Fuel", + "AIM-9*4,Fuel*3", + "LAU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM-88*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel", + "MK-82*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AIM-9*4,AIM-7*2,Fuel*3", + "AGM-65K*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "Fuel*3", + "AGM88*2_AGM65D*2_AIM9*2_AIM7_FLIR Pod_Fuel", + "GBU-12*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AIM-9*4,AIM-7*4,Fuel", + "Mk20*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "GBU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM-84A*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3" + ], + "F/A-18C":[ + "AGM-62*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "GBU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "GBU-12*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "Fuel*3", + "Mk-84*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "GBU-16*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM-154*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AIM-120*4,AIM-9*2,Fuel*3", + "AGM-65D*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM88*2_AGM65D*2_AIM9*2_AIM7_FLIR Pod_Fuel", + "AGM88*4_AIM9*2_AIM7_FLIR Pod_Fuel", + "AIM-9*2,AIM-7*6,Fuel", + "Mk20*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AIM-120*6,AIM-9*2,Fuel", + "LAU-10*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AIM-9*2,AIM-7*4,Fuel*3", + "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel", + "AIM-120*2,AIM-9*2,AIM-7*2,Fuel*3", + "AGM-88*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM-84E*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "MK-82*4,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AGM-84A*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3", + "AIM-9M*2,AGM-65D*2,Mk-82*4,FLIR Pod,Fuel", + "AIM-120*4,AIM-9*2,AIM-7*2,Fuel", + "AGM-65K*2,AIM-9*2,AIM-7,FLIR Pod,Fuel*3" + ], + "IL-76MD":[ + + ], + "IL-78M":[ + + ], + "Ka-27":[ + + ], + "Ka-52":[ + "APU-6 Vikhr-M*2, Kh-25ML*2", + "APU-6 Vikhr-M*2", + "B-8*4", + "KMGU-2 (AO-2.5RT)*4", + "B-8*2, APU-6 Vikhr-M*2", + "FAB-500*2", + "UB-13*2", + "FAB-250*4", + "Kh-25ML*2, R-73*2" + ], + "KC-10A":[ + + ], + "KC-135":[ + + ], + "Mi-24V":[ + "2xFAB-500", + "10xS-13", + "2xFAB-250", + "4x9M114, 2xFuel tank", + "128xS-5", + "80xS-8 TsM", + "4x9M114, 40xS-8 TsM", + "64xS-5", + "8x9M114", + "4x9M114, 80xS-8", + "4x9M114, 128xS-5", + "2xKMGU AP", + "4xFuel tank", + "4xUPK-23", + "4x9M114, 10xS-13", + "4x9M114, 80xS-8 TsM", + "4x9M114", + "80xS-8", + "40xS-8 TsM", + "8x9M114, 40xS-8 TsM", + "8x9M114, 10xS-13", + "2xFuel tank", + "4x9M114, 4xUPK-23", + "2xUPK-23", + "8x9M114, 64xS-5", + "8x9M114, 40xS-8", + "20xS-13", + "40xS-8", + "8x9M114, 2xUPK-23" + ], + "Mi-26":[ + + ], + "Mi-28N":[ + "2xFAB-250", + "4xFuel tank", + "80xS-8", + "4xKMGU AP", + "4xUPK-23", + "16x9M114, 10xS-13", + "4xFAB-500", + "16x9M114, 2xFAB-500", + "40xS-8", + "40xS-8 TsM", + "2xKMGU AP", + "2xUPK-23", + "16x9M114, 2xUPK-23", + "2xFAB-500", + "16x9M114, 40xS-8", + "16x9M114", + "20xS-13", + "16x9M114, 2xKMGU AP", + "4xFAB-250", + "4xKMGU AT", + "16x9M114, 40xS-8 TsM", + "80xS-8 TsM", + "2xKMGU AT", + "9x9M114", + "2xFuel tank", + "10xS-13", + "2xFAB-250, 16x9M114", + "16x9M114, 2xKMGU AT" + ], + "Mi-8MT":[ + "4 x B8", + "4 x B8 + 2GUV_AP-30 (GrL 30mm)", + "2 x UPK +2 x B8", + "2 xB8 + 2GUV_YaKB (MG-12.7+7.62)+ 2GUV_AP-30 (GrL 30mm)", + "6 x FAB-100", + "2 x B8 + 2 x UPK-23-250", + "2 x UPK--23-250" + ], + "MiG-15bis":[ + "2*FAB-50", + "2*FAB-100M", + "2*300L", + "2*400L", + "2*600L", + "Fuel tank 300", + "Fuel tank 400" + ], + "MiG-23MLD":[ + "R-60M*4", + "B-8*2,R-60M*2,Fuel-800", + "UB-32*2,R-60M*2,Fuel-800", + "R-24R*2,R-60M*4,Fuel-800", + "R-24R,R-24T,R-60M*4,Fuel-800", + "R-60M*4,Fuel-800", + "FAB-500*2,R-60M*2,Fuel-800", + "R-24R*2,R-60M*4", + "FAB-250*2,R-60M*2,Fuel-800", + "RBK-250*2,R-60M*2,Fuel-800", + "RBK-500*2,R-60M*2,Fuel-800", + "R-24R,R-24T,R-60M*4" + ], + "MiG-25PD":[ + "R-40R*2,R-40T*2", + "R-40R*4", + "R-40R*2,R-60M*2" + ], + "MiG-25RBT":[ + "FAB-500x2_60x2", + "R-60M*2" + ], + "MiG-27K":[ + "FAB-250*6,R-60M*2,Fuel", + "BetAB-500ShP*2,FAB-250*2,R-60*2", + "Kh-25MR*2,R-60M*2,Fuel", + "Kh-29L*2,R-60M*2,Fuel", + "B-8*4", + "BetAB-500*2,FAB-500*2,R-60*2", + "Kh-25MPU*2,R-60M*2,Fuel", + "Kh-29T*2,R-60M*2,Fuel", + "FAB-500*2,FAB-250*2,R-60M*2,Fuel", + "Kh-25ML*2,R-60M*2,Fuel", + "KAB-500*2,R-60M*2,Fuel", + "RBK-500AO*2,RBK-250*2,R-60M*2", + "UB-32*4", + "Kh-29L*2,R-60*2,Fuel" + ], + "MiG-29A":[ + "Fuel-1150*2,Fuel-1500", + "RBK-500AO*4,R-73*2,Fuel", + "FAB-250*4,R-73*2,Fuel", + "B-8*4,R-73*2,Fuel", + "R-60M*4,R-27R*2", + "R-73*4,R-27R*2,Fuel-1500", + "R-73*6,Fuel-1500", + "R-60M*6,Fuel-1500", + "S-24*4,R-73*2,Fuel", + "FAB-500*4,R-73*2,Fuel", + "R-60M*6", + "BetAB-500*4,R-73*2,Fuel", + "R-73*6", + "R-73*2,R-60M*2,R-27R*2,Fuel-1500", + "R-60M*4,R-27R*2,Fuel-1500", + "RBK-250*4,R-73*2,Fuel", + "R-73*4,R-27R*2", + "R-73*2,R-60M*2,R-27R*2", + "S-24*2,FAB-500*2,R-73*2,Fuel" + ], + "MiG-29G":[ + "R-73*6,Fuel-1500", + "R-73*4,R-27R*2,Fuel-1500", + "R-73*2,R-60M*2,R-27R*2", + "R-60M*4,R-27R*2,Fuel-1500", + "R-73*6", + "R-60M*4,R-27R*2", + "R-73*4,R-27R*2", + "Fuel-1150*2,Fuel-1500", + "R-60M*6", + "R-60M*6,Fuel-1500", + "R-73*2,R-60M*2,R-27R*2,Fuel-1500" + ], + "MiG-29S":[ + "R-73*2,R-60M*2,R-27R*2", + "R-73*4,R-27R*2,Fuel-1500", + "R-73*6,Fuel-1500", + "R-60M*6,Fuel-1500", + "S-24*4,R-73*2,Fuel", + "FAB-500*4,R-73*2,Fuel", + "BetAB-500*4,R-73*2,Fuel", + "RBK-500AO*4,R-73*2,Fuel", + "R-73*2,R-60M*2,R-27R*2,Fuel-1500", + "R-77*2,R-73*2,Fuel-1500,Fuel-1150*2", + "B-8*4,R-73*2,Fuel", + "RBK-250*4,R-73*2,Fuel", + "R-73*6", + "Fuel-1150*2,Fuel-1500", + "R-60M*6", + "R-60M*4,R-27R*2", + "R-73*4,R-27R*2", + "R-77*4,R-73*2", + "FAB-250*4,R-73*2,Fuel", + "R-60M*4,R-27R*2,Fuel-1500", + "R-77*4,R-73*2,Fuel-1500", + "S-24*2,FAB-500*2,R-73*2,Fuel" + ], + "MiG-31":[ + "R-40T*2,R-33*4", + "R-40T,R-33*4,R-40R", + "R-40R*2,R-33*4", + "R-60M*4,R-33*4" + ], + "Mirage 2000-5":[ + "R 550*2,MICA IR*4", + "R 550*2,MICA IR*2,MICA AR*2,Fuel*3", + "R 550*2,MICA AR*4,Fuel*3", + "R 550*2,SUPER 530F*2,Fuel", + "Fuel*3", + "R 550*2,MICA IR*4,Fuel*3", + "R 550*2,MICA AR*4", + "R 550*2,SUPER 530F*2" + ], + "MQ-9 Reaper":[ + "GBU-12*4", + "GBU-38*4", + "AGM-114K*8,GBU-38*2", + "AGM-114K*12" + ], + "OH-58D":[ + "2xAGM-114, 7xHYDRA-70", + "4xAGM-114", + "M-3, 7xHYDRA-70", + "2xAGM-114, M-3", + "14xHYDRA-70", + "14xHYDRA-70 WP" + ], + "RQ-1A Predator":[ + "AGM-114K*2" + ], + "S-3B":[ + "AGM-84A*2, Mk-82*2", + "AGM-84E*2", + "AGM-65D, AGM-65K, Mk20*4", + "AGM-65D, AGM-65K, Mk82*4", + "Mk82*10", + "Mk84*2, Mk82*4", + "ZUNI Mk71*8, Mk20*4" + ], + "SH-3W":[ + + ], + "SH-60B":[ + "AGM-119" + ], + "Su-17M4":[ + "UB-32*4,R-60M*2,FAB-250*4", + "FAB-100*24,R-60M*2", + "UB-32*4,R-60M*2,Fuel*2", + "B-8*4,R-60M*2,FAB-250*4", + "Kh-29L*2,R-60M*2,Fuel*2", + "B-8*4,R-60M*2,Fuel*2", + "Kh-29T*2,R-60M*2,Fuel*2", + "BetAB-500*6,R-60M*2", + "Kh-25MR*4,R-60M*2,Fuel*2", + "S-24*4,R-60M*2,Fuel*2", + "Kh25MPU*2_Kh25ML*2_,R60M*2_Fuel*2", + "Kh58*2_Kh25MPU*2_R60M*2_Fuel*2", + "FAB-250*16,R-60M*2", + "Kh-25ML*4,R-60M*2,Fuel*2", + "RBK-500AO*4,SPPU-22*2,R-60M*2", + "S-24*4,R-60M*2,FAB-250*4", + "Fuel*4", + "FAB-500*6,R-60M*2", + "Kh-25ML*2,Kh-29L*2,R-60*2" + ], + "Su-24M":[ + "UB-13*4,FAB-500*2", + "Kh-31A*2,R-60M*2,Fuel", + "UB-13*4", + "KAB-500*4,R-60M*2", + "S-25*2,Fuel*3", + "Kh31P*2_Kh25ML*2_L-081", + "B-8*2,Fuel*3", + "FAB-1500*2,R-60M*2", + "S-24*4", + "BetAB-500*4,R-60M*2", + "Kh-25ML*4", + "Kh-25MR*4", + "FAB-100*24", + "Kh-31A*2,R-60M*2", + "UB-13*2,Fuel*3", + "B-8*2,Fuel*2", + "Kh58*2_Kh25ML*2_L-081", + "RBK-250*8", + "UB-32*4", + "Kh-29L*2,R-60M*2", + "S-24*2,Fuel*3", + "Kh25MPU*2_Kh25ML*2_L-081", + "FAB-500*4,R-60M*2", + "FAB-250*8", + "Fuel*3", + "RBK-500AO*4,R-60M*2", + "KAB-1500*2,R-60M*2,Fuel", + "UB-32*4,FAB-250*4", + "Kh-29T*2,R-60M*2", + "UB-32*2,Fuel*3", + "Kh-59M*2,R-60M*2,Fuel", + "S-25*4", + "B-8*6" + ], + "Su-24MR":[ + "SHPIL,ETHER,R-60M*2", + "Fuel*2", + "TANGAZH,ETHER,R-60M*2,Fuel*2", + "TANGAZH,ETHER,R-60M*2", + "SHPIL,ETHER,R-60M*2,Fuel*2" + ], + "Su-25":[ + "RBK-250*2,S-8KOM*80,R-60M*2,Fuel*2", + "FAB-250*4,UB-13*2,R-60M*2,SPPU-22*2", + "S-25L*6,UB-13*2,R-60M*2", + "S-25*6,SPPU-22*2,R-60M*2", + "2-25L*2, KH-25ML*2, RBK-500*2, B-8MI*2, R-60M*2", + "S-8KOM*120,R-60M*2,Fuel*2", + "FAB-250*4,S-25*2,R-60M*2,Fuel*2", + "RBK-500AO*4,S-8KOM*40,R-60M*2,Fuel*2", + "FAB-250*2,SPPU-22*2,SAB-100*4,R-60M*2", + "RBK-500AO*6,R-60M*2,Fuel*2", + "RBK-250*8,R-60M*2", + "Kh-29L*2,Kh-25ML*4,R-60M*2", + "RBK-250*4,S-8KOM*80,R-60M*2", + "FAB-250*4,UB-13*2,R-60M*2,Fuel*2", + "S-8TsM*160,R-60*2", + "Kh-25ML*4,R-60M*2,Fuel*2", + "BetAB-500ShP*8,R-60M*2", + "SAB-100*8,R-60*2", + "Kh-29L*2,Kh-25ML*4,S-25L*2,R-60M*2", + "FAB-500*6,R-60M*2,Fuel*2", + "Kh-29L*2,Kh-25ML*2,R-60M*2,Fuel*2", + "Kh-29L*2,R-60M*2,Fuel*2", + "FAB-100*32,R-60M*2", + "FAB-100*16,R-60M*2,Fuel*2", + "FAB-250*6,R-60M*2,Fuel*2", + "BetAB-500*6,R-60M*2,Fuel*2", + "S-25*6,R-60M*2,Fuel*2", + "UB-13*6,R-60M*2,Fuel*2", + "Kh-25*4,Kh-29T*2,R-60*2", + "S-25L*6,R-60*2,Fuel*2" + ], + "Su-25T":[ + "FAB-250*4,SPPU-22*2,SAB-100*2,R-60M*2", + "Kh-29L*2,Kh-25ML*4,R-73*2,Mercury LLTV Pod,MPS-410", + "KAB-500Kr*2,Kh-25ML*2,R-73*2,MPS-410,Fuel*2", + "RBK-500AO*4,UB-32*2,R-60M*2,Fuel*2", + "BetAB-500ShP*8,R-60M*2", + "UB-13*6,R-60M*2,Fuel*2", + "Kh-29T*2,R-73*2,Fuel*2,MPS-410", + "Kh58*2_Kh25ML*4_R73*2_L-081_MPS-410", + "FAB-250*4,UB-13*2,R-60M*2,Fuel*2", + "KH-29T*2, VIKHR*2, ECM", + "Kh-29T*2,Kh-25ML*4,R-73*2,MPS-410", + "FAB-250*4,UB-13*2,R-60M*2,SPPU-22*2", + "FAB-500*6,R-60M*2,Fuel*2", + "Fuel*4", + "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410", + "S-8KOM*120,R-60M*2,Fuel*2", + "KMGU-2 (PTAB-2.5KO)*8,R-60M*2", + "FAB-250*6,R-60M*2,Fuel*2", + "Kh-29L*2,R-73*2,Fuel*2,Mercury LLTV Pod,MPS-410", + "FAB-100*32,R-60M*2", + "RBK-250*8,R-60M*2", + "S-25L*6,UB-13*2,R-60M*2", + "FAB-250*4,S-25*2,R-60M*2,Fuel*2", + "S-25*2,SPPU-22*4,R-60M*2,R-73*2", + "KMGU-2 (AO-2.5RT)*8,R-60M*2", + "APU-8 Vikhr-M*2,S-25L*2,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410", + "S-25*6,R-60M*2,Fuel*2", + "RBK-500AO*6,R-60M*2,Fuel*2", + "Kh58*2_Kh25MPU*2_Kh25ML*2_R73*2_L-081_MPS-410", + "RBK-250*4,UB-32*4,R-60M*2", + "Kh25MPU*4_R73*2_Fuel*2_L-081_MPS-410", + "BetAB-500*6,R-60M*2,Fuel*2", + "RBK-250*2,UB-32*4,R-60M*2,Fuel*2", + "FAB-100*16,R-60M*2,Fuel*2", + "Kh-29L*2,Kh-25ML*4,R-73*2,ECM" + ], + "Su-25TM":[ + "BetAB-500ShP*6,R-73*2,ECM", + "APU-8 Vikhr-M*2,R-60M*2,R-73*2,SPPU-22*2,Mercury LLTV Pod", + "Kh-29T*2,Kh-25ML*2,R-73*2,Fuel*2,ECM", + "Kh-58*2_Kh-25MPU*4_R-73*2_L-081_MPS410", + "S-25L*6,UB-13*2,R-60M*2", + "Kh-35*2_R-73*2_Fuel*2_MPS410_Kopyo-25", + "Kh-31A*2_R-73*2_Fuel*2_MPS410_Kopyo-25", + "Fuel*4", + "BetAB-500*6,R-60M*2,Fuel*2", + "KAB-500Kr*2,Kh-25ML*2,R-73*2,Fuel*2,ECM", + "Kh-29L*2,R-73*2,Fuel*2,Mercury LLTV Pod,ECM", + "UB-13*6,R-60M*2,Fuel*2", + "S-25*2,SPPU-22*4,R-60M*2,R-73*2", + "Kh-31P*2_Kh-25ML*4_R-73*2_L-081_MPS410", + "UB-32*6,R-60M*2,Fuel*2", + "FAB-100*16,R-60M*2,Fuel*2", + "FAB-250*6,R-60M*2,Fuel*2", + "FAB-500*6,R-60M*2,Fuel*2", + "Kh-25MPU*4_R-73*2_Fuel*2_L-081_MPS410", + "S-25*6,R-60M*2,Fuel*2", + "RBK-500AO*4,UB-32*2,R-60M*2,Fuel*2", + "FAB-250*4,UB-13*2,R-60M*2,SPPU-22*2", + "Kh-31P*2_Kh-25MPU*4_R-73*2_L-081_MPS410", + "RBK-500AO*6,R-60M*2,Fuel*2", + "FAB-100*32,R-60M*2", + "RBK-250*2,UB-32*4,R-60M*2,Fuel*2", + "RBK-250*8,R-60M*2", + "APU-8 Vikhr-M*2,R-73*2,SPPU-22*2,Mercury LLTV Pod,ECM", + "RBK-250*4,UB-32*4,R-60M*2", + "FAB-250*4,SPPU-22*2,SAB-100*2,R-60M*2", + "FAB-250*4,S-25*2,R-60M*2,Fuel*2", + "FAB-250*4,UB-13*2,R-60M*2,Fuel*2" + ], + "Su-27":[ + "R-73*4,R-27ER*4,R-27ET*2", + "KMGU-2 (AO-2.5RT)*5,R-73*2,ECM", + "BetAB-500ShP*6,R-73*2,ECM", + "KMGU-2 (PTAB-2.5KO)*5,R-73*2,ECM", + "R-73*2,R-27ER*6,ECM", + "R-73*6", + "S-13*10,RBK-500AO*2,FAB-500*2,R-73*2,ECM", + "R-73*4,R-27ER*6", + "R-73*2,R-27ER*4,R-27ET*2,ECM", + "R-73*4,ECM", + "ECM", + "FAB-500*6,R-73*2,ECM", + "S-25*2,FAB-500*4,R-73*4", + "S-25*4, FAB-500*4, R-73*2, ECM", + "CAS S-8KOM Rockets + RBK-500 PTAB1", + "CAS S-8OFP Rockets + FAB-500 Bombs", + "CAS S-8OFP Rockets", + "CAS S-8OFP Rockets + FAB-100 Bombs", + "CAS S-8KOM Rockets + FAB-100 Bombs", + "CAS S-13 Rockets", + "CAS S-8KOM Rockets + FAB-250 Bombs", + "CAS S-8KOM Rockets + RBK-250 PTAB2.5", + "CAS S-8KOM Rockets", + "CAS S-8KOM Rockets + FAB-500 Bombs", + "CAS S-8KOM Rockets + RBK-500 PTAB10", + "CAS S-8KOM Rockets + KMGU PTAB", + " CAS S-25 Rockets", + "CAS S-25 Rockets + FAB-500 Bombs" + ], + "Su-30":[ + "R-73*2,R-77*6,ECM", + "R-73*2,R-27T*2,R-27R*4", + "RBK-500AO*6,R-73*2,ECM", + "Kh-31P*2,Kh-31A*2,R-73*2,R-77*2,ECM", + "R-73*4,R-27T*2,R-27R*4", + "R-73*2,R-77*2,Kh-35*2,ECM", + "Kh-35*2,Kh-31P*2,R-73*2,R-77*2,ECM", + "FAB-250*4,B-8*2,R-73*2,ECM", + "ECM", + "KAB-1500*2,R-73*2,R-77*2,ECM", + "RBK-250*6,R-73*2,ECM", + "R-73*4,R-77*6", + "FAB-250*4,S-25*2,R-73*2,ECM", + "R-73*2,R-27R*2,R-27ER*4,ECM", + "R-73*2,R-27T*2,R-27ER*2,R-77*2,ECM", + "FAB-1500*2,R-73*2,R-77*2,ECM", + "R-73*4,R-27T*2,R-27ER*2,R-77*2", + "Kh-59M*2,R-73*2,R-77*2,ECM", + "FAB-500*6,R-73*2,ECM", + "R-73*4,R-27R*2,R-27ER*4", + "Kh-29L*4,R-73*2,R-77*2,ECM", + "BetAB-500*6,R-73*2,ECM", + "R-73*4", + "FAB-250*4,UB-13*2,R-73*2,ECM", + "R-73*2,R-77*4,R-27ER*2,ECM", + "KAB-500*4,R-73*2,R-77*2,ECM", + "FAB-250*6,R-73*2,ECM", + "R-73*4,R-77*4,R-27ER*2", + "Kh-29T*4,R-73*2,R-77*2,ECM", + "Kh-31P*2,Kh-31A*2,R-73*2,R-77*2,ECM", + "Kh-31P*4,R-73*2,R-77*2,ECM" + ], + "Su-33":[ + "RBK-250*6,R-73*2,R-27R*2,ECM", + "R-73*4", + "R-73*4,R-27R*2,R-27ER*6", + "R-73*2,R-27ET*2,R-27ER*6,ECM", + "R-73*4,R-27ET*2,R-27ER*6", + "FAB-250*6,R-73*2,R-27R*2,ECM", + "R-73*2,R-27R*2,R-27ER*6,ECM", + "ECM", + "BetAB-500*6,R-73*2,R-27R*2,ECM", + "RBK-500AO*6,R-73*2,R-27R*2,ECM", + "UB-13*4,FAB-250*4,R-73*2,ECM", + "S-25*4,FAB-250*4,R-73*2,ECM", + "FAB-500*6,R-73*2,R-27R*2,ECM", + "B-8*4,FAB-250*4,R-73*2,ECM", + "S-25*4,FAB-500*4,R-73*4", + "CAS S-8KOM rockets + FAB500", + "CAS S-8OFP rockets + FAB500", + "CAS S-13 Rockets + FAB500", + "CAS S-13 Rockets + FAB100", + "CAS S-8KOM rockets + FAB250", + "CAS S-25 Rockets + FAB500", + "CAS S-8KOM rockets + RBK500 PTAB10", + "CAS S-8KOM rockets + RBK500 PTAB1" + ], + "Su-34":[ + "UB-13*4,FAB-250*4,R-73*2,ECM", + "FAB-100*28,R-73*2,ECM", + "BetAB-500*8,R-73*2,ECM", + "Kh-29L*4,R-73*2,R-77*2,ECM", + "KAB-500*4,R-73*2,R-77*2,ECM", + "RBK-250 PTAB-2.5M*8,R-73*2,ECM", + "FAB-250*8,R-73*2,ECM", + "ECM", + "Kh-29T*4,R-73*2,R-77*2,ECM", + "RBK-500 PTAB-10-5*8,R-73*2,ECM", + "FAB-1500*3,R-73*2,R-77*2,ECM", + "Kh-59M*2,R-73*2,R-77*2,ECM", + "B-8*6,R-73*2,R-27R*2,ECM", + "FAB-500*8,R-73*2,ECM", + "KAB-1500*2,R-73*2,R-77*2,ECM", + "Kh-29T*4,R-73*2,R-27R*2,ECM", + "Kh-31A*4,Kh-31P*2,R-73*2,R-27R*2,ECM", + "Kh-31A*6,R-73*2,R-27R*2,ECM", + "Kh-31P*4,R-73*2,R-27R*2,ECM", + "Kh-29L*4,R-73*2,R-27R*2,ECM" + ], + "Tornado GR4":[ + "AIM-9M*2, Fuel*2, ECM", + "ALARM*4, Fuel*2, ECM", + "GBU-16*2, AIM-9M*2, Fuel*2, ECM", + "BL755*4, AIM-9M*2, Fuel*2, ECM", + "Sea Eagle*2, AIM-9M*2, Fuel*2, ECM" + ], + "Tornado IDS":[ + "Kormoran*2,AIM-9*2,Fuel*2", + "GBU-16*2,AIM-9*2,Fuel*2", + "Fuel*2", + "AGM-88*4,AIM-9*2,ECM", + "AGM-88*2,AIM-9*2,Fuel*2,ECM", + "Kormoran*4,AIM-9*2", + "Kormoran*2,AIM-9*2,AGM-88*2", + "Mk-82*4,AIM-9*2,Fuel*2" + ], + "Tu-142":[ + "Kh-35*6" + ], + "Tu-160":[ + "Kh-65*12" + ], + "Tu-22M3":[ + "Kh-22N", + "Kh-22N*2", + "FAB-250*69", + "FAB-500*33", + "FAB-500*33, FAB-250*36", + "FAB-250*33" + ], + "Tu-95MS":[ + "Kh-65*6" + ], + "UH-1H":[ + "M134 Minigun*2, XM158*2" + ], + "UH-60A":[ + + ] + } \ No newline at end of file diff --git a/scripts/server.py b/scripts/server.py deleted file mode 100644 index b4da36ea..00000000 --- a/scripts/server.py +++ /dev/null @@ -1,8 +0,0 @@ -try: - # Python 2 - from SimpleHTTPServer import test, SimpleHTTPRequestHandler -except ImportError: - # Python 3 - from http.server import test, SimpleHTTPRequestHandler - -test(SimpleHTTPRequestHandler) \ No newline at end of file diff --git a/scripts/server.spec b/scripts/server.spec deleted file mode 100644 index 886a8932..00000000 --- a/scripts/server.spec +++ /dev/null @@ -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, -) diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 4e19d032..781d4f5f 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -18,23 +18,6 @@ x64 - - - - - - - - - - - - - - - - - {2b255368-39a0-431a-a6de-cc739ac70dc1} @@ -49,6 +32,35 @@ {b85009ce-4a5c-4a5a-b85d-001b3a2651b2} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 16.0 Win32Proj diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index fe011f46..d0cc64a4 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -9,45 +9,81 @@ + + Header Files + + + Header Files + Header Files - + + Header Files + + + Header Files + + Header Files Header Files + + Header Files + + + Header Files + Header Files - + Header Files - + Header Files + + Source Files + + + Source Files + Source Files Source Files - + + Source Files + + + Source Files + + Source Files Source Files + + Source Files + + + Source Files + Source Files - + Source Files - + Source Files diff --git a/src/core/include/Commands.h b/src/core/include/Commands.h index ed01f63b..23fb1eff 100644 --- a/src/core/include/Commands.h +++ b/src/core/include/Commands.h @@ -8,7 +8,7 @@ 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 }; }; /* Base command class */ @@ -25,10 +25,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 +51,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 +69,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 +89,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,10 +114,10 @@ private: }; /* Clone unit command */ -class CloneCommand : public Command +class Clone : public Command { public: - CloneCommand(int ID) : + Clone(int ID) : ID(ID) { priority = CommandPriority::LOW; @@ -130,19 +130,73 @@ private: }; /* 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; +}; \ No newline at end of file diff --git a/src/core/include/Unit.h b/src/core/include/Unit.h index bbffb2a2..637a0576 100644 --- a/src/core/include/Unit.h +++ b/src/core/include/Unit.h @@ -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, RTB, REFUEL, AWACS, EWR, TANKER, RUN_AWAY }; +}; class Unit { @@ -17,19 +18,19 @@ public: void updateMissionData(json::value json); json::value json(); + virtual void setState(int newState) { state = newState; }; + void resetTask(); + void setPath(list 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 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); int getID() { return ID; } wstring getName() { return name; } @@ -46,12 +47,26 @@ 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; + int state = State::IDLE; + bool hasTask = false; bool AI = false; bool alive = true; wstring name = L"undefined"; @@ -67,13 +82,12 @@ protected: 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; + bool isLeader = false; + bool isWingman = false; + Offset formationOffset = Offset(NULL); wstring formation = L""; + Unit* leader = nullptr; vector wingmen; double targetSpeed = 0; double targetAltitude = 0; @@ -85,112 +99,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"; }; -}; diff --git a/src/core/include/aircraft.h b/src/core/include/aircraft.h new file mode 100644 index 00000000..d9f6c4a2 --- /dev/null +++ b/src/core/include/aircraft.h @@ -0,0 +1,19 @@ +#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; }; + +protected: + double targetSpeed = 150; + double targetAltitude = 5000; +}; \ No newline at end of file diff --git a/src/core/include/airunit.h b/src/core/include/airunit.h new file mode 100644 index 00000000..968a7ec9 --- /dev/null +++ b/src/core/include/airunit.h @@ -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(); +}; \ No newline at end of file diff --git a/src/core/include/groundunit.h b/src/core/include/groundunit.h new file mode 100644 index 00000000..6edb1a3b --- /dev/null +++ b/src/core/include/groundunit.h @@ -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; +}; \ No newline at end of file diff --git a/src/core/include/helicopter.h b/src/core/include/helicopter.h new file mode 100644 index 00000000..9573f9b4 --- /dev/null +++ b/src/core/include/helicopter.h @@ -0,0 +1,19 @@ +#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; }; + +protected: + double targetSpeed = 50; + double targetAltitude = 1000; +}; \ No newline at end of file diff --git a/src/core/include/navyunit.h b/src/core/include/navyunit.h new file mode 100644 index 00000000..d70d86d2 --- /dev/null +++ b/src/core/include/navyunit.h @@ -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; +}; \ No newline at end of file diff --git a/src/core/include/weapon.h b/src/core/include/weapon.h new file mode 100644 index 00000000..a1097bcb --- /dev/null +++ b/src/core/include/weapon.h @@ -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"; }; +}; \ No newline at end of file diff --git a/src/core/src/Commands.cpp b/src/core/src/Commands.cpp index a557730b..7f697dc2 100644 --- a/src/core/src/Commands.cpp +++ b/src/core/src/Commands.cpp @@ -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,7 +65,7 @@ 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); @@ -74,14 +74,50 @@ wstring CloneCommand::getString(lua_State* L) 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(); } \ No newline at end of file diff --git a/src/core/src/Scheduler.cpp b/src/core/src/Scheduler.cpp index 12a7903a..f6909d01 100644 --- a/src/core/src/Scheduler.cpp +++ b/src/core/src/Scheduler.cpp @@ -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(new SmokeCommand(color, loc)); + command = dynamic_cast(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(new SpawnGroundUnitCommand(coalition, type, loc)); + command = dynamic_cast(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(new SpawnAircraftCommand(coalition, type, loc, payloadName, airbaseName)); + command = dynamic_cast(new SpawnAircraft(coalition, type, loc, payloadName, airbaseName)); } else if (key.compare(L"attackUnit") == 0) { @@ -129,72 +129,87 @@ 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(new CloneCommand(ID)); + command = dynamic_cast(new Clone(ID)); 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 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 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) diff --git a/src/core/src/Unit.cpp b/src/core/src/Unit.cpp index 927f594f..bf6d7d2e 100644 --- a/src/core/src/Unit.cpp +++ b/src/core/src/Unit.cpp @@ -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 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 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(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(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,22 @@ 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"] = targetSpeed; + json[L"targetAltitude"] = targetAltitude; + json[L"hasTask"] = hasTask; 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 +147,78 @@ json::value Unit::json() return json; } -/* Air unit */ -AirUnit::AirUnit(json::value json, int ID) : Unit(json, ID) +void Unit::setPath(list 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(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(); - } -} - -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(); -} - -/* 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 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)); -}; + formationOffset = newFormationOffset; + resetTask(); +} \ No newline at end of file diff --git a/src/core/src/aircraft.cpp b/src/core/src/aircraft.cpp new file mode 100644 index 00000000..492cf4a9 --- /dev/null +++ b/src/core/src/aircraft.cpp @@ -0,0 +1,58 @@ +#include "aircraft.h" +#include "utils.h" +#include "logger.h" +#include "commands.h" +#include "scheduler.h" +#include "defines.h" +#include "unitsFactory.h" + +#include +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 */ +} \ No newline at end of file diff --git a/src/core/src/airunit.cpp b/src/core/src/airunit.cpp new file mode 100644 index 00000000..62d10cd1 --- /dev/null +++ b/src/core/src/airunit.cpp @@ -0,0 +1,331 @@ +#include "airunit.h" +#include "utils.h" +#include "logger.h" +#include "commands.h" +#include "scheduler.h" +#include "defines.h" +#include "unitsFactory.h" + +#include +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; + } + 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; + } + 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(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::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(new SetTask(ID, taskSS.str())); + scheduler->appendCommand(command); + hasTask = true; + } + } + break; + } + default: + break; + } +} + +void AirUnit::setTargetSpeed(double newTargetSpeed) { + targetSpeed = newTargetSpeed; + goToDestination(); +} + +void AirUnit::setTargetAltitude(double newTargetAltitude) { + targetAltitude = newTargetAltitude; + goToDestination(); +} \ No newline at end of file diff --git a/src/core/src/core.cpp b/src/core/src/core.cpp index 18a05792..24849509 100644 --- a/src/core/src/core.cpp +++ b/src/core/src/core.cpp @@ -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. -} \ No newline at end of file diff --git a/src/core/src/groundunit.cpp b/src/core/src/groundunit.cpp new file mode 100644 index 00000000..463843d0 --- /dev/null +++ b/src/core/src/groundunit.cpp @@ -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 +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(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) + { + + } +} \ No newline at end of file diff --git a/src/core/src/helicopter.cpp b/src/core/src/helicopter.cpp new file mode 100644 index 00000000..9d64e921 --- /dev/null +++ b/src/core/src/helicopter.cpp @@ -0,0 +1,58 @@ +#include "helicopter.h" +#include "utils.h" +#include "logger.h" +#include "commands.h" +#include "scheduler.h" +#include "defines.h" +#include "unitsFactory.h" + +#include +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 */ +} diff --git a/src/core/src/navyunit.cpp b/src/core/src/navyunit.cpp new file mode 100644 index 00000000..e9491d43 --- /dev/null +++ b/src/core/src/navyunit.cpp @@ -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 +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) + { + + } +} \ No newline at end of file diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index 57249e71..f70ff8c5 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -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 (...) { diff --git a/src/core/src/unitsFactory.cpp b/src/core/src/unitsFactory.cpp index 15736a60..554aeb1b 100644 --- a/src/core/src/unitsFactory.cpp +++ b/src/core/src/unitsFactory.cpp @@ -2,9 +2,11 @@ #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" UnitsFactory::UnitsFactory(lua_State* L) { diff --git a/src/core/src/weapon.cpp b/src/core/src/weapon.cpp new file mode 100644 index 00000000..4025bb82 --- /dev/null +++ b/src/core/src/weapon.cpp @@ -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 +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)); +}; \ No newline at end of file diff --git a/src/utils/include/Utils.h b/src/utils/include/Utils.h index ea7c34a1..852c033f 100644 --- a/src/utils/include/Utils.h +++ b/src/utils/include/Utils.h @@ -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); diff --git a/src/utils/src/Utils.cpp b/src/utils/src/Utils.cpp index 53d1994f..d471028b 100644 --- a/src/utils/src/Utils.cpp +++ b/src/utils/src/Utils.cpp @@ -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); } From 3cf133a10e41d2256a5154764ab51aca5ff61986 Mon Sep 17 00:00:00 2001 From: dpassoni Date: Thu, 2 Feb 2023 20:31:12 +0100 Subject: [PATCH 2/3] Added measure line --- client/public/stylesheets/style.css | 16 +++++++ client/src/map/map.ts | 67 ++++++++++++++++++++++++++++- client/src/units/unit.ts | 9 ++-- 3 files changed, 86 insertions(+), 6 deletions(-) diff --git a/client/public/stylesheets/style.css b/client/public/stylesheets/style.css index 1a05b38e..424efa47 100644 --- a/client/public/stylesheets/style.css +++ b/client/public/stylesheets/style.css @@ -168,3 +168,19 @@ html { 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; +} \ No newline at end of file diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 536e5470..1cf85697 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -1,6 +1,7 @@ import * as L from "leaflet" 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"; @@ -26,6 +27,9 @@ export class Map extends L.Map { #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 */ @@ -40,7 +44,12 @@ export class Map extends L.Map { 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}); + 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)); @@ -50,6 +59,7 @@ export class Map extends L.Map { 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) { @@ -235,6 +245,22 @@ export class Map extends L.Map { selectedUnitPosition = new L.LatLng(selectedUnits[0].latitude, selectedUnits[0].longitude); } getMouseInfoPanel().update(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 */ @@ -338,4 +364,43 @@ export class Map extends L.Map { 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) + } } diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 19a94a2a..d27d847d 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -218,8 +218,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) { @@ -237,8 +237,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); } } } @@ -251,7 +251,6 @@ export class Unit { this.#pathPolyline.setLatLngs([]); } - #drawTargets() { for (let typeIndex in this.targets) From 30a9d4e7302fc7b728a57018cdd8467fb822af91 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Wed, 8 Feb 2023 18:26:45 +0100 Subject: [PATCH 3/3] Added speed and altitude controls Fixed Clone Added "Land here" command Added ROE and reaction to threat buttons --- client/TODO.txt | 3 +- client/public/stylesheets/slider.css | 47 +++++ client/public/stylesheets/style.css | 1 + .../public/stylesheets/unitcontrolpanel.css | 25 ++- client/src/controls/slider.ts | 83 ++++++++ client/src/dcs/dcs.ts | 84 +++++++- client/src/index.ts | 16 ++ client/src/map/map.ts | 8 + client/src/panels/unitcontrolpanel.ts | 182 +++++++++++++++++- client/src/units/unit.ts | 33 +++- client/src/units/unitsmanager.ts | 79 ++++++-- client/views/unitcontrolpanel.ejs | 39 ++-- installer/DCSOlympus.iss | 2 +- scripts/OlympusCommand.lua | 38 ++-- src/core/include/Commands.h | 70 ++++++- src/core/include/Unit.h | 59 +++--- src/core/include/aircraft.h | 6 +- src/core/include/airunit.h | 4 +- src/core/include/helicopter.h | 6 +- src/core/include/unitsFactory.h | 1 + src/core/src/Commands.cpp | 14 +- src/core/src/Scheduler.cpp | 33 +++- src/core/src/Unit.cpp | 50 ++++- src/core/src/aircraft.cpp | 12 +- src/core/src/airunit.cpp | 33 +++- src/core/src/helicopter.cpp | 13 +- src/core/src/unitsFactory.cpp | 13 ++ 27 files changed, 852 insertions(+), 102 deletions(-) create mode 100644 client/public/stylesheets/slider.css create mode 100644 client/src/controls/slider.ts diff --git a/client/TODO.txt b/client/TODO.txt index cb925eba..43ba41c2 100644 --- a/client/TODO.txt +++ b/client/TODO.txt @@ -5,4 +5,5 @@ explosion wrong name for ground units improve map zIndex fuel is wrong (either 0 or 1, its is casting it to int somewhere) -weapons should not be selectable \ No newline at end of file +weapons should not be selectable +human symbol if user \ No newline at end of file diff --git a/client/public/stylesheets/slider.css b/client/public/stylesheets/slider.css new file mode 100644 index 00000000..cddd49ad --- /dev/null +++ b/client/public/stylesheets/slider.css @@ -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; +} \ No newline at end of file diff --git a/client/public/stylesheets/style.css b/client/public/stylesheets/style.css index 424efa47..b68fd52a 100644 --- a/client/public/stylesheets/style.css +++ b/client/public/stylesheets/style.css @@ -1,4 +1,5 @@ @import url("button.css"); +@import url("slider.css"); @import url("dropdown.css"); @import url("selectionwheel.css"); diff --git a/client/public/stylesheets/unitcontrolpanel.css b/client/public/stylesheets/unitcontrolpanel.css index 9bcd3ba9..f01d791a 100644 --- a/client/public/stylesheets/unitcontrolpanel.css +++ b/client/public/stylesheets/unitcontrolpanel.css @@ -52,7 +52,7 @@ height: 100%; } -#threat-reaction-buttons-container { +#reaction-to-threat-buttons-container { display: flex; flex-direction: row; flex-wrap: wrap; @@ -106,4 +106,25 @@ color: white; font-size: 13px; width: 100%; -} \ No newline at end of file +} + +.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; +} diff --git a/client/src/controls/slider.ts b/client/src/controls/slider.ts new file mode 100644 index 00000000..38b2a49e --- /dev/null +++ b/client/src/controls/slider.ts @@ -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 = 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 = 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() + } +} \ No newline at end of file diff --git a/client/src/dcs/dcs.ts b/client/src/dcs/dcs.ts index adf8bf17..c9ae53f7 100644 --- a/client/src/dcs/dcs.ts +++ b/client/src/dcs/dcs.ts @@ -99,7 +99,7 @@ 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"); @@ -109,12 +109,28 @@ export function cloneUnit(ID: number) { } }; - 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); @@ -131,6 +147,22 @@ 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); @@ -147,6 +179,22 @@ export function changeAltitude(ID: number, altitudeChange: string) { 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); @@ -160,5 +208,37 @@ export function createFormation(ID: number, isLeader: boolean, wingmenIDs: numbe 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)); } \ No newline at end of file diff --git a/client/src/index.ts b/client/src/index.ts index 10fbba6f..586cf390 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -10,6 +10,7 @@ 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; @@ -36,6 +37,9 @@ var aiVisibilityButton: Button; var weaponVisibilityButton: Button; var deadVisibilityButton: Button; +var altitudeSlider: Slider; +var airspeedSlider: Slider; + var connected: boolean; var activeCoalition: string; @@ -59,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"], () => { }); @@ -184,4 +192,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; \ No newline at end of file diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 1cf85697..5dbc9089 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -154,6 +154,14 @@ export class Map extends L.Map { getSelectionScroll().hide(); } + getMousePosition() { + return this.#lastMousePosition; + } + + getMouseCoordinates() { + return this.containerPointToLatLng(this.#lastMousePosition); + } + /* Event handlers */ #onClick(e: any) { if (!this.#preventLeftClick) { diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index e4851602..9a143ffd 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -1,7 +1,7 @@ import { imageOverlay } from "leaflet"; -import { getUnitsManager } from ".."; +import { getUnitControlSliders, getUnitsManager } from ".."; import { ConvertDDToDMS, rad2deg } from "../other/utils"; -import { AirUnit, Unit } from "../units/unit"; +import { Aircraft, AirUnit, GroundUnit, Helicopter, NavyUnit, Unit } from "../units/unit"; export class UnitControlPanel { #element: HTMLElement @@ -21,6 +21,25 @@ export class UnitControlPanel { var undoButton = formationCreationContainer.querySelector("#undo-formation"); undoButton?.addEventListener("click", () => getUnitsManager().selectedUnitsUndoFormation()); } + var ROEButtonsContainer = (this.#element.querySelector("#roe-buttons-container")); + if (ROEButtonsContainer != null) + { + (ROEButtonsContainer.querySelector("#free"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Free")); + (ROEButtonsContainer.querySelector("#designated-free"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Designated free")); + (ROEButtonsContainer.querySelector("#designated"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Designated")); + (ROEButtonsContainer.querySelector("#return"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Return")); + (ROEButtonsContainer.querySelector("#hold"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetROE("Hold")); + } + + var reactionToThreatButtonsContainer = (this.#element.querySelector("#reaction-to-threat-buttons-container")); + if (reactionToThreatButtonsContainer != null) + { + (reactionToThreatButtonsContainer.querySelector("#none"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("None")); + (reactionToThreatButtonsContainer.querySelector("#passive"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Passive")); + (reactionToThreatButtonsContainer.querySelector("#evade"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Evade")); + (reactionToThreatButtonsContainer.querySelector("#escape"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Escape")); + (reactionToThreatButtonsContainer.querySelector("#abort"))?.addEventListener("click", () => getUnitsManager().selectedUnitsSetReactionToThreat("Abort")); + } this.hide(); } } @@ -41,8 +60,83 @@ export class UnitControlPanel { if (selectedUnitsContainer != null && formationCreationContainer != null) { this.#addUnitsButtons(units, selectedUnitsContainer); + this.#showFlightControlSliders(units); this.#showFormationButtons(units, formationCreationContainer); } + + var ROEButtonsContainer = (this.#element.querySelector("#roe-buttons-container")); + if (ROEButtonsContainer != null) + { + (ROEButtonsContainer.querySelector("#free"))?.classList.toggle("white", this.#getROE(units) === "Free"); + (ROEButtonsContainer.querySelector("#designated-free"))?.classList.toggle("white", this.#getROE(units) === "Designated free"); + (ROEButtonsContainer.querySelector("#designated"))?.classList.toggle("white", this.#getROE(units) === "Designated"); + (ROEButtonsContainer.querySelector("#return"))?.classList.toggle("white", this.#getROE(units) === "Return"); + (ROEButtonsContainer.querySelector("#hold"))?.classList.toggle("white", this.#getROE(units) === "Hold"); + } + + var reactionToThreatButtonsContainer = (this.#element.querySelector("#reaction-to-threat-buttons-container")); + if (reactionToThreatButtonsContainer != null) + { + (reactionToThreatButtonsContainer.querySelector("#none"))?.classList.toggle("white", this.#getReactionToThreat(units) === "None"); + (reactionToThreatButtonsContainer.querySelector("#passive"))?.classList.toggle("white", this.#getReactionToThreat(units) === "Passive"); + (reactionToThreatButtonsContainer.querySelector("#evade"))?.classList.toggle("white", this.#getReactionToThreat(units) === "Evade"); + (reactionToThreatButtonsContainer.querySelector("#escape"))?.classList.toggle("white", this.#getReactionToThreat(units) === "Escape"); + (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); } } @@ -157,6 +251,38 @@ export class UnitControlPanel { 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; @@ -182,4 +308,56 @@ export class UnitControlPanel { 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; + } } \ No newline at end of file diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index d27d847d..723aac23 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -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, createFormation as setLeader } 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', @@ -36,6 +36,10 @@ export class Unit { leaderID: number = 0; wingmen: Unit[] = []; wingmenIDs: number[] = []; + targetSpeed: number = 0; + targetAltitude: number = 0; + ROE: string = ""; + reactionToThreat: string = ""; #selectable: boolean; #selected: boolean = false; @@ -296,7 +300,6 @@ export class Unit { } } - attackUnit(targetID: number) { /* Call DCS attackUnit function */ if (this.ID != targetID) { @@ -307,7 +310,11 @@ export class Unit { } } - + landAt(latlng: LatLng) + { + landAt(this.ID, latlng); + } + changeSpeed(speedChange: string) { changeSpeed(this.ID, speedChange); @@ -318,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) { diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index b85aeaa5..1149661b 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -15,6 +15,17 @@ export class UnitsManager { 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; } @@ -167,7 +178,11 @@ export class UnitsManager { selectedUnitsLandAt(latlng: LatLng) { - + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].landAt(latlng); + } } selectedUnitsChangeSpeed(speedChange: string) @@ -177,6 +192,8 @@ export class UnitsManager { { selectedUnits[idx].changeSpeed(speedChange); } + + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail } selectedUnitsChangeAltitude(altitudeChange: string) @@ -186,8 +203,51 @@ export class UnitsManager { { selectedUnits[idx].changeAltitude(altitudeChange); } + + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail } + selectedUnitsSetSpeed(speed: number) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].setSpeed(speed); + } + } + + selectedUnitsSetAltitude(altitude: number) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].setAltitude(altitude); + } + } + + selectedUnitsSetROE(ROE: string) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].setROE(ROE); + } + + 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(); @@ -198,7 +258,7 @@ export class UnitsManager { for (let idx in this.#copiedUnits) { var unit = this.#copiedUnits[idx]; - cloneUnit(unit.ID); + cloneUnit(unit.ID, getMap().getMouseCoordinates()); } } @@ -259,7 +319,7 @@ export class UnitsManager { console.log("At least 2 units must be selected to create a formation."); } } - setTimeout(() => this.#updateUnitControlPanel(), 1000); // TODO find better method, may fail + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail } selectedUnitsUndoFormation(ID: number | null = null) @@ -268,17 +328,6 @@ export class UnitsManager { { leader.setLeader(false); } - setTimeout(() => this.#updateUnitControlPanel(), 1000); // TODO find better method, may fail - } - - #updateUnitControlPanel() { - /* Update the unit control panel */ - if (this.getSelectedUnits().length > 0) { - getUnitControlPanel().show(); - getUnitControlPanel().update(this.getSelectedLeaders().concat(this.getSelectedSingletons())); - } - else { - getUnitControlPanel().hide(); - } + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail } } \ No newline at end of file diff --git a/client/views/unitcontrolpanel.ejs b/client/views/unitcontrolpanel.ejs index ab0f7820..fba42229 100644 --- a/client/views/unitcontrolpanel.ejs +++ b/client/views/unitcontrolpanel.ejs @@ -18,7 +18,20 @@
- + \ No newline at end of file diff --git a/installer/DCSOlympus.iss b/installer/DCSOlympus.iss index f123559f..61c67383 100644 --- a/installer/DCSOlympus.iss +++ b/installer/DCSOlympus.iss @@ -2,7 +2,7 @@ [Setup] AppName=DCS Olympus -AppVerName=DCS Olympus Alpha v0.0.3 +AppVerName=DCS Olympus Alpha v0.0.5 DefaultDirName={usersavedgames}\DCS.openbeta DefaultGroupName=DCSOlympus OutputBaseFilename=DCSOlympus diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index 790bd1a5..0ea22476 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -82,12 +82,20 @@ function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions) 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 if taskOptions then @@ -273,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 @@ -282,22 +290,30 @@ 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.delete(ID, lat, lng) + Olympus.notify("Olympus.delete " .. ID, 2) + local unit = Olympus.getUnitByID(ID) + 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) @@ -337,7 +353,6 @@ function Olympus.setOption(ID, optionID, optionValue) end end - function Olympus.serializeTable(val, name, skipnewlines, depth) skipnewlines = skipnewlines or false depth = depth or 0 @@ -370,5 +385,4 @@ function Olympus.serializeTable(val, name, skipnewlines, depth) return tmp end - Olympus.notify("OlympusCommand script loaded successfully", 2) \ No newline at end of file diff --git a/src/core/include/Commands.h b/src/core/include/Commands.h index 23fb1eff..95ccefbc 100644 --- a/src/core/include/Commands.h +++ b/src/core/include/Commands.h @@ -11,6 +11,54 @@ namespace CommandType { 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 { @@ -117,14 +165,32 @@ private: class Clone : public Command { public: - Clone(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; }; diff --git a/src/core/include/Unit.h b/src/core/include/Unit.h index 637a0576..3b99897b 100644 --- a/src/core/include/Unit.h +++ b/src/core/include/Unit.h @@ -5,7 +5,7 @@ #include "luatools.h" namespace State { - enum States { IDLE, REACH_DESTINATION, ATTACK, WINGMAN, FOLLOW, RTB, REFUEL, AWACS, EWR, TANKER, RUN_AWAY }; + enum States { IDLE, REACH_DESTINATION, ATTACK, WINGMAN, FOLLOW, LAND, REFUEL, AWACS, EWR, TANKER, RUN_AWAY }; }; class Unit @@ -31,6 +31,9 @@ public: void setWingmen(vector newWingmen) { wingmen = newWingmen; } void setFormation(wstring newFormation) { formation = newFormation; } void setFormationOffset(Offset formationOffset); + void setROE(wstring newROE); + void setReactionToThreat(wstring newReactionToThreat); + void landAt(Coords loc); int getID() { return ID; } wstring getName() { return name; } @@ -65,33 +68,35 @@ public: protected: int ID; - 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; + 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 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; diff --git a/src/core/include/aircraft.h b/src/core/include/aircraft.h index d9f6c4a2..6d15e4f6 100644 --- a/src/core/include/aircraft.h +++ b/src/core/include/aircraft.h @@ -12,8 +12,10 @@ public: 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 = 150; - double targetAltitude = 5000; + double targetSpeed = 300 / 1.94384; + double targetAltitude = 20000 * 0.3048; }; \ No newline at end of file diff --git a/src/core/include/airunit.h b/src/core/include/airunit.h index 968a7ec9..550bbccf 100644 --- a/src/core/include/airunit.h +++ b/src/core/include/airunit.h @@ -15,8 +15,8 @@ public: virtual wstring getCategory() = 0; virtual void changeSpeed(wstring change) {}; virtual void changeAltitude(wstring change) {}; - virtual void setTargetSpeed(double newTargetSpeed); - virtual void setTargetAltitude(double newTargetAltitude); + virtual void setTargetSpeed(double newTargetSpeed) {}; + virtual void setTargetAltitude(double newTargetAltitude) {}; protected: virtual void AIloop(); diff --git a/src/core/include/helicopter.h b/src/core/include/helicopter.h index 9573f9b4..50a0e9c2 100644 --- a/src/core/include/helicopter.h +++ b/src/core/include/helicopter.h @@ -12,8 +12,10 @@ public: 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 = 50; - double targetAltitude = 1000; + double targetSpeed = 100 / 1.94384; + double targetAltitude = 5000 * 0.3048; }; \ No newline at end of file diff --git a/src/core/include/unitsFactory.h b/src/core/include/unitsFactory.h index b1f7d978..07b0d354 100644 --- a/src/core/include/unitsFactory.h +++ b/src/core/include/unitsFactory.h @@ -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 units; diff --git a/src/core/src/Commands.cpp b/src/core/src/Commands.cpp index 7f697dc2..ef5a7535 100644 --- a/src/core/src/Commands.cpp +++ b/src/core/src/Commands.cpp @@ -69,7 +69,19 @@ 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(); } diff --git a/src/core/src/Scheduler.cpp b/src/core/src/Scheduler.cpp index f6909d01..254137e1 100644 --- a/src/core/src/Scheduler.cpp +++ b/src/core/src/Scheduler.cpp @@ -182,7 +182,10 @@ void Scheduler::handleRequest(wstring key, json::value value) else if (key.compare(L"cloneUnit") == 0) { int ID = value[L"ID"].as_integer(); - command = dynamic_cast(new Clone(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(new Clone(ID, loc)); log(L"Cloning unit " + to_wstring(ID)); } else if (key.compare(L"setLeader") == 0) @@ -219,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); diff --git a/src/core/src/Unit.cpp b/src/core/src/Unit.cpp index bf6d7d2e..8f8f502d 100644 --- a/src/core/src/Unit.cpp +++ b/src/core/src/Unit.cpp @@ -118,9 +118,11 @@ json::value Unit::json() json[L"fuel"] = fuel; json[L"ammo"] = ammo; json[L"targets"] = targets; - json[L"targetSpeed"] = targetSpeed; - json[L"targetAltitude"] = targetAltitude; + 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++) @@ -221,4 +223,48 @@ void Unit::setFormationOffset(Offset newFormationOffset) { formationOffset = newFormationOffset; resetTask(); +} + +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(new SetOption(ID, SetCommandType::ROE, ROEEnum)); + scheduler->appendCommand(command); +} + +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(new SetOption(ID, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum)); + scheduler->appendCommand(command); +} + +void Unit::landAt(Coords loc) { + activePath.clear(); + activePath.push_back(loc); + setState(State::LAND); } \ No newline at end of file diff --git a/src/core/src/aircraft.cpp b/src/core/src/aircraft.cpp index 492cf4a9..b50d8abf 100644 --- a/src/core/src/aircraft.cpp +++ b/src/core/src/aircraft.cpp @@ -48,11 +48,21 @@ void Aircraft::changeAltitude(wstring change) { if (targetAltitude > 5000) targetAltitude += 2500 / 3.28084; - else if (targetAltitude > 0) + 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(); } \ No newline at end of file diff --git a/src/core/src/airunit.cpp b/src/core/src/airunit.cpp index 62d10cd1..cb7ac8db 100644 --- a/src/core/src/airunit.cpp +++ b/src/core/src/airunit.cpp @@ -41,6 +41,9 @@ void AirUnit::setState(int newState) return; break; } + case State::LAND: { + break; + } default: break; } @@ -72,6 +75,10 @@ void AirUnit::setState(int newState) resetActiveDestination(); break; } + case State::LAND: { + resetActiveDestination(); + break; + } default: break; } @@ -242,6 +249,22 @@ void AirUnit::AIloop() 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()) { @@ -319,13 +342,3 @@ void AirUnit::AIloop() break; } } - -void AirUnit::setTargetSpeed(double newTargetSpeed) { - targetSpeed = newTargetSpeed; - goToDestination(); -} - -void AirUnit::setTargetAltitude(double newTargetAltitude) { - targetAltitude = newTargetAltitude; - goToDestination(); -} \ No newline at end of file diff --git a/src/core/src/helicopter.cpp b/src/core/src/helicopter.cpp index 9d64e921..7350e659 100644 --- a/src/core/src/helicopter.cpp +++ b/src/core/src/helicopter.cpp @@ -48,7 +48,7 @@ void Helicopter::changeAltitude(wstring change) { if (targetAltitude > 100) targetAltitude += 100 / 3.28084; - else if (targetAltitude > 0) + else if (targetAltitude >= 0) targetAltitude += 10 / 3.28084; } if (targetAltitude < 0) @@ -56,3 +56,14 @@ void Helicopter::changeAltitude(wstring change) goToDestination(); /* Send the command to reach the destination */ } + + +void Helicopter::setTargetSpeed(double newTargetSpeed) { + targetSpeed = newTargetSpeed; + goToDestination(); +} + +void Helicopter::setTargetAltitude(double newTargetAltitude) { + targetAltitude = newTargetAltitude; + goToDestination(); +} \ No newline at end of file diff --git a/src/core/src/unitsFactory.cpp b/src/core/src/unitsFactory.cpp index 554aeb1b..6a3104bf 100644 --- a/src/core/src/unitsFactory.cpp +++ b/src/core/src/unitsFactory.cpp @@ -7,6 +7,10 @@ #include "groundunit.h" #include "navyunit.h" #include "weapon.h" +#include "commands.h" +#include "scheduler.h" + +extern Scheduler* scheduler; UnitsFactory::UnitsFactory(lua_State* L) { @@ -116,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(new Delete(ID)); + scheduler->appendCommand(command); + } +} +