Compare commits

...

252 Commits

Author SHA1 Message Date
Khopa
a022f9c2e1 Changelog update 2020-06-01 15:27:45 +02:00
Khopa
5adb25a695 Using late activation trigger. Planned flight will not spawn if their home base has been captured or is contested. 2020-06-01 15:11:38 +02:00
Khopa
08c2972cf9 Added map theater : North Caucasus 2020-06-01 14:20:43 +02:00
Khopa
8132c7e676 Fix : CAP aircraft will jettison fuel tanks. 2020-06-01 13:03:35 +02:00
Khopa
8c68c9f703 Fix for payload for release versions. 2020-06-01 12:51:59 +02:00
Khopa
000b6142fd Added Ka-50 to bluefor modern. 2020-06-01 12:42:02 +02:00
Khopa
c203ded1cd Improved Mission Planning flight selection behaviour. 2020-06-01 12:36:10 +02:00
Khopa
64c5c39b2a BARCAP renamed CAP for regular airbases. 2020-06-01 11:14:30 +02:00
Khopa
a8d2a1e371 Removed Ju-88 from CAS (only has torpedo in DCS) 2020-06-01 01:49:20 +02:00
Khopa
9e5846b24a Improved WW2 support. 2020-06-01 01:27:16 +02:00
Khopa
836ff9122c Fix base defense units not being generated when other ground objects were too close. 2020-06-01 00:35:26 +02:00
Khopa
75d836358b Revert an error on sam site generation. (Disabled the wrong code.) 2020-06-01 00:05:32 +02:00
Khopa
bb11e7f90c Removed FARP AA that does not respect the factions config. 2020-05-31 23:58:43 +02:00
Khopa
cf6a71ab86 Fix issue when "no night mission" is enabled. 2020-05-31 23:50:13 +02:00
Khopa
7f7288937d Handle error when there is no AWACS for a faction. 2020-05-31 23:26:03 +02:00
Khopa
a38f9c2183 Added performance settings. 2020-05-31 23:07:54 +02:00
Khopa
7ee880cadc Generate ICLS command for carriers 2020-05-31 20:00:07 +02:00
Khopa
9ae34d474b Added Bluefor modern faction 2020-05-31 19:44:53 +02:00
Khopa
2817e2f2c8 Update for new pyDCS version. Supercarrier support added. 2020-05-31 19:19:03 +02:00
Khopa
02886a09d3 AI Strike flight with user generated STRIKE, will now perform their bombing task correctly. 2020-05-31 18:02:00 +02:00
Khopa
0b9d827ad6 AI Strike flight will bomb all their targets correctly 2020-05-31 15:43:56 +02:00
Khopa
ab3ea84d70 AI flight planner now auto generate STRIKE flights.
Fix CAS point position in predefined wpt selector.
When an airbase is captured, base defenses are re-generated for the new base owner.
2020-05-30 02:32:45 +02:00
Khopa
03a1c44659 README update for QT UI branch 2020-05-30 00:03:42 +02:00
Khopa
53364444fd Possible fix for crash when loading old save files. 2020-05-30 00:03:11 +02:00
Khopa
94040e8551 Fix new campaign wizard crashing and made the campaign generator slightly faster. 2020-05-29 23:47:13 +02:00
Khopa
34d46ee28e Fix objective name error when no more objective name are availables. 2020-05-29 23:43:26 +02:00
Khopa
59986d74f4 Generate random TACAN frequency for ships at start.
TACAN infos are displayed in briefing.
2020-05-29 03:28:09 +02:00
Khopa
8afdf5ef65 Polishing, Bug Fixes, Slight improvements to flight generator for BARCAP. 2020-05-29 03:05:27 +02:00
Khopa
fc64e57495 Ground objects have 'Objective' name for easier search. Predefined Waypoint generator entirely reworked.
Added finances menu, and info panel.
2020-05-29 00:40:06 +02:00
Khopa
6dec5ea8f8 Reworked payloads, factions, map display, carrier. Re-added Tarawa support, many minor bug fixes. UI changes. 2020-05-27 21:45:58 +02:00
Khopa
3f2aafcd28 Now possible to create/delete flights. 2020-05-25 14:54:02 +02:00
Khopa
7a3ee6b1a9 Frontline progression is now based on combat results. 2020-05-25 04:09:51 +02:00
Khopa
f7d9c62afb Updated mission launch instructions. 2020-05-25 01:22:20 +02:00
Khopa
1a26a7f346 Fix an issue on Nevada map, cp not well connected. 2020-05-25 00:52:46 +02:00
Khopa
f6ff4405f8 Merge branch 'qt_ui' of https://github.com/shdwp/dcs_liberation into qt_ui 2020-05-24 23:53:51 +02:00
Khopa
14ceb0abc6 Fix : Interceptor groups can have custom waypoints. 2020-05-24 23:50:30 +02:00
Khopa
c9a3af6065 Flight waypoint editor to select targets 2020-05-24 23:44:06 +02:00
Khopa
4e4a7fe84e Budget amount per turn, now depends on factory owned and such 2020-05-24 21:30:05 +02:00
Khopa
989d25e2e2 Updated UI ground objects icons 2020-05-24 20:53:41 +02:00
Khopa
f57e453d8d Ground war rework 2020-05-24 20:32:45 +02:00
Wrycu
fcfa785ae9 fix(save): correct openbeta detection 2020-01-18 11:29:16 -08:00
Wrycu
3de688da29 fix(payloads): fix for payloads when compiled into an exe 2020-01-18 11:28:26 -08:00
Khopa
5884d9d120 Made it possible to toggle 'flight paths' visibility. 2019-12-14 20:32:39 +01:00
Khopa
074ea5c719 JF-17 Support (Require pydcs own data export) 2019-12-09 02:09:50 +01:00
Khopa
e794446a54 Fix briefing text being wrong 2019-12-04 19:42:46 +01:00
Khopa
1320a67e1f Fix briefing generation, missing line break 2019-12-04 19:39:04 +01:00
Khopa
7c45177650 Fix issue with bullseye missing causing a crash when launching the mission in MP on a server. 2019-12-04 19:18:20 +01:00
Khopa
6b2d6ab57f Updated pydcs version in requirements.txt 2019-11-21 22:22:39 +01:00
Khopa
04add8ebb5 Fix weird import issue in operation.py. 2019-11-21 22:08:05 +01:00
Khopa
ad8138fb04 Fix an issue with player UH-1H radio 2019-11-21 19:58:28 +01:00
Khopa
6598e034c1 Briefing generation changed to match new flight. Include info for all flight containing client slots; 2019-11-21 19:56:26 +01:00
Khopa
bba51c6a23 Changed position of the carrier on Caucasus small map 2019-11-21 19:55:29 +01:00
Khopa
7d524300e5 Start window maximized 2019-11-21 19:55:03 +01:00
Khopa
a25a0031ff Improved CAS flight waypoints generated by ai flight planner. 2019-11-11 02:41:17 +01:00
Khopa
98b899c9c7 Fix for SEAD flights not being correctly configured. 2019-11-10 23:41:47 +01:00
Khopa
4424425dd4 Changed default map when creating new game. (Added 'recommended' label to the maps i really tested the most) 2019-11-03 17:32:34 +01:00
Khopa
85de3a09ea Remove triggers from generated mission for now. Do not generate the 'quick' mission
Fix range for ai flight planner so they are more likely to plan CAS flights
(It's up to the player to setup flight on the runway or in flight)
2019-11-03 17:27:30 +01:00
Khopa
e82db1fecd Only allow recruiting carrier units in aircraft carrier groups. Generate one group on carrier when possible. 2019-10-26 23:54:33 +02:00
Khopa
76638b549f Generate ship groups for carriers. Carriers can be destroyed. Faction need to have carrier available. 2019-10-26 22:59:59 +02:00
Khopa
2936df6a02 Fix error (unable to start DCS liberation if no save file exists) 2019-10-26 20:58:28 +02:00
Khopa
27e3cf8ac5 Added possibility to setup parking slot type for players' flight. 2019-10-23 17:01:41 +02:00
Khopa
adf1f8db8c Print weapons with their pretty name in loadout editor 2019-10-20 01:54:33 +02:00
Khopa
9f319ab99a Added loadout editor to mission preparation screen. 2019-10-19 16:14:40 +02:00
Khopa
48a40f2511 Stats view now has a tab for vehicles forces. Display waypoint coordinates in waypoint tab. 2019-10-19 13:12:21 +02:00
Khopa
7fbc75b375 New mission briefing menu, work in progress. 2019-10-19 00:07:37 +02:00
Khopa
65a54acd4f Generate ground forces on all active frontlines. 2019-10-17 01:45:46 +02:00
Khopa
de96552f78 Actually generate the planned flights from the flight planner. 2019-10-16 23:20:11 +02:00
Khopa
fee959940a Base menu layout improved. 2019-10-16 22:22:16 +02:00
Khopa
48748bbc39 Improved base menu to show planned AI flights and base defence. (WIP) 2019-10-16 00:41:02 +02:00
Khopa
827138fc6a AI Flight planner generate CAS & SEAD 2019-10-15 23:52:43 +02:00
Khopa
b7ee98dcd6 Flight Planner first version (just print planned flight in log but does not generate them yet) 2019-10-15 23:06:21 +02:00
Khopa
fa99df3ce7 Normandy map image added to repo (was missing in last commit). 2019-10-15 23:05:43 +02:00
Khopa
d2ea6ed2fd Don't show airbase armor & air defense. 2019-10-15 19:19:44 +02:00
Khopa
d8d17e5c18 F_14B client slots are now by default using pre-alignement. 2019-10-14 18:53:43 +02:00
Khopa
0519438292 Improved german WW2 flak group generator. 2019-10-14 01:49:30 +02:00
Khopa
09b8ff6b93 Generate AA & armor to defend airbase. Destruction status correctly tracked. 2019-10-14 01:25:48 +02:00
Khopa
bd66dcb39e Added support for Normandy WW2 map 2019-10-13 23:00:26 +02:00
Khopa
93504eaf7a Added & generated Normandy theater geometry data 2019-10-13 21:24:13 +02:00
Khopa
9e116f481e Fixed issue with P51D liveries that caused errors. 2019-10-13 17:57:38 +02:00
Khopa
bd95258176 Fixed issue with lost helicopters causing errors in debriefing.
Mission generated now configure bluefor and redfor coalition properly, so it is possible to use country that are not in the default coalitions.
Added insurgent faction.
2019-10-13 17:17:33 +02:00
Khopa
a7e202bbc8 Made it possible to capture airbase ingame; 2019-10-13 05:16:34 +02:00
Khopa
3f5f4f4bb1 New automatic de-briefing system implemented 2019-10-13 03:40:22 +02:00
Khopa
4365db0fae Fix : Create default blank liveries for the F_16C_50 to avoid errors at mission generation; 2019-10-12 11:17:00 +02:00
Khopa
82bb608fd3 Mission units destroyed are stored in a json file being written mission runtime. (First step that will remove the need to save the debriefing manually after mission)
Using Mist framework to do this in the mission script env.
2019-10-12 03:21:33 +02:00
Khopa
ba0b3adf71 Added some factions, refactor bluefor/redfor distinction code. 2019-10-12 00:02:07 +02:00
Khopa
707e1f8b67 Refactored factions in separates files to declutter db.py. 2019-10-11 23:08:00 +02:00
Khopa
7f97e894a3 Fix issue with Zu23 Ural aa site generator 2019-10-11 02:07:08 +02:00
Khopa
19118f7b84 Update factions. 2019-10-11 02:05:18 +02:00
Khopa
63c46d1b21 Removed placeholder text. Polished UI when no saved game is found. 2019-10-11 01:27:29 +02:00
Khopa
4c3ff906ff Improved layout for airbase window layout.
Fixed missing icons for some windows.
2019-10-11 01:14:43 +02:00
Khopa
6b77e1cce5 Added possibility to not display SAM ranges in display options. 2019-10-11 00:27:10 +02:00
Khopa
fdd8f102e6 Generate battle environment : SEAD + CAS flight.
Added more factions.
2019-10-11 00:18:25 +02:00
Khopa
2d0c195e46 Added SAM HQ-7, tweaks in db 2019-10-08 22:35:08 +02:00
Khopa
f5a5fb765b Show SAM range on map. 2019-10-08 21:42:48 +02:00
Khopa
f698cb66b8 Ally SAM are shown with blue icon on map. 2019-10-08 21:07:06 +02:00
Khopa
fc51b16e4a Added playable F16C_Bl50 support (For now, you need to update pydcs to the version on my own fork at https://github.com/Khopa/dcs for it to work)
+ Payload overrides for F/A-18C and M-2000-5
2019-10-08 20:45:45 +02:00
Khopa
0365d7a900 Fix issue with payloads loading. 2019-10-08 00:44:57 +02:00
Khopa
7a14376765 Generate CAP on whole map, for 2 hours. Added China. 2019-10-07 01:57:36 +02:00
Khopa
2167953b87 Ground objects are always generated and destroyable, even when it's not the current mission objective.
Fix : SAM site destruction status is saved correctly.
Added most SAM site to generator.
2019-10-06 16:14:13 +02:00
Khopa
17352bfcf7 Fix : Not possible anymore to start a mission by clicking the PendingDelivery event icon. 2019-10-06 01:02:49 +02:00
Khopa
d4577aa7a6 Briefing Window : Only show "clients slot" spinbox if the aircraft is flyable. 2019-10-06 00:54:02 +02:00
Khopa
e955c10170 Added smaller Caucasus map for dev test and smaller campaign 2019-10-05 13:13:43 +02:00
Khopa
9d4b6183b0 Added Iranian faction, SAM SA-2 & SAM HAWK site generation added 2019-10-05 04:36:40 +02:00
Khopa
3c9bffb557 Persian Gulf : Added Liwa, Ras Al Khaimah & Al Ain airbases 2019-10-05 03:47:39 +02:00
Khopa
d4d7b546e1 Fixed debriefing success validation for others events. 2019-10-05 02:03:51 +02:00
Khopa
9f0c17115e Fixed debriefing for Naval Intercept 2019-10-05 02:01:12 +02:00
Khopa
f6fdd3d12a Fixed issue with FrontlineAttack debriefing validation 2019-10-05 01:52:18 +02:00
Khopa
2401da2b24 Persian Gulf Theather : Disconnect Lar from Qeshm 2019-10-04 23:34:01 +02:00
Khopa
50ef8fef24 Fixed Persian Gulf Theater control points in Iran being blue when midgame is selected. 2019-10-04 23:12:27 +02:00
Khopa
079248bfeb Fixed a few issue with the briefing view. 2019-10-04 22:18:20 +02:00
Khopa
650ee9666d Fix turn counter, so that turn 0 is turn 1 2019-07-19 19:52:26 +02:00
Khopa
18bcc1bce7 Implemented stats view. 2019-07-13 14:08:44 +02:00
Khopa
73b4ec47b9 Apply CSS to whole app, but disabled for now 2019-07-13 00:53:11 +02:00
Khopa
70331d913d UI : Daytime effect on map background 2019-07-13 00:35:35 +02:00
Khopa
9c72c9a063 Put correct cursor style when mouse hover a map item. 2019-07-13 00:05:59 +02:00
Khopa
af8ae09434 Draw control point as Arc with life gauge representing power 2019-07-12 23:37:44 +02:00
Khopa
e8a8364ac2 Added back the settings menu in the new UI 2019-07-12 23:13:03 +02:00
Khopa
0e7d49488c Put CSS in resources folder, so that it is properly embedded in the release. Updated requirement and release build for new UI. 2019-07-12 19:35:30 +02:00
Khopa
405c6659b9 Cleaned up some 'print'. 2019-07-12 19:14:44 +02:00
Khopa
7ca435337f Merge 2019-07-12 00:35:54 +02:00
Khopa
024b665dd9 Commit before merge 2019-07-12 00:32:47 +02:00
Khopa
a397296624 Fix : Only possible to open event menu if event is actually displayed. 2019-07-11 21:38:59 +02:00
Khopa
adb9e38ff4 Waiting and Debriefing Window, QT UI is now functionnal. 2019-07-11 21:31:16 +02:00
Khopa
c4dc432be1 Fix issue when the faction lack units in a category of task 2019-07-11 12:16:47 +02:00
Wrycu
49795993f1 refactor(release): clean up release build process
* handle case where a dist has never been created
* ignore requirements.txt
* include map resources instead of pulling from submodule
2019-07-10 23:17:52 -07:00
Wrycu
ea6b2ab2dc chore(ignore): removed unused/personal files 2019-07-10 23:14:46 -07:00
Wrycu
44d04d3ba6 chore(requirements): add release requirement 2019-07-10 23:12:49 -07:00
Wrycu
577c171d48 chore(ignore): removed unused/personal files 2019-07-10 23:11:45 -07:00
Vasyl Horbachenko
c5edf7a581 Update README.md 2019-07-09 18:04:50 +03:00
Vasyl Horbachenko
ac67ec86b1 Delete a.py 2019-07-09 18:04:19 +03:00
Wrycu
291f538645 chore(requirements): add requirements file 2019-07-07 18:03:51 -07:00
Khopa
817a3a5e15 Briefing has start function and departure airbase selection 2019-07-07 13:57:04 +02:00
Khopa
0b87e192ce Briefing windows layout done 2019-07-07 13:36:40 +02:00
Khopa
fc83ca0de6 Added credits to pydcs in about window 2019-07-07 12:16:04 +02:00
Khopa
c621e822dc Fix artifact on map when drawing events & improved base menu 2019-07-07 12:11:12 +02:00
Khopa
1776452964 Show events on map 2019-07-06 13:54:45 +02:00
Khopa
4e5e0a4a7a ix : Possible to buy units in player base, not ennemy bases 2019-07-06 13:22:09 +02:00
Khopa
bb32f47b8c Implemented basic base menu to buy units 2019-07-06 13:21:03 +02:00
Khopa
6b5f77c415 Added classes for sub windows. (WIP) 2019-07-05 22:01:16 +02:00
Khopa
9a73c78705 Using a singleton QObject to propagate game model update across whole app 2019-07-05 21:43:14 +02:00
Khopa
8246c8e94b Top bar layout, added some icons and possible to pass turn 2019-07-05 21:02:18 +02:00
Khopa
89e8ef65ea Added about dialog & credits + icon in pass turn button 2019-07-05 20:44:01 +02:00
Khopa
2428d39308 Implemented top bar with turn counter and budget info. Added CSS stylesheet. 2019-07-05 20:05:27 +02:00
Khopa
dc91f5004e New Game Wizard with Qt UI 2019-07-05 01:58:23 +02:00
Khopa
b66bf4cc36 Qt Map drawn with line and frontline 2019-07-04 19:04:31 +02:00
Khopa
67910bce61 Map implementation 2019-07-04 09:33:19 +02:00
Khopa
2adacdba02 icon as png for qt 2019-07-03 23:35:22 +02:00
Khopa
4652e7d188 Basics for QT UI 2019-07-03 23:33:47 +02:00
Khopa
a30d4a4514 Fix in 'unit_task' method 2019-07-03 21:06:03 +02:00
Khopa
a17a9399ef Merge remote-tracking branch 'wrycu/develop' into develop_khopa
# Conflicts:
#	gen/aaa.py
2019-07-03 19:45:03 +02:00
Khopa
ba6d10cd9e Small fix and database changes. 2019-07-03 18:50:16 +02:00
Wrycu
b1576e8f15 fix(SAMs): correctly track when a SAM site is destroyed 2019-07-01 21:49:25 -07:00
Wrycu
55de28105e fix(farp): remove unsupported units from spawning in farps 2019-07-01 21:45:58 -07:00
Wrycu
40e3d59432 fix(cap): cap now properly spawns at a randomized interval after take-off 2019-06-30 12:59:01 -07:00
Khopa
665f022111 Fix Ship Gen 2019-06-30 19:15:46 +02:00
Khopa
309c10c4cb Selectable factions wip 2019-06-30 14:05:26 +02:00
Khopa
658120b8d9 Tweaked ground object generator 2019-06-30 05:48:19 +02:00
Khopa
fbd01fbfdb WIP for possibility to select player and ennemy faction 2019-06-30 05:31:22 +02:00
Khopa
09135adadc Allow selection of a time period, for campaign start 2019-06-30 02:58:04 +02:00
Khopa
0c7a36cef6 Start time generation now more correct. 2019-06-30 01:56:40 +02:00
Khopa
abf6fe69bd Generate start time of the mission according to settings. 2019-06-30 01:38:12 +02:00
Khopa
b3318583ed Added a filter so that map displayed change color according to the time of the day. 2019-06-30 01:32:47 +02:00
Khopa
288fe20def Doubled size of map image for Caucasus 2019-06-30 01:12:15 +02:00
Khopa
96ee14bf08 Icon order in topbar 2019-06-30 01:11:46 +02:00
Khopa
00b9ba0f32 Display current day time icon in top bar and improved overall look and feel. 2019-06-30 01:02:50 +02:00
Khopa
2a63f7b187 Drew and added misc icons 2019-06-30 00:42:07 +02:00
Khopa
c655d97109 Drew a few placeholder icons for daytime 2019-06-30 00:25:18 +02:00
Khopa
fd26700867 Show current turn, date and time of the day in overview. 2019-06-30 00:14:33 +02:00
Khopa
df5d9782e7 Added turn counter, top bar refresh correctly. 2019-06-29 21:06:05 +02:00
Khopa
61af07bd79 Fixed pydoc 2019-06-27 22:47:01 +02:00
Khopa
0e008ac87b Added method to get DCS saved games directory 2019-06-27 22:41:58 +02:00
Khopa
28eab870d4 Added methods to retrieve the DCS installation directory (with Steam/Standalone version abstraction) 2019-06-27 22:38:05 +02:00
Khopa
86aedaf631 Added a module to check DCS environment, to be completed. 2019-06-27 21:58:12 +02:00
Khopa
e242851260 Optimized imports in debriefing.py 2019-06-27 21:43:09 +02:00
Khopa
c884fb7262 Merge remote-tracking branch 'wrycu/develop' into develop_khopa
# Conflicts:
#	ui/overviewcanvas.py
2019-06-27 21:24:01 +02:00
Khopa
b6104fbdaf Removed idea files from version control 2019-06-27 21:03:14 +02:00
Khopa
9305f1d483 Added xml idea files in gitignore 2019-06-27 21:00:02 +02:00
Khopa
048cd22d85 Removed idea files from version control 2019-06-27 20:56:23 +02:00
Khopa
e1ea9664dc Added idea files to gitignore 2019-06-27 20:54:36 +02:00
Wrycu
72d3863f8a feat(intercept): add random delay to enemy intercept 2019-06-25 22:21:12 -07:00
Wrycu
49de53f1c9 feat(strike): add chance of AAA/tanks to strike targets 2019-06-24 20:44:28 -07:00
Wrycu
b17e34351c fix(strike): spawn air defense SAM sites on strike 2019-06-24 19:01:56 -07:00
Wrycu
84b1df75c2 feat(attack): air defense spawns further from the thing they are defending 2019-06-23 14:27:07 -07:00
Wrycu
89f122c325 feat(attack): add multi-unit SAM site support 2019-06-23 13:43:16 -07:00
Wrycu
e9103acb07 feat(attack): base attack - now spawn all CAP aircraft when a base is attacked 2019-06-22 14:30:27 -07:00
Wrycu
c7eae7e97a fix(base): correct AA spawn 2019-06-22 14:17:28 -07:00
Wrycu
f5851d09f5 fix(overview): correct negative radius 2019-06-22 14:15:24 -07:00
Vasyl Horbachenko
ba8112280d Merge pull request #66 from shdwp/develop
Develop
2019-05-31 16:49:03 +03:00
Vasyl Horbachenko
4cc373595c Update README.md 2019-05-31 16:39:18 +03:00
Wrycu
36aa4edb05 refactor(units): add SAMs to air defense 2019-05-30 21:32:39 -07:00
Wrycu
26840373ed refactor(turns): modify turn settings
* raise air defense limits
* enemy gains more units even if no action is taken on a turn
* player gains money even if no action was taken on a turn
2019-05-30 21:31:50 -07:00
Vasyl Horbachenko
4c5a2650d1 Merge pull request #58 from JohanAberg/patch-1
ValueError: negative radius
2019-05-21 04:50:30 +03:00
Johan Aberg
b99cdfb5c3 ValueError: negative radius
When cp.base.strength is zero 0, radius_m becomes negative and an ValueError is raised.

INFO:root:Commision Al Maktoum: {<class 'dcs.vehicles.Armor.APC_BTR_80'>: 3}
ERROR:root:<class 'ValueError'>
Traceback (most recent call last):
  File "tkinter\__init__.py", line 1702, in __call__
  File "ui\window.py", line 156, in dismiss
  File "ui\mainmenu.py", line 29, in display
  File "ui\overviewcanvas.py", line 590, in update
  File "ui\overviewcanvas.py", line 252, in draw
  File "ui\overviewcanvas.py", line 312, in draw_map
  File "ui\overviewcanvas.py", line 331, in draw_bases
ValueError: negative radius

Proposed fix:
radius_m = max(radius * cp.base.strength - 2, 0)
2019-04-04 19:10:59 +13:00
Vasyl Horbachenko
6ec14e744e fixed starting bat; updated escort options for Strikes 2019-03-31 19:55:27 +03:00
Vasyl Horbachenko
310db66c22 Merge branch 'develop' of https://github.com/shdwp/dcs_liberation into develop 2019-03-27 11:53:14 +02:00
Vasyl Horbachenko
c7c2b9a248 special flight for AI to SEAD; AI bomb task 2019-03-27 11:53:00 +02:00
Vasyl Horbachenko
b697a8b40a cleanups & minor fixesh 2019-03-27 10:41:40 +02:00
Vasyl Horbachenko
c152b49b88 minor fixes; F-14B 2019-03-27 08:47:29 +02:00
Vasyl Horbachenko
2356fc2bbf Merge pull request #54 from Khopa/develop_khopa
Re-added missing png icons
2019-01-23 23:27:34 +02:00
Vasyl Horbachenko
f7e2c8921c integration tests for operation generation; adjusted waypoint altitude for AI to not fly too low; removed C101 from list of generated AI aircrafts 2019-01-16 01:07:24 +02:00
Khopa
604352f8df Re-added missing icons (they were somehow removed from the repository, and the software couldn't be launched) 2018-12-03 21:06:47 +01:00
Vasyl Horbachenko
fbbe56f954 fixes to convoy strikes; UI updates; don't generate helis for enemy 2018-11-06 04:06:59 +02:00
Vasyl Horbachenko
7842c69ebb fixed incompatible missions being available from carriers 2018-11-06 02:59:24 +02:00
Vasyl Horbachenko
e1d50f1f27 added missing assets; convoy strike event 2018-11-06 02:33:38 +02:00
Vasyl Horbachenko
9d0997624b fixes for selectable departure; generate statics for used units; raised RTB alt 2018-11-05 03:17:06 +02:00
Vasyl Horbachenko
355cd3e0e4 display events on map 2018-11-04 04:50:51 +02:00
Vasyl Horbachenko
af5cd57094 WIP: display events on the map 2018-11-04 04:06:44 +02:00
Vasyl Horbachenko
8f85101cec WIP: display events on the map; start events from adjacent CPs 2018-11-04 02:38:14 +02:00
Vasyl Horbachenko
97be483624 Merge pull request #43 from Khopa/sdl_map
Scrollable & Zoomable Map view
2018-11-04 00:58:14 +02:00
Khopa
1ff5721912 Possible to toggle map size 2018-11-03 23:56:10 +01:00
Vasyl Horbachenko
80cbc663bf Merge pull request #45 from calvinmorrow/labels_and_coalition_view
Labels and coalition view
2018-11-04 00:54:21 +02:00
Vasyl Horbachenko
b3e729af0d Merge pull request #44 from calvinmorrow/bugfix_37
Add initiatorMissionID key to multiplayer debriefing table
2018-11-03 20:45:56 +02:00
Calvin Morrow
3ed864021d Move log button and minimize padding to fit existing window size 2018-10-31 15:24:34 -07:00
Calvin Morrow
8bc269fa99 Add labels game setting and map visibility setting to mission generation forced options 2018-10-31 15:11:12 -07:00
Khopa
c656c0f7e4 Confirm dialog to start a new game 2018-10-31 17:18:08 +01:00
Khopa
858e5d2d04 Do redraw the map when declutter options are toggled 2018-10-31 14:35:17 +01:00
Khopa
903a8db46f Commented debug mouse cursos position rect 2018-10-31 14:26:23 +01:00
Khopa
5aa731853d The zoom is a bit less annoying to use 2018-10-31 14:24:37 +01:00
Khopa
540096178c Display zoom level on pygame overlay in bottom right corner 2018-10-31 14:22:49 +01:00
Khopa
ed2a611197 Removed unnecessary print 2018-10-31 14:14:17 +01:00
Khopa
1c61b0b5a2 Refactored the way icons are loaded + reformat 2018-10-31 13:10:07 +01:00
Calvin Morrow
82f7e5d0c4 Add initiatorMissionID key to multiplayer debriefing table for dead unit detection 2018-10-30 19:40:59 -07:00
Khopa
ecb2c86dc4 Change order of map view options 2018-10-31 01:29:47 +01:00
Khopa
5cbbc3b1ab Improved base intel view 2018-10-31 01:22:08 +01:00
Khopa
e0b2e178f9 Display detailled info about base when mouse hover 2018-10-31 00:59:02 +01:00
Khopa
390974ba0f Icons for all kind of targets 2018-10-31 00:20:29 +01:00
Khopa
91379ff7d9 Instructions for map + tweaks (enable AA for text) 2018-10-30 23:32:27 +01:00
Khopa
1fe9e56997 Added missing resources. Bigger maps for nevada & caucasus 2018-10-30 20:30:10 +01:00
Khopa
e10b853712 Much better performance for the pygame viewport. 2018-10-30 20:00:14 +01:00
Khopa
7d70862e72 Work in progress : features restored. 2018-10-30 17:12:35 +01:00
Khopa
90bdcec7ff Render map on a pygame SDL layer. WIP 2018-10-30 14:47:31 +01:00
Vasyl Horbachenko
63da350223 armor balance tweaks 2018-10-23 04:04:30 +03:00
Vasyl Horbachenko
854f31cb7a armor balance improvements; fixed trigger for farps 2018-10-23 03:40:12 +03:00
Vasyl Horbachenko
eba6daf6c8 Merge remote-tracking branch 'origin/master' 2018-10-23 03:08:13 +03:00
Vasyl Horbachenko
911d57b415 fixed FOB generation 2018-10-23 03:08:01 +03:00
Vasyl Horbachenko
d91e0344a7 Update README.md 2018-10-23 00:54:55 +03:00
Vasyl Horbachenko
e9d7ee51f3 readme upd 2018-10-22 02:29:42 +03:00
Vasyl Horbachenko
9053408e13 Merge branch 'develop' 2018-10-22 02:18:26 +03:00
Vasyl Horbachenko
8f4094ee98 number of fixes 2018-10-22 02:13:38 +03:00
Vasyl Horbachenko
933e064079 fixed debriefing crash 2018-10-14 04:30:46 +03:00
Vasyl Horbachenko
274e08dd8b quick mission debriefing fixed; adjusted constants 2018-10-14 04:09:05 +03:00
Vasyl Horbachenko
05c968edc2 added Gazelle as CAS aircraft + minor fixes 2018-10-13 23:38:26 +03:00
Vasyl Horbachenko
270820de0b added Gazelle as CAS aircraft 2018-10-13 22:50:26 +03:00
Vasyl Horbachenko
e049a97bec minor text update 2018-10-13 22:30:43 +03:00
Vasyl Horbachenko
e2306ba0f3 new weather, strike objectives placement fixes & tarawa for av8b 2018-10-13 22:28:30 +03:00
Vasyl Horbachenko
6d0f488672 updated version compatibility check 2018-10-13 04:44:59 +03:00
Vasyl Horbachenko
8df4607e50 Update README.md 2018-09-14 23:49:50 +03:00
Vasyl Horbachenko
49f2c00d76 Update README.md 2018-09-09 04:39:53 +03:00
Vasyl Horbachenko
d284305323 Update README.md 2018-09-09 04:39:03 +03:00
Vasyl Horbachenko
c32ac8577c Update README.md 2018-09-09 04:38:47 +03:00
Vasyl Horbachenko
0d5530f5ea Update README.md 2018-08-15 15:14:19 +03:00
Vasyl Horbachenko
a528249062 Update README.md 2018-08-15 15:13:49 +03:00
476 changed files with 28353 additions and 3637 deletions

13
.gitignore vendored
View File

@@ -1,14 +1,13 @@
*.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/

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "submodules/dcs"]
path = submodules/dcs
url = https://github.com/pydcs/dcs

View File

@@ -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
View File

@@ -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
View File

@@ -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>

View File

@@ -1,8 +1,12 @@
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player liberation dynamic campaign.
![Logo](https://i.imgur.com/c2k18E1.png)
[Installation instructions/Manual](https://github.com/shdwp/dcs_liberation/wiki)
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player semi dynamic campaign.
Inspired by *ARMA Liberation* mission.
DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation
and [Mist](https://github.com/mrSkortch/MissionScriptingTools) for mission scripting
Uses [pydcs](http://github.com/pydcs/dcs) for mission generation.
## Resources
* [Getting Started](https://github.com/Khopa/dcs_liberation/wiki/Getting-started)
* [Tutorials](https://github.com/Khopa/dcs_liberation/wiki/Tutorial-01-:-UI)

View File

@@ -1,21 +1,16 @@
#!/usr/bin/env python3
import logging
import os
import re
import sys
import dcs
import logging
import theater.caucasus
import theater.persiangulf
import theater.nevada
import ui.window
import ui.corruptedsavemenu
import ui.mainmenu
import ui.newgamemenu
import ui.corruptedsavemenu
import ui.window
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"
@@ -43,7 +38,7 @@ def is_version_compatible(save_version):
if current_version_components == save_version_components:
return True
if save_version in ["1.4_rc1", "1.4_rc2", "1.4_rc3"]:
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]:
@@ -53,40 +48,11 @@ def is_version_compatible(save_version):
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()
ui.newgamemenu.NewGameMenu(w, w.start_new_game).display()
else:
game.settings.version = VERSION_STRING
proceed_to_main_menu(game)

29
changelog.md Normal file
View File

@@ -0,0 +1,29 @@
#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)

1
dcs
View File

@@ -1 +0,0 @@
submodules/dcs/dcs

File diff suppressed because it is too large Load Diff

View File

@@ -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 *

View File

@@ -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

View File

@@ -1,50 +1,69 @@
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
return self.departure_cp
@property
def threat_description(self) -> str:
@@ -61,17 +80,43 @@ class Event:
def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
return []
@property
def player_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
return []
@property
def global_cp_available(self) -> bool:
return False
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)
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
@@ -79,40 +124,200 @@ class Event:
self.operation.prepare(self.game.theater.terrain, is_quick=False)
self.operation.generate()
self.operation.mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
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.mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz"))
pass
# TODO : This is not needed anymore. The player can start mission in flight from the flight planner if he want it to be quick.
# TODO : remove this method
#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
# -- AA Site groups
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)
ucount = sum([len(g.units) for g in ground_object.groups])
if ucount == 0:
ground_object.is_dead = True
# ------------------------------
# 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])
for cp in self.game.theater.controlpoints:
if cp.id == id:
pname = ""
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
elif not(cp.captured) and new_owner_coalition == coalition:
cp.captured = True
info = Information(cp.name + " captured !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn)
self.game.informations.append(info)
pname = self.game.player_name
else:
continue
cp.base.aircraft = {}
cp.base.armor = {}
cp.base.aa = {}
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
except Exception as e:
print(e)
# -----------------------------------
# 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)
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:
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
@@ -122,11 +327,12 @@ class UnitsDeliveryEvent(Event):
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 +344,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)

View File

@@ -11,8 +11,6 @@ class FrontlineAttackEvent(Event):
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())
@@ -20,9 +18,13 @@ class FrontlineAttackEvent(Event):
@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]
@property
def global_cp_available(self) -> bool:
return True
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAS:
@@ -36,9 +38,20 @@ class FrontlineAttackEvent(Event):
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
if self.game.player_name == self.attacker_name:
attacker_country = self.game.player_country
defender_country = self.game.enemy_country
else:
attacker_country = self.game.enemy_country
defender_country = self.game.player_country
# TODO : Rework
#alive_attackers = sum([v for k, v in debriefing.alive_units.get(attacker_country, {}).items() if db.unit_task(k) == PinpointStrike])
#alive_defenders = sum([v for k, v in debriefing.alive_units.get(defender_country, {}).items() if db.unit_task(k) == PinpointStrike])
#attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
attackers_success = True
if self.from_cp.captured:
return attackers_success
else:
@@ -47,36 +60,51 @@ 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()
# assert CAS in flights and CAP in flights and len(flights) == 2, "Invalid flights"
op = FrontlineAttackOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp)
armor = unitdict_from(flights[PinpointStrike])
op.setup(target=self.defenders,
attackers=db.unitdict_restrict_count(armor, sum(self.defenders.values())),
strikegroup=flights[CAS])
defenders = self.to_cp.base.assemble_attack()
max_attackers = int(math.ceil(sum(defenders.values()) * self.ATTACKER_DEFENDER_FACTOR))
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
op.setup(defenders=defenders,
attackers=attackers,
strikegroup=flights[CAS],
escort=flights[CAP],
interceptors=assigned_units_from(self.to_cp.base.scramble_interceptors(1)))
self.operation = op
def player_defending(self, flights: db.TaskForceDict):
# assert CAP in flights and len(flights) == 1, "Invalid flights"
op = FrontlineAttackOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp)
defenders = self.to_cp.base.assemble_attack()
max_attackers = int(math.ceil(sum(defenders.values())))
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
op.setup(defenders=defenders,
attackers=attackers,
strikegroup=assigned_units_from(self.from_cp.base.scramble_cas(1)),
escort=assigned_units_from(self.from_cp.base.scramble_sweep(1)),
interceptors=flights[CAP])
self.operation = op

View File

@@ -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

View File

@@ -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

View File

@@ -1,60 +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
@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 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

View File

@@ -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

View File

@@ -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) * 10
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

View File

@@ -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

View File

@@ -0,0 +1,78 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters 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,
S_3B_Tanker,
C_130,
E_3A,
UH_1H,
AH_64D,
Ka_50,
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"
]
}

View File

@@ -0,0 +1,63 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
China_2000 = {
"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,
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.Soldier_AK,
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,
], "carrier_names": [
"001 Liaoning",
"002 Shandong",
]
}

View File

@@ -0,0 +1,44 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
France_1995 = {
"country": "France",
"side": "blue",
"units": [
M_2000C,
Mirage_2000_5,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
SA342M,
SA342L,
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
]
}

View File

@@ -0,0 +1,59 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
France_2005 = {
"country": "France",
"side": "blue",
"units":[
M_2000C,
Mirage_2000_5,
FA_18C_hornet, # Standing as Rafale M
KC_135,
S_3B_Tanker,
C_130,
E_3A,
SA342M,
SA342L,
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": [
"R91 Charles de Gaulle",
], "lhanames": [
"L9013 Mistral",
"L9014 Tonerre",
"L9015 Dixmude"
]
}

View File

@@ -0,0 +1,36 @@
from dcs.planes import *
from dcs.vehicles import *
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,
],
"shorad":[
AirDefence.AAA_8_8cm_Flak_36,
]
}

View File

@@ -0,0 +1,43 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Germany_1990 = {
"country": "Germany",
"side": "blue",
"units":[
MiG_29G,
Tornado_IDS,
F_4E,
KC_135,
S_3B_Tanker,
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,
]
}

View File

@@ -0,0 +1,53 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
India_2010 = {
"country": "India",
"side": "blue",
"units": [
Mirage_2000_5,
M_2000C,
MiG_27K,
MiG_21Bis,
MiG_29S,
Su_30,
KC_135,
S_3B_Tanker,
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"
]
}

View File

@@ -0,0 +1,25 @@
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,
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
]
}

View File

@@ -0,0 +1,56 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters 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
]
}

View File

@@ -0,0 +1,37 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Israel_2000 = {
"country": "Israel",
"side": "blue",
"units":[
F_16C_50,
F_15C,
F_4E,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
AH_1W,
AH_64D,
Armor.MBT_Merkava_Mk__4,
Armor.APC_M113,
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
]
}

View File

@@ -0,0 +1,46 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters 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,
], "lha_names": [
"Giuseppe Garibaldi",
"Cavour",
]
}

View File

@@ -0,0 +1,48 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Lybia_2011 = {
"country": "Russia",
"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,
]
}

View File

@@ -0,0 +1,36 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Netherlands_1990 = {
"country": "The Netherlands",
"side": "blue",
"units": [
F_16C_50,
F_5E_3,
KC_135,
S_3B_Tanker,
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
]
}

View File

@@ -0,0 +1,50 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters 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
]
}

View File

@@ -0,0 +1,38 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters 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
]
}

View 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,
]
}

View File

@@ -0,0 +1,53 @@
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
]
}

View File

@@ -0,0 +1,69 @@
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,
]
}

View File

@@ -0,0 +1,68 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Russia_1990 = {
"country": "Russia",
"side": "red",
"units": [
MiG_23MLD,
MiG_25PD,
MiG_29A,
MiG_29S,
MiG_31,
Su_27,
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.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
], "aircraft_carrier": [
CV_1143_5_Admiral_Kuznetsov,
], "destroyer": [
FF_1135M_Rezky,
], "cruiser": [
FSG_1241_1MP_Molniya,
]
}

View File

@@ -0,0 +1,67 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Russia_2010 = {
"country": "Russia",
"side": "red",
"units": [
AJS37,
MiG_23MLD,
Su_25,
Su_27,
Su_33,
MiG_29S,
Su_25T,
Su_34,
Su_24M,
L_39ZA,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Ka_50,
Mi_8MT,
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,
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,
], "destroyer": [
FF_1135M_Rezky,
], "cruiser": [
FSG_1241_1MP_Molniya,
]
}

View File

@@ -0,0 +1,48 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
Spain_1990 = {
"country": "Spain",
"side": "blue",
"units": [
FA_18C_hornet,
AV8BNA,
F_5E_3,
C_101CC,
KC_135,
S_3B_Tanker,
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",
]
}

View 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
]
}

View File

@@ -0,0 +1,40 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Turkey_2005 = {
"country": "Turkey",
"side": "blue",
"units":[
F_16C_50,
F_4E,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
UH_1H,
OH_58D,
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
]
}

34
game/factions/uae_2005.py Normal file
View File

@@ -0,0 +1,34 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
UAE_2005 = {
"country": "United Arab Emirates",
"side": "blue",
"units":[
M_2000C,
Mirage_2000_5,
F_16C_50,
KC_135,
S_3B_Tanker,
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,
]
}

50
game/factions/uk_1990.py Normal file
View File

@@ -0,0 +1,50 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
UnitedKingdom_1990 = {
"country": "UK",
"side": "blue",
"units":[
AV8BNA, # Standing as BAE Harrier 2
Tornado_GR4,
F_4E,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
SA342L,
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,
], "lha_names": [
"HMS Invincible",
"HMS Illustrious",
"HMS Ark Royal",
]
}

View File

@@ -0,0 +1,51 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters 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
]
}

39
game/factions/usa_1944.py Normal file
View File

@@ -0,0 +1,39 @@
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_1944 = {
"country": "USA",
"side": "blue",
"units": [
P_51D,
P_51D_30_NA,
SpitfireLFMkIX,
SpitfireLFMkIXCW,
A_20G,
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,
]
}

33
game/factions/usa_1955.py Normal file
View 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,
S_3B_Tanker,
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
View 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,
S_3B_Tanker,
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
]
}

40
game/factions/usa_1965.py Normal file
View File

@@ -0,0 +1,40 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
USA_1965 = {
"country": "USA",
"side": "blue",
"units": [
F_5E_3,
F_4E,
KC_135,
S_3B_Tanker,
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
]
}

63
game/factions/usa_1990.py Normal file
View File

@@ -0,0 +1,63 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
USA_1990 = {
"country": "USA",
"side": "blue",
"units": [
F_15C,
F_14B,
FA_18C_hornet,
A_10A,
AV8BNA,
B_1B,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
UH_1H,
AH_64A,
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"
]
}

71
game/factions/usa_2005.py Normal file
View File

@@ -0,0 +1,71 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
USA_2005 = {
"country": "USA",
"side": "blue",
"units": [
F_15C,
F_14B,
FA_18C_hornet,
F_16C_50,
JF_17,
A_10C,
AV8BNA,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
UH_1H,
AH_64D,
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": [
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"
]
}

View File

@@ -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
from game.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, 5],
# events randomly present; for both enemy and player
InterceptEvent: [25, 5],
NavalInterceptEvent: [25, 5],
# events randomly present; only for the enemy
InsurgentAttackEvent: [0, 4],
}
# 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 = 17
PLAYER_BUDGET_INITIAL = 650
# Bonus multiplier logarithm base
PLAYER_BUDGET_IMPORTANCE_LOG = 2
@@ -91,136 +50,79 @@ 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):
current_unit_id = 0
current_group_id = 0
def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater, start_date: datetime):
self.settings = Settings()
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))
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 +131,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)
@@ -262,23 +164,33 @@ 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()
self._enemy_reinforcement()
self._budget_player()
if not no_action:
self._budget_player()
for cp in self.theater.enemy_points():
self._commision_units(cp)
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 +198,113 @@ 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.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
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

58
game/game_stats.py Normal file
View File

@@ -0,0 +1,58 @@
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.sam_count += sum(cp.base.aa.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.sam_count += sum(cp.base.aa.values())
turn_data.enemy_units.vehicles_count += sum(cp.base.armor.values())
self.data_per_turn.append(turn_data)

11
game/infos/information.py Normal file
View 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

View File

@@ -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.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.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()

View File

@@ -7,49 +7,68 @@ 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
defenders = None # type: db.ArmorDict
def setup(self,
target: db.ArmorDict,
defenders: db.ArmorDict,
attackers: db.ArmorDict,
strikegroup: db.AssignedUnitsDict):
strikegroup: db.AssignedUnitsDict,
escort: db.AssignedUnitsDict,
interceptors: db.AssignedUnitsDict):
self.strikegroup = strikegroup
self.target = target
self.escort = escort
self.interceptors = interceptors
self.defenders = defenders
self.attackers = attackers
def prepare(self, terrain: Terrain, is_quick: bool):
super(FrontlineAttackOperation, self).prepare(terrain, is_quick)
if self.defender_name == self.game.player:
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.mission.country(self.attacker_name),
defender=self.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
)
self.initialize(mission=self.mission,
self.initialize(mission=self.current_mission,
conflict=conflict)
def generate(self):
self.armorgen.generate_vec(self.attackers, self.target)
#if self.is_player_attack:
# self.prepare_carriers(db.unitdict_from(self.strikegroup))
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)
# ground units
# self.armorgen.generate_vec(self.attackers, self.defenders)
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_cas_strikegroup(*assigned_units_split(dict),
at=farp,
escort=len(planes_flights) == 0)
## strike group w/ heli support
#planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
#self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
#heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
#if heli_flights:
# self.briefinggen.append_frequency("FARP + Heli flights", "127.5 MHz AM")
# for farp, dict in zip(self.groundobjectgen.generate_farps(sum([x[0] for x in heli_flights.values()])),
# db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
# self.airgen.generate_cas_strikegroup(*assigned_units_split(dict),
# at=farp,
# escort=len(planes_flights) == 0)
#self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
#self.airgen.generate_defense(*assigned_units_split(self.interceptors), at=self.defenders_starting_position)
self.briefinggen.title = "Frontline CAS"
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."

View File

@@ -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.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.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()

View File

@@ -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.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.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

View File

@@ -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.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.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()

View File

@@ -1,69 +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.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
for global_cp in self.game.theater.controlpoints:
if not global_cp.is_global:
continue
ship = self.shipgen.generate_carrier(type=db.find_unittype(Carriage, self.game.player)[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
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()

View File

@@ -1,60 +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.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(self.mission, conflict)
def generate(self):
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()

View File

@@ -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"]
@@ -11,12 +12,12 @@ class Operation:
attackers_starting_position = None # type: db.StartingPosition
defenders_starting_position = None # type: db.StartingPosition
mission = None # type: dcs.Mission
current_mission = None # type: dcs.Mission
regular_mission = None # type: dcs.Mission
quick_mission = None # type: dcs.Mission
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
@@ -24,6 +25,7 @@ class Operation:
envgen = None # type: EnvironmentGenerator
groundobjectgen = None # type: GroundObjectsGenerator
briefinggen = None # type: BriefingGenerator
forcedoptionsgen = None # type: ForcedOptionsGenerator
environment_settings = None
trigger_radius = TRIGGER_RADIUS_MEDIUM
@@ -36,11 +38,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
@@ -50,69 +57,108 @@ 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.mission = mission
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.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings, self.game)
self.shipgen = ShipGenerator(mission, conflict)
self.airsupportgen = AirSupportConflictGenerator(mission, conflict, self.game)
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
self.visualgen = VisualGenerator(mission, conflict, self.game)
self.envgen = EnviromentGenerator(mission, conflict, self.game)
self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game)
self.groundobjectgen = GroundObjectsGenerator(mission, conflict, self.game)
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
player_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)
player_country = self.from_cp.captured and self.attacker_country or self.defender_country
enemy_country = self.from_cp.captured and self.defender_country or self.attacker_country
def prepare(self, terrain: Terrain, is_quick: bool):
with open("resources/default_options.lua", "r") as f:
options_dict = loads(f.read())["options"]
self.mission = dcs.Mission(terrain)
self.mission.options.load_from_dict(options_dict)
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")
if self.game.player_country and self.game.player_country in db.BLUEFOR_FACTIONS:
self.current_mission.coalition["blue"].add_country(country_dict[db.country_id_from_name(self.game.player_country)]())
self.current_mission.coalition["red"].add_country(country_dict[db.country_id_from_name(self.game.enemy_country)]())
else:
self.current_mission.coalition["blue"].add_country(country_dict[db.country_id_from_name(self.game.enemy_country)]())
self.current_mission.coalition["red"].add_country(country_dict[db.country_id_from_name(self.game.player_country)]())
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:
self.regular_mission = self.current_mission
self.current_mission.options.load_from_dict(options_dict)
self.is_quick = is_quick
if is_quick:
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 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.mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
if self.game.player in [country.name for country in self.mission.coalition["blue"].countries.values()]:
self.mission.groundControl.blue_tactical_commander = self.ca_slots
else:
self.mission.groundControl.red_tactical_commander = self.ca_slots
# ground infrastructure
# Generate ground object first
self.groundobjectgen.generate()
self.extra_aagen.generate()
# 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
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_side):
if self.game.is_player_attack(self.conflict.attackers_country):
cp = self.conflict.from_cp
else:
cp = self.conflict.to_cp
self.triggersgen.generate(player_cp=cp,
is_quick=self.is_quick,
is_quick=False,
activation_trigger_radius=self.trigger_radius,
awacs_enabled=self.is_awacs_enabled)
@@ -122,10 +168,40 @@ class Operation:
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")
# options
self.forcedoptionsgen.generate()
# briefing
# 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_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(97+i, 130+i))
if self.is_awacs_enabled:
self.briefinggen.append_frequency("AWACS", "133 MHz AM")
self.briefinggen.append_frequency("Flight", "251 MHz AM")
# Generate the briefing
self.briefinggen.generate()

View File

@@ -1,86 +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
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.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
for global_cp in self.game.theater.controlpoints:
if not global_cp.is_global:
continue
ship = self.shipgen.generate_carrier(type=db.find_unittype(Carriage, self.game.player)[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
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()

View File

@@ -1,11 +1,25 @@
class Settings:
# Difficulty settings
player_skill = "Good"
enemy_skill = "Average"
enemy_vehicle_skill = "Average"
only_player_takeoff = True
map_coalition_visibility = "All Units"
labels = "Full"
only_player_takeoff = True # Legacy parameter do not use
night_disabled = False
supercarrier = False
multiplier = 1
sams = True
cold_start = False
sams = True # Legacy parameter do not use
cold_start = False # Legacy parameter do not use
version = None
# Performance oriented
perf_red_alert_state = True
perf_smoke_gen = True
perf_artillery = True
perf_moving_units = True
perf_infantry = True

View File

@@ -9,6 +9,7 @@ from .triggergen import *
from .environmentgen import *
from .groundobjectsgen import *
from .briefinggen import *
from .forcedoptionsgen import *
from . import naming

View File

@@ -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(

View File

@@ -1,55 +1,51 @@
import logging
from dcs.action import ActivateGroup
from dcs.condition import TimeAfter, CoalitionHasAirdrome
from dcs.helicopters import UH_1H
from dcs.terrain.terrain import NoParkingSlotError
from dcs.triggers import TriggerOnce, Event
from game import db
from game.settings import Settings
from gen.flights.ai_flight_planner import FlightPlanner
from gen.flights.flight import Flight, FlightType
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
ATTACK_CIRCLE_ALT = 5000
ATTACK_CIRCLE_ALT = 1000
ATTACK_CIRCLE_DURATION = 15
CAS_ALTITUDE = 1000
RTB_ALTITUDE = 1000
HELI_ALT = 900
CAS_ALTITUDE = 800
RTB_ALTITUDE = 800
RTB_DISTANCE = 5000
HELI_ALT = 500
TRANSPORT_LANDING_ALT = 1000
TRANSPORT_LANDING_ALT = 2000
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
INTERCEPT_MAX_DISTANCE = 200000
GROUP_VERTICAL_OFFSET = 300
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:
@@ -69,9 +65,15 @@ class AircraftConflictGenerator:
else:
client_count = 0
if flying_type == F_14B:
# workaround since 2 and 3 tomcat collide on carrier
group_size = 2
else:
group_size = 4
while count > 0:
group_size = min(count, 4)
client_size = max(min(client_count, 4), 0)
group_size = min(count, group_size)
client_size = max(min(client_count, group_size), 0)
yield (flying_type, group_size, client_size)
count -= group_size
@@ -80,6 +82,9 @@ class AircraftConflictGenerator:
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task], client_count: int):
did_load_loadout = False
unit_type = group.units[0].unit_type
print("SETUP GROUP : " + str(for_task) + " -- " + str(group.name))
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
override_loadout = db.PLANE_PAYLOAD_OVERRIDES[unit_type]
if type(override_loadout) == dict:
@@ -111,13 +116,32 @@ class AircraftConflictGenerator:
else:
group.units[idx].set_client()
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
group.set_frequency(251.0)
# Do not generate player group with late activation.
if group.late_activation:
group.late_activation = False
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
# 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() and unit_type not in [UH_1H]:
group.set_frequency(127.5)
else:
if unit_type not in [P_51D_30_NA, P_51D, SpitfireLFMkIX, SpitfireLFMkIXCW, FW_190A8, FW_190D9, Bf_109K_4]:
group.set_frequency(251.0)
else:
# WW2
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, 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,
@@ -125,7 +149,7 @@ class AircraftConflictGenerator:
aircraft_type=unit_type,
airport=self.m.terrain.airport_by_id(airport.id),
maintask=None,
start_type=self._start_type(),
start_type=start_type,
group_size=count,
parking_slots=None)
@@ -133,18 +157,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,
@@ -156,6 +179,9 @@ class AircraftConflictGenerator:
start_type=self._start_type(),
group_size=count)
group.points[0].alt_type = "RADIO"
return group
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup]) -> FlyingGroup:
assert count > 0
assert unit is not None
@@ -175,7 +201,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)
@@ -192,17 +220,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):
@@ -214,11 +251,11 @@ 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):
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition, cp, 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),
name=namegen.next_unit_name(side, cp.id, flying_type),
side=side,
unit_type=flying_type,
count=count,
@@ -239,241 +276,227 @@ class AircraftConflictGenerator:
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 = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE)
orbit_waypoint.tasks.append(orbit_task)
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
groups.append(group)
return groups
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
def _setup_custom_payload(self, flight, group:FlyingGroup):
if flight.use_custom_loadout:
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))
logging.info("Custom loadout for flight : " + flight.__repr__())
for p in group.units:
p.pylons.clear()
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)
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))
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 generate_flights(self, cp, country, flight_planner:FlightPlanner):
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))
for flight in flight_planner.interceptor_flights:
group = self.generate_planned_flight(cp, country, flight)
self.setup_group_as_intercept_flight(group, flight)
self._setup_custom_payload(flight, group)
self.setup_group_activation_trigger(flight, group)
escort_until_waypoint = None
for flight in flight_planner.cap_flights:
group = self.generate_planned_flight(cp, country, flight)
self.setup_group_as_cap_flight(group, flight)
self._setup_custom_payload(flight, group)
self.setup_group_activation_trigger(flight, group)
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
for flight in flight_planner.cas_flights:
group = self.generate_planned_flight(cp, country, flight)
self.setup_group_as_cas_flight(group, flight)
self._setup_custom_payload(flight, group)
self.setup_group_activation_trigger(flight, group)
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)
for flight in flight_planner.sead_flights:
group = self.generate_planned_flight(cp, country, flight)
self.setup_group_as_sead_flight(group, flight)
self._setup_custom_payload(flight, group)
self.setup_group_activation_trigger(flight, group)
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 flight in flight_planner.strike_flights:
group = self.generate_planned_flight(cp, country, flight)
self.setup_group_as_strike_flight(group, flight)
self._setup_custom_payload(flight, group)
self.setup_group_activation_trigger(flight, group)
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)
for flight in flight_planner.custom_flights:
group = self.generate_planned_flight(cp, country, flight)
if flight.flight_type == FlightType.INTERCEPTION:
self.setup_group_as_intercept_flight(group, flight)
elif flight.flight_type in [FlightType.CAP, FlightType.TARCAP, FlightType.BARCAP]:
self.setup_group_as_cap_flight(group, flight)
elif flight.flight_type in [FlightType.CAS, FlightType.BAI]:
self.setup_group_as_cas_flight(group, flight)
elif flight.flight_type in [FlightType.STRIKE]:
self.setup_group_as_strike_flight(group, flight)
elif flight.flight_type in [FlightType.ANTISHIP]:
self.setup_group_as_antiship_flight(group, flight)
elif flight.flight_type in [FlightType.SEAD, FlightType.DEAD]:
self.setup_group_as_sead_flight(group, flight)
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))
self.setup_group_as_cap_flight(group, flight)
self._setup_custom_payload(flight, group)
self.setup_group_activation_trigger(flight, group)
group.task = CAP.name
self._setup_group(group, CAP, client_count)
self._rtb_for(group, self.conflict.to_cp, at)
def setup_group_activation_trigger(self, flight, group):
if flight.scheduled_in > 0:
group.late_activation = True
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationActivationTriggerForGroup" + str(group.id))
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in*60))
def generate_transport(self, transport: db.PlaneDict, destination: Airport, escort=True):
assert not escort or len(self.escort_targets) == 0
if(flight.from_cp.cptype == ControlPointType.AIRBASE):
if not flight.from_cp.captured:
activation_trigger.add_condition(CoalitionHasAirdrome(1, flight.from_cp.id))
else:
activation_trigger.add_condition(CoalitionHasAirdrome(2, flight.from_cp.id))
for flying_type, count, client_count in self._split_to_groups(transport):
activation_trigger.add_action(ActivateGroup(group.id))
self.m.triggerrules.triggers.append(activation_trigger)
def generate_planned_flight(self, cp, country, flight:Flight):
try:
if flight.start_type == "In Flight" or flight.client_count == 0:
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),)
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=self.m.terrain.airport_by_id(cp.at.id),
start_type=st)
except Exception:
# Generated when there is no place on Runway or on Parking Slots
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))
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
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)
return group
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))
def setup_group_as_intercept_flight(self, group, flight):
group.points[0].ETA = 0
group.late_activation = True
self._setup_group(group, Intercept, flight.client_count)
for point in flight.points:
group.add_waypoint(Point(point.x,point.y), point.alt)
group.task = CAP.name
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
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)
def setup_group_as_cap_flight(self, group, flight):
self._setup_group(group, CAP, flight.client_count)
for point in flight.points:
group.add_waypoint(Point(point.x,point.y), point.alt)
self._setup_group(group, CAP, client_count)
self._rtb_for(group, self.conflict.from_cp, at)
def setup_group_as_cas_flight(self, group, flight):
group.task = CAS.name
self._setup_group(group, CAS, flight.client_count)
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)
)
group.points[0].tasks.clear()
group.points[0].tasks.append(CASTaskAction())
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)
def setup_group_as_sead_flight(self, group, flight):
group.task = SEAD.name
self._setup_group(group, SEAD, flight.client_count)
group.points[0].tasks.clear()
group.points[0].tasks.append(SEADTaskAction())
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))
i = 1
for point in flight.points:
group.add_waypoint(Point(point.x,point.y), point.alt)
group.points[i].tasks.clear()
group.points[i].tasks.append(SEADTaskAction())
i = i + 1
def setup_group_as_strike_flight(self, group, flight):
group.task = PinpointStrike.name
self._setup_group(group, GroundAttack, flight.client_count)
group.points[0].tasks.clear()
group.points[0].tasks.append(CASTaskAction())
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))
i = 1
bombing_point_found = False
for point in flight.points:
group.add_waypoint(Point(point.x,point.y), point.alt)
if not bombing_point_found:
for t in point.targets:
if hasattr(t, "obj_name"):
buildings = self.game.theater.find_ground_objects_by_obj_name(t.obj_name)
for building in buildings:
group.points[i].tasks.append(Bombing(building.position))
else:
group.points[i].tasks.append(Bombing(t.position))
bombing_point_found = True
i = i + 1
def setup_group_as_antiship_flight(self, group, flight):
group.task = AntishipStrike.name
self._setup_group(group, AntishipStrike, flight.client_count)
group.points[0].tasks.clear()
group.points[0].tasks.append(AntishipStrikeTaskAction())
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
group.points[0].tasks.append(OptRestrictJettison(True))
for point in flight.points:
group.add_waypoint(Point(point.x,point.y), point.alt)
group.add_waypoint(
pos=self.conflict.position,
altitude=HELI_ALT,
)
self._setup_group(group, Transport, client_count)

View File

@@ -9,11 +9,11 @@ from dcs.task import *
from dcs.terrain.terrain import NoParkingSlotError
TANKER_DISTANCE = 15000
TANKER_ALT = 6000
TANKER_ALT = 4572
TANKER_HEADING_OFFSET = 45
AWACS_DISTANCE = 150000
AWACS_ALT = 15000
AWACS_ALT = 13000
class AirSupportConflictGenerator:
@@ -32,13 +32,13 @@ 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)):
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,
@@ -49,16 +49,24 @@ class AirSupportConflictGenerator:
)
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=97 + i, unit_id=tanker_group.id, aa=False))
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=133,
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")

View File

@@ -1,119 +1,325 @@
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.task import *
from dcs.country import *
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
RETREAT_DISTANCE = 20000
BREAKTHROUGH_OFFENSIVE_DISTANCE = 35000
AGGRESIVE_MOVE_DISTANCE = 16000
class ArmorConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict):
FIGHT_DISTANCE = 3500
class GroundConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game, player_planned_combat_groups, enemy_planned_combat_groups, player_stance):
self.m = 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.m.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.m.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.m.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.m.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)
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.m.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.m.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:
dcs_group.points[0].tasks.append(FireAtPoint(target, len(group.units) * 10, 100))
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(-50, 50), random.randint(-50, 50))
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(-50, 50), random.randint(-50, 50))
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)
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:
# 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 find_retreat_point(self, dcs_group, frontline_heading):
"""
Find a point to retreat to
:param dcs_group: DCS mission group we are searching a retreat point for
:param frontline_heading: Heading of the frontline
:return: dcs.mapping.Point object with the desired position
"""
return dcs_group.points[0].position.point_from_heading(frontline_heading-180, RETREAT_DISTANCE)
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.m.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, 8000)
attack_dest = position.point_from_heading(self.conflict.heading + 90, 25000)
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, 4000)
def_dest = position.point_from_heading(self.conflict.heading - 90, 25000)
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

View File

@@ -18,6 +18,7 @@ class BriefingGenerator:
self.m = mission
self.conflict = conflict
self.game = game
self.description = ""
self.freqs = []
self.targets = []
@@ -32,32 +33,54 @@ 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 += 2 * "\n" + "-" * 50 + "\n"
self.description += flight_unit_name + " x " + str(flight.count) + 2 * "\n"
self.description += "#0 -- TAKEOFF : Take off\n"
for i, wpt in enumerate(flight.points):
self.description += "#" + str(1+i) + " -- " + wpt.name + " : " + wpt.description + "\n"
self.description += "#" + str(len(flight.points) + 1) + " -- RTB\n"
self.description += "-" * 50 + "\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 += "-"*50 + "\n"
if self.description:
description += "\n\n" + self.description
for planner in self.game.planners.values():
for flight in planner.flights:
self.add_flight_description(flight)
if self.freqs:
description += "\n\nCOMMS:"
self.description += "\n\nComms Frequencies:\n"
self.description += "-" * 50 + "\n"
for name, freq in self.freqs:
description += "\n{}: {}".format(name, freq)
self.description += "\n{}: {}".format(name, freq)
self.description += "\n" + ("-" * 50) + "\n"
for cp in self.game.theater.controlpoints:
if cp.captured and cp.cptype in [ControlPointType.LHA_GROUP, ControlPointType.AIRCRAFT_CARRIER_GROUP]:
self.description += cp.name + " TACAN : "
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 "")
self.description += str(cp.tacanN)
if cp.tacanY:
self.description += "Y"
else:
self.description += "X"
self.description += " " + str(cp.tacanI) + "\n"
if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP and hasattr(cp, "icls"):
self.description += "ICLS Channel : " + str(cp.icls) + "\n"
self.description += "-" * 50 + "\n"
self.m.set_description_text(self.description)
if self.waypoints:
description += "\n\nWAYPOINTS:"
for i, descr in enumerate(self.waypoints):
description += "\n#{}: {}".format(i, descr)
self.m.set_description_text(description)

View File

@@ -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),

View 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

View 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)

View File

@@ -21,19 +21,22 @@ WEATHER_CLOUD_DENSITY = 1, 8
WEATHER_CLOUD_THICKNESS = 100, 400
WEATHER_CLOUD_BASE_MIN = 1600
WEATHER_FOG_CHANCE = 20
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,
}
RANDOM_WEATHER = {
1: 0, # heavy rain
2: 5, # rain
3: 15, # dynamic
4: 40, # clear
5: 100, # random
1: 0, # thunderstorm
2: 20, # rain
3: 80, # clouds
4: 100, # clear
}
@@ -48,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),
@@ -68,6 +81,29 @@ class EnviromentGenerator:
self.mission.start_time = start_time
def _generate_wind(self, wind_speed, wind_direction=None):
# wind
if not wind_direction:
wind_direction = random.randint(0, 360)
self.mission.weather.wind_at_ground = Wind(wind_direction, wind_speed)
self.mission.weather.wind_at_2000 = Wind(wind_direction, wind_speed * 2)
self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3)
def _generate_base_weather(self):
# clouds
self.mission.weather.clouds_base = random.randint(*WEATHER_CLOUD_BASE)
self.mission.weather.clouds_density = random.randint(*WEATHER_CLOUD_DENSITY)
self.mission.weather.clouds_thickness = random.randint(*WEATHER_CLOUD_THICKNESS)
# wind
self._generate_wind(random.randint(0, 4))
# fog
if random.randint(0, 100) < WEATHER_FOG_CHANCE:
self.mission.weather.fog_visibility = random.randint(*WEATHER_FOG_VISIBILITY)
self.mission.weather.fog_thickness = random.randint(*WEATHER_FOG_THICKNESS)
def _gen_random_weather(self):
weather_type = None
for k, v in RANDOM_WEATHER.items():
@@ -77,35 +113,36 @@ class EnviromentGenerator:
logging.info("generated weather {}".format(weather_type))
if weather_type == 1:
self.mission.weather.heavy_rain()
elif weather_type == 2:
self.mission.weather.heavy_rain()
self.mission.weather.enable_fog = False
elif weather_type == 3:
self.mission.weather.random(self.mission.start_time, self.conflict.theater.terrain)
elif weather_type == 4:
pass
elif weather_type == 5:
self.mission.weather.clouds_base = random.randint(*WEATHER_CLOUD_BASE)
self.mission.weather.clouds_density = random.randint(*WEATHER_CLOUD_DENSITY)
self.mission.weather.clouds_thickness = random.randint(*WEATHER_CLOUD_THICKNESS)
# thunderstorm
self._generate_base_weather()
self._generate_wind(random.randint(8, 12))
wind_direction = random.randint(0, 360)
wind_speed = random.randint(0, 4)
self.mission.weather.wind_at_ground = Wind(wind_direction, wind_speed)
self.mission.weather.wind_at_2000 = Wind(wind_direction, wind_speed * 2)
self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3)
self.mission.weather.clouds_density = random.randint(9, 10)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Thunderstorm
elif weather_type == 2:
# rain
self._generate_base_weather()
self.mission.weather.clouds_density = random.randint(5, 8)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Rain
self._generate_wind(random.randint(4, 8))
elif weather_type == 3:
# clouds
self._generate_base_weather()
elif weather_type == 4:
# clear
pass
if self.mission.weather.clouds_density > 0:
# sometimes clouds are randomized way too low and need to be fixed
self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN)
if self.mission.weather.wind_at_ground == 0:
if self.mission.weather.wind_at_ground.speed == 0:
# frontline smokes look silly w/o any wind
self.mission.weather.wind_at_ground = random.randint(1, 2)
self._generate_wind(1)
def generate(self) -> EnvironmentSettings:
self._gen_random_time()
self._gen_time()
self._gen_random_weather()
settings = EnvironmentSettings()

View File

@@ -0,0 +1,33 @@
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
dd_type = random.choice(self.faction["destroyer"])
self.add_unit(dd_type, "DD1", self.position.x + 250, self.position.y + 450, self.heading)
self.add_unit(dd_type, "DD2", self.position.x + 250, self.position.y - 450, self.heading)
self.add_unit(dd_type, "DD3", self.position.x + 450, self.position.y + 850, self.heading)
self.add_unit(dd_type, "DD4", self.position.x + 450, self.position.y - 850, self.heading)
self.get_generated_group().points[0].speed = 20

25
gen/fleet/lha_group.py Normal file
View File

@@ -0,0 +1,25 @@
import random
from gen.sam.group_generator import GroupGenerator
class LHAGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(LHAGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
# Add carrier
if "helicopter_carrier" in self.faction.keys():
carrier_type = random.choice(self.faction["helicopter_carrier"])
self.add_unit(carrier_type, "LHA", self.position.x, self.position.y, self.heading)
# Add destroyers escort
if "destroyer" in self.faction.keys():
dd_type = random.choice(self.faction["destroyer"])
self.add_unit(dd_type, "DD1", self.position.x + 250, self.position.y + 450, self.heading)
self.add_unit(dd_type, "DD2", self.position.x + 250, self.position.y - 450, self.heading)
self.get_generated_group().points[0].speed = 20

View File

@@ -0,0 +1,29 @@
from game import db
from gen.fleet.carrier_group import CarrierGroupGenerator
from gen.fleet.lha_group import LHAGroupGenerator
def generate_carrier_group(faction:str, game, ground_object):
"""
This generate a carrier group
:param parentCp: The parent control point
:param ground_object: The ground object which will own the ship group
:param country: Owner country
:return: Nothing, but put the group reference inside the ground object
"""
generator = CarrierGroupGenerator(game, ground_object, db.FACTIONS[faction])
generator.generate()
return generator.get_generated_group()
def generate_lha_group(faction:str, game, ground_object):
"""
This generate a lha carrier group
:param parentCp: The parent control point
:param ground_object: The ground object which will own the ship group
:param country: Owner country
:return: Nothing, but put the group reference inside the ground object
"""
generator = LHAGroupGenerator(game, ground_object, db.FACTIONS[faction])
generator.generate()
return generator.get_generated_group()

View File

@@ -0,0 +1,447 @@
import math
import operator
import typing
import random
from game import db
from gen import Conflict
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE
from gen.flights.flight import Flight, FlightType, FlightWaypoint
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
STRIKE_MAX_RANGE = 1500000
SEAD_MAX_RANGE = 1500000
MAX_NUMBER_OF_INTERCEPTION_GROUP = 3
MISSION_DURATION = 120 # in minutes
CAP_EVERY_X_MINUTES = 20
CAS_EVERY_X_MINUTES = 30
SEAD_EVERY_X_MINUTES = 40
STRIKE_EVERY_X_MINUTES = 40
INGRESS_EGRESS_DISTANCE = 45000
INGRESS_ALT = 6096 # 20k feet
EGRESS_ALT = 6096 # 20k feet
PATROL_ALT_RANGE = (3600, 9200)
NAV_ALT = 9144
class FlightPlanner:
def __init__(self, from_cp, game):
# TODO : have the flight planner depend on a 'stance' setting : [Defensive, Aggresive... etc] and faction doctrine
# TODO : the flight planner should plan package and operations
self.from_cp = from_cp
self.game = game
self.aircraft_inventory = {} # local copy of the airbase inventory
def reset(self):
"""
Reset the planned flights and available units
"""
self.aircraft_inventory = dict({k: v for k, v in self.from_cp.base.aircraft.items()})
self.interceptor_flights = []
self.cap_flights = []
self.cas_flights = []
self.strike_flights = []
self.sead_flights = []
self.custom_flights = []
self.flights = []
self.potential_sead_targets = []
self.potential_strike_targets = []
def plan_flights(self):
self.reset()
self.compute_sead_targets()
self.compute_strike_targets()
# The priority is to assign air-superiority fighter or interceptor to interception roles, so they can scramble if there is an attacker
#self.commision_interceptors()
# Then some CAP patrol for the next 2 hours
self.commision_cap()
# Then setup cas
self.commision_cas()
# Then prepare some sead flights if required
self.commision_sead()
self.commision_strike()
# TODO : commision STRIKE / ANTISHIP
def remove_flight(self, index):
try:
flight = self.flights[index]
if flight in self.interceptor_flights: self.interceptor_flights.remove(flight)
if flight in self.cap_flights: self.cap_flights.remove(flight)
if flight in self.cas_flights: self.cas_flights.remove(flight)
if flight in self.strike_flights: self.strike_flights.remove(flight)
if flight in self.sead_flights: self.sead_flights.remove(flight)
if flight in self.custom_flights: self.custom_flights.remove(flight)
self.flights.remove(flight)
except IndexError:
return
def commision_interceptors(self):
"""
Pick some aircraft to assign them to interception roles
"""
# At least try to generate one interceptor group
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, MAX_NUMBER_OF_INTERCEPTION_GROUP), 1)
possible_interceptors = [k for k in self.aircraft_inventory.keys() if k in INTERCEPT_CAPABLE]
if len(possible_interceptors) <= 0:
possible_interceptors = [k for k,v in self.aircraft_inventory.items() if k in CAP_CAPABLE and v >= 2]
if number_of_interceptor_groups > 0:
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_interceptors})
for i in range(number_of_interceptor_groups):
try:
unit = random.choice([k for k,v in inventory.items() if v >= 2])
except IndexError:
break
inventory[unit] = inventory[unit] - 2
flight = Flight(unit, 2, self.from_cp, FlightType.INTERCEPTION)
flight.points = []
self.interceptor_flights.append(flight)
self.flights.append(flight)
# Update inventory
for k, v in inventory.items():
self.aircraft_inventory[k] = v
def commision_cap(self):
"""
Pick some aircraft to assign them to defensive CAP roles (BARCAP)
"""
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in CAP_CAPABLE and v >= 2]
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/CAP_EVERY_X_MINUTES)):
try:
unit = random.choice([k for k, v in inventory.items() if v >= 2])
except IndexError:
break
inventory[unit] = inventory[unit] - 2
ftype = FlightType.BARCAP if self.from_cp.is_carrier else FlightType.CAP
flight = Flight(unit, 2, self.from_cp, ftype)
# Flight path : fly over each ground object (TODO : improve)
flight.points = []
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1])
patrolled = []
for ground_object in self.from_cp.ground_objects:
if ground_object.group_id not in patrolled and not ground_object.airbase_group:
point = FlightWaypoint(ground_object.position.x, ground_object.position.y, patrol_alt)
point.name = "Patrol point"
point.description = "Patrol #" + str(len(flight.points))
point.pretty_name = "Patrol #" + str(len(flight.points))
flight.points.append(point)
patrolled.append(ground_object.group_id)
if len(flight.points) == 0:
for i in range(3):
pos = self.from_cp.position.point_from_heading(random.randint(0, 360), random.randint(30000, 80000))
point = FlightWaypoint(pos.x, pos.y, patrol_alt)
point.name = "Patrol point"
point.description = "Patrol #" + str(len(flight.points))
point.pretty_name = "Patrol #" + str(len(flight.points))
flight.points.append(point)
self.cap_flights.append(flight)
self.flights.append(flight)
# Update inventory
for k, v in inventory.items():
self.aircraft_inventory[k] = v
def commision_cas(self):
"""
Pick some aircraft to assign them to CAS
"""
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in CAS_CAPABLE and v >= 2]
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
cas_location = self._get_cas_locations()
if len(cas_location) > 0:
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/CAS_EVERY_X_MINUTES)):
try:
unit = random.choice([k for k, v in inventory.items() if v >= 2])
except IndexError:
break
inventory[unit] = inventory[unit] - 2
flight = Flight(unit, 2, self.from_cp, FlightType.CAS)
flight.points = []
flight.scheduled_in = offset + i*random.randint(CAS_EVERY_X_MINUTES-5, CAS_EVERY_X_MINUTES+5)
location = random.choice(cas_location)
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, location, self.game.theater)
center = ingress.point_from_heading(heading, distance/2)
egress = ingress.point_from_heading(heading, distance)
flight.targets.append(center)
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
ingress_point.name = "INGRESS"
ingress_point.pretty_name = "INGRESS"
ingress_point.description = "Ingress into CAS area"
flight.points.append(ingress_point)
center_point = FlightWaypoint(center.x, center.y, 1000)
center_point.description = "Provide CAS"
center_point.name = "CAS"
center_point.pretty_name = "INGRESS"
flight.points.append(center_point)
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
egress_point.description = "Egress from CAS area"
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS"
flight.points.append(egress_point)
self.cas_flights.append(flight)
self.flights.append(flight)
# Update inventory
for k, v in inventory.items():
self.aircraft_inventory[k] = v
def commision_sead(self):
"""
Pick some aircraft to assign them to SEAD tasks
"""
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in SEAD_CAPABLE and v >= 2]
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
if len(self.potential_sead_targets) > 0:
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/SEAD_EVERY_X_MINUTES)):
if len(self.potential_sead_targets) <= 0:
break
try:
unit = random.choice([k for k, v in inventory.items() if v >= 2])
except IndexError:
break
inventory[unit] = inventory[unit] - 2
flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD]))
flight.points = []
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
location = self.potential_sead_targets[0][0]
self.potential_sead_targets.pop(0)
heading = self.from_cp.position.heading_between_point(location.position)
ingress_heading = heading - 180 + 25
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE)
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
flight.points.append(ingress_point)
point = FlightWaypoint(location.position.x, location.position.y, 1000)
if flight.flight_type == FlightType.DEAD:
point.description = "SEAD on " + location.obj_name
point.pretty_name = "SEAD on " + location.obj_name
else:
point.description = "DEAD on " + location.obj_name
point.pretty_name = "DEAD on " + location.obj_name
point.targets.append(location)
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
egress_point.pretty_name = "EGRESS on " + location.obj_name
egress_point.description = "EGRESS on " + location.obj_name
flight.points.append(egress_point)
self.sead_flights.append(flight)
self.flights.append(flight)
# Update inventory
for k, v in inventory.items():
self.aircraft_inventory[k] = v
def commision_strike(self):
"""
Pick some aircraft to assign them to STRIKE tasks
"""
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in CAS_CAPABLE and v >= 2]
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
if len(self.potential_strike_targets) > 0:
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/STRIKE_EVERY_X_MINUTES)):
if len(self.potential_strike_targets) <= 0:
break
try:
unit = random.choice([k for k, v in inventory.items() if v >= 2])
except IndexError:
break
inventory[unit] = inventory[unit] - 2
flight = Flight(unit, 2, self.from_cp, FlightType.STRIKE)
flight.points = []
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
location = self.potential_strike_targets[0][0]
self.potential_strike_targets.pop(0)
heading = self.from_cp.position.heading_between_point(location.position)
ingress_heading = heading - 180 + 25
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE)
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
flight.points.append(ingress_point)
if len(location.groups) > 0:
for g in location.groups:
for j, u in enumerate(g.units):
point = FlightWaypoint(u.position.x, u.position.y, 0)
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
ingress_point.targets.append(location)
flight.points.append(point)
else:
point = FlightWaypoint(location.position.x, location.position.y, 0)
point.description = "STRIKE on " + location.obj_name + " " + str(location.category)
point.pretty_name = "STRIKE on " + location.obj_name + " " + str(location.category)
point.targets.append(location)
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
egress_point.pretty_name = "EGRESS on " + location.obj_name
egress_point.description = "EGRESS on " + location.obj_name
flight.points.append(egress_point)
self.strike_flights.append(flight)
self.flights.append(flight)
# Update inventory
for k, v in inventory.items():
self.aircraft_inventory[k] = v
def _get_cas_locations(self):
cas_locations = []
for cp in self.from_cp.connected_points:
if cp.captured != self.from_cp.captured:
cas_locations.append(cp)
return cas_locations
def compute_strike_targets(self):
"""
@return a list of potential strike targets in range
"""
# target, distance
self.potential_strike_targets = []
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance > 2*STRIKE_MAX_RANGE:
# Then it's unlikely any child ground object is in range
return
added_group = []
for g in cp.ground_objects:
if g.group_id in added_group or g.is_dead: continue
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance < SEAD_MAX_RANGE:
self.potential_strike_targets.append((g, distance))
added_group.append(g)
self.potential_strike_targets.sort(key=operator.itemgetter(1))
def compute_sead_targets(self):
"""
@return a list of potential sead targets in range
"""
# target, distance
self.potential_sead_targets = []
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
# Then it's unlikely any ground object is range
if distance > 2*SEAD_MAX_RANGE:
return
for g in cp.ground_objects:
if g.dcs_identifier == "AA":
# Check that there is at least one unit with a radar in the ground objects unit groups
number_of_units = sum([len([r for r in group.units if hasattr(db.unit_type_from_name(r.type), "detection_range")
and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups])
if number_of_units <= 0:
continue
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance < SEAD_MAX_RANGE:
self.potential_sead_targets.append((g, distance))
self.potential_sead_targets.sort(key=operator.itemgetter(1))
def __repr__(self):
return "-"*40 + "\n" + self.from_cp.name + " planned flights :\n"\
+ "-"*40 + "\n" + "\n".join([repr(f) for f in self.flights]) + "\n" + "-"*40
def get_available_aircraft(self):
base_aircraft_inventory = dict({k: v for k, v in self.from_cp.base.aircraft.items()})
for f in self.flights:
if f.unit_type in base_aircraft_inventory.keys():
base_aircraft_inventory[f.unit_type] = base_aircraft_inventory[f.unit_type] - f.count
if base_aircraft_inventory[f.unit_type] <= 0:
del base_aircraft_inventory[f.unit_type]
return base_aircraft_inventory

View File

@@ -0,0 +1,194 @@
from dcs.planes import *
from dcs.helicopters import *
# Interceptor are the aircraft prioritized for interception tasks
# If none is available, the AI will use regular CAP-capable aircraft instead
INTERCEPT_CAPABLE = [
MiG_21Bis,
MiG_25PD,
MiG_31,
M_2000C,
Mirage_2000_5,
F_14B,
F_15C,
]
# Used for CAP, Escort, and intercept if there is not a specialised aircraft available
CAP_CAPABLE = [
MiG_15bis,
MiG_19P,
MiG_21Bis,
MiG_23MLD,
MiG_29A,
MiG_29G,
MiG_29S,
Su_27,
J_11A,
JF_17,
Su_30,
Su_33,
M_2000C,
Mirage_2000_5,
F_86F_Sabre,
F_4E,
F_5E_3,
F_14B,
F_15C,
F_16C_50,
FA_18C_hornet,
C_101CC,
L_39ZA,
P_51D_30_NA,
P_51D,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
]
# USed for CAS (Close air support) and BAI (Battlefield Interdiction)
CAS_CAPABLE = [
MiG_15bis,
MiG_29A,
MiG_27K,
MiG_29S,
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_25TM,
Su_34,
JF_17,
M_2000C,
A_10A,
A_10C,
AV8BNA,
F_86F_Sabre,
F_5E_3,
F_14B,
F_16C_50,
FA_18C_hornet,
C_101CC,
L_39ZA,
AJS37,
SA342M,
SA342L,
AH_64A,
AH_64D,
UH_1H,
Mi_8MT,
Mi_28N,
Mi_24V,
Ka_50,
P_51D_30_NA,
P_51D,
A_20G,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
]
# Aircraft used for SEAD / DEAD tasks
SEAD_CAPABLE = [
F_4E,
FA_18C_hornet,
# F_16C_50, Not yet
AV8BNA,
JF_17,
Su_24M,
Su_25T,
Su_25TM,
Su_17M4,
Su_30,
Su_34,
MiG_27K,
]
# Aircraft used for Strike mission
STRIKE_CAPABLE = [
MiG_15bis,
MiG_29A,
MiG_27K,
MiG_29S,
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_34,
JF_17,
M_2000C,
A_10A,
A_10C,
AV8BNA,
F_86F_Sabre,
F_5E_3,
F_14B,
F_16C_50,
FA_18C_hornet,
C_101CC,
L_39ZA,
AJS37,
M_2000C,
P_51D_30_NA,
P_51D,
A_20G,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
]
ANTISHIP_CAPABLE = [
Su_24M,
Su_17M4,
F_A_18C,
AV8BNA,
JF_17,
F_16C_50,
A_10C,
A_10A,
Ju_88A4,
]

85
gen/flights/flight.py Normal file
View File

@@ -0,0 +1,85 @@
from enum import Enum
from typing import List
from dcs.mission import StartType
from dcs.unittype import UnitType
from game import db
class FlightType(Enum):
CAP = 0
TARCAP = 1
BARCAP = 2
CAS = 3
INTERCEPTION = 4
STRIKE = 5
ANTISHIP = 6
SEAD = 7
DEAD = 8
ESCORT = 9
BAI = 10
# Helos
TROOP_TRANSPORT = 11
LOGISTICS = 12
EVAC = 13
ELINT = 14
RECON = 15
EWAR = 16
class FlightWaypoint():
def __init__(self, x: float, y: float, alt=0):
self.x = x
self.y = y
self.alt = alt
self.name = ""
self.description = ""
self.targets = []
self.obj_name = ""
self.pretty_name = ""
class Flight:
unit_type: UnitType = None
from_cp = None
points: List[FlightWaypoint] = []
flight_type: FlightType = None
count: int = 0
client_count: int = 0
targets = []
use_custom_loadout = False
loadout = {}
preset_loadout_name = ""
start_type = "Runway"
# How long before this flight should take off
scheduled_in = 0
def __init__(self, unit_type: UnitType, count: int, from_cp, flight_type: FlightType):
self.unit_type = unit_type
self.count = count
self.from_cp = from_cp
self.flight_type = flight_type
self.points = []
self.targets = []
self.loadout = {}
self.start_type = "Runway"
def __repr__(self):
return self.flight_type.name + " | " + str(self.count) + "x" + db.unit_type_name(self.unit_type) \
+ " in " + str(self.scheduled_in) + " minutes (" + str(len(self.points)) + " wpt)"
# Test
if __name__ == '__main__':
from dcs.planes import A_10C
from theater import ControlPoint, Point, List
from_cp = ControlPoint(0, "AA", Point(0, 0), None, [], 0, 0)
f = Flight(A_10C, 4, from_cp, FlightType.CAS)
f.scheduled_in = 50
print(f)

45
gen/forcedoptionsgen.py Normal file
View File

@@ -0,0 +1,45 @@
import logging
import typing
from enum import IntEnum
from dcs.mission import Mission
from dcs.forcedoptions import ForcedOptions
from .conflictgen import *
class Labels(IntEnum):
Off = 0
Full = 1
Abbreviated = 2
Dot = 3
class ForcedOptionsGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game):
self.mission = mission
self.conflict = conflict
self.game = game
def _set_options_view(self):
if self.game.settings.map_coalition_visibility == "All Units":
self.mission.forced_options.options_view = ForcedOptions.Views.All
elif self.game.settings.map_coalition_visibility == "Allied Units":
self.mission.forced_options.options_view = ForcedOptions.Views.Allies
elif self.game.settings.map_coalition_visibility == "Own Aircraft":
self.mission.forced_options.options_view = ForcedOptions.Views.MyAircraft
elif self.game.settings.map_coalition_visibility == "None":
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyMap
def _set_labels(self):
if self.game.settings.labels == "Abbreviated":
self.mission.forced_options.labels = int(Labels.Abbreviated)
elif self.game.settings.labels == "Dot Only":
self.mission.forced_options.labels = int(Labels.Dot)
elif self.game.settings.labels == "Off":
self.mission.forced_options.labels = int(Labels.Off)
def generate(self):
self._set_options_view()
self._set_labels()

View File

@@ -0,0 +1,292 @@
import random
from enum import Enum
from dcs.vehicles import *
from gen import Conflict
from gen.ground_forces.combat_stance import CombatStance
from theater import ControlPoint
TYPE_TANKS = [
Armor.MBT_T_55,
Armor.MBT_T_72B,
Armor.MBT_T_80U,
Armor.MBT_T_90,
Armor.MBT_Leopard_2,
Armor.MBT_Leopard_1A3,
Armor.MBT_Leclerc,
Armor.MBT_Challenger_II,
Armor.MBT_M1A2_Abrams,
Armor.MBT_M60A3_Patton,
Armor.MBT_Merkava_Mk__4,
Armor.ZTZ_96B,
# WW2
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.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly,
Armor.StuG_IV,
Armor.ST_Centaur_IV,
Armor.CT_Cromwell_IV,
Armor.HIT_Churchill_VII,
]
TYPE_ATGM = [
Armor.ATGM_M1045_HMMWV_TOW,
Armor.ATGM_M1134_Stryker,
Armor.IFV_BMP_2,
# WW2 (Tank Destroyers)
Armor.M30_Cargo_Carrier,
Armor.TD_Jagdpanzer_IV,
Armor.TD_Jagdpanther_G1,
Armor.TD_M10_GMC,
]
TYPE_IFV = [
Armor.IFV_BMP_3,
Armor.IFV_BMP_2,
Armor.IFV_BMP_1,
Armor.IFV_Marder,
Armor.IFV_MCV_80,
Armor.IFV_LAV_25,
Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.IFV_M2A2_Bradley,
Armor.IFV_BMD_1,
Armor.ZBD_04A,
# WW2
Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.LAC_M8_Greyhound,
]
TYPE_APC = [
Armor.APC_M1043_HMMWV_Armament,
Armor.APC_M1126_Stryker_ICV,
Armor.APC_M113,
Armor.APC_BTR_80,
Armor.APC_MTLB,
Armor.APC_M2A1,
Armor.APC_Cobra,
Armor.APC_Sd_Kfz_251,
Armor.APC_AAV_7,
Armor.TPz_Fuchs,
Armor.ARV_BRDM_2,
Armor.ARV_BTR_RD,
Armor.FDDM_Grad,
# WW2
Armor.APC_M2A1,
Armor.APC_Sd_Kfz_251,
]
TYPE_ARTILLERY = [
Artillery.MLRS_9A52_Smerch,
Artillery.SPH_2S1_Gvozdika,
Artillery.SPH_2S3_Akatsia,
Artillery.MLRS_BM_21_Grad,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.SPH_M109_Paladin,
Artillery.MLRS_M270,
Artillery.SPH_2S9_Nona,
Artillery.SpGH_Dana,
Artillery.SPH_2S19_Msta,
Artillery.MLRS_FDDM,
# WW2
Artillery.Sturmpanzer_IV_Brummbär,
Artillery.M12_GMC
]
TYPE_LOGI = [
Unarmed.Transport_M818,
Unarmed.Transport_KAMAZ_43101,
Unarmed.Transport_Ural_375,
Unarmed.Transport_GAZ_66,
Unarmed.Transport_GAZ_3307,
Unarmed.Transport_GAZ_3308,
Unarmed.Transport_Ural_4320_31_Armored,
Unarmed.Transport_Ural_4320T,
Unarmed.Blitz_3_6_6700A,
Unarmed.Kübelwagen_82,
Unarmed.Sd_Kfz_7,
Unarmed.Sd_Kfz_2,
Unarmed.Willys_MB,
Unarmed.Land_Rover_109_S3,
Unarmed.Land_Rover_101_FC,
]
TYPE_INFANTRY = [
Infantry.Infantry_Soldier_Insurgents,
Infantry.Soldier_AK,
Infantry.Infantry_M1_Garand,
Infantry.Infantry_Mauser_98,
Infantry.Infantry_SMLE_No_4_Mk_1,
Infantry.Georgian_soldier_with_M4,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_AKS,
Infantry.Paratrooper_RPG_16,
Infantry.Soldier_M249,
Infantry.Infantry_M4,
Infantry.Soldier_RPG,
]
MAX_COMBAT_GROUP_PER_CP = 10
class CombatGroupRole(Enum):
TANK = 1
APC = 2
IFV = 3
ARTILLERY = 4
SHORAD = 5
LOGI = 6
INFANTRY = 7
ATGM = 8
DISTANCE_FROM_FRONTLINE = {
CombatGroupRole.TANK:2800,
CombatGroupRole.APC:7000,
CombatGroupRole.IFV:3000,
CombatGroupRole.ARTILLERY:14000,
CombatGroupRole.SHORAD:12000,
CombatGroupRole.LOGI:18000,
CombatGroupRole.INFANTRY:2800,
CombatGroupRole.ATGM:5500
}
GROUP_SIZES_BY_COMBAT_STANCE = {
CombatStance.DEFENSIVE: [2, 4, 6],
CombatStance.AGGRESIVE: [2, 4, 6],
CombatStance.RETREAT: [2, 4, 6, 8],
CombatStance.BREAKTHROUGH: [4, 6, 6, 8],
CombatStance.ELIMINATION: [2, 4, 4, 4, 6],
CombatStance.AMBUSH: [1, 1, 2, 2, 2, 2, 4]
}
class CombatGroup:
def __init__(self, role:CombatGroupRole):
self.units = []
self.role = role
self.assigned_enemy_cp = None
self.start_position = None
def __str__(self):
s = ""
s += "ROLE : " + str(self.role) + "\n"
if len(self.units) > 0:
s += "UNITS " + self.units[0].name + " * " + str(len(self.units))
return s
class GroundPlanner:
cp = None
combat_groups_dict = {}
connected_enemy_cp = []
tank_groups = []
apc_group = []
ifv_group = []
art_group = []
shorad_groups = []
logi_groups = []
def __init__(self, cp:ControlPoint, game):
self.cp = cp
self.game = game
self.connected_enemy_cp = [cp for cp in self.cp.connected_points if cp.captured != self.cp.captured]
self.tank_groups = []
self.apc_group = []
self.ifv_group = []
self.art_group = []
self.atgm_group = []
self.logi_groups = []
self.shorad_groups = []
self.units_per_cp = {}
for cp in self.connected_enemy_cp:
self.units_per_cp[cp.id] = []
self.reserve = []
def plan_groundwar(self):
if hasattr(self.cp, 'stance'):
group_size_choice = GROUP_SIZES_BY_COMBAT_STANCE[self.cp.stance]
else:
self.cp.stance = CombatStance.DEFENSIVE
group_size_choice = GROUP_SIZES_BY_COMBAT_STANCE[CombatStance.DEFENSIVE]
# Create combat groups and assign them randomly to each enemy CP
for key in self.cp.base.armor.keys():
role = None
collection = None
if key in TYPE_TANKS:
collection = self.tank_groups
role = CombatGroupRole.TANK
elif key in TYPE_APC:
collection = self.apc_group
role = CombatGroupRole.APC
elif key in TYPE_ARTILLERY:
collection = self.art_group
role = CombatGroupRole.ARTILLERY
elif key in TYPE_IFV:
collection = self.ifv_group
role = CombatGroupRole.IFV
elif key in TYPE_LOGI:
collection = self.logi_groups
role = CombatGroupRole.LOGI
elif key in TYPE_ATGM:
collection = self.atgm_group
role = CombatGroupRole.ATGM
else:
print("Warning unit type not handled by ground generator")
print(key)
continue
available = self.cp.base.armor[key]
while available > 0:
n = random.choice(group_size_choice)
if n > available:
if available >= 2:
n = 2
else:
n = 1
available -= n
group = CombatGroup(role)
if len(self.connected_enemy_cp) > 0:
enemy_cp = random.choice(self.connected_enemy_cp).id
self.units_per_cp[enemy_cp].append(group)
group.assigned_enemy_cp = enemy_cp
else:
self.reserve.append(group)
group.assigned_enemy_cp = "__reserve__"
for i in range(n):
group.units.append(key)
collection.append(group)
print("------------------")
print("Ground Planner : ")
print(self.cp.name)
print("------------------")
for key in self.units_per_cp.keys():
print("For : #" + str(key))
for group in self.units_per_cp[key]:
print(str(group))

View File

@@ -0,0 +1,11 @@
from enum import Enum
class CombatStance(Enum):
DEFENSIVE = 0 # Unit will adopt defensive stance with medium group of units
AGGRESIVE = 1 # Unit will attempt to make progress with medium sized group of units
RETREAT = 2 # Unit will retreat
BREAKTHROUGH = 3 # Unit will attempt a breakthrough, rushing forward very aggresively with big group of armored units, and even less armored units will move aggresively
ELIMINATION = 4 # Unit will progress aggresively toward anemy units, attempting to eliminate the ennemy force
AMBUSH = 5 # Units will adopt a defensive stance a bit different from 'DEFENSIVE', ATGM & INFANTRY with RPG will be located on frontline with the armored units. (The groups of units will be smaller)

View File

@@ -1,6 +1,7 @@
import logging
from game import db
from game.db import unit_type_from_name
from .conflictgen import *
from .naming import *
@@ -27,62 +28,137 @@ class GroundObjectsGenerator:
center, heading = self.conflict.frontline_position(self.conflict.theater, self.conflict.from_cp, self.conflict.to_cp)
heading -= 90
position = self.conflict.find_ground_position(center.point_from_heading(heading, FARP_FRONTLINE_DISTANCE), heading)
initial_position = center.point_from_heading(heading, FARP_FRONTLINE_DISTANCE)
position = self.conflict.find_ground_position(initial_position, heading)
if not position:
position = initial_position
for i, _ in enumerate(range(0, number_of_units, self.FARP_CAPACITY)):
position = position.point_from_heading(0, i * 275)
yield self.m.farp(
country=self.m.country(self.game.player),
country=self.m.country(self.game.player_country),
name="FARP",
position=position,
)
def generate(self):
side = self.m.country(self.game.enemy)
cp = None # type: ControlPoint
if self.conflict.attackers_side.name == self.game.player:
if self.conflict.attackers_country.name == self.game.player_country:
cp = self.conflict.to_cp
else:
cp = self.conflict.from_cp
for ground_object in cp.ground_objects:
if ground_object.dcs_identifier == "AA":
if ground_object.position.distance_to_point(self.conflict.from_cp.position) < AA_CP_MIN_DISTANCE:
continue
consumed_farps = set()
if ground_object.is_dead:
continue
unit_type = random.choice(self.game.commision_unit_types(cp, AirDefence))
assert unit_type is not None, "Cannot find unit type for GroundObject defense ({})!".format(cp)
for cp in self.game.theater.controlpoints:
group = self.m.vehicle_group(
country=side,
name=ground_object.string_identifier,
_type=unit_type,
position=ground_object.position,
heading=ground_object.heading,
)
logging.info("generated defense object identifier {} with mission id {}".format(group.name, group.id))
if cp.captured:
country = self.game.player_country
else:
if ground_object.dcs_identifier in warehouse_map:
static_type = warehouse_map[ground_object.dcs_identifier]
country = self.game.enemy_country
side = self.m.country(country)
for ground_object in cp.ground_objects:
if ground_object.dcs_identifier == "AA":
for g in ground_object.groups:
if len(g.units) > 0:
utype = unit_type_from_name(g.units[0].type)
vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
vg.units[0].name = self.m.string(g.units[0].name)
for i, u in enumerate(g.units):
if i > 0:
vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type)
vehicle.position.x = u.position.x
vehicle.position.y = u.position.y
vehicle.heading = u.heading
vg.add_unit(vehicle)
if self.game.settings.perf_red_alert_state:
vg.points[0].tasks.append(OptAlarmState(2))
else:
vg.points[0].tasks.append(OptAlarmState(1))
elif ground_object.dcs_identifier in ["CARRIER", "LHA"]:
for g in ground_object.groups:
if len(g.units) > 0:
utype = unit_type_from_name(g.units[0].type)
if ground_object.dcs_identifier == "CARRIER" and self.game.settings.supercarrier == True:
utype = db.upgrade_to_supercarrier(utype, cp.name)
sg = self.m.ship_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
sg.units[0].name = self.m.string(g.units[0].name)
for i, u in enumerate(g.units):
if i > 0:
ship = Ship(self.m.next_unit_id(), self.m.string(u.name), unit_type_from_name(u.type))
ship.position.x = u.position.x
ship.position.y = u.position.y
ship.heading = u.heading
sg.add_unit(ship)
# TODO : make sure the point is not on Land
sg.add_waypoint(sg.points[0].position.point_from_heading(g.units[0].heading, 100000))
# SET UP TACAN
modeChannel = "X" if not cp.tacanY else "Y"
sg.points[0].tasks.append(ActivateBeaconCommand(channel=cp.tacanN, modechannel=modeChannel, callsign=cp.tacanI, unit_id=sg.units[0].id))
if ground_object.dcs_identifier == "CARRIER" and hasattr(cp, "icls"):
sg.points[0].tasks.append(ActivateICLSCommand(cp.icls, unit_id=sg.units[0].id))
else:
static_type = fortification_map[ground_object.dcs_identifier]
if ground_object.dcs_identifier in warehouse_map:
static_type = warehouse_map[ground_object.dcs_identifier]
else:
static_type = fortification_map[ground_object.dcs_identifier]
if not static_type:
print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier))
continue
if not static_type:
print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier))
continue
group = self.m.static_group(
country=side,
name=ground_object.string_identifier,
_type=static_type,
position=ground_object.position,
heading=ground_object.heading,
dead=ground_object.is_dead,
)
group = self.m.static_group(
country=side,
name=ground_object.string_identifier,
_type=static_type,
position=ground_object.position,
heading=ground_object.heading,
dead=ground_object.is_dead,
)
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))
def farp_aa(mission_obj, country, name, position: mapping.Point):
"""
Add AAA to a FARP :)
:param mission_obj:
:param country:
:param name:
:param position:
:return:
"""
vg = unitgroup.VehicleGroup(mission_obj.next_group_id(), mission_obj.string(name))
units = [
AirDefence.SPAAA_ZSU_23_4_Shilka,
AirDefence.AAA_ZU_23_Closed,
]
v = mission_obj.vehicle(name + "_AAA", random.choice(units))
v.position.x = position.x - random.randint(5, 30)
v.position.y = position.y - random.randint(5, 30)
v.heading = random.randint(0, 359)
vg.add_unit(v)
wp = vg.add_waypoint(vg.units[0].position, PointAction.OffRoad, 0)
wp.ETA_locked = True
country.add_vehicle_group(vg)
return vg
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))

View File

@@ -1,5 +0,0 @@
from .aircraft import *
class HelicopterConflictGenerator(AircraftConflictGenerator):
pass

View File

@@ -1,12 +1,55 @@
from game import db
import random
ALPHA_MILITARY = ["Alpha","Bravo","Charlie","Delta","Echo","Foxtrot",
"Golf","Hotel","India","Juliet","Kilo","Lima","Mike",
"November","Oscar","Papa","Quebec","Romeo","Sierra",
"Tango","Uniform","Victor","Whisky","XRay","Yankee",
"Zulu","Zero"]
class NameGenerator:
number = 0
def next_unit_name(self, country, unit_type):
ANIMALS = [
"SHARK", "TORTOISE", "BAT", "PANGOLIN", "AARDWOLF",
"MONKEY", "BUFFALO", "DOG", "BOBCAT", "LYNX", "PANTHER", "TIGER",
"LION", "OWL", "BUTTERFLY", "BISON", "DUCK", "COBRA", "MAMBA",
"DOLPHIN", "PHEASANT", "ARMADILLLO", "RACOON", "ZEBRA", "COW", "COYOTE", "FOX",
"LIGHTFOOT", "COTTONMOUTH", "TAURUS", "VIPER", "CASTOR", "GIRAFFE", "SNAKE",
"MONSTER", "ALBATROSS", "HAWK", "DOVE", "MOCKINGBIRD", "GECKO", "ORYX", "GORILLA",
"HARAMBE", "GOOSE", "MAVERICK", "HARE", "JACKAL", "LEOPARD", "CAT", "MUSK", "ORCA",
"OCELOT", "BEAR", "PANDA", "GULL", "PENGUIN", "PYTHON", "RAVEN", "DEER", "MOOSE",
"REINDEER", "SHEEP", "GAZELLE", "INSECT", "VULTURE", "WALLABY", "KANGAROO", "KOALA",
"KIWI", "WHALE", "FISH", "RHINO", "HIPPO", "RAT", "WOODPECKER", "WORM", "BABOON",
"YAK", "SCORPIO", "HORSE", "POODLE", "CENTIPEDE", "CHICKEN", "CHEETAH", "CHAMELEON",
"CATFISH", "CATERPILLAR", "CARACAL", "CAMEL", "CAIMAN", "BARRACUDA", "BANDICOOT",
"ALLIGATOR", "BONGO", "CORAL", "ELEPHANT", "ANTELOPE", "CRAB", "DACHSHUND", "DODO",
"FLAMINGO", "FERRET", "FALCON", "BULLDOG", "DONKEY", "IGUANA", "TAMARIN", "HARRIER",
"GRIZZLY", "GREYHOUND", "GRASSHOPPER", "JAGUAR", "LADYBUG", "KOMODO", "DRAGON", "LIZARD",
"LLAMA", "LOBSTER", "OCTOPUS", "MANATEE", "MAGPIE", "MACAW", "OSTRICH", "OYSTER",
"MOLE", "MULE", "MOTH", "MONGOOSE", "MOLLY", "MEERKAT", "MOUSE", "PEACOCK", "PIKE", "ROBIN",
"RAGDOLL", "PLATYPUS", "PELICAN", "PARROT", "PORCUPINE", "PIRANHA", "PUMA", "PUG", "TAPIR",
"TERMITE", "URCHIN", "SHRIMP", "TURKEY", "TOUCAN", "TETRA", "HUSKY", "STARFISH", "SWAN",
"FROG", "SQUIRREL", "WALRUS", "WARTHOG", "CORGI", "WEASEL", "WOMBAT", "WOLVERINE", "MAMMOTH",
"TOAD", "WOLF", "ZEBU", "SEAL", "SKATE", "JELLYFISH", "MOSQUITO", "LOCUST", "SLUG", "SNAIL",
"HEDGEHOG", "PIGLET", "FENNEC", "BADGER", "ALPACA"
]
def __init__(self):
self.number = 0
self.ANIMALS = NameGenerator.ANIMALS.copy()
def reset(self):
self.number = 0
self.ANIMALS = NameGenerator.ANIMALS.copy()
def next_unit_name(self, country, parent_base_id, unit_type):
self.number += 1
return "unit|{}|{}|{}|".format(country.id, self.number, db.unit_type_name(unit_type))
return "unit|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type))
def next_infantry_name(self, country, parent_base_id, unit_type):
self.number += 1
return "infantry|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type))
def next_basedefense_name(self):
return "basedefense_aa|0|0|"
@@ -23,6 +66,16 @@ class NameGenerator:
self.number += 1
return "carrier|{}|{}|0|".format(country.id, self.number)
def random_objective_name(self):
if len(self.ANIMALS) == 0:
return random.choice(ALPHA_MILITARY).upper() + "#" + str(random.randint(0, 100))
else:
animal = random.choice(self.ANIMALS)
self.ANIMALS.remove(animal)
return animal
namegen = NameGenerator()

25
gen/sam/aaa_bofors.py Normal file
View File

@@ -0,0 +1,25 @@
import random
from dcs.vehicles import AirDefence
from gen.sam.group_generator import GroupGenerator
class BoforsGenerator(GroupGenerator):
"""
This generate a Bofors flak artillery group
"""
def generate(self):
grid_x = random.randint(2, 4)
grid_y = random.randint(2, 4)
spacing = random.randint(10,40)
index = 0
for i in range(grid_x):
for j in range(grid_y):
index = index+1
self.add_unit(AirDefence.AAA_Bofors_40mm, "AAA#" + str(index),
self.position.x + spacing*i,
self.position.y + spacing*j, self.heading)

39
gen/sam/aaa_flak.py Normal file
View File

@@ -0,0 +1,39 @@
import random
from dcs.vehicles import AirDefence, Unarmed
from gen.sam.group_generator import GroupGenerator
GFLAK = [AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_8_8cm_Flak_18, AirDefence.AAA_8_8cm_Flak_36, AirDefence.AAA_8_8cm_Flak_37, AirDefence.AAA_8_8cm_Flak_41, AirDefence.AAA_Flak_38]
class FlakGenerator(GroupGenerator):
"""
This generate a German flak artillery group
"""
def generate(self):
grid_x = random.randint(2, 4)
grid_y = random.randint(2, 4)
spacing = random.randint(10,40)
index = 0
mixed = random.choice([True, False])
unit_type = random.choice(GFLAK)
for i in range(grid_x):
for j in range(grid_y):
index = index+1
self.add_unit(unit_type, "AAA#" + str(index),
self.position.x + spacing*i,
self.position.y + spacing*j, self.heading)
if(mixed):
unit_type = random.choice(GFLAK)
# Enough Opel Blitz truck to transport the guns
for i in range(grid_x):
for j in range(grid_y):
self.add_unit(Unarmed.Blitz_3_6_6700A, "AAA#" + str(index),
self.position.x + 200 + 9*i,
self.position.y + 9*j, 90)

View File

@@ -0,0 +1,25 @@
import random
from dcs.vehicles import AirDefence
from gen.sam.group_generator import GroupGenerator
class ZU23InsurgentGenerator(GroupGenerator):
"""
This generate a ZU23 insurgent flak artillery group
"""
def generate(self):
grid_x = random.randint(2, 4)
grid_y = random.randint(2, 4)
spacing = random.randint(10,40)
index = 0
for i in range(grid_x):
for j in range(grid_y):
index = index+1
self.add_unit(AirDefence.AAA_ZU_23_Insurgent_Closed, "AAA#" + str(index),
self.position.x + spacing*i,
self.position.y + spacing*j, self.heading)

View File

@@ -0,0 +1,73 @@
import math
import random
from dcs import unitgroup
from dcs.point import PointAction
from dcs.unit import Vehicle
class GroupGenerator():
def __init__(self, game, ground_object):
self.game = game
self.go = ground_object
self.position = ground_object.position
self.heading = random.randint(0, 359)
self.vg = unitgroup.VehicleGroup(self.game.next_group_id(), self.go.group_identifier)
wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0)
wp.ETA_locked = True
def generate(self):
raise NotImplementedError
def get_generated_group(self):
return self.vg
def add_unit(self, unit_type, name, pos_x, pos_y, heading):
nn = "cgroup|" + str(self.go.cp_id) + '|' + str(self.go.group_id) + '|' + str(self.go.group_identifier) + "|" + name
unit = Vehicle(self.game.next_unit_id(),
nn, unit_type.id)
unit.position.x = pos_x
unit.position.y = pos_y
unit.heading = heading
self.vg.add_unit(unit)
return unit
def get_circular_position(self, num_units, launcher_distance, coverage=90):
"""
Given a position on the map, array a group of units in a circle a uniform distance from the unit
:param num_units:
number of units to play on the circle
:param launcher_distance:
distance the units should be from the center unit
:param coverage:
0-360
:return:
list of tuples representing each unit location
[(pos_x, pos_y, heading), ...]
"""
if coverage == 360:
# one of the positions is shared :'(
outer_offset = coverage / num_units
else:
outer_offset = coverage / (num_units - 1)
positions = []
if num_units % 2 == 0:
current_offset = self.heading - ((coverage / (num_units - 1)) / 2)
else:
current_offset = self.heading
current_offset -= outer_offset * (math.ceil(num_units / 2) - 1)
for x in range(1, num_units + 1):
positions.append((
self.position.x + launcher_distance * math.cos(math.radians(current_offset)),
self.position.y + launcher_distance * math.sin(math.radians(current_offset)),
current_offset,
))
current_offset += outer_offset
return positions

19
gen/sam/sam_avenger.py Normal file
View File

@@ -0,0 +1,19 @@
import random
from dcs.vehicles import AirDefence, Unarmed
from gen.sam.group_generator import GroupGenerator
class AvengerGenerator(GroupGenerator):
"""
This generate an Avenger group
"""
def generate(self):
num_launchers = random.randint(2, 3)
self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading)
positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180)
for i, position in enumerate(positions):
self.add_unit(AirDefence.SAM_Avenger_M1097, "SPAA#" + str(i), position[0], position[1], position[2])

19
gen/sam/sam_chaparral.py Normal file
View File

@@ -0,0 +1,19 @@
import random
from dcs.vehicles import AirDefence, Unarmed
from gen.sam.group_generator import GroupGenerator
class ChaparralGenerator(GroupGenerator):
"""
This generate a Chaparral group
"""
def generate(self):
num_launchers = random.randint(2, 4)
self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading)
positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180)
for i, position in enumerate(positions):
self.add_unit(AirDefence.SAM_Chaparral_M48, "SPAA#" + str(i), position[0], position[1], position[2])

18
gen/sam/sam_gepard.py Normal file
View File

@@ -0,0 +1,18 @@
import random
from dcs.vehicles import AirDefence, Unarmed
from gen.sam.group_generator import GroupGenerator
class GepardGenerator(GroupGenerator):
"""
This generate a Gepard group
"""
def generate(self):
self.add_unit(AirDefence.SPAAA_Gepard, "SPAAA", self.position.x, self.position.y, self.heading)
if random.randint(0, 1) == 1:
self.add_unit(AirDefence.SPAAA_Gepard, "SPAAA2", self.position.x, self.position.y, self.heading)
self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x + 80, self.position.y, self.heading)

View File

@@ -0,0 +1,100 @@
import random
from dcs.vehicles import AirDefence
from game import db
from gen.sam.aaa_bofors import BoforsGenerator
from gen.sam.aaa_flak import FlakGenerator
from gen.sam.aaa_zu23_insurgent import ZU23InsurgentGenerator
from gen.sam.sam_avenger import AvengerGenerator
from gen.sam.sam_chaparral import ChaparralGenerator
from gen.sam.sam_gepard import GepardGenerator
from gen.sam.sam_hawk import HawkGenerator
from gen.sam.sam_hq7 import HQ7Generator
from gen.sam.sam_linebacker import LinebackerGenerator
from gen.sam.sam_patriot import PatriotGenerator
from gen.sam.sam_rapier import RapierGenerator
from gen.sam.sam_roland import RolandGenerator
from gen.sam.sam_sa10 import SA10Generator
from gen.sam.sam_sa11 import SA11Generator
from gen.sam.sam_sa13 import SA13Generator
from gen.sam.sam_sa15 import SA15Generator
from gen.sam.sam_sa19 import SA19Generator
from gen.sam.sam_sa2 import SA2Generator
from gen.sam.sam_sa3 import SA3Generator
from gen.sam.sam_sa6 import SA6Generator
from gen.sam.sam_sa8 import SA8Generator
from gen.sam.sam_sa9 import SA9Generator
from gen.sam.sam_vulcan import VulcanGenerator
from gen.sam.sam_zsu23 import ZSU23Generator
from gen.sam.sam_zu23 import ZU23Generator
from gen.sam.sam_zu23_ural import ZU23UralGenerator
from gen.sam.sam_zu23_ural_insurgent import ZU23UralInsurgentGenerator
SAM_MAP = {
AirDefence.SAM_Hawk_PCP: HawkGenerator,
AirDefence.AAA_ZU_23_Emplacement: ZU23Generator,
AirDefence.AAA_ZU_23_Closed: ZU23Generator,
AirDefence.AAA_ZU_23_on_Ural_375: ZU23UralGenerator,
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375: ZU23UralInsurgentGenerator,
AirDefence.AAA_ZU_23_Insurgent_Closed: ZU23InsurgentGenerator,
AirDefence.AAA_ZU_23_Insurgent: ZU23InsurgentGenerator,
AirDefence.SPAAA_ZSU_23_4_Shilka: ZSU23Generator,
AirDefence.AAA_Vulcan_M163: VulcanGenerator,
AirDefence.SAM_Linebacker_M6: LinebackerGenerator,
AirDefence.Rapier_FSA_Launcher: RapierGenerator,
AirDefence.SAM_Avenger_M1097: AvengerGenerator,
AirDefence.SPAAA_Gepard: GepardGenerator,
AirDefence.SAM_Roland_ADS: RolandGenerator,
AirDefence.SAM_Patriot_LN_M901: PatriotGenerator,
AirDefence.SAM_Patriot_EPP_III: PatriotGenerator,
AirDefence.SAM_Chaparral_M48: ChaparralGenerator,
AirDefence.AAA_Bofors_40mm: BoforsGenerator,
AirDefence.AAA_8_8cm_Flak_36: FlakGenerator,
AirDefence.SAM_SA_2_LN_SM_90: SA2Generator,
AirDefence.SAM_SA_3_S_125_LN_5P73: SA3Generator,
AirDefence.SAM_SA_6_Kub_LN_2P25: SA6Generator,
AirDefence.SAM_SA_8_Osa_9A33: SA8Generator,
AirDefence.SAM_SA_9_Strela_1_9P31: SA9Generator,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C: SA10Generator,
AirDefence.SAM_SA_10_S_300PS_CP_54K6: SA10Generator,
AirDefence.SAM_SA_11_Buk_LN_9A310M1: SA11Generator,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3: SA13Generator,
AirDefence.SAM_SA_15_Tor_9A331: SA15Generator,
AirDefence.SAM_SA_19_Tunguska_2S6: SA19Generator,
AirDefence.HQ_7_Self_Propelled_LN: HQ7Generator
}
def generate_anti_air_group(game, parent_cp, ground_object, faction:str):
"""
This generate a SAM group
:param parentCp: The parent control point
:param ground_object: The ground object which will own the sam group
:param country: Owner country
:return: Nothing, but put the group reference inside the ground object
"""
possible_sams = [u for u in db.FACTIONS[faction]["units"] if u in AirDefence.__dict__.values()]
if len(possible_sams) > 0:
sam = random.choice(possible_sams)
generator = SAM_MAP[sam](game, ground_object)
generator.generate()
return generator.get_generated_group()
return None
def generate_shorad_group(game, parent_cp, ground_object, faction:str):
if("shorad") in db.FACTIONS[faction].keys():
shorad = db.FACTIONS[faction]["shorad"]
sam = random.choice(shorad)
generator = SAM_MAP[sam](game, ground_object)
generator.generate()
return generator.get_generated_group()
else:
return generate_anti_air_group(game, parent_cp, ground_object, faction)

25
gen/sam/sam_hawk.py Normal file
View File

@@ -0,0 +1,25 @@
import random
from dcs.vehicles import AirDefence
from gen.sam.group_generator import GroupGenerator
class HawkGenerator(GroupGenerator):
"""
This generate an HAWK group
"""
def generate(self):
self.add_unit(AirDefence.SAM_Hawk_PCP, "PCP", self.position.x, self.position.y, self.heading)
self.add_unit(AirDefence.SAM_Hawk_SR_AN_MPQ_50, "SR", self.position.x + 20, self.position.y, self.heading)
self.add_unit(AirDefence.SAM_Hawk_TR_AN_MPQ_46, "TR", self.position.x + 40, self.position.y, self.heading)
# Triple A for close range defense
self.add_unit(AirDefence.AAA_Vulcan_M163, "AAA", self.position.x + 20, self.position.y+30, self.heading)
num_launchers = random.randint(3, 6)
positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180)
for i, position in enumerate(positions):
self.add_unit(AirDefence.SAM_Hawk_LN_M192, "LN#" + str(i), position[0], position[1], position[2])

25
gen/sam/sam_hq7.py Normal file
View File

@@ -0,0 +1,25 @@
import random
from dcs.vehicles import AirDefence
from gen.sam.group_generator import GroupGenerator
class HQ7Generator(GroupGenerator):
"""
This generate an HQ7 group
"""
def generate(self):
self.add_unit(AirDefence.HQ_7_Self_Propelled_STR, "STR", self.position.x, self.position.y, self.heading)
self.add_unit(AirDefence.HQ_7_Self_Propelled_LN, "LN", self.position.x + 20, self.position.y, self.heading)
# Triple A for close range defense
self.add_unit(AirDefence.AAA_ZU_23_on_Ural_375, "AAA1", self.position.x + 20, self.position.y+30, self.heading)
self.add_unit(AirDefence.AAA_ZU_23_on_Ural_375, "AAA2", self.position.x - 20, self.position.y-30, self.heading)
num_launchers = random.randint(0, 3)
if num_launchers > 0:
positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360)
for i, position in enumerate(positions):
self.add_unit(AirDefence.HQ_7_Self_Propelled_LN, "LN#" + str(i), position[0], position[1], position[2])

19
gen/sam/sam_linebacker.py Normal file
View File

@@ -0,0 +1,19 @@
import random
from dcs.vehicles import AirDefence, Unarmed
from gen.sam.group_generator import GroupGenerator
class LinebackerGenerator(GroupGenerator):
"""
This generate an m6 linebacker group
"""
def generate(self):
num_launchers = random.randint(2, 4)
self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading)
positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180)
for i, position in enumerate(positions):
self.add_unit(AirDefence.SAM_Linebacker_M6, "M6#" + str(i), position[0], position[1], position[2])

30
gen/sam/sam_patriot.py Normal file
View File

@@ -0,0 +1,30 @@
import random
from dcs.vehicles import AirDefence
from gen.sam.group_generator import GroupGenerator
class PatriotGenerator(GroupGenerator):
"""
This generate a Patriot group
"""
def generate(self):
# Command Post
self.add_unit(AirDefence.SAM_Patriot_AMG_AN_MRC_137, "MRC", self.position.x, self.position.y, self.heading)
self.add_unit(AirDefence.SAM_Patriot_ECS_AN_MSQ_104, "MSQ", self.position.x + 30, self.position.y, self.heading)
self.add_unit(AirDefence.SAM_Patriot_ICC, "ICC", self.position.x + 60, self.position.y, self.heading)
self.add_unit(AirDefence.SAM_Patriot_EPP_III, "EPP", self.position.x, self.position.y + 30, self.heading)
self.add_unit(AirDefence.SAM_Patriot_STR_AN_MPQ_53, "ICC", self.position.x + 30, self.position.y + 30, self.heading)
num_launchers = random.randint(2, 4)
positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360)
for i, position in enumerate(positions):
self.add_unit(AirDefence.SAM_Patriot_LN_M901, "LN#" + str(i), position[0], position[1], position[2])
# Short range protection for high value site
num_launchers = random.randint(2, 4)
positions = self.get_circular_position(num_launchers, launcher_distance=300, coverage=360)
for i, position in enumerate(positions):
self.add_unit(AirDefence.AAA_Vulcan_M163, "SPAAA#" + str(i), position[0], position[1], position[2])

21
gen/sam/sam_rapier.py Normal file
View File

@@ -0,0 +1,21 @@
import random
from dcs.vehicles import AirDefence
from gen.sam.group_generator import GroupGenerator
class RapierGenerator(GroupGenerator):
"""
This generate a Rapier Group
"""
def generate(self):
self.add_unit(AirDefence.Rapier_FSA_Blindfire_Tracker, "BT", self.position.x, self.position.y, self.heading)
self.add_unit(AirDefence.Rapier_FSA_Optical_Tracker, "OT", self.position.x + 20, self.position.y, self.heading)
num_launchers = random.randint(3, 6)
positions = self.get_circular_position(num_launchers, launcher_distance=80, coverage=240)
for i, position in enumerate(positions):
self.add_unit(AirDefence.Rapier_FSA_Launcher, "LN#" + str(i), position[0], position[1], position[2])

Some files were not shown because too many files have changed in this diff Show More