Compare commits

...

116 Commits
7.0.0 ... 4.0.0

Author SHA1 Message Date
Dan Albert
8e6e1469d7 Merge branch 'develop-4.x' into master. 2021-06-26 12:59:07 -07:00
Dan Albert
6cc967742a Add the most important feature to the changelog.
(cherry picked from commit aa86a6e53b)
2021-06-26 12:34:51 -07:00
Brock Greman
17f2bcc9c9 Clarify the impact of non-cold flight starts.
(cherry picked from commit 34470336e4)
2021-06-26 12:29:23 -07:00
Mustang-25
9d499a1430 Update Op Mole Cricket 2010 Campaign.
Moved SAM generator at Rosh Pina so it does not spawn units on the runway.

(cherry picked from commit 5a2a89f19e)
2021-06-26 12:17:33 -07:00
Dan Albert
3b55dfad40 Revert accidental change to default pilot limit.
(cherry picked from commit 7eb4df770e)
2021-06-26 12:06:26 -07:00
Simon Clark
9d3c7a86b6 Bump campaign versions. 2021-06-26 19:29:04 +01:00
Chris Seagraves
7f68846023 Include control point name in ground object info.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/498

(cherry picked from commit ffcae66f59)
2021-06-26 11:24:20 -07:00
Dan Albert
c1534cba9e Ack campaign version update.
No scenery targets in this campaign so no work needed.

https://github.com/dcs-liberation/dcs_liberation/issues/1359
(cherry picked from commit d2df795ba7)
2021-06-26 11:19:43 -07:00
Dan Albert
eea31168c1 Remove dead campaign.
https://github.com/dcs-liberation/dcs_liberation/issues/1359
(cherry picked from commit b930e13964)
2021-06-26 11:19:42 -07:00
Dan Albert
f2de1fdac6 Fix save path for new games.
(cherry picked from commit e6bf318cdf)
2021-06-26 11:01:22 -07:00
Dan Albert
8dd29d2319 Disband unfilled incompletable transfers.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1317

(cherry picked from commit 4cfed08247)
2021-06-26 10:55:19 -07:00
Khopa
b402dad801 Mod support : Updated frenchpach to version 4.6 (Added new units VBCI and AMX-13 support) + some frenchpack units yaml tweaks 2021-06-26 19:23:27 +02:00
Khopa
7199fead00 Fixed duplicates in france 2005 faction 2021-06-26 15:23:55 +02:00
Khopa
4ff0f29fe0 Fixed yaml issue causing an issue with Leclerc MBT 2021-06-26 15:18:47 +02:00
Khopa
1b8992eb04 Updated campaign : Operation Dynamo for The Channel map 2021-06-26 14:42:42 +02:00
Khopa
ccbcf4f69a Updated campaign : Russia Small, renamed it to "From Mozdok to Maykop" 2021-06-26 13:48:55 +02:00
Khopa
0747007f58 Updated campaign : Battle for Golan Heights 2021-06-26 13:20:13 +02:00
Dan Albert
723588666f Fix save path cleanup.
(cherry picked from commit 959a13a514)
2021-06-25 23:21:49 -07:00
Chris Seagraves
94861ca477 Use directory of current save for open/save-as.
(cherry picked from commit b601d713d2)
2021-06-25 23:02:09 -07:00
Dan Albert
e8992c5bed Add "Nevada Limited Air" campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1358

(cherry picked from commit dc96d8699a)
2021-06-25 21:34:55 -07:00
Dan Albert
e841358f74 Add "Scenic Route" campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1334

(cherry picked from commit f38cdd8432)
2021-06-25 21:28:59 -07:00
Dan Albert
3c135720a0 Fix lint.
(cherry picked from commit 91655a3d5a)
2021-06-25 19:34:10 -07:00
Dan Albert
d7db290892 Move the default save game directory.
The top level DCS directory gets messy fast if we fill it with save
games.

(cherry picked from commit 7774a9b2ab)
2021-06-25 17:48:45 -07:00
Dan Albert
b7626c10da Fix targeting of carrier groups with TGOs.
The assumption that the first group is the carrier group is wrong.

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

(cherry picked from commit 80cf8f484d)
2021-06-25 16:47:39 -07:00
Dan Albert
d79e8f46f3 State carrier requirement for Blackball.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1355

(cherry picked from commit cb7c075a61)
2021-06-25 16:35:43 -07:00
Dan Albert
278b9730cd Fix crash when buying or selling TGO units.
Updating the game destroys this window so we cannot continue with the
calls. It worked in my initial testing, so presumably it's partly
dependent on when the finalizers run.

Since the windows will be destroyed there's nothing for us to actually
update, so just remove that signal and the explicit close calls.

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

(cherry picked from commit 4d0fb67c53)
2021-06-25 16:30:51 -07:00
Dan Albert
d187c571ea Ack campaign version bump.
Campaigns don't use scenery targets.

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

(cherry picked from commit 380d1d4f18)
2021-06-24 18:18:07 -07:00
Dan Albert
b3705531d4 Update pydcs to use latest master.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/993

(cherry picked from commit 71832859a5)
2021-06-24 18:14:27 -07:00
docofmur
666b389821 Fixes #1337 by making ground location search look in both directions (#1338)
(cherry picked from commit a31432ad9e)
2021-06-24 13:25:41 -04:00
bgreman
ddc076b141 Implements #1331 by changing the Pass Turn button text on Turn 0. (#1333)
(cherry picked from commit 26743154d8)
2021-06-24 11:00:05 -04:00
bgreman
eee1791a79 Adds a ruler to the map (#1332)
* Adds a ruler to the map

* Updating changelog

* Updating changelog

(cherry picked from commit a50a6fa917)
2021-06-24 02:59:16 -04:00
bgreman
fb5a6d3243 Fix #1329 player loses frontline progress when skipping turn 0 (#1330)
(cherry picked from commit b43e5bac0b)
2021-06-24 02:06:26 -04:00
Dan Albert
113c00ac05 Retry reading state.json on failure.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1313

(cherry picked from commit ddaef1fb64)
2021-06-23 20:18:36 -07:00
Dan Albert
85ca85ac6d Signal game update when buying/selling TGO units.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1312

(cherry picked from commit 6f264ff5de)
2021-06-23 20:08:31 -07:00
Dan Albert
da917a7dde Fix another unit type mismatch.
(cherry picked from commit a06fc6d80f)
2021-06-23 20:02:02 -07:00
Dan Albert
b03d1599e1 Add a feature flag for pilot limits.
This doesn't currently interact very well with the auto purchase since
the procurer might by aircraft that don't have pilots available. That
should be fixed, but for the short term we should just default to not
enabling this new feature.

(cherry picked from commit 3ddfc47d3a)
2021-06-23 18:47:47 -07:00
docofmur
2b3c56ad38 Campaign version update (#1326)
Caucasus Multi part campaign version update. No map strike objects so just the version change

(cherry picked from commit 905bd05ba8)
2021-06-23 20:01:18 -04:00
bgreman
8dc35bec5a Fix empty convoys (#1327)
* Hopefully getting rid of empty convoys for good

* changing Dict to dict for type checks

(cherry picked from commit 3274f3ec35)
2021-06-23 19:51:37 -04:00
bgreman
3f4f27612b Fixes #1310 (#1325)
* Fixes #1310 by only refunding GUs if no faction CP has an attached factory.  Previously it would refund all units at the CP, including aircraft.

Also changes the CP CAPTURE cheat to work at any CP regardless of adjacency to frontline or BLUEFOR/OPFOR state.

* Fixing typing issues, changint all Dict[] types to dict[]

* Updating changelog

(cherry picked from commit c3b8c48ca2)
2021-06-23 17:19:58 -04:00
Dan Albert
17f9487fe0 Update From Caen to Evreux.
Add support for inversion and ack the version change (Normandy is
unaffected by ID updates).

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

(cherry picked from commit d365094616)
2021-06-23 14:00:08 -07:00
Dan Albert
e15b10ae7e Ack version update for PG campaigns.
PG is unaffected by building ID changes.

(cherry picked from commit 7c76684076)
2021-06-23 13:50:01 -07:00
Dan Albert
17d56beeaa Update Vectron's Claw and Peace Spring.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1323

(cherry picked from commit 0ef27b038a)
2021-06-23 12:59:22 -07:00
Dan Albert
53c7912592 Copy initialization fix to AircraftType.
(cherry picked from commit 610a27c0e4)
2021-06-23 12:50:55 -07:00
RndName
1f318aff3c set window title empty on new game
also fixed small exception when aborting the open file dialog which lead to " as filename

fixes #1305

(cherry picked from commit 752c91a721)
2021-06-23 12:44:49 -07:00
Dan Albert
2bb1c0b3f2 Fixed missed initialization of unit data on load.
We'd only load unit data if a name lookup was done and missed it on a
type lookup. Ideally we wouldn't need to do a type lookup here until the
ground unit templates are reworked we still do.

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

(cherry picked from commit d3d655da07)
2021-06-22 23:42:25 -07:00
Dan Albert
b057f027d5 Return pilots when canceling flight creation.
(cherry picked from commit db36cf248e)
2021-06-22 23:36:49 -07:00
Dan Albert
cc079ad44e Add the Around the Mountain campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1280

(cherry picked from commit 153d8e106e)
2021-06-22 23:29:04 -07:00
Dan Albert
974c0069e6 Add Operation Blackball campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1320

(cherry picked from commit df8829b477)
2021-06-22 23:23:04 -07:00
Dan Albert
9028109fe3 Update Syria Full campaign.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1319

(cherry picked from commit 569bc297a8)
2021-06-22 23:20:17 -07:00
Dan Albert
db27f3b0d9 Update Northern Russia campaign.
I bumped the submitted 6.1 to 7.0 (which didn't exist when the files
were uploaded) because this campaign uses no scenery targets.

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

(cherry picked from commit 099cbbdb64)
2021-06-22 23:17:12 -07:00
Dan Albert
cb542b6af4 Update Allied Sword.
Only change from the uploaded files is that I increased the campaign
version to 7.0 since this doesn't use any scenery targets so has no work
to do for that.

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

(cherry picked from commit ca7469b92e)
2021-06-22 23:14:12 -07:00
Dan Albert
fcea37c340 Correct mistakenly updated campaign.
(cherry picked from commit 6db4145927)
2021-06-22 23:08:30 -07:00
Dan Albert
cf3d13f9d3 Bump campaign version to account for DCS changes.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1308

(cherry picked from commit ca93f2baff)
2021-06-22 23:04:41 -07:00
Dan Albert
6789beb4b5 Fix unit type mismatch.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1314

(cherry picked from commit 84a0a3caeb)
2021-06-22 22:55:26 -07:00
Dan Albert
8f1ec4a519 Update Operation Peace Spring.
https://github.com/dcs-liberation/dcs_liberation/issues/1303
(cherry picked from commit 7b327693e2)
2021-06-22 15:18:12 -07:00
docofmur
b8bc9d87ec Faction Audit.
Transports and mod aircraft added where needed cleaned up various
duplicates in factions.

(cherry picked from commit dba70dc6d5)
2021-06-22 15:02:04 -07:00
Mike Jones
52aff8bc30 Use pydcs has_tacan attribute to check if tankers support TACAN.
(cherry picked from commit bd1618e41d)
2021-06-22 14:35:47 -07:00
Mike Jones
5c81ac06ac Add gunfighter flag to aircraft data files.
(cherry picked from commit 08b7aff0d8)
2021-06-22 14:35:46 -07:00
Mike Jones
8364148305 Add patrol configuration to unit data files.
This allows altitude/speed of AEW&C and tankers to be configured.

(cherry picked from commit a75688f89c)
2021-06-22 14:35:45 -07:00
Mike Jones
2bcff5a5c2 Fix unit type comparisons.
When comparing UnitType against a pydcs type, use .dcs_unit_type.

(cherry picked from commit 30763b5401)
2021-06-22 14:35:43 -07:00
Chris Seagraves
c227923bdf Fix bug with file name in title with invalid save games.
(cherry picked from commit 814519248c)
2021-06-22 14:20:16 -07:00
Simon Clark
4569b1b45a Campaign clarity. 2021-06-22 17:20:35 +01:00
Simon Clark
3a193d1dd4 Add clarity for mod selection page. 2021-06-21 20:04:34 +01:00
Simon Clark
9334cba564 Add Operation Atilla campaign.
It's a Cyprus invasion campaign - what's not to like!
2021-06-21 19:45:19 +01:00
Dan Albert
4dc1daa100 Fix command line campaign generator.
(cherry picked from commit 47e038c9fa)
2021-06-20 23:46:32 -07:00
Dan Albert
0d99fc3d36 Don't order transports for incapable factions.
If these orders can't be fulfilled for the faction it will prevent the
faction from ordering any non-reserve aircraft since transports are
given priority after reserve missions, and they'll never be fulfillable.
As such, no non-reserve aircraft will ever be purchased for factions
without transport aircraft.

Factions without transport aircraft are screwed in other ways, but this
will fix their air planning for campaigns that aren't dependent on
airlift.

(cherry picked from commit e96210f48c)
2021-06-20 23:44:16 -07:00
Simon Clark
eee78288c9 Updated factions to reflect mod select changes. 2021-06-21 01:33:13 +01:00
Simon Clark
c2f112e3a6 Refactor the mod select changes, re-add accidentally deleted factions. 2021-06-21 01:14:07 +01:00
Simon Clark
ef3f7125b3 Make mod selection nicer and deprecate MB-339.
Mod selection is now done via checkbox in the new game wizard.

The MB-339 is being turned into a paid module, and the free mod no longer works, so it's been removed.
2021-06-21 00:03:22 +01:00
Dan Albert
4558088412 Revert "Don't propose missions the air wing can't plan."
This is redundant because plan_mission already checks this.

This reverts commit 3338df9836.

(cherry picked from commit d074500109)
2021-06-20 15:59:09 -07:00
Dan Albert
00ca7d0b4d Merge pull request #1208 from dcs-liberation/develop-3.x
Release 3.0.0.
2021-06-10 23:58:41 -07:00
Dan Albert
64c426653c Merge branch 'master' into develop-3.x 2021-06-10 23:46:18 -07:00
Mustang-25
a8960c9bbe Add the First Lebanon War Historical Campaign.
(cherry picked from commit 75e3b4cc84)
2021-06-10 17:39:00 -07:00
Florian
72282845e8 added missing units
(cherry picked from commit 78cd17e279)
2021-06-10 17:37:26 -07:00
Khopa
e64aff4e91 Removed helipad from golan heights campaign to avoid capture trigger error 2021-06-10 23:27:19 +02:00
Dan Albert
e192e54c90 Fix CAS commit range display.
CAS commits around the target, not its flight plan.

(cherry picked from commit 40aa7734e1)
2021-06-09 21:52:02 -07:00
Dan Albert
39b0599b7b Fix engagement distance display.
(cherry picked from commit a9dacf4a29)
2021-06-09 21:45:20 -07:00
Dan Albert
45b40e4aa3 Update Operation Mole Cricket.
https://github.com/dcs-liberation/dcs_liberation/issues/1203
(cherry picked from commit 0594e1148e)
2021-06-09 21:27:22 -07:00
Dan Albert
9887a8ff83 Add Northern Russia campaign.
https://github.com/dcs-liberation/dcs_liberation/issues/1202
(cherry picked from commit 9eacd1563f)
2021-06-09 21:27:21 -07:00
Dan Albert
8d3556aa4b Update mission start guidance.
(cherry picked from commit 66f82b6ff9)
2021-06-09 19:21:18 -07:00
Florian
a59c01bcfe added texts for all units
(cherry picked from commit eb6206ea57)
2021-06-09 19:10:02 -07:00
Brock Greman
fb72962f74 Fixing display of "sunny" during clear conditions at night.
(cherry picked from commit 3ad51cafa8)
2021-06-09 19:09:40 -07:00
Dan Albert
ed1dacfe7c Remove incompatible campaigns.
We have quite a few campaigns now, so removing the broken ones.

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

(cherry picked from commit 0e68884493)
2021-06-09 19:09:12 -07:00
Florian
794de0fcbb added missing units to price table
(cherry picked from commit 366190ee99)
2021-06-09 19:04:32 -07:00
Dan Albert
9d71b2e727 Make campaign names consistent.
(cherry picked from commit 42d56a324f)
2021-06-09 18:59:05 -07:00
docofmur
5b8f626651 Campaigns for 3.0
4 campaigns updated for 3.0 1 small PG 3 for Caucasus 1 full and 2 parts based on the full

(cherry picked from commit 7d1f1ea2f7)
2021-06-09 18:59:04 -07:00
Dan Albert
461f4b82a9 Add Around the Mountain campaign.
https://github.com/dcs-liberation/dcs_liberation/issues/1199
(cherry picked from commit 30cab8e3a7)
2021-06-09 18:54:15 -07:00
Dan Albert
15653d0628 Add Operation Allied Sword campaign and factions.
https://github.com/dcs-liberation/dcs_liberation/issues/1196
(cherry picked from commit e0e2162c6d)
2021-06-09 18:54:14 -07:00
Dan Albert
dffc631b87 Add Humble Helper campaign and factions.
https://github.com/dcs-liberation/dcs_liberation/issues/1197
(cherry picked from commit f1582fcc10)
2021-06-09 18:54:12 -07:00
Dan Albert
17efb48b2e Make enable_and_reset not half lie.
https://github.com/dcs-liberation/dcs_liberation/issues/1185
(cherry picked from commit b8c14d69c3)
2021-06-08 21:20:15 -07:00
Dan Albert
7e85825d2b Fix typo in Incirlik runway data.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1188

(cherry picked from commit 725b5083c7)
2021-06-08 21:15:04 -07:00
Dan Albert
798591b980 Fix repeated JTACs after multiple generations.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1191

(cherry picked from commit 87dd6b19bf)
2021-06-08 20:56:41 -07:00
Schneefl0cke
4a52af298c Add Recon combat role.
(cherry picked from commit e4c9d8799e)
2021-06-08 20:47:41 -07:00
dependabot[bot]
fe886a754e Bump pillow from 8.1.1 to 8.2.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 8.1.1 to 8.2.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/8.1.1...8.2.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit bc938db7f9)
2021-06-08 20:47:31 -07:00
bgreman
0220fa4ff6 Gripen mod support. 2021-06-08 19:47:44 -07:00
Dan Albert
e7336d8608 Fix hangar status display.
(cherry picked from commit d8881e2734)
2021-06-06 17:13:00 -07:00
Dan Albert
d77a174ac1 Label the player checkbox in the roster editor.
(cherry picked from commit 45869c428e)
2021-06-06 13:43:55 -07:00
Khopa
6348317893 Added a small WW2 campaign on Normandy map (Replacing the former Normandy Small campaign). 2021-06-06 18:49:11 +02:00
Khopa
a516cd2f80 Added a small WW2 campaign on Normandy map (Replacing the former Normandy Small campaign). 2021-06-06 18:45:02 +02:00
Dan Albert
e1aa3e9d0e Suppress events fired while rebuilding model.
(cherry picked from commit d316e13fa6)
2021-06-05 15:21:58 -07:00
Dan Albert
d1d1acf6e0 Hide incompatible campaigns by default.
https://github.com/dcs-liberation/dcs_liberation/issues/1178
(cherry picked from commit 1ea98a6ed1)
2021-06-05 15:15:53 -07:00
Dan Albert
a0e5a707fb Merge pull request #1053 from Khopa/develop_2_5_x
Release 2.5.1.
2021-05-02 13:28:45 -07:00
Dan Albert
4555a4968d Update changelog for 2.5.1. 2021-05-02 13:17:35 -07:00
SnappyComebacks
ae34e4749b Move base EWRs into their own category.
Without this we're sometimes spawning base EWRs at points far outside the base perimeter.
2021-04-28 21:07:22 -07:00
Khopa
635eee9590 Fixed ai_flight_planner for maps lacking frontlines (such as battle of britain on The Channel map) 2021-04-24 00:11:53 +02:00
Khopa
f0558c4c1e Fixed ai_flight_planner for maps lacking frontlines (such as battle of britain on The Channel map) 2021-04-23 23:45:14 +02:00
Dan Albert
637ca8fbca Stop projecting threat zones from front lines.
This is an interim improvement since we should probably be pushing the
BARCAPs into TARCAP roles when the front line is so close. This does
regress flight pathing for anything that should route around the front
(to avoid getting shot at by SHORADS and TARCAPs), but for now it's one
or the other and this is the one everyone's complaining about.

(cherry picked from commit e474748f4d)
2021-04-22 18:23:12 -07:00
Dan Albert
e4e65df976 Generalize commit range display for all patrols.
Fixes https://github.com/Khopa/dcs_liberation/issues/890

(cherry picked from commit 132ba905c7)
2021-04-22 17:55:49 -07:00
Dan Albert
29579a2aec Remove missed merge conflict marker. 2021-04-22 17:49:34 -07:00
Dan Albert
e32b43cffb Show BARCAP commit ranges by default.
BARCAP placement confuses a lot of people but this should make it more
clear.

(cherry picked from commit 208d1b82b5)
2021-04-22 17:46:29 -07:00
C. Perreau
de2e5f861b Merge pull request #1007 from Khopa/develop_2_5_x
Release 2.5.0
2021-04-22 00:08:42 +02:00
Khopa
b27a7fc71b Fixed Lint issue 2021-04-21 22:54:48 +02:00
Khopa
5861ce6146 Fixed error with Ramat David frequency (typo) 2021-04-21 22:38:08 +02:00
Khopa
c732ed556f Fixed airfields frequency on Persian Gulf 2021-04-21 22:30:08 +02:00
Khopa
be1a75e520 Fixed airfields frequency on Syria 2021-04-21 22:14:18 +02:00
Khopa
c41d10c581 Pydcs update to latest version 2021-04-21 12:57:19 +02:00
184 changed files with 1453 additions and 2147 deletions

View File

@@ -4,27 +4,41 @@ Saves from 3.x are not compatible with 4.0.
## Features/Improvements
* **[Campaign]** Squadrons now have a maximum size and killed pilots replenish at a limited rate.
* **[Engine]** Support for DCS 2.7.2.7910.1 and newer, including Cyprus, F-16 JDAMs, and the Hind.
* **[Campaign]** Squadrons now (optionally, off by default) have a maximum size and killed pilots replenish at a limited rate.
* **[Campaign]** Added an option to disable levelling up of AI pilots.
* **[Campaign]** Added Russian Intervention 2015 campaign on Syria, for a small and somewhat realistic Russian COIN scenario.
* **[Campaign]** Added Operation Atilla campaign on Syria, for a reasonably large invasion of Cyprus scenario.
* **[Campaign AI]** AI will plan Tanker flights.
* **[Campaign AI]** Removed max distance for AEW&C auto planning.
* **[Economy]** Adjusted prices for aircraft to balance out some price inconsistencies.
* **[Factions]** Added more tankers to factions.
* **[Flight Planner]** Added ability to plan Tankers.
* **[Modding]** Campaign format version is now 7.0 to account for DCS map changes that made scenery strike targets incompatible with existing campaigns.
* **[Mods]** Added support for the Gripen mod.
* **[Mods]** Removes MB-339PAN support, as the mod is now deprecated and no longer works with DCS 2.7+.
* **[Mission Generation]** Added support for "Neutral Dot" label options.
* **[New Game Wizard]** Mods are now selected via checkboxes in the new game wizard, not as separate factions.
* **[UI]** Ctrl click and shift click now buy or sell 5 or 10 units respectively.
* **[UI]** Multiple waypoints can now be deleted simultaneously if multiple waypoints are selected.
* **[UI]** Carriers and LHAs now match the colour of airfields, and their destination icons are translucent.
* **[UI]** Updated intel box text for first turn.
* **[UI]** Base Capture Cheat is now usable at all bases and can also be used to transfer player-owned bases to OPFOR.
* **[UI]** Pass Turn button is relabled as "Begin Campaign" on Turn 0.
* **[UI]** Added a ruler to the map.
* **[UI]** Liberation now saves games to `<DCS user directory>/Liberation/Saves` by default to declutter the main directory.
## Fixes
* **[Campaign AI]** Fix procurement for factions that lack some unit types.
* **[Campaign AI]** Improved pruning of unplannable missions which should improve turn cycle time and prevent the auto-planner from quitting early.
* **[Campaign AI]** Fix auto purchase of aircraft for factions that have no transport aircraft.
* **[Campaign AI]** Fix refunding of pending aircraft purchases when a side has no factory available.
* **[Mission Generation]** Fixed problem with mission load when control point name contained an apostrophe.
* **[Mission Generation]** Fixed EWR group names so they contribute to Skynet again.
* **[Mission Generation]** Fixed duplicate name error when generating convoys and cargo ships when creating manual transfers after loading a game.
* **[Mission Generation]** Fixed empty convoys not being disbanded when all units are killed/removed.
* **[Mission Generation]** Fixed player losing frontline progress when skipping from turn 0 to turn 1.
* **[Mission Generation]** Fixed issue where frontline would only search to the right for valid locations.
* **[UI]** Made non-interactive map elements less obstructive.
* **[UI]** Added support for Neutral Dot difficulty label
* **[UI]** Clear skies at night no longer described as "Sunny" by the weather widget.

View File

@@ -1,51 +0,0 @@
from dcs.planes import (
Bf_109K_4,
C_101CC,
FW_190A8,
FW_190D9,
F_5E_3,
F_86F_Sabre,
I_16,
L_39ZA,
MiG_15bis,
MiG_19P,
MiG_21Bis,
P_47D_30,
P_47D_30bl1,
P_47D_40,
P_51D,
P_51D_30_NA,
SpitfireLFMkIX,
SpitfireLFMkIXCW,
)
from pydcs_extensions.a4ec.a4ec import A_4E_C
"""
This list contains the aircraft that do not use the guns as the last resort weapons, but as a main weapon
They'll RTB when they don't have gun ammo left
"""
GUNFIGHTERS = [
# Cold War
MiG_15bis,
MiG_19P,
MiG_21Bis,
F_86F_Sabre,
A_4E_C,
F_5E_3,
# Trainers
C_101CC,
L_39ZA,
# WW2
P_51D_30_NA,
P_51D,
P_47D_30,
P_47D_30bl1,
P_47D_40,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
I_16,
]

View File

@@ -44,12 +44,10 @@ from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.f22a.f22a import F_22A
from pydcs_extensions.hercules.hercules import Hercules
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
from pydcs_extensions.mb339.mb339 import MB_339PAN
from pydcs_extensions.su57.su57 import Su_57
plane_map["A-4E-C"] = A_4E_C
plane_map["F-22A"] = F_22A
plane_map["MB-339PAN"] = MB_339PAN
plane_map["Su-57"] = Su_57
plane_map["Hercules"] = Hercules
plane_map["JAS39Gripen"] = JAS39Gripen
@@ -89,6 +87,14 @@ vehicle_map["Toyota_bleu"] = frenchpack.DIM__TOYOTA_BLUE
vehicle_map["Toyota_vert"] = frenchpack.DIM__TOYOTA_GREEN
vehicle_map["Toyota_desert"] = frenchpack.DIM__TOYOTA_DESERT
vehicle_map["Kamikaze"] = frenchpack.DIM__KAMIKAZE
vehicle_map["AMX1375"] = frenchpack.AMX_13_75mm
vehicle_map["AMX1390"] = frenchpack.AMX_13_90mm
vehicle_map["VBCI"] = frenchpack.VBCI
vehicle_map["T62"] = frenchpack.Char_T_62
vehicle_map["T64BV"] = frenchpack.Char_T_64BV
vehicle_map["T72M"] = frenchpack.Char_T_72A
vehicle_map["KORNET"] = frenchpack.KORNET_ATGM
vehicle_map[highdigitsams.AAA_SON_9_Fire_Can.id] = highdigitsams.AAA_SON_9_Fire_Can
vehicle_map[highdigitsams.AAA_100mm_KS_19.id] = highdigitsams.AAA_100mm_KS_19

View File

@@ -29,7 +29,7 @@ from game.radio.channels import (
ViggenRadioChannelAllocator,
NoOpChannelAllocator,
)
from game.utils import Speed, kph
from game.utils import Distance, Speed, feet, kph, knots
if TYPE_CHECKING:
from gen.aircraft import FlightData
@@ -90,12 +90,34 @@ class RadioConfig:
}[config.get("namer", "default")]
@dataclass(frozen=True)
class PatrolConfig:
altitude: Optional[Distance]
speed: Optional[Speed]
@classmethod
def from_data(cls, data: dict[str, Any]) -> PatrolConfig:
altitude = data.get("altitude", None)
speed = data.get("altitude", None)
return PatrolConfig(
feet(altitude) if altitude is not None else None,
knots(speed) if speed is not None else None,
)
@dataclass(frozen=True)
class AircraftType(UnitType[FlyingType]):
carrier_capable: bool
lha_capable: bool
always_keeps_gun: bool
# If true, the aircraft does not use the guns as the last resort weapons, but as a main weapon.
# It'll RTB when it doesn't have gun ammo left.
gunfighter: bool
max_group_size: int
patrol_altitude: Optional[Distance]
patrol_speed: Optional[Speed]
intra_flight_radio: Optional[Radio]
channel_allocator: Optional[RadioChannelAllocator]
channel_namer: Type[ChannelNamer]
@@ -162,6 +184,8 @@ class AircraftType(UnitType[FlyingType]):
@classmethod
def for_dcs_type(cls, dcs_unit_type: Type[FlyingType]) -> Iterator[AircraftType]:
if not cls._loaded:
cls._load_all()
yield from cls._by_unit_type[dcs_unit_type]
@staticmethod
@@ -192,6 +216,7 @@ class AircraftType(UnitType[FlyingType]):
raise KeyError(f"Missing required price field: {data_path}") from ex
radio_config = RadioConfig.from_data(data.get("radios", {}))
patrol_config = PatrolConfig.from_data(data.get("patrol", {}))
try:
introduction = data["introduced"]
@@ -213,7 +238,10 @@ class AircraftType(UnitType[FlyingType]):
carrier_capable=data.get("carrier_capable", False),
lha_capable=data.get("lha_capable", False),
always_keeps_gun=data.get("always_keeps_gun", False),
gunfighter=data.get("gunfighter", False),
max_group_size=data.get("max_group_size", aircraft.group_size_max),
patrol_altitude=patrol_config.altitude,
patrol_speed=patrol_config.speed,
intra_flight_radio=radio_config.intra_flight,
channel_allocator=radio_config.channel_allocator,
channel_namer=radio_config.channel_namer,

View File

@@ -45,6 +45,8 @@ class GroundUnitType(UnitType[VehicleType]):
@classmethod
def for_dcs_type(cls, dcs_unit_type: Type[VehicleType]) -> Iterator[GroundUnitType]:
if not cls._loaded:
cls._load_all()
yield from cls._by_unit_type[dcs_unit_type]
@staticmethod

View File

@@ -382,15 +382,21 @@ class PollDebriefingFileThread(threading.Thread):
else:
last_modified = 0
while not self.stopped():
if (
os.path.isfile("state.json")
and os.path.getmtime("state.json") > last_modified
):
with open("state.json", "r") as json_file:
json_data = json.load(json_file)
debriefing = Debriefing(json_data, self.game, self.unit_map)
self.callback(debriefing)
break
try:
if (
os.path.isfile("state.json")
and os.path.getmtime("state.json") > last_modified
):
with open("state.json", "r") as json_file:
json_data = json.load(json_file)
debriefing = Debriefing(json_data, self.game, self.unit_map)
self.callback(debriefing)
break
except json.JSONDecodeError:
logging.exception(
"Failed to decode state.json. Probably attempted read while DCS "
"was still writing the file. Will retry in 5 seconds."
)
time.sleep(5)

View File

@@ -54,7 +54,7 @@ class Event:
@property
def is_player_attacking(self) -> bool:
return self.attacker_name == self.game.player_name
return self.attacker_name == self.game.player_faction.name
@property
def tasks(self) -> List[Type[Task]]:

View File

@@ -257,6 +257,83 @@ class Faction:
if unit.unit_class is unit_class:
yield unit
def apply_mod_settings(self, mod_settings) -> Faction:
# aircraft
if not mod_settings.a4_skyhawk:
self.remove_aircraft("A-4E-C")
if not mod_settings.hercules:
self.remove_aircraft("Hercules")
if not mod_settings.f22_raptor:
self.remove_aircraft("F-22A")
if not mod_settings.jas39_gripen:
self.remove_aircraft("JAS39Gripen")
self.remove_aircraft("JAS39Gripen_AG")
if not mod_settings.su57_felon:
self.remove_aircraft("Su-57")
# frenchpack
if not mod_settings.frenchpack:
self.remove_vehicle("AMX10RCR")
self.remove_vehicle("SEPAR")
self.remove_vehicle("ERC")
self.remove_vehicle("M120")
self.remove_vehicle("AA20")
self.remove_vehicle("TRM2000")
self.remove_vehicle("TRM2000_Citerne")
self.remove_vehicle("TRM2000_AA20")
self.remove_vehicle("TRMMISTRAL")
self.remove_vehicle("VABH")
self.remove_vehicle("VAB_RADIO")
self.remove_vehicle("VAB_50")
self.remove_vehicle("VIB_VBR")
self.remove_vehicle("VAB_HOT")
self.remove_vehicle("VAB_MORTIER")
self.remove_vehicle("VBL50")
self.remove_vehicle("VBLANF1")
self.remove_vehicle("VBL-radio")
self.remove_vehicle("VBAE")
self.remove_vehicle("VBAE_MMP")
self.remove_vehicle("AMX-30B2")
self.remove_vehicle("Tracma")
self.remove_vehicle("JTACFP")
self.remove_vehicle("SHERIDAN")
self.remove_vehicle("Leclerc_XXI")
self.remove_vehicle("Toyota_bleu")
self.remove_vehicle("Toyota_vert")
self.remove_vehicle("Toyota_desert")
self.remove_vehicle("Kamikaze")
self.remove_vehicle("AMX1375")
self.remove_vehicle("AMX1390")
self.remove_vehicle("VBCI")
self.remove_vehicle("T62")
self.remove_vehicle("T64BV")
self.remove_vehicle("T72M")
self.remove_vehicle("KORNET")
# high digit sams
if not mod_settings.high_digit_sams:
self.remove_air_defenses("SA10BGenerator")
self.remove_air_defenses("SA12Generator")
self.remove_air_defenses("SA20Generator")
self.remove_air_defenses("SA20BGenerator")
self.remove_air_defenses("SA23Generator")
self.remove_air_defenses("SA17Generator")
self.remove_air_defenses("KS19Generator")
return self
def remove_aircraft(self, name):
for i in self.aircrafts:
if i.dcs_unit_type.id == name:
self.aircrafts.remove(i)
def remove_air_defenses(self, name):
for i in self.air_defenses:
if i == name:
self.air_defenses.remove(i)
def remove_vehicle(self, name):
for i in self.frontline_units:
if i.dcs_unit_type.id == name:
self.frontline_units.remove(i)
def load_ship(name: str) -> Optional[Type[ShipType]]:
if (ship := getattr(dcs.ships, name, None)) is not None:

View File

@@ -1,22 +1,24 @@
from game.dcs.aircrafttype import AircraftType
import itertools
import logging
import random
import sys
from datetime import date, datetime, timedelta
from enum import Enum
from typing import Any, Dict, List
from typing import Any, List
from dcs.action import Coalition
from dcs.mapping import Point
from dcs.task import CAP, CAS, PinpointStrike
from dcs.vehicles import AirDefence
from pydcs_extensions.a4ec.a4ec import A_4E_C
from faker import Faker
from game import db
from game.inventory import GlobalAircraftInventory
from game.models.game_stats import GameStats
from game.plugins import LuaPluginManager
from gen import naming
from gen import aircraft, naming
from gen.ato import AirTaskingOrder
from gen.conflictgen import Conflict
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
@@ -86,8 +88,8 @@ class TurnState(Enum):
class Game:
def __init__(
self,
player_name: str,
enemy_name: str,
player_faction: Faction,
enemy_faction: Faction,
theater: ConflictTheater,
start_date: datetime,
settings: Settings,
@@ -97,10 +99,10 @@ class Game:
self.settings = settings
self.events: List[Event] = []
self.theater = theater
self.player_name = player_name
self.player_country = db.FACTIONS[player_name].country
self.enemy_name = enemy_name
self.enemy_country = db.FACTIONS[enemy_name].country
self.player_faction = player_faction
self.player_country = player_faction.country
self.enemy_faction = enemy_faction
self.enemy_country = enemy_faction.country
# pass_turn() will be called when initialization is complete which will
# increment this to turn 0 before it reaches the player.
self.turn = -1
@@ -108,7 +110,7 @@ class Game:
self.date = date(start_date.year, start_date.month, start_date.day)
self.game_stats = GameStats()
self.game_stats.update(self)
self.ground_planners: Dict[int, GroundPlanner] = {}
self.ground_planners: dict[int, GroundPlanner] = {}
self.informations = []
self.informations.append(Information("Game Start", "-" * 40, 0))
# Culling Zones are for areas around points of interest that contain things we may not wish to cull.
@@ -149,7 +151,7 @@ class Game:
self.on_load(game_still_initializing=True)
def __getstate__(self) -> Dict[str, Any]:
def __getstate__(self) -> dict[str, Any]:
state = self.__dict__.copy()
# Avoid persisting any volatile types that can be deterministically
# recomputed on load for the sake of save compatibility.
@@ -161,7 +163,7 @@ class Game:
del state["red_faker"]
return state
def __setstate__(self, state: Dict[str, Any]) -> None:
def __setstate__(self, state: dict[str, Any]) -> None:
self.__dict__.update(state)
# Regenerate any state that was not persisted.
self.on_load()
@@ -201,14 +203,6 @@ class Game:
else:
self.enemy_country = "Russia"
@property
def player_faction(self) -> Faction:
return db.FACTIONS[self.player_name]
@property
def enemy_faction(self) -> Faction:
return db.FACTIONS[self.enemy_name]
def faction_for(self, player: bool) -> Faction:
if player:
return self.player_faction
@@ -248,8 +242,8 @@ class Game:
player_cp,
enemy_cp,
enemy_cp.position,
self.player_name,
self.enemy_name,
self.player_faction.name,
self.enemy_faction.name,
)
)
@@ -295,7 +289,7 @@ class Game:
return (
event
and event.attacker_name
and event.attacker_name == self.player_name
and event.attacker_name == self.player_faction.name
)
else:
raise RuntimeError(f"{event} was passed when an Event type was expected")
@@ -344,10 +338,10 @@ class Game:
self.blue_air_wing.replenish()
self.red_air_wing.replenish()
if not skipped and self.turn > 1:
if not skipped:
for cp in self.theater.player_points():
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
else:
elif self.turn > 1:
for cp in self.theater.player_points():
if not cp.is_carrier and not cp.is_lha:
cp.base.affect_strength(-PLAYER_BASE_STRENGTH_RECOVERY)

View File

@@ -77,8 +77,8 @@ class Operation:
yield Conflict(
cls.game.theater,
frontline,
cls.game.player_name,
cls.game.enemy_name,
cls.game.player_faction.name,
cls.game.enemy_faction.name,
cls.game.player_country,
cls.game.enemy_country,
frontline.position,
@@ -95,8 +95,8 @@ class Operation:
return Conflict(
cls.game.theater,
FrontLine(player_cp, enemy_cp),
cls.game.player_name,
cls.game.enemy_name,
cls.game.player_faction.name,
cls.game.enemy_faction.name,
cls.game.player_country,
cls.game.enemy_country,
mid_point,
@@ -389,8 +389,8 @@ class Operation:
player_cp = front_line.blue_cp
enemy_cp = front_line.red_cp
conflict = Conflict.frontline_cas_conflict(
cls.game.player_name,
cls.game.enemy_name,
cls.game.player_faction.name,
cls.game.enemy_faction.name,
cls.current_mission.country(cls.game.player_country),
cls.current_mission.country(cls.game.enemy_country),
front_line,

View File

@@ -2,16 +2,18 @@ import logging
import os
import pickle
import shutil
from pathlib import Path
from typing import Optional
_dcs_saved_game_folder: Optional[str] = None
_file_abs_path = None
def setup(user_folder: str):
global _dcs_saved_game_folder
_dcs_saved_game_folder = user_folder
_file_abs_path = os.path.join(base_path(), "default.liberation")
if not save_dir().exists():
save_dir().mkdir(parents=True)
def base_path() -> str:
@@ -20,16 +22,20 @@ def base_path() -> str:
return _dcs_saved_game_folder
def save_dir() -> Path:
return Path(base_path()) / "Liberation" / "Saves"
def _temporary_save_file() -> str:
return os.path.join(base_path(), "tmpsave.liberation")
return str(save_dir() / "tmpsave.liberation")
def _autosave_path() -> str:
return os.path.join(base_path(), "autosave.liberation")
return str(save_dir() / "autosave.liberation")
def mission_path_for(name: str) -> str:
return os.path.join(base_path(), "Missions", "{}".format(name))
return os.path.join(base_path(), "Missions", name)
def load_game(path):

View File

@@ -34,11 +34,14 @@ class Settings:
player_income_multiplier: float = 1.0
enemy_income_multiplier: float = 1.0
#: Feature flag for squadron limits.
enable_squadron_pilot_limits: bool = False
#: The maximum number of pilots a squadron can have at one time. Changing this after
#: the campaign has started will have no immediate effect; pilots already in the
#: squadron will not be removed if the limit is lowered and pilots will not be
#: immediately created if the limit is raised.
squadron_pilot_limit: int = 24
squadron_pilot_limit: int = 12
#: The number of pilots a squadron can replace per turn.
squadron_replenishment_rate: int = 4

View File

@@ -112,9 +112,19 @@ class Squadron:
return self.name
return f'{self.name} "{self.nickname}"'
@property
def pilot_limits_enabled(self) -> bool:
return self.game.settings.enable_squadron_pilot_limits
def claim_new_pilot_if_allowed(self) -> Optional[Pilot]:
if self.pilot_limits_enabled:
return None
self._recruit_pilots(1)
return self.available_pilots.pop()
def claim_available_pilot(self) -> Optional[Pilot]:
if not self.available_pilots:
return None
return self.claim_new_pilot_if_allowed()
# For opfor, so player/AI option is irrelevant.
if not self.player:
@@ -140,7 +150,7 @@ class Squadron:
# If they only *prefer* players and we're out of players, just return an AI
# pilot.
if not prefer_players:
return None
return self.claim_new_pilot_if_allowed()
return self.available_pilots.pop()
def claim_pilot(self, pilot: Pilot) -> None:
@@ -169,9 +179,12 @@ class Squadron:
self.available_pilots.extend(new_pilots)
def replenish_lost_pilots(self) -> None:
if not self.pilot_limits_enabled:
return
replenish_count = min(
self.game.settings.squadron_replenishment_rate,
self.number_of_unfilled_pilot_slots,
self._number_of_unfilled_pilot_slots,
)
if replenish_count > 0:
self._recruit_pilots(replenish_count)
@@ -213,20 +226,23 @@ class Squadron:
return len(self.current_roster)
@property
def number_of_unfilled_pilot_slots(self) -> int:
def _number_of_unfilled_pilot_slots(self) -> int:
return self.game.settings.squadron_pilot_limit - len(self.active_pilots)
@property
def number_of_available_pilots(self) -> int:
return len(self.available_pilots)
def can_provide_pilots(self, count: int) -> bool:
return not self.pilot_limits_enabled or self.number_of_available_pilots >= count
@property
def has_available_pilots(self) -> bool:
return bool(self.available_pilots)
return not self.pilot_limits_enabled or bool(self.available_pilots)
@property
def has_unfilled_pilot_slots(self) -> bool:
return self.number_of_unfilled_pilot_slots > 0
return not self.pilot_limits_enabled or self._number_of_unfilled_pilot_slots > 0
def can_auto_assign(self, task: FlightType) -> bool:
return task in self.auto_assignable_mission_types
@@ -368,6 +384,13 @@ class AirWing:
def squadrons_for(self, aircraft: AircraftType) -> Sequence[Squadron]:
return self.squadrons[aircraft]
def can_auto_plan(self, task: FlightType) -> bool:
try:
next(self.auto_assignable_for_task(task))
return True
except StopIteration:
return False
def auto_assignable_for_task(self, task: FlightType) -> Iterator[Squadron]:
for squadron in self.iter_squadrons():
if squadron.can_auto_assign(task):

View File

@@ -78,20 +78,33 @@ class GeneratorSettings:
no_enemy_navy: bool
@dataclass
class ModSettings:
a4_skyhawk: bool = False
f22_raptor: bool = False
hercules: bool = False
jas39_gripen: bool = False
su57_felon: bool = False
frenchpack: bool = False
high_digit_sams: bool = False
class GameGenerator:
def __init__(
self,
player: str,
enemy: str,
player: Faction,
enemy: Faction,
theater: ConflictTheater,
settings: Settings,
generator_settings: GeneratorSettings,
mod_settings: ModSettings,
) -> None:
self.player = player
self.enemy = enemy
self.theater = theater
self.settings = settings
self.generator_settings = generator_settings
self.mod_settings = mod_settings
def generate(self) -> Game:
with logged_duration("TGO population"):
@@ -99,8 +112,8 @@ class GameGenerator:
namegen.reset()
self.prepare_theater()
game = Game(
player_name=self.player,
enemy_name=self.enemy,
player_faction=self.player.apply_mod_settings(self.mod_settings),
enemy_faction=self.enemy.apply_mod_settings(self.mod_settings),
theater=self.theater,
start_date=self.generator_settings.start_date,
settings=self.settings,
@@ -159,9 +172,9 @@ class ControlPointGroundObjectGenerator:
@property
def faction_name(self) -> str:
if self.control_point.captured:
return self.game.player_name
return self.game.player_faction.name
else:
return self.game.enemy_name
return self.game.enemy_faction.name
@property
def faction(self) -> Faction:

View File

@@ -6,7 +6,6 @@ from collections import defaultdict
from dataclasses import dataclass, field
from functools import singledispatchmethod
from typing import (
Dict,
Generic,
Iterator,
List,
@@ -72,10 +71,18 @@ class TransferOrder:
player: bool = field(init=False)
#: The units being transferred.
units: Dict[GroundUnitType, int]
units: dict[GroundUnitType, int]
transport: Optional[Transport] = field(default=None)
def __str__(self) -> str:
"""Returns the text that should be displayed for the transfer."""
count = self.size
origin = self.origin.name
destination = self.destination.name
description = "Transfer" if self.player else "Enemy transfer"
return f"{description} of {count} units from {origin} to {destination}"
def __post_init__(self) -> None:
self.position = self.origin
self.player = self.origin.is_friendly(to_player=True)
@@ -91,12 +98,12 @@ class TransferOrder:
def kill_unit(self, unit_type: GroundUnitType) -> None:
if unit_type not in self.units or not self.units[unit_type]:
raise KeyError(f"{self.destination} has no {unit_type} remaining")
raise KeyError(f"{self} has no {unit_type} remaining")
self.units[unit_type] -= 1
@property
def size(self) -> int:
return sum(c for c in self.units.values())
return sum(self.units.values())
def iter_units(self) -> Iterator[GroundUnitType]:
for unit_type, count in self.units.items():
@@ -105,7 +112,7 @@ class TransferOrder:
@property
def completed(self) -> bool:
return self.destination == self.position or not self.units
return self.destination == self.position or not self.size
def disband_at(self, location: ControlPoint) -> None:
logging.info(f"Units halting at {location}.")
@@ -120,15 +127,17 @@ class TransferOrder:
)
return self.transport.destination
def proceed(self) -> None:
if self.transport is None:
return
def find_escape_route(self) -> Optional[ControlPoint]:
if self.transport is not None:
return self.transport.find_escape_route()
return None
def proceed(self) -> None:
if not self.destination.is_friendly(self.player):
logging.info(f"Transfer destination {self.destination} was captured.")
if self.position.is_friendly(self.player):
self.disband_at(self.position)
elif (escape_route := self.transport.find_escape_route()) is not None:
elif (escape_route := self.find_escape_route()) is not None:
self.disband_at(escape_route)
else:
logging.info(
@@ -138,6 +147,9 @@ class TransferOrder:
self.kill_all()
return
if self.transport is None:
return
self.position = self.next_stop
self.transport = None
@@ -156,7 +168,7 @@ class Airlift(Transport):
self.flight = flight
@property
def units(self) -> Dict[GroundUnitType, int]:
def units(self) -> dict[GroundUnitType, int]:
return self.transfer.units
@property
@@ -261,8 +273,12 @@ class AirliftPlanner:
required,
available_aircraft,
squadron.aircraft.dcs_unit_type.group_size_max,
squadron.number_of_available_pilots,
)
# TODO: Use number_of_available_pilots directly once feature flag is gone.
# The number of currently available pilots is not relevant when pilot limits
# are disabled.
if not squadron.can_provide_pilots(flight_size):
flight_size = squadron.number_of_available_pilots
capacity = flight_size * capacity_each
if capacity < self.transfer.size:
@@ -334,11 +350,11 @@ class MultiGroupTransport(MissionTarget, Transport):
@property
def size(self) -> int:
return sum(sum(t.units.values()) for t in self.transfers)
return sum(t.size for t in self.transfers)
@property
def units(self) -> dict[GroundUnitType, int]:
units: Dict[GroundUnitType, int] = defaultdict(int)
units: dict[GroundUnitType, int] = defaultdict(int)
for transfer in self.transfers:
for unit_type, count in transfer.units.items():
units[unit_type] += count
@@ -414,8 +430,8 @@ TransportType = TypeVar("TransportType", bound=MultiGroupTransport)
class TransportMap(Generic[TransportType]):
def __init__(self) -> None:
# Dict of origin -> destination -> transport.
self.transports: Dict[
ControlPoint, Dict[ControlPoint, TransportType]
self.transports: dict[
ControlPoint, dict[ControlPoint, TransportType]
] = defaultdict(dict)
def create_transport(
@@ -592,7 +608,10 @@ class PendingTransfers:
def order_airlift_assets(self) -> None:
for control_point in self.game.theater.controlpoints:
self.order_airlift_assets_at(control_point)
if self.game.air_wing_for(control_point.captured).can_auto_plan(
FlightType.TRANSPORT
):
self.order_airlift_assets_at(control_point)
@staticmethod
def desired_airlift_capacity(control_point: ControlPoint) -> int:

View File

@@ -3,7 +3,7 @@ from __future__ import annotations
import logging
from collections import defaultdict
from dataclasses import dataclass
from typing import Dict, Optional, TYPE_CHECKING, Any
from typing import Optional, TYPE_CHECKING, Any
from game.theater import ControlPoint
from .dcs.groundunittype import GroundUnitType
@@ -28,16 +28,16 @@ class PendingUnitDeliveries:
self.destination = destination
# Maps unit type to order quantity.
self.units: Dict[UnitType, int] = defaultdict(int)
self.units: dict[UnitType, int] = defaultdict(int)
def __str__(self) -> str:
return f"Pending delivery to {self.destination}"
def order(self, units: Dict[UnitType, int]) -> None:
def order(self, units: dict[UnitType, int]) -> None:
for k, v in units.items():
self.units[k] += v
def sell(self, units: Dict[UnitType, int]) -> None:
def sell(self, units: dict[UnitType, int]) -> None:
for k, v in units.items():
self.units[k] -= v
@@ -45,7 +45,15 @@ class PendingUnitDeliveries:
self.refund(game, self.units)
self.units = defaultdict(int)
def refund(self, game: Game, units: Dict[UnitType, int]) -> None:
def refund_ground_units(self, game: Game) -> None:
ground_units: dict[UnitType[Any], int] = {
u: self.units[u] for u in self.units.keys() if isinstance(u, GroundUnitType)
}
self.refund(game, ground_units)
for gu in ground_units.keys():
del self.units[gu]
def refund(self, game: Game, units: dict[UnitType, int]) -> None:
for unit_type, count in units.items():
logging.info(f"Refunding {count} {unit_type} at {self.destination.name}")
game.adjust_budget(
@@ -69,12 +77,11 @@ class PendingUnitDeliveries:
f"{self.destination.name} lost its source for ground unit "
"reinforcements. Refunding purchase price."
)
self.refund_all(game)
return
self.refund_ground_units(game)
bought_units: Dict[UnitType, int] = {}
units_needing_transfer: Dict[GroundUnitType, int] = {}
sold_units: Dict[UnitType, int] = {}
bought_units: dict[UnitType, int] = {}
units_needing_transfer: dict[GroundUnitType, int] = {}
sold_units: dict[UnitType, int] = {}
for unit_type, count in self.units.items():
coalition = "Ally" if self.destination.captured else "Enemy"
d: dict[Any, int]
@@ -102,11 +109,16 @@ class PendingUnitDeliveries:
self.destination.base.commit_losses(sold_units)
if units_needing_transfer:
if ground_unit_source is None:
raise RuntimeError(
f"ground unit source could not be found for {self.destination} but still tried to "
f"transfer units to there"
)
ground_unit_source.base.commission_units(units_needing_transfer)
self.create_transfer(game, ground_unit_source, units_needing_transfer)
def create_transfer(
self, game: Game, source: ControlPoint, units: Dict[GroundUnitType, int]
self, game: Game, source: ControlPoint, units: dict[GroundUnitType, int]
) -> None:
game.transfers.new_transfer(TransferOrder(source, self.destination, units))

View File

@@ -90,4 +90,10 @@ VERSION = _build_version_string()
#:
#: Version 6.1
#: * Support for new Syrian airfields in DCS 2.7.2.7910.1 (Cyprus update).
CAMPAIGN_FORMAT_VERSION = (6, 1)
#:
#: Version 7.0
#: * DCS 2.7.2.7910.1 (Cyprus update) changed the IDs of scenery strike targets. Any
#: mission using map buildings as strike targets must check and potentially recreate
#: all those objectives. This definitely affects all Syria campaigns, other maps are
#: not yet verified.
CAMPAIGN_FORMAT_VERSION = (7, 0)

View File

@@ -26,7 +26,6 @@ from dcs.planes import (
Su_33,
Tu_22M3,
)
from dcs.planes import IL_78M
from dcs.point import MovingPoint, PointAction
from dcs.task import (
AWACS,
@@ -66,7 +65,6 @@ from dcs.unitgroup import FlyingGroup, ShipGroup, StaticGroup
from dcs.unittype import FlyingType
from game import db
from game.data.cap_capabilities_db import GUNFIGHTERS
from game.data.weapons import Pylon
from game.dcs.aircrafttype import AircraftType
from game.factions.faction import Faction
@@ -840,7 +838,7 @@ class AircraftConflictGenerator:
group.task = CAP.name
self._setup_group(group, package, flight, dynamic_runways)
if flight.unit_type not in GUNFIGHTERS:
if not flight.unit_type.gunfighter:
ammo_type = OptRTBOnOutOfAmmo.Values.AAM
else:
ammo_type = OptRTBOnOutOfAmmo.Values.Cannon
@@ -857,7 +855,7 @@ class AircraftConflictGenerator:
group.task = FighterSweep.name
self._setup_group(group, package, flight, dynamic_runways)
if flight.unit_type not in GUNFIGHTERS:
if not flight.unit_type.gunfighter:
ammo_type = OptRTBOnOutOfAmmo.Values.AAM
else:
ammo_type = OptRTBOnOutOfAmmo.Values.Cannon
@@ -1182,7 +1180,7 @@ class AircraftConflictGenerator:
# under the current flight plans.
# TODO: Make this smarter, it currently selects a random unit in the group for target,
# this could be updated to make it pick the "best" two targets in the group.
if flight.unit_type is AJS37 and flight.client_count:
if flight.unit_type.dcs_unit_type is AJS37 and flight.client_count:
viggen_target_points = [
(idx, point)
for idx, point in enumerate(filtered_points)
@@ -1346,9 +1344,10 @@ class PydcsWaypointBuilder:
"""Viggen player aircraft consider any waypoint with a TOT set to be a target ("M") waypoint.
If the flight is a player controlled Viggen flight, no TOT should be set on any waypoint except actual target waypoints.
"""
if (self.flight.client_count > 0 and self.flight.unit_type == AJS37) and (
self.waypoint.waypoint_type not in TARGET_WAYPOINTS
):
if (
self.flight.client_count > 0
and self.flight.unit_type.dcs_unit_type == AJS37
) and (self.waypoint.waypoint_type not in TARGET_WAYPOINTS):
return True
else:
return False
@@ -1761,7 +1760,7 @@ class RaceTrackBuilder(PydcsWaypointBuilder):
def configure_refueling_actions(self, waypoint: MovingPoint) -> None:
waypoint.add_task(Tanker())
if self.flight.unit_type != IL_78M:
if self.flight.unit_type.dcs_unit_type.tacan:
tanker_info = self.air_support.tankers[-1]
tacan = tanker_info.tacan
tacan_callsign = {

View File

@@ -153,6 +153,8 @@ class Conflict:
if theater.is_on_land(pos):
return pos
pos = initial.point_from_heading(opposite_heading(heading), distance)
if theater.is_on_land(pos):
return pos
if coerce:
pos = theater.nearest_land_pos(initial)
return pos

View File

@@ -178,7 +178,7 @@ class AircraftAllocator:
aircraft, task
)
for squadron in squadrons:
if squadron.number_of_available_pilots >= flight.num_aircraft:
if squadron.can_provide_pilots(flight.num_aircraft):
inventory.remove_aircraft(aircraft, flight.num_aircraft)
return airfield, squadron
return None
@@ -604,27 +604,35 @@ class CoalitionMissionPlanner:
also possible for the player to exclude mission types from their squadron
designs.
"""
all_compatible = aircraft_for_task(mission_type)
for squadron in self.game.air_wing_for(self.is_player).iter_squadrons():
if (
squadron.aircraft in all_compatible
and mission_type in squadron.auto_assignable_mission_types
):
return True
return False
return self.game.air_wing_for(self.is_player).can_auto_plan(mission_type)
@property
def oca_aircraft_plannable(self) -> bool:
return (
self.air_wing_can_plan(FlightType.OCA_AIRCRAFT)
and self.game.settings.default_start_type == "Cold"
def critical_missions(self) -> Iterator[ProposedMission]:
"""Identifies the most important missions to plan this turn.
Non-critical missions that cannot be fulfilled will create purchase
orders for the next turn. Critical missions will create a purchase order
unless the mission can be doubly fulfilled. In other words, the AI will
attempt to have *double* the aircraft it needs for these missions to
ensure that they can be planned again next turn even if all aircraft are
eliminated this turn.
"""
# Find farthest, friendly CP for AEWC.
yield ProposedMission(
self.objective_finder.farthest_friendly_control_point(),
[ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)],
# Supports all the early CAP flights, so should be in the air ASAP.
asap=True,
)
yield ProposedMission(
self.objective_finder.closest_friendly_control_point(),
[ProposedFlight(FlightType.REFUELING, 1, self.MAX_TANKER_RANGE)],
)
def propose_barcap(self) -> Iterator[ProposedMission]:
# Find friendly CPs within 100 nmi from an enemy airfield, plan CAP.
for cp in self.objective_finder.vulnerable_control_points():
# Plan CAP in such a way, that it is established during the whole desired
# mission length.
# Plan CAP in such a way, that it is established during the whole desired mission length
for _ in range(
0,
int(self.game.settings.desired_player_mission_duration.total_seconds()),
@@ -637,31 +645,36 @@ class CoalitionMissionPlanner:
],
)
def propose_cas(self) -> Iterator[ProposedMission]:
# Find front lines, plan CAS.
for front_line in self.objective_finder.front_lines():
flights = [ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE)]
if self.air_wing_can_plan(FlightType.TARCAP):
# This is *not* an escort because front lines don't create a threat
# zone. Generating threat zones from front lines causes the front
# line to push back BARCAPs as it gets closer to the base. While
# front lines do have the same problem of potentially pulling
# BARCAPs off bases to engage a front line TARCAP, that's probably
# the one time where we do want that.
#
# TODO: Use intercepts and extra TARCAPs to cover bases near fronts.
# We don't have intercept missions yet so this isn't something we
# can do today, but we should probably return to having the front
# line project a threat zone (so that strike missions will route
# around it) and instead *not plan* a BARCAP at bases near the
# front, since there isn't a place to put a barrier. Instead, the
# aircraft that would have been a BARCAP could be used as additional
# interceptors and TARCAPs which will defend the base but won't be
# trying to avoid front line contacts.
flights.append(ProposedFlight(FlightType.TARCAP, 2, self.MAX_CAP_RANGE))
yield ProposedMission(front_line, flights)
yield ProposedMission(
front_line,
[
ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE),
# This is *not* an escort because front lines don't create a threat
# zone. Generating threat zones from front lines causes the front
# line to push back BARCAPs as it gets closer to the base. While
# front lines do have the same problem of potentially pulling
# BARCAPs off bases to engage a front line TARCAP, that's probably
# the one time where we do want that.
#
# TODO: Use intercepts and extra TARCAPs to cover bases near fronts.
# We don't have intercept missions yet so this isn't something we
# can do today, but we should probably return to having the front
# line project a threat zone (so that strike missions will route
# around it) and instead *not plan* a BARCAP at bases near the
# front, since there isn't a place to put a barrier. Instead, the
# aircraft that would have been a BARCAP could be used as additional
# interceptors and TARCAPs which will defend the base but won't be
# trying to avoid front line contacts.
ProposedFlight(FlightType.TARCAP, 2, self.MAX_CAP_RANGE),
],
)
def propose_missions(self) -> Iterator[ProposedMission]:
"""Identifies and iterates over potential mission in priority order."""
yield from self.critical_missions()
def propose_dead(self) -> Iterator[ProposedMission]:
# Find enemy SAM sites with ranges that cover friendly CPs, front lines,
# or objects, plan DEAD.
# Find enemy SAM sites with ranges that extend to within 50 nmi of
@@ -686,10 +699,7 @@ class CoalitionMissionPlanner:
else:
flights.append(
ProposedFlight(
FlightType.SEAD_ESCORT,
2,
self.MAX_SEAD_RANGE,
EscortType.Sead,
FlightType.SEAD_ESCORT, 2, self.MAX_SEAD_RANGE, EscortType.Sead
)
)
# TODO: Max escort range.
@@ -700,7 +710,6 @@ class CoalitionMissionPlanner:
)
yield ProposedMission(sam, flights)
def propose_convoy_interdiction(self) -> Iterator[ProposedMission]:
# These will only rarely get planned. When a convoy is travelling multiple legs,
# they're targetable after the first leg. The reason for this is that
# procurement happens *after* mission planning so that the missions that could
@@ -729,7 +738,6 @@ class CoalitionMissionPlanner:
],
)
def propose_shipping_interdiction(self) -> Iterator[ProposedMission]:
for ship in self.objective_finder.cargo_ships():
yield ProposedMission(
ship,
@@ -745,7 +753,6 @@ class CoalitionMissionPlanner:
],
)
def propose_naval_strikes(self) -> Iterator[ProposedMission]:
for group in self.objective_finder.threatening_ships():
yield ProposedMission(
group,
@@ -761,7 +768,6 @@ class CoalitionMissionPlanner:
],
)
def propose_bai(self) -> Iterator[ProposedMission]:
for group in self.objective_finder.threatening_vehicle_groups():
yield ProposedMission(
group,
@@ -777,25 +783,16 @@ class CoalitionMissionPlanner:
],
)
def propose_oca_strikes(self) -> Iterator[ProposedMission]:
for target in self.objective_finder.oca_targets(min_aircraft=20):
flights = []
if self.air_wing_can_plan(FlightType.OCA_RUNWAY):
flights.append(
ProposedFlight(FlightType.OCA_RUNWAY, 2, self.MAX_OCA_RANGE)
)
if self.oca_aircraft_plannable:
flights = [
ProposedFlight(FlightType.OCA_RUNWAY, 2, self.MAX_OCA_RANGE),
]
if self.game.settings.default_start_type == "Cold":
# Only schedule if the default start type is Cold. If the player
# has set anything else there are no targets to hit.
flights.append(
ProposedFlight(FlightType.OCA_AIRCRAFT, 2, self.MAX_OCA_RANGE)
)
if not flights:
raise RuntimeError(
"Attempted planning of OCA strikes but neither OCA/Runway nor "
f"OCA/Aircraft are plannable for {self.faction.name} with the "
"current game settings."
)
flights.extend(
[
# TODO: Max escort range.
@@ -809,7 +806,7 @@ class CoalitionMissionPlanner:
)
yield ProposedMission(target, flights)
def propose_building_strikes(self) -> Iterator[ProposedMission]:
# Plan strike missions.
for target in self.objective_finder.strike_targets():
yield ProposedMission(
target,
@@ -828,48 +825,6 @@ class CoalitionMissionPlanner:
],
)
def propose_missions(self) -> Iterator[ProposedMission]:
"""Identifies and iterates over potential mission in priority order."""
# Find farthest, friendly CP for AEWC.
if self.air_wing_can_plan(FlightType.AEWC):
yield ProposedMission(
self.objective_finder.farthest_friendly_control_point(),
[ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)],
# Supports all the early CAP flights, so should be in the air ASAP.
asap=True,
)
if self.air_wing_can_plan(FlightType.REFUELING):
yield ProposedMission(
self.objective_finder.closest_friendly_control_point(),
[ProposedFlight(FlightType.REFUELING, 1, self.MAX_TANKER_RANGE)],
)
if self.air_wing_can_plan(FlightType.BARCAP):
yield from self.propose_barcap()
if self.air_wing_can_plan(FlightType.CAS):
yield from self.propose_cas()
if self.air_wing_can_plan(FlightType.DEAD):
yield from self.propose_dead()
if self.air_wing_can_plan(FlightType.BAI):
yield from self.propose_convoy_interdiction()
if self.air_wing_can_plan(FlightType.ANTISHIP):
yield from self.propose_shipping_interdiction()
yield from self.propose_naval_strikes()
if self.air_wing_can_plan(FlightType.BAI):
yield from self.propose_bai()
if self.air_wing_can_plan(FlightType.OCA_RUNWAY) or self.oca_aircraft_plannable:
yield from self.propose_oca_strikes()
if self.air_wing_can_plan(FlightType.STRIKE):
yield from self.propose_building_strikes()
def plan_missions(self) -> None:
"""Identifies and plans mission for the turn."""
player = "Blue" if self.is_player else "Red"
@@ -878,6 +833,11 @@ class CoalitionMissionPlanner:
for proposed_mission in self.propose_missions():
self.plan_mission(proposed_mission, tracer)
with logged_duration(f"{player} reserve mission planning"):
with MultiEventTracer() as tracer:
for critical_mission in self.critical_missions():
self.plan_mission(critical_mission, tracer, reserves=True)
with logged_duration(f"{player} mission scheduling"):
self.stagger_missions()

View File

@@ -111,7 +111,6 @@ from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.f22a.f22a import F_22A
from pydcs_extensions.hercules.hercules import Hercules
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
from pydcs_extensions.mb339.mb339 import MB_339PAN
from pydcs_extensions.su57.su57 import Su_57
# All aircraft lists are in priority order. Aircraft higher in the list will be
@@ -219,7 +218,6 @@ CAS_CAPABLE = [
F_5E_3,
F_86F_Sabre,
C_101CC,
MB_339PAN,
L_39ZA,
A_20G,
Ju_88A4,
@@ -332,7 +330,6 @@ STRIKE_CAPABLE = [
MiG_15bis,
F_5E_3,
F_86F_Sabre,
MB_339PAN,
C_101CC,
L_39ZA,
B_17G,

View File

@@ -16,17 +16,6 @@ from functools import cached_property
from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple
from dcs.mapping import Point
from dcs.planes import (
E_3A,
E_2C,
A_50,
IL_78M,
KC130,
KC135MPRS,
KC_135,
KJ_2000,
S_3B_Tanker,
)
from dcs.unit import Unit
from shapely.geometry import Point as ShapelyPoint
@@ -38,8 +27,9 @@ from game.theater import (
MissionTarget,
SamGroundObject,
TheaterGroundObject,
NavalControlPoint,
)
from game.theater.theatergroundobject import EwrGroundObject
from game.theater.theatergroundobject import EwrGroundObject, NavalGroundObject
from game.utils import Distance, Speed, feet, meters, nautical_miles, knots
from .closestairfields import ObjectiveDistanceCache
from .flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
@@ -1092,15 +1082,8 @@ class FlightPlanBuilder:
orbit_location = self.aewc_orbit(location)
# As high as possible to maximize detection and on-station time.
if flight.unit_type == E_2C:
patrol_alt = feet(30000)
elif flight.unit_type == E_3A:
patrol_alt = feet(35000)
elif flight.unit_type == A_50:
patrol_alt = feet(33000)
elif flight.unit_type == KJ_2000:
patrol_alt = feet(40000)
if flight.unit_type.patrol_altitude is not None:
patrol_alt = flight.unit_type.patrol_altitude
else:
patrol_alt = feet(25000)
@@ -1164,12 +1147,9 @@ class FlightPlanBuilder:
from game.transfers import CargoShip
if isinstance(location, ControlPoint):
if not location.is_fleet:
raise InvalidObjectiveLocation(flight.flight_type, location)
# The first group generated will be the carrier group itself.
targets = self.anti_ship_targets_for_tgo(location.ground_objects[0])
elif isinstance(location, TheaterGroundObject):
if isinstance(location, NavalControlPoint):
targets = self.anti_ship_targets_for_tgo(location.find_main_tgo())
elif isinstance(location, NavalGroundObject):
targets = self.anti_ship_targets_for_tgo(location)
elif isinstance(location, CargoShip):
targets = [StrikeTarget(location.name, location)]
@@ -1680,31 +1660,17 @@ class FlightPlanBuilder:
builder = WaypointBuilder(flight, self.game, self.is_player)
tanker_type = flight.unit_type
if tanker_type is KC_135:
# ~300 knots IAS.
speed = knots(445)
altitude = feet(24000)
elif tanker_type is KC135MPRS:
# ~300 knots IAS.
speed = knots(440)
altitude = feet(23000)
elif tanker_type is KC130:
# ~210 knots IAS, roughly the max for the KC-130 at altitude.
speed = knots(370)
altitude = feet(22000)
elif tanker_type is S_3B_Tanker:
# ~265 knots IAS.
speed = knots(320)
altitude = feet(12000)
elif tanker_type is IL_78M:
# ~280 knots IAS.
speed = knots(400)
altitude = feet(21000)
if tanker_type.patrol_altitude is not None:
altitude = tanker_type.patrol_altitude
else:
# ~280 knots IAS.
speed = knots(400)
altitude = feet(21000)
if tanker_type.patrol_speed is not None:
speed = tanker_type.patrol_speed
else:
# ~280 knots IAS at 21000.
speed = knots(400)
racetrack = builder.race_track(racetrack_start, racetrack_end, altitude)
return RefuelingFlightPlan(

View File

@@ -256,8 +256,8 @@ class _53T2(unittype.VehicleType):
id = "AA20"
name = "53T2"
detection_range = 5000
threat_range = 2000
air_weapon_dist = 2000
threat_range = 4000
air_weapon_dist = 4000
class TRM_2000_53T2(unittype.VehicleType):
@@ -278,6 +278,71 @@ class TRM_2000_PAMELA(unittype.VehicleType):
eplrs = True
class Leclerc_Serie_XXI_Desert(unittype.VehicleType):
id = "Leclerc_XXI_Desert"
name = "Leclerc Série XXI Désert"
detection_range = 0
threat_range = 4000
air_weapon_dist = 4000
class AMX_13_75mm(unittype.VehicleType):
id = "AMX1375"
name = "AMX-13 75mm"
detection_range = 0
threat_range = 3500
air_weapon_dist = 3500
class AMX_13_90mm(unittype.VehicleType):
id = "AMX1390"
name = "AMX-13 90mm"
detection_range = 0
threat_range = 3500
air_weapon_dist = 3500
class VBCI(unittype.VehicleType):
id = "VBCI"
name = "VBCI"
detection_range = 0
threat_range = 3500
air_weapon_dist = 3500
eplrs = True
class Char_T_62(unittype.VehicleType):
id = "T62"
name = "Char T-62"
detection_range = 0
threat_range = 4000
air_weapon_dist = 4000
class Char_T_64BV(unittype.VehicleType):
id = "T64BV"
name = "Char T-64BV"
detection_range = 0
threat_range = 4000
air_weapon_dist = 4000
class Char_T_72A(unittype.VehicleType):
id = "T72M"
name = "Char T-72A"
detection_range = 0
threat_range = 4000
air_weapon_dist = 4000
class KORNET_ATGM(unittype.VehicleType):
id = "KORNET"
name = "KORNET ATGM"
detection_range = 0
threat_range = 0
air_weapon_dist = 0
## INFANTRY

View File

@@ -1,539 +0,0 @@
from enum import Enum
from dcs import task
from dcs.planes import PlaneType
from dcs.weapons_data import Weapons
from pydcs_extensions.weapon_injector import inject_weapons
class MB_339PAN_Weapons:
ARF8M3_TP = {"clsid": "{ARF8M3_TP}", "name": "ARF8M3 TP", "weight": None}
BRD_4_250_4_MK_76_2_ARF_8M3TP_ = {
"clsid": "{BRD-4-250}",
"name": "BRD-4-250(4*MK.76+2*ARF-8M3TP)",
"weight": 137.6,
}
Color_Oil_Tank = {"clsid": "{COLOR-TANK}", "name": "Color Oil Tank", "weight": 183}
Empty_Pylon = {"clsid": "{VOID-PYLON-MB339A}", "name": "Empty Pylon", "weight": 20}
Fuel_Tank_330lt = {
"clsid": "{FUEL-SUBAL_TANK-330}",
"name": "Fuel Tank 330lt",
"weight": 315,
}
GunPod_AN_M3 = {"clsid": "{MB339-AN-M3_L}", "name": "GunPod AN/M3", "weight": 75}
GunPod_AN_M3_ = {"clsid": "{MB339-AN-M3_R}", "name": "GunPod AN/M3", "weight": 75}
GunPod_DEFA553 = {
"clsid": "{MB339-DEFA553_L}",
"name": "GunPod DEFA553",
"weight": 190,
}
GunPod_DEFA553_ = {
"clsid": "{MB339-DEFA553_R}",
"name": "GunPod DEFA553",
"weight": 190,
}
LAU_10___4_ZUNI_MK_71___ = {
"clsid": "{LAU-10}",
"name": "LAU-10 - 4 ZUNI MK 71",
"weight": 308,
}
LR_25___25_ARF_8M3_API_ = {
"clsid": "{LR-25API}",
"name": "LR-25 - 25 ARF/8M3(API)",
"weight": 141,
}
LR_25___25_ARF_8M3_HEI_ = {
"clsid": "{LR-25HEI}",
"name": "LR-25 - 25 ARF/8M3(HEI)",
"weight": 161,
}
MAK79_2_MK_20 = {"clsid": "{MAK79_MK20 2L}", "name": "MAK79 2 MK-20", "weight": 464}
MAK79_2_MK_20_ = {
"clsid": "{MAK79_MK20 2R}",
"name": "MAK79 2 MK-20",
"weight": 464,
}
MAK79_MK_20 = {"clsid": "{MAK79_MK20 1R}", "name": "MAK79 MK-20", "weight": 232}
MAK79_MK_20_ = {"clsid": "{MAK79_MK20 1L}", "name": "MAK79 MK-20", "weight": 232}
MB339_Black_Smoke = {
"clsid": "{SMOKE-BLACK-MB339}",
"name": "MB339 Black Smoke",
"weight": 1,
}
MB339_Green_Smoke = {
"clsid": "{SMOKE-GREEN-MB339}",
"name": "MB339 Green Smoke",
"weight": 1,
}
MB339_ORANGE_Smoke = {
"clsid": "{SMOKE-ORANGE-MB339}",
"name": "MB339 ORANGE Smoke",
"weight": 1,
}
MB339_Red_Smoke = {
"clsid": "{SMOKE-RED-MB339}",
"name": "MB339 Red Smoke",
"weight": 1,
}
MB339_White_Smoke = {
"clsid": "{SMOKE-WHITE-MB339}",
"name": "MB339 White Smoke",
"weight": 1,
}
MB339_YELLOW_Smoke = {
"clsid": "{SMOKE-YELLOW-MB339}",
"name": "MB339 YELLOW Smoke",
"weight": 1,
}
MK76 = {"clsid": "{MK76}", "name": "MK76", "weight": 11.3}
Tip_Fuel_Tank_500lt = {
"clsid": "{FUEL-TIP-TANK-500-L}",
"name": "Tip Fuel Tank 500lt",
"weight": 471,
}
Tip_Fuel_Tank_500lt_ = {
"clsid": "{FUEL-TIP-TANK-500-R}",
"name": "Tip Fuel Tank 500lt",
"weight": 471,
}
Tip_Fuel_Tank_Ellittici_320lt = {
"clsid": "{FUEL-TIP-ELLITTIC-L}",
"name": "Tip Fuel Tank Ellittici 320lt",
"weight": 314.2,
}
Tip_Fuel_Tank_Ellittici_320lt_ = {
"clsid": "{FUEL-TIP-ELLITTIC-R}",
"name": "Tip Fuel Tank Ellittici 320lt",
"weight": 314.2,
}
inject_weapons(MB_339PAN_Weapons)
class MB_339PAN(PlaneType):
id = "MB-339PAN"
flyable = True
height = 4.77
width = 10.5
length = 12.13
fuel_max = 626
max_speed = 763.2
category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F}
radio_frequency = 124
panel_radio = {
1: {
"channels": {
1: 225,
2: 258,
4: 270,
8: 257,
16: 252,
17: 268,
9: 253,
18: 269,
5: 255,
10: 263,
20: 269,
11: 267,
3: 260,
6: 259,
12: 254,
13: 264,
7: 262,
14: 266,
19: 268,
15: 265,
},
},
2: {
"channels": {
1: 225,
2: 258,
4: 270,
8: 257,
16: 252,
17: 268,
9: 253,
18: 269,
5: 255,
10: 263,
20: 269,
30: 263,
21: 225,
11: 267,
22: 258,
3: 260,
6: 259,
12: 254,
24: 270,
19: 268,
25: 255,
13: 264,
26: 259,
27: 262,
7: 262,
14: 266,
28: 257,
23: 260,
29: 253,
15: 265,
},
},
}
property_defaults = {
"SoloFlight": False,
"NetCrewControlPriority": 1,
}
class Properties:
class SoloFlight:
id = "SoloFlight"
class NetCrewControlPriority:
id = "NetCrewControlPriority"
class Values:
Pilot = 0
Instructor = 1
Ask_Always = -1
Equally_Responsible = -2
class Liveries:
class Georgia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Syria(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Finland(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Australia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Germany(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class SaudiArabia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Israel(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Croatia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class CzechRepublic(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Norway(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Romania(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Spain(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Ukraine(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Belgium(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Slovakia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Greece(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class UK(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Insurgents(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Hungary(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class France(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Abkhazia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Russia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Sweden(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Austria(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Switzerland(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Italy(Enum):
MB339PAN__Frecce_Tricolori = "MB339PAN 'Frecce Tricolori'"
MB339A__SVBIA____FACTORY = "MB339A 'SVBIA' - FACTORY"
MB339A__61BRIGATA____CAMO = "MB339A '61BRIGATA' - CAMO"
MB339A__61STORMO____CAMO = "MB339A '61STORMO' - CAMO"
MB339A__61STORMO____GREY = "MB339A '61STORMO' - GREY"
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class SouthOssetia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class SouthKorea(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Iran(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class China(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Pakistan(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Belarus(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class NorthKorea(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Iraq(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Kazakhstan(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Bulgaria(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Serbia(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class India(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class USAFAggressors(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class USA(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Denmark(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Egypt(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Canada(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class TheNetherlands(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Turkey(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Japan(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Poland(Enum):
MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa"
MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band"
MB339__Factory = "MB339 'Factory'"
class Pylon1:
Tip_Fuel_Tank_500lt = (1, MB_339PAN_Weapons.Tip_Fuel_Tank_500lt)
Tip_Fuel_Tank_Ellittici_320lt = (
1,
MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt,
)
class Pylon2:
Empty_Pylon = (2, MB_339PAN_Weapons.Empty_Pylon)
LR_25___25_ARF_8M3_HEI_ = (2, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
LR_25___25_ARF_8M3_API_ = (2, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
Mk_82 = (2, Weapons.Mk_82)
Matra_Type_155_Rocket_Pod = (2, Weapons.Matra_Type_155_Rocket_Pod)
class Pylon3:
Fuel_Tank_330lt = (3, MB_339PAN_Weapons.Fuel_Tank_330lt)
Empty_Pylon = (3, MB_339PAN_Weapons.Empty_Pylon)
LR_25___25_ARF_8M3_HEI_ = (3, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
LR_25___25_ARF_8M3_API_ = (3, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
Mk_82 = (3, Weapons.Mk_82)
LAU_10___4_ZUNI_MK_71___ = (3, MB_339PAN_Weapons.LAU_10___4_ZUNI_MK_71___)
BRD_4_250_4_MK_76_2_ARF_8M3TP_ = (
3,
MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_,
)
Matra_Type_155_Rocket_Pod = (3, Weapons.Matra_Type_155_Rocket_Pod)
class Pylon4:
Color_Oil_Tank = (4, MB_339PAN_Weapons.Color_Oil_Tank)
Empty_Pylon = (4, MB_339PAN_Weapons.Empty_Pylon)
GunPod_AN_M3 = (4, MB_339PAN_Weapons.GunPod_AN_M3)
GunPod_DEFA553 = (4, MB_339PAN_Weapons.GunPod_DEFA553)
LR_25___25_ARF_8M3_HEI_ = (4, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
LR_25___25_ARF_8M3_API_ = (4, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
Mk_82 = (4, Weapons.Mk_82)
Matra_Type_155_Rocket_Pod = (4, Weapons.Matra_Type_155_Rocket_Pod)
class Pylon5:
MB339_Red_Smoke = (5, MB_339PAN_Weapons.MB339_Red_Smoke)
MB339_Green_Smoke = (5, MB_339PAN_Weapons.MB339_Green_Smoke)
MB339_YELLOW_Smoke = (5, MB_339PAN_Weapons.MB339_YELLOW_Smoke)
MB339_ORANGE_Smoke = (5, MB_339PAN_Weapons.MB339_ORANGE_Smoke)
MB339_Black_Smoke = (5, MB_339PAN_Weapons.MB339_Black_Smoke)
class Pylon6:
MB339_White_Smoke = (6, MB_339PAN_Weapons.MB339_White_Smoke)
class Pylon7:
Color_Oil_Tank = (7, MB_339PAN_Weapons.Color_Oil_Tank)
Empty_Pylon = (7, MB_339PAN_Weapons.Empty_Pylon)
GunPod_AN_M3_ = (7, MB_339PAN_Weapons.GunPod_AN_M3_)
GunPod_DEFA553_ = (7, MB_339PAN_Weapons.GunPod_DEFA553_)
LR_25___25_ARF_8M3_HEI_ = (7, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
LR_25___25_ARF_8M3_API_ = (7, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
Mk_82 = (7, Weapons.Mk_82)
Matra_Type_155_Rocket_Pod = (7, Weapons.Matra_Type_155_Rocket_Pod)
class Pylon8:
Fuel_Tank_330lt = (8, MB_339PAN_Weapons.Fuel_Tank_330lt)
Empty_Pylon = (8, MB_339PAN_Weapons.Empty_Pylon)
LR_25___25_ARF_8M3_HEI_ = (8, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
LR_25___25_ARF_8M3_API_ = (8, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
Mk_82 = (8, Weapons.Mk_82)
LAU_10___4_ZUNI_MK_71___ = (8, MB_339PAN_Weapons.LAU_10___4_ZUNI_MK_71___)
Matra_Type_155_Rocket_Pod = (8, Weapons.Matra_Type_155_Rocket_Pod)
BRD_4_250_4_MK_76_2_ARF_8M3TP_ = (
8,
MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_,
)
class Pylon9:
Empty_Pylon = (9, MB_339PAN_Weapons.Empty_Pylon)
LR_25___25_ARF_8M3_HEI_ = (9, MB_339PAN_Weapons.LR_25___25_ARF_8M3_HEI_)
LR_25___25_ARF_8M3_API_ = (9, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_)
Mk_82 = (9, Weapons.Mk_82)
Matra_Type_155_Rocket_Pod = (9, Weapons.Matra_Type_155_Rocket_Pod)
class Pylon10:
Tip_Fuel_Tank_500lt_ = (10, MB_339PAN_Weapons.Tip_Fuel_Tank_500lt_)
Tip_Fuel_Tank_Ellittici_320lt_ = (
10,
MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt_,
)
pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
tasks = [
task.GroundAttack,
task.RunwayAttack,
task.CAS,
task.AntishipStrike,
task.Reconnaissance,
]
task_default = task.Nothing

View File

@@ -3,13 +3,11 @@ from pydcs_extensions.f22a.f22a import F_22A
from pydcs_extensions.hercules.hercules import Hercules
from pydcs_extensions.highdigitsams import highdigitsams
from pydcs_extensions.jas39.jas39 import JAS39Gripen, JAS39Gripen_AG
from pydcs_extensions.mb339.mb339 import MB_339PAN
from pydcs_extensions.su57.su57 import Su_57
import pydcs_extensions.frenchpack.frenchpack as frenchpack
MODDED_AIRPLANES = [
A_4E_C,
MB_339PAN,
Su_57,
F_22A,
Hercules,
@@ -51,6 +49,13 @@ MODDED_VEHICLES = [
frenchpack.DIM__TOYOTA_GREEN,
frenchpack.DIM__TOYOTA_DESERT,
frenchpack.DIM__KAMIKAZE,
frenchpack.VBCI,
frenchpack.AMX_13_75mm,
frenchpack.AMX_13_90mm,
frenchpack.Char_T_62,
frenchpack.Char_T_64BV,
frenchpack.Char_T_72A,
frenchpack.KORNET_ATGM,
highdigitsams.AAA_SON_9_Fire_Can,
highdigitsams.AAA_100mm_KS_19,
highdigitsams.SAM_SA_10B_S_300PS_54K6_CP,

View File

@@ -18,9 +18,10 @@ from game.data.weapons import (
WEAPON_INTRODUCTION_YEARS,
Weapon,
)
from game.db import FACTIONS
from game.profiling import logged_duration
from game.settings import Settings
from game.theater.start_generator import GameGenerator, GeneratorSettings
from game.theater.start_generator import GameGenerator, GeneratorSettings, ModSettings
from qt_ui import (
liberation_install,
liberation_theme,
@@ -199,8 +200,8 @@ def create_game(
inject_custom_payloads(Path(persistency.base_path()))
campaign = Campaign.from_json(campaign_path)
generator = GameGenerator(
blue,
red,
FACTIONS[blue],
FACTIONS[red],
campaign.load_theater(),
Settings(
supercarrier=supercarrier,
@@ -221,6 +222,15 @@ def create_game(
no_player_navy=False,
no_enemy_navy=False,
),
ModSettings(
a4_skyhawk=False,
f22_raptor=False,
hercules=False,
jas39_gripen=False,
su57_felon=False,
frenchpack=False,
high_digit_sams=False,
),
)
return generator.generate()

View File

@@ -2,7 +2,7 @@
from __future__ import annotations
import datetime
from typing import Any, Callable, Dict, Iterator, Optional, TypeVar
from typing import Any, Callable, Iterator, Optional, TypeVar
from PySide2.QtCore import (
QAbstractListModel,
@@ -51,7 +51,7 @@ class DeletableChildModelManager:
#: The type of model managed by this class.
ModelType = TypeVar("ModelType")
ModelDict = Dict[DataType, ModelType]
ModelDict = dict[DataType, ModelType]
def __init__(
self,
@@ -334,11 +334,7 @@ class TransferModel(QAbstractListModel):
@staticmethod
def text_for_transfer(transfer: TransferOrder) -> str:
"""Returns the text that should be displayed for the transfer."""
count = sum(transfer.units.values())
origin = transfer.origin.name
destination = transfer.destination.name
description = "Transfer" if transfer.player else "Enemy transfer"
return f"{description} of {count} units from {origin} to {destination}"
return str(transfer)
@staticmethod
def icon_for_transfer(_transfer: TransferOrder) -> Optional[QIcon]:

View File

@@ -24,8 +24,8 @@ class QFactionsInfos(QGroupBox):
def setGame(self, game: Game):
if game is not None:
self.player_name.setText(game.player_name)
self.enemy_name.setText(game.enemy_name)
self.player_name.setText(game.player_faction.name)
self.enemy_name.setText(game.enemy_faction.name)
else:
self.player_name.setText("")
self.enemy_name.setText("")

View File

@@ -46,7 +46,10 @@ class QTopPanel(QFrame):
self.conditionsWidget = QConditionsWidget()
self.budgetBox = QBudgetBox(self.game)
self.passTurnButton = QPushButton("Pass Turn")
pass_turn_text = "Pass Turn"
if not self.game or self.game.turn == 0:
pass_turn_text = "Begin Campaign"
self.passTurnButton = QPushButton(pass_turn_text)
self.passTurnButton.setIcon(CONST.ICONS["PassTurn"])
self.passTurnButton.setProperty("style", "btn-primary")
self.passTurnButton.clicked.connect(self.passTurn)
@@ -114,6 +117,8 @@ class QTopPanel(QFrame):
self.factionsInfos.setGame(game)
self.passTurnButton.setEnabled(True)
if game and game.turn > 0:
self.passTurnButton.setText("Pass Turn")
if game and game.turn == 0:
self.proceedButton.setEnabled(False)
@@ -267,8 +272,8 @@ class QTopPanel(QFrame):
closest_cps[0],
closest_cps[1],
self.game.theater.controlpoints[0].position,
self.game.player_name,
self.game.enemy_name,
self.game.player_faction.name,
self.game.enemy_faction.name,
)
unit_map = self.game.initiate_event(game_event)

View File

@@ -178,6 +178,7 @@ class ControlPointJs(QObject):
class GroundObjectJs(QObject):
nameChanged = Signal()
controlPointNameChanged = Signal()
unitsChanged = Signal()
blueChanged = Signal()
positionChanged = Signal()
@@ -214,6 +215,10 @@ class GroundObjectJs(QObject):
def name(self) -> str:
return self.tgo.name
@Property(str, notify=controlPointNameChanged)
def controlPointName(self) -> str:
return self.tgo.control_point.name
@Property(str, notify=categoryChanged)
def category(self) -> str:
return self.tgo.category

View File

@@ -71,7 +71,7 @@ class QLiberationWindow(QMainWindow):
logging.info("Loading last saved game : " + str(last_save_file))
game = persistency.load_game(last_save_file)
self.onGameGenerated(game)
self.updateWindowTitle(last_save_file)
self.updateWindowTitle(last_save_file if game else None)
except:
logging.info("Error loading latest save game")
else:
@@ -234,13 +234,17 @@ class QLiberationWindow(QMainWindow):
wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame))
def openFile(self):
if self.game is not None and self.game.savepath:
save_dir = self.game.savepath
else:
save_dir = str(persistency.save_dir())
file = QFileDialog.getOpenFileName(
self,
"Select game file to open",
dir=persistency._dcs_saved_game_folder,
dir=save_dir,
filter="*.liberation",
)
if file is not None:
if file is not None and file[0] != "":
game = persistency.load_game(file[0])
GameUpdateSignal.get_instance().game_loaded.emit(game)
@@ -257,10 +261,14 @@ class QLiberationWindow(QMainWindow):
self.saveGameAs()
def saveGameAs(self):
if self.game is not None and self.game.savepath:
save_dir = self.game.savepath
else:
save_dir = str(persistency.save_dir())
file = QFileDialog.getSaveFileName(
self,
"Save As",
dir=persistency._dcs_saved_game_folder,
dir=save_dir,
filter="*.liberation",
)
if file is not None:
@@ -282,6 +290,7 @@ class QLiberationWindow(QMainWindow):
self.setWindowTitle(window_title)
def onGameGenerated(self, game: Game):
self.updateWindowTitle()
logging.info("On Game generated")
self.game = game
GameUpdateSignal.get_instance().game_loaded.emit(self.game)

View File

@@ -33,7 +33,7 @@ class DepartingConvoyInfo(QGroupBox):
if unit_type.dcs_id in VEHICLES_ICONS.keys():
icon.setPixmap(VEHICLES_ICONS[unit_type.dcs_id])
else:
icon.setText("<b>" + unit_type.id[:8] + "</b>")
icon.setText("<b>" + unit_type.name + "</b>")
icon.setProperty("style", "icon-armor")
unit_layout.addWidget(icon, idx, 0)
unit_layout.addWidget(

View File

@@ -284,7 +284,7 @@ class NewUnitTransferDialog(QDialog):
continue
logging.info(
f"Transferring {count} {unit_type.id} from {self.origin} to "
f"Transferring {count} {unit_type} from {self.origin} to "
f"{destination}"
)
transfers[unit_type] = count

View File

@@ -118,18 +118,10 @@ class QBaseMenu2(QDialog):
@property
def cheat_capturable(self) -> bool:
if not self.game_model.game.settings.enable_base_capture_cheat:
return False
if self.cp.captured:
return False
for connected in self.cp.connected_points:
if connected.captured:
return True
return False
return self.game_model.game.settings.enable_base_capture_cheat
def cheat_capture(self) -> None:
self.cp.capture(self.game_model.game, for_player=True)
self.cp.capture(self.game_model.game, for_player=not self.cp.captured)
# Reinitialized ground planners and the like. The ATO needs to be reset because
# missions planned against the flipped base are no longer valid.
self.game_model.game.reset_ato()

View File

@@ -18,7 +18,7 @@ from PySide2.QtWidgets import (
from dcs import Point
from dcs import vehicles
from game import Game, db
from game import Game
from game.data.building_data import FORTIFICATION_BUILDINGS
from game.db import REWARDS
from game.dcs.groundunittype import GroundUnitType
@@ -56,7 +56,9 @@ class QGroundObjectMenu(QDialog):
self.buildings = buildings
self.cp = cp
self.game = game
self.setWindowTitle("Location " + self.ground_object.obj_name)
self.setWindowTitle(
f"Location - {self.ground_object.obj_name} ({self.cp.name})"
)
self.setWindowIcon(EVENT_ICONS["capture"])
self.intelBox = QGroupBox("Units :")
self.buildingBox = QGroupBox("Buildings :")
@@ -258,20 +260,16 @@ class QGroundObjectMenu(QDialog):
self.game.budget = self.game.budget + self.total_value
self.ground_object.groups = []
self.do_refresh_layout()
GameUpdateSignal.get_instance().updateBudget(self.game)
GameUpdateSignal.get_instance().updateGame(self.game)
def buy_group(self):
self.subwindow = QBuyGroupForGroundObjectDialog(
self, self.ground_object, self.cp, self.game, self.total_value
)
self.subwindow.changed.connect(self.do_refresh_layout)
self.subwindow.show()
class QBuyGroupForGroundObjectDialog(QDialog):
changed = QtCore.Signal()
def __init__(
self,
parent,
@@ -434,10 +432,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
)
self.ground_object.groups = [group]
GameUpdateSignal.get_instance().updateBudget(self.game)
self.changed.emit()
self.close()
GameUpdateSignal.get_instance().updateGame(self.game)
def buySam(self):
sam_generator = self.samCombo.itemData(self.samCombo.currentIndex())
@@ -453,10 +448,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
generator.generate()
self.ground_object.groups = list(generator.groups)
GameUpdateSignal.get_instance().updateBudget(self.game)
self.changed.emit()
self.close()
GameUpdateSignal.get_instance().updateGame(self.game)
def buy_ewr(self):
ewr_generator = self.ewr_selector.itemData(self.ewr_selector.currentIndex())
@@ -471,10 +463,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
generator.generate()
self.ground_object.groups = [generator.vg]
GameUpdateSignal.get_instance().updateBudget(self.game)
self.changed.emit()
self.close()
GameUpdateSignal.get_instance().updateGame(self.game)
def error_money(self):
msg = QMessageBox()

View File

@@ -147,6 +147,11 @@ class QFlightCreator(QDialog):
self.on_departure_changed(self.departure.currentIndex())
def reject(self) -> None:
super().reject()
# Clear the roster to return pilots to the pool.
self.roster_editor.replace(None)
def set_custom_name_text(self, text: str):
self.custom_name_text = text
@@ -216,7 +221,7 @@ class QFlightCreator(QDialog):
# noinspection PyUnresolvedReferences
self.created.emit(flight)
self.close()
self.accept()
def on_aircraft_changed(self, index: int) -> None:
new_aircraft = self.aircraft_selector.itemData(index)

View File

@@ -11,7 +11,8 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape
from game import db
from game.settings import Settings
from game.theater.start_generator import GameGenerator, GeneratorSettings
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.spinsliders import TenthsSpinSlider, TimeInputs, CurrencySpinner
from qt_ui.windows.newgame.QCampaignList import (
@@ -102,15 +103,25 @@ class NewGameWizard(QtWidgets.QWizard):
no_player_navy=self.field("no_player_navy"),
no_enemy_navy=self.field("no_enemy_navy"),
)
mod_settings = ModSettings(
a4_skyhawk=self.field("a4_skyhawk"),
f22_raptor=self.field("f22_raptor"),
hercules=self.field("hercules"),
jas39_gripen=self.field("jas39_gripen"),
su57_felon=self.field("su57_felon"),
frenchpack=self.field("frenchpack"),
high_digit_sams=self.field("high_digit_sams"),
)
blue_faction = [c for c in db.FACTIONS][self.field("blueFaction")]
red_faction = [c for c in db.FACTIONS][self.field("redFaction")]
blue_faction = self.faction_selection_page.selected_blue_faction
red_faction = self.faction_selection_page.selected_red_faction
generator = GameGenerator(
blue_faction,
red_faction,
campaign.load_theater(),
settings,
generator_settings,
mod_settings,
)
self.generatedGame = generator.generate()
@@ -196,14 +207,6 @@ class FactionSelection(QtWidgets.QWizardPage):
self.factionsGroupLayout.addLayout(self.redGroupLayout)
self.factionsGroup.setLayout(self.factionsGroupLayout)
# Create required mod layout
self.requiredModsGroup = QtWidgets.QGroupBox("Required Mods")
self.requiredModsGroupLayout = QtWidgets.QHBoxLayout()
self.requiredMods = QtWidgets.QLabel("<ul><li>None</li></ul>")
self.requiredMods.setOpenExternalLinks(True)
self.requiredModsGroupLayout.addWidget(self.requiredMods)
self.requiredModsGroup.setLayout(self.requiredModsGroupLayout)
# Docs Link
docsText = QtWidgets.QLabel(
'<a href="https://github.com/dcs-liberation/dcs_liberation/wiki/Custom-Factions"><span style="color:#FFFFFF;">How to create your own faction</span></a>'
@@ -218,7 +221,6 @@ class FactionSelection(QtWidgets.QWizardPage):
# Build layout
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.factionsGroup)
layout.addWidget(self.requiredModsGroup)
layout.addWidget(docsText)
self.setLayout(layout)
self.updateUnitRecap()
@@ -257,42 +259,13 @@ class FactionSelection(QtWidgets.QWizardPage):
self.blueFactionDescription.setText(blue_faction_txt)
self.redFactionDescription.setText(red_faction_txt)
# Compute mod requirements txt
self.requiredMods.setText("<ul>")
has_mod = False
if len(red_faction.requirements.keys()) > 0:
has_mod = True
for mod in red_faction.requirements.keys():
self.requiredMods.setText(
self.requiredMods.text()
+ "\n<li>"
+ mod
+ ': <a href="'
+ red_faction.requirements[mod]
+ '">'
+ red_faction.requirements[mod]
+ "</a></li>"
)
@property
def selected_blue_faction(self) -> Faction:
return db.FACTIONS[self.blueFactionSelect.currentText()]
if len(blue_faction.requirements.keys()) > 0:
has_mod = True
for mod in blue_faction.requirements.keys():
if mod not in red_faction.requirements.keys():
self.requiredMods.setText(
self.requiredMods.text()
+ "\n<li>"
+ mod
+ ': <a href="'
+ blue_faction.requirements[mod]
+ '">'
+ blue_faction.requirements[mod]
+ "</a></li>"
)
if has_mod:
self.requiredMods.setText(self.requiredMods.text() + "</ul>\n\n")
else:
self.requiredMods.setText(self.requiredMods.text() + "<li>None</li></ul>\n")
@property
def selected_red_faction(self) -> Faction:
return db.FACTIONS[self.redFactionSelect.currentText()]
class TheaterConfiguration(QtWidgets.QWizardPage):
@@ -561,8 +534,48 @@ class GeneratorOptions(QtWidgets.QWizardPage):
generatorLayout.addLayout(desired_player_mission_duration, 6, 0)
generatorSettingsGroup.setLayout(generatorLayout)
modSettingsGroup = QtWidgets.QGroupBox("Mod Settings")
a4_skyhawk = QtWidgets.QCheckBox()
self.registerField("a4_skyhawk", a4_skyhawk)
hercules = QtWidgets.QCheckBox()
self.registerField("hercules", hercules)
f22_raptor = QtWidgets.QCheckBox()
self.registerField("f22_raptor", f22_raptor)
jas39_gripen = QtWidgets.QCheckBox()
self.registerField("jas39_gripen", jas39_gripen)
su57_felon = QtWidgets.QCheckBox()
self.registerField("su57_felon", su57_felon)
frenchpack = QtWidgets.QCheckBox()
self.registerField("frenchpack", frenchpack)
high_digit_sams = QtWidgets.QCheckBox()
self.registerField("high_digit_sams", high_digit_sams)
modHelpText = QtWidgets.QLabel(
"<p>Select the mods you have installed. If your chosen factions support them, you'll be able to use these mods in your campaign.</p>"
)
modHelpText.setAlignment(Qt.AlignCenter)
modLayout = QtWidgets.QGridLayout()
modLayout.addWidget(QtWidgets.QLabel("A-4E Skyhawk"), 1, 0)
modLayout.addWidget(a4_skyhawk, 1, 1)
modLayout.addWidget(QtWidgets.QLabel("F-22A Raptor"), 2, 0)
modLayout.addWidget(f22_raptor, 2, 1)
modLayout.addWidget(QtWidgets.QLabel("C-130J-30 Super Hercules"), 3, 0)
modLayout.addWidget(hercules, 3, 1)
modLayout.addWidget(QtWidgets.QLabel("JAS 39 Gripen"), 4, 0)
modLayout.addWidget(jas39_gripen, 4, 1)
modLayout.addWidget(QtWidgets.QLabel("Su-57 Felon"), 5, 0)
modLayout.addWidget(su57_felon, 5, 1)
modLayout.addWidget(QtWidgets.QLabel("Frenchpack"), 6, 0)
modLayout.addWidget(frenchpack, 6, 1)
modLayout.addWidget(QtWidgets.QLabel("High Digit SAMs"), 7, 0)
modLayout.addWidget(high_digit_sams, 7, 1)
modSettingsGroup.setLayout(modLayout)
mlayout = QVBoxLayout()
mlayout.addWidget(generatorSettingsGroup)
mlayout.addWidget(modSettingsGroup)
mlayout.addWidget(modHelpText)
self.setLayout(mlayout)

View File

@@ -179,6 +179,116 @@ class HqAutomationSettingsBox(QGroupBox):
self.game.settings.auto_ato_player_missions_asap = value
class PilotSettingsBox(QGroupBox):
def __init__(self, game: Game) -> None:
super().__init__("Pilots and Squadrons")
self.game = game
layout = QGridLayout()
self.setLayout(layout)
self.ai_pilot_levelling = QCheckBox()
self.ai_pilot_levelling.setChecked(self.game.settings.ai_pilot_levelling)
self.ai_pilot_levelling.toggled.connect(self.set_ai_pilot_leveling)
ai_pilot_levelling_info = (
"Set whether or not AI pilots will level up after completing a number of"
" sorties. Since pilot level affects the AI skill, you may wish to disable"
" this, lest you face an Ace!"
)
self.ai_pilot_levelling.setToolTip(ai_pilot_levelling_info)
ai_pilot_levelling_label = QLabel("Allow AI pilot levelling")
ai_pilot_levelling_label.setToolTip(ai_pilot_levelling_info)
layout.addWidget(ai_pilot_levelling_label, 0, 0)
layout.addWidget(self.ai_pilot_levelling, 0, 1, Qt.AlignRight)
enable_squadron_pilot_limits_info = (
"If set, squadrons will be limited to a maximum number of pilots and dead "
"pilots will replenish at a fixed rate, each defined with the settings"
"below. Auto-purchase may buy aircraft for which there are no pilots"
"available, so this feature is still a work-in-progress."
)
enable_squadron_pilot_limits_label = QLabel(
"Enable per-squadron pilot limtits (WIP)"
)
enable_squadron_pilot_limits_label.setToolTip(enable_squadron_pilot_limits_info)
enable_squadron_pilot_limits = QCheckBox()
enable_squadron_pilot_limits.setToolTip(enable_squadron_pilot_limits_info)
enable_squadron_pilot_limits.setChecked(
self.game.settings.enable_squadron_pilot_limits
)
enable_squadron_pilot_limits.toggled.connect(
self.set_enable_squadron_pilot_limits
)
layout.addWidget(enable_squadron_pilot_limits_label, 1, 0)
layout.addWidget(enable_squadron_pilot_limits, 1, 1, Qt.AlignRight)
self.pilot_limit = QSpinBox()
self.pilot_limit.setMinimum(12)
self.pilot_limit.setMaximum(72)
self.pilot_limit.setValue(self.game.settings.squadron_pilot_limit)
self.pilot_limit.setEnabled(self.game.settings.enable_squadron_pilot_limits)
self.pilot_limit.valueChanged.connect(self.set_squadron_pilot_limit)
pilot_limit_info = (
"Sets the maximum number of pilots a squadron may have active. "
"Changing this value will not have an immediate effect, but will alter "
"replenishment for future turns."
)
self.pilot_limit.setToolTip(pilot_limit_info)
pilot_limit_label = QLabel("Maximum number of pilots per squadron")
pilot_limit_label.setToolTip(pilot_limit_info)
layout.addWidget(pilot_limit_label, 2, 0)
layout.addWidget(self.pilot_limit, 2, 1, Qt.AlignRight)
self.squadron_replenishment_rate = QSpinBox()
self.squadron_replenishment_rate.setMinimum(1)
self.squadron_replenishment_rate.setMaximum(20)
self.squadron_replenishment_rate.setValue(
self.game.settings.squadron_replenishment_rate
)
self.squadron_replenishment_rate.setEnabled(
self.game.settings.enable_squadron_pilot_limits
)
self.squadron_replenishment_rate.valueChanged.connect(
self.set_squadron_replenishment_rate
)
squadron_replenishment_rate_info = (
"Sets the maximum number of pilots that will be recruited to each squadron "
"at the end of each turn. Squadrons will not recruit new pilots beyond the "
"pilot limit, but each squadron with room for more pilots will recruit "
"this many pilots each turn up to the limit."
)
self.squadron_replenishment_rate.setToolTip(squadron_replenishment_rate_info)
squadron_replenishment_rate_label = QLabel("Squadron pilot replenishment rate")
squadron_replenishment_rate_label.setToolTip(squadron_replenishment_rate_info)
layout.addWidget(squadron_replenishment_rate_label, 3, 0)
layout.addWidget(self.squadron_replenishment_rate, 3, 1, Qt.AlignRight)
def set_enable_squadron_pilot_limits(self, checked: bool) -> None:
self.game.settings.enable_squadron_pilot_limits = checked
self.pilot_limit.setEnabled(checked)
self.squadron_replenishment_rate.setEnabled(checked)
def set_squadron_pilot_limit(self, value: int) -> None:
self.game.settings.squadron_pilot_limit = value
def set_squadron_replenishment_rate(self, value: int) -> None:
self.game.settings.squadron_replenishment_rate = value
def set_ai_pilot_leveling(self, checked: bool) -> None:
self.game.settings.ai_pilot_levelling = checked
START_TYPE_TOOLTIP = "Selects the start type used for AI aircraft."
@@ -516,72 +626,7 @@ class QSettingsWindow(QDialog):
general_layout.addWidget(old_tanker_label, 2, 0)
general_layout.addWidget(old_tanker, 2, 1, Qt.AlignRight)
def set_squadron_pilot_limit(value: int) -> None:
self.game.settings.squadron_pilot_limit = value
pilot_limit = QSpinBox()
pilot_limit.setMinimum(12)
pilot_limit.setMaximum(72)
pilot_limit.setValue(self.game.settings.squadron_pilot_limit)
pilot_limit.valueChanged.connect(set_squadron_pilot_limit)
pilot_limit_info = (
"Sets the maximum number of pilots a squadron may have active. "
"Changing this value will not have an immediate effect, but will alter "
"replenishment for future turns."
)
pilot_limit.setToolTip(pilot_limit_info)
pilot_limit_label = QLabel("Maximum number of pilots per squadron")
pilot_limit_label.setToolTip(pilot_limit_info)
general_layout.addWidget(pilot_limit_label, 3, 0)
general_layout.addWidget(pilot_limit, 3, 1, Qt.AlignRight)
def set_squadron_replenishment_rate(value: int) -> None:
self.game.settings.squadron_replenishment_rate = value
squadron_replenishment_rate = QSpinBox()
squadron_replenishment_rate.setMinimum(1)
squadron_replenishment_rate.setMaximum(20)
squadron_replenishment_rate.setValue(
self.game.settings.squadron_replenishment_rate
)
squadron_replenishment_rate.valueChanged.connect(
set_squadron_replenishment_rate
)
squadron_replenishment_rate_info = (
"Sets the maximum number of pilots that will be recruited to each squadron "
"at the end of each turn. Squadrons will not recruit new pilots beyond the "
"pilot limit, but each squadron with room for more pilots will recruit "
"this many pilots each turn up to the limit."
)
squadron_replenishment_rate.setToolTip(squadron_replenishment_rate_info)
squadron_replenishment_rate_label = QLabel("Squadron pilot replenishment rate")
squadron_replenishment_rate_label.setToolTip(squadron_replenishment_rate_info)
general_layout.addWidget(squadron_replenishment_rate_label, 4, 0)
general_layout.addWidget(squadron_replenishment_rate, 4, 1, Qt.AlignRight)
ai_pilot_levelling = QCheckBox()
ai_pilot_levelling.setChecked(self.game.settings.ai_pilot_levelling)
ai_pilot_levelling.toggled.connect(self.applySettings)
ai_pilot_levelling_info = (
"Set whether or not AI pilots will level up after completing a number of"
" sorties. Since pilot level affects the AI skill, you may wish to disable"
" this, lest you face an Ace!"
)
ai_pilot_levelling.setToolTip(ai_pilot_levelling_info)
ai_pilot_levelling_label = QLabel("Allow AI pilot levelling")
ai_pilot_levelling_label.setToolTip(ai_pilot_levelling_info)
general_layout.addWidget(ai_pilot_levelling_label, 5, 0)
general_layout.addWidget(ai_pilot_levelling, 5, 1, Qt.AlignRight)
campaign_layout.addWidget(PilotSettingsBox(self.game))
campaign_layout.addWidget(HqAutomationSettingsBox(self.game))
def initGeneratorLayout(self):
@@ -660,7 +705,10 @@ class QSettingsWindow(QDialog):
start_type_label = QLabel(
"Default start type for AI aircraft<br /><strong>Warning: "
"Any option other than Cold breaks OCA/Aircraft missions.</strong>"
"Options other than Cold will significantly reduce the<br />"
"number of targets available for OCA/Aircraft missions,<br />"
"and OCA/Aircraft flights will not be included in<br />"
"automatically planned OCA packages.</strong>"
)
start_type_label.setToolTip(START_TYPE_TOOLTIP)
start_type = StartTypeComboBox(self.game.settings)

View File

@@ -19,7 +19,7 @@ pathspec==0.8.1
pefile==2019.4.18
Pillow==8.2.0
pre-commit==2.10.1
-e git://github.com/pydcs/dcs@a459d8e1b0bd59bbd7f35390d7ad1bedc0caf76b#egg=pydcs
-e git://github.com/pydcs/dcs@7dea4f516d943c1f48454a46043b5f38d42a35f0#egg=pydcs
pyinstaller==4.3
pyinstaller-hooks-contrib==2021.1
pyparsing==2.4.7

View File

@@ -5,7 +5,7 @@
"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": "6.0",
"version": "7.0",
"miz": "Battle_for_the_UAE_v3.0.2.miz",
"performance": 2
}

View File

@@ -5,7 +5,7 @@
"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": "6.0",
"version": "7.0",
"miz": "Caucasus_Multi_Full.miz",
"performance": 3
}

View File

@@ -5,7 +5,7 @@
"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": "6.0",
"version": "7.0",
"miz": "Caucasus_Multi_Georgia.miz",
"performance": 1
}

View File

@@ -5,7 +5,7 @@
"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": "6.0",
"version": "7.0",
"miz": "Caucasus_Multi_Russia.miz",
"performance": 2
}

View File

@@ -6,6 +6,6 @@
"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": "6.0",
"version": "7.0",
"performance": 2
}

View File

@@ -0,0 +1,11 @@
{
"name": "Syria - Operation Atilla",
"theater": "Syria",
"authors": "Malakhit",
"recommended_player_faction": "Turkey 2005",
"recommended_enemy_faction": "Greece 2005",
"description": "<p>This is based on the Turkish invasion of Cyprus, and the Greek defense of the island. You must make sure to keep your beachhead at all costs, otherwise reclaiming it will be tricky. It is recommended to reduce the per-turn income rate for both factions in this scenario due to the large oil depots both sides have - setting it to around 15-20% is probably reasonable.</p>",
"version": "7.0",
"miz": "Operation_Atilla.miz",
"performance": 2
}

Binary file not shown.

View File

@@ -5,7 +5,7 @@
"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": "6.0",
"version": "7.0",
"miz": "Operation_Mole_Cricket_2010_v3.0.2.miz",
"performance": 2
}

View File

@@ -3,7 +3,7 @@
"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": "6.0",
"version": "7.0",
"recommended_player_faction": "USA 2005",
"recommended_enemy_faction": "Iran 2015",
"miz": "Road_to_Dubai.miz",

View File

@@ -4,8 +4,8 @@
"authors": "Malakhit",
"recommended_player_faction": "Russia 2010",
"recommended_enemy_faction": "Insurgents (Hard)",
"description": "<p>This short campaign is loosely based on the 2015 Russian military intervention in Syria. It should be perfect for COIN operations, especially with the Hind.</p>",
"version": "6.0",
"description": "<p>This short campaign is loosely based on the 2015 Russian military intervention in Syria. It should be perfect for COIN operations, especially with the Hind. Not designed for campaign inversion.</p>",
"version": "7.0",
"miz": "Russian_Intervention_2015.miz",
"performance": 1
}

View File

@@ -0,0 +1,11 @@
{
"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": "7.0",
"miz": "around_the_mountain.miz",
"performance": 2
}

Binary file not shown.

View File

@@ -7,5 +7,5 @@
"description": "<p>You have managed to establish a foothold at Khasab. Continue pushing south.</p>",
"miz": "battle_of_abu_dhabi.miz",
"performance": 2,
"version": "6.0"
"version": "7.0"
}

View File

@@ -5,5 +5,5 @@
"description": "<p>A medium sized theater with bases along the coast of the Black Sea.</p>",
"miz": "black_sea.miz",
"performance": 2,
"version": "6.0"
"version": "7.0"
}

View File

@@ -7,5 +7,5 @@
"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": "6.0"
"version": "7.0"
}

View File

@@ -5,7 +5,7 @@
"recommended_player_faction": "Bluefor Modern",
"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 resource advantage.</p>",
"version": "6.0",
"version": "7.0",
"miz": "exercise_vegas_nerve.miz",
"performance": 0
}

View File

@@ -7,5 +7,5 @@
"description": "<p>In this scenario, you start in Israel and the conflict is focused around the golan heights, an historically disputed territory.<br/><br/>This scenario is designed to be performance friendly.</p>",
"miz": "golan_heights_lite.miz",
"performance": 1,
"version": "6.0"
"version": "7.0"
}

View File

@@ -7,5 +7,5 @@
"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": "6.0"
"version": "7.0"
}

View File

@@ -1,11 +0,0 @@
{
"name": "Syria - Inherent Resolve",
"theater": "Syria",
"authors": "Khopa",
"recommended_player_faction": "USA 2005",
"recommended_enemy_faction": "Insurgents (Hard)",
"description": "<p>In this scenario, you start from Jordan, and have to fight your way through eastern Syria.</p>",
"version": "6.1",
"miz": "inherent_resolve.miz",
"performance": 2
}

View File

@@ -0,0 +1,11 @@
{
"name": "Caucasus - Mozdok to Maykop",
"theater": "Caucasus",
"authors": "Khopa",
"recommended_player_faction": "Russia 2010",
"recommended_enemy_faction": "USA 1990",
"description": "<p>A small theater in Russia, progress from Mozdok to Maykop.</p><p>This scenario is pretty simple, and is ideal if you want to run a short campaign to try liberation. If your PC is not powerful, this is also the less performance heavy scenario.</p>",
"miz": "mozdok_to_maykop.miz",
"performance": 0,
"version": "7.0"
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
{
"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": "7.0",
"miz": "nevada_limited_air.miz",
"performance": 1
}

Binary file not shown.

View File

@@ -2,10 +2,10 @@
"name": "Caucasus - Northern Russia",
"theater": "Caucasus",
"authors": "Plob",
"recommended_player_faction": "USA 2005",
"recommended_enemy_faction": "Russia 1990",
"description": "<p>A medium campaign through the north eastern part of the Caucasus map.</p><p>Russia has invaded Georgia through the eastern mountains. Mount a counter offense and push them back!",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Russia 1975",
"description": "<p>A medium campaign through the north eastern part of the Caucasus map. Play vs 1975 Russia for an low-medium difficulty campaign, play vs russia 1990 for a hard difficulty campaign. </p><p>Russia has invaded Georgia through the eastern mountains. Mount a counter offense and push them back!",
"miz": "northern_russia.miz",
"performance": 2,
"version": "6.0"
"version": "7.0"
}

View File

@@ -2,8 +2,10 @@
"name": "Syria - Operation Allied Sword",
"theater": "Syria",
"authors": "Fuzzle",
"recommended_player_faction": "Israel-USN 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>",
"version": "6.0",
"version": "7.0",
"miz": "operation_allied_sword.miz",
"performance": 2
}

View File

@@ -0,0 +1,11 @@
{
"name": "Syria - Operation Blackball",
"theater": "Syria",
"authors": "Fuzzle",
"recommended_player_faction": "US Navy 2005",
"recommended_enemy_faction": "Russia 2010",
"description": "<p><strong>Warning: This campaign will not work if the attacking faction does not have a carrier.</strong></p><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.</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": "7.0",
"miz": "operation_blackball.miz",
"performance": 1
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
{
"name": "The Channel - Operation Dynamo",
"theater": "The Channel",
"authors": "Khopa",
"recommended_player_faction": "Allies 1940",
"recommended_enemy_faction": "Germany 1940",
"description": "<p>The Battle of Dunkirk (French: Bataille de Dunkerque) was fought around the French port of Dunkirk (Dunkerque) during the Second World War, between the Allies and Nazi Germany. As the Allies were losing the Battle of France on the Western Front, the Battle of Dunkirk was the defence and evacuation of British and other Allied forces to Britain from 26 May to 4 June 1940..</p>",
"version": 7.0,
"miz": "operation_dynamo.miz",
"performance": 1
}

Binary file not shown.

View File

@@ -5,7 +5,7 @@
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Turkey 2005",
"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. Campaign inversion is available if you wish to play as Turkey.</p>",
"version": "6.0",
"version": "7.0",
"miz": "operation_peace_spring.miz",
"performance": 1
}

View File

@@ -5,7 +5,7 @@
"recommended_player_faction": "USA 2005",
"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.<br/></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. 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 ground war.</p>",
"version": "6.0",
"version": "7.0",
"miz": "operation_vectrons_claw.miz",
"performance": 1
}

View File

@@ -1,11 +0,0 @@
{
"name": "Caucasus - Russia Small",
"theater": "Caucasus",
"authors": "Khopa",
"recommended_player_faction": "Russia 2010",
"recommended_enemy_faction": "USA 1990",
"description": "<p>A small theater in Russia, progress from Mozdok to Maykop.</p><p>This scenario is pretty simple, it is ideal if you want to run a short campaign. If your PC is not powerful, this is also the less performance heavy scenario.</p>",
"miz": "russia_small.miz",
"performance": 0,
"version": "6.0"
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
{
"name": "Persian Gulf - Scenic Route",
"theater": "Persian Gulf",
"authors": "Fuzzle",
"recommended_player_faction": "US Navy 2005",
"recommended_enemy_faction": "Iran 2015",
"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": "7.0",
"miz": "scenic_route.miz",
"performance": 1
}

Binary file not shown.

View File

@@ -4,8 +4,8 @@
"authors": "Plob",
"recommended_player_faction": "Bluefor Modern",
"recommended_enemy_faction": "Syria 2011",
"description": "<p>Syria Full map, designed for groups of 4-12 players.</p>",
"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": "6.0"
"version": "7.0"
}

View File

@@ -1,164 +0,0 @@
local unitPayloads = {
["name"] = "MB-339PAN",
["payloads"] = {
[1] = {
["name"] = "CAP",
["pylons"] = {
[1] = {
["CLSID"] = "{MB339-DEFA553_R}",
["num"] = 7,
},
[2] = {
["CLSID"] = "{MB339-DEFA553_L}",
["num"] = 4,
},
[3] = {
["CLSID"] = "{FUEL-TIP-ELLITTIC-L}",
["num"] = 1,
},
[4] = {
["CLSID"] = "{FUEL-TIP-ELLITTIC-R}",
["num"] = 10,
},
},
["tasks"] = {
},
},
[2] = {
["name"] = "CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{LAU-10}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{LAU-10}",
["num"] = 8,
},
[5] = {
["CLSID"] = "{MB339-DEFA553_L}",
["num"] = 4,
},
[6] = {
["CLSID"] = "{MB339-DEFA553_R}",
["num"] = 7,
},
},
["tasks"] = {
},
},
[3] = {
["name"] = "ANTISHIP",
["pylons"] = {
[1] = {
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{LAU-10}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{LAU-10}",
["num"] = 8,
},
[5] = {
["CLSID"] = "{MB339-DEFA553_L}",
["num"] = 4,
},
[6] = {
["CLSID"] = "{MB339-DEFA553_R}",
["num"] = 7,
},
},
["tasks"] = {
},
},
[4] = {
["name"] = "STRIKE",
["pylons"] = {
[1] = {
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 8,
},
[5] = {
["CLSID"] = "{MB339-AN-M3_L}",
["num"] = 4,
},
[6] = {
["CLSID"] = "{MB339-AN-M3_R}",
["num"] = 7,
},
[7] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 9,
},
[8] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 2,
},
},
["tasks"] = {
},
},
[5] = {
["name"] = "SEAD",
["pylons"] = {
[1] = {
["CLSID"] = "{FUEL-TIP-TANK-500-R}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{FUEL-TIP-TANK-500-L}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{LAU-10}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{LAU-10}",
["num"] = 8,
},
[5] = {
["CLSID"] = "{MB339-AN-M3_L}",
["num"] = 4,
},
[6] = {
["CLSID"] = "{MB339-AN-M3_R}",
["num"] = 7,
},
},
["tasks"] = {
},
},
},
["tasks"] = {
},
["unitType"] = "MB-339PAN",
}
return unitPayloads

View File

@@ -1,49 +1,39 @@
{
"country": "USA",
"name": "USA 2005 Modded",
"authors": "Khopa",
"description": "<p>USA 2005 with the Raptor mod, with the F-22 mod by Grinelli Designs.</p>",
"country": "Israel",
"name": "Israel-USN 2005 (Allied Sword)",
"authors": "Fuzzle",
"description": "<p>A joint US Navy/Israeli modern faction for use with the Operation Allied Sword scenario.</p>",
"locales": [
"en_US"
],
"aircrafts": [
"A-10C Thunderbolt II (Suite 3)",
"A-10C Thunderbolt II (Suite 7)",
"AH-64D Apache Longbow",
"AV-8B Harrier II Night Attack",
"B-1B Lancer",
"B-52H Stratofortress",
"F-117A Nighthawk",
"F-14B Tomcat",
"F-4E Phantom II",
"F-15C Eagle",
"F-15E Strike Eagle",
"F-16CM Fighting Falcon (Block 50)",
"F-22A Raptor",
"F-14B Tomcat",
"F/A-18C Hornet (Lot 20)",
"AV-8B Harrier II Night Attack",
"AH-1W SuperCobra",
"AH-64A Apache",
"S-3B Viking",
"SH-60B Seahawk",
"UH-1H Iroquois"
],
"awacs": [
"E-2C Hawkeye",
"E-3A"
"E-2C Hawkeye"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker",
"KC-135 Stratotanker MPRS",
"KC-130",
"S-3B Tanker"
],
"frontline_units": [
"LAV-25",
"M113",
"M1043 HMMWV (M2 HMG)",
"M1045 HMMWV (BGM-71 TOW)",
"M1097 Heavy HMMWV Avenger",
"M1126 Stryker ICV (M2 HMG)",
"M1134 Stryker ATGM (BGM-71 TOW)",
"M1A2 Abrams",
"M2A2 Bradley",
"M6 Linebacker"
"Merkava Mk IV",
"M163 Vulcan Air Defense System"
],
"artillery_units": [
"M109A6 Paladin",
@@ -53,19 +43,18 @@
"Truck M818 6x6"
],
"infantry_units": [
"Infantry M249",
"Infantry M4",
"MANPADS Stinger",
"Mortar 2B11 120mm"
"Infantry M249",
"MANPADS Stinger"
],
"air_defenses": [
"AvengerGenerator",
"ChaparralGenerator",
"HawkGenerator",
"LinebackerGenerator",
"VulcanGenerator",
"PatriotGenerator"
],
"ewrs": [
"PatriotEwrGenerator"
"HawkEwrGenerator"
],
"aircraft_carrier": [
"Stennis"
@@ -74,16 +63,20 @@
"LHA_Tarawa"
],
"destroyers": [
"USS_Arleigh_Burke_IIa"
"USS_Arleigh_Burke_IIa",
"PERRY"
],
"cruisers": [
"TICONDEROG"
],
"requirements": {
},
"carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis"
"CVN-74 John C. Stennis",
"CVN-75 Harry S. Truman"
],
"helicopter_carrier_names": [
"LHA-1 Tarawa",
@@ -96,29 +89,24 @@
"ArleighBurkeGroupGenerator",
"OliverHazardPerryGroupGenerator"
],
"requirements": {
"F-22A mod by Grinnelli Designs": "https://bestf22modever.com"
},
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper",
"doctrine": "modern",
"liveries_overrides": {
"F-14B Tomcat": [
"VF-142 Ghostriders"
],
"F/A-18C Hornet (Lot 20)": [
"VFA-106",
"VFA-113",
"VFA-122",
"VFA-131",
"VFA-192",
"VFA-34",
"VFA-37",
"VFA-83",
"VFA-87",
"VFA-97",
"VMFA-122",
"VMFA-132",
"VMFA-251",
"VMFA-312",
"VMFA-314",
"VMFA-323"
"VMFA-251 high visibility"
],
"AV-8B Harrier II Night Attack": [
"VMAT-542"
],
"AH-1W SuperCobra": [
"Marines"
],
"UH-1H Iroquois": [
"US NAVY"
]
}
}
}

View File

@@ -9,6 +9,8 @@
"AV-8B Harrier II Night Attack",
"B-1B Lancer",
"B-52H Stratofortress",
"C-130",
"C-130J-30 Super Hercules",
"F-117A Nighthawk",
"F-14A Tomcat (Block 135-GR Late)",
"F-14B Tomcat",

View File

@@ -0,0 +1,102 @@
{
"country": "Combined Joint Task Forces Red",
"name": "Syria-Lebanon 2005 (Allied Sword)",
"authors": "Fuzzle",
"description": "<p>Syria-Lebanon alliance in a modern setting with several imported Russian assets. Designed for use with the Allied Sword scenario.</p>",
"aircrafts": [
"MiG-23ML Flogger-G",
"MiG-25RBT Foxbat-B",
"MiG-29A Fulcrum-A",
"Su-17M4 Fitter-K",
"Su-24M Fencer-D",
"Su-30 Flanker-C",
"Su-34 Fullback",
"L-39ZA Albatros",
"Tu-22M3 Backfire-C",
"Mi-24V Hind-E",
"Mi-8MTV2 Hip",
"SA 342M Gazelle",
"SA 342L Gazelle"
],
"awacs": [
"A-50"
],
"tankers": [
"IL-78M"
],
"frontline_units": [
"BMP-1",
"BMP-2",
"BTR-80",
"BRDM-2",
"MT-LB",
"T-55A",
"T-72B with Kontakt-1 ERA",
"T-90A",
"ZSU-57-2 'Sparka'"
],
"artillery_units": [
"BM-21 Grad",
"2S1 Gvozdika"
],
"logistics_units": [
"Truck Ural-375",
"LUV UAZ-469 Jeep"
],
"infantry_units": [
"Paratrooper AKS",
"Infantry AK-74 Rus",
"Paratrooper RPG-16",
"MANPADS SA-18 Igla-S \"Grouse\""
],
"air_defenses": [
"ColdWarFlakGenerator",
"SA2Generator",
"SA3Generator",
"SA6Generator",
"SA8Generator",
"SA9Generator",
"SA10Generator",
"SA11Generator",
"SA13Generator",
"SA19Generator",
"ZSU23Generator",
"ZU23Generator",
"ZU23UralGenerator",
"ZSU57Generator"
],
"ewrs": [
"BoxSpringGenerator",
"TallRackGenerator"
],
"missiles": [
"ScudGenerator"
],
"missiles_group_count": 2,
"coastal_defenses": [
"SilkwormGenerator"
],
"coastal_group_count": 4,
"aircraft_carrier": [
],
"helicopter_carrier": [
],
"helicopter_carrier_names": [
],
"destroyers": [
"REZKY",
"MOLNIYA"
],
"cruisers": [
],
"requirements": {},
"carrier_names": [
],
"coastal_group_count": 8,
"navy_generators": [
"GrishaGroupGenerator",
"MolniyaGroupGenerator",
"RussianNavyGroupGenerator",
"LaCombattanteIIGroupGenerator"
]
}

View File

@@ -1,10 +1,11 @@
{
"country": "Australia",
"name": "Australia 2005",
"authors": "Khopa",
"authors": "Khopa, SpaceEnthusiast",
"description": "<p>The Australian army in 2005.</p><p>Some units might not be accurate, but were picked to represent at best this army.</p>",
"aircrafts": [
"AH-1W SuperCobra",
"C-130J-30 Super Hercules",
"F/A-18C Hornet (Lot 20)",
"SH-60B Seahawk",
"UH-1H Iroquois"
@@ -47,7 +48,9 @@
"USS_Arleigh_Burke_IIa"
],
"cruisers": [],
"requirements": {},
"requirements": {
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
},
"carrier_names": [],
"helicopter_carrier_names": [
"HMAS Canberra",

View File

@@ -1,70 +0,0 @@
{
"country": "Australia",
"name": "Australia 2005 (With C-130)",
"authors": "Khopa, SpaceEnthusiast",
"description": "<p>The Australian army in 2005.</p><p>Some units might not be accurate, but were picked to represent at best this army.</p>",
"aircrafts": [
"AH-1W SuperCobra",
"C-130J-30 Super Hercules",
"F/A-18C Hornet (Lot 20)",
"SH-60B Seahawk",
"UH-1H Iroquois"
],
"awacs": [
"E-3A"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker"
],
"frontline_units": [
"FV510 Warrior",
"LAV-25",
"Leopard 1A3",
"M113",
"M1A2 Abrams"
],
"artillery_units": [],
"logistics_units": [
"Truck M818 6x6"
],
"infantry_units": [
"Infantry M249",
"Infantry M4",
"MANPADS Stinger"
],
"air_defenses": [
"HawkGenerator",
"RapierGenerator"
],
"ewrs": [
"HawkEwrGenerator"
],
"aircraft_carrier": [],
"helicopter_carrier": [
"LHA_Tarawa"
],
"destroyers": [
"USS_Arleigh_Burke_IIa"
],
"cruisers": [],
"requirements": {
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
},
"carrier_names": [],
"helicopter_carrier_names": [
"HMAS Canberra",
"HMAS Adelaide"
],
"navy_generators": [
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper",
"liveries_overrides": {
"F/A-18C Hornet (Lot 20)": [
"Australian 75th Squadron",
"Australian 77th Squadron"
]
}
}

View File

@@ -2,11 +2,14 @@
"country": "Combined Joint Task Forces Blue",
"name": "Bluefor Coldwar",
"authors": "Khopa",
"description": "<p>A generic bluefor coldwar faction.</p>",
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C mod)</p>",
"aircrafts": [
"A-10A Thunderbolt II",
"A-4E Skyhawk",
"AJS-37 Viggen",
"B-52H Stratofortress",
"C-130",
"C-130J-30 Super Hercules",
"F-14A Tomcat (Block 135-GR Late)",
"F-14B Tomcat",
"F-4E Phantom II",
@@ -16,7 +19,6 @@
"UH-1H Iroquois"
],
"awacs": [
"C-130",
"E-2C Hawkeye",
"E-3A"
],
@@ -60,7 +62,9 @@
"cruisers": [
"TICONDEROG"
],
"requirements": {},
"requirements": {
"Community A-4E": "https://heclak.github.io/community-a4e-c/"
},
"carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",

View File

@@ -1,86 +0,0 @@
{
"country": "Combined Joint Task Forces Blue",
"name": "Bluefor Coldwar (With A4)",
"authors": "Khopa",
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C mod)</p>",
"aircrafts": [
"A-10A Thunderbolt II",
"A-4E Skyhawk",
"AJS-37 Viggen",
"B-52H Stratofortress",
"F-14A Tomcat (Block 135-GR Late)",
"F-14B Tomcat",
"F-4E Phantom II",
"F-5E Tiger II",
"SA 342L Gazelle",
"SA 342M Gazelle",
"UH-1H Iroquois"
],
"awacs": [
"C-130",
"E-2C Hawkeye",
"E-3A"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker"
],
"frontline_units": [
"M113",
"M48 Chaparral",
"M60A3 \"Patton\""
],
"artillery_units": [
"M109A6 Paladin"
],
"logistics_units": [
"Truck M818 6x6"
],
"infantry_units": [
"Infantry M249",
"Infantry M4"
],
"air_defenses": [
"ChaparralGenerator",
"EarlyColdWarFlakGenerator",
"HawkGenerator",
"VulcanGenerator"
],
"ewrs": [
"HawkEwrGenerator"
],
"aircraft_carrier": [
"Stennis"
],
"helicopter_carrier": [
"LHA_Tarawa"
],
"destroyers": [
"USS_Arleigh_Burke_IIa"
],
"cruisers": [
"TICONDEROG"
],
"requirements": {
"Community A-4E": "https://heclak.github.io/community-a4e-c/"
},
"carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis"
],
"helicopter_carrier_names": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
],
"navy_generators": [
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper",
"doctrine": "coldwar"
}

View File

@@ -1,88 +0,0 @@
{
"country": "Combined Joint Task Forces Blue",
"name": "Bluefor Coldwar (With A4 & MB339)",
"authors": "Khopa",
"description": "<p>A generic bluefor coldwar faction. (With the A-4E-C and the MB-339 mods)</p>",
"aircrafts": [
"A-10A Thunderbolt II",
"A-4E Skyhawk",
"AJS-37 Viggen",
"B-52H Stratofortress",
"F-14A Tomcat (Block 135-GR Late)",
"F-14B Tomcat",
"F-4E Phantom II",
"F-5E Tiger II",
"MB-339PAN",
"SA 342L Gazelle",
"SA 342M Gazelle",
"UH-1H Iroquois"
],
"awacs": [
"C-130",
"E-2C Hawkeye",
"E-3A"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker"
],
"frontline_units": [
"M113",
"M48 Chaparral",
"M60A3 \"Patton\""
],
"artillery_units": [
"M109A6 Paladin"
],
"logistics_units": [
"Truck M818 6x6"
],
"infantry_units": [
"Infantry M249",
"Infantry M4"
],
"air_defenses": [
"ChaparralGenerator",
"EarlyColdWarFlakGenerator",
"HawkGenerator",
"VulcanGenerator"
],
"ewrs": [
"HawkEwrGenerator"
],
"aircraft_carrier": [
"Stennis"
],
"helicopter_carrier": [
"LHA_Tarawa"
],
"destroyers": [
"USS_Arleigh_Burke_IIa"
],
"cruisers": [
"TICONDEROG"
],
"requirements": {
"MB-339A/PAN by Frecce Tricolori Virtuali": "http://www.freccetricolorivirtuali.net/",
"Community A-4E": "https://heclak.github.io/community-a4e-c/"
},
"carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis"
],
"helicopter_carrier_names": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
],
"navy_generators": [
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper",
"doctrine": "coldwar"
}

View File

@@ -12,15 +12,20 @@
"AV-8B Harrier II Night Attack",
"B-1B Lancer",
"B-52H Stratofortress",
"C-130",
"C-130J-30 Super Hercules",
"F-14B Tomcat",
"F-15C Eagle",
"F-15E Strike Eagle",
"F-16CM Fighting Falcon (Block 50)",
"F-22A Raptor",
"F-5E Tiger II",
"F/A-18C Hornet (Lot 20)",
"JF-17 Thunder",
"Ka-50 Hokum",
"Mirage 2000C",
"Mi-24P Hind-F",
"Mi-8MTV2 Hip",
"SA 342L Gazelle",
"SA 342M Gazelle",
"Su-25T Frogfoot",

View File

@@ -1,7 +1,7 @@
{
"country": "Canada",
"name": "Canada 2005",
"authors": "Khopa",
"authors": "Khopa, SpaceEnthusiast",
"description": "<p>Canada in the 2000s, an F/A-18C Hornet focused faction.</p>",
"locales": [
"en_US",
@@ -9,6 +9,7 @@
],
"aircrafts": [
"AH-1W SuperCobra",
"C-130J-30 Super Hercules",
"CF-188 Hornet",
"UH-1H Iroquois"
],
@@ -52,7 +53,9 @@
"cruisers": [
"TICONDEROG"
],
"requirements": {},
"requirements": {
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
},
"carrier_names": [],
"helicopter_carrier_names": [],
"navy_generators": [
@@ -64,6 +67,9 @@
"CF-188 Hornet": [
"Canada 409th Squadron",
"Canada 425th Squadron"
],
"C-130J-30 Super Hercules": [
"Royal Canadian AF CC-130J"
]
}
}

View File

@@ -1,75 +0,0 @@
{
"country": "Canada",
"name": "Canada 2005 (With C-130)",
"authors": "Khopa, SpaceEnthusiast",
"description": "<p>Canada in the 2000s, an F/A-18C Hornet focused faction.</p>",
"locales": [
"en_US",
"fr_CA"
],
"aircrafts": [
"AH-1W SuperCobra",
"C-130J-30 Super Hercules",
"CF-188 Hornet",
"UH-1H Iroquois"
],
"awacs": [
"E-3A"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker"
],
"frontline_units": [
"FV510 Warrior",
"LAV-25",
"Leopard 1A3",
"Leopard 2",
"Leopard 2A4",
"M1097 Heavy HMMWV Avenger",
"M113"
],
"artillery_units": [],
"logistics_units": [
"Truck M818 6x6"
],
"infantry_units": [
"Infantry M249",
"Infantry M4",
"MANPADS Stinger"
],
"air_defenses": [
"AvengerGenerator",
"HawkGenerator"
],
"ewrs": [
"HawkEwrGenerator"
],
"aircraft_carrier": [],
"helicopter_carrier": [],
"destroyers": [
"USS_Arleigh_Burke_IIa"
],
"cruisers": [
"TICONDEROG"
],
"requirements": {
"C-130J-30 Super Hercules Mod by Anubis": "https://forums.eagle.ru/topic/252075-dcs-super-hercules-mod-by-anubis/"
},
"carrier_names": [],
"helicopter_carrier_names": [],
"navy_generators": [
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper",
"liveries_overrides": {
"CF-188 Hornet": [
"Canada 409th Squadron",
"Canada 425th Squadron"
],
"C-130J-30 Super Hercules": [
"Royal Canadian AF CC-130J"
]
}
}

View File

@@ -8,10 +8,11 @@
],
"aircrafts": [
"FC-1 Fierce Dragon",
"IL-76MD",
"J-11A Flanker-L",
"J-15 Flanker X-2",
"J-7B",
"Mi-28N Havoc",
"Mi-24P Hind-F",
"Mi-8MTV2 Hip",
"Su-30MKK Flanker-G"
],
@@ -51,6 +52,7 @@
"SA10Generator",
"SA11Generator",
"SA13Generator",
"SA20BGenerator",
"Tier2SA10Generator",
"ZSU23Generator",
"ZSU57Generator",

View File

@@ -0,0 +1,84 @@
{
"country": "France",
"name": "France 1985",
"authors": "Colonel Panic",
"description": "<p>France 1985. Frenchpack 4.6+ mod is recommended to enable most of the ground units of this faction available.</p>",
"locales": [
"fr_FR"
],
"doctrine": "coldwar",
"aircrafts": [
"C-130",
"Mirage 2000C",
"SA 342L Gazelle",
"SA 342M Gazelle",
"SA 342M Gazelle Mistral"
],
"awacs": [
"E-3A"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker"
],
"frontline_units": [
"AMX.30B2",
"Leclerc S\u00e9ries 2",
"Leclerc S\u00e9ries 2",
"Pamela",
"Panhard",
"Roland 2 (Marder Chassis)",
"VAB .50",
"VAB Mephisto",
"VAB T20/13",
"VBL .50",
"VBL AANF1",
"AMX-13 75mm",
"AMX-13 90mm",
"VBL AANF1"
],
"artillery_units": [
"M109A6 Paladin",
"M270 Multiple Launch Rocket System"
],
"logistics_units": [
"Truck M818 6x6"
],
"infantry_units": [
"Infantry M249",
"Infantry M4",
"MANPADS Stinger"
],
"air_defenses": [
"RolandGenerator",
"HawkGenerator"
],
"aircraft_carrier": [],
"helicopter_carrier": [
"LHA_Tarawa"
],
"destroyers": [
"USS_Arleigh_Burke_IIa"
],
"cruisers": [
"TICONDEROG"
],
"requirements": {
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974"
},
"carrier_names": [
"R91 Charles de Gaulle"
],
"helicopter_carrier_names": [
"R97 Jeanne d'Arc",
"L9013 Mistral",
"L9014 Tonerre",
"L9015 Dixmude"
],
"navy_generators": [
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper"
}

View File

@@ -1,79 +0,0 @@
{
"country": "France",
"name": "France 1985 (Frenchpack)",
"authors": "Colonel Panic",
"description": "<p>1980s French equipment using FrenchPack.</p>",
"locales": [
"fr_FR"
],
"doctrine": "coldwar",
"aircrafts": [
"Mirage 2000C",
"SA 342L Gazelle",
"SA 342M Gazelle",
"SA 342M Gazelle Mistral"
],
"awacs": [
"E-3A"
],
"tankers": [
"KC-130",
"KC-135 Stratotanker"
],
"frontline_units": [
"AMX.30B2",
"Leclerc S\u00e9ries 2",
"Leclerc S\u00e9ries 2",
"Pamela",
"Panhard",
"Roland 2 (Marder Chassis)",
"VAB .50",
"VAB Mephisto",
"VAB T20/13",
"VBL .50",
"VBL AANF1"
],
"artillery_units": [
"M109A6 Paladin",
"M270 Multiple Launch Rocket System"
],
"logistics_units": [
"Truck M818 6x6"
],
"infantry_units": [
"Infantry M249",
"Infantry M4",
"MANPADS Stinger"
],
"air_defenses": [
"RolandGenerator",
"HawkGenerator"
],
"aircraft_carrier": [],
"helicopter_carrier": [
"LHA_Tarawa"
],
"destroyers": [
"USS_Arleigh_Burke_IIa"
],
"cruisers": [
"TICONDEROG"
],
"requirements": {
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974"
},
"carrier_names": [
"R91 Charles de Gaulle"
],
"helicopter_carrier_names": [
"R97 Jeanne d'Arc",
"L9013 Mistral",
"L9014 Tonerre",
"L9015 Dixmude"
],
"navy_generators": [
"ArleighBurkeGroupGenerator"
],
"has_jtac": true,
"jtac_unit": "SA 342L Gazelle"
}

View File

@@ -2,11 +2,12 @@
"country": "France",
"name": "France 1995",
"authors": "Khopa",
"description": "<p>France in the late 90s before Rafale introduction. A Mirage-2000 centric faction choice.</p>",
"description": "<p>France in the late 90s before Rafale introduction. A Mirage-2000 centric faction choice. Frenchpack 4.6+ mod is recommended to enable most of the ground units of this faction available.</p>",
"locales": [
"fr_FR"
],
"aircrafts": [
"C-130",
"Mirage 2000-5",
"Mirage 2000C",
"SA 342L Gazelle",
@@ -21,12 +22,19 @@
"KC-135 Stratotanker"
],
"frontline_units": [
"Cobra",
"LAV-25",
"AMX-10 RCR",
"AMX-13 90mm",
"AMX.30B2",
"Leclerc S\u00e9ries 2",
"Leclerc_XXI",
"Pamela",
"Panhard",
"Roland 2 (Marder Chassis)",
"TPz Fuchs",
"VAB Mephisto"
"VAB .50",
"VAB Mephisto",
"VAB T20/13",
"VBL .50",
"VBL AANF1"
],
"artillery_units": [
"M109A6 Paladin",
@@ -70,4 +78,4 @@
],
"has_jtac": true,
"jtac_unit": "MQ-9 Reaper"
}
}

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