Compare commits

...

115 Commits

Author SHA1 Message Date
RndName
edb10f60fb Update Changelog 2022-03-18 10:19:09 +01:00
DillieKoe
a7a28205a9 Fix unit role of Type 04A (ZBD-04A).
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2072.
2022-03-18 10:16:19 +01:00
Dan Albert
a22452baca Add Sith1144's new campaigns.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1833.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1834.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1835.
2022-03-18 10:16:14 +01:00
Benjamin Fischer
8422823921 another high dpi fix which fixes some resizing issues. 2022-03-18 10:15:27 +01:00
Benjamin Fischer
30fe1ca6af Round balance and income in the finance window.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1983
2022-03-18 10:14:01 +01:00
Benjamin Fischer
faf70debce fixes #1987 by adding highdpi qt attributes. 2022-03-18 10:13:56 +01:00
Benjamin Fischer
42929b08d8 Fixes #1774 by adding a logmessage for non ascii install path. 2022-03-18 10:13:50 +01:00
Benjamin Fischer
91bb5acd04 Log information about the generated campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1735
2022-03-18 10:13:30 +01:00
Benjamin Fischer
d69841f261 Make the flight dialog modal to prevent inventory loss.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1971.
2022-03-18 10:13:18 +01:00
RndName
dba8c26887 Bump Version to 5.2.0 2022-03-18 09:45:57 +01:00
MetalStormGhost
bef27ffb20 UH-60L mod support
Resolves #1964 but adding CTLD will require the use of Liberation plugins and thus are not included in this pull request.

- Implemented support for the UH-60L mod by =X51= Kinkkujuustovoileipa, Cubeboy, Jonas, JinxxDCS, Dorian, =X51= Parzival & =X51= Lawson.
- Because the standard DCS tankers will not fly below 160ish knots, the UH-60L mod team have included a modified version of the KC-130 tanker designed for refueling at speeds of 120-130kts.
2022-03-17 21:47:05 +01:00
C. Perreau
d9eac37766 Update README.md 2022-03-17 20:31:52 +01:00
Khopa
39a5d4e7c4 Properly credit CC artwork author. 2022-03-17 20:31:48 +01:00
Khopa
ce4d388e27 🇺🇦 Support for Ukraine 2022-03-17 20:31:43 +01:00
RndName
3a318fbbdf Add OCA/Runway loadouts for F-15E and F-16C.
Unguided bombs for the AI.

cherry-pick from ac5d20ff82
2022-03-17 20:28:12 +01:00
RndName
526d082020 Loaadouts for the Tornado GR4
cherry-pick from 9f7f391609
2022-03-17 20:23:34 +01:00
Hornet2041
3d68eabcaa Remove Fw A-8 and D-9 from Germany 1940/1942 faction
cherry-pick from 8a66bf2e09
2022-03-17 20:20:51 +01:00
RndName
409a099339 changelog 2022-02-02 21:31:38 +01:00
RndName
0ba86af4fd Add NASAM Radar and Launcher to radar_db
This will allow correct calculation of the threat range and prevent creating a threat zone when the SR is dead.

cherry-pick from 3afc6ba
2022-02-02 21:31:38 +01:00
MetalStormGhost
99d4cb7ad5 Tolerate empty TGOs in max_threat_range.
cherry-pick from 3231d00
2022-02-02 21:31:21 +01:00
SnappyComebacks
815849ae64 Typhoon GR4 and Typhoon IDS images (#1970)
* Add icons for Tornado GR4 and Tornado IDS.

cherry-pick from b3a3eb4
2022-02-02 21:31:07 +01:00
Starfire13
31efac53f4 Update bluefor_modern.json
cherry-pick from 0300b77
2022-02-02 21:30:51 +01:00
RndName
3ede2b5755 Add otion to ignore the empty install dir warning message
Adds a do not show again checkbox to the empty install dir warning popup on lib start. This only appears when the install dir is empty to prevent mission scripting replacement.

Also adds a warning message to the log when the dir is empty. Will help to identify this in bug reports.

cherry-pick from 93a0db3
2022-02-02 21:30:35 +01:00
leemarov
de43c0d621 Fix unculled zones not updating when needed.
cherry-pick from 5d29184
2022-02-02 21:30:18 +01:00
MetalStormGhost
7a6ec30368 Add missing HTTPS to pydcs URL.
cherry-pick from ecd2f2b
2022-02-02 21:29:51 +01:00
RndName
08c3da9502 Update Iran factions / Revert previous changes
readded ZSU-57 and SA-17 as they can be used from the country even if dcs mission editor leaves the type empty / does not have them for the country.

cherry-pick from 194b4df
2022-02-02 21:28:36 +01:00
RndName
6d389f7612 Fix the P-47D and P-51D radio definition
set the correct ChannelAllocator
from common to SCR-522)

cherry-pick from d9b4342
2022-02-02 21:28:15 +01:00
Starfire13
bc3b6c2a26 Update Vegas Nerve campaign
cherry-pick from eb6c1871
2022-01-07 18:02:11 +01:00
RndName
041cd0057a Fix threat range calculation
threat range calculation had a hidden bug which could possibly lead to wrong threat range calculation

cherry-pick from 808db05d
2022-01-07 15:47:26 +01:00
RndName
57ee611d06 Update Distance comparison operators
remove order=True and implement the comparison operators manually to fix typing error

cherry-pick from 795df1a9
2022-01-07 15:47:11 +01:00
RndName
f63fae2d4f Fix incorrect threat range of SA-5
The SA-5 was not part of the radar_db.py and therefore the threat_range calculation was wrong / ever LN counted as threat even when the TR was dead. Also fixed a wrong unit for the SA-11 TELAR.

cherry-pick from 54745e78
2022-01-07 14:49:02 +01:00
RndName
cd120047cb Update Version to 5.1.1 2022-01-07 14:44:50 +01:00
RndName
7bb41d07b3 Revert "Remove front line minimum distance."
This reverts commit dbe9691d
2022-01-07 09:59:14 +01:00
RndName
8aca036bf0 Update pydcs
Latest version with the new terrain from 2.7.9.18080

cherry-pick from 82dfeb8a
2022-01-06 21:30:50 +01:00
RndName
878695b2c7 Update for the nevada map update
Conflicttheater and Airfields needed an update as nearly all Nevada Airports were renamed with the last map update

cherry-pick from 8df9f698
2022-01-06 21:30:35 +01:00
Starfire13
f57bd2f253 Update campaign Exercise Vegas Nerve by Starfire
cherry-pick from 19713e61
2022-01-06 18:07:54 +01:00
RndName
cf7330e938 Update SA-5 Generator for factions without the TinShield
- added an EarlySA5Generator with the P-19 FlatFace SR which is supported by all factions
- allows factions without access to the TinShield to have a working SA-5

cherry-pick from f526681f
2022-01-05 01:09:49 +01:00
RndName
e56abbe768 Add Igla manpad INS ground unit
cherry-pick from 77edeac9
2022-01-04 23:56:37 +01:00
RndName
b7a92a2ebf Campaign submission from Fuzzle
- new campaign Scenic Route 2 - Dust To Dust including NATO_OIF faction and 3 squadrons
- update for pacific repartee

cherry-pick from 2c0d7c8e
2022-01-04 23:56:37 +01:00
RndName
a17b8dac7f Update faction Iran 1988 & 2015
support for the latest pydcs export as some units were not usable by the country

cherry-pick from 5c0227b8
2022-01-04 21:31:31 +01:00
MetalStormGhost
dbe9691d9e Remove front line minimum distance.
This isn't what it says. It doesn't enforce a minimum distance between
points, but a minimum path length, which isn't useful and isn't a
documented requirement.

cherry-pick from c844c364
2022-01-02 23:59:45 +01:00
MetalStormGhost
d2eb98bcc5 Added option for only night missions.
- Moved the night mission setting back from the Mission restrictions section to the Mission difficulty section.
- Changed the Night/day mission option into a dropdown menu.

Resolves #1786
2022-01-02 13:23:36 +01:00
RndName
475cb4851a Cleanup the killed map objects recognition
removed the extra array to track killed_map_objects and reuse the existing killed_ground_units routine to remove duplicate code and possible confusion

cherry-pick from 41392585
2022-01-02 12:55:42 +01:00
MetalStormGhost
24e904134e Update multi-part Caucasus campaigns. 2022-01-02 01:52:38 +01:00
RndName
365eaa98f7 Update pydcs 2022-01-02 01:23:49 +01:00
RndName
e070d5bf0d Rework killed map_objects recognition
- removed the map_object_id from the TGO
- add a new TriggerRule with the MapObjectIsDead Condition which adds the map object to the killed_map_objects array in the state.json
- Use the trigger_zone_name as the unique identifier used for the unit_map to recognize the kill
2022-01-02 01:23:48 +01:00
MetalStormGhost
ffd8152b36 Improved the F-104G CAP loadout.
Replace hydra pods with more AIM-9s.
2022-01-02 01:23:06 +01:00
Starfire13
11812f07fc Added missing squadron nickname 2022-01-02 01:23:05 +01:00
RndName
ba4de3dc27 Fix Viggen FR22 group channel id and mention in changelog
cherry-pick from deb7227c
2021-12-30 18:49:00 +01:00
RndName
208382f3c4 Update Viggen radio presets for 2.7.9
the radio presets of the Viggen were changed with the recent dcs update, they will no longer be automatically generated so we have to add them manually
cherry-pick from 0063d8db
2021-12-30 18:19:57 +01:00
RndName
aadb2dd54d Updated changelog
- marked as compat as currently there seems to be no change that the update is not save compat
- mentioned the recent dcs update
2021-12-30 11:13:37 +01:00
RndName
1b2eb1236f Update Fuzzle Campaigns
cherry-pick from 81cbf807
2021-12-30 11:11:42 +01:00
Dan Albert
00bdbf65ce Update pydcs.
cherry-pick from d81c6a0
2021-12-28 17:52:12 +01:00
MetalStormGhost
290646c8ae Updates for WW2 loadouts.
Major changes:
 - FW190A8 ground attack loadout WGr21 rockets (primarily an air-to-air weapon) replaced with SC500J or AB500 bombs.
 - FW190D9 WGr21 rockets replaced with R4M rocket packs and SC500 bombs for CAS.
 - Added droptanks for I-16.
 - Ju-88A4 CAS loadout changed for AB500 cluster bombs.
 - Mosquito now has rockets which were introduced in DCS World 2.7.9.17830 open beta
 - Added more/heavier bombs to P-47
 - P-51 now has a separate OCA/Runway loadout with bombs. Other ground attack loadouts switched to rockets.

Also includes an SA342Minigun loadout which we didn't previously have.

cherry-pick from 5684570
2021-12-28 17:50:56 +01:00
Dan Albert
8f58832527 Add ECM pod to Viper A2G loadouts.
Didn't add to the A2A loadouts since the ECM interferes with radar
operation and is otherwise weight and drag. It does have a radar
priority mode though, so maybe it should be added there too?

cherry-pick from c0dfa77
2021-12-28 17:50:02 +01:00
Dan Albert
f426a16e9d Add weapon data for the AN/ALQ-184.
cherry-pick from 0c7d549
2021-12-28 17:49:38 +01:00
Dan Albert
6f4e436305 Fix misclassification of the HTS as a TGP.
cherry-pick from fc4022a
2021-12-28 17:49:16 +01:00
RndName
08288c9da9 Mention squadron config and f-104 mod in changelog 2021-12-23 13:46:18 +01:00
RndName
9bfac347db Finish implementation of add/remove squadrons from AirWingConfigurationDialog
Users can now add and remove squadrons with specific buttons. this also allows new aircraft types to be added as well.

- rebased existing PR to develop
- reverted observable and changed to signals
- changed the general concept so that changes only affect the ui data model and not the game directly. Game will only be updated on apply
- removed unused code
- adopt to review comments
- allow user to choose a predefined squadron preset (also alow none value to use the random generator)
- Reuse the squadron defs from the default assigner in the AirWing class
- allow user to re-roll the squadron nickname (also added new ui icons for the button)

cherry-pick from d2f7785f
2021-12-23 13:45:13 +01:00
Richard Pump
22a35b0d2e Added squadron configuration gui
- added add/remove buttons to Air Wing Menu, implemented remove

cherry-pick from e4ba9a8b
2021-12-23 13:45:13 +01:00
Starfire13
e56d8de800 Fixed typo in squadrondefgenerator.py
cherry-pick from 4803ae5f
2021-12-23 13:43:49 +01:00
Starfire13
c806980ab8 Fixed typo in naming.py
cherry-pick from d739d830
2021-12-23 13:43:24 +01:00
MetalStormGhost
4750fff114 Re-add From Caen to Evreux campaign.
cherry-pick from 3626fa79
2021-12-23 13:43:04 +01:00
MetalStormGhost
b405b8e7ee F-104 mod support.
cherry-pick from 88bc4fd8
2021-12-23 13:42:49 +01:00
RndName
074ae328ee Revert "Temporary Fix for broken upload action"
This reverts commit 64d273b433.
2021-12-21 09:35:18 +01:00
RndName
5fd6ccb81d Enable / Disable game specific functions if no game is loaded
- Disable Save, Settings, Stats and Notes if game is none

cherry-pick from f9ed61d1
2021-12-20 13:56:20 +01:00
RndName
64d273b433 Temporary Fix for broken upload action
The upload-artifact version 2.3 has problems with 0byte files and prevent the build action from beeing completed.
This temporary fix should be reverted when they fixed the bug.
2021-12-13 20:49:50 +01:00
RndName
d7f833ecfb Fix empty convoy when transfer is not completable
fixes #1553
2021-12-13 20:49:49 +01:00
RndName
5d89b9ac25 Add Easy Communication Setting
allow to enforce the mission setting for easy communication
cherry-pick from 2c21644
2021-12-12 01:24:01 +01:00
RndName
056851765f Update Branch to V5.1.0 2021-12-10 00:14:32 +01:00
MetalStormGhost
54bf9357bf Add option to limit convoy travel distances.
CPU load seems to scale with route length, so add an option to limit
the length of the convoy route. The tradeoff is that the performance
sensitive route won't necessarily be a correct route.
2021-12-10 00:14:32 +01:00
Dan Albert
dfcc458479 Note HTS support in the changelog. 2021-12-10 00:14:31 +01:00
Dan Albert
f3f0e23eff Add weapon data and loadouts for the HTS.
Used in the default Viper DEAD and SEAD loadouts.
2021-12-10 00:14:30 +01:00
Dan Albert
5ce6de6645 Update pydcs.
HTS support.
2021-12-10 00:14:30 +01:00
Dan Albert
50b4d420fe Clarify the role of the faction aircraft list. 2021-12-10 00:14:29 +01:00
MetalStormGhost
224c78ac11 Add a new base banner for FARPs. 2021-12-10 00:14:28 +01:00
Dan Albert
227b054279 Fix incorrect radio range for AN/ARC-222.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1740
2021-12-10 00:14:27 +01:00
Khopa
0093fa0fe1 Fix #1718 : Remove Merkava IV from faction "Israel 1982" 2021-12-10 00:14:02 +01:00
Khopa
3cdfd9d7d2 Fix #1717 : Removed SA5Generator from Iran 1988 faction 2021-12-10 00:14:02 +01:00
Khopa
5e2e07da80 pydcs : Updated to latest pydcs master commit 2021-12-10 00:14:01 +01:00
Khopa
5f3f6f8c9f SA5Generator : Added an ural truck to reload SA-5 sites 2021-12-10 00:13:56 +01:00
RndName
0077b04698 Update UI when buy or sell raises exception
fixes #1562 cherry-pick from 34100d1c
2021-11-26 21:00:44 +01:00
RndName
bb1e314260 Generate unit_id for helipads
fixes mission scripting error (1748)

cherry-picked from 676a256
2021-11-22 15:10:03 +01:00
Dan Albert
d710c2631a Update branch to 5.0.1. 2021-11-13 11:48:25 -08:00
Dan Albert
7278878266 Remove incompatible campaigns.
(cherry picked from commit 2ea2ecec94)
2021-11-13 11:20:42 -08:00
Dan Albert
ec8391bbfb Add A-4E squadrons.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1716

(cherry picked from commit a3038e75cf)
2021-11-13 11:16:27 -08:00
Dan Albert
74504173c7 Add IRIAF F-4E squadron.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1721

(cherry picked from commit 5dcd4580c3)
2021-11-13 11:12:36 -08:00
MetalStormGhost
792c7c5091 Update Marianas campaigns.
More FARPs to both Orote Point and Andersen Northwest Field.

(cherry picked from commit 68b48ad610)
2021-11-13 11:10:26 -08:00
Dan Albert
b7e9a4a243 Drop a few unsupported campaigns.
Probably more coming but these are the ones that are confirmed not
happening before release.

(cherry picked from commit 94f65d8f70)
2021-11-12 14:03:21 -08:00
Dan Albert
bdbb338e83 Update Syria full campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1728

(cherry picked from commit 46e5299c60)
2021-11-12 13:42:55 -08:00
Dan Albert
98fa70c73d Update Pacific Repartee.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1707

(cherry picked from commit d645b4fe73)
2021-11-06 19:13:40 -07:00
Dan Albert
7991e0157d Update Caucasus multi-part Georgia.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1706

(cherry picked from commit 68c794f608)
2021-11-06 19:13:39 -07:00
Dan Albert
21643c500f 5.0 changelog fixes.
(cherry picked from commit 475d18b701)
2021-11-06 19:07:18 -07:00
Dan Albert
b4ddfb9dfd Prevent assigning fixed wing squadrons to FARPs.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1671

(cherry picked from commit e9634b7066)
2021-11-06 18:45:05 -07:00
Dan Albert
d1e50a5bbe Fix fixed wing squadrons retreating to FARPs.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1615

(cherry picked from commit 35900c2350)
2021-11-06 16:54:59 -07:00
Dan Albert
a188f7b7e5 Add missing NavalControlPoint case for BAI.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1702

(cherry picked from commit 7a18d160c8)
2021-11-06 16:36:44 -07:00
Dan Albert
4f76b73de2 Split runway attack list from strike list.
Not all strike aircraft are capable of runway attack, so copy the strike
list into the runway attack list and remove the incompatible aircraft.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1703

(cherry picked from commit a33104d7c4)
2021-11-06 16:26:36 -07:00
Dan Albert
a4b09bc973 Update Skynet to 2.4.0.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1713

(cherry picked from commit 7f57180da4)
2021-11-06 16:22:11 -07:00
Dan Albert
a792c73cae Restore missing income multiplier labels.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1712

(cherry picked from commit a23e7fe83d)
2021-11-06 16:20:05 -07:00
Dan Albert
113380661c Updates for Syria full.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1675

(cherry picked from commit c854508381)
2021-11-06 16:16:19 -07:00
Dan Albert
3ab9b25b08 Updates for Northern Russia.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1676

(cherry picked from commit d1cf8915e3)
2021-11-06 16:16:19 -07:00
Dan Albert
29d4ca38f9 Check in Pacific Repartee campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1701

(cherry picked from commit d9b5b87f2b)
2021-11-06 16:16:18 -07:00
Dan Albert
6a3ff8d6ac Tell Qt that we actually want text to fit.
Why isn't this the default?

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1670

(cherry picked from commit 5923ba21de)
2021-11-03 19:36:12 -07:00
Dan Albert
c1f194b3d5 Add E-2D variant of the E-2C.
In game this is an E-2D, but the ID of the aircraft in the game data is
E-2C. Presumably it was repainted at some point in a DCS update.

This adds a variant but doesn't delete the old one to avoid breaking
campaigns and factions. I moved blufor modern to the E-2D but the rest
of the factions are too old so we'll let them pretend.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1696

(cherry picked from commit 33f00fb811)
2021-10-30 15:29:41 -07:00
Dan Albert
a6809e0103 Document Su-33 carrier takeoff fix.
https://github.com/dcs-liberation/dcs_liberation/issues/1352
(cherry picked from commit ae99558f40)
2021-10-30 15:17:18 -07:00
Dan Albert
a559aa8646 Fix (presumable) accidental edit of A-4 pylons.
I'm not sure if this was a mistaken edit that the author made on check-
in or if we have a broken script that's generating these. For now I've
manually fixed it.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1574

(cherry picked from commit 70dbe7c9ca)
2021-10-30 15:06:26 -07:00
ghost
c766322960 Update Marianas campaigns to use the Tu-142.
(cherry picked from commit 2d0b5023c9)
2021-10-30 15:06:18 -07:00
ghost
c7581568c2 Enable anti-ship missions for the Tu-142.
This is the only mission type that the Tu-142 is capable of.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1683

(cherry picked from commit 92fdd0b80d)
2021-10-30 15:06:08 -07:00
Starfire13
f0827a429e Campaigns updated to 9.1
Peace Spring, Vectron's Claw, and Vegas Nerve have been updated to 9.1. Squadrons have also been completely overhauled to work much better, so there should no longer be a whole bunch of squadrons on OPFOR that are never used.

(cherry picked from commit 2d93ac58fc)
2021-10-30 15:06:00 -07:00
Starfire13
c2ee44d8bb H-6J Loadout Update
Added loadouts for DEAD and OCA/Aircraft as those are default mission types for the H-6J.

(cherry picked from commit 79924a59bc)
2021-10-30 15:05:57 -07:00
MetalStormGhost
af362be3a2 Forcibly allow GPS for more non-US factions.
Enable unrestricted_satnav with non-US factions operating either the
F-16CM or the F/A-18C to allow the use of GPS in missions.

(cherry picked from commit 545f974552)
2021-10-30 15:05:50 -07:00
Dan Albert
80c8563d67 Use stored alignment for the F-14A.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1689

(cherry picked from commit 2699a38f7b)
2021-10-30 15:05:28 -07:00
MetalStormGhost
f44654f66e Use Chinese navy group generators for China.
Replaced Chinese factions' Type54GroupGenerator naval group generators with
ChineseNavyGroupGenerator to make Type 52B and Type 52C destroyers also
spawn besides frigates.

(cherry picked from commit 13d52803d6)
2021-10-30 15:04:05 -07:00
MetalStormGhost
ca7f61c938 Added H-6J support for China 2010 and Iraq 1991.
Includes H-6J loadouts by @Starfire13

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1661

(cherry picked from commit 49033f67f3)
2021-10-30 15:03:22 -07:00
MetalStormGhost
10ccada17a Marianas campaigns 2.7.7.14727
Updated Marianas Mount Barrigada and Landing at Agat campaigns to DCS World 2.7.7.14727 open beta.

(cherry picked from commit 410077467b)
2021-10-30 15:03:18 -07:00
208 changed files with 10679 additions and 1611 deletions

View File

@@ -1,4 +1,6 @@
![Logo](https://i.imgur.com/c2k18E1.png) [![Logo](https://i.imgur.com/HJBT4BL.png)](https://shdwp.github.io/ukraine/)
(Github Readme Banner and Splash screen Artwork by Andriy Dankovych, CC BY-SA 4.0)
[![Patreon](https://img.shields.io/badge/patreon-become%20a%20patron-orange?logo=patreon)](https://patreon.com/khopa) [![Patreon](https://img.shields.io/badge/patreon-become%20a%20patron-orange?logo=patreon)](https://patreon.com/khopa)

View File

@@ -1,3 +1,48 @@
# 5.2.0
Saves from 5.1.0 are compatible with 5.2.0
## Features/Improvements
* **[Modding]** Add UH-60L mod support
* **[Campaign]** Vegas Nerve campaign update
* **[Campaign]** Add 3 new campaigns from Sith1144
## Fixes
* **[Mission Generation]** Fixed incorrect SA-5 and NASAMS threat range when TR destroyed. It will not count as threat anymore when the TR is dead.
* **[Mission Generation]** Fixed "Max Threat Range" error
* **[Mission Generation]** Fix unculled zones not updating when needed
* **[Data]** Removed Fw 190 A-8 and D-9 from Germany 1940 and 1942 faction list for historical accuracy.
* **[Data]** Updated Loadouts for Tornado GR4, F-15E and F-16C
* **[Data]** Corrected some unit data
* **[UI]** Fixed various UI issues (for example Scaling and HighDPI)
* **[UI]** Typhoon GR4 and IDS images
# 5.1.0
Saves from 5.0.0 are compatible with 5.1.0
## Features/Improvements
* **[Engine]** Support for DCS 2.7.9.17830 and newer, including the HTS and ECM pod.
* **[Campaign]** Add option to manually add and remove squadrons and different aircraft type in the new game wizard / air wing configuration dialog.
* **[Mission Generation]** Add Option to enforce the Easy Communication setting for the mission
* **[Mission Generation]** Add Option to select between only night missions, day missions or any time (default).
* **[Modding]** Add F-104 mod support
## Fixes
* **[Campaign]** Fixed some minor issues in campaigns which generated error messages in the log.
* **[Campaign]** Changed the way how map object / scenery kills where tracked. This fixes issues with kill recognition after map updates from ED which change the object ids and therefore prevent correct kill recognition.
* **[Mission Generation]** Fixed incorrect radio specification for the AN/ARC-222.
* **[Mission Generation]** Fixed mission scripting error when using a dedicated server.
* **[Mission Generation]** Fixed an issue where empty convoys lead to an index error when a point capture made a pending transfer of units not completable anymore.
* **[Mission Generation]** Corrected Viggen FR22 & FR24 preset channels for the DCS 2.7.9 update
* **[Mission Generation]** Fixed the SA-5 Generator to use the P-19 FlatFace SR as a Fallback radar if the faction does not have access to the TinShield SR.
* **[UI]** Enable / Disable the settings, save and stats actions if no game is loaded to prevent an error as these functions can only be used on a valid game.
* **[UI]** Added missing icons for Tornado GR4, and Tornado IDS.
# 5.0.0 # 5.0.0
Saves from 4.x are not compatible with 5.0. Saves from 4.x are not compatible with 5.0.
@@ -11,14 +56,14 @@ Saves from 4.x are not compatible with 5.0.
* **[Campaign]** Squadrons now have a home base and will not operate out of other bases. See https://github.com/dcs-liberation/dcs_liberation/issues/1145 for status. * **[Campaign]** Squadrons now have a home base and will not operate out of other bases. See https://github.com/dcs-liberation/dcs_liberation/issues/1145 for status.
* **[Campaign]** Aircraft now belong to squadrons rather than bases to support squadron location transfers. * **[Campaign]** Aircraft now belong to squadrons rather than bases to support squadron location transfers.
* **[Campaign]** Skipped turns are no longer counted as defeats on front lines. * **[Campaign]** Skipped turns are no longer counted as defeats on front lines.
* **[Campaign AI]** Overhauled campaign AI target prioritization. This currently only affects the ordering of DEAD missions. * **[Campaign AI]** Overhauled campaign AI target prioritization.
* **[Campaign AI]** Player front line stances can now be automated. Improved stance selection for AI. * **[Campaign AI]** Player front line stances can now be automated. Improved stance selection for AI.
* **[Campaign AI]** Reworked layout of hold, join, split, and ingress points. Should result in much shorter flight plans in general while still maintaining safe join/split/hold points. * **[Campaign AI]** Reworked layout of hold, join, split, and ingress points. Should result in much shorter flight plans in general while still maintaining safe join/split/hold points.
* **[Campaign AI]** Auto-planning mission range limits are now specified per-aircraft. On average this means that longer range missions will now be plannable. The limit only accounts for the direct distance to the target, not the path taken. * **[Campaign AI]** Auto-planning mission range limits are now specified per-aircraft. On average this means that longer range missions will now be plannable. The limit only accounts for the direct distance to the target, not the path taken.
* **[Campaign AI]** Transport aircraft will now be bought only if necessary at control points which can produce ground units and are capable to operate transport aircraft. * **[Campaign AI]** Transport aircraft will now be bought only if necessary at control points which can produce ground units and are capable to operate transport aircraft.
* **[Campaign AI]** Aircraft will now only be automatically purchased or assigned at appropriate bases. Naval aircraft will default to only operating from carriers, Harriers will default to LHAs and shore bases, helicopters will operate from anywhere. This can be customized per-squadron. * **[Campaign AI]** Aircraft will now only be automatically purchased or assigned at appropriate bases. Naval aircraft will default to only operating from carriers, Harriers will default to LHAs and shore bases, helicopters will operate from anywhere. This can be customized per-squadron.
* **[Engine]** Support for DCS 2.7.7.14727 and newer, including support for F-16 CBU-105s, SA-5s, and the Forrestal. * **[Engine]** Support for DCS 2.7.7.14727 and newer, including support for F-16 CBU-105s, SA-5s, and the Forrestal.
* **[Kneeboard]** Minimum required fuel estimates have been added to the kneeboard for aircraft with supporting data (currently only the Hornet). * **[Kneeboard]** Minimum required fuel estimates have been added to the kneeboard for aircraft with supporting data (currently only the Hornet and Viper).
* **[Kneeboard]** QNH (pressure MSL) and temperature have been added to the kneeboard. * **[Kneeboard]** QNH (pressure MSL) and temperature have been added to the kneeboard.
* **[Mission Generation]** EWRs are now also headed towards the center of the conflict * **[Mission Generation]** EWRs are now also headed towards the center of the conflict
* **[Mission Generation]** FACs can now use FC3 compatible laser codes. Note that this setting is global, not per FAC. * **[Mission Generation]** FACs can now use FC3 compatible laser codes. Note that this setting is global, not per FAC.
@@ -26,19 +71,23 @@ Saves from 4.x are not compatible with 5.0.
* **[Modding]** Campaigns can now define a default start date. * **[Modding]** Campaigns can now define a default start date.
* **[Modding]** Campaigns now specify the squadrons that are present in the campaign, their roles, and their starting bases. Players can customize this at game start but the campaign will choose the defaults. * **[Modding]** Campaigns now specify the squadrons that are present in the campaign, their roles, and their starting bases. Players can customize this at game start but the campaign will choose the defaults.
* **[New Game Wizard]** Can now customize the player's air wing before campaign start to disable, relocate, or rename squadrons. * **[New Game Wizard]** Can now customize the player's air wing before campaign start to disable, relocate, or rename squadrons.
* **[Plugins]** Updated SkynetIADS to 2.3.0 (adds SA-5 support). * **[Plugins]** Updated SkynetIADS to 2.4.0 (adds SA-5 support).
* **[UI]** Sell Button for aircraft will be disabled if there are no units available to be sold or all are already assigned to a mission * **[UI]** Sell Button for aircraft will be disabled if there are no units available to be sold or all are already assigned to a mission
* **[UI]** Enemy aircraft inventory now viewable in the air wing menu. * **[UI]** Enemy aircraft inventory now viewable in the air wing menu.
## Fixes ## Fixes
* **[Campaign]** Naval control points will no longer claim ground objectives during campaign generation and prevent them from spawning. * **[Campaign]** Naval control points will no longer claim ground objectives during campaign generation and prevent them from spawning.
* **[Campaign]** Units aboard suck cargo ships will now have their losses tracked properly. * **[Campaign]** Units aboard sunk cargo ships will now have their losses tracked properly.
* **[Mission Generation]** Mission results and other files will now be opened with enforced utf-8 encoding to prevent an issue where destroyed ground units were untracked because of special characters in their names. * **[Mission Generation]** Mission results and other files will now be opened with enforced utf-8 encoding to prevent an issue where destroyed ground units were untracked because of special characters in their names.
* **[Mission Generation]** Fixed generation of landing waypoints so that the AI obeys them. * **[Mission Generation]** Fixed generation of landing waypoints so that the AI obeys them.
* **[Mission Generation]** AI carrier aircraft with a start time of T+0 will now start at T+1s to avoid traffic jams. * **[Mission Generation]** AI carrier aircraft with a start time of T+0 will now start at T+1s to avoid traffic jams.
* **[Mission Generation]** Fixed cases of unused aircraft not being spawned at airfields as soon as any airport filled up. * **[Mission Generation]** Fixed cases of unused aircraft not being spawned at airfields as soon as any airport filled up.
* **[Mission Generation]** Fixed cases with multiple client flights of the same airframe all received the same preset channels. * **[Mission Generation]** Fixed cases with multiple client flights of the same airframe all received the same preset channels.
* **[Mission Generation]** F-14A is now generated with stored alignment.
* **[Mission Generation]** Su-33s set to cold or warm start on the Kuznetsov will always be generated as runway starts to avoid the AI getting stuck.
* **[Mission Generation]** Fixed AI not receiving anti-ship tasks against carriers and LHAs.
* **[Mods]** Fixed broken A-4 support causing no weapons to be available.
* **[UI]** Selling of Units is now visible again in the UI dialog and shows the correct amount of sold units * **[UI]** Selling of Units is now visible again in the UI dialog and shows the correct amount of sold units
* **[UI]** Fixed bug where an incompatible campaign could be generated if no action is taken on the campaign selection screen. * **[UI]** Fixed bug where an incompatible campaign could be generated if no action is taken on the campaign selection screen.

View File

@@ -5,10 +5,8 @@ from typing import Optional, TYPE_CHECKING
from game.squadrons import Squadron from game.squadrons import Squadron
from game.squadrons.squadrondef import SquadronDef from game.squadrons.squadrondef import SquadronDef
from game.squadrons.squadrondefloader import SquadronDefLoader
from gen.flights.flight import FlightType from gen.flights.flight import FlightType
from .campaignairwingconfig import CampaignAirWingConfig, SquadronConfig from .campaignairwingconfig import CampaignAirWingConfig, SquadronConfig
from .squadrondefgenerator import SquadronDefGenerator
from ..dcs.aircrafttype import AircraftType from ..dcs.aircrafttype import AircraftType
from ..theater import ControlPoint from ..theater import ControlPoint
@@ -25,14 +23,6 @@ class DefaultSquadronAssigner:
self.game = game self.game = game
self.coalition = coalition self.coalition = coalition
self.air_wing = coalition.air_wing self.air_wing = coalition.air_wing
self.squadron_defs = SquadronDefLoader(game, coalition).load()
self.squadron_def_generator = SquadronDefGenerator(self.coalition)
def claim_squadron_def(self, squadron: SquadronDef) -> None:
try:
self.squadron_defs[squadron.aircraft].remove(squadron)
except ValueError:
pass
def assign(self) -> None: def assign(self) -> None:
for control_point in self.game.theater.control_points_for( for control_point in self.game.theater.control_points_for(
@@ -47,7 +37,6 @@ class DefaultSquadronAssigner:
) )
continue continue
self.claim_squadron_def(squadron_def)
squadron = Squadron.create_from( squadron = Squadron.create_from(
squadron_def, control_point, self.coalition, self.game squadron_def, control_point, self.coalition, self.game
) )
@@ -74,7 +63,7 @@ class DefaultSquadronAssigner:
# If we can't find any squadron matching the requirement, we should # If we can't find any squadron matching the requirement, we should
# create one. # create one.
return self.squadron_def_generator.generate_for_task( return self.air_wing.squadron_def_generator.generate_for_task(
config.primary, control_point config.primary, control_point
) )
@@ -105,7 +94,7 @@ class DefaultSquadronAssigner:
# No premade squadron available for this aircraft that meets the requirements, # No premade squadron available for this aircraft that meets the requirements,
# so generate one if possible. # so generate one if possible.
return self.squadron_def_generator.generate_for_aircraft(aircraft) return self.air_wing.squadron_def_generator.generate_for_aircraft(aircraft)
@staticmethod @staticmethod
def squadron_compatible_with( def squadron_compatible_with(
@@ -121,18 +110,24 @@ 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]:
for squadron in self.squadron_defs[aircraft]: for squadron in self.air_wing.squadron_defs[aircraft]:
if self.squadron_compatible_with(squadron, task, control_point): if not squadron.claimed and self.squadron_compatible_with(
squadron, task, control_point
):
return squadron return squadron
return None return None
def find_squadron_by_name( def find_squadron_by_name(
self, name: str, task: FlightType, control_point: ControlPoint self, name: str, task: FlightType, control_point: ControlPoint
) -> Optional[SquadronDef]: ) -> Optional[SquadronDef]:
for squadrons in self.squadron_defs.values(): for squadrons in self.air_wing.squadron_defs.values():
for squadron in squadrons: for squadron in squadrons:
if squadron.name == name and self.squadron_compatible_with( if (
squadron, task, control_point, ignore_base_preference=True not squadron.claimed
and squadron.name == name
and self.squadron_compatible_with(
squadron, task, control_point, ignore_base_preference=True
)
): ):
return squadron return squadron
return None return None
@@ -140,8 +135,10 @@ class DefaultSquadronAssigner:
def find_squadron_for_task( def find_squadron_for_task(
self, task: FlightType, control_point: ControlPoint self, task: FlightType, control_point: ControlPoint
) -> Optional[SquadronDef]: ) -> Optional[SquadronDef]:
for squadrons in self.squadron_defs.values(): for squadrons in self.air_wing.squadron_defs.values():
for squadron in squadrons: for squadron in squadrons:
if self.squadron_compatible_with(squadron, task, control_point): if not squadron.claimed and self.squadron_compatible_with(
squadron, task, control_point
):
return squadron return squadron
return None return None

View File

@@ -12,12 +12,12 @@ from gen.flights.ai_flight_planner_db import aircraft_for_task, tasks_for_aircra
from gen.flights.flight import FlightType from gen.flights.flight import FlightType
if TYPE_CHECKING: if TYPE_CHECKING:
from game.coalition import Coalition from game.factions.faction import Faction
class SquadronDefGenerator: class SquadronDefGenerator:
def __init__(self, coalition: Coalition) -> None: def __init__(self, faction: Faction) -> None:
self.coalition = coalition self.faction = faction
self.count = itertools.count(1) self.count = itertools.count(1)
self.used_nicknames: set[str] = set() self.used_nicknames: set[str] = set()
@@ -26,7 +26,7 @@ class SquadronDefGenerator:
) -> Optional[SquadronDef]: ) -> Optional[SquadronDef]:
aircraft_choice: Optional[AircraftType] = None aircraft_choice: Optional[AircraftType] = None
for aircraft in aircraft_for_task(task): for aircraft in aircraft_for_task(task):
if aircraft not in self.coalition.faction.aircrafts: if aircraft not in self.faction.aircrafts:
continue continue
if not control_point.can_operate(aircraft): if not control_point.can_operate(aircraft):
continue continue
@@ -44,7 +44,7 @@ class SquadronDefGenerator:
return SquadronDef( return SquadronDef(
name=f"Squadron {next(self.count):03}", name=f"Squadron {next(self.count):03}",
nickname=self.random_nickname(), nickname=self.random_nickname(),
country=self.coalition.country_name, country=self.faction.country,
role="Flying Squadron", role="Flying Squadron",
aircraft=aircraft, aircraft=aircraft,
livery=None, livery=None,
@@ -91,9 +91,9 @@ class SquadronDefGenerator:
"Brass", "Brass",
"Brave", "Brave",
"Brazen", "Brazen",
"Bronze",
"Brown", "Brown",
"Brutal", "Brutal",
"Brzone",
"Burning", "Burning",
"Buzzing", "Buzzing",
"Celestial", "Celestial",

View File

@@ -40,7 +40,7 @@ class Coalition:
self.procurement_requests: OrderedSet[AircraftProcurementRequest] = OrderedSet() self.procurement_requests: OrderedSet[AircraftProcurementRequest] = OrderedSet()
self.bullseye = Bullseye(Point(0, 0)) self.bullseye = Bullseye(Point(0, 0))
self.faker = Faker(self.faction.locales) self.faker = Faker(self.faction.locales)
self.air_wing = AirWing(player) self.air_wing = AirWing(player, game, self.faction)
self.transfers = PendingTransfers(game, player) self.transfers = PendingTransfers(game, player)
# Late initialized because the two coalitions in the game are mutually # Late initialized because the two coalitions in the game are mutually

View File

@@ -25,7 +25,7 @@ from dcs.vehicles import AirDefence
TELARS = { TELARS = {
AirDefence._2S6_Tunguska, AirDefence._2S6_Tunguska,
AirDefence.SA_11_Buk_SR_9S18M1, AirDefence.SA_11_Buk_LN_9A310M1,
AirDefence.Osa_9A33_ln, AirDefence.Osa_9A33_ln,
AirDefence.Tor_9A331, AirDefence.Tor_9A331,
AirDefence.Roland_ADS, AirDefence.Roland_ADS,
@@ -38,8 +38,10 @@ TRACK_RADARS = {
AirDefence.Hawk_tr, AirDefence.Hawk_tr,
AirDefence.Patriot_str, AirDefence.Patriot_str,
AirDefence.SNR_75V, AirDefence.SNR_75V,
AirDefence.RPC_5N62V,
AirDefence.Rapier_fsa_blindfire_radar, AirDefence.Rapier_fsa_blindfire_radar,
AirDefence.HQ_7_STR_SP, AirDefence.HQ_7_STR_SP,
AirDefence.NASAMS_Radar_MPQ64F1,
} }
LAUNCHER_TRACKER_PAIRS = { LAUNCHER_TRACKER_PAIRS = {
@@ -52,6 +54,9 @@ LAUNCHER_TRACKER_PAIRS = {
AirDefence.S_75M_Volhov: AirDefence.SNR_75V, AirDefence.S_75M_Volhov: AirDefence.SNR_75V,
AirDefence.Rapier_fsa_launcher: AirDefence.Rapier_fsa_blindfire_radar, AirDefence.Rapier_fsa_launcher: AirDefence.Rapier_fsa_blindfire_radar,
AirDefence.HQ_7_LN_SP: AirDefence.HQ_7_STR_SP, AirDefence.HQ_7_LN_SP: AirDefence.HQ_7_STR_SP,
AirDefence.S_200_Launcher: AirDefence.RPC_5N62V,
AirDefence.NASAMS_LN_B: AirDefence.NASAMS_Radar_MPQ64F1,
AirDefence.NASAMS_LN_C: AirDefence.NASAMS_Radar_MPQ64F1,
} }
UNITS_WITH_RADAR = { UNITS_WITH_RADAR = {
@@ -80,6 +85,8 @@ UNITS_WITH_RADAR = {
AirDefence.Roland_Radar, AirDefence.Roland_Radar,
AirDefence.Snr_s_125_tr, AirDefence.Snr_s_125_tr,
AirDefence.SNR_75V, AirDefence.SNR_75V,
AirDefence.RLS_19J6,
AirDefence.RPC_5N62V,
AirDefence.Rapier_fsa_blindfire_radar, AirDefence.Rapier_fsa_blindfire_radar,
AirDefence.HQ_7_LN_SP, AirDefence.HQ_7_LN_SP,
AirDefence.HQ_7_STR_SP, AirDefence.HQ_7_STR_SP,

View File

@@ -42,17 +42,25 @@ import pydcs_extensions.highdigitsams.highdigitsams as highdigitsams
# PATCH pydcs data with MODS # PATCH pydcs data with MODS
from game.factions.faction_loader import FactionLoader from game.factions.faction_loader import FactionLoader
from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.f104.f104 import VSN_F104G, VSN_F104S, VSN_F104S_AG
from pydcs_extensions.f22a.f22a import F_22A from pydcs_extensions.f22a.f22a import F_22A
from pydcs_extensions.hercules.hercules import Hercules from pydcs_extensions.hercules.hercules import Hercules
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
from pydcs_extensions.su57.su57 import Su_57 from pydcs_extensions.su57.su57 import Su_57
from pydcs_extensions.uh60l.uh60l import UH_60L, KC130J
plane_map["A-4E-C"] = A_4E_C plane_map["A-4E-C"] = A_4E_C
plane_map["F-22A"] = F_22A plane_map["F-22A"] = F_22A
plane_map["Su-57"] = Su_57 plane_map["Su-57"] = Su_57
plane_map["Hercules"] = Hercules plane_map["Hercules"] = Hercules
plane_map["KC130J"] = KC130J
plane_map["JAS39Gripen"] = JAS39Gripen plane_map["JAS39Gripen"] = JAS39Gripen
plane_map["JAS39Gripen_AG"] = JAS39Gripen_AG plane_map["JAS39Gripen_AG"] = JAS39Gripen_AG
plane_map["VSN_F104G"] = VSN_F104G
plane_map["VSN_F104S"] = VSN_F104S
plane_map["VSN_F104S_AG"] = VSN_F104S_AG
helicopter_map["UH-60L"] = UH_60L
vehicle_map["FieldHL"] = frenchpack._FIELD_HIDE vehicle_map["FieldHL"] = frenchpack._FIELD_HIDE
vehicle_map["HARRIERH"] = frenchpack._FIELD_HIDE_SMALL vehicle_map["HARRIERH"] = frenchpack._FIELD_HIDE_SMALL

View File

@@ -101,7 +101,7 @@ class StateData:
#: Names of aircraft units that were killed during the mission. #: Names of aircraft units that were killed during the mission.
killed_aircraft: List[str] killed_aircraft: List[str]
#: Names of vehicle (and ship) units that were killed during the mission. #: Names of vehicles, ships or buildings that were killed during the mission.
killed_ground_units: List[str] killed_ground_units: List[str]
#: List of descriptions of destroyed statics. Format of each element is a mapping of #: List of descriptions of destroyed statics. Format of each element is a mapping of

View File

@@ -266,8 +266,15 @@ class Faction:
self.remove_aircraft("A-4E-C") self.remove_aircraft("A-4E-C")
if not mod_settings.hercules: if not mod_settings.hercules:
self.remove_aircraft("Hercules") self.remove_aircraft("Hercules")
if not mod_settings.uh_60l:
self.remove_aircraft("UH-60L")
self.remove_aircraft("KC130J")
if not mod_settings.f22_raptor: if not mod_settings.f22_raptor:
self.remove_aircraft("F-22A") self.remove_aircraft("F-22A")
if not mod_settings.f104_starfighter:
self.remove_aircraft("VSN_F104G")
self.remove_aircraft("VSN_F104S")
self.remove_aircraft("VSN_F104S_AG")
if not mod_settings.jas39_gripen: if not mod_settings.jas39_gripen:
self.remove_aircraft("JAS39Gripen") self.remove_aircraft("JAS39Gripen")
self.remove_aircraft("JAS39Gripen_AG") self.remove_aircraft("JAS39Gripen_AG")

View File

@@ -248,7 +248,7 @@ class Game:
naming.namegen = self.name_generator naming.namegen = self.name_generator
LuaPluginManager.load_settings(self.settings) LuaPluginManager.load_settings(self.settings)
ObjectiveDistanceCache.set_theater(self.theater) ObjectiveDistanceCache.set_theater(self.theater)
self.compute_conflicts_position() self.compute_unculled_zones()
if not game_still_initializing: if not game_still_initializing:
self.compute_threat_zones() self.compute_threat_zones()
@@ -389,8 +389,6 @@ class Game:
return self.process_win_loss(turn_state) return self.process_win_loss(turn_state)
# Plan flights & combat for next turn # Plan flights & combat for next turn
with logged_duration("Computing conflict positions"):
self.compute_conflicts_position()
with logged_duration("Threat zone computation"): with logged_duration("Threat zone computation"):
self.compute_threat_zones() self.compute_threat_zones()
@@ -408,6 +406,10 @@ class Game:
gplanner.plan_groundwar() gplanner.plan_groundwar()
self.ground_planners[cp.id] = gplanner self.ground_planners[cp.id] = gplanner
# Update cull zones
with logged_duration("Computing culling positions"):
self.compute_unculled_zones()
def message(self, title: str, text: str = "") -> None: def message(self, title: str, text: str = "") -> None:
self.informations.append(Information(title, text, turn=self.turn)) self.informations.append(Information(title, text, turn=self.turn))
@@ -448,10 +450,9 @@ class Game:
def navmesh_for(self, player: bool) -> NavMesh: def navmesh_for(self, player: bool) -> NavMesh:
return self.coalition_for(player).nav_mesh return self.coalition_for(player).nav_mesh
def compute_conflicts_position(self) -> None: def compute_unculled_zones(self) -> None:
""" """
Compute the current conflict center position(s), mainly used for culling calculation Compute the current conflict position(s) used for culling calculation
:return: List of points of interests
""" """
zones = [] zones = []

View File

@@ -144,22 +144,35 @@ class ViggenRadioChannelAllocator(RadioChannelAllocator):
self, flight: FlightData, air_support: AirSupport self, flight: FlightData, air_support: AirSupport
) -> None: ) -> None:
# The Viggen's preset channels are handled differently from other # The Viggen's preset channels are handled differently from other
# aircraft. The aircraft automatically configures channels for every # aircraft. Since 2.7.9 the group channels will not be generated automatically
# allied flight in the game (including AWACS) and for every airfield. As # anymore. So we have to set AWACS and JTAC manually. There are also seven
# such, we don't need to allocate any of those. There are seven presets # special channels we can modify. We'll set the first channel of the main radio
# we can modify, however: three channels for the main radio intended for # to the intra-flight channel, and the first three emergency channels to each
# communication with wingmen, and four emergency channels for the backup
# radio. We'll set the first channel of the main radio to the
# intra-flight channel, and the first three emergency channels to each
# of the flight plan's airfields. The fourth emergency channel is always # of the flight plan's airfields. The fourth emergency channel is always
# the guard channel. # the guard channel.
radio_id = 1 radio_id = 1
flight.assign_channel(radio_id, 1, flight.intra_flight_channel)
# Possible Group Channels (100-139)
channel_alloc = iter(range(1, 40))
# Intra-Flight channel on Special 1 and Group 100 (required by module)
flight.assign_channel(radio_id, 41, flight.intra_flight_channel) # Special 1
flight.assign_channel(
radio_id, next(channel_alloc), flight.intra_flight_channel
)
for awacs in air_support.awacs:
flight.assign_channel(radio_id, next(channel_alloc), awacs.freq)
for jtac in air_support.jtacs:
flight.assign_channel(radio_id, next(channel_alloc), jtac.freq)
if flight.departure.atc is not None: if flight.departure.atc is not None:
flight.assign_channel(radio_id, 4, flight.departure.atc) flight.assign_channel(radio_id, 44, flight.departure.atc) # FR24 E
if flight.arrival.atc is not None: if flight.arrival.atc is not None:
flight.assign_channel(radio_id, 5, flight.arrival.atc) flight.assign_channel(radio_id, 45, flight.arrival.atc) # FR24 F
# TODO: Assign divert to 6 when we support divert airfields. if flight.divert is not None and flight.divert.atc is not None:
flight.assign_channel(radio_id, 46, flight.divert.atc) # FR24 G
@classmethod @classmethod
def name(cls) -> str: def name(cls) -> str:
@@ -263,10 +276,18 @@ class ViggenChannelNamer(ChannelNamer):
@staticmethod @staticmethod
def channel_name(radio_id: int, channel_id: int) -> str: def channel_name(radio_id: int, channel_id: int) -> str:
if channel_id >= 4: special_channels = [
channel_letter = "EFGH"[channel_id - 4] "FR 22 Special 1",
return f"FR 24 {channel_letter}" "FR 22 Special 2",
return f"FR 22 Special {channel_id}" "FR 22 Special 3",
"FR 24 E",
"FR 24 F",
"FR 24 G",
"FR 24 H",
]
if channel_id >= 41: # Special channels are 41-47
return special_channels[channel_id - 41]
return f"FR 22 Group {99 + channel_id}"
@classmethod @classmethod
def name(cls) -> str: def name(cls) -> str:

View File

@@ -19,6 +19,7 @@ def bounded_int_option(
max: int, max: int,
detail: Optional[str] = None, detail: Optional[str] = None,
tooltip: Optional[str] = None, tooltip: Optional[str] = None,
causes_expensive_game_update: bool = False,
**kwargs: Any, **kwargs: Any,
) -> int: ) -> int:
return field( return field(
@@ -29,7 +30,7 @@ def bounded_int_option(
text, text,
detail, detail,
tooltip, tooltip,
causes_expensive_game_update=False, causes_expensive_game_update,
min=min, min=min,
max=max, max=max,
) )

View File

@@ -23,6 +23,12 @@ class AutoAtoBehavior(Enum):
Prefer = "Prefer player pilots" Prefer = "Prefer player pilots"
class NightMissions(Enum):
DayAndNight = "nightmissions_nightandday"
OnlyDay = "nightmissions_onlyday"
OnlyNight = "nightmissions_onlynight"
DIFFICULTY_PAGE = "Difficulty" DIFFICULTY_PAGE = "Difficulty"
AI_DIFFICULTY_SECTION = "AI Difficulty" AI_DIFFICULTY_SECTION = "AI Difficulty"
@@ -104,11 +110,16 @@ class Settings:
section=MISSION_DIFFICULTY_SECTION, section=MISSION_DIFFICULTY_SECTION,
default=True, default=True,
) )
night_disabled: bool = boolean_option( night_day_missions: NightMissions = choices_option(
"No night missions", "Night/day mission options",
page=DIFFICULTY_PAGE, page=DIFFICULTY_PAGE,
section=MISSION_DIFFICULTY_SECTION, section=MISSION_DIFFICULTY_SECTION,
default=False, choices={
"Generate night and day missions": NightMissions.DayAndNight,
"Only generate day missions": NightMissions.OnlyDay,
"Only generate night missions": NightMissions.OnlyNight,
},
default=NightMissions.DayAndNight,
) )
# Mission Restrictions # Mission Restrictions
labels: str = choices_option( labels: str = choices_option(
@@ -137,6 +148,15 @@ class Settings:
MISSION_RESTRICTIONS_SECTION, MISSION_RESTRICTIONS_SECTION,
default=True, default=True,
) )
easy_communication: Optional[bool] = choices_option(
"Easy Communication",
page=DIFFICULTY_PAGE,
section=MISSION_RESTRICTIONS_SECTION,
choices={"Player preference": None, "Enforced on": True, "Enforced off": False},
default=None,
)
battle_damage_assessment: Optional[bool] = choices_option( battle_damage_assessment: Optional[bool] = choices_option(
"Battle damage assessment", "Battle damage assessment",
page=DIFFICULTY_PAGE, page=DIFFICULTY_PAGE,
@@ -378,6 +398,12 @@ class Settings:
section=PERFORMANCE_SECTION, section=PERFORMANCE_SECTION,
default=True, default=True,
) )
convoys_travel_full_distance: bool = boolean_option(
"Convoys drive the full distance between control points",
page=MISSION_GENERATOR_PAGE,
section=PERFORMANCE_SECTION,
default=True,
)
perf_infantry: bool = boolean_option( perf_infantry: bool = boolean_option(
"Generate infantry squads alongside vehicles", "Generate infantry squads alongside vehicles",
page=MISSION_GENERATOR_PAGE, page=MISSION_GENERATOR_PAGE,
@@ -404,6 +430,7 @@ class Settings:
default=100, default=100,
min=10, min=10,
max=10000, max=10000,
causes_expensive_game_update=True,
) )
perf_do_not_cull_carrier: bool = boolean_option( perf_do_not_cull_carrier: bool = boolean_option(
"Do not cull carrier's surroundings", "Do not cull carrier's surroundings",

View File

@@ -7,17 +7,30 @@ from typing import Sequence, Iterator, TYPE_CHECKING, Optional
from game.dcs.aircrafttype import AircraftType from game.dcs.aircrafttype import AircraftType
from gen.flights.ai_flight_planner_db import aircraft_for_task from gen.flights.ai_flight_planner_db import aircraft_for_task
from gen.flights.closestairfields import ObjectiveDistanceCache from gen.flights.closestairfields import ObjectiveDistanceCache
from .squadron import Squadron from .squadrondef import SquadronDef
from .squadrondefloader import SquadronDefLoader
from ..campaignloader.squadrondefgenerator import SquadronDefGenerator
from ..factions.faction import Faction
from ..theater import ControlPoint, MissionTarget from ..theater import ControlPoint, MissionTarget
if TYPE_CHECKING: if TYPE_CHECKING:
from game.game import Game
from gen.flights.flight import FlightType from gen.flights.flight import FlightType
from .squadron import Squadron
class AirWing: class AirWing:
def __init__(self, player: bool) -> None: def __init__(self, player: bool, game: Game, faction: Faction) -> None:
self.player = player self.player = player
self.squadrons: dict[AircraftType, list[Squadron]] = defaultdict(list) self.squadrons: dict[AircraftType, list[Squadron]] = defaultdict(list)
self.squadron_defs = SquadronDefLoader(game, faction).load()
self.squadron_def_generator = SquadronDefGenerator(faction)
def unclaim_squadron_def(self, squadron: Squadron) -> None:
if squadron.aircraft in self.squadron_defs:
for squadron_def in self.squadron_defs[squadron.aircraft]:
if squadron_def.claimed and squadron_def.name == squadron.name:
squadron_def.claimed = False
def add_squadron(self, squadron: Squadron) -> None: def add_squadron(self, squadron: Squadron) -> None:
self.squadrons[squadron.aircraft].append(squadron) self.squadrons[squadron.aircraft].append(squadron)

View File

@@ -3,17 +3,13 @@ from __future__ import annotations
import logging import logging
from collections import Iterable from collections import Iterable
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import ( from typing import Optional, Sequence, TYPE_CHECKING
TYPE_CHECKING,
Optional,
Sequence,
)
from faker import Faker from faker import Faker
from game.settings import AutoAtoBehavior, Settings from game.settings import AutoAtoBehavior, Settings
from gen.ato import Package from gen.ato import Package
from gen.flights.flight import FlightType, Flight from gen.flights.flight import Flight, FlightType
from gen.flights.flightplan import FlightPlanBuilder from gen.flights.flightplan import FlightPlanBuilder
from .pilot import Pilot, PilotStatus from .pilot import Pilot, PilotStatus
from ..utils import meters from ..utils import meters
@@ -265,6 +261,8 @@ class Squadron:
return distance_to_target <= self.aircraft.max_mission_range return distance_to_target <= self.aircraft.max_mission_range
def operates_from(self, control_point: ControlPoint) -> bool: def operates_from(self, control_point: ControlPoint) -> bool:
if not control_point.can_operate(self.aircraft):
return False
if control_point.is_carrier: if control_point.is_carrier:
return self.operating_bases.carrier return self.operating_bases.carrier
elif control_point.is_lha: elif control_point.is_lha:
@@ -421,6 +419,7 @@ class Squadron:
coalition: Coalition, coalition: Coalition,
game: Game, game: Game,
) -> Squadron: ) -> Squadron:
squadron_def.claimed = True
return Squadron( return Squadron(
squadron_def.name, squadron_def.name,
squadron_def.nickname, squadron_def.nickname,

View File

@@ -4,10 +4,7 @@ import logging
from collections import Iterable from collections import Iterable
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from typing import ( from typing import Optional, TYPE_CHECKING
TYPE_CHECKING,
Optional,
)
import yaml import yaml
@@ -31,6 +28,7 @@ class SquadronDef:
mission_types: tuple[FlightType, ...] mission_types: tuple[FlightType, ...]
operating_bases: OperatingBases operating_bases: OperatingBases
pilot_pool: list[Pilot] pilot_pool: list[Pilot]
claimed: bool = False
auto_assignable_mission_types: set[FlightType] = field( auto_assignable_mission_types: set[FlightType] = field(
init=False, hash=False, compare=False init=False, hash=False, compare=False
@@ -52,6 +50,8 @@ class SquadronDef:
return task in self.auto_assignable_mission_types return task in self.auto_assignable_mission_types
def operates_from(self, control_point: ControlPoint) -> bool: def operates_from(self, control_point: ControlPoint) -> bool:
if not control_point.can_operate(self.aircraft):
return False
if control_point.is_carrier: if control_point.is_carrier:
return self.operating_bases.carrier return self.operating_bases.carrier
elif control_point.is_lha: elif control_point.is_lha:

View File

@@ -10,13 +10,13 @@ from .squadrondef import SquadronDef
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
from game.coalition import Coalition from ..factions.faction import Faction
class SquadronDefLoader: class SquadronDefLoader:
def __init__(self, game: Game, coalition: Coalition) -> None: def __init__(self, game: Game, faction: Faction) -> None:
self.game = game self.game = game
self.coalition = coalition self.faction = faction
@staticmethod @staticmethod
def squadron_directories() -> Iterator[Path]: def squadron_directories() -> Iterator[Path]:
@@ -27,8 +27,8 @@ class SquadronDefLoader:
def load(self) -> dict[AircraftType, list[SquadronDef]]: def load(self) -> dict[AircraftType, list[SquadronDef]]:
squadrons: dict[AircraftType, list[SquadronDef]] = defaultdict(list) squadrons: dict[AircraftType, list[SquadronDef]] = defaultdict(list)
country = self.coalition.country_name country = self.faction.country
faction = self.coalition.faction faction = self.faction
any_country = country.startswith("Combined Joint Task Forces ") any_country = country.startswith("Combined Joint Task Forces ")
for directory in self.squadron_directories(): for directory in self.squadron_directories():
for path, squadron_def in self.load_squadrons_from(directory): for path, squadron_def in self.load_squadrons_from(directory):

View File

@@ -325,8 +325,8 @@ class NevadaTheater(ConflictTheater):
terrain = nevada.Nevada() terrain = nevada.Nevada()
overview_image = "nevada.gif" overview_image = "nevada.gif"
reference_points = ( reference_points = (
ReferencePoint(nevada.Mina_Airport_3Q0.position, Point(252, 295)), ReferencePoint(nevada.Mina.position, Point(252, 295)),
ReferencePoint(nevada.Laughlin_Airport.position, Point(844, 909)), ReferencePoint(nevada.Laughlin.position, Point(844, 909)),
) )
landmap = load_landmap(Path("resources/nevlandmap.p")) landmap = load_landmap(Path("resources/nevlandmap.p"))
daytime_map = { daytime_map = {

View File

@@ -70,7 +70,9 @@ class GeneratorSettings:
class ModSettings: class ModSettings:
a4_skyhawk: bool = False a4_skyhawk: bool = False
f22_raptor: bool = False f22_raptor: bool = False
f104_starfighter: bool = False
hercules: bool = False hercules: bool = False
uh_60l: bool = False
jas39_gripen: bool = False jas39_gripen: bool = False
su57_felon: bool = False su57_felon: bool = False
frenchpack: bool = False frenchpack: bool = False

View File

@@ -176,7 +176,9 @@ class TheaterGroundObject(MissionTarget, Generic[GroupT]):
return self._max_range_of_type(group, "detection_range") return self._max_range_of_type(group, "detection_range")
def max_threat_range(self) -> Distance: def max_threat_range(self) -> Distance:
return max(self.threat_range(g) for g in self.groups) return (
max(self.threat_range(g) for g in self.groups) if self.groups else meters(0)
)
def threat_range(self, group: GroupT, radar_only: bool = False) -> Distance: def threat_range(self, group: GroupT, radar_only: bool = False) -> Distance:
return self._max_range_of_type(group, "threat_range") return self._max_range_of_type(group, "threat_range")
@@ -316,16 +318,6 @@ class SceneryGroundObject(BuildingGroundObject):
is_fob_structure=False, is_fob_structure=False,
) )
self.zone = zone self.zone = zone
try:
# In the default TriggerZone using "assign as..." in the DCS Mission Editor,
# property three has the scenery's object ID as its value.
self.map_object_id = self.zone.properties[3]["value"]
except (IndexError, KeyError):
logging.exception(
"Invalid TriggerZone for Scenery definition. The third property must "
"be the map object ID."
)
raise
class FactoryGroundObject(BuildingGroundObject): class FactoryGroundObject(BuildingGroundObject):
@@ -537,7 +529,7 @@ class SamGroundObject(IadsGroundObject):
max_tel_range = meters(0) max_tel_range = meters(0)
for launcher in launchers: for launcher in launchers:
if LAUNCHER_TRACKER_PAIRS[launcher] in live_trs: if LAUNCHER_TRACKER_PAIRS[launcher] in live_trs:
max_tel_range = max(max_tel_range, meters(unit_type.threat_range)) max_tel_range = max(max_tel_range, meters(launcher.threat_range))
if radar_only: if radar_only:
return max(max_tel_range, max_telar_range) return max(max_tel_range, max_telar_range)
else: else:

View File

@@ -699,6 +699,8 @@ class PendingTransfers:
completable_transfers = [] completable_transfers = []
for transfer in self.pending_transfers: for transfer in self.pending_transfers:
if not transfer.is_completable(self.network_for(transfer.position)): if not transfer.is_completable(self.network_for(transfer.position)):
if transfer.transport:
self.cancel_transport(transfer.transport, transfer)
transfer.disband() transfer.disband()
else: else:
completable_transfers.append(transfer) completable_transfers.append(transfer)

View File

@@ -217,7 +217,7 @@ class UnitMap:
self.buildings[name] = Building(ground_object) self.buildings[name] = Building(ground_object)
def add_scenery(self, ground_object: SceneryGroundObject) -> None: def add_scenery(self, ground_object: SceneryGroundObject) -> None:
name = str(ground_object.map_object_id) name = str(ground_object.zone.name)
if name in self.buildings: if name in self.buildings:
raise RuntimeError( raise RuntimeError(
f"Duplicate TGO unit: {name}. TriggerZone name: " f"Duplicate TGO unit: {name}. TriggerZone name: "

View File

@@ -21,7 +21,7 @@ INHG_TO_HPA = 33.86389
INHG_TO_MMHG = 25.400002776728 INHG_TO_MMHG = 25.400002776728
@dataclass(frozen=True, order=True) @dataclass(frozen=True)
class Distance: class Distance:
distance_in_meters: float distance_in_meters: float
@@ -73,6 +73,18 @@ class Distance:
def __bool__(self) -> bool: def __bool__(self) -> bool:
return not math.isclose(self.meters, 0.0) return not math.isclose(self.meters, 0.0)
def __lt__(self, other: Distance) -> bool:
return self.meters < other.meters
def __le__(self, other: Distance) -> bool:
return self.meters <= other.meters
def __gt__(self, other: Distance) -> bool:
return self.meters > other.meters
def __ge__(self, other: Distance) -> bool:
return self.meters >= other.meters
def feet(value: float) -> Distance: def feet(value: float) -> Distance:
return Distance.from_feet(value) return Distance.from_feet(value)

View File

@@ -2,7 +2,7 @@ from pathlib import Path
MAJOR_VERSION = 5 MAJOR_VERSION = 5
MINOR_VERSION = 0 MINOR_VERSION = 2
MICRO_VERSION = 0 MICRO_VERSION = 0

View File

@@ -10,7 +10,7 @@ from typing import Optional, TYPE_CHECKING, Any
from dcs.cloud_presets import Clouds as PydcsClouds from dcs.cloud_presets import Clouds as PydcsClouds
from dcs.weather import CloudPreset, Weather as PydcsWeather, Wind from dcs.weather import CloudPreset, Weather as PydcsWeather, Wind
from game.settings import Settings from game.settings.settings import Settings, NightMissions
from game.utils import Distance, Heading, meters, interpolate, Pressure, inches_hg from game.utils import Distance, Heading, meters, interpolate, Pressure, inches_hg
from game.theater.seasonalconditions import determine_season from game.theater.seasonalconditions import determine_season
@@ -301,7 +301,10 @@ class Conditions:
settings: Settings, settings: Settings,
) -> Conditions: ) -> Conditions:
_start_time = cls.generate_start_time( _start_time = cls.generate_start_time(
theater, day, time_of_day, settings.night_disabled theater,
day,
time_of_day,
settings.night_day_missions,
) )
return cls( return cls(
time_of_day=time_of_day, time_of_day=time_of_day,
@@ -315,9 +318,9 @@ class Conditions:
theater: ConflictTheater, theater: ConflictTheater,
day: datetime.date, day: datetime.date,
time_of_day: TimeOfDay, time_of_day: TimeOfDay,
night_disabled: bool, night_day_missions: NightMissions,
) -> datetime.datetime: ) -> datetime.datetime:
if night_disabled: if night_day_missions == NightMissions.OnlyDay:
logging.info("Skip Night mission due to user settings") logging.info("Skip Night mission due to user settings")
time_range = { time_range = {
TimeOfDay.Dawn: (8, 9), TimeOfDay.Dawn: (8, 9),
@@ -325,6 +328,14 @@ class Conditions:
TimeOfDay.Dusk: (12, 14), TimeOfDay.Dusk: (12, 14),
TimeOfDay.Night: (14, 17), TimeOfDay.Night: (14, 17),
}[time_of_day] }[time_of_day]
elif night_day_missions == NightMissions.OnlyNight:
logging.info("Skip Day mission due to user settings")
time_range = {
TimeOfDay.Dawn: (0, 3),
TimeOfDay.Day: (3, 6),
TimeOfDay.Dusk: (21, 22),
TimeOfDay.Night: (22, 23),
}[time_of_day]
else: else:
time_range = theater.daytime_map[time_of_day.value] time_range = theater.daytime_map[time_of_day.value]

View File

@@ -6,7 +6,7 @@ import random
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import timedelta from datetime import timedelta
from functools import cached_property from functools import cached_property
from typing import Dict, List, Optional, TYPE_CHECKING, Type, Union, Iterable, Any from typing import Any, Dict, Iterable, List, Optional, TYPE_CHECKING, Type, Union
from dcs import helicopters from dcs import helicopters
from dcs.action import AITaskPush, ActivateGroup from dcs.action import AITaskPush, ActivateGroup
@@ -21,6 +21,7 @@ from dcs.planes import (
B_52H, B_52H,
C_101CC, C_101CC,
C_101EB, C_101EB,
F_14A_135_GR,
F_14B, F_14B,
JF_17, JF_17,
Su_33, Su_33,
@@ -44,6 +45,7 @@ from dcs.task import (
EngageTargetsInZone, EngageTargetsInZone,
FighterSweep, FighterSweep,
GroundAttack, GroundAttack,
Nothing,
OptROE, OptROE,
OptRTBOnBingoFuel, OptRTBOnBingoFuel,
OptRTBOnOutOfAmmo, OptRTBOnOutOfAmmo,
@@ -54,15 +56,14 @@ from dcs.task import (
RunwayAttack, RunwayAttack,
StartCommand, StartCommand,
Tanker, Tanker,
TargetType,
Targets, Targets,
Transport, Transport,
WeaponType, WeaponType,
TargetType,
Nothing,
) )
from dcs.terrain.terrain import Airport, NoParkingSlotError from dcs.terrain.terrain import Airport, NoParkingSlotError
from dcs.triggers import Event, TriggerOnce, TriggerRule from dcs.triggers import Event, TriggerOnce, TriggerRule
from dcs.unit import Unit, Skill from dcs.unit import Skill, Unit
from dcs.unitgroup import FlyingGroup, ShipGroup, StaticGroup from dcs.unitgroup import FlyingGroup, ShipGroup, StaticGroup
from dcs.unittype import FlyingType from dcs.unittype import FlyingType
@@ -379,6 +380,8 @@ class AircraftConflictGenerator:
# Set up F-14 Client to have pre-stored alignment # Set up F-14 Client to have pre-stored alignment
if unit_type is F_14B: if unit_type is F_14B:
unit.set_property(F_14B.Properties.INSAlignmentStored.id, True) unit.set_property(F_14B.Properties.INSAlignmentStored.id, True)
elif unit_type is F_14A_135_GR:
unit.set_property(F_14A_135_GR.Properties.INSAlignmentStored.id, True)
group.points[0].tasks.append( group.points[0].tasks.append(
OptReactOnThreat(OptReactOnThreat.Values.EvadeFire) OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)
@@ -1526,6 +1529,8 @@ class BaiIngressBuilder(PydcsWaypointBuilder):
group_names.append(group.name) group_names.append(group.name)
elif isinstance(target, MultiGroupTransport): elif isinstance(target, MultiGroupTransport):
group_names.append(target.name) group_names.append(target.name)
elif isinstance(target, NavalControlPoint):
group_names.append(target.get_carrier_group_name())
else: else:
logging.error( logging.error(
"Unexpected target type for BAI mission: %s", "Unexpected target type for BAI mission: %s",

View File

@@ -1058,20 +1058,20 @@ AIRFIELD_DATA = {
"8": ("IGZP", MHz(108, 500)), "8": ("IGZP", MHz(108, 500)),
}, },
), ),
# NTTR # Nevada
"Mina Airport 3Q0": AirfieldData( "Mina": AirfieldData(
theater="NTTR", theater="Nevada",
elevation=4562, elevation=4562,
runway_length=4222, runway_length=4222,
), ),
"Tonopah Airport": AirfieldData( "Tonopah": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KTPH", icao="KTPH",
elevation=5394, elevation=5394,
runway_length=6715, runway_length=6715,
), ),
"Tonopah Test Range Airfield": AirfieldData( "Tonopah Test Range": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KTNX", icao="KTNX",
elevation=5534, elevation=5534,
runway_length=11633, runway_length=11633,
@@ -1083,19 +1083,19 @@ AIRFIELD_DATA = {
"14": ("I-RVP", MHz(108, 300)), "14": ("I-RVP", MHz(108, 300)),
}, },
), ),
"Beatty Airport": AirfieldData( "Beatty": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KBTY", icao="KBTY",
elevation=3173, elevation=3173,
runway_length=5380, runway_length=5380,
), ),
"Pahute Mesa Airstrip": AirfieldData( "Pahute Mesa": AirfieldData(
theater="NTTR", theater="Nevada",
elevation=5056, elevation=5056,
runway_length=5420, runway_length=5420,
), ),
"Groom Lake AFB": AirfieldData( "Groom Lake": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KXTA", icao="KXTA",
elevation=4494, elevation=4494,
runway_length=11008, runway_length=11008,
@@ -1107,18 +1107,18 @@ AIRFIELD_DATA = {
}, },
), ),
"Lincoln County": AirfieldData( "Lincoln County": AirfieldData(
theater="NTTR", theater="Nevada",
elevation=4815, elevation=4815,
runway_length=4408, runway_length=4408,
), ),
"Mesquite": AirfieldData( "Mesquite": AirfieldData(
theater="NTTR", theater="Nevada",
icao="67L", icao="67L",
elevation=1858, elevation=1858,
runway_length=4937, runway_length=4937,
), ),
"Creech AFB": AirfieldData( "Creech": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KINS", icao="KINS",
elevation=3126, elevation=3126,
runway_length=6100, runway_length=6100,
@@ -1130,7 +1130,7 @@ AIRFIELD_DATA = {
}, },
), ),
"Echo Bay": AirfieldData( "Echo Bay": AirfieldData(
theater="NTTR", theater="Nevada",
icao="OL9", icao="OL9",
elevation=3126, elevation=3126,
runway_length=6100, runway_length=6100,
@@ -1138,8 +1138,8 @@ AIRFIELD_DATA = {
tacan_callsign="INS", tacan_callsign="INS",
atc=AtcData(MHz(3, 825), MHz(118, 300), MHz(38, 550), MHz(360, 600)), atc=AtcData(MHz(3, 825), MHz(118, 300), MHz(38, 550), MHz(360, 600)),
), ),
"Nellis AFB": AirfieldData( "Nellis": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KLSV", icao="KLSV",
elevation=1841, elevation=1841,
runway_length=9454, runway_length=9454,
@@ -1151,14 +1151,14 @@ AIRFIELD_DATA = {
}, },
), ),
"North Las Vegas": AirfieldData( "North Las Vegas": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KVGT", icao="KVGT",
elevation=2228, elevation=2228,
runway_length=4734, runway_length=4734,
atc=AtcData(MHz(3, 775), MHz(125, 700), MHz(38, 450), MHz(360, 750)), atc=AtcData(MHz(3, 775), MHz(125, 700), MHz(38, 450), MHz(360, 750)),
), ),
"McCarran International Airport": AirfieldData( "McCarran International": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KLAS", icao="KLAS",
elevation=2169, elevation=2169,
runway_length=10377, runway_length=10377,
@@ -1169,26 +1169,26 @@ AIRFIELD_DATA = {
"25": ("I-LAS", MHz(110, 300)), "25": ("I-LAS", MHz(110, 300)),
}, },
), ),
"Henderson Executive Airport": AirfieldData( "Henderson Executive": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KHND", icao="KHND",
elevation=2491, elevation=2491,
runway_length=5999, runway_length=5999,
atc=AtcData(MHz(3, 925), MHz(125, 100), MHz(38, 750), MHz(250, 100)), atc=AtcData(MHz(3, 925), MHz(125, 100), MHz(38, 750), MHz(250, 100)),
), ),
"Boulder City Airport": AirfieldData( "Boulder City": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KBVU", icao="KBVU",
elevation=2121, elevation=2121,
runway_length=4612, runway_length=4612,
), ),
"Jean Airport": AirfieldData( "Jean": AirfieldData(
theater="NTTR", theater="Nevada",
elevation=2824, elevation=2824,
runway_length=4053, runway_length=4053,
), ),
"Laughlin Airport": AirfieldData( "Laughlin": AirfieldData(
theater="NTTR", theater="Nevada",
icao="KIFP", icao="KIFP",
elevation=656, elevation=656,
runway_length=7139, runway_length=7139,

View File

@@ -10,6 +10,7 @@ from dcs.unit import Vehicle
from dcs.unitgroup import VehicleGroup from dcs.unitgroup import VehicleGroup
from game.dcs.groundunittype import GroundUnitType from game.dcs.groundunittype import GroundUnitType
from game.theater import FrontLine
from game.transfers import Convoy from game.transfers import Convoy
from game.unitmap import UnitMap from game.unitmap import UnitMap
from game.utils import kph from game.utils import kph
@@ -38,11 +39,26 @@ class ConvoyGenerator:
convoy.units, convoy.units,
convoy.player_owned, convoy.player_owned,
) )
if self.game.settings.convoys_travel_full_distance:
end_point = convoy.route_end
else:
# convoys_travel_full_distance is disabled, so have the convoy only move the first segment on the route.
# This option aims to remove long routes for ground vehicles between control points,
# since the CPU load for pathfinding long routes on DCS can be pretty heavy.
frontline = FrontLine(convoy.origin, convoy.destination)
# Select the first route segment from the origin towards the destination
# so the convoy spawns at the origin CP. This allows the convoy to be
# targeted by BAI flights and starts it within the protection umbrella of the CP.
end_point = frontline.segments[0].point_b
group.add_waypoint( group.add_waypoint(
convoy.route_end, end_point,
speed=kph(40).kph, speed=kph(40).kph,
move_formation=PointAction.OnRoad, move_formation=PointAction.OnRoad,
) )
self.make_drivable(group) self.make_drivable(group)
self.unit_map.add_convoy_units(group, convoy) self.unit_map.add_convoy_units(group, convoy)
return group return group

View File

@@ -52,8 +52,10 @@ from dcs.planes import (
F_4E, F_4E,
F_5E_3, F_5E_3,
F_86F_Sabre, F_86F_Sabre,
H_6J,
IL_76MD, IL_76MD,
IL_78M, IL_78M,
I_16,
JF_17, JF_17,
J_11A, J_11A,
Ju_88A4, Ju_88A4,
@@ -75,6 +77,7 @@ from dcs.planes import (
MiG_29S, MiG_29S,
MiG_31, MiG_31,
Mirage_2000_5, Mirage_2000_5,
MosquitoFBMkVI,
P_47D_30, P_47D_30,
P_47D_30bl1, P_47D_30bl1,
P_47D_40, P_47D_40,
@@ -87,7 +90,6 @@ from dcs.planes import (
SpitfireLFMkIXCW, SpitfireLFMkIXCW,
Su_17M4, Su_17M4,
Su_24M, Su_24M,
Su_24MR,
Su_25, Su_25,
Su_25T, Su_25T,
Su_25TM, Su_25TM,
@@ -97,13 +99,12 @@ from dcs.planes import (
Su_34, Su_34,
Tornado_GR4, Tornado_GR4,
Tornado_IDS, Tornado_IDS,
Tu_142,
Tu_160, Tu_160,
Tu_22M3, Tu_22M3,
Tu_95MS, Tu_95MS,
WingLoong_I, WingLoong_I,
I_16,
Yak_40, Yak_40,
MosquitoFBMkVI,
) )
from dcs.unittype import FlyingType from dcs.unittype import FlyingType
@@ -111,6 +112,7 @@ from game.dcs.aircrafttype import AircraftType
from gen.flights.flight import FlightType from gen.flights.flight import FlightType
from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.f22a.f22a import F_22A from pydcs_extensions.f22a.f22a import F_22A
from pydcs_extensions.f104.f104 import VSN_F104G, VSN_F104S, VSN_F104S_AG
from pydcs_extensions.hercules.hercules import Hercules from pydcs_extensions.hercules.hercules import Hercules
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
from pydcs_extensions.su57.su57 import Su_57 from pydcs_extensions.su57.su57 import Su_57
@@ -123,6 +125,8 @@ from pydcs_extensions.su57.su57 import Su_57
# factions that also have F-4s should not. # factions that also have F-4s should not.
# Used for CAP, Escort, and intercept if there is not a specialised aircraft available # Used for CAP, Escort, and intercept if there is not a specialised aircraft available
from pydcs_extensions.uh60l.uh60l import UH_60L, KC130J
CAP_CAPABLE = [ CAP_CAPABLE = [
Su_57, Su_57,
F_22A, F_22A,
@@ -150,6 +154,8 @@ CAP_CAPABLE = [
F_15E, F_15E,
M_2000C, M_2000C,
F_5E_3, F_5E_3,
VSN_F104S,
VSN_F104G,
MiG_19P, MiG_19P,
A_4E_C, A_4E_C,
F_86F_Sabre, F_86F_Sabre,
@@ -215,6 +221,7 @@ CAS_CAPABLE = [
Mi_24P, Mi_24P,
Mi_24V, Mi_24V,
Mi_8MT, Mi_8MT,
H_6J,
MiG_19P, MiG_19P,
MiG_15bis, MiG_15bis,
M_2000C, M_2000C,
@@ -223,6 +230,8 @@ CAS_CAPABLE = [
C_101CC, C_101CC,
L_39ZA, L_39ZA,
UH_1H, UH_1H,
VSN_F104S_AG,
VSN_F104G,
A_20G, A_20G,
Ju_88A4, Ju_88A4,
P_47D_40, P_47D_40,
@@ -276,8 +285,11 @@ DEAD_CAPABLE = [
B_52H, B_52H,
Tu_160, Tu_160,
Tu_95MS, Tu_95MS,
H_6J,
A_20G, A_20G,
Ju_88A4, Ju_88A4,
VSN_F104S_AG,
VSN_F104G,
P_47D_40, P_47D_40,
P_47D_30bl1, P_47D_30bl1,
P_47D_30, P_47D_30,
@@ -300,6 +312,7 @@ STRIKE_CAPABLE = [
Tu_160, Tu_160,
Tu_95MS, Tu_95MS,
Tu_22M3, Tu_22M3,
H_6J,
F_15E, F_15E,
AJS37, AJS37,
Tornado_GR4, Tornado_GR4,
@@ -340,6 +353,8 @@ STRIKE_CAPABLE = [
B_17G, B_17G,
A_20G, A_20G,
Ju_88A4, Ju_88A4,
VSN_F104S_AG,
VSN_F104G,
P_47D_40, P_47D_40,
P_47D_30bl1, P_47D_30bl1,
P_47D_30, P_47D_30,
@@ -356,7 +371,9 @@ STRIKE_CAPABLE = [
ANTISHIP_CAPABLE = [ ANTISHIP_CAPABLE = [
AJS37, AJS37,
Tu_142,
Tu_22M3, Tu_22M3,
H_6J,
FA_18C_hornet, FA_18C_hornet,
JAS39Gripen_AG, JAS39Gripen_AG,
Su_24M, Su_24M,
@@ -376,14 +393,73 @@ ANTISHIP_CAPABLE = [
] ]
# Duplicates some list entries but that's fine. # This list does not "inherit" from the strike list because some strike aircraft can
# only carry guided weapons, and the AI cannot do runway attack with dguided weapons.
# https://github.com/dcs-liberation/dcs_liberation/issues/1703
RUNWAY_ATTACK_CAPABLE = [ RUNWAY_ATTACK_CAPABLE = [
JF_17, JF_17,
Su_34, Su_34,
Su_30, Su_30,
Tornado_IDS, Tornado_IDS,
M_2000C, M_2000C,
] + STRIKE_CAPABLE H_6J,
B_1B,
B_52H,
Tu_22M3,
H_6J,
F_15E,
AJS37,
F_16C_50,
FA_18C_hornet,
AV8BNA,
JF_17,
F_16A,
F_14B,
F_14A_135_GR,
JAS39Gripen_AG,
Tornado_IDS,
Su_17M4,
Su_24M,
Su_25TM,
Su_25T,
Su_25,
Su_34,
Su_33,
Su_30,
Su_27,
MiG_29S,
MiG_29G,
MiG_29A,
F_4E,
A_10C_2,
A_10C,
S_3B,
A_4E_C,
M_2000C,
MiG_27K,
MiG_21Bis,
MiG_15bis,
F_5E_3,
F_86F_Sabre,
C_101CC,
L_39ZA,
B_17G,
A_20G,
Ju_88A4,
VSN_F104S_AG,
VSN_F104G,
P_47D_40,
P_47D_30bl1,
P_47D_30,
P_51D_30_NA,
P_51D,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
MosquitoFBMkVI,
Bf_109K_4,
FW_190D9,
FW_190A8,
]
# For any aircraft that isn't necessarily directly involved in strike # For any aircraft that isn't necessarily directly involved in strike
# missions in a direct combat sense, but can transport objects and infantry. # missions in a direct combat sense, but can transport objects and infantry.
@@ -396,6 +472,7 @@ TRANSPORT_CAPABLE = [
Yak_40, Yak_40,
CH_53E, CH_53E,
CH_47D, CH_47D,
UH_60L,
SH_60B, SH_60B,
UH_60A, UH_60A,
UH_1H, UH_1H,
@@ -418,6 +495,7 @@ REFUELING_CAPABALE = [
KC_135, KC_135,
KC135MPRS, KC135MPRS,
IL_78M, IL_78M,
KC130J,
KC130, KC130,
S_3B_Tanker, S_3B_Tanker,
] ]

View File

@@ -25,6 +25,11 @@ class ForcedOptionsGenerator:
self.game.settings.external_views_allowed self.game.settings.external_views_allowed
) )
def _set_easy_communication(self) -> None:
self.mission.forced_options.easy_communication = (
self.game.settings.easy_communication
)
def _set_labels(self) -> None: def _set_labels(self) -> None:
# TODO: Fix settings to use the real type. # TODO: Fix settings to use the real type.
# TODO: Allow forcing "full" and have default do nothing. # TODO: Allow forcing "full" and have default do nothing.
@@ -51,6 +56,7 @@ class ForcedOptionsGenerator:
def generate(self) -> None: def generate(self) -> None:
self._set_options_view() self._set_options_view()
self._set_external_views() self._set_external_views()
self._set_easy_communication()
self._set_labels() self._set_labels()
self._set_unrestricted_satnav() self._set_unrestricted_satnav()
self._set_battle_damage_assessment() self._set_battle_damage_assessment()

View File

@@ -24,7 +24,8 @@ from typing import (
) )
from dcs import Mission, Point, unitgroup from dcs import Mission, Point, unitgroup
from dcs.action import SceneryDestructionZone from dcs.action import SceneryDestructionZone, DoScript
from dcs.condition import MapObjectIsDead
from dcs.country import Country from dcs.country import Country
from dcs.point import StaticPoint from dcs.point import StaticPoint
from dcs.statics import Fortification, fortification_map, warehouse_map from dcs.statics import Fortification, fortification_map, warehouse_map
@@ -35,7 +36,8 @@ from dcs.task import (
OptAlarmState, OptAlarmState,
FireAtPoint, FireAtPoint,
) )
from dcs.triggers import TriggerStart, TriggerZone from dcs.translation import String
from dcs.triggers import TriggerStart, TriggerZone, Event, TriggerOnce
from dcs.unit import Ship, Unit, Vehicle, InvisibleFARP from dcs.unit import Ship, Unit, Vehicle, InvisibleFARP
from dcs.unitgroup import ShipGroup, StaticGroup, VehicleGroup from dcs.unitgroup import ShipGroup, StaticGroup, VehicleGroup
from dcs.unittype import StaticType, ShipType, VehicleType from dcs.unittype import StaticType, ShipType, VehicleType
@@ -297,7 +299,9 @@ class SceneryGenerator(BuildingSiteGenerator):
# this trigger rule is applied. Otherwise you can kill a # this trigger rule is applied. Otherwise you can kill a
# structure twice. # structure twice.
if self.ground_object.is_dead: if self.ground_object.is_dead:
self.generate_dead_trigger_rule(trigger_zone) self.generate_destruction_trigger_rule(trigger_zone)
else:
self.generate_on_dead_trigger_rule(trigger_zone)
# Tell Liberation to manage this groundobjectsgen as part of the campaign. # Tell Liberation to manage this groundobjectsgen as part of the campaign.
self.register_scenery() self.register_scenery()
@@ -325,7 +329,7 @@ class SceneryGenerator(BuildingSiteGenerator):
zone.properties, zone.properties,
) )
def generate_dead_trigger_rule(self, trigger_zone: TriggerZone) -> None: def generate_destruction_trigger_rule(self, trigger_zone: TriggerZone) -> None:
# Add destruction zone trigger # Add destruction zone trigger
t = TriggerStart(comment="Destruction") t = TriggerStart(comment="Destruction")
t.actions.append( t.actions.append(
@@ -333,6 +337,17 @@ class SceneryGenerator(BuildingSiteGenerator):
) )
self.m.triggerrules.triggers.append(t) self.m.triggerrules.triggers.append(t)
def generate_on_dead_trigger_rule(self, trigger_zone: TriggerZone) -> None:
# Add a TriggerRule with the MapObjectIsDead condition to recognize killed
# map objects and add them to the state.json with a DoScript
t = TriggerOnce(Event.NoEvent, f"MapObjectIsDead Trigger {trigger_zone.id}")
t.add_condition(MapObjectIsDead(trigger_zone.id))
script_string = String(
f'killed_ground_units[#killed_ground_units + 1] = "{trigger_zone.name}"'
)
t.actions.append(DoScript(script_string))
self.m.triggerrules.triggers.append(t)
def register_scenery(self) -> None: def register_scenery(self) -> None:
scenery = self.ground_object scenery = self.ground_object
assert isinstance(scenery, SceneryGroundObject) assert isinstance(scenery, SceneryGroundObject)
@@ -600,7 +615,7 @@ class HelipadGenerator:
for i, helipad in enumerate(self.cp.helipads): for i, helipad in enumerate(self.cp.helipads):
name = self.cp.name + "_helipad_" + str(i) name = self.cp.name + "_helipad_" + str(i)
logging.info("Generating helipad static : " + name) logging.info("Generating helipad static : " + name)
pad = InvisibleFARP(name=name) pad = InvisibleFARP(unit_id=self.m.next_unit_id(), name=name)
pad.position = Point(helipad.x, helipad.y) pad.position = Point(helipad.x, helipad.y)
pad.heading = helipad.heading.degrees pad.heading = helipad.heading.degrees
sg = unitgroup.StaticGroup(self.m.next_group_id(), name) sg = unitgroup.StaticGroup(self.m.next_group_id(), name)

View File

@@ -161,10 +161,10 @@ ANIMALS: tuple[str, ...] = (
"FLAMINGO", "FLAMINGO",
"FLEA", "FLEA",
"FLOUNDER", "FLOUNDER",
"FORGMOUTH",
"FOX", "FOX",
"FRINGEHEAD", "FRINGEHEAD",
"FROG", "FROG",
"FROGMOUTH",
"GAR", "GAR",
"GAZELLE", "GAZELLE",
"GECKO", "GECKO",

View File

@@ -2,7 +2,7 @@
import itertools import itertools
import logging import logging
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, FrozenSet, Iterator, List, Reversible, Set, Tuple from typing import Dict, FrozenSet, Iterator, List, Set, Tuple
@dataclass(frozen=True) @dataclass(frozen=True)
@@ -127,7 +127,7 @@ RADIOS: List[Radio] = [
RadioRange(MHz(30), MHz(88), MHz(1)), RadioRange(MHz(30), MHz(88), MHz(1)),
), ),
), ),
Radio("AN/ARC-222", (RadioRange(MHz(116), MHz(174), step=MHz(1)),)), Radio("AN/ARC-222", (RadioRange(MHz(116), MHz(152), step=MHz(1)),)),
Radio("SCR-522", (RadioRange(MHz(100), MHz(156), step=MHz(1)),)), Radio("SCR-522", (RadioRange(MHz(100), MHz(156), step=MHz(1)),)),
Radio("A.R.I. 1063", (RadioRange(MHz(100), MHz(156), step=MHz(1)),)), Radio("A.R.I. 1063", (RadioRange(MHz(100), MHz(156), step=MHz(1)),)),
Radio("BC-1206", (RadioRange(kHz(200), kHz(400), step=kHz(10)),)), Radio("BC-1206", (RadioRange(kHz(200), kHz(400), step=kHz(10)),)),

View File

@@ -49,7 +49,7 @@ from gen.sam.sam_sa17 import SA17Generator
from gen.sam.sam_sa19 import SA19Generator from gen.sam.sam_sa19 import SA19Generator
from gen.sam.sam_sa2 import SA2Generator from gen.sam.sam_sa2 import SA2Generator
from gen.sam.sam_sa3 import SA3Generator from gen.sam.sam_sa3 import SA3Generator
from gen.sam.sam_sa5 import SA5Generator from gen.sam.sam_sa5 import SA5Generator, SA5FlatFaceGenerator
from gen.sam.sam_sa6 import SA6Generator from gen.sam.sam_sa6 import SA6Generator
from gen.sam.sam_sa8 import SA8Generator from gen.sam.sam_sa8 import SA8Generator
from gen.sam.sam_sa9 import SA9Generator from gen.sam.sam_sa9 import SA9Generator
@@ -79,6 +79,7 @@ SAM_MAP: Dict[str, Type[AirDefenseGroupGenerator]] = {
"SA2Generator": SA2Generator, "SA2Generator": SA2Generator,
"SA3Generator": SA3Generator, "SA3Generator": SA3Generator,
"SA5Generator": SA5Generator, "SA5Generator": SA5Generator,
"SA5FlatFaceGenerator": SA5FlatFaceGenerator,
"SA6Generator": SA6Generator, "SA6Generator": SA6Generator,
"SA8Generator": SA8Generator, "SA8Generator": SA8Generator,
"SA9Generator": SA9Generator, "SA9Generator": SA9Generator,

View File

@@ -1,5 +1,10 @@
from dcs.vehicles import AirDefence from typing import Type
from dcs.unittype import VehicleType
from dcs.vehicles import AirDefence, Unarmed
from game import Game
from game.theater import SamGroundObject
from gen.sam.airdefensegroupgenerator import ( from gen.sam.airdefensegroupgenerator import (
AirDefenseRange, AirDefenseRange,
AirDefenseGroupGenerator, AirDefenseGroupGenerator,
@@ -8,14 +13,18 @@ from gen.sam.airdefensegroupgenerator import (
class SA5Generator(AirDefenseGroupGenerator): class SA5Generator(AirDefenseGroupGenerator):
""" """
This generate a SA-5 group This generate a SA-5 group using the TinShield SR
""" """
name = "SA-5/S-200 Site" name = "SA-5/S-200 Site"
def __init__(self, game: Game, ground_object: SamGroundObject):
super().__init__(game, ground_object)
self.sr: Type[VehicleType] = AirDefence.RLS_19J6
def generate(self) -> None: def generate(self) -> None:
self.add_unit( self.add_unit(
AirDefence.RLS_19J6, self.sr,
"SR", "SR",
self.position.x, self.position.x,
self.position.y, self.position.y,
@@ -28,6 +37,13 @@ class SA5Generator(AirDefenseGroupGenerator):
self.position.y, self.position.y,
self.heading, self.heading,
) )
self.add_unit(
Unarmed.Ural_375,
"LOGI",
self.position.x - 20,
self.position.y,
self.heading,
)
num_launchers = 6 num_launchers = 6
positions = self.get_circular_position( positions = self.get_circular_position(
@@ -46,3 +62,14 @@ class SA5Generator(AirDefenseGroupGenerator):
@classmethod @classmethod
def range(cls) -> AirDefenseRange: def range(cls) -> AirDefenseRange:
return AirDefenseRange.Long return AirDefenseRange.Long
# TODO Replace this legacy handling once the Generators can be faction sensitive #1903
class SA5FlatFaceGenerator(SA5Generator):
"""
This generate a SA-5 group using the P-19 FlatFace SR
"""
def __init__(self, game: Game, ground_object: SamGroundObject):
super().__init__(game, ground_object)
self.sr: Type[VehicleType] = AirDefence.P_19_s_125_sr

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,19 +1,28 @@
from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.f104.f104 import VSN_F104G, VSN_F104S, VSN_F104S_AG
from pydcs_extensions.f22a.f22a import F_22A from pydcs_extensions.f22a.f22a import F_22A
from pydcs_extensions.hercules.hercules import Hercules from pydcs_extensions.hercules.hercules import Hercules
from pydcs_extensions.highdigitsams import highdigitsams from pydcs_extensions.highdigitsams import highdigitsams
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
from pydcs_extensions.su57.su57 import Su_57 from pydcs_extensions.su57.su57 import Su_57
import pydcs_extensions.frenchpack.frenchpack as frenchpack import pydcs_extensions.frenchpack.frenchpack as frenchpack
from pydcs_extensions.uh60l.uh60l import UH_60L, KC130J
MODDED_AIRPLANES = [ MODDED_AIRPLANES = [
A_4E_C, A_4E_C,
Su_57, Su_57,
F_22A, F_22A,
VSN_F104G,
VSN_F104S,
VSN_F104S_AG,
Hercules, Hercules,
KC130J,
JAS39Gripen, JAS39Gripen,
JAS39Gripen_AG, JAS39Gripen_AG,
] ]
MODDED_HELICOPTERS = [
UH_60L,
]
MODDED_VEHICLES = [ MODDED_VEHICLES = [
frenchpack._FIELD_HIDE, frenchpack._FIELD_HIDE,
frenchpack._FIELD_HIDE_SMALL, frenchpack._FIELD_HIDE_SMALL,

File diff suppressed because it is too large Load Diff

View File

@@ -22,6 +22,7 @@ def init():
global __dcs_saved_game_directory global __dcs_saved_game_directory
global __dcs_installation_directory global __dcs_installation_directory
global __last_save_file global __last_save_file
global __ignore_empty_install_directory
if PREFERENCES_PATH.exists(): if PREFERENCES_PATH.exists():
try: try:
@@ -31,14 +32,19 @@ def init():
__dcs_saved_game_directory = pref_data["saved_game_dir"] __dcs_saved_game_directory = pref_data["saved_game_dir"]
__dcs_installation_directory = pref_data["dcs_install_dir"] __dcs_installation_directory = pref_data["dcs_install_dir"]
__last_save_file = pref_data.get("last_save_file", "") __last_save_file = pref_data.get("last_save_file", "")
__ignore_empty_install_directory = pref_data.get(
"ignore_empty_install_directory", False
)
is_first_start = False is_first_start = False
except KeyError: except KeyError:
__dcs_saved_game_directory = "" __dcs_saved_game_directory = ""
__dcs_installation_directory = "" __dcs_installation_directory = ""
__last_save_file = "" __last_save_file = ""
__ignore_empty_install_directory = False
is_first_start = True is_first_start = True
else: else:
__last_save_file = "" __last_save_file = ""
__ignore_empty_install_directory = False
try: try:
__dcs_saved_game_directory = ( __dcs_saved_game_directory = (
dcs.installation.get_dcs_saved_games_directory() dcs.installation.get_dcs_saved_games_directory()
@@ -76,10 +82,12 @@ def save_config():
global __dcs_saved_game_directory global __dcs_saved_game_directory
global __dcs_installation_directory global __dcs_installation_directory
global __last_save_file global __last_save_file
global __ignore_empty_install_directory
pref_data = { pref_data = {
"saved_game_dir": __dcs_saved_game_directory, "saved_game_dir": __dcs_saved_game_directory,
"dcs_install_dir": __dcs_installation_directory, "dcs_install_dir": __dcs_installation_directory,
"last_save_file": __last_save_file, "last_save_file": __last_save_file,
"ignore_empty_install_directory": __ignore_empty_install_directory,
} }
PREFERENCES_PATH.parent.mkdir(exist_ok=True, parents=True) PREFERENCES_PATH.parent.mkdir(exist_ok=True, parents=True)
with PREFERENCES_PATH.open("w") as prefs: with PREFERENCES_PATH.open("w") as prefs:
@@ -96,6 +104,16 @@ def get_saved_game_dir():
return __dcs_saved_game_directory return __dcs_saved_game_directory
def ignore_empty_install_directory():
global __ignore_empty_install_directory
return __ignore_empty_install_directory
def set_ignore_empty_install_directory(value: bool):
global __ignore_empty_install_directory
__ignore_empty_install_directory = value
def get_last_save_file(): def get_last_save_file():
global __last_save_file global __last_save_file
print(__last_save_file) print(__last_save_file)

View File

@@ -2,6 +2,7 @@ import argparse
import logging import logging
import os import os
import sys import sys
import time
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from typing import Optional from typing import Optional
@@ -9,7 +10,7 @@ from typing import Optional
from PySide2 import QtWidgets from PySide2 import QtWidgets
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtGui import QPixmap from PySide2.QtGui import QPixmap
from PySide2.QtWidgets import QApplication, QSplashScreen from PySide2.QtWidgets import QApplication, QSplashScreen, QCheckBox
from dcs.payloads import PayloadDirectories from dcs.payloads import PayloadDirectories
from game import Game, VERSION, persistency from game import Game, VERSION, persistency
@@ -56,10 +57,15 @@ def inject_custom_payloads(user_path: Path) -> None:
def run_ui(game: Optional[Game]) -> None: def run_ui(game: Optional[Game]) -> None:
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # Potential fix for 4K screens os.environ["QT_ENABLE_HIGHDPI_SCALING"] = "1" # Potential fix for 4K screens
QApplication.setHighDpiScaleFactorRoundingPolicy(
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough
)
app = QApplication(sys.argv) app = QApplication(sys.argv)
app.setAttribute(Qt.AA_DisableWindowContextHelpButton) app.setAttribute(Qt.AA_DisableWindowContextHelpButton)
app.setAttribute(Qt.AA_EnableHighDpiScaling, True) # enable highdpi scaling
app.setAttribute(Qt.AA_UseHighDpiPixmaps, True) # use highdpi icons
# init the theme and load the stylesheet based on the theme index # init the theme and load the stylesheet based on the theme index
liberation_theme.init() liberation_theme.init()
@@ -89,6 +95,9 @@ def run_ui(game: Optional[Game]) -> None:
splash = QSplashScreen(pixmap) splash = QSplashScreen(pixmap)
splash.show() splash.show()
# Give enough time to read splash screen
time.sleep(3)
# Once splash screen is up : load resources & setup stuff # Once splash screen is up : load resources & setup stuff
uiconstants.load_icons() uiconstants.load_icons()
uiconstants.load_event_icons() uiconstants.load_event_icons()
@@ -99,20 +108,29 @@ def run_ui(game: Optional[Game]) -> None:
# Show warning if no DCS Installation directory was set # Show warning if no DCS Installation directory was set
if liberation_install.get_dcs_install_directory() == "": if liberation_install.get_dcs_install_directory() == "":
QtWidgets.QMessageBox.warning( logging.warning(
splash, "DCS Installation directory is empty. MissionScripting file will not be replaced!"
"No DCS installation directory.",
"The DCS Installation directory is not set correctly. "
"This will prevent DCS Liberation to work properly as the MissionScripting "
"file will not be modified."
"<br/><br/>To solve this problem, you can set the Installation directory "
"within the preferences menu. You can also manually edit or replace the "
"following file:"
"<br/><br/><strong>&lt;dcs_installation_directory&gt;/Scripts/MissionScripting.lua</strong>"
"<br/><br/>The easiest way to do it is to replace the original file with the file in dcs-liberation distribution (&lt;dcs_liberation_installation&gt;/resources/scripts/MissionScripting.lua)."
"<br/><br/>You can find more information on how to manually change this file in the Liberation Wiki (Page: Dedicated Server Guide) on GitHub.</p>",
QtWidgets.QMessageBox.StandardButton.Ok,
) )
if not liberation_install.ignore_empty_install_directory():
ignore_checkbox = QCheckBox("Do not show again")
ignore_checkbox.stateChanged.connect(set_ignore_empty_install_directory)
message_box = QtWidgets.QMessageBox(parent=splash)
message_box.setIcon(QtWidgets.QMessageBox.Icon.Warning)
message_box.setWindowTitle("No DCS installation directory.")
message_box.setText(
"The DCS Installation directory is not set correctly. "
"This will prevent DCS Liberation to work properly as the MissionScripting "
"file will not be modified."
"<br/><br/>To solve this problem, you can set the Installation directory "
"within the preferences menu. You can also manually edit or replace the "
"following file:"
"<br/><br/><strong>&lt;dcs_installation_directory&gt;/Scripts/MissionScripting.lua</strong>"
"<br/><br/>The easiest way to do it is to replace the original file with the file in dcs-liberation distribution (&lt;dcs_liberation_installation&gt;/resources/scripts/MissionScripting.lua)."
"<br/><br/>You can find more information on how to manually change this file in the Liberation Wiki (Page: Dedicated Server Guide) on GitHub.</p>"
)
message_box.setDefaultButton(QtWidgets.QMessageBox.StandardButton.Ok)
message_box.setCheckBox(ignore_checkbox)
message_box.exec_()
# Replace DCS Mission scripting file to allow DCS Liberation to work # Replace DCS Mission scripting file to allow DCS Liberation to work
try: try:
liberation_install.replace_mission_scripting_file() liberation_install.replace_mission_scripting_file()
@@ -261,6 +279,7 @@ def create_game(
ModSettings( ModSettings(
a4_skyhawk=False, a4_skyhawk=False,
f22_raptor=False, f22_raptor=False,
f104_starfighter=False,
hercules=False, hercules=False,
jas39_gripen=False, jas39_gripen=False,
su57_felon=False, su57_felon=False,
@@ -273,6 +292,11 @@ def create_game(
return game return game
def set_ignore_empty_install_directory(value: bool) -> None:
liberation_install.set_ignore_empty_install_directory(value)
liberation_install.save_config()
def lint_all_weapon_data() -> None: def lint_all_weapon_data() -> None:
for weapon in WeaponGroup.named("Unknown").weapons: for weapon in WeaponGroup.named("Unknown").weapons:
logging.warning(f"No weapon data for {weapon}: {weapon.clsid}") logging.warning(f"No weapon data for {weapon}: {weapon.clsid}")
@@ -293,6 +317,11 @@ def main():
logging.debug("Python version %s", sys.version) logging.debug("Python version %s", sys.version)
if not str(Path(__file__)).isascii():
logging.warning(
"Installation path contains non-ASCII characters. This is known to cause problems."
)
game: Optional[Game] = None game: Optional[Game] = None
args = parse_args() args = parse_args()

View File

@@ -214,6 +214,7 @@ class AtoModel(QAbstractListModel):
PackageRole = Qt.UserRole PackageRole = Qt.UserRole
client_slots_changed = Signal() client_slots_changed = Signal()
packages_changed = Signal()
def __init__(self, game_model: GameModel, ato: AirTaskingOrder) -> None: def __init__(self, game_model: GameModel, ato: AirTaskingOrder) -> None:
super().__init__() super().__init__()
@@ -245,6 +246,7 @@ class AtoModel(QAbstractListModel):
self.endInsertRows() self.endInsertRows()
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.client_slots_changed.emit() self.client_slots_changed.emit()
self.on_packages_changed()
def delete_package_at_index(self, index: QModelIndex) -> None: def delete_package_at_index(self, index: QModelIndex) -> None:
"""Removes the package at the given index from the ATO.""" """Removes the package at the given index from the ATO."""
@@ -263,6 +265,12 @@ class AtoModel(QAbstractListModel):
self.endRemoveRows() self.endRemoveRows()
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.client_slots_changed.emit() self.client_slots_changed.emit()
self.on_packages_changed()
def on_packages_changed(self) -> None:
if self.game is not None:
self.game.compute_unculled_zones()
self.packages_changed.emit()
def package_at_index(self, index: QModelIndex) -> Package: def package_at_index(self, index: QModelIndex) -> Package:
"""Returns the package at the given index.""" """Returns the package at the given index."""

View File

@@ -34,6 +34,7 @@ def load_icons():
ICONS["Github"] = QPixmap( ICONS["Github"] = QPixmap(
"./resources/ui/misc/" + get_theme_icons() + "/github.png" "./resources/ui/misc/" + get_theme_icons() + "/github.png"
) )
ICONS["Ukraine"] = QPixmap("./resources/ui/misc/ukraine.png")
ICONS["Control Points"] = QPixmap( ICONS["Control Points"] = QPixmap(
"./resources/ui/misc/" + get_theme_icons() + "/circle.png" "./resources/ui/misc/" + get_theme_icons() + "/circle.png"
@@ -111,6 +112,9 @@ def load_icons():
"./resources/ui/misc/" + get_theme_icons() + "/pluginsoptions.png" "./resources/ui/misc/" + get_theme_icons() + "/pluginsoptions.png"
) )
ICONS["Notes"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/notes.png") ICONS["Notes"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/notes.png")
ICONS["Reload"] = QPixmap(
"./resources/ui/misc/" + get_theme_icons() + "/reload.png"
)
ICONS["TaskCAS"] = QPixmap("./resources/ui/tasks/cas.png") ICONS["TaskCAS"] = QPixmap("./resources/ui/tasks/cas.png")
ICONS["TaskCAP"] = QPixmap("./resources/ui/tasks/cap.png") ICONS["TaskCAP"] = QPixmap("./resources/ui/tasks/cap.png")

View File

@@ -1057,6 +1057,12 @@ class MapModel(QObject):
GameUpdateSignal.get_instance().flight_selection_changed.connect( GameUpdateSignal.get_instance().flight_selection_changed.connect(
self.set_flight_selection self.set_flight_selection
) )
self.game_model.ato_model_for(True).packages_changed.connect(
self.on_package_change
)
self.game_model.ato_model_for(False).packages_changed.connect(
self.on_package_change
)
self.reset() self.reset()
def clear(self) -> None: def clear(self) -> None:
@@ -1291,6 +1297,9 @@ class MapModel(QObject):
def mapZones(self) -> NavMeshJs: def mapZones(self) -> NavMeshJs:
return self._map_zones return self._map_zones
def on_package_change(self) -> None:
self.reset_unculled_zones()
def reset_unculled_zones(self) -> None: def reset_unculled_zones(self) -> None:
self._unculled_zones = list(UnculledZone.each_from_game(self.game)) self._unculled_zones = list(UnculledZone.each_from_game(self.game))
self.unculledZonesChanged.emit() self.unculledZonesChanged.emit()

View File

@@ -10,28 +10,33 @@ from PySide2.QtCore import (
) )
from PySide2.QtGui import QStandardItemModel, QStandardItem, QIcon from PySide2.QtGui import QStandardItemModel, QStandardItem, QIcon
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
QComboBox,
QDialog, QDialog,
QListView, QListView,
QVBoxLayout,
QGroupBox, QGroupBox,
QLabel, QLabel,
QWidget,
QScrollArea, QScrollArea,
QLineEdit, QLineEdit,
QTextEdit,
QCheckBox,
QHBoxLayout, QHBoxLayout,
QStackedLayout, QStackedLayout,
QTabWidget, QTabWidget,
QComboBox, QTextEdit,
QVBoxLayout,
QWidget,
QCheckBox,
QPushButton,
QGridLayout,
QToolButton,
) )
from game import Game from game import Game
from gen.flights.flight import FlightType
from game.coalition import Coalition
from game.dcs.aircrafttype import AircraftType from game.dcs.aircrafttype import AircraftType
from game.squadrons import AirWing, Pilot, Squadron from game.squadrons import AirWing, Pilot, Squadron
from game.theater import ControlPoint, ConflictTheater from game.squadrons.squadrondef import SquadronDef
from gen.flights.flight import FlightType from game.theater import ConflictTheater, ControlPoint
from qt_ui.uiconstants import AIRCRAFT_ICONS from qt_ui.uiconstants import AIRCRAFT_ICONS, ICONS
class AllowedMissionTypeControls(QVBoxLayout): class AllowedMissionTypeControls(QVBoxLayout):
@@ -76,25 +81,37 @@ class SquadronBaseSelector(QComboBox):
def __init__( def __init__(
self, self,
bases: Iterable[ControlPoint], bases: Iterable[ControlPoint],
squadron: Squadron, selected_base: Optional[ControlPoint],
aircraft_type: Optional[AircraftType],
) -> None: ) -> None:
super().__init__() super().__init__()
self.bases = list(bases)
self.squadron = squadron
self.setSizeAdjustPolicy(self.AdjustToContents) self.setSizeAdjustPolicy(self.AdjustToContents)
self.bases = list(bases)
self.set_aircraft_type(aircraft_type)
for base in self.bases: if selected_base:
if not base.can_operate(self.squadron.aircraft): self.setCurrentText(selected_base.name)
continue
self.addItem(base.name, base) def set_aircraft_type(self, aircraft_type: Optional[AircraftType]):
self.model().sort(0) self.clear()
self.setCurrentText(self.squadron.location.name) if aircraft_type:
for base in self.bases:
if not base.can_operate(aircraft_type):
continue
self.addItem(base.name, base)
self.model().sort(0)
self.setEnabled(True)
else:
self.addItem("Select aircraft type first", None)
self.setEnabled(False)
self.update()
class SquadronConfigurationBox(QGroupBox): class SquadronConfigurationBox(QGroupBox):
remove_squadron_signal = Signal(Squadron)
def __init__(self, squadron: Squadron, theater: ConflictTheater) -> None: def __init__(self, squadron: Squadron, theater: ConflictTheater) -> None:
super().__init__() super().__init__()
self.setCheckable(True)
self.squadron = squadron self.squadron = squadron
self.reset_title() self.reset_title()
@@ -109,14 +126,24 @@ class SquadronConfigurationBox(QGroupBox):
self.name_edit.textChanged.connect(self.on_name_changed) self.name_edit.textChanged.connect(self.on_name_changed)
left_column.addWidget(self.name_edit) left_column.addWidget(self.name_edit)
left_column.addWidget(QLabel("Nickname:")) nickname_edit_layout = QGridLayout()
left_column.addLayout(nickname_edit_layout)
nickname_edit_layout.addWidget(QLabel("Nickname:"), 0, 0, 1, 2)
self.nickname_edit = QLineEdit(squadron.nickname) self.nickname_edit = QLineEdit(squadron.nickname)
self.nickname_edit.textChanged.connect(self.on_nickname_changed) self.nickname_edit.textChanged.connect(self.on_nickname_changed)
left_column.addWidget(self.nickname_edit) nickname_edit_layout.addWidget(self.nickname_edit, 1, 0, Qt.AlignTop)
reroll_nickname_button = QToolButton()
reroll_nickname_button.setIcon(QIcon(ICONS["Reload"]))
reroll_nickname_button.setToolTip("Re-roll nickname")
reroll_nickname_button.clicked.connect(self.reroll_nickname)
nickname_edit_layout.addWidget(reroll_nickname_button, 1, 1, Qt.AlignTop)
left_column.addWidget(QLabel("Base:")) left_column.addWidget(QLabel("Base:"))
self.base_selector = SquadronBaseSelector( self.base_selector = SquadronBaseSelector(
theater.control_points_for(squadron.player), squadron theater.control_points_for(squadron.player),
squadron.location,
squadron.aircraft,
) )
self.base_selector.currentIndexChanged.connect(self.on_base_changed) self.base_selector.currentIndexChanged.connect(self.on_base_changed)
left_column.addWidget(self.base_selector) left_column.addWidget(self.base_selector)
@@ -138,12 +165,18 @@ class SquadronConfigurationBox(QGroupBox):
self.player_list.setAcceptRichText(False) self.player_list.setAcceptRichText(False)
self.player_list.setEnabled(squadron.player) self.player_list.setEnabled(squadron.player)
left_column.addWidget(self.player_list) left_column.addWidget(self.player_list)
delete_button = QPushButton("Remove Squadron")
delete_button.setMaximumWidth(140)
delete_button.clicked.connect(self.remove_from_squadron_config)
left_column.addWidget(delete_button)
left_column.addStretch() left_column.addStretch()
self.allowed_missions = AllowedMissionTypeControls(squadron) self.allowed_missions = AllowedMissionTypeControls(squadron)
columns.addLayout(self.allowed_missions) columns.addLayout(self.allowed_missions)
def remove_from_squadron_config(self) -> None:
self.remove_squadron_signal.emit(self.squadron)
def on_name_changed(self, text: str) -> None: def on_name_changed(self, text: str) -> None:
self.squadron.name = text self.squadron.name = text
self.reset_title() self.reset_title()
@@ -160,6 +193,11 @@ class SquadronConfigurationBox(QGroupBox):
def reset_title(self) -> None: def reset_title(self) -> None:
self.setTitle(f"{self.squadron.name} - {self.squadron.aircraft}") self.setTitle(f"{self.squadron.name} - {self.squadron.aircraft}")
def reroll_nickname(self) -> None:
self.nickname_edit.setText(
self.squadron.coalition.air_wing.squadron_def_generator.random_nickname()
)
def apply(self) -> Squadron: def apply(self) -> Squadron:
player_names = self.player_list.toPlainText().splitlines() player_names = self.player_list.toPlainText().splitlines()
# Prepend player pilots so they get set active first. # Prepend player pilots so they get set active first.
@@ -173,29 +211,48 @@ class SquadronConfigurationBox(QGroupBox):
class SquadronConfigurationLayout(QVBoxLayout): class SquadronConfigurationLayout(QVBoxLayout):
config_changed = Signal(AircraftType)
def __init__(self, squadrons: list[Squadron], theater: ConflictTheater) -> None: def __init__(self, squadrons: list[Squadron], theater: ConflictTheater) -> None:
super().__init__() super().__init__()
self.squadron_configs = [] self.squadron_configs = []
self.theater = theater
for squadron in squadrons: for squadron in squadrons:
squadron_config = SquadronConfigurationBox(squadron, theater) self.add_squadron(squadron)
self.squadron_configs.append(squadron_config)
self.addWidget(squadron_config)
def apply(self) -> list[Squadron]: def apply(self) -> list[Squadron]:
keep_squadrons = [] keep_squadrons = []
for squadron_config in self.squadron_configs: for squadron_config in self.squadron_configs:
if squadron_config.isChecked(): keep_squadrons.append(squadron_config.apply())
keep_squadrons.append(squadron_config.apply())
return keep_squadrons return keep_squadrons
def remove_squadron(self, squadron: Squadron) -> None:
for squadron_config in self.squadron_configs:
if squadron_config.squadron == squadron:
squadron_config.deleteLater()
self.squadron_configs.remove(squadron_config)
squadron.coalition.air_wing.unclaim_squadron_def(squadron)
self.update()
self.config_changed.emit(squadron.aircraft)
return
def add_squadron(self, squadron: Squadron) -> None:
squadron_config = SquadronConfigurationBox(squadron, self.theater)
squadron_config.remove_squadron_signal.connect(self.remove_squadron)
self.squadron_configs.append(squadron_config)
self.addWidget(squadron_config)
class AircraftSquadronsPage(QWidget): class AircraftSquadronsPage(QWidget):
remove_squadron_page = Signal(AircraftType)
def __init__(self, squadrons: list[Squadron], theater: ConflictTheater) -> None: def __init__(self, squadrons: list[Squadron], theater: ConflictTheater) -> None:
super().__init__() super().__init__()
layout = QVBoxLayout() layout = QVBoxLayout()
self.setLayout(layout) self.setLayout(layout)
self.squadrons_config = SquadronConfigurationLayout(squadrons, theater) self.squadrons_config = SquadronConfigurationLayout(squadrons, theater)
self.squadrons_config.config_changed.connect(self.on_squadron_config_changed)
scrolling_widget = QWidget() scrolling_widget = QWidget()
scrolling_widget.setLayout(self.squadrons_config) scrolling_widget.setLayout(self.squadrons_config)
@@ -208,21 +265,56 @@ class AircraftSquadronsPage(QWidget):
layout.addWidget(scrolling_area) layout.addWidget(scrolling_area)
def on_squadron_config_changed(self, aircraft_type: AircraftType):
if len(self.squadrons_config.squadron_configs) == 0:
self.remove_squadron_page.emit(aircraft_type)
def add_squadron_to_page(self, squadron: Squadron):
self.squadrons_config.add_squadron(squadron)
def apply(self) -> list[Squadron]: def apply(self) -> list[Squadron]:
return self.squadrons_config.apply() return self.squadrons_config.apply()
class AircraftSquadronsPanel(QStackedLayout): class AircraftSquadronsPanel(QStackedLayout):
page_removed = Signal(AircraftType)
def __init__(self, air_wing: AirWing, theater: ConflictTheater) -> None: def __init__(self, air_wing: AirWing, theater: ConflictTheater) -> None:
super().__init__() super().__init__()
self.air_wing = air_wing self.air_wing = air_wing
self.theater = theater
self.squadrons_pages: dict[AircraftType, AircraftSquadronsPage] = {} self.squadrons_pages: dict[AircraftType, AircraftSquadronsPage] = {}
for aircraft, squadrons in self.air_wing.squadrons.items(): for aircraft, squadrons in self.air_wing.squadrons.items():
page = AircraftSquadronsPage(squadrons, theater) self.new_page_for_type(aircraft, squadrons)
self.addWidget(page)
self.squadrons_pages[aircraft] = page def remove_page_for_type(self, aircraft_type: AircraftType):
page = self.squadrons_pages[aircraft_type]
self.removeWidget(page)
page.deleteLater()
self.squadrons_pages.pop(aircraft_type)
self.page_removed.emit(aircraft_type)
self.update()
def new_page_for_type(
self, aircraft_type: AircraftType, squadrons: list[Squadron]
) -> None:
page = AircraftSquadronsPage(squadrons, self.theater)
page.remove_squadron_page.connect(self.remove_page_for_type)
self.addWidget(page)
self.squadrons_pages[aircraft_type] = page
def add_squadron_to_panel(self, squadron: Squadron):
# Find existing page or add new one
if squadron.aircraft in self.squadrons_pages:
page = self.squadrons_pages[squadron.aircraft]
page.add_squadron_to_page(squadron)
else:
self.new_page_for_type(squadron.aircraft, [squadron])
self.update()
def apply(self) -> None: def apply(self) -> None:
self.air_wing.squadrons = {}
for aircraft, page in self.squadrons_pages.items(): for aircraft, page in self.squadrons_pages.items():
self.air_wing.squadrons[aircraft] = page.apply() self.air_wing.squadrons[aircraft] = page.apply()
@@ -235,21 +327,29 @@ class AircraftTypeList(QListView):
self.setIconSize(QSize(91, 24)) self.setIconSize(QSize(91, 24))
self.setMinimumWidth(300) self.setMinimumWidth(300)
model = QStandardItemModel(self) self.item_model = QStandardItemModel(self)
self.setModel(model) self.setModel(self.item_model)
self.selectionModel().setCurrentIndex( self.selectionModel().setCurrentIndex(
model.index(0, 0), QItemSelectionModel.Select self.item_model.index(0, 0), QItemSelectionModel.Select
) )
self.selectionModel().selectionChanged.connect(self.on_selection_changed) self.selectionModel().selectionChanged.connect(self.on_selection_changed)
for aircraft in air_wing.squadrons: for aircraft in air_wing.squadrons:
aircraft_item = QStandardItem(aircraft.name) self.add_aircraft_type(aircraft)
icon = self.icon_for(aircraft)
if icon is not None: def remove_aircraft_type(self, aircraft: AircraftType):
aircraft_item.setIcon(icon) for item in self.item_model.findItems(aircraft.name):
aircraft_item.setEditable(False) self.item_model.removeRow(item.row())
aircraft_item.setSelectable(True) self.page_index_changed.emit(self.selectionModel().currentIndex().row())
model.appendRow(aircraft_item)
def add_aircraft_type(self, aircraft: AircraftType):
aircraft_item = QStandardItem(aircraft.name)
icon = self.icon_for(aircraft)
if icon is not None:
aircraft_item.setIcon(icon)
aircraft_item.setEditable(False)
aircraft_item.setSelectable(True)
self.item_model.appendRow(aircraft_item)
def on_selection_changed( def on_selection_changed(
self, selected: QItemSelection, _deselected: QItemSelection self, selected: QItemSelection, _deselected: QItemSelection
@@ -270,25 +370,70 @@ class AircraftTypeList(QListView):
class AirWingConfigurationTab(QWidget): class AirWingConfigurationTab(QWidget):
def __init__(self, air_wing: AirWing, theater: ConflictTheater) -> None: def __init__(self, coalition: Coalition, game: Game) -> None:
super().__init__() super().__init__()
layout = QHBoxLayout() layout = QGridLayout()
self.setLayout(layout) self.setLayout(layout)
self.game = game
self.coalition = coalition
type_list = AircraftTypeList(air_wing) self.type_list = AircraftTypeList(coalition.air_wing)
type_list.page_index_changed.connect(self.on_aircraft_changed)
layout.addWidget(type_list)
self.squadrons_panel = AircraftSquadronsPanel(air_wing, theater) layout.addWidget(self.type_list, 1, 1, 1, 2)
layout.addLayout(self.squadrons_panel)
add_button = QPushButton("Add Squadron")
add_button.clicked.connect(lambda state: self.add_squadron())
layout.addWidget(add_button, 2, 1, 1, 1)
self.squadrons_panel = AircraftSquadronsPanel(coalition.air_wing, game.theater)
self.squadrons_panel.page_removed.connect(self.type_list.remove_aircraft_type)
layout.addLayout(self.squadrons_panel, 1, 3, 2, 1)
self.type_list.page_index_changed.connect(self.squadrons_panel.setCurrentIndex)
def add_squadron(self) -> None:
selected_aircraft = None
if self.type_list.selectionModel().currentIndex().row() >= 0:
selected_aircraft = self.type_list.item_model.item(
self.type_list.selectionModel().currentIndex().row()
).text()
popup = SquadronConfigPopup(
selected_aircraft,
self.coalition.faction.aircrafts,
list(self.game.theater.control_points_for(self.coalition.player)),
self.coalition.air_wing.squadron_defs,
)
if popup.exec_() != QDialog.Accepted:
return
selected_type = popup.aircraft_type_selector.currentData()
selected_base = popup.squadron_base_selector.currentData()
selected_def = popup.squadron_def_selector.currentData()
# Let user choose the preset or generate one
squadron_def = (
selected_def
or self.coalition.air_wing.squadron_def_generator.generate_for_aircraft(
selected_type
)
)
squadron = Squadron.create_from(
squadron_def, selected_base, self.coalition, self.game
)
# Add Squadron
if not self.type_list.item_model.findItems(selected_type.name):
self.type_list.add_aircraft_type(selected_type)
# TODO Select the newly added type
self.squadrons_panel.add_squadron_to_panel(squadron)
self.update()
def apply(self) -> None: def apply(self) -> None:
self.squadrons_panel.apply() self.squadrons_panel.apply()
def on_aircraft_changed(self, index: QModelIndex) -> None:
self.squadrons_panel.setCurrentIndex(index)
class AirWingConfigurationDialog(QDialog): class AirWingConfigurationDialog(QDialog):
"""Dialog window for air wing configuration.""" """Dialog window for air wing configuration."""
@@ -302,22 +447,13 @@ class AirWingConfigurationDialog(QDialog):
layout = QVBoxLayout() layout = QVBoxLayout()
self.setLayout(layout) self.setLayout(layout)
doc_url = (
"https://github.com/dcs-liberation/dcs_liberation/wiki/Squadrons-and-pilots"
)
doc_label = QLabel( doc_label = QLabel(
"Use this opportunity to customize the squadrons available to your " "Use this opportunity to customize the squadrons available to your "
"coalition. <strong>This is your only opportunity to make changes.</strong>" "coalition. <strong>This is your only opportunity to make changes.</strong>"
"<br /><br />" "<br /><br />"
"To accept your changes and continue, close this window.<br />" "To accept your changes and continue, close this window."
"<br />"
"To remove a squadron from the game, uncheck the box in the title. New "
"squadrons cannot be added via the UI at this time. To add a custom "
"squadron,<br />"
f'see <a style="color:#ffffff" href="{doc_url}">the wiki</a>.'
) )
doc_label.setOpenExternalLinks(True)
layout.addWidget(doc_label) layout.addWidget(doc_label)
tab_widget = QTabWidget() tab_widget = QTabWidget()
@@ -325,7 +461,7 @@ class AirWingConfigurationDialog(QDialog):
self.tabs = [] self.tabs = []
for coalition in game.coalitions: for coalition in game.coalitions:
coalition_tab = AirWingConfigurationTab(coalition.air_wing, game.theater) coalition_tab = AirWingConfigurationTab(coalition, game)
name = "Blue" if coalition.player else "Red" name = "Blue" if coalition.player else "Red"
tab_widget.addTab(coalition_tab, name) tab_widget.addTab(coalition_tab, name)
self.tabs.append(coalition_tab) self.tabs.append(coalition_tab)
@@ -334,3 +470,104 @@ class AirWingConfigurationDialog(QDialog):
for tab in self.tabs: for tab in self.tabs:
tab.apply() tab.apply()
super().reject() super().reject()
class SquadronAircraftTypeSelector(QComboBox):
def __init__(
self, types: list[AircraftType], selected_aircraft: Optional[str]
) -> None:
super().__init__()
self.setSizeAdjustPolicy(self.AdjustToContents)
for type in sorted(types, key=lambda type: type.name):
self.addItem(type.name, type)
if selected_aircraft:
self.setCurrentText(selected_aircraft)
class SquadronDefSelector(QComboBox):
def __init__(
self,
squadron_defs: dict[AircraftType, list[SquadronDef]],
aircraft: Optional[AircraftType],
) -> None:
super().__init__()
self.setSizeAdjustPolicy(self.AdjustToContents)
self.squadron_defs = squadron_defs
self.set_aircraft_type(aircraft)
def set_aircraft_type(self, aircraft: Optional[AircraftType]):
self.clear()
self.addItem("None (Random)", None)
if aircraft and aircraft in self.squadron_defs:
for squadron_def in sorted(
self.squadron_defs[aircraft], key=lambda squadron_def: squadron_def.name
):
if not squadron_def.claimed:
squadron_name = squadron_def.name
if squadron_def.nickname:
squadron_name += " (" + squadron_def.nickname + ")"
self.addItem(squadron_name, squadron_def)
self.setCurrentText("None (Random)")
class SquadronConfigPopup(QDialog):
def __init__(
self,
selected_aircraft: Optional[str],
types: list[AircraftType],
bases: list[ControlPoint],
squadron_defs: dict[AircraftType, list[SquadronDef]],
) -> None:
super().__init__()
self.setWindowTitle(f"Add new Squadron")
self.column = QVBoxLayout()
self.setLayout(self.column)
self.bases = bases
self.column.addWidget(QLabel("Aircraft:"))
self.aircraft_type_selector = SquadronAircraftTypeSelector(
types, selected_aircraft
)
self.aircraft_type_selector.currentIndexChanged.connect(
self.on_aircraft_selection
)
self.column.addWidget(self.aircraft_type_selector)
self.column.addWidget(QLabel("Base:"))
self.squadron_base_selector = SquadronBaseSelector(
bases, None, self.aircraft_type_selector.currentData()
)
self.column.addWidget(self.squadron_base_selector)
self.column.addWidget(QLabel("Preset:"))
self.squadron_def_selector = SquadronDefSelector(
squadron_defs, self.aircraft_type_selector.currentData()
)
self.column.addWidget(self.squadron_def_selector)
self.column.addStretch()
self.button_layout = QHBoxLayout()
self.column.addLayout(self.button_layout)
self.accept_button = QPushButton("Accept")
self.accept_button.clicked.connect(lambda state: self.accept())
self.button_layout.addWidget(self.accept_button)
self.cancel_button = QPushButton("Cancel")
self.cancel_button.clicked.connect(lambda state: self.reject())
self.button_layout.addWidget(self.cancel_button)
def on_aircraft_selection(self) -> None:
self.squadron_base_selector.set_aircraft_type(
self.aircraft_type_selector.currentData()
)
self.squadron_def_selector.set_aircraft_type(
self.aircraft_type_selector.currentData()
)
self.update()

View File

@@ -155,6 +155,12 @@ class QLiberationWindow(QMainWindow):
) )
) )
self.ukraineAction = QAction("&Ukraine", self)
self.ukraineAction.setIcon(CONST.ICONS["Ukraine"])
self.ukraineAction.triggered.connect(
lambda: webbrowser.open_new_tab("https://shdwp.github.io/ukraine/")
)
self.openLogsAction = QAction("Show &logs", self) self.openLogsAction = QAction("Show &logs", self)
self.openLogsAction.triggered.connect(self.showLogsDialog) self.openLogsAction.triggered.connect(self.showLogsDialog)
@@ -170,6 +176,19 @@ class QLiberationWindow(QMainWindow):
self.openNotesAction.setIcon(CONST.ICONS["Notes"]) self.openNotesAction.setIcon(CONST.ICONS["Notes"])
self.openNotesAction.triggered.connect(self.showNotesDialog) self.openNotesAction.triggered.connect(self.showNotesDialog)
self.enable_game_actions(False)
def enable_game_actions(self, enabled: bool):
self.openSettingsAction.setVisible(enabled)
self.openStatsAction.setVisible(enabled)
self.openNotesAction.setVisible(enabled)
# Also Disable SaveAction to prevent Keyboard Shortcut
self.saveGameAction.setEnabled(enabled)
self.saveGameAction.setVisible(enabled)
self.saveAsAction.setEnabled(enabled)
self.saveAsAction.setVisible(enabled)
def initToolbar(self): def initToolbar(self):
self.tool_bar = self.addToolBar("File") self.tool_bar = self.addToolBar("File")
self.tool_bar.addAction(self.newGameAction) self.tool_bar.addAction(self.newGameAction)
@@ -179,6 +198,7 @@ class QLiberationWindow(QMainWindow):
self.links_bar = self.addToolBar("Links") self.links_bar = self.addToolBar("Links")
self.links_bar.addAction(self.openDiscordAction) self.links_bar.addAction(self.openDiscordAction)
self.links_bar.addAction(self.openGithubAction) self.links_bar.addAction(self.openGithubAction)
self.links_bar.addAction(self.ukraineAction)
self.actions_bar = self.addToolBar("Actions") self.actions_bar = self.addToolBar("Actions")
self.actions_bar.addAction(self.openSettingsAction) self.actions_bar.addAction(self.openSettingsAction)
@@ -202,6 +222,7 @@ class QLiberationWindow(QMainWindow):
help_menu = self.menu.addMenu("&Help") help_menu = self.menu.addMenu("&Help")
help_menu.addAction(self.openDiscordAction) help_menu.addAction(self.openDiscordAction)
help_menu.addAction(self.openGithubAction) help_menu.addAction(self.openGithubAction)
help_menu.addAction(self.ukraineAction)
help_menu.addAction( help_menu.addAction(
"&Releases", "&Releases",
lambda: webbrowser.open_new_tab( lambda: webbrowser.open_new_tab(
@@ -328,6 +349,8 @@ class QLiberationWindow(QMainWindow):
QMessageBox.Ok, QMessageBox.Ok,
) )
GameUpdateSignal.get_instance().updateGame(None) GameUpdateSignal.get_instance().updateGame(None)
finally:
self.enable_game_actions(self.game is not None)
def showAboutDialog(self): def showAboutDialog(self):
text = ( text = (
@@ -345,6 +368,8 @@ class QLiberationWindow(QMainWindow):
"<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>" "<b>Ciribob </b> <i>for the JTACAutoLase.lua script</i><br/>"
"<b>Walder </b> <i>for the Skynet-IADS script</i><br/>" "<b>Walder </b> <i>for the Skynet-IADS script</i><br/>"
"<b>Anubis Yinepu </b> <i>for the Hercules Cargo script</i><br/>" "<b>Anubis Yinepu </b> <i>for the Hercules Cargo script</i><br/>"
+ "<h4>Splash Screen :</h4>"
+ "Artwork by Andriy Dankovych (CC BY-SA) [https://www.facebook.com/AndriyDankovych]"
) )
about = QMessageBox() about = QMessageBox()
about.setWindowTitle("About DCS Liberation") about.setWindowTitle("About DCS Liberation")

View File

@@ -245,6 +245,8 @@ class QBaseMenu2(QDialog):
return "./resources/ui/carrier.png" return "./resources/ui/carrier.png"
elif self.cp.cptype == ControlPointType.LHA_GROUP: elif self.cp.cptype == ControlPointType.LHA_GROUP:
return "./resources/ui/lha.png" return "./resources/ui/lha.png"
elif self.cp.cptype == ControlPointType.FOB and self.cp.has_helipads:
return "./resources/ui/heliport.png"
elif self.cp.cptype == ControlPointType.FOB: elif self.cp.cptype == ControlPointType.FOB:
return "./resources/ui/fob.png" return "./resources/ui/fob.png"
else: else:

View File

@@ -2,27 +2,27 @@ from __future__ import annotations
import logging import logging
from enum import Enum from enum import Enum
from typing import TypeVar, Generic from typing import Generic, TypeVar
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtWidgets import ( from PySide2.QtWidgets import (
QApplication,
QFrame,
QGridLayout,
QGroupBox, QGroupBox,
QHBoxLayout, QHBoxLayout,
QLabel, QLabel,
QLayout,
QMessageBox,
QPushButton, QPushButton,
QSizePolicy, QSizePolicy,
QSpacerItem, QSpacerItem,
QGridLayout,
QApplication,
QFrame,
QMessageBox,
) )
from game.dcs.unittype import UnitType from game.purchaseadapter import PurchaseAdapter, TransactionError
from qt_ui.models import GameModel from qt_ui.models import GameModel
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.QUnitInfoWindow import QUnitInfoWindow from qt_ui.windows.QUnitInfoWindow import QUnitInfoWindow
from game.purchaseadapter import PurchaseAdapter, TransactionError
class RecruitType(Enum): class RecruitType(Enum):
@@ -153,6 +153,7 @@ class UnitTransactionFrame(QFrame, Generic[TransactionItemType]):
exist.setMaximumHeight(72) exist.setMaximumHeight(72)
exist.setMinimumHeight(36) exist.setMinimumHeight(36)
existLayout = QHBoxLayout() existLayout = QHBoxLayout()
existLayout.setSizeConstraint(QLayout.SetMinimumSize)
exist.setLayout(existLayout) exist.setLayout(existLayout)
existing_units = self.current_quantity_of(item) existing_units = self.current_quantity_of(item)
@@ -229,25 +230,23 @@ class UnitTransactionFrame(QFrame, Generic[TransactionItemType]):
self.update_purchase_controls() self.update_purchase_controls()
self.update_available_budget() self.update_available_budget()
def buy(self, item: TransactionItemType, quantity: int) -> bool: def buy(self, item: TransactionItemType, quantity: int) -> None:
try: try:
self.purchase_adapter.buy(item, quantity) self.purchase_adapter.buy(item, quantity)
except TransactionError as ex: except TransactionError as ex:
logging.exception(f"Purchase of {self.display_name_of(item)} failed") logging.exception(f"Purchase of {self.display_name_of(item)} failed")
QMessageBox.warning(self, "Purchase failed", str(ex), QMessageBox.Ok) QMessageBox.warning(self, "Purchase failed", str(ex), QMessageBox.Ok)
return False finally:
self.post_transaction_update() self.post_transaction_update()
return True
def sell(self, item: TransactionItemType, quantity: int) -> bool: def sell(self, item: TransactionItemType, quantity: int) -> None:
try: try:
self.purchase_adapter.sell(item, quantity) self.purchase_adapter.sell(item, quantity)
except TransactionError as ex: except TransactionError as ex:
logging.exception(f"Sale of {self.display_name_of(item)} failed") logging.exception(f"Sale of {self.display_name_of(item)} failed")
QMessageBox.warning(self, "Sale failed", str(ex), QMessageBox.Ok) QMessageBox.warning(self, "Sale failed", str(ex), QMessageBox.Ok)
return False finally:
self.post_transaction_update() self.post_transaction_update()
return True
def update_purchase_controls(self) -> None: def update_purchase_controls(self) -> None:
for group in self.purchase_groups.values(): for group in self.purchase_groups.values():

View File

@@ -55,10 +55,10 @@ class FinancesLayout(QGridLayout):
def add_total(self, game, income, player): def add_total(self, game, income, player):
self.add_row( self.add_row(
middle=f"Income multiplier: {income.multiplier:.1f}", middle=f"Income multiplier: {income.multiplier:.1f}",
right=f"<b>{income.total}M</b>", right=f"<b>{income.total:.1f}M</b>",
) )
budget = game.coalition_for(player).budget budget = game.coalition_for(player).budget
self.add_row(middle="Balance", right=f"<b>{budget}M</b>") self.add_row(middle="Balance", right=f"<b>{budget:.1f}M</b>")
self.setRowStretch(next(self.row), 1) self.setRowStretch(next(self.row), 1)
def add_row( def add_row(

View File

@@ -40,6 +40,9 @@ class QFlightCreator(QDialog):
self.custom_name_text = None self.custom_name_text = None
self.country = self.game.blue.country_name self.country = self.game.blue.country_name
# Make dialog modal to prevent background windows to close unexpectedly.
self.setModal(True)
self.setWindowTitle("Create flight") self.setWindowTitle("Create flight")
self.setWindowIcon(EVENT_ICONS["strike"]) self.setWindowIcon(EVENT_ICONS["strike"])

View File

@@ -5,17 +5,17 @@ from datetime import timedelta
from typing import List from typing import List
from PySide2 import QtGui, QtWidgets from PySide2 import QtGui, QtWidgets
from PySide2.QtCore import QItemSelectionModel, QPoint, Qt, QDate from PySide2.QtCore import QDate, QItemSelectionModel, QPoint, Qt
from PySide2.QtWidgets import QVBoxLayout, QTextEdit, QLabel, QCheckBox from PySide2.QtWidgets import QCheckBox, QLabel, QTextEdit, QVBoxLayout
from jinja2 import Environment, FileSystemLoader, select_autoescape from jinja2 import Environment, FileSystemLoader, select_autoescape
from game import db from game import db
from game.campaignloader.campaign import Campaign from game.campaignloader.campaign import Campaign
from game.factions.faction import Faction
from game.settings import Settings from game.settings import Settings
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
from game.factions.faction import Faction
from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar from qt_ui.widgets.QLiberationCalendar import QLiberationCalendar
from qt_ui.widgets.spinsliders import FloatSpinSlider, TimeInputs, CurrencySpinner from qt_ui.widgets.spinsliders import CurrencySpinner, FloatSpinSlider, TimeInputs
from qt_ui.windows.AirWingConfigurationDialog import AirWingConfigurationDialog from qt_ui.windows.AirWingConfigurationDialog import AirWingConfigurationDialog
from qt_ui.windows.newgame.QCampaignList import QCampaignList from qt_ui.windows.newgame.QCampaignList import QCampaignList
@@ -70,6 +70,8 @@ class NewGameWizard(QtWidgets.QWizard):
if campaign is None: if campaign is None:
campaign = self.campaigns[0] campaign = self.campaigns[0]
logging.info("New campaign selected: %s", campaign.name)
if self.field("usePreset"): if self.field("usePreset"):
start_date = db.TIME_PERIODS[ start_date = db.TIME_PERIODS[
list(db.TIME_PERIODS.keys())[self.field("timePeriod")] list(db.TIME_PERIODS.keys())[self.field("timePeriod")]
@@ -77,6 +79,7 @@ class NewGameWizard(QtWidgets.QWizard):
else: else:
start_date = self.theater_page.calendar.selectedDate().toPython() start_date = self.theater_page.calendar.selectedDate().toPython()
logging.info("New campaign start date: %s", start_date.strftime("%m/%d/%Y"))
settings = Settings( settings = Settings(
player_income_multiplier=self.field("player_income_multiplier") / 10, player_income_multiplier=self.field("player_income_multiplier") / 10,
enemy_income_multiplier=self.field("enemy_income_multiplier") / 10, enemy_income_multiplier=self.field("enemy_income_multiplier") / 10,
@@ -105,7 +108,9 @@ class NewGameWizard(QtWidgets.QWizard):
mod_settings = ModSettings( mod_settings = ModSettings(
a4_skyhawk=self.field("a4_skyhawk"), a4_skyhawk=self.field("a4_skyhawk"),
f22_raptor=self.field("f22_raptor"), f22_raptor=self.field("f22_raptor"),
f104_starfighter=self.field("f104_starfighter"),
hercules=self.field("hercules"), hercules=self.field("hercules"),
uh_60l=self.field("uh_60l"),
jas39_gripen=self.field("jas39_gripen"), jas39_gripen=self.field("jas39_gripen"),
su57_felon=self.field("su57_felon"), su57_felon=self.field("su57_felon"),
frenchpack=self.field("frenchpack"), frenchpack=self.field("frenchpack"),
@@ -114,7 +119,14 @@ class NewGameWizard(QtWidgets.QWizard):
blue_faction = self.faction_selection_page.selected_blue_faction blue_faction = self.faction_selection_page.selected_blue_faction
red_faction = self.faction_selection_page.selected_red_faction red_faction = self.faction_selection_page.selected_red_faction
logging.info("New campaign blue faction: %s", blue_faction.name)
logging.info("New campaign red faction: %s", red_faction.name)
theater = campaign.load_theater() theater = campaign.load_theater()
logging.info("New campaign theater: %s", theater.terrain.name)
generator = GameGenerator( generator = GameGenerator(
blue_faction, blue_faction,
red_faction, red_faction,
@@ -472,11 +484,12 @@ class DifficultyAndAutomationOptions(QtWidgets.QWizardPage):
economy_layout = QtWidgets.QVBoxLayout() economy_layout = QtWidgets.QVBoxLayout()
economy_group.setLayout(economy_layout) economy_group.setLayout(economy_layout)
# TODO: Put labels back. economy_layout.addWidget(QLabel("Player income multiplier"))
player_income = FloatSpinSlider(0, 5, 1, divisor=10) player_income = FloatSpinSlider(0, 5, 1, divisor=10)
self.registerField("player_income_multiplier", player_income.spinner) self.registerField("player_income_multiplier", player_income.spinner)
economy_layout.addLayout(player_income) economy_layout.addLayout(player_income)
economy_layout.addWidget(QLabel("Enemy income multiplier"))
enemy_income = FloatSpinSlider(0, 5, 1, divisor=10) enemy_income = FloatSpinSlider(0, 5, 1, divisor=10)
self.registerField("enemy_income_multiplier", enemy_income.spinner) self.registerField("enemy_income_multiplier", enemy_income.spinner)
economy_layout.addLayout(enemy_income) economy_layout.addLayout(enemy_income)
@@ -561,8 +574,12 @@ class GeneratorOptions(QtWidgets.QWizardPage):
self.registerField("a4_skyhawk", a4_skyhawk) self.registerField("a4_skyhawk", a4_skyhawk)
hercules = QtWidgets.QCheckBox() hercules = QtWidgets.QCheckBox()
self.registerField("hercules", hercules) self.registerField("hercules", hercules)
uh_60l = QtWidgets.QCheckBox()
self.registerField("uh_60l", uh_60l)
f22_raptor = QtWidgets.QCheckBox() f22_raptor = QtWidgets.QCheckBox()
self.registerField("f22_raptor", f22_raptor) self.registerField("f22_raptor", f22_raptor)
f104_starfighter = QtWidgets.QCheckBox()
self.registerField("f104_starfighter", f104_starfighter)
jas39_gripen = QtWidgets.QCheckBox() jas39_gripen = QtWidgets.QCheckBox()
self.registerField("jas39_gripen", jas39_gripen) self.registerField("jas39_gripen", jas39_gripen)
su57_felon = QtWidgets.QCheckBox() su57_felon = QtWidgets.QCheckBox()
@@ -578,20 +595,38 @@ class GeneratorOptions(QtWidgets.QWizardPage):
modHelpText.setAlignment(Qt.AlignCenter) modHelpText.setAlignment(Qt.AlignCenter)
modLayout = QtWidgets.QGridLayout() modLayout = QtWidgets.QGridLayout()
modLayout.addWidget(QtWidgets.QLabel("A-4E Skyhawk"), 1, 0) modLayout_row = 1
modLayout.addWidget(a4_skyhawk, 1, 1) modLayout.addWidget(QtWidgets.QLabel("A-4E Skyhawk"), modLayout_row, 0)
modLayout.addWidget(QtWidgets.QLabel("F-22A Raptor"), 2, 0) modLayout.addWidget(a4_skyhawk, modLayout_row, 1)
modLayout.addWidget(f22_raptor, 2, 1) modLayout_row += 1
modLayout.addWidget(QtWidgets.QLabel("C-130J-30 Super Hercules"), 3, 0) modLayout.addWidget(QtWidgets.QLabel("F-22A Raptor"), modLayout_row, 0)
modLayout.addWidget(hercules, 3, 1) modLayout.addWidget(f22_raptor, modLayout_row, 1)
modLayout.addWidget(QtWidgets.QLabel("JAS 39 Gripen"), 4, 0) modLayout_row += 1
modLayout.addWidget(jas39_gripen, 4, 1) modLayout.addWidget(QtWidgets.QLabel("F-104 Starfighter"), modLayout_row, 0)
modLayout.addWidget(QtWidgets.QLabel("Su-57 Felon"), 5, 0) modLayout.addWidget(f104_starfighter, modLayout_row, 1)
modLayout.addWidget(su57_felon, 5, 1) modLayout_row += 1
modLayout.addWidget(QtWidgets.QLabel("Frenchpack"), 6, 0) modLayout.addWidget(
modLayout.addWidget(frenchpack, 6, 1) QtWidgets.QLabel("C-130J-30 Super Hercules"), modLayout_row, 0
modLayout.addWidget(QtWidgets.QLabel("High Digit SAMs"), 7, 0) )
modLayout.addWidget(high_digit_sams, 7, 1) modLayout.addWidget(hercules, modLayout_row, 1)
modLayout_row += 1
modLayout.addWidget(QtWidgets.QLabel("UH-60L Black Hawk"), modLayout_row, 0)
modLayout.addWidget(uh_60l, modLayout_row, 1)
modLayout_row += 1
# Section break here for readability
modLayout.addWidget(QtWidgets.QWidget(), modLayout_row, 0)
modLayout_row += 1
modLayout.addWidget(QtWidgets.QLabel("JAS 39 Gripen"), modLayout_row, 0)
modLayout.addWidget(jas39_gripen, modLayout_row, 1)
modLayout_row += 1
modLayout.addWidget(QtWidgets.QLabel("Su-57 Felon"), modLayout_row, 0)
modLayout.addWidget(su57_felon, modLayout_row, 1)
modLayout_row += 1
modLayout.addWidget(QtWidgets.QLabel("Frenchpack"), modLayout_row, 0)
modLayout.addWidget(frenchpack, modLayout_row, 1)
modLayout_row += 1
modLayout.addWidget(QtWidgets.QLabel("High Digit SAMs"), modLayout_row, 0)
modLayout.addWidget(high_digit_sams, modLayout_row, 1)
modSettingsGroup.setLayout(modLayout) modSettingsGroup.setLayout(modLayout)
mlayout = QVBoxLayout() mlayout = QVBoxLayout()

View File

@@ -169,6 +169,8 @@ class AutoSettingsLayout(QGridLayout):
) -> None: ) -> None:
def on_changed(value: int) -> None: def on_changed(value: int) -> None:
self.settings.__dict__[name] = value self.settings.__dict__[name] = value
if description.causes_expensive_game_update:
self.write_full_settings()
spinner = QSpinBox() spinner = QSpinBox()
spinner.setMinimum(description.min) spinner.setMinimum(description.min)
@@ -354,7 +356,7 @@ class QSettingsWindow(QDialog):
self.cheat_options.show_base_capture_cheat self.cheat_options.show_base_capture_cheat
) )
self.game.compute_conflicts_position() self.game.compute_unculled_zones()
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)
def onSelectionChanged(self): def onSelectionChanged(self):

View File

@@ -24,7 +24,7 @@ Pillow==8.3.2
pluggy==0.13.1 pluggy==0.13.1
pre-commit==2.10.1 pre-commit==2.10.1
py==1.10.0 py==1.10.0
-e git://github.com/pydcs/dcs@c06f6bc1a842f890c88e7ccbcb14af5ae32c9cfd#egg=pydcs -e git+https://github.com/pydcs/dcs@5d1f581b260fdc6091744ab927a58cdee586e681#egg=pydcs
pyinstaller==4.3 pyinstaller==4.3
pyinstaller-hooks-contrib==2021.1 pyinstaller-hooks-contrib==2021.1
pyparsing==2.4.7 pyparsing==2.4.7

View File

@@ -1,11 +0,0 @@
{
"name": "Persian Gulf - Battle for the UAE",
"theater": "Persian Gulf",
"authors": "Mustang25",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Iran 2015",
"description": "<p>Following the Battle of Abu Dhabi, Iran's invasion of the UAE has been halted approximately 20 miles Northeast of Liwa Airbase by coalition forces.</p><p>After weeks of stalemate, coalition forces have consolidated their position and are ready to launch their counterattack to push Iranian forces off the peninsula.</p>",
"version": "8.0",
"miz": "Battle_for_the_UAE_v3.0.2.miz",
"performance": 2
}

View File

@@ -1,11 +0,0 @@
{
"name": "Caucasus - Full",
"theater": "Caucasus",
"authors": "Doc_of_Mur",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Russia 2010",
"description": "<p>This is a complete map of every airbase in the Caucasus Region, all bases are fully defended by Air, Land and/or Sea. The player starts by invading southern Georgia and works their way through Russia. The Strike and SAM targets are limited for performance reasons. If this Scenario is too taxing for your computer you may use the Multi-Part Scenarios. They are copied from this Campaign and are catered toward less powerful machines.</p>",
"version": "8.0",
"miz": "Caucasus_Multi_Full.miz",
"performance": 3
}

View File

@@ -0,0 +1,323 @@
---
name: Caucasus - Full
theater: Caucasus
authors: Doc_of_Mur
description: <p>This is a complete map of every airbase in the Caucasus Region, all bases are fully defended by Air, Land and/or Sea. The player starts by invading southern Georgia and works their way through Russia. The Strike and SAM targets are limited for performance reasons. If this Scenario is too taxing for your computer you may use the Multi-Part Scenarios. They are copied from this Campaign and are catered toward less powerful machines.</p>
recommended_player_faction: Bluefor Modern
recommended_enemy_faction: Russia 2010
recommended_start_date: 2008-08-01
miz: Caucasus_Multi_Full.miz
performance: 3
version: "9.1"
squadrons:
# Anapa-Vityazevo
12:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: Strike
secondary: air-to-ground
aircraft:
- Tu-160 Blackjack
# Krasnodar-Center
13:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-31 Foxhound
- MiG-25PD Foxbat-E
- primary: SEAD
secondary: any
- primary: DEAD
secondary: any
# Novorossiysk
14:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
# Krymsk
15:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: Strike
secondary: air-to-ground
# Maykop-Khanskaya
16:
- primary: CAS
secondary: air-to-ground
aircraft:
- Su-25 Frogfoot
- primary: BAI
secondary: air-to-ground
aircraft:
- Su-34 Fullback
- Su-24M Fencer-D
- primary: DEAD
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
# Gelendzhik
17:
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: DEAD
secondary: air-to-ground
- primary: Transport
# Sochi-Adler
18:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: Strike
secondary: air-to-ground
- primary: Anti-ship
secondary: air-to-ground
# Sukhumi-Babushara
20:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
# Gudauta
21:
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: DEAD
secondary: air-to-ground
# Batumi
22:
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
- A-10C Thunderbolt II (Suite 3)
- primary: BAI
secondary: air-to-ground
aircraft:
- F-15E Strike Eagle
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-15C Eagle
- primary: SEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: DEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: Transport
aircraft:
- UH-60A
- primary: AEW&C
aircraft:
- E-2D Advanced Hawkeye
- primary: Refueling
aircraft:
- C-130
# Senaki-Kholki
23:
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: DEAD
secondary: air-to-ground
- primary: Transport
# Kobuleti
24:
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
- A-10C Thunderbolt II (Suite 3)
- primary: BAI
secondary: air-to-ground
aircraft:
- F-15E Strike Eagle
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-15C Eagle
- primary: SEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: DEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: Transport
aircraft:
- UH-60A
- primary: AEW&C
aircraft:
- E-3A
- primary: Refueling
aircraft:
- KC-135 Stratotanker
# Kutaisi
25:
- primary: BARCAP
- primary: AEW&C
- primary: Refueling
- primary: Transport
- primary: Strike
secondary: air-to-ground
# Mineralnye Vody
26:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-31 Foxhound
- MiG-25PD Foxbat-E
- primary: SEAD
secondary: any
- primary: DEAD
secondary: any
# Nalchik
27:
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: DEAD
secondary: air-to-ground
- primary: Transport
# Mozdok
28:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: Strike
secondary: air-to-ground
aircraft:
- Tu-160 Blackjack
# Tbilisi-Lochini
29:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: Strike
secondary: air-to-ground
aircraft:
- Tu-160 Blackjack
# Beslan
32:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
# Blue CV
Naval-5:
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-14B Tomcat
- primary: BARCAP
secondary: any
aircraft:
- F-14B Tomcat
- primary: Strike
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: BAI
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: Refueling
aircraft:
- S-3B Tanker
# Blue LHA
Naval-4:
- primary: BAI
secondary: air-to-ground
aircraft:
- AV-8B Harrier II Night Attack
- primary: CAS
secondary: air-to-ground
aircraft:
- UH-1H Iroquois
# Red CV
Naval-1:
- primary: BARCAP
secondary: air-to-air
- primary: BARCAP
secondary: any
- primary: Strike
secondary: any
- primary: BAI
secondary: any
- primary: Refueling
# Red LHA
Naval-2:
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground

View File

@@ -1,11 +0,0 @@
{
"name": "Caucasus - Multi-Part Georgia",
"theater": "Caucasus",
"authors": "Doc_of_Mur",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Georgia 2008",
"description": "<p>This is Part 1 of the Caucasus Multi-Part Campaign. This is the invasion of Georgia starting from the southwest (Batumi) and ending in both Gudauta and Tiblisi. This is a straightforward campaign that is smaller and simpler than most. However, it acts great as either a stand alone campaign for beginners, or as a lead into the Caucasus Multi-Part Russia campaign.</p>",
"version": "8.0",
"miz": "Caucasus_Multi_Georgia.miz",
"performance": 1
}

View File

@@ -0,0 +1,185 @@
---
name: Caucasus - Muti-Part Georgia
theater: Caucasus
authors: Doc_of_Mur
description: <p><p>This is part 2 of the Caucasus Multi-part campaign. After completing Multi-Part Georgia, play this campaign to invade Russia and finish the theater. As this is now Russia the recommended enemy faction has changed. To simulate still owning Georgia the player income has been supplemented through an increased number of blue strike targets at the starting bases. This is a more difficult scenario with a higher concentration of Redfor SAMs and Strike targets than usual.</p>
recommended_player_faction: Bluefor Modern
recommended_enemy_faction: Georgia 2008
recommended_start_date: 1995-06-13
miz: Caucasus_Multi_Georgia.miz
performance: 2
version: "9.1"
squadrons:
# Kutaisi
22:
- primary: Refueling
aircraft:
- KC-135 Stratotanker MPRS
- primary: Refueling
aircraft:
- KC-135 Stratotanker
- primary: AEW&C
aircraft:
- E-3A
- primary: Transport
secondary: any
aircraft:
- C-130
- primary: Strike
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: BAI
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
- primary: BAI
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
- primary: BAI
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 3)
- primary: BARCAP
secondary: any
aircraft:
- JF-17 Thunder
- primary: BARCAP
aircraft:
- MiG-23MLD Flogger-K
- primary: BAI
secondary: air-to-ground
aircraft:
- Ka-50 Hokum
- primary: Strike
secondary: any
aircraft:
- AJS-37 Viggen
- primary: Strike
secondary: any
aircraft:
- Mirage 2000C
- primary: CAS
secondary: air-to-ground
aircraft:
- UH-1H Iroquois
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-8MTV2 Hip
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# Kobuleti
24:
- primary: BARCAP
aircraft:
- L-39ZA Albatros
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-8MTV2 Hip
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# Senaki-Kolkhi
23:
- primary: BARCAP
aircraft:
- L-39ZA Albatros
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-8MTV2 Hip
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- primary: CAS
secondary: air-to-ground
aircraft:
- UH-1H Iroquois
# Kataisi
25:
- primary: BARCAP
aircraft:
- L-39ZA Albatros
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-8MTV2 Hip
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# Sukhumi-Babushara
20:
- primary: BARCAP
aircraft:
- L-39ZA Albatros
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-8MTV2 Hip
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# Gudauta
21:
- primary: BARCAP
aircraft:
- L-39ZA Albatros
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-8MTV2 Hip
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- primary: Strike
secondary: air-to-ground
aircraft:
- Su-25 Frogfoot
- primary: OCA/Runway
secondary: air-to-ground
aircraft:
- Su-25 Frogfoot
Blue CV:
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-14B Tomcat
- primary: BARCAP
secondary: any
aircraft:
- F-14B Tomcat
- primary: Strike
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: BAI
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: Refueling
aircraft:
- S-3B Tanker
- primary: AEW&C
aircraft:
- E-2C Hawkeye
Blue LHA:
- primary: BAI
secondary: air-to-ground
aircraft:
- AV-8B Harrier II Night Attack

View File

@@ -1,11 +0,0 @@
{
"name": "Caucasus - Multi-Part Russia",
"theater": "Caucasus",
"authors": "Doc_of_Mur",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Russia 2010",
"description": "<p>This is part 2 of the Caucasus Multi-part campaign. After completing Multi-Part Georgia, play this campaign to invade Russia and finish the theater. As this is now Russia the recommended enemy faction has changed. To simulate still owning Georgia the player income has been supplemented through an increased number of blue strike targets at the starting bases. This is a more difficult scenario with a higher concentration of Redfor SAMs and Strike targets than usual.</p>",
"version": "8.0",
"miz": "Caucasus_Multi_Russia.miz",
"performance": 2
}

View File

@@ -0,0 +1,269 @@
---
name: Caucasus - Multi-Part Russia
theater: Caucasus
authors: Doc_of_Mur
description: <p>This is part 2 of the Caucasus Multi-part campaign. After completing Multi-Part Georgia, play this campaign to invade Russia and finish the theater. As this is now Russia the recommended enemy faction has changed. To simulate still owning Georgia the player income has been supplemented through an increased number of blue strike targets at the starting bases. This is a more difficult scenario with a higher concentration of Redfor SAMs and Strike targets than usual.</p>
recommended_player_faction: Bluefor Modern
recommended_enemy_faction: Russia 2010
recommended_start_date: 2008-08-01
miz: Caucasus_Multi_Russia.miz
performance: 2
version: "9.1"
squadrons:
# Anapa-Vityazevo
12:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: Strike
secondary: air-to-ground
aircraft:
- Tu-160 Blackjack
# Krasnodar-Center
13:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-31 Foxhound
- MiG-25PD Foxbat-E
- primary: SEAD
secondary: any
- primary: DEAD
secondary: any
# Novorossiysk
14:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
# Krymsk
15:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: Strike
secondary: air-to-ground
# Maykop-Khanskaya
16:
- primary: CAS
secondary: air-to-ground
aircraft:
- Su-25 Frogfoot
- primary: BAI
secondary: air-to-ground
aircraft:
- Su-34 Fullback
- Su-24M Fencer-D
- primary: DEAD
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
# Gelendzhik
17:
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: DEAD
secondary: air-to-ground
- primary: Transport
# Sochi-Adler
18:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: Strike
secondary: air-to-ground
- primary: Anti-ship
secondary: air-to-ground
# Gudauta
21:
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
- A-10C Thunderbolt II (Suite 3)
- primary: BAI
secondary: air-to-ground
aircraft:
- F-15E Strike Eagle
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-15C Eagle
- primary: SEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: DEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: Transport
aircraft:
- UH-60A
- primary: AEW&C
aircraft:
- E-2D Advanced Hawkeye
- primary: Refueling
aircraft:
- C-130
# Mineralnye Vody
26:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-31 Foxhound
- MiG-25PD Foxbat-E
- primary: SEAD
secondary: any
- primary: DEAD
secondary: any
# Nalchik
27:
- primary: CAS
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: DEAD
secondary: air-to-ground
- primary: Transport
# Mozdok
28:
- primary: BARCAP
aircraft:
- Su-30 Flanker-C
- Su-27 Flanker-B
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: Strike
secondary: air-to-ground
aircraft:
- Tu-160 Blackjack
# Tbilisi-Lochini
29:
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
- A-10C Thunderbolt II (Suite 3)
- primary: BAI
secondary: air-to-ground
aircraft:
- F-15E Strike Eagle
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-15C Eagle
- primary: SEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: DEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: Transport
aircraft:
- UH-60A
- primary: AEW&C
aircraft:
- E-3A
- primary: Refueling
aircraft:
- KC-135 Stratotanker
# Beslan
32:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
- primary: BAI
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
# Blue CV
Naval-5:
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-14B Tomcat
- primary: BARCAP
secondary: any
aircraft:
- F-14B Tomcat
- primary: Strike
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: BAI
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: Refueling
aircraft:
- S-3B Tanker
# Blue LHA
Naval-4:
- primary: BAI
secondary: air-to-ground
aircraft:
- AV-8B Harrier II Night Attack
- primary: CAS
secondary: air-to-ground
aircraft:
- UH-1H Iroquois
# Red CV
Naval-1:
- primary: BARCAP
secondary: air-to-air
- primary: BARCAP
secondary: any
- primary: Strike
secondary: any
- primary: BAI
secondary: any
- primary: Refueling
# Red LHA
Naval-2:
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground

View File

@@ -1,11 +0,0 @@
{
"name": "Syria - First Lebanon War",
"theater": "Syria",
"authors": "Mustang25",
"recommended_player_faction": "Israel 1982",
"recommended_enemy_faction": "Syria 1982",
"description": "<p> 1100HRS, 06 June 1982: H-hour for Operation Peace for Galilee. </p><p>Objective: Push North towards Beirut and into the Bekaa Valley, eliminating or displacing any PLO and Syrian resistance. Airbases and their surrounding infrastructure in Syria are not the main objective but are still viable strategic targets.</p> <p>Background: Years of PLO encroachment into the UN neutral zone and their resulting terror attacks against Israelis have pushed tension along the border to a breaking point. On June 3, the attempted assassination of Israeli Ambassador, Shlomo Argov by gunmen with ties to the PLO have finally pushed the Israelis to action.</p><p>Recommended Starting Budget:</p><p>$1500m for recommended factions, $$2000m for modern scenarios</p><p>Income Multiplier:</p><p>Blue: 1.0x</p><p>Red: 0.7x-1.0x</p>",
"miz": "First_Lebanon_War_v3.0.2.miz",
"version": "8.0",
"performance": 2
}

View File

@@ -1,11 +0,0 @@
{
"name": "Syria - Operation Mole Cricket 2010",
"theater": "Syria",
"authors": "Mustang25",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Syria 2011",
"description": "<p>In a scenario reminescent of the First Lebanon War, hostile Syrian-backed forces have flooded into the Bekaa Valley.</p><p>The objective of this operation is twofold: drive the enemy out of the Bekaa Valley and push past the Golan Heights into Syrian territory to capture Tiyas Airbase.</p>",
"version": "8.0",
"miz": "Operation_Mole_Cricket_2010_v3.0.2.miz",
"performance": 2
}

View File

@@ -1,11 +0,0 @@
{
"name": "Persian Gulf - Road To Dubai",
"theater": "Persian Gulf",
"authors": "Doc_of_Mur",
"description": "<p>Small beginner friendly map</p><p><strong>Note:</strong> This scenario is based around Iran invading the UAE and you are trying to take it back. It is small and beginner friendly.</p>",
"version": "8.0",
"recommended_player_faction": "USA 2005",
"recommended_enemy_faction": "Iran 2015",
"miz": "Road_to_Dubai.miz",
"performance": 1
}

Binary file not shown.

View File

@@ -0,0 +1,162 @@
---
name: Syria - Task Force Thunder
theater: Syria
authors: Sith1144
description: <p>A campaign based on the campaign from Combat Mission Shock Force. US forces attempt to bisect the country by advancing towards Homs from Iraq in the east and an amphibious landing near Tartus in the west. Recommended starting budge 3000-4000M for REDFOR, 2000-5000M for BLUFOR. Cannot be inverted. NOTE; the marine landing in the west does NOT have a workshop and cannot produce new units, nor does the first enemy airfield in the area. How many resources you pour into it is up to you.</p>
recommended_player_faction: USA 2005
recommended_enemy_faction: Syria 2011
recommended_start_date: 2009-06-19
miz: Task Force Thunder.miz
performance: 3
version: "9.1"
squadrons:
# Airspawn from Iraq
Al Asad Airbase:
- primary: AEW&C
aircraft:
- E-3A
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-15C Eagle
- primary: Strike
secondary: air-to-ground
aircraft:
- B-1B Lancer
- B-52H Stratofortress
- F-117A Nighthawk
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 7)
- primary: SEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: DEAD
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: BAI
secondary: air-to-ground
aircraft: F-15E Strike Eagle
- primary: Refueling
aircraft:
- KC-135 Stratotanker MPRS
#H4 airbase
12:
- primary: BARCAP
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 3)
#Northern FOB
Staging Point Alpha:
- primary: CAS
aircraft:
- AH-64D Apache Longbow
#Carrier is called Naval-1
Naval-1:
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-14B Tomcat
- primary: BARCAP
secondary: air-to-air
aircraft:
- F-14B Tomcat
- primary: Strike
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: SEAD
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: Refueling
aircraft:
- S-3B Tanker
- primary: AEW&C
aircraft:
- E-2C Hawkeye
#LHA is called Naval-2
Naval-2:
- primary: CAS
secondary: air-to-ground
aircraft:
- AV-8B Harrier II Night Attack
- primary: CAS
aircraft:
- AH-1W SuperCobra
# Al Qusayr - opposing the beach landing with old fighters (mig-23), CAS (su-17), helicopters (mi-24)
3:
- primary: BARCAP
secondary: any
aircraft:
- MiG-23MLD Flogger-K
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24V Hind-E
- primary: CAS
secondary: air-to-ground
aircraft:
- Su-17M4 Fitter-K
# Tiyas - secondary base for good fighters (mig-25) and trainers (L-39)
39:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-25PD Foxbat-E
- primary: Strike
secondary: air-to-ground
aircraft:
- L-39ZA Albatros
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-29S Fulcrum-C
# Palmyra - forward base, helicopters (mi-24, mi-8), old fighters (mig-21) and cas (su-17)
28:
- primary: BARCAP
secondary: any
aircraft:
- MiG-21bis Fishbed-N
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Su-17M4 Fitter-K
- primary: CAS
secondary: air-to-ground
aircraft:
- Su-17M4 Fitter-K
- primary: Transport
secondary: air-to-ground
aircraft:
- Mi-8MTV2 Hip
# Shayrat - main base for support planes (A-50, transport, tanker), strike craft (su-24) and modern fighters (mig-29)
36:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-29S Fulcrum-C
- primary: Strike
secondary: air-to-ground
aircraft:
- Su-24M Fencer-D
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-76MD
# Defensive Line FARP for gazelles
Defensive Line:
- primary: CAS
aircraft:
- SA 342M Gazelle

Binary file not shown.

View File

@@ -0,0 +1,86 @@
---
name: Caucasus - The Tblisi Gap
theater: Caucasus
authors: Sith1144
description: <p>A 1980 Cold-war-gone-hot campaign set in the hilly terrain between Tblisi and Kutaisi. Made for the Hind and Hip with a narrow front with lots of targets. Can also be played as Blufor Cold War vs GDR 1985 or as Blufor Modern vs a modern opfor faction. Recommended way to play is focusing on helicopters, with aggressive culling (50-60km is workable, maybe even lower)</p>
recommended_player_faction: Russia 1975 (Mi-24P)
recommended_enemy_faction: Germany 1990
recommended_start_date: 1980-09-21
miz: TblisiGap.miz
performance: 2
version: "9.1"
squadrons:
#Vaziani, blufor jet base
31:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-23MLD Flogger-K
- F-14A Tomcat (Block 135-GR Late)
- F/A-18C Hornet (Lot 20)
- primary: TARCAP
secondary: any
aircraft:
- MiG-21bis Fishbed-N
- F-16CM Fighting Falcon (Block 50)
- F-5E Tiger II
- primary: Strike
secondary: air-to-ground
aircraft:
- Su-17M4 Fitter-K
- AJS-37 Viggen
- primary: BAI
secondary: air-to-ground
aircraft:
- Su-25 Frogfoot
- A-10C Thunderbolt II (Suite 3)
- A-4E Skyhawk
- A-10A Thunderbolt II
- primary: Refueling
- primary: AEW&C
#Tbilisi-Lochini, blue helicopter base
29:
- primary: CAS
aircraft:
- Mi-24P Hind-F
- AV-8B Harrier II Night Attack
- Ka-50 Hokum
- AH-64D Apache Longbow
- primary: CAS
aircraft:
- Ka-50 Hokum
- SA 342M Gazelle
- Mi-24V Hind-E
- primary: Transport
aircraft:
- Mi-8MTV2 Hip
- UH-1H Iroquois
#frontline FARP, called "FARP Mayhem"
FARP Mayhem:
- primary: CAS
aircraft:
- AH-1W SuperCobra
- AH-64D Apache Longbow
- SA 342M Gazelle
- Ka-50 Hokum
- Mi-24V Hind-E
#Kutaisi, primary opfor base
25:
- primary: BAI
aircraft:
- Tornado IDS
- Su-17M4 Fitter-K
- Su-24M Fencer-D
- primary: BARCAP
aircraft:
- F-16CM Fighting Falcon (Block 50)
- F-4F Phantom II
- F-5E Tiger II
- MiG-23MLD Flogger-K
- FC-1 Fierce Dragon
- JF-17 Thunder
- Su-27 Flanker-B
#Senaki, secondary opfor base
23:
- primary: Transport
- primary: AEW&C

Binary file not shown.

View File

@@ -0,0 +1,65 @@
---
name: Persian Gulf - Valley of Rotary
theater: Persian Gulf
authors: Sith1144
description: <p>Helicopter counterinsurgency campaign set in Southern Iran</p>
recommended_player_faction: Iran 2015
recommended_enemy_faction: Insurgents (Hard)
recommended_start_date: 2022-06-13
miz: TheValleyOfRotary.miz
performance: 2
version: "9.1"
squadrons:
# Shiraz International, BLUFOR start
19:
- primary: CAS
aircraft:
- Mi-24P Hind-F
- AH-64D Apache Longbow
- AH-1J SeaCobra
- AH-1W SuperCobra
- primary: CAS
aircraft:
- Mi-24V Hind-E
- UH-1H Iroquois
- A-4E Skyhawk
- AV-8B Harrier II Night Attack
- L-39ZA Albatros
- C-101CC Aviojet
- primary: BAI
aircraft:
- Ka-50 Hokum
- SA 342M Gazelle
- AH-64D Apache Longbow
- Mi-28N Havoc
- primary: Transport
aircraft:
- Mi-8MTV2 Hip
- UH-1H Iroquois
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10C Thunderbolt II (Suite 3)
- Su-25 Frogfoot
- Su-25T Frogfoot
- A-10A Thunderbolt II
# Jowkan FARP, REDFOR frontline.
Jowkan:
- primary: CAS
# Lar, REDFOR airbase + inverted start
11:
- primary: CAS
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
- UH-1H Iroquois
- A-4E Skyhawk
- AV-8B Harrier II Night Attack
- L-39ZA Albatros
- C-101CC Aviojet
# Mansurabad, inverted start frontline north
Mansurabad:
- primary: CAS
# Bagh, inverted start frontline south
Bagh:
- primary: Transport

View File

@@ -1,11 +0,0 @@
{
"name": "Caucasus - Around The Mountain",
"theater": "Caucasus",
"authors": "Dillie",
"recommended_player_faction": "Russia 2010",
"recommended_enemy_faction": "USA 1990",
"description": "<p>Scenario from Russia to Georgia in two Frontlines.</p>",
"version": "8.0",
"miz": "around_the_mountain.miz",
"performance": 2
}

View File

@@ -1,11 +0,0 @@
{
"name": "Normandy - From Caen to Evreux",
"theater": "Normandy",
"authors": "Khopa",
"recommended_player_faction": "Allies 1944",
"recommended_enemy_faction": "Germany 1944",
"description": "<p>This is a light scenario on the Normandy map.</p><p>August 1944, allied forces are pushing from Caen/Carpiquet to the cities of Lisieux and Evreux.<p>Lisieux is an important logistic hub for the Werhmacht, and Evreux airbase is hosting most of the Luftwaffe forces in the region.</p>",
"miz": "caen_to_evreux.miz",
"performance": 1,
"version": "8.0"
}

View File

@@ -0,0 +1,96 @@
---
name: Normandy - From Caen to Evreux
theater: Normandy
authors: Khopa
description: <p>This is a light scenario on the Normandy map.</p><p>August 1944, allied forces are pushing from Caen/Carpiquet to the cities of Lisieux and Evreux.<p>Lisieux is an important logistic hub for the Werhmacht, and Evreux airbase is hosting most of the Luftwaffe forces in the region.</p>
recommended_player_faction: Allies 1944
recommended_enemy_faction: Germany 1944
recommended_start_date: 1944-07-04
miz: caen_to_evreux.miz
performance: 1
version: "9.0"
squadrons:
# Evreux
26:
- primary: BARCAP
aircraft:
- Bf 109 K-4 Kurf\u00fcrst
- primary: BARCAP
aircraft:
- Fw 190 A-8 Anton
- primary: BARCAP
aircraft:
- Fw 190 D-9 Dora
- primary: Strike
secondary: air-to-ground
aircraft:
- Ju 88 A-4
- primary: AEW&C
- primary: Refueling
- primary: Transport
# Conches
40:
- primary: BARCAP
aircraft:
- Bf 109 K-4 Kurf\u00fcrst
- primary: BARCAP
aircraft:
- Fw 190 A-8 Anton
- primary: BARCAP
aircraft:
- Fw 190 D-9 Dora
- primary: SEAD
secondary: any
- primary: DEAD
secondary: any
# Carpiquet
19:
- primary: BARCAP
aircraft:
- Thunderbolt Mk.II (Late)
- P-47D-40 Thunderbolt
- primary: BARCAP
aircraft:
- Mustang Mk.IV (Late)
- P-51D-30-NA Mustang
- primary: BARCAP
aircraft:
- Spitfire LF Mk IX
- primary: BARCAP
aircraft:
- Spitfire LF Mk IX (Clipped Wings)
- primary: Strike
secondary: air-to-ground
aircraft:
- MosquitoFBMkVI
- primary: SEAD
secondary: any
- primary: DEAD
secondary: any
# Ford_AF
31:
- primary: BARCAP
aircraft:
- Thunderbolt Mk.II (Mid)
- P-47D-30 Thunderbolt (Late)
- primary: BARCAP
aircraft:
- Thunderbolt Mk.II (Early)
- P-47D-30 Thunderbolt (Early)
- primary: BARCAP
aircraft:
- Mustang Mk.IV (Early)
- P-51D-25-NA Mustang
- primary: Strike
secondary: air-to-ground
aircraft:
- Boston Mk.III
- A-20G Havoc
- primary: Strike
secondary: air-to-ground
aircraft:
- Fortress Mk.III
- B-17G Flying Fortress
- primary: AEW&C
- primary: Refueling
- primary: Transport

View File

@@ -4,46 +4,54 @@ theater: Nevada
authors: Starfire authors: Starfire
recommended_player_faction: USA 2005 recommended_player_faction: USA 2005
recommended_enemy_faction: Redfor (China) 2010 recommended_enemy_faction: Redfor (China) 2010
description: <p>This is an asymmetrical Red Flag Exercise scenario for the NTTR comprising 4 control points. You start off in control of the two Tonopah airports, and will push south to capture Groom Lake and Nellis AFBs. Taking down Nellis AFB's IADS and striking their resource sites ASAP once Groom Lake has been captured is recommended to offset their substantial resource advantage.</p> description: <p>Welcome to Vegas Nerve, an asymmetrical Red Flag Exercise scenario. You start off in control of the two Tonopah airports, and will push south from there. For the duration of this exercise, Creech AFB has been cleared of all fixed wing aircraft and will function as a FARP. OPFOR has a substantial resource advantage and reducing that advantage will be key to your success. Good luck, Commander.</p>
miz: exercise_vegas_nerve.miz miz: exercise_vegas_nerve.miz
performance: 1 performance: 1
version: "9.0" recommended_start_date: 2011-04-24
version: "9.1"
squadrons: squadrons:
# Tonopah Airport # Tonopah Airport
17: 17:
- primary: BARCAP - primary: TARCAP
secondary: air-to-air secondary: air-to-air
aircraft: aircraft:
- F-15C Eagle - F-15C Eagle
- primary: BARCAP - primary: BARCAP
secondary: air-to-air secondary: any
aircraft: aircraft:
- F-14B Tomcat - F-14B Tomcat
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-3A - E-3A
- primary: Refueling
aircraft:
- KC-135 Stratotanker
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker MPRS - KC-135 Stratotanker MPRS
- primary: Transport - primary: Transport
aircraft: aircraft:
- CH-47D - C-130
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-15E Strike Eagle - F-15E Strike Eagle
# Tonopah Test Range # Tonopah Test Range
18: 18:
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- A-10C Thunderbolt II (Suite 7) - A-10C Thunderbolt II (Suite 7)
- primary: CAS
secondary: air-to-ground
aircraft:
- AH-64D Apache Longbow
- primary: SEAD - primary: SEAD
secondary: any secondary: air-to-ground
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - F/A-18C Hornet (Lot 20)
- primary: DEAD - primary: DEAD
secondary: any secondary: air-to-ground
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
- primary: BAI - primary: BAI
@@ -56,28 +64,18 @@ squadrons:
secondary: air-to-air secondary: air-to-air
aircraft: aircraft:
- J-11A Flanker-L - J-11A Flanker-L
- primary: CAS
secondary: air-to-ground
aircraft:
- Su-25T Frogfoot
- primary: DEAD
secondary: any
aircraft:
- Su-30MKK Flanker-G
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Su-34 Fullback - Su-34 Fullback
# Creech
Creech FARP:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# Nellis AFB # Nellis AFB
4: 4:
- primary: TARCAP
secondary: any
aircraft:
- J-15 Flanker X-2
- primary: BARCAP
secondary: any
aircraft:
- MiG-29S Fulcrum-C
- primary: SEAD - primary: SEAD
secondary: any secondary: any
aircraft: aircraft:
@@ -85,11 +83,9 @@ squadrons:
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Su-24M Fencer-D - H-6J Badger
- primary: Strike # Boulder City Airport
secondary: air-to-ground 6:
aircraft:
- Tu-22M3 Backfire-C
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- KJ-2000 - KJ-2000
@@ -98,4 +94,4 @@ squadrons:
- IL-78M - IL-78M
- primary: Transport - primary: Transport
aircraft: aircraft:
- Mi-8MTV2 Hip - IL-76MD

View File

@@ -1,11 +0,0 @@
{
"name": "Syria - Humble Helper",
"theater": "Syria",
"authors": "Headiii",
"recommended_player_faction": "Israel 2012'ish",
"recommended_enemy_faction": "Syria 2012'ish",
"description": "<p>In this scenario, you start in Israel in an high intensity conflict with Syria, backed by a Russian Expeditiary Force. Your goal is to take the heavily fortified city of Damascus, as fast as you can. The longer you wait, the more resources the enemy can pump into the defense of the city or even might try to take chunks of Israel. ATTENTION: CAMPAIGN INVERTING IS NOT YET IMPLEMENTED!!! Feedback: @Headiii in the DCSLiberation Discord</p>",
"miz": "humble_helper.miz",
"performance": 1,
"version": "8.0"
}

View File

@@ -4,7 +4,7 @@ theater: MarianaIslands
authors: Ghosti authors: Ghosti
description: <p>The objective of this campaign is the capture of Guam. Blue side, having landed their forces on the beaches of Agat, controls Antonio B. Won Pat airfield and are pushing towards the enemy stronghold of Mount Barrigada.</p> description: <p>The objective of this campaign is the capture of Guam. Blue side, having landed their forces on the beaches of Agat, controls Antonio B. Won Pat airfield and are pushing towards the enemy stronghold of Mount Barrigada.</p>
miz: marianas_guam_barrigada.miz miz: marianas_guam_barrigada.miz
performance: 3, performance: 2
version: "9.0" version: "9.0"
squadrons: squadrons:
Blue CV: Blue CV:
@@ -59,10 +59,15 @@ squadrons:
secondary: air-to-air secondary: air-to-air
- primary: Strike - primary: Strike
secondary: any secondary: any
- primary: Anti-ship
secondary: any
- primary: BAI - primary: BAI
secondary: any secondary: any
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
- primary: AEW&C
- primary: Refueling
- primary: Transport
# Antonio B. Won Pat Intl # Antonio B. Won Pat Intl
4: 4:
- primary: BARCAP - primary: BARCAP
@@ -71,6 +76,8 @@ squadrons:
secondary: air-to-air secondary: air-to-air
- primary: Strike - primary: Strike
secondary: any secondary: any
- primary: Anti-ship
secondary: any
- primary: BAI - primary: BAI
secondary: any secondary: any
- primary: CAS - primary: CAS

View File

@@ -4,7 +4,7 @@ theater: MarianaIslands
authors: Ghosti authors: Ghosti
description: <p>The objective of this campaign is the capture of Guam. Blue side, having landed their forces on the beaches of Agat, are pushing inland. <strong>Note:</strong> This campaign requires a carrier-capable (or LHA-capable) BLUFOR faction to be able to field aircraft.</p> description: <p>The objective of this campaign is the capture of Guam. Blue side, having landed their forces on the beaches of Agat, are pushing inland. <strong>Note:</strong> This campaign requires a carrier-capable (or LHA-capable) BLUFOR faction to be able to field aircraft.</p>
miz: marianas_guam_landing_at_agat.miz miz: marianas_guam_landing_at_agat.miz
performance: 3, performance: 2
version: "9.0" version: "9.0"
squadrons: squadrons:
Blue CV: Blue CV:
@@ -59,10 +59,15 @@ squadrons:
secondary: air-to-air secondary: air-to-air
- primary: Strike - primary: Strike
secondary: any secondary: any
- primary: Anti-ship
secondary: any
- primary: BAI - primary: BAI
secondary: any secondary: any
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
- primary: AEW&C
- primary: Refueling
- primary: Transport
# Antonio B. Won Pat Intl # Antonio B. Won Pat Intl
4: 4:
- primary: BARCAP - primary: BARCAP
@@ -71,6 +76,8 @@ squadrons:
secondary: air-to-air secondary: air-to-air
- primary: Strike - primary: Strike
secondary: any secondary: any
- primary: Anti-ship
secondary: any
- primary: BAI - primary: BAI
secondary: any secondary: any
- primary: CAS - primary: CAS
@@ -80,18 +87,6 @@ squadrons:
- primary: AEW&C - primary: AEW&C
- primary: Refueling - primary: Refueling
- primary: Transport - primary: Transport
# Andersen AFB
6:
- primary: BARCAP
secondary: air-to-air
- primary: BARCAP
secondary: air-to-air
- primary: Strike
secondary: any
- primary: BAI
secondary: any
- primary: CAS
secondary: air-to-ground
Andersen AFB Northwest Field: Andersen AFB Northwest Field:
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground

View File

@@ -1,11 +0,0 @@
{
"name": "Nevada - Limited Air",
"theater": "Nevada",
"authors": "Doc_of_Mur",
"recommended_player_faction": "USA 2005",
"recommended_enemy_faction": "Russia 1975",
"description": "<p>This campaign is designed to be beginner friendly in that the number of aircraft slot have been limited. Other than the starting point and the 'boss' base the max slots in each of the airbases have a mere 3-5 slots.</p><p>This should prevent the airpower rush escperienced in most of the other larger campaign.</p>",
"version": "8.0",
"miz": "nevada_limited_air.miz",
"performance": 1
}

View File

@@ -15,6 +15,9 @@ squadrons:
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker MPRS - KC-135 Stratotanker MPRS
- primary: Refueling
aircraft:
- KC-135 Stratotanker
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-3A - E-3A
@@ -134,6 +137,10 @@ squadrons:
- primary: BARCAP - primary: BARCAP
aircraft: aircraft:
- MiG-29A Fulcrum-A - MiG-29A Fulcrum-A
- primary: OCA/Runway
secondary: air-to-ground
aircraft:
- Su-24M Fencer-D
# Mineralnye Vody # Mineralnye Vody
26: 26:
- primary: BARCAP - primary: BARCAP

View File

@@ -5,7 +5,7 @@ authors: Fuzzle
recommended_player_faction: Israel-USN 2005 (Allied Sword) recommended_player_faction: Israel-USN 2005 (Allied Sword)
recommended_enemy_faction: Syria-Lebanon 2005 (Allied Sword) recommended_enemy_faction: Syria-Lebanon 2005 (Allied Sword)
description: <p>In this fictional scenario, a US/Israeli coalition must push north from the Israeli border, through Syria and Lebanon to Aleppo.</p><p><strong>Backstory:</strong> A Syrian-Lebanese joint force (with Russian materiel support) has attacked Israel, attmepting to cross the northern border. With the arrival of a US carrier group, Israel prepares its counterattack. The US Navy will handle the Beirut region's coastal arena, while the IAF will push through Damascus and the inland mountain ranges.</p>, description: <p>In this fictional scenario, a US/Israeli coalition must push north from the Israeli border, through Syria and Lebanon to Aleppo.</p><p><strong>Backstory:</strong> A Syrian-Lebanese joint force (with Russian materiel support) has attacked Israel, attmepting to cross the northern border. With the arrival of a US carrier group, Israel prepares its counterattack. The US Navy will handle the Beirut region's coastal arena, while the IAF will push through Damascus and the inland mountain ranges.</p>,
version: 9.1 version: "9.1"
miz: operation_allied_sword.miz miz: operation_allied_sword.miz
performance: 2 performance: 2
recommended_start_date: 2004-07-17 recommended_start_date: 2004-07-17
@@ -135,7 +135,7 @@ squadrons:
aircraft: aircraft:
- SA 342M Gazelle - SA 342M Gazelle
# Palmyra # Palmyra
6: 28:
- primary: BARCAP - primary: BARCAP
secondary: air-to-air secondary: air-to-air
aircraft: aircraft:
@@ -160,7 +160,7 @@ squadrons:
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- IL-78M - IL-78M
- primary: Transport - primary: Transport
aircraft: aircraft:
- IL-76MD - IL-76MD
- primary: AEW&C - primary: AEW&C

View File

@@ -3,7 +3,7 @@ name: Syria - Operation Blackball
theater: Syria theater: Syria
authors: Fuzzle authors: Fuzzle
description: <p>A lightweight fictional showcase of Cyprus for the Syria terrain. A US Navy force must deploy from a FOB and carrier group to push from the north-east down through the island. <strong>Note that the ground units purchased on turn zero must sustain you until you've taken the first hostile FOB. The starting point does not have a factory to simulate a Marine Expeditionary Force deploying from the carrier group.</strong></p><p><strong>Backstory:</strong> The world is at war. With the help of her eastern allies Russia has taken the Suez Canal and deployed a large naval force to the Mediterranean trapping a US carrier group near the Turkish-Syrian border. Now they must break out by taking Cyprus back.</p> description: <p>A lightweight fictional showcase of Cyprus for the Syria terrain. A US Navy force must deploy from a FOB and carrier group to push from the north-east down through the island. <strong>Note that the ground units purchased on turn zero must sustain you until you've taken the first hostile FOB. The starting point does not have a factory to simulate a Marine Expeditionary Force deploying from the carrier group.</strong></p><p><strong>Backstory:</strong> The world is at war. With the help of her eastern allies Russia has taken the Suez Canal and deployed a large naval force to the Mediterranean trapping a US carrier group near the Turkish-Syrian border. Now they must break out by taking Cyprus back.</p>
version: 9.1 version: "9.1"
recommended_player_faction: US Navy 2005 recommended_player_faction: US Navy 2005
recommended_enemy_faction: Russia 2010 recommended_enemy_faction: Russia 2010
miz: operation_blackball.miz miz: operation_blackball.miz

View File

@@ -3,11 +3,12 @@ name: Syria - Operation Peace Spring
theater: Syria theater: Syria
authors: Starfire authors: Starfire
recommended_player_faction: Bluefor Modern recommended_player_faction: Bluefor Modern
recommended_enemy_faction: Turkey 2005 recommended_enemy_faction: Iraq 1991
description: <p>This is a semi-fictional what-if scenario for Operation Peace Spring, during which Turkish forces that crossed into Syria on an offensive against Kurdish militias were emboldened by early successes to continue pushing further southward. Attempts to broker a ceasefire have failed. Members of Operation Inherent Resolve have gathered at Ramat David Airbase in Israel to launch a counter-offensive.</p><p><strong>Note:</strong> While Turkey 2005 is the default faction for historical reasons, the OPFOR squadron complement in this campaign scenario has also been specifically customised to work just as well with the Iraq 1991 faction in order to provide an opponent with a wide variety of units.</p> description: <p>This is a semi-fictional what-if scenario for Operation Peace Spring, during which Turkish forces that crossed into Syria on an offensive against Kurdish militias were emboldened by early successes to continue pushing further southward. Attempts to broker a ceasefire have failed. Members of Operation Inherent Resolve have gathered at Ramat David Airbase in Israel to launch a counter-offensive.</p><p><strong>Note:</strong> The default faction is set as Iraq 1991 in order to provide an opponent with a wider variety of units. While Turkey 2005 would be the historical faction, you would be facing nothing in the air except F-4 Phantoms.</p>
miz: operation_peace_spring.miz miz: operation_peace_spring.miz
performance: 1 performance: 1
version: "9.0" recommended_start_date: 2019-12-23
version: "9.1"
squadrons: squadrons:
# Ramat David # Ramat David
30: 30:
@@ -64,7 +65,10 @@ squadrons:
- B-52H Stratofortress - B-52H Stratofortress
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-2C Hawkeye - E-3A
- primary: Refueling
aircraft:
- KC-135 Stratotanker
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker MPRS - KC-135 Stratotanker MPRS
@@ -77,60 +81,60 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- F-4E Phantom II - F-4E Phantom II
- MiG-21bis Fishbed-N - MiG-21bis Fishbed-N
- primary: CAS
secondary: air-to-ground
aircraft:
- AH-1W SuperCobra
- Su-25 Frogfoot
# Tiyas # Tiyas
39: 39:
- primary: TARCAP
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- MiG-23ML Flogger-G
- primary: SEAD - primary: SEAD
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
- Su-24M Fencer-D - Su-24M Fencer-D
- primary: CAS
secondary: air-to-ground
aircraft:
- AH-1W SuperCobra
- Su-25T Frogfoot
- Su-25 Frogfoot
- primary: Transport - primary: Transport
secondary: any
aircraft: aircraft:
- UH-60A - UH-60A
- Mi-8MTV2 Hip - Mi-8MTV2 Hip
# Abu Al Duhur # Abu Al Duhur
# 1: # 1:
# Gaziantep # Gaziantep
# 11: 11:
- primary: CAS
secondary: air-to-ground
aircraft:
- OH-58D Kiowa Warrior
- Mi-24P Hind-F
# Incirlik # Incirlik
16: 16:
- primary: BARCAP - primary: TARCAP
secondary: any secondary: any
aircraft: aircraft:
- F-16CM Fighting Falcon (Block 50) - F-16CM Fighting Falcon (Block 50)
- MiG-29A Fulcrum-A - MiG-29A Fulcrum-A
- primary: Strike - primary: Strike
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- F-4E Phantom II - F-4E Phantom II
- Tu-22M3 Backfire-C - Tu-22M3 Backfire-C
- primary: CAS - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- AH-1W SuperCobra - F-4E Phantom II
- Su-22M4 Fitter-K - H-6J Badger
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- E-3A - E-3A
- A-50 - A-50
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker MPRS
- KC-135 Stratotanker - KC-135 Stratotanker
- IL-78M - IL-78M
- primary: Transport - primary: Transport
aircraft: aircraft:
- C-130 - C-130
- IL-76MD - IL-76MD

View File

@@ -7,11 +7,12 @@ recommended_enemy_faction: Russia 1990
description: <p>United Nations Observer Mission in Georgia (UNOMIG) observers stationed in Georgia to monitor the ceasefire between Georgia and Abkhazia have been cut off from friendly forces by Russian troops backing the separatist state. The UNOMIG HQ at Sukhumi has been taken, and a small contingent of observers and troops at the Zugdidi Sector HQ will have to make a run for the coast, supported by offshore US naval aircraft. The contingent is aware that their best shot at survival is to swiftly retake Sukhumi before Russian forces have a chance to dig in, so that friendly ground forces can land and reinforce them.</p><p><strong>Note:</strong> Ground unit purchase will not be available past Turn 0 until Sukhumi is retaken, so it is imperative you reach Sukhumi with at least one surviving ground unit to capture it. Two Hueys are available at Zugdidi for some close air support. The player can either play the first leg of the scenario as an evacuation with a couple of light vehicles (e.g. Humvees) set on breakthrough (modifying waypoints in the mission editor so they are not charging head-on into enemy ground forces is suggested), or purchase heavier ground units if they wish to experience a more traditional frontline ground war. Once Sukhumi has been captured, squadrons based in Incirlik Turkey can be ferried in via the "From Incirlik" off-map spawn point.</p> description: <p>United Nations Observer Mission in Georgia (UNOMIG) observers stationed in Georgia to monitor the ceasefire between Georgia and Abkhazia have been cut off from friendly forces by Russian troops backing the separatist state. The UNOMIG HQ at Sukhumi has been taken, and a small contingent of observers and troops at the Zugdidi Sector HQ will have to make a run for the coast, supported by offshore US naval aircraft. The contingent is aware that their best shot at survival is to swiftly retake Sukhumi before Russian forces have a chance to dig in, so that friendly ground forces can land and reinforce them.</p><p><strong>Note:</strong> Ground unit purchase will not be available past Turn 0 until Sukhumi is retaken, so it is imperative you reach Sukhumi with at least one surviving ground unit to capture it. Two Hueys are available at Zugdidi for some close air support. The player can either play the first leg of the scenario as an evacuation with a couple of light vehicles (e.g. Humvees) set on breakthrough (modifying waypoints in the mission editor so they are not charging head-on into enemy ground forces is suggested), or purchase heavier ground units if they wish to experience a more traditional frontline ground war. Once Sukhumi has been captured, squadrons based in Incirlik Turkey can be ferried in via the "From Incirlik" off-map spawn point.</p>
miz: operation_vectrons_claw.miz miz: operation_vectrons_claw.miz
performance: 1 performance: 1
version: "9.0" recommended_start_date: 2008-08-08
version: "9.1"
squadrons: squadrons:
Blue CV: Blue CV:
- primary: BARCAP - primary: BARCAP
secondary: air-to-air secondary: any
aircraft: aircraft:
- F-14B Tomcat - F-14B Tomcat
- F-14A Tomcat (Block 135-GR Late) - F-14A Tomcat (Block 135-GR Late)
@@ -24,7 +25,7 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - F/A-18C Hornet (Lot 20)
- primary: BAI - primary: DEAD
secondary: any secondary: any
aircraft: aircraft:
- F/A-18C Hornet (Lot 20) - F/A-18C Hornet (Lot 20)
@@ -64,7 +65,7 @@ squadrons:
- E-3A - E-3A
- primary: Refueling - primary: Refueling
aircraft: aircraft:
- KC-135 Stratotanker MPRS - KC-135 Stratotanker
- primary: Transport - primary: Transport
aircraft: aircraft:
- C-130 - C-130
@@ -87,28 +88,16 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- MiG-29S Fulcrum-C - MiG-29S Fulcrum-C
- primary: BAI
secondary: air-to-ground
aircraft:
- Su-25T Frogfoot
- primary: CAS - primary: CAS
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Ka-50 Hokum - Su-24M Fencer-D
#Anapa-Vityazevo #Anapa-Vityazevo
12: 12:
- primary: BARCAP - primary: CAS
secondary: any
aircraft:
- Su-27 Flanker-B
- primary: SEAD
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Su-24M Fencer-D - Su-25T Frogfoot
- primary: Strike
secondary: air-to-ground
aircraft:
- Tu-22M3 Backfire-C
- primary: AEW&C - primary: AEW&C
aircraft: aircraft:
- A-50 - A-50
@@ -123,8 +112,9 @@ squadrons:
secondary: any secondary: any
aircraft: aircraft:
- SU-33 Flanker-D - SU-33 Flanker-D
# I am aware there is no Russian LHA. This is just for campaign inversion.
Red LHA: Red LHA:
- primary: BAI - primary: BAI
secondary: air-to-ground secondary: air-to-ground
aircraft: aircraft:
- Mi-8MTV2 Hip - AV-8B Harrier II Night Attack

Binary file not shown.

View File

@@ -0,0 +1,134 @@
---
name: Marianas - Pacific Repartee
theater: MarianaIslands
authors: Fuzzle
description: <p>A naval campaign where a US carrier group must retake Guam, Saipan and the Marianas Islands from the Chinese. <strong>This is a purely offensive air campaign, with no frontline gameplay. Upon securing the area around a FOB, you may either ignore it, bomb it to dust or use the cheats to capture it.</strong></p><p><strong>Backstory:</strong> After an escalation in the South China Sea, the PLAN has taken the US by surprise and invaded Guam, setting up supporting positions throughout the Marianas island chain. With the rest of the US Navy engaged near Japan, a carrier task group must push through China's forces, assist a small Marine contingent holding out on Farallon de Pajaros and liberate Guam.</p>
version: "9.1"
recommended_player_faction: US Navy 2005
recommended_enemy_faction: China 2010
miz: pacific_repartee.miz
performance: 1
recommended_start_date: 2006-02-17
squadrons:
#BLUFOR CVN
Naval-1:
- primary: BARCAP
secondary: any
aircraft:
- VF-143
- primary: SEAD
secondary: any
aircraft:
- VMFA-251
- primary: Strike
secondary: any
aircraft:
- VMFA-122
- primary: AEW&C
aircraft:
- VAW-125
- primary: Refueling
aircraft:
- VS-35 (Tanker)
- primary: Anti-ship
secondary: air-to-ground
aircraft:
- VS-35
- primary: Transport
aircraft:
- HSM-40
# BLUFOR LHA
Naval-2:
- primary: BAI
secondary: air-to-ground
aircraft:
- VMA-223
- primary: Transport
secondary: air-to-ground
aircraft:
- HMLA-169 (UH-1H)
- primary: CAS
secondary: air-to-ground
aircraft:
- HMLA-169 (AH-1W)
# OPFOR CVN 1
Naval-3:
- primary: BARCAP
secondary: any
aircraft:
- J-15 Flanker X-2
- primary: Strike
secondary: any
aircraft:
- J-15 Flanker X-2
# OPFOR CVN 2
Naval-4:
- primary: BARCAP
secondary: any
aircraft:
- J-15 Flanker X-2
- primary: Strike
secondary: any
aircraft:
- J-15 Flanker X-2
# OPFOR CVN 3
Naval-28:
- primary: BARCAP
secondary: any
aircraft:
- J-15 Flanker X-2
- primary: TARCAP
secondary: any
aircraft:
- J-15 Flanker X-2
# OPFOR LHA
OPLHA:
- primary: Strike
secondary: any
aircraft:
- Mi-24P Hind-F
# Saipan Intl
2:
- primary: Transport
aircraft:
- IL-76MD
- primary: BARCAP
secondary: any
aircraft:
- J-7B
- primary: BARCAP
secondary: any
aircraft:
- FC-1 Fierce Dragon
# Tinian Intl
3:
- primary: Refueling
aircraft:
- IL-78M
# Andersen AFB
6:
- primary: TARCAP
secondary: any
aircraft:
- J-11A Flanker-L
- primary: BARCAP
secondary: air-to-ground
aircraft:
- FC-1 Fierce Dragon
- primary: Anti-ship
secondary: air-to-ground
aircraft:
- H-6J Badger
- primary: Transport
aircraft:
- IL-76MD
# Antonio B. Won Pat Intl
4:
- primary: TARCAP
secondary: any
aircraft:
- J-7B
- primary: BARCAP
secondary: air-to-ground
aircraft:
- FC-1 Fierce Dragon

Binary file not shown.

View File

@@ -0,0 +1,248 @@
---
name: Persian Gulf - Scenic Route 2 - Dust To Dust
theater: Persian Gulf
authors: Fuzzle
description: <p>A continuation of Scenic Route. A NATO coalition pushes inland along a protracted axis of advance. Built with helicopters/FOB-based gameplay in mind. <p><strong>Backstory:</strong> With Iran's coastal defences pacified and their forces pushed inland, a beleaguered US Navy is reinforced by a NATO coalition task force. The going will not be easy however; Iran has assembled the full might of its armoured and mechanized divisions alongside rotary support to defend their heartland. The conflict intensifies.</p>
version: "9.1"
advanced_iads: true
recommended_player_faction: NATO OIF
recommended_enemy_faction: Iran 2015
miz: scenic_inland.miz
performance: 2
recommended_start_date: 2005-06-29
squadrons:
# Bandar Abbas Intl
2:
- primary: TARCAP
secondary: air-to-air
aircraft:
- Escadron de chasse 1/30
#French Mirage2000C
- primary: SEAD
secondary: any
aircraft:
- 77th FS
#USAF F-16C
- primary: Strike
secondary: air-to-ground
aircraft:
- No. 12 Squadron
#RAF Tornado GR.4
- primary: Transport
secondary: air-to-ground
aircraft:
- 101st Combat Aviation Brigade
#US Army UH-60
# Havadarya
9:
- primary: BARCAP
secondary: any
aircraft:
- 493rd FS
#USAF F-15C
- primary: BAI
secondary: air-to-ground
aircraft:
- 81st FS
#USAF A-10CI
#BLUFOR CVN
BLUFOR CVN:
- primary: BARCAP
secondary: air-to-air
aircraft:
- VF-143
- primary: TARCAP
secondary: any
aircraft:
- VMFA-251
- primary: AEW&C
aircraft:
- VAW-125
- primary: Refueling
aircraft:
- VS-35 (Tanker)
# BLUFOR LHA
BLUFOR LHA:
- primary: BAI
secondary: air-to-ground
aircraft:
- VMA-223
- primary: Transport
secondary: air-to-ground
aircraft:
- HMLA-169 (UH-1H)
# BLUFOR Start FOB
FOB Anguran:
- primary: CAS
secondary: air-to-ground
aircraft:
- SA 342M Gazelle
#French Gazelle
- primary: CAS
secondary: air-to-ground
aircraft:
- 229th Aviation Battalion
#US Army Apache AH-64D
# OPFOR L1F1
FOB Tang-e Dalan:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L1F2
FOB Fars Border:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L1F2 split
FOB Bikuyeh:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-28N Havoc
# Lar
11:
- primary: BARCAP
secondary: air-to-air
aircraft:
- MiG-21bis Fishbed-N
# OPFOR L2F1
FOB Mansurabad:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L2F2
FOB Jahrom:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L2F3
FOB Tower:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L2F3 split
FOB Nouderan:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-28N Havoc
# Shiraz Intl
19:
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: BARCAP
secondary: any
aircraft:
- F-4E Phantom II
- primary: TARCAP
secondary: air-to-air
aircraft:
- MiG-29A Fulcrum-A
- primary: Strike
secondary: air-to-ground
aircraft:
- Su-22M4 Fitter-K
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- primary: SEAD
secondary: air-to-ground
aircraft:
- Su-25T Frogfoot
# OPFOR L3F1
FOB Kherameh:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F2
FOB Tashk:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F3
FOB Chahak:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F4
FOB Plainhold:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F4 split
FOB Robat:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F5
FOB Plainguard:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F6
FOB Hasanabad:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F6 split
FOB Najafabad:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-28N Havoc
# OPFOR L3F7
FOB Kabutarkhan:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# OPFOR L3F8
FOB Sa'di:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
# Kerman
18:
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-78MD
- primary: BARCAP
secondary: any
aircraft:
- F-14A Tomcat (Block 135-GR Late)
- primary: TARCAP
secondary: air-to-air
aircraft:
- MiG-29A Fulcrum-A
- primary: Strike
secondary: air-to-ground
aircraft:
- Su-24MK Fencer-D
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F

View File

@@ -3,7 +3,7 @@ name: Persian Gulf - Scenic Route
theater: Persian Gulf theater: Persian Gulf
authors: Fuzzle authors: Fuzzle
description: <p>A lightweight naval campaign involving a US Navy carrier group pushing across the coast of Iran. <strong>Note that the ground units purchased on turn zero must sustain you until you've taken the first hostile FOB. The starting point does not have a factory to simulate a Marine Expeditionary Force deploying from the carrier group.</strong></p><p><strong>Backstory:</strong> Iran has declared war on all US forces in the Gulf resulting in all local allies withdrawing their support for American troops. A lone carrier group must pacify the southern coast of Iran and hold out until backup can arrive lest the US and her interests be ejected from the region permanently.</p> description: <p>A lightweight naval campaign involving a US Navy carrier group pushing across the coast of Iran. <strong>Note that the ground units purchased on turn zero must sustain you until you've taken the first hostile FOB. The starting point does not have a factory to simulate a Marine Expeditionary Force deploying from the carrier group.</strong></p><p><strong>Backstory:</strong> Iran has declared war on all US forces in the Gulf resulting in all local allies withdrawing their support for American troops. A lone carrier group must pacify the southern coast of Iran and hold out until backup can arrive lest the US and her interests be ejected from the region permanently.</p>
version: 9.1 version: "9.1"
recommended_player_faction: US Navy 2005 recommended_player_faction: US Navy 2005
recommended_enemy_faction: Iran 2015 recommended_enemy_faction: Iran 2015
miz: scenic_route.miz miz: scenic_route.miz

View File

@@ -1,11 +0,0 @@
{
"name": "Syria - Full Map",
"theater": "Syria",
"authors": "Plob",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Syria 2011",
"description": "<p>A long campaign across the syria map, strap in.</p></p>Each turn after planning missions, enable culling to ensure correct culling behaviour.</p>",
"miz": "syria_full_map.miz",
"performance": 3,
"version": "8.0"
}

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