mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
476 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
a7824039e3 | ||
|
|
bb9247e821 | ||
|
|
4bc04ec031 | ||
|
|
8fd91e6c5c | ||
|
|
ee137d086a | ||
|
|
fcd81850cb | ||
|
|
aa4b07d024 | ||
|
|
16a096d288 | ||
|
|
b219b2a71b | ||
|
|
ce70242c35 | ||
|
|
dec01e2611 | ||
|
|
4373d89661 | ||
|
|
c73290eebb | ||
|
|
a022f9c2e1 | ||
|
|
5adb25a695 | ||
|
|
08c2972cf9 | ||
|
|
8132c7e676 | ||
|
|
8c68c9f703 | ||
|
|
000b6142fd | ||
|
|
c203ded1cd | ||
|
|
64c5c39b2a | ||
|
|
a8d2a1e371 | ||
|
|
9e5846b24a | ||
|
|
836ff9122c | ||
|
|
75d836358b | ||
|
|
bb11e7f90c | ||
|
|
cf6a71ab86 | ||
|
|
7f7288937d | ||
|
|
a38f9c2183 | ||
|
|
7ee880cadc | ||
|
|
9ae34d474b | ||
|
|
2817e2f2c8 | ||
|
|
02886a09d3 | ||
|
|
0b9d827ad6 | ||
|
|
ab3ea84d70 | ||
|
|
03a1c44659 | ||
|
|
53364444fd | ||
|
|
94040e8551 | ||
|
|
34d46ee28e | ||
|
|
59986d74f4 | ||
|
|
8afdf5ef65 | ||
|
|
fc64e57495 | ||
|
|
6dec5ea8f8 | ||
|
|
3f2aafcd28 | ||
|
|
7a3ee6b1a9 | ||
|
|
f7d9c62afb | ||
|
|
1a26a7f346 | ||
|
|
f6ff4405f8 | ||
|
|
14ceb0abc6 | ||
|
|
c9a3af6065 | ||
|
|
4e4a7fe84e | ||
|
|
989d25e2e2 | ||
|
|
f57e453d8d | ||
|
|
fcfa785ae9 | ||
|
|
3de688da29 | ||
|
|
5884d9d120 | ||
|
|
074ea5c719 | ||
|
|
e794446a54 | ||
|
|
1320a67e1f | ||
|
|
7c45177650 | ||
|
|
6b2d6ab57f | ||
|
|
04add8ebb5 | ||
|
|
ad8138fb04 | ||
|
|
6598e034c1 | ||
|
|
bba51c6a23 | ||
|
|
7d524300e5 | ||
|
|
a25a0031ff | ||
|
|
98b899c9c7 | ||
|
|
4424425dd4 | ||
|
|
85de3a09ea | ||
|
|
e82db1fecd | ||
|
|
76638b549f | ||
|
|
2936df6a02 | ||
|
|
27e3cf8ac5 | ||
|
|
adf1f8db8c | ||
|
|
9f319ab99a | ||
|
|
48a40f2511 | ||
|
|
7fbc75b375 | ||
|
|
65a54acd4f | ||
|
|
de96552f78 | ||
|
|
fee959940a | ||
|
|
48748bbc39 | ||
|
|
827138fc6a | ||
|
|
b7ee98dcd6 | ||
|
|
fa99df3ce7 | ||
|
|
d2ea6ed2fd | ||
|
|
d8d17e5c18 | ||
|
|
0519438292 | ||
|
|
09b8ff6b93 | ||
|
|
bd66dcb39e | ||
|
|
93504eaf7a | ||
|
|
9e116f481e | ||
|
|
bd95258176 | ||
|
|
a7e202bbc8 | ||
|
|
3f5f4f4bb1 | ||
|
|
4365db0fae | ||
|
|
82bb608fd3 | ||
|
|
ba0b3adf71 | ||
|
|
707e1f8b67 | ||
|
|
7f97e894a3 | ||
|
|
19118f7b84 | ||
|
|
63c46d1b21 | ||
|
|
4c3ff906ff | ||
|
|
6b77e1cce5 | ||
|
|
fdd8f102e6 | ||
|
|
2d0c195e46 | ||
|
|
f5a5fb765b | ||
|
|
f698cb66b8 | ||
|
|
fc51b16e4a | ||
|
|
0365d7a900 | ||
|
|
7a14376765 | ||
|
|
2167953b87 | ||
|
|
17352bfcf7 | ||
|
|
d4577aa7a6 | ||
|
|
e955c10170 | ||
|
|
9d4b6183b0 | ||
|
|
3c9bffb557 | ||
|
|
d4d7b546e1 | ||
|
|
9f0c17115e | ||
|
|
f6fdd3d12a | ||
|
|
2401da2b24 | ||
|
|
50ef8fef24 | ||
|
|
079248bfeb | ||
|
|
650ee9666d | ||
|
|
18bcc1bce7 | ||
|
|
73b4ec47b9 | ||
|
|
70331d913d | ||
|
|
9c72c9a063 | ||
|
|
af8ae09434 | ||
|
|
e8a8364ac2 | ||
|
|
0e7d49488c | ||
|
|
405c6659b9 | ||
|
|
7ca435337f | ||
|
|
024b665dd9 | ||
|
|
a397296624 | ||
|
|
adb9e38ff4 | ||
|
|
c4dc432be1 | ||
|
|
49795993f1 | ||
|
|
ea6b2ab2dc | ||
|
|
44d04d3ba6 | ||
|
|
577c171d48 | ||
|
|
c5edf7a581 | ||
|
|
ac67ec86b1 | ||
|
|
291f538645 | ||
|
|
817a3a5e15 | ||
|
|
0b87e192ce | ||
|
|
fc83ca0de6 | ||
|
|
c621e822dc | ||
|
|
1776452964 | ||
|
|
4e5e0a4a7a | ||
|
|
bb32f47b8c | ||
|
|
6b5f77c415 | ||
|
|
9a73c78705 | ||
|
|
8246c8e94b | ||
|
|
89e8ef65ea | ||
|
|
2428d39308 | ||
|
|
dc91f5004e | ||
|
|
b66bf4cc36 | ||
|
|
67910bce61 | ||
|
|
2adacdba02 | ||
|
|
4652e7d188 | ||
|
|
a30d4a4514 | ||
|
|
a17a9399ef | ||
|
|
ba6d10cd9e | ||
|
|
b1576e8f15 | ||
|
|
55de28105e | ||
|
|
40e3d59432 | ||
|
|
665f022111 | ||
|
|
309c10c4cb | ||
|
|
658120b8d9 | ||
|
|
fbd01fbfdb | ||
|
|
09135adadc | ||
|
|
0c7a36cef6 | ||
|
|
abf6fe69bd | ||
|
|
b3318583ed | ||
|
|
288fe20def | ||
|
|
96ee14bf08 | ||
|
|
00b9ba0f32 | ||
|
|
2a63f7b187 | ||
|
|
c655d97109 | ||
|
|
fd26700867 | ||
|
|
df5d9782e7 | ||
|
|
61af07bd79 | ||
|
|
0e008ac87b | ||
|
|
28eab870d4 | ||
|
|
86aedaf631 | ||
|
|
e242851260 | ||
|
|
c884fb7262 | ||
|
|
b6104fbdaf | ||
|
|
9305f1d483 | ||
|
|
048cd22d85 | ||
|
|
e1ea9664dc | ||
|
|
72d3863f8a | ||
|
|
49de53f1c9 | ||
|
|
b17e34351c | ||
|
|
84b1df75c2 | ||
|
|
89f122c325 | ||
|
|
e9103acb07 | ||
|
|
c7eae7e97a | ||
|
|
f5851d09f5 | ||
|
|
ba8112280d | ||
|
|
4cc373595c | ||
|
|
36aa4edb05 | ||
|
|
26840373ed | ||
|
|
4c5a2650d1 | ||
|
|
b99cdfb5c3 | ||
|
|
6ec14e744e | ||
|
|
310db66c22 | ||
|
|
c7c2b9a248 | ||
|
|
b697a8b40a | ||
|
|
c152b49b88 | ||
|
|
2356fc2bbf | ||
|
|
f7e2c8921c | ||
|
|
604352f8df | ||
|
|
fbbe56f954 | ||
|
|
7842c69ebb | ||
|
|
e1d50f1f27 | ||
|
|
9d0997624b | ||
|
|
355cd3e0e4 | ||
|
|
af5cd57094 | ||
|
|
8f85101cec | ||
|
|
97be483624 | ||
|
|
1ff5721912 | ||
|
|
80cbc663bf | ||
|
|
b3e729af0d | ||
|
|
3ed864021d | ||
|
|
8bc269fa99 | ||
|
|
c656c0f7e4 | ||
|
|
858e5d2d04 | ||
|
|
903a8db46f | ||
|
|
5aa731853d | ||
|
|
540096178c | ||
|
|
ed2a611197 | ||
|
|
1c61b0b5a2 | ||
|
|
82f7e5d0c4 | ||
|
|
ecb2c86dc4 | ||
|
|
5cbbc3b1ab | ||
|
|
e0b2e178f9 | ||
|
|
390974ba0f | ||
|
|
91379ff7d9 | ||
|
|
1fe9e56997 | ||
|
|
e10b853712 | ||
|
|
7d70862e72 | ||
|
|
90bdcec7ff | ||
|
|
63da350223 | ||
|
|
854f31cb7a |
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,14 +1,21 @@
|
||||
*.pyc
|
||||
__pycache__
|
||||
build/*
|
||||
build/**
|
||||
resources/payloads/*.lua
|
||||
venv
|
||||
logs.txt
|
||||
.DS_Store
|
||||
dist/**
|
||||
a.py
|
||||
resources/tools/a.miz
|
||||
tests/**
|
||||
# User-specific stuff
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
.idea/misc.xml
|
||||
.idea/*.iml
|
||||
.idea/
|
||||
|
||||
liberation_preferences.json
|
||||
|
||||
logs/liberation.log
|
||||
|
||||
qt_ui/logs/liberation.log
|
||||
|
||||
*.psd
|
||||
|
||||
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -1,3 +0,0 @@
|
||||
[submodule "submodules/dcs"]
|
||||
path = submodules/dcs
|
||||
url = https://github.com/pydcs/dcs
|
||||
10
.idea/inspectionProfiles/Project_Default.xml
generated
10
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -1,10 +0,0 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
9
.idea/modules.xml
generated
9
.idea/modules.xml
generated
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
11
.idea/vcs.xml
generated
11
.idea/vcs.xml
generated
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GitSharedSettings">
|
||||
<option name="FORCE_PUSH_PROHIBITED_PATTERNS">
|
||||
<list />
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
41
README.md
41
README.md
@@ -1,33 +1,36 @@
|
||||

|
||||
|
||||
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player dynamic campaign.
|
||||
[](https://www.paypal.com/paypalme/KhopaDCSL)
|
||||
[](https://patreon.com/khopa)
|
||||
|
||||
Uses [pydcs](http://github.com/pydcs/dcs) for mission generation.
|
||||
[](https://github.com/Khopa/dcs_liberation/releases)
|
||||
|
||||
## Installation
|
||||
1. Download and install **Python 3.6.0** package from https://www.python.org/downloads/release/python-360/ (look at the bottom under *Files*; any option will do if it matches your architecture) with default set of options (you need to have *Install launcher for all users (recommended)* checked)
|
||||
1. Download archived release (https://github.com/shdwp/dcs_liberation/releases; **not source code zip**, file should be named **dcs_liberation_xx.zip**)
|
||||
1. Unzip the archive somewhere. Path does not matter. **Application will not work** if you start it without extracting
|
||||
1. Run **start.bat**
|
||||
1. If **"Windows protected your PC"** popup appears on your computer (windows blocks any application unknown to it), you can click on **"More info"** and **"Run anyway"**
|
||||
[](https://discord.gg/bKrtrkJ)
|
||||
|
||||
## Tutorials
|
||||
* [Manual](https://github.com/shdwp/dcs_liberation/wiki/Manual)
|
||||
[](https://github.com/Khopa/dcs_liberation)
|
||||
[](https://github.com/Khopa/dcs_liberation/issues)
|
||||

|
||||
|
||||
You should start with the manual, it covers everything you need to know before playing the campaign.
|
||||
## 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.
|
||||
|
||||
* [Strike objectives reference images](https://imgur.com/a/vCSHa9f)
|
||||

|
||||
|
||||
If you can't find the strike objective you can see here how it's supposed to look.
|
||||
## Downloads
|
||||
|
||||
* [Troubleshooting](https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting)
|
||||
Latest release is available here : https://github.com/Khopa/dcs_liberation/releases
|
||||
|
||||
You could also briefly check the troubleshooting page to get familiar with the known issues that you could probably fix by yourself.
|
||||
## Resources
|
||||
|
||||
* [Modding tutorial](https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial)
|
||||
Tutorials, contributors and developer's guides are available in the project's [Wiki](https://github.com/Khopa/dcs_liberation/wiki/)
|
||||
|
||||
Modding tutorial will cover how to change default loadouts, configure which planes are present in the campaign (or add new altogether) and more. Check this out if you find that something is not going for your liking, there could be a tutorial for changing that. Although be aware that it would require changing source files and could easily result in non functioning application.
|
||||
## Special Thanks
|
||||
|
||||
* [Development guide](https://github.com/shdwp/dcs_liberation/wiki/Development-guide)
|
||||
First, a big thanks to shdwp, for starting the original DCS Liberation project.
|
||||
|
||||
If you want to contribute to the project, this will give you a brief overview and on how to actually run it from source files.
|
||||
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 !
|
||||
|
||||
98
__init__.py
98
__init__.py
@@ -1,98 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import dcs
|
||||
import logging
|
||||
|
||||
import theater.caucasus
|
||||
import theater.persiangulf
|
||||
import theater.nevada
|
||||
|
||||
import ui.window
|
||||
import ui.mainmenu
|
||||
import ui.newgamemenu
|
||||
import ui.corruptedsavemenu
|
||||
|
||||
from game.game import Game
|
||||
from theater import start_generator
|
||||
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):
|
||||
new_game_menu = None # type: NewGameMenu
|
||||
|
||||
def start_new_game(player_name: str, enemy_name: str, terrain: str, sams: bool, midgame: bool, multiplier: float):
|
||||
if terrain == "persiangulf":
|
||||
conflicttheater = theater.persiangulf.PersianGulfTheater()
|
||||
elif terrain == "nevada":
|
||||
conflicttheater = theater.nevada.NevadaTheater()
|
||||
else:
|
||||
conflicttheater = theater.caucasus.CaucasusTheater()
|
||||
|
||||
if midgame:
|
||||
for i in range(0, int(len(conflicttheater.controlpoints) / 2)):
|
||||
conflicttheater.controlpoints[i].captured = True
|
||||
|
||||
start_generator.generate_inital_units(conflicttheater, enemy_name, sams, multiplier)
|
||||
start_generator.generate_groundobjects(conflicttheater)
|
||||
game = Game(player_name=player_name,
|
||||
enemy_name=enemy_name,
|
||||
theater=conflicttheater)
|
||||
game.budget = int(game.budget * multiplier)
|
||||
game.settings.multiplier = multiplier
|
||||
game.settings.sams = sams
|
||||
game.settings.version = VERSION_STRING
|
||||
|
||||
if midgame:
|
||||
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
|
||||
|
||||
proceed_to_main_menu(game)
|
||||
|
||||
new_game_menu = ui.newgamemenu.NewGameMenu(w, start_new_game)
|
||||
new_game_menu.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()
|
||||
|
||||
207
changelog.md
Normal file
207
changelog.md
Normal file
@@ -0,0 +1,207 @@
|
||||
# 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 :
|
||||
|
||||
* **[Units/Factions]** Added P-47D-30 for factions allies_1944
|
||||
* **[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 (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
|
||||
* **[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
|
||||
* **[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
|
||||
|
||||
* **[Mission Generator]** When playing as RED the activation trigger would not be properly generated
|
||||
* **[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]** Change power station template. (Buildings could end up superposed).
|
||||
|
||||
* **[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 :
|
||||
* **[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
|
||||
* **[Units/Factions]** Added support for newest WW2 Units
|
||||
* **[Campaign logic]** When a base is captured, refill the "base defenses" group with units for the new owner.
|
||||
* **[Mission Generator]** Carrier ICLS channel will now be configured (check your briefing)
|
||||
* **[Mission Generator]** SAM units will spawn on RED Alarm state
|
||||
* **[Mission Generator]** AI Flight planner now creates its own STRIKE flights
|
||||
* **[Mission Generator]** AI units assigned to Strike flight will now actually engage the buildings they have been assigned.
|
||||
* **[Mission Generator]** Added performance settings to allow disabling : smoke, artillery strike, moving units, infantry, SAM Red alert mode.
|
||||
* **[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 :
|
||||
* **[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)
|
||||
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', '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
|
||||
]
|
||||
1095
game/db.py
1095
game/db.py
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,2 @@
|
||||
from .event import *
|
||||
from .frontlineattack import *
|
||||
from .frontlinepatrol import *
|
||||
from .intercept import *
|
||||
from .baseattack import *
|
||||
from .navalintercept import *
|
||||
from .insurgentattack import *
|
||||
from .infantrytransport import *
|
||||
from .strike import *
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
from game.operation.baseattack import BaseAttackOperation
|
||||
|
||||
from .event import *
|
||||
from game.db import assigned_units_from
|
||||
|
||||
|
||||
class BaseAttackEvent(Event):
|
||||
silent = True
|
||||
BONUS_BASE = 15
|
||||
STRENGTH_RECOVERY = 0.55
|
||||
|
||||
def __str__(self):
|
||||
return "Base attack"
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
return [CAP, CAS, PinpointStrike]
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == CAP:
|
||||
return "Escort flight"
|
||||
elif for_task == CAS:
|
||||
return "CAS flight"
|
||||
elif for_task == PinpointStrike:
|
||||
return "Ground attack"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
attackers_success = alive_attackers >= alive_defenders
|
||||
if self.from_cp.captured:
|
||||
return attackers_success
|
||||
else:
|
||||
return not attackers_success
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(BaseAttackEvent, self).commit(debriefing)
|
||||
if self.is_successfull(debriefing):
|
||||
if self.from_cp.captured:
|
||||
self.to_cp.captured = True
|
||||
self.to_cp.ground_objects = []
|
||||
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
|
||||
|
||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||
else:
|
||||
if not self.from_cp.captured:
|
||||
self.to_cp.captured = False
|
||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||
|
||||
def skip(self):
|
||||
if not self.is_player_attacking and self.to_cp.captured:
|
||||
self.to_cp.captured = False
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and len(flights) == 1, "Invalid scrambled flights"
|
||||
|
||||
cas = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||
escort = self.from_cp.base.scramble_sweep(self.game.settings.multiplier)
|
||||
attackers = self.from_cp.base.armor
|
||||
|
||||
op = BaseAttackOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
op.setup(cas=assigned_units_from(cas),
|
||||
escort=assigned_units_from(escort),
|
||||
intercept=flights[CAP],
|
||||
attack=attackers,
|
||||
defense=self.to_cp.base.armor,
|
||||
aa=self.to_cp.base.aa)
|
||||
|
||||
self.operation = op
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and CAS in flights and PinpointStrike in flights and len(flights) == 3, "Invalid flights"
|
||||
|
||||
op = BaseAttackOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
|
||||
defenders.update(self.to_cp.base.scramble_cas(self.game.settings.multiplier))
|
||||
|
||||
op.setup(cas=flights[CAS],
|
||||
escort=flights[CAP],
|
||||
attack=unitdict_from(flights[PinpointStrike]),
|
||||
intercept=assigned_units_from(defenders),
|
||||
defense=self.to_cp.base.armor,
|
||||
aa=self.to_cp.base.assemble_aa())
|
||||
|
||||
self.operation = op
|
||||
|
||||
@@ -1,65 +1,89 @@
|
||||
import typing
|
||||
import logging
|
||||
|
||||
from dcs.action import Coalition
|
||||
from dcs.unittype import UnitType
|
||||
from dcs.task import *
|
||||
from dcs.vehicles import AirDefence
|
||||
from dcs.unittype import UnitType
|
||||
|
||||
from game import *
|
||||
from game.infos.information import Information
|
||||
from theater import *
|
||||
from gen.environmentgen import EnvironmentSettings
|
||||
from gen.conflictgen import Conflict
|
||||
from game.db import assigned_units_from, unitdict_from
|
||||
from theater.start_generator import generate_airbase_defense_group
|
||||
|
||||
from userdata.debriefing import Debriefing
|
||||
from userdata import persistency
|
||||
|
||||
DIFFICULTY_LOG_BASE = 1.1
|
||||
import game.db as db
|
||||
|
||||
DIFFICULTY_LOG_BASE = 1.1
|
||||
EVENT_DEPARTURE_MAX_DISTANCE = 340000
|
||||
|
||||
|
||||
MINOR_DEFEAT_INFLUENCE = 0.1
|
||||
DEFEAT_INFLUENCE = 0.3
|
||||
STRONG_DEFEAT_INFLUENCE = 0.5
|
||||
|
||||
class Event:
|
||||
silent = False
|
||||
informational = False
|
||||
is_awacs_enabled = False
|
||||
ca_slots = 0
|
||||
|
||||
game = None # type: Game
|
||||
location = None # type: Point
|
||||
from_cp = None # type: ControlPoint
|
||||
departure_cp = None # type: ControlPoint
|
||||
to_cp = None # type: ControlPoint
|
||||
|
||||
operation = None # type: Operation
|
||||
difficulty = 1 # type: int
|
||||
game = None # type: Game
|
||||
environment_settings = None # type: EnvironmentSettings
|
||||
BONUS_BASE = 5
|
||||
|
||||
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
||||
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str):
|
||||
self.game = game
|
||||
self.departure_cp = None
|
||||
self.from_cp = from_cp
|
||||
self.to_cp = target_cp
|
||||
self.location = location
|
||||
self.attacker_name = attacker_name
|
||||
self.defender_name = defender_name
|
||||
self.to_cp = to_cp
|
||||
self.from_cp = from_cp
|
||||
self.game = game
|
||||
|
||||
@property
|
||||
def is_player_attacking(self) -> bool:
|
||||
return self.attacker_name == self.game.player
|
||||
return self.attacker_name == self.game.player_name
|
||||
|
||||
@property
|
||||
def enemy_cp(self) -> ControlPoint:
|
||||
if self.attacker_name == self.game.player:
|
||||
if self.attacker_name == self.game.player_name:
|
||||
return self.to_cp
|
||||
else:
|
||||
return self.from_cp
|
||||
|
||||
@property
|
||||
def threat_description(self) -> str:
|
||||
return ""
|
||||
|
||||
def flight_name(self, for_task: typing.Type[typing.Type[Task]]) -> str:
|
||||
return "Flight"
|
||||
return self.departure_cp
|
||||
|
||||
@property
|
||||
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
return []
|
||||
|
||||
@property
|
||||
def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
return []
|
||||
def global_cp_available(self) -> bool:
|
||||
return False
|
||||
|
||||
def is_departure_available_from(self, cp: ControlPoint) -> bool:
|
||||
if not cp.captured:
|
||||
return False
|
||||
|
||||
if self.location.distance_to_point(cp.position) > EVENT_DEPARTURE_MAX_DISTANCE:
|
||||
return False
|
||||
|
||||
if cp.is_global and not self.global_cp_available:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def bonus(self) -> int:
|
||||
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
|
||||
@@ -67,11 +91,17 @@ class Event:
|
||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||
return self.operation.is_successfull(debriefing)
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert False
|
||||
def player_attacking(self, cp: ControlPoint, flights: db.TaskForceDict):
|
||||
if self.is_player_attacking:
|
||||
self.departure_cp = cp
|
||||
else:
|
||||
self.to_cp = cp
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
assert False
|
||||
def player_defending(self, cp: ControlPoint, flights: db.TaskForceDict):
|
||||
if self.is_player_attacking:
|
||||
self.departure_cp = cp
|
||||
else:
|
||||
self.to_cp = cp
|
||||
|
||||
def generate(self):
|
||||
self.operation.is_awacs_enabled = self.is_awacs_enabled
|
||||
@@ -82,51 +112,276 @@ class Event:
|
||||
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
|
||||
self.environment_settings = self.operation.environment_settings
|
||||
|
||||
def generate_quick(self):
|
||||
self.operation.is_awacs_enabled = self.is_awacs_enabled
|
||||
self.operation.environment_settings = self.environment_settings
|
||||
|
||||
self.operation.prepare(self.game.theater.terrain, is_quick=True)
|
||||
self.operation.generate()
|
||||
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz"))
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
for country, losses in debriefing.destroyed_units.items():
|
||||
if country == self.attacker_name:
|
||||
cp = self.from_cp
|
||||
else:
|
||||
cp = self.to_cp
|
||||
|
||||
logging.info("base {} commit losses {}".format(cp.base, losses))
|
||||
cp.base.commit_losses(losses)
|
||||
logging.info("Commiting mission results")
|
||||
|
||||
for object_identifier in debriefing.destroyed_objects:
|
||||
# ------------------------------
|
||||
# Destroyed aircrafts
|
||||
cp_map = {cp.id: cp for cp in self.game.theater.controlpoints}
|
||||
for destroyed_aircraft in debriefing.killed_aircrafts:
|
||||
try:
|
||||
cpid = int(destroyed_aircraft.split("|")[3])
|
||||
type = db.unit_type_from_name(destroyed_aircraft.split("|")[4])
|
||||
if cpid in cp_map.keys():
|
||||
cp = cp_map[cpid]
|
||||
if type in cp.base.aircraft.keys():
|
||||
logging.info("Aircraft destroyed : " + str(type))
|
||||
cp.base.aircraft[type] = max(0, cp.base.aircraft[type]-1)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# ------------------------------
|
||||
# Destroyed ground units
|
||||
killed_unit_count_by_cp = {cp.id: 0 for cp in self.game.theater.controlpoints}
|
||||
cp_map = {cp.id: cp for cp in self.game.theater.controlpoints}
|
||||
for killed_ground_unit in debriefing.killed_ground_units:
|
||||
try:
|
||||
cpid = int(killed_ground_unit.split("|")[3])
|
||||
type = db.unit_type_from_name(killed_ground_unit.split("|")[4])
|
||||
if cpid in cp_map.keys():
|
||||
killed_unit_count_by_cp[cpid] = killed_unit_count_by_cp[cpid] + 1
|
||||
cp = cp_map[cpid]
|
||||
if type in cp.base.armor.keys():
|
||||
logging.info("Ground unit destroyed : " + str(type))
|
||||
cp.base.armor[type] = max(0, cp.base.armor[type] - 1)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# ------------------------------
|
||||
# Static ground objects
|
||||
for destroyed_ground_unit_name in debriefing.killed_ground_units:
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if not cp.ground_objects:
|
||||
continue
|
||||
|
||||
# -- Static ground objects
|
||||
for i, ground_object in enumerate(cp.ground_objects):
|
||||
if ground_object.is_dead:
|
||||
continue
|
||||
|
||||
if ground_object.matches_string_identifier(object_identifier):
|
||||
if ground_object.matches_string_identifier(destroyed_ground_unit_name):
|
||||
logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
|
||||
cp.ground_objects[i].is_dead = True
|
||||
|
||||
info = Information("Building destroyed",
|
||||
ground_object.dcs_identifier + " has been destroyed at location " + ground_object.obj_name,
|
||||
self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
|
||||
|
||||
# -- AA Site groups
|
||||
destroyed_units = 0
|
||||
info = Information("Units destroyed at " + ground_object.obj_name,
|
||||
"",
|
||||
self.game.turn)
|
||||
for i, ground_object in enumerate(cp.ground_objects):
|
||||
if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
||||
for g in ground_object.groups:
|
||||
for u in g.units:
|
||||
if u.name == destroyed_ground_unit_name:
|
||||
g.units.remove(u)
|
||||
destroyed_units = destroyed_units + 1
|
||||
info.text = u.type
|
||||
ucount = sum([len(g.units) for g in ground_object.groups])
|
||||
if ucount == 0:
|
||||
ground_object.is_dead = True
|
||||
if destroyed_units > 0:
|
||||
self.game.informations.append(info)
|
||||
|
||||
# ------------------------------
|
||||
# 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
|
||||
|
||||
for captured in debriefing.base_capture_events:
|
||||
try:
|
||||
id = int(captured.split("||")[0])
|
||||
new_owner_coalition = int(captured.split("||")[1])
|
||||
|
||||
captured_cps = []
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.id == id:
|
||||
|
||||
if cp.captured and new_owner_coalition != coalition:
|
||||
cp.captured = False
|
||||
info = Information(cp.name + " lost !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
pname = self.game.enemy_name
|
||||
captured_cps.append(cp)
|
||||
elif not(cp.captured) and new_owner_coalition == coalition:
|
||||
cp.captured = True
|
||||
info = Information(cp.name + " captured !", "We took control of " + cp.name + "! Great job !", self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
pname = self.game.player_name
|
||||
captured_cps.append(cp)
|
||||
else:
|
||||
continue
|
||||
|
||||
cp.base.aircraft = {}
|
||||
cp.base.armor = {}
|
||||
|
||||
airbase_def_id = 0
|
||||
for g in cp.ground_objects:
|
||||
g.groups = []
|
||||
if g.airbase_group and pname != "":
|
||||
generate_airbase_defense_group(airbase_def_id, g, pname, self.game, cp)
|
||||
airbase_def_id = airbase_def_id + 1
|
||||
|
||||
for cp in captured_cps:
|
||||
logging.info("Will run redeploy for " + cp.name)
|
||||
self.redeploy_units(cp)
|
||||
|
||||
|
||||
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():
|
||||
enemy_cps = [e for e in cp.connected_points if not e.captured]
|
||||
for enemy_cp in enemy_cps:
|
||||
print("Compute frontline progression for : " + cp.name + " to " + enemy_cp.name)
|
||||
|
||||
delta = 0
|
||||
player_won = True
|
||||
ally_casualties = killed_unit_count_by_cp[cp.id]
|
||||
enemy_casualties = killed_unit_count_by_cp[enemy_cp.id]
|
||||
ally_units_alive = cp.base.total_armor
|
||||
enemy_units_alive = enemy_cp.base.total_armor
|
||||
|
||||
print(ally_units_alive)
|
||||
print(enemy_units_alive)
|
||||
print(ally_casualties)
|
||||
print(enemy_casualties)
|
||||
|
||||
ratio = (1.0 + enemy_casualties) / (1.0 + ally_casualties)
|
||||
|
||||
player_aggresive = cp.stances[enemy_cp.id] in [CombatStance.AGGRESIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]
|
||||
|
||||
if ally_units_alive == 0:
|
||||
player_won = False
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
elif enemy_units_alive == 0:
|
||||
player_won = True
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
elif cp.stances[enemy_cp.id] == CombatStance.RETREAT:
|
||||
player_won = False
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
else:
|
||||
if enemy_casualties > ally_casualties:
|
||||
player_won = True
|
||||
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
else:
|
||||
if ratio > 3:
|
||||
delta = STRONG_DEFEAT_INFLUENCE
|
||||
elif ratio < 1.5:
|
||||
delta = MINOR_DEFEAT_INFLUENCE
|
||||
else:
|
||||
delta = DEFEAT_INFLUENCE
|
||||
elif ally_casualties > enemy_casualties:
|
||||
|
||||
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:
|
||||
# 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]:
|
||||
print("Defensive stance, progress is limited")
|
||||
delta = MINOR_DEFEAT_INFLUENCE
|
||||
|
||||
if player_won:
|
||||
print(cp.name + " won ! factor > " + str(delta))
|
||||
cp.base.affect_strength(delta)
|
||||
enemy_cp.base.affect_strength(-delta)
|
||||
info = Information("Frontline Report",
|
||||
"Our ground forces from " + cp.name + " are making progress toward " + enemy_cp.name,
|
||||
self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
else:
|
||||
print(cp.name + " lost ! factor > " + str(delta))
|
||||
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,
|
||||
self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
|
||||
def skip(self):
|
||||
pass
|
||||
|
||||
def redeploy_units(self, cp):
|
||||
""""
|
||||
Auto redeploy units to newly captured base
|
||||
"""
|
||||
|
||||
ally_connected_cps = [ocp for ocp in cp.connected_points if cp.captured == ocp.captured]
|
||||
enemy_connected_cps = [ocp for ocp in cp.connected_points if cp.captured != ocp.captured]
|
||||
|
||||
# If the newly captured cp does not have enemy connected cp,
|
||||
# then it is not necessary to redeploy frontline units there.
|
||||
if len(enemy_connected_cps) == 0:
|
||||
return
|
||||
else:
|
||||
# From each ally cp, send reinforcements
|
||||
for ally_cp in ally_connected_cps:
|
||||
total_units_redeployed = 0
|
||||
own_enemy_cp = [ocp for ocp in ally_cp.connected_points if ally_cp.captured != ocp.captured]
|
||||
|
||||
moved_units = {}
|
||||
|
||||
# If the connected base, does not have any more enemy cp connected.
|
||||
# Or if it is not the opponent redeploying forces there (enemy AI will never redeploy all their forces at once)
|
||||
if len(own_enemy_cp) > 0 or not cp.captured:
|
||||
for frontline_unit, count in ally_cp.base.armor.items():
|
||||
moved_units[frontline_unit] = int(count/2)
|
||||
total_units_redeployed = total_units_redeployed + int(count/2)
|
||||
else: # So if the old base, does not have any more enemy cp connected, or if it is an enemy base
|
||||
for frontline_unit, count in ally_cp.base.armor.items():
|
||||
moved_units[frontline_unit] = count
|
||||
total_units_redeployed = total_units_redeployed + count
|
||||
|
||||
cp.base.commision_units(moved_units)
|
||||
ally_cp.base.commit_losses(moved_units)
|
||||
|
||||
if total_units_redeployed > 0:
|
||||
info = Information("Units redeployed", "", self.game.turn)
|
||||
info.text = str(total_units_redeployed) + " units have been redeployed from " + ally_cp.name + " to " + cp.name
|
||||
self.game.informations.append(info)
|
||||
logging.info(info.text)
|
||||
|
||||
|
||||
|
||||
class UnitsDeliveryEvent(Event):
|
||||
informational = True
|
||||
units = None # type: typing.Dict[UnitType, int]
|
||||
|
||||
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
||||
super(UnitsDeliveryEvent, self).__init__(attacker_name=attacker_name,
|
||||
defender_name=defender_name,
|
||||
super(UnitsDeliveryEvent, self).__init__(game=game,
|
||||
location=to_cp.position,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
game=game)
|
||||
target_cp=to_cp,
|
||||
attacker_name=attacker_name,
|
||||
defender_name=defender_name)
|
||||
|
||||
self.units = {}
|
||||
|
||||
@@ -138,4 +393,9 @@ class UnitsDeliveryEvent(Event):
|
||||
self.units[k] = self.units.get(k, 0) + v
|
||||
|
||||
def skip(self):
|
||||
|
||||
for k, v in self.units.items():
|
||||
info = Information("Ally Reinforcement", str(k.id) + " x " + str(v) + " at " + self.to_cp.name, self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
|
||||
self.to_cp.base.commision_units(self.units)
|
||||
|
||||
@@ -4,41 +4,23 @@ 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
|
||||
|
||||
defenders = None # type: db.ArmorDict
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
return "{} vehicles".format(self.to_cp.base.assemble_count())
|
||||
|
||||
@property
|
||||
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
if self.is_player_attacking:
|
||||
return [CAS, PinpointStrike]
|
||||
return [CAS, CAP]
|
||||
else:
|
||||
return [CAP, PinpointStrike]
|
||||
return [CAP]
|
||||
|
||||
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"
|
||||
@property
|
||||
def global_cp_available(self) -> bool:
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
return "Frontline attack"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].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:
|
||||
@@ -47,36 +29,25 @@ class FrontlineAttackEvent(Event):
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(FrontlineAttackEvent, self).commit(debriefing)
|
||||
|
||||
if self.from_cp.captured:
|
||||
if self.is_successfull(debriefing):
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
if self.is_successfull(debriefing):
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def skip(self):
|
||||
if self.to_cp.captured:
|
||||
self.to_cp.base.affect_strength(-0.1)
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert CAS in flights and PinpointStrike in flights and len(flights) == 2, "Invalid flights"
|
||||
|
||||
self.defenders = self.to_cp.base.assemble_attack()
|
||||
|
||||
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)
|
||||
self.operation = op
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
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)
|
||||
|
||||
armor = unitdict_from(flights[PinpointStrike])
|
||||
op.setup(target=self.defenders,
|
||||
attackers=db.unitdict_restrict_count(armor, sum(self.defenders.values())),
|
||||
strikegroup=flights[CAS])
|
||||
|
||||
self.operation = op
|
||||
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
from game.event import *
|
||||
from game.operation.frontlinepatrol import FrontlinePatrolOperation
|
||||
from userdata.debriefing import Debriefing
|
||||
|
||||
|
||||
class FrontlinePatrolEvent(Event):
|
||||
ESCORT_FACTOR = 0.5
|
||||
STRENGTH_INFLUENCE = 0.3
|
||||
SUCCESS_FACTOR = 0.8
|
||||
|
||||
cas = None # type: db.PlaneDict
|
||||
escort = None # type: db.PlaneDict
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
return "{} aircraft + ? CAS".format(self.to_cp.base.scramble_count(self.game.settings.multiplier * self.ESCORT_FACTOR, CAP))
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
return [CAP, PinpointStrike]
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == CAP:
|
||||
return "CAP flight"
|
||||
elif for_task == PinpointStrike:
|
||||
return "Ground attack"
|
||||
|
||||
def __str__(self):
|
||||
return "Frontline CAP"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) >= self.SUCCESS_FACTOR
|
||||
if self.from_cp.captured:
|
||||
return attackers_success
|
||||
else:
|
||||
return not attackers_success
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(FrontlinePatrolEvent, self).commit(debriefing)
|
||||
|
||||
if self.from_cp.captured:
|
||||
if self.is_successfull(debriefing):
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
if self.is_successfull(debriefing):
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def skip(self):
|
||||
pass
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and PinpointStrike in flights and len(flights) == 2, "Invalid flights"
|
||||
|
||||
self.cas = self.to_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||
self.escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier * self.ESCORT_FACTOR)
|
||||
|
||||
op = FrontlinePatrolOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
defenders = self.to_cp.base.assemble_attack()
|
||||
op.setup(cas=assigned_units_from(self.cas),
|
||||
escort=assigned_units_from(self.escort),
|
||||
interceptors=flights[CAP],
|
||||
armor_attackers=db.unitdict_restrict_count(db.unitdict_from(flights[PinpointStrike]), sum(defenders.values())),
|
||||
armor_defenders=defenders)
|
||||
|
||||
self.operation = op
|
||||
@@ -1,55 +0,0 @@
|
||||
import math
|
||||
import random
|
||||
|
||||
from dcs.task import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from game import db
|
||||
from game.operation.infantrytransport import InfantryTransportOperation
|
||||
from theater.conflicttheater import *
|
||||
from userdata.debriefing import Debriefing
|
||||
|
||||
from .event import *
|
||||
|
||||
|
||||
class InfantryTransportEvent(Event):
|
||||
STRENGTH_INFLUENCE = 0.3
|
||||
|
||||
def __str__(self):
|
||||
return "Frontline transport troops"
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
return [Embarking]
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == Embarking:
|
||||
return "Transport flight"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
return True
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(InfantryTransportEvent, self).commit(debriefing)
|
||||
|
||||
if self.is_successfull(debriefing):
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert Embarking in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
op = InfantryTransportOperation(
|
||||
game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp
|
||||
)
|
||||
|
||||
air_defense = db.find_unittype(AirDefence, self.defender_name)[0]
|
||||
op.setup(transport=flights[Embarking],
|
||||
aa={air_defense: 2})
|
||||
|
||||
self.operation = op
|
||||
@@ -1,64 +0,0 @@
|
||||
import math
|
||||
import random
|
||||
|
||||
from dcs.task import *
|
||||
|
||||
from game import *
|
||||
from game.event import *
|
||||
from game.event.frontlineattack import FrontlineAttackEvent
|
||||
from game.operation.insurgentattack import InsurgentAttackOperation
|
||||
|
||||
from .event import *
|
||||
|
||||
|
||||
class InsurgentAttackEvent(Event):
|
||||
SUCCESS_FACTOR = 0.7
|
||||
TARGET_VARIETY = 2
|
||||
TARGET_AMOUNT_FACTOR = 0.5
|
||||
STRENGTH_INFLUENCE = 0.1
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
return ""
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
return [CAS]
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == CAS:
|
||||
return "Ground intercept flight"
|
||||
|
||||
def __str__(self):
|
||||
return "Destroy insurgents"
|
||||
|
||||
def skip(self):
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
killed_units = sum([v for k, v in debriefing.destroyed_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
all_units = sum(self.targets.values())
|
||||
attackers_success = (float(killed_units) / (all_units + 0.01)) > self.SUCCESS_FACTOR
|
||||
if self.from_cp.captured:
|
||||
return attackers_success
|
||||
else:
|
||||
return not attackers_success
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
suitable_unittypes = db.find_unittype(Reconnaissance, self.attacker_name)
|
||||
random.shuffle(suitable_unittypes)
|
||||
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
|
||||
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
|
||||
self.targets = {unittype: typecount for unittype in unittypes}
|
||||
|
||||
op = InsurgentAttackOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp)
|
||||
op.setup(target=self.targets,
|
||||
strikegroup=flights[CAS])
|
||||
|
||||
self.operation = op
|
||||
@@ -1,105 +0,0 @@
|
||||
from game.operation.intercept import InterceptOperation
|
||||
|
||||
from .event import *
|
||||
|
||||
|
||||
class InterceptEvent(Event):
|
||||
STRENGTH_INFLUENCE = 0.3
|
||||
GLOBAL_STRENGTH_INFLUENCE = 0.3
|
||||
AIRDEFENSE_COUNT = 3
|
||||
|
||||
transport_unit = None # type: FlyingType
|
||||
|
||||
def __str__(self):
|
||||
return "Air Intercept"
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
return [CAP]
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == CAP:
|
||||
if self.is_player_attacking:
|
||||
return "Intercept flight"
|
||||
else:
|
||||
return "Escort flight"
|
||||
|
||||
def _enemy_scramble_multiplier(self) -> float:
|
||||
is_global = self.from_cp.is_global or self.to_cp.is_global
|
||||
return self.game.settings.multiplier * is_global and 0.5 or 1
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
return "{} aircraft".format(self.enemy_cp.base.scramble_count(self._enemy_scramble_multiplier(), CAP))
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
units_destroyed = debriefing.destroyed_units[self.defender_name].get(self.transport_unit, 0)
|
||||
if self.from_cp.captured:
|
||||
return units_destroyed > 0
|
||||
else:
|
||||
return units_destroyed == 0
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(InterceptEvent, self).commit(debriefing)
|
||||
|
||||
if self.attacker_name == self.game.player:
|
||||
if self.is_successfull(debriefing):
|
||||
for _, cp in self.game.theater.conflicts(True):
|
||||
cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
# enemy attacking
|
||||
if self.is_successfull(debriefing):
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def skip(self):
|
||||
if self.to_cp.captured:
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
escort = self.to_cp.base.scramble_sweep(self._enemy_scramble_multiplier())
|
||||
|
||||
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
|
||||
assert self.transport_unit is not None
|
||||
|
||||
airdefense_unit = db.find_unittype(AirDefence, self.defender_name)[-1]
|
||||
op = InterceptOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
op.setup(escort=assigned_units_from(escort),
|
||||
transport={self.transport_unit: 1},
|
||||
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
|
||||
interceptors=flights[CAP])
|
||||
|
||||
self.operation = op
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
interceptors = self.from_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
||||
|
||||
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
|
||||
assert self.transport_unit is not None
|
||||
|
||||
op = InterceptOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
op.setup(escort=flights[CAP],
|
||||
transport={self.transport_unit: 1},
|
||||
interceptors=assigned_units_from(interceptors),
|
||||
airdefense={})
|
||||
|
||||
self.operation = op
|
||||
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
from game.operation.navalintercept import NavalInterceptionOperation
|
||||
|
||||
from .event import *
|
||||
|
||||
|
||||
class NavalInterceptEvent(Event):
|
||||
STRENGTH_INFLUENCE = 0.3
|
||||
SUCCESS_RATE = 0.5
|
||||
|
||||
targets = None # type: db.ShipDict
|
||||
|
||||
def _targets_count(self) -> int:
|
||||
from gen.conflictgen import IMPORTANCE_LOW
|
||||
factor = (self.to_cp.importance - IMPORTANCE_LOW + 0.1) * 20
|
||||
return max(int(factor), 1)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "Naval intercept"
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
if self.is_player_attacking:
|
||||
return [CAS]
|
||||
else:
|
||||
return [CAP]
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == CAS:
|
||||
return "Naval intercept flight"
|
||||
elif for_task == CAP:
|
||||
return "CAP flight"
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
s = "{} ship(s)".format(self._targets_count())
|
||||
if not self.from_cp.captured:
|
||||
s += ", {} aircraft".format(self.from_cp.base.scramble_count(self.game.settings.multiplier))
|
||||
return s
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
total_targets = sum(self.targets.values())
|
||||
destroyed_targets = 0
|
||||
for unit, count in debriefing.destroyed_units[self.defender_name].items():
|
||||
if unit in self.targets:
|
||||
destroyed_targets += count
|
||||
|
||||
if self.from_cp.captured:
|
||||
return math.ceil(float(destroyed_targets) / total_targets) > self.SUCCESS_RATE
|
||||
else:
|
||||
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_RATE
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(NavalInterceptEvent, self).commit(debriefing)
|
||||
|
||||
if self.attacker_name == self.game.player:
|
||||
if self.is_successfull(debriefing):
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
# enemy attacking
|
||||
if self.is_successfull(debriefing):
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def skip(self):
|
||||
if self.to_cp.captured:
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
self.targets = {
|
||||
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
|
||||
}
|
||||
|
||||
op = NavalInterceptionOperation(
|
||||
self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp
|
||||
)
|
||||
|
||||
op.setup(strikegroup=flights[CAS],
|
||||
interceptors={},
|
||||
targets=self.targets)
|
||||
|
||||
self.operation = op
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
self.targets = {
|
||||
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
|
||||
}
|
||||
|
||||
op = NavalInterceptionOperation(
|
||||
self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp
|
||||
)
|
||||
|
||||
strikegroup = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||
op.setup(strikegroup=assigned_units_from(strikegroup),
|
||||
interceptors=flights[CAP],
|
||||
targets=self.targets)
|
||||
|
||||
self.operation = op
|
||||
@@ -1,61 +0,0 @@
|
||||
from game.operation.strike import StrikeOperation
|
||||
|
||||
from .event import *
|
||||
|
||||
|
||||
class StrikeEvent(Event):
|
||||
STRENGTH_INFLUENCE = 0.0
|
||||
SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.05
|
||||
|
||||
def __str__(self):
|
||||
return "Strike"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
return True
|
||||
|
||||
@property
|
||||
def threat_description(self):
|
||||
return "{} aircraft + AA".format(self.to_cp.base.scramble_count(self.game.settings.multiplier, CAP))
|
||||
|
||||
@property
|
||||
def tasks(self):
|
||||
if self.is_player_attacking:
|
||||
return [CAP, CAS]
|
||||
else:
|
||||
return [CAP]
|
||||
|
||||
@property
|
||||
def ai_banned_tasks(self):
|
||||
return [CAS]
|
||||
|
||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||
if for_task == CAP:
|
||||
if self.is_player_attacking:
|
||||
return "Escort flight"
|
||||
else:
|
||||
return "CAP flight"
|
||||
elif for_task == CAS:
|
||||
return "Strike flight"
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
super(StrikeEvent, self).commit(debriefing)
|
||||
|
||||
self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects))
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and CAS in flights and len(flights) == 2, "Invalid flights"
|
||||
|
||||
op = StrikeOperation(
|
||||
self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp
|
||||
)
|
||||
|
||||
interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
||||
op.setup(strikegroup=flights[CAS],
|
||||
escort=flights[CAP],
|
||||
interceptors=assigned_units_from(interceptors))
|
||||
|
||||
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": "USA",
|
||||
"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": "USA",
|
||||
"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
|
||||
}
|
||||
82
game/factions/bluefor_modern.py
Normal file
82
game/factions/bluefor_modern.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
BLUEFOR_MODERN = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
|
||||
F_15C,
|
||||
F_14B,
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
JF_17,
|
||||
M_2000C,
|
||||
F_5E_3,
|
||||
Su_27,
|
||||
|
||||
Su_25T,
|
||||
A_10A,
|
||||
A_10C,
|
||||
AV8BNA,
|
||||
AJS37,
|
||||
|
||||
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.IFV_Marder,
|
||||
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"
|
||||
], "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
|
||||
}
|
||||
80
game/factions/china_2010.py
Normal file
80
game/factions/china_2010.py
Normal file
@@ -0,0 +1,80 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
China_2010 = {
|
||||
"country": "China",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis, # Standing as J-7
|
||||
Su_30,
|
||||
Su_33,
|
||||
J_11A,
|
||||
JF_17,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
A_50,
|
||||
|
||||
Mi_8MT,
|
||||
Mi_28N,
|
||||
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C, # Standing as HQ-9+
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
|
||||
Armor.ZTZ_96B,
|
||||
Armor.MBT_T_55,
|
||||
Armor.ZBD_04A,
|
||||
Armor.IFV_BMP_1,
|
||||
Artillery.MLRS_9A52_Smerch,
|
||||
Artillery.SPH_2S9_Nona,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka,
|
||||
AirDefence.Rapier_FSA_Launcher, # Standing as PL-9C Shorad
|
||||
AirDefence.HQ_7_Self_Propelled_LN
|
||||
], "aircraft_carrier": [
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
], "destroyer": [
|
||||
Type_052B_Destroyer,
|
||||
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
|
||||
}
|
||||
47
game/factions/france_1995.py
Normal file
47
game/factions/france_1995.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
France_1995 = {
|
||||
"country": "France",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
M_2000C,
|
||||
Mirage_2000_5,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
SA342M,
|
||||
SA342L,
|
||||
SA342Mistral,
|
||||
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.TPz_Fuchs, # Standing as VAB
|
||||
Armor.APC_Cobra, # Standing as VBL
|
||||
Armor.ATGM_M1134_Stryker, # Standing as VAB Mephisto
|
||||
Artillery.SPH_M109_Paladin, # Standing as AMX30 AuF1
|
||||
Artillery.MLRS_M270,
|
||||
|
||||
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
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
62
game/factions/france_2005.py
Normal file
62
game/factions/france_2005.py
Normal file
@@ -0,0 +1,62 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
France_2005 = {
|
||||
"country": "France",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
M_2000C,
|
||||
Mirage_2000_5,
|
||||
FA_18C_hornet, # Standing as Rafale M
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
SA342M,
|
||||
SA342L,
|
||||
SA342Mistral,
|
||||
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.TPz_Fuchs, # Standing as VAB
|
||||
Armor.APC_Cobra, # Standing as VBL
|
||||
Armor.ATGM_M1134_Stryker, # Standing as VAB Mephisto
|
||||
Artillery.SPH_M109_Paladin, # Standing as AMX30 AuF1
|
||||
Artillery.MLRS_M270,
|
||||
|
||||
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"
|
||||
], "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
|
||||
}
|
||||
47
game/factions/germany_1944.py
Normal file
47
game/factions/germany_1944.py
Normal file
@@ -0,0 +1,47 @@
|
||||
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",
|
||||
"units": [
|
||||
|
||||
FW_190A8,
|
||||
FW_190D9,
|
||||
Bf_109K_4,
|
||||
Ju_88A4,
|
||||
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.MT_Pz_Kpfw_IV_Ausf_H,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I,
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II,
|
||||
Armor.APC_Sd_Kfz_251,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.Sd_Kfz_184_Elefant,
|
||||
Armor.TD_Jagdpanther_G1,
|
||||
Armor.TD_Jagdpanzer_IV,
|
||||
|
||||
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": 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
|
||||
}
|
||||
45
game/factions/germany_1990.py
Normal file
45
game/factions/germany_1990.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Germany_1990 = {
|
||||
"country": "Germany",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
MiG_29G,
|
||||
Tornado_IDS,
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
SA342M,
|
||||
SA342L,
|
||||
|
||||
Armor.TPz_Fuchs,
|
||||
Armor.MBT_Leopard_1A3,
|
||||
Armor.MBT_Leopard_2,
|
||||
Armor.IFV_Marder,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Roland_ADS,
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.SPAAA_Gepard,
|
||||
AirDefence.SAM_Roland_ADS,
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
]
|
||||
}
|
||||
55
game/factions/india_2010.py
Normal file
55
game/factions/india_2010.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
India_2010 = {
|
||||
"country": "India",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
Mirage_2000_5,
|
||||
M_2000C,
|
||||
MiG_27K,
|
||||
MiG_21Bis,
|
||||
MiG_29S,
|
||||
Su_30,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
AH_64A,
|
||||
Mi_8MT,
|
||||
|
||||
Armor.MBT_T_90,
|
||||
Armor.MBT_T_72B,
|
||||
Armor.IFV_BMP_2,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka,
|
||||
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6
|
||||
], "aircraft_carrier": [
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
], "destroyer": [
|
||||
FSG_1241_1MP_Molniya,
|
||||
], "carrier_names": [
|
||||
"INS Vikramaditya"
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator", "MolniyaGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
27
game/factions/insurgent.py
Normal file
27
game/factions/insurgent.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
|
||||
Insurgent = {
|
||||
"country": "Insurgents",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
AirDefence.AAA_ZU_23_Insurgent_Closed,
|
||||
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.Infantry_Soldier_Insurgents,
|
||||
Infantry.Soldier_RPG,
|
||||
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
]
|
||||
}
|
||||
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",
|
||||
}
|
||||
}
|
||||
58
game/factions/iran_2015.py
Normal file
58
game/factions/iran_2015.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Iran_2015 = {
|
||||
"country": "Iran",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_29A,
|
||||
F_4E,
|
||||
F_14B,
|
||||
F_5E_3,
|
||||
|
||||
MiG_21Bis,
|
||||
Su_24M,
|
||||
Su_25,
|
||||
Su_17M4,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
A_50,
|
||||
|
||||
Mi_28N,
|
||||
Mi_24V,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
|
||||
|
||||
Armor.APC_M113,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_T_72B,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
],
|
||||
"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
|
||||
}
|
||||
44
game/factions/israel_2000.py
Normal file
44
game/factions/israel_2000.py
Normal file
@@ -0,0 +1,44 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Israel_2000 = {
|
||||
"country": "Israel",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
F_16C_50,
|
||||
F_15C,
|
||||
F_15E,
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
AH_1W,
|
||||
AH_64D,
|
||||
|
||||
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,
|
||||
|
||||
AirDefence.SAM_Patriot_EPP_III,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
], "boat": [
|
||||
"ArleighBurkeGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
48
game/factions/italy_1990.py
Normal file
48
game/factions/italy_1990.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Italy_1990 = {
|
||||
"country": "Italy",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
Tornado_IDS,
|
||||
AV8BNA,
|
||||
# MB339,
|
||||
|
||||
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"
|
||||
], "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
|
||||
}
|
||||
49
game/factions/libya_2011.py
Normal file
49
game/factions/libya_2011.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Libya_2011 = {
|
||||
"country": "Libya",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
Su_24M,
|
||||
Su_17M4,
|
||||
Mi_24V,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
A_50,
|
||||
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.ARV_BRDM_2,
|
||||
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.Infantry_Soldier_Insurgents
|
||||
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.SAM_SA_8_Osa_9A33,
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
38
game/factions/netherlands_1990.py
Normal file
38
game/factions/netherlands_1990.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Netherlands_1990 = {
|
||||
"country": "The Netherlands",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
F_16C_50,
|
||||
F_5E_3,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
AH_64A,
|
||||
|
||||
Armor.APC_M113,
|
||||
Armor.MBT_Leopard_1A3,
|
||||
|
||||
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
|
||||
], "boat": [
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
53
game/factions/north_korea_2000.py
Normal file
53
game/factions/north_korea_2000.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
NorthKorea_2000 = {
|
||||
"country": "North Korea",
|
||||
"side": "red",
|
||||
"units":[
|
||||
MiG_29A,
|
||||
Su_25,
|
||||
MiG_15bis,
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
MiG_19P,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
A_50,
|
||||
|
||||
Mi_8MT,
|
||||
Mi_24V,
|
||||
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_72B,
|
||||
Armor.MBT_T_80U,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.ARV_BRDM_2,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
],
|
||||
"boat": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
42
game/factions/pakistan_2015.py
Normal file
42
game/factions/pakistan_2015.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Pakistan_2015 = {
|
||||
"country": "Pakistan",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
JF_17,
|
||||
F_16C_50,
|
||||
MiG_21Bis, # Standing as J-7
|
||||
MiG_19P, # Standing as J-6
|
||||
IL_78M,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
AH_1W,
|
||||
|
||||
Armor.MBT_T_80U,
|
||||
Armor.MBT_T_55, # Standing as Al-Zarrar / Type 59 MBT
|
||||
Armor.ZBD_04A,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_SA_2_LN_SM_90, # Standing as HQ-2
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C, # Standing as HQ-9
|
||||
|
||||
Armed_speedboat,
|
||||
], "shorad": [
|
||||
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
|
||||
}
|
||||
|
||||
38
game/factions/russia_1955.py
Normal file
38
game/factions/russia_1955.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from dcs.planes import MiG_15bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40
|
||||
from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160
|
||||
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
|
||||
|
||||
Russia_1955 = {
|
||||
"country": "Russia",
|
||||
"side": "red",
|
||||
"units": [
|
||||
MiG_15bis,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.FDDM_Grad,
|
||||
Armor.APC_MTLB,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
]
|
||||
}
|
||||
55
game/factions/russia_1965.py
Normal file
55
game/factions/russia_1965.py
Normal file
@@ -0,0 +1,55 @@
|
||||
from dcs.helicopters import Mi_8MT
|
||||
from dcs.planes import MiG_15bis, MiG_19P, MiG_21Bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40, A_50
|
||||
from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160
|
||||
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
|
||||
|
||||
Russia_1965 = {
|
||||
"country": "Russia",
|
||||
"side": "red",
|
||||
"units": [
|
||||
MiG_15bis,
|
||||
MiG_19P,
|
||||
MiG_21Bis,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
A_50,
|
||||
|
||||
Mi_8MT,
|
||||
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.SAM_SA_2_LN_SM_90,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.ARV_BTR_RD,
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.AAA_ZU_23_Closed
|
||||
], "boat": [
|
||||
"GrishaGroupGenerator"
|
||||
]
|
||||
}
|
||||
71
game/factions/russia_1975.py
Normal file
71
game/factions/russia_1975.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from dcs.helicopters import Mi_8MT, Mi_24V
|
||||
from dcs.planes import MiG_21Bis, MiG_23MLD, MiG_25PD, MiG_29A, Su_17M4, Su_24M, Su_25, IL_76MD, IL_78M, An_26B, An_30M, \
|
||||
Yak_40, A_50
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
|
||||
|
||||
Russia_1975 = {
|
||||
"country": "Russia",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
MiG_25PD,
|
||||
MiG_29A,
|
||||
|
||||
Su_17M4,
|
||||
Su_24M,
|
||||
Su_25,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
A_50,
|
||||
|
||||
Mi_8MT,
|
||||
Mi_24V,
|
||||
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.MBT_T_55,
|
||||
|
||||
Artillery.SPH_2S9_Nona,
|
||||
Artillery.SPH_2S1_Gvozdika,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
], "aircraft_carrier": [
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
], "destroyer": [
|
||||
FF_1135M_Rezky,
|
||||
], "cruiser": [
|
||||
CGN_1144_2_Pyotr_Velikiy,
|
||||
], "boat": [
|
||||
"RussianNavyGroupGenerator", "KiloSubGroupGenerator", "MolniyaGroupGenerator"
|
||||
]
|
||||
}
|
||||
74
game/factions/russia_1990.py
Normal file
74
game/factions/russia_1990.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Russia_1990 = {
|
||||
"country": "Russia",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_23MLD,
|
||||
MiG_25PD,
|
||||
MiG_29A,
|
||||
MiG_29S,
|
||||
MiG_31,
|
||||
Su_27,
|
||||
|
||||
Su_24M,
|
||||
Su_25,
|
||||
Ka_50,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
|
||||
A_50,
|
||||
|
||||
Mi_8MT,
|
||||
Mi_24V,
|
||||
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_9K57_Uragan_BM_27,
|
||||
Artillery.SPH_2S19_Msta,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
],
|
||||
"shorad":[
|
||||
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"
|
||||
]
|
||||
}
|
||||
77
game/factions/russia_2010.py
Normal file
77
game/factions/russia_2010.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Russia_2010 = {
|
||||
"country": "Russia",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
Su_27,
|
||||
Su_30,
|
||||
Su_33,
|
||||
MiG_29S,
|
||||
MiG_31,
|
||||
|
||||
Su_25,
|
||||
Su_25T,
|
||||
Su_34,
|
||||
Su_24M,
|
||||
L_39ZA,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
A_50,
|
||||
|
||||
Ka_50,
|
||||
Mi_8MT,
|
||||
Mi_24V,
|
||||
Mi_28N,
|
||||
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6,
|
||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
|
||||
|
||||
Armor.APC_BTR_80,
|
||||
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,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6,
|
||||
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"
|
||||
]
|
||||
}
|
||||
50
game/factions/spain_1990.py
Normal file
50
game/factions/spain_1990.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Spain_1990 = {
|
||||
"country": "Spain",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
FA_18C_hornet,
|
||||
AV8BNA,
|
||||
F_5E_3,
|
||||
C_101CC,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_Leopard_2,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad":[
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
], "aircraft_carrier": [
|
||||
CVN_74_John_C__Stennis, # Standing as Principe de Asturias
|
||||
], "helicopter_carrier": [
|
||||
LHA_1_Tarawa, # Standing as Juan Carlos
|
||||
], "destroyer": [
|
||||
Oliver_Hazzard_Perry_class,
|
||||
], "cruiser": [
|
||||
Ticonderoga_class,
|
||||
], "carrier_names": [
|
||||
"Principe de Asturias",
|
||||
], "lhanames": [
|
||||
"Juan Carlos I",
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
31
game/factions/sweden_1990.py
Normal file
31
game/factions/sweden_1990.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
|
||||
Sweden_1990 = {
|
||||
"country": "Sweden",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
AJS37,
|
||||
|
||||
UH_1H,
|
||||
|
||||
AirDefence.SAM_Hawk_LN_M192,
|
||||
|
||||
Armor.IFV_MCV_80, # Standing as Strf 90
|
||||
Armor.MBT_Leopard_2,
|
||||
Armor.APC_M1126_Stryker_ICV, # Closest thing available
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.SAM_Avenger_M1097
|
||||
], "has_jtac": True
|
||||
}
|
||||
261
game/factions/syria.py
Normal file
261
game/factions/syria.py
Normal file
@@ -0,0 +1,261 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Syria_2011 = {
|
||||
"country": "Syria",
|
||||
"side": "red",
|
||||
"units": [
|
||||
|
||||
MiG_29S,
|
||||
MiG_25PD,
|
||||
L_39ZA,
|
||||
Su_24M,
|
||||
Su_17M4,
|
||||
Mi_24V,
|
||||
SA342M,
|
||||
Mi_8MT,
|
||||
|
||||
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"
|
||||
]
|
||||
}
|
||||
41
game/factions/turkey_2005.py
Normal file
41
game/factions/turkey_2005.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Turkey_2005 = {
|
||||
"country": "Turkey",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
F_16C_50,
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
AH_1W,
|
||||
|
||||
Armor.MBT_Leopard_2,
|
||||
Armor.MBT_Leopard_1A3,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_Cobra,
|
||||
Armor.APC_BTR_80,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_Avenger_M1097,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
], "shorad":[
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka
|
||||
], "boat":[
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
38
game/factions/uae_2005.py
Normal file
38
game/factions/uae_2005.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
UAE_2005 = {
|
||||
"country": "United Arab Emirates",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
M_2000C,
|
||||
Mirage_2000_5,
|
||||
F_16C_50,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
AH_64D,
|
||||
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.IFV_BMP_3,
|
||||
Armor.TPz_Fuchs,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.Rapier_FSA_Launcher,
|
||||
|
||||
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
|
||||
}
|
||||
52
game/factions/uk_1990.py
Normal file
52
game/factions/uk_1990.py
Normal file
@@ -0,0 +1,52 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
UnitedKingdom_1990 = {
|
||||
"country": "UK",
|
||||
"side": "blue",
|
||||
"units":[
|
||||
AV8BNA, # Standing as BAE Harrier 2
|
||||
Tornado_GR4,
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
SA342M,
|
||||
AH_64A,
|
||||
|
||||
Armor.MBT_Challenger_II,
|
||||
Armor.IFV_MCV_80,
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.ATGM_M1045_HMMWV_TOW,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.Rapier_FSA_Launcher,
|
||||
AirDefence.SAM_Avenger_M1097, # Standing as Starstreak
|
||||
|
||||
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": [
|
||||
"HMS Invincible",
|
||||
"HMS Illustrious",
|
||||
"HMS Ark Royal",
|
||||
], "boat":[
|
||||
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
|
||||
], "has_jtac": True
|
||||
}
|
||||
53
game/factions/ukraine_2010.py
Normal file
53
game/factions/ukraine_2010.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
Ukraine_2010 = {
|
||||
"country": "Ukraine",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
Su_25,
|
||||
Su_25T,
|
||||
Su_24M,
|
||||
Su_27,
|
||||
MiG_29S,
|
||||
L_39ZA,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
An_26B,
|
||||
An_30M,
|
||||
Yak_40,
|
||||
A_50,
|
||||
|
||||
Mi_8MT,
|
||||
Mi_24V,
|
||||
|
||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
|
||||
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
|
||||
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.IFV_BMP_3,
|
||||
Armor.IFV_BMP_2,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.MBT_T_80U,
|
||||
Armor.MBT_T_72B,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
],
|
||||
"shorad":[
|
||||
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"
|
||||
]
|
||||
}
|
||||
86
game/factions/usa_1944.py
Normal file
86
game/factions/usa_1944.py
Normal file
@@ -0,0 +1,86 @@
|
||||
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": [
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
P_47D_30,
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
A_20G,
|
||||
B_17G,
|
||||
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.APC_M2A1,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.ST_Centaur_IV,
|
||||
Armor.HIT_Churchill_VII,
|
||||
Armor.LAC_M8_Greyhound,
|
||||
Armor.TD_M10_GMC,
|
||||
Artillery.M12_GMC,
|
||||
|
||||
Infantry.Infantry_M1_Garand,
|
||||
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": 2
|
||||
}
|
||||
33
game/factions/usa_1955.py
Normal file
33
game/factions/usa_1955.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
|
||||
USA_1955 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
F_86F_Sabre,
|
||||
P_51D,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M2A1,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
]
|
||||
}
|
||||
36
game/factions/usa_1960.py
Normal file
36
game/factions/usa_1960.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from dcs.vehicles import *
|
||||
from dcs.ships import *
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
|
||||
USA_1960 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
F_86F_Sabre,
|
||||
P_51D,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M113,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.AAA_Vulcan_M163
|
||||
]
|
||||
}
|
||||
41
game/factions/usa_1965.py
Normal file
41
game/factions/usa_1965.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from dcs.helicopters import *
|
||||
from dcs.planes import *
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
USA_1965 = {
|
||||
"country": "USA",
|
||||
"side": "blue",
|
||||
"units": [
|
||||
|
||||
F_5E_3,
|
||||
F_4E,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
B_52H,
|
||||
|
||||
UH_1H,
|
||||
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.APC_M113,
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
Armed_speedboat,
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
AirDefence.SAM_Chaparral_M48
|
||||
], "boat":[
|
||||
]
|
||||
}
|
||||
67
game/factions/usa_1990.py
Normal file
67
game/factions/usa_1990.py
Normal file
@@ -0,0 +1,67 @@
|
||||
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,
|
||||
|
||||
A_10A,
|
||||
AV8BNA,
|
||||
|
||||
B_1B,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
AH_64A,
|
||||
OH_58D,
|
||||
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.IFV_LAV_25,
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.ATGM_M1045_HMMWV_TOW,
|
||||
Armor.ATGM_M1134_Stryker,
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
|
||||
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-72 Abraham Lincoln",
|
||||
"CVN-73 Georges 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"
|
||||
], "has_jtac": True
|
||||
}
|
||||
72
game/factions/usa_2005.py
Normal file
72
game/factions/usa_2005.py
Normal file
@@ -0,0 +1,72 @@
|
||||
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,
|
||||
A_10C,
|
||||
AV8BNA,
|
||||
|
||||
KC_135,
|
||||
KC130,
|
||||
C_130,
|
||||
E_3A,
|
||||
|
||||
UH_1H,
|
||||
AH_64D,
|
||||
OH_58D,
|
||||
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.ATGM_M1134_Stryker,
|
||||
Armor.APC_M1126_Stryker_ICV,
|
||||
Armor.IFV_M2A2_Bradley,
|
||||
Armor.IFV_LAV_25,
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.ATGM_M1045_HMMWV_TOW,
|
||||
|
||||
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": [
|
||||
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"
|
||||
], "has_jtac": True
|
||||
}
|
||||
468
game/game.py
468
game/game.py
@@ -1,18 +1,11 @@
|
||||
import logging
|
||||
import typing
|
||||
import random
|
||||
import math
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from dcs.task import *
|
||||
from dcs.vehicles import *
|
||||
|
||||
from gen.conflictgen import Conflict
|
||||
from userdata.debriefing import Debriefing
|
||||
from theater import *
|
||||
|
||||
from . import db
|
||||
from .settings import Settings
|
||||
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 *
|
||||
from .settings import Settings
|
||||
|
||||
COMMISION_UNIT_VARIETY = 4
|
||||
COMMISION_LIMITS_SCALE = 1.5
|
||||
@@ -20,7 +13,7 @@ COMMISION_LIMITS_FACTORS = {
|
||||
PinpointStrike: 10,
|
||||
CAS: 5,
|
||||
CAP: 8,
|
||||
AirDefence: 1,
|
||||
AirDefence: 8,
|
||||
}
|
||||
|
||||
COMMISION_AMOUNTS_SCALE = 1.5
|
||||
@@ -28,46 +21,13 @@ COMMISION_AMOUNTS_FACTORS = {
|
||||
PinpointStrike: 3,
|
||||
CAS: 1,
|
||||
CAP: 2,
|
||||
AirDefence: 0.3,
|
||||
AirDefence: 0.8,
|
||||
}
|
||||
|
||||
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30
|
||||
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
|
||||
PLAYER_BASEATTACK_THRESHOLD = 0.4
|
||||
|
||||
"""
|
||||
Various events probabilities. First key is player probabilty, second is enemy probability.
|
||||
For the enemy events, only 1 event of each type could be generated for a turn.
|
||||
|
||||
Events:
|
||||
* BaseAttackEvent - capture base
|
||||
* InterceptEvent - air intercept
|
||||
* FrontlineAttackEvent - frontline attack
|
||||
* FrontlineCAPEvent - frontline attack
|
||||
* NavalInterceptEvent - naval intercept
|
||||
* StrikeEvent - strike event
|
||||
* InfantryTransportEvent - helicopter infantry transport
|
||||
"""
|
||||
EVENT_PROBABILITIES = {
|
||||
# events always present; only for the player
|
||||
FrontlineAttackEvent: [100, 0],
|
||||
FrontlinePatrolEvent: [100, 0],
|
||||
StrikeEvent: [100, 0],
|
||||
|
||||
# events randomly present; only for the player
|
||||
InfantryTransportEvent: [25, 0],
|
||||
|
||||
# events conditionally present; for both enemy and player
|
||||
BaseAttackEvent: [100, 9],
|
||||
|
||||
# events randomly present; for both enemy and player
|
||||
InterceptEvent: [25, 9],
|
||||
NavalInterceptEvent: [25, 9],
|
||||
|
||||
# events randomly present; only for the enemy
|
||||
InsurgentAttackEvent: [0, 6],
|
||||
}
|
||||
|
||||
# amount of strength player bases recover for the turn
|
||||
PLAYER_BASE_STRENGTH_RECOVERY = 0.2
|
||||
|
||||
@@ -78,9 +38,8 @@ ENEMY_BASE_STRENGTH_RECOVERY = 0.05
|
||||
AWACS_BUDGET_COST = 4
|
||||
|
||||
# Initial budget value
|
||||
PLAYER_BUDGET_INITIAL = 170
|
||||
# Base post-turn bonus value
|
||||
PLAYER_BUDGET_BASE = 14
|
||||
PLAYER_BUDGET_INITIAL = 650
|
||||
|
||||
# Bonus multiplier logarithm base
|
||||
PLAYER_BUDGET_IMPORTANCE_LOG = 2
|
||||
|
||||
@@ -91,136 +50,108 @@ class Game:
|
||||
events = None # type: typing.List[Event]
|
||||
pending_transfers = None # type: typing.Dict[]
|
||||
ignored_cps = None # type: typing.Collection[ControlPoint]
|
||||
turn = 0
|
||||
game_stats: GameStats = None
|
||||
|
||||
def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater):
|
||||
self.settings = Settings()
|
||||
current_unit_id = 0
|
||||
current_group_id = 0
|
||||
|
||||
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 = player_name
|
||||
self.enemy = enemy_name
|
||||
self.player_name = player_name
|
||||
self.player_country = db.FACTIONS[player_name]["country"]
|
||||
self.enemy_name = enemy_name
|
||||
self.enemy_country = db.FACTIONS[enemy_name]["country"]
|
||||
self.turn = 0
|
||||
self.date = datetime(start_date.year, start_date.month, start_date.day)
|
||||
self.game_stats = GameStats()
|
||||
self.game_stats.update(self)
|
||||
self.planners = {}
|
||||
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):
|
||||
return random.randint(1, 100) <= prob * mult
|
||||
|
||||
def _generate_globalinterceptions(self):
|
||||
global_count = len([x for x in self.theater.player_points() if x.is_global])
|
||||
for from_cp in [x for x in self.theater.player_points() if x.is_global]:
|
||||
probability_base = max(PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE / global_count, 1)
|
||||
probability = probability_base * math.log(len(self.theater.player_points()) + 1, PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG)
|
||||
if self._roll(probability, from_cp.base.strength):
|
||||
to_cp = random.choice([x for x in self.theater.enemy_points() if x not in self.theater.conflicts()])
|
||||
self.events.append(InterceptEvent(attacker_name=self.player,
|
||||
defender_name=self.enemy,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
game=self))
|
||||
break
|
||||
if self.settings.version == "dev":
|
||||
# always generate all events for dev
|
||||
return 100
|
||||
else:
|
||||
return random.randint(1, 100) <= prob * mult
|
||||
|
||||
def _generate_player_event(self, event_class, player_cp, enemy_cp):
|
||||
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
|
||||
# skip naval events for non-coastal CPs
|
||||
return
|
||||
|
||||
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
||||
# skip base attack events for CPs yet too strong
|
||||
return
|
||||
|
||||
if event_class == StrikeEvent and not enemy_cp.ground_objects:
|
||||
# skip strikes in case of no targets
|
||||
return
|
||||
|
||||
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
|
||||
|
||||
def _generate_enemy_event(self, event_class, player_cp, enemy_cp):
|
||||
if event_class in [type(x) for x in self.events if not self.is_player_attack(x)]:
|
||||
# skip already generated enemy event types
|
||||
return
|
||||
|
||||
if player_cp in self.ignored_cps:
|
||||
# skip attacks against ignored CPs (for example just captured ones)
|
||||
return
|
||||
|
||||
if enemy_cp.base.total_planes == 0:
|
||||
# skip event if there's no planes on the base
|
||||
return
|
||||
|
||||
if player_cp.is_global:
|
||||
# skip carriers
|
||||
return
|
||||
|
||||
if event_class == NavalInterceptEvent:
|
||||
if player_cp.radials == LAND:
|
||||
# skip naval events for non-coastal CPs
|
||||
return
|
||||
elif event_class == StrikeEvent:
|
||||
if not player_cp.ground_objects:
|
||||
# skip strikes if there's no ground objects
|
||||
return
|
||||
elif event_class == BaseAttackEvent:
|
||||
if BaseAttackEvent in [type(x) for x in self.events]:
|
||||
# skip base attack event if there's another one going on
|
||||
return
|
||||
|
||||
if enemy_cp.base.total_armor == 0:
|
||||
# skip base attack if there's no armor
|
||||
return
|
||||
|
||||
if player_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
||||
# skip base attack if strength is too high
|
||||
return
|
||||
|
||||
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))
|
||||
self.events.append(event_class(self, player_cp, enemy_cp, enemy_cp.position, self.player_name, self.enemy_name))
|
||||
|
||||
def _generate_events(self):
|
||||
for player_cp, enemy_cp in self.theater.conflicts(True):
|
||||
if enemy_cp.is_global:
|
||||
continue
|
||||
|
||||
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
|
||||
if event_class in [FrontlineAttackEvent, FrontlinePatrolEvent, InfantryTransportEvent]:
|
||||
# skip events requiring frontline
|
||||
if not Conflict.has_frontline_between(player_cp, enemy_cp):
|
||||
continue
|
||||
|
||||
if player_cp.is_global:
|
||||
# skip events requiring ground CP
|
||||
if event_class not in [InterceptEvent, StrikeEvent, NavalInterceptEvent]:
|
||||
continue
|
||||
|
||||
if player_probability == 100 or self._roll(player_probability, player_cp.base.strength):
|
||||
self._generate_player_event(event_class, player_cp, enemy_cp)
|
||||
|
||||
if enemy_probability == 100 or self._roll(enemy_probability, enemy_cp.base.strength):
|
||||
self._generate_enemy_event(event_class, player_cp, enemy_cp)
|
||||
self._generate_player_event(FrontlineAttackEvent, player_cp, enemy_cp)
|
||||
|
||||
def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
|
||||
importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW)
|
||||
|
||||
if for_task == AirDefence and not self.settings.sams:
|
||||
return [x for x in db.find_unittype(AirDefence, self.enemy) if x not in db.SAM_BAN]
|
||||
return [x for x in db.find_unittype(AirDefence, self.enemy_name) if x not in db.SAM_BAN]
|
||||
else:
|
||||
return db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy)
|
||||
return db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy_name)
|
||||
|
||||
def _commision_units(self, cp: ControlPoint):
|
||||
for for_task in [PinpointStrike, CAS, CAP, AirDefence]:
|
||||
limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) * self.settings.multiplier
|
||||
for for_task in [CAS, CAP, AirDefence]:
|
||||
limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance,
|
||||
COMMISION_LIMITS_SCALE) * self.settings.multiplier
|
||||
missing_units = limit - cp.base.total_units(for_task)
|
||||
if missing_units > 0:
|
||||
awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_AMOUNTS_SCALE) * self.settings.multiplier
|
||||
awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance,
|
||||
COMMISION_AMOUNTS_SCALE) * self.settings.multiplier
|
||||
points_to_spend = cp.base.append_commision_points(for_task, awarded_points)
|
||||
if points_to_spend > 0:
|
||||
unittypes = self.commision_unit_types(cp, for_task)
|
||||
d = {random.choice(unittypes): points_to_spend}
|
||||
logging.info("Commision {}: {}".format(cp, d))
|
||||
cp.base.commision_units(d)
|
||||
if len(unittypes) > 0:
|
||||
d = {random.choice(unittypes): points_to_spend}
|
||||
logging.info("Commision {}: {}".format(cp, d))
|
||||
cp.base.commision_units(d)
|
||||
|
||||
@property
|
||||
def budget_reward_amount(self):
|
||||
reward = 0
|
||||
if len(self.theater.player_points()) > 0:
|
||||
total_importance = sum([x.importance * x.base.strength for x in self.theater.player_points()])
|
||||
return math.ceil(math.log(total_importance + 1, PLAYER_BUDGET_IMPORTANCE_LOG) * PLAYER_BUDGET_BASE * self.settings.multiplier)
|
||||
reward = PLAYER_BUDGET_BASE * len(self.theater.player_points())
|
||||
for cp in self.theater.player_points():
|
||||
for g in cp.ground_objects:
|
||||
if g.category in REWARDS.keys():
|
||||
reward = reward + REWARDS[g.category]
|
||||
return reward
|
||||
else:
|
||||
return 0
|
||||
return reward
|
||||
|
||||
def _budget_player(self):
|
||||
self.budget += self.budget_reward_amount
|
||||
@@ -229,8 +160,8 @@ class Game:
|
||||
self.budget -= AWACS_BUDGET_COST
|
||||
|
||||
def units_delivery_event(self, to_cp: ControlPoint) -> UnitsDeliveryEvent:
|
||||
event = UnitsDeliveryEvent(attacker_name=self.player,
|
||||
defender_name=self.player,
|
||||
event = UnitsDeliveryEvent(attacker_name=self.player_name,
|
||||
defender_name=self.player_name,
|
||||
from_cp=to_cp,
|
||||
to_cp=to_cp,
|
||||
game=self)
|
||||
@@ -242,12 +173,9 @@ 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()
|
||||
logging.info("Generating {} (quick)".format(event))
|
||||
event.generate_quick()
|
||||
|
||||
def finish_event(self, event: Event, debriefing: Debriefing):
|
||||
logging.info("Finishing event {}".format(event))
|
||||
@@ -262,23 +190,34 @@ class Game:
|
||||
|
||||
def is_player_attack(self, event):
|
||||
if isinstance(event, Event):
|
||||
return event.attacker_name == self.player
|
||||
return event.attacker_name == self.player_name
|
||||
else:
|
||||
return event.name == self.player
|
||||
return event.name == self.player_name
|
||||
|
||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
|
||||
|
||||
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
|
||||
|
||||
for event in self.events:
|
||||
event.skip()
|
||||
if self.settings.version == "dev":
|
||||
# don't damage player CPs in by skipping in dev mode
|
||||
if isinstance(event, UnitsDeliveryEvent):
|
||||
event.skip()
|
||||
else:
|
||||
event.skip()
|
||||
|
||||
if not no_action:
|
||||
self._budget_player()
|
||||
|
||||
for cp in self.theater.enemy_points():
|
||||
self._commision_units(cp)
|
||||
self._enemy_reinforcement()
|
||||
self._budget_player()
|
||||
|
||||
if not no_action and self.turn > 1:
|
||||
for cp in self.theater.player_points():
|
||||
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
|
||||
else:
|
||||
for cp in self.theater.player_points():
|
||||
if not cp.is_carrier and not cp.is_lha:
|
||||
cp.base.affect_strength(-PLAYER_BASE_STRENGTH_RECOVERY)
|
||||
|
||||
self.ignored_cps = []
|
||||
if ignored_cps:
|
||||
@@ -286,5 +225,196 @@ class Game:
|
||||
|
||||
self.events = [] # type: typing.List[Event]
|
||||
self._generate_events()
|
||||
self._generate_globalinterceptions()
|
||||
|
||||
# Update statistics
|
||||
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:
|
||||
if cp.has_runway():
|
||||
planner = FlightPlanner(cp, self)
|
||||
planner.plan_flights()
|
||||
self.planners[cp.id] = planner
|
||||
|
||||
if cp.has_frontline:
|
||||
gplanner = GroundPlanner(cp, self)
|
||||
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
|
||||
"""
|
||||
|
||||
MAX_ARMOR = 30 * self.settings.multiplier
|
||||
MAX_AIRCRAFT = 25 * self.settings.multiplier
|
||||
|
||||
production = 0.0
|
||||
for enemy_point in self.theater.enemy_points():
|
||||
for g in enemy_point.ground_objects:
|
||||
if g.category in REWARDS.keys():
|
||||
production = production + REWARDS[g.category]
|
||||
|
||||
production = production * 0.75
|
||||
budget_for_armored_units = production / 2
|
||||
budget_for_aircraft = production / 2
|
||||
|
||||
potential_cp_armor = []
|
||||
for cp in self.theater.enemy_points():
|
||||
for cpe in cp.connected_points:
|
||||
if cpe.captured and cp.base.total_armor < MAX_ARMOR:
|
||||
potential_cp_armor.append(cp)
|
||||
if len(potential_cp_armor) == 0:
|
||||
potential_cp_armor = self.theater.enemy_points()
|
||||
|
||||
i = 0
|
||||
potential_units = [u for u in db.FACTIONS[self.enemy_name]["units"] if u in db.UNIT_BY_TASK[PinpointStrike]]
|
||||
|
||||
print("Enemy Recruiting")
|
||||
print(potential_cp_armor)
|
||||
print(budget_for_armored_units)
|
||||
print(potential_units)
|
||||
|
||||
if len(potential_units) > 0 and len(potential_cp_armor) > 0:
|
||||
while budget_for_armored_units > 0:
|
||||
i = i + 1
|
||||
if i > 50 or budget_for_armored_units <= 0:
|
||||
break
|
||||
target_cp = random.choice(potential_cp_armor)
|
||||
if target_cp.base.total_armor >= MAX_ARMOR:
|
||||
continue
|
||||
unit = random.choice(potential_units)
|
||||
price = db.PRICES[unit] * 2
|
||||
budget_for_armored_units -= price * 2
|
||||
target_cp.base.armor[unit] = target_cp.base.armor.get(unit, 0) + 2
|
||||
info = Information("Enemy Reinforcement", unit.id + " x 2 at " + target_cp.name, self.turn)
|
||||
print(str(info))
|
||||
self.informations.append(info)
|
||||
|
||||
if budget_for_armored_units > 0:
|
||||
budget_for_aircraft += budget_for_armored_units
|
||||
|
||||
potential_units = [u for u in db.FACTIONS[self.enemy_name]["units"] if
|
||||
u in db.UNIT_BY_TASK[CAS] or u in db.UNIT_BY_TASK[CAP]]
|
||||
if len(potential_units) > 0 and len(potential_cp_armor) > 0:
|
||||
while budget_for_aircraft > 0:
|
||||
i = i + 1
|
||||
if i > 50 or budget_for_aircraft <= 0:
|
||||
break
|
||||
target_cp = random.choice(potential_cp_armor)
|
||||
if target_cp.base.total_planes >= MAX_AIRCRAFT:
|
||||
continue
|
||||
unit = random.choice(potential_units)
|
||||
price = db.PRICES[unit] * 2
|
||||
budget_for_aircraft -= price * 2
|
||||
target_cp.base.aircraft[unit] = target_cp.base.aircraft.get(unit, 0) + 2
|
||||
info = Information("Enemy Reinforcement", unit.id + " x 2 at " + target_cp.name, self.turn)
|
||||
print(str(info))
|
||||
self.informations.append(info)
|
||||
|
||||
@property
|
||||
def current_turn_daytime(self):
|
||||
return ["dawn", "day", "dusk", "night"][self.turn % 4]
|
||||
|
||||
@property
|
||||
def current_day(self):
|
||||
return self.date + timedelta(days=self.turn // 4)
|
||||
|
||||
def next_unit_id(self):
|
||||
"""
|
||||
Next unit id for pre-generated units
|
||||
"""
|
||||
self.current_unit_id += 1
|
||||
return self.current_unit_id
|
||||
|
||||
def next_group_id(self):
|
||||
"""
|
||||
Next unit id for pre-generated units
|
||||
"""
|
||||
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"
|
||||
11
game/infos/information.py
Normal file
11
game/infos/information.py
Normal file
@@ -0,0 +1,11 @@
|
||||
|
||||
class Information():
|
||||
|
||||
def __init__(self, title="", text="", turn=0):
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.turn = turn
|
||||
|
||||
def __str__(self):
|
||||
s = "[" + str(self.turn) + "] " + self.title + "\n" + self.text
|
||||
return s
|
||||
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 = []
|
||||
56
game/models/game_stats.py
Normal file
56
game/models/game_stats.py
Normal file
@@ -0,0 +1,56 @@
|
||||
class FactionTurnMetadata:
|
||||
"""
|
||||
Store metadata about a faction
|
||||
"""
|
||||
|
||||
aircraft_count: int = 0
|
||||
vehicles_count: int = 0
|
||||
sam_count: int = 0
|
||||
|
||||
def __init__(self):
|
||||
self.aircraft_count = 0
|
||||
self.vehicles_count = 0
|
||||
self.sam_count = 0
|
||||
|
||||
|
||||
class GameTurnMetadata:
|
||||
"""
|
||||
Store metadata about a game turn
|
||||
"""
|
||||
|
||||
allied_units:FactionTurnMetadata
|
||||
enemy_units:FactionTurnMetadata
|
||||
|
||||
def __init__(self):
|
||||
self.allied_units = FactionTurnMetadata()
|
||||
self.enemy_units = FactionTurnMetadata()
|
||||
|
||||
|
||||
class GameStats:
|
||||
"""
|
||||
Store statistics for the current game
|
||||
"""
|
||||
|
||||
data_per_turn: [GameTurnMetadata] = []
|
||||
|
||||
def __init__(self):
|
||||
self.data_per_turn = []
|
||||
|
||||
def update(self, game):
|
||||
"""
|
||||
Save data for current turn
|
||||
:param game: Game we want to save the data about
|
||||
"""
|
||||
|
||||
turn_data = GameTurnMetadata()
|
||||
|
||||
for cp in game.theater.controlpoints:
|
||||
if cp.captured:
|
||||
turn_data.allied_units.aircraft_count += sum(cp.base.aircraft.values())
|
||||
turn_data.allied_units.vehicles_count += sum(cp.base.armor.values())
|
||||
else:
|
||||
turn_data.enemy_units.aircraft_count += sum(cp.base.aircraft.values())
|
||||
turn_data.enemy_units.vehicles_count += sum(cp.base.armor.values())
|
||||
|
||||
self.data_per_turn.append(turn_data)
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
from game.db import assigned_units_split
|
||||
|
||||
from gen.triggergen import *
|
||||
|
||||
from .operation import *
|
||||
|
||||
|
||||
class BaseAttackOperation(Operation):
|
||||
cas = None # type: db.AssignedUnitsDict
|
||||
escort = None # type: db.AssignedUnitsDict
|
||||
intercept = None # type: db.AssignedUnitsDict
|
||||
attack = None # type: db.ArmorDict
|
||||
defense = None # type: db.ArmorDict
|
||||
aa = None # type: db.AirDefenseDict
|
||||
|
||||
trigger_radius = TRIGGER_RADIUS_SMALL
|
||||
|
||||
def setup(self,
|
||||
cas: db.AssignedUnitsDict,
|
||||
escort: db.AssignedUnitsDict,
|
||||
attack: db.AssignedUnitsDict,
|
||||
intercept: db.AssignedUnitsDict,
|
||||
defense: db.ArmorDict,
|
||||
aa: db.AirDefenseDict):
|
||||
self.cas = cas
|
||||
self.escort = escort
|
||||
self.intercept = intercept
|
||||
self.attack = attack
|
||||
self.defense = defense
|
||||
self.aa = aa
|
||||
|
||||
def prepare(self, terrain: dcs.terrain.Terrain, is_quick: bool):
|
||||
super(BaseAttackOperation, self).prepare(terrain, is_quick)
|
||||
|
||||
self.defenders_starting_position = None
|
||||
if self.game.player == self.defender_name:
|
||||
self.attackers_starting_position = None
|
||||
|
||||
conflict = Conflict.capture_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
)
|
||||
self.initialize(mission=self.current_mission,
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
self.armorgen.generate(self.attack, self.defense)
|
||||
self.aagen.generate(self.aa)
|
||||
|
||||
self.airgen.generate_defense(*assigned_units_split(self.intercept), at=self.defenders_starting_position)
|
||||
|
||||
self.airgen.generate_cas_strikegroup(*assigned_units_split(self.cas), at=self.attackers_starting_position)
|
||||
self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
|
||||
|
||||
self.visualgen.generate_target_smokes(self.to_cp)
|
||||
|
||||
self.briefinggen.title = "Base attack"
|
||||
self.briefinggen.description = "The goal of an attacker is to lower defender presence by destroying their armor and aircraft. Base will be considered captured if attackers on the ground overrun the defenders. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||
|
||||
if self.game.player == self.attacker_name:
|
||||
self.briefinggen.append_waypoint("TARGET")
|
||||
else:
|
||||
pass
|
||||
|
||||
super(BaseAttackOperation, self).generate()
|
||||
|
||||
@@ -7,27 +7,24 @@ MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
||||
|
||||
|
||||
class FrontlineAttackOperation(Operation):
|
||||
interceptors = None # type: db.AssignedUnitsDict
|
||||
escort = None # type: db.AssignedUnitsDict
|
||||
strikegroup = None # type: db.AssignedUnitsDict
|
||||
attackers = None # type: db.ArmorDict
|
||||
target = None # type: db.ArmorDict
|
||||
|
||||
def setup(self,
|
||||
target: db.ArmorDict,
|
||||
attackers: db.ArmorDict,
|
||||
strikegroup: db.AssignedUnitsDict):
|
||||
self.strikegroup = strikegroup
|
||||
self.target = target
|
||||
self.attackers = attackers
|
||||
attackers = None # type: db.ArmorDict
|
||||
defenders = None # type: db.ArmorDict
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(FrontlineAttackOperation, self).prepare(terrain, is_quick)
|
||||
if self.defender_name == self.game.player:
|
||||
if self.defender_name == self.game.player_name:
|
||||
self.attackers_starting_position = None
|
||||
self.defenders_starting_position = None
|
||||
|
||||
conflict = Conflict.frontline_cas_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
attacker=self.current_mission.country(self.attacker_country),
|
||||
defender=self.current_mission.country(self.defender_country),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
@@ -37,20 +34,6 @@ class FrontlineAttackOperation(Operation):
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
self.armorgen.generate_vec(self.attackers, self.target)
|
||||
|
||||
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.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")
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
from game.db import assigned_units_split
|
||||
|
||||
from .operation import *
|
||||
|
||||
|
||||
MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
||||
|
||||
|
||||
class FrontlinePatrolOperation(Operation):
|
||||
cas = None # type: db.AssignedUnitsDict
|
||||
escort = None # type: db.AssignedUnitsDict
|
||||
interceptors = None # type: db.AssignedUnitsDict
|
||||
|
||||
armor_attackers = None # type: db.ArmorDict
|
||||
armor_defenders = None # type: db.ArmorDict
|
||||
|
||||
def setup(self,
|
||||
cas: db.AssignedUnitsDict,
|
||||
escort: db.AssignedUnitsDict,
|
||||
interceptors: db.AssignedUnitsDict,
|
||||
armor_attackers: db.ArmorDict,
|
||||
armor_defenders: db.ArmorDict):
|
||||
self.cas = cas
|
||||
self.escort = escort
|
||||
self.interceptors = interceptors
|
||||
|
||||
self.armor_attackers = armor_attackers
|
||||
self.armor_defenders = armor_defenders
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(FrontlinePatrolOperation, self).prepare(terrain, is_quick)
|
||||
self.defenders_starting_position = None
|
||||
|
||||
conflict = Conflict.frontline_cap_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
)
|
||||
|
||||
self.initialize(mission=self.current_mission,
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
self.airgen.generate_defenders_cas(*assigned_units_split(self.cas), at=self.defenders_starting_position)
|
||||
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
||||
self.airgen.generate_migcap(*assigned_units_split(self.interceptors), at=self.attackers_starting_position)
|
||||
|
||||
self.armorgen.generate_vec(self.armor_attackers, self.armor_defenders)
|
||||
|
||||
self.briefinggen.title = "Frontline CAP"
|
||||
self.briefinggen.description = "Providing CAP support for ground units attacking enemy lines. Enemy will scramble its CAS and your task is to intercept it. Operation will be considered successful if total number of friendly units will be lower than enemy by at least a factor of 0.8 (i.e. with 12 units from both sides, there should be at least 8 friendly units alive), lowering targets strength as a result."
|
||||
self.briefinggen.append_waypoint("CAP AREA IP")
|
||||
self.briefinggen.append_waypoint("CAP AREA EGRESS")
|
||||
super(FrontlinePatrolOperation, self).generate()
|
||||
@@ -1,46 +0,0 @@
|
||||
from game.db import assigned_units_split
|
||||
|
||||
from .operation import *
|
||||
|
||||
|
||||
class InfantryTransportOperation(Operation):
|
||||
transport = None # type: db.AssignedUnitsDict
|
||||
aa = None # type: db.AirDefenseDict
|
||||
|
||||
def setup(self, transport: db.AssignedUnitsDict, aa: db.AirDefenseDict):
|
||||
self.transport = transport
|
||||
self.aa = aa
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(InfantryTransportOperation, self).prepare(terrain, is_quick)
|
||||
|
||||
conflict = Conflict.transport_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
)
|
||||
|
||||
self.initialize(mission=self.current_mission,
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
self.airgen.generate_passenger_transport(*assigned_units_split(self.transport), at=self.attackers_starting_position)
|
||||
|
||||
self.armorgen.generate_passengers(count=6)
|
||||
self.aagen.generate_at_defenders_location(self.aa)
|
||||
|
||||
self.visualgen.generate_transportation_marker(self.conflict.ground_attackers_location)
|
||||
self.visualgen.generate_transportation_destination(self.conflict.position)
|
||||
|
||||
self.briefinggen.title = "Infantry transport"
|
||||
self.briefinggen.description = "Helicopter operation to transport infantry troops from the base to the front line. Lowers target strength"
|
||||
self.briefinggen.append_waypoint("DROP POINT")
|
||||
|
||||
# TODO: horrible, horrible hack
|
||||
# this will disable vehicle activation triggers,
|
||||
# which aren't needed on this type of missions
|
||||
self.is_quick = True
|
||||
super(InfantryTransportOperation, self).generate()
|
||||
self.is_quick = False
|
||||
@@ -1,38 +0,0 @@
|
||||
from game.db import assigned_units_split
|
||||
|
||||
from .operation import *
|
||||
|
||||
|
||||
class InsurgentAttackOperation(Operation):
|
||||
strikegroup = None # type: db.AssignedUnitsDict
|
||||
target = None # type: db.ArmorDict
|
||||
|
||||
def setup(self,
|
||||
target: db.ArmorDict,
|
||||
strikegroup: db.AssignedUnitsDict):
|
||||
self.strikegroup = strikegroup
|
||||
self.target = target
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(InsurgentAttackOperation, self).prepare(terrain, is_quick)
|
||||
|
||||
conflict = Conflict.ground_attack_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
)
|
||||
|
||||
self.initialize(mission=self.current_mission,
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
self.airgen.generate_defenders_cas(*assigned_units_split(self.strikegroup), at=self.defenders_starting_position)
|
||||
self.armorgen.generate(self.target, {})
|
||||
|
||||
self.briefinggen.title = "Destroy insurgents"
|
||||
self.briefinggen.description = "Destroy vehicles of insurgents in close proximity of the friendly base. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||
self.briefinggen.append_waypoint("TARGET")
|
||||
|
||||
super(InsurgentAttackOperation, self).generate()
|
||||
@@ -1,60 +0,0 @@
|
||||
from game.db import assigned_units_split
|
||||
|
||||
from .operation import *
|
||||
|
||||
|
||||
class InterceptOperation(Operation):
|
||||
escort = None # type: db.AssignedUnitsDict
|
||||
transport = None # type: db.PlaneDict
|
||||
interceptors = None # type: db.AssignedUnitsDict
|
||||
airdefense = None # type: db.AirDefenseDict
|
||||
|
||||
trigger_radius = TRIGGER_RADIUS_LARGE
|
||||
|
||||
def setup(self,
|
||||
escort: db.AssignedUnitsDict,
|
||||
transport: db.PlaneDict,
|
||||
airdefense: db.AirDefenseDict,
|
||||
interceptors: db.AssignedUnitsDict):
|
||||
self.escort = escort
|
||||
self.transport = transport
|
||||
self.airdefense = airdefense
|
||||
self.interceptors = interceptors
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(InterceptOperation, self).prepare(terrain, is_quick)
|
||||
self.defenders_starting_position = None
|
||||
if self.defender_name == self.game.player:
|
||||
self.attackers_starting_position = None
|
||||
|
||||
conflict = Conflict.intercept_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
)
|
||||
|
||||
self.initialize(mission=self.current_mission,
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
self.prepare_carriers(db.unitdict_from(self.interceptors))
|
||||
|
||||
self.airgen.generate_transport(self.transport, self.to_cp.at)
|
||||
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
||||
|
||||
self.airgen.generate_interception(*assigned_units_split(self.interceptors), at=self.attackers_starting_position)
|
||||
|
||||
self.briefinggen.title = "Air Intercept"
|
||||
|
||||
if self.game.player == self.attacker_name:
|
||||
self.briefinggen.description = "Intercept enemy supply transport aircraft. Escort will also be present if there are available planes on the base. Operation will be considered successful if most of the targets are destroyed, lowering targets strength as a result"
|
||||
self.briefinggen.append_waypoint("TARGET")
|
||||
for unit_type, count in self.transport.items():
|
||||
self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count))
|
||||
else:
|
||||
self.briefinggen.description = "Escort friendly supply transport aircraft. Operation will be considered failed if most of the targets are destroyed, lowering CP strength as a result"
|
||||
|
||||
super(InterceptOperation, self).generate()
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
from game.db import assigned_units_split
|
||||
|
||||
from .operation import *
|
||||
|
||||
|
||||
class NavalInterceptionOperation(Operation):
|
||||
strikegroup = None # type: db.AssignedUnitsDict
|
||||
interceptors = None # type: db.AssignedUnitsDict
|
||||
targets = None # type: db.ShipDict
|
||||
trigger_radius = TRIGGER_RADIUS_LARGE
|
||||
|
||||
def setup(self,
|
||||
strikegroup: db.AssignedUnitsDict,
|
||||
interceptors: db.AssignedUnitsDict,
|
||||
targets: db.ShipDict):
|
||||
self.strikegroup = strikegroup
|
||||
self.interceptors = interceptors
|
||||
self.targets = targets
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(NavalInterceptionOperation, self).prepare(terrain, is_quick)
|
||||
if self.defender_name == self.game.player:
|
||||
self.attackers_starting_position = None
|
||||
|
||||
conflict = Conflict.naval_intercept_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
)
|
||||
|
||||
self.initialize(self.current_mission, conflict)
|
||||
|
||||
def generate(self):
|
||||
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||
|
||||
target_groups = self.shipgen.generate_cargo(units=self.targets)
|
||||
|
||||
self.airgen.generate_ship_strikegroup(
|
||||
*assigned_units_split(self.strikegroup),
|
||||
target_groups=target_groups,
|
||||
at=self.attackers_starting_position
|
||||
)
|
||||
|
||||
if self.interceptors:
|
||||
self.airgen.generate_defense(
|
||||
*assigned_units_split(self.interceptors),
|
||||
at=self.defenders_starting_position
|
||||
)
|
||||
|
||||
self.briefinggen.title = "Naval Intercept"
|
||||
if self.game.player == self.attacker_name:
|
||||
self.briefinggen.description = "Destroy supply transport ships. Lowers target strength. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||
for unit_type, count in self.targets.items():
|
||||
self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count))
|
||||
else:
|
||||
self.briefinggen.description = "Protect supply transport ships."
|
||||
self.briefinggen.append_waypoint("TARGET")
|
||||
|
||||
super(NavalInterceptionOperation, self).generate()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from dcs.countries import country_dict
|
||||
from dcs.lua.parse import loads
|
||||
|
||||
from userdata.debriefing import *
|
||||
from dcs.terrain import Terrain
|
||||
|
||||
from gen import *
|
||||
from userdata.debriefing import *
|
||||
|
||||
TANKER_CALLSIGNS = ["Texaco", "Arco", "Shell"]
|
||||
|
||||
@@ -17,15 +18,13 @@ class Operation:
|
||||
conflict = None # type: Conflict
|
||||
armorgen = None # type: ArmorConflictGenerator
|
||||
airgen = None # type: AircraftConflictGenerator
|
||||
aagen = None # type: AAConflictGenerator
|
||||
extra_aagen = None # type: ExtraAAConflictGenerator
|
||||
shipgen = None # type: ShipGenerator
|
||||
triggersgen = None # type: TriggersGenerator
|
||||
airsupportgen = None # type: AirSupportConflictGenerator
|
||||
visualgen = None # type: VisualGenerator
|
||||
envgen = None # type: EnvironmentGenerator
|
||||
groundobjectgen = None # type: GroundObjectsGenerator
|
||||
briefinggen = None # type: BriefingGenerator
|
||||
forcedoptionsgen = None # type: ForcedOptionsGenerator
|
||||
|
||||
environment_settings = None
|
||||
trigger_radius = TRIGGER_RADIUS_MEDIUM
|
||||
@@ -38,11 +37,16 @@ class Operation:
|
||||
attacker_name: str,
|
||||
defender_name: str,
|
||||
from_cp: ControlPoint,
|
||||
departure_cp: ControlPoint,
|
||||
to_cp: ControlPoint = None):
|
||||
self.game = game
|
||||
self.attacker_name = attacker_name
|
||||
self.attacker_country = db.FACTIONS[attacker_name]["country"]
|
||||
self.defender_name = defender_name
|
||||
self.defender_country = db.FACTIONS[defender_name]["country"]
|
||||
print(self.defender_country, self.attacker_country)
|
||||
self.from_cp = from_cp
|
||||
self.departure_cp = departure_cp
|
||||
self.to_cp = to_cp
|
||||
self.is_quick = False
|
||||
|
||||
@@ -52,29 +56,44 @@ class Operation:
|
||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||
return True
|
||||
|
||||
@property
|
||||
def is_player_attack(self) -> bool:
|
||||
return self.from_cp.captured
|
||||
|
||||
def initialize(self, mission: Mission, conflict: Conflict):
|
||||
self.current_mission = mission
|
||||
self.conflict = conflict
|
||||
self.armorgen = ArmorConflictGenerator(mission, conflict)
|
||||
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
|
||||
self.aagen = AAConflictGenerator(mission, conflict)
|
||||
self.shipgen = ShipGenerator(mission, conflict)
|
||||
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings, self.game)
|
||||
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_name = self.from_cp.captured and self.attacker_name or self.defender_name
|
||||
enemy_name = self.from_cp.captured and self.defender_name or self.attacker_name
|
||||
self.extra_aagen = ExtraAAConflictGenerator(mission, conflict, self.game, player_name, enemy_name)
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
with open("resources/default_options.lua", "r") as f:
|
||||
options_dict = loads(f.read())["options"]
|
||||
|
||||
self.current_mission = dcs.Mission(terrain)
|
||||
|
||||
print(self.game.player_country)
|
||||
print(country_dict[db.country_id_from_name(self.game.player_country)])
|
||||
print(country_dict[db.country_id_from_name(self.game.player_country)]())
|
||||
|
||||
# Setup coalition :
|
||||
self.current_mission.coalition["blue"] = Coalition("blue")
|
||||
self.current_mission.coalition["red"] = Coalition("red")
|
||||
|
||||
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()])
|
||||
|
||||
if is_quick:
|
||||
self.quick_mission = self.current_mission
|
||||
else:
|
||||
@@ -87,64 +106,132 @@ class Operation:
|
||||
self.attackers_starting_position = None
|
||||
self.defenders_starting_position = None
|
||||
else:
|
||||
self.attackers_starting_position = self.from_cp.at
|
||||
self.attackers_starting_position = self.departure_cp.at
|
||||
self.defenders_starting_position = self.to_cp.at
|
||||
|
||||
def prepare_carriers(self, for_units: db.UnitsDict):
|
||||
for global_cp in self.game.theater.controlpoints:
|
||||
if not global_cp.is_global:
|
||||
continue
|
||||
|
||||
ship = self.shipgen.generate_carrier(for_units=[t for t, c in for_units.items() if c > 0],
|
||||
country=self.game.player,
|
||||
at=global_cp.at)
|
||||
|
||||
if global_cp == self.from_cp and not self.is_quick:
|
||||
self.attackers_starting_position = ship
|
||||
|
||||
def generate(self):
|
||||
self.visualgen.generate()
|
||||
|
||||
# air support
|
||||
self.airsupportgen.generate(self.is_awacs_enabled)
|
||||
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))
|
||||
|
||||
if self.is_awacs_enabled:
|
||||
self.briefinggen.append_frequency("AWACS", "133 MHz AM")
|
||||
|
||||
# combined arms
|
||||
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
|
||||
if self.game.player in [country.name for country in self.current_mission.coalition["blue"].countries.values()]:
|
||||
self.current_mission.groundControl.blue_tactical_commander = self.ca_slots
|
||||
else:
|
||||
self.current_mission.groundControl.red_tactical_commander = self.ca_slots
|
||||
|
||||
# ground infrastructure
|
||||
self.groundobjectgen.generate()
|
||||
self.extra_aagen.generate()
|
||||
|
||||
# triggers
|
||||
if self.game.is_player_attack(self.conflict.attackers_side):
|
||||
cp = self.conflict.from_cp
|
||||
else:
|
||||
cp = self.conflict.to_cp
|
||||
|
||||
self.triggersgen.generate(player_cp=cp,
|
||||
is_quick=self.is_quick,
|
||||
activation_trigger_radius=self.trigger_radius,
|
||||
awacs_enabled=self.is_awacs_enabled)
|
||||
|
||||
# env settings
|
||||
# Generate meteo
|
||||
if self.environment_settings is None:
|
||||
self.environment_settings = self.envgen.generate()
|
||||
else:
|
||||
self.envgen.load(self.environment_settings)
|
||||
|
||||
# main frequencies
|
||||
self.briefinggen.append_frequency("Flight", "251 MHz AM")
|
||||
if self.conflict.from_cp.is_global or self.conflict.to_cp.is_global:
|
||||
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
|
||||
# Generate ground object first
|
||||
self.groundobjectgen.generate()
|
||||
|
||||
# briefing
|
||||
# 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)
|
||||
|
||||
# Generate Activity on the map
|
||||
for cp in self.game.theater.controlpoints:
|
||||
side = cp.captured
|
||||
if side:
|
||||
country = self.current_mission.country(self.game.player_country)
|
||||
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])
|
||||
|
||||
# Generate ground units on frontline everywhere
|
||||
self.game.jtacs = []
|
||||
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),
|
||||
self.current_mission.country(self.defender_country),
|
||||
player_cp, enemy_cp, self.game.theater)
|
||||
# Generate frontline ops
|
||||
player_gp = self.game.ground_planners[player_cp.id].units_per_cp[enemy_cp.id]
|
||||
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()
|
||||
|
||||
# Setup combined arms parameters
|
||||
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
|
||||
if self.game.player_country in [country.name for country in self.current_mission.coalition["blue"].countries.values()]:
|
||||
self.current_mission.groundControl.blue_tactical_commander = self.ca_slots
|
||||
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
|
||||
self.triggersgen.generate()
|
||||
|
||||
# Options
|
||||
self.forcedoptionsgen.generate()
|
||||
|
||||
# Generate Visuals Smoke Effects
|
||||
if self.game.settings.perf_smoke_gen:
|
||||
self.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)
|
||||
|
||||
# Load Ciribob's JTACAutoLase script
|
||||
load_autolase = TriggerStart(comment="Load JTAC script")
|
||||
with open("./resources/scripts/JTACAutoLase.lua") as f:
|
||||
|
||||
script = f.read()
|
||||
script = script + "\n"
|
||||
|
||||
smoke = "true"
|
||||
if hasattr(self.game.settings, "jtac_smoke_on"):
|
||||
if not self.game.settings.jtac_smoke_on:
|
||||
smoke = "false"
|
||||
|
||||
for jtac in self.game.jtacs:
|
||||
script = script + "\n" + "JTACAutoLase('" + str(jtac[2]) + "', " + str(jtac[1]) + ", " + smoke + ", \"vehicle\")" + "\n"
|
||||
|
||||
load_autolase.add_action(DoScript(String(script)))
|
||||
self.current_mission.triggerrules.triggers.append(load_autolase)
|
||||
|
||||
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)
|
||||
|
||||
# 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(60+i, 130+i))
|
||||
|
||||
if self.is_awacs_enabled:
|
||||
self.briefinggen.append_frequency("AWACS", "233 MHz AM")
|
||||
|
||||
self.briefinggen.append_frequency("Flight", "251 MHz AM")
|
||||
|
||||
# Generate the briefing
|
||||
self.briefinggen.generate()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
from game.db import assigned_units_split
|
||||
|
||||
from .operation import *
|
||||
|
||||
|
||||
class StrikeOperation(Operation):
|
||||
strikegroup = None # type: db.AssignedUnitsDict
|
||||
escort = None # type: db.AssignedUnitsDict
|
||||
interceptors = None # type: db.AssignedUnitsDict
|
||||
|
||||
trigger_radius = TRIGGER_RADIUS_ALL_MAP
|
||||
|
||||
def setup(self,
|
||||
strikegroup: db.AssignedUnitsDict,
|
||||
escort: db.AssignedUnitsDict,
|
||||
interceptors: db.AssignedUnitsDict):
|
||||
self.strikegroup = strikegroup
|
||||
self.escort = escort
|
||||
self.interceptors = interceptors
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
super(StrikeOperation, self).prepare(terrain, is_quick)
|
||||
|
||||
self.defenders_starting_position = None
|
||||
if self.game.player == self.defender_name:
|
||||
self.attackers_starting_position = None
|
||||
|
||||
conflict = Conflict.strike_conflict(
|
||||
attacker=self.current_mission.country(self.attacker_name),
|
||||
defender=self.current_mission.country(self.defender_name),
|
||||
from_cp=self.from_cp,
|
||||
to_cp=self.to_cp,
|
||||
theater=self.game.theater
|
||||
)
|
||||
|
||||
self.initialize(mission=self.current_mission,
|
||||
conflict=conflict)
|
||||
|
||||
def generate(self):
|
||||
self.prepare_carriers(db.unitdict_merge(db.unitdict_from(self.strikegroup), db.unitdict_from(self.escort)))
|
||||
|
||||
targets = [] # type: typing.List[typing.Tuple[str, str, Point]]
|
||||
category_counters = {} # type: typing.Dict[str, int]
|
||||
processed_groups = []
|
||||
for object in self.to_cp.ground_objects:
|
||||
if object.group_identifier in processed_groups:
|
||||
continue
|
||||
|
||||
processed_groups.append(object.group_identifier)
|
||||
category_counters[object.category] = category_counters.get(object.category, 0) + 1
|
||||
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
||||
targets.append((str(object), markpoint_name, object.position))
|
||||
|
||||
targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[2]))
|
||||
|
||||
for (name, markpoint_name, _) in targets:
|
||||
self.briefinggen.append_waypoint("TARGET {} (TP {})".format(str(name), markpoint_name))
|
||||
|
||||
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||
self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(planes_flights),
|
||||
targets=[(mp, pos) for (n, mp, pos) in targets],
|
||||
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", "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_ground_attack_strikegroup(*assigned_units_split(dict),
|
||||
targets=[(mp, pos) for (n, mp, pos) in targets],
|
||||
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_barcap(*assigned_units_split(self.interceptors), at=self.defenders_starting_position)
|
||||
|
||||
self.briefinggen.title = "Strike"
|
||||
self.briefinggen.description = "Destroy infrastructure assets and military supplies in the region. Each building destroyed will lower targets strength."
|
||||
super(StrikeOperation, self).generate()
|
||||
@@ -1,12 +1,43 @@
|
||||
|
||||
class Settings:
|
||||
player_skill = "Good"
|
||||
enemy_skill = "Average"
|
||||
enemy_vehicle_skill = "Average"
|
||||
only_player_takeoff = True
|
||||
night_disabled = False
|
||||
|
||||
multiplier = 1
|
||||
sams = True
|
||||
cold_start = False
|
||||
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
|
||||
|
||||
# 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,12 +3,12 @@ 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 . import naming
|
||||
|
||||
|
||||
43
gen/aaa.py
43
gen/aaa.py
@@ -2,52 +2,25 @@ from .conflictgen import *
|
||||
from .naming import *
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.mission import *
|
||||
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
|
||||
DISTANCE_FACTOR = 0.5, 1
|
||||
EXTRA_AA_MIN_DISTANCE = 50000
|
||||
EXTRA_AA_MAX_DISTANCE = 150000
|
||||
EXTRA_AA_POSITION_FROM_CP = 550
|
||||
|
||||
|
||||
class AAConflictGenerator:
|
||||
def __init__(self, mission: Mission, conflict: Conflict):
|
||||
self.m = mission
|
||||
self.conflict = conflict
|
||||
|
||||
def generate_at_defenders_location(self, units: db.AirDefenseDict):
|
||||
for unit_type, count in units.items():
|
||||
for _ in range(count):
|
||||
self.m.vehicle_group(
|
||||
country=self.conflict.defenders_side,
|
||||
name=namegen.next_unit_name(self.conflict.defenders_side, unit_type),
|
||||
_type=unit_type,
|
||||
position=self.conflict.ground_defenders_location.random_point_within(100, 100),
|
||||
group_size=1)
|
||||
|
||||
def generate(self, units: db.AirDefenseDict):
|
||||
for type, count in units.items():
|
||||
for _, radial in zip(range(count), self.conflict.radials):
|
||||
distance = randint(self.conflict.size * DISTANCE_FACTOR[0], self.conflict.size * DISTANCE_FACTOR[1])
|
||||
p = self.conflict.position.point_from_heading(radial, distance)
|
||||
|
||||
self.m.vehicle_group(
|
||||
country=self.conflict.defenders_side,
|
||||
name=namegen.next_unit_name(self.conflict.defenders_side, type),
|
||||
_type=type,
|
||||
position=p,
|
||||
group_size=1)
|
||||
|
||||
|
||||
class ExtraAAConflictGenerator:
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game, player_name: Country, enemy_name: Country):
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game, player_country: Country, enemy_country: Country):
|
||||
self.mission = mission
|
||||
self.game = game
|
||||
self.conflict = conflict
|
||||
self.player_name = player_name
|
||||
self.enemy_name = enemy_name
|
||||
self.player_country = player_country
|
||||
self.enemy_country = enemy_country
|
||||
|
||||
def generate(self):
|
||||
from theater.conflicttheater import ControlPoint
|
||||
|
||||
for cp in self.game.theater.controlpoints:
|
||||
if cp.is_global:
|
||||
@@ -65,7 +38,7 @@ class ExtraAAConflictGenerator:
|
||||
if cp.position.distance_to_point(self.conflict.position) > EXTRA_AA_MAX_DISTANCE:
|
||||
continue
|
||||
|
||||
country_name = cp.captured and self.player_name or self.enemy_name
|
||||
country_name = cp.captured and self.player_country or self.enemy_country
|
||||
position = cp.position.point_from_heading(0, EXTRA_AA_POSITION_FROM_CP)
|
||||
|
||||
self.mission.vehicle_group(
|
||||
|
||||
659
gen/aircraft.py
659
gen/aircraft.py
@@ -1,88 +1,56 @@
|
||||
import logging
|
||||
from dcs.action import ActivateGroup, AITaskPush, MessageToCoalition, MessageToAll
|
||||
from dcs.condition import TimeAfter, CoalitionHasAirdrome, PartOfCoalitionInZone
|
||||
from dcs.helicopters import UH_1H
|
||||
from dcs.terrain.terrain import NoParkingSlotError
|
||||
from dcs.triggers import TriggerOnce, Event
|
||||
|
||||
from game import db
|
||||
from game.data.cap_capabilities_db import GUNFIGHTERS
|
||||
from game.settings import Settings
|
||||
from game.utils import nm_to_meter
|
||||
from gen.flights.ai_flight_planner import FlightPlanner
|
||||
from gen.flights.flight import Flight, FlightType, FlightWaypointType
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
from .triggergen import TRIGGER_WAYPOINT_OFFSET
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.unitgroup import *
|
||||
from dcs.unittype import *
|
||||
from dcs.task import *
|
||||
from dcs.terrain.terrain import NoParkingSlotError
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 1, 2
|
||||
ESCORT_ENGAGEMENT_MAX_DIST = 100000
|
||||
WORKAROUND_WAYP_DIST = 1000
|
||||
|
||||
WARM_START_HELI_AIRSPEED = 120
|
||||
WARM_START_HELI_ALT = 1000
|
||||
|
||||
WARM_START_HELI_ALT = 500
|
||||
WARM_START_ALTITUDE = 3000
|
||||
WARM_START_AIRSPEED = 550
|
||||
|
||||
INTERCEPTION_ALT = 3000
|
||||
INTERCEPTION_AIRSPEED = 1000
|
||||
BARCAP_RACETRACK_DISTANCE = 20000
|
||||
CAP_DURATION = 30 # minutes
|
||||
|
||||
ATTACK_CIRCLE_ALT = 5000
|
||||
ATTACK_CIRCLE_DURATION = 15
|
||||
|
||||
CAS_ALTITUDE = 1000
|
||||
RTB_ALTITUDE = 1000
|
||||
HELI_ALT = 900
|
||||
|
||||
TRANSPORT_LANDING_ALT = 1000
|
||||
|
||||
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
||||
INTERCEPT_MAX_DISTANCE = 200000
|
||||
|
||||
GROUP_VERTICAL_OFFSET = 300
|
||||
RTB_ALTITUDE = 800
|
||||
RTB_DISTANCE = 5000
|
||||
HELI_ALT = 500
|
||||
|
||||
|
||||
class AircraftConflictGenerator:
|
||||
escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]]
|
||||
vertical_offset = None # type: int
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings):
|
||||
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings, game):
|
||||
self.m = mission
|
||||
self.game = game
|
||||
self.settings = settings
|
||||
self.conflict = conflict
|
||||
self.vertical_offset = 0
|
||||
self.escort_targets = []
|
||||
|
||||
def _start_type(self) -> StartType:
|
||||
return self.settings.cold_start and StartType.Cold or StartType.Warm
|
||||
|
||||
def _group_point(self, point) -> Point:
|
||||
distance = randint(
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
|
||||
)
|
||||
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0])
|
||||
|
||||
def _split_to_groups(self, dict: db.PlaneDict, clients: db.PlaneDict = None) -> typing.Collection[typing.Tuple[FlyingType, int, int]]:
|
||||
for flying_type, count in dict.items():
|
||||
if clients:
|
||||
client_count = clients.get(flying_type, 0)
|
||||
else:
|
||||
client_count = 0
|
||||
|
||||
while count > 0:
|
||||
group_size = min(count, 4)
|
||||
client_size = max(min(client_count, 4), 0)
|
||||
|
||||
yield (flying_type, group_size, client_size)
|
||||
count -= group_size
|
||||
client_count -= client_size
|
||||
|
||||
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):
|
||||
did_load_loadout = False
|
||||
unit_type = group.units[0].unit_type
|
||||
|
||||
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)
|
||||
@@ -104,33 +72,63 @@ 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)):
|
||||
if single_client:
|
||||
group.units[idx].set_player()
|
||||
else:
|
||||
group.units[idx].set_client()
|
||||
|
||||
# Do not generate player group with late activation.
|
||||
if group.late_activation:
|
||||
group.late_activation = False
|
||||
|
||||
# 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)
|
||||
|
||||
|
||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||
|
||||
if unit_type in helicopters.helicopter_map.values():
|
||||
print(unit_type)
|
||||
# TODO : refactor this following bad specific special case code :(
|
||||
|
||||
if unit_type in helicopters.helicopter_map.values() and unit_type not in [UH_1H]:
|
||||
group.set_frequency(127.5)
|
||||
else:
|
||||
group.set_frequency(251.0)
|
||||
if unit_type not in [P_51D_30_NA, P_51D, SpitfireLFMkIX, SpitfireLFMkIXCW, P_47D_30, I_16, FW_190A8, FW_190D9, Bf_109K_4]:
|
||||
group.set_frequency(251.0)
|
||||
else:
|
||||
# WW2
|
||||
if unit_type in [FW_190A8, FW_190D9, Bf_109K_4, Ju_88A4]:
|
||||
group.set_frequency(40)
|
||||
else:
|
||||
group.set_frequency(124.0)
|
||||
|
||||
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
|
||||
# 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:
|
||||
for unit in group.units:
|
||||
unit.fuel = Su_33.fuel_max * 0.8
|
||||
|
||||
|
||||
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
|
||||
assert unit is not None
|
||||
|
||||
if start_type is None:
|
||||
start_type = self._start_type()
|
||||
|
||||
logging.info("airgen: {} for {} at {}".format(unit_type, side.id, airport))
|
||||
return self.m.flight_group_from_airport(
|
||||
country=side,
|
||||
name=name,
|
||||
aircraft_type=unit_type,
|
||||
airport=self.m.terrain.airport_by_id(airport.id),
|
||||
airport=airport,
|
||||
maintask=None,
|
||||
start_type=self._start_type(),
|
||||
start_type=start_type,
|
||||
group_size=count,
|
||||
parking_slots=None)
|
||||
|
||||
@@ -138,18 +136,17 @@ class AircraftConflictGenerator:
|
||||
assert count > 0
|
||||
assert unit is not None
|
||||
|
||||
self.vertical_offset += GROUP_VERTICAL_OFFSET
|
||||
if unit_type in helicopters.helicopter_map.values():
|
||||
alt = WARM_START_HELI_ALT + self.vertical_offset
|
||||
alt = WARM_START_HELI_ALT
|
||||
speed = WARM_START_HELI_AIRSPEED
|
||||
else:
|
||||
alt = WARM_START_ALTITUDE + self.vertical_offset
|
||||
alt = WARM_START_ALTITUDE
|
||||
speed = WARM_START_AIRSPEED
|
||||
|
||||
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
||||
|
||||
logging.info("airgen: {} for {} at {} at {}".format(unit_type, side.id, alt, speed))
|
||||
return self.m.flight_group(
|
||||
group = self.m.flight_group(
|
||||
country=side,
|
||||
name=name,
|
||||
aircraft_type=unit_type,
|
||||
@@ -161,10 +158,16 @@ class AircraftConflictGenerator:
|
||||
start_type=self._start_type(),
|
||||
group_size=count)
|
||||
|
||||
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup]) -> FlyingGroup:
|
||||
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], 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,
|
||||
@@ -172,7 +175,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):
|
||||
@@ -180,7 +183,9 @@ class AircraftConflictGenerator:
|
||||
return self._generate_inflight(name, side, unit_type, count, client_count, at)
|
||||
elif isinstance(at, Group):
|
||||
takeoff_ban = unit_type in db.CARRIER_TAKEOFF_BAN
|
||||
if not takeoff_ban:
|
||||
ai_ban = client_count == 0 and self.settings.only_player_takeoff
|
||||
|
||||
if not takeoff_ban and not ai_ban:
|
||||
return self._generate_at_group(name, side, unit_type, count, client_count, at)
|
||||
else:
|
||||
return self._generate_inflight(name, side, unit_type, count, client_count, at.position)
|
||||
@@ -197,17 +202,26 @@ class AircraftConflictGenerator:
|
||||
else:
|
||||
assert False
|
||||
|
||||
def _add_radio_waypoint(self, group: FlyingGroup, position, altitude: int, airspeed: int = 600):
|
||||
point = group.add_waypoint(position, altitude, airspeed)
|
||||
point.alt_type = "RADIO"
|
||||
return point
|
||||
|
||||
def _rtb_for(self, group: FlyingGroup, cp: ControlPoint, at: db.StartingPosition = None):
|
||||
if not at:
|
||||
at = cp.at
|
||||
position = at if isinstance(at, Point) else at.position
|
||||
|
||||
if isinstance(at, Point):
|
||||
group.add_waypoint(at, RTB_ALTITUDE)
|
||||
elif isinstance(at, Group):
|
||||
group.add_waypoint(at.position, RTB_ALTITUDE)
|
||||
elif issubclass(at, Airport):
|
||||
group.add_waypoint(at.position, RTB_ALTITUDE)
|
||||
last_waypoint = group.points[-1]
|
||||
if last_waypoint is not None:
|
||||
heading = position.heading_between_point(last_waypoint.position)
|
||||
tod_location = position.point_from_heading(heading, RTB_DISTANCE)
|
||||
self._add_radio_waypoint(group, tod_location, last_waypoint.alt)
|
||||
|
||||
destination_waypoint = self._add_radio_waypoint(group, position, RTB_ALTITUDE)
|
||||
if isinstance(at, Airport):
|
||||
group.land_at(at)
|
||||
return destination_waypoint
|
||||
|
||||
def _at_position(self, at) -> Point:
|
||||
if isinstance(at, Point):
|
||||
@@ -219,266 +233,289 @@ class AircraftConflictGenerator:
|
||||
else:
|
||||
assert False
|
||||
|
||||
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition, is_quick=False, should_orbit=False):
|
||||
groups = []
|
||||
for flying_type, count, client_count in self._split_to_groups(units, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(side, flying_type),
|
||||
side=side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at)
|
||||
|
||||
group.task = Escort.name
|
||||
self._setup_group(group, CAP, client_count)
|
||||
def _setup_custom_payload(self, flight, group:FlyingGroup):
|
||||
if flight.use_custom_loadout:
|
||||
|
||||
for escorted_group, waypoint_index in self.escort_targets:
|
||||
waypoint_index += 1
|
||||
if not is_quick:
|
||||
waypoint_index += TRIGGER_WAYPOINT_OFFSET
|
||||
logging.info("Custom loadout for flight : " + flight.__repr__())
|
||||
for p in group.units:
|
||||
p.pylons.clear()
|
||||
|
||||
group.points[0].tasks.append(EscortTaskAction(escorted_group.id, engagement_max_dist=ESCORT_ENGAGEMENT_MAX_DIST, lastwpt=waypoint_index))
|
||||
for key in flight.loadout.keys():
|
||||
if "Pylon" + key in flight.unit_type.__dict__.keys():
|
||||
print(flight.loadout)
|
||||
weapon_dict = flight.unit_type.__dict__["Pylon" + key].__dict__
|
||||
if flight.loadout[key] in weapon_dict.keys():
|
||||
weapon = weapon_dict[flight.loadout[key]]
|
||||
group.load_pylon(weapon, int(key))
|
||||
else:
|
||||
logging.warning("Pylon not found ! => Pylon" + key + " on " + str(flight.unit_type))
|
||||
|
||||
if should_orbit:
|
||||
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
||||
|
||||
orbit_waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
||||
orbit_waypoint.tasks.append(orbit_task)
|
||||
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||
def generate_flights(self, cp, country, flight_planner:FlightPlanner):
|
||||
|
||||
groups.append(group)
|
||||
return groups
|
||||
# Clear pydcs parking slots
|
||||
if cp.airport is not None:
|
||||
for ps in cp.airport.parking_slots:
|
||||
ps.unit_id = None
|
||||
|
||||
def generate_cas_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
for flight in flight_planner.flights:
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
|
||||
side=self.conflict.attackers_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
if flight.client_count == 0 and self.game.position_culled(flight.from_cp.position):
|
||||
logging.info("Flight not generated : culled")
|
||||
continue
|
||||
|
||||
waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
group = self.generate_planned_flight(cp, country, flight)
|
||||
self.setup_flight_group(group, flight, flight.flight_type)
|
||||
self.setup_group_activation_trigger(flight, group)
|
||||
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
def setup_group_activation_trigger(self, flight, group):
|
||||
if flight.scheduled_in > 0 and flight.client_count == 0:
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
|
||||
side=self.conflict.attackers_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
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
|
||||
|
||||
escort_until_waypoint = None
|
||||
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:
|
||||
activation_trigger.add_condition(
|
||||
CoalitionHasAirdrome(self.game.get_player_coalition_id(), flight.from_cp.id))
|
||||
else:
|
||||
activation_trigger.add_condition(
|
||||
CoalitionHasAirdrome(self.game.get_enemy_coalition_id(), flight.from_cp.id))
|
||||
|
||||
for name, pos in targets:
|
||||
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
||||
if escort_until_waypoint is None:
|
||||
escort_until_waypoint = waypoint
|
||||
if flight.flight_type == FlightType.INTERCEPTION:
|
||||
self.setup_interceptor_triggers(group, flight, activation_trigger)
|
||||
|
||||
group.task = GroundAttack.name
|
||||
self._setup_group(group, GroundAttack, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
group.add_trigger_action(StartCommand())
|
||||
activation_trigger.add_action(AITaskPush(group.id, len(group.tasks)))
|
||||
|
||||
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.defenders_side, flying_type),
|
||||
side=self.conflict.defenders_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
location = self._group_point(self.conflict.air_defenders_location)
|
||||
insertion_point = self.conflict.find_insertion_point(location)
|
||||
waypoint = group.add_waypoint(insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
if self.conflict.is_vector:
|
||||
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
||||
group.add_waypoint(destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||
self._rtb_for(group, self.conflict.to_cp, at)
|
||||
|
||||
def generate_ship_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, target_groups: typing.Collection[ShipGroup], at: db.StartingPosition = None, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
|
||||
side=self.conflict.attackers_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
for target_group in target_groups:
|
||||
wayp.tasks.append(AttackGroup(target_group.id))
|
||||
|
||||
group.task = AntishipStrike.name
|
||||
self._setup_group(group, AntishipStrike, client_count)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(wayp)))
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_attackers_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for g in self._generate_escort(
|
||||
side=self.conflict.attackers_side,
|
||||
units=attackers,
|
||||
clients=clients,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location),
|
||||
is_quick=at is None,
|
||||
should_orbit=True):
|
||||
self._rtb_for(g, self.conflict.from_cp, at)
|
||||
|
||||
def generate_defenders_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for g in self._generate_escort(
|
||||
side=self.conflict.defenders_side,
|
||||
units=escort,
|
||||
clients=clients,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location),
|
||||
is_quick=at is None,
|
||||
should_orbit=False):
|
||||
self._rtb_for(g, self.conflict.to_cp, at)
|
||||
|
||||
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.defenders_side, flying_type),
|
||||
side=self.conflict.defenders_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
group.task = CAP.name
|
||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.to_cp, at)
|
||||
|
||||
def generate_migcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(patrol, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
|
||||
side=self.conflict.attackers_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
|
||||
def generate_barcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(patrol, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.defenders_side, flying_type),
|
||||
side=self.conflict.defenders_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
self.m.triggerrules.triggers.append(activation_trigger)
|
||||
else:
|
||||
heading = group.position.heading_between_point(self.conflict.position)
|
||||
waypoint = group.add_waypoint(self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
||||
WARM_START_ALTITUDE,
|
||||
WARM_START_AIRSPEED)
|
||||
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
||||
group.late_activation = True
|
||||
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):
|
||||
if flight.from_cp.captured:
|
||||
activation_trigger.add_condition(CoalitionHasAirdrome(self.game.get_player_coalition_id(), flight.from_cp.id))
|
||||
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 aircrafts 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 aircrafts 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"
|
||||
|
||||
if flight.start_type == "In Flight":
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||
side=country,
|
||||
unit_type=flight.unit_type,
|
||||
count=flight.count,
|
||||
client_count=0,
|
||||
at=cp.position)
|
||||
else:
|
||||
st = StartType.Runway
|
||||
if flight.start_type == "Cold":
|
||||
st = StartType.Cold
|
||||
elif flight.start_type == "Warm":
|
||||
st = StartType.Warm
|
||||
|
||||
if cp.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]:
|
||||
group_name = cp.get_carrier_group_name()
|
||||
group = self._generate_at_group(
|
||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||
side=country,
|
||||
unit_type=flight.unit_type,
|
||||
count=flight.count,
|
||||
client_count=0,
|
||||
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),
|
||||
side=country,
|
||||
unit_type=flight.unit_type,
|
||||
count=flight.count,
|
||||
client_count=0,
|
||||
airport=cp.airport,
|
||||
start_type=st)
|
||||
except Exception:
|
||||
# Generated when there is no place on Runway or on Parking Slots
|
||||
flight.start_type = "In Flight"
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||
side=country,
|
||||
unit_type=flight.unit_type,
|
||||
count=flight.count,
|
||||
client_count=0,
|
||||
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)
|
||||
for point in flight.points:
|
||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||
|
||||
|
||||
def setup_flight_group(self, group, flight, flight_type):
|
||||
|
||||
if flight_type in [FlightType.CAP, FlightType.BARCAP, FlightType.TARCAP, FlightType.INTERCEPTION]:
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.to_cp, at)
|
||||
self._setup_group(group, CAP, flight)
|
||||
# group.points[0].tasks.clear()
|
||||
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]))
|
||||
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))
|
||||
|
||||
def generate_transport(self, transport: db.PlaneDict, destination: Airport, escort=True):
|
||||
assert not escort or len(self.escort_targets) == 0
|
||||
elif flight_type in [FlightType.CAS, FlightType.BAI]:
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, flight)
|
||||
group.points[0].tasks.clear()
|
||||
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)
|
||||
group.points[0].tasks.clear()
|
||||
group.points[0].tasks.append(NoTask())
|
||||
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(OptRTBOnOutOfAmmo(OptRTBOnOutOfAmmo.Values.ASM))
|
||||
elif flight_type in [FlightType.STRIKE]:
|
||||
group.task = PinpointStrike.name
|
||||
self._setup_group(group, GroundAttack, flight)
|
||||
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)
|
||||
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))
|
||||
|
||||
for flying_type, count, client_count in self._split_to_groups(transport):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.defenders_side, flying_type),
|
||||
side=self.conflict.defenders_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=self._group_point(self.conflict.air_defenders_location))
|
||||
group.points[0].tasks.append(OptRTBOnBingoFuel(True))
|
||||
group.points[0].tasks.append(OptRestrictAfterburner(True))
|
||||
|
||||
waypoint = group.add_waypoint(destination.position.random_point_within(0, 0), TRANSPORT_LANDING_ALT)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||
group.task = Transport.name
|
||||
group.land_at(destination)
|
||||
if hasattr(flight.unit_type, 'eplrs'):
|
||||
if flight.unit_type.eplrs:
|
||||
group.points[0].tasks.append(EPLRS(group.id))
|
||||
|
||||
def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||
for flying_type, count, client_count in self._split_to_groups(interceptors, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
|
||||
side=self.conflict.attackers_side,
|
||||
unit_type=flying_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
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 = 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"
|
||||
elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE:
|
||||
|
||||
group.task = CAP.name
|
||||
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||
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:
|
||||
|
||||
wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
||||
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
||||
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)
|
||||
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
for j, t in enumerate(point.targets):
|
||||
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))
|
||||
|
||||
def generate_passenger_transport(self, helis: db.HeliDict, clients: db.HeliDict, at: db.StartingPosition):
|
||||
for heli_type, count, client_count in self._split_to_groups(helis, clients):
|
||||
group = self._generate_group(
|
||||
name=namegen.next_unit_name(self.conflict.attackers_side, heli_type),
|
||||
side=self.conflict.attackers_side,
|
||||
unit_type=heli_type,
|
||||
count=count,
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
||||
)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
group.add_waypoint(
|
||||
pos=self.conflict.position,
|
||||
altitude=HELI_ALT,
|
||||
)
|
||||
|
||||
self._setup_group(group, Transport, client_count)
|
||||
|
||||
@@ -32,33 +32,47 @@ 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
|
||||
|
||||
for i, tanker_unit_type in enumerate(db.find_unittype(Refueling, self.conflict.attackers_side.name)):
|
||||
CALLSIGNS = ["TKR", "TEX", "FUL", "FUE", ""]
|
||||
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))
|
||||
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),
|
||||
name=namegen.next_tanker_name(self.mission.country(self.game.player)),
|
||||
country=self.mission.country(self.game.player_country),
|
||||
name=namegen.next_tanker_name(self.mission.country(self.game.player_country)),
|
||||
airport=None,
|
||||
plane_type=tanker_unit_type,
|
||||
position=tanker_position,
|
||||
altitude=TANKER_ALT,
|
||||
race_distance=58000,
|
||||
frequency=130 + i,
|
||||
start_type=StartType.Warm,
|
||||
tacanchannel="{}X".format(97 + i),
|
||||
speed=574,
|
||||
tacanchannel="{}X".format(60 + i),
|
||||
)
|
||||
|
||||
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=97 + i, unit_id=tanker_group.id, aa=False))
|
||||
if tanker_unit_type != IL_78M:
|
||||
tanker_group.points[0].tasks.pop() # Override PyDCS tacan channel
|
||||
tanker_group.points[0].tasks.append(ActivateBeaconCommand(60 + i, "X", CALLSIGNS[i], True, tanker_group.units[0].id, True))
|
||||
|
||||
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
if is_awacs_enabled:
|
||||
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0]
|
||||
self.mission.awacs_flight(
|
||||
country=self.mission.country(self.game.player),
|
||||
name=namegen.next_awacs_name(self.mission.country(self.game.player),),
|
||||
plane_type=awacs_unit,
|
||||
altitude=AWACS_ALT,
|
||||
airport=None,
|
||||
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
|
||||
frequency=133,
|
||||
start_type=StartType.Warm,
|
||||
)
|
||||
try:
|
||||
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),
|
||||
name=namegen.next_awacs_name(self.mission.country(self.game.player_country)),
|
||||
plane_type=awacs_unit,
|
||||
altitude=AWACS_ALT,
|
||||
airport=None,
|
||||
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
|
||||
frequency=233,
|
||||
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")
|
||||
|
||||
|
||||
517
gen/armor.py
517
gen/armor.py
@@ -1,121 +1,440 @@
|
||||
import logging
|
||||
|
||||
from random import randint
|
||||
from itertools import zip_longest
|
||||
|
||||
from game import db
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.unittype import *
|
||||
from dcs.point import *
|
||||
from dcs.action import AITaskPush, AITaskSet
|
||||
from dcs.condition import TimeAfter, UnitDamaged, Or, GroupLifeLess
|
||||
from dcs.task import *
|
||||
from dcs.country import *
|
||||
from dcs.triggers import TriggerOnce, Event
|
||||
|
||||
from gen import namegen
|
||||
from gen.ground_forces.ai_ground_planner import CombatGroupRole, DISTANCE_FROM_FRONTLINE
|
||||
from .conflictgen import *
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 0.1, 0.3
|
||||
SPREAD_DISTANCE_SIZE_FACTOR = 0.1
|
||||
|
||||
FRONTLINE_CAS_FIGHTS_COUNT = 4, 8
|
||||
FRONTLINE_CAS_FIGHTS_COUNT = 16, 24
|
||||
FRONTLINE_CAS_GROUP_MIN = 1, 2
|
||||
FRONTLINE_CAS_PADDING = 12000
|
||||
|
||||
FIGHT_DISTANCE = 1500
|
||||
RETREAT_DISTANCE = 20000
|
||||
BREAKTHROUGH_OFFENSIVE_DISTANCE = 35000
|
||||
AGGRESIVE_MOVE_DISTANCE = 16000
|
||||
|
||||
FIGHT_DISTANCE = 3500
|
||||
|
||||
class ArmorConflictGenerator:
|
||||
def __init__(self, mission: Mission, conflict: Conflict):
|
||||
self.m = mission
|
||||
RANDOM_OFFSET_ATTACK = 250
|
||||
|
||||
class GroundConflictGenerator:
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game, player_planned_combat_groups, enemy_planned_combat_groups, player_stance):
|
||||
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.game = game
|
||||
|
||||
def _group_point(self, point) -> Point:
|
||||
distance = randint(
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
|
||||
)
|
||||
|
||||
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
|
||||
|
||||
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None):
|
||||
for c in range(count):
|
||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||
group = self.m.vehicle_group(
|
||||
side,
|
||||
namegen.next_unit_name(side, unit),
|
||||
unit,
|
||||
position=self._group_point(at),
|
||||
group_size=1,
|
||||
move_formation=PointAction.OffRoad)
|
||||
def generate(self):
|
||||
|
||||
vehicle: Vehicle = group.units[0]
|
||||
player_groups = []
|
||||
enemy_groups = []
|
||||
|
||||
combat_width = self.conflict.distance/2
|
||||
if combat_width > 500000:
|
||||
combat_width = 500000
|
||||
if combat_width < 35000:
|
||||
combat_width = 35000
|
||||
|
||||
position = Conflict.frontline_position(self.game.theater, self.conflict.from_cp, self.conflict.to_cp)
|
||||
|
||||
# Create player groups at random position
|
||||
for group in self.player_planned_combat_groups:
|
||||
if group.role == CombatGroupRole.ARTILLERY:
|
||||
distance_from_frontline = self.get_artilery_group_distance_from_frontline(group)
|
||||
else:
|
||||
distance_from_frontline = DISTANCE_FROM_FRONTLINE[group.role]
|
||||
final_position = self.get_valid_position_for_group(position, True, combat_width, distance_from_frontline)
|
||||
|
||||
if final_position is not None:
|
||||
g = self._generate_group(
|
||||
side=self.mission.country(self.game.player_country),
|
||||
unit=group.units[0],
|
||||
heading=self.conflict.heading+90,
|
||||
count=len(group.units),
|
||||
at=final_position)
|
||||
g.set_skill(self.game.settings.player_skill)
|
||||
player_groups.append((g,group))
|
||||
|
||||
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:
|
||||
if group.role == CombatGroupRole.ARTILLERY:
|
||||
distance_from_frontline = self.get_artilery_group_distance_from_frontline(group)
|
||||
else:
|
||||
distance_from_frontline = DISTANCE_FROM_FRONTLINE[group.role]
|
||||
final_position = self.get_valid_position_for_group(position, False, combat_width, distance_from_frontline)
|
||||
|
||||
if final_position is not None:
|
||||
g = self._generate_group(
|
||||
side=self.mission.country(self.game.enemy_country),
|
||||
unit=group.units[0],
|
||||
heading=self.conflict.heading - 90,
|
||||
count=len(group.units),
|
||||
at=final_position)
|
||||
g.set_skill(self.game.settings.enemy_vehicle_skill)
|
||||
enemy_groups.append((g, group))
|
||||
|
||||
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.game.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))
|
||||
self.game.jtacs.append(("Frontline " + self.conflict.from_cp.name + "/" + self.conflict.to_cp.name, code, n))
|
||||
|
||||
def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading):
|
||||
|
||||
# Disable infantry unit gen if disabled
|
||||
if not self.game.settings.perf_infantry:
|
||||
return
|
||||
|
||||
infantry_position = group.points[0].position.random_point_within(250, 50)
|
||||
|
||||
if side == self.conflict.attackers_country:
|
||||
cp = self.conflict.from_cp
|
||||
else:
|
||||
cp = self.conflict.to_cp
|
||||
|
||||
if is_player:
|
||||
faction = self.game.player_name
|
||||
else:
|
||||
faction = self.game.enemy_name
|
||||
|
||||
possible_infantry_units = db.find_infantry(faction)
|
||||
if len(possible_infantry_units) == 0:
|
||||
return
|
||||
|
||||
u = random.choice(possible_infantry_units)
|
||||
self.mission.vehicle_group(
|
||||
side,
|
||||
namegen.next_infantry_name(side, cp, u), u,
|
||||
position=infantry_position,
|
||||
group_size=1,
|
||||
heading=forward_heading,
|
||||
move_formation=PointAction.OffRoad)
|
||||
|
||||
for i in range(randint(3, 10)):
|
||||
u = random.choice(possible_infantry_units)
|
||||
position = infantry_position.random_point_within(55, 5)
|
||||
self.mission.vehicle_group(
|
||||
side,
|
||||
namegen.next_infantry_name(side, cp, u), u,
|
||||
position=position,
|
||||
group_size=1,
|
||||
heading=forward_heading,
|
||||
move_formation=PointAction.OffRoad)
|
||||
|
||||
|
||||
def plan_action_for_groups(self, stance, ally_groups, enemy_groups, forward_heading, from_cp, to_cp):
|
||||
|
||||
if not self.game.settings.perf_moving_units:
|
||||
return
|
||||
|
||||
for dcs_group, group in ally_groups:
|
||||
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:
|
||||
|
||||
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:
|
||||
# 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(-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))
|
||||
|
||||
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)
|
||||
else:
|
||||
attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE)
|
||||
dcs_group.add_waypoint(attack_point, PointAction.OnRoad)
|
||||
elif stance == CombatStance.BREAKTHROUGH:
|
||||
# In breakthrough mode, the units will move forward
|
||||
# If the enemy base is close enough, the units will attack the base
|
||||
if to_cp.position.distance_to_point(
|
||||
dcs_group.points[0].position) <= BREAKTHROUGH_OFFENSIVE_DISTANCE:
|
||||
attack_point = to_cp.position.random_point_within(500, 0)
|
||||
else:
|
||||
attack_point = self.find_offensive_point(dcs_group, forward_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE)
|
||||
dcs_group.add_waypoint(attack_point, PointAction.OnRoad)
|
||||
elif stance == CombatStance.ELIMINATION:
|
||||
# In elimination mode, the units focus on destroying as much enemy groups as possible
|
||||
targets = self.find_n_nearest_enemy_groups(dcs_group, enemy_groups, 3)
|
||||
i = 1
|
||||
for target in targets:
|
||||
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]:
|
||||
# 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)
|
||||
else:
|
||||
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
|
||||
if from_cp.position.distance_to_point(dcs_group.points[0].position) <= RETREAT_DISTANCE:
|
||||
retreat_point = from_cp.position.random_point_within(500, 250)
|
||||
else:
|
||||
retreat_point = self.find_retreat_point(dcs_group, forward_heading)
|
||||
reposition_point = retreat_point.point_from_heading(forward_heading, 10) # Another point to make the unit face the enemy
|
||||
dcs_group.add_waypoint(retreat_point, PointAction.OnRoad)
|
||||
dcs_group.add_waypoint(reposition_point, PointAction.OffRoad)
|
||||
|
||||
|
||||
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, distance)
|
||||
|
||||
def find_offensive_point(self, dcs_group, frontline_heading, distance):
|
||||
"""
|
||||
Find a point to attack
|
||||
:param dcs_group: DCS mission group we are searching an attack point for
|
||||
:param frontline_heading: Heading of the frontline
|
||||
:param distance: Distance of the offensive (how far unit should move)
|
||||
:return: dcs.mapping.Point object with the desired position
|
||||
"""
|
||||
return dcs_group.points[0].position.point_from_heading(frontline_heading, distance)
|
||||
|
||||
def find_n_nearest_enemy_groups(self, player_group, enemy_groups, n):
|
||||
"""
|
||||
Return the neaarest enemy group for the player group
|
||||
@param group Group for which we should find the nearest ennemies
|
||||
@param enemy_groups Potential enemy groups
|
||||
@param n number of nearby groups to take
|
||||
"""
|
||||
targets = []
|
||||
sorted_list = sorted(enemy_groups, key=lambda group: player_group.points[0].position.distance_to_point(group[0].points[0].position))
|
||||
for i in range(n):
|
||||
if len(sorted_list) <= i:
|
||||
break
|
||||
else:
|
||||
targets.append(sorted_list[i][0])
|
||||
return targets
|
||||
|
||||
|
||||
def find_nearest_enemy_group(self, player_group, enemy_groups):
|
||||
"""
|
||||
Search the enemy groups for a potential target suitable to armored assault
|
||||
@param group Group for which we should find the nearest ennemy
|
||||
@param enemy_groups Potential enemy groups
|
||||
"""
|
||||
min_distance = 99999999
|
||||
target = None
|
||||
for dcs_group, group in enemy_groups:
|
||||
dist = player_group.points[0].position.distance_to_point(dcs_group.points[0].position)
|
||||
if dist < min_distance:
|
||||
min_distance = dist
|
||||
target = dcs_group
|
||||
return target
|
||||
|
||||
|
||||
def get_artillery_target_in_range(self, dcs_group, group, enemy_groups):
|
||||
"""
|
||||
Search the enemy groups for a potential target suitable to an artillery unit
|
||||
"""
|
||||
rng = group.units[0].threat_range
|
||||
if len(enemy_groups) == 0:
|
||||
return None
|
||||
for o in range(10):
|
||||
potential_target = random.choice(enemy_groups)[0]
|
||||
distance_to_target = dcs_group.points[0].position.distance_to_point(potential_target.points[0].position)
|
||||
if distance_to_target < rng:
|
||||
return potential_target.points[0].position
|
||||
return None
|
||||
|
||||
|
||||
def get_artilery_group_distance_from_frontline(self, group):
|
||||
"""
|
||||
For artilery group, decide the distance from frontline with the range of the unit
|
||||
"""
|
||||
rg = group.units[0].threat_range - 7500
|
||||
if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY]:
|
||||
rg = DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY]
|
||||
if rg < DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK]:
|
||||
rg = DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK] + 100
|
||||
return rg
|
||||
|
||||
|
||||
def get_valid_position_for_group(self, conflict_position, isplayer, combat_width, distance_from_frontline):
|
||||
i = 0
|
||||
while i < 25: # 25 attempt for valid position
|
||||
heading_diff = -90 if isplayer else 90
|
||||
shifted = conflict_position[0].point_from_heading(self.conflict.heading,
|
||||
random.randint((int)(-combat_width / 2), (int)(combat_width / 2)))
|
||||
final_position = shifted.point_from_heading(self.conflict.heading + heading_diff, distance_from_frontline)
|
||||
|
||||
if self.conflict.theater.is_on_land(final_position):
|
||||
return final_position
|
||||
else:
|
||||
i = i + 1
|
||||
continue
|
||||
return None
|
||||
|
||||
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, move_formation: PointAction = PointAction.OffRoad, heading=0):
|
||||
|
||||
if side == self.conflict.attackers_country:
|
||||
cp = self.conflict.from_cp
|
||||
else:
|
||||
cp = self.conflict.to_cp
|
||||
|
||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||
group = self.mission.vehicle_group(
|
||||
side,
|
||||
namegen.next_unit_name(side, cp.id, unit), unit,
|
||||
position=self._group_point(at),
|
||||
group_size=count,
|
||||
heading=heading,
|
||||
move_formation=move_formation)
|
||||
|
||||
for c in range(count):
|
||||
vehicle: Vehicle = group.units[c]
|
||||
vehicle.player_can_drive = True
|
||||
|
||||
if not to:
|
||||
to = self.conflict.position.point_from_heading(0, 500)
|
||||
|
||||
wayp = group.add_waypoint(self._group_point(to))
|
||||
wayp.tasks = []
|
||||
|
||||
def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point):
|
||||
if attackers:
|
||||
attack_pos = position.point_from_heading(self.conflict.heading - 90, FIGHT_DISTANCE)
|
||||
attack_dest = position.point_from_heading(self.conflict.heading + 90, FIGHT_DISTANCE * 2)
|
||||
for type, count in attackers.items():
|
||||
self._generate_group(
|
||||
side=self.conflict.attackers_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=attack_pos,
|
||||
to=attack_dest,
|
||||
)
|
||||
|
||||
if defenders:
|
||||
def_pos = position.point_from_heading(self.conflict.heading + 90, FIGHT_DISTANCE)
|
||||
def_dest = position.point_from_heading(self.conflict.heading - 90, FIGHT_DISTANCE * 2)
|
||||
for type, count in defenders.items():
|
||||
self._generate_group(
|
||||
side=self.conflict.defenders_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=def_pos,
|
||||
to=def_dest,
|
||||
)
|
||||
|
||||
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
||||
for type, count in attackers.items():
|
||||
self._generate_group(
|
||||
side=self.conflict.attackers_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=self.conflict.ground_attackers_location)
|
||||
|
||||
for type, count in defenders.items():
|
||||
self._generate_group(
|
||||
side=self.conflict.defenders_side,
|
||||
unit=type,
|
||||
count=count,
|
||||
at=self.conflict.ground_defenders_location)
|
||||
|
||||
def generate_vec(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
||||
fights_count = randint(*FRONTLINE_CAS_FIGHTS_COUNT)
|
||||
single_fight_defenders_count = min(int(sum(defenders.values()) / fights_count), randint(*FRONTLINE_CAS_GROUP_MIN))
|
||||
defender_groups = list(db.unitdict_split(defenders, single_fight_defenders_count))
|
||||
|
||||
single_fight_attackers_count = min(int(sum(attackers.values()) / len(defender_groups)), randint(*FRONTLINE_CAS_GROUP_MIN))
|
||||
attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count))
|
||||
|
||||
for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups):
|
||||
position = self.conflict.position.point_from_heading(self.conflict.heading,
|
||||
random.randint(0, self.conflict.distance))
|
||||
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
||||
|
||||
def generate_passengers(self, count: int):
|
||||
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))
|
||||
|
||||
self.m.vehicle_group(
|
||||
country=self.conflict.attackers_side,
|
||||
name=namegen.next_unit_name(self.conflict.attackers_side, unit_type),
|
||||
_type=unit_type,
|
||||
position=self.conflict.ground_attackers_location,
|
||||
group_size=count
|
||||
)
|
||||
return group
|
||||
@@ -18,11 +18,14 @@ class BriefingGenerator:
|
||||
self.m = mission
|
||||
self.conflict = conflict
|
||||
self.game = game
|
||||
self.description = ""
|
||||
|
||||
self.freqs = []
|
||||
self.targets = []
|
||||
self.waypoints = []
|
||||
|
||||
self.jtacs = []
|
||||
|
||||
def append_frequency(self, name: str, frequency: str):
|
||||
self.freqs.append((name, frequency))
|
||||
|
||||
@@ -32,32 +35,156 @@ class BriefingGenerator:
|
||||
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 += "-" * 50 + "\n"
|
||||
self.description += flight_unit_name + " x " + str(flight.count) + 2 * "\n"
|
||||
|
||||
self.description += "#0 -- TAKEOFF : Take off from " + flight.from_cp.name + "\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\n"
|
||||
|
||||
group = flight.group
|
||||
if group is not None:
|
||||
for i, nav_target in enumerate(group.nav_target_points):
|
||||
self.description += nav_target.text_comment + "\n"
|
||||
self.description += "\n"
|
||||
self.description += "-" * 50 + "\n"
|
||||
|
||||
def add_ally_flight_description(self, flight):
|
||||
if flight.client_count == 0:
|
||||
flight_unit_name = db.unit_type_name(flight.unit_type)
|
||||
self.description += flight.flight_type.name + " " + flight_unit_name + " x " + str(flight.count) + ", departing in " + str(flight.scheduled_in) + " minutes \n"
|
||||
|
||||
def generate(self):
|
||||
self.waypoints.insert(0, "INITIAL")
|
||||
self.waypoints.append("RTB")
|
||||
self.waypoints.append("RTB Landing")
|
||||
|
||||
description = ""
|
||||
self.description = ""
|
||||
|
||||
if self.title:
|
||||
description += self.title
|
||||
self.description += "DCS Liberation turn #" + str(self.game.turn) + "\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
if self.description:
|
||||
description += "\n\n" + self.description
|
||||
self.generate_ongoing_war_text()
|
||||
|
||||
self.description += "\n"*2
|
||||
self.description += "Your flights:" + "\n"
|
||||
self.description += "=" * 15 + "\n\n"
|
||||
|
||||
for planner in self.game.planners.values():
|
||||
for flight in planner.flights:
|
||||
self.add_flight_description(flight)
|
||||
|
||||
self.description += "\n"*2
|
||||
self.description += "Planned ally flights:" + "\n"
|
||||
self.description += "=" * 15 + "\n"
|
||||
for planner in self.game.planners.values():
|
||||
if planner.from_cp.captured and len(planner.flights) > 0:
|
||||
self.description += "\nFrom " + planner.from_cp.full_name + " \n"
|
||||
self.description += "-" * 50 + "\n\n"
|
||||
for flight in planner.flights:
|
||||
self.add_ally_flight_description(flight)
|
||||
|
||||
if self.freqs:
|
||||
description += "\n\nCOMMS:"
|
||||
self.description += "\n\nComms Frequencies:\n"
|
||||
self.description += "=" * 15 + "\n"
|
||||
for name, freq in self.freqs:
|
||||
description += "\n{}: {}".format(name, freq)
|
||||
self.description += "{}: {}\n".format(name, freq)
|
||||
self.description += ("-" * 50) + "\n"
|
||||
|
||||
if self.targets:
|
||||
description += "\n\nTARGETS:"
|
||||
for i, (name, tp) in enumerate(self.targets):
|
||||
description += "\n#{} {} {}".format(i+1, name, "(TP {})".format(tp) if tp else "")
|
||||
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 + "\n"
|
||||
self.description += "RADIO : 127.5 Mhz AM\n"
|
||||
self.description += "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.description += "JTACS [F-10 Menu] : \n"
|
||||
self.description += "===================\n\n"
|
||||
for jtac in self.game.jtacs:
|
||||
self.description += str(jtac[0]) + " -- Code : " + str(jtac[1]) + "\n"
|
||||
|
||||
self.m.set_description_text(self.description)
|
||||
|
||||
self.m.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.AGGRESIVE:
|
||||
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 {} an {}. ",
|
||||
"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)
|
||||
|
||||
if self.waypoints:
|
||||
description += "\n\nWAYPOINTS:"
|
||||
for i, descr in enumerate(self.waypoints):
|
||||
description += "\n#{}: {}".format(i, descr)
|
||||
|
||||
self.m.set_description_text(description)
|
||||
|
||||
@@ -27,8 +27,8 @@ STRIKE_AIR_DEFENDERS_DISTANCE = 25000
|
||||
CAP_CAS_DISTANCE = 10000, 120000
|
||||
|
||||
GROUND_INTERCEPT_SPREAD = 5000
|
||||
GROUND_DISTANCE_FACTOR = 1
|
||||
GROUND_DISTANCE = 4000
|
||||
GROUND_DISTANCE_FACTOR = 1.4
|
||||
GROUND_DISTANCE = 2000
|
||||
|
||||
GROUND_ATTACK_DISTANCE = 25000, 13000
|
||||
|
||||
@@ -65,8 +65,10 @@ def _heading_sum(h, a) -> int:
|
||||
|
||||
|
||||
class Conflict:
|
||||
attackers_side = None # type: Country
|
||||
defenders_side = None # type: Country
|
||||
attackers_side = None # type: str
|
||||
defenders_side = None # type: str
|
||||
attackers_country = None # type: Country
|
||||
defenders_country = None # type: Country
|
||||
from_cp = None # type: ControlPoint
|
||||
to_cp = None # type: ControlPoint
|
||||
position = None # type: Point
|
||||
@@ -85,8 +87,10 @@ class Conflict:
|
||||
theater: ConflictTheater,
|
||||
from_cp: ControlPoint,
|
||||
to_cp: ControlPoint,
|
||||
attackers_side: Country,
|
||||
defenders_side: Country,
|
||||
attackers_side: str,
|
||||
defenders_side: str,
|
||||
attackers_country: Country,
|
||||
defenders_country: Country,
|
||||
position: Point,
|
||||
heading=None,
|
||||
distance=None,
|
||||
@@ -94,8 +98,12 @@ class Conflict:
|
||||
ground_defenders_location: Point = None,
|
||||
air_attackers_location: Point = None,
|
||||
air_defenders_location: Point = None):
|
||||
|
||||
self.attackers_side = attackers_side
|
||||
self.defenders_side = defenders_side
|
||||
self.attackers_country = attackers_country
|
||||
self.defenders_country = defenders_country
|
||||
|
||||
self.from_cp = from_cp
|
||||
self.to_cp = to_cp
|
||||
self.theater = theater
|
||||
@@ -162,16 +170,28 @@ class Conflict:
|
||||
|
||||
strength_delta = (from_cp.base.strength - to_cp.base.strength) / 1.0
|
||||
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
|
||||
ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater)
|
||||
if ground_position:
|
||||
return ground_position, _opposite_heading(attack_heading)
|
||||
else:
|
||||
logging.warning("Coudn't find frontline position between {} and {}!".format(from_cp, to_cp))
|
||||
return position, _opposite_heading(attack_heading)
|
||||
return position, _opposite_heading(attack_heading)
|
||||
|
||||
|
||||
@classmethod
|
||||
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
|
||||
initial, heading = cls.frontline_position(theater, from_cp, to_cp)
|
||||
|
||||
"""
|
||||
probe_end_point = initial.point_from_heading(heading, FRONTLINE_LENGTH)
|
||||
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ])
|
||||
intersection = probe.intersection(theater.land_poly)
|
||||
|
||||
if isinstance(intersection, geometry.LineString):
|
||||
intersection = intersection
|
||||
elif isinstance(intersection, geometry.MultiLineString):
|
||||
intersection = intersection.geoms[0]
|
||||
else:
|
||||
print(intersection)
|
||||
return None
|
||||
|
||||
return Point(*intersection.xy[0]), _heading_sum(heading, 90), intersection.length
|
||||
"""
|
||||
frontline = cls.frontline_position(theater, from_cp, to_cp)
|
||||
if not frontline:
|
||||
return None
|
||||
@@ -207,9 +227,21 @@ class Conflict:
|
||||
pos = new_pos
|
||||
else:
|
||||
return pos
|
||||
|
||||
return pos
|
||||
|
||||
"""
|
||||
probe_end_point = initial.point_from_heading(heading, max_distance)
|
||||
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y)])
|
||||
|
||||
intersection = probe.intersection(theater.land_poly)
|
||||
if intersection is geometry.LineString:
|
||||
return Point(*intersection.xy[1])
|
||||
elif intersection is geometry.MultiLineString:
|
||||
return Point(*intersection.geoms[0].xy[1])
|
||||
|
||||
return None
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> typing.Optional[Point]:
|
||||
pos = initial
|
||||
@@ -218,12 +250,22 @@ class Conflict:
|
||||
return pos
|
||||
|
||||
pos = pos.point_from_heading(heading, 500)
|
||||
"""
|
||||
probe_end_point = initial.point_from_heading(heading, max_distance)
|
||||
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ])
|
||||
|
||||
logging.info("Didn't find ground position!")
|
||||
return None
|
||||
intersection = probe.intersection(theater.land_poly)
|
||||
if isinstance(intersection, geometry.LineString):
|
||||
return Point(*intersection.xy[1])
|
||||
elif isinstance(intersection, geometry.MultiLineString):
|
||||
return Point(*intersection.geoms[0].xy[1])
|
||||
"""
|
||||
|
||||
logging.error("Didn't find ground position ({})!".format(initial))
|
||||
return initial
|
||||
|
||||
@classmethod
|
||||
def capture_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def capture_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
position = to_cp.position
|
||||
attack_raw_heading = to_cp.position.heading_between_point(from_cp.position)
|
||||
attack_heading = to_cp.find_radial(attack_raw_heading)
|
||||
@@ -241,8 +283,10 @@ class Conflict:
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=attackers_location,
|
||||
ground_defenders_location=defenders_location,
|
||||
air_attackers_location=position.point_from_heading(attack_raw_heading, CAPTURE_AIR_ATTACKERS_DISTANCE),
|
||||
@@ -250,7 +294,7 @@ class Conflict:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def strike_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def strike_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
position = to_cp.position
|
||||
attack_raw_heading = to_cp.position.heading_between_point(from_cp.position)
|
||||
attack_heading = to_cp.find_radial(attack_raw_heading)
|
||||
@@ -268,8 +312,10 @@ class Conflict:
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=attackers_location,
|
||||
ground_defenders_location=defenders_location,
|
||||
air_attackers_location=position.point_from_heading(attack_raw_heading, STRIKE_AIR_ATTACKERS_DISTANCE),
|
||||
@@ -277,20 +323,24 @@ class Conflict:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def intercept_position(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> Point:
|
||||
raw_distance = from_cp.position.distance_to_point(to_cp.position) * 1.5
|
||||
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
|
||||
|
||||
heading = _heading_sum(from_cp.position.heading_between_point(to_cp.position), random.choice([-1, 1]) * random.randint(60, 100))
|
||||
position = from_cp.position.point_from_heading(heading, distance)
|
||||
return from_cp.position.point_from_heading(heading, distance)
|
||||
|
||||
@classmethod
|
||||
def intercept_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, position: Point, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
heading = from_cp.position.heading_between_point(position)
|
||||
return cls(
|
||||
position=position.point_from_heading(position.heading_between_point(to_cp.position), INTERCEPT_CONFLICT_DISTANCE),
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=None,
|
||||
ground_defenders_location=None,
|
||||
air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, INTERCEPT_ATTACKERS_DISTANCE),
|
||||
@@ -298,7 +348,7 @@ class Conflict:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def ground_attack_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def ground_attack_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
heading = random.choice(to_cp.radials)
|
||||
initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE)
|
||||
position = Conflict._find_ground_position(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), theater)
|
||||
@@ -311,8 +361,10 @@ class Conflict:
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=position,
|
||||
ground_defenders_location=None,
|
||||
air_attackers_location=None,
|
||||
@@ -320,7 +372,38 @@ class Conflict:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def frontline_cas_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def convoy_strike_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
frontline_position, frontline_heading, frontline_length = Conflict.frontline_vector(from_cp, to_cp, theater)
|
||||
if not frontline_position:
|
||||
assert False
|
||||
|
||||
heading = frontline_heading
|
||||
starting_position = Conflict._find_ground_position(frontline_position.point_from_heading(heading, 7000),
|
||||
GROUND_INTERCEPT_SPREAD,
|
||||
_opposite_heading(heading), theater)
|
||||
if not starting_position:
|
||||
starting_position = frontline_position
|
||||
destination_position = frontline_position
|
||||
else:
|
||||
destination_position = frontline_position
|
||||
|
||||
return cls(
|
||||
position=destination_position,
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=None,
|
||||
ground_defenders_location=starting_position,
|
||||
air_attackers_location=starting_position.point_from_heading(_opposite_heading(heading), AIR_DISTANCE),
|
||||
air_defenders_location=starting_position.point_from_heading(heading, AIR_DISTANCE),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def frontline_cas_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
assert cls.has_frontline_between(from_cp, to_cp)
|
||||
position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
|
||||
|
||||
@@ -331,8 +414,10 @@ class Conflict:
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=None,
|
||||
ground_defenders_location=None,
|
||||
air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE),
|
||||
@@ -340,7 +425,7 @@ class Conflict:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def frontline_cap_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def frontline_cap_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
assert cls.has_frontline_between(from_cp, to_cp)
|
||||
|
||||
position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
|
||||
@@ -355,14 +440,16 @@ class Conflict:
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
air_attackers_location=attackers_position,
|
||||
air_defenders_location=defenders_position,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def ground_base_attack(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def ground_base_attack(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
position = to_cp.position
|
||||
attack_heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
|
||||
defense_heading = to_cp.find_radial(from_cp.position.heading_between_point(to_cp.position), ignored_radial=attack_heading)
|
||||
@@ -376,8 +463,10 @@ class Conflict:
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=None,
|
||||
ground_defenders_location=defenders_location,
|
||||
air_attackers_location=position.point_from_heading(attack_heading, AIR_DISTANCE),
|
||||
@@ -385,7 +474,7 @@ class Conflict:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def naval_intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def naval_intercept_position(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
radial = random.choice(to_cp.sea_radials)
|
||||
|
||||
initial_distance = min(int(from_cp.position.distance_to_point(to_cp.position) * NAVAL_INTERCEPT_DISTANCE_FACTOR), NAVAL_INTERCEPT_DISTANCE_MAX)
|
||||
@@ -395,15 +484,20 @@ class Conflict:
|
||||
|
||||
if not theater.is_on_land(position):
|
||||
break
|
||||
return position
|
||||
|
||||
@classmethod
|
||||
def naval_intercept_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, position: Point, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
attacker_heading = from_cp.position.heading_between_point(to_cp.position)
|
||||
return cls(
|
||||
position=position,
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=None,
|
||||
ground_defenders_location=position,
|
||||
air_attackers_location=position.point_from_heading(attacker_heading, AIR_DISTANCE),
|
||||
@@ -411,7 +505,7 @@ class Conflict:
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
def transport_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
frontline_position, heading = cls.frontline_position(theater, from_cp, to_cp)
|
||||
initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST)
|
||||
dest = cls._find_ground_position(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater)
|
||||
@@ -424,8 +518,10 @@ class Conflict:
|
||||
theater=theater,
|
||||
from_cp=from_cp,
|
||||
to_cp=to_cp,
|
||||
attackers_side=attacker,
|
||||
defenders_side=defender,
|
||||
attackers_side=attacker_name,
|
||||
defenders_side=defender_name,
|
||||
attackers_country=attacker,
|
||||
defenders_country=defender,
|
||||
ground_attackers_location=from_cp.position,
|
||||
ground_defenders_location=frontline_position,
|
||||
air_attackers_location=from_cp.position.point_from_heading(0, 100),
|
||||
|
||||
24
gen/defenses/armor_group_generator.py
Normal file
24
gen/defenses/armor_group_generator.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import random
|
||||
|
||||
from dcs.vehicles import Armor
|
||||
|
||||
from game import db
|
||||
from gen.defenses.armored_group_generator import ArmoredGroupGenerator
|
||||
|
||||
|
||||
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 None
|
||||
27
gen/defenses/armored_group_generator.py
Normal file
27
gen/defenses/armored_group_generator.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import random
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
|
||||
class ArmoredGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, unit_type):
|
||||
super(ArmoredGroupGenerator, self).__init__(game, ground_object)
|
||||
self.unit_type = unit_type
|
||||
|
||||
def generate(self):
|
||||
|
||||
grid_x = random.randint(2, 3)
|
||||
grid_y = random.randint(1, 2)
|
||||
|
||||
spacing = random.randint(30, 80)
|
||||
|
||||
index = 0
|
||||
for i in range(grid_x):
|
||||
for j in range(grid_y):
|
||||
index = index + 1
|
||||
self.add_unit(self.unit_type, "Armor#" + str(index),
|
||||
self.position.x + spacing * i,
|
||||
self.position.y + spacing * j, self.heading)
|
||||
|
||||
|
||||
@@ -26,9 +26,9 @@ WEATHER_FOG_VISIBILITY = 2500, 5000
|
||||
WEATHER_FOG_THICKNESS = 100, 500
|
||||
|
||||
RANDOM_TIME = {
|
||||
"night": 5,
|
||||
"dusk": 30,
|
||||
"dawn": 30,
|
||||
"night": 7,
|
||||
"dusk": 40,
|
||||
"dawn": 40,
|
||||
"day": 100,
|
||||
}
|
||||
|
||||
@@ -51,19 +51,29 @@ class EnviromentGenerator:
|
||||
self.conflict = conflict
|
||||
self.game = game
|
||||
|
||||
def _gen_random_time(self):
|
||||
start_time = datetime.strptime('May 25 2018 12:00AM', '%b %d %Y %I:%M%p')
|
||||
def _gen_time(self):
|
||||
|
||||
time_range = None
|
||||
for k, v in RANDOM_TIME.items():
|
||||
if self.game.settings.night_disabled and k == "night":
|
||||
continue
|
||||
start_time = self.game.current_day
|
||||
|
||||
if random.randint(0, 100) <= v:
|
||||
time_range = self.game.theater.daytime_map[k]
|
||||
break
|
||||
daytime = self.game.current_turn_daytime
|
||||
logging.info("Mission time will be {}".format(daytime))
|
||||
if self.game.settings.night_disabled:
|
||||
logging.info("Skip Night mission due to user settings")
|
||||
if daytime == "dawn":
|
||||
time_range = (8, 9)
|
||||
elif daytime == "day":
|
||||
time_range = (10, 12)
|
||||
elif daytime == "dusk":
|
||||
time_range = (12, 14)
|
||||
elif daytime == "night":
|
||||
time_range = (14, 17)
|
||||
else:
|
||||
time_range = (10, 12)
|
||||
else:
|
||||
time_range = self.game.theater.daytime_map[daytime]
|
||||
|
||||
start_time += timedelta(hours=random.randint(*time_range))
|
||||
|
||||
logging.info("time - {}, slot - {}, night skipped - {}".format(
|
||||
str(start_time),
|
||||
str(time_range),
|
||||
@@ -105,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
|
||||
@@ -115,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()
|
||||
@@ -132,7 +142,7 @@ class EnviromentGenerator:
|
||||
self._generate_wind(1)
|
||||
|
||||
def generate(self) -> EnvironmentSettings:
|
||||
self._gen_random_time()
|
||||
self._gen_time()
|
||||
self._gen_random_weather()
|
||||
|
||||
settings = EnvironmentSettings()
|
||||
|
||||
34
gen/fleet/carrier_group.py
Normal file
34
gen/fleet/carrier_group.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import random
|
||||
|
||||
from gen.sam.group_generator import GroupGenerator
|
||||
|
||||
|
||||
class CarrierGroupGenerator(GroupGenerator):
|
||||
|
||||
def __init__(self, game, ground_object, faction):
|
||||
super(CarrierGroupGenerator, self).__init__(game, ground_object)
|
||||
self.faction = faction
|
||||
|
||||
def generate(self):
|
||||
|
||||
# Add carrier
|
||||
if "aircraft_carrier" in self.faction.keys():
|
||||
|
||||
if "supercarrier" in self.faction.keys() and self.game.settings.supercarrier:
|
||||
carrier_type = random.choice(self.faction["supercarrier"])
|
||||
else:
|
||||
carrier_type = random.choice(self.faction["aircraft_carrier"])
|
||||
self.add_unit(carrier_type, "Carrier", self.position.x, self.position.y, self.heading)
|
||||
else:
|
||||
return
|
||||
|
||||
# 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 + 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 + 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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user