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..43ba41c2 100644 --- a/client/TODO.txt +++ b/client/TODO.txt @@ -1,11 +1,9 @@ -Change cursor when in moving mode -Show airfields and enable airfield spawn RTB tanker scenario dropdown explosion wrong name for ground units -ground units don't move improve map zIndex fuel is wrong (either 0 or 1, its is casting it to int somewhere) -weapons should not be selected \ 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/package-lock.json b/client/package-lock.json index 3dee87cc..4e0499d0 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,11 +1,11 @@ { - "name": "DCS Olympus", + "name": "DCSOlympus", "version": "0.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "DCS Olympus", + "name": "DCSOlympus", "version": "0.0.0", "dependencies": { "@types/geojson": "^7946.0.10", @@ -23,6 +23,7 @@ "@types/gtag.js": "^0.0.12", "browserify": "^17.0.0", "concurrently": "^7.6.0", + "nodemon": "^2.0.20", "tsify": "^5.0.4", "typescript": "^4.9.4", "watchify": "^4.0.0" @@ -47,6 +48,12 @@ "@types/geojson": "*" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -1515,6 +1522,12 @@ } ] }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1979,6 +1992,94 @@ "node": ">= 0.6" } }, + "node_modules/nodemon": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", + "dev": true, + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -2186,6 +2287,12 @@ "node": ">= 0.10" } }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -2493,6 +2600,27 @@ } ] }, + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -2774,6 +2902,18 @@ "node": ">=8.0" } }, + "node_modules/touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "dependencies": { + "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -2884,6 +3024,12 @@ "undeclared-identifiers": "bin.js" } }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -3102,6 +3248,12 @@ "@types/geojson": "*" } }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true + }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -4294,6 +4446,12 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "dev": true }, + "ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -4661,6 +4819,71 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "nodemon": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.20.tgz", + "integrity": "sha512-Km2mWHKKY5GzRg6i1j5OxOHQtuvVsgskLfigG25yTtbyfRGn/GNvIbRyOf1PSCKJ2aT/58TiuUsuOU5UToVViw==", + "dev": true, + "requires": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4829,6 +5052,12 @@ "ipaddr.js": "1.9.1" } }, + "pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true + }, "public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -5083,6 +5312,23 @@ "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", "dev": true }, + "simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "requires": { + "semver": "~7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", @@ -5314,6 +5560,15 @@ "is-number": "^7.0.0" } }, + "touch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", + "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", + "dev": true, + "requires": { + "nopt": "~1.0.10" + } + }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -5398,6 +5653,12 @@ "xtend": "^4.0.1" } }, + "undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/client/package.json b/client/package.json index e26205f6..f96322ca 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", @@ -25,6 +25,7 @@ "@types/gtag.js": "^0.0.12", "browserify": "^17.0.0", "concurrently": "^7.6.0", + "nodemon": "^2.0.20", "tsify": "^5.0.4", "typescript": "^4.9.4", "watchify": "^4.0.0" diff --git a/client/public/images/BEBlue.png b/client/public/images/BEBlue.png new file mode 100644 index 00000000..93338e8a Binary files /dev/null and b/client/public/images/BEBlue.png differ diff --git a/client/public/images/BERed.png b/client/public/images/BERed.png new file mode 100644 index 00000000..367984f9 Binary files /dev/null and b/client/public/images/BERed.png differ diff --git a/client/public/images/bullseye.png b/client/public/images/bullseye.png new file mode 100644 index 00000000..f7024954 Binary files /dev/null and b/client/public/images/bullseye.png differ diff --git a/client/public/images/bullseye.xcf b/client/public/images/bullseye.xcf new file mode 100644 index 00000000..097722f6 Binary files /dev/null and b/client/public/images/bullseye.xcf differ diff --git a/client/public/images/bullseye0.png b/client/public/images/bullseye0.png new file mode 100644 index 00000000..a51fe39f Binary files /dev/null and b/client/public/images/bullseye0.png differ diff --git a/client/public/images/bullseye1.png b/client/public/images/bullseye1.png new file mode 100644 index 00000000..6d574bbd Binary files /dev/null and b/client/public/images/bullseye1.png differ diff --git a/client/public/images/bullseye2.png b/client/public/images/bullseye2.png new file mode 100644 index 00000000..4e99f79e Binary files /dev/null and b/client/public/images/bullseye2.png differ diff --git a/client/public/images/buttons/create.svg b/client/public/images/buttons/create.svg new file mode 100644 index 00000000..bc76cb21 --- /dev/null +++ b/client/public/images/buttons/create.svg @@ -0,0 +1 @@ + \ 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 00000000..b140e9f6 Binary files /dev/null and b/client/public/images/icons/formation.png differ diff --git a/client/public/images/icons/leader.png b/client/public/images/icons/leader.png new file mode 100644 index 00000000..8744386f Binary files /dev/null and b/client/public/images/icons/leader.png differ diff --git a/client/public/images/icons/ruler.svg b/client/public/images/icons/ruler.svg new file mode 100644 index 00000000..30ddf813 --- /dev/null +++ b/client/public/images/icons/ruler.svg @@ -0,0 +1 @@ + \ 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 00000000..06644361 Binary files /dev/null and b/client/public/images/icons/singleton.png differ diff --git a/client/public/images/pin.png b/client/public/images/pin.png new file mode 100644 index 00000000..c6222cd2 Binary files /dev/null and b/client/public/images/pin.png differ 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 00000000..ae72bc19 Binary files /dev/null and b/client/public/images/unit.png differ diff --git a/client/public/javascripts/placeholder b/client/public/javascripts/placeholder new file mode 100644 index 00000000..e69de29b diff --git a/client/public/stylesheets/layout.css b/client/public/stylesheets/layout.css index 7e9cbbaa..10c37c90 100644 --- a/client/public/stylesheets/layout.css +++ b/client/public/stylesheets/layout.css @@ -56,16 +56,15 @@ body { } #unit-control-buttons { - position: absolute; + position: fixed; top: 10px; height: fit-content; width: fit-content; - right: 10px; + right: 270px; z-index: 1000; } #unit-control-panel { - display: none; position: absolute; top: 10px; height: fit-content; @@ -77,12 +76,21 @@ body { #connection-status-panel { position: absolute; height: 30px; - width: 140px; + width: 160px; bottom: 10px; right: 10px; z-index: 1000; } +#mouse-info-panel { + position: absolute; + height: fit-content; + width: 160px; + bottom: 50px; + right: 10px; + z-index: 1000; +} + @media only screen and (max-width: 1000px) { #unit-control-buttons { top: 50px; diff --git a/client/public/stylesheets/mouseinfopanel.css b/client/public/stylesheets/mouseinfopanel.css new file mode 100644 index 00000000..3bf331ad --- /dev/null +++ b/client/public/stylesheets/mouseinfopanel.css @@ -0,0 +1,31 @@ +#mouse-info-panel { + display: flex; + flex-direction: column; + padding: 10px; + row-gap: 5px; +} + +#mouse-info-panel .rectangular-container{ + width: 100%; + font-weight: 600; + font-size: 12px; + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 10px; + padding-right: 10px; + border-radius: 10px; + background-color: #FFF3; +} + +#mouse-info-panel img { + height: 24px; +} + +#measure-position-container{ + display: none; +} + +#unit-position-container{ + display: none; +} \ No newline at end of file diff --git a/client/public/stylesheets/selectionscroll.css b/client/public/stylesheets/selectionscroll.css index 4d3aceff..70cd38d3 100644 --- a/client/public/stylesheets/selectionscroll.css +++ b/client/public/stylesheets/selectionscroll.css @@ -1,20 +1,40 @@ .olympus-selection-scroll-container { - position: fixed; - background-color: var(--background-color-dark); + position: absolute; font-size: 12px; - transition: bottom 0.2s; - border-radius: 15px; - box-shadow: 0px 2px 5px #000A; - width: 180px; + border-radius: 5px; + width: 220px; height: fit-content; - z-index: 1000; + z-index: 2000; max-height: 400px; - padding: 10px; + padding: 8px; + display: flex; + flex-direction: column; + row-gap: 5px; + align-items: center; +} + +#olympus-selection-scroll-top-bar { + color: white; + font-size: 14px; + opacity: 1; + border-radius: 5px; + padding: 5px; + background-color: #333D; + width: 100%; + text-align: center; + display: flex; + align-items: center; + justify-content: space-between; + height: 40px; + padding-left: 15px; + padding-right: 15px; } .olympus-selection-scroll { + overflow-x: hidden; overflow-y: auto; - max-height: calc(400px - 60px); + height: 100%; + width: 100%; } .olympus-selection-scroll::-webkit-scrollbar { @@ -34,48 +54,51 @@ } .olympus-selection-scroll-element { - margin: 2px; + border-bottom: 1px solid #FFF5; color: white; cursor: pointer; - font-size: 14px; + font-size: 13px; opacity: 1; - border-radius: 5px; - margin: 5px; - padding: 5px; + padding-top: 10px; + padding-bottom: 10px; padding-left: 15px; + background-color: var(--background-color-dark); + font-weight: 600; } -.olympus-selection-scroll-element:hover { - background-color: var(--highlight-color); +.olympus-selection-scroll:last-child { + border-radius: 5px; + border-bottom: 0px transparent !important; } .olympus-selection-scroll-container label { - width: 0; - height: 0; + display: inline-block; + width: 40px; + height: 24px; } .olympus-selection-scroll-container input { + display: inline-block; width: 0; height: 0; + margin: 0px; } .olympus-selection-scroll-switch { position: relative; display: inline-block; - left: calc(50% - 10px); - width: 60px; - height: 34px; + width: 40px; + height: 24px; background-color: var(--active-coalition-color); border-radius: 999px; cursor: pointer; - margin-left: -30px; } .olympus-selection-scroll-switch:before { position: absolute; content: ""; - height: 26px; - width: 26px; + height: 16px; + width: 16px; left: 4px; bottom: 4px; background-color: white; @@ -85,7 +108,12 @@ } input:checked+.olympus-selection-scroll-switch:before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); + -webkit-transform: translateX(16px); + -ms-transform: translateX(16px); + transform: translateX(16px); +} + +.olympus-selection-scroll-title { + font-size: 11px; + font-weight: 600; } \ 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 a5c77863..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"); @@ -12,6 +13,7 @@ @import url("unitcontrolpanel.css"); @import url("visibilitycontrolpanel.css"); @import url("unitinfopanel.css"); +@import url("mouseinfopanel.css"); @import url("layout.css"); @@ -19,14 +21,14 @@ /* Variables definitions */ :root { --background-color-dark: #202831; - --background-color-light: #aaaaaa; + --background-color-light: #AAA; --title-color: #d3e9ff; --text-color: white; - --blue-coalition-color: #2196F3; + --blue-coalition-color: #247be2; --red-coalition-color: #f32121; - --neutral-coalition-color: #AAAAAA; + --neutral-coalition-color: #202831; --active-coalition-color: var(--blue-coalition-color); - --highlight-color: #FFFFFFAA; + --highlight-color: #FFF5; } * { @@ -69,15 +71,90 @@ html { } .rounded-container { + position: relative; padding: 0.5em; - background-color: #247be2; - border-radius: 10px; width: fit-content; height: fit-content; text-align: center; color: white; font-size: 12px; border-radius: 9999px; + background-color: gray; +} + +.rounded-container.blue { + background-color: var(--blue-coalition-color); + border: 1px solid var(--blue-coalition-color); +} + +.rounded-container.red { + background-color: var(--red-coalition-color); + border: 1px solid var(--red-coalition-color); +} + +.rounded-container.neutral { + background-color: var(--neutral-coalition-color); +} + +.rounded-container-small { + padding: 0.2em; + width: fit-content; + height: fit-content; + text-align: center; + color: black; + font-weight: 600; + background-color: #FFFA; + font-size: 11px; + border-radius: 9999px; + padding-left: 5px; + padding-right: 5px; +} + +.rectangular-button { + position: relative; + padding: 0.5em; + width: fit-content; + height: fit-content; + text-align: center; + color: var(--highlight-color); + font-size: 12px; + border-radius: 5px; + background-color: transparent; + border: 1px solid var(--highlight-color); + cursor: pointer; + font-weight: 600; + display: flex; + align-items: center; + column-gap: 5px; +} + +.rectangular-button.blue { + border: 1px solid var(--blue-coalition-color); + color: var(--blue-coalition-color); +} + +.rectangular-button.red { + border: 1px solid var(--red-coalition-color); + color: var(--red-coalition-color); +} + +.rectangular-button.white { + border: 1px solid white; + color: white; +} + +.rectangular-button.white>img { + filter: invert(100%); +} + +.rectangular-button>img { + display: inline-block; + height: 18px; + width: 18px; +} + +.rectangular-button.red { + border: 1px solid var(--red-coalition-color); } .vl { @@ -85,3 +162,26 @@ html { width: 1px !important; display: inline-block; } + +.hl { + border-top: 1px solid #555; + height: 1px !important; + display: inline-block; +} + +.measure-box { + position: absolute; + padding-left: 0.5em; + padding-right: 0.5em; + padding-top: 0.2em; + padding-bottom: 0.2em; + background-color: #151b20; + border-radius: 5px; + width: fit-content; + height: fit-content; + text-align: center; + color: white; + font-size: 12px; + z-index: 2000; + font-weight: 600; +} \ No newline at end of file diff --git a/client/public/stylesheets/unitcontrolpanel.css b/client/public/stylesheets/unitcontrolpanel.css index 20a2a45c..f01d791a 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%; +} + +#reaction-to-threat-buttons-container { + display: flex; + flex-direction: row; + flex-wrap: wrap; + row-gap: 5px; + column-gap: 5px; + width: 100%; + height: 100%; +} + +#selected-units-container .rounded-container { + width: calc(100% - 25px); + cursor: pointer; + margin-left: 25px; +} + +#selected-units-container .rounded-container.not-selected { + background-color: transparent; +} + +#selected-units-container .rounded-container .rounded-container-small { + display: inline-block; + position: absolute; + left: 5px; + top: 5px; +} + +#selected-units-container img { + height: calc(100% + 6px); + display: inline-block; + position: absolute; + left: -32px; + top: -3px; +} + +#selected-units-container img.blue { + filter: invert(81%) sepia(6%) saturate(1685%) hue-rotate(181deg) brightness(103%) contrast(92%); +} + +#selected-units-container img.red { + filter: invert(93%) sepia(97%) saturate(1174%) hue-rotate(291deg) brightness(105%) contrast(97%); } #unit-control-panel #title-label { @@ -38,4 +100,31 @@ font-size: 14px; width: 100%; font-weight: 600; -} \ No newline at end of file +} + +#unit-control-panel #section-label { + color: white; + font-size: 13px; + width: 100%; +} + +.flight-control-slider { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.flight-control-title { + font-size: 13px; + color: white; +} + +.flight-control-value { + font-size: 14px; + font-weight: 600; + color: gray; +} + +.active .flight-control-value { + color: #5ca7ff; +} 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/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 581e1a0f..c9ae53f7 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); } }; @@ -99,29 +99,45 @@ export function attackUnit(ID: number, targetID: number) { xhr.send(JSON.stringify(data)); } -export function cloneUnit(ID: number) { +export function cloneUnit(ID: number, latlng: L.LatLng) { var xhr = new XMLHttpRequest(); xhr.open("PUT", RESTaddress); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { - console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned"); + //console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned"); } }; - var command = { "ID": ID }; + var command = { "ID": ID, "location": latlng }; var data = { "cloneUnit": command } xhr.send(JSON.stringify(data)); } +export function landAt(ID: number, latlng: L.LatLng) { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + //console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned"); + } + }; + + var command = { "ID": ID, "location": latlng }; + var data = { "landAt": command } + + xhr.send(JSON.stringify(data)); +} + export function changeSpeed(ID: number, speedChange: string) { var xhr = new XMLHttpRequest(); xhr.open("PUT", RESTaddress); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { - console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange); + //console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange); } }; @@ -131,18 +147,98 @@ export function changeSpeed(ID: number, speedChange: string) { xhr.send(JSON.stringify(data)); } +export function setSpeed(ID: number, speed: number) { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + //console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange); + } + }; + + var command = {"ID": ID, "speed": speed} + var data = {"setSpeed": command} + + xhr.send(JSON.stringify(data)); +} + export function changeAltitude(ID: number, altitudeChange: string) { var xhr = new XMLHttpRequest(); xhr.open("PUT", RESTaddress); xhr.setRequestHeader("Content-Type", "application/json"); xhr.onreadystatechange = () => { if (xhr.readyState === 4) { - console.log(getUnitsManager().getUnitByID(ID).unitName + " altitude change request: " + altitudeChange); + //console.log(getUnitsManager().getUnitByID(ID).unitName + " altitude change request: " + altitudeChange); } }; var command = {"ID": ID, "change": altitudeChange} var data = {"changeAltitude": command} + xhr.send(JSON.stringify(data)); +} + +export function setAltitude(ID: number, altitude: number) { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + //console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange); + } + }; + + var command = {"ID": ID, "altitude": altitude} + var data = {"setAltitude": command} + + xhr.send(JSON.stringify(data)); +} + +export function createFormation(ID: number, isLeader: boolean, wingmenIDs: number[]) { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + //console.log(getUnitsManager().getUnitByID(ID).unitName + " created formation with: " + wingmenIDs); + } + }; + + var command = {"ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader} + var data = {"setLeader": command} + + xhr.send(JSON.stringify(data)); +} + +export function setROE(ID: number, ROE: string) { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + //console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange); + } + }; + + var command = {"ID": ID, "ROE": ROE} + var data = {"setROE": command} + + xhr.send(JSON.stringify(data)); +} + +export function setReactionToThreat(ID: number, reactionToThreat: string) { + var xhr = new XMLHttpRequest(); + xhr.open("PUT", RESTaddress); + xhr.setRequestHeader("Content-Type", "application/json"); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + //console.log(getUnitsManager().getUnitByID(ID).unitName + " speed change request: " + speedChange); + } + }; + + var command = {"ID": ID, "reactionToThreat": reactionToThreat} + var data = {"setReactionToThreat": command} + xhr.send(JSON.stringify(data)); } \ No newline at end of file diff --git a/client/src/index.ts b/client/src/index.ts index 94f0330c..6534ade8 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -8,19 +8,25 @@ import { Dropdown } from "./controls/dropdown"; import { ConnectionStatusPanel } from "./panels/connectionstatuspanel"; import { Button } from "./controls/button"; import { MissionData } from "./missiondata/missiondata"; +import { UnitControlPanel } from "./panels/unitcontrolpanel"; +import { MouseInfoPanel } from "./panels/mouseInfoPanel"; +import { Slider } from "./controls/slider"; /* TODO: should this be a class? */ var map: Map; var selectionWheel: SelectionWheel; var selectionScroll: SelectionScroll; + var unitsManager: UnitsManager; +var missionData: MissionData; + var unitInfoPanel: UnitInfoPanel; -var activeCoalition: string; +var connectionStatusPanel: ConnectionStatusPanel; +var unitControlPanel: UnitControlPanel; +var mouseInfoPanel: MouseInfoPanel; + var scenarioDropdown: Dropdown; var mapSourceDropdown: Dropdown; -var connected: boolean; -var connectionStatusPanel: ConnectionStatusPanel; -var missionData: MissionData; var slowButton: Button; var fastButton: Button; @@ -31,6 +37,12 @@ var aiVisibilityButton: Button; var weaponVisibilityButton: Button; var deadVisibilityButton: Button; +var altitudeSlider: Slider; +var airspeedSlider: Slider; + +var connected: boolean; +var activeCoalition: string; + function setup() { /* Initialize */ map = new Map('map-container'); @@ -38,9 +50,11 @@ function setup() { selectionScroll = new SelectionScroll("selection-scroll"); unitsManager = new UnitsManager(); unitInfoPanel = new UnitInfoPanel("unit-info-panel"); + unitControlPanel = new UnitControlPanel("unit-control-panel"); scenarioDropdown = new Dropdown("scenario-dropdown", ["Caucasus", "Marianas", "Nevada", "South Atlantic", "Syria", "The Channel"], () => { }); mapSourceDropdown = new Dropdown("map-source-dropdown", map.getLayers(), (option: string) => map.setLayer(option)); connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel"); + mouseInfoPanel = new MouseInfoPanel("mouse-info-panel"); missionData = new MissionData(); /* Unit control buttons */ @@ -49,6 +63,10 @@ function setup() { climbButton = new Button("climb-button", ["images/buttons/climb.svg"], () => { getUnitsManager().selectedUnitsChangeAltitude("climb"); }); descendButton = new Button("descend-button", ["images/buttons/descend.svg"], () => { getUnitsManager().selectedUnitsChangeAltitude("descend"); }); + /* Unit control sliders */ + altitudeSlider = new Slider("altitude-slider", 0, 100, "ft", (value: number) => getUnitsManager().selectedUnitsSetAltitude(value * 0.3048)); + airspeedSlider = new Slider("airspeed-slider", 0, 100, "kts", (value: number) => getUnitsManager().selectedUnitsSetSpeed(value / 1.94384)); + /* Visibility buttons */ userVisibilityButton = new Button("user-visibility-button", ["images/buttons/user-full.svg", "images/buttons/user-partial.svg", "images/buttons/user-none.svg", "images/buttons/user-hidden.svg"], () => { }); aiVisibilityButton = new Button("ai-visibility-button", ["images/buttons/ai-full.svg", "images/buttons/ai-partial.svg", "images/buttons/ai-none.svg", "images/buttons/ai-hidden.svg"], () => { }); @@ -93,6 +111,10 @@ export function getMap() { return map; } +export function getMissionData() { + return missionData; +} + export function getSelectionWheel() { return selectionWheel; } @@ -109,6 +131,14 @@ export function getUnitInfoPanel() { return unitInfoPanel; } +export function getUnitControlPanel() { + return unitControlPanel; +} + +export function getMouseInfoPanel() { + return mouseInfoPanel; +} + export function setActiveCoalition(newActiveCoalition: string) { activeCoalition = newActiveCoalition; } @@ -173,4 +203,12 @@ export function getVisibilitySettings() { return visibility; } +export function getVisibilityButtons() { + return {user: userVisibilityButton, ai: aiVisibilityButton, weapon: weaponVisibilityButton, dead: deadVisibilityButton} +} + +export function getUnitControlSliders() { + return {altitude: altitudeSlider, airspeed: airspeedSlider} +} + window.onload = setup; \ No newline at end of file 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..5dbc9089 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 } from ".."; +import { getSelectionWheel, getSelectionScroll, getUnitsManager, getActiveCoalition, getMouseInfoPanel } from ".."; import { spawnAircraft, spawnGroundUnit, spawnSmoke } from "../dcs/dcs"; +import { bearing, distance, zeroAppend } from "../other/utils"; import { payloadNames } from "../units/payloadNames"; import { unitTypes } from "../units/unitTypes"; import { BoxSelect } from "./boxselect"; @@ -21,8 +22,14 @@ export interface SpawnEvent extends ClickEvent{ export class Map extends L.Map { #state: string; #layer?: L.TileLayer; - #preventRightClick: boolean = false; - #rightClickTimer: number = 0; + #preventLeftClick: boolean = false; + #leftClickTimer: number = 0; + #measurePoint: L.LatLng | null; + #measureIcon: L.Icon; + #measureMarker: L.Marker; + #measureLine: L.Polyline = new L.Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1, interactive: false }); + #measureLineDiv: HTMLElement; + #lastMousePosition: L.Point = new L.Point(0, 0); constructor(ID: string) { /* Init the leaflet map */ @@ -34,12 +41,25 @@ export class Map extends L.Map { /* Init the state machine */ this.#state = "IDLE"; + this.#measurePoint = null; + + this.#measureIcon = new L.Icon({ iconUrl: 'images/pin.png', iconAnchor: [16, 32]}); + this.#measureMarker = new L.Marker([0, 0], {icon: this.#measureIcon, interactive: false}); + this.#measureLineDiv = document.createElement("div"); + this.#measureLineDiv.classList.add("measure-box"); + this.#measureLineDiv.style.display = 'none'; + + document.body.appendChild(this.#measureLineDiv); /* Register event handles */ this.on("click", (e: any) => this.#onClick(e)); this.on("dblclick", (e: any) => this.#onDoubleClick(e)); this.on("contextmenu", (e: any) => this.#onContextMenu(e)); this.on('selectionend', (e: any) => this.#onSelectionEnd(e)); + this.on('mousedown', (e: any) => this.#onMouseDown(e)); + this.on('mouseup', (e: any) => this.#onMouseUp(e)); + this.on('mousemove', (e: any) => this.#onMouseMove(e)); + this.on('zoom', (e: any) => this.#onZoom(e)); } setLayer(layerName: string) { @@ -124,63 +144,133 @@ export class Map extends L.Map { } /* Selection scroll */ - showSelectionScroll(e: ClickEvent | SpawnEvent, options: any, callback: CallableFunction, showCoalition: boolean = false) { + showSelectionScroll(e: ClickEvent | SpawnEvent, title: string, options: any, callback: CallableFunction, showCoalition: boolean = false) { var x = e.x; var y = e.y; - getSelectionScroll().show(x, y, options, callback, showCoalition); + getSelectionScroll().show(x, y, title, options, callback, showCoalition); } hideSelectionScroll() { getSelectionScroll().hide(); } + getMousePosition() { + return this.#lastMousePosition; + } + + getMouseCoordinates() { + return this.containerPointToLatLng(this.#lastMousePosition); + } + /* Event handlers */ #onClick(e: any) { - this.hideSelectionWheel(); - this.hideSelectionScroll(); - if (this.#state === "IDLE") { - - } - else if (this.#state === "MOVE_UNIT") { - if (!e.originalEvent.ctrlKey) { - getUnitsManager().clearDestinations(); + if (!this.#preventLeftClick) { + this.hideSelectionWheel(); + this.hideSelectionScroll(); + if (this.#state === "IDLE") { + if (e.originalEvent.ctrlKey) + if (!this.#measurePoint) + { + this.#measurePoint = e.latlng; + this.#measureMarker.setLatLng(e.latlng); + this.#measureMarker.addTo(this); + } + else + { + this.#measurePoint = null; + if (this.hasLayer(this.#measureMarker)) + this.removeLayer(this.#measureMarker); + } } - getUnitsManager().addDestination(e.latlng) - } - } - - #onDoubleClick(e: any) { - var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: null, coalitionID: null}; - if (this.#state == "IDLE") { - var options = [ - { "tooltip": "Spawn air unit", "src": "spawnAir.png", "callback": () => this.#aircraftSpawnMenu(spawnEvent) }, - { "tooltip": "Spawn ground unit", "src": "spawnGround.png", "callback": () => this.#groundUnitSpawnMenu(spawnEvent) }, - { "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) }, - //{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) } - ] - this.showSelectionScroll(spawnEvent, options, () => {}, true); - } - } - - #onContextMenu(e: any) { - this.#rightClickTimer = setTimeout(() => { - if (!this.#preventRightClick) { + else if (this.#state === "MOVE_UNIT") { this.setState("IDLE"); getUnitsManager().deselectAllUnits(); this.hideSelectionWheel(); this.hideSelectionScroll(); } - this.#preventRightClick = false; - }, 200); + } + } + + #onDoubleClick(e: any) { + + } + + #onContextMenu(e: any) { + this.hideSelectionWheel(); + this.hideSelectionScroll(); + if (this.#state === "IDLE") { + var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: null, coalitionID: null}; + if (this.#state == "IDLE") { + var options = [ + { "tooltip": "Spawn air unit", "src": "spawnAir.png", "callback": () => this.#aircraftSpawnMenu(spawnEvent) }, + { "tooltip": "Spawn ground unit", "src": "spawnGround.png", "callback": () => this.#groundUnitSpawnMenu(spawnEvent) }, + { "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) }, + //{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) } + ] + this.showSelectionScroll(spawnEvent, "Action", options, () => {}, false); + } + } + else if (this.#state === "MOVE_UNIT") { + if (!e.originalEvent.ctrlKey) { + getUnitsManager().selectedUnitsClearDestinations(); + } + getUnitsManager().selectedUnitsAddDestination(e.latlng) + } } #onSelectionEnd(e: any) { - clearTimeout(this.#rightClickTimer); - this.#preventRightClick = true; + clearTimeout(this.#leftClickTimer); + this.#preventLeftClick = true; + this.#leftClickTimer = setTimeout(() => { + this.#preventLeftClick = false; + }, 200); getUnitsManager().selectFromBounds(e.selectionBounds); } + #onMouseDown(e: any) + { + if ((e.originalEvent.which == 1) && (e.originalEvent.button == 0)) + { + this.dragging.disable(); + } + } + + #onMouseUp(e: any) + { + if ((e.originalEvent.which == 1) && (e.originalEvent.button == 0)) + { + this.dragging.enable(); + } + } + + #onMouseMove(e: any) + { + var selectedUnitPosition = null; + var selectedUnits = getUnitsManager().getSelectedUnits(); + if (selectedUnits && selectedUnits.length == 1) + { + selectedUnitPosition = new L.LatLng(selectedUnits[0].latitude, selectedUnits[0].longitude); + } + getMouseInfoPanel().update(e.latlng, this.#measurePoint, selectedUnitPosition); + + this.#lastMousePosition.x = e.originalEvent.x; + this.#lastMousePosition.y = e.originalEvent.y; + + if ( this.#measurePoint) + this.#drawMeasureLine(); + else + this.#hideMeasureLine(); + } + + #onZoom(e: any) + { + if (this.#measurePoint) + this.#drawMeasureLine(); + else + this.#hideMeasureLine(); + } + /* Spawn from air base */ spawnFromAirbase(e: SpawnEvent) { @@ -198,7 +288,7 @@ export class Map extends L.Map { {'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this.#selectGroundUnit(e, "Radar")}, {'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this.#selectGroundUnit(e, "Unarmed")} ] - this.showSelectionScroll(e, options, () => {}, true); + this.showSelectionScroll(e, "Spawn ground unit", options, () => {}, true); } #smokeSpawnMenu(e: SpawnEvent) { @@ -211,7 +301,7 @@ export class Map extends L.Map { {'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('green', e.latlng)}, 'tint': 'green'}, {'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideSelectionWheel(); this.hideSelectionScroll(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'}, ] - this.showSelectionScroll(e, options, () => {}, true); + this.showSelectionScroll(e, "Spawn smoke", options, () => {}, false); } #explosionSpawnMenu(e: SpawnEvent) { @@ -228,7 +318,10 @@ export class Map extends L.Map { { 'coalition': true, 'tooltip': 'Drone', 'src': 'spawnDrone.png', 'callback': () => this.#selectAircraft(e, "drone") }, { 'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png', 'callback': () => this.#selectAircraft(e, "transport") }, ] - this.showSelectionScroll(e, options, () => {}, true); + if (e.airbaseName != null) + this.showSelectionScroll(e, "Spawn at " + e.airbaseName, options, () => {}, true); + else + this.showSelectionScroll(e, "Spawn air unit", options, () => {}, true); } /* Show unit selection for air units */ @@ -240,11 +333,11 @@ export class Map extends L.Map { options.sort(); else options = []; - this.showSelectionScroll(e, options, (unitType: string) => { + this.showSelectionScroll(e, "Select aircraft", options, (unitType: string) => { this.hideSelectionWheel(); this.hideSelectionScroll(); this.#unitSelectPayload(e, unitType); - }); + }, true); } /* Show weapon selection for air units */ @@ -255,11 +348,11 @@ export class Map extends L.Map { options = payloadNames[unitType] if (options != undefined && options.length > 0) { options.sort(); - this.showSelectionScroll({x: e.x, y: e.y, latlng: e.latlng}, options, (payloadName: string) => { + this.showSelectionScroll({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout", options, (payloadName: string) => { this.hideSelectionWheel(); this.hideSelectionScroll(); spawnAircraft(unitType, e.latlng, getActiveCoalition(), payloadName, e.airbaseName); - }); + }, true); } else { spawnAircraft(unitType, e.latlng, getActiveCoalition()); @@ -273,10 +366,49 @@ export class Map extends L.Map { this.hideSelectionScroll(); var options = unitTypes.vehicles[group]; options.sort(); - this.showSelectionScroll(e, options, (unitType: string) => { + this.showSelectionScroll(e, "Select ground unit", options, (unitType: string) => { this.hideSelectionWheel(); this.hideSelectionScroll(); spawnGroundUnit(unitType, e.latlng, getActiveCoalition()); - }); + }, true); + } + + #drawMeasureLine() + { + var mouseLatLng = this.containerPointToLatLng(this.#lastMousePosition); + if (this.#measurePoint != null) + { + var points = [this.#measurePoint, mouseLatLng]; + this.#measureLine.setLatLngs(points); + var dist = distance(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng); + var bear = bearing(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng); + var startXY = this.latLngToContainerPoint(this.#measurePoint); + var dx = (this.#lastMousePosition.x - startXY.x); + var dy = (this.#lastMousePosition.y - startXY.y); + + var angle = Math.atan2(dy, dx); + if (angle > Math.PI / 2) + angle = angle - Math.PI; + + if (angle < -Math.PI / 2) + angle = angle + Math.PI; + + this.#measureLineDiv.innerHTML = `${zeroAppend(Math.floor(bear), 3)}° / ${zeroAppend(Math.floor(dist*0.000539957), 3)} NM` + this.#measureLineDiv.style.left = (this.#lastMousePosition.x + startXY.x) / 2 - this.#measureLineDiv.offsetWidth / 2 + "px"; + this.#measureLineDiv.style.top = (this.#lastMousePosition.y + startXY.y) / 2 - this.#measureLineDiv.offsetHeight / 2 + "px"; + this.#measureLineDiv.style.rotate = angle + "rad"; + this.#measureLineDiv.style.display = ""; + } + + if (!this.hasLayer(this.#measureLine)) + this.#measureLine.addTo(this); + } + + #hideMeasureLine() + { + this.#measureLineDiv.style.display = "none"; + + if (this.hasLayer(this.#measureLine)) + this.removeLayer(this.#measureLine) } } 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..9a143ffd --- /dev/null +++ b/client/src/panels/unitcontrolpanel.ts @@ -0,0 +1,363 @@ +import { imageOverlay } from "leaflet"; +import { getUnitControlSliders, getUnitsManager } from ".."; +import { ConvertDDToDMS, rad2deg } from "../other/utils"; +import { Aircraft, AirUnit, GroundUnit, Helicopter, NavyUnit, Unit } from "../units/unit"; + +export class UnitControlPanel { + #element: HTMLElement + #display: string; + + constructor(ID: string) { + this.#element = 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()); + } + 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(); + } + } + + 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.#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); + } + } + + #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 + } + + #checkAllUnitsAircraft(units: Unit[]) + { + for (let unit of units) + if (!(unit instanceof Aircraft)) + return false + return true + } + + #checkAllUnitsHelicopter(units: Unit[]) + { + for (let unit of units) + if (!(unit instanceof Helicopter)) + return false + return true + } + + #checkAllUnitsGroundUnit(units: Unit[]) + { + for (let unit of units) + if (!(unit instanceof GroundUnit)) + return false + return true + } + + #checkAllUnitsNavyUnit(units: Unit[]) + { + for (let unit of units) + if (!(unit instanceof NavyUnit)) + return false + return true + } + + #checkAllUnitsSameFormation(units: Unit[]) + { + var leaderFound = false; + for (let unit of units) + { + if (unit.isLeader) + { + if (leaderFound) + return false + else + leaderFound = true; + } + if (!unit.isLeader) + return false + } + return true + } + + #checkUnitsAlreadyInFormation(units: Unit[]) + { + for (let unit of units) + if (unit.isLeader) + return true + return false + } + + #getTargetAirspeed(units: Unit[]) + { + var airspeed = null; + for (let unit of units) + { + if (unit.targetSpeed != airspeed && airspeed != null) + return null + else + airspeed = unit.targetSpeed; + } + return airspeed; + } + + #getTargetAltitude(units: Unit[]) + { + var altitude = null; + for (let unit of units) + { + if (unit.targetAltitude != altitude && altitude != null) + return null + else + altitude = unit.targetAltitude; + } + return altitude; + } + + #getROE(units: Unit[]) + { + var ROE = null; + for (let unit of units) + { + if (unit.ROE !== ROE && ROE != null) + return null + else + ROE = unit.ROE; + } + return ROE; + } + + #getReactionToThreat(units: Unit[]) + { + var reactionToThreat = null; + for (let unit of units) + { + if (unit.reactionToThreat !== reactionToThreat && reactionToThreat != null) + return null + else + reactionToThreat = unit.reactionToThreat; + } + return reactionToThreat; + } +} \ 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..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 } from '../dcs/dcs'; +import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, landAt, setAltitude, setReactionToThreat, setROE, setSpeed } from '../dcs/dcs'; var pathIcon = new Icon({ iconUrl: 'images/marker-icon.png', @@ -12,9 +12,6 @@ var pathIcon = new Icon({ export class Unit { ID: number = -1; - leader: boolean = false; - wingman: boolean = false; - wingmen: Unit[] = []; formation: string = ""; name: string = ""; unitName: string = ""; @@ -33,7 +30,17 @@ export class Unit { activePath: any = null; ammo: any = null; targets: any = null; - + hasTask: boolean = false; + isLeader: boolean = false; + isWingman: boolean = false; + leaderID: number = 0; + wingmen: Unit[] = []; + wingmenIDs: number[] = []; + targetSpeed: number = 0; + targetAltitude: number = 0; + ROE: string = ""; + reactionToThreat: string = ""; + #selectable: boolean; #selected: boolean = false; #preventClick: boolean = false; @@ -61,42 +68,36 @@ export class Unit { this.#marker = marker; this.#marker.on('click', (e) => this.#onClick(e)); this.#marker.on('dblclick', (e) => this.#onDoubleClick(e)); + this.#marker.on('contextmenu', (e) => this.#onContextMenu(e)); this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 }); this.#pathPolyline.addTo(getMap()); this.#targetsPolylines = []; } - update(response: JSON) { + update(response: any) { for (let entry in response) { - // @ts-ignore + // @ts-ignore TODO handle better this[entry] = response[entry]; } + // TODO handle better + if (response['activePath'] == undefined) + this.activePath = null + /* Dead units can't be selected */ this.setSelected(this.getSelected() && this.alive) this.#updateMarker(); this.#clearTargets(); - if (this.getSelected()) + if (this.getSelected() && this.activePath != null) { this.#drawPath(); this.#drawTargets(); } else - this.#clearPath(); - - /* - this.wingmen = []; - if (response["wingmenIDs"] != null) - { - for (let ID of response["wingmenIDs"]) - { - this.wingmen.push(unitsManager.getUnitByID(ID)); - } - } - */ + this.#clearPath(); } setSelected(selected: boolean) { @@ -105,6 +106,7 @@ export class Unit { this.#selected = selected; this.#marker.setSelected(selected); getUnitsManager().onUnitSelection(); + } } @@ -140,6 +142,28 @@ export class Unit { return false; } + getLeader() { + return getUnitsManager().getUnitByID(this.leaderID); + } + + getFormation() { + return [this].concat(this.getWingmen()) + } + + getWingmen() { + var wingmen: Unit[] = []; + if (this.wingmenIDs != null) + { + for (let ID of this.wingmenIDs) + { + var unit = getUnitsManager().getUnitByID(ID) + if (unit) + wingmen.push(unit); + } + } + return wingmen; + } + #onClick(e: any) { this.#timer = setTimeout(() => { if (!this.#preventClick) { @@ -157,22 +181,21 @@ export class Unit { #onDoubleClick(e: any) { clearTimeout(this.#timer); this.#preventClick = true; + } + #onContextMenu(e: any) { var options = [ 'Attack', + 'Follow' ] - //if (!this.leader && !this.wingman) { - // options.push({ 'tooltip': 'Create formation', 'src': 'formation.png', 'callback': () => { getMap().hideSelectionWheel(); /*unitsManager.createFormation(this.ID);*/ } }); - //} - - getMap().showSelectionScroll(e.originalEvent, options, (action: string) => this.#executeAction(action)); + getMap().showSelectionScroll(e.originalEvent, "Action: " + this.unitName, options, (action: string) => this.#executeAction(action)); } #executeAction(action: string) { getMap().hideSelectionScroll(); if (action === "Attack") - getUnitsManager().attackUnit(this.ID); + getUnitsManager().selectedUnitsAttackUnit(this.ID); } #updateMarker() { @@ -199,8 +222,8 @@ export class Unit { #drawPath() { if (this.activePath != null) { - var _points = []; - _points.push(new LatLng(this.latitude, this.longitude)); + var points = []; + points.push(new LatLng(this.latitude, this.longitude)); /* Add markers if missing */ while (this.#pathMarkers.length < Object.keys(this.activePath).length) { @@ -218,8 +241,8 @@ export class Unit { for (let WP in this.activePath) { var destination = this.activePath[WP]; this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]); - _points.push(new LatLng(destination.lat, destination.lng)); - this.#pathPolyline.setLatLngs(_points); + points.push(new LatLng(destination.lat, destination.lng)); + this.#pathPolyline.setLatLngs(points); } } } @@ -232,7 +255,6 @@ export class Unit { this.#pathPolyline.setLatLngs([]); } - #drawTargets() { for (let typeIndex in this.targets) @@ -278,7 +300,6 @@ export class Unit { } } - attackUnit(targetID: number) { /* Call DCS attackUnit function */ if (this.ID != targetID) { @@ -289,7 +310,11 @@ export class Unit { } } - + landAt(latlng: LatLng) + { + landAt(this.ID, latlng); + } + changeSpeed(speedChange: string) { changeSpeed(this.ID, speedChange); @@ -300,6 +325,26 @@ export class Unit { changeAltitude(this.ID, altitudeChange); } + setSpeed(speed: number) + { + setSpeed(this.ID, speed); + } + + setAltitude(altitude: number) + { + setAltitude(this.ID, altitude); + } + + setROE(ROE: string) + { + setROE(this.ID, ROE); + } + + setReactionToThreat(reactionToThreat: string) + { + setReactionToThreat(this.ID, reactionToThreat); + } + /* setformation(formation) { @@ -318,25 +363,13 @@ export class Unit { xhr.send(JSON.stringify(data)); } - - setLeader(wingmenIDs) - { - // TODO move in dedicated file - var xhr = new XMLHttpRequest(); - xhr.open("PUT", RESTaddress); - xhr.setRequestHeader("Content-Type", "application/json"); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - console.log(this.unitName + " created formation with: " + wingmenIDs); - } - }; - - var command = {"ID": this.ID, "wingmenIDs": wingmenIDs} - var data = {"setLeader": command} - - xhr.send(JSON.stringify(data)); - } */ + + setLeader(isLeader: boolean, wingmenIDs: number[] = []) + { + setLeader(this.ID, isLeader, wingmenIDs); + } + } export class AirUnit extends Unit { diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 4533d988..1149661b 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,24 @@ export class UnitsManager { constructor() { this.#units = {}; this.#copiedUnits = []; + + document.addEventListener('copy', () => this.copyUnits()); + document.addEventListener('paste', () => this.pasteUnits()); + } + + #updateUnitControlPanel() { + /* Update the unit control panel */ + if (this.getSelectedUnits().length > 0) { + getUnitControlPanel().show(); + getUnitControlPanel().update(this.getSelectedLeaders().concat(this.getSelectedSingletons())); + } + else { + getUnitControlPanel().hide(); + } + } + + getUnits() { + return this.#units; } addUnit(ID: number, data: any) { @@ -27,7 +46,10 @@ export class UnitsManager { } getUnitByID(ID: number) { - return this.#units[ID]; + if (ID in this.#units) + return this.#units[ID]; + else + return null; } removeUnit(ID: number) { @@ -40,6 +62,13 @@ export class UnitsManager { } } + selectUnit(ID: number, deselectAllUnits: boolean = true) + { + if (deselectAllUnits) + this.deselectAllUnits(); + this.#units[ID]?.setSelected(true); + } + update(data: any) { for (let ID in data["units"]) { /* Create the unit if missing from the local array, then update the data. Drawing is handled by leaflet. */ @@ -49,6 +78,7 @@ export class UnitsManager { this.#units[parseInt(ID)].update(data["units"][ID]); } + /* Update the unit info panel */ if (this.getSelectedUnits().length == 1) { getUnitInfoPanel().show(); getUnitInfoPanel().update(this.getSelectedUnits()[0]); @@ -67,6 +97,8 @@ export class UnitsManager { getMap().setState("IDLE"); //unitControlPanel.setEnabled(false); } + + this.#updateUnitControlPanel(); } selectFromBounds(bounds: LatLngBounds) @@ -92,7 +124,35 @@ export class UnitsManager { return selectedUnits; } - addDestination(latlng: L.LatLng) { + getSelectedLeaders() { + var leaders: Unit[] = []; + for (let idx in this.getSelectedUnits()) + { + var unit = this.getSelectedUnits()[idx]; + if (unit.isLeader) + leaders.push(unit); + else if (unit.isWingman) + { + var leader = unit.getLeader(); + if (leader && !leaders.includes(leader)) + leaders.push(leader); + } + } + return leaders; + } + + getSelectedSingletons() { + var singletons: Unit[] = []; + for (let idx in this.getSelectedUnits()) + { + var unit = this.getSelectedUnits()[idx]; + if (!unit.isLeader && !unit.isWingman) + singletons.push(unit); + } + return singletons; + } + + selectedUnitsAddDestination(latlng: L.LatLng) { var selectedUnits = this.getSelectedUnits(); for (let idx in selectedUnits) { var commandedUnit = selectedUnits[idx]; @@ -104,7 +164,7 @@ export class UnitsManager { } } - clearDestinations() { + selectedUnitsClearDestinations() { var selectedUnits = this.getSelectedUnits(); for (let idx in selectedUnits) { var commandedUnit = selectedUnits[idx]; @@ -116,6 +176,15 @@ export class UnitsManager { } } + selectedUnitsLandAt(latlng: LatLng) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].landAt(latlng); + } + } + selectedUnitsChangeSpeed(speedChange: string) { var selectedUnits = this.getSelectedUnits(); @@ -123,6 +192,8 @@ export class UnitsManager { { selectedUnits[idx].changeSpeed(speedChange); } + + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail } selectedUnitsChangeAltitude(altitudeChange: string) @@ -132,35 +203,66 @@ export class UnitsManager { { selectedUnits[idx].changeAltitude(altitudeChange); } + + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail } - // handleKeyEvent(e) - // { - // if (e.originalEvent.code === 'KeyC' && e.originalEvent.ctrlKey) - // { - // this.copyUnits(); - // } - // else if (e.originalEvent.code === 'KeyV' && e.originalEvent.ctrlKey) - // { - // this.pasteUnits(); - // } - // } + selectedUnitsSetSpeed(speed: number) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].setSpeed(speed); + } + } - // copyUnits() - // { - // this.#copiedUnits = this.getSelectedUnits(); - // } + selectedUnitsSetAltitude(altitude: number) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].setAltitude(altitude); + } + } - // pasteUnits() - // { - // for (let idx in this.#copiedUnits) - // { - // var unit = this.#copiedUnits[idx]; - // cloneUnit(unit.ID); - // } - // } + selectedUnitsSetROE(ROE: string) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].setROE(ROE); + } - attackUnit(ID: number) { + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail + } + + selectedUnitsSetReactionToThreat(reactionToThreat: string) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) + { + selectedUnits[idx].setReactionToThreat(reactionToThreat); + } + + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail + } + + + copyUnits() + { + this.#copiedUnits = this.getSelectedUnits(); + } + + pasteUnits() + { + for (let idx in this.#copiedUnits) + { + var unit = this.#copiedUnits[idx]; + cloneUnit(unit.ID, getMap().getMouseCoordinates()); + } + } + + selectedUnitsAttackUnit(ID: number) { var selectedUnits = this.getSelectedUnits(); for (let idx in selectedUnits) { /* If a unit is a wingman, send the command to its leader */ @@ -173,59 +275,59 @@ export class UnitsManager { } } - // createFormation(ID) - // { - // var selectedUnits = this.getSelectedUnits(); - // var wingmenIDs = []; - // for (let idx in selectedUnits) - // { - // if (selectedUnits[idx].wingman) - // { - // showMessage(selectedUnits[idx].unitName + " is already in a formation."); - // return; - // } - // else if (selectedUnits[idx].leader) - // { - // showMessage(selectedUnits[idx].unitName + " is already in a formation."); - // return; - // } - // else - // { - // /* TODO - // if (selectedUnits[idx].category !== this.getUnitByID(ID).category) - // { - // showMessage("All units must be of the same category to create a formation."); - // } - // */ - // if (selectedUnits[idx].ID != ID) - // { - // wingmenIDs.push(selectedUnits[idx].ID); - // } - // } - // } - // if (wingmenIDs.length > 0) - // { - // this.getUnitByID(ID).setLeader(wingmenIDs); - // } - // else - // { - // showMessage("At least 2 units must be selected to create a formation."); - // } - // } + selectedUnitsCreateFormation(ID: number | null = null) + { + var selectedUnits = this.getSelectedUnits(); + if (selectedUnits.length >= 2) + { + if (ID == null) + ID = selectedUnits[0].ID + + var wingmenIDs = []; + for (let idx in selectedUnits) + { + if (selectedUnits[idx].isWingman) + { + console.log(selectedUnits[idx].unitName + " is already in a formation."); + return; + } + else if (selectedUnits[idx].isLeader) + { + console.log(selectedUnits[idx].unitName + " is already in a formation."); + return; + } + else + { + /* TODO + if (selectedUnits[idx].category !== this.getUnitByID(ID).category) + { + showMessage("All units must be of the same category to create a formation."); + } + */ + if (selectedUnits[idx].ID != ID) + { + wingmenIDs.push(selectedUnits[idx].ID); + } + } + } + if (wingmenIDs.length > 0) + { + this.getUnitByID(ID)?.setLeader(true, wingmenIDs); + } + else + { + console.log("At least 2 units must be selected to create a formation."); + } + } + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail + } - // getLeader(ID) - // { - // for (let idx in this.#units) - // { - // var unit = this.#units[idx]; - // if (unit.leader) - // { - // if (unit.wingmen.includes(this.getUnitByID(ID))) - // { - // return unit; - // } - // } - // } - // showMessage("Error: no leader found for this unit") - // } + selectedUnitsUndoFormation(ID: number | null = null) + { + for (let leader of this.getSelectedLeaders()) + { + leader.setLeader(false); + } + setTimeout(() => this.#updateUnitControlPanel(), 300); // TODO find better method, may fail + } } \ No newline at end of file diff --git a/client/views/index.ejs b/client/views/index.ejs index 02a7903b..fea64c45 100644 --- a/client/views/index.ejs +++ b/client/views/index.ejs @@ -30,6 +30,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..fba42229 100644 --- a/client/views/unitcontrolpanel.ejs +++ b/client/views/unitcontrolpanel.ejs @@ -1,12 +1,65 @@ -
-
-
-
-
-
-
+
+
+
+
+
+
+
Selected units
-
Olympus-1
-
Olympus-2
+ +
+ +
+ +
+
Create formation
+
Undo formation
+
+
+ +
Controls
+
+
+
Altitude
+
+ +
+
+
Speed
+
+ +
+
+ +
Formation
+
+
Echelon
+
Fingertip
+
Trail
+
Line abreast
+
+ +
+ +
Rules of engagement
+
+
Free
+
Designated free
+
Designated
+
Return
+
Hold
+
+ +
+ +
Reaction to threat
+
+
None
+
Passive
+
Evade
+
Escape
+
Abort
+
+
\ No newline at end of file diff --git a/installer/DCSOlympus.iss b/installer/DCSOlympus.iss index d10a00c2..61c67383 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.5 DefaultDirName={usersavedgames}\DCS.openbeta DefaultGroupName=DCSOlympus OutputBaseFilename=DCSOlympus diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index dea45dc1..0ea22476 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,25 +54,56 @@ function Olympus.buildTask(options) return task end --- Move a unit. Since most tasks in DCS are Enroute tasks, this function is the main way to control the unit AI +-- Builds a valid task depending on the provided options +function Olympus.buildTask(options) + local task = nil + -- Engage specific target by ID. Checks if target exists. + if options['id'] == 'FollowUnit' and options['leaderID'] and options['offset'] then + local leader = Olympus.getUnitByID(options['leaderID']) + if leader and leader:isExist() then + task = { + id = 'Follow', + params = { + groupId = leader:getGroup():getID(), + pos = options['offset'], + lastWptIndexFlag = false, + lastWptIndex = 1 + } + } + end + end + return task +end + +-- Move a unit. Since many tasks in DCS are Enroute tasks, this function is an important way to control the unit AI function Olympus.move(ID, lat, lng, altitude, speed, category, taskOptions) Olympus.notify("Olympus.move " .. ID .. " (" .. lat .. ", " .. lng ..") " .. altitude .. "m " .. speed .. "m/s " .. category, 2) local unit = Olympus.getUnitByID(ID) if unit then if category == "Aircraft" then local startPoint = mist.getLeadPos(unit:getGroup()) - local endPoint = coord.LLtoLO(lat, lng, 0) + local endPoint = coord.LLtoLO(lat, lng, 0) - local path = { - [1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'), - [2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO') - } + local path = {} + if taskOptions and taskOptions['id'] == 'Land' then + path = { + [1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'), + [2] = mist.fixedWing.buildWP(endPoint, landing, speed, 0, 'AGL') + } + else + path = { + [1] = mist.fixedWing.buildWP(startPoint, flyOverPoint, speed, altitude, 'BARO'), + [2] = mist.fixedWing.buildWP(endPoint, turningPoint, speed, altitude, 'BARO') + } + end -- If a task exists assign it to the controller - local task = Olympus.buildTask(taskOptions) - if task then - path[1].task = task - path[2].task = task + if taskOptions then + local task = Olympus.buildEnrouteTask(taskOptions) + if task then + path[1].task = task + path[2].task = task + end end -- Assign the mission task to the controller @@ -171,7 +202,7 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions) local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)) if payload == nil then - if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] ~= nil then + if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] then payload = Olympus.unitPayloads[unitType][payloadName] else payload = {} @@ -250,7 +281,7 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions) task = 'CAP', } - mist.dynAdd(vars) + local newGroup = mist.dynAdd(vars) -- Save the payload to be reused in case the unit is cloned. TODO: save by ID not by name (it works but I like consistency) Olympus.payloadRegistry[vars.name] = payload @@ -259,40 +290,99 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, spawnOptions) end -- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units. -function Olympus.clone(ID) +function Olympus.clone(ID, lat, lng) Olympus.notify("Olympus.clone " .. ID, 2) local unit = Olympus.getUnitByID(ID) if unit then local coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition()) - local lat, lng, alt = coord.LOtoLL(unit:getPoint()) -- TODO: only works on Aircraft local spawnOptions = { - payload = Olympus.payloadRegistry[unitName] + payload = Olympus.payloadRegistry[unit:getName()] } - Olympus.spawnAircraft(coalition, unit:getTypeName(), lat + 0.001, lng + 0.001, spawnOptions) + Olympus.spawnAircraft(coalition, unit:getTypeName(), lat, lng, spawnOptions) end Olympus.notify("Olympus.clone completed successfully", 2) end -function Olympus.follow(leaderID, ID) - Olympus.notify("Olympus.follow " .. ID .. " " .. leaderID, 2) - local leader = Olympus.getUnitByID(leaderID) +function Olympus.delete(ID, lat, lng) + Olympus.notify("Olympus.delete " .. ID, 2) local unit = Olympus.getUnitByID(ID) - local followTask = { - id = 'Follow', - params = { - groupId = leader:getGroup():getID(), - pos = {x = 0 , y = 0, z = 20} , - lastWptIndexFlag = false, - lastWptIndex = 1 - } - } - Olympus.notify("Olympus.follow group ID" .. unit:getGroup():getID(), 2) - unit:getGroup():getController():pushTask(followTask) - Olympus.notify("Olympus.follow completed successfully", 2) + if unit then + unit:destroy(); + Olympus.notify("Olympus.delete completed successfully", 2) + end end +function Olympus.setTask(ID, taskOptions) + Olympus.notify("Olympus.setTask " .. ID .. " " .. Olympus.serializeTable(taskOptions), 2) + local unit = Olympus.getUnitByID(ID) + if unit then + local task = Olympus.buildTask(taskOptions); + if task then + unit:getGroup():getController():setTask(task) + Olympus.notify("Olympus.setTask completed successfully", 2) + end + end +end +function Olympus.resetTask(ID) + Olympus.notify("Olympus.resetTask " .. ID, 2) + local unit = Olympus.getUnitByID(ID) + if unit then + unit:getGroup():getController():resetTask() + Olympus.notify("Olympus.resetTask completed successfully", 2) + end +end + +function Olympus.setCommand(ID, command) + Olympus.notify("Olympus.setCommand " .. ID .. " " .. Olympus.serializeTable(command), 2) + local unit = Olympus.getUnitByID(ID) + if unit then + unit:getGroup():getController():setCommand(command) + Olympus.notify("Olympus.setCommand completed successfully", 2) + end +end + +function Olympus.setOption(ID, optionID, optionValue) + Olympus.notify("Olympus.setCommand " .. ID .. " " .. optionID .. " " .. optionValue, 2) + local unit = Olympus.getUnitByID(ID) + if unit then + unit:getGroup():getController():setOption(optionID, optionValue) + Olympus.notify("Olympus.setOption completed successfully", 2) + end +end + +function Olympus.serializeTable(val, name, skipnewlines, depth) + skipnewlines = skipnewlines or false + depth = depth or 0 + + local tmp = string.rep(" ", depth) + if name then + if type(name) == "number" then + tmp = tmp .. "[" .. name .. "]" .. " = " + else + tmp = tmp .. name .. " = " + end + end + + if type(val) == "table" then + tmp = tmp .. "{" .. (not skipnewlines and "\n" or "") + for k, v in pairs(val) do + tmp = tmp .. Olympus.serializeTable(v, k, skipnewlines, depth + 1) .. "," .. (not skipnewlines and "\n" or "") + end + tmp = tmp .. string.rep(" ", depth) .. "}" + elseif type(val) == "number" then + tmp = tmp .. tostring(val) + elseif type(val) == "string" then + tmp = tmp .. string.format("%q", val) + elseif type(val) == "boolean" then + tmp = tmp .. (val and "true" or "false") + else + tmp = tmp .. "\"[inserializeable datatype:" .. type(val) .. "]\"" + end + + return tmp +end Olympus.notify("OlympusCommand script loaded successfully", 2) \ No newline at end of file diff --git a/scripts/OlympusMission.lua b/scripts/OlympusMission.lua index e089a657..815ea7bc 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..95ccefbc 100644 --- a/src/core/include/Commands.h +++ b/src/core/include/Commands.h @@ -8,9 +8,57 @@ namespace CommandPriority { }; namespace CommandType { - enum CommandTypes { NO_TYPE, MOVE, SMOKE, LASE, EXPLODE, SPAWN_AIR, SPAWN_GROUND, CLONE, LAND, REFUEL, FOLLOW }; + enum CommandTypes { NO_TYPE, MOVE, SMOKE, SPAWN_AIR, SPAWN_GROUND, CLONE, FOLLOW, RESET_TASK, SET_OPTION, SET_COMMAND, SET_TASK }; }; +namespace SetCommandType { + enum SetCommandTypes { + ROE = 0, + REACTION_ON_THREAT = 1, + RADAR_USING = 3, + FLARE_USING = 4, + Formation = 5, + RTB_ON_BINGO = 6, + SILENCE = 7, + RTB_ON_OUT_OF_AMMO = 10, + ECM_USING = 13, + PROHIBIT_AA = 14, + PROHIBIT_JETT = 15, + PROHIBIT_AB = 16, + PROHIBIT_AG = 17, + MISSILE_ATTACK = 18, + PROHIBIT_WP_PASS_REPORT = 19, + OPTION_RADIO_USAGE_CONTACT = 21, + OPTION_RADIO_USAGE_ENGAGE = 22, + OPTION_RADIO_USAGE_KILL = 23, + JETT_TANKS_IF_EMPTY = 25, + FORCED_ATTACK = 26 + }; +} + +namespace ROE { + enum ROEs { + WEAPON_FREE = 0, + OPEN_FIRE_WEAPON_FREE = 1, + OPEN_FIRE = 2, + RETURN_FIRE = 3, + WEAPON_HOLD = 4, + }; +} + +namespace ReactionToThreat { + enum ReactionToThreats { + NO_REACTION = 0, + PASSIVE_DEFENCE = 1, + EVADE_FIRE = 2, + BYPASS_AND_ESCAPE = 3, + ALLOW_ABORT_MISSION = 4 + }; +} + + + + /* Base command class */ class Command { @@ -25,10 +73,10 @@ protected: }; /* Simple low priority move command (from user click) */ -class MoveCommand : public Command +class Move : public Command { public: - MoveCommand(int ID, Coords destination, double speed, double altitude, wstring unitCategory, wstring taskOptions): + Move(int ID, Coords destination, double speed, double altitude, wstring unitCategory, wstring taskOptions): ID(ID), destination(destination), speed(speed), @@ -51,10 +99,10 @@ private: }; /* Smoke command */ -class SmokeCommand : public Command +class Smoke : public Command { public: - SmokeCommand(wstring color, Coords location) : + Smoke(wstring color, Coords location) : color(color), location(location) { @@ -69,10 +117,10 @@ private: }; /* Spawn ground unit command */ -class SpawnGroundUnitCommand : public Command +class SpawnGroundUnit : public Command { public: - SpawnGroundUnitCommand(wstring coalition, wstring unitType, Coords location) : + SpawnGroundUnit(wstring coalition, wstring unitType, Coords location) : coalition(coalition), unitType(unitType), location(location) @@ -89,10 +137,10 @@ private: }; /* Spawn air unit command */ -class SpawnAircraftCommand : public Command +class SpawnAircraft : public Command { public: - SpawnAircraftCommand(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName) : + SpawnAircraft(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName) : coalition(coalition), unitType(unitType), location(location), @@ -114,35 +162,107 @@ private: }; /* Clone unit command */ -class CloneCommand : public Command +class Clone : public Command { public: - CloneCommand(int ID) : - ID(ID) + Clone(int ID, Coords location) : + ID(ID), + location(location) { priority = CommandPriority::LOW; type = CommandType::CLONE; }; virtual wstring getString(lua_State* L); +private: + const int ID; + const Coords location; +}; + +/* Delete unit command */ +class Delete : public Command +{ +public: + Delete(int ID) : + ID(ID) + { + priority = CommandPriority::HIGH; + type = CommandType::CLONE; + }; + virtual wstring getString(lua_State* L); + private: const int ID; }; /* Follow command */ -class FollowCommand : public Command +class SetTask : public Command { public: - FollowCommand(int leaderID, int ID) : - leaderID(leaderID), - ID(ID) + SetTask(int ID, wstring task) : + ID(ID), + task(task) { - priority = CommandPriority::LOW; + priority = CommandPriority::MEDIUM; type = CommandType::FOLLOW; }; virtual wstring getString(lua_State* L); private: - const int leaderID; + const int ID; + const wstring task; +}; + +/* Reset task command */ +class ResetTask : public Command +{ +public: + ResetTask(int ID) : + ID(ID) + { + priority = CommandPriority::HIGH; + type = CommandType::RESET_TASK; + }; + virtual wstring getString(lua_State* L); + +private: const int ID; }; + +/* Set command */ +class SetCommand : public Command +{ +public: + SetCommand(int ID, wstring command) : + ID(ID), + command(command) + { + priority = CommandPriority::HIGH; + type = CommandType::RESET_TASK; + }; + virtual wstring getString(lua_State* L); + +private: + const int ID; + const wstring command; +}; + +/* Set option command */ +class SetOption : public Command +{ +public: + SetOption(int ID, int optionID, int optionValue) : + ID(ID), + optionID(optionID), + optionValue(optionValue) + { + priority = CommandPriority::HIGH; + type = CommandType::RESET_TASK; + }; + virtual wstring getString(lua_State* L); + +private: + const int ID; + const int optionID; + const int optionValue; +}; \ No newline at end of file diff --git a/src/core/include/Unit.h b/src/core/include/Unit.h index bbffb2a2..3b99897b 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, LAND, REFUEL, AWACS, EWR, TANKER, RUN_AWAY }; +}; class Unit { @@ -17,19 +18,22 @@ public: void updateMissionData(json::value json); json::value json(); + virtual void setState(int newState) { state = newState; }; + void resetTask(); + void setPath(list 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); + void setROE(wstring newROE); + void setReactionToThreat(wstring newReactionToThreat); + void landAt(Coords loc); int getID() { return ID; } wstring getName() { return name; } @@ -46,38 +50,53 @@ public: Coords getActiveDestination() { return activeDestination; } virtual wstring getCategory() { return L"No category"; }; wstring getTarget(); + bool isTargetAlive(); wstring getCurrentTask() { return currentTask; } + bool getAlive() { return alive; } + bool getIsLeader() { return isLeader; } + bool getIsWingman() { return isWingman; } + wstring getFormation() { return formation; } + virtual double getTargetSpeed() { return targetSpeed; }; virtual double getTargetAltitude() { return targetAltitude; }; + virtual void setTargetSpeed(double newSpeed) { targetSpeed = newSpeed; } + virtual void setTargetAltitude(double newAltitude) { targetAltitude = newAltitude; } + virtual void changeSpeed(wstring change) {}; + virtual void changeAltitude(wstring change) {}; + + void resetActiveDestination(); protected: int ID; - bool AI = false; - bool alive = true; - wstring name = L"undefined"; - wstring unitName = L"undefined"; - wstring groupName = L"undefined"; - json::value type = json::value::null(); - int country = NULL; - int coalitionID = NULL; - double latitude = NULL; - double longitude = NULL; - double altitude = NULL; - double heading = NULL; - double speed = NULL; - json::value flags = json::value::null(); - int targetID = NULL; - bool holding = false; - bool looping = false; - wstring taskOptions = L"{}"; - wstring currentTask = L""; - bool leader = false; - bool wingman = false; - wstring formation = L""; + int state = State::IDLE; + bool hasTask = false; + bool AI = false; + bool alive = true; + wstring name = L"undefined"; + wstring unitName = L"undefined"; + wstring groupName = L"undefined"; + json::value type = json::value::null(); + int country = NULL; + int coalitionID = NULL; + double latitude = NULL; + double longitude = NULL; + double altitude = NULL; + double heading = NULL; + double speed = NULL; + json::value flags = json::value::null(); + int targetID = NULL; + wstring currentTask = L""; + bool isLeader = false; + bool isWingman = false; + Offset formationOffset = Offset(NULL); + wstring formation = L""; + Unit* leader = nullptr; + wstring ROE = L""; + wstring reactionToThreat = L""; vector wingmen; - double targetSpeed = 0; - double targetAltitude = 0; - double fuel = 0; + double targetSpeed = 0; + double targetAltitude = 0; + double fuel = 0; json::value ammo; json::value targets; @@ -85,112 +104,16 @@ protected: Coords activeDestination = Coords(0); Coords oldPosition = Coords(0); // Used to approximate speed - virtual void AIloop(); + virtual void AIloop() = 0; private: mutex mutexLock; }; -class AirUnit : public Unit -{ -public: - AirUnit(json::value json, int ID); - virtual wstring getCategory() = 0; -protected: - virtual void AIloop(); -}; -class Aircraft : public AirUnit -{ -public: - Aircraft(json::value json, int ID); - virtual wstring getCategory() { return L"Aircraft"; }; - virtual void changeSpeed(wstring change); - virtual void changeAltitude(wstring change); - virtual double getTargetSpeed() { return targetSpeed; }; - virtual double getTargetAltitude() { return targetAltitude; }; -protected: - double targetSpeed = 150; - double targetAltitude = 5000; -}; - -class Helicopter : public AirUnit -{ -public: - Helicopter(json::value json, int ID); - - virtual wstring getCategory() { return L"Helicopter"; }; - - virtual void changeSpeed(wstring change); - virtual void changeAltitude(wstring change); - virtual double getTargetSpeed() { return targetSpeed; }; - virtual double getTargetAltitude() { return targetAltitude; }; - -protected: - double targetSpeed = 50; - double targetAltitude = 1000; -}; - -class GroundUnit : public Unit -{ -public: - GroundUnit(json::value json, int ID); - virtual void AIloop(); - - virtual wstring getCategory() { return L"GroundUnit"; }; - virtual void changeSpeed(wstring change); - virtual void changeAltitude(wstring change) {}; - virtual double getTargetSpeed() { return targetSpeed; }; - -protected: - double targetSpeed = 10; -}; - -class NavyUnit : public Unit -{ -public: - NavyUnit(json::value json, int ID); - virtual void AIloop(); - - virtual wstring getCategory() { return L"NavyUnit"; }; - virtual void changeSpeed(wstring change); - virtual void changeAltitude(wstring change) {}; - virtual double getTargetSpeed() { return targetSpeed; }; - -protected: - double targetSpeed = 10; -}; - -class Weapon : public Unit -{ -public: - Weapon(json::value json, int ID); - - virtual wstring getCategory() = 0; - -protected: - /* Weapons are not controllable and have no AIloop */ - virtual void AIloop() {}; -}; - -class Missile : public Weapon -{ -public: - Missile(json::value json, int ID); - - virtual wstring getCategory() { return L"Missile"; }; -}; - -class Bomb : public Weapon -{ -public: - Bomb(json::value json, int ID); - - virtual wstring getCategory() { return L"Bomb"; }; -}; diff --git a/src/core/include/aircraft.h b/src/core/include/aircraft.h new file mode 100644 index 00000000..6d15e4f6 --- /dev/null +++ b/src/core/include/aircraft.h @@ -0,0 +1,21 @@ +#pragma once +#include "airunit.h" + +class Aircraft : public AirUnit +{ +public: + Aircraft(json::value json, int ID); + + virtual wstring getCategory() { return L"Aircraft"; }; + + virtual void changeSpeed(wstring change); + virtual void changeAltitude(wstring change); + virtual double getTargetSpeed() { return targetSpeed; }; + virtual double getTargetAltitude() { return targetAltitude; }; + virtual void setTargetSpeed(double newTargetSpeed); + virtual void setTargetAltitude(double newTargetAltitude); + +protected: + double targetSpeed = 300 / 1.94384; + double targetAltitude = 20000 * 0.3048; +}; \ 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..550bbccf --- /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..50a0e9c2 --- /dev/null +++ b/src/core/include/helicopter.h @@ -0,0 +1,21 @@ +#pragma once +#include "airunit.h" + +class Helicopter : public AirUnit +{ +public: + Helicopter(json::value json, int ID); + + virtual wstring getCategory() { return L"Helicopter"; }; + + virtual void changeSpeed(wstring change); + virtual void changeAltitude(wstring change); + virtual double getTargetSpeed() { return targetSpeed; }; + virtual double getTargetAltitude() { return targetAltitude; }; + virtual void setTargetSpeed(double newTargetSpeed); + virtual void setTargetAltitude(double newTargetAltitude); + +protected: + double targetSpeed = 100 / 1.94384; + double targetAltitude = 5000 * 0.3048; +}; \ 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/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/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..ef5a7535 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,23 +65,71 @@ wstring SpawnAircraftCommand::getString(lua_State* L) } /* Clone unit command */ -wstring CloneCommand::getString(lua_State* L) +wstring Clone::getString(lua_State* L) { std::wostringstream commandSS; commandSS.precision(10); - commandSS << "Olympus.clone, " + commandSS << "Olympus.clone, " + << ID << ", " + << location.lat << ", " + << location.lng; + return commandSS.str(); +} + +/* Delete unit command */ +wstring Delete::getString(lua_State* L) +{ + std::wostringstream commandSS; + commandSS.precision(10); + commandSS << "Olympus.delete, " << ID; return commandSS.str(); } -/* Follow unit command */ -wstring FollowCommand::getString(lua_State* L) +/* Set task command */ +wstring SetTask::getString(lua_State* L) { std::wostringstream commandSS; commandSS.precision(10); - commandSS << "Olympus.follow, " - << leaderID << "," + commandSS << "Olympus.setTask, " + << ID << "," + << task; + + return commandSS.str(); +} + +/* Reset task command */ +wstring ResetTask::getString(lua_State* L) +{ + std::wostringstream commandSS; + commandSS.precision(10); + commandSS << "Olympus.resetTask, " << ID; + return commandSS.str(); +} + +/* Set command command */ +wstring SetCommand::getString(lua_State* L) +{ + std::wostringstream commandSS; + commandSS.precision(10); + commandSS << "Olympus.setCommand, " + << ID << "," + << command; + + return commandSS.str(); +} + +/* Set option command */ +wstring SetOption::getString(lua_State* L) +{ + std::wostringstream commandSS; + commandSS.precision(10); + commandSS << "Olympus.setOption, " + << ID << "," + << optionID << "," + << optionValue; + return commandSS.str(); } \ No newline at end of file diff --git a/src/core/src/Scheduler.cpp b/src/core/src/Scheduler.cpp index 12a7903a..254137e1 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,90 @@ void Scheduler::handleRequest(wstring key, json::value value) wstring targetName; if (unit != nullptr) - { unitName = unit->getUnitName(); - } else - { return; - } if (target != nullptr) - { targetName = target->getUnitName(); - } else - { return; - } log(L"Unit " + unitName + L" attacking unit " + targetName); unit->setTarget(targetID); + unit->setState(State::ATTACK); + } + else if (key.compare(L"stopAttack") == 0) + { + int ID = value[L"ID"].as_integer(); + Unit* unit = unitsFactory->getUnit(ID); + if (unit != nullptr) + unit->setState(State::REACH_DESTINATION); + else + return; } else if (key.compare(L"changeSpeed") == 0) { int ID = value[L"ID"].as_integer(); Unit* unit = unitsFactory->getUnit(ID); if (unit != nullptr) - { unit->changeSpeed(value[L"change"].as_string()); - } } else if (key.compare(L"changeAltitude") == 0) { int ID = value[L"ID"].as_integer(); Unit* unit = unitsFactory->getUnit(ID); if (unit != nullptr) - { unit->changeAltitude(value[L"change"].as_string()); - } + } + else if (key.compare(L"setSpeed") == 0) + { + int ID = value[L"ID"].as_integer(); + Unit* unit = unitsFactory->getUnit(ID); + if (unit != nullptr) + unit->setTargetSpeed(value[L"speed"].as_double()); + } + else if (key.compare(L"setAltitude") == 0) + { + int ID = value[L"ID"].as_integer(); + Unit* unit = unitsFactory->getUnit(ID); + if (unit != nullptr) + unit->setTargetAltitude(value[L"altitude"].as_double()); } else if (key.compare(L"cloneUnit") == 0) { int ID = value[L"ID"].as_integer(); - command = dynamic_cast(new CloneCommand(ID)); + double lat = value[L"location"][L"lat"].as_double(); + double lng = value[L"location"][L"lng"].as_double(); + Coords loc; loc.lat = lat; loc.lng = lng; + command = dynamic_cast(new Clone(ID, loc)); log(L"Cloning unit " + to_wstring(ID)); } else if (key.compare(L"setLeader") == 0) { int ID = value[L"ID"].as_integer(); Unit* unit = unitsFactory->getUnit(ID); - json::value wingmenIDs = value[L"wingmenIDs"]; - vector 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) @@ -204,6 +222,34 @@ void Scheduler::handleRequest(wstring key, json::value value) wstring formation = value[L"formation"].as_string(); unit->setFormation(formation); } + else if (key.compare(L"setROE") == 0) + { + int ID = value[L"ID"].as_integer(); + Unit* unit = unitsFactory->getUnit(ID); + wstring ROE = value[L"ROE"].as_string(); + unit->setROE(ROE); + } + else if (key.compare(L"setReactionToThreat") == 0) + { + int ID = value[L"ID"].as_integer(); + Unit* unit = unitsFactory->getUnit(ID); + wstring reactionToThreat = value[L"reactionToThreat"].as_string(); + unit->setReactionToThreat(reactionToThreat); + } + else if (key.compare(L"landAt") == 0) + { + int ID = value[L"ID"].as_integer(); + Unit* unit = unitsFactory->getUnit(ID); + double lat = value[L"location"][L"lat"].as_double(); + double lng = value[L"location"][L"lng"].as_double(); + Coords loc; loc.lat = lat; loc.lng = lng; + unit->landAt(loc); + } + else if (key.compare(L"deleteUnit") == 0) + { + int ID = value[L"ID"].as_integer(); + unitsFactory->deleteUnit(ID); + } else { log(L"Unknown command: " + key); diff --git a/src/core/src/Unit.cpp b/src/core/src/Unit.cpp index 927f594f..8f8f502d 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,24 @@ json::value Unit::json() json[L"flags"] = flags; json[L"category"] = json::value::string(getCategory()); json[L"currentTask"] = json::value::string(getCurrentTask()); - json[L"leader"] = leader; - json[L"wingman"] = wingman; + json[L"isLeader"] = isLeader; + json[L"isWingman"] = isWingman; json[L"formation"] = json::value::string(formation); json[L"fuel"] = fuel; json[L"ammo"] = ammo; json[L"targets"] = targets; + json[L"targetSpeed"] = getTargetSpeed(); + json[L"targetAltitude"] = getTargetAltitude(); + json[L"hasTask"] = hasTask; + json[L"ROE"] = json::value::string(ROE); + json[L"reactionToThreat"] = json::value::string(reactionToThreat); int i = 0; for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++) - { json[L"wingmenIDs"][i++] = (*itr)->getID(); - } + + if (leader != nullptr) + json[L"leaderID"] = leader->getID(); /* Send the active path as a json object */ if (activePath.size() > 0) { @@ -225,239 +149,122 @@ json::value Unit::json() return json; } -/* Air unit */ -AirUnit::AirUnit(json::value json, int ID) : Unit(json, ID) +void Unit::setPath(list 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(); - } + formationOffset = newFormationOffset; + resetTask(); } -void Aircraft::changeAltitude(wstring change) -{ - if (change.compare(L"descend") == 0) - { - targetAltitude *= 0.9; - } - else if (change.compare(L"climb") == 0) - { - targetAltitude *= 1.1; - } - resetActiveDestination(); +void Unit::setROE(wstring newROE) { + ROE = newROE; + int ROEEnum; + if (newROE.compare(L"Free") == 0) + ROEEnum = ROE::WEAPON_FREE; + else if (newROE.compare(L"Designated free") == 0) + ROEEnum = ROE::OPEN_FIRE_WEAPON_FREE; + else if (newROE.compare(L"Designated") == 0) + ROEEnum = ROE::OPEN_FIRE; + else if (newROE.compare(L"Return") == 0) + ROEEnum = ROE::RETURN_FIRE; + else if (newROE.compare(L"Hold") == 0) + ROEEnum = ROE::WEAPON_HOLD; + else + return; + Command* command = dynamic_cast(new SetOption(ID, SetCommandType::ROE, ROEEnum)); + scheduler->appendCommand(command); } -/* Helicopter */ -Helicopter::Helicopter(json::value json, int ID) : AirUnit(json, ID) -{ - log("New Helicopter created with ID: " + to_string(ID)); -}; - -void Helicopter::changeSpeed(wstring change) -{ - if (change.compare(L"stop") == 0) - { - /* Air units can't hold a position, so we can only set them to hold. At the moment, this will erase any other command. TODO: helicopters should be able to hover in place */ - activePath.clear(); - } - else if (change.compare(L"slow") == 0) - { - targetSpeed *= 0.9; - resetActiveDestination(); - } - else if (change.compare(L"fast") == 0) - { - targetSpeed *= 1.1; - resetActiveDestination(); - } +void Unit::setReactionToThreat(wstring newReactionToThreat) { + reactionToThreat = newReactionToThreat; + int reactionToThreatEnum; + if (newReactionToThreat.compare(L"None") == 0) + reactionToThreatEnum = ReactionToThreat::NO_REACTION; + else if (newReactionToThreat.compare(L"Passive") == 0) + reactionToThreatEnum = ReactionToThreat::PASSIVE_DEFENCE; + else if (newReactionToThreat.compare(L"Evade") == 0) + reactionToThreatEnum = ReactionToThreat::EVADE_FIRE; + else if (newReactionToThreat.compare(L"Escape") == 0) + reactionToThreatEnum = ReactionToThreat::BYPASS_AND_ESCAPE; + else if (newReactionToThreat.compare(L"Abort") == 0) + reactionToThreatEnum = ReactionToThreat::ALLOW_ABORT_MISSION; + else + return; + Command* command = dynamic_cast(new SetOption(ID, SetCommandType::REACTION_ON_THREAT, reactionToThreatEnum)); + scheduler->appendCommand(command); } -void Helicopter::changeAltitude(wstring change) -{ - if (change.compare(L"descend") == 0) - { - targetAltitude *= 0.9; - } - else if (change.compare(L"climb") == 0) - { - targetAltitude *= 1.1; - } - resetActiveDestination(); -} - - -/* Ground unit */ -GroundUnit::GroundUnit(json::value json, int ID) : Unit(json, ID) -{ - log("New Ground Unit created with ID: " + to_string(ID)); -}; - -void GroundUnit::AIloop() -{ - /* Call the common AI loop */ - Unit::AIloop(); - - /* Ground unit AI Loop */ - if (activeDestination != NULL) - { - double newDist = 0; - geod.Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, newDist); - if (newDist < GROUND_DEST_DIST_THR) - { - /* Destination reached */ - activePath.pop_front(); - log(unitName + L" destination reached"); - } - } -} - -void GroundUnit::changeSpeed(wstring change) -{ - if (change.compare(L"stop") == 0) - { - - } - else if (change.compare(L"slow") == 0) - { - - } - else if (change.compare(L"fast") == 0) - { - - } -} - -/* Navy Unit */ -NavyUnit::NavyUnit(json::value json, int ID) : Unit(json, ID) -{ - log("New Navy Unit created with ID: " + to_string(ID)); -}; - -void NavyUnit::AIloop() -{ - /* Call the common AI loop */ - Unit::AIloop(); - - /* Navy unit AI Loop */ - if (activeDestination != NULL) - { - double newDist = 0; - geod.Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, newDist); - if (newDist < GROUND_DEST_DIST_THR) - { - /* Destination reached */ - activePath.pop_front(); - log(unitName + L" destination reached"); - } - } -} - -void NavyUnit::changeSpeed(wstring change) -{ - if (change.compare(L"stop") == 0) - { - - } - else if (change.compare(L"slow") == 0) - { - - } - else if (change.compare(L"fast") == 0) - { - - } -} - -/* Weapon */ -Weapon::Weapon(json::value json, int ID) : Unit(json, ID) -{ - -}; - -/* Missile */ -Missile::Missile(json::value json, int ID) : Weapon(json, ID) -{ - log("New Missile created with ID: " + to_string(ID)); -}; - -/* Bomb */ -Bomb::Bomb(json::value json, int ID) : Weapon(json, ID) -{ - log("New Bomb created with ID: " + to_string(ID)); -}; +void Unit::landAt(Coords loc) { + activePath.clear(); + activePath.push_back(loc); + setState(State::LAND); +} \ 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..b50d8abf --- /dev/null +++ b/src/core/src/aircraft.cpp @@ -0,0 +1,68 @@ +#include "aircraft.h" +#include "utils.h" +#include "logger.h" +#include "commands.h" +#include "scheduler.h" +#include "defines.h" +#include "unitsFactory.h" + +#include +using namespace GeographicLib; + +extern Scheduler* scheduler; +extern UnitsFactory* unitsFactory; + +/* Aircraft */ +Aircraft::Aircraft(json::value json, int ID) : AirUnit(json, ID) +{ + log("New Aircraft created with ID: " + to_string(ID)); +}; + +void Aircraft::changeSpeed(wstring change) +{ + if (change.compare(L"stop") == 0) + { + setState(State::IDLE); + } + else if (change.compare(L"slow") == 0) + targetSpeed -= 25 / 1.94384; + else if (change.compare(L"fast") == 0) + targetSpeed += 25 / 1.94384; + + if (targetSpeed < 50 / 1.94384) + targetSpeed = 50 / 1.94384; + + goToDestination(); /* Send the command to reach the destination */ +} + +void Aircraft::changeAltitude(wstring change) +{ + if (change.compare(L"descend") == 0) + { + if (targetAltitude > 5000) + targetAltitude -= 2500 / 3.28084; + else if (targetAltitude > 0) + targetAltitude -= 500 / 3.28084; + } + else if (change.compare(L"climb") == 0) + { + if (targetAltitude > 5000) + targetAltitude += 2500 / 3.28084; + else if (targetAltitude >= 0) + targetAltitude += 500 / 3.28084; + } + if (targetAltitude < 0) + targetAltitude = 0; + + goToDestination(); /* Send the command to reach the destination */ +} + +void Aircraft::setTargetSpeed(double newTargetSpeed) { + targetSpeed = newTargetSpeed; + goToDestination(); +} + +void Aircraft::setTargetAltitude(double newTargetAltitude) { + targetAltitude = newTargetAltitude; + goToDestination(); +} \ 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..cb7ac8db --- /dev/null +++ b/src/core/src/airunit.cpp @@ -0,0 +1,344 @@ +#include "airunit.h" +#include "utils.h" +#include "logger.h" +#include "commands.h" +#include "scheduler.h" +#include "defines.h" +#include "unitsFactory.h" + +#include +using namespace GeographicLib; + +extern Scheduler* scheduler; +extern UnitsFactory* unitsFactory; + +/* Air unit */ +AirUnit::AirUnit(json::value json, int ID) : Unit(json, ID) +{ + +}; + +void AirUnit::setState(int newState) +{ + if (state != newState) + { + switch (state) { + case State::IDLE: { + break; + } + case State::REACH_DESTINATION: { + break; + } + case State::ATTACK: { + setTarget(NULL); + break; + } + case State::FOLLOW: { + break; + } + case State::WINGMAN: { + if (isWingman) + return; + break; + } + case State::LAND: { + break; + } + default: + break; + } + + switch (newState) { + case State::IDLE: { + resetActiveDestination(); + break; + } + case State::REACH_DESTINATION: { + resetActiveDestination(); + break; + } + case State::ATTACK: { + if (isTargetAlive()) { + Unit* target = unitsFactory->getUnit(targetID); + Coords targetPosition = Coords(target->getLatitude(), target->getLongitude(), 0); + activePath.clear(); + activePath.push_front(targetPosition); + resetActiveDestination(); + } + break; + } + case State::FOLLOW: { + resetActiveDestination(); + break; + } + case State::WINGMAN: { + resetActiveDestination(); + break; + } + case State::LAND: { + resetActiveDestination(); + break; + } + default: + break; + } + + resetTask(); + + log(unitName + L" setting state from " + to_wstring(state) + L" to " + to_wstring(newState)); + state = newState; + } +} + +bool AirUnit::isDestinationReached() +{ + if (activeDestination != NULL) + { + double dist = 0; + Geodesic::WGS84().Inverse(latitude, longitude, activeDestination.lat, activeDestination.lng, dist); + if (dist < AIR_DEST_DIST_THR) + { + log(unitName + L" destination reached"); + return true; + } + else { + return false; + } + } + else + return true; +} + +bool AirUnit::setActiveDestination() +{ + if (activePath.size() > 0) + { + activeDestination = activePath.front(); + log(unitName + L" active destination set to queue front"); + return true; + } + else + { + activeDestination = Coords(0); + log(unitName + L" active destination set to NULL"); + return false; + } +} + +void AirUnit::createHoldingPattern() +{ + /* Air units must ALWAYS have a destination or they will RTB and become uncontrollable */ + activePath.clear(); + Coords point1; + Coords point2; + Coords point3; + Geodesic::WGS84().Direct(latitude, longitude, 45, 10000, point1.lat, point1.lng); + Geodesic::WGS84().Direct(point1.lat, point1.lng, 135, 10000, point2.lat, point2.lng); + Geodesic::WGS84().Direct(point2.lat, point2.lng, 225, 10000, point3.lat, point3.lng); + activePath.push_back(point1); + activePath.push_back(point2); + activePath.push_back(point3); + activePath.push_back(Coords(latitude, longitude)); + log(unitName + L" holding pattern created"); +} + +bool AirUnit::updateActivePath(bool looping) +{ + if (activePath.size() > 0) + { + /* Push the next destination in the queue to the front */ + if (looping) + activePath.push_back(activePath.front()); + activePath.pop_front(); + log(unitName + L" active path front popped"); + return true; + } + else { + return false; + } +} + +void AirUnit::goToDestination(wstring enrouteTask) +{ + if (activeDestination != NULL) + { + Command* command = dynamic_cast(new Move(ID, activeDestination, getTargetSpeed(), getTargetAltitude(), getCategory(), enrouteTask)); + scheduler->appendCommand(command); + hasTask = true; + } + else + log(unitName + L" error, no active destination!"); +} + +void AirUnit::taskWingmen() +{ + switch (state) { + case State::IDLE: + case State::REACH_DESTINATION: + case State::ATTACK:{ + int idx = 1; + for (auto const& wingman : wingmen) + { + if (!wingman->getIsWingman()) + { + wingman->setIsWingman(true); + wingman->setLeader(this); + } + + if (wingman->getFormation().compare(formation) != 0) + { + wingman->resetTask(); + wingman->setFormation(formation); + if (formation.compare(L"Line abreast") == 0) + wingman->setFormationOffset(Offset(0 * idx, 0 * idx, 1852 * idx)); + idx++; + } + } + break; + } + default: + break; + } +} + +void AirUnit::AIloop() +{ + /* State machine */ + switch (state) { + case State::IDLE: { + wstring enrouteTask = L"nil"; + currentTask = L"Idle"; + + if (activeDestination == NULL || !hasTask) + { + createHoldingPattern(); + setActiveDestination(); + goToDestination(enrouteTask); + } + else { + if (isDestinationReached() && updateActivePath(true) && setActiveDestination()) + goToDestination(enrouteTask); + } + + if (isLeader) + taskWingmen(); + break; + } + case State::REACH_DESTINATION: { + wstring enrouteTask = L"nil"; + currentTask = L"Reaching destination"; + + if (activeDestination == NULL || !hasTask) + { + setActiveDestination(); + goToDestination(enrouteTask); + } + else { + if (isDestinationReached()) { + if (updateActivePath(false) && setActiveDestination()) + goToDestination(enrouteTask); + else { + setState(State::IDLE); + break; + } + } + } + + if (isLeader) + taskWingmen(); + + break; + } + + case State::LAND: { + wstring enrouteTask = L"{" "id = 'land' }"; + currentTask = L"Landing"; + + if (activeDestination == NULL) + { + setActiveDestination(); + goToDestination(enrouteTask); + } + + if (isLeader) + taskWingmen(); + + break; + } + case State::ATTACK: { + /* If the target is not alive (either not set or was succesfully destroyed) go back to REACH_DESTINATION */ + if (!isTargetAlive()) { + setState(State::REACH_DESTINATION); + break; + } + + /* Attack state is an "enroute" task, meaning the unit will keep trying to attack even if a new destination is set. This is useful to + manoeuvre the unit so that it can detect and engage the target. */ + std::wostringstream enrouteTaskSS; + enrouteTaskSS << "{" + << "id = 'EngageUnit'" << "," + << "targetID = " << targetID << "," + << "}"; + wstring enrouteTask = enrouteTaskSS.str(); + currentTask = L"Attacking " + getTarget(); + + if (activeDestination == NULL || !hasTask) + { + setActiveDestination(); + goToDestination(enrouteTask); + } + else { + if (isDestinationReached()) { + if (updateActivePath(false) && setActiveDestination()) + goToDestination(enrouteTask); + else { + setState(State::IDLE); + break; + } + } + } + + if (isLeader) + taskWingmen(); + + break; + } + case State::FOLLOW: { + /* TODO */ + setState(State::IDLE); + break; + } + case State::WINGMAN: { + /* In the WINGMAN state, the unit relinquishes control to the leader */ + activePath.clear(); + activeDestination = Coords(NULL); + if (leader == nullptr || !leader->getAlive()) + { + this->setFormation(L""); + this->setIsWingman(false); + break; + } + if (!hasTask) { + if (leader != nullptr && leader->getAlive() && formationOffset != NULL) + { + std::wostringstream taskSS; + taskSS << "{" + << "id = 'FollowUnit'" << ", " + << "leaderID = " << leader->getID() << "," + << "offset = {" + << "x = " << formationOffset.x << "," + << "y = " << formationOffset.y << "," + << "z = " << formationOffset.z + << "}," + << "}"; + Command* command = dynamic_cast(new SetTask(ID, taskSS.str())); + scheduler->appendCommand(command); + hasTask = true; + } + } + break; + } + default: + break; + } +} 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..7350e659 --- /dev/null +++ b/src/core/src/helicopter.cpp @@ -0,0 +1,69 @@ +#include "helicopter.h" +#include "utils.h" +#include "logger.h" +#include "commands.h" +#include "scheduler.h" +#include "defines.h" +#include "unitsFactory.h" + +#include +using namespace GeographicLib; + +extern Scheduler* scheduler; +extern UnitsFactory* unitsFactory; + +/* Helicopter */ +Helicopter::Helicopter(json::value json, int ID) : AirUnit(json, ID) +{ + log("New Helicopter created with ID: " + to_string(ID)); +}; + +void Helicopter::changeSpeed(wstring change) +{ + if (change.compare(L"stop") == 0) + { + /* Air units can't hold a position, so we can only set them to hold. At the moment, this will erase any other command. TODO: helicopters should be able to hover in place */ + activePath.clear(); + } + else if (change.compare(L"slow") == 0) + targetSpeed -= 10 / 1.94384; + else if (change.compare(L"fast") == 0) + targetSpeed += 10 / 1.94384; + if (targetSpeed < 0) + targetSpeed = 0; + + goToDestination(); /* Send the command to reach the destination */ +} + +void Helicopter::changeAltitude(wstring change) +{ + if (change.compare(L"descend") == 0) + { + if (targetAltitude > 100) + targetAltitude -= 100 / 3.28084; + else if (targetAltitude > 0) + targetAltitude -= 10 / 3.28084; + } + else if (change.compare(L"climb") == 0) + { + if (targetAltitude > 100) + targetAltitude += 100 / 3.28084; + else if (targetAltitude >= 0) + targetAltitude += 10 / 3.28084; + } + if (targetAltitude < 0) + targetAltitude = 0; + + goToDestination(); /* Send the command to reach the destination */ +} + + +void Helicopter::setTargetSpeed(double newTargetSpeed) { + targetSpeed = newTargetSpeed; + goToDestination(); +} + +void Helicopter::setTargetAltitude(double newTargetAltitude) { + targetAltitude = newTargetAltitude; + goToDestination(); +} \ No newline at end of file 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..6a3104bf 100644 --- a/src/core/src/unitsFactory.cpp +++ b/src/core/src/unitsFactory.cpp @@ -2,9 +2,15 @@ #include "unitsFactory.h" #include "logger.h" #include "unit.h" -#include "utils.h" - +#include "aircraft.h" +#include "helicopter.h" +#include "groundunit.h" +#include "navyunit.h" +#include "weapon.h" +#include "commands.h" +#include "scheduler.h" +extern Scheduler* scheduler; UnitsFactory::UnitsFactory(lua_State* L) { @@ -114,3 +120,12 @@ void UnitsFactory::updateAnswer(json::value& answer) answer[L"units"] = unitsJson; } +void UnitsFactory::deleteUnit(int ID) +{ + if (getUnit(ID) != nullptr) + { + Command* command = dynamic_cast(new Delete(ID)); + scheduler->appendCommand(command); + } +} + 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); }