mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
394 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41445c3092 | ||
|
|
2a3bf9821b | ||
|
|
819d775282 | ||
|
|
efbc6fe3ae | ||
|
|
262ba6c113 | ||
|
|
c41ecb6735 | ||
|
|
f5c32c6b98 | ||
|
|
a1886e37f8 | ||
|
|
7dd3367203 | ||
|
|
e72c82521a | ||
|
|
aca415db23 | ||
|
|
98b2d8b3b9 | ||
|
|
f8ef5db5a3 | ||
|
|
a44cbe5972 | ||
|
|
2066a2e9bc | ||
|
|
f381bf85a4 | ||
|
|
44dcdcc8bb | ||
|
|
01220800f3 | ||
|
|
8ecb4cdcf4 | ||
|
|
8402d108c0 | ||
|
|
75af2d468e | ||
|
|
72ce37f008 | ||
|
|
e48e884286 | ||
|
|
f9d5c1f8de | ||
|
|
0873dcab0a | ||
|
|
a1343c2849 | ||
|
|
f732cc54d0 | ||
|
|
473a7d5fa4 | ||
|
|
8054a0b62f | ||
|
|
7f9cba5d37 | ||
|
|
f68e6387e6 | ||
|
|
f032001bee | ||
|
|
3bae591c04 | ||
|
|
18a1f0af94 | ||
|
|
afbd4a4716 | ||
|
|
a98da14c6f | ||
|
|
a2a70213a7 | ||
|
|
8b0f877041 | ||
|
|
bf7ad4cad2 | ||
|
|
66b659c0af | ||
|
|
8709ea948f | ||
|
|
7236c10403 | ||
|
|
dde703ec41 | ||
|
|
aa2e9b123c | ||
|
|
737e04d09e | ||
|
|
0fe59efd72 | ||
|
|
ddb50e6254 | ||
|
|
72e6ae4186 | ||
|
|
4d510f643a | ||
|
|
66f607b5e6 | ||
|
|
1a125c62e7 | ||
|
|
84da44a27b | ||
|
|
5e35efcaef | ||
|
|
83f221c5e3 | ||
|
|
e86a10e9ba | ||
|
|
7eea328706 | ||
|
|
aa9dcec0ad | ||
|
|
e9bad2c7eb | ||
|
|
ce257a31bb | ||
|
|
c96b5cf4d7 | ||
|
|
fb40e9273d | ||
|
|
42c9af102b | ||
|
|
b7dff59542 | ||
|
|
266927aa9a | ||
|
|
dcaa8d4e96 | ||
|
|
0e2a449553 | ||
|
|
a70e035e02 | ||
|
|
4019da8ba9 | ||
|
|
5042ac1789 | ||
|
|
4031c9b978 | ||
|
|
65dd9bc286 | ||
|
|
e210dcb4df | ||
|
|
52a379229e | ||
|
|
93b2e91c10 | ||
|
|
b8afb01c46 | ||
|
|
59e7665b65 | ||
|
|
8a8a835a32 | ||
|
|
6ceab69656 | ||
|
|
9e98f05be0 | ||
|
|
5da1db91fd | ||
|
|
f0d58acd62 | ||
|
|
722ec00076 | ||
|
|
2db740e1ad | ||
|
|
70cd0e8c31 | ||
|
|
848c92ec25 | ||
|
|
98946d0a63 | ||
|
|
e0a39104b1 | ||
|
|
a4f66298a4 | ||
|
|
993bf50012 | ||
|
|
51bfc9a59b | ||
|
|
837795e87a | ||
|
|
d4820b2435 | ||
|
|
180537cd48 | ||
|
|
8bc77bbf18 | ||
|
|
474b606524 | ||
|
|
8a7e43ef42 | ||
|
|
7b5b486f0e | ||
|
|
ebedc02a0a | ||
|
|
ad42a3d956 | ||
|
|
98d75bc721 | ||
|
|
14fe68eb54 | ||
|
|
b6938c14ca | ||
|
|
3c96c1d5b1 | ||
|
|
2969ce2d56 | ||
|
|
bca92f5ee7 | ||
|
|
1c1982952e | ||
|
|
4b74b5a13d | ||
|
|
0fc00fac38 | ||
|
|
4446a7f060 | ||
|
|
9d31c478d3 | ||
|
|
b31e186d1d | ||
|
|
d02a3a0d3f | ||
|
|
a9e65cc83d | ||
|
|
010d505f04 | ||
|
|
95b9a3e1aa | ||
|
|
d051859371 | ||
|
|
af596c58c3 | ||
|
|
b4e3067718 | ||
|
|
40f8ae1cde | ||
|
|
f5f45a098e | ||
|
|
d429804c1f | ||
|
|
f5f770f401 | ||
|
|
91d6ed0ee7 | ||
|
|
06858b26c7 | ||
|
|
7e60a43f53 | ||
|
|
e7e82dcd0b | ||
|
|
66af6be063 | ||
|
|
69e1d2779d | ||
|
|
001752a81e | ||
|
|
1000041bce | ||
|
|
d50e791c30 | ||
|
|
7817d59989 | ||
|
|
139c4c1dd8 | ||
|
|
75bb6941d3 | ||
|
|
e92fb38271 | ||
|
|
e4eeef8f99 | ||
|
|
21c355bc9f | ||
|
|
04c878f57c | ||
|
|
ef23ce58d1 | ||
|
|
d213fa1b91 | ||
|
|
4b2804427e | ||
|
|
d707a59a71 | ||
|
|
923358b364 | ||
|
|
1d0c0ac19c | ||
|
|
7c97ecddac | ||
|
|
bcae51cc92 | ||
|
|
18896a69cf | ||
|
|
4c310d268d | ||
|
|
70babd9c32 | ||
|
|
15d0edce2e | ||
|
|
d7ab1774ac | ||
|
|
42d9607b0d | ||
|
|
bf82474fd7 | ||
|
|
64211275cc | ||
|
|
cf8060f7ff | ||
|
|
cee1d01d9a | ||
|
|
536d763a8e | ||
|
|
6d27b6ce41 | ||
|
|
0d05655e94 | ||
|
|
9cf697d72c | ||
|
|
f0f818c524 | ||
|
|
d2a3448819 | ||
|
|
1a25b15bce | ||
|
|
83808a63ed | ||
|
|
515d1d0dee | ||
|
|
2d47df96b6 | ||
|
|
ad4d71316f | ||
|
|
40932c9e84 | ||
|
|
59bee5f23e | ||
|
|
e6f00febea | ||
|
|
6ea694bc7e | ||
|
|
8f7a8edde4 | ||
|
|
11c7107152 | ||
|
|
d18a9f376f | ||
|
|
6ba95d8c70 | ||
|
|
1c00418d64 | ||
|
|
602e7fb530 | ||
|
|
e6a0a1d4a4 | ||
|
|
136cf143bd | ||
|
|
5afa2a23f6 | ||
|
|
513c81b508 | ||
|
|
45af66e000 | ||
|
|
b38f6c39e4 | ||
|
|
036874fbf0 | ||
|
|
9f3b49a7f5 | ||
|
|
9c35156db9 | ||
|
|
ab2edc6e59 | ||
|
|
9af278217c | ||
|
|
13dbc9c0fe | ||
|
|
14f7fbe794 | ||
|
|
a60e6aa860 | ||
|
|
220e72322c | ||
|
|
1a100dc54b | ||
|
|
04b95c986d | ||
|
|
8f6bb90945 | ||
|
|
50e78bd069 | ||
|
|
ce60e1a8f8 | ||
|
|
fb7ed19f7a | ||
|
|
970888b5ca | ||
|
|
8c934654ba | ||
|
|
ad3dc70cfd | ||
|
|
ae5949bc7f | ||
|
|
c7c563e68c | ||
|
|
3dac0af6c4 | ||
|
|
f9ce6966bb | ||
|
|
f61e23153b | ||
|
|
ff4f008a63 | ||
|
|
cbcf8a0a90 | ||
|
|
647e62059f | ||
|
|
6e3ef24e3a | ||
|
|
2559b27a6f | ||
|
|
421e2508d4 | ||
|
|
c9f8a93813 | ||
|
|
126849cf9a | ||
|
|
c08768f648 | ||
|
|
2c07257bf6 | ||
|
|
f797bbb97f | ||
|
|
a7f3b6e0dc | ||
|
|
60732c33c0 | ||
|
|
65b77e241f | ||
|
|
a167b95cec | ||
|
|
283cfd1ce9 | ||
|
|
e16db60d0f | ||
|
|
6a3b5bbe1d | ||
|
|
339c3f506c | ||
|
|
9fade70092 | ||
|
|
e18d84ae5e | ||
|
|
fa76e31640 | ||
|
|
2fd4fa25f7 | ||
|
|
c27d8e3b16 | ||
|
|
0841c52a75 | ||
|
|
669bff13c7 | ||
|
|
01ea4fa7a6 | ||
|
|
ef024b5118 | ||
|
|
a96a107ef9 | ||
|
|
8d3ab2be5d | ||
|
|
6ed407f656 | ||
|
|
6b3625f0ea | ||
|
|
db8e7f0474 | ||
|
|
d6398630df | ||
|
|
464bfccfb6 | ||
|
|
aa16da837d | ||
|
|
ce4478803a | ||
|
|
114239fe8e | ||
|
|
67d96061da | ||
|
|
2d5ad16399 | ||
|
|
4c7f79c6f8 | ||
|
|
2a50768db1 | ||
|
|
bff33e6992 | ||
|
|
448057a0b9 | ||
|
|
0a47669b14 | ||
|
|
b5893ce521 | ||
|
|
23537211b0 | ||
|
|
39f25db439 | ||
|
|
a551067c72 | ||
|
|
23f4df766c | ||
|
|
7354a34f1a | ||
|
|
2de48b3918 | ||
|
|
e823a7a7b0 | ||
|
|
ffc1f9d48e | ||
|
|
bb19bfb925 | ||
|
|
b3a2464249 | ||
|
|
09718e73a3 | ||
|
|
d5f20377ea | ||
|
|
f703d620c2 | ||
|
|
5ac680b37d | ||
|
|
9bf57e99fb | ||
|
|
b787f7cb11 | ||
|
|
7b35965dbf | ||
|
|
1618c1e677 | ||
|
|
0a5ce108b7 | ||
|
|
99c180441d | ||
|
|
79a7b0557c | ||
|
|
91cf192d2e | ||
|
|
66d7435ed6 | ||
|
|
19ea75b281 | ||
|
|
f0350b7045 | ||
|
|
9d4d3d0523 | ||
|
|
a76962e206 | ||
|
|
4060039440 | ||
|
|
c3a3a428a4 | ||
|
|
171e23bd09 | ||
|
|
6f4b7e0f1a | ||
|
|
ccf2cd3425 | ||
|
|
6ee444efd7 | ||
|
|
3e5be909a2 | ||
|
|
dac78f8f09 | ||
|
|
6b91e1b03c | ||
|
|
772295fc04 | ||
|
|
a4e93276b8 | ||
|
|
456a82acaa | ||
|
|
f2fb2cb363 | ||
|
|
2a7e5eecd7 | ||
|
|
79502f56a0 | ||
|
|
12dacfe2d2 | ||
|
|
d46337d694 | ||
|
|
f897cf745f | ||
|
|
cfa4f7da2e | ||
|
|
b21272cfab | ||
|
|
8ce0520101 | ||
|
|
4c17e1fd33 | ||
|
|
d5fb1f62f5 | ||
|
|
b34ede3795 | ||
|
|
4989d84693 | ||
|
|
2f210ab59f | ||
|
|
3ffea901f1 | ||
|
|
5e2e6520ce | ||
|
|
a8679c8eef | ||
|
|
469e842e96 | ||
|
|
cd945c625f | ||
|
|
1346192b75 | ||
|
|
45bebdd94e | ||
|
|
d0bbb025d3 | ||
|
|
8d0c53ef69 | ||
|
|
a59c2dfb10 | ||
|
|
9c7689f9b5 | ||
|
|
c580979fee | ||
|
|
e8e7bc95ea | ||
|
|
839e3e0833 | ||
|
|
4f451fab2f | ||
|
|
7d0413f41d | ||
|
|
bbac78195d | ||
|
|
20fef86b84 | ||
|
|
9581a8f1f4 | ||
|
|
58b4c36b6c | ||
|
|
15aaa5d9a1 | ||
|
|
b38332d061 | ||
|
|
4cfbbb6756 | ||
|
|
dec7db9e69 | ||
|
|
274be3bcfc | ||
|
|
d8a668ce60 | ||
|
|
a845ed1998 | ||
|
|
53bd147de2 | ||
|
|
4248b518a2 | ||
|
|
8f65a88d8b | ||
|
|
4d4a640f34 | ||
|
|
af9ead5937 | ||
|
|
ae3518f450 | ||
|
|
04e77a97f2 | ||
|
|
0ff3ce98e0 | ||
|
|
df2659787c | ||
|
|
d994673e91 | ||
|
|
dce781ef0e | ||
|
|
b0e2c73024 | ||
|
|
bda28f81cc | ||
|
|
d8dc3d48b1 | ||
|
|
5fdfa6339d | ||
|
|
9a799190ef | ||
|
|
8d32ba5b8e | ||
|
|
85a7f89ba9 | ||
|
|
17f5378326 | ||
|
|
9d67741310 | ||
|
|
ac6a106c6a | ||
|
|
d775b8baa0 | ||
|
|
2dc1b3ec43 | ||
|
|
d3d5160861 | ||
|
|
cdf8c3b6e5 | ||
|
|
29b1bbab9d | ||
|
|
c5d055c19b | ||
|
|
8f7b51a3df | ||
|
|
44e8cc810f | ||
|
|
b994878465 | ||
|
|
8d5d703cbe | ||
|
|
1c1936d8f8 | ||
|
|
7890fd5cc7 | ||
|
|
4bdf11eb79 | ||
|
|
81f4c7303f | ||
|
|
0641907ea0 | ||
|
|
ff0f32fcf5 | ||
|
|
768049ca36 | ||
|
|
88c8fe7cca | ||
|
|
5e7facfeb6 | ||
|
|
6e43467ef6 | ||
|
|
cd4ef8ae32 | ||
|
|
93a4463f22 | ||
|
|
56cf6bdaa4 | ||
|
|
fe02df27a2 | ||
|
|
a60ab68287 | ||
|
|
83e46ddc97 | ||
|
|
9c89ad7c72 | ||
|
|
0518abdedc | ||
|
|
93bcd07c7f | ||
|
|
5f1f4f8d81 | ||
|
|
e78120d9c6 | ||
|
|
cc5986d435 | ||
|
|
5192306b06 | ||
|
|
826935eb7d | ||
|
|
cd41bcf45c | ||
|
|
601375d06f | ||
|
|
c708abafc8 | ||
|
|
90d588353c | ||
|
|
2d4287df2a | ||
|
|
fde3a988b7 | ||
|
|
397164e667 |
39
.github/workflows/build.yml
vendored
Normal file
39
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Install environment
|
||||
run: |
|
||||
py -m venv ./venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
pip install -r requirements.txt
|
||||
# For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead
|
||||
Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force
|
||||
|
||||
- name: Build binaries
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
$env:PYTHONPATH=".;./pydcs"
|
||||
pyinstaller pyinstaller.spec
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dcs_liberation
|
||||
path: dist/
|
||||
107
.github/workflows/release.yml
vendored
Normal file
107
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
name: Release Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [ '*' ]
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Set up Python 3.8
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.8
|
||||
|
||||
- name: Install environment
|
||||
run: |
|
||||
py -m venv ./venv
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
pip install -r requirements.txt
|
||||
# For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead
|
||||
Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force
|
||||
|
||||
- name: Build binaries
|
||||
run: |
|
||||
./venv/scripts/activate
|
||||
$env:PYTHONPATH=".;./pydcs"
|
||||
pyinstaller pyinstaller.spec
|
||||
|
||||
- name: Create Installer
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
run: |
|
||||
$version = ($env:TAG_NAME -split "/") | Select-Object -Last 1
|
||||
(Get-Content .\installer\dcs_liberation.iss) -replace "{{version}}",$version | Out-File .\build\installer.iss
|
||||
cd .\installer
|
||||
iscc.exe ..\build\installer.iss
|
||||
cd ..
|
||||
Copy-Item .\changelog.md .\dist
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: dcs_liberation
|
||||
path: dist/
|
||||
|
||||
release:
|
||||
needs: [ build ]
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: dcs_liberation
|
||||
|
||||
- name: "Get Version"
|
||||
id: version
|
||||
env:
|
||||
TAG_NAME: ${{ github.ref }}
|
||||
run: |
|
||||
Get-ChildItem -Recurse -Depth 1
|
||||
$version = ($env:TAG_NAME -split "/") | Select-Object -Last 1
|
||||
$prerelease = ("2.1.1-alpha3" -match '[^\.\d]').ToString().ToLower()
|
||||
Write-Host $version
|
||||
Write-Host $prerelease
|
||||
Write-Output "::set-output name=number::$version"
|
||||
Write-Output "::set-output name=prerelease::$prerelease"
|
||||
$changelog = Get-Content .\changelog.md
|
||||
$last_change = ($changelog | Select-String -Pattern "^#\s" | Select-Object -Skip 1 -First 1).LineNumber - 2
|
||||
($changelog | Select-Object -First $last_change) -join "`n" | Out-File .\releasenotes.md
|
||||
Compress-Archive -Path .\dcs_liberation -DestinationPath "dcs_liberation.$version.zip" -Compression Optimal
|
||||
|
||||
- uses: actions/create-release@v1
|
||||
id: create_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: ${{ github.ref }}
|
||||
release_name: ${{ github.ref }}
|
||||
body_path: releasenotes.md
|
||||
draft: false
|
||||
prerelease: ${{ steps.version.outputs.prerelease }}
|
||||
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dcs_liberation.exe
|
||||
asset_name: dcs_liberation.${{ steps.version.outputs.number }}.exe
|
||||
asset_content_type: application/exe
|
||||
|
||||
- uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: ./dcs_liberation.${{ steps.version.outputs.number }}.zip
|
||||
asset_name: dcs_liberation.${{ steps.version.outputs.number }}.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -11,3 +11,14 @@ resources/tools/a.miz
|
||||
tests/**
|
||||
# User-specific stuff
|
||||
.idea/
|
||||
|
||||
/kneeboards
|
||||
/liberation_preferences.json
|
||||
/state.json
|
||||
|
||||
logs/liberation.log
|
||||
|
||||
qt_ui/logs/liberation.log
|
||||
|
||||
*.psd
|
||||
resources/scripts/plugins/*
|
||||
|
||||
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[submodule "pydcs"]
|
||||
path = pydcs
|
||||
url = https://github.com/pydcs/dcs
|
||||
branch = master
|
||||
30
.vscode/launch.json
vendored
Normal file
30
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Main",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "qt_ui\\main.py",
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs"
|
||||
},
|
||||
"preLaunchTask": "Prepare Environment"
|
||||
},
|
||||
{
|
||||
"name": "Python: Make Release",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "resources\\tools\\mkrelease.py",
|
||||
"console": "integratedTerminal",
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs"
|
||||
},
|
||||
"preLaunchTask": "Prepare Environment"
|
||||
}
|
||||
]
|
||||
}
|
||||
4
.vscode/settings.json
vendored
Normal file
4
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"python.pythonPath": "g:\\python\\dcs_liberation\\venv\\Scripts\\python.exe",
|
||||
"vsintellicode.python.completionsEnabled": true
|
||||
}
|
||||
35
.vscode/tasks.json
vendored
Normal file
35
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||
// for the documentation about the tasks.json format
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Prepare Environment",
|
||||
"type": "shell",
|
||||
"isBackground": false,
|
||||
"problemMatcher": [],
|
||||
"command": "powershell",
|
||||
"args": [
|
||||
"-Command",
|
||||
"& {if (-not (Test-Path ${workspaceFolder}\\venv)) { python -m venv ${workspaceFolder}\\venv; . ${workspaceFolder}\\venv\\scripts\\activate.ps1; pip install -r requirements.txt; Copy-Item ${workspaceFolder}\\venv\\Lib\\site-packages\\shiboken2\\shiboken2.abi3.dll ${workspaceFolder}\\venv\\Lib\\site-packages\\PySide2 } }",
|
||||
],
|
||||
"group": "build",
|
||||
"options": {
|
||||
"env": {
|
||||
"PYTHONPATH": ".;./pydcs"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "never",
|
||||
"focus": false,
|
||||
"panel": "shared",
|
||||
"showReuseMessage": true,
|
||||
"clear": false
|
||||
},
|
||||
"runOptions": {
|
||||
"runOn": "folderOpen"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
34
README.md
34
README.md
@@ -1,12 +1,36 @@
|
||||

|
||||
|
||||
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player semi dynamic campaign.
|
||||
[](https://www.paypal.com/paypalme/KhopaDCSL)
|
||||
[](https://patreon.com/khopa)
|
||||
|
||||
DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation
|
||||
and [Mist](https://github.com/mrSkortch/MissionScriptingTools) for mission scripting
|
||||
[](https://github.com/Khopa/dcs_liberation/releases)
|
||||
|
||||
[](https://discord.gg/bKrtrkJ)
|
||||
|
||||
[](https://github.com/Khopa/dcs_liberation)
|
||||
[](https://github.com/Khopa/dcs_liberation/issues)
|
||||

|
||||
|
||||
## About DCS Liberation
|
||||
DCS Liberation is a [DCS World](https://www.digitalcombatsimulator.com/en/products/world/) turn based single-player semi dynamic campaign.
|
||||
It is an external program that generates full and complex DCS missions and manage a persistent combat environment.
|
||||
|
||||

|
||||
|
||||
## Downloads
|
||||
|
||||
Latest release is available here : https://github.com/Khopa/dcs_liberation/releases
|
||||
|
||||
## Resources
|
||||
|
||||
* [Getting Started](https://github.com/Khopa/dcs_liberation/wiki/Getting-started)
|
||||
Tutorials, contributors and developer's guides are available in the project's [Wiki](https://github.com/Khopa/dcs_liberation/wiki/)
|
||||
|
||||
* [Tutorials](https://github.com/Khopa/dcs_liberation/wiki/Tutorial-01-:-UI)
|
||||
## Special Thanks
|
||||
|
||||
First, a big thanks to shdwp, for starting the original DCS Liberation project.
|
||||
|
||||
Then, DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation, and nothing would be possible without this.
|
||||
It also uses the popular [Mist](https://github.com/mrSkortch/MissionScriptingTools) lua framework for mission scripting.
|
||||
And for the JTAC feature, DCS Liberation embed Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze).
|
||||
|
||||
Please also show some support to these projects !
|
||||
|
||||
64
__init__.py
64
__init__.py
@@ -1,64 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import dcs
|
||||
|
||||
import ui.corruptedsavemenu
|
||||
import ui.mainmenu
|
||||
import ui.newgamemenu
|
||||
import ui.window
|
||||
from game.game import Game
|
||||
from userdata import persistency, logging as logging_module
|
||||
|
||||
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
||||
|
||||
persistency.setup(sys.argv[1])
|
||||
dcs.planes.FlyingType.payload_dirs = [os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")]
|
||||
|
||||
VERSION_STRING = sys.argv[2]
|
||||
logging_module.setup_version_string(VERSION_STRING)
|
||||
logging.info("Using {} as userdata folder".format(persistency.base_path()))
|
||||
|
||||
|
||||
def proceed_to_main_menu(game: Game):
|
||||
m = ui.mainmenu.MainMenu(w, None, game)
|
||||
m.display()
|
||||
|
||||
|
||||
def is_version_compatible(save_version):
|
||||
current_version_components = re.split(r"[\._]", VERSION_STRING)
|
||||
save_version_components = re.split(r"[\._]", save_version)
|
||||
|
||||
if "--ignore-save" in sys.argv:
|
||||
return False
|
||||
|
||||
if current_version_components == save_version_components:
|
||||
return True
|
||||
|
||||
if save_version in ["1.4_rc1", "1.4_rc2", "1.4_rc3", "1.4_rc4", "1.4_rc5", "1.4_rc6"]:
|
||||
return False
|
||||
|
||||
if current_version_components[:2] == save_version_components[:2]:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
w = ui.window.Window()
|
||||
|
||||
try:
|
||||
game = persistency.restore_game()
|
||||
if not game or not is_version_compatible(game.settings.version):
|
||||
ui.newgamemenu.NewGameMenu(w, w.start_new_game).display()
|
||||
else:
|
||||
game.settings.version = VERSION_STRING
|
||||
proceed_to_main_menu(game)
|
||||
except Exception as e:
|
||||
logging.exception(e)
|
||||
ui.corruptedsavemenu.CorruptedSaveMenu(w).display()
|
||||
|
||||
w.run()
|
||||
|
||||
258
changelog.md
258
changelog.md
@@ -1,39 +1,261 @@
|
||||
#2.0 RC 7
|
||||
# 2.1.5
|
||||
|
||||
## Features/Improvements :
|
||||
* **[Units/Factions]** Enabled EPLRS for ground units that supports it (so they appear on A-10C II TAD and Helmet)
|
||||
|
||||
## Fixes :
|
||||
* **[UI]** Fixed an issue that prevent saving after aborting a mission
|
||||
* **[Mission Generator]** Fixed aircraft landing point type being wrong
|
||||
|
||||
# 2.1.4
|
||||
|
||||
## Fixes :
|
||||
* **[UI]** Fixed an issue that prevent generating the mission (take off button no working) on old savegames.
|
||||
|
||||
# 2.1.3
|
||||
|
||||
## Features/Improvements :
|
||||
* **[Units/Factions]** Added A-10C_2 to USA 2005 and Bluefor modern factions
|
||||
* **[UI]** Limit number of aircraft that can be bought to the number of available parking slots.
|
||||
* **[Mission Generator]** Use inline loading of the JSON.lua library, and save to either %LIBERATION_EXPORT_DIR%, or to DCS working directory
|
||||
|
||||
## Changes :
|
||||
* **[Units/Factions]** Bluefor generic factions will now use the new "Combined Joint Task Forces Blue" country in the generated mission instead of "USA"
|
||||
|
||||
## Fixes :
|
||||
* **[UI]** Fixed icon for Viggen
|
||||
* **[UI]** Added icons for some ground units
|
||||
* **[Misc]** Fixed issue with Chinese characters in pydcs preventing generating the mission. (Take Off button not working) (thanks to spark135246)
|
||||
* **[Misc]** Fixed an error causing with ATC frequency preventing generating the mission. (Take Off button not working) (thanks to danalbert)
|
||||
|
||||
# 2.1.2
|
||||
|
||||
## Fixes :
|
||||
* **[Mission Generator]** Fix mission generation issues with radio frequencies (Thanks to contributors davidp57 and danalbert)
|
||||
* **[Mission Generator]** AI should now properly plan flights for Tornados
|
||||
|
||||
# 2.1.1
|
||||
|
||||
## Features/Improvements :
|
||||
* **[Other]** Added an installer option (thanks to contributor parithon)
|
||||
* **[Kneeboards]** Generate mission kneeboards for player flights. Kneeboards include
|
||||
airfield/carrier information (ATC frequencies, ILS, TACAN, and runway
|
||||
assignments), assigned radio channels, waypoint lists, and AWACS/JTAC/tanker
|
||||
information. (Thanks to contributor danalbert)
|
||||
* **[Radios]** Allocate separate intra-flight channels for most aircraft to reduce global
|
||||
chatter. (Thanks to contributor danalbert)
|
||||
* **[Radios]** Configure radio channel presets for most aircraft. Currently supported are:
|
||||
* AJS37
|
||||
* AV-8B
|
||||
* F-14B
|
||||
* F-16C
|
||||
* F/A-18C
|
||||
* JF-17
|
||||
* M-2000C (Thanks to contributor danalbert)
|
||||
* **[Base Menu]** Added possibility to repair destroyed SAM and base defenses units for the player (Click on a SAM site to fix it)
|
||||
* **[Base Menu]** Added possibility to buy/sell/replace SAM units
|
||||
* **[Map]** Added recon images for buildings on strike targets, click on a Strike target to get detailled informations
|
||||
* **[Units/Factions]** Added F-16C to USA 1990
|
||||
* **[Units/Factions]** Added MQ-9 Reaper as CAS unit for USA 2005
|
||||
* **[Units/Factions]** Added Mig-21, Mig-23, SA-342L to Syria 2011
|
||||
* **[Cheat Menu]** Added buttons to remove money
|
||||
|
||||
## Fixed issues :
|
||||
* **[UI/UX]** Spelling issues (Thanks to contributor steveveepee)
|
||||
* **[Campaign Generator]** LHA was placed on land in Syrian Civil War campaign
|
||||
* **[Campaign Generator]** Fixed inverted configuration for Syria full map
|
||||
* **[Campaign Generator]** Syria "Inherent Resolve" campaign, added Incirlik Air Base
|
||||
* **[Mission Generator]** AH-1W was not used by AI to generate CAS mission by default
|
||||
* **[Mission Generator]** Fixed F-16C targeting pod not being added to payload
|
||||
* **[Mission Generator]** AH-64A and AH-64D payloads fix.
|
||||
* **[Units/Factions]** China will use KJ-2000 as awacs instead of A-50
|
||||
|
||||
# 2.1.0
|
||||
|
||||
## Features/Improvements :
|
||||
|
||||
* **[Campaign Generator]** Added Syria map
|
||||
* **[Campaign Generator]** Added 5 campaigns for the Syria map
|
||||
* **[Campaign Generator]** Added 2 small scale campaign for Persian Gulf map
|
||||
* **[Units/Factions]** Added factions for Syria map : Syria 2011, Arab Armies 1982, 1973, 1968, 1948, Israel 1982, 1973, 1948
|
||||
* **[Base Menu]** Budget is visible in recruitment menu. (Thanks to Github contributor root0fall)
|
||||
* **[Misc]** Added error message in mission when the state file can not be written
|
||||
* **[Units/Factions]** China, Pakistan, UAE will now use the new WingLoong drone as JTAC instead of the MQ-9 Reaper
|
||||
* **[Units/Factions]** Minor changes to Syria 2011 and Turkey 2005 factions
|
||||
* **[UI]** Version number is shown in about dialog
|
||||
|
||||
## Fixed issues :
|
||||
|
||||
* **[Mission Generator]** Caucasus terrain improvement on exclusions zone (added forests between Vaziani and Beslan to exclusion zones)
|
||||
* **[Mission Generator]** The first unit of every base defenses group could not be controlled with Combined Arms.
|
||||
* **[Mission Generator]** Reduced generated helicopter altitude for CAS missions
|
||||
* **[Mission Generator]** F-16C default CAS payload was asymmetric, fixed.
|
||||
* **[Mission Generator]** AH-1W couldn't be bought, and added default payloads.
|
||||
* **[UI/UX]** Fixed Mi-28N missing thumbnail
|
||||
* **[UI/UX]** Fixed list of flights not refreshing when changing the mission departure (T+).
|
||||
|
||||
# 2.0.11
|
||||
|
||||
## Features/Improvements :
|
||||
|
||||
* **[Units/Factions]** Added Mig-31, Su-30, Mi-24V, Mi-28N to Russia 2010 faction.
|
||||
* **[Units/Factions]** Added F-15E to USA 2005 and USA 1990 factions.
|
||||
* **[Mission Generator]** Added a parameter to choose whether the JTACs should use smoke markers or not
|
||||
|
||||
## Fixed issues :
|
||||
|
||||
* **[Units/Factions]** Fixed big performance issue in new release UI that occurred only when running the .exe
|
||||
* **[Units/Factions]** Fixed mission generation not working with Libya faction
|
||||
* **[Units/Factions]** Fixed OH-58D not being used by AI
|
||||
* **[Units/Factions]** Typo in UK 1990 name (fixed by bwRavencl)
|
||||
* **[Units/Factions]** Fixed Tanker Tacan channel not being the same as the briefing one. (Sorry)
|
||||
* **[Mission Generator]** Neutral airbases services will now be disabled. (Not possible to refuel or re-arm there)
|
||||
* **[Mission Generator]** AI will be configured to limit afterburner usage
|
||||
* **[Mission Generator]** JTAC will not use laser codes above 1688 anymore
|
||||
* **[Mission Generator]** JTAC units were misconfigured and would not be invisible/immortal.
|
||||
* **[Mission Generator]** Increased JTAC status message duration to 25s, so you have more time to enter coordinates;
|
||||
* **[Mission Generator]** Destroyed units carcass will not appear on airfields to avoid having a destroyed vehicle blocking a runway or taxiway.
|
||||
|
||||
|
||||
# 2.0.10
|
||||
|
||||
## Features/Improvements :
|
||||
* **[Misc]** Now possible to save game in a different file, and to open DCS Liberation savegame files. (You are not restricted to a single save file anymore)
|
||||
* **[UI/UX]** New dark UI Theme and default theme improvement by Deus
|
||||
* **[UI/UX]** New "satellite" map backgrounds
|
||||
* **[UX]** Base menu is opened with a single mouse click
|
||||
* **[Units/Factions/Mods]** Added Community A-4E-C support for faction Bluefor Cold War
|
||||
* **[Units/Factions/Mods]** Added MB-339PAN support for faction Bluefor Cold War
|
||||
* **[Units/Factions/Mods]** Added Rafale AI mod support
|
||||
* **[Units/Factions/Mods]** Added faction "France Modded" with units from frenchpack v3.5 mod
|
||||
* **[Units/Factions/Mods]** Added faction "Insurgent modded" with Insurgent units from frenchpack v3.5 mod (Toyota truck)
|
||||
* **[Units/Factions/Mods]** Added factions Canada 2005, Australia 2005, Japan 2005, USA Aggressors, PMC
|
||||
* **[New Game Wizard]** Added the list of required mods for modded factions.
|
||||
* **[New Game Wizard]** No more RED vs BLUE opposing faction restrictions.
|
||||
* **[New Game Wizard]** New campaign generation settings added : No aircraft carrier, no lha, no navy, invert map starting positions.
|
||||
* **[Mission Generator]** Artillery units will start firing mission after a random delay. It should reduces lag spikes induced by artillery strikes by spreading them out.
|
||||
* **[Mission Generator]** Ground units will retreat after taking too much casualties. Artillery units will retreat if engaged.
|
||||
* **[Mission Generator]** The briefing will now contain the carrier ATC frequency
|
||||
* **[Mission Generator]** The briefing contains a small situation update.
|
||||
* **[Mission Generator]** Previously destroyed units are visible in the mission. (And added a performance settings to disable this behaviour)
|
||||
* **[Mission Generator]*c* Basic JTAC on Frontlines
|
||||
* **[Campaign Generator]** Added Tarawa in caucasus campaigns
|
||||
* **[Campaign Generator]** Tuned the various existing campaign parameters
|
||||
* **[Campaign Generator]** Added small campaign : "Russia" on Caucasus Theater
|
||||
|
||||
## Fixed issues :
|
||||
* **[Mission Generator]** Carrier will sail into the wind, not in the same direction
|
||||
* **[Mission Generator]** Carrier cold start was not working (flight was starting warm even when cold was selected)
|
||||
* **[Mission Generator]** Carrier group ships are more spread out
|
||||
* **[Mission Generator]** Fixed wrong radio frequency for german WW2 warbirds
|
||||
* **[Mission Generator]** Fixed FW-190A8 spawning with bomb rack for CAP missions
|
||||
* **[Mission Generator]** Fixed A-20G spawning with no payload
|
||||
* **[Mission Generator]** Fixed Su-33 spawning too heavy to take off from carrier
|
||||
* **[Mission Generator]** Fixed Harrier AV-8B spawning too heavy to take off from tarawa
|
||||
* **[Mission Generator]** Base defense units were not controllable with Combined Arms
|
||||
* **[Mission Generator]** Tanker speed was too low
|
||||
* **[Mission Generator]** Tanker TACAN settings were wrong
|
||||
* **[Mission Generator]** AI aircraft should start datalink ON (EPLRS)
|
||||
* **[Mission Generator]** Base defense units should not spawn on runway and or taxyway. (The chance for this to happen should now be really really low)
|
||||
* **[Mission Generator]** Fixed all flights starting "In flight" after playing a few missions (parking slot reset issue)
|
||||
* **[Mission Script/Performance]** Mission lua script will not listen to weapons fired event anymore and register every fired weapons. This should improve performance especially in WW2 scenarios or when rocket artillery is firing.
|
||||
* **[Campaign Generator]** Carrier name will now not appear for faction who do not have carriers
|
||||
* **[Campaign Generator]** SA-10 sites will now have a tracking radar.
|
||||
* **[Units/Factions]** Remove JF-17 from USA 2005 faction
|
||||
* **[Units/Factions]** Remove AJS-37 from Russia 2010
|
||||
* **[Units/Factions]** Removed Oliver Hazard Perry from cold war factions (too powerful sam system for the era)
|
||||
* **[Bug]** On the persian gulf full map campaign, the two carriers were sharing the same id, this was causing a lot of bugs
|
||||
* **[Performance]** Tuned the culling setting so that you cannot run into situation where no friendly or enemy AI flights are generated
|
||||
* **[Other]** Application doesn't gracefully exit.
|
||||
* **[Other]** Other minor fixes, and multiples factions small changes
|
||||
|
||||
# 2.0 RC 9
|
||||
|
||||
## Features/Improvements :
|
||||
* **[UI/UX]** New icons from contributor Deus
|
||||
|
||||
## Fixed issues :
|
||||
* **[Mission Generator]** Carrier TACAN was wrongfully set up as an A/A TACAN
|
||||
* **[Campaign Generator]** Fixed issue with Russian navy group generator causing a random crash on campaign creation.
|
||||
|
||||
# 2.0 RC 8
|
||||
|
||||
## Fixed issues :
|
||||
* **[Mission Generator]** Frequency for P-47D-30 changed to 124Mhz (Generated mission with 251Mhz would not work)
|
||||
* **[Mission Generator]** Reduced the maximum number of uboat per generated group
|
||||
* **[Mission Generator]** Fixed an issue with the WW2 LST groups (superposed units).
|
||||
* **[UI]** Fixed issue with the zoom
|
||||
|
||||
# 2.0 RC 7
|
||||
|
||||
## Features/Improvements :
|
||||
|
||||
##Features/Improvements :
|
||||
* **[Units/Factions]** Added P-47D-30 for factions allies_1944
|
||||
* **[Units/Factions]** Replaced S3-B Tanker by KC130 for most factions
|
||||
* **[Units/Factions]** Added factions : Bluefor Coldwar, Germany 1944 Easy
|
||||
|
||||
* **[Campaign/Map]** Added a campaign in the Channel map
|
||||
* **[Campaign/Map]** Changed the Normandy campaign map
|
||||
* **[Campaign/Map]** Added new campaign Normandy Small
|
||||
|
||||
* **[Mission Generator]** AI Flight generator has been reworked
|
||||
* **[Mission Generator]** Add PP points for JF-17 on STRIKE missions
|
||||
* **[Mission Generator]** Add ST point for F-14B on STRIKE missions
|
||||
* **[Mission Generator]** Flights with client slots will never be delayed
|
||||
* **[Mission Generator]** AI units can start from parking (Added a new setting)
|
||||
* **[Mission Generator]** AI units can start from parking (With a new setting in Settings Window to disable it)
|
||||
* **[Mission Generator]** Tacan for carrier will only be in Mode X from now
|
||||
* **[Mission Generator]** RTB waypoints for autogenerated flights
|
||||
|
||||
* **[Flight Planner]** Added CAS mission generator
|
||||
* **[Flight Planner]** Added CAP mission generator
|
||||
* **[Flight Planner]** Added SEAD mission generator
|
||||
* **[Flight Planner]** Added STRIKE mission generator
|
||||
* **[Flight Planner]** Added buttons to add autogenerated waypoints (ASCEND, DESCEND, RTB)
|
||||
* **[Flight Planner]** Improved waypoint list
|
||||
* **[Flight Planner]** WW2 factions uses different parameters for flight planning.
|
||||
|
||||
* **[Settings]** Added settings to disallow external views
|
||||
* **[Settings]** Added settings to choose F10 Map mode (All, Allies only, Player only, Fog of War, Map Only)
|
||||
* **[Settings]** Added settings to choose whether to auto-generate objective marks on the F10 map
|
||||
|
||||
* **[Info Panel]** Added information about destroyed buildings in info panel
|
||||
* **[Info Panel]** Added information about destroyed units at SAM site in info panel
|
||||
* **[Info Panel]** Added information about units destroyed outside the frontline in the debriefing window
|
||||
* **[Info Panel]** Added information about buildings destroyed in the debriefing window
|
||||
* **[Debriefing]** Added information about units destroyed outside the frontline in the debriefing window
|
||||
* **[Debriefing]** Added destroyed buildings in the debriefing window
|
||||
|
||||
* **[Map]** Tooltip now contains the list of building for Strike targets on the map
|
||||
* **[Map]** Added "Oil derrick" building
|
||||
* **[Misc]** Made it possible to setup DCS Saved Games directory and DCS installation directory manually
|
||||
* **[Map]** Added "ww2 bunker" building (WW2)
|
||||
* **[Map]** Added "ally camp" building (WW2)
|
||||
* **[Map]** Added "V1 Site" (WW2)
|
||||
|
||||
* **[Misc]** Made it possible to setup DCS Saved Games directory and DCS installation directory manually at first start
|
||||
* **[Misc]** Added culling performance settings
|
||||
|
||||
## Fixed issues :
|
||||
|
||||
* **[Units/Factions]** Replaced S3-B Tanker by KC130 for most factions (More fuel)
|
||||
* **[Units/Factions]** WW2 factions will not have offshore oil station and other modern buildings generated. No more third-reich operated offshore stations will spawn on normandy's coast.
|
||||
* **[Units/Factions]** Aircraft carrier will try to move in the wind direction
|
||||
* **[Units/Factions]** Missing icons added for some aircraft
|
||||
|
||||
##Fixed issues :
|
||||
* **[Mission Generator]** When playing as RED the activation trigger would not be properly generated
|
||||
* **[Mission Generator]** Changed "strike" payload for Su-24M that was innefective
|
||||
* **[Mission Generator]** FW-190A8 is now properly considered as a flyable aircraft
|
||||
* **[Mission Generator]** Changed "strike" payload for Su-24M that was ineffective
|
||||
* **[Mission Generator]** Changed "strike" payload for JF-17 to use LS-6 bombs instead of GBU
|
||||
* **[Mission Generator]** FW-190A8 is now properly considered as a flyable
|
||||
* **[Maps/Campaign]** Now using Vasiani airport instead of Soganlung in North Caucasus campaign (More parking slots)
|
||||
* **[Info Panel]** Message displayed on base capture event stated that the ennemy captured an airbase, while it was the player who captured it.
|
||||
* **[Map]** Graphical glitch on map when one building of an objective was destroyed, but not the others
|
||||
* **[Map]** Change power station template. (Buildings could end up superposed).
|
||||
* **[Mission Generator]** Change power station template. (Buildings could end up superposed).
|
||||
|
||||
#2.0 RC 6
|
||||
* **[Maps/Campaign]** Now using Vasiani airbase instead of Soganlung airport in Caucasus campaigns (more parking slot)
|
||||
* **[Info Panel]** Message displayed on base capture event stated that the enemy captured an airbase, while it was the player who captured it.
|
||||
* **[Map View]** Graphical glitch on map when one building of an objective was destroyed, but not the others
|
||||
* **[Mission Planner]** The list of flights was not updated on departure time change.
|
||||
|
||||
|
||||
# 2.0 RC 6
|
||||
|
||||
Saves file from RC5 are not compatible with the new version.
|
||||
Sorry :(
|
||||
|
||||
##Features/Improvements :
|
||||
## Features/Improvements :
|
||||
* **[Units/Factions]** Supercarrier support (You have to go to settings to enable it, if you have the supercarrier module)
|
||||
* **[Units/Factions]** Added 'Modern Bluefor' factions, containing all most popular DCS flyable units
|
||||
* **[Units/Factions]** Factions US 2005 / 1990 will now sometimes have Arleigh Burke class ships instead of Perry as carrier escorts
|
||||
@@ -47,11 +269,11 @@ Sorry :(
|
||||
* **[Mission Generator]** Using Late Activation & Trigger in attempt to improve performance & reduce stutter (Previously they were spawned through 'ETA' feature)
|
||||
* **[UX]** : Improved flight selection behaviour in the Mission Planning Window
|
||||
|
||||
##Fixed issues :
|
||||
## Fixed issues :
|
||||
* **[Mission Generator]** Payloads were not correctly assigned in the release version.
|
||||
* **[Mission Generator]** Game generation does not work when "no night mission" settings was selected and the current time was "day"
|
||||
* **[Mission Generator]** Game generation does not work when the player selected faction has no AWACS
|
||||
* **[Mission Generator]** Planned flights will spawn even if their home base has been captured or is being contested by enemy ground units.
|
||||
* **[Campaign Generator]** Base defenses would not be generated on Normandy map and in some rare cases on others maps as well
|
||||
* **[Mission Planning]** CAS waypoints created from the "Predefined waypoint selector" would not be at the exact location of the frontline
|
||||
* **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP)
|
||||
* **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP)
|
||||
21
game/data/aaa_db.py
Normal file
21
game/data/aaa_db.py
Normal file
@@ -0,0 +1,21 @@
|
||||
from dcs.vehicles import AirDefence
|
||||
|
||||
AAA_UNITS = [
|
||||
AirDefence.SPAAA_Gepard,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka,
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
AirDefence.AAA_ZU_23_Insurgent_Closed,
|
||||
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
|
||||
AirDefence.AAA_ZU_23_Insurgent,
|
||||
AirDefence.AAA_8_8cm_Flak_18,
|
||||
AirDefence.AAA_Flak_38,
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
AirDefence.AAA_8_8cm_Flak_37,
|
||||
AirDefence.AAA_Flak_Vierling_38,
|
||||
AirDefence.AAA_Kdo_G_40,
|
||||
AirDefence.AAA_8_8cm_Flak_41,
|
||||
AirDefence.AAA_Bofors_40mm
|
||||
]
|
||||
16
game/data/building_data.py
Normal file
16
game/data/building_data.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import inspect
|
||||
import dcs
|
||||
|
||||
DEFAULT_AVAILABLE_BUILDINGS = ['fuel', 'ammo', 'comms', 'oil', 'ware', 'farp', 'fob', 'power', 'factory', 'derrick', 'aa']
|
||||
|
||||
WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp', 'aa']
|
||||
WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'aa']
|
||||
|
||||
FORTIFICATION_BUILDINGS = ['Siegfried Line', 'Concertina wire', 'Concertina Wire', 'Czech hedgehogs 1', 'Czech hedgehogs 2',
|
||||
'Dragonteeth 1', 'Dragonteeth 2', 'Dragonteeth 3', 'Dragonteeth 4', 'Dragonteeth 5',
|
||||
'Haystack 1', 'Haystack 2', 'Haystack 3', 'Haystack 4', 'Hemmkurvenvenhindernis',
|
||||
'Log posts 1', 'Log posts 2', 'Log posts 3', 'Log ramps 1', 'Log ramps 2', 'Log ramps 3',
|
||||
'Belgian Gate', 'Container white']
|
||||
|
||||
FORTIFICATION_UNITS = [c for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)]
|
||||
FORTIFICATION_UNITS_ID = [c.id for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)]
|
||||
33
game/data/cap_capabilities_db.py
Normal file
33
game/data/cap_capabilities_db.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from dcs.planes import *
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
|
||||
"""
|
||||
This list contains the aircraft that do not use the guns as the last resort weapons, but as a main weapon
|
||||
They'll RTB when they don't have gun ammo left
|
||||
"""
|
||||
GUNFIGHTERS = [
|
||||
|
||||
# Cold War
|
||||
MiG_15bis,
|
||||
MiG_19P,
|
||||
MiG_21Bis,
|
||||
F_86F_Sabre,
|
||||
A_4E_C,
|
||||
F_5E_3,
|
||||
|
||||
# Trainers
|
||||
C_101CC,
|
||||
L_39ZA,
|
||||
|
||||
# WW2
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
Bf_109K_4,
|
||||
FW_190D9,
|
||||
FW_190A8,
|
||||
I_16,
|
||||
|
||||
]
|
||||
95
game/data/doctrine.py
Normal file
95
game/data/doctrine.py
Normal file
@@ -0,0 +1,95 @@
|
||||
from game.utils import nm_to_meter, feet_to_meter
|
||||
|
||||
MODERN_DOCTRINE = {
|
||||
|
||||
"GENERATORS": {
|
||||
"CAS": True,
|
||||
"CAP": True,
|
||||
"SEAD": True,
|
||||
"STRIKE": True,
|
||||
"ANTISHIP": True,
|
||||
},
|
||||
|
||||
"STRIKE_MAX_RANGE": 1500000,
|
||||
"SEAD_MAX_RANGE": 1500000,
|
||||
|
||||
"CAP_EVERY_X_MINUTES": 20,
|
||||
"CAS_EVERY_X_MINUTES": 30,
|
||||
"SEAD_EVERY_X_MINUTES": 40,
|
||||
"STRIKE_EVERY_X_MINUTES": 40,
|
||||
|
||||
"INGRESS_EGRESS_DISTANCE": nm_to_meter(45),
|
||||
"INGRESS_ALT": feet_to_meter(20000),
|
||||
"EGRESS_ALT": feet_to_meter(20000),
|
||||
"PATROL_ALT_RANGE": (feet_to_meter(15000), feet_to_meter(33000)),
|
||||
"PATTERN_ALTITUDE": feet_to_meter(5000),
|
||||
|
||||
"CAP_PATTERN_LENGTH": (nm_to_meter(15), nm_to_meter(40)),
|
||||
"FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(6), nm_to_meter(15)),
|
||||
"CAP_DISTANCE_FROM_CP": (nm_to_meter(10), nm_to_meter(40)),
|
||||
|
||||
"MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
|
||||
}
|
||||
|
||||
COLDWAR_DOCTRINE = {
|
||||
|
||||
"GENERATORS": {
|
||||
"CAS": True,
|
||||
"CAP": True,
|
||||
"SEAD": True,
|
||||
"STRIKE": True,
|
||||
"ANTISHIP": True,
|
||||
},
|
||||
|
||||
"STRIKE_MAX_RANGE": 1500000,
|
||||
"SEAD_MAX_RANGE": 1500000,
|
||||
|
||||
"CAP_EVERY_X_MINUTES": 20,
|
||||
"CAS_EVERY_X_MINUTES": 30,
|
||||
"SEAD_EVERY_X_MINUTES": 40,
|
||||
"STRIKE_EVERY_X_MINUTES": 40,
|
||||
|
||||
"INGRESS_EGRESS_DISTANCE": nm_to_meter(30),
|
||||
"INGRESS_ALT": feet_to_meter(18000),
|
||||
"EGRESS_ALT": feet_to_meter(18000),
|
||||
"PATROL_ALT_RANGE": (feet_to_meter(10000), feet_to_meter(24000)),
|
||||
"PATTERN_ALTITUDE": feet_to_meter(5000),
|
||||
|
||||
"CAP_PATTERN_LENGTH": (nm_to_meter(12), nm_to_meter(24)),
|
||||
"FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(2), nm_to_meter(8)),
|
||||
"CAP_DISTANCE_FROM_CP": (nm_to_meter(8), nm_to_meter(25)),
|
||||
|
||||
"MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
|
||||
}
|
||||
|
||||
WWII_DOCTRINE = {
|
||||
|
||||
"GENERATORS": {
|
||||
"CAS": True,
|
||||
"CAP": True,
|
||||
"SEAD": False,
|
||||
"STRIKE": True,
|
||||
"ANTISHIP": True,
|
||||
},
|
||||
|
||||
"STRIKE_MAX_RANGE": 1500000,
|
||||
"SEAD_MAX_RANGE": 1500000,
|
||||
|
||||
"CAP_EVERY_X_MINUTES": 20,
|
||||
"CAS_EVERY_X_MINUTES": 30,
|
||||
"SEAD_EVERY_X_MINUTES": 40,
|
||||
"STRIKE_EVERY_X_MINUTES": 40,
|
||||
|
||||
"INGRESS_EGRESS_DISTANCE": nm_to_meter(7),
|
||||
"INGRESS_ALT": feet_to_meter(8000),
|
||||
"EGRESS_ALT": feet_to_meter(8000),
|
||||
"PATROL_ALT_RANGE": (feet_to_meter(4000), feet_to_meter(15000)),
|
||||
"PATTERN_ALTITUDE": feet_to_meter(5000),
|
||||
|
||||
"CAP_PATTERN_LENGTH": (nm_to_meter(8), nm_to_meter(18)),
|
||||
"FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(1), nm_to_meter(6)),
|
||||
"CAP_DISTANCE_FROM_CP": (nm_to_meter(0), nm_to_meter(5)),
|
||||
|
||||
"MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
|
||||
|
||||
}
|
||||
53
game/data/radar_db.py
Normal file
53
game/data/radar_db.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from dcs.vehicles import AirDefence
|
||||
from dcs.ships import *
|
||||
|
||||
UNITS_WITH_RADAR = [
|
||||
|
||||
# Radars
|
||||
AirDefence.SAM_SA_15_Tor_9A331,
|
||||
AirDefence.SAM_SA_11_Buk_CC_9S470M1,
|
||||
AirDefence.SAM_Patriot_AMG_AN_MRC_137,
|
||||
AirDefence.SAM_Patriot_ECS_AN_MSQ_104,
|
||||
AirDefence.SPAAA_Gepard,
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka,
|
||||
AirDefence.EWR_1L13,
|
||||
AirDefence.SAM_SA_6_Kub_STR_9S91,
|
||||
AirDefence.SAM_SA_10_S_300PS_TR_30N6,
|
||||
AirDefence.SAM_SA_10_S_300PS_SR_5N66M,
|
||||
AirDefence.EWR_55G6,
|
||||
AirDefence.SAM_SA_10_S_300PS_SR_64H6E,
|
||||
AirDefence.SAM_SA_11_Buk_SR_9S18M1,
|
||||
AirDefence.CP_9S80M1_Sborka,
|
||||
AirDefence.SAM_Hawk_TR_AN_MPQ_46,
|
||||
AirDefence.SAM_Hawk_SR_AN_MPQ_50,
|
||||
AirDefence.SAM_Patriot_STR_AN_MPQ_53,
|
||||
AirDefence.SAM_Hawk_CWAR_AN_MPQ_55,
|
||||
AirDefence.SAM_SR_P_19,
|
||||
AirDefence.SAM_Roland_EWR,
|
||||
AirDefence.SAM_SA_3_S_125_TR_SNR,
|
||||
AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song,
|
||||
AirDefence.HQ_7_Self_Propelled_STR,
|
||||
|
||||
# Ships
|
||||
CVN_70_Carl_Vinson,
|
||||
Oliver_Hazzard_Perry_class,
|
||||
Ticonderoga_class,
|
||||
FFL_1124_4_Grisha,
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
FSG_1241_1MP_Molniya,
|
||||
CG_1164_Moskva,
|
||||
FFG_11540_Neustrashimy,
|
||||
CGN_1144_2_Pyotr_Velikiy,
|
||||
FF_1135M_Rezky,
|
||||
CV_1143_5_Admiral_Kuznetsov_2017,
|
||||
CVN_74_John_C__Stennis,
|
||||
CVN_71_Theodore_Roosevelt,
|
||||
CVN_72_Abraham_Lincoln,
|
||||
CVN_73_George_Washington,
|
||||
USS_Arleigh_Burke_IIa,
|
||||
LHA_1_Tarawa,
|
||||
Type_052B_Destroyer,
|
||||
Type_054A_Frigate,
|
||||
Type_052C_Destroyer
|
||||
]
|
||||
534
game/db.py
534
game/db.py
@@ -13,18 +13,31 @@ from dcs.unit import *
|
||||
from dcs.unittype import *
|
||||
from dcs.unitgroup import *
|
||||
|
||||
from game.factions.china_2000 import China_2000
|
||||
from game.factions.australia_2005 import Australia_2005
|
||||
from game.factions.bluefor_coldwar import BLUEFOR_COLDWAR
|
||||
from game.factions.bluefor_coldwar_a4 import BLUEFOR_COLDWAR_A4
|
||||
from game.factions.bluefor_coldwar_mods import BLUEFOR_COLDWAR_MODS
|
||||
from game.factions.canada_2005 import Canada_2005
|
||||
from game.factions.china_2010 import China_2010
|
||||
from game.factions.france_1995 import France_1995
|
||||
from game.factions.france_2005 import France_2005
|
||||
from game.factions.france_modded import France_2005_Modded
|
||||
from game.factions.germany_1944_easy import Germany_1944_Easy
|
||||
from game.factions.germany_1990 import Germany_1990
|
||||
from game.factions.insurgent import Insurgent
|
||||
from game.factions.insurgent_modded import Insurgent_modded
|
||||
from game.factions.iran_2015 import Iran_2015
|
||||
from game.factions.israel_1948 import Israel_1948
|
||||
from game.factions.israel_1973 import Israel_1973, Israel_1973_NO_WW2_UNITS, Israel_1982
|
||||
from game.factions.israel_2000 import Israel_2000
|
||||
from game.factions.italy_1990 import Italy_1990
|
||||
from game.factions.libya_2011 import Lybia_2011
|
||||
from game.factions.italy_1990_mb339 import Italy_1990_MB339
|
||||
from game.factions.japan_2005 import Japan_2005
|
||||
from game.factions.libya_2011 import Libya_2011
|
||||
from game.factions.netherlands_1990 import Netherlands_1990
|
||||
from game.factions.north_korea_2000 import NorthKorea_2000
|
||||
from game.factions.pakistan_2015 import Pakistan_2015
|
||||
from game.factions.private_miltary_companies import PMC_WESTERN_B, PMC_RUSSIAN, PMC_WESTERN_A
|
||||
from game.factions.russia_1975 import Russia_1975
|
||||
from game.factions.germany_1944 import Germany_1944
|
||||
from game.factions.india_2010 import India_2010
|
||||
@@ -34,11 +47,14 @@ from game.factions.russia_1990 import Russia_1990
|
||||
from game.factions.russia_2010 import Russia_2010
|
||||
from game.factions.spain_1990 import Spain_1990
|
||||
from game.factions.sweden_1990 import Sweden_1990
|
||||
from game.factions.syria import Syria_2011, Syria_1967, Syria_1967_WW2_Weapons, Syria_1973, Arab_Armies_1948, Syria_1982
|
||||
from game.factions.turkey_2005 import Turkey_2005
|
||||
from game.factions.uae_2005 import UAE_2005
|
||||
from game.factions.uk_1944 import UK_1944
|
||||
from game.factions.uk_1990 import UnitedKingdom_1990
|
||||
from game.factions.ukraine_2010 import Ukraine_2010
|
||||
from game.factions.usa_1944 import USA_1944
|
||||
from game.factions.us_aggressors import US_Aggressors
|
||||
from game.factions.usa_1944 import USA_1944, ALLIES_1944
|
||||
from game.factions.usa_1955 import USA_1955
|
||||
from game.factions.usa_1960 import USA_1960
|
||||
from game.factions.usa_1965 import USA_1965
|
||||
@@ -46,6 +62,53 @@ from game.factions.usa_1990 import USA_1990
|
||||
from game.factions.usa_2005 import USA_2005
|
||||
from game.factions.bluefor_modern import BLUEFOR_MODERN
|
||||
|
||||
# PATCH pydcs data with MODS
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
import pydcs_extensions.frenchpack.frenchpack as frenchpack
|
||||
from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M
|
||||
|
||||
plane_map["A-4E-C"] = A_4E_C
|
||||
plane_map["MB-339PAN"] = MB_339PAN
|
||||
plane_map["Rafale_M"] = Rafale_M
|
||||
plane_map["Rafale_A_S"] = Rafale_A_S
|
||||
|
||||
vehicle_map["FieldHL"] = frenchpack._FIELD_HIDE
|
||||
vehicle_map["HARRIERH"] = frenchpack._FIELD_HIDE_SMALL
|
||||
vehicle_map["SMOKESAM"] = frenchpack.SMOKE_SAM_IR
|
||||
vehicle_map["SmokeD1"] = frenchpack.SmokeD1
|
||||
vehicle_map["SmokeD3"] = frenchpack.SmokeD3
|
||||
vehicle_map["AMX10RCR"] = frenchpack.AMX_10RCR
|
||||
vehicle_map["SEPAR"] = frenchpack.AMX_10RCR_SEPAR
|
||||
vehicle_map["ERC"] = frenchpack.ERC_90
|
||||
vehicle_map["M120"] = frenchpack.MO_120_RT
|
||||
vehicle_map["AA20"] = frenchpack._53T2
|
||||
vehicle_map["TRM2000"] = frenchpack.TRM_2000
|
||||
vehicle_map["TRM2000_Citerne"] = frenchpack.TRM_2000_Fuel
|
||||
vehicle_map["TRM2000_AA20"] = frenchpack.TRM_2000_53T2
|
||||
vehicle_map["TRMMISTRAL"] = frenchpack.TRM_2000_PAMELA
|
||||
vehicle_map["VABH"] = frenchpack.VAB_MEDICAL
|
||||
vehicle_map["VAB_RADIO"] = frenchpack.VAB
|
||||
vehicle_map["VAB_50"] = frenchpack.VAB__50
|
||||
vehicle_map["VIB_VBR"] = frenchpack.VAB_T20_13
|
||||
vehicle_map["VAB_HOT"] = frenchpack.VAB_MEPHISTO
|
||||
vehicle_map["VAB_MORTIER"] = frenchpack.VAB_MORTIER
|
||||
vehicle_map["VBL50"] = frenchpack.VBL__50
|
||||
vehicle_map["VBLANF1"] = frenchpack.VBL_AANF1
|
||||
vehicle_map["VBL-radio"] = frenchpack.VBL
|
||||
vehicle_map["VBAE"] = frenchpack.VBAE_CRAB
|
||||
vehicle_map["VBAE_MMP"] = frenchpack.VBAE_CRAB_MMP
|
||||
vehicle_map["AMX-30B2"] = frenchpack.AMX_30B2
|
||||
vehicle_map["Tracma"] = frenchpack.Tracma_TD_1500
|
||||
vehicle_map["JTACFP"] = frenchpack.Infantry_Soldier_JTAC
|
||||
vehicle_map["SHERIDAN"] = frenchpack.Char_M551_Sheridan
|
||||
vehicle_map["Leclerc_XXI"] = frenchpack.Leclerc_Serie_XXI
|
||||
vehicle_map["Toyota_bleu"] = frenchpack.DIM__TOYOTA_BLUE
|
||||
vehicle_map["Toyota_vert"] = frenchpack.DIM__TOYOTA_GREEN
|
||||
vehicle_map["Toyota_desert"] = frenchpack.DIM__TOYOTA_DESERT
|
||||
vehicle_map["Kamikaze"] = frenchpack.DIM__KAMIKAZE
|
||||
|
||||
|
||||
"""
|
||||
---------- BEGINNING OF CONFIGURATION SECTION
|
||||
"""
|
||||
@@ -80,18 +143,19 @@ PRICES = {
|
||||
MiG_29A: 18,
|
||||
MiG_29S: 20,
|
||||
MiG_29G: 18,
|
||||
MiG_25PD: 20,
|
||||
MiG_31: 30,
|
||||
J_11A: 26,
|
||||
JF_17: 20,
|
||||
Su_30: 24,
|
||||
|
||||
SpitfireLFMkIX: 8,
|
||||
SpitfireLFMkIXCW: 8,
|
||||
Bf_109K_4: 8,
|
||||
FW_190D9: 8,
|
||||
FW_190A8: 8,
|
||||
A_20G: 12,
|
||||
Ju_88A4: 12,
|
||||
SpitfireLFMkIX: 14,
|
||||
SpitfireLFMkIXCW: 14,
|
||||
Bf_109K_4: 14,
|
||||
FW_190D9: 16,
|
||||
FW_190A8: 14,
|
||||
A_20G: 22,
|
||||
Ju_88A4: 24,
|
||||
|
||||
F_5E_3: 8,
|
||||
MiG_15bis: 4,
|
||||
@@ -101,16 +165,20 @@ PRICES = {
|
||||
F_4E: 10,
|
||||
AJS37: 12,
|
||||
C_101CC: 6,
|
||||
A_4E_C: 8,
|
||||
MB_339PAN: 6,
|
||||
|
||||
AV8BNA: 14,
|
||||
M_2000C: 16,
|
||||
Mirage_2000_5: 22,
|
||||
FA_18C_hornet: 24,
|
||||
F_15C: 26,
|
||||
Mirage_2000_5: 20,
|
||||
FA_18C_hornet: 22,
|
||||
F_15C: 22,
|
||||
F_15E: 24,
|
||||
F_16C_50: 20,
|
||||
F_14B: 22,
|
||||
Tornado_IDS: 24,
|
||||
Tornado_GR4: 24,
|
||||
F_16A: 14,
|
||||
F_14B: 24,
|
||||
Tornado_IDS: 20,
|
||||
Tornado_GR4: 20,
|
||||
|
||||
# bomber
|
||||
Su_17M4: 10,
|
||||
@@ -124,42 +192,58 @@ PRICES = {
|
||||
|
||||
A_10A: 16,
|
||||
A_10C: 22,
|
||||
A_10C_2: 24,
|
||||
|
||||
# heli
|
||||
Ka_50: 13,
|
||||
SA342M: 8,
|
||||
SA342L: 5,
|
||||
SA342Minigun: 4,
|
||||
SA342Mistral: 8,
|
||||
UH_1H: 4,
|
||||
Mi_8MT: 5,
|
||||
Mi_24V: 12,
|
||||
Mi_28N: 16,
|
||||
AH_1W: 10,
|
||||
AH_64A: 12,
|
||||
AH_64D: 16,
|
||||
Mi_24V: 18,
|
||||
Mi_28N: 24,
|
||||
AH_1W: 20,
|
||||
AH_64A: 24,
|
||||
AH_64D: 30,
|
||||
OH_58D: 6,
|
||||
|
||||
# Bombers
|
||||
B_52H: 25,
|
||||
B_52H: 35,
|
||||
B_1B: 50,
|
||||
|
||||
# special
|
||||
IL_76MD: 13,
|
||||
An_26B: 13,
|
||||
An_30M: 13,
|
||||
Yak_40: 13,
|
||||
S_3B_Tanker: 13,
|
||||
IL_78M: 13,
|
||||
KC_135: 13,
|
||||
KC130: 13,
|
||||
IL_76MD: 30,
|
||||
An_26B: 25,
|
||||
An_30M: 25,
|
||||
Yak_40: 25,
|
||||
S_3B_Tanker: 20,
|
||||
IL_78M: 25,
|
||||
KC_135: 25,
|
||||
KC130: 25,
|
||||
|
||||
A_50: 8,
|
||||
E_3A: 8,
|
||||
C_130: 8,
|
||||
A_50: 50,
|
||||
KJ_2000: 50,
|
||||
E_3A: 50,
|
||||
C_130: 25,
|
||||
|
||||
# WW2
|
||||
P_51D_30_NA: 6,
|
||||
P_51D: 6,
|
||||
P_47D_30: 6,
|
||||
P_51D_30_NA: 18,
|
||||
P_51D: 16,
|
||||
P_47D_30: 17,
|
||||
P_47D_30bl1: 16,
|
||||
P_47D_40: 18,
|
||||
B_17G: 30,
|
||||
|
||||
# Drones
|
||||
MQ_9_Reaper: 12,
|
||||
RQ_1A_Predator: 6,
|
||||
WingLoong_I: 6,
|
||||
|
||||
# Modded
|
||||
Rafale_M: 26,
|
||||
Rafale_A_S: 26,
|
||||
|
||||
# armor
|
||||
Armor.APC_MTLB: 4,
|
||||
@@ -168,15 +252,15 @@ PRICES = {
|
||||
Armor.ARV_BTR_RD: 8,
|
||||
Armor.APC_BTR_80: 8,
|
||||
Armor.MBT_T_55: 18,
|
||||
Armor.MBT_T_72B: 25,
|
||||
Armor.MBT_T_80U: 30,
|
||||
Armor.MBT_T_90: 35,
|
||||
Armor.MBT_T_72B: 22,
|
||||
Armor.MBT_T_80U: 25,
|
||||
Armor.MBT_T_90: 30,
|
||||
Armor.IFV_BMD_1: 8,
|
||||
Armor.IFV_BMP_1: 14,
|
||||
Armor.IFV_BMP_2: 16,
|
||||
Armor.IFV_BMP_3: 18,
|
||||
Armor.ZBD_04A: 12,
|
||||
Armor.ZTZ_96B: 35,
|
||||
Armor.ZTZ_96B: 30,
|
||||
|
||||
Armor.APC_Cobra: 4,
|
||||
Armor.APC_M113: 6,
|
||||
@@ -185,14 +269,14 @@ PRICES = {
|
||||
Armor.IFV_M2A2_Bradley: 12,
|
||||
Armor.APC_M1126_Stryker_ICV: 10,
|
||||
Armor.ATGM_M1134_Stryker: 12,
|
||||
Armor.MBT_M60A3_Patton: 18,
|
||||
Armor.MBT_M1A2_Abrams: 35,
|
||||
Armor.MBT_Leclerc: 35,
|
||||
Armor.MBT_Leopard_1A3: 24,
|
||||
Armor.MBT_Leopard_2: 35,
|
||||
Armor.MBT_Merkava_Mk__4: 35,
|
||||
Armor.MBT_M60A3_Patton: 16,
|
||||
Armor.MBT_M1A2_Abrams: 25,
|
||||
Armor.MBT_Leclerc: 25,
|
||||
Armor.MBT_Leopard_1A3: 20,
|
||||
Armor.MBT_Leopard_2: 25,
|
||||
Armor.MBT_Merkava_Mk__4: 25,
|
||||
Armor.TPz_Fuchs: 5,
|
||||
Armor.MBT_Challenger_II: 30,
|
||||
Armor.MBT_Challenger_II: 25,
|
||||
Armor.IFV_Marder: 10,
|
||||
Armor.IFV_MCV_80: 10,
|
||||
Armor.IFV_LAV_25: 7,
|
||||
@@ -205,7 +289,7 @@ PRICES = {
|
||||
Artillery.SPH_2S3_Akatsia: 24,
|
||||
Artillery.SPH_2S19_Msta: 30,
|
||||
Artillery.MLRS_BM_21_Grad: 15,
|
||||
Artillery.MLRS_9K57_Uragan_BM_27: 40,
|
||||
Artillery.MLRS_9K57_Uragan_BM_27: 50,
|
||||
Artillery.MLRS_9A52_Smerch: 40,
|
||||
|
||||
Unarmed.Transport_UAZ_469: 3,
|
||||
@@ -215,49 +299,28 @@ PRICES = {
|
||||
|
||||
Unarmed.Transport_M818: 3,
|
||||
|
||||
AirDefence.AAA_Vulcan_M163: 5,
|
||||
AirDefence.SAM_Linebacker_M6: 10,
|
||||
|
||||
AirDefence.AAA_ZU_23_Closed: 2,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka: 4,
|
||||
AirDefence.SAM_SA_9_Strela_1_9P31: 8,
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6: 15,
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25: 22,
|
||||
AirDefence.SAM_SA_8_Osa_9A33: 12,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73: 20,
|
||||
AirDefence.SAM_SA_2_LN_SM_90: 15,
|
||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1: 25,
|
||||
AirDefence.SAM_Hawk_PCP: 20,
|
||||
AirDefence.SAM_Patriot_LN_M901: 60,
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C: 60,
|
||||
AirDefence.SAM_Chaparral_M48: 10,
|
||||
|
||||
# WW2
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:18,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H:8,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I:22,
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H:16,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I:24,
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:26,
|
||||
Armor.TD_Jagdpanther_G1: 16,
|
||||
Armor.TD_Jagdpanzer_IV: 10,
|
||||
Armor.TD_Jagdpanther_G1: 18,
|
||||
Armor.TD_Jagdpanzer_IV: 11,
|
||||
Armor.Sd_Kfz_184_Elefant: 18,
|
||||
Armor.APC_Sd_Kfz_251:2,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma:6,
|
||||
Armor.MT_M4_Sherman:5,
|
||||
Armor.MT_M4A4_Sherman_Firefly:8,
|
||||
Armor.CT_Cromwell_IV:8,
|
||||
Armor.APC_Sd_Kfz_251:4,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma:8,
|
||||
Armor.MT_M4_Sherman:12,
|
||||
Armor.MT_M4A4_Sherman_Firefly:16,
|
||||
Armor.CT_Cromwell_IV:12,
|
||||
Armor.M30_Cargo_Carrier:2,
|
||||
Armor.APC_M2A1:2,
|
||||
Armor.ST_Centaur_IV: 8,
|
||||
Armor.HIT_Churchill_VII: 12,
|
||||
Armor.LAC_M8_Greyhound: 4,
|
||||
Armor.TD_M10_GMC: 8,
|
||||
Armor.StuG_III_Ausf__G: 6,
|
||||
AirDefence.AAA_Bofors_40mm: 4,
|
||||
AirDefence.AAA_8_8cm_Flak_36: 6,
|
||||
AirDefence.AAA_8_8cm_Flak_18: 4,
|
||||
Artillery.M12_GMC: 2,
|
||||
Artillery.Sturmpanzer_IV_Brummbär: 2,
|
||||
|
||||
Armor.APC_M2A1:4,
|
||||
Armor.ST_Centaur_IV: 10,
|
||||
Armor.HIT_Churchill_VII: 16,
|
||||
Armor.LAC_M8_Greyhound: 8,
|
||||
Armor.TD_M10_GMC: 14,
|
||||
Armor.StuG_III_Ausf__G: 12,
|
||||
Artillery.M12_GMC: 10,
|
||||
Artillery.Sturmpanzer_IV_Brummbär: 10,
|
||||
|
||||
# ship
|
||||
CV_1143_5_Admiral_Kuznetsov: 100,
|
||||
@@ -268,6 +331,111 @@ PRICES = {
|
||||
Armed_speedboat: 10,
|
||||
Dry_cargo_ship_Ivanov: 10,
|
||||
Tanker_Elnya_160: 10,
|
||||
|
||||
# Air Defence units
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6: 30,
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25: 20,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73: 6,
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C: 22,
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85D: 22,
|
||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1: 30,
|
||||
AirDefence.SAM_SA_8_Osa_9A33: 28,
|
||||
AirDefence.SAM_SA_15_Tor_9A331: 40,
|
||||
AirDefence.SAM_SA_13_Strela_10M3_9A35M3: 16,
|
||||
AirDefence.SAM_SA_9_Strela_1_9P31: 12,
|
||||
AirDefence.SAM_SA_11_Buk_CC_9S470M1: 25,
|
||||
AirDefence.SAM_SA_8_Osa_LD_9T217: 22,
|
||||
AirDefence.SAM_Patriot_AMG_AN_MRC_137: 35,
|
||||
AirDefence.SAM_Patriot_ECS_AN_MSQ_104: 30,
|
||||
AirDefence.SPAAA_Gepard: 24,
|
||||
AirDefence.SAM_Hawk_PCP: 14,
|
||||
AirDefence.AAA_Vulcan_M163: 10,
|
||||
AirDefence.SAM_Hawk_LN_M192: 8,
|
||||
AirDefence.SAM_Chaparral_M48: 16,
|
||||
AirDefence.SAM_Linebacker_M6: 18,
|
||||
AirDefence.SAM_Patriot_LN_M901: 15,
|
||||
AirDefence.SAM_Avenger_M1097: 20,
|
||||
AirDefence.SAM_Patriot_EPP_III: 15,
|
||||
AirDefence.SAM_Patriot_ICC: 18,
|
||||
AirDefence.SAM_Roland_ADS: 12,
|
||||
AirDefence.SAM_SA_10_S_300PS_CP_54K6: 18,
|
||||
AirDefence.Stinger_MANPADS: 6,
|
||||
AirDefence.SAM_Stinger_comm_dsr: 4,
|
||||
AirDefence.SAM_Stinger_comm: 4,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka: 10,
|
||||
AirDefence.AAA_ZU_23_Closed: 6,
|
||||
AirDefence.AAA_ZU_23_Emplacement: 6,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375: 8,
|
||||
AirDefence.AAA_ZU_23_Insurgent_Closed: 6,
|
||||
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375: 8,
|
||||
AirDefence.AAA_ZU_23_Insurgent: 6,
|
||||
AirDefence.SAM_SA_18_Igla_MANPADS: 10,
|
||||
AirDefence.SAM_SA_18_Igla_comm: 8,
|
||||
AirDefence.SAM_SA_18_Igla_S_MANPADS: 12,
|
||||
AirDefence.SAM_SA_18_Igla_S_comm: 8,
|
||||
AirDefence.EWR_1L13: 30,
|
||||
AirDefence.SAM_SA_6_Kub_STR_9S91: 22,
|
||||
AirDefence.SAM_SA_10_S_300PS_TR_30N6: 24,
|
||||
AirDefence.SAM_SA_10_S_300PS_SR_5N66M: 30,
|
||||
AirDefence.EWR_55G6: 30,
|
||||
AirDefence.SAM_SA_10_S_300PS_SR_64H6E: 30,
|
||||
AirDefence.SAM_SA_11_Buk_SR_9S18M1: 28,
|
||||
AirDefence.CP_9S80M1_Sborka: 10,
|
||||
AirDefence.SAM_Hawk_TR_AN_MPQ_46: 14,
|
||||
AirDefence.SAM_Hawk_SR_AN_MPQ_50: 18,
|
||||
AirDefence.SAM_Patriot_STR_AN_MPQ_53: 22,
|
||||
AirDefence.SAM_Hawk_CWAR_AN_MPQ_55: 20,
|
||||
AirDefence.SAM_SR_P_19: 14,
|
||||
AirDefence.SAM_Roland_EWR: 16,
|
||||
AirDefence.SAM_SA_3_S_125_TR_SNR: 14,
|
||||
AirDefence.SAM_SA_2_LN_SM_90: 8,
|
||||
AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song: 12,
|
||||
AirDefence.Rapier_FSA_Launcher: 6,
|
||||
AirDefence.Rapier_FSA_Optical_Tracker: 6,
|
||||
AirDefence.Rapier_FSA_Blindfire_Tracker: 8,
|
||||
AirDefence.HQ_7_Self_Propelled_LN: 20,
|
||||
AirDefence.HQ_7_Self_Propelled_STR: 24,
|
||||
AirDefence.AAA_8_8cm_Flak_18: 6,
|
||||
AirDefence.AAA_Flak_38: 6,
|
||||
AirDefence.AAA_8_8cm_Flak_36: 8,
|
||||
AirDefence.AAA_8_8cm_Flak_37: 9,
|
||||
AirDefence.AAA_Flak_Vierling_38:6,
|
||||
AirDefence.AAA_Kdo_G_40: 8,
|
||||
AirDefence.Flak_Searchlight_37: 4,
|
||||
AirDefence.Maschinensatz_33: 10,
|
||||
AirDefence.AAA_8_8cm_Flak_41: 10,
|
||||
AirDefence.AAA_Bofors_40mm: 8,
|
||||
|
||||
# FRENCH PACK MOD
|
||||
frenchpack.AMX_10RCR: 10,
|
||||
frenchpack.AMX_10RCR_SEPAR: 12,
|
||||
frenchpack.ERC_90: 12,
|
||||
frenchpack.MO_120_RT: 10,
|
||||
frenchpack._53T2: 4,
|
||||
frenchpack.TRM_2000: 4,
|
||||
frenchpack.TRM_2000_Fuel: 4,
|
||||
frenchpack.TRM_2000_53T2: 8,
|
||||
frenchpack.TRM_2000_PAMELA: 14,
|
||||
frenchpack.VAB_MEDICAL: 8,
|
||||
frenchpack.VAB: 6,
|
||||
frenchpack.VAB__50: 4,
|
||||
frenchpack.VAB_T20_13: 6,
|
||||
frenchpack.VAB_MEPHISTO: 8,
|
||||
frenchpack.VAB_MORTIER: 10,
|
||||
frenchpack.VBL__50: 4,
|
||||
frenchpack.VBL_AANF1: 2,
|
||||
frenchpack.VBL: 1,
|
||||
frenchpack.VBAE_CRAB: 8,
|
||||
frenchpack.VBAE_CRAB_MMP: 12,
|
||||
frenchpack.AMX_30B2: 18,
|
||||
frenchpack.Tracma_TD_1500: 2,
|
||||
frenchpack.Infantry_Soldier_JTAC: 1,
|
||||
frenchpack.Leclerc_Serie_XXI: 35,
|
||||
frenchpack.DIM__TOYOTA_BLUE: 2,
|
||||
frenchpack.DIM__TOYOTA_GREEN: 2,
|
||||
frenchpack.DIM__TOYOTA_DESERT: 2,
|
||||
frenchpack.DIM__KAMIKAZE: 6,
|
||||
|
||||
}
|
||||
|
||||
"""
|
||||
@@ -289,16 +457,19 @@ Following tasks are present:
|
||||
UNIT_BY_TASK = {
|
||||
CAP: [
|
||||
F_5E_3,
|
||||
MiG_23MLD,
|
||||
Su_27,
|
||||
Su_33,
|
||||
MiG_19P,
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
MiG_25PD,
|
||||
MiG_29A,
|
||||
MiG_29S,
|
||||
MiG_31,
|
||||
FA_18C_hornet,
|
||||
F_15C,
|
||||
F_14B,
|
||||
F_16A,
|
||||
F_16C_50,
|
||||
M_2000C,
|
||||
Mirage_2000_5,
|
||||
@@ -315,8 +486,12 @@ UNIT_BY_TASK = {
|
||||
FW_190A8,
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
A_4E_C,
|
||||
Rafale_M,
|
||||
SA342Mistral
|
||||
],
|
||||
CAS: [
|
||||
F_15E,
|
||||
F_86F_Sabre,
|
||||
MiG_15bis,
|
||||
L_39ZA,
|
||||
@@ -324,6 +499,7 @@ UNIT_BY_TASK = {
|
||||
AJS37,
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
Su_17M4,
|
||||
Su_25,
|
||||
Su_25T,
|
||||
@@ -331,6 +507,7 @@ UNIT_BY_TASK = {
|
||||
Ka_50,
|
||||
SA342M,
|
||||
SA342L,
|
||||
SA342Minigun,
|
||||
Su_24M,
|
||||
Su_24MR,
|
||||
AH_64A,
|
||||
@@ -347,7 +524,16 @@ UNIT_BY_TASK = {
|
||||
MiG_27K,
|
||||
A_20G,
|
||||
P_47D_30,
|
||||
P_47D_30bl1,
|
||||
P_47D_40,
|
||||
Ju_88A4,
|
||||
B_17G,
|
||||
MB_339PAN,
|
||||
Rafale_A_S,
|
||||
WingLoong_I,
|
||||
MQ_9_Reaper,
|
||||
RQ_1A_Predator,
|
||||
AH_1W
|
||||
],
|
||||
Transport: [
|
||||
IL_76MD,
|
||||
@@ -363,7 +549,7 @@ UNIT_BY_TASK = {
|
||||
KC130,
|
||||
S_3B_Tanker,
|
||||
],
|
||||
AWACS: [E_3A, A_50, ],
|
||||
AWACS: [E_3A, A_50, KJ_2000],
|
||||
PinpointStrike: [
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_MTLB,
|
||||
@@ -510,6 +696,29 @@ UNIT_BY_TASK = {
|
||||
Artillery.M12_GMC,
|
||||
Artillery.Sturmpanzer_IV_Brummbär,
|
||||
|
||||
frenchpack.DIM__TOYOTA_BLUE,
|
||||
frenchpack.DIM__TOYOTA_DESERT,
|
||||
frenchpack.DIM__TOYOTA_GREEN,
|
||||
frenchpack.DIM__KAMIKAZE,
|
||||
|
||||
frenchpack.AMX_10RCR,
|
||||
frenchpack.AMX_10RCR_SEPAR,
|
||||
frenchpack.ERC_90,
|
||||
frenchpack.TRM_2000_PAMELA,
|
||||
frenchpack.VAB__50,
|
||||
frenchpack.VAB_MEPHISTO,
|
||||
frenchpack.VAB_T20_13,
|
||||
frenchpack.VBL__50,
|
||||
frenchpack.VBL_AANF1,
|
||||
frenchpack.VBAE_CRAB,
|
||||
frenchpack.VBAE_CRAB_MMP,
|
||||
frenchpack.AMX_30B2,
|
||||
frenchpack.Leclerc_Serie_XXI,
|
||||
frenchpack.DIM__TOYOTA_BLUE,
|
||||
frenchpack.DIM__TOYOTA_GREEN,
|
||||
frenchpack.DIM__TOYOTA_DESERT,
|
||||
frenchpack.DIM__KAMIKAZE,
|
||||
|
||||
],
|
||||
AirDefence: [
|
||||
|
||||
@@ -595,46 +804,100 @@ CARRIER_TAKEOFF_BAN = [
|
||||
]
|
||||
|
||||
"""
|
||||
Units separated by country. Currently only Russia and USA are supported.
|
||||
Units separated by country.
|
||||
country : DCS Country name
|
||||
"""
|
||||
FACTIONS = {
|
||||
|
||||
"Bluefor Modern": BLUEFOR_MODERN,
|
||||
"Bluefor Cold War 1970s": BLUEFOR_COLDWAR,
|
||||
"Bluefor Cold War (with A-4)": BLUEFOR_COLDWAR_A4,
|
||||
"Bluefor Cold War (with A-4 and MB339)": BLUEFOR_COLDWAR_MODS,
|
||||
|
||||
"USA 1955 (WW2 Pack)": USA_1955,
|
||||
"USA 1960": USA_1960,
|
||||
"USA 1965": USA_1965,
|
||||
"USA 1990": USA_1990,
|
||||
"USA 2005": USA_2005,
|
||||
"USA Aggressors 2005": US_Aggressors,
|
||||
|
||||
"Russia 1955": Russia_1955,
|
||||
"Russia 1965": Russia_1965,
|
||||
"Russia 1975": Russia_1975,
|
||||
"Russia 1990": Russia_1990,
|
||||
"Russia 2010": Russia_2010,
|
||||
"Iran 2015": Iran_2015,
|
||||
"Lybia 2011": Lybia_2011,
|
||||
"China 2000": China_2000,
|
||||
"North Korea 2000": NorthKorea_2000,
|
||||
"Insurgent": Insurgent,
|
||||
|
||||
"USA 2005": USA_2005,
|
||||
"USA 1990": USA_1990,
|
||||
"USA 1965": USA_1965,
|
||||
"USA 1960": USA_1960,
|
||||
"USA 1955 (Require WW2 Pack)": USA_1955,
|
||||
"Allies 1944 (Require WW2 Pack)": USA_1944,
|
||||
"Bluefor Modern": BLUEFOR_MODERN,
|
||||
"France 2005": France_2005,
|
||||
"France 1995": France_1995,
|
||||
"France 2005": France_2005,
|
||||
"France 2005 (Modded)": France_2005_Modded,
|
||||
|
||||
"Germany 1990": Germany_1990,
|
||||
|
||||
"Netherlands 1990": Netherlands_1990,
|
||||
"United Kingdown 1990": UnitedKingdom_1990,
|
||||
|
||||
"United Kingdom 1990": UnitedKingdom_1990,
|
||||
|
||||
"Spain 1990": Spain_1990,
|
||||
|
||||
"Italy 1990": Italy_1990,
|
||||
"Sweden 1990": Sweden_1990,
|
||||
"Ukraine 2010": Ukraine_2010,
|
||||
"India 2010": India_2010,
|
||||
"Pakistan 2015": Pakistan_2015,
|
||||
"Italy 1990 (With MB339)": Italy_1990_MB339,
|
||||
|
||||
"Israel 2000": Israel_2000,
|
||||
"Israel 1982": Israel_1982,
|
||||
"Israel 1973 (WW2 Pack)": Israel_1973,
|
||||
"Israel 1973": Israel_1973_NO_WW2_UNITS,
|
||||
"Israel 1948": Israel_1948,
|
||||
|
||||
"Arab Armies 1982": Syria_1982,
|
||||
"Arab Armies 1973": Syria_1973,
|
||||
"Arab Armies 1967 (WW2 Pack)": Syria_1967_WW2_Weapons,
|
||||
"Arab Armies 1967": Syria_1967,
|
||||
"Arab League 1948": Arab_Armies_1948,
|
||||
|
||||
"China 2010": China_2010,
|
||||
|
||||
"Sweden 1990": Sweden_1990,
|
||||
|
||||
"Australia 2005": Australia_2005,
|
||||
|
||||
"Canada 2005": Canada_2005,
|
||||
|
||||
"Japan 2005": Japan_2005,
|
||||
|
||||
"Turkey 2005": Turkey_2005,
|
||||
|
||||
"United Arab Emirates 2005": UAE_2005,
|
||||
"Germany 1944 (Require WW2 Pack)": Germany_1944
|
||||
|
||||
"Ukraine 2010": Ukraine_2010,
|
||||
|
||||
"India 2010": India_2010,
|
||||
|
||||
"Libya 2011": Libya_2011,
|
||||
|
||||
"Syria 2011": Syria_2011,
|
||||
|
||||
|
||||
"Pakistan 2015": Pakistan_2015,
|
||||
|
||||
"Iran 2015": Iran_2015,
|
||||
|
||||
"North Korea 2000": NorthKorea_2000,
|
||||
|
||||
"Insurgent": Insurgent,
|
||||
"Insurgent (Modded)": Insurgent_modded,
|
||||
|
||||
"PMC (American)": PMC_WESTERN_A,
|
||||
"PMC (American) - MB339": PMC_WESTERN_B,
|
||||
"PMC (Russian)": PMC_RUSSIAN,
|
||||
|
||||
"Allies 1944 (WW2 Pack)": USA_1944,
|
||||
"USA 1944 (WW2 Pack)": ALLIES_1944,
|
||||
"UK 1944 (WW2 Pack)": UK_1944,
|
||||
|
||||
"Germany 1944 (WW2 Pack)": Germany_1944,
|
||||
"Germany 1944 Easy (WW2 Pack)": Germany_1944_Easy,
|
||||
|
||||
}
|
||||
BLUEFOR_FACTIONS = [FACTIONS[f]["country"] for f in FACTIONS if FACTIONS[f]["side"] == "blue"]
|
||||
print(BLUEFOR_FACTIONS)
|
||||
|
||||
|
||||
CARRIER_TYPE_BY_PLANE = {
|
||||
@@ -696,11 +959,13 @@ PLANE_PAYLOAD_OVERRIDES = {
|
||||
},
|
||||
A_10A: COMMON_OVERRIDE,
|
||||
A_10C: COMMON_OVERRIDE,
|
||||
A_10C_2: COMMON_OVERRIDE,
|
||||
AV8BNA: COMMON_OVERRIDE,
|
||||
C_101CC: COMMON_OVERRIDE,
|
||||
F_5E_3: COMMON_OVERRIDE,
|
||||
F_14B: COMMON_OVERRIDE,
|
||||
F_15C: COMMON_OVERRIDE,
|
||||
F_15E: COMMON_OVERRIDE,
|
||||
F_16C_50: COMMON_OVERRIDE,
|
||||
JF_17: COMMON_OVERRIDE,
|
||||
M_2000C: COMMON_OVERRIDE,
|
||||
@@ -728,15 +993,37 @@ PLANE_PAYLOAD_OVERRIDES = {
|
||||
SA342L:COMMON_OVERRIDE,
|
||||
SA342Mistral:COMMON_OVERRIDE,
|
||||
Mi_8MT:COMMON_OVERRIDE,
|
||||
Mi_24V:COMMON_OVERRIDE,
|
||||
Mi_28N:COMMON_OVERRIDE,
|
||||
Ka_50:COMMON_OVERRIDE,
|
||||
L_39ZA:COMMON_OVERRIDE,
|
||||
L_39C:COMMON_OVERRIDE,
|
||||
Su_17M4: COMMON_OVERRIDE,
|
||||
F_4E: COMMON_OVERRIDE,
|
||||
|
||||
AH_64D:{
|
||||
CAS: "AGM-114K*16"
|
||||
},
|
||||
P_47D_30:COMMON_OVERRIDE,
|
||||
P_47D_30bl1:COMMON_OVERRIDE,
|
||||
P_47D_40:COMMON_OVERRIDE,
|
||||
B_17G: COMMON_OVERRIDE,
|
||||
P_51D: COMMON_OVERRIDE,
|
||||
P_51D_30_NA: COMMON_OVERRIDE,
|
||||
FW_190D9: COMMON_OVERRIDE,
|
||||
FW_190A8: COMMON_OVERRIDE,
|
||||
Bf_109K_4: COMMON_OVERRIDE,
|
||||
SpitfireLFMkIXCW: COMMON_OVERRIDE,
|
||||
SpitfireLFMkIX: COMMON_OVERRIDE,
|
||||
A_20G: COMMON_OVERRIDE,
|
||||
A_4E_C: COMMON_OVERRIDE,
|
||||
MB_339PAN: COMMON_OVERRIDE,
|
||||
Rafale_M: COMMON_OVERRIDE,
|
||||
Rafale_A_S: COMMON_OVERRIDE,
|
||||
OH_58D: COMMON_OVERRIDE,
|
||||
F_16A: COMMON_OVERRIDE,
|
||||
MQ_9_Reaper: COMMON_OVERRIDE,
|
||||
RQ_1A_Predator: COMMON_OVERRIDE,
|
||||
WingLoong_I: COMMON_OVERRIDE,
|
||||
AH_1W: COMMON_OVERRIDE,
|
||||
AH_64D: COMMON_OVERRIDE,
|
||||
AH_64A: COMMON_OVERRIDE,
|
||||
|
||||
Su_25TM: {
|
||||
SEAD: "Kh-31P*2_Kh-25ML*4_R-73*2_L-081_MPS410",
|
||||
@@ -756,8 +1043,6 @@ PLANE_LIVERY_OVERRIDES = {
|
||||
FA_18C_hornet: "VFA-34", # default livery for the hornet is blue angels one
|
||||
}
|
||||
|
||||
|
||||
|
||||
"""
|
||||
Possible time periods for new games
|
||||
|
||||
@@ -792,7 +1077,11 @@ TIME_PERIODS = {
|
||||
"Modern - Summer [2010]": datetime(2010, 6, 1),
|
||||
"Modern - Fall [2010]": datetime(2010, 10, 1),
|
||||
"Georgian War [2008]": datetime(2008, 8, 7),
|
||||
"Syrian War [2011]": datetime(2011, 8, 7),
|
||||
"Syrian War [2011]": datetime(2011, 3, 15),
|
||||
"6 days war [1967]": datetime(1967, 6, 5),
|
||||
"Yom Kippour War [1973]": datetime(1973, 10, 6),
|
||||
"Lebanon War [1982]": datetime(1982, 6, 6),
|
||||
"Arab-Israeli War [1948]": datetime(1948, 5, 15),
|
||||
}
|
||||
|
||||
REWARDS = {
|
||||
@@ -809,12 +1098,14 @@ CARRIER_CAPABLE = [
|
||||
F_14B,
|
||||
AV8BNA,
|
||||
Su_33,
|
||||
A_4E_C,
|
||||
|
||||
UH_1H,
|
||||
Mi_8MT,
|
||||
Ka_50,
|
||||
AH_1W,
|
||||
OH_58D,
|
||||
UH_60A,
|
||||
|
||||
SA342L,
|
||||
SA342M,
|
||||
@@ -830,6 +1121,7 @@ LHA_CAPABLE = [
|
||||
Ka_50,
|
||||
AH_1W,
|
||||
OH_58D,
|
||||
UH_60A,
|
||||
|
||||
SA342L,
|
||||
SA342M,
|
||||
@@ -837,6 +1129,7 @@ LHA_CAPABLE = [
|
||||
SA342Mistral
|
||||
]
|
||||
|
||||
|
||||
"""
|
||||
---------- END OF CONFIGURATION SECTION
|
||||
"""
|
||||
@@ -903,6 +1196,8 @@ def find_infantry(country_name: str) -> typing.List[UnitType]:
|
||||
def unit_type_name(unit_type) -> str:
|
||||
return unit_type.id and unit_type.id or unit_type.name
|
||||
|
||||
def unit_type_name_2(unit_type) -> str:
|
||||
return unit_type.name and unit_type.name or unit_type.id
|
||||
|
||||
def unit_type_from_name(name: str) -> UnitType:
|
||||
if name in vehicle_map:
|
||||
@@ -1047,4 +1342,5 @@ class DefaultLiveries:
|
||||
OH_58D.Liveries = DefaultLiveries
|
||||
F_16C_50.Liveries = DefaultLiveries
|
||||
P_51D_30_NA.Liveries = DefaultLiveries
|
||||
Ju_88A4.Liveries = DefaultLiveries
|
||||
Ju_88A4.Liveries = DefaultLiveries
|
||||
B_17G.Liveries = DefaultLiveries
|
||||
@@ -65,25 +65,10 @@ class Event:
|
||||
else:
|
||||
return self.departure_cp
|
||||
|
||||
@property
|
||||
def threat_description(self) -> str:
|
||||
return ""
|
||||
|
||||
def flight_name(self, for_task: typing.Type[typing.Type[Task]]) -> str:
|
||||
return "Flight"
|
||||
|
||||
@property
|
||||
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
return []
|
||||
|
||||
@property
|
||||
def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
return []
|
||||
|
||||
@property
|
||||
def player_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
return []
|
||||
|
||||
@property
|
||||
def global_cp_available(self) -> bool:
|
||||
return False
|
||||
@@ -193,9 +178,12 @@ class Event:
|
||||
for i, ground_object in enumerate(cp.ground_objects):
|
||||
if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
||||
for g in ground_object.groups:
|
||||
if not hasattr(g, "units_losts"):
|
||||
g.units_losts = []
|
||||
for u in g.units:
|
||||
if u.name == destroyed_ground_unit_name:
|
||||
g.units.remove(u)
|
||||
g.units_losts.append(u)
|
||||
destroyed_units = destroyed_units + 1
|
||||
info.text = u.type
|
||||
ucount = sum([len(g.units) for g in ground_object.groups])
|
||||
@@ -206,10 +194,10 @@ class Event:
|
||||
|
||||
# ------------------------------
|
||||
# Captured bases
|
||||
if self.game.player_country in db.BLUEFOR_FACTIONS:
|
||||
coalition = 2 # Value in DCS mission event for BLUE
|
||||
else:
|
||||
coalition = 1 # Value in DCS mission event for RED
|
||||
#if self.game.player_country in db.BLUEFOR_FACTIONS:
|
||||
coalition = 2 # Value in DCS mission event for BLUE
|
||||
#else:
|
||||
# coalition = 1 # Value in DCS mission event for RED
|
||||
|
||||
for captured in debriefing.base_capture_events:
|
||||
try:
|
||||
@@ -253,6 +241,11 @@ class Event:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# Destroyed units carcass
|
||||
# -------------------------
|
||||
for destroyed_unit in debriefing.destroyed_units:
|
||||
self.game.add_destroyed_units(destroyed_unit)
|
||||
|
||||
# -----------------------------------
|
||||
# Compute damage to bases
|
||||
for cp in self.game.theater.player_points():
|
||||
@@ -274,6 +267,8 @@ class Event:
|
||||
|
||||
ratio = (1.0 + enemy_casualties) / (1.0 + ally_casualties)
|
||||
|
||||
player_aggresive = cp.stances[enemy_cp.id] in [CombatStance.AGGRESSIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]
|
||||
|
||||
if ally_units_alive == 0:
|
||||
player_won = False
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
@@ -296,11 +291,21 @@ class Event:
|
||||
else:
|
||||
delta = DEFEAT_INFLUENCE
|
||||
elif ally_casualties > enemy_casualties:
|
||||
player_won = False
|
||||
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
|
||||
|
||||
if ally_units_alive > 2*enemy_units_alive and player_aggresive:
|
||||
# Even with casualties if the enemy is overwhelmed, they are going to lose ground
|
||||
player_won = True
|
||||
delta = MINOR_DEFEAT_INFLUENCE
|
||||
elif ally_units_alive > 3*enemy_units_alive and player_aggresive:
|
||||
player_won = True
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
else:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
# But is the enemy is not outnumbered, we lose
|
||||
player_won = False
|
||||
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
else:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
|
||||
# No progress with defensive strategies
|
||||
if player_won and cp.stances[enemy_cp.id] in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]:
|
||||
@@ -320,7 +325,7 @@ class Event:
|
||||
enemy_cp.base.affect_strength(delta)
|
||||
cp.base.affect_strength(-delta)
|
||||
info = Information("Frontline Report",
|
||||
"Our ground forces from " + cp.name + " are losing ground against the enemy forces from" + enemy_cp.name,
|
||||
"Our ground forces from " + cp.name + " are losing ground against the enemy forces from " + enemy_cp.name,
|
||||
self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
|
||||
|
||||
@@ -4,16 +4,6 @@ from userdata.debriefing import Debriefing
|
||||
|
||||
|
||||
class FrontlineAttackEvent(Event):
|
||||
TARGET_VARIETY = 2
|
||||
TARGET_AMOUNT_FACTOR = 0.5
|
||||
ATTACKER_AMOUNT_FACTOR = 0.4
|
||||
ATTACKER_DEFENDER_FACTOR = 0.7
|
||||
STRENGTH_INFLUENCE = 0.3
|
||||
SUCCESS_FACTOR = 1.5
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
return "{} vehicles".format(self.to_cp.base.assemble_count())
|
||||
|
||||
@property
|
||||
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
@@ -26,32 +16,11 @@ class FrontlineAttackEvent(Event):
|
||||
def global_cp_available(self) -> bool:
|
||||
return True
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == CAS:
|
||||
return "CAS flight"
|
||||
elif for_task == CAP:
|
||||
return "CAP flight"
|
||||
elif for_task == PinpointStrike:
|
||||
return "Ground attack"
|
||||
|
||||
def __str__(self):
|
||||
return "Frontline attack"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
|
||||
if self.game.player_name == self.attacker_name:
|
||||
attacker_country = self.game.player_country
|
||||
defender_country = self.game.enemy_country
|
||||
else:
|
||||
attacker_country = self.game.enemy_country
|
||||
defender_country = self.game.player_country
|
||||
|
||||
# TODO : Rework
|
||||
#alive_attackers = sum([v for k, v in debriefing.alive_units.get(attacker_country, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||
#alive_defenders = sum([v for k, v in debriefing.alive_units.get(defender_country, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||
#attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
|
||||
attackers_success = True
|
||||
|
||||
if self.from_cp.captured:
|
||||
return attackers_success
|
||||
else:
|
||||
@@ -65,46 +34,20 @@ class FrontlineAttackEvent(Event):
|
||||
self.to_cp.base.affect_strength(-0.1)
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
# assert CAS in flights and CAP in flights and len(flights) == 2, "Invalid flights"
|
||||
|
||||
op = FrontlineAttackOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
departure_cp=self.departure_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
defenders = self.to_cp.base.assemble_attack()
|
||||
max_attackers = int(math.ceil(sum(defenders.values()) * self.ATTACKER_DEFENDER_FACTOR))
|
||||
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||
op.setup(defenders=defenders,
|
||||
attackers=attackers,
|
||||
strikegroup=flights[CAS],
|
||||
escort=flights[CAP],
|
||||
interceptors=assigned_units_from(self.to_cp.base.scramble_interceptors(1)))
|
||||
|
||||
self.operation = op
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
# assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
op = FrontlineAttackOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
departure_cp=self.departure_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
defenders = self.to_cp.base.assemble_attack()
|
||||
|
||||
max_attackers = int(math.ceil(sum(defenders.values())))
|
||||
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||
|
||||
op.setup(defenders=defenders,
|
||||
attackers=attackers,
|
||||
strikegroup=assigned_units_from(self.from_cp.base.scramble_cas(1)),
|
||||
escort=assigned_units_from(self.from_cp.base.scramble_sweep(1)),
|
||||
interceptors=flights[CAP])
|
||||
|
||||
self.operation = op
|
||||
|
||||
|
||||
50
game/factions/australia_2005.py
Normal file
50
game/factions/australia_2005.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Australia_2005 = {
|
||||
"country": "Australia",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
FA_18C_hornet,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.MBT_Leopard_1A3,
|
||||
Armor.APC_M113,
|
||||
Armor.IFV_LAV_25,
|
||||
Armor.IFV_MCV_80,
|
||||
|
||||
UH_1H,
|
||||
AH_1W, # Standing as EC Tiger
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.Rapier_FSA_Launcher,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
AirDefence.Rapier_FSA_Launcher,
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "destroyer": [
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "lhanames": [
|
||||
"HMAS Canberra",
|
||||
"HMAS Adelaide"
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
58
game/factions/bluefor_coldwar.py
Normal file
58
game/factions/bluefor_coldwar.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
BLUEFOR_COLDWAR = {
|
||||
"country": "Combined Joint Task Forces Blue",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
|
||||
F_14B,
|
||||
F_4E,
|
||||
F_5E_3,
|
||||
A_10A,
|
||||
AJS37,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
], "aircraft_carrier": [
|
||||
CVN_74_John_C__Stennis,
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "carrier_names": [
|
||||
"CVN-71 Theodore Roosevelt",
|
||||
"CVN-72 Abraham Lincoln",
|
||||
"CVN-73 George Washington",
|
||||
"CVN-74 John C. Stennis",
|
||||
], "lhanames": [
|
||||
"LHA-1 Tarawa",
|
||||
"LHA-2 Saipan",
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
], "boat": [
|
||||
], "has_jtac": True
|
||||
}
|
||||
65
game/factions/bluefor_coldwar_a4.py
Normal file
65
game/factions/bluefor_coldwar_a4.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
|
||||
BLUEFOR_COLDWAR_A4 = {
|
||||
"country": "Combined Joint Task Forces Blue",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
|
||||
F_14B,
|
||||
F_4E,
|
||||
F_5E_3,
|
||||
A_10A,
|
||||
AJS37,
|
||||
A_4E_C,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
], "aircraft_carrier": [
|
||||
CVN_74_John_C__Stennis,
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
"CVN-71 Theodore Roosevelt",
|
||||
"CVN-72 Abraham Lincoln",
|
||||
"CVN-73 George Washington",
|
||||
"CVN-74 John C. Stennis",
|
||||
], "lhanames": [
|
||||
"LHA-1 Tarawa",
|
||||
"LHA-2 Saipan",
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
], "boat": [
|
||||
], "requirements": {
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
|
||||
}, "has_jtac": True
|
||||
}
|
||||
68
game/factions/bluefor_coldwar_mods.py
Normal file
68
game/factions/bluefor_coldwar_mods.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
|
||||
BLUEFOR_COLDWAR_MODS = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
|
||||
F_14B,
|
||||
F_4E,
|
||||
F_5E_3,
|
||||
A_10A,
|
||||
AJS37,
|
||||
A_4E_C,
|
||||
MB_339PAN,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
], "aircraft_carrier": [
|
||||
CVN_74_John_C__Stennis,
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
"CVN-71 Theodore Roosevelt",
|
||||
"CVN-72 Abraham Lincoln",
|
||||
"CVN-73 George Washington",
|
||||
"CVN-74 John C. Stennis",
|
||||
], "lhanames": [
|
||||
"LHA-1 Tarawa",
|
||||
"LHA-2 Saipan",
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
], "boat": [
|
||||
], "requirements": {
|
||||
"MB-339A": "http://www.freccetricolorivirtuali.net/",
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
|
||||
}, "has_jtac": True
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
BLUEFOR_MODERN = {
|
||||
"country": "USA",
|
||||
"country": "Combined Joint Task Forces Blue",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
|
||||
@@ -20,6 +20,7 @@ BLUEFOR_MODERN = {
|
||||
Su_25T,
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
AV8BNA,
|
||||
AJS37,
|
||||
|
||||
@@ -31,6 +32,8 @@ BLUEFOR_MODERN = {
|
||||
UH_1H,
|
||||
AH_64D,
|
||||
Ka_50,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.MBT_Leopard_2,
|
||||
@@ -74,5 +77,7 @@ BLUEFOR_MODERN = {
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
]
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
43
game/factions/canada_2005.py
Normal file
43
game/factions/canada_2005.py
Normal file
@@ -0,0 +1,43 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Canada_2005 = {
|
||||
"country": "Canada",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
FA_18C_hornet,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
Armor.MBT_Leopard_1A3,
|
||||
Armor.MBT_Leopard_2,
|
||||
Armor.IFV_LAV_25,
|
||||
Armor.APC_M113,
|
||||
Armor.IFV_MCV_80,
|
||||
|
||||
UH_1H,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
], "destroyer": [
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
China_2000 = {
|
||||
China_2010 = {
|
||||
"country": "China",
|
||||
"side": "red",
|
||||
"units": [
|
||||
@@ -20,9 +20,10 @@ China_2000 = {
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
A_50,
|
||||
KJ_2000,
|
||||
|
||||
Mi_8MT,
|
||||
Mi_28N,
|
||||
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C, # Standing as HQ-9+
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
@@ -37,7 +38,9 @@ China_2000 = {
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
@@ -56,8 +59,22 @@ China_2000 = {
|
||||
Type_052C_Destroyer
|
||||
], "cruiser": [
|
||||
Type_054A_Frigate,
|
||||
], "helicopter_carrier": [
|
||||
Type_071_Amphibious_Transport_Dock,
|
||||
], "lhanames": [
|
||||
"Kunlun Shan",
|
||||
"Jinggang Shan",
|
||||
"Changbai Shan",
|
||||
"Yimeng Shan",
|
||||
"Longhu Shan",
|
||||
"Wuzhi Shan",
|
||||
"Wudang Shan"
|
||||
], "carrier_names": [
|
||||
"001 Liaoning",
|
||||
"002 Shandong",
|
||||
]
|
||||
], "boat":[
|
||||
"Type54GroupGenerator"
|
||||
],
|
||||
"has_jtac": True,
|
||||
"jtac_unit": WingLoong_I
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
France_1995 = {
|
||||
"country": "France",
|
||||
@@ -17,6 +17,7 @@ France_1995 = {
|
||||
|
||||
SA342M,
|
||||
SA342L,
|
||||
SA342Mistral,
|
||||
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.TPz_Fuchs, # Standing as VAB
|
||||
@@ -40,5 +41,7 @@ France_1995 = {
|
||||
], "shorad": [
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.SAM_Roland_ADS
|
||||
]
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
France_2005 = {
|
||||
"country": "France",
|
||||
@@ -18,6 +18,7 @@ France_2005 = {
|
||||
|
||||
SA342M,
|
||||
SA342L,
|
||||
SA342Mistral,
|
||||
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.TPz_Fuchs, # Standing as VAB
|
||||
@@ -50,10 +51,12 @@ France_2005 = {
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
"R91 Charles de Gaulle",
|
||||
"PA Charles de Gaulle",
|
||||
], "lhanames": [
|
||||
"L9013 Mistral",
|
||||
"L9014 Tonerre",
|
||||
"L9015 Dixmude"
|
||||
]
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
80
game/factions/france_modded.py
Normal file
80
game/factions/france_modded.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
import pydcs_extensions.frenchpack.frenchpack as frenchpack
|
||||
from pydcs_extensions.rafale.rafale import Rafale_M, Rafale_A_S
|
||||
|
||||
France_2005_Modded = {
|
||||
"country": "France",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
M_2000C,
|
||||
Mirage_2000_5,
|
||||
Rafale_M,
|
||||
Rafale_A_S,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
SA342M,
|
||||
SA342L,
|
||||
SA342Mistral,
|
||||
|
||||
Armor.MBT_Leclerc,
|
||||
Artillery.SPH_M109_Paladin, # Standing as AMX30 AuF1
|
||||
Artillery.MLRS_M270,
|
||||
|
||||
frenchpack.AMX_10RCR,
|
||||
frenchpack.AMX_10RCR_SEPAR,
|
||||
frenchpack.ERC_90,
|
||||
frenchpack.TRM_2000_PAMELA,
|
||||
frenchpack.VAB__50,
|
||||
frenchpack.VAB_MEPHISTO,
|
||||
frenchpack.VAB_T20_13,
|
||||
frenchpack.VBL__50,
|
||||
frenchpack.VBL_AANF1,
|
||||
frenchpack.VBAE_CRAB,
|
||||
frenchpack.VBAE_CRAB_MMP,
|
||||
frenchpack.AMX_30B2,
|
||||
frenchpack.Leclerc_Serie_XXI,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Roland_ADS,
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.HQ_7_Self_Propelled_LN, # Standing as Crotale
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
|
||||
], "shorad": [
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.SAM_Roland_ADS
|
||||
], "aircraft_carrier": [
|
||||
CVN_74_John_C__Stennis, # Standing as CDG Aircraft Carrier
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa, # Standing as Mistral Class
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
"PA Charles de Gaulle",
|
||||
], "lhanames": [
|
||||
"L9013 Mistral",
|
||||
"L9014 Tonerre",
|
||||
"L9015 Dixmude"
|
||||
], "boat": [
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "requirements": {
|
||||
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974",
|
||||
"RAFALE 2.5.5": "https://www.digitalcombatsimulator.com/fr/files/3307478/",
|
||||
}, "has_jtac": True
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from game.data.building_data import WW2_GERMANY_BUILDINGS
|
||||
from game.data.doctrine import WWII_DOCTRINE
|
||||
|
||||
Germany_1944 = {
|
||||
"country": "Third Reich",
|
||||
"side": "red",
|
||||
@@ -29,8 +32,16 @@ Germany_1944 = {
|
||||
|
||||
Infantry.Infantry_Mauser_98,
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
],
|
||||
"shorad":[
|
||||
],"requirements":{
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"shorad": [
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
]
|
||||
],
|
||||
"objects": WW2_GERMANY_BUILDINGS,
|
||||
"doctrine": WWII_DOCTRINE,
|
||||
"boat": ["UBoatGroupGenerator", "SchnellbootGroupGenerator"],
|
||||
"boat_count": 2,
|
||||
"missiles": ["V1GroupGenerator"],
|
||||
"missiles_count": 1
|
||||
}
|
||||
40
game/factions/germany_1944_easy.py
Normal file
40
game/factions/germany_1944_easy.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from game.data.building_data import WW2_GERMANY_BUILDINGS
|
||||
from game.data.doctrine import WWII_DOCTRINE
|
||||
|
||||
Germany_1944_Easy = {
|
||||
"country": "Third Reich",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
FW_190A8,
|
||||
FW_190D9,
|
||||
Bf_109K_4,
|
||||
Ju_88A4,
|
||||
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Artillery.Sturmpanzer_IV_Brummbär,
|
||||
|
||||
Unarmed.Sd_Kfz_2,
|
||||
Unarmed.Sd_Kfz_7,
|
||||
Unarmed.Kübelwagen_82,
|
||||
|
||||
Infantry.Infantry_Mauser_98,
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
],"requirements":{
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"shorad":[
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
],
|
||||
"objects": WW2_GERMANY_BUILDINGS,
|
||||
"doctrine": WWII_DOCTRINE,
|
||||
"boat": ["UBoatGroupGenerator", "SchnellbootGroupGenerator"],
|
||||
"boat_count": 1,
|
||||
"missiles": ["V1GroupGenerator"],
|
||||
"missiles_count": 1
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Germany_1990 = {
|
||||
"country": "Germany",
|
||||
@@ -39,5 +39,7 @@ Germany_1990 = {
|
||||
"shorad":[
|
||||
AirDefence.SPAAA_Gepard,
|
||||
AirDefence.SAM_Roland_ADS,
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
India_2010 = {
|
||||
"country": "India",
|
||||
@@ -49,5 +49,7 @@ India_2010 = {
|
||||
FSG_1241_1MP_Molniya,
|
||||
], "carrier_names": [
|
||||
"INS Vikramaditya"
|
||||
]
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator", "MolniyaGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -12,11 +12,13 @@ Insurgent = {
|
||||
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
|
||||
|
||||
Armor.APC_Cobra,
|
||||
Armor.APC_MTLB,
|
||||
Armor.ARV_BRDM_2,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
Infantry.Infantry_Soldier_Insurgents,
|
||||
Infantry.Soldier_RPG,
|
||||
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
|
||||
33
game/factions/insurgent_modded.py
Normal file
33
game/factions/insurgent_modded.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from pydcs_extensions.frenchpack.frenchpack import DIM__TOYOTA_BLUE, DIM__TOYOTA_DESERT, DIM__TOYOTA_GREEN, \
|
||||
DIM__KAMIKAZE
|
||||
|
||||
Insurgent_modded = {
|
||||
"country": "Insurgents",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
AirDefence.AAA_ZU_23_Insurgent_Closed,
|
||||
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
|
||||
|
||||
DIM__TOYOTA_BLUE,
|
||||
DIM__TOYOTA_DESERT,
|
||||
DIM__TOYOTA_GREEN,
|
||||
DIM__KAMIKAZE,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.APC_Cobra,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
Infantry.Infantry_Soldier_Insurgents,
|
||||
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
], "requirements": {
|
||||
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974",
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Iran_2015 = {
|
||||
"country": "Iran",
|
||||
@@ -52,5 +52,7 @@ Iran_2015 = {
|
||||
"shorad":[
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.AAA_ZU_23_Insurgent_Closed
|
||||
], "boat":[
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator", "KiloSubGroupGenerator"
|
||||
]
|
||||
}
|
||||
33
game/factions/israel_1948.py
Normal file
33
game/factions/israel_1948.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Israel_1948 = {
|
||||
"country": "Israel",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
Bf_109K_4, # Standing as Avia S-199
|
||||
B_17G,
|
||||
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.APC_M2A1,
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_SMLE_No_4_Mk_1,
|
||||
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
Armed_speedboat,
|
||||
],"requirements":{
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"shorad": [
|
||||
AirDefence.AAA_Bofors_40mm
|
||||
], "boat": [
|
||||
], "has_jtac": False
|
||||
}
|
||||
112
game/factions/israel_1973.py
Normal file
112
game/factions/israel_1973.py
Normal file
@@ -0,0 +1,112 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
|
||||
Israel_1973 = {
|
||||
"country": "Israel",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
F_4E,
|
||||
A_4E_C,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.APC_M2A1,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
|
||||
Armed_speedboat,
|
||||
], "requirements": {
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
}, "shorad": [
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
AirDefence.AAA_Bofors_40mm
|
||||
], "boat": [
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
Israel_1973_NO_WW2_UNITS = {
|
||||
"country": "Israel",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
F_4E,
|
||||
A_4E_C,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
|
||||
Armed_speedboat,
|
||||
], "requirements": {
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
|
||||
}, "shorad": [
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
], "boat": [
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
Israel_1982 = {
|
||||
"country": "Israel",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
F_4E,
|
||||
A_4E_C,
|
||||
F_15C,
|
||||
F_16A,
|
||||
F_16C_50,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
AH_1W,
|
||||
|
||||
Armor.APC_M113,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_Merkava_Mk__4,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
|
||||
Armed_speedboat,
|
||||
], "requirements": {
|
||||
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
|
||||
}, "shorad": [
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
], "boat": [
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Israel_2000 = {
|
||||
"country": "Israel",
|
||||
@@ -9,6 +9,7 @@ Israel_2000 = {
|
||||
"units":[
|
||||
F_16C_50,
|
||||
F_15C,
|
||||
F_15E,
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
@@ -21,6 +22,10 @@ Israel_2000 = {
|
||||
|
||||
Armor.MBT_Merkava_Mk__4,
|
||||
Armor.APC_M113,
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.ATGM_M1045_HMMWV_TOW,
|
||||
Artillery.SPH_M109_Paladin,
|
||||
Artillery.MLRS_M270,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
@@ -31,7 +36,9 @@ Israel_2000 = {
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
]
|
||||
], "boat": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Italy_1990 = {
|
||||
"country": "Italy",
|
||||
@@ -39,8 +39,10 @@ Italy_1990 = {
|
||||
Oliver_Hazzard_Perry_class,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "lha_names": [
|
||||
], "lhanames": [
|
||||
"Giuseppe Garibaldi",
|
||||
"Cavour",
|
||||
]
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
52
game/factions/italy_1990_mb339.py
Normal file
52
game/factions/italy_1990_mb339.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
|
||||
Italy_1990_MB339 = {
|
||||
"country": "Italy",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
Tornado_IDS,
|
||||
AV8BNA,
|
||||
MB_339PAN,
|
||||
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
AH_1W,
|
||||
UH_1H,
|
||||
|
||||
Armor.MBT_Leopard_1A3, # OF-40 MBT
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad":[
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "lhanames": [
|
||||
"Giuseppe Garibaldi",
|
||||
"Cavour",
|
||||
], "boat": [
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "requirements": {
|
||||
"MB-339A": "http://www.freccetricolorivirtuali.net/",
|
||||
}, "has_jtac": True
|
||||
}
|
||||
54
game/factions/japan_2005.py
Normal file
54
game/factions/japan_2005.py
Normal file
@@ -0,0 +1,54 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Japan_2005 = {
|
||||
"country": "Japan",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
F_15C, # F-15J/DJ
|
||||
F_16C_50, # F-2A/B
|
||||
F_4E, # F-4EJ
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
AH_1W,
|
||||
AH_64D,
|
||||
|
||||
Armor.MBT_Merkava_Mk__4, # Standing as Type 10 MBT
|
||||
Armor.MBT_M1A2_Abrams, # Standing as Type 90 MBT
|
||||
Armor.IFV_Marder, # Standing as Type 89 IFV
|
||||
Armor.TPz_Fuchs, # Standing as Type 96 APC
|
||||
Armor.IFV_LAV_25, # Standing as Type 16 or Type 87
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
|
||||
Artillery.MLRS_M270,
|
||||
Artillery.SPH_M109_Paladin, # Standing as Type 99 SPH
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Patriot_EPP_III,
|
||||
|
||||
LHA_1_Tarawa,
|
||||
], "shorad": [
|
||||
AirDefence.SPAAA_Gepard, # Type 87 SPAG
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa, # Standing as Hyuga-class helicopter carrier
|
||||
], "destroyer": [
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "lhanames": [
|
||||
"Hyuga",
|
||||
"Ise",
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Lybia_2011 = {
|
||||
"country": "Russia",
|
||||
Libya_2011 = {
|
||||
"country": "Libya",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
@@ -44,5 +43,7 @@ Lybia_2011 = {
|
||||
"shorad":[
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Netherlands_1990 = {
|
||||
"country": "The Netherlands",
|
||||
@@ -30,7 +30,9 @@ Netherlands_1990 = {
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
]
|
||||
}
|
||||
], "boat": [
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
NorthKorea_2000 = {
|
||||
"country": "North Korea",
|
||||
@@ -46,5 +46,8 @@ NorthKorea_2000 = {
|
||||
"shorad":[
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
],
|
||||
"boat": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Pakistan_2015 = {
|
||||
"country": "Pakistan",
|
||||
@@ -34,5 +34,9 @@ Pakistan_2015 = {
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
|
||||
AirDefence.AAA_ZU_23_Closed
|
||||
]
|
||||
], "boat": [
|
||||
"Type54GroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
],
|
||||
"has_jtac": True,
|
||||
"jtac_unit": WingLoong_I
|
||||
}
|
||||
|
||||
91
game/factions/private_miltary_companies.py
Normal file
91
game/factions/private_miltary_companies.py
Normal file
@@ -0,0 +1,91 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
|
||||
PMC_WESTERN_A = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
C_101CC,
|
||||
|
||||
UH_1H,
|
||||
Mi_8MT,
|
||||
OH_58D,
|
||||
SA342M,
|
||||
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.IFV_MCV_80,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
|
||||
Armed_speedboat,
|
||||
], "shorad":[
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
PMC_WESTERN_B = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
MB_339PAN,
|
||||
C_101CC,
|
||||
|
||||
UH_1H,
|
||||
Mi_8MT,
|
||||
OH_58D,
|
||||
SA342M,
|
||||
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.IFV_MCV_80,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
|
||||
Armed_speedboat,
|
||||
], "shorad":[
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
], "has_jtac": True,
|
||||
"requirements": {
|
||||
"MB-339A": "http://www.freccetricolorivirtuali.net/",
|
||||
}
|
||||
}
|
||||
|
||||
PMC_RUSSIAN = {
|
||||
"country": "Russia",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
L_39C,
|
||||
L_39ZA,
|
||||
|
||||
Mi_8MT,
|
||||
Mi_24V,
|
||||
Ka_50,
|
||||
|
||||
Armor.APC_Cobra,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.ARV_BRDM_2,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
|
||||
Armed_speedboat,
|
||||
], "shorad":[
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
@@ -49,5 +49,7 @@ Russia_1965 = {
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.AAA_ZU_23_Closed
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -65,5 +65,7 @@ Russia_1975 = {
|
||||
FF_1135M_Rezky,
|
||||
], "cruiser": [
|
||||
CGN_1144_2_Pyotr_Velikiy,
|
||||
], "boat": [
|
||||
"RussianNavyGroupGenerator", "KiloSubGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Russia_1990 = {
|
||||
"country": "Russia",
|
||||
@@ -17,6 +17,7 @@ Russia_1990 = {
|
||||
|
||||
Su_24M,
|
||||
Su_25,
|
||||
Ka_50,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
@@ -58,11 +59,16 @@ Russia_1990 = {
|
||||
AirDefence.SAM_SA_9_Strela_1_9P31,
|
||||
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
], "carrier_names": [
|
||||
"Admiral Kuznetov",
|
||||
"Admiral Gorshkov"
|
||||
], "aircraft_carrier": [
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
], "destroyer": [
|
||||
FF_1135M_Rezky,
|
||||
], "cruiser": [
|
||||
FSG_1241_1MP_Molniya,
|
||||
], "boat":[
|
||||
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Russia_2010 = {
|
||||
"country": "Russia",
|
||||
"side": "red",
|
||||
"units": [
|
||||
AJS37,
|
||||
MiG_23MLD,
|
||||
Su_25,
|
||||
|
||||
Su_27,
|
||||
Su_30,
|
||||
Su_33,
|
||||
MiG_29S,
|
||||
MiG_31,
|
||||
|
||||
Su_25,
|
||||
Su_25T,
|
||||
Su_34,
|
||||
Su_24M,
|
||||
@@ -28,6 +29,8 @@ Russia_2010 = {
|
||||
|
||||
Ka_50,
|
||||
Mi_8MT,
|
||||
Mi_24V,
|
||||
Mi_28N,
|
||||
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6,
|
||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
|
||||
@@ -37,6 +40,9 @@ Russia_2010 = {
|
||||
Armor.MBT_T_90,
|
||||
Armor.MBT_T_80U,
|
||||
Armor.MBT_T_72B,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.IFV_BMP_2,
|
||||
Armor.IFV_BMP_3,
|
||||
|
||||
Artillery.MLRS_9K57_Uragan_BM_27,
|
||||
Artillery.SPH_2S19_Msta,
|
||||
@@ -59,9 +65,13 @@ Russia_2010 = {
|
||||
AirDefence.SAM_SA_13_Strela_10M3_9A35M3
|
||||
], "aircraft_carrier": [
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
], "carrier_names": [
|
||||
"Admiral Kuznetov"
|
||||
], "destroyer": [
|
||||
FF_1135M_Rezky,
|
||||
], "cruiser": [
|
||||
FSG_1241_1MP_Molniya,
|
||||
], "boat": [
|
||||
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Spain_1990 = {
|
||||
"country": "Spain",
|
||||
@@ -44,5 +44,7 @@ Spain_1990 = {
|
||||
"Principe de Asturias",
|
||||
], "lhanames": [
|
||||
"Juan Carlos I",
|
||||
]
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -11,7 +11,7 @@ Sweden_1990 = {
|
||||
|
||||
UH_1H,
|
||||
|
||||
AirDefence.SAM_Hawk_LN_M192,
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
|
||||
Armor.IFV_MCV_80, # Standing as Strf 90
|
||||
Armor.MBT_Leopard_2,
|
||||
@@ -25,7 +25,7 @@ Sweden_1990 = {
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
],
|
||||
"shorad":[
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
]
|
||||
], "has_jtac": True
|
||||
}
|
||||
267
game/factions/syria.py
Normal file
267
game/factions/syria.py
Normal file
@@ -0,0 +1,267 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Syria_2011 = {
|
||||
"country": "Syria",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
MiG_25PD,
|
||||
MiG_29S,
|
||||
|
||||
Su_17M4,
|
||||
Su_24M,
|
||||
|
||||
L_39ZA,
|
||||
|
||||
Mi_24V,
|
||||
Mi_8MT,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
A_50,
|
||||
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
|
||||
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.IFV_BMP_2,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_Cobra,
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_72B,
|
||||
Armor.MBT_T_90,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
Artillery.MLRS_9K57_Uragan_BM_27,
|
||||
Artillery.SPH_2S1_Gvozdika,
|
||||
Artillery.SPH_2S9_Nona,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
Infantry.Soldier_AK
|
||||
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
|
||||
AirDefence.SAM_SA_9_Strela_1_9P31,
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
Syria_1973 = {
|
||||
"country": "Syria",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis,
|
||||
MiG_19P,
|
||||
MiG_15bis, # Standing as Mig-17
|
||||
|
||||
Su_17M4, # Standing as Su-7
|
||||
Mi_8MT,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.APC_MTLB,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
Infantry.Soldier_AK
|
||||
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Syria_1982 = {
|
||||
"country": "Syria",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
MiG_25PD,
|
||||
MiG_19P,
|
||||
|
||||
Su_17M4, # Standing as Su-7
|
||||
Mi_8MT,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.APC_MTLB,
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_72B,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
Infantry.Soldier_AK
|
||||
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Syria_1967 = {
|
||||
"country": "Syria",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis,
|
||||
MiG_19P,
|
||||
MiG_15bis, # Standing as Mig-17
|
||||
|
||||
Su_17M4, # Standing as Su-7
|
||||
Mi_8MT,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
Infantry.Soldier_AK
|
||||
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
Syria_1967_WW2_Weapons = {
|
||||
"country": "Syria",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis,
|
||||
MiG_19P,
|
||||
MiG_15bis, # Standing as Mig-17
|
||||
|
||||
Su_17M4, # Standing as Su-7
|
||||
Mi_8MT,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.MBT_T_55,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H,
|
||||
Armor.StuG_III_Ausf__G,
|
||||
Armor.TD_Jagdpanzer_IV,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
Infantry.Soldier_RPG,
|
||||
Infantry.Soldier_AK
|
||||
|
||||
], "requirements": {
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"shorad": [
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
Arab_Armies_1948 = {
|
||||
"country": "Syria",
|
||||
"side": "red",
|
||||
"units": [
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
Infantry.Infantry_SMLE_No_4_Mk_1,
|
||||
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
|
||||
], "requirements": {
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"shorad": [
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Turkey_2005 = {
|
||||
"country": "Turkey",
|
||||
@@ -16,7 +16,6 @@ Turkey_2005 = {
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
OH_58D,
|
||||
AH_1W,
|
||||
|
||||
Armor.MBT_Leopard_2,
|
||||
@@ -36,5 +35,7 @@ Turkey_2005 = {
|
||||
], "shorad":[
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
]
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
UAE_2005 = {
|
||||
"country": "United Arab Emirates",
|
||||
@@ -30,5 +30,9 @@ UAE_2005 = {
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
]
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
],
|
||||
"has_jtac": True,
|
||||
"jtac_unit": WingLoong_I
|
||||
}
|
||||
44
game/factions/uk_1944.py
Normal file
44
game/factions/uk_1944.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from game.data.building_data import WW2_ALLIES_BUILDINGS
|
||||
from game.data.doctrine import WWII_DOCTRINE
|
||||
|
||||
UK_1944 = {
|
||||
"country": "UK",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
P_47D_30,
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
A_20G,
|
||||
B_17G,
|
||||
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.APC_M2A1,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.ST_Centaur_IV,
|
||||
Armor.HIT_Churchill_VII,
|
||||
|
||||
Infantry.Infantry_SMLE_No_4_Mk_1,
|
||||
|
||||
LS_Samuel_Chase,
|
||||
LST_Mk_II,
|
||||
LCVP__Higgins_boat,
|
||||
|
||||
Unarmed.CCKW_353,
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
], "shorad":[
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
],"requirements":{
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"objects": WW2_ALLIES_BUILDINGS,
|
||||
"doctrine": WWII_DOCTRINE,
|
||||
"boat": ["WW2LSTGroupGenerator"],
|
||||
"boat_count": 1
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
UnitedKingdom_1990 = {
|
||||
"country": "UK",
|
||||
@@ -16,7 +16,7 @@ UnitedKingdom_1990 = {
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
SA342L,
|
||||
SA342M,
|
||||
AH_64A,
|
||||
|
||||
Armor.MBT_Challenger_II,
|
||||
@@ -42,9 +42,11 @@ UnitedKingdom_1990 = {
|
||||
Oliver_Hazzard_Perry_class,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "lha_names": [
|
||||
], "lhanames": [
|
||||
"HMS Invincible",
|
||||
"HMS Illustrious",
|
||||
"HMS Ark Royal",
|
||||
]
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Ukraine_2010 = {
|
||||
"country": "Ukraine",
|
||||
@@ -47,5 +47,7 @@ Ukraine_2010 = {
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6,
|
||||
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375
|
||||
], "boat":[
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
72
game/factions/us_aggressors.py
Normal file
72
game/factions/us_aggressors.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
US_Aggressors = {
|
||||
"country": "USAF Aggressors",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
F_15C,
|
||||
F_5E_3,
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
Su_27,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
AH_64D,
|
||||
Ka_50,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.MBT_Leopard_2,
|
||||
Armor.ATGM_M1134_Stryker,
|
||||
Armor.IFV_M2A2_Bradley,
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
|
||||
Artillery.MLRS_M270,
|
||||
Artillery.SPH_M109_Paladin,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Patriot_EPP_III,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
], "aircraft_carrier": [
|
||||
CVN_74_John_C__Stennis,
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
"CVN-71 Theodore Roosevelt",
|
||||
"CVN-72 Abraham Lincoln",
|
||||
"CVN-73 George Washington",
|
||||
"CVN-74 John C. Stennis",
|
||||
], "lhanames": [
|
||||
"LHA-1 Tarawa",
|
||||
"LHA-2 Saipan",
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
@@ -2,7 +2,46 @@ from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from game.data.building_data import WW2_ALLIES_BUILDINGS
|
||||
from game.data.doctrine import WWII_DOCTRINE
|
||||
|
||||
USA_1944 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
P_47D_30,
|
||||
A_20G,
|
||||
B_17G,
|
||||
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.APC_M2A1,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
Armor.TD_M10_GMC,
|
||||
Artillery.M12_GMC,
|
||||
|
||||
Infantry.Infantry_M1_Garand,
|
||||
|
||||
LS_Samuel_Chase,
|
||||
LST_Mk_II,
|
||||
LCVP__Higgins_boat,
|
||||
|
||||
Unarmed.CCKW_353,
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
], "shorad":[
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
],"requirements":{
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"objects": WW2_ALLIES_BUILDINGS,
|
||||
"doctrine": WWII_DOCTRINE,
|
||||
"boat": ["WW2LSTGroupGenerator"],
|
||||
"boat_count": 2
|
||||
}
|
||||
|
||||
ALLIES_1944 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
@@ -12,6 +51,7 @@ USA_1944 = {
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
A_20G,
|
||||
B_17G,
|
||||
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
@@ -36,5 +76,11 @@ USA_1944 = {
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
], "shorad":[
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
]
|
||||
],"requirements":{
|
||||
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
|
||||
},
|
||||
"objects": WW2_ALLIES_BUILDINGS,
|
||||
"doctrine": WWII_DOCTRINE,
|
||||
"boat": ["WW2LSTGroupGenerator"],
|
||||
"boat_count": 2
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
USA_1965 = {
|
||||
"country": "USA",
|
||||
@@ -36,5 +36,6 @@ USA_1965 = {
|
||||
"shorad":[
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
AirDefence.SAM_Chaparral_M48
|
||||
], "boat":[
|
||||
]
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
USA_1990 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
F_15C,
|
||||
F_15E,
|
||||
F_14B,
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
|
||||
A_10A,
|
||||
AV8BNA,
|
||||
|
||||
B_1B,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
@@ -59,5 +59,7 @@ USA_1990 = {
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
]
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
@@ -1,19 +1,21 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
USA_2005 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
F_15C,
|
||||
F_15E,
|
||||
F_14B,
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
JF_17,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
AV8BNA,
|
||||
MQ_9_Reaper,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
@@ -51,7 +53,6 @@ USA_2005 = {
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa,
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
USS_Arleigh_Burke_IIa,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
@@ -66,5 +67,7 @@ USA_2005 = {
|
||||
"LHA-3 Belleau Wood",
|
||||
"LHA-4 Nassau",
|
||||
"LHA-5 Peleliu"
|
||||
]
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
|
||||
141
game/game.py
141
game/game.py
@@ -1,7 +1,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from game.db import REWARDS, PLAYER_BUDGET_BASE
|
||||
from game.game_stats import GameStats
|
||||
from game.db import REWARDS, PLAYER_BUDGET_BASE, sys
|
||||
from game.models.game_stats import GameStats
|
||||
from gen.flights.ai_flight_planner import FlightPlanner
|
||||
from gen.ground_forces.ai_ground_planner import GroundPlanner
|
||||
from .event import *
|
||||
@@ -56,8 +56,8 @@ class Game:
|
||||
current_unit_id = 0
|
||||
current_group_id = 0
|
||||
|
||||
def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater, start_date: datetime):
|
||||
self.settings = Settings()
|
||||
def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater, start_date: datetime, settings):
|
||||
self.settings = settings
|
||||
self.events = []
|
||||
self.theater = theater
|
||||
self.player_name = player_name
|
||||
@@ -72,6 +72,35 @@ class Game:
|
||||
self.ground_planners = {}
|
||||
self.informations = []
|
||||
self.informations.append(Information("Game Start", "-" * 40, 0))
|
||||
self.__culling_points = self.compute_conflicts_position()
|
||||
self.__frontlineData = []
|
||||
self.__destroyed_units = []
|
||||
self.jtacs = []
|
||||
self.savepath = ""
|
||||
|
||||
self.sanitize_sides()
|
||||
|
||||
|
||||
def sanitize_sides(self):
|
||||
"""
|
||||
Make sure the opposing factions are using different countries
|
||||
:return:
|
||||
"""
|
||||
if self.player_country == self.enemy_country:
|
||||
if self.player_country == "USA":
|
||||
self.enemy_country = "USAF Aggressors"
|
||||
elif self.player_country == "Russia":
|
||||
self.enemy_country = "USSR"
|
||||
else:
|
||||
self.enemy_country = "Russia"
|
||||
|
||||
@property
|
||||
def player_faction(self):
|
||||
return db.FACTIONS[self.player_name]
|
||||
|
||||
@property
|
||||
def enemy_faction(self):
|
||||
return db.FACTIONS[self.enemy_name]
|
||||
|
||||
def _roll(self, prob, mult):
|
||||
if self.settings.version == "dev":
|
||||
@@ -144,7 +173,7 @@ class Game:
|
||||
self.events.remove(event)
|
||||
|
||||
def initiate_event(self, event: Event):
|
||||
assert event in self.events
|
||||
#assert event in self.events
|
||||
logging.info("Generating {} (regular)".format(event))
|
||||
event.generate()
|
||||
|
||||
@@ -161,23 +190,12 @@ class Game:
|
||||
|
||||
def is_player_attack(self, event):
|
||||
if isinstance(event, Event):
|
||||
return event.attacker_name == self.player_name
|
||||
return event and event.attacker_name and event.attacker_name == self.player_name
|
||||
else:
|
||||
return event.name == self.player_name
|
||||
|
||||
def get_player_coalition_id(self):
|
||||
if self.player_country in db.BLUEFOR_FACTIONS:
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
|
||||
def get_enemy_coalition_id(self):
|
||||
if self.get_player_coalition_id() == 1:
|
||||
return 2
|
||||
else:
|
||||
return 1
|
||||
return event and event.name and event.name == self.player_name
|
||||
|
||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
|
||||
|
||||
logging.info("Pass turn")
|
||||
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
|
||||
self.turn = self.turn + 1
|
||||
@@ -193,7 +211,7 @@ class Game:
|
||||
self._enemy_reinforcement()
|
||||
self._budget_player()
|
||||
|
||||
if not no_action:
|
||||
if not no_action and self.turn > 1:
|
||||
for cp in self.theater.player_points():
|
||||
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
|
||||
else:
|
||||
@@ -212,6 +230,7 @@ class Game:
|
||||
self.game_stats.update(self)
|
||||
|
||||
# Plan flights & combat for next turn
|
||||
self.__culling_points = self.compute_conflicts_position()
|
||||
self.planners = {}
|
||||
self.ground_planners = {}
|
||||
for cp in self.theater.controlpoints:
|
||||
@@ -225,6 +244,9 @@ class Game:
|
||||
gplanner.plan_groundwar()
|
||||
self.ground_planners[cp.id] = gplanner
|
||||
|
||||
# Autosave progress
|
||||
persistency.autosave(self)
|
||||
|
||||
def _enemy_reinforcement(self):
|
||||
"""
|
||||
Compute and commision reinforcement for enemy bases
|
||||
@@ -317,3 +339,82 @@ class Game:
|
||||
"""
|
||||
self.current_group_id += 1
|
||||
return self.current_group_id
|
||||
|
||||
def compute_conflicts_position(self):
|
||||
"""
|
||||
Compute the current conflict center position(s), mainly used for culling calculation
|
||||
:return: List of points of interests
|
||||
"""
|
||||
points = []
|
||||
|
||||
# By default, use the existing frontline conflict position
|
||||
for conflict in self.theater.conflicts():
|
||||
points.append(Conflict.frontline_position(self.theater, conflict[0], conflict[1])[0])
|
||||
points.append(conflict[0].position)
|
||||
points.append(conflict[1].position)
|
||||
|
||||
# If there is no conflict take the center point between the two nearest opposing bases
|
||||
if len(points) == 0:
|
||||
cpoint = None
|
||||
min_distance = sys.maxsize
|
||||
for cp in self.theater.player_points():
|
||||
for cp2 in self.theater.enemy_points():
|
||||
d = cp.position.distance_to_point(cp2.position)
|
||||
if d < min_distance:
|
||||
min_distance = d
|
||||
cpoint = Point((cp.position.x + cp2.position.x) / 2, (cp.position.y + cp2.position.y) / 2)
|
||||
points.append(cp.position)
|
||||
points.append(cp2.position)
|
||||
break
|
||||
if cpoint is not None:
|
||||
break
|
||||
if cpoint is not None:
|
||||
points.append(cpoint)
|
||||
|
||||
# Else 0,0, since we need a default value
|
||||
# (in this case this means the whole map is owned by the same player, so it is not an issue)
|
||||
if len(points) == 0:
|
||||
points.append(Point(0, 0))
|
||||
|
||||
return points
|
||||
|
||||
def add_destroyed_units(self, data):
|
||||
pos = Point(data["x"], data["z"])
|
||||
if self.theater.is_on_land(pos):
|
||||
self.__destroyed_units.append(data)
|
||||
|
||||
def get_destroyed_units(self):
|
||||
return self.__destroyed_units
|
||||
|
||||
def position_culled(self, pos):
|
||||
"""
|
||||
Check if unit can be generated at given position depending on culling performance settings
|
||||
:param pos: Position you are tryng to spawn stuff at
|
||||
:return: True if units can not be added at given position
|
||||
"""
|
||||
if self.settings.perf_culling == False:
|
||||
return False
|
||||
else:
|
||||
for c in self.__culling_points:
|
||||
if c.distance_to_point(pos) < self.settings.perf_culling_distance * 1000:
|
||||
return False
|
||||
return True
|
||||
|
||||
# 1 = red, 2 = blue
|
||||
def get_player_coalition_id(self):
|
||||
return 2
|
||||
|
||||
def get_enemy_coalition_id(self):
|
||||
return 1
|
||||
|
||||
def get_player_coalition(self):
|
||||
return dcs.action.Coalition.Blue
|
||||
|
||||
def get_enemy_coalition(self):
|
||||
return dcs.action.Coalition.Red
|
||||
|
||||
def get_player_color(self):
|
||||
return "blue"
|
||||
|
||||
def get_enemy_color(self):
|
||||
return "red"
|
||||
14
game/models/destroyed_units.py
Normal file
14
game/models/destroyed_units.py
Normal file
@@ -0,0 +1,14 @@
|
||||
class DestroyedUnit:
|
||||
"""
|
||||
Store info about a destroyed unit
|
||||
"""
|
||||
|
||||
x: int
|
||||
y: int
|
||||
name: str
|
||||
|
||||
def __init__(self, x , y, name):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.name = name
|
||||
|
||||
13
game/models/frontline_data.py
Normal file
13
game/models/frontline_data.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from theater import ControlPoint
|
||||
|
||||
|
||||
class FrontlineData:
|
||||
"""
|
||||
This Data structure will store information about an existing frontline
|
||||
"""
|
||||
|
||||
def __init__(self, from_cp:ControlPoint, to_cp: ControlPoint):
|
||||
self.to_cp = to_cp
|
||||
self.from_cp = from_cp
|
||||
self.enemy_units_position = []
|
||||
self.blue_units_position = []
|
||||
@@ -14,19 +14,6 @@ class FrontlineAttackOperation(Operation):
|
||||
attackers = None # type: db.ArmorDict
|
||||
defenders = None # type: db.ArmorDict
|
||||
|
||||
def setup(self,
|
||||
defenders: db.ArmorDict,
|
||||
attackers: db.ArmorDict,
|
||||
strikegroup: db.AssignedUnitsDict,
|
||||
escort: db.AssignedUnitsDict,
|
||||
interceptors: db.AssignedUnitsDict):
|
||||
self.strikegroup = strikegroup
|
||||
self.escort = escort
|
||||
self.interceptors = interceptors
|
||||
|
||||
self.defenders = defenders
|
||||
self.attackers = attackers
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(FrontlineAttackOperation, self).prepare(terrain, is_quick)
|
||||
if self.defender_name == self.game.player_name:
|
||||
@@ -47,31 +34,6 @@ class FrontlineAttackOperation(Operation):
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
#if self.is_player_attack:
|
||||
# self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||
|
||||
# ground units
|
||||
# self.armorgen.generate_vec(self.attackers, self.defenders)
|
||||
|
||||
## strike group w/ heli support
|
||||
#planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||
#self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
|
||||
|
||||
#heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
|
||||
#if heli_flights:
|
||||
# self.briefinggen.append_frequency("FARP + Heli flights", "127.5 MHz AM")
|
||||
# for farp, dict in zip(self.groundobjectgen.generate_farps(sum([x[0] for x in heli_flights.values()])),
|
||||
# db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
|
||||
# self.airgen.generate_cas_strikegroup(*assigned_units_split(dict),
|
||||
# at=farp,
|
||||
# escort=len(planes_flights) == 0)
|
||||
|
||||
#self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
|
||||
|
||||
#self.airgen.generate_defense(*assigned_units_split(self.interceptors), at=self.defenders_starting_position)
|
||||
|
||||
self.briefinggen.title = "Frontline CAS"
|
||||
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||
self.briefinggen.append_waypoint("CAS AREA IP")
|
||||
self.briefinggen.append_waypoint("CAS AREA EGRESS")
|
||||
super(FrontlineAttackOperation, self).generate()
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
from dcs.countries import country_dict
|
||||
from dcs.lua.parse import loads
|
||||
from dcs.terrain import Terrain
|
||||
from typing import Set
|
||||
|
||||
from gen import *
|
||||
from gen.airfields import AIRFIELD_DATA
|
||||
from gen.beacons import load_beacons_for_terrain
|
||||
from gen.radios import RadioRegistry
|
||||
from gen.tacan import TacanRegistry
|
||||
from dcs.countries import country_dict
|
||||
from dcs.lua.parse import loads
|
||||
from dcs.terrain.terrain import Terrain
|
||||
from userdata.debriefing import *
|
||||
|
||||
TANKER_CALLSIGNS = ["Texaco", "Arco", "Shell"]
|
||||
|
||||
|
||||
class Operation:
|
||||
attackers_starting_position = None # type: db.StartingPosition
|
||||
@@ -18,7 +21,6 @@ class Operation:
|
||||
conflict = None # type: Conflict
|
||||
armorgen = None # type: ArmorConflictGenerator
|
||||
airgen = None # type: AircraftConflictGenerator
|
||||
shipgen = None # type: ShipGenerator
|
||||
triggersgen = None # type: TriggersGenerator
|
||||
airsupportgen = None # type: AirSupportConflictGenerator
|
||||
visualgen = None # type: VisualGenerator
|
||||
@@ -26,6 +28,8 @@ class Operation:
|
||||
groundobjectgen = None # type: GroundObjectsGenerator
|
||||
briefinggen = None # type: BriefingGenerator
|
||||
forcedoptionsgen = None # type: ForcedOptionsGenerator
|
||||
radio_registry: Optional[RadioRegistry] = None
|
||||
tacan_registry: Optional[TacanRegistry] = None
|
||||
|
||||
environment_settings = None
|
||||
trigger_radius = TRIGGER_RADIUS_MEDIUM
|
||||
@@ -64,18 +68,8 @@ class Operation:
|
||||
def initialize(self, mission: Mission, conflict: Conflict):
|
||||
self.current_mission = mission
|
||||
self.conflict = conflict
|
||||
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings, self.game)
|
||||
self.shipgen = ShipGenerator(mission, conflict)
|
||||
self.airsupportgen = AirSupportConflictGenerator(mission, conflict, self.game)
|
||||
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
|
||||
self.visualgen = VisualGenerator(mission, conflict, self.game)
|
||||
self.envgen = EnviromentGenerator(mission, conflict, self.game)
|
||||
self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game)
|
||||
self.groundobjectgen = GroundObjectsGenerator(mission, conflict, self.game)
|
||||
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
|
||||
|
||||
player_country = self.from_cp.captured and self.attacker_country or self.defender_country
|
||||
enemy_country = self.from_cp.captured and self.defender_country or self.attacker_country
|
||||
self.briefinggen = BriefingGenerator(self.current_mission,
|
||||
self.conflict, self.game)
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
with open("resources/default_options.lua", "r") as f:
|
||||
@@ -90,12 +84,12 @@ class Operation:
|
||||
# Setup coalition :
|
||||
self.current_mission.coalition["blue"] = Coalition("blue")
|
||||
self.current_mission.coalition["red"] = Coalition("red")
|
||||
if self.game.player_country and self.game.player_country in db.BLUEFOR_FACTIONS:
|
||||
self.current_mission.coalition["blue"].add_country(country_dict[db.country_id_from_name(self.game.player_country)]())
|
||||
self.current_mission.coalition["red"].add_country(country_dict[db.country_id_from_name(self.game.enemy_country)]())
|
||||
else:
|
||||
self.current_mission.coalition["blue"].add_country(country_dict[db.country_id_from_name(self.game.enemy_country)]())
|
||||
self.current_mission.coalition["red"].add_country(country_dict[db.country_id_from_name(self.game.player_country)]())
|
||||
|
||||
p_country = self.game.player_country
|
||||
e_country = self.game.enemy_country
|
||||
self.current_mission.coalition["blue"].add_country(country_dict[db.country_id_from_name(p_country)]())
|
||||
self.current_mission.coalition["red"].add_country(country_dict[db.country_id_from_name(e_country)]())
|
||||
|
||||
print([c for c in self.current_mission.coalition["blue"].countries.keys()])
|
||||
print([c for c in self.current_mission.coalition["red"].countries.keys()])
|
||||
|
||||
@@ -115,14 +109,82 @@ class Operation:
|
||||
self.defenders_starting_position = self.to_cp.at
|
||||
|
||||
def generate(self):
|
||||
radio_registry = RadioRegistry()
|
||||
tacan_registry = TacanRegistry()
|
||||
|
||||
# Dedup beacon/radio frequencies, since some maps have some frequencies
|
||||
# used multiple times.
|
||||
beacons = load_beacons_for_terrain(self.game.theater.terrain.name)
|
||||
unique_map_frequencies: Set[RadioFrequency] = set()
|
||||
for beacon in beacons:
|
||||
unique_map_frequencies.add(beacon.frequency)
|
||||
if beacon.is_tacan:
|
||||
if beacon.channel is None:
|
||||
logging.error(
|
||||
f"TACAN beacon has no channel: {beacon.callsign}")
|
||||
else:
|
||||
tacan_registry.reserve(beacon.tacan_channel)
|
||||
|
||||
for airfield, data in AIRFIELD_DATA.items():
|
||||
if data.theater == self.game.theater.terrain.name:
|
||||
unique_map_frequencies.add(data.atc.hf)
|
||||
unique_map_frequencies.add(data.atc.vhf_fm)
|
||||
unique_map_frequencies.add(data.atc.vhf_am)
|
||||
unique_map_frequencies.add(data.atc.uhf)
|
||||
# No need to reserve ILS or TACAN because those are in the
|
||||
# beacon list.
|
||||
|
||||
for frequency in unique_map_frequencies:
|
||||
radio_registry.reserve(frequency)
|
||||
|
||||
# Generate meteo
|
||||
envgen = EnviromentGenerator(self.current_mission, self.conflict,
|
||||
self.game)
|
||||
if self.environment_settings is None:
|
||||
self.environment_settings = envgen.generate()
|
||||
else:
|
||||
envgen.load(self.environment_settings)
|
||||
|
||||
# Generate ground object first
|
||||
self.groundobjectgen.generate()
|
||||
|
||||
groundobjectgen = GroundObjectsGenerator(
|
||||
self.current_mission,
|
||||
self.conflict,
|
||||
self.game,
|
||||
radio_registry,
|
||||
tacan_registry
|
||||
)
|
||||
groundobjectgen.generate()
|
||||
|
||||
# Generate destroyed units
|
||||
for d in self.game.get_destroyed_units():
|
||||
try:
|
||||
utype = db.unit_type_from_name(d["type"])
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
pos = Point(d["x"], d["z"])
|
||||
if utype is not None and not self.game.position_culled(pos) and self.game.settings.perf_destroyed_units:
|
||||
self.current_mission.static_group(
|
||||
country=self.current_mission.country(self.game.player_country),
|
||||
name="",
|
||||
_type=utype,
|
||||
hidden=True,
|
||||
position=pos,
|
||||
heading=d["orientation"],
|
||||
dead=True,
|
||||
)
|
||||
|
||||
# Air Support (Tanker & Awacs)
|
||||
self.airsupportgen.generate(self.is_awacs_enabled)
|
||||
airsupportgen = AirSupportConflictGenerator(
|
||||
self.current_mission, self.conflict, self.game, radio_registry,
|
||||
tacan_registry)
|
||||
airsupportgen.generate(self.is_awacs_enabled)
|
||||
|
||||
# Generate Activity on the map
|
||||
airgen = AircraftConflictGenerator(
|
||||
self.current_mission, self.conflict, self.game.settings, self.game,
|
||||
radio_registry)
|
||||
for cp in self.game.theater.controlpoints:
|
||||
side = cp.captured
|
||||
if side:
|
||||
@@ -130,9 +192,15 @@ class Operation:
|
||||
else:
|
||||
country = self.current_mission.country(self.game.enemy_country)
|
||||
if cp.id in self.game.planners.keys():
|
||||
self.airgen.generate_flights(cp, country, self.game.planners[cp.id])
|
||||
airgen.generate_flights(
|
||||
cp,
|
||||
country,
|
||||
self.game.planners[cp.id],
|
||||
groundobjectgen.runways
|
||||
)
|
||||
|
||||
# Generate ground units on frontline everywhere
|
||||
jtacs: List[JtacInfo] = []
|
||||
for player_cp, enemy_cp in self.game.theater.conflicts(True):
|
||||
conflict = Conflict.frontline_cas_conflict(self.attacker_name, self.defender_name,
|
||||
self.current_mission.country(self.attacker_country),
|
||||
@@ -143,6 +211,7 @@ class Operation:
|
||||
enemy_gp = self.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id]
|
||||
groundConflictGen = GroundConflictGenerator(self.current_mission, conflict, self.game, player_gp, enemy_gp, player_cp.stances[enemy_cp.id])
|
||||
groundConflictGen.generate()
|
||||
jtacs.extend(groundConflictGen.jtacs)
|
||||
|
||||
# Setup combined arms parameters
|
||||
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
|
||||
@@ -151,57 +220,162 @@ class Operation:
|
||||
else:
|
||||
self.current_mission.groundControl.red_tactical_commander = self.ca_slots
|
||||
|
||||
# triggers
|
||||
if self.game.is_player_attack(self.conflict.attackers_country):
|
||||
cp = self.conflict.from_cp
|
||||
else:
|
||||
cp = self.conflict.to_cp
|
||||
# Triggers
|
||||
triggersgen = TriggersGenerator(self.current_mission, self.conflict,
|
||||
self.game)
|
||||
triggersgen.generate()
|
||||
|
||||
self.triggersgen.generate(player_cp=cp,
|
||||
is_quick=False,
|
||||
activation_trigger_radius=self.trigger_radius,
|
||||
awacs_enabled=self.is_awacs_enabled)
|
||||
|
||||
# env settings
|
||||
if self.environment_settings is None:
|
||||
self.environment_settings = self.envgen.generate()
|
||||
else:
|
||||
self.envgen.load(self.environment_settings)
|
||||
|
||||
# options
|
||||
self.forcedoptionsgen.generate()
|
||||
# Options
|
||||
forcedoptionsgen = ForcedOptionsGenerator(self.current_mission,
|
||||
self.conflict, self.game)
|
||||
forcedoptionsgen.generate()
|
||||
|
||||
# Generate Visuals Smoke Effects
|
||||
visualgen = VisualGenerator(self.current_mission, self.conflict,
|
||||
self.game)
|
||||
if self.game.settings.perf_smoke_gen:
|
||||
self.visualgen.generate()
|
||||
visualgen.generate()
|
||||
|
||||
# Inject Lua Scripts
|
||||
load_mist = TriggerStart(comment="Load Mist Lua Framework")
|
||||
with open("./resources/scripts/mist_4_3_74.lua") as f:
|
||||
load_mist.add_action(DoScript(String(f.read())))
|
||||
self.current_mission.triggerrules.triggers.append(load_mist)
|
||||
# Inject Plugins Lua Scripts
|
||||
listOfPluginsScripts = []
|
||||
plugin_file_path = Path("./resources/scripts/plugins/__plugins.lst")
|
||||
if plugin_file_path.exists():
|
||||
for line in plugin_file_path.read_text().splitlines():
|
||||
name = line.strip()
|
||||
if not name.startswith( '#' ):
|
||||
trigger = TriggerStart(comment="Load " + name)
|
||||
listOfPluginsScripts.append(name)
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/plugins/" + name)
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
else:
|
||||
logging.info(
|
||||
f"Not loading plugins, {plugin_file_path} does not exist")
|
||||
|
||||
load_dcs_libe = TriggerStart(comment="Load DCS Liberation Script")
|
||||
with open("./resources/scripts/dcs_liberation.lua") as f:
|
||||
script = f.read()
|
||||
json_location = "[["+os.path.abspath("resources\\scripts\\json.lua")+"]]"
|
||||
state_location = "[[" + os.path.abspath("state.json") + "]]"
|
||||
script = script.replace("{{json_file_abs_location}}", json_location)
|
||||
script = script.replace("{{debriefing_file_location}}", state_location)
|
||||
load_dcs_libe.add_action(DoScript(String(script)))
|
||||
self.current_mission.triggerrules.triggers.append(load_dcs_libe)
|
||||
# Inject Mist Script if not done already in the plugins
|
||||
if not "mist.lua" in listOfPluginsScripts and not "mist_4_3_74.lua" in listOfPluginsScripts: # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load Mist Lua framework")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/mist_4_3_74.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
# Briefing Generation
|
||||
for i, tanker_type in enumerate(self.airsupportgen.generated_tankers):
|
||||
self.briefinggen.append_frequency("Tanker {} ({})".format(TANKER_CALLSIGNS[i], tanker_type), "{}X/{} MHz AM".format(97+i, 130+i))
|
||||
# Inject JSON library if not done already in the plugins
|
||||
if not "json.lua" in listOfPluginsScripts : # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load JSON Lua library")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/json.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
# Inject Ciribob's JTACAutoLase if not done already in the plugins
|
||||
if not "JTACAutoLase.lua" in listOfPluginsScripts : # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load JTACAutoLase.lua script")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/JTACAutoLase.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
# set a LUA table with data from Liberation that we want to set
|
||||
# at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function
|
||||
# later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts
|
||||
state_location = "[[" + os.path.abspath("state.json") + "]]"
|
||||
lua = """
|
||||
-- setting configuration table
|
||||
env.info("DCSLiberation|: setting configuration table")
|
||||
|
||||
-- all data in this table is overridable.
|
||||
dcsLiberation = {}
|
||||
|
||||
-- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory
|
||||
dcsLiberation.installPath=""" + state_location + """
|
||||
|
||||
-- you can override dcsLiberation.JTACAutoLase to make it use your own function ; it will be called with these parameters : ({jtac.unit_name}, {jtac.code}, {smoke}, 'vehicle') for all JTACs
|
||||
if ctld then
|
||||
dcsLiberation.JTACAutoLase=ctld.JTACAutoLase
|
||||
elseif JTACAutoLase then
|
||||
dcsLiberation.JTACAutoLase=JTACAutoLase
|
||||
end
|
||||
|
||||
-- later, we'll add more data to the table
|
||||
--dcsLiberation.POIs = {}
|
||||
--dcsLiberation.BASEs = {}
|
||||
--dcsLiberation.JTACs = {}
|
||||
"""
|
||||
|
||||
trigger = TriggerStart(comment="Set DCS Liberation data")
|
||||
trigger.add_action(DoScript(String(lua)))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
# Inject DCS-Liberation script if not done already in the plugins
|
||||
if not "dcs_liberation.lua" in listOfPluginsScripts : # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load DCS Liberation script")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/dcs_liberation.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
# add a configuration for JTACAutoLase and start lasing for all JTACs
|
||||
smoke = "true"
|
||||
if hasattr(self.game.settings, "jtac_smoke_on"):
|
||||
if not self.game.settings.jtac_smoke_on:
|
||||
smoke = "false"
|
||||
|
||||
lua = """
|
||||
-- setting and starting JTACs
|
||||
env.info("DCSLiberation|: setting and starting JTACs")
|
||||
"""
|
||||
|
||||
for jtac in jtacs:
|
||||
lua += f"if dcsLiberation.JTACAutoLase then dcsLiberation.JTACAutoLase('{jtac.unit_name}', {jtac.code}, {smoke}, 'vehicle') end\n"
|
||||
|
||||
trigger = TriggerStart(comment="Start JTACs")
|
||||
trigger.add_action(DoScript(String(lua)))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
self.assign_channels_to_flights(airgen.flights,
|
||||
airsupportgen.air_support)
|
||||
|
||||
kneeboard_generator = KneeboardGenerator(self.current_mission)
|
||||
|
||||
for dynamic_runway in groundobjectgen.runways.values():
|
||||
self.briefinggen.add_dynamic_runway(dynamic_runway)
|
||||
|
||||
for tanker in airsupportgen.air_support.tankers:
|
||||
self.briefinggen.add_tanker(tanker)
|
||||
kneeboard_generator.add_tanker(tanker)
|
||||
|
||||
if self.is_awacs_enabled:
|
||||
self.briefinggen.append_frequency("AWACS", "133 MHz AM")
|
||||
for awacs in airsupportgen.air_support.awacs:
|
||||
self.briefinggen.add_awacs(awacs)
|
||||
kneeboard_generator.add_awacs(awacs)
|
||||
|
||||
self.briefinggen.append_frequency("Flight", "251 MHz AM")
|
||||
for jtac in jtacs:
|
||||
self.briefinggen.add_jtac(jtac)
|
||||
kneeboard_generator.add_jtac(jtac)
|
||||
|
||||
for flight in airgen.flights:
|
||||
self.briefinggen.add_flight(flight)
|
||||
kneeboard_generator.add_flight(flight)
|
||||
|
||||
# Generate the briefing
|
||||
self.briefinggen.generate()
|
||||
kneeboard_generator.generate()
|
||||
|
||||
def assign_channels_to_flights(self, flights: List[FlightData],
|
||||
air_support: AirSupport) -> None:
|
||||
"""Assigns preset radio channels for client flights."""
|
||||
for flight in flights:
|
||||
if not flight.client_units:
|
||||
continue
|
||||
self.assign_channels_to_flight(flight, air_support)
|
||||
|
||||
def assign_channels_to_flight(self, flight: FlightData,
|
||||
air_support: AirSupport) -> None:
|
||||
"""Assigns preset radio channels for a client flight."""
|
||||
airframe = flight.aircraft_type
|
||||
|
||||
try:
|
||||
aircraft_data = AIRCRAFT_DATA[airframe.id]
|
||||
except KeyError:
|
||||
logging.warning(f"No aircraft data for {airframe.id}")
|
||||
return
|
||||
|
||||
aircraft_data.channel_allocator.assign_channels_for_flight(
|
||||
flight, air_support
|
||||
)
|
||||
|
||||
@@ -1,26 +1,43 @@
|
||||
|
||||
class Settings:
|
||||
|
||||
# Difficulty settings
|
||||
player_skill = "Good"
|
||||
enemy_skill = "Average"
|
||||
enemy_vehicle_skill = "Average"
|
||||
map_coalition_visibility = "All Units"
|
||||
labels = "Full"
|
||||
only_player_takeoff = True # Legacy parameter do not use
|
||||
night_disabled = False
|
||||
supercarrier = False
|
||||
multiplier = 1
|
||||
sams = True # Legacy parameter do not use
|
||||
cold_start = False # Legacy parameter do not use
|
||||
version = None
|
||||
def __init__(self):
|
||||
# Generator settings
|
||||
self.inverted = False
|
||||
self.do_not_generate_carrier = False # TODO : implement
|
||||
self.do_not_generate_lha = False # TODO : implement
|
||||
self.do_not_generate_player_navy = True # TODO : implement
|
||||
self.do_not_generate_enemy_navy = True # TODO : implement
|
||||
|
||||
# Performance oriented
|
||||
perf_red_alert_state = True
|
||||
perf_smoke_gen = True
|
||||
perf_artillery = True
|
||||
perf_moving_units = True
|
||||
perf_infantry = True
|
||||
perf_ai_parking_start = True
|
||||
# Difficulty settings
|
||||
self.player_skill = "Good"
|
||||
self.enemy_skill = "Average"
|
||||
self.enemy_vehicle_skill = "Average"
|
||||
self.map_coalition_visibility = "All Units"
|
||||
self.labels = "Full"
|
||||
self.only_player_takeoff = True # Legacy parameter do not use
|
||||
self.night_disabled = False
|
||||
self.external_views_allowed = True
|
||||
self.supercarrier = False
|
||||
self.multiplier = 1
|
||||
self.generate_marks = True
|
||||
self.sams = True # Legacy parameter do not use
|
||||
self.cold_start = False # Legacy parameter do not use
|
||||
self.version = None
|
||||
self.include_jtac_if_available = True
|
||||
self.jtac_smoke_on = True
|
||||
|
||||
# Performance oriented
|
||||
self.perf_red_alert_state = True
|
||||
self.perf_smoke_gen = True
|
||||
self.perf_artillery = True
|
||||
self.perf_moving_units = True
|
||||
self.perf_infantry = True
|
||||
self.perf_ai_parking_start = True
|
||||
self.perf_destroyed_units = True
|
||||
|
||||
# Performance culling
|
||||
self.perf_culling = False
|
||||
self.perf_culling_distance = 100
|
||||
|
||||
|
||||
|
||||
14
game/utils.py
Normal file
14
game/utils.py
Normal file
@@ -0,0 +1,14 @@
|
||||
def meter_to_feet(value_in_meter):
|
||||
return int(3.28084 * value_in_meter)
|
||||
|
||||
|
||||
def feet_to_meter(value_in_feet):
|
||||
return int(float(value_in_feet)/3.048)
|
||||
|
||||
|
||||
def meter_to_nm(value_in_meter):
|
||||
return int(float(value_in_meter)*0.000539957)
|
||||
|
||||
|
||||
def nm_to_meter(value_in_nm):
|
||||
return int(float(value_in_nm)*1852)
|
||||
@@ -3,13 +3,13 @@ from .aircraft import *
|
||||
from .armor import *
|
||||
from .airsupportgen import *
|
||||
from .conflictgen import *
|
||||
from .shipgen import *
|
||||
from .visualgen import *
|
||||
from .triggergen import *
|
||||
from .environmentgen import *
|
||||
from .groundobjectsgen import *
|
||||
from .briefinggen import *
|
||||
from .forcedoptionsgen import *
|
||||
from .kneeboard import *
|
||||
|
||||
from . import naming
|
||||
|
||||
|
||||
764
gen/aircraft.py
764
gen/aircraft.py
@@ -1,66 +1,521 @@
|
||||
from dcs.action import ActivateGroup, AITaskPush
|
||||
from dcs.condition import TimeAfter, CoalitionHasAirdrome
|
||||
from dcs.helicopters import UH_1H
|
||||
from dcs.terrain.terrain import NoParkingSlotError
|
||||
from dataclasses import dataclass
|
||||
from typing import Type
|
||||
|
||||
from dcs import helicopters
|
||||
from dcs.action import ActivateGroup, AITaskPush, MessageToAll
|
||||
from dcs.condition import TimeAfter, CoalitionHasAirdrome, PartOfCoalitionInZone
|
||||
from dcs.flyingunit import FlyingUnit
|
||||
from dcs.helicopters import helicopter_map, UH_1H
|
||||
from dcs.terrain.terrain import Airport, NoParkingSlotError
|
||||
from dcs.triggers import TriggerOnce, Event
|
||||
|
||||
from game.data.cap_capabilities_db import GUNFIGHTERS
|
||||
from game.settings import Settings
|
||||
from gen.flights.ai_flight_planner import FlightPlanner, CAP_DEFAULT_ENGAGE_DISTANCE, nm_to_meter
|
||||
from gen.flights.flight import Flight, FlightType, FlightWaypointType
|
||||
from game.utils import nm_to_meter
|
||||
from gen.airfields import RunwayData
|
||||
from gen.airsupportgen import AirSupport
|
||||
from gen.callsigns import create_group_callsign_from_unit
|
||||
from gen.flights.ai_flight_planner import FlightPlanner
|
||||
from gen.flights.flight import (
|
||||
Flight,
|
||||
FlightType,
|
||||
FlightWaypoint,
|
||||
FlightWaypointType,
|
||||
)
|
||||
from gen.radios import get_radio, MHz, Radio, RadioFrequency, RadioRegistry
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
from .triggergen import TRIGGER_WAYPOINT_OFFSET
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 1, 2
|
||||
ESCORT_ENGAGEMENT_MAX_DIST = 100000
|
||||
WORKAROUND_WAYP_DIST = 1000
|
||||
|
||||
WARM_START_HELI_AIRSPEED = 120
|
||||
WARM_START_HELI_ALT = 500
|
||||
|
||||
WARM_START_ALTITUDE = 3000
|
||||
WARM_START_AIRSPEED = 550
|
||||
|
||||
INTERCEPTION_AIRSPEED = 1000
|
||||
BARCAP_RACETRACK_DISTANCE = 20000
|
||||
CAP_DURATION = 30 # minutes
|
||||
|
||||
ATTACK_CIRCLE_ALT = 1000
|
||||
ATTACK_CIRCLE_DURATION = 15
|
||||
|
||||
CAS_ALTITUDE = 800
|
||||
RTB_ALTITUDE = 800
|
||||
RTB_DISTANCE = 5000
|
||||
HELI_ALT = 500
|
||||
|
||||
TRANSPORT_LANDING_ALT = 2000
|
||||
# Note that fallback radio channels will *not* be reserved. It's possible that
|
||||
# flights using these will overlap with other channels. This is because we would
|
||||
# need to make sure we fell back to a frequency that is not used by any beacon
|
||||
# or ATC, which we don't have the information to predict. Deal with the minor
|
||||
# annoyance for now since we'll be fleshing out radio info soon enough.
|
||||
ALLIES_WW2_CHANNEL = MHz(124)
|
||||
GERMAN_WW2_CHANNEL = MHz(40)
|
||||
HELICOPTER_CHANNEL = MHz(127)
|
||||
UHF_FALLBACK_CHANNEL = MHz(251)
|
||||
|
||||
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
||||
INTERCEPT_MAX_DISTANCE = 200000
|
||||
|
||||
# TODO: Get radio information for all the special cases.
|
||||
def get_fallback_channel(unit_type: UnitType) -> RadioFrequency:
|
||||
if unit_type in helicopter_map.values() and unit_type != UH_1H:
|
||||
return HELICOPTER_CHANNEL
|
||||
|
||||
german_ww2_aircraft = [
|
||||
Bf_109K_4,
|
||||
FW_190A8,
|
||||
FW_190D9,
|
||||
Ju_88A4,
|
||||
]
|
||||
|
||||
if unit_type in german_ww2_aircraft:
|
||||
return GERMAN_WW2_CHANNEL
|
||||
|
||||
allied_ww2_aircraft = [
|
||||
I_16,
|
||||
P_47D_30,
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
]
|
||||
|
||||
if unit_type in allied_ww2_aircraft:
|
||||
return ALLIES_WW2_CHANNEL
|
||||
|
||||
return UHF_FALLBACK_CHANNEL
|
||||
|
||||
|
||||
class ChannelNamer:
|
||||
"""Base class allowing channel name customization per-aircraft.
|
||||
|
||||
Most aircraft will want to customize this behavior, but the default is
|
||||
reasonable for any aircraft with numbered radios.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def channel_name(radio_id: int, channel_id: int) -> str:
|
||||
"""Returns the name of the channel for the given radio and channel."""
|
||||
return f"COMM{radio_id} Ch {channel_id}"
|
||||
|
||||
|
||||
class MirageChannelNamer(ChannelNamer):
|
||||
"""Channel namer for the M-2000."""
|
||||
|
||||
@staticmethod
|
||||
def channel_name(radio_id: int, channel_id: int) -> str:
|
||||
radio_name = ["V/UHF", "UHF"][radio_id - 1]
|
||||
return f"{radio_name} Ch {channel_id}"
|
||||
|
||||
|
||||
class TomcatChannelNamer(ChannelNamer):
|
||||
"""Channel namer for the F-14."""
|
||||
|
||||
@staticmethod
|
||||
def channel_name(radio_id: int, channel_id: int) -> str:
|
||||
radio_name = ["UHF", "VHF/UHF"][radio_id - 1]
|
||||
return f"{radio_name} Ch {channel_id}"
|
||||
|
||||
|
||||
class ViggenChannelNamer(ChannelNamer):
|
||||
"""Channel namer for the AJS37."""
|
||||
|
||||
@staticmethod
|
||||
def channel_name(radio_id: int, channel_id: int) -> str:
|
||||
if channel_id >= 4:
|
||||
channel_letter = "EFGH"[channel_id - 4]
|
||||
return f"FR 24 {channel_letter}"
|
||||
return f"FR 22 Special {channel_id}"
|
||||
|
||||
|
||||
class ViperChannelNamer(ChannelNamer):
|
||||
"""Channel namer for the F-16."""
|
||||
|
||||
@staticmethod
|
||||
def channel_name(radio_id: int, channel_id: int) -> str:
|
||||
return f"COM{radio_id} Ch {channel_id}"
|
||||
|
||||
|
||||
class SCR522ChannelNamer(ChannelNamer):
|
||||
"""
|
||||
Channel namer for P-51 & P-47D
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def channel_name(radio_id: int, channel_id: int) -> str:
|
||||
if channel_id > 3:
|
||||
return "?"
|
||||
else:
|
||||
return f"Button " + "ABCD"[channel_id - 1]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ChannelAssignment:
|
||||
radio_id: int
|
||||
channel: int
|
||||
|
||||
|
||||
@dataclass
|
||||
class FlightData:
|
||||
"""Details of a planned flight."""
|
||||
|
||||
flight_type: FlightType
|
||||
|
||||
#: All units in the flight.
|
||||
units: List[FlyingUnit]
|
||||
|
||||
#: Total number of aircraft in the flight.
|
||||
size: int
|
||||
|
||||
#: True if this flight belongs to the player's coalition.
|
||||
friendly: bool
|
||||
|
||||
#: Number of minutes after mission start the flight is set to depart.
|
||||
departure_delay: int
|
||||
|
||||
#: Arrival airport.
|
||||
arrival: RunwayData
|
||||
|
||||
#: Departure airport.
|
||||
departure: RunwayData
|
||||
|
||||
#: Diver airport.
|
||||
divert: Optional[RunwayData]
|
||||
|
||||
#: Waypoints of the flight plan.
|
||||
waypoints: List[FlightWaypoint]
|
||||
|
||||
#: Radio frequency for intra-flight communications.
|
||||
intra_flight_channel: RadioFrequency
|
||||
|
||||
#: Map of radio frequencies to their assigned radio and channel, if any.
|
||||
frequency_to_channel_map: Dict[RadioFrequency, ChannelAssignment]
|
||||
|
||||
def __init__(self, flight_type: FlightType, units: List[FlyingUnit],
|
||||
size: int, friendly: bool, departure_delay: int,
|
||||
departure: RunwayData, arrival: RunwayData,
|
||||
divert: Optional[RunwayData], waypoints: List[FlightWaypoint],
|
||||
intra_flight_channel: RadioFrequency) -> None:
|
||||
self.flight_type = flight_type
|
||||
self.units = units
|
||||
self.size = size
|
||||
self.friendly = friendly
|
||||
self.departure_delay = departure_delay
|
||||
self.departure = departure
|
||||
self.arrival = arrival
|
||||
self.divert = divert
|
||||
self.waypoints = waypoints
|
||||
self.intra_flight_channel = intra_flight_channel
|
||||
self.frequency_to_channel_map = {}
|
||||
self.callsign = create_group_callsign_from_unit(self.units[0])
|
||||
|
||||
@property
|
||||
def client_units(self) -> List[FlyingUnit]:
|
||||
"""List of playable units in the flight."""
|
||||
return [u for u in self.units if u.is_human()]
|
||||
|
||||
@property
|
||||
def aircraft_type(self) -> FlyingType:
|
||||
"""Returns the type of aircraft in this flight."""
|
||||
return self.units[0].unit_type
|
||||
|
||||
def num_radio_channels(self, radio_id: int) -> int:
|
||||
"""Returns the number of preset channels for the given radio."""
|
||||
# Note: pydcs only initializes the radio presets for client slots.
|
||||
return self.client_units[0].num_radio_channels(radio_id)
|
||||
|
||||
def channel_for(
|
||||
self, frequency: RadioFrequency) -> Optional[ChannelAssignment]:
|
||||
"""Returns the radio and channel number for the given frequency."""
|
||||
return self.frequency_to_channel_map.get(frequency, None)
|
||||
|
||||
def assign_channel(self, radio_id: int, channel_id: int,
|
||||
frequency: RadioFrequency) -> None:
|
||||
"""Assigns a preset radio channel to the given frequency."""
|
||||
for unit in self.client_units:
|
||||
unit.set_radio_channel_preset(radio_id, channel_id, frequency.mhz)
|
||||
|
||||
# One frequency could be bound to multiple channels. Prefer the first,
|
||||
# since with the current implementation it will be the lowest numbered
|
||||
# channel.
|
||||
if frequency not in self.frequency_to_channel_map:
|
||||
self.frequency_to_channel_map[frequency] = ChannelAssignment(
|
||||
radio_id, channel_id
|
||||
)
|
||||
|
||||
|
||||
class RadioChannelAllocator:
|
||||
"""Base class for radio channel allocators."""
|
||||
|
||||
def assign_channels_for_flight(self, flight: FlightData,
|
||||
air_support: AirSupport) -> None:
|
||||
"""Assigns mission frequencies to preset channels for the flight."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CommonRadioChannelAllocator(RadioChannelAllocator):
|
||||
"""Radio channel allocator suitable for most aircraft.
|
||||
|
||||
Most of the aircraft with preset channels available have one or more radios
|
||||
with 20 or more channels available (typically per-radio, but this is not the
|
||||
case for the JF-17).
|
||||
"""
|
||||
|
||||
#: Index of the radio used for intra-flight communications. Matches the
|
||||
#: index of the panel_radio field of the pydcs.dcs.planes object.
|
||||
inter_flight_radio_index: Optional[int]
|
||||
|
||||
#: Index of the radio used for intra-flight communications. Matches the
|
||||
#: index of the panel_radio field of the pydcs.dcs.planes object.
|
||||
intra_flight_radio_index: Optional[int]
|
||||
|
||||
def assign_channels_for_flight(self, flight: FlightData,
|
||||
air_support: AirSupport) -> None:
|
||||
flight.assign_channel(
|
||||
self.intra_flight_radio_index, 1, flight.intra_flight_channel)
|
||||
|
||||
# For cases where the inter-flight and intra-flight radios share presets
|
||||
# (the JF-17 only has one set of channels, even though it can use two
|
||||
# channels simultaneously), start assigning inter-flight channels at 2.
|
||||
radio_id = self.inter_flight_radio_index
|
||||
if self.intra_flight_radio_index == radio_id:
|
||||
first_channel = 2
|
||||
else:
|
||||
first_channel = 1
|
||||
|
||||
last_channel = flight.num_radio_channels(radio_id)
|
||||
channel_alloc = iter(range(first_channel, last_channel + 1))
|
||||
|
||||
if flight.departure.atc is not None:
|
||||
flight.assign_channel(radio_id, next(channel_alloc),
|
||||
flight.departure.atc)
|
||||
|
||||
# TODO: If there ever are multiple AWACS, limit to mission relevant.
|
||||
for awacs in air_support.awacs:
|
||||
flight.assign_channel(radio_id, next(channel_alloc), awacs.freq)
|
||||
|
||||
if flight.arrival != flight.departure and flight.arrival.atc is not None:
|
||||
flight.assign_channel(radio_id, next(channel_alloc),
|
||||
flight.arrival.atc)
|
||||
|
||||
try:
|
||||
# TODO: Skip incompatible tankers.
|
||||
for tanker in air_support.tankers:
|
||||
flight.assign_channel(
|
||||
radio_id, next(channel_alloc), tanker.freq)
|
||||
|
||||
if flight.divert is not None and flight.divert.atc is not None:
|
||||
flight.assign_channel(radio_id, next(channel_alloc),
|
||||
flight.divert.atc)
|
||||
except StopIteration:
|
||||
# Any remaining channels are nice-to-haves, but not necessary for
|
||||
# the few aircraft with a small number of channels available.
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class WarthogRadioChannelAllocator(RadioChannelAllocator):
|
||||
"""Preset channel allocator for the A-10C."""
|
||||
|
||||
def assign_channels_for_flight(self, flight: FlightData,
|
||||
air_support: AirSupport) -> None:
|
||||
# The A-10's radio works differently than most aircraft. Doesn't seem to
|
||||
# be a way to set these from the mission editor, let alone pydcs.
|
||||
pass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ViggenRadioChannelAllocator(RadioChannelAllocator):
|
||||
"""Preset channel allocator for the AJS37."""
|
||||
|
||||
def assign_channels_for_flight(self, flight: FlightData,
|
||||
air_support: AirSupport) -> None:
|
||||
# The Viggen's preset channels are handled differently from other
|
||||
# aircraft. The aircraft automatically configures channels for every
|
||||
# allied flight in the game (including AWACS) and for every airfield. As
|
||||
# such, we don't need to allocate any of those. There are seven presets
|
||||
# we can modify, however: three channels for the main radio intended for
|
||||
# communication with wingmen, and four emergency channels for the backup
|
||||
# radio. We'll set the first channel of the main radio to the
|
||||
# intra-flight channel, and the first three emergency channels to each
|
||||
# of the flight plan's airfields. The fourth emergency channel is always
|
||||
# the guard channel.
|
||||
radio_id = 1
|
||||
flight.assign_channel(radio_id, 1, flight.intra_flight_channel)
|
||||
flight.assign_channel(radio_id, 4, flight.departure.atc)
|
||||
flight.assign_channel(radio_id, 5, flight.arrival.atc)
|
||||
# TODO: Assign divert to 6 when we support divert airfields.
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SCR522RadioChannelAllocator(RadioChannelAllocator):
|
||||
"""Preset channel allocator for the SCR522 WW2 radios. (4 channels)"""
|
||||
|
||||
def assign_channels_for_flight(self, flight: FlightData,
|
||||
air_support: AirSupport) -> None:
|
||||
radio_id = 1
|
||||
flight.assign_channel(radio_id, 1, flight.intra_flight_channel)
|
||||
flight.assign_channel(radio_id, 2, flight.departure.atc)
|
||||
flight.assign_channel(radio_id, 3, flight.arrival.atc)
|
||||
|
||||
# TODO : Some GCI on Channel 4 ?
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AircraftData:
|
||||
"""Additional aircraft data not exposed by pydcs."""
|
||||
|
||||
#: The type of radio used for inter-flight communications.
|
||||
inter_flight_radio: Radio
|
||||
|
||||
#: The type of radio used for intra-flight communications.
|
||||
intra_flight_radio: Radio
|
||||
|
||||
#: The radio preset channel allocator, if the aircraft supports channel
|
||||
#: presets. If the aircraft does not support preset channels, this will be
|
||||
#: None.
|
||||
channel_allocator: Optional[RadioChannelAllocator]
|
||||
|
||||
#: Defines how channels should be named when printed in the kneeboard.
|
||||
channel_namer: Type[ChannelNamer] = ChannelNamer
|
||||
|
||||
|
||||
# Indexed by the id field of the pydcs PlaneType.
|
||||
AIRCRAFT_DATA: Dict[str, AircraftData] = {
|
||||
"A-10C": AircraftData(
|
||||
inter_flight_radio=get_radio("AN/ARC-164"),
|
||||
intra_flight_radio=get_radio("AN/ARC-164"), # VHF for intraflight is not accepted anymore by DCS (see https://forums.eagle.ru/showthread.php?p=4499738)
|
||||
channel_allocator=WarthogRadioChannelAllocator()
|
||||
),
|
||||
|
||||
"AJS37": AircraftData(
|
||||
# The AJS37 has somewhat unique radio configuration. Two backup radio
|
||||
# (FR 24) can only operate simultaneously with the main radio in guard
|
||||
# mode. As such, we only use the main radio for both inter- and intra-
|
||||
# flight communication.
|
||||
inter_flight_radio=get_radio("FR 22"),
|
||||
intra_flight_radio=get_radio("FR 22"),
|
||||
channel_allocator=ViggenRadioChannelAllocator(),
|
||||
channel_namer=ViggenChannelNamer
|
||||
),
|
||||
|
||||
"AV8BNA": AircraftData(
|
||||
inter_flight_radio=get_radio("AN/ARC-210"),
|
||||
intra_flight_radio=get_radio("AN/ARC-210"),
|
||||
channel_allocator=CommonRadioChannelAllocator(
|
||||
inter_flight_radio_index=2,
|
||||
intra_flight_radio_index=1
|
||||
)
|
||||
),
|
||||
|
||||
"F-14B": AircraftData(
|
||||
inter_flight_radio=get_radio("AN/ARC-159"),
|
||||
intra_flight_radio=get_radio("AN/ARC-182"),
|
||||
channel_allocator=CommonRadioChannelAllocator(
|
||||
inter_flight_radio_index=1,
|
||||
intra_flight_radio_index=2
|
||||
),
|
||||
channel_namer=TomcatChannelNamer
|
||||
),
|
||||
|
||||
"F-16C_50": AircraftData(
|
||||
inter_flight_radio=get_radio("AN/ARC-164"),
|
||||
intra_flight_radio=get_radio("AN/ARC-222"),
|
||||
# COM2 is the AN/ARC-222, which is the VHF radio we want to use for
|
||||
# intra-flight communication to leave COM1 open for UHF inter-flight.
|
||||
channel_allocator=CommonRadioChannelAllocator(
|
||||
inter_flight_radio_index=1,
|
||||
intra_flight_radio_index=2
|
||||
),
|
||||
channel_namer=ViperChannelNamer
|
||||
),
|
||||
|
||||
"FA-18C_hornet": AircraftData(
|
||||
inter_flight_radio=get_radio("AN/ARC-210"),
|
||||
intra_flight_radio=get_radio("AN/ARC-210"),
|
||||
# DCS will clobber channel 1 of the first radio compatible with the
|
||||
# flight's assigned frequency. Since the F/A-18's two radios are both
|
||||
# AN/ARC-210s, radio 1 will be compatible regardless of which frequency
|
||||
# is assigned, so we must use radio 1 for the intra-flight radio.
|
||||
channel_allocator=CommonRadioChannelAllocator(
|
||||
inter_flight_radio_index=2,
|
||||
intra_flight_radio_index=1
|
||||
)
|
||||
),
|
||||
|
||||
"JF-17": AircraftData(
|
||||
inter_flight_radio=get_radio("R&S M3AR UHF"),
|
||||
intra_flight_radio=get_radio("R&S M3AR VHF"),
|
||||
channel_allocator=CommonRadioChannelAllocator(
|
||||
inter_flight_radio_index=1,
|
||||
intra_flight_radio_index=1
|
||||
),
|
||||
# Same naming pattern as the Viper, so just reuse that.
|
||||
channel_namer=ViperChannelNamer
|
||||
),
|
||||
|
||||
"M-2000C": AircraftData(
|
||||
inter_flight_radio=get_radio("TRT ERA 7000 V/UHF"),
|
||||
intra_flight_radio=get_radio("TRT ERA 7200 UHF"),
|
||||
channel_allocator=CommonRadioChannelAllocator(
|
||||
inter_flight_radio_index=1,
|
||||
intra_flight_radio_index=2
|
||||
),
|
||||
channel_namer=MirageChannelNamer
|
||||
),
|
||||
|
||||
"P-51D": AircraftData(
|
||||
inter_flight_radio=get_radio("SCR522"),
|
||||
intra_flight_radio=get_radio("SCR522"),
|
||||
channel_allocator=CommonRadioChannelAllocator(
|
||||
inter_flight_radio_index=1,
|
||||
intra_flight_radio_index=1
|
||||
),
|
||||
channel_namer=SCR522ChannelNamer
|
||||
),
|
||||
}
|
||||
AIRCRAFT_DATA["P-51D-30-NA"] = AIRCRAFT_DATA["P-51D"]
|
||||
AIRCRAFT_DATA["P-47D-30"] = AIRCRAFT_DATA["P-51D"]
|
||||
|
||||
|
||||
class AircraftConflictGenerator:
|
||||
escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]]
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings, game):
|
||||
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings,
|
||||
game, radio_registry: RadioRegistry):
|
||||
self.m = mission
|
||||
self.game = game
|
||||
self.settings = settings
|
||||
self.conflict = conflict
|
||||
self.radio_registry = radio_registry
|
||||
self.escort_targets = []
|
||||
self.flights: List[FlightData] = []
|
||||
|
||||
def get_intra_flight_channel(self, airframe: UnitType) -> RadioFrequency:
|
||||
"""Allocates an intra-flight channel to a group.
|
||||
|
||||
Args:
|
||||
airframe: The type of aircraft a channel should be allocated for.
|
||||
|
||||
Returns:
|
||||
The frequency of the intra-flight channel.
|
||||
"""
|
||||
try:
|
||||
aircraft_data = AIRCRAFT_DATA[airframe.id]
|
||||
return self.radio_registry.alloc_for_radio(
|
||||
aircraft_data.intra_flight_radio)
|
||||
except KeyError:
|
||||
return get_fallback_channel(airframe)
|
||||
|
||||
def _start_type(self) -> StartType:
|
||||
return self.settings.cold_start and StartType.Cold or StartType.Warm
|
||||
|
||||
|
||||
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task], client_count: int):
|
||||
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task],
|
||||
flight: Flight, dynamic_runways: Dict[str, RunwayData]):
|
||||
did_load_loadout = False
|
||||
unit_type = group.units[0].unit_type
|
||||
|
||||
print("SETUP GROUP : " + str(for_task) + " -- " + str(group.name))
|
||||
|
||||
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
|
||||
override_loadout = db.PLANE_PAYLOAD_OVERRIDES[unit_type]
|
||||
if type(override_loadout) == dict:
|
||||
|
||||
# Clear pylons
|
||||
for p in group.units:
|
||||
p.pylons.clear()
|
||||
|
||||
# Now load loadout
|
||||
if for_task in db.PLANE_PAYLOAD_OVERRIDES[unit_type]:
|
||||
payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][for_task]
|
||||
group.load_loadout(payload_name)
|
||||
@@ -82,12 +537,13 @@ class AircraftConflictGenerator:
|
||||
for unit_instance in group.units:
|
||||
unit_instance.livery_id = db.PLANE_LIVERY_OVERRIDES[unit_type]
|
||||
|
||||
single_client = client_count == 1
|
||||
for idx in range(0, min(len(group.units), client_count)):
|
||||
single_client = flight.client_count == 1
|
||||
for idx in range(0, min(len(group.units), flight.client_count)):
|
||||
unit = group.units[idx]
|
||||
if single_client:
|
||||
group.units[idx].set_player()
|
||||
unit.set_player()
|
||||
else:
|
||||
group.units[idx].set_client()
|
||||
unit.set_client()
|
||||
|
||||
# Do not generate player group with late activation.
|
||||
if group.late_activation:
|
||||
@@ -95,18 +551,67 @@ class AircraftConflictGenerator:
|
||||
|
||||
# Set up F-14 Client to have pre-stored alignement
|
||||
if unit_type is F_14B:
|
||||
group.units[idx].set_property(F_14B.Properties.INSAlignmentStored.id, True)
|
||||
unit.set_property(F_14B.Properties.INSAlignmentStored.id, True)
|
||||
|
||||
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
|
||||
if unit_type in helicopters.helicopter_map.values() and unit_type not in [UH_1H]:
|
||||
group.set_frequency(127.5)
|
||||
channel = self.get_intra_flight_channel(unit_type)
|
||||
group.set_frequency(channel.mhz)
|
||||
|
||||
# TODO: Support for different departure/arrival airfields.
|
||||
cp = flight.from_cp
|
||||
fallback_runway = RunwayData(cp.full_name, runway_name="")
|
||||
if cp.cptype == ControlPointType.AIRBASE:
|
||||
departure_runway = self.get_preferred_runway(flight.from_cp.airport)
|
||||
elif cp.is_fleet:
|
||||
departure_runway = dynamic_runways.get(cp.name, fallback_runway)
|
||||
else:
|
||||
if unit_type not in [P_51D_30_NA, P_51D, SpitfireLFMkIX, SpitfireLFMkIXCW, FW_190A8, FW_190D9, Bf_109K_4]:
|
||||
group.set_frequency(251.0)
|
||||
logging.warning(f"Unhandled departure control point: {cp.cptype}")
|
||||
departure_runway = fallback_runway
|
||||
|
||||
# The first waypoint is set automatically by pydcs, so it's not in our
|
||||
# list. Convert the pydcs MovingPoint to a FlightWaypoint so it shows up
|
||||
# in our FlightData.
|
||||
first_point = FlightWaypoint.from_pydcs(group.points[0], flight.from_cp)
|
||||
self.flights.append(FlightData(
|
||||
flight_type=flight.flight_type,
|
||||
units=group.units,
|
||||
size=len(group.units),
|
||||
friendly=flight.from_cp.captured,
|
||||
departure_delay=flight.scheduled_in,
|
||||
departure=departure_runway,
|
||||
arrival=departure_runway,
|
||||
# TODO: Support for divert airfields.
|
||||
divert=None,
|
||||
waypoints=[first_point] + flight.points,
|
||||
intra_flight_channel=channel
|
||||
))
|
||||
|
||||
# Special case so Su 33 carrier take off
|
||||
if unit_type is Su_33:
|
||||
if task is not CAP:
|
||||
for unit in group.units:
|
||||
unit.fuel = Su_33.fuel_max / 2.2
|
||||
else:
|
||||
# WW2
|
||||
group.set_frequency(124.0)
|
||||
for unit in group.units:
|
||||
unit.fuel = Su_33.fuel_max * 0.8
|
||||
|
||||
def get_preferred_runway(self, airport: Airport) -> RunwayData:
|
||||
"""Returns the preferred runway for the given airport.
|
||||
|
||||
Right now we're only selecting runways based on whether or not they have
|
||||
ILS, but we could also choose based on wind conditions, or which
|
||||
direction flight plans should follow.
|
||||
"""
|
||||
runways = list(RunwayData.for_pydcs_airport(airport))
|
||||
for runway in runways:
|
||||
# Prefer any runway with ILS.
|
||||
if runway.ils is not None:
|
||||
return runway
|
||||
# Otherwise we lack the mission information to pick more usefully,
|
||||
# so just use the first runway.
|
||||
return runways[0]
|
||||
|
||||
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None, start_type = None) -> FlyingGroup:
|
||||
assert count > 0
|
||||
@@ -120,7 +625,7 @@ class AircraftConflictGenerator:
|
||||
country=side,
|
||||
name=name,
|
||||
aircraft_type=unit_type,
|
||||
airport=self.m.terrain.airport_by_id(airport.id),
|
||||
airport=airport,
|
||||
maintask=None,
|
||||
start_type=start_type,
|
||||
group_size=count,
|
||||
@@ -155,10 +660,13 @@ class AircraftConflictGenerator:
|
||||
group.points[0].alt_type = "RADIO"
|
||||
return group
|
||||
|
||||
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup]) -> FlyingGroup:
|
||||
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup], start_type=None) -> FlyingGroup:
|
||||
assert count > 0
|
||||
assert unit is not None
|
||||
|
||||
if start_type is None:
|
||||
start_type = self._start_type()
|
||||
|
||||
logging.info("airgen: {} for {} at unit {}".format(unit_type, side.id, at))
|
||||
return self.m.flight_group_from_unit(
|
||||
country=side,
|
||||
@@ -166,7 +674,7 @@ class AircraftConflictGenerator:
|
||||
aircraft_type=unit_type,
|
||||
pad_group=at,
|
||||
maintask=None,
|
||||
start_type=self._start_type(),
|
||||
start_type=start_type,
|
||||
group_size=count)
|
||||
|
||||
def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition):
|
||||
@@ -188,6 +696,7 @@ class AircraftConflictGenerator:
|
||||
try:
|
||||
return self._generate_at_airport(name, side, unit_type, count, client_count, at)
|
||||
except NoParkingSlotError:
|
||||
logging.info("No parking slot found at " + at.name + ", switching to air start.")
|
||||
pass
|
||||
return self._generate_inflight(name, side, unit_type, count, client_count, at.position)
|
||||
else:
|
||||
@@ -243,26 +752,39 @@ class AircraftConflictGenerator:
|
||||
logging.warning("Pylon not found ! => Pylon" + key + " on " + str(flight.unit_type))
|
||||
|
||||
|
||||
def generate_flights(self, cp, country, flight_planner:FlightPlanner):
|
||||
def generate_flights(self, cp, country, flight_planner: FlightPlanner,
|
||||
dynamic_runways: Dict[str, RunwayData]):
|
||||
# Clear pydcs parking slots
|
||||
if cp.airport is not None:
|
||||
logging.info("CLEARING SLOTS @ " + cp.airport.name)
|
||||
logging.info("===============")
|
||||
if cp.airport is not None:
|
||||
for ps in cp.airport.parking_slots:
|
||||
logging.info("SLOT : " + str(ps.unit_id))
|
||||
ps.unit_id = None
|
||||
logging.info("----------------")
|
||||
logging.info("===============")
|
||||
|
||||
for flight in flight_planner.flights:
|
||||
|
||||
if flight.client_count == 0 and self.game.position_culled(flight.from_cp.position):
|
||||
logging.info("Flight not generated : culled")
|
||||
continue
|
||||
logging.info("Generating flight : " + str(flight.unit_type))
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
if flight.flight_type == FlightType.INTERCEPTION:
|
||||
self.setup_group_as_intercept_flight(group, flight)
|
||||
self._setup_custom_payload(flight, group)
|
||||
else:
|
||||
self.setup_flight_group(group, flight, flight.flight_type)
|
||||
self.setup_flight_group(group, flight, flight.flight_type,
|
||||
dynamic_runways)
|
||||
self.setup_group_activation_trigger(flight, group)
|
||||
|
||||
|
||||
def setup_group_activation_trigger(self, flight, group):
|
||||
if flight.scheduled_in > 0 and flight.client_count == 0:
|
||||
|
||||
if flight.start_type != "In Flight":
|
||||
if flight.start_type != "In Flight" and flight.from_cp.cptype not in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]:
|
||||
group.late_activation = False
|
||||
group.uncontrolled = True
|
||||
|
||||
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationControlTriggerForGroup" + str(group.id))
|
||||
activation_trigger = TriggerOnce(Event.NoEvent, "FlightStartTrigger" + str(group.id))
|
||||
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in * 60))
|
||||
if (flight.from_cp.cptype == ControlPointType.AIRBASE):
|
||||
if flight.from_cp.captured:
|
||||
@@ -272,12 +794,16 @@ class AircraftConflictGenerator:
|
||||
activation_trigger.add_condition(
|
||||
CoalitionHasAirdrome(self.game.get_enemy_coalition_id(), flight.from_cp.id))
|
||||
|
||||
if flight.flight_type == FlightType.INTERCEPTION:
|
||||
self.setup_interceptor_triggers(group, flight, activation_trigger)
|
||||
|
||||
group.add_trigger_action(StartCommand())
|
||||
activation_trigger.add_action(AITaskPush(group.id, len(group.tasks)))
|
||||
|
||||
self.m.triggerrules.triggers.append(activation_trigger)
|
||||
else:
|
||||
group.late_activation = True
|
||||
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationActivationTriggerForGroup" + str(group.id))
|
||||
activation_trigger = TriggerOnce(Event.NoEvent, "FlightLateActivationTrigger" + str(group.id))
|
||||
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in*60))
|
||||
|
||||
if(flight.from_cp.cptype == ControlPointType.AIRBASE):
|
||||
@@ -286,13 +812,26 @@ class AircraftConflictGenerator:
|
||||
else:
|
||||
activation_trigger.add_condition(CoalitionHasAirdrome(self.game.get_enemy_coalition_id(), flight.from_cp.id))
|
||||
|
||||
if flight.flight_type == FlightType.INTERCEPTION:
|
||||
self.setup_interceptor_triggers(group, flight, activation_trigger)
|
||||
|
||||
activation_trigger.add_action(ActivateGroup(group.id))
|
||||
self.m.triggerrules.triggers.append(activation_trigger)
|
||||
|
||||
def setup_interceptor_triggers(self, group, flight, activation_trigger):
|
||||
|
||||
detection_zone = self.m.triggers.add_triggerzone(flight.from_cp.position, radius=25000, hidden=False, name="ITZ")
|
||||
if flight.from_cp.captured:
|
||||
activation_trigger.add_condition(PartOfCoalitionInZone(self.game.get_enemy_color(), detection_zone.id)) # TODO : support unit type in part of coalition
|
||||
activation_trigger.add_action(MessageToAll(String("WARNING : Enemy aircraft have been detected in the vicinity of " + flight.from_cp.name + ". Interceptors are taking off."), 20))
|
||||
else:
|
||||
activation_trigger.add_condition(PartOfCoalitionInZone(self.game.get_player_color(), detection_zone.id))
|
||||
activation_trigger.add_action(MessageToAll(String("WARNING : We have detected that enemy aircraft are scrambling for an interception on " + flight.from_cp.name + " airbase."), 20))
|
||||
|
||||
def generate_planned_flight(self, cp, country, flight:Flight):
|
||||
try:
|
||||
if flight.client_count == 0 and self.game.settings.perf_ai_parking_start:
|
||||
flight.start_type = "Warm"
|
||||
flight.start_type = "Cold"
|
||||
|
||||
if flight.start_type == "In Flight":
|
||||
group = self._generate_group(
|
||||
@@ -317,7 +856,8 @@ class AircraftConflictGenerator:
|
||||
unit_type=flight.unit_type,
|
||||
count=flight.count,
|
||||
client_count=0,
|
||||
at=self.m.find_group(group_name),)
|
||||
at=self.m.find_group(group_name),
|
||||
start_type=st)
|
||||
else:
|
||||
group = self._generate_at_airport(
|
||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||
@@ -325,10 +865,12 @@ class AircraftConflictGenerator:
|
||||
unit_type=flight.unit_type,
|
||||
count=flight.count,
|
||||
client_count=0,
|
||||
airport=self.m.terrain.airport_by_id(cp.at.id),
|
||||
airport=cp.airport,
|
||||
start_type=st)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
# Generated when there is no place on Runway or on Parking Slots
|
||||
logging.error(e)
|
||||
logging.warning("No room on runway or parking slots. Starting from the air.")
|
||||
flight.start_type = "In Flight"
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||
@@ -339,97 +881,129 @@ class AircraftConflictGenerator:
|
||||
at=cp.position)
|
||||
group.points[0].alt = 1500
|
||||
|
||||
flight.group = group
|
||||
return group
|
||||
|
||||
def setup_group_as_intercept_flight(self, group, flight):
|
||||
group.points[0].ETA = 0
|
||||
group.late_activation = True
|
||||
self._setup_group(group, Intercept, flight.client_count)
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
def setup_flight_group(self, group, flight, flight_type,
|
||||
dynamic_runways: Dict[str, RunwayData]):
|
||||
|
||||
def setup_flight_group(self, group, flight, flight_type):
|
||||
|
||||
if flight_type in [FlightType.CAP, FlightType.BARCAP, FlightType.TARCAP]:
|
||||
if flight_type in [FlightType.CAP, FlightType.BARCAP, FlightType.TARCAP, FlightType.INTERCEPTION]:
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, CAP, flight.client_count)
|
||||
self._setup_group(group, CAP, flight, dynamic_runways)
|
||||
# group.points[0].tasks.clear()
|
||||
# group.tasks.clear()
|
||||
# group.tasks.append(EngageTargets(max_distance=40, targets=[Targets.All.Air]))
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(EngageTargets(max_distance=nm_to_meter(50), targets=[Targets.All.Air]))
|
||||
# group.tasks.append(EngageTargets(max_distance=nm_to_meter(120), targets=[Targets.All.Air]))
|
||||
pass
|
||||
if flight.unit_type not in GUNFIGHTERS:
|
||||
group.points[0].tasks.append(OptRTBOnOutOfAmmo(OptRTBOnOutOfAmmo.Values.AAM))
|
||||
else:
|
||||
group.points[0].tasks.append(OptRTBOnOutOfAmmo(OptRTBOnOutOfAmmo.Values.Cannon))
|
||||
|
||||
elif flight_type in [FlightType.CAS, FlightType.BAI]:
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, flight.client_count)
|
||||
self._setup_group(group, CAS, flight, dynamic_runways)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(CASTaskAction())
|
||||
group.points[0].tasks.append(EngageTargets(max_distance=nm_to_meter(10), targets=[Targets.All.GroundUnits.GroundVehicles]))
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
group.points[0].tasks.append(OptRTBOnOutOfAmmo(OptRTBOnOutOfAmmo.Values.Unguided))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
elif flight_type in [FlightType.SEAD, FlightType.DEAD]:
|
||||
group.task = SEAD.name
|
||||
self._setup_group(group, SEAD, flight.client_count)
|
||||
self._setup_group(group, SEAD, flight, dynamic_runways)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(SEADTaskAction())
|
||||
group.points[0].tasks.append(NoTask())
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
group.points[0].tasks.append(OptRTBOnOutOfAmmo(OptRTBOnOutOfAmmo.Values.ASM))
|
||||
elif flight_type in [FlightType.STRIKE]:
|
||||
group.task = PinpointStrike.name
|
||||
self._setup_group(group, GroundAttack, flight.client_count)
|
||||
self._setup_group(group, GroundAttack, flight, dynamic_runways)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
elif flight_type in [FlightType.ANTISHIP]:
|
||||
group.task = AntishipStrike.name
|
||||
self._setup_group(group, AntishipStrike, flight.client_count)
|
||||
self._setup_group(group, AntishipStrike, flight, dynamic_runways)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
|
||||
group.points[0].tasks.append(OptRTBOnBingoFuel(True))
|
||||
group.points[0].tasks.append(OptRestrictAfterburner(True))
|
||||
|
||||
if hasattr(flight.unit_type, 'eplrs'):
|
||||
if flight.unit_type.eplrs:
|
||||
group.points[0].tasks.append(EPLRS(group.id))
|
||||
|
||||
for i, point in enumerate(flight.points):
|
||||
if not point.only_for_player or (point.only_for_player and flight.client_count > 0):
|
||||
pt = group.add_waypoint(Point(point.x, point.y), point.alt)
|
||||
if point.waypoint_type == FlightWaypointType.PATROL_TRACK:
|
||||
action = OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack)
|
||||
pt.tasks.append(action)
|
||||
action = ControlledTask(OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack))
|
||||
action.stop_after_duration(CAP_DURATION * 60)
|
||||
#for tgt in point.targets:
|
||||
# if hasattr(tgt, "position"):
|
||||
# engagetgt = EngageTargetsInZone(tgt.position, radius=CAP_DEFAULT_ENGAGE_DISTANCE, targets=[Targets.All.Air])
|
||||
# pt.tasks.append(engagetgt)
|
||||
elif point.waypoint_type == FlightWaypointType.LANDING_POINT:
|
||||
pt.type = "Land"
|
||||
pt.action = PointAction.Landing
|
||||
elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE:
|
||||
print("TGTS :")
|
||||
print(point.targets)
|
||||
|
||||
if group.units[0].unit_type == B_17G:
|
||||
if len(point.targets) > 0:
|
||||
bcenter = Point(0,0)
|
||||
for j, t in enumerate(point.targets):
|
||||
bcenter.x += t.position.x
|
||||
bcenter.y += t.position.y
|
||||
bcenter.x = bcenter.x / len(point.targets)
|
||||
bcenter.y = bcenter.y / len(point.targets)
|
||||
bombing = Bombing(bcenter)
|
||||
bombing.params["expend"] = "All"
|
||||
bombing.params["attackQtyLimit"] = False
|
||||
bombing.params["directionEnabled"] = False
|
||||
bombing.params["altitudeEnabled"] = False
|
||||
bombing.params["weaponType"] = 2032
|
||||
bombing.params["groupAttack"] = True
|
||||
pt.tasks.append(bombing)
|
||||
else:
|
||||
for j, t in enumerate(point.targets):
|
||||
print(t.position)
|
||||
pt.tasks.append(Bombing(t.position))
|
||||
if group.units[0].unit_type == JF_17 and j < 4:
|
||||
group.add_nav_target_point(t.position, "PP" + str(j + 1))
|
||||
if group.units[0].unit_type == F_14B and j == 0:
|
||||
group.add_nav_target_point(t.position, "ST")
|
||||
if group.units[0].unit_type == AJS37 and j < 9:
|
||||
group.add_nav_target_point(t.position, "M" + str(j + 1))
|
||||
elif point.waypoint_type == FlightWaypointType.INGRESS_SEAD:
|
||||
|
||||
tgroup = self.m.find_group(point.targetGroup.group_identifier)
|
||||
if tgroup is not None:
|
||||
task = AttackGroup(tgroup.id)
|
||||
task.params["expend"] = "All"
|
||||
task.params["attackQtyLimit"] = False
|
||||
task.params["directionEnabled"] = False
|
||||
task.params["altitudeEnabled"] = False
|
||||
task.params["weaponType"] = 268402702 # Guided Weapons
|
||||
task.params["groupAttack"] = True
|
||||
pt.tasks.append(task)
|
||||
|
||||
for j, t in enumerate(point.targets):
|
||||
print(t.position)
|
||||
pt.tasks.append(Bombing(t.position))
|
||||
if group.units[0].unit_type == JF_17 and j < 4:
|
||||
group.add_nav_target_point(t.position, "PP" + str(j + 1))
|
||||
if group.units[0].unit_type == F_14B and j == 0:
|
||||
group.add_nav_target_point(t.position, "ST")
|
||||
if group.units[0].unit_type == AJS37 and j < 9:
|
||||
group.add_nav_target_point(t.position, "M" + str(j + 1))
|
||||
|
||||
if pt is not None:
|
||||
pt.alt_type = point.alt_type
|
||||
pt.name = String(point.name)
|
||||
|
||||
self._setup_custom_payload(flight, group)
|
||||
|
||||
|
||||
def setup_group_as_antiship_flight(self, group, flight):
|
||||
group.task = AntishipStrike.name
|
||||
self._setup_group(group, AntishipStrike, flight.client_count)
|
||||
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(AntishipStrikeTaskAction())
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
|
||||
|
||||
1561
gen/airfields.py
Normal file
1561
gen/airfields.py
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,10 @@
|
||||
from game import db
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from .callsigns import callsign_for_support_unit
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.unitgroup import *
|
||||
from dcs.unittype import *
|
||||
from dcs.task import *
|
||||
from dcs.terrain.terrain import NoParkingSlotError
|
||||
from .radios import RadioFrequency, RadioRegistry
|
||||
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
||||
|
||||
TANKER_DISTANCE = 15000
|
||||
TANKER_ALT = 4572
|
||||
@@ -16,14 +14,39 @@ AWACS_DISTANCE = 150000
|
||||
AWACS_ALT = 13000
|
||||
|
||||
|
||||
class AirSupportConflictGenerator:
|
||||
generated_tankers = None # type: typing.List[str]
|
||||
@dataclass
|
||||
class AwacsInfo:
|
||||
"""AWACS information for the kneeboard."""
|
||||
callsign: str
|
||||
freq: RadioFrequency
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||
|
||||
@dataclass
|
||||
class TankerInfo:
|
||||
"""Tanker information for the kneeboard."""
|
||||
callsign: str
|
||||
variant: str
|
||||
freq: RadioFrequency
|
||||
tacan: TacanChannel
|
||||
|
||||
|
||||
@dataclass
|
||||
class AirSupport:
|
||||
awacs: List[AwacsInfo] = field(default_factory=list)
|
||||
tankers: List[TankerInfo] = field(default_factory=list)
|
||||
|
||||
|
||||
class AirSupportConflictGenerator:
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game,
|
||||
radio_registry: RadioRegistry,
|
||||
tacan_registry: TacanRegistry) -> None:
|
||||
self.mission = mission
|
||||
self.conflict = conflict
|
||||
self.game = game
|
||||
self.generated_tankers = []
|
||||
self.air_support = AirSupport()
|
||||
self.radio_registry = radio_registry
|
||||
self.tacan_registry = tacan_registry
|
||||
|
||||
@classmethod
|
||||
def support_tasks(cls) -> typing.Collection[typing.Type[MainTask]]:
|
||||
@@ -32,28 +55,58 @@ class AirSupportConflictGenerator:
|
||||
def generate(self, is_awacs_enabled):
|
||||
player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp
|
||||
|
||||
fallback_tanker_number = 0
|
||||
|
||||
for i, tanker_unit_type in enumerate(db.find_unittype(Refueling, self.conflict.attackers_side)):
|
||||
self.generated_tankers.append(db.unit_type_name(tanker_unit_type))
|
||||
variant = db.unit_type_name(tanker_unit_type)
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
||||
tanker_heading = self.conflict.to_cp.position.heading_between_point(self.conflict.from_cp.position) + TANKER_HEADING_OFFSET * i
|
||||
tanker_position = player_cp.position.point_from_heading(tanker_heading, TANKER_DISTANCE)
|
||||
tanker_group = self.mission.refuel_flight(
|
||||
country=self.mission.country(self.game.player_country),
|
||||
name=namegen.next_tanker_name(self.mission.country(self.game.player_country)),
|
||||
name=namegen.next_tanker_name(self.mission.country(self.game.player_country), tanker_unit_type),
|
||||
airport=None,
|
||||
plane_type=tanker_unit_type,
|
||||
position=tanker_position,
|
||||
altitude=TANKER_ALT,
|
||||
frequency=130 + i,
|
||||
race_distance=58000,
|
||||
frequency=freq.mhz,
|
||||
start_type=StartType.Warm,
|
||||
tacanchannel="{}X".format(97 + i),
|
||||
speed=574,
|
||||
tacanchannel=str(tacan),
|
||||
)
|
||||
|
||||
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=97 + i, unit_id=tanker_group.id, aa=False))
|
||||
callsign = callsign_for_support_unit(tanker_group)
|
||||
tacan_callsign = {
|
||||
"Texaco": "TEX",
|
||||
"Arco": "ARC",
|
||||
"Shell": "SHL",
|
||||
}.get(callsign)
|
||||
if tacan_callsign is None:
|
||||
# The dict above is all the callsigns currently in the game, but
|
||||
# non-Western countries don't use the callsigns and instead just
|
||||
# use numbers. It's possible that none of those nations have
|
||||
# TACAN compatible refueling aircraft, but fallback just in
|
||||
# case.
|
||||
tacan_callsign = f"TK{fallback_tanker_number}"
|
||||
fallback_tanker_number += 1
|
||||
|
||||
if tanker_unit_type != IL_78M:
|
||||
# Override PyDCS tacan channel.
|
||||
tanker_group.points[0].tasks.pop()
|
||||
tanker_group.points[0].tasks.append(ActivateBeaconCommand(
|
||||
tacan.number, tacan.band.value, tacan_callsign, True,
|
||||
tanker_group.units[0].id, True))
|
||||
|
||||
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
self.air_support.tankers.append(TankerInfo(callsign, variant, freq, tacan))
|
||||
|
||||
if is_awacs_enabled:
|
||||
try:
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side)[0]
|
||||
awacs_flight = self.mission.awacs_flight(
|
||||
country=self.mission.country(self.game.player_country),
|
||||
@@ -62,11 +115,13 @@ class AirSupportConflictGenerator:
|
||||
altitude=AWACS_ALT,
|
||||
airport=None,
|
||||
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
|
||||
frequency=133,
|
||||
frequency=freq.mhz,
|
||||
start_type=StartType.Warm,
|
||||
)
|
||||
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||
except:
|
||||
print("No AWACS for faction")
|
||||
|
||||
self.air_support.awacs.append(AwacsInfo(
|
||||
callsign_for_support_unit(awacs_flight), freq))
|
||||
except:
|
||||
print("No AWACS for faction")
|
||||
175
gen/armor.py
175
gen/armor.py
@@ -1,7 +1,12 @@
|
||||
from dcs.task import *
|
||||
from dataclasses import dataclass
|
||||
|
||||
from dcs.action import AITaskPush
|
||||
from dcs.condition import TimeAfter, UnitDamaged, Or, GroupLifeLess
|
||||
from dcs.triggers import TriggerOnce, Event
|
||||
|
||||
from gen import namegen
|
||||
from gen.ground_forces.ai_ground_planner import CombatGroupRole, DISTANCE_FROM_FRONTLINE
|
||||
from .callsigns import callsign_for_support_unit
|
||||
from .conflictgen import *
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 0.1, 0.3
|
||||
@@ -17,16 +22,30 @@ AGGRESIVE_MOVE_DISTANCE = 16000
|
||||
|
||||
FIGHT_DISTANCE = 3500
|
||||
|
||||
RANDOM_OFFSET_ATTACK = 250
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class JtacInfo:
|
||||
"""JTAC information."""
|
||||
unit_name: str
|
||||
callsign: str
|
||||
region: str
|
||||
code: str
|
||||
# TODO: Radio info? Type?
|
||||
|
||||
|
||||
class GroundConflictGenerator:
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game, player_planned_combat_groups, enemy_planned_combat_groups, player_stance):
|
||||
self.m = mission
|
||||
self.mission = mission
|
||||
self.conflict = conflict
|
||||
self.enemy_planned_combat_groups = enemy_planned_combat_groups
|
||||
self.player_planned_combat_groups = player_planned_combat_groups
|
||||
self.player_stance = CombatStance(player_stance)
|
||||
self.enemy_stance = random.choice([CombatStance.AGGRESIVE, CombatStance.AGGRESIVE, CombatStance.AGGRESIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]) if len(enemy_planned_combat_groups) > len(player_planned_combat_groups) else random.choice([CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.AMBUSH, CombatStance.AGGRESIVE])
|
||||
self.enemy_stance = random.choice([CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]) if len(enemy_planned_combat_groups) > len(player_planned_combat_groups) else random.choice([CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.AMBUSH, CombatStance.AGGRESSIVE])
|
||||
self.game = game
|
||||
self.jtacs: List[JtacInfo] = []
|
||||
|
||||
def _group_point(self, point) -> Point:
|
||||
distance = randint(
|
||||
@@ -58,7 +77,7 @@ class GroundConflictGenerator:
|
||||
|
||||
if final_position is not None:
|
||||
g = self._generate_group(
|
||||
side=self.m.country(self.game.player_country),
|
||||
side=self.mission.country(self.game.player_country),
|
||||
unit=group.units[0],
|
||||
heading=self.conflict.heading+90,
|
||||
count=len(group.units),
|
||||
@@ -66,7 +85,7 @@ class GroundConflictGenerator:
|
||||
g.set_skill(self.game.settings.player_skill)
|
||||
player_groups.append((g,group))
|
||||
|
||||
self.gen_infantry_group_for_group(g, True, self.m.country(self.game.player_country), self.conflict.heading + 90)
|
||||
self.gen_infantry_group_for_group(g, True, self.mission.country(self.game.player_country), self.conflict.heading + 90)
|
||||
|
||||
# Create enemy groups at random position
|
||||
for group in self.enemy_planned_combat_groups:
|
||||
@@ -78,7 +97,7 @@ class GroundConflictGenerator:
|
||||
|
||||
if final_position is not None:
|
||||
g = self._generate_group(
|
||||
side=self.m.country(self.game.enemy_country),
|
||||
side=self.mission.country(self.game.enemy_country),
|
||||
unit=group.units[0],
|
||||
heading=self.conflict.heading - 90,
|
||||
count=len(group.units),
|
||||
@@ -86,14 +105,34 @@ class GroundConflictGenerator:
|
||||
g.set_skill(self.game.settings.enemy_vehicle_skill)
|
||||
enemy_groups.append((g, group))
|
||||
|
||||
self.gen_infantry_group_for_group(g, False, self.m.country(self.game.enemy_country), self.conflict.heading - 90)
|
||||
|
||||
self.gen_infantry_group_for_group(g, False, self.mission.country(self.game.enemy_country), self.conflict.heading - 90)
|
||||
|
||||
# Plan combat actions for groups
|
||||
self.plan_action_for_groups(self.player_stance, player_groups, enemy_groups, self.conflict.heading + 90, self.conflict.from_cp, self.conflict.to_cp)
|
||||
self.plan_action_for_groups(self.enemy_stance, enemy_groups, player_groups, self.conflict.heading - 90, self.conflict.to_cp, self.conflict.from_cp)
|
||||
|
||||
# Add JTAC
|
||||
if "has_jtac" in self.game.player_faction and self.game.player_faction["has_jtac"] and self.game.settings.include_jtac_if_available:
|
||||
n = "JTAC" + str(self.conflict.from_cp.id) + str(self.conflict.to_cp.id)
|
||||
code = 1688 - len(self.jtacs)
|
||||
|
||||
utype = MQ_9_Reaper
|
||||
if "jtac_unit" in self.game.player_faction:
|
||||
utype = self.game.player_faction["jtac_unit"]
|
||||
|
||||
jtac = self.mission.flight_group(country=self.mission.country(self.game.player_country),
|
||||
name=n,
|
||||
aircraft_type=utype,
|
||||
position=position[0],
|
||||
airport=None,
|
||||
altitude=5000)
|
||||
jtac.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
jtac.points[0].tasks.append(SetImmortalCommand(True))
|
||||
jtac.points[0].tasks.append(OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle))
|
||||
frontline = f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}"
|
||||
# Note: Will need to change if we ever add ground based JTAC.
|
||||
callsign = callsign_for_support_unit(jtac)
|
||||
self.jtacs.append(JtacInfo(n, callsign, frontline, str(code)))
|
||||
|
||||
def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading):
|
||||
|
||||
@@ -118,7 +157,7 @@ class GroundConflictGenerator:
|
||||
return
|
||||
|
||||
u = random.choice(possible_infantry_units)
|
||||
self.m.vehicle_group(
|
||||
self.mission.vehicle_group(
|
||||
side,
|
||||
namegen.next_infantry_name(side, cp, u), u,
|
||||
position=infantry_position,
|
||||
@@ -129,7 +168,7 @@ class GroundConflictGenerator:
|
||||
for i in range(randint(3, 10)):
|
||||
u = random.choice(possible_infantry_units)
|
||||
position = infantry_position.random_point_within(55, 5)
|
||||
self.m.vehicle_group(
|
||||
self.mission.vehicle_group(
|
||||
side,
|
||||
namegen.next_infantry_name(side, cp, u), u,
|
||||
position=position,
|
||||
@@ -144,19 +183,73 @@ class GroundConflictGenerator:
|
||||
return
|
||||
|
||||
for dcs_group, group in ally_groups:
|
||||
|
||||
if hasattr(group.units[0], 'eplrs'):
|
||||
if group.units[0].eplrs:
|
||||
dcs_group.points[0].tasks.append(EPLRS(dcs_group.id))
|
||||
|
||||
if group.role == CombatGroupRole.ARTILLERY:
|
||||
# Fire on any ennemy in range
|
||||
if self.game.settings.perf_artillery:
|
||||
target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups)
|
||||
if target is not None:
|
||||
dcs_group.points[0].tasks.append(FireAtPoint(target, len(group.units) * 10, 100))
|
||||
|
||||
if stance != CombatStance.RETREAT:
|
||||
hold_task = Hold()
|
||||
hold_task.number = 1
|
||||
dcs_group.add_trigger_action(hold_task)
|
||||
|
||||
# Artillery strike random start
|
||||
artillery_trigger = TriggerOnce(Event.NoEvent, "ArtilleryFireTask #" + str(dcs_group.id))
|
||||
artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45)* 60))
|
||||
|
||||
fire_task = FireAtPoint(target, len(group.units) * 10, 100)
|
||||
if stance != CombatStance.RETREAT:
|
||||
fire_task.number = 2
|
||||
else:
|
||||
fire_task.number = 1
|
||||
dcs_group.add_trigger_action(fire_task)
|
||||
artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
|
||||
self.mission.triggerrules.triggers.append(artillery_trigger)
|
||||
|
||||
# Artillery will fall back when under attack
|
||||
if stance != CombatStance.RETREAT:
|
||||
|
||||
# Hold position
|
||||
dcs_group.points[0].tasks.append(Hold())
|
||||
retreat = self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE/3))
|
||||
dcs_group.add_waypoint(dcs_group.position.point_from_heading(forward_heading, 1), PointAction.OffRoad)
|
||||
dcs_group.points[1].tasks.append(Hold())
|
||||
dcs_group.add_waypoint(retreat, PointAction.OffRoad)
|
||||
|
||||
artillery_fallback = TriggerOnce(Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id))
|
||||
for i, u in enumerate(dcs_group.units):
|
||||
artillery_fallback.add_condition(UnitDamaged(u.id))
|
||||
if i < len(dcs_group.units) - 1:
|
||||
artillery_fallback.add_condition(Or())
|
||||
|
||||
hold_2 = Hold()
|
||||
hold_2.number = 3
|
||||
dcs_group.add_trigger_action(hold_2)
|
||||
|
||||
retreat_task = GoToWaypoint(toIndex=3)
|
||||
retreat_task.number = 4
|
||||
dcs_group.add_trigger_action(retreat_task)
|
||||
|
||||
artillery_fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
|
||||
self.mission.triggerrules.triggers.append(artillery_fallback)
|
||||
|
||||
for u in dcs_group.units:
|
||||
u.initial = True
|
||||
u.heading = forward_heading + random.randint(-5,5)
|
||||
|
||||
elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]:
|
||||
if stance == CombatStance.AGGRESIVE:
|
||||
if stance == CombatStance.AGGRESSIVE:
|
||||
# Attack nearest enemy if any
|
||||
# Then move forward OR Attack enemy base if it is not too far away
|
||||
target = self.find_nearest_enemy_group(dcs_group, enemy_groups)
|
||||
if target is not None:
|
||||
rand_offset = Point(random.randint(-50, 50), random.randint(-50, 50))
|
||||
rand_offset = Point(random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK))
|
||||
dcs_group.add_waypoint(target.points[0].position + rand_offset, PointAction.OffRoad)
|
||||
dcs_group.points[1].tasks.append(AttackGroup(target.id))
|
||||
|
||||
@@ -179,16 +272,20 @@ class GroundConflictGenerator:
|
||||
targets = self.find_n_nearest_enemy_groups(dcs_group, enemy_groups, 3)
|
||||
i = 1
|
||||
for target in targets:
|
||||
rand_offset = Point(random.randint(-50, 50), random.randint(-50, 50))
|
||||
dcs_group.add_waypoint(target.points[0].position+rand_offset,PointAction.OffRoad)
|
||||
rand_offset = Point(random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK))
|
||||
dcs_group.add_waypoint(target.points[0].position+rand_offset, PointAction.OffRoad)
|
||||
dcs_group.points[i].tasks.append(AttackGroup(target.id))
|
||||
i = i + 1
|
||||
if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE:
|
||||
attack_point = to_cp.position.random_point_within(500, 0)
|
||||
dcs_group.add_waypoint(attack_point)
|
||||
|
||||
if stance != CombatStance.RETREAT:
|
||||
self.add_morale_trigger(dcs_group, forward_heading)
|
||||
|
||||
elif group.role in [CombatGroupRole.APC, CombatGroupRole.ATGM]:
|
||||
|
||||
if stance in [CombatStance.AGGRESIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]:
|
||||
if stance in [CombatStance.AGGRESSIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]:
|
||||
# APC & ATGM will never move too much forward, but will follow along any offensive
|
||||
if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE:
|
||||
attack_point = to_cp.position.random_point_within(500, 0)
|
||||
@@ -196,6 +293,9 @@ class GroundConflictGenerator:
|
||||
attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE)
|
||||
dcs_group.add_waypoint(attack_point, PointAction.OnRoad)
|
||||
|
||||
if stance != CombatStance.RETREAT:
|
||||
self.add_morale_trigger(dcs_group, forward_heading)
|
||||
|
||||
if stance == CombatStance.RETREAT:
|
||||
# In retreat mode, the units will fall back
|
||||
# If the ally base is close enough, the units will even regroup there
|
||||
@@ -208,14 +308,51 @@ class GroundConflictGenerator:
|
||||
dcs_group.add_waypoint(reposition_point, PointAction.OffRoad)
|
||||
|
||||
|
||||
def find_retreat_point(self, dcs_group, frontline_heading):
|
||||
def add_morale_trigger(self, dcs_group, forward_heading):
|
||||
"""
|
||||
This add a trigger to manage units fleeing whenever their group is hit hard, or being engaged by CAS
|
||||
"""
|
||||
|
||||
if len(dcs_group.units) == 1:
|
||||
return
|
||||
|
||||
# Units should hold position on last waypoint
|
||||
dcs_group.points[len(dcs_group.points) - 1].tasks.append(Hold())
|
||||
|
||||
# Force unit heading
|
||||
for unit in dcs_group.units:
|
||||
unit.heading = forward_heading
|
||||
dcs_group.manualHeading = True
|
||||
|
||||
# We add a new retreat waypoint
|
||||
dcs_group.add_waypoint(self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 8)), PointAction.OffRoad)
|
||||
|
||||
# Fallback task
|
||||
fallback = ControlledTask(GoToWaypoint(toIndex=len(dcs_group.points)))
|
||||
fallback.enabled = False
|
||||
dcs_group.add_trigger_action(Hold())
|
||||
dcs_group.add_trigger_action(fallback)
|
||||
|
||||
# Create trigger
|
||||
fallback = TriggerOnce(Event.NoEvent, "Morale manager #" + str(dcs_group.id))
|
||||
|
||||
# Usually more than 50% casualties = RETREAT
|
||||
fallback.add_condition(GroupLifeLess(dcs_group.id, random.randint(51, 76)))
|
||||
|
||||
# Do retreat to the configured retreat waypoint
|
||||
fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
|
||||
|
||||
self.mission.triggerrules.triggers.append(fallback)
|
||||
|
||||
|
||||
def find_retreat_point(self, dcs_group, frontline_heading, distance=RETREAT_DISTANCE):
|
||||
"""
|
||||
Find a point to retreat to
|
||||
:param dcs_group: DCS mission group we are searching a retreat point for
|
||||
:param frontline_heading: Heading of the frontline
|
||||
:return: dcs.mapping.Point object with the desired position
|
||||
"""
|
||||
return dcs_group.points[0].position.point_from_heading(frontline_heading-180, RETREAT_DISTANCE)
|
||||
return dcs_group.points[0].position.point_from_heading(frontline_heading-180, distance)
|
||||
|
||||
def find_offensive_point(self, dcs_group, frontline_heading, distance):
|
||||
"""
|
||||
@@ -310,7 +447,7 @@ class GroundConflictGenerator:
|
||||
cp = self.conflict.to_cp
|
||||
|
||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||
group = self.m.vehicle_group(
|
||||
group = self.mission.vehicle_group(
|
||||
side,
|
||||
namegen.next_unit_name(side, cp.id, unit), unit,
|
||||
position=self._group_point(at),
|
||||
|
||||
74
gen/beacons.py
Normal file
74
gen/beacons.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from dataclasses import dataclass
|
||||
from enum import auto, IntEnum
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Optional
|
||||
|
||||
from gen.radios import RadioFrequency
|
||||
from gen.tacan import TacanBand, TacanChannel
|
||||
|
||||
|
||||
BEACONS_RESOURCE_PATH = Path("resources/dcs/beacons")
|
||||
|
||||
|
||||
class BeaconType(IntEnum):
|
||||
BEACON_TYPE_NULL = auto()
|
||||
BEACON_TYPE_VOR = auto()
|
||||
BEACON_TYPE_DME = auto()
|
||||
BEACON_TYPE_VOR_DME = auto()
|
||||
BEACON_TYPE_TACAN = auto()
|
||||
BEACON_TYPE_VORTAC = auto()
|
||||
BEACON_TYPE_RSBN = auto()
|
||||
BEACON_TYPE_BROADCAST_STATION = auto()
|
||||
|
||||
BEACON_TYPE_HOMER = auto()
|
||||
BEACON_TYPE_AIRPORT_HOMER = auto()
|
||||
BEACON_TYPE_AIRPORT_HOMER_WITH_MARKER = auto()
|
||||
BEACON_TYPE_ILS_FAR_HOMER = auto()
|
||||
BEACON_TYPE_ILS_NEAR_HOMER = auto()
|
||||
|
||||
BEACON_TYPE_ILS_LOCALIZER = auto()
|
||||
BEACON_TYPE_ILS_GLIDESLOPE = auto()
|
||||
|
||||
BEACON_TYPE_PRMG_LOCALIZER = auto()
|
||||
BEACON_TYPE_PRMG_GLIDESLOPE = auto()
|
||||
|
||||
BEACON_TYPE_ICLS_LOCALIZER = auto()
|
||||
BEACON_TYPE_ICLS_GLIDESLOPE = auto()
|
||||
|
||||
BEACON_TYPE_NAUTICAL_HOMER = auto()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Beacon:
|
||||
name: str
|
||||
callsign: str
|
||||
beacon_type: BeaconType
|
||||
hertz: int
|
||||
channel: Optional[int]
|
||||
|
||||
@property
|
||||
def frequency(self) -> RadioFrequency:
|
||||
return RadioFrequency(self.hertz)
|
||||
|
||||
@property
|
||||
def is_tacan(self) -> bool:
|
||||
return self.beacon_type in (
|
||||
BeaconType.BEACON_TYPE_VORTAC,
|
||||
BeaconType.BEACON_TYPE_TACAN,
|
||||
)
|
||||
|
||||
@property
|
||||
def tacan_channel(self) -> TacanChannel:
|
||||
assert self.is_tacan
|
||||
assert self.channel is not None
|
||||
return TacanChannel(self.channel, TacanBand.X)
|
||||
|
||||
|
||||
def load_beacons_for_terrain(name: str) -> Iterable[Beacon]:
|
||||
beacons_file = BEACONS_RESOURCE_PATH / f"{name.lower()}.json"
|
||||
if not beacons_file.exists():
|
||||
raise RuntimeError(f"Beacon file {beacons_file.resolve()} is missing")
|
||||
|
||||
for beacon in json.loads(beacons_file.read_text()):
|
||||
yield Beacon(**beacon)
|
||||
@@ -1,86 +1,247 @@
|
||||
import logging
|
||||
import os
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
import random
|
||||
from typing import List
|
||||
|
||||
from game import db
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.mission import Mission
|
||||
from .aircraft import FlightData
|
||||
from .airfields import RunwayData
|
||||
from .airsupportgen import AwacsInfo, TankerInfo
|
||||
from .armor import JtacInfo
|
||||
from .conflictgen import Conflict
|
||||
from .ground_forces.combat_stance import CombatStance
|
||||
from .radios import RadioFrequency
|
||||
|
||||
|
||||
class BriefingGenerator:
|
||||
freqs = None # type: typing.List[typing.Tuple[str, str]]
|
||||
title = "" # type: str
|
||||
description = "" # type: str
|
||||
targets = None # type: typing.List[typing.Tuple[str, str]]
|
||||
waypoints = None # type: typing.List[str]
|
||||
@dataclass
|
||||
class CommInfo:
|
||||
"""Communications information for the kneeboard."""
|
||||
name: str
|
||||
freq: RadioFrequency
|
||||
|
||||
|
||||
class MissionInfoGenerator:
|
||||
"""Base type for generators of mission information for the player.
|
||||
|
||||
Examples of subtypes include briefing generators, kneeboard generators, etc.
|
||||
"""
|
||||
|
||||
def __init__(self, mission: Mission) -> None:
|
||||
self.mission = mission
|
||||
self.awacs: List[AwacsInfo] = []
|
||||
self.comms: List[CommInfo] = []
|
||||
self.flights: List[FlightData] = []
|
||||
self.jtacs: List[JtacInfo] = []
|
||||
self.tankers: List[TankerInfo] = []
|
||||
|
||||
def add_awacs(self, awacs: AwacsInfo) -> None:
|
||||
"""Adds an AWACS/GCI to the mission.
|
||||
|
||||
Args:
|
||||
awacs: AWACS information.
|
||||
"""
|
||||
self.awacs.append(awacs)
|
||||
|
||||
def add_comm(self, name: str, freq: RadioFrequency) -> None:
|
||||
"""Adds communications info to the mission.
|
||||
|
||||
Args:
|
||||
name: Name of the radio channel.
|
||||
freq: Frequency of the radio channel.
|
||||
"""
|
||||
self.comms.append(CommInfo(name, freq))
|
||||
|
||||
def add_flight(self, flight: FlightData) -> None:
|
||||
"""Adds flight info to the mission.
|
||||
|
||||
Args:
|
||||
flight: Flight information.
|
||||
"""
|
||||
self.flights.append(flight)
|
||||
|
||||
def add_jtac(self, jtac: JtacInfo) -> None:
|
||||
"""Adds a JTAC to the mission.
|
||||
|
||||
Args:
|
||||
jtac: JTAC information.
|
||||
"""
|
||||
self.jtacs.append(jtac)
|
||||
|
||||
def add_tanker(self, tanker: TankerInfo) -> None:
|
||||
"""Adds a tanker to the mission.
|
||||
|
||||
Args:
|
||||
tanker: Tanker information.
|
||||
"""
|
||||
self.tankers.append(tanker)
|
||||
|
||||
def generate(self) -> None:
|
||||
"""Generates the mission information."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class BriefingGenerator(MissionInfoGenerator):
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||
self.m = mission
|
||||
super().__init__(mission)
|
||||
self.conflict = conflict
|
||||
self.game = game
|
||||
self.title = ""
|
||||
self.description = ""
|
||||
self.dynamic_runways: List[RunwayData] = []
|
||||
|
||||
self.freqs = []
|
||||
self.targets = []
|
||||
self.waypoints = []
|
||||
def add_dynamic_runway(self, runway: RunwayData) -> None:
|
||||
"""Adds a dynamically generated runway to the briefing.
|
||||
|
||||
def append_frequency(self, name: str, frequency: str):
|
||||
self.freqs.append((name, frequency))
|
||||
Dynamic runways are any valid landing point that is a unit rather than a
|
||||
map feature. These include carriers, ships with a helipad, and FARPs.
|
||||
"""
|
||||
self.dynamic_runways.append(runway)
|
||||
|
||||
def append_target(self, description: str, markpoint: str = None):
|
||||
self.targets.append((description, markpoint))
|
||||
|
||||
def append_waypoint(self, description: str):
|
||||
self.waypoints.append(description)
|
||||
|
||||
def add_flight_description(self, flight):
|
||||
|
||||
if flight.client_count <= 0:
|
||||
return
|
||||
|
||||
flight_unit_name = db.unit_type_name(flight.unit_type)
|
||||
self.description += 2 * "\n" + "-" * 50 + "\n"
|
||||
self.description += flight_unit_name + " x " + str(flight.count) + 2 * "\n"
|
||||
|
||||
self.description += "#0 -- TAKEOFF : Take off\n"
|
||||
for i, wpt in enumerate(flight.points):
|
||||
self.description += "#" + str(1+i) + " -- " + wpt.name + " : " + wpt.description + "\n"
|
||||
self.description += "#" + str(len(flight.points) + 1) + " -- RTB\n"
|
||||
def add_flight_description(self, flight: FlightData):
|
||||
assert flight.client_units
|
||||
|
||||
aircraft = flight.aircraft_type
|
||||
flight_unit_name = db.unit_type_name(aircraft)
|
||||
self.description += "-" * 50 + "\n"
|
||||
self.description += f"{flight_unit_name} x {flight.size + 2}\n\n"
|
||||
|
||||
for i, wpt in enumerate(flight.waypoints):
|
||||
self.description += f"#{i + 1} -- {wpt.name} : {wpt.description}\n"
|
||||
self.description += f"#{len(flight.waypoints) + 1} -- RTB\n\n"
|
||||
|
||||
def add_ally_flight_description(self, flight: FlightData):
|
||||
assert not flight.client_units
|
||||
aircraft = flight.aircraft_type
|
||||
flight_unit_name = db.unit_type_name(aircraft)
|
||||
self.description += (
|
||||
f"{flight.flight_type.name} {flight_unit_name} x {flight.size}, "
|
||||
f"departing in {flight.departure_delay} minutes\n"
|
||||
)
|
||||
|
||||
def generate(self):
|
||||
|
||||
self.description = ""
|
||||
|
||||
self.description += "DCS Liberation turn #" + str(self.game.turn) + "\n"
|
||||
self.description += "-"*50 + "\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
for planner in self.game.planners.values():
|
||||
for flight in planner.flights:
|
||||
self.generate_ongoing_war_text()
|
||||
|
||||
self.description += "\n"*2
|
||||
self.description += "Your flights:" + "\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
for flight in self.flights:
|
||||
if flight.client_units:
|
||||
self.add_flight_description(flight)
|
||||
|
||||
if self.freqs:
|
||||
self.description += "\n"*2
|
||||
self.description += "Planned ally flights:" + "\n"
|
||||
self.description += "=" * 15 + "\n"
|
||||
allied_flights_by_departure = defaultdict(list)
|
||||
for flight in self.flights:
|
||||
if not flight.client_units and flight.friendly:
|
||||
name = flight.departure.airfield_name
|
||||
allied_flights_by_departure[name].append(flight)
|
||||
for departure, flights in allied_flights_by_departure.items():
|
||||
self.description += f"\nFrom {departure}\n"
|
||||
self.description += "-" * 50 + "\n\n"
|
||||
for flight in flights:
|
||||
self.add_ally_flight_description(flight)
|
||||
|
||||
if self.comms:
|
||||
self.description += "\n\nComms Frequencies:\n"
|
||||
self.description += "=" * 15 + "\n"
|
||||
for comm_info in self.comms:
|
||||
self.description += f"{comm_info.name}: {comm_info.freq}\n"
|
||||
self.description += ("-" * 50) + "\n"
|
||||
|
||||
for runway in self.dynamic_runways:
|
||||
self.description += f"{runway.airfield_name}\n"
|
||||
self.description += f"RADIO : {runway.atc}\n"
|
||||
if runway.tacan is not None:
|
||||
self.description += f"TACAN : {runway.tacan} {runway.tacan_callsign}\n"
|
||||
if runway.icls is not None:
|
||||
self.description += f"ICLS Channel : {runway.icls}\n"
|
||||
self.description += "-" * 50 + "\n"
|
||||
for name, freq in self.freqs:
|
||||
self.description += "\n{}: {}".format(name, freq)
|
||||
self.description += "\n" + ("-" * 50) + "\n"
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.captured and cp.cptype in [ControlPointType.LHA_GROUP, ControlPointType.AIRCRAFT_CARRIER_GROUP]:
|
||||
self.description += cp.name + " TACAN : "
|
||||
|
||||
self.description += str(cp.tacanN)
|
||||
if cp.tacanY:
|
||||
self.description += "Y"
|
||||
else:
|
||||
self.description += "X"
|
||||
self.description += " " + str(cp.tacanI) + "\n"
|
||||
|
||||
if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP and hasattr(cp, "icls"):
|
||||
self.description += "ICLS Channel : " + str(cp.icls) + "\n"
|
||||
self.description += "-" * 50 + "\n"
|
||||
|
||||
self.m.set_description_text(self.description)
|
||||
|
||||
|
||||
self.description += "JTACS [F-10 Menu] : \n"
|
||||
self.description += "===================\n\n"
|
||||
for jtac in self.jtacs:
|
||||
self.description += f"{jtac.region} -- Code : {jtac.code}\n"
|
||||
|
||||
self.mission.set_description_text(self.description)
|
||||
|
||||
self.mission.add_picture_blue(os.path.abspath(
|
||||
"./resources/ui/splash_screen.png"))
|
||||
|
||||
|
||||
def generate_ongoing_war_text(self):
|
||||
|
||||
self.description += "Current situation:\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
conflict_number = 0
|
||||
|
||||
for c in self.game.theater.conflicts():
|
||||
conflict_number = conflict_number + 1
|
||||
if c[0].captured:
|
||||
player_base = c[0]
|
||||
enemy_base = c[1]
|
||||
else:
|
||||
player_base = c[1]
|
||||
enemy_base = c[0]
|
||||
|
||||
has_numerical_superiority = player_base.base.total_armor > enemy_base.base.total_armor
|
||||
self.description += self.__random_frontline_sentence(player_base.name, enemy_base.name)
|
||||
|
||||
if enemy_base.id in player_base.stances.keys():
|
||||
stance = player_base.stances[enemy_base.id]
|
||||
|
||||
if player_base.base.total_armor == 0:
|
||||
self.description += "We do not have a single vehicle available to hold our position, the situation is critical, and we will lose ground inevitably.\n"
|
||||
elif enemy_base.base.total_armor == 0:
|
||||
self.description += "The enemy forces have been crushed, we will be able to make significant progress toward " + enemy_base.name + ". \n"
|
||||
if stance == CombatStance.AGGRESSIVE:
|
||||
if has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces will try to make progress against the enemy"
|
||||
self.description += ". As the enemy is outnumbered, our forces should have no issue making progress.\n"
|
||||
elif has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces will try an audacious assault against enemies in superior numbers. The operation is risky, and the enemy might counter attack.\n"
|
||||
elif stance == CombatStance.ELIMINATION:
|
||||
if has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces will focus on the destruction of enemy assets, before attempting to make progress toward " + enemy_base.name + ". "
|
||||
self.description += "The enemy is already outnumbered, and this maneuver might draw a final blow to their forces.\n"
|
||||
elif has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces will try an audacious assault against enemies in superior numbers. The operation is risky, and the enemy might counter attack.\n"
|
||||
elif stance == CombatStance.BREAKTHROUGH:
|
||||
if has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces will focus on progression toward " + enemy_base.name + ".\n"
|
||||
elif has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces have been ordered to rush toward " + enemy_base.name + ". Wish them luck... We are also expecting a counter attack.\n"
|
||||
elif stance in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]:
|
||||
if has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces will hold position. We are not expecting an enemy assault.\n"
|
||||
elif has_numerical_superiority:
|
||||
self.description += "On this location, our ground forces have been ordered to hold still, and defend against enemy attacks. An enemy assault might be iminent.\n"
|
||||
|
||||
if conflict_number == 0:
|
||||
self.description += "There are currently no fights on the ground.\n"
|
||||
|
||||
self.description += "\n\n"
|
||||
|
||||
|
||||
def __random_frontline_sentence(self, player_base_name, enemy_base_name):
|
||||
templates = [
|
||||
"There are combats between {} and {}. ",
|
||||
"The war on the ground is still going on between {} and {}. ",
|
||||
"Our ground forces in {} are opposed to enemy forces based in {}. ",
|
||||
"Our forces from {} are fighting enemies based in {}. ",
|
||||
"There is an active frontline between {} and {}. ",
|
||||
]
|
||||
return random.choice(templates).format(player_base_name, enemy_base_name)
|
||||
|
||||
|
||||
|
||||
34
gen/callsigns.py
Normal file
34
gen/callsigns.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""Support for working with DCS group callsigns."""
|
||||
import logging
|
||||
import re
|
||||
|
||||
from dcs.unitgroup import FlyingGroup
|
||||
from dcs.flyingunit import FlyingUnit
|
||||
|
||||
|
||||
def callsign_for_support_unit(group: FlyingGroup) -> str:
|
||||
# Either something like Overlord11 for Western AWACS, or else just a number.
|
||||
# Convert to either "Overlord" or "Flight 123".
|
||||
lead = group.units[0]
|
||||
raw_callsign = lead.callsign_as_str()
|
||||
try:
|
||||
return f"Flight {int(raw_callsign)}"
|
||||
except ValueError:
|
||||
return raw_callsign.rstrip("1234567890")
|
||||
|
||||
|
||||
def create_group_callsign_from_unit(lead: FlyingUnit) -> str:
|
||||
raw_callsign = lead.callsign_as_str()
|
||||
if not lead.callsign_is_western:
|
||||
# Callsigns for non-Western countries are just a number per flight,
|
||||
# similar to tail numbers.
|
||||
return f"Flight {raw_callsign}"
|
||||
|
||||
# Callsign from pydcs is in the format `<name><group ID><unit ID>`,
|
||||
# where unit ID is guaranteed to be a single digit but the group ID may
|
||||
# be more.
|
||||
match = re.search(r"^(\D+)(\d+)(\d)$", raw_callsign)
|
||||
if match is None:
|
||||
logging.error(f"Could not parse unit callsign: {raw_callsign}")
|
||||
return f"Flight {raw_callsign}"
|
||||
return f"{match.group(1)} {match.group(2)}"
|
||||
@@ -3,22 +3,38 @@ import random
|
||||
from dcs.vehicles import Armor
|
||||
|
||||
from game import db
|
||||
from gen.defenses.armored_group_generator import ArmoredGroupGenerator
|
||||
from gen.defenses.armored_group_generator import ArmoredGroupGenerator, FixedSizeArmorGroupGenerator
|
||||
|
||||
|
||||
def generate_armor_group(faction:str, game, ground_object):
|
||||
"""
|
||||
This generate a group of ground units
|
||||
:param parentCp: The parent control point
|
||||
:param ground_object: The ground object which will own the group
|
||||
:param country: Owner country
|
||||
:return: Generated group
|
||||
"""
|
||||
|
||||
possible_unit = [u for u in db.FACTIONS[faction]["units"] if u in Armor.__dict__.values()]
|
||||
if len(possible_unit) > 0:
|
||||
unit_type = random.choice(possible_unit)
|
||||
generator = ArmoredGroupGenerator(game, ground_object, unit_type)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
return generate_armor_group_of_type(game, ground_object, unit_type)
|
||||
return None
|
||||
|
||||
|
||||
def generate_armor_group_of_type(game, ground_object, unit_type):
|
||||
"""
|
||||
This generate a group of ground units of given type
|
||||
:return: Generated group
|
||||
"""
|
||||
generator = ArmoredGroupGenerator(game, ground_object, unit_type)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
|
||||
|
||||
def generate_armor_group_of_type_and_size(game, ground_object, unit_type, size: int):
|
||||
"""
|
||||
This generate a group of ground units of given type and size
|
||||
:return: Generated group
|
||||
"""
|
||||
generator = FixedSizeArmorGroupGenerator(game, ground_object, unit_type, size)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
|
||||
|
||||
@@ -25,3 +25,20 @@ class ArmoredGroupGenerator(GroupGenerator):
|
||||
self.position.y + spacing * j, self.heading)
|
||||
|
||||
|
||||
class FixedSizeArmorGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, unit_type, size):
|
||||
super(FixedSizeArmorGroupGenerator, self).__init__(game, ground_object)
|
||||
self.unit_type = unit_type
|
||||
self.size = size
|
||||
|
||||
def generate(self):
|
||||
spacing = random.randint(20, 70)
|
||||
|
||||
index = 0
|
||||
for i in range(self.size):
|
||||
index = index + 1
|
||||
self.add_unit(self.unit_type, "Armor#" + str(index),
|
||||
self.position.x + spacing * i,
|
||||
self.position.y, self.heading)
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ class EnviromentGenerator:
|
||||
if weather_type == 1:
|
||||
# thunderstorm
|
||||
self._generate_base_weather()
|
||||
self._generate_wind(random.randint(8, 12))
|
||||
self._generate_wind(random.randint(0, 8))
|
||||
|
||||
self.mission.weather.clouds_density = random.randint(9, 10)
|
||||
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Thunderstorm
|
||||
@@ -125,7 +125,7 @@ class EnviromentGenerator:
|
||||
self.mission.weather.clouds_density = random.randint(5, 8)
|
||||
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Rain
|
||||
|
||||
self._generate_wind(random.randint(4, 8))
|
||||
self._generate_wind(random.randint(0, 6))
|
||||
elif weather_type == 3:
|
||||
# clouds
|
||||
self._generate_base_weather()
|
||||
|
||||
@@ -23,11 +23,12 @@ class CarrierGroupGenerator(GroupGenerator):
|
||||
return
|
||||
|
||||
# Add destroyers escort
|
||||
dd_type = random.choice(self.faction["destroyer"])
|
||||
self.add_unit(dd_type, "DD1", self.position.x + 250, self.position.y + 450, self.heading)
|
||||
self.add_unit(dd_type, "DD2", self.position.x + 250, self.position.y - 450, self.heading)
|
||||
if "destroyer" in self.faction.keys():
|
||||
dd_type = random.choice(self.faction["destroyer"])
|
||||
self.add_unit(dd_type, "DD1", self.position.x + 2500, self.position.y + 4500, self.heading)
|
||||
self.add_unit(dd_type, "DD2", self.position.x + 2500, self.position.y - 4500, self.heading)
|
||||
|
||||
self.add_unit(dd_type, "DD3", self.position.x + 450, self.position.y + 850, self.heading)
|
||||
self.add_unit(dd_type, "DD4", self.position.x + 450, self.position.y - 850, self.heading)
|
||||
self.add_unit(dd_type, "DD3", self.position.x + 4500, self.position.y + 8500, self.heading)
|
||||
self.add_unit(dd_type, "DD4", self.position.x + 4500, self.position.y - 8500, self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
42
gen/fleet/cn_dd_group.py
Normal file
42
gen/fleet/cn_dd_group.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import random
|
||||
|
||||
from gen.fleet.dd_group import DDGroupGenerator
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
from dcs.ships import *
|
||||
|
||||
|
||||
class ChineseNavyGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(ChineseNavyGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
include_frigate = random.choice([True, True, False])
|
||||
include_dd = random.choice([True, False])
|
||||
|
||||
if include_dd:
|
||||
include_cc = random.choice([True, False])
|
||||
else:
|
||||
include_cc = False
|
||||
|
||||
if include_frigate:
|
||||
self.add_unit(Type_054A_Frigate, "FF1", self.position.x + 1200, self.position.y + 900, self.heading)
|
||||
self.add_unit(Type_054A_Frigate, "FF2", self.position.x + 1200, self.position.y - 900, self.heading)
|
||||
|
||||
if include_dd:
|
||||
dd_type = random.choice([Type_052C_Destroyer, Type_052B_Destroyer])
|
||||
self.add_unit(dd_type, "FF1", self.position.x + 2400, self.position.y + 900, self.heading)
|
||||
self.add_unit(dd_type, "FF2", self.position.x + 2400, self.position.y - 900, self.heading)
|
||||
|
||||
if include_cc:
|
||||
cc_type = random.choice([CGN_1144_2_Pyotr_Velikiy])
|
||||
self.add_unit(cc_type, "CC1", self.position.x, self.position.y, self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
|
||||
|
||||
class Type54GroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(Type54GroupGenerator, self).__init__(game, ground_object, faction, Type_054A_Frigate)
|
||||
27
gen/fleet/dd_group.py
Normal file
27
gen/fleet/dd_group.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import random
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
from dcs.ships import *
|
||||
|
||||
|
||||
class DDGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction, ddtype):
|
||||
super(DDGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
self.ddtype = ddtype
|
||||
|
||||
def generate(self):
|
||||
self.add_unit(self.ddtype, "DD1", self.position.x + 500, self.position.y + 900, self.heading)
|
||||
self.add_unit(self.ddtype, "DD2", self.position.x + 500, self.position.y - 900, self.heading)
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
|
||||
|
||||
class OliverHazardPerryGroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(OliverHazardPerryGroupGenerator, self).__init__(game, ground_object, faction, Oliver_Hazzard_Perry_class)
|
||||
|
||||
|
||||
class ArleighBurkeGroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(ArleighBurkeGroupGenerator, self).__init__(game, ground_object, faction, USS_Arleigh_Burke_IIa)
|
||||
@@ -19,7 +19,7 @@ class LHAGroupGenerator(GroupGenerator):
|
||||
# Add destroyers escort
|
||||
if "destroyer" in self.faction.keys():
|
||||
dd_type = random.choice(self.faction["destroyer"])
|
||||
self.add_unit(dd_type, "DD1", self.position.x + 250, self.position.y + 450, self.heading)
|
||||
self.add_unit(dd_type, "DD2", self.position.x + 250, self.position.y - 450, self.heading)
|
||||
self.add_unit(dd_type, "DD1", self.position.x + 1250, self.position.y + 1450, self.heading)
|
||||
self.add_unit(dd_type, "DD2", self.position.x + 1250, self.position.y - 1450, self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
59
gen/fleet/ru_dd_group.py
Normal file
59
gen/fleet/ru_dd_group.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import random
|
||||
|
||||
from gen.fleet.dd_group import DDGroupGenerator
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
from dcs.ships import *
|
||||
|
||||
|
||||
class RussianNavyGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(RussianNavyGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
include_frigate = random.choice([True, True, False])
|
||||
include_dd = random.choice([True, False])
|
||||
|
||||
if include_dd:
|
||||
include_cc = random.choice([True, False])
|
||||
else:
|
||||
include_cc = False
|
||||
|
||||
if include_frigate:
|
||||
frigate_type = random.choice([FFL_1124_4_Grisha, FSG_1241_1MP_Molniya])
|
||||
self.add_unit(frigate_type, "FF1", self.position.x + 1200, self.position.y + 900, self.heading)
|
||||
self.add_unit(frigate_type, "FF2", self.position.x + 1200, self.position.y - 900, self.heading)
|
||||
|
||||
if include_dd:
|
||||
dd_type = random.choice([FFG_11540_Neustrashimy, FF_1135M_Rezky])
|
||||
self.add_unit(dd_type, "FF1", self.position.x + 2400, self.position.y + 900, self.heading)
|
||||
self.add_unit(dd_type, "FF2", self.position.x + 2400, self.position.y - 900, self.heading)
|
||||
|
||||
if include_cc:
|
||||
cc_type = random.choice([CG_1164_Moskva, CGN_1144_2_Pyotr_Velikiy])
|
||||
self.add_unit(cc_type, "CC1", self.position.x, self.position.y, self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
|
||||
|
||||
class GrishaGroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(GrishaGroupGenerator, self).__init__(game, ground_object, faction, FFL_1124_4_Grisha)
|
||||
|
||||
|
||||
class MolniyaGroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(MolniyaGroupGenerator, self).__init__(game, ground_object, faction, FSG_1241_1MP_Molniya)
|
||||
|
||||
|
||||
class KiloSubGroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(KiloSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_877)
|
||||
|
||||
|
||||
class TangoSubGroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(TangoSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_641B)
|
||||
|
||||
19
gen/fleet/schnellboot.py
Normal file
19
gen/fleet/schnellboot.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import random
|
||||
|
||||
from dcs.ships import Schnellboot_type_S130
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
|
||||
class SchnellbootGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(SchnellbootGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
for i in range(random.randint(2, 4)):
|
||||
self.add_unit(Schnellboot_type_S130, "Schnellboot" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
@@ -1,6 +1,50 @@
|
||||
import logging
|
||||
import random
|
||||
|
||||
from game import db
|
||||
from gen.fleet.carrier_group import CarrierGroupGenerator
|
||||
from gen.fleet.cn_dd_group import ChineseNavyGroupGenerator, Type54GroupGenerator
|
||||
from gen.fleet.dd_group import ArleighBurkeGroupGenerator, OliverHazardPerryGroupGenerator
|
||||
from gen.fleet.lha_group import LHAGroupGenerator
|
||||
from gen.fleet.ru_dd_group import RussianNavyGroupGenerator, GrishaGroupGenerator, MolniyaGroupGenerator, \
|
||||
KiloSubGroupGenerator, TangoSubGroupGenerator
|
||||
from gen.fleet.schnellboot import SchnellbootGroupGenerator
|
||||
from gen.fleet.uboat import UBoatGroupGenerator
|
||||
from gen.fleet.ww2lst import WW2LSTGroupGenerator
|
||||
|
||||
SHIP_MAP = {
|
||||
"SchnellbootGroupGenerator": SchnellbootGroupGenerator,
|
||||
"WW2LSTGroupGenerator": WW2LSTGroupGenerator,
|
||||
"UBoatGroupGenerator": UBoatGroupGenerator,
|
||||
"OliverHazardPerryGroupGenerator": OliverHazardPerryGroupGenerator,
|
||||
"ArleighBurkeGroupGenerator": ArleighBurkeGroupGenerator,
|
||||
"RussianNavyGroupGenerator": RussianNavyGroupGenerator,
|
||||
"ChineseNavyGroupGenerator": ChineseNavyGroupGenerator,
|
||||
"GrishaGroupGenerator": GrishaGroupGenerator,
|
||||
"MolniyaGroupGenerator": MolniyaGroupGenerator,
|
||||
"KiloSubGroupGenerator": KiloSubGroupGenerator,
|
||||
"TangoSubGroupGenerator": TangoSubGroupGenerator,
|
||||
"Type54GroupGenerator": Type54GroupGenerator
|
||||
}
|
||||
|
||||
|
||||
def generate_ship_group(game, ground_object, faction:str):
|
||||
"""
|
||||
This generate a ship group
|
||||
:return: Nothing, but put the group reference inside the ground object
|
||||
"""
|
||||
faction = db.FACTIONS[faction]
|
||||
if "boat" in faction.keys():
|
||||
generators = faction["boat"]
|
||||
if len(generators) > 0:
|
||||
gen = random.choice(generators)
|
||||
if gen in SHIP_MAP.keys():
|
||||
generator = SHIP_MAP[gen](game, ground_object, faction)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
else:
|
||||
logging.info("Unable to generate ship group, generator : " + str(gen) + "does not exists")
|
||||
return None
|
||||
|
||||
|
||||
def generate_carrier_group(faction:str, game, ground_object):
|
||||
|
||||
19
gen/fleet/uboat.py
Normal file
19
gen/fleet/uboat.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import random
|
||||
|
||||
from dcs.ships import Uboat_VIIC_U_flak
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
|
||||
class UBoatGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(UBoatGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
for i in range(random.randint(1, 4)):
|
||||
self.add_unit(Uboat_VIIC_U_flak, "Uboat" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
22
gen/fleet/ww2lst.py
Normal file
22
gen/fleet/ww2lst.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import random
|
||||
|
||||
from dcs.ships import LS_Samuel_Chase, LST_Mk_II
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
|
||||
class WW2LSTGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(WW2LSTGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
# Add LS Samuel Chase
|
||||
self.add_unit(LS_Samuel_Chase, "SamuelChase", self.position.x, self.position.y, self.heading)
|
||||
|
||||
for i in range(1, random.randint(3, 4)):
|
||||
self.add_unit(LST_Mk_II, "LST" + str(i), self.position.x + i * random.randint(800, 1200), self.position.y, self.heading)
|
||||
|
||||
self.get_generated_group().points[0].speed = 20
|
||||
@@ -1,49 +1,18 @@
|
||||
import math
|
||||
import operator
|
||||
import typing
|
||||
import random
|
||||
|
||||
from game import db
|
||||
from game.data.doctrine import MODERN_DOCTRINE
|
||||
from game.data.radar_db import UNITS_WITH_RADAR
|
||||
from game.utils import meter_to_feet, nm_to_meter
|
||||
from gen import Conflict
|
||||
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE, STRIKE_CAPABLE
|
||||
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE, STRIKE_CAPABLE, \
|
||||
DRONES
|
||||
from gen.flights.flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||
|
||||
|
||||
def meter_to_feet(value_in_meter):
|
||||
return int(3.28084 * value_in_meter)
|
||||
|
||||
|
||||
def feet_to_meter(value_in_feet):
|
||||
return int(float(value_in_feet)/3.048)
|
||||
|
||||
|
||||
def meter_to_nm(value_in_meter):
|
||||
return int(float(value_in_meter)*0.000539957)
|
||||
|
||||
|
||||
def nm_to_meter(value_in_nm):
|
||||
return int(float(value_in_nm)*1852)
|
||||
|
||||
|
||||
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
|
||||
STRIKE_MAX_RANGE = 1500000
|
||||
SEAD_MAX_RANGE = 1500000
|
||||
|
||||
MAX_NUMBER_OF_INTERCEPTION_GROUP = 3
|
||||
MISSION_DURATION = 120 # in minutes
|
||||
CAP_EVERY_X_MINUTES = 20
|
||||
CAS_EVERY_X_MINUTES = 30
|
||||
SEAD_EVERY_X_MINUTES = 40
|
||||
STRIKE_EVERY_X_MINUTES = 40
|
||||
|
||||
INGRESS_EGRESS_DISTANCE = nm_to_meter(45)
|
||||
INGRESS_ALT = feet_to_meter(20000)
|
||||
EGRESS_ALT = feet_to_meter(20000)
|
||||
PATROL_ALT_RANGE = (feet_to_meter(15000), feet_to_meter(33000))
|
||||
NAV_ALT = 9144
|
||||
PATTERN_ALTITUDE = feet_to_meter(5000)
|
||||
|
||||
CAP_DEFAULT_ENGAGE_DISTANCE = nm_to_meter(40)
|
||||
MISSION_DURATION = 80
|
||||
|
||||
|
||||
class FlightPlanner:
|
||||
@@ -55,6 +24,17 @@ class FlightPlanner:
|
||||
self.game = game
|
||||
self.aircraft_inventory = {} # local copy of the airbase inventory
|
||||
|
||||
if from_cp.captured:
|
||||
self.faction = self.game.player_faction
|
||||
else:
|
||||
self.faction = self.game.enemy_faction
|
||||
|
||||
if "doctrine" in self.faction.keys():
|
||||
self.doctrine = self.faction["doctrine"]
|
||||
else:
|
||||
self.doctrine = MODERN_DOCTRINE
|
||||
|
||||
|
||||
def reset(self):
|
||||
"""
|
||||
Reset the planned flights and available units
|
||||
@@ -77,7 +57,7 @@ class FlightPlanner:
|
||||
self.compute_strike_targets()
|
||||
|
||||
# The priority is to assign air-superiority fighter or interceptor to interception roles, so they can scramble if there is an attacker
|
||||
#self.commision_interceptors()
|
||||
# self.commision_interceptors()
|
||||
|
||||
# Then some CAP patrol for the next 2 hours
|
||||
self.commision_cap()
|
||||
@@ -112,7 +92,7 @@ class FlightPlanner:
|
||||
"""
|
||||
|
||||
# At least try to generate one interceptor group
|
||||
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, MAX_NUMBER_OF_INTERCEPTION_GROUP), 1)
|
||||
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, self.doctrine["MAX_NUMBER_OF_INTERCEPTION_GROUP"]), 1)
|
||||
possible_interceptors = [k for k in self.aircraft_inventory.keys() if k in INTERCEPT_CAPABLE]
|
||||
|
||||
if len(possible_interceptors) <= 0:
|
||||
@@ -127,6 +107,7 @@ class FlightPlanner:
|
||||
break
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.INTERCEPTION)
|
||||
flight.scheduled_in = 1
|
||||
flight.points = []
|
||||
|
||||
self.interceptor_flights.append(flight)
|
||||
@@ -145,7 +126,7 @@ class FlightPlanner:
|
||||
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/CAP_EVERY_X_MINUTES)):
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["CAP_EVERY_X_MINUTES"])):
|
||||
|
||||
try:
|
||||
unit = random.choice([k for k, v in inventory.items() if v >= 2])
|
||||
@@ -153,67 +134,16 @@ class FlightPlanner:
|
||||
break
|
||||
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
ftype = FlightType.BARCAP if self.from_cp.is_carrier else FlightType.CAP
|
||||
flight = Flight(unit, 2, self.from_cp, ftype)
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.CAP)
|
||||
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
|
||||
flight.scheduled_in = offset + i*random.randint(self.doctrine["CAP_EVERY_X_MINUTES"] - 5, self.doctrine["CAP_EVERY_X_MINUTES"] + 5)
|
||||
|
||||
patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1])
|
||||
|
||||
# Choose a location for CAP patrols (Either behind frontline if there is one, or to protect ground objects)
|
||||
if len(self._get_cas_locations()) > 0:
|
||||
loc = random.choice(self._get_cas_locations())
|
||||
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, loc, self.game.theater)
|
||||
center = ingress.point_from_heading(heading, distance / 2)
|
||||
orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15)))
|
||||
radius = distance * 2
|
||||
orbit0p = orbit_center.point_from_heading(heading, radius)
|
||||
orbit1p = orbit_center.point_from_heading(heading + 180, radius)
|
||||
elif len(self.from_cp.ground_objects) > 0:
|
||||
loc = random.choice(self.from_cp.ground_objects)
|
||||
hdg = self.from_cp.position.heading_between_point(loc.position)
|
||||
radius = random.randint(nm_to_meter(5), nm_to_meter(10))
|
||||
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
|
||||
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
|
||||
enemy_cp = random.choice(self._get_cas_locations())
|
||||
self.generate_frontline_cap(flight, flight.from_cp, enemy_cp)
|
||||
else:
|
||||
loc = self.from_cp.position.point_from_heading(random.randint(0, 360), random.randint(nm_to_meter(5), nm_to_meter(40)))
|
||||
hdg = self.from_cp.position.heading_between_point(loc.position)
|
||||
radius = random.randint(nm_to_meter(40), nm_to_meter(120))
|
||||
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
|
||||
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
|
||||
|
||||
|
||||
# Create points
|
||||
ascend = self.generate_ascend_point(self.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
|
||||
orbit0.name = "ORBIT 0"
|
||||
orbit0.description = "Standby between this point and the next one"
|
||||
orbit0.pretty_name = "Orbit race-track start"
|
||||
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
|
||||
flight.points.append(orbit0)
|
||||
|
||||
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
|
||||
orbit1.name = "ORBIT 1"
|
||||
orbit1.description = "Standby between this point and the previous one"
|
||||
orbit1.pretty_name = "Orbit race-track end"
|
||||
orbit1.waypoint_type = FlightWaypointType.PATROL
|
||||
flight.points.append(orbit1)
|
||||
|
||||
orbit0.targets.append(self.from_cp)
|
||||
obj_added = []
|
||||
for ground_object in self.from_cp.ground_objects:
|
||||
if ground_object.obj_name not in obj_added and not ground_object.airbase_group:
|
||||
orbit0.targets.append(ground_object)
|
||||
obj_added.append(ground_object.obj_name)
|
||||
|
||||
descend = self.generate_descend_point(self.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||
flight.points.append(rtb)
|
||||
self.generate_barcap(flight, flight.from_cp)
|
||||
|
||||
self.cap_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@@ -234,7 +164,7 @@ class FlightPlanner:
|
||||
if len(cas_location) > 0:
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/CAS_EVERY_X_MINUTES)):
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["CAS_EVERY_X_MINUTES"])):
|
||||
|
||||
try:
|
||||
unit = random.choice([k for k, v in inventory.items() if v >= 2])
|
||||
@@ -243,47 +173,11 @@ class FlightPlanner:
|
||||
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.CAS)
|
||||
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(CAS_EVERY_X_MINUTES-5, CAS_EVERY_X_MINUTES+5)
|
||||
|
||||
flight.scheduled_in = offset + i * random.randint(self.doctrine["CAS_EVERY_X_MINUTES"] - 5, self.doctrine["CAS_EVERY_X_MINUTES"] + 5)
|
||||
location = random.choice(cas_location)
|
||||
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, location, self.game.theater)
|
||||
center = ingress.point_from_heading(heading, distance/2)
|
||||
egress = ingress.point_from_heading(heading, distance)
|
||||
|
||||
ascend = self.generate_ascend_point(self.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
|
||||
ingress_point.alt_type = "RADIO"
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.pretty_name = "INGRESS"
|
||||
ingress_point.description = "Ingress into CAS area"
|
||||
ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
center_point = FlightWaypoint(center.x, center.y, 1000)
|
||||
center_point.alt_type = "RADIO"
|
||||
center_point.description = "Provide CAS"
|
||||
center_point.name = "CAS"
|
||||
center_point.pretty_name = "CAS"
|
||||
center_point.waypoint_type = FlightWaypointType.CAS
|
||||
flight.points.append(center_point)
|
||||
|
||||
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
|
||||
egress_point.alt_type = "RADIO"
|
||||
egress_point.description = "Egress from CAS area"
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS"
|
||||
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(self.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||
flight.points.append(rtb)
|
||||
self.generate_cas(flight, flight.from_cp, location)
|
||||
|
||||
self.cas_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@@ -303,7 +197,7 @@ class FlightPlanner:
|
||||
if len(self.potential_sead_targets) > 0:
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/SEAD_EVERY_X_MINUTES)):
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["SEAD_EVERY_X_MINUTES"])):
|
||||
|
||||
if len(self.potential_sead_targets) <= 0:
|
||||
break
|
||||
@@ -317,51 +211,12 @@ class FlightPlanner:
|
||||
flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD]))
|
||||
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||
|
||||
ascend = self.generate_ascend_point(self.from_cp)
|
||||
flight.points.append(ascend)
|
||||
flight.scheduled_in = offset + i*random.randint(self.doctrine["SEAD_EVERY_X_MINUTES"] - 5, self.doctrine["SEAD_EVERY_X_MINUTES"] + 5)
|
||||
|
||||
location = self.potential_sead_targets[0][0]
|
||||
self.potential_sead_targets.pop(0)
|
||||
|
||||
heading = self.from_cp.position.heading_between_point(location.position)
|
||||
ingress_heading = heading - 180 + 25
|
||||
egress_heading = heading - 180 - 25
|
||||
|
||||
ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE)
|
||||
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
|
||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||
ingress_point.description = "INGRESS on " + location.obj_name
|
||||
ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
point = FlightWaypoint(location.position.x, location.position.y, 0)
|
||||
point.alt_type = "RADIO"
|
||||
if flight.flight_type == FlightType.DEAD:
|
||||
point.description = "SEAD on " + location.obj_name
|
||||
point.pretty_name = "SEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
else:
|
||||
point.description = "DEAD on " + location.obj_name
|
||||
point.pretty_name = "DEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
|
||||
ingress_point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
|
||||
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
|
||||
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
|
||||
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||
egress_point.description = "EGRESS from " + location.obj_name
|
||||
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(self.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||
flight.points.append(rtb)
|
||||
self.generate_sead(flight, location, [])
|
||||
|
||||
self.sead_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@@ -381,7 +236,7 @@ class FlightPlanner:
|
||||
if len(self.potential_strike_targets) > 0:
|
||||
|
||||
offset = random.randint(0,5)
|
||||
for i in range(int(MISSION_DURATION/STRIKE_EVERY_X_MINUTES)):
|
||||
for i in range(int(MISSION_DURATION/self.doctrine["STRIKE_EVERY_X_MINUTES"])):
|
||||
|
||||
if len(self.potential_strike_targets) <= 0:
|
||||
break
|
||||
@@ -391,81 +246,21 @@ class FlightPlanner:
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.STRIKE)
|
||||
if unit in DRONES:
|
||||
count = 1
|
||||
else:
|
||||
count = 2
|
||||
|
||||
inventory[unit] = inventory[unit] - count
|
||||
flight = Flight(unit, count, self.from_cp, FlightType.STRIKE)
|
||||
|
||||
flight.points = []
|
||||
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||
|
||||
ascend = self.generate_ascend_point(self.from_cp)
|
||||
flight.points.append(ascend)
|
||||
flight.scheduled_in = offset + i*random.randint(self.doctrine["STRIKE_EVERY_X_MINUTES"] - 5, self.doctrine["STRIKE_EVERY_X_MINUTES"] + 5)
|
||||
|
||||
location = self.potential_strike_targets[0][0]
|
||||
self.potential_strike_targets.pop(0)
|
||||
|
||||
heading = self.from_cp.position.heading_between_point(location.position)
|
||||
ingress_heading = heading - 180 + 25
|
||||
egress_heading = heading - 180 - 25
|
||||
|
||||
ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE)
|
||||
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
|
||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||
ingress_point.description = "INGRESS on " + location.obj_name
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
if len(location.groups) > 0 and location.dcs_identifier == "AA":
|
||||
for g in location.groups:
|
||||
for j, u in enumerate(g.units):
|
||||
point = FlightWaypoint(u.position.x, u.position.y, 0)
|
||||
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
point.name = location.obj_name + "#" + str(j)
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
else:
|
||||
if hasattr(location, "obj_name"):
|
||||
buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name)
|
||||
print(buildings)
|
||||
for building in buildings:
|
||||
print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier))
|
||||
if building.is_dead:
|
||||
continue
|
||||
|
||||
point = FlightWaypoint(building.position.x, building.position.y, 0)
|
||||
point.description = "STRIKE on " + building.obj_name + " " + str(building.category)
|
||||
point.pretty_name = "STRIKE on " + building.obj_name + " " + str(building.category)
|
||||
point.name = building.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(building)
|
||||
flight.points.append(point)
|
||||
else:
|
||||
point = FlightWaypoint(location.position.x, location.position.y, 0)
|
||||
point.description = "STRIKE on " + location.obj_name + " " + str(location.category)
|
||||
point.pretty_name = "STRIKE on " + location.obj_name + " " + str(location.category)
|
||||
point.name = location.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
|
||||
|
||||
|
||||
|
||||
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
|
||||
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||
egress_point.description = "EGRESS from " + location.obj_name
|
||||
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(self.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||
flight.points.append(rtb)
|
||||
self.generate_strike(flight, location)
|
||||
|
||||
self.strike_flights.append(flight)
|
||||
self.flights.append(flight)
|
||||
@@ -475,9 +270,12 @@ class FlightPlanner:
|
||||
self.aircraft_inventory[k] = v
|
||||
|
||||
def _get_cas_locations(self):
|
||||
return self._get_cas_locations_for_cp(self.from_cp)
|
||||
|
||||
def _get_cas_locations_for_cp(self, for_cp):
|
||||
cas_locations = []
|
||||
for cp in self.from_cp.connected_points:
|
||||
if cp.captured != self.from_cp.captured:
|
||||
for cp in for_cp.connected_points:
|
||||
if cp.captured != for_cp.captured:
|
||||
cas_locations.append(cp)
|
||||
return cas_locations
|
||||
|
||||
@@ -495,7 +293,7 @@ class FlightPlanner:
|
||||
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
if distance > 2*STRIKE_MAX_RANGE:
|
||||
if distance > 2*self.doctrine["STRIKE_MAX_RANGE"]:
|
||||
# Then it's unlikely any child ground object is in range
|
||||
return
|
||||
|
||||
@@ -507,7 +305,7 @@ class FlightPlanner:
|
||||
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
if distance < SEAD_MAX_RANGE:
|
||||
if distance < self.doctrine["SEAD_MAX_RANGE"]:
|
||||
self.potential_strike_targets.append((g, distance))
|
||||
added_group.append(g)
|
||||
|
||||
@@ -528,7 +326,7 @@ class FlightPlanner:
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
# Then it's unlikely any ground object is range
|
||||
if distance > 2*SEAD_MAX_RANGE:
|
||||
if distance > 2*self.doctrine["SEAD_MAX_RANGE"]:
|
||||
return
|
||||
|
||||
for g in cp.ground_objects:
|
||||
@@ -536,8 +334,7 @@ class FlightPlanner:
|
||||
if g.dcs_identifier == "AA":
|
||||
|
||||
# Check that there is at least one unit with a radar in the ground objects unit groups
|
||||
number_of_units = sum([len([r for r in group.units if hasattr(db.unit_type_from_name(r.type), "detection_range")
|
||||
and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups])
|
||||
number_of_units = sum([len([r for r in group.units if db.unit_type_from_name(r.type) in UNITS_WITH_RADAR]) for group in g.groups])
|
||||
if number_of_units <= 0:
|
||||
continue
|
||||
|
||||
@@ -545,7 +342,7 @@ class FlightPlanner:
|
||||
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
||||
cp.position.y - self.from_cp.position.y)
|
||||
|
||||
if distance < SEAD_MAX_RANGE:
|
||||
if distance < self.doctrine["SEAD_MAX_RANGE"]:
|
||||
self.potential_sead_targets.append((g, distance))
|
||||
|
||||
self.potential_sead_targets.sort(key=operator.itemgetter(1))
|
||||
@@ -563,35 +360,430 @@ class FlightPlanner:
|
||||
del base_aircraft_inventory[f.unit_type]
|
||||
return base_aircraft_inventory
|
||||
|
||||
|
||||
def generate_strike(self, flight, location):
|
||||
|
||||
flight.flight_type = FlightType.STRIKE
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
heading = flight.from_cp.position.heading_between_point(location.position)
|
||||
ingress_heading = heading - 180 + 25
|
||||
egress_heading = heading - 180 - 25
|
||||
|
||||
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
ingress_point = FlightWaypoint(
|
||||
FlightWaypointType.INGRESS_STRIKE,
|
||||
ingress_pos.x,
|
||||
ingress_pos.y,
|
||||
self.doctrine["INGRESS_ALT"]
|
||||
)
|
||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||
ingress_point.description = "INGRESS on " + location.obj_name
|
||||
ingress_point.name = "INGRESS"
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
if len(location.groups) > 0 and location.dcs_identifier == "AA":
|
||||
for g in location.groups:
|
||||
for j, u in enumerate(g.units):
|
||||
point = FlightWaypoint(
|
||||
FlightWaypointType.TARGET_POINT,
|
||||
u.position.x,
|
||||
u.position.y,
|
||||
0
|
||||
)
|
||||
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||
point.name = location.obj_name + "#" + str(j)
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
else:
|
||||
if hasattr(location, "obj_name"):
|
||||
buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name)
|
||||
print(buildings)
|
||||
for building in buildings:
|
||||
print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier))
|
||||
if building.is_dead:
|
||||
continue
|
||||
|
||||
point = FlightWaypoint(
|
||||
FlightWaypointType.TARGET_POINT,
|
||||
building.position.x,
|
||||
building.position.y,
|
||||
0
|
||||
)
|
||||
point.description = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
|
||||
point.pretty_name = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
|
||||
point.name = building.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(building)
|
||||
flight.points.append(point)
|
||||
else:
|
||||
point = FlightWaypoint(
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
location.position.x,
|
||||
location.position.y,
|
||||
0
|
||||
)
|
||||
point.description = "STRIKE on " + location.obj_name
|
||||
point.pretty_name = "STRIKE on " + location.obj_name
|
||||
point.name = location.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
flight.points.append(point)
|
||||
|
||||
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
egress_point = FlightWaypoint(
|
||||
FlightWaypointType.EGRESS,
|
||||
egress_pos.x,
|
||||
egress_pos.y,
|
||||
self.doctrine["EGRESS_ALT"]
|
||||
)
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||
egress_point.description = "EGRESS from " + location.obj_name
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
def generate_barcap(self, flight, for_cp):
|
||||
"""
|
||||
Generate a barcap flight at a given location
|
||||
:param flight: Flight to setup
|
||||
:param for_cp: CP to protect
|
||||
"""
|
||||
flight.flight_type = FlightType.BARCAP if for_cp.is_carrier else FlightType.CAP
|
||||
patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1])
|
||||
|
||||
if len(for_cp.ground_objects) > 0:
|
||||
loc = random.choice(for_cp.ground_objects)
|
||||
hdg = for_cp.position.heading_between_point(loc.position)
|
||||
radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1])
|
||||
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
|
||||
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
|
||||
else:
|
||||
loc = for_cp.position.point_from_heading(random.randint(0, 360), random.randint(self.doctrine["CAP_DISTANCE_FROM_CP"][0], self.doctrine["CAP_DISTANCE_FROM_CP"][1]))
|
||||
hdg = for_cp.position.heading_between_point(loc)
|
||||
radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1])
|
||||
orbit0p = loc.point_from_heading(hdg - 90, radius)
|
||||
orbit1p = loc.point_from_heading(hdg + 90, radius)
|
||||
|
||||
# Create points
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
orbit0 = FlightWaypoint(
|
||||
FlightWaypointType.PATROL_TRACK,
|
||||
orbit0p.x,
|
||||
orbit0p.y,
|
||||
patrol_alt
|
||||
)
|
||||
orbit0.name = "ORBIT 0"
|
||||
orbit0.description = "Standby between this point and the next one"
|
||||
orbit0.pretty_name = "Race-track start"
|
||||
flight.points.append(orbit0)
|
||||
|
||||
orbit1 = FlightWaypoint(
|
||||
FlightWaypointType.PATROL,
|
||||
orbit1p.x,
|
||||
orbit1p.y,
|
||||
patrol_alt
|
||||
)
|
||||
orbit1.name = "ORBIT 1"
|
||||
orbit1.description = "Standby between this point and the previous one"
|
||||
orbit1.pretty_name = "Race-track end"
|
||||
flight.points.append(orbit1)
|
||||
|
||||
orbit0.targets.append(for_cp)
|
||||
obj_added = []
|
||||
for ground_object in for_cp.ground_objects:
|
||||
if ground_object.obj_name not in obj_added and not ground_object.airbase_group:
|
||||
orbit0.targets.append(ground_object)
|
||||
obj_added.append(ground_object.obj_name)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
|
||||
def generate_frontline_cap(self, flight, ally_cp, enemy_cp):
|
||||
"""
|
||||
Generate a cap flight for the frontline between ally_cp and enemy cp in order to ensure air superiority and
|
||||
protect friendly CAP airbase
|
||||
:param flight: Flight to setup
|
||||
:param ally_cp: CP to protect
|
||||
:param enemy_cp: Enemy connected cp
|
||||
"""
|
||||
flight.flight_type = FlightType.CAP
|
||||
patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1])
|
||||
|
||||
# Find targets waypoints
|
||||
ingress, heading, distance = Conflict.frontline_vector(ally_cp, enemy_cp, self.game.theater)
|
||||
center = ingress.point_from_heading(heading, distance / 2)
|
||||
orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15)))
|
||||
|
||||
combat_width = distance / 2
|
||||
if combat_width > 500000:
|
||||
combat_width = 500000
|
||||
if combat_width < 35000:
|
||||
combat_width = 35000
|
||||
|
||||
radius = combat_width*1.25
|
||||
orbit0p = orbit_center.point_from_heading(heading, radius)
|
||||
orbit1p = orbit_center.point_from_heading(heading + 180, radius)
|
||||
|
||||
# Create points
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
orbit0 = FlightWaypoint(
|
||||
FlightWaypointType.PATROL_TRACK,
|
||||
orbit0p.x,
|
||||
orbit0p.y,
|
||||
patrol_alt
|
||||
)
|
||||
orbit0.name = "ORBIT 0"
|
||||
orbit0.description = "Standby between this point and the next one"
|
||||
orbit0.pretty_name = "Race-track start"
|
||||
flight.points.append(orbit0)
|
||||
|
||||
orbit1 = FlightWaypoint(
|
||||
FlightWaypointType.PATROL,
|
||||
orbit1p.x,
|
||||
orbit1p.y,
|
||||
patrol_alt
|
||||
)
|
||||
orbit1.name = "ORBIT 1"
|
||||
orbit1.description = "Standby between this point and the previous one"
|
||||
orbit1.pretty_name = "Race-track end"
|
||||
flight.points.append(orbit1)
|
||||
|
||||
# Note : Targets of a PATROL TRACK waypoints are the points to be defended
|
||||
orbit0.targets.append(flight.from_cp)
|
||||
orbit0.targets.append(center)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
|
||||
def generate_sead(self, flight, location, custom_targets = []):
|
||||
"""
|
||||
Generate a sead flight at a given location
|
||||
:param flight: Flight to setup
|
||||
:param location: Location of the SEAD target
|
||||
:param custom_targets: Custom targets if any
|
||||
"""
|
||||
flight.points = []
|
||||
flight.flight_type = random.choice([FlightType.SEAD, FlightType.DEAD])
|
||||
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
flight.points.append(ascend)
|
||||
|
||||
heading = flight.from_cp.position.heading_between_point(location.position)
|
||||
ingress_heading = heading - 180 + 25
|
||||
egress_heading = heading - 180 - 25
|
||||
|
||||
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
ingress_point = FlightWaypoint(
|
||||
FlightWaypointType.INGRESS_SEAD,
|
||||
ingress_pos.x,
|
||||
ingress_pos.y,
|
||||
self.doctrine["INGRESS_ALT"]
|
||||
)
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||
ingress_point.description = "INGRESS on " + location.obj_name
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
if len(custom_targets) > 0:
|
||||
for target in custom_targets:
|
||||
point = FlightWaypoint(
|
||||
FlightWaypointType.TARGET_POINT,
|
||||
target.position.x,
|
||||
target.position.y,
|
||||
0
|
||||
)
|
||||
point.alt_type = "RADIO"
|
||||
if flight.flight_type == FlightType.DEAD:
|
||||
point.description = "DEAD on " + target.type
|
||||
point.pretty_name = "DEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
else:
|
||||
point.description = "SEAD on " + location.obj_name
|
||||
point.pretty_name = "SEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
flight.points.append(point)
|
||||
ingress_point.targets.append(location)
|
||||
ingress_point.targetGroup = location
|
||||
else:
|
||||
point = FlightWaypoint(
|
||||
FlightWaypointType.TARGET_GROUP_LOC,
|
||||
location.position.x,
|
||||
location.position.y,
|
||||
0
|
||||
)
|
||||
point.alt_type = "RADIO"
|
||||
if flight.flight_type == FlightType.DEAD:
|
||||
point.description = "DEAD on " + location.obj_name
|
||||
point.pretty_name = "DEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
else:
|
||||
point.description = "SEAD on " + location.obj_name
|
||||
point.pretty_name = "SEAD on " + location.obj_name
|
||||
point.only_for_player = True
|
||||
ingress_point.targets.append(location)
|
||||
ingress_point.targetGroup = location
|
||||
flight.points.append(point)
|
||||
|
||||
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
|
||||
egress_point = FlightWaypoint(
|
||||
FlightWaypointType.EGRESS,
|
||||
egress_pos.x,
|
||||
egress_pos.y,
|
||||
self.doctrine["EGRESS_ALT"]
|
||||
)
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||
egress_point.description = "EGRESS from " + location.obj_name
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
|
||||
def generate_cas(self, flight, from_cp, location):
|
||||
"""
|
||||
Generate a CAS flight at a given location
|
||||
:param flight: Flight to setup
|
||||
:param location: Location of the CAS targets
|
||||
"""
|
||||
is_helo = hasattr(flight.unit_type, "helicopter") and flight.unit_type.helicopter
|
||||
cap_alt = 1000
|
||||
flight.points = []
|
||||
flight.flight_type = FlightType.CAS
|
||||
|
||||
ingress, heading, distance = Conflict.frontline_vector(from_cp, location, self.game.theater)
|
||||
center = ingress.point_from_heading(heading, distance / 2)
|
||||
egress = ingress.point_from_heading(heading, distance)
|
||||
|
||||
ascend = self.generate_ascend_point(flight.from_cp)
|
||||
if is_helo:
|
||||
cap_alt = 500
|
||||
ascend.alt = 500
|
||||
flight.points.append(ascend)
|
||||
|
||||
ingress_point = FlightWaypoint(
|
||||
FlightWaypointType.INGRESS_CAS,
|
||||
ingress.x,
|
||||
ingress.y,
|
||||
cap_alt
|
||||
)
|
||||
ingress_point.alt_type = "RADIO"
|
||||
ingress_point.name = "INGRESS"
|
||||
ingress_point.pretty_name = "INGRESS"
|
||||
ingress_point.description = "Ingress into CAS area"
|
||||
flight.points.append(ingress_point)
|
||||
|
||||
center_point = FlightWaypoint(
|
||||
FlightWaypointType.CAS,
|
||||
center.x,
|
||||
center.y,
|
||||
cap_alt
|
||||
)
|
||||
center_point.alt_type = "RADIO"
|
||||
center_point.description = "Provide CAS"
|
||||
center_point.name = "CAS"
|
||||
center_point.pretty_name = "CAS"
|
||||
flight.points.append(center_point)
|
||||
|
||||
egress_point = FlightWaypoint(
|
||||
FlightWaypointType.EGRESS,
|
||||
egress.x,
|
||||
egress.y,
|
||||
cap_alt
|
||||
)
|
||||
egress_point.alt_type = "RADIO"
|
||||
egress_point.description = "Egress from CAS area"
|
||||
egress_point.name = "EGRESS"
|
||||
egress_point.pretty_name = "EGRESS"
|
||||
flight.points.append(egress_point)
|
||||
|
||||
descend = self.generate_descend_point(flight.from_cp)
|
||||
if is_helo:
|
||||
descend.alt = 300
|
||||
flight.points.append(descend)
|
||||
|
||||
rtb = self.generate_rtb_waypoint(flight.from_cp)
|
||||
flight.points.append(rtb)
|
||||
|
||||
def generate_ascend_point(self, from_cp):
|
||||
"""
|
||||
Generate ascend point
|
||||
:param from_cp: Airport you're taking off from
|
||||
:return:
|
||||
"""
|
||||
ascend_heading = from_cp.heading
|
||||
pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000)
|
||||
ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, PATTERN_ALTITUDE)
|
||||
ascend = FlightWaypoint(
|
||||
FlightWaypointType.ASCEND_POINT,
|
||||
pos_ascend.x,
|
||||
pos_ascend.y,
|
||||
self.doctrine["PATTERN_ALTITUDE"]
|
||||
)
|
||||
ascend.name = "ASCEND"
|
||||
ascend.alt_type = "RADIO"
|
||||
ascend.description = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], then proceed to next waypoint"
|
||||
ascend.pretty_name = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]"
|
||||
ascend.waypoint_type = FlightWaypointType.ASCEND_POINT
|
||||
ascend.description = "Ascend"
|
||||
ascend.pretty_name = "Ascend"
|
||||
return ascend
|
||||
|
||||
def generate_descend_point(self, from_cp):
|
||||
"""
|
||||
Generate approach/descend point
|
||||
:param from_cp: Airport you're landing at
|
||||
:return:
|
||||
"""
|
||||
ascend_heading = from_cp.heading
|
||||
descend = from_cp.position.point_from_heading(ascend_heading - 180, 30000)
|
||||
descend = FlightWaypoint(descend.x, descend.y, PATTERN_ALTITUDE)
|
||||
descend = from_cp.position.point_from_heading(ascend_heading - 180, 10000)
|
||||
descend = FlightWaypoint(
|
||||
FlightWaypointType.DESCENT_POINT,
|
||||
descend.x,
|
||||
descend.y,
|
||||
self.doctrine["PATTERN_ALTITUDE"]
|
||||
)
|
||||
descend.name = "DESCEND"
|
||||
descend.alt_type = "RADIO"
|
||||
descend.description = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], contact tower, and land"
|
||||
descend.pretty_name = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]"
|
||||
descend.waypoint_type = FlightWaypointType.DESCENT_POINT
|
||||
descend.description = "Descend to pattern alt"
|
||||
descend.pretty_name = "Descend to pattern alt"
|
||||
return descend
|
||||
|
||||
def generate_rtb_waypoint(self, from_cp):
|
||||
"""
|
||||
Generate RTB landing point
|
||||
:param from_cp: Airport you're landing at
|
||||
:return:
|
||||
"""
|
||||
rtb = from_cp.position
|
||||
rtb = FlightWaypoint(rtb.x, rtb.y, 0)
|
||||
rtb = FlightWaypoint(
|
||||
FlightWaypointType.LANDING_POINT,
|
||||
rtb.x,
|
||||
rtb.y,
|
||||
0
|
||||
)
|
||||
rtb.name = "LANDING"
|
||||
rtb.alt_type = "RADIO"
|
||||
rtb.description = "RTB"
|
||||
rtb.pretty_name = "RTB"
|
||||
rtb.waypoint_type = FlightWaypointType.LANDING_POINT
|
||||
return rtb
|
||||
|
||||
|
||||
@@ -3,13 +3,22 @@ from dcs.helicopters import *
|
||||
|
||||
# Interceptor are the aircraft prioritized for interception tasks
|
||||
# If none is available, the AI will use regular CAP-capable aircraft instead
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from pydcs_extensions.mb339.mb339 import MB_339PAN
|
||||
from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M
|
||||
|
||||
INTERCEPT_CAPABLE = [
|
||||
MiG_21Bis,
|
||||
MiG_25PD,
|
||||
MiG_31,
|
||||
MiG_29S,
|
||||
MiG_29A,
|
||||
MiG_29G,
|
||||
MiG_29K,
|
||||
|
||||
M_2000C,
|
||||
Mirage_2000_5,
|
||||
Rafale_M,
|
||||
|
||||
F_14B,
|
||||
F_15C,
|
||||
@@ -18,13 +27,16 @@ INTERCEPT_CAPABLE = [
|
||||
|
||||
# Used for CAP, Escort, and intercept if there is not a specialised aircraft available
|
||||
CAP_CAPABLE = [
|
||||
|
||||
MiG_15bis,
|
||||
MiG_19P,
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
MiG_25PD,
|
||||
MiG_29A,
|
||||
MiG_29G,
|
||||
MiG_29S,
|
||||
MiG_31,
|
||||
|
||||
Su_27,
|
||||
J_11A,
|
||||
@@ -40,6 +52,8 @@ CAP_CAPABLE = [
|
||||
F_5E_3,
|
||||
F_14B,
|
||||
F_15C,
|
||||
F_15E,
|
||||
F_16A,
|
||||
F_16C_50,
|
||||
FA_18C_hornet,
|
||||
|
||||
@@ -49,6 +63,8 @@ CAP_CAPABLE = [
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
P_47D_30bl1,
|
||||
P_47D_40,
|
||||
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
@@ -56,9 +72,12 @@ CAP_CAPABLE = [
|
||||
Bf_109K_4,
|
||||
FW_190D9,
|
||||
FW_190A8,
|
||||
|
||||
A_4E_C,
|
||||
Rafale_M,
|
||||
]
|
||||
|
||||
# USed for CAS (Close air support) and BAI (Battlefield Interdiction)
|
||||
# Used for CAS (Close air support) and BAI (Battlefield Interdiction)
|
||||
CAS_CAPABLE = [
|
||||
|
||||
MiG_15bis,
|
||||
@@ -80,23 +99,32 @@ CAS_CAPABLE = [
|
||||
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
AV8BNA,
|
||||
|
||||
F_86F_Sabre,
|
||||
F_5E_3,
|
||||
F_14B,
|
||||
F_15E,
|
||||
F_16A,
|
||||
F_16C_50,
|
||||
FA_18C_hornet,
|
||||
|
||||
Tornado_IDS,
|
||||
Tornado_GR4,
|
||||
|
||||
C_101CC,
|
||||
MB_339PAN,
|
||||
L_39ZA,
|
||||
AJS37,
|
||||
|
||||
SA342M,
|
||||
SA342L,
|
||||
OH_58D,
|
||||
|
||||
AH_64A,
|
||||
AH_64D,
|
||||
AH_1W,
|
||||
|
||||
UH_1H,
|
||||
|
||||
@@ -108,6 +136,8 @@ CAS_CAPABLE = [
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
P_47D_30bl1,
|
||||
P_47D_40,
|
||||
A_20G,
|
||||
|
||||
SpitfireLFMkIXCW,
|
||||
@@ -116,12 +146,20 @@ CAS_CAPABLE = [
|
||||
Bf_109K_4,
|
||||
FW_190D9,
|
||||
FW_190A8,
|
||||
|
||||
A_4E_C,
|
||||
Rafale_A_S,
|
||||
|
||||
WingLoong_I,
|
||||
MQ_9_Reaper,
|
||||
RQ_1A_Predator
|
||||
]
|
||||
|
||||
# Aircraft used for SEAD / DEAD tasks
|
||||
SEAD_CAPABLE = [
|
||||
F_4E,
|
||||
FA_18C_hornet,
|
||||
F_15E,
|
||||
# F_16C_50, Not yet
|
||||
AV8BNA,
|
||||
JF_17,
|
||||
@@ -133,14 +171,19 @@ SEAD_CAPABLE = [
|
||||
Su_30,
|
||||
Su_34,
|
||||
MiG_27K,
|
||||
|
||||
Tornado_IDS,
|
||||
Tornado_GR4,
|
||||
|
||||
A_4E_C,
|
||||
Rafale_A_S
|
||||
]
|
||||
|
||||
# Aircraft used for Strike mission
|
||||
STRIKE_CAPABLE = [
|
||||
MiG_15bis,
|
||||
MiG_29A,
|
||||
MiG_27K,
|
||||
MiG_29S,
|
||||
MB_339PAN,
|
||||
|
||||
Su_17M4,
|
||||
Su_24M,
|
||||
@@ -155,14 +198,20 @@ STRIKE_CAPABLE = [
|
||||
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
AV8BNA,
|
||||
|
||||
F_86F_Sabre,
|
||||
F_5E_3,
|
||||
F_14B,
|
||||
F_15E,
|
||||
F_16A,
|
||||
F_16C_50,
|
||||
FA_18C_hornet,
|
||||
|
||||
Tornado_IDS,
|
||||
Tornado_GR4,
|
||||
|
||||
C_101CC,
|
||||
L_39ZA,
|
||||
AJS37,
|
||||
@@ -170,7 +219,10 @@ STRIKE_CAPABLE = [
|
||||
P_51D_30_NA,
|
||||
P_51D,
|
||||
P_47D_30,
|
||||
P_47D_30bl1,
|
||||
P_47D_40,
|
||||
A_20G,
|
||||
B_17G,
|
||||
|
||||
SpitfireLFMkIXCW,
|
||||
SpitfireLFMkIX,
|
||||
@@ -179,17 +231,33 @@ STRIKE_CAPABLE = [
|
||||
FW_190D9,
|
||||
FW_190A8,
|
||||
|
||||
A_4E_C,
|
||||
Rafale_A_S
|
||||
|
||||
]
|
||||
|
||||
ANTISHIP_CAPABLE = [
|
||||
Su_24M,
|
||||
Su_17M4,
|
||||
F_A_18C,
|
||||
F_15E,
|
||||
AV8BNA,
|
||||
JF_17,
|
||||
F_16A,
|
||||
F_16C_50,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
A_10A,
|
||||
|
||||
Tornado_IDS,
|
||||
Tornado_GR4,
|
||||
|
||||
Ju_88A4,
|
||||
]
|
||||
Rafale_A_S
|
||||
]
|
||||
|
||||
DRONES = [
|
||||
MQ_9_Reaper,
|
||||
RQ_1A_Predator,
|
||||
WingLoong_I
|
||||
]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
from enum import Enum
|
||||
from typing import List
|
||||
|
||||
from dcs.mission import StartType
|
||||
from dcs.unittype import UnitType
|
||||
|
||||
from game import db
|
||||
from dcs.unittype import UnitType
|
||||
from dcs.point import MovingPoint, PointAction
|
||||
from theater.controlpoint import ControlPoint
|
||||
|
||||
|
||||
class FlightType(Enum):
|
||||
@@ -46,11 +46,25 @@ class FlightWaypointType(Enum):
|
||||
TARGET_POINT = 12 # A target building or static object, position
|
||||
TARGET_GROUP_LOC = 13 # A target group approximate location
|
||||
TARGET_SHIP = 14 # A target ship known location
|
||||
CUSTOM = 15 # User waypoint (no specific behaviour)
|
||||
|
||||
|
||||
class PredefinedWaypointCategory(Enum):
|
||||
NOT_PREDEFINED = 0
|
||||
ALLY_CP = 1
|
||||
ENEMY_CP = 2
|
||||
FRONTLINE = 3
|
||||
ENEMY_BUILDING = 4
|
||||
ENEMY_UNIT = 5
|
||||
ALLY_BUILDING = 6
|
||||
ALLY_UNIT = 7
|
||||
|
||||
|
||||
class FlightWaypoint:
|
||||
|
||||
def __init__(self, x: float, y: float, alt=0):
|
||||
def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float,
|
||||
alt: int = 0) -> None:
|
||||
self.waypoint_type = waypoint_type
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.alt = alt
|
||||
@@ -58,10 +72,39 @@ class FlightWaypoint:
|
||||
self.name = ""
|
||||
self.description = ""
|
||||
self.targets = []
|
||||
self.targetGroup = None
|
||||
self.obj_name = ""
|
||||
self.pretty_name = ""
|
||||
self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType
|
||||
self.category: PredefinedWaypointCategory = PredefinedWaypointCategory.NOT_PREDEFINED
|
||||
self.only_for_player = False
|
||||
self.data = None
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_pydcs(cls, point: MovingPoint,
|
||||
from_cp: ControlPoint) -> "FlightWaypoint":
|
||||
waypoint = FlightWaypoint(point.position.x, point.position.y,
|
||||
point.alt)
|
||||
waypoint.alt_type = point.alt_type
|
||||
# Other actions exist... but none of them *should* be the first
|
||||
# waypoint for a flight.
|
||||
waypoint.waypoint_type = {
|
||||
PointAction.TurningPoint: FlightWaypointType.NAV,
|
||||
PointAction.FlyOverPoint: FlightWaypointType.NAV,
|
||||
PointAction.FromParkingArea: FlightWaypointType.TAKEOFF,
|
||||
PointAction.FromParkingAreaHot: FlightWaypointType.TAKEOFF,
|
||||
PointAction.FromRunway: FlightWaypointType.TAKEOFF,
|
||||
}[point.action]
|
||||
if waypoint.waypoint_type == FlightWaypointType.NAV:
|
||||
waypoint.name = "NAV"
|
||||
waypoint.pretty_name = "Nav"
|
||||
waypoint.description = "Nav"
|
||||
else:
|
||||
waypoint.name = "TAKEOFF"
|
||||
waypoint.pretty_name = "Takeoff"
|
||||
waypoint.description = "Takeoff"
|
||||
waypoint.description = f"Takeoff from {from_cp.name}"
|
||||
return waypoint
|
||||
|
||||
|
||||
class Flight:
|
||||
@@ -76,6 +119,7 @@ class Flight:
|
||||
loadout = {}
|
||||
preset_loadout_name = ""
|
||||
start_type = "Runway"
|
||||
group = False # Contains DCS Mission group data after mission has been generated
|
||||
|
||||
# How long before this flight should take off
|
||||
scheduled_in = 0
|
||||
@@ -97,10 +141,10 @@ class Flight:
|
||||
|
||||
# Test
|
||||
if __name__ == '__main__':
|
||||
from dcs.planes import A_10C
|
||||
from pydcs.dcs.planes import A_10C
|
||||
from theater import ControlPoint, Point, List
|
||||
|
||||
from_cp = ControlPoint(0, "AA", Point(0, 0), None, [], 0, 0)
|
||||
f = Flight(A_10C, 4, from_cp, FlightType.CAS)
|
||||
f = Flight(A_10C(), 4, from_cp, FlightType.CAS)
|
||||
f.scheduled_in = 50
|
||||
print(f)
|
||||
|
||||
@@ -22,15 +22,22 @@ class ForcedOptionsGenerator:
|
||||
self.game = game
|
||||
|
||||
def _set_options_view(self):
|
||||
if self.game.settings.map_coalition_visibility == "All Units":
|
||||
|
||||
if self.game.settings.map_coalition_visibility == ForcedOptions.Views.All:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.All
|
||||
elif self.game.settings.map_coalition_visibility == "Allied Units":
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.Allies
|
||||
elif self.game.settings.map_coalition_visibility == "Own Aircraft":
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyAllies
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.MyAircraft
|
||||
elif self.game.settings.map_coalition_visibility == "None":
|
||||
elif self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap:
|
||||
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyMap
|
||||
|
||||
def _set_external_views(self):
|
||||
if not self.game.settings.external_views_allowed:
|
||||
self.mission.forced_options.external_views = self.game.settings.external_views_allowed
|
||||
|
||||
def _set_labels(self):
|
||||
if self.game.settings.labels == "Abbreviated":
|
||||
self.mission.forced_options.labels = int(Labels.Abbreviated)
|
||||
@@ -41,5 +48,8 @@ class ForcedOptionsGenerator:
|
||||
|
||||
def generate(self):
|
||||
self._set_options_view()
|
||||
self._set_external_views()
|
||||
self._set_labels()
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ from gen import Conflict
|
||||
from gen.ground_forces.combat_stance import CombatStance
|
||||
from theater import ControlPoint
|
||||
|
||||
import pydcs_extensions.frenchpack.frenchpack as frenchpack
|
||||
|
||||
TYPE_TANKS = [
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_72B,
|
||||
@@ -32,6 +34,18 @@ TYPE_TANKS = [
|
||||
Armor.ST_Centaur_IV,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.HIT_Churchill_VII,
|
||||
|
||||
# Mods
|
||||
frenchpack.DIM__TOYOTA_BLUE,
|
||||
frenchpack.DIM__TOYOTA_GREEN,
|
||||
frenchpack.DIM__TOYOTA_DESERT,
|
||||
frenchpack.DIM__KAMIKAZE,
|
||||
|
||||
frenchpack.AMX_10RCR,
|
||||
frenchpack.AMX_10RCR_SEPAR,
|
||||
frenchpack.AMX_30B2,
|
||||
frenchpack.Leclerc_Serie_XXI,
|
||||
|
||||
]
|
||||
|
||||
TYPE_ATGM = [
|
||||
@@ -44,6 +58,12 @@ TYPE_ATGM = [
|
||||
Armor.TD_Jagdpanzer_IV,
|
||||
Armor.TD_Jagdpanther_G1,
|
||||
Armor.TD_M10_GMC,
|
||||
|
||||
# Mods
|
||||
frenchpack.VBAE_CRAB_MMP,
|
||||
frenchpack.VAB_MEPHISTO,
|
||||
frenchpack.TRM_2000_PAMELA,
|
||||
|
||||
]
|
||||
|
||||
TYPE_IFV = [
|
||||
@@ -61,6 +81,12 @@ TYPE_IFV = [
|
||||
# WW2
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
|
||||
# Mods
|
||||
frenchpack.ERC_90,
|
||||
frenchpack.VBAE_CRAB,
|
||||
frenchpack.VAB_T20_13
|
||||
|
||||
]
|
||||
|
||||
TYPE_APC = [
|
||||
@@ -81,6 +107,12 @@ TYPE_APC = [
|
||||
# WW2
|
||||
Armor.APC_M2A1,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
|
||||
# Mods
|
||||
frenchpack.VAB__50,
|
||||
frenchpack.VBL__50,
|
||||
frenchpack.VBL_AANF1,
|
||||
|
||||
]
|
||||
|
||||
TYPE_ARTILLERY = [
|
||||
@@ -117,6 +149,11 @@ TYPE_LOGI = [
|
||||
Unarmed.Willys_MB,
|
||||
Unarmed.Land_Rover_109_S3,
|
||||
Unarmed.Land_Rover_101_FC,
|
||||
|
||||
# Mods
|
||||
frenchpack.VBL,
|
||||
frenchpack.VAB,
|
||||
|
||||
]
|
||||
|
||||
TYPE_INFANTRY = [
|
||||
@@ -148,19 +185,19 @@ class CombatGroupRole(Enum):
|
||||
|
||||
|
||||
DISTANCE_FROM_FRONTLINE = {
|
||||
CombatGroupRole.TANK:2800,
|
||||
CombatGroupRole.APC:7000,
|
||||
CombatGroupRole.IFV:3000,
|
||||
CombatGroupRole.ARTILLERY:14000,
|
||||
CombatGroupRole.SHORAD:12000,
|
||||
CombatGroupRole.LOGI:18000,
|
||||
CombatGroupRole.INFANTRY:2800,
|
||||
CombatGroupRole.ATGM:5500
|
||||
CombatGroupRole.TANK:3200,
|
||||
CombatGroupRole.APC:8000,
|
||||
CombatGroupRole.IFV:3700,
|
||||
CombatGroupRole.ARTILLERY:18000,
|
||||
CombatGroupRole.SHORAD:13000,
|
||||
CombatGroupRole.LOGI:20000,
|
||||
CombatGroupRole.INFANTRY:3000,
|
||||
CombatGroupRole.ATGM:6200
|
||||
}
|
||||
|
||||
GROUP_SIZES_BY_COMBAT_STANCE = {
|
||||
CombatStance.DEFENSIVE: [2, 4, 6],
|
||||
CombatStance.AGGRESIVE: [2, 4, 6],
|
||||
CombatStance.AGGRESSIVE: [2, 4, 6],
|
||||
CombatStance.RETREAT: [2, 4, 6, 8],
|
||||
CombatStance.BREAKTHROUGH: [4, 6, 6, 8],
|
||||
CombatStance.ELIMINATION: [2, 4, 4, 4, 6],
|
||||
|
||||
@@ -3,7 +3,7 @@ from enum import Enum
|
||||
|
||||
class CombatStance(Enum):
|
||||
DEFENSIVE = 0 # Unit will adopt defensive stance with medium group of units
|
||||
AGGRESIVE = 1 # Unit will attempt to make progress with medium sized group of units
|
||||
AGGRESSIVE = 1 # Unit will attempt to make progress with medium sized group of units
|
||||
RETREAT = 2 # Unit will retreat
|
||||
BREAKTHROUGH = 3 # Unit will attempt a breakthrough, rushing forward very aggresively with big group of armored units, and even less armored units will move aggresively
|
||||
ELIMINATION = 4 # Unit will progress aggresively toward anemy units, attempting to eliminate the ennemy force
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import logging
|
||||
from dcs.statics import *
|
||||
from dcs.unit import Ship, Vehicle
|
||||
|
||||
from game import db
|
||||
from game.data.building_data import FORTIFICATION_UNITS_ID, FORTIFICATION_UNITS
|
||||
from game.db import unit_type_from_name
|
||||
from .airfields import RunwayData
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.statics import *
|
||||
from .radios import RadioRegistry
|
||||
from .tacan import TacanBand, TacanRegistry
|
||||
|
||||
FARP_FRONTLINE_DISTANCE = 10000
|
||||
AA_CP_MIN_DISTANCE = 40000
|
||||
@@ -15,10 +16,15 @@ AA_CP_MIN_DISTANCE = 40000
|
||||
class GroundObjectsGenerator:
|
||||
FARP_CAPACITY = 4
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game,
|
||||
radio_registry: RadioRegistry, tacan_registry: TacanRegistry):
|
||||
self.m = mission
|
||||
self.conflict = conflict
|
||||
self.game = game
|
||||
self.radio_registry = radio_registry
|
||||
self.tacan_registry = tacan_registry
|
||||
self.icls_alloc = iter(range(1, 21))
|
||||
self.runways: Dict[str, RunwayData] = {}
|
||||
|
||||
def generate_farps(self, number_of_units=1) -> typing.Collection[StaticGroup]:
|
||||
if self.conflict.is_vector:
|
||||
@@ -44,15 +50,6 @@ class GroundObjectsGenerator:
|
||||
|
||||
def generate(self):
|
||||
|
||||
cp = None # type: ControlPoint
|
||||
if self.conflict.attackers_country.name == self.game.player_country:
|
||||
cp = self.conflict.to_cp
|
||||
else:
|
||||
cp = self.conflict.from_cp
|
||||
|
||||
consumed_farps = set()
|
||||
|
||||
|
||||
for cp in self.game.theater.controlpoints:
|
||||
|
||||
if cp.captured:
|
||||
@@ -63,26 +60,49 @@ class GroundObjectsGenerator:
|
||||
|
||||
for ground_object in cp.ground_objects:
|
||||
if ground_object.dcs_identifier == "AA":
|
||||
|
||||
if self.game.position_culled(ground_object.position):
|
||||
continue
|
||||
|
||||
for g in ground_object.groups:
|
||||
if len(g.units) > 0:
|
||||
|
||||
utype = unit_type_from_name(g.units[0].type)
|
||||
|
||||
vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
|
||||
vg.units[0].name = self.m.string(g.units[0].name)
|
||||
for i, u in enumerate(g.units):
|
||||
if i > 0:
|
||||
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
|
||||
vehicle.position.x = u.position.x
|
||||
vehicle.position.y = u.position.y
|
||||
vehicle.heading = u.heading
|
||||
vg.add_unit(vehicle)
|
||||
if not ground_object.sea_object:
|
||||
vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
|
||||
vg.units[0].name = self.m.string(g.units[0].name)
|
||||
vg.units[0].player_can_drive = True
|
||||
for i, u in enumerate(g.units):
|
||||
if i > 0:
|
||||
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
|
||||
vehicle.position.x = u.position.x
|
||||
vehicle.position.y = u.position.y
|
||||
vehicle.heading = u.heading
|
||||
vehicle.player_can_drive = True
|
||||
vg.add_unit(vehicle)
|
||||
|
||||
if hasattr(utype, 'eplrs'):
|
||||
if utype.eplrs:
|
||||
vg.points[0].tasks.append(EPLRS(vg.id))
|
||||
else:
|
||||
vg = self.m.ship_group(side, g.name, utype, position=g.position,
|
||||
heading=g.units[0].heading)
|
||||
vg.units[0].name = self.m.string(g.units[0].name)
|
||||
for i, u in enumerate(g.units):
|
||||
utype = unit_type_from_name(u.type)
|
||||
if i > 0:
|
||||
ship = Ship(self.m.next_unit_id(), self.m.string(u.name), utype)
|
||||
ship.position.x = u.position.x
|
||||
ship.position.y = u.position.y
|
||||
ship.heading = u.heading
|
||||
vg.add_unit(ship)
|
||||
|
||||
if self.game.settings.perf_red_alert_state:
|
||||
vg.points[0].tasks.append(OptAlarmState(2))
|
||||
else:
|
||||
vg.points[0].tasks.append(OptAlarmState(1))
|
||||
|
||||
|
||||
elif ground_object.dcs_identifier in ["CARRIER", "LHA"]:
|
||||
for g in ground_object.groups:
|
||||
if len(g.units) > 0:
|
||||
@@ -92,6 +112,8 @@ class GroundObjectsGenerator:
|
||||
utype = db.upgrade_to_supercarrier(utype, cp.name)
|
||||
|
||||
sg = self.m.ship_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
|
||||
atc_channel = self.radio_registry.alloc_uhf()
|
||||
sg.set_frequency(atc_channel.hertz)
|
||||
sg.units[0].name = self.m.string(g.units[0].name)
|
||||
|
||||
for i, u in enumerate(g.units):
|
||||
@@ -100,65 +122,113 @@ class GroundObjectsGenerator:
|
||||
ship.position.x = u.position.x
|
||||
ship.position.y = u.position.y
|
||||
ship.heading = u.heading
|
||||
# TODO: Verify.
|
||||
ship.set_frequency(atc_channel.hertz)
|
||||
sg.add_unit(ship)
|
||||
|
||||
# TODO : make sure the point is not on Land
|
||||
sg.add_waypoint(sg.points[0].position.point_from_heading(g.units[0].heading, 100000))
|
||||
# Find carrier direction (In the wind)
|
||||
found_carrier_destination = False
|
||||
attempt = 0
|
||||
while not found_carrier_destination and attempt < 5:
|
||||
point = sg.points[0].position.point_from_heading(self.m.weather.wind_at_ground.direction + 180, 100000-attempt*20000)
|
||||
if self.game.theater.is_in_sea(point):
|
||||
found_carrier_destination = True
|
||||
sg.add_waypoint(point)
|
||||
else:
|
||||
attempt = attempt + 1
|
||||
|
||||
# SET UP TACAN
|
||||
modeChannel = "X" if not cp.tacanY else "Y"
|
||||
sg.points[0].tasks.append(ActivateBeaconCommand(channel=cp.tacanN, modechannel=modeChannel, callsign=cp.tacanI, unit_id=sg.units[0].id))
|
||||
|
||||
if ground_object.dcs_identifier == "CARRIER" and hasattr(cp, "icls"):
|
||||
sg.points[0].tasks.append(ActivateICLSCommand(cp.icls, unit_id=sg.units[0].id))
|
||||
# Set UP TACAN and ICLS
|
||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.X)
|
||||
icls_channel = next(self.icls_alloc)
|
||||
# TODO: Assign these properly.
|
||||
if ground_object.dcs_identifier == "CARRIER":
|
||||
tacan_callsign = random.choice([
|
||||
"STE",
|
||||
"CVN",
|
||||
"CVH",
|
||||
"CCV",
|
||||
"ACC",
|
||||
"ARC",
|
||||
"GER",
|
||||
"ABR",
|
||||
"LIN",
|
||||
"TRU",
|
||||
])
|
||||
else:
|
||||
tacan_callsign = random.choice([
|
||||
"LHD",
|
||||
"LHA",
|
||||
"LHB",
|
||||
"LHC",
|
||||
"LHD",
|
||||
"LDS",
|
||||
])
|
||||
sg.points[0].tasks.append(ActivateBeaconCommand(
|
||||
channel=tacan.number,
|
||||
modechannel=tacan.band.value,
|
||||
callsign=tacan_callsign,
|
||||
unit_id=sg.units[0].id,
|
||||
aa=False
|
||||
))
|
||||
sg.points[0].tasks.append(ActivateICLSCommand(
|
||||
icls_channel,
|
||||
unit_id=sg.units[0].id
|
||||
))
|
||||
# TODO: Make unit name usable.
|
||||
# This relies on one control point mapping exactly
|
||||
# to one LHA, carrier, or other usable "runway".
|
||||
# This isn't wholly true, since the DD escorts of
|
||||
# the carrier group are valid for helicopters, but
|
||||
# they aren't exposed as such to the game. Should
|
||||
# clean this up so that's possible. We can't use the
|
||||
# unit name since it's an arbitrary ID.
|
||||
self.runways[cp.name] = RunwayData(
|
||||
cp.name,
|
||||
"N/A",
|
||||
atc=atc_channel,
|
||||
tacan=tacan,
|
||||
tacan_callsign=tacan_callsign,
|
||||
icls=icls_channel,
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
if self.game.position_culled(ground_object.position):
|
||||
continue
|
||||
|
||||
static_type = None
|
||||
if ground_object.dcs_identifier in warehouse_map:
|
||||
static_type = warehouse_map[ground_object.dcs_identifier]
|
||||
else:
|
||||
elif ground_object.dcs_identifier in fortification_map:
|
||||
static_type = fortification_map[ground_object.dcs_identifier]
|
||||
|
||||
if not static_type:
|
||||
elif ground_object.dcs_identifier in FORTIFICATION_UNITS_ID:
|
||||
for f in FORTIFICATION_UNITS:
|
||||
if f.id == ground_object.dcs_identifier:
|
||||
unit_type = f
|
||||
break
|
||||
else:
|
||||
print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier))
|
||||
continue
|
||||
|
||||
group = self.m.static_group(
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=static_type,
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading,
|
||||
dead=ground_object.is_dead,
|
||||
)
|
||||
|
||||
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))
|
||||
|
||||
|
||||
def farp_aa(mission_obj, country, name, position: mapping.Point):
|
||||
"""
|
||||
Add AAA to a FARP :)
|
||||
:param mission_obj:
|
||||
:param country:
|
||||
:param name:
|
||||
:param position:
|
||||
:return:
|
||||
"""
|
||||
vg = unitgroup.VehicleGroup(mission_obj.next_group_id(), mission_obj.string(name))
|
||||
|
||||
units = [
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka,
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
]
|
||||
|
||||
v = mission_obj.vehicle(name + "_AAA", random.choice(units))
|
||||
v.position.x = position.x - random.randint(5, 30)
|
||||
v.position.y = position.y - random.randint(5, 30)
|
||||
v.heading = random.randint(0, 359)
|
||||
vg.add_unit(v)
|
||||
|
||||
wp = vg.add_waypoint(vg.units[0].position, PointAction.OffRoad, 0)
|
||||
wp.ETA_locked = True
|
||||
|
||||
country.add_vehicle_group(vg)
|
||||
return vg
|
||||
if static_type is None:
|
||||
if not ground_object.is_dead:
|
||||
group = self.m.vehicle_group(
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=unit_type,
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading,
|
||||
)
|
||||
logging.info("generated {}object identifier {} with mission id {}".format(
|
||||
"dead " if ground_object.is_dead else "", group.name, group.id))
|
||||
else:
|
||||
group = self.m.static_group(
|
||||
country=side,
|
||||
name=ground_object.string_identifier,
|
||||
_type=static_type,
|
||||
position=ground_object.position,
|
||||
heading=ground_object.heading,
|
||||
dead=ground_object.is_dead,
|
||||
)
|
||||
|
||||
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))
|
||||
290
gen/kneeboard.py
Normal file
290
gen/kneeboard.py
Normal file
@@ -0,0 +1,290 @@
|
||||
"""Generates kneeboard pages relevant to the player's mission.
|
||||
|
||||
The player kneeboard includes the following information:
|
||||
|
||||
* Airfield (departure, arrival, divert) info.
|
||||
* Flight plan (waypoint numbers, names, altitudes).
|
||||
* Comm channels.
|
||||
* AWACS info.
|
||||
* Tanker info.
|
||||
* JTAC info.
|
||||
|
||||
Things we should add:
|
||||
|
||||
* Flight plan ToT and fuel ladder (current have neither available).
|
||||
* Support for planning an arrival/divert airfield separate from departure.
|
||||
* Mission package infrastructure to include information about the larger
|
||||
mission, i.e. information about the escort flight for a strike package.
|
||||
* Target information. Steerpoints, preplanned objectives, ToT, etc.
|
||||
|
||||
For multiplayer missions, a kneeboard will be generated per flight.
|
||||
https://forums.eagle.ru/showthread.php?t=206360 claims that kneeboard pages can
|
||||
only be added per airframe, so PvP missions where each side have the same
|
||||
aircraft will be able to see the enemy's kneeboard for the same airframe.
|
||||
"""
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from dcs.mission import Mission
|
||||
from dcs.unittype import FlyingType
|
||||
from tabulate import tabulate
|
||||
|
||||
from . import units
|
||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||
from .airfields import RunwayData
|
||||
from .airsupportgen import AwacsInfo, TankerInfo
|
||||
from .briefinggen import CommInfo, JtacInfo, MissionInfoGenerator
|
||||
from .flights.flight import FlightWaypoint, FlightWaypointType
|
||||
from .radios import RadioFrequency
|
||||
|
||||
|
||||
class KneeboardPageWriter:
|
||||
"""Creates kneeboard images."""
|
||||
|
||||
def __init__(self, page_margin: int = 24, line_spacing: int = 12) -> None:
|
||||
self.image = Image.new('RGB', (768, 1024), (0xff, 0xff, 0xff))
|
||||
# These font sizes create a relatively full page for current sorties. If
|
||||
# we start generating more complicated flight plans, or start including
|
||||
# more information in the comm ladder (the latter of which we should
|
||||
# probably do), we'll need to split some of this information off into a
|
||||
# second page.
|
||||
self.title_font = ImageFont.truetype("arial.ttf", 32)
|
||||
self.heading_font = ImageFont.truetype("arial.ttf", 24)
|
||||
self.content_font = ImageFont.truetype("arial.ttf", 20)
|
||||
self.table_font = ImageFont.truetype(
|
||||
"resources/fonts/Inconsolata.otf", 20)
|
||||
self.draw = ImageDraw.Draw(self.image)
|
||||
self.x = page_margin
|
||||
self.y = page_margin
|
||||
self.line_spacing = line_spacing
|
||||
|
||||
@property
|
||||
def position(self) -> Tuple[int, int]:
|
||||
return self.x, self.y
|
||||
|
||||
def text(self, text: str, font=None,
|
||||
fill: Tuple[int, int, int] = (0, 0, 0)) -> None:
|
||||
if font is None:
|
||||
font = self.content_font
|
||||
|
||||
self.draw.text(self.position, text, font=font, fill=fill)
|
||||
width, height = self.draw.textsize(text, font=font)
|
||||
self.y += height + self.line_spacing
|
||||
|
||||
def title(self, title: str) -> None:
|
||||
self.text(title, font=self.title_font)
|
||||
|
||||
def heading(self, text: str) -> None:
|
||||
self.text(text, font=self.heading_font)
|
||||
|
||||
def table(self, cells: List[List[str]],
|
||||
headers: Optional[List[str]] = None) -> None:
|
||||
table = tabulate(cells, headers=headers, numalign="right")
|
||||
self.text(table, font=self.table_font)
|
||||
|
||||
def write(self, path: Path) -> None:
|
||||
self.image.save(path)
|
||||
|
||||
|
||||
class KneeboardPage:
|
||||
"""Base class for all kneeboard pages."""
|
||||
|
||||
def write(self, path: Path) -> None:
|
||||
"""Writes the kneeboard page to the given path."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NumberedWaypoint:
|
||||
number: int
|
||||
waypoint: FlightWaypoint
|
||||
|
||||
|
||||
class FlightPlanBuilder:
|
||||
def __init__(self) -> None:
|
||||
self.rows: List[List[str]] = []
|
||||
self.target_points: List[NumberedWaypoint] = []
|
||||
|
||||
def add_waypoint(self, waypoint_num: int, waypoint: FlightWaypoint) -> None:
|
||||
if waypoint.waypoint_type == FlightWaypointType.TARGET_POINT:
|
||||
self.target_points.append(NumberedWaypoint(waypoint_num, waypoint))
|
||||
return
|
||||
|
||||
if self.target_points:
|
||||
self.coalesce_target_points()
|
||||
self.target_points = []
|
||||
|
||||
self.add_waypoint_row(NumberedWaypoint(waypoint_num, waypoint))
|
||||
|
||||
def coalesce_target_points(self) -> None:
|
||||
if len(self.target_points) <= 4:
|
||||
for steerpoint in self.target_points:
|
||||
self.add_waypoint_row(steerpoint)
|
||||
return
|
||||
|
||||
first_waypoint_num = self.target_points[0].number
|
||||
last_waypoint_num = self.target_points[-1].number
|
||||
|
||||
self.rows.append([
|
||||
f"{first_waypoint_num}-{last_waypoint_num}",
|
||||
"Target points",
|
||||
"0"
|
||||
])
|
||||
|
||||
def add_waypoint_row(self, waypoint: NumberedWaypoint) -> None:
|
||||
self.rows.append([
|
||||
waypoint.number,
|
||||
waypoint.waypoint.pretty_name,
|
||||
str(int(units.meters_to_feet(waypoint.waypoint.alt)))
|
||||
])
|
||||
|
||||
def build(self) -> List[List[str]]:
|
||||
return self.rows
|
||||
|
||||
|
||||
class BriefingPage(KneeboardPage):
|
||||
"""A kneeboard page containing briefing information."""
|
||||
def __init__(self, flight: FlightData, comms: List[CommInfo],
|
||||
awacs: List[AwacsInfo], tankers: List[TankerInfo],
|
||||
jtacs: List[JtacInfo]) -> None:
|
||||
self.flight = flight
|
||||
self.comms = list(comms)
|
||||
self.awacs = awacs
|
||||
self.tankers = tankers
|
||||
self.jtacs = jtacs
|
||||
self.comms.append(CommInfo("Flight", self.flight.intra_flight_channel))
|
||||
|
||||
def write(self, path: Path) -> None:
|
||||
writer = KneeboardPageWriter()
|
||||
writer.title(f"{self.flight.callsign} Mission Info")
|
||||
|
||||
# TODO: Handle carriers.
|
||||
writer.heading("Airfield Info")
|
||||
writer.table([
|
||||
self.airfield_info_row("Departure", self.flight.departure),
|
||||
self.airfield_info_row("Arrival", self.flight.arrival),
|
||||
self.airfield_info_row("Divert", self.flight.divert),
|
||||
], headers=["", "Airbase", "ATC", "TCN", "I(C)LS", "RWY"])
|
||||
|
||||
writer.heading("Flight Plan")
|
||||
flight_plan_builder = FlightPlanBuilder()
|
||||
for num, waypoint in enumerate(self.flight.waypoints):
|
||||
flight_plan_builder.add_waypoint(num, waypoint)
|
||||
writer.table(flight_plan_builder.build(),
|
||||
headers=["STPT", "Action", "Alt"])
|
||||
|
||||
writer.heading("Comm Ladder")
|
||||
comms = []
|
||||
for comm in self.comms:
|
||||
comms.append([comm.name, self.format_frequency(comm.freq)])
|
||||
writer.table(comms, headers=["Name", "UHF"])
|
||||
|
||||
writer.heading("AWACS")
|
||||
awacs = []
|
||||
for a in self.awacs:
|
||||
awacs.append([a.callsign, self.format_frequency(a.freq)])
|
||||
writer.table(awacs, headers=["Callsign", "UHF"])
|
||||
|
||||
writer.heading("Tankers")
|
||||
tankers = []
|
||||
for tanker in self.tankers:
|
||||
tankers.append([
|
||||
tanker.callsign,
|
||||
tanker.variant,
|
||||
tanker.tacan,
|
||||
self.format_frequency(tanker.freq),
|
||||
])
|
||||
writer.table(tankers, headers=["Callsign", "Type", "TACAN", "UHF"])
|
||||
|
||||
writer.heading("JTAC")
|
||||
jtacs = []
|
||||
for jtac in self.jtacs:
|
||||
jtacs.append([jtac.callsign, jtac.region, jtac.code])
|
||||
writer.table(jtacs, headers=["Callsign", "Region", "Laser Code"])
|
||||
|
||||
writer.write(path)
|
||||
|
||||
def airfield_info_row(self, row_title: str,
|
||||
runway: Optional[RunwayData]) -> List[str]:
|
||||
"""Creates a table row for a given airfield.
|
||||
|
||||
Args:
|
||||
row_title: Purpose of the airfield. e.g. "Departure", "Arrival" or
|
||||
"Divert".
|
||||
runway: The runway described by this row.
|
||||
|
||||
Returns:
|
||||
A list of strings to be used as a row of the airfield table.
|
||||
"""
|
||||
if runway is None:
|
||||
return [row_title, "", "", "", "", ""]
|
||||
|
||||
atc = ""
|
||||
if runway.atc is not None:
|
||||
atc = self.format_frequency(runway.atc)
|
||||
return [
|
||||
row_title,
|
||||
runway.airfield_name,
|
||||
atc,
|
||||
runway.tacan or "",
|
||||
runway.ils or runway.icls or "",
|
||||
runway.runway_name,
|
||||
]
|
||||
|
||||
def format_frequency(self, frequency: RadioFrequency) -> str:
|
||||
channel = self.flight.channel_for(frequency)
|
||||
if channel is None:
|
||||
return str(frequency)
|
||||
|
||||
namer = AIRCRAFT_DATA[self.flight.aircraft_type.id].channel_namer
|
||||
channel_name = namer.channel_name(channel.radio_id, channel.channel)
|
||||
return f"{channel_name} {frequency}"
|
||||
|
||||
|
||||
class KneeboardGenerator(MissionInfoGenerator):
|
||||
"""Creates kneeboard pages for each client flight in the mission."""
|
||||
|
||||
def __init__(self, mission: Mission) -> None:
|
||||
super().__init__(mission)
|
||||
|
||||
def generate(self) -> None:
|
||||
"""Generates a kneeboard per client flight."""
|
||||
temp_dir = Path("kneeboards")
|
||||
temp_dir.mkdir(exist_ok=True)
|
||||
for aircraft, pages in self.pages_by_airframe().items():
|
||||
aircraft_dir = temp_dir / aircraft.id
|
||||
aircraft_dir.mkdir(exist_ok=True)
|
||||
for idx, page in enumerate(pages):
|
||||
page_path = aircraft_dir / f"page{idx:02}.png"
|
||||
page.write(page_path)
|
||||
self.mission.add_aircraft_kneeboard(aircraft, page_path)
|
||||
|
||||
def pages_by_airframe(self) -> Dict[FlyingType, List[KneeboardPage]]:
|
||||
"""Returns a list of kneeboard pages per airframe in the mission.
|
||||
|
||||
Only client flights will be included, but because DCS does not support
|
||||
group-specific kneeboard pages, flights (possibly from opposing sides)
|
||||
will be able to see the kneeboards of all aircraft of the same type.
|
||||
|
||||
Returns:
|
||||
A dict mapping aircraft types to the list of kneeboard pages for
|
||||
that aircraft.
|
||||
"""
|
||||
all_flights: Dict[FlyingType, List[KneeboardPage]] = defaultdict(list)
|
||||
for flight in self.flights:
|
||||
if not flight.client_units:
|
||||
continue
|
||||
all_flights[flight.aircraft_type].extend(
|
||||
self.generate_flight_kneeboard(flight))
|
||||
return all_flights
|
||||
|
||||
def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
|
||||
"""Returns a list of kneeboard pages for the given flight."""
|
||||
return [
|
||||
BriefingPage(
|
||||
flight, self.comms, self.awacs, self.tankers, self.jtacs
|
||||
),
|
||||
]
|
||||
27
gen/missiles/missiles_group_generator.py
Normal file
27
gen/missiles/missiles_group_generator.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import logging
|
||||
import random
|
||||
from game import db
|
||||
from gen.missiles.v1_group import V1GroupGenerator
|
||||
|
||||
MISSILES_MAP = {
|
||||
"V1GroupGenerator": V1GroupGenerator,
|
||||
}
|
||||
|
||||
|
||||
def generate_missile_group(game, ground_object, faction:str):
|
||||
"""
|
||||
This generate a ship group
|
||||
:return: Nothing, but put the group reference inside the ground object
|
||||
"""
|
||||
faction = db.FACTIONS[faction]
|
||||
if "missiles" in faction.keys():
|
||||
generators = faction["missiles"]
|
||||
if len(generators) > 0:
|
||||
gen = random.choice(generators)
|
||||
if gen in MISSILES_MAP.keys():
|
||||
generator = MISSILES_MAP[gen](game, ground_object, faction)
|
||||
generator.generate()
|
||||
return generator.get_generated_group()
|
||||
else:
|
||||
logging.info("Unable to generate missile group, generator : " + str(gen) + "does not exists")
|
||||
return None
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user