905 Commits
v1.3.0 ... dev

Author SHA1 Message Date
Taco
9520b76246 Updated soundhandler with mirror performance for Red and Blue Clients… (#575) 2025-11-09 22:21:04 -05:00
Starfire13
67183c31da Improve B-52 mission types and update B-52 and B-1 loadouts (#615) 2025-11-09 21:50:01 -05:00
Druss99
54759f1f94 Fixed bugs wrt planning escort flights (#612) 2025-11-09 21:49:39 -05:00
Starfire13
0fcf308079 Add Mi-17 variant to Mi-8MT configuration (#614) 2025-11-08 18:09:44 -05:00
Drexyl
1ad27e27ea Update Splash Damage Plugin to 3.4.2 (#601)
Co-authored-by: Druss99 <42724070+Druss99@users.noreply.github.com>
2025-11-08 14:35:17 +00:00
Starfire13
eae5e1af30 Starfire campaign updates (#613) 2025-11-08 08:40:40 -05:00
Starfire13
b268d09432 Swap intra-flight and inter-flight radio indices (#608) 2025-11-07 08:33:25 -05:00
Drexyl
220b83a22d Add additional supported Carrier/LHA for Airboss (#603) 2025-11-07 08:32:07 -05:00
Starfire13
83530af35d 10.8 Update for Noisy Cricket, Aegean Aegis, and Vegas Nerve (#607) 2025-11-06 15:18:57 -05:00
Starfire13
00475d767b Update Final Countdown II and Operation Velvet Thunder to 10.8 (#606) 2025-11-06 09:43:14 -05:00
Starfire13
1a8faff717 Update Tornado loadouts (#610) 2025-11-06 09:39:36 -05:00
Druss99
68e0d43a1d fix frontline generation (#609) 2025-11-05 17:52:38 -05:00
Starfire13
0510af8b6b Added missing High Digit SAMs mod version (#605) 2025-11-02 14:59:32 -05:00
Raffson
c54ac4e21a Save compat fix w.r.t. renamed airport in AFG 2025-10-28 21:05:56 +01:00
Raffson
bbc41f4c1a Pydcs update for airfields (Normandy/Lappi/AFG) 2025-10-27 21:10:59 +01:00
Raffson
8bc8eb0e28 Update beacons 2025-10-27 20:50:24 +01:00
Starfire13
86f647cc6d Starfire campaign updates #1 (#604) 2025-10-26 23:16:40 +00:00
Raffson
2f16916ac4 Rename & Extend WW2-Essex layout to support ARA-VDM + CVA-31 2025-10-26 18:21:38 +01:00
Druss99
06044dd4c3 fix frontline debriefing (#602) 2025-10-23 07:15:37 -04:00
Eclipse/Druss99
c46cce01b6 fix ownfor being set to UN Peacekeepers regardless of campaign 2025-10-21 14:20:20 -04:00
Eclipse/Druss99
d78e570d9d fix a bug with untasked aircraft still spawning 2025-10-20 15:14:43 -04:00
Eclipse/Druss99
f4a1908088 fix a bug with pretense generation wrt neutral bases 2025-10-20 15:14:43 -04:00
Druss99
23341e69fa update changelog (#596) 2025-10-19 22:09:48 -04:00
Druss99
11fc28ad3a fix bugs wrt divert airfields for opfor (#595) 2025-10-19 21:40:40 -04:00
Druss99
9b96b7be26 fix runtime error where dictionary size changes (#594) 2025-10-19 20:10:19 -04:00
Raffson
f7ee923cbf Bump campaign version to 10.8 for neutral base support 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
3d788e3ae8 fix numerous UI elements using is_ownfor 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
624f2a00fd show PRESENT status for neutral TGOs 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
6c3cbbf0de avoid spawning neutral tgos with units 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
96d0b2b7d9 fix save compat 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
855d269420 refactor trigger zone generation 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
c0748e2a3e add support for neutral FOBs 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
9f10ecc884 fix save compat 2025-10-19 19:34:38 +02:00
Eclipse/Druss99
31c80dfd02 refactor of previous commits
refactor to enum

typing and many other fixes

fix tests

attempt to fix some typescript

more typescript fixes

more typescript test fixes

revert all API changes

update to pydcs

mypy fixes

Use properties to check if player is blue/red/neutral

update requirements.txt

black -_-

bump pydcs and fix mypy

add opponent property

bump pydcs
2025-10-19 19:34:38 +02:00
Drexyl
362ce66f80 Airboss Moose Module 1.0 (#592)
* Initial Commit

* Rescue Helo Options

* Main modifications to Airboss/Moose Dev Branch

* Restore plugin.json

* Airboss Moose Module 1.0

* Fix plugins.json

---------

Co-authored-by: Druss99 <42724070+Druss99@users.noreply.github.com>
2025-10-18 14:08:48 +00:00
Drexyl
96ee73dd50 Fix for Offensive Jamming when object has been destroyed (#591)
* Fix Offensive Jamming when object has been destroyed
2025-10-18 09:42:02 -04:00
Eclipse/Druss99
f82fc3a930 use aircraft display names instead of variant ids in faction editor 2025-10-12 19:53:01 +02:00
Eclipse/Druss99
cf575fba2a fix a bug with parking allocation 2025-10-12 19:42:52 +02:00
Eclipse/Druss99
96f940bc59 fix bugs with mixed parking 2025-10-12 19:01:07 +02:00
Drexyl
98c71c255b Fix to exclude Moose plugins from Pretense injection (#585) 2025-10-07 18:58:19 -04:00
Drexyl
0e5567ceee Update EW Script with fixes/enhancements (#584) 2025-10-07 08:13:44 -04:00
Druss99
2832ece316 add channels for MiG-29 Fulcrum (#582) 2025-10-05 14:27:06 -04:00
Druss99
5cfd6c86f3 add radios for MiG-29 Fulcrum (#581) 2025-10-04 13:48:11 -04:00
Druss99
27ef269435 add support for more units (#580) 2025-09-22 18:27:34 -04:00
Druss99
8b46ef5e35 add support for MiG-29A Fulcrum (#579) 2025-09-20 18:41:23 -04:00
Eclipse/Druss99
e243d074ff su30 mod update 2025-09-20 19:36:41 +02:00
Eclipse/Druss99
cc39e6579b bump pydcs 2025-09-20 19:36:41 +02:00
Druss99
d7eaf1330f remove antiship from tu-160 (#577) 2025-09-20 13:05:02 -04:00
Druss99
c44ea0025d Fix plugins.json & moose plugins (#574) 2025-09-08 00:35:54 +02:00
Taco
1216c90891 Add Moose's Soundhandler Plugin (#558)
* Soundhandler: Adds sounds for weapons fired at or by blue units as well as destruction sounds for enemy jets and ground units.  By default plays only to the groups involved.

* Added Specific Options and removed 0.1 from mnemonic

* Update was incomplete before, use this one

* Linked in UI choices and consolidated logging functions

* Remove soundhandler duplicate

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-09-07 17:25:14 +00:00
Drexyl
ea80ddc321 Update CJS Super Hornet Mod to 2.4.4 (#572)
* Update CJS Super Hornet Mod to 2.4.4

* Update UI for CJS Super Hornet Mod to 2.4.4

* Update default loadouts for CJS Super Hornet Mod to 2.4.4

* Update changelog.md for CJS Super Hornet Mod to 2.4.4

* Update Growler loadout for missing external fuel tank
2025-09-07 19:24:07 +02:00
Druss99
121ca20edb Fix naval squadron retreat bug (#567) 2025-09-07 17:37:07 +02:00
Starfire13
e4586add72 Add factories to Northern Guardian (#571) 2025-09-07 17:29:45 +02:00
Xisco Lladó
32b2e40b37 Update convoys spawns & CTLD landing zones for Syrian Shield (#570)
* updated convoys spawns and CTLD landing zones

* removed red helipads on palmyra zone

---------

Co-authored-by: Xisco <x.llado@hotelinking.com>
2025-09-07 17:29:00 +02:00
Taco
e8190748eb New Moose-based Autolase alternative with UI options (#565) 2025-08-27 19:24:00 +02:00
Drexyl
d2aa615133 Update EW Script and refine logic (#555) 2025-08-27 19:20:27 +02:00
Starfire13
756602e682 Update Su-24 loadouts (#566) 2025-08-27 19:18:39 +02:00
Taco
204950e487 MarkerOps Plugin (#560)
* MarkerOps

* Change MarkerOps to MooseMarkerOps in all places

* Prettify name in UI

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-08-23 16:37:34 +02:00
Druss99
6290607eb6 Expend all ARMs for SEAD (#562) 2025-08-13 12:32:36 +02:00
Druss99
afad73ac27 Fix bugs while loading air wing configs (#552)
* fix bugs with air wing config loading

* add aircraft type to airwing config for better loading
2025-08-10 21:14:38 +02:00
Eclipse/Druss99
52fbc75e1c update changelog 2025-08-10 19:40:06 +02:00
Xisco Lladó
5c5af40bd5 Update Operation syrian shield (#554)
* New campaign Operation_syrian_shield

* removed author on player factions. Credited to 'Fuzzle'

* changed recommended factions to the easy way

* Update operation_syrian_shield.yaml

removed mission time, and squadrons start full

* Update operation_syrian_shield.yaml

better campaign description

* added better squadron descriptions

* updated campaign description

---------

Co-authored-by: Xisco <x.llado@hotelinking.com>
2025-08-10 16:19:50 +02:00
ingax01
d1f66ed040 Remove unused SU-30 payload files (#551) 2025-08-04 00:08:28 +02:00
Druss99
6467193c88 Save-compat fix for su-30 update (#553) 2025-08-04 00:07:38 +02:00
Drexyl
b2670ba30b Add Military Aircraft Mod 1.7.2 (#547) 2025-08-03 02:02:14 +02:00
Raffson
dcf4da57b3 Update starlette + fastapi 2025-08-02 19:27:30 +02:00
Raffson
6f825db086 Update h11 2025-08-02 19:27:15 +02:00
Raffson
afa9122885 Bump electron to 28.3.2 2025-08-02 19:09:31 +02:00
Raffson
df8e09a1ad Update npm packages 2025-08-02 19:00:36 +02:00
Druss99
5d038e8247 Add support for Control Point Influence Zones (#540) 2025-08-02 18:47:55 +02:00
Xisco Lladó
ce70952ee0 New campaign Operation_syrian_shield (#544)
* New campaign Operation_syrian_shield

* removed author on player factions. Credited to 'Fuzzle'

* changed recommended factions to the easy way

* Update operation_syrian_shield.yaml

removed mission time, and squadrons start full

* Update operation_syrian_shield.yaml

better campaign description

* added better squadron descriptions

---------

Co-authored-by: Xisco <x.llado@hotelinking.com>
2025-08-02 18:30:15 +02:00
Drexyl
1d16aba678 Add Moose as base plugin (#522) 2025-08-02 18:28:36 +02:00
Revinl
f3156ab51a Northern Guardian campaign for the Germany map by Revinl (#545) 2025-08-02 16:27:52 +00:00
ingax01
ed9fa37341 Updated SU-30 weapon definitions to version v2.8.03 Beta + CWS 3.70 (#546) 2025-08-02 18:20:01 +02:00
Starfire13
12aa1ee287 Add sniper pod to Viper loadout (#549) 2025-08-02 18:15:07 +02:00
Druss99
7e755f1eb1 bump pydcs (#548) 2025-07-28 22:18:29 +02:00
Starfire13
3da1689767 Fixed typos in Tetrarch description (#542) 2025-07-21 16:53:02 +02:00
Raffson
0671e45dde Restore squadrons for Essex-transformed carriers 2025-07-20 12:36:48 +02:00
Drexyl
c15b4ac83f Essex as AircraftCarrier hack (#541) 2025-07-19 18:21:33 +00:00
Starfire13
f1eb514295 Crossing the Rubicon improved performance (#539) 2025-07-14 12:38:57 +02:00
Raffson
df8f1c4291 Fix some auto-rotate issues in exercise quasar 2025-07-13 19:19:09 +02:00
Raffson
c32525b542 Formatting -_- 2025-07-13 19:05:10 +02:00
Raffson
db14b691cc Make F4U-1D Carrier/LHA capable 2025-07-13 18:55:09 +02:00
Raffson
052a04ca05 Properly support Essex carrier 2025-07-13 18:41:20 +02:00
Raffson
ad100a1a17 Overrride WFMR-window's reject to properly reset game-state 2025-07-13 13:46:57 +02:00
Drexyl
4412a0fa91 Add Tornado F3 ADV (1.0) mod support 2025-07-13 13:35:53 +02:00
Starfire13
23a7add2f1 Add missing Apache and FC Tiger II to Blufor Late Cold War faction (#538) 2025-07-13 13:34:21 +02:00
Starfire13
040362b333 Add Viet Cong 1965 faction (#537) 2025-07-13 13:32:59 +02:00
Starfire13
4155afc69a Set performance to 3/3 for Crossing The Rubicon (#536)
Setting this to 3/3 for now as folks are having performance issues
2025-07-13 13:32:05 +02:00
Starfire13
cf84c03d8f Implements campaign inversion for 13 of Starfire's campaigns (#535) 2025-07-13 13:31:32 +02:00
Starfire13
336384cd74 Implement campaign inversion for Crossing the Rubicon (#534) 2025-07-13 13:30:27 +02:00
Druss99
da7d3a3f1a Fix S-3B unable to do OCA/Aircraft (#533) 2025-07-08 22:25:40 +00:00
Starfire13
1231cb145d Add Germany Cold War campaign (#530) 2025-07-09 00:16:50 +02:00
Drexyl
47488a0bdd F4U-1D Banner Image (#529) 2025-07-08 07:55:58 +02:00
Starfire13
015859e91e F4U-1D loadouts (#528) 2025-07-08 00:39:34 +02:00
Raffson
9c0d114085 Save compat fix for Sinai's renamed Palmahim to Palmachim 2025-07-06 22:40:23 +02:00
Raffson
f907356f93 Pydcs update for new Sinai airports + save-compat fix 2025-07-06 22:37:07 +02:00
Raffson
fcb610423b Update beacon-data 2025-07-06 22:12:32 +02:00
Raffson
3086f64e98 F4U-1D support except banner & payloads 2025-07-06 22:09:50 +02:00
Raffson
5592a834c9 Downgrade h11 to 0.14 2025-07-06 21:59:09 +02:00
Raffson
1e48affc1c Add Japanese WW2 ground units 2025-07-06 21:59:09 +02:00
Raffson
f27b6e28e0 Add support Essex 2025-07-06 21:58:59 +02:00
Raffson
d0ff271a79 Update pydcs for 2.9.17.12034 2025-07-06 19:31:05 +02:00
Cedric Menard
9a00a60cd4 Initial support for Germany Cold War (#518)
* Initial support of Germany Cold War terrain

* Updated with latest beacon import

* Updated changelog and ran black

* Fixed the GermanyCW landmap and naming scheme

* Temporarily changed the requirements for pydcs to my branch (will switch back when merged)

* Updated landmap for germanycw with even fewer exclusions

* Removed unnecessary changes

* Updated requirements to lastest pydcs

* Fixed naming issues for Germany

* Update pydcs to latest commit for GCW support

* Add landmap to game.theater.__init__

* Update requirements.txt

* Resolve PyCharm type-waring

* Updated pydcs and exclusion of germany

* Updated land and sea shapefiles for Germany Cold War with new map limits

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-07-06 19:30:00 +02:00
MrPozor
5254ef3442 Fix #304: Waypoint numbers should start at 0 on map (#526) 2025-06-11 19:50:30 +02:00
MrPozor
fe8efbe905 Revert "Waypoint numbers should start at 0 on map. (#524)" (#525) 2025-06-10 21:28:14 +02:00
MrPozor
48040d7306 Waypoint numbers should start at 0 on map. (#524) 2025-06-09 22:58:15 +02:00
Raffson
f3ed25b5b2 Make EPLRS configurable
Resolves #517
2025-06-07 22:04:56 +02:00
Raffson
2d15db1932 Update h11 to 0.16.0 2025-06-07 19:40:19 +02:00
Raffson
1fa1f830d4 Update bug-report templates for v1.4.1 2025-06-07 19:33:05 +02:00
Raffson
0c0291fc5e Changelog for v1.4.1 2025-06-07 19:32:29 +02:00
Raffson
24c6b62ad4 Fix missing check in handle_ew_jamming w.r.t. plugin option 2025-06-07 19:23:36 +02:00
Starfire13
8c9343e0db Campaign inversion fix for Operation Vectron's Claw (#520) 2025-06-07 16:04:56 +02:00
Starfire13
a33da01af6 Blufor Late Cold War & Russia 1980 faction update (#521) 2025-06-07 16:03:40 +02:00
Starfire13
ce8ccb0227 Update README.md with correct link to preview builds page on the wiki (#519) 2025-05-30 13:10:07 +02:00
Drexyl
68a9dbd141 Update Grinnelli Designs F-22A Mod to 2.0.0 (May 2025) (#515) 2025-05-28 00:54:14 +02:00
Drexyl
4c3f0a82d9 Add EA-6B Prowler to ALQ-99 Jammer Pod (#516) 2025-05-28 00:48:55 +02:00
Starfire13
dc85287cd8 Add locales to Ukraine factions (#513)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-05-24 15:48:38 +00:00
szcz13
65f4a2cf86 Added new and updated weapons CLSIDs and payloads for MiG-31BM mod (#514) 2025-05-24 17:38:16 +02:00
Druss99
02de6d325a Add invisible FOBs (#504)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-05-18 17:50:47 +02:00
Druss99
7b16967641 Prevent saving packages with Escorts only (#509)
Fixes a bug that would later pop up when trying to calculate start-times...
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-05-17 15:58:35 +00:00
Starfire13
0e7ea0050f Add OCA-Aircraft to B-21's tasks (#510)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-05-17 15:57:12 +00:00
Starfire13
ec0e64eaf4 Add Growler + Raptor squadrons to some campaigns (#505) 2025-05-17 17:55:41 +02:00
szcz13
b12169f180 Update for MiG-31BM v2.6 (#512)
* Add files via upload

* Add files via upload

* Update start_generator.py

* Update faction.py

* Update QNewGameWizard.py

* Update QGeneratorSettings.py

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Update MiG-31BM.lua

* Added SEAD Escort to MiG-31BM.yaml

* mig31bm.py fixed weapon injection

* Update mig31bm.py

formatted black

* Update faction.py

reformatted black

* Update start_generator.py

reformatted black

* Update __init__.py

* Update QGeneratorSettings.py

reformatted black

* added support for MiG-31BM mod

* Commit changes which can't seem to be discarded...

* Remove (presumably) accidentally copied file

* Remove unused imports

* Updated MiG-31BM mod version in QGeneratorSettings.py

* Added new Kh-31P missile to MiG-31BM mod

* Added new Kh-31P missile to MiG-31BM mod

* Updated MiG-31BM mod payloads for a new Kh-31P.lua

* Remove double entry

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-05-17 15:55:05 +00:00
Raffson
3a5d0e831c Formatting -_- 2025-05-10 16:40:57 +02:00
Raffson
903a6c4d5f Update changelog 2025-05-07 20:12:03 +02:00
Raffson
e36f07a3ff Preference option to set up preferences on every start (#503) 2025-05-07 20:02:21 +02:00
Raffson
2d65a038ee Remove print statements 2025-05-06 17:24:29 +02:00
Raffson
09617adc56 Passive defense for non-air-to-air flights in package with jammer 2025-05-01 19:57:01 +02:00
Raffson
01832b6594 Add MPS-410 to weapons data 2025-05-01 19:41:02 +02:00
Raffson
30ad71f93b Refactor & improve EW-jamming setup in mission-generator 2025-05-01 17:22:46 +02:00
Raffson
4f8ce77e50 Add "has_built_in_ecm" field to AircraftType
To be used with EW-jamming script...
2025-05-01 17:21:52 +02:00
Raffson
464ece60da Save-compat fix w.r.t. renamed airfield 2025-04-21 21:24:32 +02:00
Raffson
cafaea897d Pydcs update for new Lappi airfields 2025-04-21 21:21:36 +02:00
Raffson
58820dd6c6 Update Lappi beacons 2025-04-21 21:18:29 +02:00
Starfire13
8e845e6a27 Add Tornado IDS to Blufor Late Cold War faction (#502)
Add missing Tornado IDS
2025-04-21 18:04:13 +02:00
Raffson
e1b55816e3 Update actions/checkout to v4 2025-04-20 23:21:00 +02:00
Raffson
e95661c6a2 Update actions/setup-node to v4 2025-04-20 23:17:23 +02:00
Raffson
c96e0d8f28 Update actions/setup-python to v5? 2025-04-20 23:14:52 +02:00
Raffson
dfb65facb5 Another attempt at fixing the pipeline -_- 2025-04-20 23:01:22 +02:00
Raffson
8c6ca4de5f Pin python version for black in lint.yml 2025-04-20 22:58:19 +02:00
Raffson
1c3dee2098 Pydcs data update 2025-04-19 17:49:21 +02:00
Raffson
170e432008 Preparations for v1.5.0 2025-04-14 21:34:17 +02:00
Raffson
c8e3274c4e Update upload/download-artifact to v4 in release pipeline 2025-04-14 21:31:04 +02:00
Raffson
94105158de Remove note wr.t. datalink 2025-04-14 21:15:37 +02:00
Raffson
410821d63e Fix switched up radios for M-2000C 2025-04-14 14:11:10 +02:00
Raffson
2140ed9b67 Fix loadout issues w.r.t. Growler 2025-04-13 18:00:43 +02:00
Starfire13
cd77e3ee11 Squadron update for Clash of the Titans (#501) 2025-04-13 15:11:22 +02:00
Raffson
780078fec4 Fix issues w.r.t. custom forced-options config 2025-04-13 15:06:31 +02:00
Raffson
f8b4c7bcf5 Ability to configure forced-options via a file
https://github.com/dcs-retribution/dcs-retribution/issues/490
2025-04-12 18:31:01 +02:00
Raffson
de1f8a7abc Attempt to fix SplashDamage error w.r.t. line 604
Guard against event.target:getPosition() potentially being nil
2025-04-12 17:24:23 +02:00
Raffson
1db84307fe Add missing datalink properties for Superhornet mod 2025-04-06 15:38:30 +02:00
Raffson
ace9f6bbc5 Fix bug in Refueling wpt's predicate 2025-04-06 15:26:46 +02:00
Raffson
8cd2b55e04 Pydcs datalink fix w.r.t. Superhornet mod 2025-04-06 15:08:42 +02:00
Raffson
c404ea83fc Follow-up w.r.t. MiG-31BM by Szcs 2025-04-06 15:02:42 +02:00
Drexyl
a544b96f2c Super Hornet 2.4.2 Fixes 2025-04-05 22:28:41 +02:00
szcz13
ffaf905bf6 MiG-31BM by szcz mod support (#496)
* Add files via upload

* Add files via upload

* Update start_generator.py

* Update faction.py

* Update QNewGameWizard.py

* Update QGeneratorSettings.py

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Update MiG-31BM.lua

* Added SEAD Escort to MiG-31BM.yaml

* mig31bm.py fixed weapon injection

* Update mig31bm.py

formatted black

* Update faction.py

reformatted black

* Update start_generator.py

reformatted black

* Update __init__.py

* Update QGeneratorSettings.py

reformatted black

* added support for MiG-31BM mod

* Commit changes which can't seem to be discarded...

* Remove (presumably) accidentally copied file

* Remove unused imports

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-04-05 20:19:22 +00:00
Raffson
1520c8c3a3 Attempt to improve SEAD with ARM 2025-03-30 21:48:09 +02:00
Raffson
fc1a768e4c Update/Add F/A-18C's SEAD loadouts 2025-03-30 21:41:08 +02:00
Raffson
89f159edcb Fix FF-bug when interrupt is set to "At takeoff time" 2025-03-30 18:23:37 +02:00
MetalStormGhost
b671d520c9 Cold War assets mod v1.2 support (#482)
* Implemented support for the Cold War assets mod (ex Tu-16) v1.0 by tripod3

* Rename     resources/ui/units/aircrafts/banners/Tu-16.jpg (from resources/ui/units/aircrafts/banners/Tu_16.jpg)
Rename     resources/ui/units/aircrafts/banners/Tu-4K.jpg (from resources/ui/units/aircrafts/banners/Tu_4K.jpg)
Rename     resources/ui/units/aircrafts/icons/Tu-16_24.jpg (from resources/ui/units/aircrafts/icons/Tu_16_24.jpg)
Rename     resources/ui/units/aircrafts/icons/Tu-4K_24.jpg (from resources/ui/units/aircrafts/icons/Tu_4K_24.jpg)

Also added coldwarassets handling in ModSettings() call.

* Added a DEAD loadout for the Tu-22KD.

* Updated Cold War assets mod by tripod3 support to v1.2

* Modified   pydcs_extensions/coldwarassets/coldwarassets.py
Modified   resources/units/aircraft/B_47.yaml

* Modified   qt_ui/windows/newgame/WizardPages/QGeneratorSettings.py

* Added      resources/units/aircraft/Yak_28.yaml

* Added banners, icons, loadouts and unit yamls by Biggus for new Cold War Assets added in mod version 1.2.

* Removed the Tu-128 Fiddler from soviet_union_1965.json because the Tu-128M variant is a 1979 modernization.

* Remove CAS & Armed Recon from Yak-28

* Fix typo

* Add BAI & DEAD to Tu-4K

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-03-29 20:09:58 +00:00
Drexyl
889b7299d3 Add Cowboy's E-7A Wedgetail AI Mod (#495) 2025-03-26 20:40:07 +01:00
Drexyl
631321950e Update Super Hornet 2.4.2 Payloads (#494) 2025-03-25 18:48:32 +01:00
Drexyl
af4bba7396 EW Script 2.0 - Enable for AI (#491)
* EW Script 2.0 - Enable for AI

* Refactor

* Remove unnecessary code + refactor

* Fix test -_-

* Cleanup

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
Co-authored-by: Starfire13 <72491792+Starfire13@users.noreply.github.com>
2025-03-23 20:32:51 +01:00
Raffson
c3fea788f3 Improve AntiShip tasking 2025-03-23 20:27:11 +01:00
Raffson
ee8552a4c6 Update most Python packages 2025-03-23 16:08:48 +01:00
Raffson
2cb1695fb1 Shorten line for black -_- 2025-03-23 14:57:44 +01:00
Starfire13
07aea3985a Clash of the Titans - new AFG East + West campaign (#493) 2025-03-23 12:31:50 +01:00
Raffson
0e0f1ef315 Fix plugin-option exception on new-game
https://discord.com/channels/1015931619187621999/1352641851374571582
2025-03-22 22:46:33 +01:00
Raffson
3610062976 Update beacons for Afghanistan 2025-03-22 21:48:27 +01:00
Raffson
e7c3e41396 Pydcs update (data export + airports Afghanistan) 2025-03-22 21:47:32 +01:00
Raffson
019141f25b Update npm packages 2025-03-22 17:30:41 +01:00
Drexyl
0a930fa2ce Update F/A-18E/F/G Super Hornet Mod to 2.4.0 (#484)
* Update F/A-18E/F/G Super Hornet Mod to 2.4.0

* Update F/A-18E/F/G Super Hornet Mod to 2.4.0

* ECM Pods for E/A-18G Growler to allow EW Jammer 2.0 Plugin

* Update weapons classes for custom weapons

* Adjust default loadouts

* Update for Super Hornet 2.4.2

* Change Settings page to reflect 2.4.2

* Remove redeclared weapons

* Add Recovery-tasking to tankers

* Rewire new tankers to old tanker modsetting

* Small bugfix w.r.t. CLI-tool

* Save-compat fix: remap old Superbug_AITanker to FA_18ET

* Add empty line at EOF

* Revert "Add empty line at EOF"

This reverts commit 4a7f616e47.

* Restore checkbox for AI Tankers

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2025-03-22 16:15:33 +00:00
Raffson
398bb0775d Run black after update 2025-03-22 17:10:01 +01:00
Raffson
71504afd01 Update black 2025-03-22 16:06:54 +01:00
Raffson
5744b12699 Update Jinja2 to patch vulnerability 2025-03-22 16:05:04 +01:00
Starfire13
f03f0bf103 Fix incorrect aircraft type in Anvil of War (#488) 2025-03-02 19:52:08 +01:00
Raffson
aaa47191af Pydcs update for the proper fix wrt liveries... 2025-02-16 18:37:53 +01:00
Raffson
083475b40c Revert "Update pydcs for livery-scanner fix"
This reverts commit 2660affa63.
2025-02-16 17:43:32 +01:00
Raffson
2660affa63 Update pydcs for livery-scanner fix 2025-02-16 15:44:37 +01:00
Starfire13
cb988f854d Add new Caucasus campaign - Slava Ukraini (#485) 2025-02-16 00:18:28 +01:00
Druss99
d4371ccbf6 CurrentHill Ukraine Assets Pack (#481) 2025-02-16 00:17:43 +01:00
MetalStormGhost
308805e09b Mirage III mod version 2.5.7.01 support (#351)
* Implemented support for the Mirage III mod version 2.5.7.01.

* Switched the Mirage III CAS loadouts for bombs.

* Updated changelog.

* Modified   resources/factions/argentina_1982.json
2025-02-15 18:52:52 +01:00
Starfire13
34e7093198 Fix Able Archer strike targets broken by map update (#486) 2025-02-15 18:46:16 +01:00
Starfire13
459940308e Add Mi-26 to Redfor factions (#483) 2025-02-08 21:40:41 +01:00
Eclipse/Druss99
5fc5e27ee0 remove unnecessary aircraft icon mapping 2025-02-05 23:37:07 +01:00
Eclipse/Druss99
394a9d05b2 fix missile sites not being removed when mod is disabled 2025-02-05 23:37:07 +01:00
Eclipse/Druss99
1e74f8f124 fix error if missions directory does not exist 2025-02-05 23:37:07 +01:00
Druss99
4ef2f6c22a Allow refueling & escort on friendly FOBs, Airfields & Carriers (#477) 2025-02-04 22:36:41 +01:00
Raffson
5270ad42db Update upload-artifact to v4 2025-01-30 21:58:48 +01:00
Starfire13
7e297135be Update campaign & add new faction (USA 2020) (#476)
* Add files via upload

* Add files via upload
2025-01-30 21:32:42 +01:00
Eclipse/Druss99
60bfde3b61 update changelog and fix typing 2025-01-28 19:09:02 +01:00
Eclipse/Druss99
5fc967cfd2 Missile sites now fire at random times 2025-01-28 19:09:02 +01:00
Druss99
e41c923ad4 Adjust FF to check for flights (#473) 2025-01-28 19:05:11 +01:00
Druss99
1bb0db920b More generic layout for CH anti ship sites, nerf + add new sites for China (#472) 2025-01-28 19:03:17 +01:00
Starfire13
eeaae101d2 Remove SEAD and SEAD Escort from Gripen (#475) 2025-01-28 18:58:32 +01:00
Druss99
a1df78c099 fix save compat for changed mod unit names (#471) 2025-01-28 18:56:05 +01:00
Starfire13
0be821b972 Update S-350.yaml to LORAD (#470) 2025-01-28 18:54:42 +01:00
Starfire13
10b3dbf7f8 Update S-350_Site.yaml to LORAD (#469) 2025-01-28 18:53:57 +01:00
Starfire13
e48bf4a435 Update Blufor/Redfor Current + update Starfire’s campaigns (#468)
* Add files via upload

* Add files via upload
2025-01-27 20:33:00 +01:00
Druss99
3c955036d2 Fix blufor current (#467) 2025-01-26 22:39:45 +01:00
Raffson
2570293416 Fix incorrect stop trigger trigger for BARCAP 2025-01-26 18:57:59 +01:00
Raffson
f31098419e Fix bad unit yaml 2025-01-26 16:03:09 +01:00
Druss99
1d4b0608de Update swedish military assets pack (#464)
* Update swedish military assets pack

* re add old variant names for save compat

* Update faction.py

fixed presets

* fix double variant name
2025-01-26 14:54:58 +00:00
Druss99
431ec1c382 Correctly remove CH presets for russia (#466) 2025-01-26 15:40:18 +01:00
Starfire13
7c8a0fc032 Update Graveyard of Empires + Red Sea Rising (#463)
Use Blufor Current to support CH USA Asset Pack
2025-01-26 15:37:34 +01:00
Starfire13
da96904016 Anvil of War + Add CH USA Asset Pack to Blufor Current (#462)
* Add files via upload

* Add files via upload
2025-01-26 15:33:39 +01:00
Druss99
a76fe34f3f CurrentHill USA military assets pack (#461)
* usa military assets pack

* minor fixes to have missiles

* tweak nasams 3

* fix preset groups bug

* adjust b-21 name

* Update faction.py

bugfixes will go in a different branch
2025-01-26 15:32:12 +01:00
Druss99
05e80fa144 Adjust S-350 site for balance (#465) 2025-01-26 15:29:25 +01:00
Raffson
5db63a2ebf Fix mission-scheduler bugs wrt BARCAP
- BARCAPs over regular CPs are no longer planned at the same time
- Carriers were planning 1 BARCAP too much if a second wave would be introduced, i.e. (BARCAP-duration / mission-duration) < 1
2025-01-25 19:13:55 +01:00
Druss99
0613c8f423 Minor Asset pack fixes (#460) 2025-01-20 20:38:26 +01:00
Raffson
db73a3aa27 Align pilots in create-flight dialog 2025-01-19 20:53:50 +01:00
Raffson
431492fa77 Introduce support for "rebel-zones" 2025-01-19 19:06:19 +01:00
dependabot[bot]
942faeeaa3 Bump virtualenv from 20.26.2 to 20.26.6 (#459)
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 20.26.2 to 20.26.6.
- [Release notes](https://github.com/pypa/virtualenv/releases)
- [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pypa/virtualenv/compare/20.26.2...20.26.6)

---
updated-dependencies:
- dependency-name: virtualenv
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-19 13:57:01 +00:00
Starfire13
0b40777d68 New Kola campaign - The Anvil of War (#458) 2025-01-19 13:47:45 +01:00
Starfire13
bb3c657158 Add in F-22A mod version number (#454) 2025-01-17 08:06:32 +01:00
Starfire13
83aac3df44 Add Blufor Current faction (#453) 2025-01-17 08:05:10 +01:00
Starfire13
72910b3353 Update Velvet Thunder with Vietnam War Vessels (#452) 2025-01-15 06:27:47 +01:00
Starfire13
587741383c Update Tu-160 mission types (#451) 2025-01-12 20:39:59 +01:00
Raffson
6965133a33 Filter "self" when ground units retreat from CP
Fixes an infinite loop in case a connection to the same CP is made in the template miz file of the campaign. It's basically wrong input, but suppose we'd have a use-case for it in the future, it would still make sense to have ground units retreat to a CP that's different from their origin...
2025-01-12 16:04:52 +01:00
Starfire13
ed4961cdbc New Redfor factions with CurrentHill mods + update Graveyard of Empires (#449)
* Add files via upload

* Add files via upload

* Update and rename redfor_modern.json to redfor_current.json
2025-01-09 20:58:12 +01:00
Druss99
f07cc83c5b CurrentHill Russian Military Assets Pack (#448) 2025-01-07 20:54:50 +01:00
Raffson
a10b94dcee Update fastapi, starlette & Jinja2 2025-01-05 20:43:03 +01:00
Raffson
f112f227cc Update armed forces when preset-group is added 2025-01-05 14:48:28 +01:00
Raffson
807bb16ddf Save-compat improvements 2025-01-05 14:48:28 +01:00
Astro
4c9dba2fe5 Vietnam War Vessels Mod v0.9.0 integration (#435)
* vietnamwarvessels first batch

* Ship YAMLs

* aircraft yamls initial version, need more work

* initial helicopter yamls

* update aircraft yamls

* Added DDs Fletcher and Sullivans

* ship icons

* aircraft banners and icons

* no huts

* update py files to VWV v0.9.0

* update aircraft yamls, add vigilante

* added 2 ships for VWV v0.9.0

* mig-21mf yaml

* icons and banners additional units v0.9.0

* added VWV units to USA_1970 and Vietnam_1970 JSONs

* Revert "added VWV units to USA_1970 and Vietnam_1970 JSONs"

This reverts commit ed0b28dc36.

* A-1H yaml update

* mig-17 yaml update

* update helicopter yamls

* extension init

* weapon injections

* icon filenames _24 added

* removed tasks 0 from yamls

* hh2d yaml fix

* added VWV v0.9.0 to factions USA and Vietnam

* added max_range to aircraft yamls

* housekeeping

* Flyable to False - not available in mod version

* minor edits

* ignore test campaign

* deleted tasks

* weapon luas blue air

* added task numbers from task.py

* weapon luas red air

* task id numbers in comment

* switched weapon lua from aim-9J to aim-9D

* removed test campaigns

* update payload luas with payload names from flighttype.py

* Changed AIM-9D to 9B, 9D does not work

* removed air assault task for HH-2D

* Cva_31 added to runway_is_operational()

* CVA-31 added to naval_units in faction jsons

* add strike and cas tasks to ra-5c

* correct typo

* Added Armed Recon as task and payload to most a/c

* ignore pre-commit-config.yaml

* pre-commit-config

* black reformat controlpoint.py

* Added tasks to Vigilante (next to Recon) containing attack subtasks, which allow it to be scheduled for missions

* added ships to UNITS_WITH_RADAR

* remove pre-commit-config from gitignore

* added red aircraft to nva_1970 faction

* fixed black's complaint (two empty lines, should be one)
2025-01-05 13:50:01 +01:00
Druss99
e93ebbe78c add chinese asset pack to radar db (#445) 2025-01-04 17:52:35 +00:00
Astro
3e1f0bc694 Skynet additional settings (#443) 2025-01-04 17:24:51 +01:00
Starfire13
4e6b4a9b5d Add Redfor (China) 2020 faction + update campaigns (#444)
Red Sea Rising & Exercise Vegas Nerve updated with new faction
2025-01-04 03:12:54 +01:00
Astro
ec81e6c659 Campaign Into the Hornet's Nest (#442) 2025-01-03 18:21:11 +00:00
Druss99
bc157630e9 China military assets support (#440) 2025-01-03 19:19:10 +01:00
Starfire13
918789cee4 Fix Wadiya 2010 formatting (#441)
Fixed formatting
2025-01-02 16:50:55 +01:00
Eclipse/Druss99
dec57fccf9 add vertical takeoff and landing option 2025-01-02 03:24:01 +01:00
Druss99
dd408f392b Fix airwing config not properly saving or loading (#437)
* fix airwing config not properly saving or loading

* add helper message to save function

* bugfixes

* remove unnecessary import used during testing

* yet another forgotten import

* change raise error to log warning
2025-01-01 15:56:08 +01:00
Raffson
164873d3b1 Fix planning error in CAP-builder if no enemy airfields left 2024-12-31 20:11:18 +01:00
Raffson
7ea144e7c7 Fix save-compatibility issue 2024-12-31 19:36:13 +01:00
Druss99
0d04e0c72e Faction editor update (#434)
Resolves #166 

* init faction editor

* update persistency

* minor fixes

* typing smh

* small fixes

* forgot the changelog -_-
2024-12-31 00:24:12 +01:00
Raffson
f36526b5de Fix mismatch between terrain names
Pydcs & Retribution don't always match up...
2024-12-28 20:46:41 +01:00
Starfire13
56e0c901c5 Operation Desert Aladeen (#433) 2024-12-28 17:56:58 +01:00
Raffson
592b1a7833 Don't allow carrier's path over land in UI 2024-12-28 01:06:08 +01:00
Raffson
cf38113cde Only remove landmap before saving and immediately restore 2024-12-28 00:32:32 +01:00
Raffson
10e7ee7902 Add first Iraq campaign by Starfire
Co-Authored-By: Starfire13 <72491792+Starfire13@users.noreply.github.com>
2024-12-27 23:32:22 +01:00
Raffson
f2eec530db Load landmap data on the fly
Avoids saving unnecessary data to the save-file
2024-12-27 23:32:21 +01:00
Raffson
9c50c062e9 Support for Iraq 2024-12-27 23:30:57 +01:00
Raffson
a173fa7e5b Update Kola beacons 2024-12-26 17:27:09 +01:00
Raffson
63785174db pydcs update wrt Iraq temperatures 2024-12-26 16:45:58 +01:00
Raffson
ffe0ede285 Fix error while loading/setting campaign settings 2024-12-26 16:23:04 +01:00
Raffson
d1b484f560 Support for auto-fog
implemented as a setting...
2024-12-26 02:51:58 +01:00
Raffson
3ef6ae20c7 Support for Bandit's cloud presets mod (v15) 2024-12-26 01:35:30 +01:00
Raffson
f3b1964e06 pydcs fix 2024-12-26 00:21:56 +01:00
Raffson
a9e784c0e3 Regroup settings per page in code 2024-12-25 23:40:06 +01:00
Raffson
3012fb8661 More pydcs updates 2024-12-25 23:03:38 +01:00
Raffson
1c5445c254 Add types in method's signature + formatting 2024-12-25 00:25:34 +01:00
Raffson
db3670b759 Fix NGW bug wrt missing plugin-options in campaign settings 2024-12-24 18:14:24 +01:00
Raffson
ea09b6be32 Have BARCAP/TARCAP engage anti-ship/cruise missiles 2024-12-24 06:43:52 +01:00
Raffson
881228d273 Potentially useful method for Doctrine class... 2024-12-24 06:35:10 +01:00
Raffson
c75bf23701 Small cleanup 2024-12-24 06:35:10 +01:00
Raffson
020281bf80 Fine-tuning recovery tankers 2024-12-24 06:35:09 +01:00
Raffson
ef77637c9a Refactor patrol_duration for refueling flight-plans 2024-12-24 06:34:46 +01:00
Raffson
15e6f81bb5 Reference primary flight's RTB time for Escort's split wpt 2024-12-24 06:34:46 +01:00
Raffson
8b03bc496d Move tanker patrol duration setting to doctrine page 2024-12-24 06:34:46 +01:00
Raffson
add81472c6 Schedule 2 simultaneous BARCAPs over carriers 2024-12-24 06:34:45 +01:00
Raffson
0e1f2e4185 Configurable BARCAP patrol duration in settings 2024-12-24 06:34:13 +01:00
Raffson
1a628888f7 AEWC optimizations
- setting for on-station time
- always include CVN carriers as targets
- autoplanner adjustment for continuous coverage
2024-12-24 06:34:12 +01:00
Raffson
2c2573240a Lower unusually high anti-ship task-priority for S-3B 2024-12-24 06:15:59 +01:00
Raffson
75c29dd907 Always plan & plan more BARCAP for carriers 2024-12-24 06:15:59 +01:00
Raffson
f1dc431d2f Fix scheduling issue for recovery tankers 2024-12-24 06:15:59 +01:00
Raffson
db9cf441fb Avoid autoplanner planning AirAssault against carriers 2024-12-24 06:15:58 +01:00
Raffson
a06e9d0ec7 Plan more ANTI-SHIP against ship-groups with air-defences 2024-12-24 06:15:58 +01:00
Druss99
dd7e4c908e Recovery tanker support (#429)
* fix conflict

* squash bugs and reuse patrol layout

* fix tanker tacan and formatting

* fix unlimited fuel option

* update pretense for tanker changes

* reuse refueling flight plan and bugfix for sunken carrier

changelog

* remove unitmap dependency

* formatting and more unit map removal

* more formatting

* typing and black

* keep tanker out of clouds

* fix if there are no clouds

* better cloud handling

* groundwork for recovery task

* remove changes to game/commander

* Finishing up recovery tankers

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-12-23 04:39:10 +00:00
Raffson
a4671571bc Fix stupid mistake w.r.t. SC deck-crew sync -_- 2024-12-22 03:19:13 +01:00
Raffson
5470cb416c Sync SC deck-crew option to miz' options 2024-12-21 19:07:05 +01:00
Druss99
6d0dbd4d1c Bugfix if settings directory doesn't exist (#432)
* bugfix if settings directory doesnt exist

* Create directories in persistency

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-12-21 16:44:31 +00:00
Druss99
266e69a6ce Default settings support (#428) 2024-12-18 13:47:05 +01:00
Raffson
8f384b35dc Fix bug in waypoint tab
Seems like I gave some inaccurate feedback -_-
2024-12-18 00:47:56 +01:00
Raffson
e02698d8a8 Autoplan Air-to-Air Escorts for AWACS & Tankers 2024-12-18 00:47:56 +01:00
Raffson
d2fa027cdd Fix outdated settings in campaign definitions 2024-12-17 14:44:28 +01:00
Starfire13
4c7aa8ddde Red Sea Rising update (#431) 2024-12-17 14:25:40 +01:00
Druss99
9d6f1552c8 Loadout selector bugfix (#430)
fix loadout selector being disabled if awacs or refueling is selected and then changed to something else
2024-12-17 14:23:30 +01:00
Raffson
06d3805946 Enable Supercarrier's LSO & Airboss stations 2024-12-16 19:59:59 +01:00
Raffson
caf624832f Small refactor 2024-12-16 19:59:49 +01:00
Raffson
cc90627ee5 Support for supercarrier deck-crew 2024-12-16 19:59:49 +01:00
Eclipse/Druss99
aae4c905b9 Better handling of flights with no loadouts ex. Tankers 2024-12-16 03:11:21 +01:00
Raffson
64d60e5ccf Datalink support + pydcs update
Support for 2.9.10.4160, though without Iraq, but finally there's some basic support for datalink...
2024-12-16 03:08:40 +01:00
Druss99
0eb06f9af5 Add TOT generation for all waypoints (#425) 2024-12-15 00:51:49 +01:00
Raffson
80b3af18df Follow-up on loadout-selector in create-flight dialog 2024-12-14 23:54:02 +01:00
Druss99
3d778a1ce0 Add ability to select loadout on flight creation (#426)
* Add ability to select loadout on flight creation

* Add pylon info on hover and add default loadout autoselect

* move loadout selector to method

* Cleanup loadout delegate
2024-12-14 23:38:41 +01:00
Druss99
09d7a4babf Bugfix for UnitPaloads directory not found (#424)
* Bugfix for mission editor directory not found

* cleanup code
2024-12-09 21:45:51 +00:00
Raffson
94a14f40cb Fix bug w.r.t. enable/disable JTAC for OWNFOR 2024-12-08 17:32:25 +01:00
Starfire13
6d9478e0d4 Fix Harrier loadout conflict (#423)
GBU & Sidearm were causing conflicts
2024-12-08 16:51:33 +01:00
Starfire13
1c36d9ec75 Fix F-4E loadout w.r.t. GBU-8 (#422) 2024-12-07 21:43:06 +01:00
Raffson
772a15bab5 Enable/Disable JTAC for OWNFOR faction 2024-12-01 00:36:09 +01:00
Raffson
98f0c93012 Avoid recreating flight-plan when syncing package wpts 2024-11-24 17:44:01 +01:00
Starfire13
5e2f86de96 Add JTAC to Blufor Late Cold War (#418) 2024-11-21 21:26:02 +01:00
Raffson
94baaecf8c Fix TOT not changing after abort 2024-11-16 19:31:36 +01:00
Dan Albert
057f3fe53b Don't allow changing TOT for started packages. 2024-11-16 19:28:23 +01:00
Dan Albert
ce4c73098d Add locking to some UI actions.
This is by no means complete. The bugs that this solves were already in
6.x, but we'd hidden the speed controls for the sim in that release, and
have always said that anything done after pressing "go" the first time
is undefined behavior. This is the first step on making those mid-sim
actions behave correctly.

UI actions such as creating a new package need to be executed between
ticks of the sim. We can either do this synchronously by blocking the UI
until the tick is done executing, acquiring a lock on the sim, executing
the action, then releasing the lock; or asynchronously by queueing
events and letting the sim execute them when it completes the current
tick (or instantly if the sim is paused).

Anything that comes from the new UI (currently just the map) must be
asynchronous because it goes through the REST API, but for the old UI
it's simpler (and because the lock will only be acquired as quickly as
the user can act, shouldn't slow anything down) to do this
synchronously, since it's difficult to use coroutines in Qt.

https://github.com/dcs-liberation/dcs_liberation/issues/1680
2024-11-16 19:09:42 +01:00
Starfire13
eb06a9a9e8 Update F-4E loadouts (#416) 2024-11-16 13:07:22 +01:00
Starfire13
a6b1805290 Update for Able Archer 83 (#417) 2024-11-16 13:03:47 +01:00
Raffson
08e55d9e43 Avoid autoplanning ArmedRecon against naval CPs 2024-11-15 18:53:43 +01:00
Starfire13
27c8f01853 Update Operation Peace Spring with new scenery targets (#415) 2024-11-14 18:59:18 +01:00
Raffson
84c4b992e0 Improve AGL to AMSL transition for naval waypoints
Some maps have inaccurate bounds for sea-zones, thus waypoints that end up being too close to the shore may keep using AGL which could still lead to trouble. Adding a condition to check whether a point is "not on land" will likely fix this issue for maps that have gaps between sea-zones and inclusion-zones around the shoreline...
2024-11-09 14:49:38 +01:00
Raffson
b5225f03a1 Fix #414
Since there's little difference between the two generators for `generate_plugin_data`, we'll have the pretense lua-generator call its super...
2024-11-08 21:02:08 +01:00
Chris Ella
add26ab18e fix: wrong runway repair message for player/enemy
fix #412
2024-11-05 12:41:04 +01:00
Raffson
aef931fc3a Update pydcs for fix w.r.t. AFAK exception 2024-11-03 14:25:48 +01:00
Cedric Menard
2091fdbb27 Add Mbot's Call Artillery Script plugin (#410)
* Add Mbot's Call Artillery Script plugin

* Applied PR comments

* Fix for wrong indentation, remove unused client skill check, added changelog item
2024-11-03 13:02:36 +00:00
Raffson
e959933861 Save-compat fix for Syria 2024-11-01 03:47:01 +01:00
Raffson
eaa73c84ae Set preferred red-alert state for frontline units 2024-10-31 14:01:48 +01:00
Raffson
3147ae6e5c Save-compat fix for South Atlantic 2024-10-31 00:55:58 +01:00
Raffson
7b9bdf84dc Update beacons 2024-10-31 00:45:48 +01:00
Raffson
9e370e1d8d Update pydcs 2024-10-31 00:44:16 +01:00
Raffson
e85d1e8ca2 AGL to AMSL option for naval waypoints
Introduce an option to switch waypoints to AMSL when waypoint is above water. DCS references the bottom of the sea when using AGL, which causes issues for helicopters when they try to fly at altitudes that are lower that the sea bottom, giving them a tendency to fly into the water...
2024-10-30 01:11:53 +01:00
Starfire13
696e2dbbe8 Fix very outdated links (#409) 2024-10-29 22:14:42 +01:00
Raffson
6a5097659b Sync package wpts when primary flight's wpts change
Also recreates all other flight-plans in the package to ensure JOIN, INGRESS & SPLIT are updated
2024-10-28 23:47:26 +01:00
Raffson
b124c6cc61 Fix nav_to for AirAssault
Depending on whether it's a Hercules with paratroopers or a helicopter, ingress' position is going to be different
2024-10-28 20:23:22 +01:00
Raffson
4de7b1c923 Force utf8 encoding in TXT-briefing
Fix #408
2024-10-28 12:50:01 +01:00
Raffson
4711473b64 Fix one-off in FlightWaypointList's indexes
Fixes #304
2024-10-12 18:33:10 +02:00
Raffson
6ae32b13b8 Format missed files with black 2024-10-12 17:50:54 +02:00
Raffson
5c56e7f64e Update npm packages 2024-10-12 17:33:01 +02:00
Raffson
5fdf38c663 Format with updated black 2024-10-12 17:33:01 +02:00
Raffson
11a9d67b91 Update black 2024-10-12 17:33:01 +02:00
dependabot[bot]
216d9bc048 Bump webpack from 5.91.0 to 5.95.0 in /client (#407)
Bumps [webpack](https://github.com/webpack/webpack) from 5.91.0 to 5.95.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Commits](https://github.com/webpack/webpack/compare/v5.91.0...v5.95.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-12 15:25:14 +00:00
dependabot[bot]
3bb6c43aca Bump micromatch from 4.0.5 to 4.0.8 in /client (#404)
Bumps [micromatch](https://github.com/micromatch/micromatch) from 4.0.5 to 4.0.8.
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

---
updated-dependencies:
- dependency-name: micromatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-12 17:23:29 +02:00
dependabot[bot]
af9416e225 Bump body-parser and express in /client (#403)
Bumps [body-parser](https://github.com/expressjs/body-parser) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `body-parser` from 1.20.2 to 1.20.3
- [Release notes](https://github.com/expressjs/body-parser/releases)
- [Changelog](https://github.com/expressjs/body-parser/blob/master/HISTORY.md)
- [Commits](https://github.com/expressjs/body-parser/compare/1.20.2...1.20.3)

Updates `express` from 4.19.2 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.2...4.21.1)

---
updated-dependencies:
- dependency-name: body-parser
  dependency-type: indirect
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-12 15:19:19 +00:00
dependabot[bot]
73feccde9d Bump rollup from 2.79.1 to 2.79.2 in /client (#402)
Bumps [rollup](https://github.com/rollup/rollup) from 2.79.1 to 2.79.2.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.79.1...v2.79.2)

---
updated-dependencies:
- dependency-name: rollup
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-12 15:18:52 +00:00
Raffson
ff2ec07d83 Move constants from laypout.py to theathergroup.py
Should fix the "FIXED_POS_ARG not defined" error
2024-10-12 16:35:04 +02:00
Raffson
4aacc68f4a Include layouts & groups from Saved Games folder
Partial #244
2024-10-06 22:33:01 +02:00
Raffson
69c6f8f2f2 Update beacons 2024-10-06 22:27:51 +02:00
Raffson
4c3b16e1da Support for fixed position & heading in layouts 2024-10-06 22:27:51 +02:00
Raffson
2b780e3d69 Adjustable OPFOR headings when OPFOR ATO is selected 2024-10-06 22:27:50 +02:00
Raffson
a68bcb9ea1 Add class to Patriot AMG 2024-10-06 22:27:50 +02:00
Raffson
0770fafbcb Save-compat fix for Syria airport rename
Amman is now Marka...
2024-10-06 12:41:51 +02:00
M Chimiste
169ef26329 Set Pretense Player Flight Waypoints to CPs (#385) 2024-10-06 01:12:02 +02:00
Raffson
168ba7bd1e Pydcs update 2024-10-06 00:18:08 +02:00
Raffson
59f98a4c16 Make combat landing configurable 2024-10-05 23:04:06 +02:00
Raffson
9bd6c6c404 Overload takeoff_time for PatrollingFlightPlan
Fixes bug where TARCAP get screwed TOTs due to a SEAD Sweep flight in a CAS package
2024-10-05 22:40:07 +02:00
Starfire13
96e8208e3c Fix typo + improve arty spotter script description (#400) 2024-09-22 23:35:48 +02:00
Raffson
44658f8eec Skip EscortType for SEAD Sweep planned during CAS
Seems like the EscortType would cause the flight to be pruned if no IADS threats surround the CAS zone, but we'd like to plan it regardless to tackle air defenses along the front-line
2024-09-22 22:47:26 +02:00
Raffson
dae57abeff Fix 'getName' error in base script
Turns out weapons also generate a dead event, but presumably some of them don't have a 'getName' method...
2024-09-22 22:38:14 +02:00
Raffson
87ca233f20 Only pass air-defenses to IpSolver when creating package waypoints
Aims to solve an issue where waypoints would completely disregard air defenses and overfly them due to interference from airbase threat ranges...
2024-09-21 22:57:08 +02:00
Raffson
126d70792f Another attempt to improve DEAD autoplanner 2024-09-21 22:57:07 +02:00
Raffson
9d2345ea3a Fix weird spacing in Campaign Management settings 2024-09-21 20:12:41 +02:00
Raffson
7a84181bbc Add UI-zone for SEAD Sweep 2024-09-21 19:30:21 +02:00
Raffson
a7d091c142 Fine-tune attempt wrt DEAD autoplanning
A small note, this also affects the aggressiveness wrt SHORAD & AAA
2024-09-15 22:08:58 +02:00
Raffson
be554c54b3 Plan IADS degradation as last
The previous steps identify IADS locations, so moving this "up the list" has detrimental effects...
2024-09-15 22:05:43 +02:00
Raffson
49211dae4f Autoplanner support for ArmedRecon 2024-09-15 22:05:42 +02:00
Raffson
73d2f8fda1 Change ArmedRecon's task-priority when determining primary task 2024-09-15 22:05:42 +02:00
Raffson
2cba1f4124 Account for aircaft's task-priorities in AirWing.best_squadrons_for 2024-09-15 22:05:42 +02:00
Raffson
f5bb653ad8 Rework (OPFOR) autoplanner aggressiveness setting
Also introduces a setting for OWNFOR
2024-09-15 22:05:42 +02:00
Raffson
d35962b784 AirAssault autoplanner support for non-frontline CPs
Aims to solve the issue where AirAssault won't plan against CPs that don't have an active frontline, including isolated CPs.
2024-09-15 22:05:41 +02:00
Raffson
f63939ce3c Include isolated points in objectivefinder 2024-09-15 22:05:41 +02:00
Raffson
8f8ee8c48e Update upload-artifact to v3 2024-09-15 02:38:16 +02:00
Raffson
7e8cbe0de2 Fix incorrect strike target location for TheaterUnit 2024-09-14 16:37:10 +02:00
Starfire13
8a47dce4b5 Update max frontline units description (#399) 2024-09-09 21:10:02 +02:00
Raffson
242d1a14a7 Limit/Suppress AI radio callouts for player flights 2024-09-07 21:04:49 +02:00
Starfire13
1f21ed0c1d Add new Sinai campaign - Red Sea Rising (#397) 2024-08-31 19:56:24 +02:00
Raffson
d44218ba55 Invalidate squadron livery if not found or incompatible 2024-08-25 15:53:35 +02:00
Raffson
aa26a2fa79 Avoid restoring old AircraftType data
This solves the bug where the default_livery would get overwritten with whatever value was already present from data that's loaded from the last campaign, which was usually set to None...
Possibly this also solves the bug where Armed Recon is suddenly not available anymore, because most likely the tasking was being overwritten as well...
2024-08-25 15:20:16 +02:00
Starfire13
ced69c98fc Change country to CJTF-Red for Redfor factions (#396)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-08-25 10:59:53 +00:00
Starfire13
4082cad3cf Add livery for 18th ARS (KC-135 squadron) (#395)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-08-25 10:58:55 +00:00
Raffson
fd42b03d78 Log error if beacon data can't be found
This would prevent mission generation otherwise...
2024-08-24 20:22:14 +02:00
Raffson
98360c8388 Remove obsolete beacons file 2024-08-24 18:28:44 +02:00
Starfire13
daee4d96d7 Add default liveries to some aircraft with no preset squadrons (#393)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-08-24 16:28:32 +00:00
Starfire13
3e488a738e Update Able Archer 83 and Exercise Bright Star (#394) 2024-08-24 18:25:49 +02:00
Starfire13
26bd7629f0 Disable Hercules autoplanner tasks that AI can't perform (#392)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-08-24 16:23:30 +00:00
Raffson
9b857d7b0e Cope with mismatch in folder-/file-name for Sinai wrt beacons 2024-08-23 19:09:29 +02:00
Raffson
1388985048 pydcs beacon fix 2024-08-18 22:11:19 +02:00
Raffson
ac6c8c3e14 Add more bombers & AEW&Cs to Redfor factions
Co-Authored-By: Starfire13 <72491792+Starfire13@users.noreply.github.com>
2024-08-18 21:51:40 +02:00
Raffson
171f0722dc Fix bug in default squadron assigner/loader
The root cause of the issue are lines like "if aircraft in faction.all_aircrafts" that weren't working as intended, i.e. the condition would always fail. By implementing the __eq__ method we fix this issue, effectively making the default squadron assigner make use of squadrons instead of simply skipping them.
2024-08-18 18:19:58 +02:00
Starfire13
3a1d90ce6a Add MiG-21 to Blufor Modern (#391) 2024-08-17 11:49:10 +02:00
Starfire13
818803254b Add CH-47F to campaigns and fix static armour group spawn points (#389) 2024-08-17 07:15:25 +02:00
dependabot[bot]
a9a4a1fed4 Bump axios from 1.6.8 to 1.7.4 in /client (#388)
Bumps [axios](https://github.com/axios/axios) from 1.6.8 to 1.7.4.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.6.8...v1.7.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-16 11:04:16 +00:00
Raffson
ea9c459942 Disable Nevatim/Ramon airbase hack for inoperable slots 2024-08-16 12:57:25 +02:00
Raffson
16162f2698 Fix whitespace in factions 2024-08-16 12:56:41 +02:00
Raffson
a8c9a81ccb Update factions with CH-47F
Co-Authored-By: Starfire13 <72491792+Starfire13@users.noreply.github.com>
2024-08-16 12:56:41 +02:00
Raffson
9cf52ab11d Add Starfire's banner & payloads for the CH-47F
Co-Authored-By: Starfire13 <72491792+Starfire13@users.noreply.github.com>
2024-08-16 02:41:22 +02:00
Raffson
86a93efc71 Support for CH-47F except banner & payloads 2024-08-16 02:41:21 +02:00
Raffson
c262bb3681 Reload terrain and airports in migrator 2024-08-16 00:34:41 +02:00
Raffson
86e4f45152 Update beacons 2024-08-15 23:49:00 +02:00
Raffson
bc3ec4ffb9 Save-compat fix for Bas_100 being renamed to Vuojarvi 2024-08-15 23:48:29 +02:00
Raffson
5a25b97a9e Update pydcs for 2.9.7.59074 2024-08-15 23:48:29 +02:00
tmz
9e2c5c06b9 Deleted __str__ from StartType to limit impact of change. 2024-08-15 22:27:14 +02:00
tmz
5b4ed979b1 Modified Planned Flights in airfield command to show startup type 2024-08-05 12:41:43 +02:00
Raffson
d29f5a26a1 Revert unnecessary change causing exceptions 2024-08-04 22:44:28 +02:00
Raffson
97c65cfefa Fix date lagging behind 1 turn 2024-08-04 13:14:40 +02:00
Raffson
9dd25e933e RoundRobinize random livery selection from livery_set 2024-08-04 12:28:05 +02:00
Raffson
8796d629e8 Fix probable bug in procurement 2024-08-04 02:37:43 +02:00
Raffson
9dba85e95b Update map upon abort 2024-08-04 01:31:51 +02:00
Raffson
583c87b2e3 Emit game_loaded after restore pre-pretense backup 2024-08-04 01:19:07 +02:00
Raffson
4405764006 Generate warehouses for pretense missions 2024-08-03 22:22:21 +02:00
Raffson
ba25f6a8df Fix missing VSN F4 liveries 2024-08-03 21:28:25 +02:00
Raffson
d2547def51 Fix bug introduced in improvements to livery-selector
This will fix the bug I missed where the squadron needs to be using the livery-set before it could take effect for flight-members.
2024-08-03 20:47:34 +02:00
Raffson
532515fad7 Better save-compat w.r.t. changes in AC's yaml 2024-08-03 20:46:06 +02:00
Raffson
f79e96a474 Further improvements to livery-selector
Unlocking the full potential, i.e. allowing users to switch back & forth between livery-sets, livery-overrides and normal liveries, including support for overriding flight/flight-member specific liveries.
2024-08-03 19:42:45 +02:00
tmz
5aca992140 The Long Road to H3 : added Strike Targets in Damascus, Damascus airfield, modified OWNFOR configuration. 2024-08-03 19:42:45 +02:00
tmz
b1efaf6448 Squadron Livery Selector : Allow to show all liveries even if squadron is using livery set in the selector when in the Flight Payload Tab. 2024-08-03 19:42:45 +02:00
Raffson
ecb81ec32d Merge pull request #204 from MetalStormGhost/pretense-generator
Pretense generator + additional options
2024-07-28 19:43:08 +02:00
Raffson
242ef30469 Formatting -_- 2024-07-28 19:09:59 +02:00
Raffson
3d1a5bea67 Adjust pre-pretense backup strategy 2024-07-28 19:05:37 +02:00
Raffson
dcc424d7e1 Update gitignore 2024-07-28 18:27:27 +02:00
Raffson
f12a1350ce Fix exceptions thrown for Armed Recon 2024-07-28 18:06:55 +02:00
Raffson
d2c45c97fe Refactor invisible FARP remover 2024-07-28 17:55:51 +02:00
Raffson
3bdda3b852 Readd removed newline 2024-07-28 17:14:08 +02:00
Raffson
d604905f52 Streamlining after merge 2024-07-28 17:13:26 +02:00
Raffson
5bb70626d2 Revert unnecessary change 2024-07-28 17:08:01 +02:00
Raffson
8eb30445c2 Restore hidden_on_mfd functionality 2024-07-28 16:59:21 +02:00
Raffson
1b757ec544 Fixing warehouse stuff 2024-07-28 16:53:09 +02:00
Raffson
65e529aa55 Merge branch 'dev' into pr/204 2024-07-28 15:57:38 +02:00
tmz42
e0d4a031bb Correct details for option in settings.py (#380)
Corrected details for player startup time setting
2024-07-28 14:53:54 +02:00
Raffson
ef0e6bb4f5 Account for default livery in QSuadronLiverySelector 2024-07-28 11:21:33 +02:00
ColonelAkirNakesh
8011d711af Update bluefor_modern.json (#379) 2024-07-27 23:12:44 +02:00
Raffson
3b360cfcf5 Pull up AEWC & Refueling mission types to NavalControlPoint 2024-07-27 22:58:43 +02:00
Raffson
e5b06e6ba4 Fix liveries overrides preventing assignment of preset squadrons 2024-07-27 22:52:08 +02:00
Raffson
8bc902fe64 Fix too many ground spawns being popped 2024-07-27 20:52:13 +02:00
Raffson
0ecbd81a20 Revert yet another bogus change 2024-07-27 20:16:00 +02:00
tmz42
dd6267c5ed Simplified Scenic Merge for performance, added Strike targets (#374) 2024-07-27 18:31:43 +02:00
tmz42
ecf11363f2 Player sixpack setting (#378) 2024-07-27 18:30:40 +02:00
tmz42
9e9ea09f70 Player Startup Time customization (#377) 2024-07-27 18:08:40 +02:00
ColonelAkirNakesh
cc6fbc0379 Adds description for MaxxPro_MRAP.yaml (#376) 2024-07-27 18:03:50 +02:00
ColonelAkirNakesh
5ad4dc51fa Fixes description for M1A2C_SEP_V3.yaml (#375) 2024-07-27 18:02:57 +02:00
MetalStormGhost
140410f1ed OH-6 Cayuse mod v1.2 support (#346)
Resolves #313

* Initial commit of OH-6 Modpack v1.2 + Vietnam Asset Pack v1 support.

* Added OH-6 Mod loadouts by @Starfire13

* Modified   pydcs_extensions/oh6_vietnamassetpack/oh6_vietnamassetpack.py
Added      resources/units/ground_units/vap_m35_truck.yaml
Added      resources/units/ground_units/vap_mule.yaml
Added      resources/units/ground_units/vap_mutt.yaml
Added      resources/units/ground_units/vap_mutt_gun.yaml
Added      resources/units/ground_units/vap_type63_mlrs.yaml
Added      resources/units/ground_units/vap_vc_bicycle_ak.yaml
Added      resources/units/ground_units/vap_vc_zis.yaml

* Modified   changelog.md
Modified   resources/factions/USA 1970 Vietnam War.json
Modified   resources/factions/USA 1971 Vietnam War.json
Modified   resources/units/ground_units/vap_mule.yaml
Added      resources/units/aircraft/OH-6A.yaml

* Added the OH-6 to factions and implemented the mod selection in the new game wizard. Added an icon an a banner.

* Modified   resources/units/aircraft/OH-6A.yaml

* Added icons for the Vietnam Asset Pack ground units. Also added an icon for the PT-76 since it was missing. Added a Viet Cong 1970s faction.

* Added      resources/units/ships/vap_us_seafloat.yaml

* Adjust tasking for OH-6A

OH-6A is only capable of Transport & Reconnaissance, but we can ignore this if the lead slot is a client. AI however will most likely not support this, but we can add Transport & Air Assault instead...

* Fix bug in configure task + client override fallback

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-07-22 20:53:14 +02:00
Raffson
b2ae10ce6f Remove fuel restriction for FOB/FARPs
Probably why we end up with 3/4 of a tank on roadbases -_-
2024-07-21 22:48:26 +02:00
Raffson
a658e41c54 Use EngageTargetsInZone for SEAD Sweep instead
Seems to perform better, AI wonders less off to places it shouldn't...
2024-07-21 22:42:33 +02:00
Raffson
2b65d2f800 Always use altitude in GroundSpeed.for_flight 2024-07-21 22:42:33 +02:00
Raffson
7ea1569f22 Switch AGM-114K's to L's for Apache Escorts
AI doesn't seem to do well with the K variants when flying Escort, not sure about CAS though...
2024-07-21 22:42:32 +02:00
Raffson
8d11feae4a Add AirAssault to PAC & Armed Recon as support in PAC 2024-07-21 22:42:32 +02:00
Raffson
1c9b4b2317 Remove STRIKE as mission type from FOBs 2024-07-21 22:42:32 +02:00
Raffson
9cca4e8244 Revert helicopter escort aggressiveness hack
AI got a little better, at least that's what tests in AFG showed. The main issue is that helicopters tended to attack static objects, which we obviously don't want...
2024-07-21 22:42:31 +02:00
Raffson
e280763a39 Fix wrong parameter for OrbitAction
pydcs expects kph -_-
2024-07-21 22:42:31 +02:00
Raffson
b7ad1048bd Perturb join waypoint for helicopters 2024-07-21 22:42:30 +02:00
Raffson
98b49d5fca Revert removed speed_between_waypoints overload 2024-07-21 22:42:30 +02:00
Raffson
4423391287 Adjust preferred patrol speeds 2024-07-21 21:38:23 +02:00
Raffson
ad87182cf9 Reflect changes in aircraft's yaml through migrator 2024-07-21 21:38:23 +02:00
Raffson
510e23ebba Define cruise/combat altitudes for A-10 2024-07-21 21:38:22 +02:00
tmz42
5bb5294826 Minor campaign updates (#373)
* Simplified Retake the Falklands
Optimized The Long Road to H3 for performance

* Small alterations to The Long Road to H3 - changed options and reverted some units
2024-07-21 12:33:28 +02:00
MetalStormGhost
3de59064de Ukrainian, Russian and US assets in Frenchpack
Added unit yamls for M551 Sheridan, T-62 and T-64BV from Frenchpack mod.
2024-07-21 12:32:21 +02:00
Raffson
da64cdbab7 Remove ROE from SEAD Sweep to avoid engaging statics 2024-07-21 12:06:03 +02:00
Raffson
e9d0273056 Attempt at fixing too slow helicopter speeds 2024-07-21 02:22:14 +02:00
Raffson
f405ffdfe2 Initial support for Armed Recon flight plan 2024-07-21 02:22:14 +02:00
MetalStormGhost
8dcf25d9ee Updated MIST to 4.5.126 2024-07-20 19:21:56 +03:00
MetalStormGhost
e88a4e1c51 Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-07-20 19:21:06 +03:00
Raffson
c24fba0ba4 Fix OCA-Aircraft bug in mission generator 2024-07-20 15:35:23 +02:00
Raffson
44daa002ab Fix bug in pydcs 2024-07-20 14:17:55 +02:00
Raffson
7d898f8b48 Account for free heli slots & ground spawns for airfields 2024-07-20 02:53:12 +02:00
Raffson
6194ec7301 Move import outside of TYPE_CHECKING 2024-07-20 02:53:05 +02:00
Raffson
6702a91802 Show airfields in AWCD regardless of runway state 2024-07-20 02:53:04 +02:00
Raffson
dc2d541b50 Formatting -_- 2024-07-20 01:26:20 +02:00
Raffson
141fc642d5 Fix Anti-Ship being planned against original location 2024-07-20 01:26:10 +02:00
Raffson
0d56c271df Serialize warehouses & support for dynamic slots/cargo 2024-07-20 01:04:24 +02:00
Raffson
75dd67a145 Update pydcs
- coldAttStart support
- support for dynamic slots/cargo
2024-07-20 00:17:16 +02:00
Raffson
eb841f1bf4 Update MiG-21bis radios 2024-07-19 16:32:28 +02:00
Raffson
9b54414525 Update MIST to 4.5.126 2024-07-18 18:56:59 +02:00
Raffson
319b2f768a Forgot to remove copy during renaming 2024-07-18 13:05:16 +02:00
ColonelAkirNakesh
1752465c9b Add MRAP & new Abrams to USA 2005 (#367)
* Update usa_2005.json

Adjust spacing in some flyable aircraft (tabs vs spaces),
Adds MRAP and new Abrams model
Removes Hornet livery enforcement

* Update usa_2005.json

Adds MRAP, new Abrams, restores liveries

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-07-18 12:37:56 +02:00
Starfire13
b0d10f07de Add new Afghanistan campaign - Graveyard of Empires (#370) 2024-07-18 12:20:58 +02:00
Starfire13
ed7a358699 Add new Afghanistan campaign - Operation Shattered Dagger (#371) 2024-07-18 12:20:07 +02:00
MetalStormGhost
4c05837b61 Changed the naming of afghanistan_full.yaml campaign based on feedback from the Discord server. 2024-07-17 19:51:03 +03:00
MetalStormGhost
16239d4089 Added Afghanistan - Pretense Full campaign by @ColonelAkirNakesh 2024-07-17 19:48:51 +03:00
MetalStormGhost
69b828ddf4 Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-07-17 19:40:57 +03:00
MetalStormGhost
93da86eed3 Escaped the colon character in the description of the persian_gulf_full.yaml and added default settings to it. Changed the naming of the campaigns based on feedback from the Discord server. 2024-07-17 19:40:29 +03:00
ColonelAkirNakesh
655ed23d79 Add files via upload
Full campaigns for Marianas, Nevada, and Persian Gulf.
2024-07-17 18:45:00 +03:00
ColonelAkirNakesh
209f2c7b67 Update bluefor_modern.json (#368) 2024-07-16 22:31:42 +02:00
ColonelAkirNakesh
9b48acfe28 Create M1A2C_SEP_V3.yaml (#365) 2024-07-16 20:29:00 +00:00
ColonelAkirNakesh
ecc7b603b9 Create MaxxPro_MRAP.yaml (#366) 2024-07-16 20:28:45 +00:00
Starfire13
0f0dc97376 Loadouts for new FC2024 aircraft (#364)
Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-07-16 20:28:17 +00:00
Raffson
b2c66c10dc Add icon for AFG 2024-07-16 22:10:06 +02:00
Raffson
f9e7772e09 Fine-tune TGO-purchasability logic 2024-07-16 21:46:22 +02:00
Raffson
2c270306a1 Pydcs fix for AFG 2024-07-16 20:59:39 +02:00
Raffson
08ecbb14f1 Add Starfire's Second Afghan War
Co-Authored-By: Starfire13 <72491792+Starfire13@users.noreply.github.com>
2024-07-16 20:55:04 +02:00
Raffson
d9788148c2 Formatting 2024-07-16 20:51:52 +02:00
Raffson
8918d4e64f Preliminary support for Afghanistan 2024-07-16 00:19:58 +02:00
Raffson
4b51c23c43 Add QGIS shapefiles for AFG 2024-07-16 00:19:58 +02:00
Raffson
6cfe948ef2 Add new FC aircraft 2024-07-16 00:19:57 +02:00
Raffson
068165c588 Add radio for F-5E-3 2024-07-16 00:19:57 +02:00
Raffson
b09d095723 Add radio for F-86 2024-07-16 00:19:57 +02:00
MetalStormGhost
19f6bafff1 Cold War assets mod v1.0 support (#343)
Implemented support for the Cold War assets mod (ex Tu-16) v1.0 by tripod3
2024-07-14 16:41:19 +03:00
Raffson
6238830a20 Add beacons for Afghanistan 2024-07-14 02:03:58 +02:00
Raffson
eb6701ab89 Update beacons 2024-07-14 02:03:49 +02:00
Raffson
ff311bd6fe Adjust weapon references for pydcs extensions 2024-07-14 02:03:06 +02:00
Raffson
597da1c84d Update pydcs
- Export for 2.9.6.57650
- Adds pydcs support for Afghanistan
2024-07-14 01:15:19 +02:00
Raffson
206f0c5196 Remove redundant debugMsg for missing weapon 2024-07-14 01:02:14 +02:00
Raffson
739405d716 Add work-around for SplashDamage errors 2024-07-14 01:01:44 +02:00
Chilli
3626cdc086 Updated CJS Super hornet mod support from version 2.2.5 to version 2.3.2 (#312)
* updated pydcs file to mod version 2.2.5

* Created Australia 2009

Australia has both the two seater version of the super hornet and the growler in it's arsenal, I've set the year to 2009 because the growler went into service in 2009.

* Created usn_2009 and edited usn_2005

As previously mentioned, the growler did not enter service until 2009. So it has been removed from usn_2005 and usn_2009 has been created.

* Update EA-18G.yaml

The EA-18G is barely/not capable of strike, A/A or anti-ship missions as it does not have the armament needed.

* Update fa18efg.py

reverted changes where WeaponsF18EFG. was replaced with Weapons.

* Update fa18efg.py

Only weapons added by the mod now referred to from the WeaponsFA18EFG class

* Update fa18efg.py

All of the weapons added by the mod now referred to from the WeaponsFA18EFG class

* Update fa18efg.py

Reverted AIM-120B being replaced by AIM-120D

* Update fa18efg.py

Updated fa18efg pydcs export to mod version 2.3.2

* Update QGeneratorSettings.py

Changed mod version in QGeneratorSettings.py from 2.2.5 to 2.3.2

* Update fa18efg.py

Changed AIM_120D back to AIM_120B

* Update fa18efg.py

Changed "Rdr" to "Radar" under AIM_120B

* Update fa18efg.py

* Update fa18efg.py

* Update usn_2009.json
2024-07-14 00:59:52 +02:00
MetalStormGhost
8e8bc3f907 Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-07-09 19:09:58 +03:00
Turbolious
eb5cceff16 Sk 60 mod support (#360) 2024-07-07 12:47:07 -05:00
Raffson
eb5062579f Fix lint-error 2024-07-07 18:44:45 +02:00
Raffson
9a0ddd683c Update version for Su30 in GeneratorSettings page 2024-07-07 18:43:26 +02:00
Raffson
02568eaeee Update factions w.r.t. Su-30 mod update 2024-07-07 18:38:48 +02:00
Raffson
7e6810a299 Update Su30 payloads 2024-07-07 18:38:47 +02:00
Raffson
2f33277a49 Update Su-30 mod 2024-07-07 18:38:47 +02:00
Raffson
c7644ba9e5 Fix UI bug in debriefing window 2024-07-07 15:36:14 +02:00
Raffson
d57521c03d Fix UI bug wrt selected flight-plans 2024-07-07 15:36:13 +02:00
Raffson
db12ac4f54 Rename custom payloads for Su-15
BARCAP on its own didn't actually assign the payload, it should either be CAP, CAP HEAVY, Retribution BARCAP or Liberation BARCAP
2024-07-07 15:24:08 +02:00
dependabot[bot]
3cda9fb36b Bump certifi from 2024.6.2 to 2024.7.4 (#363)
Bumps [certifi](https://github.com/certifi/python-certifi) from 2024.6.2 to 2024.7.4.
- [Commits](https://github.com/certifi/python-certifi/compare/2024.06.02...2024.07.04)

---
updated-dependencies:
- dependency-name: certifi
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-07 12:21:01 +00:00
ColonelAkirNakesh
b8b39bf2fe Add requirements to nva_1970.json (#362)
A note that this faction requires WW2 Assets Pack because of ze Schnellboot
2024-07-07 11:44:43 +02:00
Starfire13
65a62b3951 Afghanistan insurgent faction & Toyota Technicals class fix (#359)
* Add files via upload

* Add files via upload
2024-07-07 11:41:27 +02:00
MetalStormGhost
ec7982f181 Replaced OCA/Runway and OCA/Aircraft mission with Strike missions in the Pretense generator, in order to allow for Pretense campaigns where one (or both) side only has one airbase with a runway. 2024-07-05 12:51:22 +03:00
MetalStormGhost
81ad559ca1 Pretense generator now handles Severomorsk-1 and Severomorsk-3 correctly. 2024-07-05 12:51:22 +03:00
MetalStormGhost
081c981c86 The squadrons are now re-shuffled between different mission types in generate_pretense_aircraft. 2024-07-05 12:51:21 +03:00
MetalStormGhost
e704d2aa53 Large ground spawn support in PretenseMissionGenerator. 2024-07-05 12:29:19 +03:00
MetalStormGhost
59fa77e10b Large ground spawn support in PretenseAircraftGenerator. 2024-07-05 12:23:42 +03:00
MetalStormGhost
2b62f5919f Large ground spawn support in PretenseFlightGroupSpawner. 2024-07-05 12:14:47 +03:00
MetalStormGhost
0b8aa9b96e Merge branch 'pretense-generator' of https://github.com/MetalStormGhost/dcs-retribution into pretense-generator 2024-07-05 12:07:06 +03:00
MetalStormGhost
1a6d73f055 Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-07-05 12:06:53 +03:00
Raffson
9f5db20195 Fix cloud-base selector initialization bug 2024-07-02 18:42:43 +02:00
Raffson
24fc438fda Fix save-compat issue 2024-07-02 18:42:43 +02:00
Raffson
02161c02cf Fix another weather selector bug -_- 2024-07-02 18:42:43 +02:00
MetalStormGhost
07cc85f6fa Large aircraft ground spawns (#237)
* Large aircraft ground spawns

Added the ability to add separate ground spawns for C-130 and other large aircraft to campaigns. Implemented on @holyorangejuice 's request.

Large aircraft ground spawns are added to the campaign by placing a C-130 on the ramp, just like an A-10 or AJS37 previously. Note: use the stock DCS C-130, so the campaign miz can be safely opened without the C-130 mod (or any other mod) installed. Not the C-130J player-flyable transport, not the KC-130J tanker included in the UH-60L mod etc.

Large planes (wingspan more than 40 meters, such as the C-130):
- First try spawning on large ground spawns
- Then try the regular airfield ramp spawns

Below 40 meter wingspan aircraft:
- First try spawning on regular or roadbase ground spawns
- Then try the regular airfield ramp spawns
- Then, if both of the above fail, use the large ground spawns

* Specify explicit black version 23.9.1 to fix lint error.

* Update lint.yml

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-07-01 20:26:50 +00:00
Turbolious
d84a0ebc78 Support for Carsten's Arty Spotter script (#357)
* Added Cartsens Arty Script

* Added Options to Arty script

Added all options and for configuration.

* Carstens Arty Spotter with options

* Finishing touch

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-07-01 19:43:17 +00:00
Raffson
d0a1a01b12 Add last guard for weather selector, I hope... 2024-07-01 21:39:12 +02:00
Raffson
1df042fcc6 Fix possible None-exceptions in weather selector 2024-07-01 18:56:10 +02:00
Raffson
e9a14f066a Update changelog for Su-15 mod
Co-Authored-By: MetalStormGhost <89945461+MetalStormGhost@users.noreply.github.com>
2024-06-30 22:07:59 +02:00
MetalStormGhost
4762b490c0 Su-15 Flagon mod v1.0 support (#345)
* Su-15 Flagon mod v1.0 support

Implemented support for Su-15 Flagon mod version 1.0 by Marco1985_IT.

* Replaced the external fuel tanks with gunpods in Su-15 loadouts and added gunfighter: true
2024-06-30 22:05:45 +02:00
Raffson
3bda2929aa Fix issues in F106 that broke Su30 2024-06-30 21:52:39 +02:00
Raffson
4ca0137a24 Keep CAS & OCA/AC below cloud base if BKN/OVC
Partial #285
2024-06-30 19:35:33 +02:00
Raffson
824312e19d Allow changing time, date & weather
Resolves #103
2024-06-30 19:35:33 +02:00
dependabot[bot]
06960db5e5 Bump ejs from 3.1.9 to 3.1.10 in /client
Bumps [ejs](https://github.com/mde/ejs) from 3.1.9 to 3.1.10.
- [Release notes](https://github.com/mde/ejs/releases)
- [Commits](https://github.com/mde/ejs/compare/v3.1.9...v3.1.10)

---
updated-dependencies:
- dependency-name: ejs
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-30 19:35:28 +02:00
dependabot[bot]
eceec83a01 Bump braces from 3.0.2 to 3.0.3 in /client
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-06-30 19:35:20 +02:00
Turbolious
a9f968f7cc A6 Task change
Removed Escort task as its not capable
2024-06-30 19:27:35 +02:00
Turbolious
7d1e3c94f5 F-104S Task Changes
Aligned A2A and A2G tasks as each version is not capable of doing the other as Ai and will fly around without weapons while doing the opposite type task.
2024-06-30 19:24:57 +02:00
Raffson
6dc5296130 Disband squadrons sinking with ship + Sink/Resurrect cheat 2024-06-30 02:09:08 +02:00
Raffson
22f7767b99 Add AGM-62 Walleye I weapons data 2024-06-29 23:56:53 +02:00
Raffson
c27f3e67c0 Add AGM-12 weapons data 2024-06-29 23:55:10 +02:00
Raffson
9a6d912a30 Update weapons data 2024-06-29 23:36:54 +02:00
Starfire13
6e3f66437e Remove PDU from Kiowa (#347) 2024-06-25 00:15:24 +02:00
MetalStormGhost
b27141cb1e Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-06-22 09:57:18 +03:00
Raffson
af83088dfd Lock numpy to v1.26.4 2024-06-17 19:58:59 +02:00
Raffson
e6683c0237 Update most dependencies 2024-06-17 19:58:40 +02:00
Raffson
e5f7b577ca Revert "Add pybind11 to requirements.txt"
This reverts commit b45821de52.
2024-06-17 19:22:51 +02:00
Raffson
08de46a3b6 Restore missing weapon-injections
Restores functionality that was lost in 0ec375a
2024-06-17 19:19:56 +02:00
Raffson
9c820f12cf Assign SEAD-Sweep task priorities if missing
Restores behavior that got lost in 0ec375a
2024-06-17 19:19:10 +02:00
Raffson
b45821de52 Add pybind11 to requirements.txt 2024-06-17 19:04:44 +02:00
Starfire13
192fb2087f Update Peace Spring & Grabthar's Hammer (#344) 2024-06-16 21:03:09 +02:00
Raffson
c22f7cbff3 Overhaul redeployment of units after base-capture(s) 2024-06-15 23:40:16 +02:00
Raffson
38e62e7636 Fix Kiowa squadrons 2024-06-15 19:54:24 +02:00
Starfire13
78fdc1bcfc Fix for Operation Gazelle (#342) 2024-06-15 19:53:58 +02:00
MetalStormGhost
7f7821f878 Stop the execution of the pretense_extra_zone_connections in case no more connected points can be found in the campaign. Should fix an error which prevents campaign generation. 2024-06-11 19:35:45 +03:00
MetalStormGhost
cc7ff3f1c2 Added "LARC-V" as a separate variant in LARC-V.yaml 2024-06-10 18:27:27 +03:00
MetalStormGhost
00356ea3ec EA-6B Prowler mod version 2.9.4.102 support (#337) 2024-06-09 20:02:04 +02:00
MetalStormGhost
7eb2c59b86 Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-06-09 20:25:55 +03:00
Starfire13
3c0f0b28ea Updates for all of my campaigns (#341) 2024-06-09 10:57:48 +02:00
Starfire13
8a57e8e5f3 Factions update for Kiowa (#340) 2024-06-09 10:53:45 +02:00
Starfire13
e6fc0a9758 Fix Roughnecks squadron file (#339) 2024-06-09 10:53:05 +02:00
Raffson
31630b4d42 Allow squadron switch in flight's edit-dialog 2024-06-09 00:52:09 +02:00
Raffson
b2315efb6c Deepcopy of waypoints for cloned flights 2024-06-08 21:51:09 +02:00
Raffson
9a55b4de1c Fix wrong/non-existing country names in squadrons 2024-06-08 19:03:39 +02:00
Raffson
d190d23655 Avoid scrubbing flights if escorts are unplannable 2024-06-08 18:55:28 +02:00
Raffson
f8e5a2e38d Drop country guard in AC combobox 2024-06-08 18:15:53 +02:00
Raffson
ec4f19e930 Add support for flyable OH-58D
Co-Authored-By: Starfire13 <72491792+Starfire13@users.noreply.github.com>
Co-Authored-By: zhexu14 <64713351+zhexu14@users.noreply.github.com>
2024-06-08 18:13:05 +02:00
Raffson
7f74203013 Pydcs Update for Kiowa
Co-Authored-By: M Chimiste <37678080+M-Chimiste@users.noreply.github.com>
2024-06-08 16:09:44 +02:00
MetalStormGhost
dc52457d80 Off-map spawns are now ignored in on_pilot_selected(). 2024-06-08 14:01:17 +02:00
tmz
a9950d4b80 The Long Road to H3 - Perf to 2 instead of 3 2024-06-08 13:43:17 +02:00
tmz
6577d6a417 Aleppo zone tweaks for The Long Road to H3 2024-06-08 13:43:17 +02:00
tmz
c6bd1e0cc3 Added Golden Warriors 2024-06-08 13:43:17 +02:00
tmz
dd1f080f97 Allied Sword update to support F-4E out of the box 2024-06-08 13:43:17 +02:00
Starfire13
68bdb86c71 Add NASAMs to MERAD unit list for campaign template (#334) 2024-06-08 10:37:24 +02:00
Starfire13
5cd0859004 Add SA-6 to Russia 2010 (#333) 2024-06-06 08:22:14 +02:00
ColonelAkirNakesh
1d02917c52 Update russia_2010.json (#332) 2024-06-04 08:26:01 +02:00
ColonelAkirNakesh
6a3a33170a Update russia_1990.json (#331) 2024-06-04 08:25:31 +02:00
ColonelAkirNakesh
50e3f89199 Create Yak-52.yaml (#330) 2024-06-04 08:24:36 +02:00
Starfire13
4c8d99e40a Change Black Sea performance to 3/3 (#329) 2024-06-04 00:35:21 +02:00
ColonelAkirNakesh
163841f8b0 Create FuSe-65.yaml (#327) 2024-06-04 00:12:22 +02:00
ColonelAkirNakesh
bf3d34ff5d Update germany_1944.json (#328) 2024-06-04 00:11:17 +02:00
Raffson
b501009285 Skip completed flights during mission generation 2024-06-01 21:17:06 +02:00
Raffson
c399002d1d Cleanup missed print statement 2024-06-01 21:17:06 +02:00
Raffson
8c9f62a2fb Add extra condition to preferred client start-type 2024-06-01 21:17:05 +02:00
Raffson
f88c103db5 Send events for AirLifts cancelled through PTD 2024-06-01 20:31:17 +02:00
Raffson
bb42a1f63a Fix cancel button in PendingTransfersDialog 2024-06-01 20:13:14 +02:00
tmz
58b4e5284e Add new Syria campaign 2024-06-01 19:42:27 +02:00
tmz
8e5b6b1e0c Introduce F-4E to allied Sword 2024-06-01 19:42:27 +02:00
tmz
030762a1dd Update Splash Damage 2024-06-01 19:42:27 +02:00
MetalStormGhost
8df77dec2a Fixed a bug where Pretense campaign generation would hang if the campaign had off-map spawns. 2024-06-01 16:59:30 +03:00
MetalStormGhost
28efecc03a Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-06-01 16:49:58 +03:00
MetalStormGhost
25e2da2b91 Pretense should always use Client slots. 2024-06-01 16:45:15 +03:00
Chris Tartamella
46f9cb62c1 Add new mission modification as run by WRL. (#325) 2024-05-30 08:22:23 +02:00
MetalStormGhost
b801136145 Updated pretenseluagenerator.py for the latest pydcs. 2024-05-28 18:59:09 +03:00
MetalStormGhost
0fd0e2c3c2 Added function return type annotation to pretense_trimmed_cp_name() 2024-05-28 18:40:25 +03:00
MetalStormGhost
bfe008775d Implemented PretenseNameGenerator.pretense_trimmed_cp_name to remove Scandic characters from Pretense zone names. 2024-05-28 18:40:25 +03:00
Raffson
7802ab992e Follow-up on F-4E-45MC
- missing radio config
- missing stored heading alignment
2024-05-27 23:53:10 +02:00
tmz42
a1fa58326c Splashdamage data update (#322)
* Splash Damage for missing Heatblur F-4E weapons (AGM-12, AGM-62 I and
GBU-8)

* Fixed Walleye I damage

* Set Bullpup C to correct value

* Modified warhead masses according to online sources and Janes Air Launched Weapons

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-05-27 21:26:36 +00:00
Starfire13
db93e56d6d Update F-4E-45MC loadouts (#323) 2024-05-27 12:46:42 +02:00
MetalStormGhost
9775d3b97c Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-05-26 22:06:19 +03:00
Raffson
3d6da6ae3d Deselect flights on ATO change 2024-05-25 23:42:59 +02:00
Raffson
8562c48753 Account for 'is_ownfor' in package dialog 2024-05-25 23:42:58 +02:00
Raffson
12d0632415 Add new warheads to splash damage 2024-05-25 23:42:58 +02:00
Raffson
0ab07c3878 Offset by 1 for UI 2024-05-25 23:42:58 +02:00
Raffson
b3f9cdfa74 Fix SunTime exceptions if sun doesn't rise of set 2024-05-25 23:42:57 +02:00
Raffson
0f53d00d25 F-106 fix 2024-05-25 19:40:08 +02:00
Raffson
16b6c00c0d Remove vertical scrollbar if there's no need in AWCD 2024-05-25 14:04:28 +02:00
Raffson
8482829f94 Fix bug in ATO panel where buttons remain disabled 2024-05-25 13:57:49 +02:00
Starfire13
881e9a4301 Adding Heatblur F-4E to Starfire’s campaigns (#320)
* Add files via upload

* Add files via upload
2024-05-25 12:34:13 +02:00
Starfire13
4bc7b41828 Fix liveries in squadron files for F-4E-45MC (#321) 2024-05-25 12:32:46 +02:00
Raffson
ee1a0cc9f1 Fix save compat issue 2024-05-25 02:32:50 +02:00
Raffson
13509bbda5 Babysteps towards fully editable faction 2024-05-25 02:18:43 +02:00
Raffson
692ceefadb Support for F-4E-45MC Phantom II
Co-Authored-By: Starfire13 <72491792+Starfire13@users.noreply.github.com>
2024-05-25 02:10:04 +02:00
Raffson
c09a5b68d5 Pydcs update for Phantom II
Save compat changes included
2024-05-24 21:30:48 +02:00
M Chimiste
c947ac0ef1 Support for VSN F-106 Delta Dart (#319)
* Add Delta Dart

* Added Banners and Icons

* Add loadouts

* Update f106.py

* Update f106.py

* Update VSN_F106A.yaml

* Update VSN_F106B.yaml

* Added the F-106 Delta Dart to factions. Also, replaced the banner with one by datoneslav, used with permission. Includes the official VSN logo (also used with permission). Also tweaked the backgrounds of the icons to blue to match the other aircraft.

* Added changelog entry.

* Add files via upload

---------

Co-authored-by: MetalStormGhost <ghosti@smytky.org>
2024-05-21 23:03:53 +02:00
Starfire13
3b65a0ab88 Able Archer update (#318) 2024-05-21 20:02:41 +02:00
Raffson
b07aae3d6e Allow [start, end] times for Night to traverse midnight
Daytime mapping entries like [23, 2] were causing trouble, for which the following fix was implemented...
2024-05-20 17:11:27 +02:00
Starfire13
562be59765 UI consistency fixes (#306)
* Update settings.py

Replace blue/red with ownfor/opfor
Also replaced all the NMI with NM. NMI is used by IEEE. NM is used by the military.
Also added in two missing periods.

* Update QSettingsWindow.py

Changed Red ATO to OPFOR ATO

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-05-20 15:06:18 +00:00
Starfire13
179936146f Fixes for Able Archer 83 (#317) 2024-05-20 16:09:43 +02:00
M Chimiste
6108727841 Add first testing version of operation frostbite (#316) 2024-05-19 22:55:38 +02:00
MetalStormGhost
c4d2f0e833 Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-05-19 19:04:37 +03:00
tmz42
2cc8e90e2f Allied Sword + faction files updates (#315) 2024-05-19 12:30:21 +02:00
Raffson
75ddfb9e17 Update changelog 2024-05-19 12:12:39 +02:00
Raffson
3a46bdcd1e Fix wrong init value for GameModel.is_ownfor 2024-05-19 12:05:13 +02:00
Starfire13
2ad3bff988 Add Able Archer 83 campaign for Kola map (#314) 2024-05-19 10:22:42 +02:00
Raffson
88b3264fe3 Fix TypeScript issues preventing build 2024-05-18 21:54:43 +02:00
Raffson
cbd90a5e36 Sync MapZones when changing campaigns 2024-05-18 21:13:32 +02:00
Raffson
0f9781ff30 Retain current zoom when switching campaigns 2024-05-18 20:24:55 +02:00
Raffson
0f612ac9d9 Final preparations for Lappi 2024-05-18 20:15:43 +02:00
Raffson
2b7113ec2a Add ArcGIS data for Lappi 2024-05-18 19:16:44 +02:00
M Chimiste
a8bf817b5a Diet Kola - Adding Beacons and Info.yaml (#311)
* Create kola.json

* Create info.yaml
2024-05-12 14:04:15 +02:00
Raffson
9d9295ed2d Pydcs Update (Kola + data export 2.9.4.53990) 2024-05-12 13:55:15 +02:00
Raffson
f203a5cf7a Babysteps towards full OPFOR control 2024-05-11 23:23:10 +02:00
Raffson
b61f625828 Fix bug in earlier bugfix -_- 2024-05-11 19:00:56 +02:00
Raffson
779f66670f Expose OPFOR squadrons
This allows for customizing OPFOR liveries, checking whether pilots are assigned, number of pilots available, and more stuff to get information & debug in an easier way...
2024-05-11 18:08:12 +02:00
Starfire13
86ef189da4 Campaign updates for Bright Star and Noisy Cricket (#310) 2024-05-11 18:07:40 +02:00
Raffson
d3e1a1df92 Fix bug w.r.t. OffMapSpawn CPs starting cold 2024-05-11 17:26:36 +02:00
MetalStormGhost
150a4dde49 Added missing call squadron: Optional[Squadron] = None to help avoid reference before assignment error. 2024-05-10 12:46:27 +03:00
MetalStormGhost
48861c53da Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-05-09 21:16:32 +03:00
MetalStormGhost
85f4f66f21 pretenseaircraftgenerator.py now obeys the new setting Default start type for Player flights. 2024-05-09 20:57:29 +03:00
MetalStormGhost
a27663e4b6 Default start type for player flights (#303)
* Implemented a new option in settings: Default start type for Player flights.

* Updated changelog.

* Removed unnecessary country parameter.

* Restore missing parameter

* on_pilot_changed should emit pilots_changed in its finally block, otherwise the start-type isn't updated if you have a single client pilot which you switch to a non-client pilot.

Also implemented other changes suggested by @Raffson, such as a more streamlined start_type QComboBox handling and moving the pilots_changed Signal to FlightRosterEditor.

* Decouple Signal from QFlighStartType

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
2024-05-09 10:19:30 +00:00
MetalStormGhost
4c455426e9 Added an icon for the Kola / Lapland terrain. (#308) 2024-05-08 21:54:20 +02:00
MetalStormGhost
04e60fb08d Ramon Airbase hack (#309) 2024-05-08 21:52:12 +02:00
MetalStormGhost
16377fe563 Added Iron Dome & David's Sling sites to Pretense generator. 2024-05-07 15:20:55 +03:00
MetalStormGhost
3bf77042fd Added HQ7 (Crotale) sites to Pretense generator. 2024-05-07 15:20:20 +03:00
Starfire13
0ab7dd2f15 Sort mod list by alphabetical order (#302) 2024-05-05 22:02:46 +02:00
Starfire13
61bfebb6fa Updated UH-60L loadouts(#301) 2024-05-05 21:56:49 +02:00
Starfire13
ea6591463f Updated factions for Able Archer 83 with some missing units (#300)
* Add files via upload

* Update blufor_late_coldwar.json

* Update russia_1980.json

* Update russia_1980.json

* Update russia_1980.json

* Update russia_1980.json
2024-05-05 21:55:38 +02:00
MetalStormGhost
e34a8c7875 Renamed CTLD plugin settings to Retribution
Renamed CTLD plugin settings to Retribution, as well as some other miscellaneous files too.
2024-05-05 11:04:56 +02:00
MetalStormGhost
557b441d13 Replaced expend=AI.Task.WeaponExpend.ONE with expend=AI.Task.WeaponExpend.QUARTER on CAS and BAI flights. Added HQ7 (Crotale) sites to Pretense generator. Also, added logistics units to most SAM sites to ensure that launchers have access to missile/ammunition resupply. 2024-05-04 09:27:36 +03:00
MetalStormGhost
c11575fcff Added missing custom_waypoints parameter in PretenseCargoFlightPlan. 2024-05-04 09:27:17 +03:00
MetalStormGhost
cbd230862f Air-start supersonic AI aircraft if the campaign is being flown in a WWII terrain. This will improve these terrains' use in cold war campaigns.
Air-start AI fixed wing (non-VTOL) aircraft if the campaign is being flown in the South Atlantic terrain and the airfield is one of the Harrier-only ones in East Falklands. This will help avoid AI aircraft from smashing into the end of the runway and exploding.
2024-05-04 09:26:02 +03:00
MetalStormGhost
e69708ada9 Removed Retribution convoys from Pretense campaigns. Removed deep copies of both coalitions when saving the pre-Pretense generation backup. 2024-05-04 09:24:56 +03:00
MetalStormGhost
c795ed01a0 Merge remote-tracking branch 'remotes/dcs-retribution/dcs-retribution/dev' into pretense-generator 2024-05-03 11:19:48 +03:00
Raffson
79d79645b0 Make sure escort task is added if flight has passed IP 2024-05-01 17:06:25 +02:00
Raffson
4b75913420 Insert orientation waypoint at/after IP
As a result of using fast-forward, AI flights are already oriented towards SPLIT, which slows things down during mission start. The escorts tend to go all over the place because of this...
2024-05-01 17:06:25 +02:00
Starfire13
4ef95a1939 Add MiG-21 to Blufor Late Cold War for Finland (#299)
* Add files via upload

* Fixed formatting
2024-05-01 13:33:24 +00:00
Starfire13
51a82f431d Update Operation Velvet Thunder (#298) 2024-05-01 14:14:39 +02:00
Starfire13
5891fb7441 Add C-130J to USA 1970 (#297) 2024-05-01 14:14:05 +02:00
Raffson
0ffe2f11bc Add guard before custom kneeboard injection 2024-05-01 14:12:23 +02:00
MetalStormGhost
40a01218dd Implemented saving of a Pretense pre-generation backup save before generating a Pretense campaign. 2024-05-01 11:01:59 +03:00
Raffson
e81d4068be Pydcs update for custom kneeboard injection 2024-04-28 21:26:24 +02:00
Raffson
4043e2452f Custom kneeboard injection
Drop custom kneeboards in SavedGames/DCS/Retribution/Kneeboards as you would normally do, i.e. using the necessary folders if kneeboards are aircraft-specific
2024-04-28 21:21:01 +02:00
Raffson
71965e7183 Fix exception after suntime update 2024-04-28 21:21:00 +02:00
Raffson
9fc34a56a3 Update most dependencies 2024-04-28 21:21:00 +02:00
Raffson
19523e35cf Fine-tune Strike-ingress waypoint tasks 2024-04-28 21:20:59 +02:00
Starfire13
015aa98f4c Add factions for Able Archer 83 (#296)
Campaign is yet to be added, merging these factions in preparation of the actual campaign...
2024-04-28 15:57:09 +02:00
Starfire13
c34ff3eea0 Update Operation Grabthar's Hammer (#295) 2024-04-28 15:48:05 +02:00
Starfire13
2ce776ff55 Update Operation Peace Spring (#294) 2024-04-28 15:47:24 +02:00
Starfire13
55bb54b1c2 Update Exercise Bright Star (#293) 2024-04-28 15:45:46 +02:00
Starfire13
4f7169da2a Update Operation Velvet Thunder (#292) 2024-04-28 15:44:11 +02:00
Starfire13
d005e03218 Add C-130J squadron to Operation Noisy Cricket for paradrops (#291) 2024-04-28 15:43:01 +02:00
Starfire13
b1cc2cace1 Add Operation Gazelle campaign scenario (#290) 2024-04-28 11:25:36 +02:00
dependabot[bot]
e9f39fec2c Bump express from 4.19.1 to 4.19.2 in /client (#289)
Bumps [express](https://github.com/expressjs/express) from 4.19.1 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.19.1...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-27 20:18:08 +00:00
Starfire13
f587132da3 Update for F-15E Suite 4+ loadout (#288) 2024-04-25 07:55:40 +02:00
Raffson
79515c6dfd Pydcs update for livery scanner fix 2024-04-21 14:52:50 +02:00
Starfire13
8b4a93ad6b Add MiG-15 to Egypt 1973 (#287) 2024-04-20 17:57:48 +02:00
Starfire13
ae719e520f Add Egypt 1973 faction (#284)
Egyptian faction for Yom Kippur War
2024-04-16 20:42:29 +02:00
Starfire13
215f15fb48 Add C-130J to Israel 1973 (#283) 2024-04-15 08:09:37 +02:00
Raffson
09ddc3c3c6 Fix flight-plans no longer being highlighted
Bug introduced as part of the upgrade to Python 3.11?
2024-04-14 14:15:02 +02:00
Raffson
f76fe6eeec Downgrade PySide to 6.4.2 2024-04-14 01:38:26 +02:00
Raffson
243eed27bb Update changlog w.r.t. NGW errors 2024-04-13 22:20:55 +02:00
Starfire13
31a9391fd0 Add missing C-130 to Israel 1973 faction (#282) 2024-04-13 21:32:15 +02:00
Raffson
377d0a2fad mypy fix 2024-04-13 19:52:38 +02:00
Raffson
3cf4729f64 Use "pydcs @" instead of "-e" in requirements.txt 2024-04-13 19:47:34 +02:00
Raffson
f1eb8f9a87 Revert "Pin python to 3.11.5"
This reverts commit 6bcd8a6910.
2024-04-13 18:06:47 +02:00
Raffson
6bcd8a6910 Pin python to 3.11.5 2024-04-13 18:01:37 +02:00
Raffson
07eeb3c627 Update pydantic 2024-04-13 16:15:49 +02:00
Raffson
a7e4cad3b7 mypy update + fix 2024-04-13 16:15:33 +02:00
Raffson
50808c3c06 Slightly downgrade PySide6 to nice-playing version with mypy 2024-04-13 16:12:53 +02:00
Raffson
e86e49cb6f Update PySide6 2024-04-13 15:18:27 +02:00
Raffson
61fe67660f Upgrade to Python 3.11
Partially fixes the bug where no error is shown in case of campaign errors when clicking Finish in the NGW
2024-04-13 15:18:02 +02:00
Raffson
327c934ed3 Properly determine Carrier/LHA TGO in apply_carrier_config
The first ground object of a naval control-point isn't always the CV or LHA, but can also be a different TGO if 1 or more TGOs are linked to the naval control point.
2024-04-07 17:15:41 +02:00
Raffson
d2fd7bbb4e Configurable carriers 2024-04-07 00:12:08 +02:00
Raffson
e416e07366 Log error if exception occurs during MissionScripting replacement 2024-04-06 22:01:15 +02:00
MetalStormGhost
0a7b885626 Now will not call CarrierMap:new() when game.settings.pretense_controllable_carrier is disabled. 2024-04-06 20:27:39 +03:00
Raffson
c5897950b6 Skip dead connections in IADS network
The "no skynet usable units" exception would get triggered when an IADS-node would form its connections. If one of those connections contained an IADS unit that was dead, the exception would get triggered since we weren't skipping "dead connections".
2024-04-06 17:39:27 +02:00
MetalStormGhost
64b1410de8 Implemented support for player controllable carriers in Pretense campaigns. This functionality can be enabled or disabled in settings, because the controllable carriers in Pretense do not build and deploy AI missions autonomously, so the old functionality is retained.
Added new options in settings:
- Carriers steam into wind
- Navmesh to use for Pretense carrier zones
- Remove ground spawn statics, including invisible FARPs, at airbases.
- Percentage of randomly selected aircraft types (only for generated squadrons)
intended to allow the user to increase aircraft variety.

Will now store the ICLS channel and Link4 frequency in missiondata.py CarrierInfo.

Implemented artillery groups as Pretense garrisons. Artillery groups are spawned by the Artillery Bunker. Will now also ensure that the logistics units spawned as part of Pretense garrisons are actually capable of ammo resupply.

Fixed the Pretense generator generating a bit too many missions per squadron. Ground spawns: Also hot start aircraft which require ground crew support (ground air or chock removal) which might not be available at roadbases. Also, pretensetgogenerator.py will now correctly handle air defence units in ground_unit_of_class(). Added Roland groups in the Pretense generator.
2024-04-06 15:46:11 +03:00
MetalStormGhost
55b173ec10 Merge branch 'pretense-generator' of https://github.com/MetalStormGhost/dcs-retribution into pretense-generator 2024-04-06 13:58:06 +03:00
tmz42
d4234dd7b3 Allied Sword updates (#279)
allowed IADS, added new Orchard at Kharab Ishk, added some strike targets
2024-04-02 00:04:15 +02:00
Raffson
d051774464 Allow naval adjustments on turn 0 2024-04-01 22:08:00 +02:00
Chilli
a9a56bfcc4 CJS super hornet mod update (#277) 2024-04-01 21:39:46 +02:00
Raffson
185911942a Fine-tune Herc's AirAssault 2024-04-01 19:14:42 +02:00
Raffson
dd2fb432f2 Use initial point for Herc AirAssault ingress 2024-04-01 16:03:40 +02:00
Raffson
bd7f7615b0 Skip discord notification in forks 2024-03-28 15:08:12 +01:00
Raffson
4bbe28838d Add livery_set to migrator 2024-03-24 13:29:05 +01:00
tmz42
60fafd262d Small update for Scenic Route I + Tomcat Livery Sets (#276) 2024-03-24 11:27:45 +01:00
Raffson
52b25821af Introduce livery-sets on squadron level
Randomly chooses one of the liveries defined if present in the squadron's yaml, overrides entry for "livery" or whatever is chosen in the UI except liveries on flight-member level
2024-03-23 21:29:01 +01:00
Raffson
6ee74cf079 Support for inline factions in campaign's yaml 2024-03-22 22:50:55 +01:00
Raffson
6b1b612c4b Update react packages 2024-03-22 21:54:27 +01:00
Raffson
a56aa7a766 Support yaml factions 2024-03-22 21:13:45 +01:00
Raffson
ab32c44b9d Ability to configure liveries on flight/flight-member level 2024-03-22 20:41:28 +01:00
Raffson
a7d65866e8 Fix A-10A payload bug leading to uneditable flight 2024-03-22 19:40:48 +01:00
Raffson
36ef246fcd Update changelog 2024-03-17 01:26:12 +01:00
Raffson
cbaf509375 Avoid crash with incompatible save on startup 2024-03-17 01:25:52 +01:00
Raffson
c4bc515e19 Update bug-report templates 2024-03-17 00:58:09 +01:00
Raffson
ae2f1dddf4 Fix save-compat issue 2024-03-17 00:57:34 +01:00
Raffson
0784efc1c7 Update bug-report templates for v1.3.0 2024-03-16 19:25:12 +01:00
Raffson
7306427487 Preparations for v1.4.0 2024-03-16 19:23:39 +01:00
MetalStormGhost
ff096b7d8d Will now generate multiple missions per squadron in Pretense campaigns to ensure most mission types are available. 2024-02-25 12:40:50 +02:00
MetalStormGhost
a49a1e9461 Updated pretense_compiled.lua to version 1.7.2:
Pretense v1.7.2 - 23 Feb 2024
Fixed occasional script error when saving zone groups
2024-02-25 12:40:50 +02:00
MetalStormGhost
e816e6990a Updated pretense_compiled.lua to version 1.6.5:
Pretense v1.6.5 - 04 Feb 2024
Fixed error on restoring strike targets
Block slots from within the mission, removing the need for slotblock.lua
2024-02-25 12:40:49 +02:00
MetalStormGhost
e549a923d5 Updated the Pretense script to version 1.4.5 2024-02-25 12:40:49 +02:00
MetalStormGhost
2edc0a7305 Ground unit presets are now generated from the coalition/faction definitions. 2024-02-25 12:40:49 +02:00
MetalStormGhost
200812a39b Other coalition TGO spawns are now generated with the correct faction. 2024-02-25 12:40:48 +02:00
MetalStormGhost
a4d8c14579 Added Iron Dome, David's Slinh, NASAMS-B and Rapier sites to Pretense. Fixed the timestamp in the save file name. 2024-02-25 12:40:48 +02:00
MetalStormGhost
969f0e26c7 Pretense zone radius (radii) for FOBs with FARPs will now be dynamically adjusted. Increased the size of Pretense zones at Damascus, Khalkhalah and Krasnodar-Pashkovsky (which are quite spread out) so the zone would encompass the entire airfield. 2024-02-25 12:40:48 +02:00
MetalStormGhost
49ba40aaf3 Now randomly shuffles the Pretense squadrons when generating a Pretense campaign. 2024-02-25 12:40:48 +02:00
MetalStormGhost
5cfeee7b2a Helicopter escort squadrons no longer cause an error on Pretense campaign generation. 2024-02-25 12:40:47 +02:00
MetalStormGhost
100eafcffd Separated init_header.lua into init_body_1.lua and init_header.lua. Deleted the ground unit groups since they will eventually be generated dynamically. 2024-02-25 12:40:47 +02:00
MetalStormGhost
c0a887b258 Copied init_header.lua to init_body_1.lua 2024-02-25 12:40:47 +02:00
MetalStormGhost
cafc6b5d30 Rename resources/plugins/pretense/init_body_2.lua (from resources/plugins/pretense/init_body_1.lua) 2024-02-25 12:40:47 +02:00
MetalStormGhost
3ab0f2a554 Rename resources/plugins/pretense/init_body_3.lua (from resources/plugins/pretense/init_body_2.lua) 2024-02-25 12:40:46 +02:00
MetalStormGhost
3d8956434c Will now append the date and time in the Pretense savefile, mitigating risks of missions script errors when trying to load a savefile from a previously generated mission. 2024-02-25 12:40:46 +02:00
MetalStormGhost
3d435c7821 Removed a non-functional option from the settings. 2024-02-25 12:40:46 +02:00
MetalStormGhost
76bab86e67 Updated the Pretense script to version 1.3.8 - 3 Dec 2023 and MIST to version 4.5.122. 2024-02-25 12:40:45 +02:00
MetalStormGhost
00760b831e Will now correctly generate Pretense campaigns with CJTF factions. 2024-02-25 12:40:45 +02:00
MetalStormGhost
f4e8e30cb1 Implemented new options in settings:
- Pretense: Extra friendly zone connections - Add connections from each zone to this many closest friendly zones, which don't have an existing supply route defined in the campaign.
- Number of cargo planes per side
- Number of AI SEAD flights per control point / zone
- Number of AI CAS flights per control point / zone
- Number of AI BAI flights per control point / zone
- Number of AI Strike flights per control point / zone
- Number of AI BARCAP flights per control point / zone
- Number of AI aircraft per flight
- Number of player flights per aircraft type at each base
- Number of AI cargo planes per side

Implemented CAS helo mission handling for Pretense. Implemented separate pretense_air_groups container for storing/referencing Flight objects. Tweaked the supply costs of SAM sites and Command centers. Will no longer generate player air starts at roadbases either. Restored the missing DEAD flights to Pretense. Removed spawning of frontline units and moved the JTAC spawning to pretensemissiongenerator.py
2024-02-25 12:40:44 +02:00
MetalStormGhost
e15aca8c54 Updated pretense_compiled.lua to version 1.3.6.
Implemented Pretense settings in pretense_compiled.lua
Added new function moveOffRoadToPointAndAssault() to enable assault groups to drive off-road and thus avoid some of the bridges where they might get stuck.
2024-02-25 12:40:44 +02:00
MetalStormGhost
93ab146cf5 Restored Retribution scripts and triggers to Pretense campaigns. Pretense progress can now be translated back to Retribution. Moved the trigger clearing from pretenseluagenerator.py to pretensemissiongenerator.py
keepActive is now only enabled for airbases and carriers/LHAs, for performance reasons.
2024-02-25 12:40:44 +02:00
MetalStormGhost
72d1c105a7 Implemented a Pretense settings page. 2024-02-25 12:40:43 +02:00
MetalStormGhost
5b60d64437 Reduced the number of cargo planes to 2 per side. 2024-02-25 12:40:43 +02:00
MetalStormGhost
2808d0f72c Fixed the bug of not generating opposing force ground unit groups at Pretense zones. Added the country name to ground vehicle group names to avoid duplicates. 2024-02-25 12:40:43 +02:00
MetalStormGhost
64e425012c Implemented generating runway zones at airports in Pretense. 2024-02-25 12:40:42 +02:00
MetalStormGhost
85db8c908e Restored compatibility of the Pretense generator after the timedelta -> datetime change and other changes caused by the Liberation merge/sync. 2024-02-25 12:40:42 +02:00
MetalStormGhost
b6449a7056 In the event of too many channel users, fail gracefully by reusing a random channel instead of always the previous one. 2024-02-25 12:40:42 +02:00
MetalStormGhost
91a9a9b88f Moved SAM site presets from presets.defenses.sam to presets.defenses.red/blue 2024-02-25 12:40:42 +02:00
Raffson
b36215b808 Fix errors due to LaserCodeRegistry move 2024-02-25 12:40:41 +02:00
MetalStormGhost
ced7aa4d72 Implemented generating amphibious units for Pretense unit groups. Amphibious units are selected out of a pre-defined list. Units which the faction has access to are preferred, but certain default unit types are selected as a fall-back to ensure that all the generated units can swim. 2024-02-25 12:40:41 +02:00
MetalStormGhost
08930f4a9a Added new icons and a separate button which will point to the Pretense/Foothold Discord. 2024-02-25 12:39:48 +02:00
MetalStormGhost
921b1ee9fd Fixed a bug in SHORAD and SAM generation. 2024-02-25 12:39:47 +02:00
MetalStormGhost
713cd98d10 Implemented SAM sites as products of a Command Center at a control point / zone, if the Retribution campaign has the corresponding SAM site there. The SAM site presets are still static, I might make them dynamic in the future. 2024-02-25 12:39:47 +02:00
MetalStormGhost
cf9cc9ba33 Fixed a bug in generate_pretense_zone_connection(). 2024-02-25 12:39:47 +02:00
MetalStormGhost
39c80cb974 Implemented a list of units which will be removed from Pretense ground assault groups due to pathfinding problems. The units will just remain still instead of advancing. Also added one tank to each group and increased the maximum size of the groups to 5. Removed artillery units from the groups, similarly due to pathfinding problems. 2024-02-25 12:39:47 +02:00
MetalStormGhost
cf9538f7df Implemented spawning of ship statics/units at naval control points instead of land structures or SHORAD sites. 2024-02-25 12:39:46 +02:00
MetalStormGhost
3d15505347 Implemented a static method for creating the Pretense zone connections and to avoid duplicate connections. 2024-02-25 12:39:46 +02:00
MetalStormGhost
11a0713e50 Pretense SEAD missions will now also target AAA. Also incorporates the fix to fullBuild():
Dzsekeb — 03/09/2023 11:50
add the highlighted line to the fullbuild function
https://discord.com/channels/959044877470027848/1031459721313517578/1147815809075392604
2024-02-25 12:39:46 +02:00
MetalStormGhost
dd0217a220 Pretense SEAD flights will use the SEAD Sweep loadout, when available. 2024-02-25 12:39:45 +02:00
MetalStormGhost
d870099f41 Replaced the air defence Command Center with a bunker for SHORADs. Will use the Command Center in the future for medium/long range SAMs. 2024-02-25 12:39:45 +02:00
MetalStormGhost
c6a8673b5e Added missing generation of AI BAI flights. Player flight groups are now generated as single-ship groups to support the Pretense missions for all players. Moved repetitive code in PretenseFlightGroupSpawner to insert_into_pretense() method. Disabled spawning of air assault statics by PretenseFlightGroupConfigurator since they are unnecessary in Pretense. 2024-02-25 12:39:45 +02:00
MetalStormGhost
cc3bef7937 Copied flightgroupconfigurator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-25 12:39:44 +02:00
MetalStormGhost
0bdb989a2c Aircraft squadrons are now products of the Hangar, instead of the air defence Command Center. 2024-02-25 12:39:44 +02:00
MetalStormGhost
765c85b639 Now generates one transport helicopter squadron for each control point which can operate one. Also implemented generating several Pretense cargo plane squadrons, defined by PRETENSE_AI_CARGO_PLANES_PER_SIDE. Cleaned up PretenseMissionGenerator.generate_air_units() a bit. 2024-02-25 12:39:44 +02:00
Raffson
7b9ddb1ece Formatting 2024-02-25 12:39:44 +02:00
MetalStormGhost
5fa7757ae1 Fixed not all generated flights getting inserted into Pretense data structures. 2024-02-25 12:39:43 +02:00
MetalStormGhost
064d17aab0 Improved the typing of game.pretense_air 2024-02-25 12:39:43 +02:00
MetalStormGhost
47ee0e5340 Added naval units (supply/cargo/landing ships) to Pretense scripts. 2024-02-25 12:39:43 +02:00
MetalStormGhost
6fd60d150e Raise NoParkingSlotError instead of RuntimeError when running out of ground spawns. 2024-02-25 12:39:43 +02:00
MetalStormGhost
946d578ffb Implemented generating player slots in the Pretense campaign. 2024-02-25 12:39:42 +02:00
MetalStormGhost
95bac8ec17 Run aircraft_generator.generate_packages() only once per ATO, instead of multiple times. Fixed the duplicated flights issue. 2024-02-25 12:39:42 +02:00
MetalStormGhost
91cfff58d9 Changed tanker_tacan from "N/A" to 0.0 when the tanker is not compatible with TACAN, in order to avoid mission script errors. 2024-02-25 12:39:42 +02:00
MetalStormGhost
585b3e74a0 Implemented a separate generate_packages() method in PretenseAircraftGenerator to prevent generating the same ATO multiple times over. 2024-02-25 12:39:41 +02:00
Raffson
4ff31ea86d Blast through errors 2024-02-25 12:39:41 +02:00
Raffson
231a8609a1 imports 2024-02-25 12:39:41 +02:00
MetalStormGhost
a6104968e8 Don't spawn flights for the other squadron in generate_pretense_aircraft(). 2024-02-25 12:39:40 +02:00
MetalStormGhost
77060031da Cleaned up some code. 2024-02-25 12:39:40 +02:00
MetalStormGhost
51a4b6603d Now generates air units for both sides at airfields. 2024-02-25 12:39:40 +02:00
MetalStormGhost
4e4eeff9ec Split the Pretense zone definition generation into separate methods: generate_pretense_zone_land() for land control points and generate_pretense_zone_sea() for carriers/LHAs. 2024-02-25 12:39:39 +02:00
MetalStormGhost
4b092e0763 Fixed FlightType.AEWC and FlightType.REFUELING handling in generate_pretense_aircraft(). 2024-02-25 12:39:39 +02:00
MetalStormGhost
aa641e1ff6 Added configuration constants for flights generated for Pretense. Fixed a bug which caused only one squadron per CP to be generated. Will now not generate Pretense cargo flights from Retribution off-map spawns, but instead will generate own air spawn points for them. Added a helper function initialize_pretense_data_structures(). 2024-02-25 12:39:39 +02:00
MetalStormGhost
d0ff363bb5 Now splits the generated Pretense group role properly in create_vehicle_group(). 2024-02-25 12:39:39 +02:00
MetalStormGhost
a97b3fee99 Will now dynamically generate offmapZones for supply cargo aircraft. 2024-02-25 12:39:38 +02:00
MetalStormGhost
51462673a2 Cleaned up pretense/pretenseaircraftgenerator.py
Moved a lot of logic into proper methods and added comments for all of those methods.
2024-02-25 12:39:38 +02:00
MetalStormGhost
3c139d8038 Fixed conflicting types for cp in generate_mid_mission(). 2024-02-25 12:39:38 +02:00
MetalStormGhost
b99a719d1c Converted closest_friendly_control_points_to from returning a tuple of the two closest control points to returning a list of all in sorted order. 2024-02-25 12:39:38 +02:00
MetalStormGhost
992beb5f45 Fixed a type assignment in pretensecargo.py 2024-02-25 12:39:37 +02:00
MetalStormGhost
ae13a422c3 Calling the constructor of MissionGenerator in PretenseMissionGenerator in unnecessary, so leaving it out. 2024-02-25 12:39:37 +02:00
MetalStormGhost
0b8fa9099c Disabled the base Retribution plugin in pretenseluagenerator.py and disabled adding zones for OffMapSpawns. 2024-02-25 12:39:37 +02:00
MetalStormGhost
13e7e976c3 Now connects more isolated zones in Pretense. 2024-02-25 12:39:36 +02:00
MetalStormGhost
529841bfe4 Add CJTF factions to the coalitions in Pretense, if they're not being used in the Retribution campaign. 2024-02-25 12:39:36 +02:00
MetalStormGhost
c0e26b3b7f Fixed Pretense ground unit group id/name handling. 2024-02-25 12:39:36 +02:00
MetalStormGhost
a869c2a758 Also connect carrier and LHA control points to adjacent friendly points in Pretense. Enlarged the carrier trigger zones. 2024-02-25 12:39:35 +02:00
MetalStormGhost
60fde46249 Split the init_body.lua script in two parts to facilitate dynamic JTAC spawning. 2024-02-25 12:39:35 +02:00
MetalStormGhost
27b1c92815 PretenseFlightGroupSpawner method for spawning mid-mission flights and setting the appropriate Pretense names to them. 2024-02-25 12:39:35 +02:00
MetalStormGhost
d965f90bb4 Implemented spawning of Pretense cargo aircraft. To support that, implemented a separate flight plan called PretenseCargoFlightPlan. Also, will now automatically generate transport squadrons for factions which don't have pre-defined squadrons for it, but have access to transport aircraft. 2024-02-25 12:39:34 +02:00
MetalStormGhost
4ad87aef3e Add CJTF factions to the coalitions in Pretense, if they're not being used in the campaign. 2024-02-25 12:39:34 +02:00
MetalStormGhost
daa3259e59 Implemented dynamic outputting of JTAC units to the Pretense init script. 2024-02-25 12:39:34 +02:00
MetalStormGhost
545210b35d Split the init_body.lua script in two parts. 2024-02-25 12:39:33 +02:00
MetalStormGhost
1dd9089477 First version of the generated Pretense campaign running in-game. 2024-02-25 12:39:33 +02:00
MetalStormGhost
0faa27dca5 Now clears Retribution triggers when generating a Pretense campaign. 2024-02-25 12:39:33 +02:00
MetalStormGhost
1db232417f Fixed missing return statement with isinstance(cp, OffMapSpawn). 2024-02-25 12:39:33 +02:00
MetalStormGhost
8253d41d37 Implemented adding ground unit groups to pretense data containers. 2024-02-25 12:39:32 +02:00
MetalStormGhost
d8b0283efe Deleted pretense/pretenseflightgroupconfigurator.py since it looks like it's not needed (at least for the time being). 2024-02-25 12:39:32 +02:00
MetalStormGhost
8dfb05b0d1 Tanker and AWACS frequency, TACAN and variant handling. 2024-02-25 12:39:32 +02:00
MetalStormGhost
aad48c0b78 Dynamically generate Pretense air groups (pretense/pretenseluagenerator.py) 2024-02-25 12:39:32 +02:00
MetalStormGhost
ecf659097a Copied flightgroupconfigurator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-25 12:39:31 +02:00
MetalStormGhost
15fafc20df Removed the reference to (nonexistent) NewPretenseWizard. 2024-02-25 12:39:31 +02:00
MetalStormGhost
7d0234820c First version of PretenseLuaGenerator, inherited from LuaGenerator 2024-02-25 12:39:30 +02:00
MetalStormGhost
9f271cdb91 ntentionally don't spawn anything at OffMapSpawns in Pretense 2024-02-25 12:39:30 +02:00
MetalStormGhost
7b2424c746 Added newlines to Pretense init scripts. 2024-02-25 12:39:30 +02:00
MetalStormGhost
5c9a5a5e35 Split init.lua code into three pieces. 2024-02-25 12:39:29 +02:00
MetalStormGhost
b6f0ec4da0 Copied init.lua into init_header.lua, init_body.lua and init_footer.lua
The init.lua script will be generated by
 1. injecting the header, then
 2. generating the ZoneCommand zones, then
 3. generating ConnectionManager connections, then
 4. injecting init_body.lua, then
 5. generating sypply injection and finally by
 6. injecting init_footer.lua
2024-02-25 12:39:29 +02:00
MetalStormGhost
c95e746687 Lua scripts from Pretense Caucasus 1.3.5, credit: Dzsekeb, original author of DCS Pretense.
Used with permission.
2024-02-25 12:39:29 +02:00
MetalStormGhost
d58f06be6d Copied luagenerator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-25 12:39:28 +02:00
MetalStormGhost
f752d422df Added Pretense icon, credit: Dzsekeb, original author of DCS Pretense.
Used with permission.
2024-02-25 12:39:28 +02:00
MetalStormGhost
1322c3ca54 Cleaned up some of my recent Pretense code. 2024-02-25 12:39:28 +02:00
MetalStormGhost
dc02c6f857 Will now generate ground units for the Pretense campaign. 2024-02-25 12:39:27 +02:00
MetalStormGhost
3d781d89de Copied tgogenerator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-25 12:39:27 +02:00
MetalStormGhost
9b54730191 Implemented generating trigger zones for supply routes, theater ground objects and helicopter supply points. Implemented name generator for Pretense air units. 2024-02-25 12:39:27 +02:00
MetalStormGhost
af4cf03335 Copied flightgroupspawner.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-25 12:39:27 +02:00
MetalStormGhost
31fb340ac8 Will now generate control point trigger zones and AI aircraft for the Pretense campaign. 2024-02-25 12:39:26 +02:00
MetalStormGhost
975471e942 Copied three files as templates/inheritance for generating Pretense campaigns from Retribution campaigns:
- missiongenerator.py
 - aircraftgenerator.py
 - triggergenerator.py
2024-02-25 12:39:26 +02:00
MetalStormGhost
a24925bc45 Updated pretense_compiled.lua to version 1.6.5:
Pretense v1.6.5 - 04 Feb 2024
Fixed error on restoring strike targets
Block slots from within the mission, removing the need for slotblock.lua
2024-02-06 11:02:15 +02:00
MetalStormGhost
3d8eed7334 Updated the Pretense script to version 1.4.5 2024-02-06 11:02:15 +02:00
MetalStormGhost
6e7844982d Ground unit presets are now generated from the coalition/faction definitions. 2024-02-06 11:02:14 +02:00
MetalStormGhost
1835743af0 Other coalition TGO spawns are now generated with the correct faction. 2024-02-06 10:59:25 +02:00
MetalStormGhost
3831f4f309 Added Iron Dome, David's Slinh, NASAMS-B and Rapier sites to Pretense. Fixed the timestamp in the save file name. 2024-02-06 10:59:25 +02:00
MetalStormGhost
226efe1151 Pretense zone radius (radii) for FOBs with FARPs will now be dynamically adjusted. Increased the size of Pretense zones at Damascus, Khalkhalah and Krasnodar-Pashkovsky (which are quite spread out) so the zone would encompass the entire airfield. 2024-02-06 10:59:24 +02:00
MetalStormGhost
db620a9056 Now randomly shuffles the Pretense squadrons when generating a Pretense campaign. 2024-02-06 10:59:24 +02:00
MetalStormGhost
2011a5f555 Helicopter escort squadrons no longer cause an error on Pretense campaign generation. 2024-02-06 10:59:24 +02:00
MetalStormGhost
6c9115f517 Separated init_header.lua into init_body_1.lua and init_header.lua. Deleted the ground unit groups since they will eventually be generated dynamically. 2024-02-06 10:59:24 +02:00
MetalStormGhost
17b9d9c3d5 Copied init_header.lua to init_body_1.lua 2024-02-06 10:59:23 +02:00
MetalStormGhost
2b63f45392 Rename resources/plugins/pretense/init_body_2.lua (from resources/plugins/pretense/init_body_1.lua) 2024-02-06 10:59:23 +02:00
MetalStormGhost
c830a6e7c0 Rename resources/plugins/pretense/init_body_3.lua (from resources/plugins/pretense/init_body_2.lua) 2024-02-06 10:59:23 +02:00
MetalStormGhost
59f6a3dcba Will now append the date and time in the Pretense savefile, mitigating risks of missions script errors when trying to load a savefile from a previously generated mission. 2024-02-06 10:59:22 +02:00
MetalStormGhost
09362cade4 Removed a non-functional option from the settings. 2024-02-06 10:59:22 +02:00
MetalStormGhost
85cec06098 Updated the Pretense script to version 1.3.8 - 3 Dec 2023 and MIST to version 4.5.122. 2024-02-06 10:59:21 +02:00
MetalStormGhost
4c201f25ba Will now correctly generate Pretense campaigns with CJTF factions. 2024-02-06 10:59:21 +02:00
MetalStormGhost
de0802a3c2 Implemented new options in settings:
- Pretense: Extra friendly zone connections - Add connections from each zone to this many closest friendly zones, which don't have an existing supply route defined in the campaign.
- Number of cargo planes per side
- Number of AI SEAD flights per control point / zone
- Number of AI CAS flights per control point / zone
- Number of AI BAI flights per control point / zone
- Number of AI Strike flights per control point / zone
- Number of AI BARCAP flights per control point / zone
- Number of AI aircraft per flight
- Number of player flights per aircraft type at each base
- Number of AI cargo planes per side

Implemented CAS helo mission handling for Pretense. Implemented separate pretense_air_groups container for storing/referencing Flight objects. Tweaked the supply costs of SAM sites and Command centers. Will no longer generate player air starts at roadbases either. Restored the missing DEAD flights to Pretense. Removed spawning of frontline units and moved the JTAC spawning to pretensemissiongenerator.py
2024-02-06 10:59:21 +02:00
MetalStormGhost
7908858433 Updated pretense_compiled.lua to version 1.3.6.
Implemented Pretense settings in pretense_compiled.lua
Added new function moveOffRoadToPointAndAssault() to enable assault groups to drive off-road and thus avoid some of the bridges where they might get stuck.
2024-02-06 10:59:20 +02:00
MetalStormGhost
595c468ab2 Restored Retribution scripts and triggers to Pretense campaigns. Pretense progress can now be translated back to Retribution. Moved the trigger clearing from pretenseluagenerator.py to pretensemissiongenerator.py
keepActive is now only enabled for airbases and carriers/LHAs, for performance reasons.
2024-02-06 10:59:20 +02:00
MetalStormGhost
87975b9aad Implemented a Pretense settings page. 2024-02-06 10:59:19 +02:00
MetalStormGhost
b508ef9aec Reduced the number of cargo planes to 2 per side. 2024-02-06 10:58:45 +02:00
MetalStormGhost
18a421f78e Fixed the bug of not generating opposing force ground unit groups at Pretense zones. Added the country name to ground vehicle group names to avoid duplicates. 2024-02-06 10:58:44 +02:00
MetalStormGhost
2b5c415159 Implemented generating runway zones at airports in Pretense. 2024-02-06 10:58:44 +02:00
MetalStormGhost
a3575995bd Restored compatibility of the Pretense generator after the timedelta -> datetime change and other changes caused by the Liberation merge/sync. 2024-02-06 10:58:44 +02:00
MetalStormGhost
04c32073aa In the event of too many channel users, fail gracefully by reusing a random channel instead of always the previous one. 2024-02-06 10:58:44 +02:00
MetalStormGhost
4f742b51f7 Moved SAM site presets from presets.defenses.sam to presets.defenses.red/blue 2024-02-06 10:58:43 +02:00
Raffson
e7b84e18d1 Fix errors due to LaserCodeRegistry move 2024-02-06 10:58:43 +02:00
MetalStormGhost
6c2e14c6d9 Implemented generating amphibious units for Pretense unit groups. Amphibious units are selected out of a pre-defined list. Units which the faction has access to are preferred, but certain default unit types are selected as a fall-back to ensure that all the generated units can swim. 2024-02-06 10:58:43 +02:00
MetalStormGhost
dc504005a1 Added new icons and a separate button which will point to the Pretense/Foothold Discord. 2024-02-06 10:58:42 +02:00
MetalStormGhost
359a579e65 Fixed a bug in SHORAD and SAM generation. 2024-02-06 10:58:42 +02:00
MetalStormGhost
473e70c44b Implemented SAM sites as products of a Command Center at a control point / zone, if the Retribution campaign has the corresponding SAM site there. The SAM site presets are still static, I might make them dynamic in the future. 2024-02-06 10:58:41 +02:00
MetalStormGhost
9be06d9601 Fixed a bug in generate_pretense_zone_connection(). 2024-02-06 10:58:41 +02:00
MetalStormGhost
9edb4ecc45 Implemented a list of units which will be removed from Pretense ground assault groups due to pathfinding problems. The units will just remain still instead of advancing. Also added one tank to each group and increased the maximum size of the groups to 5. Removed artillery units from the groups, similarly due to pathfinding problems. 2024-02-06 10:58:41 +02:00
MetalStormGhost
ffac036266 Implemented spawning of ship statics/units at naval control points instead of land structures or SHORAD sites. 2024-02-06 10:58:40 +02:00
MetalStormGhost
687ae88685 Implemented a static method for creating the Pretense zone connections and to avoid duplicate connections. 2024-02-06 10:58:40 +02:00
MetalStormGhost
4d8031ef96 Pretense SEAD missions will now also target AAA. Also incorporates the fix to fullBuild():
Dzsekeb — 03/09/2023 11:50
add the highlighted line to the fullbuild function
https://discord.com/channels/959044877470027848/1031459721313517578/1147815809075392604
2024-02-06 10:58:40 +02:00
MetalStormGhost
a60d1dce19 Pretense SEAD flights will use the SEAD Sweep loadout, when available. 2024-02-06 10:58:39 +02:00
MetalStormGhost
d1b8e20fde Replaced the air defence Command Center with a bunker for SHORADs. Will use the Command Center in the future for medium/long range SAMs. 2024-02-06 10:58:39 +02:00
MetalStormGhost
9d9740cdb3 Added missing generation of AI BAI flights. Player flight groups are now generated as single-ship groups to support the Pretense missions for all players. Moved repetitive code in PretenseFlightGroupSpawner to insert_into_pretense() method. Disabled spawning of air assault statics by PretenseFlightGroupConfigurator since they are unnecessary in Pretense. 2024-02-06 10:58:39 +02:00
MetalStormGhost
bd4e2d7189 Copied flightgroupconfigurator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-06 10:58:38 +02:00
MetalStormGhost
8f883d51d7 Aircraft squadrons are now products of the Hangar, instead of the air defence Command Center. 2024-02-06 10:58:38 +02:00
MetalStormGhost
fbbc2536a1 Now generates one transport helicopter squadron for each control point which can operate one. Also implemented generating several Pretense cargo plane squadrons, defined by PRETENSE_AI_CARGO_PLANES_PER_SIDE. Cleaned up PretenseMissionGenerator.generate_air_units() a bit. 2024-02-06 10:58:38 +02:00
Raffson
6f4a12658d Formatting 2024-02-06 10:58:38 +02:00
MetalStormGhost
1bcecd5b3a Fixed not all generated flights getting inserted into Pretense data structures. 2024-02-06 10:58:37 +02:00
MetalStormGhost
7653866ddb Improved the typing of game.pretense_air 2024-02-06 10:58:37 +02:00
MetalStormGhost
ecaa68f32a Added naval units (supply/cargo/landing ships) to Pretense scripts. 2024-02-06 10:58:37 +02:00
MetalStormGhost
bd0deed16a Raise NoParkingSlotError instead of RuntimeError when running out of ground spawns. 2024-02-06 10:58:36 +02:00
MetalStormGhost
d890ef0808 Implemented generating player slots in the Pretense campaign. 2024-02-06 10:58:36 +02:00
MetalStormGhost
8520764308 Run aircraft_generator.generate_packages() only once per ATO, instead of multiple times. Fixed the duplicated flights issue. 2024-02-06 10:58:36 +02:00
MetalStormGhost
1e3f6baceb Changed tanker_tacan from "N/A" to 0.0 when the tanker is not compatible with TACAN, in order to avoid mission script errors. 2024-02-06 10:58:36 +02:00
MetalStormGhost
00bcd547d3 Implemented a separate generate_packages() method in PretenseAircraftGenerator to prevent generating the same ATO multiple times over. 2024-02-06 10:58:35 +02:00
Raffson
a30fb0f368 Blast through errors 2024-02-06 10:58:35 +02:00
Raffson
087a38d105 imports 2024-02-06 10:58:35 +02:00
MetalStormGhost
bc92a2ade3 Don't spawn flights for the other squadron in generate_pretense_aircraft(). 2024-02-06 10:58:34 +02:00
MetalStormGhost
bc27dc4621 Cleaned up some code. 2024-02-06 10:58:34 +02:00
MetalStormGhost
94c1455249 Now generates air units for both sides at airfields. 2024-02-06 10:58:34 +02:00
MetalStormGhost
4be122e11a Split the Pretense zone definition generation into separate methods: generate_pretense_zone_land() for land control points and generate_pretense_zone_sea() for carriers/LHAs. 2024-02-06 10:58:34 +02:00
MetalStormGhost
f7c6f14220 Fixed FlightType.AEWC and FlightType.REFUELING handling in generate_pretense_aircraft(). 2024-02-06 10:58:33 +02:00
MetalStormGhost
c7ec3283bd Added configuration constants for flights generated for Pretense. Fixed a bug which caused only one squadron per CP to be generated. Will now not generate Pretense cargo flights from Retribution off-map spawns, but instead will generate own air spawn points for them. Added a helper function initialize_pretense_data_structures(). 2024-02-06 10:58:33 +02:00
MetalStormGhost
1d6c8a2453 Now splits the generated Pretense group role properly in create_vehicle_group(). 2024-02-06 10:58:33 +02:00
MetalStormGhost
730b982247 Will now dynamically generate offmapZones for supply cargo aircraft. 2024-02-06 10:58:33 +02:00
MetalStormGhost
8aea905640 Cleaned up pretense/pretenseaircraftgenerator.py
Moved a lot of logic into proper methods and added comments for all of those methods.
2024-02-06 10:58:32 +02:00
MetalStormGhost
3809e28a6a Fixed conflicting types for cp in generate_mid_mission(). 2024-02-06 10:58:32 +02:00
MetalStormGhost
19984636e3 Converted closest_friendly_control_points_to from returning a tuple of the two closest control points to returning a list of all in sorted order. 2024-02-06 10:58:32 +02:00
MetalStormGhost
f4c5c96fba Fixed a type assignment in pretensecargo.py 2024-02-06 10:58:31 +02:00
MetalStormGhost
2e889325e1 Calling the constructor of MissionGenerator in PretenseMissionGenerator in unnecessary, so leaving it out. 2024-02-06 10:58:31 +02:00
MetalStormGhost
a787cd4f70 Disabled the base Retribution plugin in pretenseluagenerator.py and disabled adding zones for OffMapSpawns. 2024-02-06 10:58:31 +02:00
MetalStormGhost
a975e2c2dc Now connects more isolated zones in Pretense. 2024-02-06 10:58:30 +02:00
MetalStormGhost
ddc5709a17 Add CJTF factions to the coalitions in Pretense, if they're not being used in the Retribution campaign. 2024-02-06 10:58:30 +02:00
MetalStormGhost
34fae51189 Fixed Pretense ground unit group id/name handling. 2024-02-06 10:58:30 +02:00
MetalStormGhost
5621649f10 Also connect carrier and LHA control points to adjacent friendly points in Pretense. Enlarged the carrier trigger zones. 2024-02-06 10:58:29 +02:00
MetalStormGhost
f4a239aaad Split the init_body.lua script in two parts to facilitate dynamic JTAC spawning. 2024-02-06 10:58:29 +02:00
MetalStormGhost
46e7f89da9 PretenseFlightGroupSpawner method for spawning mid-mission flights and setting the appropriate Pretense names to them. 2024-02-06 10:58:29 +02:00
MetalStormGhost
b73a18b7b9 Implemented spawning of Pretense cargo aircraft. To support that, implemented a separate flight plan called PretenseCargoFlightPlan. Also, will now automatically generate transport squadrons for factions which don't have pre-defined squadrons for it, but have access to transport aircraft. 2024-02-06 10:58:29 +02:00
MetalStormGhost
4a859e00ae Add CJTF factions to the coalitions in Pretense, if they're not being used in the campaign. 2024-02-06 10:58:28 +02:00
MetalStormGhost
acf035697f Implemented dynamic outputting of JTAC units to the Pretense init script. 2024-02-06 10:58:28 +02:00
MetalStormGhost
04993ddf1b Split the init_body.lua script in two parts. 2024-02-06 10:58:28 +02:00
MetalStormGhost
93265040f6 First version of the generated Pretense campaign running in-game. 2024-02-06 10:58:27 +02:00
MetalStormGhost
d517b1cdeb Now clears Retribution triggers when generating a Pretense campaign. 2024-02-06 10:58:27 +02:00
MetalStormGhost
5687c15c21 Fixed missing return statement with isinstance(cp, OffMapSpawn). 2024-02-06 10:58:27 +02:00
MetalStormGhost
c4effe88ee Implemented adding ground unit groups to pretense data containers. 2024-02-06 10:58:26 +02:00
MetalStormGhost
ab76d18621 Deleted pretense/pretenseflightgroupconfigurator.py since it looks like it's not needed (at least for the time being). 2024-02-06 10:58:26 +02:00
MetalStormGhost
f7e618bd0f Tanker and AWACS frequency, TACAN and variant handling. 2024-02-06 10:58:26 +02:00
MetalStormGhost
73f7d94bda Dynamically generate Pretense air groups (pretense/pretenseluagenerator.py) 2024-02-06 10:58:26 +02:00
MetalStormGhost
33a2c27f5e Copied flightgroupconfigurator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-06 10:58:25 +02:00
MetalStormGhost
17533399b3 Removed the reference to (nonexistent) NewPretenseWizard. 2024-02-06 10:58:25 +02:00
MetalStormGhost
cc713f089a First version of PretenseLuaGenerator, inherited from LuaGenerator 2024-02-06 10:58:25 +02:00
MetalStormGhost
84c20e8d81 ntentionally don't spawn anything at OffMapSpawns in Pretense 2024-02-06 10:58:24 +02:00
MetalStormGhost
c126d1dff7 Added newlines to Pretense init scripts. 2024-02-06 10:58:24 +02:00
MetalStormGhost
f500ec916e Split init.lua code into three pieces. 2024-02-06 10:58:24 +02:00
MetalStormGhost
842c2f8aaf Copied init.lua into init_header.lua, init_body.lua and init_footer.lua
The init.lua script will be generated by
 1. injecting the header, then
 2. generating the ZoneCommand zones, then
 3. generating ConnectionManager connections, then
 4. injecting init_body.lua, then
 5. generating sypply injection and finally by
 6. injecting init_footer.lua
2024-02-06 10:58:22 +02:00
MetalStormGhost
db993697f0 Lua scripts from Pretense Caucasus 1.3.5, credit: Dzsekeb, original author of DCS Pretense.
Used with permission.
2024-02-06 10:58:22 +02:00
MetalStormGhost
c8f78c1bf4 Copied luagenerator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-06 10:58:22 +02:00
MetalStormGhost
9135a91bb9 Added Pretense icon, credit: Dzsekeb, original author of DCS Pretense.
Used with permission.
2024-02-06 10:58:22 +02:00
MetalStormGhost
94d755e9a0 Cleaned up some of my recent Pretense code. 2024-02-06 10:58:21 +02:00
MetalStormGhost
d09cb70948 Will now generate ground units for the Pretense campaign. 2024-02-06 10:58:21 +02:00
MetalStormGhost
86e32b1990 Copied tgogenerator.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-06 10:58:21 +02:00
MetalStormGhost
676e4dcebf Implemented generating trigger zones for supply routes, theater ground objects and helicopter supply points. Implemented name generator for Pretense air units. 2024-02-06 10:58:20 +02:00
MetalStormGhost
d6ab4e9891 Copied flightgroupspawner.py as a template/inheritance for generating Pretense campaigns from Retribution campaigns. 2024-02-06 10:58:20 +02:00
MetalStormGhost
db95fc17dc Will now generate control point trigger zones and AI aircraft for the Pretense campaign. 2024-02-06 10:58:20 +02:00
MetalStormGhost
8402e5d9bb Copied three files as templates/inheritance for generating Pretense campaigns from Retribution campaigns:
- missiongenerator.py
 - aircraftgenerator.py
 - triggergenerator.py
2024-02-06 10:58:19 +02:00
1605 changed files with 246869 additions and 34378 deletions

View File

@@ -31,9 +31,8 @@ body:
If the bug was found in a test/development build, select "Test build" or "Development build" If the bug was found in a test/development build, select "Test build" or "Development build"
and provide a link to the applicable build in the field below. and provide a link to the applicable build in the field below.
options: options:
- v1.2.1 - v1.4.1
- Test build - Test build
- Development build
- type: input - type: input
attributes: attributes:
label: Test/Development build label: Test/Development build

View File

@@ -39,9 +39,8 @@ body:
If the bug was found in a test/development build, select "Test build" or "Development build" If the bug was found in a test/development build, select "Test build" or "Development build"
and provide a link to the applicable build in the field below. and provide a link to the applicable build in the field below.
options: options:
- 1.0.0 - 1.4.1
- Test build - Test build
- Development build
- type: input - type: input
attributes: attributes:
label: Test/Development build label: Test/Development build

View File

@@ -1,5 +1,5 @@
name: Build Liberation package name: Build Retribution package
description: Assembles the full Liberation application. description: Assembles the full Retribution application.
runs: runs:
using: composite using: composite
steps: steps:

View File

@@ -1,10 +1,10 @@
name: Liberation JS set-up name: Retribution JS set-up
description: Sets up the Liberation Javascript environment. description: Sets up the Retribution Javascript environment.
runs: runs:
using: composite using: composite
steps: steps:
- name: Set up Node - name: Set up Node
uses: actions/setup-node@v2 uses: actions/setup-node@v4
with: with:
node-version: "16" node-version: "16"
cache: npm cache: npm

View File

@@ -1,12 +1,12 @@
name: Liberation Python set-up name: Retribution Python set-up
description: Sets up the Liberation Python environment. description: Sets up the Retribution Python environment.
runs: runs:
using: composite using: composite
steps: steps:
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v2 uses: actions/setup-python@v5
with: with:
python-version: "3.10" python-version: "3.11"
cache: pip cache: pip
- name: Install environment - name: Install environment

View File

@@ -12,7 +12,7 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
@@ -35,7 +35,7 @@ jobs:
Compress-Archive -Path .\dist\dcs-retribution\ -DestinationPath Compress-Archive -Path .\dist\dcs-retribution\ -DestinationPath
dist\dcs-retribution.zip dist\dcs-retribution.zip
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v4
with: with:
name: dcs-retribution name: dcs-retribution
path: dist/dcs-retribution.zip path: dist/dcs-retribution.zip
@@ -49,7 +49,7 @@ jobs:
include_image: true include_image: true
avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
username: GitHub Build Notifier username: GitHub Build Notifier
if: startsWith(github.ref_name, 'test') if: github.repository == 'dcs-retribution/dcs-retribution' && startsWith(github.ref_name, 'test')
- name: Send status to Discord - name: Send status to Discord
uses: stegzilla/discord-notify@v2 uses: stegzilla/discord-notify@v2
@@ -60,4 +60,4 @@ jobs:
include_image: true include_image: true
avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png avatar_url: https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png
username: GitHub Build Notifier username: GitHub Build Notifier
if: github.ref_name == 'dev' if: github.repository == 'dcs-retribution/dcs-retribution' && github.ref_name == 'dev'

View File

@@ -7,19 +7,18 @@ jobs:
name: Black name: Black
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- uses: actions/setup-python@v2 - uses: actions/setup-python@v5
- uses: psf/black@stable - uses: psf/black@stable
with: with:
src: "." src: "."
options: "--check" options: "--check"
version: "23.9.1"
mypy: mypy:
name: Type checking name: Type checking
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true

View File

@@ -14,7 +14,7 @@ jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
@@ -33,7 +33,7 @@ jobs:
with: with:
release: true release: true
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v4
with: with:
name: dcs-retribution name: dcs-retribution
path: dist/ path: dist/
@@ -42,7 +42,7 @@ jobs:
needs: [build] needs: [build]
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/download-artifact@v2 - uses: actions/download-artifact@v4
with: with:
name: dcs-retribution name: dcs-retribution

View File

@@ -5,7 +5,7 @@ jobs:
name: Python tests name: Python tests
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
with: with:
submodules: true submodules: true
@@ -24,7 +24,7 @@ jobs:
name: Typescript tests name: Typescript tests
runs-on: windows-latest runs-on: windows-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Set up JS environment - name: Set up JS environment
uses: ./.github/actions/setup-liberation-js uses: ./.github/actions/setup-liberation-js

1
.gitignore vendored
View File

@@ -22,5 +22,6 @@ env/
/logs/ /logs/
/resources/logging.yaml /resources/logging.yaml
/resources/plugins/pretense/pretense_output.lua
*.psd *.psd

View File

@@ -1,6 +1,6 @@
repos: repos:
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: 22.6.0 rev: 24.8.0
hooks: hooks:
- id: black - id: black
language_version: python3 language_version: python3

View File

@@ -68,7 +68,7 @@ Our plans for future releases can be found on our
[Projects page](https://github.com/dcs-retribution/dcs-retribution/projects). [Projects page](https://github.com/dcs-retribution/dcs-retribution/projects).
Each planned release has a Project, and the page for that project has columns for to do, Each planned release has a Project, and the page for that project has columns for to do,
in progress, and done. Items in the Done column are in the in progress, and done. Items in the Done column are in the
[preview build](https://github.com/dcs-retribution/dcs-retribution/wiki/Betas) [preview build](https://github.com/dcs-retribution/dcs-retribution/wiki/Preview-builds)
for that release. Items in the To do column are planned to be added to that release. for that release. Items in the To do column are planned to be added to that release.
## Resources ## Resources
@@ -90,5 +90,7 @@ Excellent lua scripts DCS Liberation/Retribution uses as plugins:
* For the JTAC feature, DCS Retribution embeds Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze). * For the JTAC feature, DCS Retribution embeds Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze).
* Walder's [Skynet-IADS](https://github.com/walder/Skynet-IADS) is used for Integrated Air Defense System. * Walder's [Skynet-IADS](https://github.com/walder/Skynet-IADS) is used for Integrated Air Defense System.
* Carstens Arty Spotter https://www.digitalcombatsimulator.com/en/files/3339128/ is an amazing force multiplyer to drop the hammer on enemies.
* MBot's [Call Artillery Script](https://forum.dcs.world/topic/310506-call-artillery-script/) uses in-map artillery and forward observers to enable artillery fire missions.
Please also show some support to these projects ! Please also show some support to these projects !

View File

@@ -1,3 +1,123 @@
# Retribution v1.5.0
## Features/Improvements
* **[Campaigns]** Ability to define invisible FOBs
* **[Campaigns]** Ability to define influence zones for Control Points
* **[Plugins]** Improvements to AI support for EW Script 2.0
* **[Plugins]** Add moose as a base plugin
* **[Config]** New preference setting to trigger the first-start window on every start (could help in scenarios multiple Retribution instances need to run concurrently)
* **[Modding]** Update Grinelli Designs F-22A Mod to 2.0.0 (May 2025)
* **[Campaign Design]** Added support for Germany Cold War terrain by Ugra Media
* **[Options]** New option to control EPLRS-task injection in mission generator
* **[Modding]** Add Tornado F3 ADV (v1.0, requires FC3 fix)
* **[Modding]** Add Military Aircraft Mod 1.7.2 (See VSN Discord)
* **[Payload Editor]** Add Sniper POD
* **[Modding]** Update SU-30 mod to v2.8.04 Beta + CWS 3.72
* **[Plugins]** EW Script - DEAD added to applicable flight types.
* **[Plugins]** EW Script - Offensive Jamming restricted to aircraft with ALQ99/249 pods, or "has_built_in_jamming: true" in aircarft yaml (AI and Player)
* **[Modding]** Update to CJS Super Hornet Mod v2.4.4
* **[Engine]** Support for DCS 2.9.20.15010, including MiG-29A Fulcrum support.
* **[Campaigns]** Ability to define neutral FOBs and Airfields
* **[Modding]** Add Airboss Moose Module
* **[AirWing]** Use aircraft display names for easier differentiation between modules
## Fixes
* **[Flight Plans]** Fixed bugs wrt planning escort flights
* **[Flight Plans]** Added AntiShipStrike as a fallback task for OCA/Aircraft to fix a bug where the S-3B could not do OCA/Aircraft
* **[Squadrons]** Fixed a bug where loading an air wing config would not properly load all squadrons
* **[Flight Plans]** Fixed a bug where SEAD flights would fire one ARM and RTB
* **[Plugins]** EW Script - Fix radar detection routine.
* **[Campaign]** Fixed a bug where sinking a destroyer in a carrier group would cause squadrons to be removed from the carrier
* **[Engine]** Fixed a bug with transfers to Helipads
* **[Engine]** Fixed a bug with parking allocation
* **[Flight Plans]** Fixed a bug where divert airfield was broken for opfor
* **[Engine]** Fixed a bug with state.json loading wrt transfers
* **[Engine]** Fixed a bug wrt pretense generation and moose script conflicts
* **[Engine]** Fixed a bug where frontline debriefing was not properly calculated
* **[Mission Generation]** Fixed an issue where blue and red units spawned next to each other on frontlines
# Retribution v1.4.1 (hotfix)
## Fixes
* **[Plugins]** Avoid injecting jamming-tasks for player-controller aircraft when EW-Jamming plugin is disabled
# Retribution v1.4.0
## Features/Improvements
* **[Payload Editor]** Ability to configure liveries on flight/flight-member level
* **[Factions]** Support for definitions in yml/yaml format
* **[Campaigns/Factions]** Support for inline recommended faction in campaign's yaml file
* **[Squadrons]** Ability to define a livery-set for each squadron from which Retribution will randomly choose during mission generation
* **[Modding]** Updated support for F/A-18E/F/G mod version 2.2.5
* **[Modding]** Added VSN F-106 Delta Dart mod support (v2.9.4.101)
* **[Modding]** Added OH-6 Cayuse (v1.2) mod support, including the Vietnam Asset Pack v1.0
* **[Modding]** Added VSN EA-6B Prowler mod support (v2.9.4.102)
* **[Modding]** Added tripod3 Cold War assets mod support (v1.2)
* **[Modding]** Added VSN Mirage III mod support (2.5.7.01)
* **[Campaign Setup]** Allow adjustments to naval TGOs (except carriers) on turn 0
* **[Campaign Design]** Ability to configure specific carrier names & types in campaign's yaml file
* **[Mission Generation]** Ability to inject custom kneeboards
* **[Options]** Extend option (so it can be disabled when fixed in DCS) to force air-starts (except for the slots that work) at Ramon Airbase, similar to the Nevatim fix in Retribution 1.3.0
* **[Options]** New option in Settings: Default start type for Player flights.
* **[AirWing]** Expose OPFOR Squadrons, giving the ability to change liveries, auto-assignable mission types & an easy way to retrieve debug information.
* **[ATO]** Allow planning as OPFOR
* **[Campaign Design]** Support for latest maps (Kola, Afghanistan, Iraq)
* **[UI]** Zoom level retained when switching campaigns
* **[UX]** Allow changing squadrons in flight's edit dialog
* **[Cheats]** Sink/Resurrect carriers instead of showing an error during cheat-capture (use AWCD-cheat to add squadrons upon resurrection)
* **[UI/UX]** Allow changing conditions such as Time, Date & Weather
* **[Modding]** Added support for Su-15 Flagon mod (v1.0)
* **[Plugins]** Support for Carsten's Arty Spotter script
* **[Plugins]** Support for MBot's Call Artillery script (using on-map artillery)
* **[Modding]** Added support for SK-60 mod (v1.2.1)
* **[Mission Generation]** Introducing the Armed Recon flight plan, i.e. CAS against any Theater Ground Object
* **[Doctrine]** Ability to customize the startup time allocated to the player
* **[Mission Generation]** Ability to choose whether player flights can spawn on the sixpack or not
* **[Options]** New options in Mission Generator section: Limit AI radio callouts & Suppress AI radio callouts.
* **[Options]** New option to use the combat landing flag in the landing waypoint task for helicopters.
* **[UI/UX]** Sync package waypoints when primary flight's waypoints are updated and recreate other flights within the package to ensure JOIN, INGRESS & SPLIT are synced
* **[UI/UX]** Allow changing loadout on flight creation
* **[UI]** Display TOT for all waypoints in the flight plan
* **[UI]** Edit basic datalink properties for applicable aircraft
* **[Mission Generation]** Automatic datalink network setup for applicable aircraft (_should_ in theory avoid the need to re-save the mission)
* **[Options]** New option to force-enable deck-crew for super-carriers on dedicated server.
* **[Mission Generation]** Enable Supercarrier's LSO & Airboss stations
* **[UX]** Default settings are now loaded from Default.zip
* **[Autoplanner]** Plan Air-to-Air Escorts for AWACS & Tankers
* **[Package Planning]** Ability to plan recovery tanker flights
* **[Modding]** Support for Bandit's cloud presets mod (v15)
* **[UX]** Reduce size of save-file by loading landmap data on the fly, which also implies no new campaign needs to be started to benefit from an updated landmap
* **[New Game Wizard]** Ability to save an edited faction during new game creation
* **[Options]** New option to make AI helicopters prefer vertical takeoff and landing
* **[Campaign Design/Mission Generation]** Introduction of "rebel zones" which randomly spawn units according to the campaign's definitions.
* **[Mission Generation]** Missile sites now fire at random times instead of all at the beginning of the mission
* **[Modding]** Support for CurrentHill's Chinese Asset Pack (v1.1.4)
* **[Modding]** Updated support for CurrentHill's Swedish Asset Pack (v1.1.0)
* **[Modding]** Support for CurrentHill's Russian Asset Pack (v1.2.0)
* **[Modding]** Support for CurrentHill's USA Asset Pack (v1.1.5)
* **[Modding]** Update CJS Super Hornet to 2.4.2
* **[Modding]** Support for Cowboy's E-7A Wedgetail mod (Supports EW Script Offensive Jamming)
* **[Modding]** Support for szcz's MiG-31BM (v2.0)
* **[Plugins]** Added initial AI support for EW Script 2.0
* **[Options]** Ability to configure certain forced-options via a file (WIP: https://github.com/dcs-retribution/dcs-retribution/issues/490).
## Fixes
* **[UI/UX]** A-10A flights can be edited again
* **[Mission Generation]** IADS bug sometimes triggering "no skynet usable units" error during mission generation
* **[New Game Wizard]** Campaign errors show a dialog again and avoid CTDs
* **[UI]** Landmap wasn't updating when switching to a different theater
* **[Mission Results Processor]** Squadrons of a sunken carrier are now disbanded
* **[Mission Generation]** Introduced option to switch alt-type to AMSL during mission generation to avoid helicopters wanting to submerge over certain parts of the sea.
# Retribution v1.3.1
#### Note: Re-save your missions in DCS' Mission Editor to avoid possible crashes due to datalink (usually the case when F-16C blk50s are used) when hosting missions on a dedicated server.
## Fixes
* **[UX]** Fix save-compatibility issue
* **[UX]** Avoid crash on startup due to incompatible save
# Retribution v1.3.0 # Retribution v1.3.0
#### Note: Re-save your missions in DCS' Mission Editor to avoid possible crashes due to datalink (usually the case when F-16C blk50s are used) when hosting missions on a dedicated server. #### Note: Re-save your missions in DCS' Mission Editor to avoid possible crashes due to datalink (usually the case when F-16C blk50s are used) when hosting missions on a dedicated server.
@@ -40,6 +160,7 @@
* **[Mission Generator]** Set F-14's IP waypoint according to the flight-plan's ingress point * **[Mission Generator]** Set F-14's IP waypoint according to the flight-plan's ingress point
* **[Mission Generator]** Automatically de-spawn aircraft when arrival/divert is an off-map spawn * **[Mission Generator]** Automatically de-spawn aircraft when arrival/divert is an off-map spawn
* **[Options]** Option to de-spawn AI flights in the air if their start-type was manually set to In-Flight * **[Options]** Option to de-spawn AI flights in the air if their start-type was manually set to In-Flight
* **[Campaign Design]** Ability to add separate ground spawns for C-130 and other large aircraft to campaigns.
* **[Config]** Preference setting to use custom Liberation payloads instead of prioritizing Retribution's default * **[Config]** Preference setting to use custom Liberation payloads instead of prioritizing Retribution's default
* **[Config]** Preference setting to configure the server-port on which Retribution's back-end will run * **[Config]** Preference setting to configure the server-port on which Retribution's back-end will run
* **[Options]** Made AI jettisoning empty fuel tanks optional (disabled by default) * **[Options]** Made AI jettisoning empty fuel tanks optional (disabled by default)

28708
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,26 +6,26 @@
"license": "LGPL-3.0-or-later", "license": "LGPL-3.0-or-later",
"homepage": ".", "homepage": ".",
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^1.8.5", "@reduxjs/toolkit": "^1.9.7",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3", "@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.1.2", "@types/jest": "^29.5.12",
"@types/node": "^18.8.3", "@types/node": "^18.19.26",
"@types/react": "^18.0.21", "@types/react": "^18.2.69",
"@types/react-dom": "^18.0.6", "@types/react-dom": "^18.2.22",
"@types/react-redux": "^7.1.24", "@types/react-redux": "^7.1.33",
"axios": "^1.6.0", "axios": "^1.7.4",
"electron-window-state": "^5.0.3", "electron-window-state": "^5.0.3",
"esri-leaflet": "^3.0.8", "esri-leaflet": "^3.0.12",
"leaflet": "^1.9.2", "leaflet": "^1.9.4",
"leaflet-ruler": "^1.0.0", "leaflet-ruler": "^1.0.0",
"milsymbol": "^2.0.0", "milsymbol": "^2.2.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-esri-leaflet": "^2.0.1", "react-esri-leaflet": "^2.0.1",
"react-leaflet": "^4.1.0", "react-leaflet": "^4.2.1",
"react-redux": "^8.0.4", "react-redux": "^8.1.3",
"redux-logger": "^3.0.6", "redux-logger": "^3.0.6",
"typescript": "~4.8.4" "typescript": "~4.8.4"
}, },
@@ -61,20 +61,20 @@
] ]
}, },
"devDependencies": { "devDependencies": {
"@rtk-query/codegen-openapi": "^1.0.0", "@rtk-query/codegen-openapi": "^1.2.0",
"@trivago/prettier-plugin-sort-imports": "^4.3.0", "@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/leaflet": "^1.8.0", "@types/leaflet": "^1.9.8",
"@types/redux-logger": "^3.0.9", "@types/redux-logger": "^3.0.13",
"@types/websocket": "^1.0.5", "@types/websocket": "^1.0.10",
"electron": "^22.3.25", "electron": "^28.3.2",
"electron-is-dev": "^2.0.0", "electron-is-dev": "^2.0.0",
"generate-license-file": "^2.0.0", "generate-license-file": "^2.0.0",
"identity-obj-proxy": "^3.0.0", "identity-obj-proxy": "^3.0.0",
"license-checker": "^25.0.1", "license-checker": "^25.0.1",
"nth-check": "^2.0.1", "nth-check": "^2.1.1",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"ts-node": "^10.9.1", "ts-node": "^10.9.2",
"wait-on": "^6.0.1" "wait-on": "^7.2.0"
}, },
"jest": { "jest": {
"transformIgnorePatterns": [ "transformIgnorePatterns": [

View File

@@ -466,6 +466,7 @@ export type Game = {
navmeshes: NavMeshes; navmeshes: NavMeshes;
map_center?: LatLng; map_center?: LatLng;
unculled_zones: UnculledZone[]; unculled_zones: UnculledZone[];
map_zones: MapZones;
}; };
export type MapZones = { export type MapZones = {
inclusion: LatLng[][][]; inclusion: LatLng[][][];

View File

@@ -0,0 +1,34 @@
import { RootState } from "../app/store";
import { gameLoaded, gameUnloaded } from "./actions";
import { createSlice } from "@reduxjs/toolkit";
import { MapZones } from "./_liberationApi";
interface MapZonesState {
mapZones: MapZones;
}
const initialState: MapZonesState = {
mapZones: { inclusion: [], exclusion: [], sea: [] },
};
const mapZonesSlice = createSlice({
name: "map",
initialState: initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(gameLoaded, (state, action) => {
if (action.payload != null) {
state.mapZones.exclusion = action.payload.map_zones.exclusion;
state.mapZones.inclusion = action.payload.map_zones.inclusion;
state.mapZones.sea = action.payload.map_zones.sea;
}
});
builder.addCase(gameUnloaded, (state) => {
state.mapZones = initialState.mapZones;
});
},
});
export const selectMapZones = (state: RootState) => state.mapZones;
export default mapZonesSlice.reducer;

View File

@@ -10,6 +10,7 @@ import supplyRoutesReducer from "../api/supplyRoutesSlice";
import tgosReducer from "../api/tgosSlice"; import tgosReducer from "../api/tgosSlice";
import threatZonesReducer from "../api/threatZonesSlice"; import threatZonesReducer from "../api/threatZonesSlice";
import unculledZonesReducer from "../api/unculledZonesSlice"; import unculledZonesReducer from "../api/unculledZonesSlice";
import mapZonesReducer from "../api/mapZonesSlice";
import { import {
Action, Action,
PreloadedState, PreloadedState,
@@ -31,6 +32,7 @@ const rootReducer = combineReducers({
threatZones: threatZonesReducer, threatZones: threatZonesReducer,
[baseApi.reducerPath]: baseApi.reducer, [baseApi.reducerPath]: baseApi.reducer,
unculledZones: unculledZonesReducer, unculledZones: unculledZonesReducer,
mapZones: mapZonesReducer,
}); });
export function setupStore(preloadedState?: PreloadedState<RootState>) { export function setupStore(preloadedState?: PreloadedState<RootState>) {

View File

@@ -1,10 +1,10 @@
import { Flight } from "../../api/liberationApi"; import { Flight } from "../../api/liberationApi";
import { Icon, Point } from "leaflet"; import { Icon, Point } from "leaflet";
import { Symbol } from "milsymbol"; import ms from "milsymbol";
import { Marker } from "react-leaflet"; import { Marker } from "react-leaflet";
function iconForFlight(flight: Flight) { function iconForFlight(flight: Flight) {
const symbol = new Symbol(flight.sidc, { const symbol = new ms.Symbol(flight.sidc, {
size: 20, size: 20,
}); });

View File

@@ -1,9 +1,9 @@
import { ControlPoint } from "../../api/_liberationApi"; import { ControlPoint } from "../../api/_liberationApi";
import { Icon, Point } from "leaflet"; import { Icon, Point } from "leaflet";
import { Symbol } from "milsymbol"; import ms from "milsymbol";
export const iconForControlPoint = (cp: ControlPoint) => { export const iconForControlPoint = (cp: ControlPoint) => {
const symbol = new Symbol(cp.sidc, { const symbol = new ms.Symbol(cp.sidc, {
size: 24, size: 24,
colorMode: "Dark", colorMode: "Dark",
}); });

View File

@@ -182,7 +182,7 @@ describe("FlightPlansLayer", () => {
expect(mockLayerGroup).toBeCalledTimes(1); expect(mockLayerGroup).toBeCalledTimes(1);
}); });
it("are not drawn when only selected flights are to be drawn", () => { it("are not drawn when only selected flights are to be drawn", () => {
renderWithProviders(<FlightPlansLayer blue={true} selectedOnly />, { renderWithProviders(<FlightPlansLayer selectedOnly />, {
preloadedState: { preloadedState: {
flights: { flights: {
flights: { flights: {
@@ -355,7 +355,7 @@ describe("FlightPlansLayer", () => {
expect(mockLayerGroup).toBeCalledTimes(1); expect(mockLayerGroup).toBeCalledTimes(1);
}); });
it("are not drawn if red", () => { it("are not drawn if red", () => {
renderWithProviders(<FlightPlansLayer blue={false} selectedOnly />, { renderWithProviders(<FlightPlansLayer selectedOnly />, {
preloadedState: { preloadedState: {
flights: { flights: {
flights: { flights: {
@@ -397,7 +397,7 @@ describe("FlightPlansLayer", () => {
}, },
}, },
}); });
expect(mockPolyline).not.toHaveBeenCalled(); expect(mockPolyline).toHaveBeenCalled();
expect(mockLayerGroup).toBeCalledTimes(1); expect(mockLayerGroup).toBeCalledTimes(1);
}); });
}); });

View File

@@ -5,7 +5,7 @@ import FlightPlan from "../flightplan";
import { LayerGroup } from "react-leaflet"; import { LayerGroup } from "react-leaflet";
interface FlightPlansLayerProps { interface FlightPlansLayerProps {
blue: boolean; blue?: boolean;
selectedOnly?: true; selectedOnly?: true;
} }
@@ -15,11 +15,6 @@ function SelectedFlightPlan(props: FlightPlansLayerProps) {
return <></>; return <></>;
} }
if (!props.blue) {
// We don't currently support playing as red, so nothing to draw.
return <></>;
}
return ( return (
<FlightPlan <FlightPlan
key={flight.id} key={flight.id}

View File

@@ -25,10 +25,10 @@ export default function LiberationMap() {
const map = useRef<Map>(null); const map = useRef<Map>(null);
const mapCenter = useAppSelector(selectMapCenter); const mapCenter = useAppSelector(selectMapCenter);
useEffect(() => { useEffect(() => {
map.current?.setView(mapCenter, 8, { animate: true, duration: 1 }); map.current?.setView(mapCenter, map.current?.getZoom() ?? 8, { animate: true, duration: 1 });
}); });
return ( return (
<MapContainer zoom={8} zoomControl={false} ref={map}> <MapContainer zoom={map.current?.getZoom() ?? 8} zoomControl={false} ref={map}>
<ScaleControl /> <ScaleControl />
<LeafletRuler /> <LeafletRuler />
<LayersControl collapsed={false}> <LayersControl collapsed={false}>
@@ -98,8 +98,8 @@ export default function LiberationMap() {
<LayersControl.Overlay name="Allied IADS Network"> <LayersControl.Overlay name="Allied IADS Network">
<Iadsnetworklayer blue={true} /> <Iadsnetworklayer blue={true} />
</LayersControl.Overlay> </LayersControl.Overlay>
<LayersControl.Overlay name="Selected blue flight plan"> <LayersControl.Overlay name="Selected flight plan">
<FlightPlansLayer blue={true} selectedOnly /> <FlightPlansLayer selectedOnly />
</LayersControl.Overlay> </LayersControl.Overlay>
<LayersControl.Overlay name="All blue flight plans" checked> <LayersControl.Overlay name="All blue flight plans" checked>
<FlightPlansLayer blue={true} /> <FlightPlansLayer blue={true} />

View File

@@ -1,6 +1,7 @@
import { useGetTerrainZonesQuery } from "../../api/liberationApi"; import { useAppSelector } from "../../app/hooks";
import { LatLngLiteral } from "leaflet"; import { LatLngLiteral } from "leaflet";
import { LayerGroup, LayersControl, Polygon } from "react-leaflet"; import { LayerGroup, LayersControl, Polygon } from "react-leaflet";
import { selectMapZones } from "../../api/mapZonesSlice";
interface TerrainZoneLayerProps { interface TerrainZoneLayerProps {
zones: LatLngLiteral[][][]; zones: LatLngLiteral[][][];
@@ -28,33 +29,28 @@ function TerrainZoneLayer(props: TerrainZoneLayerProps) {
} }
export default function TerrainZonesLayers() { export default function TerrainZonesLayers() {
const { data, error, isLoading } = useGetTerrainZonesQuery(); const zones = useAppSelector(selectMapZones).mapZones;
var exclusion = <></>; var exclusion = <></>;
var inclusion = <></>; var inclusion = <></>;
var sea = <></>; var sea = <></>;
if (error) { if (zones) {
console.error("Error while loading terrain zones", error);
} else if (isLoading) {
} else if (!data) {
console.log("Empty response when loading terrain zones");
} else {
exclusion = ( exclusion = (
<TerrainZoneLayer <TerrainZoneLayer
zones={data.exclusion} zones={zones.exclusion}
color="#969696" color="#969696"
fillColor="#303030" fillColor="#303030"
/> />
); );
inclusion = ( inclusion = (
<TerrainZoneLayer <TerrainZoneLayer
zones={data.inclusion} zones={zones.inclusion}
color="#969696" color="#969696"
fillColor="#4b4b4b" fillColor="#4b4b4b"
/> />
); );
sea = ( sea = (
<TerrainZoneLayer zones={data.sea} color="#344455" fillColor="#344455" /> <TerrainZoneLayer zones={zones.sea} color="#344455" fillColor="#344455" />
); );
} }
return ( return (

View File

@@ -5,11 +5,11 @@ import {
import { Tgo as TgoModel } from "../../api/liberationApi"; import { Tgo as TgoModel } from "../../api/liberationApi";
import SplitLines from "../splitlines/SplitLines"; import SplitLines from "../splitlines/SplitLines";
import { Icon, Point } from "leaflet"; import { Icon, Point } from "leaflet";
import { Symbol as MilSymbol } from "milsymbol"; import ms from "milsymbol";
import { Marker, Tooltip } from "react-leaflet"; import { Marker, Tooltip } from "react-leaflet";
function iconForTgo(cp: TgoModel) { function iconForTgo(cp: TgoModel) {
const symbol = new MilSymbol(cp.sidc, { const symbol = new ms.Symbol(cp.sidc, {
size: 24, size: 24,
}); });

View File

@@ -60,7 +60,7 @@ const WaypointMarker = (props: WaypointMarkerProps) => {
useEffect(() => { useEffect(() => {
const waypoint = props.waypoint; const waypoint = props.waypoint;
marker.current?.setTooltipContent( marker.current?.setTooltipContent(
`${props.number} ${waypoint.name}<br />` + `${props.number-1} ${waypoint.name}<br />` +
`${waypoint.altitude_ft.toFixed()} ft ${waypoint.altitude_reference}<br />` + `${waypoint.altitude_ft.toFixed()} ft ${waypoint.altitude_reference}<br />` +
waypoint.timing waypoint.timing
); );

View File

@@ -6,10 +6,10 @@
# -- Project information ----------------------------------------------------- # -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = "DCS Liberation" project = "DCS Retribution"
copyright = "2023, DCS Liberation Team" copyright = "2025, DCS Retribution Team"
author = "DCS Liberation Team" author = "DCS Retribution Team"
release = "7.0.0" release = "1.5.0"
# -- General configuration --------------------------------------------------- # -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

View File

@@ -40,7 +40,7 @@ class ArmedForces:
# all layouts which are usable by the given faction. # all layouts which are usable by the given faction.
for layout in LAYOUTS.layouts: for layout in LAYOUTS.layouts:
if layout.generic and layout.usable_by_faction(faction): if layout.generic and layout.usable_by_faction(faction):
# Creates a faction compatible GorceGroup # Creates a faction compatible ForceGroup
self.add_or_update_force_group(ForceGroup.for_layout(layout, faction)) self.add_or_update_force_group(ForceGroup.for_layout(layout, faction))
# Add all preset groups afterwards to prevent them being merged with generics # Add all preset groups afterwards to prevent them being merged with generics

View File

@@ -9,20 +9,30 @@ from typing import Any, ClassVar, Iterator, Optional, TYPE_CHECKING, Type
import yaml import yaml
from dcs.unittype import ShipType, StaticType, UnitType as DcsUnitType, VehicleType from dcs.unittype import ShipType, StaticType, UnitType as DcsUnitType, VehicleType
from game import persistency
from game.data.groups import GroupTask from game.data.groups import GroupTask
from game.dcs.groundunittype import GroundUnitType from game.dcs.groundunittype import GroundUnitType
from game.dcs.helpers import static_type_from_name from game.dcs.helpers import static_type_from_name
from game.dcs.shipunittype import ShipUnitType from game.dcs.shipunittype import ShipUnitType
from game.dcs.unittype import UnitType from game.dcs.unittype import UnitType
from game.layout import LAYOUTS from game.layout import LAYOUTS
from game.layout.layout import TgoLayout, TgoLayoutUnitGroup from game.layout.layout import (
TgoLayout,
TgoLayoutUnitGroup,
)
from game.point_with_heading import PointWithHeading from game.point_with_heading import PointWithHeading
from game.theater.theatergroundobject import ( from game.theater.theatergroundobject import (
IadsGroundObject, IadsGroundObject,
IadsBuildingGroundObject, IadsBuildingGroundObject,
NavalGroundObject, NavalGroundObject,
) )
from game.theater.theatergroup import IadsGroundGroup, IadsRole, TheaterGroup from game.theater.theatergroup import (
IadsGroundGroup,
IadsRole,
TheaterGroup,
FIXED_POS_ARG,
FIXED_HDG_ARG,
)
from game.utils import escape_string_for_lua from game.utils import escape_string_for_lua
if TYPE_CHECKING: if TYPE_CHECKING:
@@ -248,8 +258,15 @@ class ForceGroup:
if unit_count == 0: if unit_count == 0:
# No units to be created so dont create a theater group for them # No units to be created so dont create a theater group for them
return return
# If the control point is neutral, don't create any units at all
if ground_object.control_point.captured.is_neutral:
return
# Generate Units # Generate Units
units = unit_group.generate_units(ground_object, unit_type, unit_count) fixed_pos = FIXED_POS_ARG in unit_group.name
fixed_hdg = FIXED_HDG_ARG in unit_group.name
units = unit_group.generate_units(
ground_object, unit_type, unit_count, fixed_pos, fixed_hdg
)
# Get or create the TheaterGroup # Get or create the TheaterGroup
ground_group = ground_object.group_by_name(group_name) ground_group = ground_object.group_by_name(group_name)
if ground_group is not None: if ground_group is not None:
@@ -304,11 +321,22 @@ class ForceGroup:
# Reverse the heading of the unit # Reverse the heading of the unit
unit.position.heading = unit.position.heading.opposite unit.position.heading = unit.position.heading.opposite
# Rotate unit around the center to align the orientation of the group # Rotate unit around the center to align the orientation of the group
unit.position.rotate(ground_object.position, rotation) unit.rotate_position_clockwise(ground_object.position, rotation)
@classmethod @classmethod
def _load_all(cls) -> None: def _load_all(cls) -> None:
for file in Path("resources/groups").glob("*.yaml"): locations = [
Path("resources/groups"),
persistency.groups_dir(),
]
for path in locations:
cls._process_path(path)
cls._loaded = True
@classmethod
def _process_path(cls, path: Path) -> None:
for file in path.glob("*.yaml"):
if not file.is_file(): if not file.is_file():
raise RuntimeError(f"{file.name} is not a valid ForceGroup") raise RuntimeError(f"{file.name} is not a valid ForceGroup")
@@ -356,5 +384,3 @@ class ForceGroup:
) )
cls._by_name[force_group.name] = force_group cls._by_name[force_group.name] = force_group
cls._loaded = True

View File

@@ -1,6 +1,7 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List from typing import List
from game.ato import FlightType
from game.ato.package import Package from game.ato.package import Package
@@ -11,6 +12,16 @@ class AirTaskingOrder:
#: The set of all planned packages in the ATO. #: The set of all planned packages in the ATO.
packages: List[Package] = field(default_factory=list) packages: List[Package] = field(default_factory=list)
@property
def has_awacs_package(self) -> bool:
return any(
[
p
for p in self.packages
if any([f for f in p.flights if f.flight_type is FlightType.AEWC])
]
)
def add_package(self, package: Package) -> None: def add_package(self, package: Package) -> None:
"""Adds a package to the ATO.""" """Adds a package to the ATO."""
self.packages.append(package) self.packages.append(package)

View File

@@ -1,4 +1,5 @@
"""Objective adjacency lists.""" """Objective adjacency lists."""
from __future__ import annotations from __future__ import annotations
from typing import Dict, Iterator, List, Optional, TYPE_CHECKING from typing import Dict, Iterator, List, Optional, TYPE_CHECKING

View File

@@ -35,6 +35,7 @@ if TYPE_CHECKING:
from game.sim.gameupdateevents import GameUpdateEvents from game.sim.gameupdateevents import GameUpdateEvents
from game.sim.simulationresults import SimulationResults from game.sim.simulationresults import SimulationResults
from game.squadrons import Squadron, Pilot from game.squadrons import Squadron, Pilot
from game.theater.player import Player
from game.transfers import TransferOrder from game.transfers import TransferOrder
from game.data.weapons import WeaponType from game.data.weapons import WeaponType
from .flightmember import FlightMember from .flightmember import FlightMember
@@ -89,6 +90,7 @@ class Flight(
self.initialize_fuel() self.initialize_fuel()
self.use_same_loadout_for_all_members = True self.use_same_loadout_for_all_members = True
self.use_same_livery_for_all_members = True
# Only used by transport missions. # Only used by transport missions.
self.cargo = cargo self.cargo = cargo
@@ -173,7 +175,7 @@ class Flight(
self.roster = FlightMembers.from_roster(self, self.roster) self.roster = FlightMembers.from_roster(self, self.roster)
@property @property
def blue(self) -> bool: def blue(self) -> Player:
return self.squadron.player return self.squadron.player
@property @property
@@ -274,8 +276,6 @@ class Flight(
self.fuel = unit_type.fuel_max * 0.5 self.fuel = unit_type.fuel_max * 0.5
elif unit_type == Hercules: elif unit_type == Hercules:
self.fuel = unit_type.fuel_max * 0.75 self.fuel = unit_type.fuel_max * 0.75
elif self.departure.cptype.name in ["FARP", "FOB"] and not self.is_helo:
self.fuel = unit_type.fuel_max * 0.75
def any_member_has_weapon_of_type(self, weapon_type: WeaponType) -> bool: def any_member_has_weapon_of_type(self, weapon_type: WeaponType) -> bool:
return any( return any(
@@ -286,7 +286,7 @@ class Flight(
return self.__str__() return self.__str__()
def __str__(self) -> str: def __str__(self) -> str:
string = f"[{self.flight_type}] {self.count} x {self.unit_type}" string = f"[{self.flight_type}] {self.count} x {self.unit_type} - {self.start_type.value}"
if self.custom_name: if self.custom_name:
return f"{self.custom_name} - {string}" return f"{self.custom_name} - {string}"
return string return string

View File

@@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any, Optional
from game.ato.loadouts import Loadout from game.ato.loadouts import Loadout
from game.lasercodes import LaserCode from game.lasercodes import LaserCode
@@ -16,14 +16,14 @@ class FlightMember:
self.use_custom_loadout = False self.use_custom_loadout = False
self.tgp_laser_code: LaserCode | None = None self.tgp_laser_code: LaserCode | None = None
self.weapon_laser_code: LaserCode | None = None self.weapon_laser_code: LaserCode | None = None
self.properties: dict[str, bool | float | int] = {} self.properties: dict[str, bool | float | int | str] = {}
self.livery: Optional[str] = None
self.use_livery_set: bool = True
def __setstate__(self, state: dict[str, Any]) -> None: def __setstate__(self, state: dict[str, Any]) -> None:
if "tgp_laser_code" not in state: new_state = FlightMember(state["pilot"], state["loadout"])
state["tgp_laser_code"] = None new_state.__dict__.update(state)
if "weapon_laser_code" not in state: self.__dict__.update(new_state.__dict__)
state["weapon_laser_code"] = None
self.__dict__.update(state)
def assign_tgp_laser_code(self, code: LaserCode) -> None: def assign_tgp_laser_code(self, code: LaserCode) -> None:
if self.tgp_laser_code is not None: if self.tgp_laser_code is not None:

View File

@@ -95,6 +95,13 @@ class FlightMembers(IFlightRoster):
# across all flight members. # across all flight members.
member.loadout = loadout member.loadout = loadout
def use_same_livery_for_all_members(self) -> None:
if not self.members:
return
livery = self.members[0].livery
for member in self.members[1:]:
member.livery = livery
def use_distinct_loadouts_for_each_member(self) -> None: def use_distinct_loadouts_for_each_member(self) -> None:
for member in self.members: for member in self.members:
member.loadout = member.loadout.clone() member.loadout = member.loadout.clone()

View File

@@ -12,7 +12,7 @@ from game.utils import Distance, Heading, Speed, knots, meters, nautical_miles
class AewcFlightPlan(PatrollingFlightPlan[PatrollingLayout]): class AewcFlightPlan(PatrollingFlightPlan[PatrollingLayout]):
@property @property
def patrol_duration(self) -> timedelta: def patrol_duration(self) -> timedelta:
return timedelta(hours=4) return self.flight.coalition.game.settings.desired_awacs_mission_duration
@property @property
def patrol_speed(self) -> Speed: def patrol_speed(self) -> Speed:

View File

@@ -75,13 +75,6 @@ class AirAssaultFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
) )
return tot - travel_time return tot - travel_time
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
if waypoint is self.tot_waypoint:
return self.tot
elif waypoint is self.layout.ingress:
return self.ingress_time
return None
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None: def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
return None return None
@@ -89,10 +82,6 @@ class AirAssaultFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
def ctld_target_zone_radius(self) -> Distance: def ctld_target_zone_radius(self) -> Distance:
return meters(2500) return meters(2500)
@property
def mission_begin_on_station_time(self) -> datetime | None:
return None
@property @property
def mission_departure_time(self) -> datetime: def mission_departure_time(self) -> datetime:
return self.package.time_over_target return self.package.time_over_target
@@ -136,10 +125,18 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
pickup.alt = altitude pickup.alt = altitude
pickup_position = pickup.position pickup_position = pickup.position
ingress = builder.ingress( ingress = (
FlightWaypointType.INGRESS_AIR_ASSAULT, builder.ingress(
self.package.waypoints.ingress, FlightWaypointType.INGRESS_AIR_ASSAULT,
self.package.target, self.package.waypoints.ingress,
self.package.target,
)
if not self.flight.is_hercules
else builder.ingress(
FlightWaypointType.INGRESS_AIR_ASSAULT,
self.package.waypoints.initial,
self.package.target,
)
) )
assault_area = builder.assault_area(self.package.target) assault_area = builder.assault_area(self.package.target)
@@ -165,7 +162,7 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
pickup=pickup, pickup=pickup,
nav_to=builder.nav_path( nav_to=builder.nav_path(
pickup_position, pickup_position,
self.package.waypoints.ingress, ingress.position,
altitude, altitude,
altitude_is_agl, altitude_is_agl,
), ),
@@ -182,7 +179,7 @@ class Builder(FormationAttackBuilder[AirAssaultFlightPlan, AirAssaultLayout]):
divert=builder.divert(self.flight.divert), divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(), bullseye=builder.bullseye(),
hold=None, hold=None,
join=builder.join(ingress.position), join=builder.join(self.package.waypoints.ingress),
split=builder.split(self.flight.arrival.position), split=builder.split(self.flight.arrival.position),
refuel=None, refuel=None,
custom_waypoints=list(), custom_waypoints=list(),

View File

@@ -201,9 +201,11 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
return_ascent = self._create_ascent_or_descent( return_ascent = self._create_ascent_or_descent(
builder, builder,
cargo.next_stop.position (
if cargo.next_stop != self.flight.arrival cargo.next_stop.position
else cargo.origin.position, if cargo.next_stop != self.flight.arrival
else cargo.origin.position
),
self.flight.arrival.position, self.flight.arrival.position,
altitude, altitude,
altitude_is_agl, altitude_is_agl,
@@ -211,9 +213,11 @@ class Builder(IBuilder[AirliftFlightPlan, AirliftLayout]):
return_descent = self._create_ascent_or_descent( return_descent = self._create_ascent_or_descent(
builder, builder,
self.flight.arrival.position, self.flight.arrival.position,
cargo.next_stop.position (
if cargo.next_stop != self.flight.arrival cargo.next_stop.position
else cargo.origin.position, if cargo.next_stop != self.flight.arrival
else cargo.origin.position
),
altitude, altitude,
altitude_is_agl, altitude_is_agl,
) )

View File

@@ -0,0 +1,34 @@
from __future__ import annotations
from typing import Type
from .formationattack import (
FormationAttackBuilder,
FormationAttackFlightPlan,
FormationAttackLayout,
)
from .uizonedisplay import UiZone, UiZoneDisplay
from ..flightwaypointtype import FlightWaypointType
from ...utils import nautical_miles
class ArmedReconFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
@staticmethod
def builder_type() -> Type[Builder]:
return Builder
def ui_zone(self) -> UiZone:
return UiZone(
[self.tot_waypoint.position],
nautical_miles(
self.flight.coalition.game.settings.armed_recon_engagement_range_distance
),
)
class Builder(FormationAttackBuilder[ArmedReconFlightPlan, FormationAttackLayout]):
def layout(self) -> FormationAttackLayout:
return self._build(FlightWaypointType.INGRESS_ARMED_RECON)
def build(self, dump_debug_info: bool = False) -> ArmedReconFlightPlan:
return ArmedReconFlightPlan(self.flight, self.layout())

View File

@@ -18,7 +18,7 @@ class BarCapFlightPlan(PatrollingFlightPlan[PatrollingLayout]):
@property @property
def patrol_duration(self) -> timedelta: def patrol_duration(self) -> timedelta:
return self.flight.coalition.doctrine.cap_duration return self.flight.coalition.game.settings.desired_barcap_mission_duration
@property @property
def patrol_speed(self) -> Speed: def patrol_speed(self) -> Speed:

View File

@@ -35,7 +35,12 @@ class CapBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
closest_airfield = airfield closest_airfield = airfield
break break
else: else:
raise PlanningError("Could not find any enemy airfields") for airfield in closest_cache.closest_airfields:
if airfield.captured != self.is_player:
closest_airfield = airfield
break
else:
raise PlanningError("Could not find any enemy airfields")
heading = Heading.from_degrees( heading = Heading.from_degrees(
location.position.heading_between_point(closest_airfield.position) location.position.heading_between_point(closest_airfield.position)

View File

@@ -125,16 +125,12 @@ class Builder(IBuilder[CasFlightPlan, CasLayout]):
ingress_point_shapely.x, ingress_point_shapely.y ingress_point_shapely.x, ingress_point_shapely.y
) )
patrol_start_waypoint = builder.nav( patrol_start_waypoint = builder.cas(patrol_start, ingress_egress_altitude)
patrol_start, ingress_egress_altitude, use_agl_patrol_altitude
)
patrol_start_waypoint.name = "FLOT START" patrol_start_waypoint.name = "FLOT START"
patrol_start_waypoint.pretty_name = "FLOT start" patrol_start_waypoint.pretty_name = "FLOT start"
patrol_start_waypoint.description = "FLOT boundary" patrol_start_waypoint.description = "FLOT boundary"
patrol_end_waypoint = builder.nav( patrol_end_waypoint = builder.cas(patrol_end, ingress_egress_altitude)
patrol_end, ingress_egress_altitude, use_agl_patrol_altitude
)
patrol_end_waypoint.name = "FLOT END" patrol_end_waypoint.name = "FLOT END"
patrol_end_waypoint.pretty_name = "FLOT end" patrol_end_waypoint.pretty_name = "FLOT end"
patrol_end_waypoint.description = "FLOT boundary" patrol_end_waypoint.description = "FLOT boundary"

View File

@@ -60,6 +60,16 @@ class CustomFlightPlan(FlightPlan[CustomLayout]):
def mission_departure_time(self) -> datetime: def mission_departure_time(self) -> datetime:
return self.package.time_over_target return self.package.time_over_target
@property
def landing_time(self) -> datetime:
arrival = (
self.layout.custom_waypoints[-1]
if self.layout.custom_waypoints
else self.layout.departure
)
return_time = self.total_time_between_waypoints(self.tot_waypoint, arrival)
return self.mission_departure_time + return_time
class Builder(IBuilder[CustomFlightPlan, CustomLayout]): class Builder(IBuilder[CustomFlightPlan, CustomLayout]):
def __init__( def __init__(

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
from datetime import datetime
from typing import Type from typing import Type
from .airassault import AirAssaultLayout from .airassault import AirAssaultLayout
@@ -11,6 +12,8 @@ from .formationattack import (
) )
from .waypointbuilder import WaypointBuilder from .waypointbuilder import WaypointBuilder
from .. import FlightType from .. import FlightType
from ..packagewaypoints import PackageWaypoints
from ...utils import feet
class EscortFlightPlan(FormationAttackFlightPlan): class EscortFlightPlan(FormationAttackFlightPlan):
@@ -18,23 +21,48 @@ class EscortFlightPlan(FormationAttackFlightPlan):
def builder_type() -> Type[Builder]: def builder_type() -> Type[Builder]:
return Builder return Builder
@property
def split_time(self) -> datetime:
# Avoid infinite recursion when this escort flight is itself the primary flight
# This can happen when only escort flights remain in a package
if (
self.package.primary_flight
and self.package.primary_flight != self.flight
and self.package.primary_flight.flight_plan
):
return self.package.primary_flight.flight_plan.mission_departure_time
else:
return super().split_time
class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]): class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
def layout(self) -> FormationAttackLayout: def layout(self) -> FormationAttackLayout:
assert self.package.waypoints is not None non_formation_escort = False
if self.package.waypoints is None:
self.package.waypoints = PackageWaypoints.create(
self.package, self.coalition, dump_debug_info=False
)
if self.package.primary_flight:
departure = self.package.primary_flight.flight_plan.layout.departure
self.package.waypoints.join = departure.position.lerp(
self.package.target.position, 0.2
)
non_formation_escort = True
builder = WaypointBuilder(self.flight) builder = WaypointBuilder(self.flight)
ingress, target = builder.escort( ingress, target = builder.escort(
self.package.waypoints.ingress, self.package.target self.package.waypoints.ingress, self.package.target
) )
if non_formation_escort:
target.position = self.package.waypoints.join
ingress.only_for_player = True ingress.only_for_player = True
target.only_for_player = True target.only_for_player = True
hold = None hold = None
if not self.flight.is_helo: if not (self.flight.is_helo or non_formation_escort):
hold = builder.hold(self._hold_point()) hold = builder.hold(self._hold_point())
join_pos = ( join_pos = (
self.package.waypoints.ingress WaypointBuilder.perturb(self.package.waypoints.ingress, feet(500))
if self.flight.is_helo if self.flight.is_helo
else self.package.waypoints.join else self.package.waypoints.join
) )
@@ -59,8 +87,6 @@ class Builder(FormationAttackBuilder[EscortFlightPlan, FormationAttackLayout]):
join = builder.join(ascent.position) join = builder.join(ascent.position)
if layout.pickup and layout.drop_off_ascent: if layout.pickup and layout.drop_off_ascent:
join = builder.join(layout.drop_off_ascent.position) join = builder.join(layout.drop_off_ascent.position)
elif layout.pickup:
join = builder.join(layout.pickup.position)
split = builder.split(layout.arrival.position) split = builder.split(layout.arrival.position)
if layout.drop_off: if layout.drop_off:
initial = builder.escort_hold( initial = builder.escort_hold(

View File

@@ -5,6 +5,7 @@ MissionPlanner. Those only plan basic information like the objective, aircraft
type, and the size of the flight. The FlightPlanBuilder is responsible for type, and the size of the flight. The FlightPlanBuilder is responsible for
generating the waypoints for the mission. generating the waypoints for the mission.
""" """
from __future__ import annotations from __future__ import annotations
import math import math
@@ -105,19 +106,6 @@ class FlightPlan(ABC, Generic[LayoutT]):
# #
# Plus, it's a loiter point so there's no reason to hurry. # Plus, it's a loiter point so there's no reason to hurry.
factor = 0.75 factor = 0.75
elif (
self.flight.is_helo
and (
a.waypoint_type == FlightWaypointType.JOIN
or "INGRESS" in a.waypoint_type.name
or a.waypoint_type == FlightWaypointType.CUSTOM
)
and self.package.primary_flight
and not self.package.primary_flight.flight_plan.is_airassault
):
# Helicopter flights should be slowed down between JOIN & INGRESS
# to allow the escort to keep up while engaging targets along the way.
factor = 0.50
# TODO: Adjust if AGL. # TODO: Adjust if AGL.
# We don't have an exact heightmap, but we should probably be performing # We don't have an exact heightmap, but we should probably be performing
# *some* adjustment for NTTR since the minimum altitude of the map is # *some* adjustment for NTTR since the minimum altitude of the map is
@@ -268,7 +256,9 @@ class FlightPlan(ABC, Generic[LayoutT]):
def estimate_startup(self) -> timedelta: def estimate_startup(self) -> timedelta:
if self.flight.start_type is StartType.COLD: if self.flight.start_type is StartType.COLD:
if self.flight.client_count: if self.flight.client_count:
return timedelta(minutes=10) return timedelta(
minutes=self.flight.coalition.game.settings.player_startup_time
)
else: else:
# The AI doesn't seem to have a real startup procedure. # The AI doesn't seem to have a real startup procedure.
return timedelta(minutes=2) return timedelta(minutes=2)
@@ -306,6 +296,10 @@ class FlightPlan(ABC, Generic[LayoutT]):
"""The time that the mission is complete and the flight RTBs.""" """The time that the mission is complete and the flight RTBs."""
raise NotImplementedError raise NotImplementedError
@property
def landing_time(self) -> datetime:
raise NotImplementedError
@self_type_guard @self_type_guard
def is_loiter(self, flight_plan: FlightPlan[Any]) -> TypeGuard[LoiterFlightPlan]: def is_loiter(self, flight_plan: FlightPlan[Any]) -> TypeGuard[LoiterFlightPlan]:
return False return False

View File

@@ -3,10 +3,12 @@ from __future__ import annotations
from typing import Any, TYPE_CHECKING, Type from typing import Any, TYPE_CHECKING, Type
from game.ato import FlightType from game.ato import FlightType
from game.theater.controlpoint import NavalControlPoint
from .aewc import AewcFlightPlan from .aewc import AewcFlightPlan
from .airassault import AirAssaultFlightPlan from .airassault import AirAssaultFlightPlan
from .airlift import AirliftFlightPlan from .airlift import AirliftFlightPlan
from .antiship import AntiShipFlightPlan from .antiship import AntiShipFlightPlan
from .armedrecon import ArmedReconFlightPlan
from .bai import BaiFlightPlan from .bai import BaiFlightPlan
from .barcap import BarCapFlightPlan from .barcap import BarCapFlightPlan
from .cas import CasFlightPlan from .cas import CasFlightPlan
@@ -18,8 +20,10 @@ from .ocaaircraft import OcaAircraftFlightPlan
from .ocarunway import OcaRunwayFlightPlan from .ocarunway import OcaRunwayFlightPlan
from .packagerefueling import PackageRefuelingFlightPlan from .packagerefueling import PackageRefuelingFlightPlan
from .planningerror import PlanningError from .planningerror import PlanningError
from .pretensecargo import PretenseCargoFlightPlan
from .sead import SeadFlightPlan from .sead import SeadFlightPlan
from .seadsweep import SeadSweepFlightPlan from .seadsweep import SeadSweepFlightPlan
from .shiprecoverytanker import RecoveryTankerFlightPlan
from .strike import StrikeFlightPlan from .strike import StrikeFlightPlan
from .sweep import SweepFlightPlan from .sweep import SweepFlightPlan
from .tarcap import TarCapFlightPlan from .tarcap import TarCapFlightPlan
@@ -60,6 +64,9 @@ class FlightPlanBuilderTypes:
FlightType.TRANSPORT: AirliftFlightPlan.builder_type(), FlightType.TRANSPORT: AirliftFlightPlan.builder_type(),
FlightType.FERRY: FerryFlightPlan.builder_type(), FlightType.FERRY: FerryFlightPlan.builder_type(),
FlightType.AIR_ASSAULT: AirAssaultFlightPlan.builder_type(), FlightType.AIR_ASSAULT: AirAssaultFlightPlan.builder_type(),
FlightType.PRETENSE_CARGO: PretenseCargoFlightPlan.builder_type(),
FlightType.ARMED_RECON: ArmedReconFlightPlan.builder_type(),
FlightType.RECOVERY: RecoveryTankerFlightPlan.builder_type(),
} }
try: try:
return builder_dict[flight.flight_type] return builder_dict[flight.flight_type]

View File

@@ -33,8 +33,7 @@ class FormationLayout(LoiterLayout, ABC):
class FormationFlightPlan(LoiterFlightPlan, ABC): class FormationFlightPlan(LoiterFlightPlan, ABC):
@property @property
@abstractmethod @abstractmethod
def package_speed_waypoints(self) -> set[FlightWaypoint]: def package_speed_waypoints(self) -> set[FlightWaypoint]: ...
...
@property @property
def combat_speed_waypoints(self) -> set[FlightWaypoint]: def combat_speed_waypoints(self) -> set[FlightWaypoint]:
@@ -64,8 +63,10 @@ class FormationFlightPlan(LoiterFlightPlan, ABC):
return min(speeds) return min(speeds)
def speed_between_waypoints(self, a: FlightWaypoint, b: FlightWaypoint) -> Speed: def speed_between_waypoints(self, a: FlightWaypoint, b: FlightWaypoint) -> Speed:
if self.package.formation_speed and b in self.package_speed_waypoints: if (
return self.package.formation_speed speed := self.package.formation_speed(self.flight.is_helo)
) and b in self.package_speed_waypoints:
return speed
return super().speed_between_waypoints(a, b) return super().speed_between_waypoints(a, b)
@property @property
@@ -75,13 +76,11 @@ class FormationFlightPlan(LoiterFlightPlan, ABC):
@property @property
@abstractmethod @abstractmethod
def join_time(self) -> datetime: def join_time(self) -> datetime: ...
...
@property @property
@abstractmethod @abstractmethod
def split_time(self) -> datetime: def split_time(self) -> datetime: ...
...
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None: def tot_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
if waypoint == self.layout.join: if waypoint == self.layout.join:

View File

@@ -11,7 +11,7 @@ from dcs import Point
from game.flightplan import HoldZoneGeometry from game.flightplan import HoldZoneGeometry
from game.theater import MissionTarget from game.theater import MissionTarget
from game.utils import Speed, meters, nautical_miles from game.utils import nautical_miles, Speed, feet
from .flightplan import FlightPlan from .flightplan import FlightPlan
from .formation import FormationFlightPlan, FormationLayout from .formation import FormationFlightPlan, FormationLayout
from .ibuilder import IBuilder from .ibuilder import IBuilder
@@ -39,8 +39,9 @@ class FormationAttackFlightPlan(FormationFlightPlan, ABC):
if b.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC: if b.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC:
# Should be impossible, as any package with at least one # Should be impossible, as any package with at least one
# FormationFlightPlan flight needs a formation speed. # FormationFlightPlan flight needs a formation speed.
assert self.package.formation_speed is not None speed = self.package.formation_speed(self.flight.is_helo)
return self.package.formation_speed assert speed is not None
return speed
return super().speed_between_waypoints(a, b) return super().speed_between_waypoints(a, b)
@property @property
@@ -53,7 +54,7 @@ class FormationAttackFlightPlan(FormationFlightPlan, ABC):
"TARGET AREA", "TARGET AREA",
FlightWaypointType.TARGET_GROUP_LOC, FlightWaypointType.TARGET_GROUP_LOC,
self.package.target.position, self.package.target.position,
meters(0), feet(0),
"RADIO", "RADIO",
) )
@@ -192,6 +193,7 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
join_pos = self.package.waypoints.join join_pos = self.package.waypoints.join
if self.flight.is_helo: if self.flight.is_helo:
join_pos = self.package.waypoints.ingress join_pos = self.package.waypoints.ingress
join_pos = WaypointBuilder.perturb(join_pos, feet(500))
join = builder.join(join_pos) join = builder.join(join_pos)
split = builder.split(self._get_split()) split = builder.split(self._get_split())
refuel = self._build_refuel(builder) refuel = self._build_refuel(builder)
@@ -286,6 +288,8 @@ class FormationAttackBuilder(IBuilder[FlightPlanT, LayoutT], ABC):
return builder.sead_area(location) return builder.sead_area(location)
elif flight.flight_type == FlightType.OCA_AIRCRAFT: elif flight.flight_type == FlightType.OCA_AIRCRAFT:
return builder.oca_strike_area(location) return builder.oca_strike_area(location)
elif flight.flight_type == FlightType.ARMED_RECON:
return builder.armed_recon_area(location)
else: else:
return builder.strike_area(location) return builder.strike_area(location)

View File

@@ -11,7 +11,7 @@ from ..packagewaypoints import PackageWaypoints
if TYPE_CHECKING: if TYPE_CHECKING:
from game.coalition import Coalition from game.coalition import Coalition
from game.data.doctrine import Doctrine from game.data.doctrine import Doctrine
from game.theater import ConflictTheater from game.theater import ConflictTheater, Player
from game.threatzones import ThreatZones from game.threatzones import ThreatZones
from ..flight import Flight from ..flight import Flight
from ..package import Package from ..package import Package
@@ -38,7 +38,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
self._generate_package_waypoints_if_needed(dump_debug_info) self._generate_package_waypoints_if_needed(dump_debug_info)
self._flight_plan = self.build(dump_debug_info) self._flight_plan = self.build(dump_debug_info)
except NavMeshError as ex: except NavMeshError as ex:
color = "blue" if self.flight.squadron.player else "red" color = "blue" if self.flight.squadron.player.is_blue else "red"
raise PlanningError( raise PlanningError(
f"Could not plan {color} {self.flight.flight_type.value} from " f"Could not plan {color} {self.flight.flight_type.value} from "
f"{self.flight.departure} to {self.package.target}" f"{self.flight.departure} to {self.package.target}"
@@ -60,8 +60,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
return self.flight.departure.theater return self.flight.departure.theater
@abstractmethod @abstractmethod
def build(self, dump_debug_info: bool = False) -> FlightPlanT: def build(self, dump_debug_info: bool = False) -> FlightPlanT: ...
...
@property @property
def package(self) -> Package: def package(self) -> Package:
@@ -72,7 +71,7 @@ class IBuilder(ABC, Generic[FlightPlanT, LayoutT]):
return self.flight.coalition return self.flight.coalition
@property @property
def is_player(self) -> bool: def is_player(self) -> Player:
return self.coalition.player return self.coalition.player
@property @property

View File

@@ -26,8 +26,7 @@ class LoiterFlightPlan(StandardFlightPlan[Any], ABC):
@property @property
@abstractmethod @abstractmethod
def push_time(self) -> datetime: def push_time(self) -> datetime: ...
...
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None: def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
if waypoint == self.layout.hold: if waypoint == self.layout.hold:

View File

@@ -80,6 +80,9 @@ class PatrollingFlightPlan(StandardFlightPlan[LayoutT], UiZoneDisplay, ABC):
return self.patrol_end_time return self.patrol_end_time
return None return None
def takeoff_time(self) -> datetime:
return self.patrol_start_time - self._travel_time_to_waypoint(self.tot_waypoint)
@property @property
def package_speed_waypoints(self) -> set[FlightWaypoint]: def package_speed_waypoints(self) -> set[FlightWaypoint]:
return {self.layout.patrol_start, self.layout.patrol_end} return {self.layout.patrol_start, self.layout.patrol_end}

View File

@@ -0,0 +1,112 @@
from __future__ import annotations
import random
from collections.abc import Iterator
from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING, Type
from game.utils import feet
from .ferry import FerryLayout
from .ibuilder import IBuilder
from .planningerror import PlanningError
from .standard import StandardFlightPlan, StandardLayout
from .waypointbuilder import WaypointBuilder
from ...theater import OffMapSpawn
if TYPE_CHECKING:
from ..flightwaypoint import FlightWaypoint
PRETENSE_CARGO_FLIGHT_DISTANCE = 100000
PRETENSE_CARGO_FLIGHT_HEADING_RANGE = 20
class PretenseCargoFlightPlan(StandardFlightPlan[FerryLayout]):
@staticmethod
def builder_type() -> Type[Builder]:
return Builder
@property
def tot_waypoint(self) -> FlightWaypoint:
return self.layout.arrival
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
# TOT planning isn't really useful for ferries. They're behind the front
# lines so no need to wait for escorts or for other missions to complete.
return None
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> datetime | None:
return None
@property
def mission_begin_on_station_time(self) -> datetime | None:
return None
@property
def mission_departure_time(self) -> datetime:
return self.package.time_over_target
class Builder(IBuilder[PretenseCargoFlightPlan, FerryLayout]):
def layout(self) -> FerryLayout:
# Find the spawn location for off-map transport planes
distance_to_flot = 0.0
heading_from_flot = 0.0
offmap_transport_cp_id = self.flight.departure.id
for front_line_cp in self.coalition.game.theater.controlpoints:
if isinstance(front_line_cp, OffMapSpawn):
continue
for front_line in self.coalition.game.theater.conflicts():
if front_line_cp.captured == self.flight.coalition.player:
if (
front_line_cp.position.distance_to_point(front_line.position)
> distance_to_flot
):
distance_to_flot = front_line_cp.position.distance_to_point(
front_line.position
)
heading_from_flot = front_line.position.heading_between_point(
front_line_cp.position
)
offmap_transport_cp_id = front_line_cp.id
offmap_transport_cp = self.coalition.game.theater.find_control_point_by_id(
offmap_transport_cp_id
)
offmap_heading = random.randrange(
int(heading_from_flot - PRETENSE_CARGO_FLIGHT_HEADING_RANGE),
int(heading_from_flot + PRETENSE_CARGO_FLIGHT_HEADING_RANGE),
)
offmap_transport_spawn = offmap_transport_cp.position.point_from_heading(
offmap_heading, PRETENSE_CARGO_FLIGHT_DISTANCE
)
altitude_is_agl = self.flight.is_helo
altitude = (
feet(self.coalition.game.settings.heli_cruise_alt_agl)
if altitude_is_agl
else self.flight.unit_type.preferred_patrol_altitude
)
builder = WaypointBuilder(self.flight)
ferry_layout = FerryLayout(
departure=builder.join(offmap_transport_spawn),
nav_to=builder.nav_path(
offmap_transport_spawn,
self.flight.arrival.position,
altitude,
altitude_is_agl,
),
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
nav_from=[],
custom_waypoints=list(),
)
ferry_layout.departure = builder.join(offmap_transport_spawn)
ferry_layout.nav_to.append(builder.join(offmap_transport_spawn))
ferry_layout.nav_from.append(builder.join(offmap_transport_spawn))
return ferry_layout
def build(self, dump_debug_info: bool = False) -> PretenseCargoFlightPlan:
return PretenseCargoFlightPlan(self.flight, self.layout())

View File

@@ -1,10 +1,15 @@
from abc import ABC from abc import ABC
from datetime import timedelta
from game.utils import Distance, Speed, knots, meters from game.utils import Speed, knots, Distance, meters
from .patrolling import PatrollingFlightPlan, PatrollingLayout from .patrolling import PatrollingFlightPlan, PatrollingLayout
class RefuelingFlightPlan(PatrollingFlightPlan[PatrollingLayout], ABC): class RefuelingFlightPlan(PatrollingFlightPlan[PatrollingLayout], ABC):
@property
def patrol_duration(self) -> timedelta:
return self.flight.coalition.game.settings.desired_tanker_on_station_time
@property @property
def patrol_speed(self) -> Speed: def patrol_speed(self) -> Speed:
# TODO: Could use self.flight.unit_type.preferred_patrol_speed(altitude). # TODO: Could use self.flight.unit_type.preferred_patrol_speed(altitude).

View File

@@ -8,10 +8,12 @@ from .formationattack import (
FormationAttackFlightPlan, FormationAttackFlightPlan,
FormationAttackLayout, FormationAttackLayout,
) )
from .uizonedisplay import UiZoneDisplay, UiZone
from ..flightwaypointtype import FlightWaypointType from ..flightwaypointtype import FlightWaypointType
from ...utils import nautical_miles
class SeadSweepFlightPlan(FormationAttackFlightPlan): class SeadSweepFlightPlan(FormationAttackFlightPlan, UiZoneDisplay):
@staticmethod @staticmethod
def builder_type() -> Type[Builder]: def builder_type() -> Type[Builder]:
return Builder return Builder
@@ -19,6 +21,14 @@ class SeadSweepFlightPlan(FormationAttackFlightPlan):
def default_tot_offset(self) -> timedelta: def default_tot_offset(self) -> timedelta:
return -timedelta(minutes=2) return -timedelta(minutes=2)
def ui_zone(self) -> UiZone:
return UiZone(
[self.tot_waypoint.position],
nautical_miles(
self.flight.coalition.game.settings.sead_sweep_engagement_range_distance
),
)
class Builder(FormationAttackBuilder[SeadSweepFlightPlan, FormationAttackLayout]): class Builder(FormationAttackBuilder[SeadSweepFlightPlan, FormationAttackLayout]):
def layout(self) -> FormationAttackLayout: def layout(self) -> FormationAttackLayout:

View File

@@ -0,0 +1,56 @@
from __future__ import annotations
from typing import Type
from game.ato.flightplans.ibuilder import IBuilder
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from .patrolling import PatrollingLayout
from .refuelingflightplan import RefuelingFlightPlan
from .. import FlightWaypoint
from ...utils import knots
class RecoveryTankerFlightPlan(RefuelingFlightPlan):
@staticmethod
def builder_type() -> Type[Builder]:
return Builder
@property
def tot_waypoint(self) -> FlightWaypoint:
return self.layout.departure
class Builder(IBuilder[RecoveryTankerFlightPlan, PatrollingLayout]):
def layout(self) -> PatrollingLayout:
builder = WaypointBuilder(self.flight)
altitude = builder.get_patrol_altitude
station_time = self.coalition.game.settings.desired_tanker_on_station_time
time_to_landing = station_time.total_seconds()
hdg = (self.coalition.game.conditions.weather.wind.at_0m.direction + 180) % 360
recovery_ship = self.package.target.position.point_from_heading(
hdg, time_to_landing * knots(20).meters_per_second
)
recovery_tanker = builder.recovery_tanker(recovery_ship)
patrol_end = builder.race_track_end(recovery_tanker.position, altitude)
patrol_end.only_for_player = True # avoid generating the waypoints
return PatrollingLayout(
departure=builder.takeoff(self.flight.departure),
nav_to=builder.nav_path(
self.flight.departure.position, recovery_ship, altitude
),
nav_from=builder.nav_path(
recovery_ship, self.flight.arrival.position, altitude
),
patrol_start=recovery_tanker,
patrol_end=patrol_end,
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
custom_waypoints=list(),
)
def build(self, dump_debug_info: bool = False) -> RecoveryTankerFlightPlan:
return RecoveryTankerFlightPlan(self.flight, self.layout())

View File

@@ -3,6 +3,7 @@ from __future__ import annotations
from abc import ABC from abc import ABC
from copy import deepcopy from copy import deepcopy
from dataclasses import dataclass from dataclasses import dataclass
from datetime import datetime
from typing import TYPE_CHECKING, TypeVar, Optional from typing import TYPE_CHECKING, TypeVar, Optional
from game.ato.flightplans.flightplan import FlightPlan, Layout from game.ato.flightplans.flightplan import FlightPlan, Layout
@@ -86,3 +87,10 @@ class StandardFlightPlan(FlightPlan[LayoutT], ABC):
others are guaranteed to have certain properties like departure and arrival points, others are guaranteed to have certain properties like departure and arrival points,
potentially a divert field, and a bullseye potentially a divert field, and a bullseye
""" """
@property
def landing_time(self) -> datetime:
return_time = self.total_time_between_waypoints(
self.tot_waypoint, self.layout.arrival
)
return self.mission_departure_time + return_time

View File

@@ -1,6 +1,5 @@
from __future__ import annotations from __future__ import annotations
from datetime import timedelta
from typing import Type from typing import Type
from game.utils import Heading, meters, nautical_miles from game.utils import Heading, meters, nautical_miles
@@ -15,10 +14,6 @@ class TheaterRefuelingFlightPlan(RefuelingFlightPlan):
def builder_type() -> Type[Builder]: def builder_type() -> Type[Builder]:
return Builder return Builder
@property
def patrol_duration(self) -> timedelta:
return self.flight.coalition.game.settings.desired_tanker_on_station_time
class Builder(IBuilder[TheaterRefuelingFlightPlan, PatrollingLayout]): class Builder(IBuilder[TheaterRefuelingFlightPlan, PatrollingLayout]):
def layout(self) -> PatrollingLayout: def layout(self) -> PatrollingLayout:

View File

@@ -14,5 +14,4 @@ class UiZone:
class UiZoneDisplay(abc.ABC): class UiZoneDisplay(abc.ABC):
@abc.abstractmethod @abc.abstractmethod
def ui_zone(self) -> UiZone: def ui_zone(self) -> UiZone: ...
...

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import math
import random import random
from dataclasses import dataclass from dataclasses import dataclass
from typing import ( from typing import (
@@ -17,6 +18,7 @@ from dcs.mapping import Point, Vector2
from game.ato.flightwaypoint import AltitudeReference, FlightWaypoint from game.ato.flightwaypoint import AltitudeReference, FlightWaypoint
from game.ato.flightwaypointtype import FlightWaypointType from game.ato.flightwaypointtype import FlightWaypointType
from game.data.weapons import WeaponType
from game.theater import ( from game.theater import (
ControlPoint, ControlPoint,
MissionTarget, MissionTarget,
@@ -24,13 +26,13 @@ from game.theater import (
TheaterGroundObject, TheaterGroundObject,
TheaterUnit, TheaterUnit,
) )
from game.theater.theatergroup import TheaterGroup
from game.utils import Distance, meters, nautical_miles, feet from game.utils import Distance, meters, nautical_miles, feet
AGL_TRANSITION_ALT = 5000 AGL_TRANSITION_ALT = 5000
if TYPE_CHECKING: if TYPE_CHECKING:
from game.transfers import MultiGroupTransport from game.transfers import MultiGroupTransport
from game.theater.theatergroup import TheaterGroup
from game.ato.flight import Flight from game.ato.flight import Flight
@@ -249,6 +251,13 @@ class WaypointBuilder:
objective: MissionTarget, objective: MissionTarget,
) -> FlightWaypoint: ) -> FlightWaypoint:
alt = self.get_combat_altitude alt = self.get_combat_altitude
if ingress_type in [
FlightWaypointType.INGRESS_CAS,
FlightWaypointType.INGRESS_OCA_AIRCRAFT,
FlightWaypointType.INGRESS_ARMED_RECON,
]:
alt = self._adjust_altitude_for_clouds(alt)
alt_type: AltitudeReference = "BARO" alt_type: AltitudeReference = "BARO"
if self.is_helo or self.flight.is_hercules: if self.is_helo or self.flight.is_hercules:
alt_type = "RADIO" alt_type = "RADIO"
@@ -265,9 +274,11 @@ class WaypointBuilder:
return FlightWaypoint( return FlightWaypoint(
"INGRESS", "INGRESS",
ingress_type, ingress_type,
objective.position.point_from_heading(heading, nautical_miles(5).meters) (
if self.is_helo objective.position.point_from_heading(heading, nautical_miles(5).meters)
else position, if self.is_helo
else position
),
alt, alt,
alt_type, alt_type,
description=f"INGRESS on {objective.name}", description=f"INGRESS on {objective.name}",
@@ -275,6 +286,19 @@ class WaypointBuilder:
targets=objective.strike_targets, targets=objective.strike_targets,
) )
def _adjust_altitude_for_clouds(self, alt: Distance) -> Distance:
weather = self.flight.coalition.game.conditions.weather
max_alt = feet(math.inf)
if weather.clouds and (
weather.clouds.preset
and "overcast" in weather.clouds.preset.description.lower()
or weather.clouds.density > 5
):
max_alt = meters(
max(feet(500).meters, weather.clouds.base - feet(500).meters)
)
return min(alt, max_alt)
def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint: def egress(self, position: Point, target: MissionTarget) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO" alt_type: AltitudeReference = "BARO"
if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT: if self.is_helo or self.get_combat_altitude.feet <= AGL_TRANSITION_ALT:
@@ -307,7 +331,11 @@ class WaypointBuilder:
return FlightWaypoint( return FlightWaypoint(
target.name, target.name,
FlightWaypointType.TARGET_POINT, FlightWaypointType.TARGET_POINT,
target.target.position, (
target.target.ground_object.position
if isinstance(target.target, TheaterGroup)
else target.target.position
),
meters(0), meters(0),
"RADIO", "RADIO",
description=description, description=description,
@@ -336,6 +364,21 @@ class WaypointBuilder:
def dead_area(self, target: MissionTarget) -> FlightWaypoint: def dead_area(self, target: MissionTarget) -> FlightWaypoint:
return self._target_area(f"DEAD on {target.name}", target) return self._target_area(f"DEAD on {target.name}", target)
def armed_recon_area(self, target: MissionTarget) -> FlightWaypoint:
# Force AI aircraft to fly towards target area
alt = self.get_combat_altitude
alt = self._adjust_altitude_for_clouds(alt)
alt_type: AltitudeReference = "BARO"
if self.is_helo or alt.feet <= AGL_TRANSITION_ALT:
alt_type = "RADIO"
return self._target_area(
f"ARMED RECON {target.name}",
target,
altitude=alt,
alt_type=alt_type,
flyover=True,
)
def oca_strike_area(self, target: MissionTarget) -> FlightWaypoint: def oca_strike_area(self, target: MissionTarget) -> FlightWaypoint:
return self._target_area(f"ATTACK {target.name}", target, flyover=True) return self._target_area(f"ATTACK {target.name}", target, flyover=True)
@@ -380,14 +423,25 @@ class WaypointBuilder:
waypoint.only_for_player = True waypoint.only_for_player = True
return waypoint return waypoint
def cas(self, position: Point) -> FlightWaypoint: def cas(self, position: Point, altitude: Distance) -> FlightWaypoint:
weather = self.flight.coalition.game.conditions.weather
if weather.clouds and (
weather.clouds.preset
and "overcast" in weather.clouds.preset.description.lower()
or weather.clouds.density > 5
):
altitude = meters(
max(feet(500).meters, weather.clouds.base - feet(500).meters)
)
return FlightWaypoint( return FlightWaypoint(
"CAS", "CAS",
FlightWaypointType.CAS, FlightWaypointType.CAS,
position, position,
feet(self.flight.coalition.game.settings.heli_combat_alt_agl) (
if self.is_helo feet(self.flight.coalition.game.settings.heli_combat_alt_agl)
else meters(1000), if self.is_helo
else max(meters(1000), altitude)
),
"RADIO", "RADIO",
description="Provide CAS", description="Provide CAS",
pretty_name="CAS", pretty_name="CAS",
@@ -505,8 +559,13 @@ class WaypointBuilder:
self.flight.coalition.game.settings.sead_threat_buffer_min_distance self.flight.coalition.game.settings.sead_threat_buffer_min_distance
).meters ).meters
if target.strike_targets: if target.strike_targets:
factor = (
0.8
if self.flight.any_member_has_weapon_of_type(WeaponType.ARM)
else 1.1
)
threat_range = ( threat_range = (
1.1 * max([x.threat_range for x in target.strike_targets]).meters factor * max([x.threat_range for x in target.strike_targets]).meters
) )
hdg = target.position.heading_between_point(ingress) hdg = target.position.heading_between_point(ingress)
hold = target.position.point_from_heading( hold = target.position.point_from_heading(
@@ -639,14 +698,13 @@ class WaypointBuilder:
This waypoint is used to generate the Trigger Zone used for AirAssault and This waypoint is used to generate the Trigger Zone used for AirAssault and
AirLift using the CTLD plugin (see LogisticsGenerator) AirLift using the CTLD plugin (see LogisticsGenerator)
""" """
heli_alt = feet(self.flight.coalition.game.settings.heli_cruise_alt_agl) alt = self.get_combat_altitude if self.flight.is_helo else meters(0)
altitude = heli_alt if self.flight.is_helo else meters(0)
return FlightWaypoint( return FlightWaypoint(
"DROPOFFZONE", "DROPOFFZONE",
FlightWaypointType.DROPOFF_ZONE, FlightWaypointType.DROPOFF_ZONE,
drop_off.position, drop_off.position,
altitude, alt,
"RADIO", "RADIO",
description=f"Drop off cargo at {drop_off.name}", description=f"Drop off cargo at {drop_off.name}",
pretty_name="Drop-off zone", pretty_name="Drop-off zone",
@@ -744,8 +802,22 @@ class WaypointBuilder:
return previous_threatened and next_threatened return previous_threatened and next_threatened
@staticmethod @staticmethod
def perturb(point: Point) -> Point: def perturb(point: Point, deviation: Distance = nautical_miles(1)) -> Point:
deviation = nautical_miles(1)
x_adj = random.randint(int(-deviation.meters), int(deviation.meters)) x_adj = random.randint(int(-deviation.meters), int(deviation.meters))
y_adj = random.randint(int(-deviation.meters), int(deviation.meters)) y_adj = random.randint(int(-deviation.meters), int(deviation.meters))
return point + Vector2(x_adj, y_adj) return point + Vector2(x_adj, y_adj)
@staticmethod
def recovery_tanker(position: Point) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
return FlightWaypoint(
"RECOVERY",
FlightWaypointType.RECOVERY_TANKER,
position,
feet(6000),
alt_type,
description="Recovery tanker for aircraft carriers",
pretty_name="Recovery",
only_for_player=True, # for visual purposes in Retribution only
)

View File

@@ -22,6 +22,10 @@ class FlightRoster(IFlightRoster):
def iter_pilots(self) -> Iterator[Pilot | None]: def iter_pilots(self) -> Iterator[Pilot | None]:
yield from self.pilots yield from self.pilots
@property
def player_count(self) -> int:
return len([p for p in self.pilots if p is not None and p.player])
def pilot_at(self, idx: int) -> Pilot | None: def pilot_at(self, idx: int) -> Pilot | None:
return self.pilots[idx] return self.pilots[idx]

View File

@@ -60,14 +60,12 @@ class FlightState(ABC):
@property @property
@abstractmethod @abstractmethod
def cancelable(self) -> bool: def cancelable(self) -> bool: ...
...
@abstractmethod @abstractmethod
def on_game_tick( def on_game_tick(
self, events: GameUpdateEvents, time: datetime, duration: timedelta self, events: GameUpdateEvents, time: datetime, duration: timedelta
) -> None: ) -> None: ...
...
@property @property
def in_flight(self) -> bool: def in_flight(self) -> bool:
@@ -98,17 +96,14 @@ class FlightState(ABC):
@property @property
@abstractmethod @abstractmethod
def is_waiting_for_start(self) -> bool: def is_waiting_for_start(self) -> bool: ...
...
@abstractmethod @abstractmethod
def estimate_position(self) -> Point: def estimate_position(self) -> Point: ...
...
@property @property
@abstractmethod @abstractmethod
def spawn_type(self) -> StartType: def spawn_type(self) -> StartType: ...
...
def a2a_commit_region(self) -> Optional[ThreatPoly]: def a2a_commit_region(self) -> Optional[ThreatPoly]:
return None return None

View File

@@ -57,16 +57,13 @@ class InFlight(FlightState, ABC):
) )
@abstractmethod @abstractmethod
def estimate_position(self) -> Point: def estimate_position(self) -> Point: ...
...
@abstractmethod @abstractmethod
def estimate_altitude(self) -> tuple[Distance, str]: def estimate_altitude(self) -> tuple[Distance, str]: ...
...
@abstractmethod @abstractmethod
def estimate_speed(self) -> Speed: def estimate_speed(self) -> Speed: ...
...
def estimate_fuel_at_current_waypoint(self) -> float: def estimate_fuel_at_current_waypoint(self) -> float:
initial_fuel = super().estimate_fuel() initial_fuel = super().estimate_fuel()

View File

@@ -51,7 +51,7 @@ class Takeoff(AtDeparture):
if ( if (
self.flight.client_count > 0 self.flight.client_count > 0
and self.settings.player_mission_interrupts_sim_at and self.settings.player_mission_interrupts_sim_at
is [StartType.COLD, StartType.WARM, StartType.RUNWAY] in [StartType.COLD, StartType.WARM, StartType.RUNWAY]
): ):
logging.info( logging.info(
f"Interrupting simulation because {self.flight} has players and has " f"Interrupting simulation because {self.flight} has players and has "

View File

@@ -25,7 +25,7 @@ class Uninitialized(FlightState):
@property @property
def is_waiting_for_start(self) -> bool: def is_waiting_for_start(self) -> bool:
raise RuntimeError("Attempted to simulate flight that is not fully initialized") return True
def estimate_position(self) -> Point: def estimate_position(self) -> Point:
raise RuntimeError("Attempted to simulate flight that is not fully initialized") raise RuntimeError("Attempted to simulate flight that is not fully initialized")

View File

@@ -58,6 +58,9 @@ class FlightType(Enum):
FERRY = "Ferry" FERRY = "Ferry"
AIR_ASSAULT = "Air Assault" AIR_ASSAULT = "Air Assault"
SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD SEAD_SWEEP = "SEAD Sweep" # Reintroduce legacy "engage-whatever-you-can-find" SEAD
PRETENSE_CARGO = "Cargo Transport" # For Pretense campaign AI cargo planes
ARMED_RECON = "Armed Recon"
RECOVERY = "Recovery"
def __str__(self) -> str: def __str__(self) -> str:
return self.value return self.value
@@ -93,6 +96,7 @@ class FlightType(Enum):
FlightType.SEAD_ESCORT, FlightType.SEAD_ESCORT,
FlightType.AIR_ASSAULT, FlightType.AIR_ASSAULT,
FlightType.SEAD_SWEEP, FlightType.SEAD_SWEEP,
FlightType.ARMED_RECON,
} }
@property @property
@@ -104,6 +108,7 @@ class FlightType(Enum):
return { return {
FlightType.AEWC: AirEntity.AIRBORNE_EARLY_WARNING, FlightType.AEWC: AirEntity.AIRBORNE_EARLY_WARNING,
FlightType.ANTISHIP: AirEntity.ANTISURFACE_WARFARE, FlightType.ANTISHIP: AirEntity.ANTISURFACE_WARFARE,
FlightType.ARMED_RECON: AirEntity.ATTACK_STRIKE,
FlightType.BAI: AirEntity.ATTACK_STRIKE, FlightType.BAI: AirEntity.ATTACK_STRIKE,
FlightType.BARCAP: AirEntity.FIGHTER, FlightType.BARCAP: AirEntity.FIGHTER,
FlightType.CAS: AirEntity.ATTACK_STRIKE, FlightType.CAS: AirEntity.ATTACK_STRIKE,
@@ -113,6 +118,7 @@ class FlightType(Enum):
FlightType.INTERCEPTION: AirEntity.FIGHTER, FlightType.INTERCEPTION: AirEntity.FIGHTER,
FlightType.OCA_AIRCRAFT: AirEntity.ATTACK_STRIKE, FlightType.OCA_AIRCRAFT: AirEntity.ATTACK_STRIKE,
FlightType.OCA_RUNWAY: AirEntity.ATTACK_STRIKE, FlightType.OCA_RUNWAY: AirEntity.ATTACK_STRIKE,
FlightType.RECOVERY: AirEntity.TANKER,
FlightType.REFUELING: AirEntity.TANKER, FlightType.REFUELING: AirEntity.TANKER,
FlightType.SEAD: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE, FlightType.SEAD: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE,
FlightType.SEAD_ESCORT: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE, FlightType.SEAD_ESCORT: AirEntity.SUPPRESSION_OF_ENEMY_AIR_DEFENCE,
@@ -121,5 +127,6 @@ class FlightType(Enum):
FlightType.SWEEP: AirEntity.FIGHTER, FlightType.SWEEP: AirEntity.FIGHTER,
FlightType.TARCAP: AirEntity.FIGHTER, FlightType.TARCAP: AirEntity.FIGHTER,
FlightType.TRANSPORT: AirEntity.UTILITY, FlightType.TRANSPORT: AirEntity.UTILITY,
FlightType.PRETENSE_CARGO: AirEntity.UTILITY,
FlightType.AIR_ASSAULT: AirEntity.ROTARY_WING, FlightType.AIR_ASSAULT: AirEntity.ROTARY_WING,
}.get(self, AirEntity.UNSPECIFIED) }.get(self, AirEntity.UNSPECIFIED)

View File

@@ -1,8 +1,9 @@
from __future__ import annotations from __future__ import annotations
from copy import deepcopy
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import datetime from datetime import datetime
from typing import Literal, TYPE_CHECKING from typing import Literal, TYPE_CHECKING, Any, Dict, Optional
from dcs import Point from dcs import Point
@@ -56,3 +57,17 @@ class FlightWaypoint:
def __hash__(self) -> int: def __hash__(self) -> int:
return hash(id(self)) return hash(id(self))
def __deepcopy__(self, memo: Optional[Dict[int, Any]] = None) -> FlightWaypoint:
obj = FlightWaypoint(self.name, self.waypoint_type, self.position)
for attr in dir(self):
if attr == "control_point":
obj.control_point = self.control_point
elif attr == "targets":
obj.targets = self.targets
elif "__" in attr or attr not in obj.__dataclass_fields__:
continue
else:
attr_copy = deepcopy(getattr(self, attr))
setattr(obj, attr, attr_copy)
return obj

View File

@@ -51,3 +51,5 @@ class FlightWaypointType(IntEnum):
INGRESS_AIR_ASSAULT = 31 INGRESS_AIR_ASSAULT = 31
INGRESS_ANTI_SHIP = 32 INGRESS_ANTI_SHIP = 32
INGRESS_SEAD_SWEEP = 33 INGRESS_SEAD_SWEEP = 33
INGRESS_ARMED_RECON = 34
RECOVERY_TANKER = 35 # Tanker recovery point

View File

@@ -9,31 +9,28 @@ if TYPE_CHECKING:
class IFlightRoster(ABC): class IFlightRoster(ABC):
@abstractmethod @abstractmethod
def iter_pilots(self) -> Iterator[Pilot | None]: def iter_pilots(self) -> Iterator[Pilot | None]: ...
...
@abstractmethod @abstractmethod
def pilot_at(self, idx: int) -> Pilot | None: def pilot_at(self, idx: int) -> Pilot | None: ...
...
@property @property
@abstractmethod @abstractmethod
def squadron(self) -> Squadron: def squadron(self) -> Squadron: ...
...
@property @property
@abstractmethod @abstractmethod
def max_size(self) -> int: def max_size(self) -> int: ...
...
@property
@abstractmethod
def player_count(self) -> int: ...
@abstractmethod @abstractmethod
def resize(self, new_size: int) -> None: def resize(self, new_size: int) -> None: ...
...
@abstractmethod @abstractmethod
def set_pilot(self, index: int, pilot: Optional[Pilot]) -> None: def set_pilot(self, index: int, pilot: Optional[Pilot]) -> None: ...
...
@abstractmethod @abstractmethod
def clear(self) -> None: def clear(self) -> None: ...
...

View File

@@ -137,11 +137,15 @@ class Loadout:
continue continue
name = payload["name"] name = payload["name"]
pylons = payload["pylons"] pylons = payload["pylons"]
yield Loadout( try:
name, yield Loadout(
{p["num"]: Weapon.with_clsid(p["CLSID"]) for p in pylons.values()}, name,
date=None, {p["num"]: Weapon.with_clsid(p["CLSID"]) for p in pylons.values()},
) date=None,
)
except KeyError:
# invalid loadout
continue
@staticmethod @staticmethod
def valid_payload(pylons: Dict[int, Dict[str, str]]) -> bool: def valid_payload(pylons: Dict[int, Dict[str, str]]) -> bool:
@@ -162,9 +166,11 @@ class Loadout:
# last - the first element in the tuple will be tried first, then the second, # last - the first element in the tuple will be tried first, then the second,
# etc. # etc.
loadout_names = { loadout_names = {
t: [f"Liberation {t.value}", f"Retribution {t.value}"] t: (
if prefer_liberation_payloads() [f"Liberation {t.value}", f"Retribution {t.value}"]
else [f"Retribution {t.value}", f"Liberation {t.value}"] if prefer_liberation_payloads()
else [f"Retribution {t.value}", f"Liberation {t.value}"]
)
for t in FlightType for t in FlightType
} }
legacy_names = { legacy_names = {
@@ -204,6 +210,7 @@ class Loadout:
loadout_names[FlightType.INTERCEPTION].extend(loadout_names[FlightType.BARCAP]) loadout_names[FlightType.INTERCEPTION].extend(loadout_names[FlightType.BARCAP])
# OCA/Aircraft falls back to BAI, which falls back to CAS. # OCA/Aircraft falls back to BAI, which falls back to CAS.
loadout_names[FlightType.BAI].extend(loadout_names[FlightType.CAS]) loadout_names[FlightType.BAI].extend(loadout_names[FlightType.CAS])
loadout_names[FlightType.ARMED_RECON].extend(loadout_names[FlightType.CAS])
loadout_names[FlightType.OCA_AIRCRAFT].extend(loadout_names[FlightType.BAI]) loadout_names[FlightType.OCA_AIRCRAFT].extend(loadout_names[FlightType.BAI])
# DEAD also falls back to BAI. # DEAD also falls back to BAI.
loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI]) loadout_names[FlightType.DEAD].extend(loadout_names[FlightType.BAI])

View File

@@ -54,8 +54,7 @@ class Package(RadioFrequencyContainer):
def has_players(self) -> bool: def has_players(self) -> bool:
return any(flight.client_count for flight in self.flights) return any(flight.client_count for flight in self.flights)
@property def formation_speed(self, is_helo: bool) -> Optional[Speed]:
def formation_speed(self) -> Optional[Speed]:
"""The speed of the package when in formation. """The speed of the package when in formation.
If none of the flights in the package will join a formation, this If none of the flights in the package will join a formation, this
@@ -66,7 +65,10 @@ class Package(RadioFrequencyContainer):
""" """
speeds = [] speeds = []
for flight in self.flights: for flight in self.flights:
if isinstance(flight.flight_plan, FormationFlightPlan): if (
isinstance(flight.flight_plan, FormationFlightPlan)
and flight.is_helo == is_helo
):
speeds.append(flight.flight_plan.best_flight_formation_speed) speeds.append(flight.flight_plan.best_flight_formation_speed)
if not speeds: if not speeds:
return None return None
@@ -179,12 +181,14 @@ class Package(RadioFrequencyContainer):
FlightType.DEAD, FlightType.DEAD,
FlightType.TRANSPORT, FlightType.TRANSPORT,
FlightType.AIR_ASSAULT, FlightType.AIR_ASSAULT,
FlightType.ARMED_RECON,
FlightType.SEAD, FlightType.SEAD,
FlightType.SEAD_SWEEP, FlightType.SEAD_SWEEP,
FlightType.TARCAP, FlightType.TARCAP,
FlightType.BARCAP, FlightType.BARCAP,
FlightType.AEWC, FlightType.AEWC,
FlightType.FERRY, FlightType.FERRY,
FlightType.RECOVERY,
FlightType.REFUELING, FlightType.REFUELING,
FlightType.SWEEP, FlightType.SWEEP,
FlightType.SEAD_ESCORT, FlightType.SEAD_ESCORT,
@@ -235,6 +239,14 @@ class Package(RadioFrequencyContainer):
clone.time_over_target = deepcopy(package.time_over_target) clone.time_over_target = deepcopy(package.time_over_target)
for f in package.flights: for f in package.flights:
cf = Flight.clone_flight(f) cf = Flight.clone_flight(f)
cf.flight_plan.layout = deepcopy(f.flight_plan.layout)
cf.package = clone cf.package = clone
clone.add_flight(cf) clone.add_flight(cf)
return clone return clone
def all_flights_waiting_for_start(self) -> bool:
"""Returns True if all flights in the package are waiting for start."""
for flight in self.flights:
if not flight.state.is_waiting_for_start:
return False
return True

View File

@@ -19,7 +19,7 @@ if TYPE_CHECKING:
from game.coalition import Coalition from game.coalition import Coalition
@dataclass(frozen=True) @dataclass
class PackageWaypoints: class PackageWaypoints:
join: Point join: Point
ingress: Point ingress: Point
@@ -38,7 +38,7 @@ class PackageWaypoints:
dcs_to_shapely_point(origin.position), dcs_to_shapely_point(origin.position),
dcs_to_shapely_point(package.target.position), dcs_to_shapely_point(package.target.position),
coalition.doctrine, coalition.doctrine,
coalition.opponent.threat_zone.all, coalition.opponent.threat_zone.air_defenses,
) )
ip_solver.set_debug_properties( ip_solver.set_debug_properties(
waypoint_debug_directory() / "IP", coalition.game.theater.terrain waypoint_debug_directory() / "IP", coalition.game.theater.terrain
@@ -51,10 +51,8 @@ class PackageWaypoints:
ingress_point_shapely.x, ingress_point_shapely.y ingress_point_shapely.x, ingress_point_shapely.y
) )
hdg = package.target.position.heading_between_point(ingress_point) tgt_point = package.target.position
# Generate a waypoint randomly between 7 & 9 NM initial_point = PackageWaypoints.get_initial_point(ingress_point, tgt_point)
dist = nautical_miles(random.random() * 2 + 7).meters
initial_point = package.target.position.point_from_heading(hdg, dist)
join_point = JoinZoneGeometry( join_point = JoinZoneGeometry(
package.target.position, package.target.position,
@@ -79,3 +77,11 @@ class PackageWaypoints:
WaypointBuilder.perturb(join_point), WaypointBuilder.perturb(join_point),
refuel_point, refuel_point,
) )
@staticmethod
def get_initial_point(ingress_point: Point, tgt_point: Point) -> Point:
hdg = tgt_point.heading_between_point(ingress_point)
# Generate a waypoint randomly between 7 & 9 NM
dist = nautical_miles(random.random() * 2 + 7).meters
initial_point = tgt_point.point_from_heading(hdg, dist)
return initial_point

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from game.utils import Distance, SPEED_OF_SOUND_AT_SEA_LEVEL, Speed, mach, meters from game.utils import Distance, SPEED_OF_SOUND_AT_SEA_LEVEL, Speed, mach
if TYPE_CHECKING: if TYPE_CHECKING:
from .flight import Flight from .flight import Flight
@@ -30,8 +30,8 @@ class GroundSpeed:
# as it can at sea level. This probably isn't great assumption, but # as it can at sea level. This probably isn't great assumption, but
# might. be sufficient given the wiggle room. We can come up with # might. be sufficient given the wiggle room. We can come up with
# another heuristic if needed. # another heuristic if needed.
cruise_mach = max_speed.mach() * (0.60 if flight.is_helo else 0.85) cruise_mach = max_speed.mach() * (0.7 if flight.is_helo else 0.85)
return mach(cruise_mach, altitude if not flight.is_helo else meters(0)) return mach(cruise_mach, altitude)
# TODO: Most if not all of this should move into FlightPlan. # TODO: Most if not all of this should move into FlightPlan.

View File

@@ -1,4 +1,5 @@
"""Support for working with DCS group callsigns.""" """Support for working with DCS group callsigns."""
import logging import logging
import re import re
from typing import Any from typing import Any

View File

@@ -5,7 +5,7 @@ import logging
from collections.abc import Iterator from collections.abc import Iterator
from dataclasses import dataclass from dataclasses import dataclass
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Tuple from typing import Any, Dict, Tuple, Optional
import yaml import yaml
from packaging.version import Version from packaging.version import Version
@@ -19,8 +19,10 @@ from game.theater.iadsnetwork.iadsnetwork import IadsNetwork
from game.theater.theaterloader import TheaterLoader from game.theater.theaterloader import TheaterLoader
from game.version import CAMPAIGN_FORMAT_VERSION from game.version import CAMPAIGN_FORMAT_VERSION
from .campaignairwingconfig import CampaignAirWingConfig from .campaignairwingconfig import CampaignAirWingConfig
from .campaigncarrierconfig import CampaignCarrierConfig
from .campaigngroundconfig import TgoConfig from .campaigngroundconfig import TgoConfig
from .mizcampaignloader import MizCampaignLoader from .mizcampaignloader import MizCampaignLoader
from ..factions import FACTIONS, Faction
PERF_FRIENDLY = 0 PERF_FRIENDLY = 0
PERF_MEDIUM = 1 PERF_MEDIUM = 1
@@ -90,6 +92,16 @@ class Campaign:
f"Invalid value for recommended_start_date in {path}: {start_date_raw}" f"Invalid value for recommended_start_date in {path}: {start_date_raw}"
) )
player_faction = data.get("recommended_player_faction", "USA 2005")
if isinstance(player_faction, dict):
faction_name = cls.register_faction(campaign_file.name, player_faction)
player_faction = faction_name if faction_name else "USA 2005"
enemy_faction = data.get("recommended_enemy_faction", "Russia 1990")
if isinstance(enemy_faction, dict):
faction_name = cls.register_faction(campaign_file.name, enemy_faction)
enemy_faction = faction_name if faction_name else "Russia 1990"
return cls( return cls(
data["name"], data["name"],
TheaterLoader(data["theater"].lower()).menu_thumbnail_dcs_relative_path, TheaterLoader(data["theater"].lower()).menu_thumbnail_dcs_relative_path,
@@ -97,8 +109,8 @@ class Campaign:
data.get("authors", "???"), data.get("authors", "???"),
data.get("description", ""), data.get("description", ""),
(version.major, version.minor), (version.major, version.minor),
data.get("recommended_player_faction", "USA 2005"), player_faction,
data.get("recommended_enemy_faction", "Russia 1990"), enemy_faction,
start_date, start_date,
start_time, start_time,
data.get("recommended_player_money", DEFAULT_BUDGET), data.get("recommended_player_money", DEFAULT_BUDGET),
@@ -112,6 +124,19 @@ class Campaign:
data.get("settings", {}), data.get("settings", {}),
) )
@classmethod
def register_faction(
cls, filename: str, player_faction: dict[str, Any]
) -> Optional[str]:
try:
f = Faction.from_dict(player_faction)
FACTIONS.factions[f.name] = f
logging.info(f"Loaded faction from campaign: {filename}")
return f.name
except Exception:
logging.exception(f"Unable to load faction from campaign: {filename}")
return None
def load_theater(self, advanced_iads: bool) -> ConflictTheater: def load_theater(self, advanced_iads: bool) -> ConflictTheater:
t = TheaterLoader(self.data["theater"].lower()).load() t = TheaterLoader(self.data["theater"].lower()).load()
@@ -140,6 +165,13 @@ class Campaign:
return CampaignAirWingConfig({}) return CampaignAirWingConfig({})
return CampaignAirWingConfig.from_campaign_data(squadron_data, theater) return CampaignAirWingConfig.from_campaign_data(squadron_data, theater)
def load_carrier_config(self) -> CampaignCarrierConfig:
try:
carrier_data = self.data["carriers"]
except KeyError:
return CampaignCarrierConfig({})
return CampaignCarrierConfig.from_campaign_data(carrier_data)
def load_ground_forces_config(self) -> TgoConfig: def load_ground_forces_config(self) -> TgoConfig:
ground_forces = self.data.get("ground_forces", {}) ground_forces = self.data.get("ground_forces", {})
if not ground_forces: if not ground_forces:

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import logging
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any, Optional, TYPE_CHECKING, Union from typing import Any, Optional, TYPE_CHECKING, Union
@@ -24,6 +25,7 @@ class SquadronConfig:
name: Optional[str] name: Optional[str]
nickname: Optional[str] nickname: Optional[str]
female_pilot_percentage: Optional[int] female_pilot_percentage: Optional[int]
aircraft_type: Optional[str]
@property @property
def auto_assignable(self) -> set[FlightType]: def auto_assignable(self) -> set[FlightType]:
@@ -49,6 +51,7 @@ class SquadronConfig:
data.get("name", None), data.get("name", None),
data.get("nickname", None), data.get("nickname", None),
data.get("female_pilot_percentage", None), data.get("female_pilot_percentage", None),
data.get("aircraft_type", None),
) )
@staticmethod @staticmethod
@@ -71,13 +74,30 @@ class CampaignAirWingConfig:
cls, data: dict[Union[str, int], Any], theater: ConflictTheater cls, data: dict[Union[str, int], Any], theater: ConflictTheater
) -> CampaignAirWingConfig: ) -> CampaignAirWingConfig:
by_location: dict[ControlPoint, list[SquadronConfig]] = defaultdict(list) by_location: dict[ControlPoint, list[SquadronConfig]] = defaultdict(list)
carriers = theater.find_carriers()
lhas = theater.find_lhas()
for base_id, squadron_configs in data.items(): for base_id, squadron_configs in data.items():
base: Optional[ControlPoint] = None
if isinstance(base_id, int): if isinstance(base_id, int):
base = theater.find_control_point_by_airport_id(base_id) base = theater.find_control_point_by_airport_id(base_id)
else: else:
base = theater.control_point_named(base_id) try:
base = theater.control_point_named(base_id)
except:
logging.warning(
f"Control point {base_id} not found, trying to match by full name"
)
if not base:
try:
base = theater.control_point_by_full_name(base_id)
except KeyError:
logging.error(f"Control point {base_id} not found, skipping")
for squadron_data in squadron_configs: for squadron_data in squadron_configs:
by_location[base].append(SquadronConfig.from_data(squadron_data)) if base is None:
logging.warning(
f"Skipping squadron config for unknown base: {base_id}"
)
else:
by_location[base].append(SquadronConfig.from_data(squadron_data))
return CampaignAirWingConfig(by_location) return CampaignAirWingConfig(by_location)

View File

@@ -0,0 +1,42 @@
from __future__ import annotations
import logging
from collections import defaultdict
from dataclasses import dataclass
from typing import Any, TYPE_CHECKING
from game.dcs.shipunittype import ShipUnitType
if TYPE_CHECKING:
pass
@dataclass(frozen=True)
class CarrierConfig:
preferred_name: str
preferred_type: ShipUnitType
@classmethod
def from_data(cls, data: dict[str, Any]) -> CarrierConfig:
return CarrierConfig(
str(data["preferred_name"]), ShipUnitType.named(data["preferred_type"])
)
@dataclass(frozen=True)
class CampaignCarrierConfig:
by_original_name: dict[str, CarrierConfig]
@classmethod
def from_campaign_data(cls, data: dict[str, Any]) -> CampaignCarrierConfig:
by_original_name: dict[str, CarrierConfig] = defaultdict()
for original_name, carrier_config_data in data.items():
try:
carrier_config = CarrierConfig.from_data(carrier_config_data)
by_original_name[original_name] = carrier_config
except KeyError:
logging.warning(
f"Skipping invalid carrier config for '{original_name}'"
)
return CampaignCarrierConfig(by_original_name)

View File

@@ -1,6 +1,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
import random
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from game.squadrons import Squadron from game.squadrons import Squadron
@@ -59,7 +60,7 @@ class DefaultSquadronAssigner:
) -> Optional[SquadronDef]: ) -> Optional[SquadronDef]:
for preferred_aircraft in config.aircraft: for preferred_aircraft in config.aircraft:
squadron_def = self.find_preferred_squadron( squadron_def = self.find_preferred_squadron(
preferred_aircraft, config.primary, control_point preferred_aircraft, config.aircraft_type, config.primary, control_point
) )
if squadron_def is not None: if squadron_def is not None:
return squadron_def return squadron_def
@@ -77,7 +78,11 @@ class DefaultSquadronAssigner:
) )
def find_preferred_squadron( def find_preferred_squadron(
self, preferred_aircraft: str, task: FlightType, control_point: ControlPoint self,
preferred_aircraft: str,
aircraft_type: Optional[str],
task: FlightType,
control_point: ControlPoint,
) -> Optional[SquadronDef]: ) -> Optional[SquadronDef]:
# Attempt to find a squadron with the name in the request. # Attempt to find a squadron with the name in the request.
squadron_def = self.find_squadron_by_name( squadron_def = self.find_squadron_by_name(
@@ -89,7 +94,7 @@ class DefaultSquadronAssigner:
# If the name didn't match a squadron available to this coalition, try to find # If the name didn't match a squadron available to this coalition, try to find
# an aircraft with the matching name that meets the requirements. # an aircraft with the matching name that meets the requirements.
try: try:
aircraft = AircraftType.named(preferred_aircraft) aircraft = AircraftType.named(aircraft_type or preferred_aircraft)
except KeyError: except KeyError:
logging.warning( logging.warning(
"%s is neither a compatible squadron or a known aircraft type, " "%s is neither a compatible squadron or a known aircraft type, "
@@ -101,9 +106,10 @@ class DefaultSquadronAssigner:
if aircraft not in self.coalition.faction.all_aircrafts: if aircraft not in self.coalition.faction.all_aircrafts:
return None return None
lo = self.coalition.faction.liveries_overrides
squadron_def = self.find_squadron_for_airframe(aircraft, task, control_point) squadron_def = self.find_squadron_for_airframe(aircraft, task, control_point)
if squadron_def is not None and lo.get(aircraft) is None: if squadron_def is not None and (
squadron_def.livery is not None or squadron_def.livery_set is not None
):
return squadron_def return squadron_def
# No premade squadron available for this aircraft that meets the requirements, # No premade squadron available for this aircraft that meets the requirements,
@@ -124,11 +130,14 @@ class DefaultSquadronAssigner:
def find_squadron_for_airframe( def find_squadron_for_airframe(
self, aircraft: AircraftType, task: FlightType, control_point: ControlPoint self, aircraft: AircraftType, task: FlightType, control_point: ControlPoint
) -> Optional[SquadronDef]: ) -> Optional[SquadronDef]:
choices = []
for squadron in self.air_wing.squadron_defs[aircraft]: for squadron in self.air_wing.squadron_defs[aircraft]:
if not squadron.claimed and self.squadron_compatible_with( if not squadron.claimed and self.squadron_compatible_with(
squadron, task, control_point squadron, task, control_point
): ):
return squadron choices.append(squadron)
if choices:
return random.choice(choices)
return None return None
def find_squadron_by_name( def find_squadron_by_name(

View File

@@ -9,13 +9,14 @@ from uuid import UUID
from dcs import Mission from dcs import Mission
from dcs.countries import CombinedJointTaskForcesBlue, CombinedJointTaskForcesRed from dcs.countries import CombinedJointTaskForcesBlue, CombinedJointTaskForcesRed
from dcs.country import Country from dcs.country import Country
from dcs.planes import F_15C, A_10A, AJS37 from dcs.planes import F_15C, A_10A, AJS37, C_130
from dcs.ships import HandyWind, LHA_Tarawa, Stennis, USS_Arleigh_Burke_IIa from dcs.ships import HandyWind, LHA_Tarawa, Stennis, USS_Arleigh_Burke_IIa
from dcs.statics import Fortification, Warehouse from dcs.statics import Fortification, Warehouse
from dcs.terrain import Airport from dcs.terrain import Airport
from dcs.unitgroup import PlaneGroup, ShipGroup, StaticGroup, VehicleGroup from dcs.unitgroup import PlaneGroup, ShipGroup, StaticGroup, VehicleGroup
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
from game.controlpoint_influenceradius import ControlPointInfluenceRadius, point_in_zone
from game.point_with_heading import PointWithHeading from game.point_with_heading import PointWithHeading
from game.positioned import Positioned from game.positioned import Positioned
from game.profiling import logged_duration from game.profiling import logged_duration
@@ -24,6 +25,8 @@ from game.theater.controlpoint import (
Airfield, Airfield,
Carrier, Carrier,
ControlPoint, ControlPoint,
ControlPointType,
Player,
Fob, Fob,
Lha, Lha,
OffMapSpawn, OffMapSpawn,
@@ -43,6 +46,7 @@ class MizCampaignLoader:
OFF_MAP_UNIT_TYPE = F_15C.id OFF_MAP_UNIT_TYPE = F_15C.id
GROUND_SPAWN_UNIT_TYPE = A_10A.id GROUND_SPAWN_UNIT_TYPE = A_10A.id
GROUND_SPAWN_ROADBASE_UNIT_TYPE = AJS37.id GROUND_SPAWN_ROADBASE_UNIT_TYPE = AJS37.id
GROUND_SPAWN_LARGE_UNIT_TYPE = C_130.id
CV_UNIT_TYPE = Stennis.id CV_UNIT_TYPE = Stennis.id
LHA_UNIT_TYPE = LHA_Tarawa.id LHA_UNIT_TYPE = LHA_Tarawa.id
@@ -52,11 +56,13 @@ class MizCampaignLoader:
FOB_UNIT_TYPE = Unarmed.SKP_11.id FOB_UNIT_TYPE = Unarmed.SKP_11.id
FARP_HELIPADS_TYPE = ["Invisible FARP", "SINGLE_HELIPAD", "FARP"] FARP_HELIPADS_TYPE = ["Invisible FARP", "SINGLE_HELIPAD", "FARP"]
INVISIBLE_FOB_UNIT_TYPE = Unarmed.M_818.id
NEUTRAL_FOB_UNIT_TYPE = Unarmed.KrAZ6322.id
OFFSHORE_STRIKE_TARGET_UNIT_TYPE = Fortification.Oil_platform.id OFFSHORE_STRIKE_TARGET_UNIT_TYPE = Fortification.Oil_platform.id
SHIP_UNIT_TYPE = USS_Arleigh_Burke_IIa.id SHIP_UNIT_TYPE = USS_Arleigh_Burke_IIa.id
MISSILE_SITE_UNIT_TYPE = MissilesSS.Scud_B.id MISSILE_SITE_UNIT_TYPE = MissilesSS.Scud_B.id
COASTAL_DEFENSE_UNIT_TYPE = MissilesSS.Hy_launcher.id COASTAL_DEFENSE_UNIT_TYPE = MissilesSS.hy_launcher.id
COMMAND_CENTER_UNIT_TYPE = Fortification._Command_Center.id COMMAND_CENTER_UNIT_TYPE = Fortification._Command_Center.id
CONNECTION_NODE_UNIT_TYPE = Fortification.Comms_tower_M.id CONNECTION_NODE_UNIT_TYPE = Fortification.Comms_tower_M.id
@@ -73,23 +79,25 @@ class MizCampaignLoader:
MEDIUM_RANGE_SAM_UNIT_TYPES = { MEDIUM_RANGE_SAM_UNIT_TYPES = {
AirDefence.Hawk_ln.id, AirDefence.Hawk_ln.id,
AirDefence.S_75M_Volhov.id, AirDefence.S_75M_Volhov.id,
AirDefence.X_5p73_s_125_ln.id, AirDefence.x_5p73_s_125_ln.id,
AirDefence.NASAMS_LN_B.id,
AirDefence.NASAMS_LN_C.id,
} }
SHORT_RANGE_SAM_UNIT_TYPES = { SHORT_RANGE_SAM_UNIT_TYPES = {
AirDefence.M1097_Avenger.id, AirDefence.M1097_Avenger.id,
AirDefence.Rapier_fsa_launcher.id, AirDefence.rapier_fsa_launcher.id,
AirDefence.X_2S6_Tunguska.id, AirDefence.x_2S6_Tunguska.id,
AirDefence.Strela_1_9P31.id, AirDefence.Strela_1_9P31.id,
} }
AAA_UNIT_TYPES = { AAA_UNIT_TYPES = {
AirDefence.Flak18.id, AirDefence.flak18.id,
AirDefence.Vulcan.id, AirDefence.Vulcan.id,
AirDefence.ZSU_23_4_Shilka.id, AirDefence.ZSU_23_4_Shilka.id,
} }
EWR_UNIT_TYPE = AirDefence.X_1L13_EWR.id EWR_UNIT_TYPE = AirDefence.x_1L13_EWR.id
ARMOR_GROUP_UNIT_TYPE = Armor.M_1_Abrams.id ARMOR_GROUP_UNIT_TYPE = Armor.M_1_Abrams.id
@@ -117,9 +125,13 @@ class MizCampaignLoader:
def control_point_from_airport( def control_point_from_airport(
self, airport: Airport, ctld_zones: List[Tuple[Point, float]] self, airport: Airport, ctld_zones: List[Tuple[Point, float]]
) -> ControlPoint: ) -> ControlPoint:
cp = Airfield( if airport.dynamic_spawn:
airport, self.theater, starts_blue=airport.is_blue(), ctld_zones=ctld_zones starting_coalition = Player.NEUTRAL
) elif airport.is_blue():
starting_coalition = Player.BLUE
else:
starting_coalition = Player.RED
cp = Airfield(airport, self.theater, starting_coalition, ctld_zones=ctld_zones)
# Use the unlimited aircraft option to determine if an airfield should # Use the unlimited aircraft option to determine if an airfield should
# be owned by the player when the campaign is "inverted". # be owned by the player when the campaign is "inverted".
@@ -127,42 +139,54 @@ class MizCampaignLoader:
return cp return cp
def country(self, blue: bool) -> Country: def country(self, blue: Player) -> Country:
country = self.mission.country( if blue.is_blue:
self.BLUE_COUNTRY.name if blue else self.RED_COUNTRY.name country = self.mission.country(self.BLUE_COUNTRY.name)
) else:
country = self.mission.country(self.RED_COUNTRY.name)
# Should be guaranteed because we initialized them. # Should be guaranteed because we initialized them.
assert country assert country
return country return country
@property @property
def blue(self) -> Country: def blue(self) -> Country:
return self.country(blue=True) return self.country(blue=Player.BLUE)
@property @property
def red(self) -> Country: def red(self) -> Country:
return self.country(blue=False) return self.country(blue=Player.RED)
def off_map_spawns(self, blue: bool) -> Iterator[PlaneGroup]: def off_map_spawns(self, blue: Player) -> Iterator[PlaneGroup]:
for group in self.country(blue).plane_group: for group in self.country(blue).plane_group:
if group.units[0].type == self.OFF_MAP_UNIT_TYPE: if group.units[0].type == self.OFF_MAP_UNIT_TYPE:
yield group yield group
def carriers(self, blue: bool) -> Iterator[ShipGroup]: def carriers(self, blue: Player) -> Iterator[ShipGroup]:
for group in self.country(blue).ship_group: for group in self.country(blue).ship_group:
if group.units[0].type == self.CV_UNIT_TYPE: if group.units[0].type == self.CV_UNIT_TYPE:
yield group yield group
def lhas(self, blue: bool) -> Iterator[ShipGroup]: def lhas(self, blue: Player) -> Iterator[ShipGroup]:
for group in self.country(blue).ship_group: for group in self.country(blue).ship_group:
if group.units[0].type == self.LHA_UNIT_TYPE: if group.units[0].type == self.LHA_UNIT_TYPE:
yield group yield group
def fobs(self, blue: bool) -> Iterator[VehicleGroup]: def fobs(self, blue: Player) -> Iterator[VehicleGroup]:
for group in self.country(blue).vehicle_group: for group in self.country(blue).vehicle_group:
if group.units[0].type == self.FOB_UNIT_TYPE: if group.units[0].type == self.FOB_UNIT_TYPE:
yield group yield group
def invisible_fobs(self, blue: Player) -> Iterator[VehicleGroup]:
for group in self.country(blue).vehicle_group:
if group.units[0].type == self.INVISIBLE_FOB_UNIT_TYPE:
yield group
@property
def neutral_fobs(self) -> Iterator[VehicleGroup]:
for group in self.red.vehicle_group:
if group.units[0].type == self.NEUTRAL_FOB_UNIT_TYPE:
yield group
@property @property
def ships(self) -> Iterator[ShipGroup]: def ships(self) -> Iterator[ShipGroup]:
for group in self.red.ship_group: for group in self.red.ship_group:
@@ -235,6 +259,12 @@ class MizCampaignLoader:
if group.units[0].type == self.GROUND_SPAWN_ROADBASE_UNIT_TYPE: if group.units[0].type == self.GROUND_SPAWN_ROADBASE_UNIT_TYPE:
yield group yield group
@property
def ground_spawns_large(self) -> Iterator[PlaneGroup]:
for group in itertools.chain(self.blue.plane_group, self.red.plane_group):
if group.units[0].type == self.GROUND_SPAWN_LARGE_UNIT_TYPE:
yield group
@property @property
def ground_spawns(self) -> Iterator[PlaneGroup]: def ground_spawns(self) -> Iterator[PlaneGroup]:
for group in itertools.chain(self.blue.plane_group, self.red.plane_group): for group in itertools.chain(self.blue.plane_group, self.red.plane_group):
@@ -263,16 +293,22 @@ class MizCampaignLoader:
def scenery(self) -> List[SceneryGroup]: def scenery(self) -> List[SceneryGroup]:
return SceneryGroup.from_trigger_zones(self.mission.triggers._zones) return SceneryGroup.from_trigger_zones(self.mission.triggers._zones)
@cached_property
def cp_influence_zones(self) -> List[ControlPointInfluenceRadius]:
return ControlPointInfluenceRadius.from_trigger_zones(
self.mission.triggers._zones
)
@cached_property @cached_property
def control_points(self) -> dict[UUID, ControlPoint]: def control_points(self) -> dict[UUID, ControlPoint]:
control_points = {} control_points = {}
for airport in self.mission.terrain.airport_list(): for airport in self.mission.terrain.airport_list():
if airport.is_blue() or airport.is_red(): if airport.is_blue() or airport.is_red() or airport.is_neutral():
ctld_zones = self.get_ctld_zones(airport.name) ctld_zones = self.get_ctld_zones(airport.name)
control_point = self.control_point_from_airport(airport, ctld_zones) control_point = self.control_point_from_airport(airport, ctld_zones)
control_points[control_point.id] = control_point control_points[control_point.id] = control_point
for blue in (False, True): for blue in (Player.RED, Player.BLUE):
for group in self.off_map_spawns(blue): for group in self.off_map_spawns(blue):
control_point = OffMapSpawn( control_point = OffMapSpawn(
str(group.name), group.position, self.theater, starts_blue=blue str(group.name), group.position, self.theater, starts_blue=blue
@@ -303,17 +339,47 @@ class MizCampaignLoader:
control_point.captured_invert = fob.late_activation control_point.captured_invert = fob.late_activation
control_points[control_point.id] = control_point control_points[control_point.id] = control_point
for fob in self.invisible_fobs(blue):
ctld_zones = self.get_ctld_zones(fob.name)
control_point = Fob(
str(fob.name),
fob.position,
self.theater,
starts_blue=blue,
ctld_zones=ctld_zones,
is_invisible=True,
)
control_point.captured_invert = fob.late_activation
control_points[control_point.id] = control_point
for fob in self.neutral_fobs:
ctld_zones = self.get_ctld_zones(fob.name)
control_point = Fob(
str(fob.name),
fob.position,
self.theater,
starts_blue=Player.NEUTRAL,
ctld_zones=ctld_zones,
)
control_point.captured_invert = fob.late_activation
control_points[control_point.id] = control_point
if self.cp_influence_zones:
for cp in control_points.values():
for influence_radius in self.cp_influence_zones:
if cp.full_name == influence_radius.cp_name:
cp.influence_radius = influence_radius
return control_points return control_points
@property @property
def front_line_path_groups(self) -> Iterator[VehicleGroup]: def front_line_path_groups(self) -> Iterator[VehicleGroup]:
for group in self.country(blue=True).vehicle_group: for group in self.country(blue=Player.BLUE).vehicle_group:
if group.units[0].type == self.FRONT_LINE_UNIT_TYPE: if group.units[0].type == self.FRONT_LINE_UNIT_TYPE:
yield group yield group
@property @property
def shipping_lane_groups(self) -> Iterator[ShipGroup]: def shipping_lane_groups(self) -> Iterator[ShipGroup]:
for group in self.country(blue=True).ship_group: for group in self.country(blue=Player.BLUE).ship_group:
if group.units[0].type == self.SHIPPING_LANE_UNIT_TYPE: if group.units[0].type == self.SHIPPING_LANE_UNIT_TYPE:
yield group yield group
@@ -337,7 +403,7 @@ class MizCampaignLoader:
@property @property
def cp_convoy_spawns(self) -> Iterator[VehicleGroup]: def cp_convoy_spawns(self) -> Iterator[VehicleGroup]:
for group in self.country(blue=True).vehicle_group: for group in self.country(blue=Player.BLUE).vehicle_group:
if group.units[0].type == self.CP_CONVOY_SPAWN_TYPE: if group.units[0].type == self.CP_CONVOY_SPAWN_TYPE:
yield group yield group
@@ -462,7 +528,59 @@ class MizCampaignLoader:
def objective_info( def objective_info(
self, near: Positioned, allow_naval: bool = False self, near: Positioned, allow_naval: bool = False
) -> Tuple[ControlPoint, Distance]: ) -> Tuple[ControlPoint, Distance]:
closest = self.theater.closest_control_point(near.position, allow_naval) zones_containing_point = [
z
for z in self.cp_influence_zones
if point_in_zone(z.zone_def, near.position)
]
# Ensure we only consider naval control points if allow_naval is True
candidates = [
self.theater.control_point_named(z.cp_name) for z in zones_containing_point
]
if not allow_naval:
candidates = [
cp
for cp in candidates
if cp.cptype
not in [
ControlPointType.AIRCRAFT_CARRIER_GROUP,
ControlPointType.LHA_GROUP,
]
]
if candidates:
closest = min(
candidates, key=lambda cp: cp.position.distance_to_point(near.position)
)
distance = meters(closest.position.distance_to_point(near.position))
return closest, distance
# If no zones contain the point, find the closest control point without an influence radius
if not allow_naval:
fallback_candidates = [
cp
for cp in self.theater.controlpoints
if cp.cptype
not in [
ControlPointType.AIRCRAFT_CARRIER_GROUP,
ControlPointType.LHA_GROUP,
]
]
else:
fallback_candidates = self.theater.controlpoints
fallback_candidates = [
cp for cp in fallback_candidates if not cp.influence_radius
]
if not fallback_candidates:
raise RuntimeError(
f"All control points have an influence zone but no zones contain {near} at {near.position}"
)
closest = min(
fallback_candidates,
key=lambda cp: cp.position.distance_to_point(near.position),
)
distance = meters(closest.position.distance_to_point(near.position)) distance = meters(closest.position.distance_to_point(near.position))
return closest, distance return closest, distance
@@ -534,6 +652,10 @@ class MizCampaignLoader:
closest, distance = self.objective_info(plane_group) closest, distance = self.objective_info(plane_group)
self._add_ground_spawn(closest.ground_spawns_roadbase, plane_group) self._add_ground_spawn(closest.ground_spawns_roadbase, plane_group)
for plane_group in self.ground_spawns_large:
closest, distance = self.objective_info(plane_group)
self._add_ground_spawn(closest.ground_spawns_large, plane_group)
for plane_group in self.ground_spawns: for plane_group in self.ground_spawns:
closest, distance = self.objective_info(plane_group) closest, distance = self.objective_info(plane_group)
self._add_ground_spawn(closest.ground_spawns, plane_group) self._add_ground_spawn(closest.ground_spawns, plane_group)
@@ -582,9 +704,18 @@ class MizCampaignLoader:
self.add_preset_locations() self.add_preset_locations()
self.add_supply_routes() self.add_supply_routes()
self.add_shipping_lanes() self.add_shipping_lanes()
self.add_rebel_zones()
def get_ctld_zones(self, prefix: str) -> List[Tuple[Point, float]]: def get_ctld_zones(self, prefix: str) -> List[Tuple[Point, float]]:
zones = [t for t in self.mission.triggers.zones() if prefix + " CTLD" in t.name] zones = [t for t in self.mission.triggers.zones() if prefix + " CTLD" in t.name]
for z in zones: for z in zones:
self.mission.triggers.zones().remove(z) self.mission.triggers.zones().remove(z)
return [(z.position, z.radius) for z in zones] return [(z.position, z.radius) for z in zones]
def add_rebel_zones(self) -> None:
zones = [
t for t in self.mission.triggers.zones() if t.name.startswith("Rebels")
]
for z in zones:
self.mission.triggers.zones().remove(z)
self.theater.add_rebel_zones(zones)

View File

@@ -23,6 +23,8 @@ class SquadronDefGenerator:
def generate_for_task( def generate_for_task(
self, task: FlightType, control_point: ControlPoint self, task: FlightType, control_point: ControlPoint
) -> Optional[SquadronDef]: ) -> Optional[SquadronDef]:
settings = control_point.coalition.game.settings
squadron_random_chance = settings.squadron_random_chance
aircraft_choice: Optional[AircraftType] = None aircraft_choice: Optional[AircraftType] = None
for aircraft in AircraftType.priority_list_for_task(task): for aircraft in AircraftType.priority_list_for_task(task):
if aircraft not in self.faction.all_aircrafts: if aircraft not in self.faction.all_aircrafts:
@@ -30,9 +32,9 @@ class SquadronDefGenerator:
if not control_point.can_operate(aircraft): if not control_point.can_operate(aircraft):
continue continue
aircraft_choice = aircraft aircraft_choice = aircraft
# 50/50 chance to keep looking for an aircraft that isn't as far up the # squadron_random_chance percent chance to keep looking for an aircraft that isn't as far up the
# priority list to maintain some unit variety. # priority list to maintain some unit variety.
if random.choice([True, False]): if squadron_random_chance >= random.randint(1, 100):
break break
if aircraft_choice is None: if aircraft_choice is None:
@@ -47,6 +49,7 @@ class SquadronDefGenerator:
role="Flying Squadron", role="Flying Squadron",
aircraft=aircraft, aircraft=aircraft,
livery=None, livery=None,
livery_set=[],
auto_assignable_mission_types=set(aircraft.iter_task_capabilities()), auto_assignable_mission_types=set(aircraft.iter_task_capabilities()),
radio_presets={}, radio_presets={},
operating_bases=OperatingBases.default_for_aircraft(aircraft), operating_bases=OperatingBases.default_for_aircraft(aircraft),

View File

@@ -17,6 +17,7 @@ from game.procurement import AircraftProcurementRequest, ProcurementAi
from game.profiling import MultiEventTracer, logged_duration from game.profiling import MultiEventTracer, logged_duration
from game.squadrons import AirWing from game.squadrons import AirWing
from game.theater.bullseye import Bullseye from game.theater.bullseye import Bullseye
from game.theater.player import Player
from game.theater.transitnetwork import TransitNetwork, TransitNetworkBuilder from game.theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
from game.threatzones import ThreatZones from game.threatzones import ThreatZones
from game.transfers import PendingTransfers from game.transfers import PendingTransfers
@@ -32,7 +33,7 @@ if TYPE_CHECKING:
class Coalition: class Coalition:
def __init__( def __init__(
self, game: Game, faction: Faction, budget: float, player: bool self, game: Game, faction: Faction, budget: float, player: Player
) -> None: ) -> None:
self.game = game self.game = game
self.player = player self.player = player
@@ -68,9 +69,11 @@ class Coalition:
@property @property
def coalition_id(self) -> int: def coalition_id(self) -> int:
if self.player: if self.player.is_blue:
return 2 return 2
return 1 elif self.player.is_red:
return 1
return 0
@property @property
def opponent(self) -> Coalition: def opponent(self) -> Coalition:
@@ -95,12 +98,23 @@ class Coalition:
state = self.__dict__.copy() state = self.__dict__.copy()
# Avoid persisting any volatile types that can be deterministically # Avoid persisting any volatile types that can be deterministically
# recomputed on load for the sake of save compatibility. # recomputed on load for the sake of save compatibility.
del state["_threat_zone"]
del state["_navmesh"]
del state["faker"] del state["faker"]
# TODO: Figure out why this is needed after adding neutral point support
if state["player"] != Player.NEUTRAL:
del state["_threat_zone"]
del state["_navmesh"]
return state return state
def __setstate__(self, state: dict[str, Any]) -> None: def __setstate__(self, state: dict[str, Any]) -> None:
# Migration: Convert old boolean player values to Player enum
if "player" in state and isinstance(state["player"], bool):
from game.theater.player import Player
if state["player"]:
state["player"] = Player.BLUE
else:
state["player"] = Player.RED
self.__dict__.update(state) self.__dict__.update(state)
# Regenerate any state that was not persisted. # Regenerate any state that was not persisted.
self.on_load() self.on_load()
@@ -203,7 +217,7 @@ class Coalition:
squadron.refund_orders() squadron.refund_orders()
def plan_missions(self, now: datetime) -> None: def plan_missions(self, now: datetime) -> None:
color = "Blue" if self.player else "Red" color = "Blue" if self.player.is_blue else "Red"
with MultiEventTracer() as tracer: with MultiEventTracer() as tracer:
with tracer.trace(f"{color} mission planning"): with tracer.trace(f"{color} mission planning"):
with tracer.trace(f"{color} mission identification"): with tracer.trace(f"{color} mission identification"):
@@ -220,7 +234,7 @@ class Coalition:
# to ground forces and 1400 to aircraft. After that the budget will be spent # to ground forces and 1400 to aircraft. After that the budget will be spent
# proportionally based on how much is already invested. # proportionally based on how much is already invested.
if self.player: if self.player.is_blue:
manage_runways = self.game.settings.automate_runway_repair manage_runways = self.game.settings.automate_runway_repair
manage_front_line = self.game.settings.automate_front_line_reinforcements manage_front_line = self.game.settings.automate_front_line_reinforcements
manage_aircraft = self.game.settings.automate_aircraft_reinforcements manage_aircraft = self.game.settings.automate_aircraft_reinforcements

View File

@@ -8,10 +8,11 @@ from typing import Iterator, TYPE_CHECKING
from game.ato.flighttype import FlightType from game.ato.flighttype import FlightType
from game.ato.traveltime import TotEstimator from game.ato.traveltime import TotEstimator
from game.theater import MissionTarget from game.theater import MissionTarget, NavalControlPoint
if TYPE_CHECKING: if TYPE_CHECKING:
from game.coalition import Coalition from game.coalition import Coalition
from game.ato import Package
class MissionScheduler: class MissionScheduler:
@@ -40,6 +41,13 @@ class MissionScheduler:
p for p in self.coalition.ato.packages if p.primary_task not in dca_types p for p in self.coalition.ato.packages if p.primary_task not in dca_types
] ]
previous_aewc_end_time: dict[MissionTarget, datetime] = defaultdict(now.replace)
max_simultaneous_recovery_tankers = 2 # TODO: make configurable
carrier_etas: dict[MissionTarget, list[datetime]] = defaultdict(list)
max_carrier_simultaneous_barcaps = 2 # TODO: make configurable
carrier_barcaps: dict[MissionTarget, int] = defaultdict(int)
start_time = start_time_generator( start_time = start_time_generator(
count=len(non_dca_packages), count=len(non_dca_packages),
earliest=5 * 60, earliest=5 * 60,
@@ -47,6 +55,8 @@ class MissionScheduler:
margin=5 * 60, margin=5 * 60,
) )
for package in self.coalition.ato.packages: for package in self.coalition.ato.packages:
if package.primary_task is FlightType.RECOVERY:
continue
tot = TotEstimator(package).earliest_tot(now) tot = TotEstimator(package).earliest_tot(now)
if package.primary_task in dca_types: if package.primary_task in dca_types:
previous_end_time = previous_cap_end_time[package.target] previous_end_time = previous_cap_end_time[package.target]
@@ -58,14 +68,27 @@ class MissionScheduler:
else: else:
package.time_over_target = previous_end_time package.time_over_target = previous_end_time
departure_time = package.mission_departure_time departure_time = self._get_departure_time(package)
# Should be impossible for CAPs
if departure_time is None: if departure_time is None:
logging.error(f"Could not determine mission end time for {package}")
continue continue
previous_cap_end_time[package.target] = departure_time is_naval_cp = isinstance(package.target, NavalControlPoint)
count = carrier_barcaps[package.target]
if count >= max_carrier_simultaneous_barcaps - 1 and is_naval_cp:
previous_cap_end_time[package.target] = departure_time
carrier_barcaps[package.target] = 0
elif is_naval_cp:
carrier_barcaps[package.target] += 1
elif not is_naval_cp:
previous_cap_end_time[package.target] = departure_time
elif package.auto_asap: elif package.auto_asap:
package.set_tot_asap(now) package.set_tot_asap(now)
elif package.primary_task is FlightType.AEWC:
last = previous_aewc_end_time[package.target]
package.time_over_target = tot if tot > last else last
departure_time = self._get_departure_time(package)
if departure_time is None:
continue
previous_aewc_end_time[package.target] = departure_time
else: else:
# But other packages should be spread out a bit. Note that take # But other packages should be spread out a bit. Note that take
# times are delayed, but all aircraft will become active at # times are delayed, but all aircraft will become active at
@@ -74,3 +97,34 @@ class MissionScheduler:
# to be present. Runway and air started aircraft will be # to be present. Runway and air started aircraft will be
# delayed until their takeoff time by AirConflictGenerator. # delayed until their takeoff time by AirConflictGenerator.
package.time_over_target = next(start_time) + tot package.time_over_target = next(start_time) + tot
for f in package.flights:
if f.departure.is_fleet and not f.is_helo:
carrier_etas[f.departure].append(
f.flight_plan.landing_time - timedelta(minutes=10)
)
# division by 2 is meant to provide some leeway to avoid filtering out too many ETAs
duration = self.coalition.game.settings.desired_tanker_on_station_time / 2
for cp in carrier_etas:
filtered: list[datetime] = []
for eta in sorted(carrier_etas[cp]):
count = len([t for t in filtered if eta < t + duration])
if count < max_simultaneous_recovery_tankers:
filtered.append(eta)
carrier_etas[cp] = filtered
for package in [
p
for p in self.coalition.ato.packages
if p.primary_task is FlightType.RECOVERY
]:
if carrier_etas[package.target]:
package.time_over_target = carrier_etas[package.target].pop(0)
@staticmethod
def _get_departure_time(package: Package) -> datetime | None:
departure_time = package.mission_departure_time
# Should be impossible for CAP/AEWC
if departure_time is None:
logging.error(f"Could not determine mission end time for {package}")
return departure_time

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import itertools
import math import math
import operator import operator
from collections.abc import Iterable, Iterator from collections.abc import Iterable, Iterator
@@ -15,6 +16,8 @@ from game.theater import (
MissionTarget, MissionTarget,
OffMapSpawn, OffMapSpawn,
ParkingType, ParkingType,
NavalControlPoint,
Player,
) )
from game.theater.theatergroundobject import ( from game.theater.theatergroundobject import (
BuildingGroundObject, BuildingGroundObject,
@@ -34,7 +37,7 @@ MissionTargetType = TypeVar("MissionTargetType", bound=MissionTarget)
class ObjectiveFinder: class ObjectiveFinder:
"""Identifies potential objectives for the mission planner.""" """Identifies potential objectives for the mission planner."""
def __init__(self, game: Game, is_player: bool) -> None: def __init__(self, game: Game, is_player: Player) -> None:
self.game = game self.game = game
self.is_player = is_player self.is_player = is_player
@@ -147,10 +150,13 @@ class ObjectiveFinder:
if isinstance(cp, OffMapSpawn): if isinstance(cp, OffMapSpawn):
# Off-map spawn locations don't need protection. # Off-map spawn locations don't need protection.
continue continue
if isinstance(cp, NavalControlPoint):
yield cp # always consider CVN/LHA as vulnerable
continue
airfields_in_proximity = self.closest_airfields_to(cp) airfields_in_proximity = self.closest_airfields_to(cp)
airbase_threat_range = self.game.settings.airbase_threat_range airbase_threat_range = self.game.settings.airbase_threat_range
if ( if (
not self.is_player self.is_player.is_red
and randint(1, 100) and randint(1, 100)
> self.game.settings.opfor_autoplanner_aggressiveness > self.game.settings.opfor_autoplanner_aggressiveness
): ):
@@ -167,24 +173,6 @@ class ObjectiveFinder:
yield cp yield cp
break break
def vulnerable_enemy_control_points(self) -> Iterator[ControlPoint]:
"""Iterates over enemy CPs that are vulnerable to Air Assault.
Vulnerability is defined as any unit being alive in the CP's "blocking_capture" groups.
"""
for cp in self.enemy_control_points():
include = True
for tgo in cp.connected_objectives:
if tgo.distance_to(cp) > cp.CAPTURE_DISTANCE.meters:
continue
for u in tgo.units:
if u.is_vehicle and u.alive:
include = False
break
if not include:
break
if include:
yield cp
def oca_targets(self, min_aircraft: int) -> Iterator[ControlPoint]: def oca_targets(self, min_aircraft: int) -> Iterator[ControlPoint]:
parking_type = ParkingType() parking_type = ParkingType()
parking_type.include_rotary_wing = True parking_type.include_rotary_wing = True
@@ -230,7 +218,7 @@ class ObjectiveFinder:
def farthest_friendly_control_point(self) -> ControlPoint: def farthest_friendly_control_point(self) -> ControlPoint:
"""Finds the friendly control point that is farthest from any threats.""" """Finds the friendly control point that is farthest from any threats."""
threat_zones = self.game.threat_zone_for(not self.is_player) threat_zones = self.game.threat_zone_for(self.is_player.opponent)
farthest = None farthest = None
max_distance = meters(0) max_distance = meters(0)
@@ -248,7 +236,7 @@ class ObjectiveFinder:
def closest_friendly_control_point(self) -> ControlPoint: def closest_friendly_control_point(self) -> ControlPoint:
"""Finds the friendly control point that is closest to any threats.""" """Finds the friendly control point that is closest to any threats."""
threat_zones = self.game.threat_zone_for(not self.is_player) threat_zones = self.game.threat_zone_for(self.is_player.opponent)
closest = None closest = None
min_distance = meters(math.inf) min_distance = meters(math.inf)
@@ -264,25 +252,54 @@ class ObjectiveFinder:
raise RuntimeError("Found no friendly control points. You probably lost.") raise RuntimeError("Found no friendly control points. You probably lost.")
return closest return closest
def friendly_naval_control_points(self) -> Iterator[ControlPoint]:
return (cp for cp in self.friendly_control_points() if cp.is_fleet)
def enemy_control_points(self) -> Iterator[ControlPoint]: def enemy_control_points(self) -> Iterator[ControlPoint]:
"""Iterates over all enemy control points.""" """Iterates over all enemy control points."""
return ( return (
c c
for c in self.game.theater.controlpoints for c in self.game.theater.controlpoints
if not c.is_friendly(self.is_player) if not c.is_friendly(self.is_player) and c.captured != Player.NEUTRAL
) )
def prioritized_unisolated_points(self) -> list[ControlPoint]: def prioritized_points(self) -> list[ControlPoint]:
prioritized = [] prioritized = []
capturable_later = [] capturable_later = []
for cp in self.game.theater.control_points_for(not self.is_player): isolated = []
for cp in self.game.theater.control_points_for(self.is_player.opponent):
if cp.is_isolated: if cp.is_isolated:
isolated.append(cp)
continue continue
if cp.has_active_frontline: if cp.has_active_frontline:
prioritized.append(cp) prioritized.append(cp)
else: else:
capturable_later.append(cp) capturable_later.append(cp)
prioritized.extend(self._targets_by_range(capturable_later)) prioritized.extend(self._targets_by_range(capturable_later))
prioritized.extend(self._targets_by_range(isolated))
return prioritized
def air_assault_targets(self) -> list[ControlPoint]:
"""Returns control points suitable for air assault missions, including neutral bases."""
prioritized = []
capturable_later = []
isolated = []
combined_control_points = itertools.chain(
self.game.theater.control_points_for(self.is_player.opponent),
self.game.theater.control_points_for(Player.NEUTRAL),
)
for cp in combined_control_points:
if cp.is_isolated:
isolated.append(cp)
continue
if cp.has_active_frontline:
prioritized.append(cp)
else:
capturable_later.append(cp)
prioritized.extend(self._targets_by_range(capturable_later))
prioritized.extend(self._targets_by_range(isolated))
return prioritized return prioritized
@staticmethod @staticmethod

View File

@@ -4,6 +4,7 @@ from typing import Optional, TYPE_CHECKING
from game.theater import ControlPoint, MissionTarget, OffMapSpawn from game.theater import ControlPoint, MissionTarget, OffMapSpawn
from game.utils import nautical_miles from game.utils import nautical_miles
from ..ato import FlightType
from ..ato.flight import Flight from ..ato.flight import Flight
from ..ato.package import Package from ..ato.package import Package
from ..ato.starttype import StartType from ..ato.starttype import StartType
@@ -46,10 +47,19 @@ class PackageBuilder:
caller should return any previously planned flights to the inventory caller should return any previously planned flights to the inventory
using release_planned_aircraft. using release_planned_aircraft.
""" """
target = self.package.target
heli = False
pf = self.package.primary_flight pf = self.package.primary_flight
heli = pf.is_helo if pf else False if pf:
target = (
pf.departure
if pf.flight_type
in [FlightType.AEWC, FlightType.REFUELING, FlightType.RECOVERY]
else target
)
heli = pf.is_helo
squadron = self.air_wing.best_squadron_for( squadron = self.air_wing.best_squadron_for(
self.package.target, target,
plan.task, plan.task,
plan.num_aircraft, plan.num_aircraft,
heli, heli,
@@ -76,6 +86,16 @@ class PackageBuilder:
member.assign_tgp_laser_code( member.assign_tgp_laser_code(
self.laser_code_registry.alloc_laser_code() self.laser_code_registry.alloc_laser_code()
) )
# If this is a client flight, set the start_type again to match the configured default
# https://github.com/dcs-liberation/dcs_liberation/issues/1567
if (
squadron.location.required_aircraft_start_type is None
and flight.roster is not None
and flight.roster.player_count > 0
):
flight.start_type = (
squadron.coalition.game.settings.default_start_type_client
)
self.package.add_flight(flight) self.package.add_flight(flight)
return True return True

View File

@@ -43,7 +43,9 @@ class PackageFulfiller:
@property @property
def is_player(self) -> bool: def is_player(self) -> bool:
return self.coalition.player if self.coalition.player.is_blue:
return True
return False
@property @property
def ato(self) -> AirTaskingOrder: def ato(self) -> AirTaskingOrder:
@@ -83,12 +85,19 @@ class PackageFulfiller:
purchase_multiplier: int, purchase_multiplier: int,
ignore_range: bool = False, ignore_range: bool = False,
) -> None: ) -> None:
target = mission.location
pf = builder.package.primary_flight
if (
pf
and pf.flight_type in [FlightType.AEWC, FlightType.REFUELING]
and flight.task is FlightType.ESCORT
):
target = pf.departure
if not builder.plan_flight(flight, ignore_range): if not builder.plan_flight(flight, ignore_range):
pf = builder.package.primary_flight
heli = pf.is_helo if pf else False heli = pf.is_helo if pf else False
missing_types.add(flight.task) missing_types.add(flight.task)
purchase_order = AircraftProcurementRequest( purchase_order = AircraftProcurementRequest(
near=mission.location, near=target,
task_capability=flight.task, task_capability=flight.task,
number=flight.num_aircraft * purchase_multiplier, number=flight.num_aircraft * purchase_multiplier,
heli=heli, heli=heli,
@@ -133,6 +142,21 @@ class PackageFulfiller:
threats[EscortType.Sead] = True threats[EscortType.Sead] = True
return threats return threats
def can_plan_escort(self, type: EscortType) -> bool:
if type == EscortType.AirToAir:
return self.air_wing_can_plan(FlightType.ESCORT)
elif type == EscortType.Sead:
for task in [
FlightType.SEAD,
FlightType.SEAD_ESCORT,
FlightType.SEAD_SWEEP,
]:
if self.air_wing_can_plan(task):
return True
elif type == EscortType.Refuel:
return self.air_wing_can_plan(FlightType.REFUELING)
return False
def plan_mission( def plan_mission(
self, self,
mission: ProposedMission, mission: ProposedMission,
@@ -207,7 +231,9 @@ class PackageFulfiller:
# This list was generated from the not None set, so this should be # This list was generated from the not None set, so this should be
# impossible. # impossible.
assert escort.escort_type is not None assert escort.escort_type is not None
if needed_escorts[escort.escort_type]: if needed_escorts[escort.escort_type] and self.can_plan_escort(
escort.escort_type
):
with tracer.trace("Flight planning"): with tracer.trace("Flight planning"):
self.plan_flight( self.plan_flight(
mission, escort, builder, missing_types, purchase_multiplier mission, escort, builder, missing_types, purchase_multiplier

View File

@@ -1,5 +1,6 @@
from collections.abc import Iterator from collections.abc import Iterator
from game.commander.tasks.primitive.armedrecon import PlanArmedRecon
from game.commander.tasks.primitive.bai import PlanBai from game.commander.tasks.primitive.bai import PlanBai
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method from game.htn import CompoundTask, Method
@@ -10,3 +11,7 @@ class AttackBattlePositions(CompoundTask[TheaterState]):
for battle_positions in state.enemy_battle_positions.values(): for battle_positions in state.enemy_battle_positions.values():
for battle_position in battle_positions.in_priority_order: for battle_position in battle_positions.in_priority_order:
yield [PlanBai(battle_position)] yield [PlanBai(battle_position)]
# Only plan against the 2 most important CPs
for cp in state.control_point_priority_queue[:2]:
if not cp.is_fleet:
yield [PlanArmedRecon(cp)]

View File

@@ -8,8 +8,4 @@ from game.htn import CompoundTask, Method
class AttackShips(CompoundTask[TheaterState]): class AttackShips(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]: def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
for ship in state.enemy_ships: for ship in state.enemy_ships:
# Ammo depots are targeted based on the needs of the front line by yield [PlanAntiShip(ship)]
# ReduceEnemyFrontLineCapacity. No reason to target them before that front
# line is active.
if ship.has_live_radar_sam:
yield [PlanAntiShip(ship)]

View File

@@ -7,11 +7,10 @@ from game.commander.tasks.compound.destroyenemygroundunits import (
from game.commander.tasks.compound.reduceenemyfrontlinecapacity import ( from game.commander.tasks.compound.reduceenemyfrontlinecapacity import (
ReduceEnemyFrontLineCapacity, ReduceEnemyFrontLineCapacity,
) )
from game.commander.tasks.primitive.airassault import PlanAirAssault
from game.commander.tasks.primitive.breakthroughattack import BreakthroughAttack from game.commander.tasks.primitive.breakthroughattack import BreakthroughAttack
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method from game.htn import CompoundTask, Method
from game.theater import ControlPoint, FrontLine from game.theater import ControlPoint, FrontLine, Player
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -23,17 +22,16 @@ class CaptureBase(CompoundTask[TheaterState]):
yield [DestroyEnemyGroundUnits(self.front_line)] yield [DestroyEnemyGroundUnits(self.front_line)]
if self.worth_destroying_ammo_depots(state): if self.worth_destroying_ammo_depots(state):
yield [ReduceEnemyFrontLineCapacity(self.enemy_cp(state))] yield [ReduceEnemyFrontLineCapacity(self.enemy_cp(state))]
yield [PlanAirAssault(self.enemy_cp(state))]
def enemy_cp(self, state: TheaterState) -> ControlPoint: def enemy_cp(self, state: TheaterState) -> ControlPoint:
return self.front_line.control_point_hostile_to(state.context.coalition.player) return self.front_line.control_point_hostile_to(state.context.coalition.player)
def units_deployable(self, state: TheaterState, player: bool) -> int: def units_deployable(self, state: TheaterState, player: Player) -> int:
cp = self.front_line.control_point_friendly_to(player) cp = self.front_line.control_point_friendly_to(player)
ammo_depots = list(state.ammo_dumps_at(cp)) ammo_depots = list(state.ammo_dumps_at(cp))
return cp.deployable_front_line_units_with(len(ammo_depots)) return cp.deployable_front_line_units_with(len(ammo_depots))
def unit_cap(self, state: TheaterState, player: bool) -> int: def unit_cap(self, state: TheaterState, player: Player) -> int:
cp = self.front_line.control_point_friendly_to(player) cp = self.front_line.control_point_friendly_to(player)
ammo_depots = list(state.ammo_dumps_at(cp)) ammo_depots = list(state.ammo_dumps_at(cp))
return cp.front_line_capacity_with(len(ammo_depots)) return cp.front_line_capacity_with(len(ammo_depots))

View File

@@ -2,6 +2,7 @@ from collections.abc import Iterator
from dataclasses import dataclass from dataclasses import dataclass
from game.commander.tasks.compound.capturebase import CaptureBase from game.commander.tasks.compound.capturebase import CaptureBase
from game.commander.tasks.primitive.airassault import PlanAirAssault
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method from game.htn import CompoundTask, Method
@@ -11,3 +12,5 @@ class CaptureBases(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]: def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
for front in state.active_front_lines: for front in state.active_front_lines:
yield [CaptureBase(front)] yield [CaptureBase(front)]
for cp in state.vulnerable_control_points:
yield [PlanAirAssault(cp)]

View File

@@ -4,6 +4,7 @@ from typing import Union
from game.commander.tasks.primitive.antiship import PlanAntiShip from game.commander.tasks.primitive.antiship import PlanAntiShip
from game.commander.tasks.primitive.dead import PlanDead from game.commander.tasks.primitive.dead import PlanDead
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.data.groups import GroupTask
from game.htn import CompoundTask, Method from game.htn import CompoundTask, Method
from game.theater.theatergroundobject import IadsGroundObject, NavalGroundObject from game.theater.theatergroundobject import IadsGroundObject, NavalGroundObject
@@ -12,12 +13,25 @@ class DegradeIads(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]: def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
for air_defense in state.threatening_air_defenses: for air_defense in state.threatening_air_defenses:
yield [self.plan_against(air_defense)] yield [self.plan_against(air_defense)]
prioritized_air_defenses = sorted(
[
tgo
for tgo in state.enemy_air_defenses
if tgo.task in [GroupTask.LORAD, GroupTask.MERAD]
],
key=lambda x: (state.priority_cp.distance_to(x) if state.priority_cp else 0)
- x.max_threat_range().meters,
)
for air_defense in prioritized_air_defenses:
yield [self.plan_against(air_defense)]
for detector in state.detecting_air_defenses: for detector in state.detecting_air_defenses:
yield [self.plan_against(detector)] yield [self.plan_against(detector)]
@staticmethod @staticmethod
def plan_against( def plan_against(
target: Union[IadsGroundObject, NavalGroundObject] target: Union[IadsGroundObject, NavalGroundObject],
) -> Union[PlanDead, PlanAntiShip]: ) -> Union[PlanDead, PlanAntiShip]:
if isinstance(target, IadsGroundObject): if isinstance(target, IadsGroundObject):
return PlanDead(target) return PlanDead(target)

View File

@@ -14,6 +14,7 @@ from game.commander.tasks.compound.interdictreinforcements import (
InterdictReinforcements, InterdictReinforcements,
) )
from game.commander.tasks.compound.protectairspace import ProtectAirSpace from game.commander.tasks.compound.protectairspace import ProtectAirSpace
from game.commander.tasks.compound.recoverysupport import RecoverySupport
from game.commander.tasks.compound.theatersupport import TheaterSupport from game.commander.tasks.compound.theatersupport import TheaterSupport
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method from game.htn import CompoundTask, Method
@@ -28,9 +29,10 @@ class PlanNextAction(CompoundTask[TheaterState]):
yield [ProtectAirSpace()] yield [ProtectAirSpace()]
yield [DefendBases()] yield [DefendBases()]
yield [InterdictReinforcements()] yield [InterdictReinforcements()]
yield [DegradeIads()]
yield [AttackBattlePositions()] yield [AttackBattlePositions()]
yield [CaptureBases()] yield [CaptureBases()]
yield [AttackAirInfrastructure(self.aircraft_cold_start)] yield [AttackAirInfrastructure(self.aircraft_cold_start)]
yield [AttackBuildings()] yield [AttackBuildings()]
yield [AttackShips()] yield [AttackShips()]
yield [DegradeIads()]
yield [RecoverySupport()] # for recovery tankers

View File

@@ -0,0 +1,16 @@
from collections.abc import Iterator
from game.commander.tasks.primitive.recovery import PlanRecovery
from game.commander.theaterstate import TheaterState
from game.htn import CompoundTask, Method
class RecoverySupport(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
yield [PlanRecoverySupport()]
class PlanRecoverySupport(CompoundTask[TheaterState]):
def each_valid_method(self, state: TheaterState) -> Iterator[Method[TheaterState]]:
for target in state.recovery_targets:
yield [PlanRecovery(target)]

View File

@@ -11,23 +11,23 @@ from game.theater import FrontLine
if TYPE_CHECKING: if TYPE_CHECKING:
from game.coalition import Coalition from game.coalition import Coalition
from game.theater.player import Player
class FrontLineStanceTask(TheaterCommanderTask, ABC): class FrontLineStanceTask(TheaterCommanderTask, ABC):
def __init__(self, front_line: FrontLine, player: bool) -> None: def __init__(self, front_line: FrontLine, player: Player) -> None:
self.front_line = front_line self.front_line = front_line
self.friendly_cp = self.front_line.control_point_friendly_to(player) self.friendly_cp = self.front_line.control_point_friendly_to(player)
self.enemy_cp = self.front_line.control_point_hostile_to(player) self.enemy_cp = self.front_line.control_point_hostile_to(player)
@property @property
@abstractmethod @abstractmethod
def stance(self) -> CombatStance: def stance(self) -> CombatStance: ...
...
@staticmethod @staticmethod
def management_allowed(state: TheaterState) -> bool: def management_allowed(state: TheaterState) -> bool:
return ( return (
not state.context.coalition.player not state.context.coalition.player.is_blue
or state.context.settings.automate_front_line_stance or state.context.settings.automate_front_line_stance
) )
@@ -49,8 +49,7 @@ class FrontLineStanceTask(TheaterCommanderTask, ABC):
@property @property
@abstractmethod @abstractmethod
def have_sufficient_front_line_advantage(self) -> bool: def have_sufficient_front_line_advantage(self) -> bool: ...
...
@property @property
def ground_force_balance(self) -> float: def ground_force_balance(self) -> float:

View File

@@ -14,8 +14,9 @@ from game.commander.missionproposals import EscortType, ProposedFlight, Proposed
from game.commander.packagefulfiller import PackageFulfiller from game.commander.packagefulfiller import PackageFulfiller
from game.commander.tasks.theatercommandertask import TheaterCommanderTask from game.commander.tasks.theatercommandertask import TheaterCommanderTask
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.data.groups import GroupTask
from game.settings import AutoAtoBehavior from game.settings import AutoAtoBehavior
from game.theater import MissionTarget from game.theater import MissionTarget, ControlPoint
from game.theater.theatergroundobject import IadsGroundObject, NavalGroundObject from game.theater.theatergroundobject import IadsGroundObject, NavalGroundObject
from game.utils import Distance, meters from game.utils import Distance, meters
@@ -44,20 +45,28 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
def preconditions_met(self, state: TheaterState) -> bool: def preconditions_met(self, state: TheaterState) -> bool:
if ( if (
state.context.coalition.player state.context.coalition.player.is_blue
and state.context.settings.auto_ato_behavior is AutoAtoBehavior.Disabled and state.context.settings.auto_ato_behavior is AutoAtoBehavior.Disabled
): ):
return False return False
return self.fulfill_mission(state) return self.fulfill_mission(state)
def apply_effects(self, state: TheaterState) -> None:
seen: set[ControlPoint] = set()
if not self.package:
return
for f in self.package.flights:
if f.departure.is_fleet and not f.is_helo and f.departure not in seen:
state.recovery_targets[f.departure] += f.count
seen.add(f.departure)
def execute(self, coalition: Coalition) -> None: def execute(self, coalition: Coalition) -> None:
if self.package is None: if self.package is None:
raise RuntimeError("Attempted to execute failed package planning task") raise RuntimeError("Attempted to execute failed package planning task")
coalition.ato.add_package(self.package) coalition.ato.add_package(self.package)
@abstractmethod @abstractmethod
def propose_flights(self) -> None: def propose_flights(self) -> None: ...
...
def propose_flight( def propose_flight(
self, self,
@@ -93,7 +102,7 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
return 1 return 1
def fulfill_mission(self, state: TheaterState) -> bool: def fulfill_mission(self, state: TheaterState) -> bool:
color = "blue" if state.context.coalition.player else "red" color = "blue" if state.context.coalition.player.is_blue else "red"
self.propose_flights() self.propose_flights()
fulfiller = PackageFulfiller( fulfiller = PackageFulfiller(
state.context.coalition, state.context.coalition,
@@ -102,8 +111,14 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
state.context.settings, state.context.settings,
) )
with state.context.tracer.trace(f"{color} {self.flights[0].task} planning"): with state.context.tracer.trace(f"{color} {self.flights[0].task} planning"):
asap = False
if (
not state.context.coalition.ato.has_awacs_package
and FlightType.AEWC in [f.task for f in self.flights]
):
asap = True
self.package = fulfiller.plan_mission( self.package = fulfiller.plan_mission(
ProposedMission(self.target, self.flights), ProposedMission(self.target, self.flights, asap=asap),
self.purchase_multiplier, self.purchase_multiplier,
state.context.now, state.context.now,
state.context.tracer, state.context.tracer,
@@ -121,9 +136,9 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
target_ranges: list[ target_ranges: list[
tuple[Union[IadsGroundObject, NavalGroundObject], Distance] tuple[Union[IadsGroundObject, NavalGroundObject], Distance]
] = [] ] = []
all_iads: Iterator[ all_iads: Iterator[Union[IadsGroundObject, NavalGroundObject]] = (
Union[IadsGroundObject, NavalGroundObject] itertools.chain(state.enemy_air_defenses, state.enemy_ships)
] = itertools.chain(state.enemy_air_defenses, state.enemy_ships) )
for target in all_iads: for target in all_iads:
distance = meters(target.distance_to(self.target)) distance = meters(target.distance_to(self.target))
if range_type is RangeType.Detection: if range_type is RangeType.Detection:
@@ -150,6 +165,16 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
for target, _range in target_ranges: for target, _range in target_ranges:
yield target yield target
@staticmethod
def corrective_factor_for_type(
target: IadsGroundObject | NavalGroundObject,
) -> float:
return (
1.0
if target.task in [GroupTask.LORAD, GroupTask.MERAD]
else 0.5 if target.task == GroupTask.AAA else 0.9
)
def iter_detecting_iads( def iter_detecting_iads(
self, state: TheaterState self, state: TheaterState
) -> Iterator[Union[IadsGroundObject, NavalGroundObject]]: ) -> Iterator[Union[IadsGroundObject, NavalGroundObject]]:
@@ -173,19 +198,31 @@ class PackagePlanningTask(TheaterCommanderTask, Generic[MissionTargetT]):
if not ignore_iads: if not ignore_iads:
for iads_threat in self.iter_iads_threats(state): for iads_threat in self.iter_iads_threats(state):
# Only consider blue faction flights threatened. weighted = self._get_weighted_threat_range(iads_threat, state)
# Red might still plan missions into hostile territory, if weighted < meters(0):
# depending on the aggressiveness setting.
if (
state.context.coalition.player
or random.randint(1, 100)
> state.context.coalition.game.settings.opfor_autoplanner_aggressiveness
):
threatened = True threatened = True
if iads_threat not in state.threatening_air_defenses: if iads_threat not in state.threatening_air_defenses:
state.threatening_air_defenses.append(iads_threat) state.threatening_air_defenses.append(iads_threat)
return not threatened return not threatened
def _get_weighted_threat_range(
self,
iads_threat: Union[IadsGroundObject | NavalGroundObject],
state: TheaterState,
) -> Distance:
distance = meters(iads_threat.distance_to(self.target))
settings = state.context.coalition.game.settings
margin = 100 - (
settings.ownfor_autoplanner_aggressiveness
if state.context.coalition.player.is_blue
else settings.opfor_autoplanner_aggressiveness
)
threat_range = iads_threat.max_threat_range() * (margin / 100)
corrective_factor = self.corrective_factor_for_type(iads_threat)
threat_range *= corrective_factor
distance_to_threat = distance - threat_range
return distance_to_threat
def get_flight_size(self) -> int: def get_flight_size(self) -> int:
settings = self.target.coalition.game.settings settings = self.target.coalition.game.settings
weights = [ weights = [

View File

@@ -12,7 +12,7 @@ from game.theater import MissionTarget
class PlanAewc(PackagePlanningTask[MissionTarget]): class PlanAewc(PackagePlanningTask[MissionTarget]):
def preconditions_met(self, state: TheaterState) -> bool: def preconditions_met(self, state: TheaterState) -> bool:
if ( if (
state.context.coalition.player state.context.coalition.player.is_blue
and not state.context.settings.auto_ato_behavior_awacs and not state.context.settings.auto_ato_behavior_awacs
): ):
return False return False
@@ -22,9 +22,11 @@ class PlanAewc(PackagePlanningTask[MissionTarget]):
def apply_effects(self, state: TheaterState) -> None: def apply_effects(self, state: TheaterState) -> None:
state.aewc_targets.remove(self.target) state.aewc_targets.remove(self.target)
super().apply_effects(state)
def propose_flights(self) -> None: def propose_flights(self) -> None:
self.propose_flight(FlightType.AEWC, 1) self.propose_flight(FlightType.AEWC, 1)
self.propose_flight(FlightType.ESCORT, 2)
@property @property
def asap(self) -> bool: def asap(self) -> bool:

View File

@@ -11,7 +11,7 @@ from game.theater import ControlPoint
@dataclass @dataclass
class PlanAirAssault(PackagePlanningTask[ControlPoint]): class PlanAirAssault(PackagePlanningTask[ControlPoint]):
def preconditions_met(self, state: TheaterState) -> bool: def preconditions_met(self, state: TheaterState) -> bool:
if self.target not in state.vulnerable_control_points: if self.target not in state.vulnerable_control_points or self.target.is_fleet:
return False return False
if not self.target_area_preconditions_met(state): if not self.target_area_preconditions_met(state):
return False return False
@@ -19,8 +19,8 @@ class PlanAirAssault(PackagePlanningTask[ControlPoint]):
def apply_effects(self, state: TheaterState) -> None: def apply_effects(self, state: TheaterState) -> None:
state.vulnerable_control_points.remove(self.target) state.vulnerable_control_points.remove(self.target)
super().apply_effects(state)
def propose_flights(self) -> None: def propose_flights(self) -> None:
size = self.get_flight_size() self.propose_flight(FlightType.AIR_ASSAULT, self.get_flight_size())
self.propose_flight(FlightType.AIR_ASSAULT, size)
self.propose_common_escorts() self.propose_common_escorts()

View File

@@ -1,19 +1,22 @@
from __future__ import annotations from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from random import randint
from game.ato.flighttype import FlightType from game.ato.flighttype import FlightType
from game.commander.missionproposals import EscortType from game.commander.missionproposals import EscortType
from game.commander.tasks.packageplanningtask import PackagePlanningTask from game.commander.tasks.packageplanningtask import PackagePlanningTask
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.theater.theatergroundobject import NavalGroundObject from game.theater.theatergroundobject import NavalGroundObject
from game.utils import meters
@dataclass @dataclass
class PlanAntiShip(PackagePlanningTask[NavalGroundObject]): class PlanAntiShip(PackagePlanningTask[NavalGroundObject]):
def preconditions_met(self, state: TheaterState) -> bool: def preconditions_met(self, state: TheaterState) -> bool:
if self.target not in state.threatening_air_defenses: if (
self.target not in state.threatening_air_defenses
and not self.target.is_naval_control_point
):
return False return False
if not self.target_area_preconditions_met(state, ignore_iads=True): if not self.target_area_preconditions_met(state, ignore_iads=True):
return False return False
@@ -21,8 +24,15 @@ class PlanAntiShip(PackagePlanningTask[NavalGroundObject]):
def apply_effects(self, state: TheaterState) -> None: def apply_effects(self, state: TheaterState) -> None:
state.eliminate_ship(self.target) state.eliminate_ship(self.target)
super().apply_effects(state)
def propose_flights(self) -> None: def propose_flights(self) -> None:
size = self.get_flight_size() size = self.get_flight_size()
if self.target.max_detection_range() > meters(0):
size = 4 # attempt to saturate ship's air-defences
self.propose_flight(FlightType.ANTISHIP, size)
self.propose_flight(FlightType.ESCORT, 2, EscortType.AirToAir)
self.propose_flight(FlightType.ANTISHIP, size) self.propose_flight(FlightType.ANTISHIP, size)
self.propose_flight(FlightType.ESCORT, 2, EscortType.AirToAir) self.propose_flight(FlightType.ESCORT, 2, EscortType.AirToAir)
if self.target.max_detection_range() > meters(0):
self.propose_flight(FlightType.SEAD, 2, EscortType.Sead)

View File

@@ -20,6 +20,7 @@ class PlanAntiShipping(PackagePlanningTask[CargoShip]):
def apply_effects(self, state: TheaterState) -> None: def apply_effects(self, state: TheaterState) -> None:
state.enemy_shipping.remove(self.target) state.enemy_shipping.remove(self.target)
super().apply_effects(state)
def propose_flights(self) -> None: def propose_flights(self) -> None:
size = self.get_flight_size() size = self.get_flight_size()

View File

@@ -0,0 +1,26 @@
from __future__ import annotations
from dataclasses import dataclass
from game.ato.flighttype import FlightType
from game.commander.tasks.packageplanningtask import PackagePlanningTask
from game.commander.theaterstate import TheaterState
from game.theater import ControlPoint
@dataclass
class PlanArmedRecon(PackagePlanningTask[ControlPoint]):
def preconditions_met(self, state: TheaterState) -> bool:
if self.target not in state.control_point_priority_queue:
return False
if not self.target_area_preconditions_met(state):
return False
return super().preconditions_met(state)
def apply_effects(self, state: TheaterState) -> None:
state.control_point_priority_queue.remove(self.target)
super().apply_effects(state)
def propose_flights(self) -> None:
self.propose_flight(FlightType.ARMED_RECON, self.get_flight_size())
self.propose_common_escorts()

View File

@@ -19,6 +19,7 @@ class PlanBai(PackagePlanningTask[VehicleGroupGroundObject]):
def apply_effects(self, state: TheaterState) -> None: def apply_effects(self, state: TheaterState) -> None:
state.eliminate_battle_position(self.target) state.eliminate_battle_position(self.target)
super().apply_effects(state)
def propose_flights(self) -> None: def propose_flights(self) -> None:
tgt_count = self.target.alive_unit_count tgt_count = self.target.alive_unit_count

View File

@@ -1,8 +1,6 @@
from __future__ import annotations from __future__ import annotations
import random
from dataclasses import dataclass from dataclasses import dataclass
from random import randint
from game.ato.flighttype import FlightType from game.ato.flighttype import FlightType
from game.commander.tasks.packageplanningtask import PackagePlanningTask from game.commander.tasks.packageplanningtask import PackagePlanningTask
@@ -21,6 +19,7 @@ class PlanBarcap(PackagePlanningTask[ControlPoint]):
def apply_effects(self, state: TheaterState) -> None: def apply_effects(self, state: TheaterState) -> None:
state.barcaps_needed[self.target] -= 1 state.barcaps_needed[self.target] -= 1
super().apply_effects(state)
def propose_flights(self) -> None: def propose_flights(self) -> None:
size = self.get_flight_size() size = self.get_flight_size()

View File

@@ -6,7 +6,7 @@ from game.ato.flighttype import FlightType
from game.commander.missionproposals import EscortType from game.commander.missionproposals import EscortType
from game.commander.tasks.packageplanningtask import PackagePlanningTask from game.commander.tasks.packageplanningtask import PackagePlanningTask
from game.commander.theaterstate import TheaterState from game.commander.theaterstate import TheaterState
from game.theater import FrontLine from game.theater import FrontLine, Player
@dataclass @dataclass
@@ -19,18 +19,18 @@ class PlanCas(PackagePlanningTask[FrontLine]):
# An exception is made for turn zero since that's not being truly planned, but # An exception is made for turn zero since that's not being truly planned, but
# just to determine what missions should be planned on turn 1 (when there *will* # just to determine what missions should be planned on turn 1 (when there *will*
# be ground units) and what aircraft should be ordered. # be ground units) and what aircraft should be ordered.
enemy_cp = self.target.control_point_friendly_to( player = state.context.coalition.player.opponent
player=not state.context.coalition.player enemy_cp = self.target.control_point_friendly_to(player)
)
if enemy_cp.deployable_front_line_units == 0 and state.context.turn > 0: if enemy_cp.deployable_front_line_units == 0 and state.context.turn > 0:
return False return False
return super().preconditions_met(state) return super().preconditions_met(state)
def apply_effects(self, state: TheaterState) -> None: def apply_effects(self, state: TheaterState) -> None:
state.vulnerable_front_lines.remove(self.target) state.vulnerable_front_lines.remove(self.target)
super().apply_effects(state)
def propose_flights(self) -> None: def propose_flights(self) -> None:
size = self.get_flight_size() size = self.get_flight_size()
self.propose_flight(FlightType.CAS, size) self.propose_flight(FlightType.CAS, size)
self.propose_flight(FlightType.TARCAP, 2, EscortType.AirToAir) self.propose_flight(FlightType.TARCAP, 2, EscortType.AirToAir)
self.propose_flight(FlightType.SEAD_SWEEP, 2, EscortType.Sead) self.propose_flight(FlightType.SEAD_SWEEP, 2)

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