Compare commits

...

59 Commits

Author SHA1 Message Date
Raffson
801e9efe8c Fix heli spawn/landing at FOB/FARP
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2719.

(cherry picked from commit 34de855b8f889b72406c101d0cea385988e24bf9)
(cherry picked from commit b41ef0ab13)
2023-02-05 14:41:47 -08:00
Dan Albert
5708fe625b Don't generate runway data for heliports.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2710.

(cherry picked from commit c33a0d5deb)
2023-02-05 14:41:47 -08:00
Dan Albert
1dda0c9497 Pin version of black used in GHA.
Black rolls out style changes every year, and using "stable" means that
the check run on PRs might start formatting differently than we do
locally, or require a reformat of the codebase to make a PR submittable.

Pin to the version that we've been using. We should update to 23 at some
point, but we want to do that deliberately.

(cherry picked from commit 937bacacb7)
2023-02-05 12:15:50 -08:00
Dan Albert
56d7641251 Fix line endings.
(cherry-picked from commit e8824e5d03)
2023-02-05 12:15:50 -08:00
Dan Albert
938d6b4bdf Fix invalid tanker planning.
All three refueling missions share a common task type and differentiate
their behavior based on the type and allegiance of the package target.
This means that if the theater commander identifies a carrier as the
best location for a theater refueling task, a recovery tanker will be
planned by mistake.

If this happens on a sunken carrier, mission generation will fail
because a recovery tanker cannot be generated for a sunken carrier.

Fix the crash and the misplanned theater tanker by preventing the
commander from choosing fleet control points as refueling targets.

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

(cherry picked from commit eea98b01f6)
2023-01-28 14:05:57 -08:00
Dan Albert
5caf07f476 Fix unit ID of the KS-19.
It seems like this unit has never worked because of the unit ID
mismatch.

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

(cherry picked from commit 0df268f331)
2023-01-28 12:10:38 -08:00
Dan Albert
d1d4fde6a3 Update game version to 6.1.1. 2023-01-28 12:10:38 -08:00
RndName
440fe2e019 Only check for ground units in capture trigger zone
requires pydcs update: https://github.com/pydcs/dcs/pull/279

(cherry picked from commit f9903f1e19)
2022-12-21 13:26:42 -08:00
RndName
c82bc1c211 Change iads command unit type
(cherry picked from commit 4a4935f165)
2022-12-21 13:26:42 -08:00
RndName
f855d2956c Only add skynet iads command unit when advanced iads is in use
(cherry picked from commit 09f92cc5e4)
2022-12-21 13:26:42 -08:00
Dan Albert
24aebf5ff6 Update pydcs.
Includes UnitType parameter of AllOfCoalition in/out zone condition.

(cherry picked from commit 887e5997c2)
2022-12-21 13:26:42 -08:00
Dan Albert
8004dccdb2 Update changelog for Blackshark 3.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2657.

(cherry picked from commit f88a50dd07)
2022-12-21 12:43:05 -08:00
Dan Albert
7e7cb4f0bc Add icons and banners for BS3.
https://github.com/dcs-liberation/dcs_liberation/issues/2657
(cherry picked from commit 3f12a5ae3d)
2022-12-21 12:43:05 -08:00
Dan Albert
801e3afb98 Add BS3 loadouts.
https://github.com/dcs-liberation/dcs_liberation/issues/2657
(cherry picked from commit f2946817bf)
2022-12-21 12:43:05 -08:00
Dan Albert
48ea33611d Add BS3 to factions that have BS2.
https://github.com/dcs-liberation/dcs_liberation/issues/2657
(cherry picked from commit 935a9b0631)
2022-12-21 12:43:05 -08:00
Dan Albert
7b588c5437 Add blackshark 3 yaml.
There are no significant notable changes from Blackshark 2, so this is
the same YAML.

https://github.com/dcs-liberation/dcs_liberation/issues/2657
(cherry picked from commit c0dc411102)
2022-12-21 12:43:05 -08:00
Dan Albert
e85b1a3195 Add Blackshark 3 to the mission planning DB.
https://github.com/dcs-liberation/dcs_liberation/issues/2657
(cherry picked from commit 55037626a4)
2022-12-21 12:43:05 -08:00
Dan Albert
8132b9a080 Fix type-only import that needs to be real.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2660.

(cherry picked from commit fc3e72bacf)
2022-12-21 12:43:05 -08:00
Dan Albert
8e0cf2284b Update changelog for pydcs update.
(cherry picked from commit 66523301aa)
2022-12-21 12:43:05 -08:00
Dan Albert
87405ba58a Expand gitattributes to cover common files.
Specifically adding this so that yaml changes stop being uploaded as
CRLF, but the rest is likely useful too.

(cherry picked from commit 54546aaefb)
2022-12-21 12:43:05 -08:00
Dan Albert
0952cf4c75 Update bug templates to 6.0.0.
We're not fixing 5.x bugs any more.

(cherry picked from commit ded5fc8b1d)
2022-12-21 12:43:05 -08:00
SnappyComebacks
5bf5b41f2d Add Recovery Tankers (#2661)
Add support for recovery tankers at aircraft carriers.

Cherry picked from 9a81121ac1 and
0fd0f0e7c0
2022-12-21 05:17:02 +00:00
Dan Albert
715c60583a Save the last turn for bug reports.
We often get save games uploaded with bug reports that are already in a
broken state with nothing we can do about it. This saves that turn to
`last_turn.liberation` so users are less likely to have clobbered the
useful data before filing the report.

(cherry picked from commit 22503d4e95)
2022-12-20 14:09:25 -08:00
Dan Albert
5dcd1e9360 Fix the channel's landmap.
Caused by a bad rename when I did the migration. The landmap in 6.0.0
was actually a GIF...

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

(cherry picked from commit de9236e93a)
2022-12-20 13:06:15 -08:00
SnappyComebacks
ef7978e887 Update F-15E loadouts.
(cherry picked from commit a245ba80c3)
2022-12-14 22:00:06 -07:00
Dan Albert
8cfb1790b2 Add new British navy units to the UK faction.
I haven't removed the old US navy stuff from this faction, since all the
new UK ships are frigates, and removing the US stuff would deprive the
faction of cruisers and destroyers, which might break generation (I'm
not familiar enough with the new system to say for sure).

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

(cherry picked from commit 9a1860fc5e)
2022-12-13 00:14:48 -08:00
Dan Albert
41203b9bed Add HMS Invincible as a valid runway.
(cherry picked from commit f3f5ab70ea)
2022-12-13 00:14:48 -08:00
ColonelAkirNakesh
50fb1f5d97 Add HMS Invincible.
(cherry picked from commit 7f916d55e7)
2022-12-13 00:14:48 -08:00
Dan Albert
e041fa3c70 Fix changelog formatting.
(cherry picked from commit 6ce7638fdc)
2022-12-12 23:43:09 -08:00
Dan Albert
f358c1c0b2 Add missing changelog note.
(cherry picked from commit 43ea019091)
2022-12-12 21:33:12 -08:00
ColonelAkirNakesh
a18dbe0a72 Add support for Leander class HMS Andromeda.
https://github.com/dcs-liberation/dcs_liberation/issues/2571
(cherry picked from commit 7673ca5481)
2022-12-12 19:30:26 -08:00
ColonelAkirNakesh
00e7a1fe55 Add support for Leander class HMS Ariadne.
https://github.com/dcs-liberation/dcs_liberation/issues/2571
(cherry picked from commit 774a37a7d2)
2022-12-12 19:30:26 -08:00
ColonelAkirNakesh
b664a8cac5 Add support for Castle Class.
https://github.com/dcs-liberation/dcs_liberation/issues/2571
(cherry picked from commit 905094f63f)
2022-12-12 19:30:26 -08:00
ColonelAkirNakesh
2c4678aa48 Add support for Leander class HMS Achilles.
https://github.com/dcs-liberation/dcs_liberation/issues/2571
(cherry picked from commit fd5b7ba49d)
2022-12-12 19:30:26 -08:00
Dan Albert
eafc70a5e5 Fix livery for VF-33 F-14A squadron.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2610.

(cherry picked from commit 6025cad716)
2022-12-12 19:30:26 -08:00
Dan Albert
7fb1d25887 Add changelog notes that were missed in PRs.
(cherry picked from commit 28859a8a9c)
2022-12-12 19:30:26 -08:00
Dan Albert
d9491c47b8 Fix encoding issues with the Peru faction.
Not really sure what's going on here, but presumably it's UTF-16 and
UTF-8 fighting.

(cherry picked from commit 9365aea724)
2022-12-12 19:30:26 -08:00
DillieKoe
2c26f366a8 Add Peru faction.
A new Peru faction with a mirage squadon

(cherry picked from commit 304fd7ea80)
2022-12-12 19:30:26 -08:00
ColonelAkirNakesh
c30636b4d2 Override liveries for Russian aircraft in bluefor faction.
(cherry picked from commit 5e345263a7)
2022-12-12 18:47:44 -08:00
dependabot[bot]
a3a91c5771 Bump certifi from 2022.6.15 to 2022.12.7
Bumps [certifi](https://github.com/certifi/python-certifi) from 2022.6.15 to 2022.12.7.
- [Release notes](https://github.com/certifi/python-certifi/releases)
- [Commits](https://github.com/certifi/python-certifi/compare/2022.06.15...2022.12.07)

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

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 445ee25bbf)
2022-12-12 17:50:19 -08:00
Dan Albert
3d64619481 Fix CAS not having landing waypoints.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2611.

(cherry picked from commit 20937815f8)
2022-12-03 17:01:26 -08:00
RndName
3b97f7a2ba Fix manual debrief submit not ending mission
cherry-pick from 3863b8ef40
2022-11-30 13:29:40 +01:00
SnappyComebacks
6779ee96be Add F-15E to DEAD_CAPABLE in AI flight planner.
(cherry picked from commit 1b828b95b3)
2022-11-27 22:16:30 -07:00
Dan Albert
d9134650dc Branch for 6.1. 2022-11-27 21:07:47 -08:00
Dan Albert
4ef2cc26c8 Remove incompatible campaigns.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2558.
2022-11-25 15:29:49 -08:00
Starfire13
a03cfdf305 Updates Khopa's Normandy and Channel campaign to 10.0
Note that Operation Dynamo only requires a yaml file update. The .miz file is fine and is not included here.
2022-11-25 15:29:49 -08:00
Dan Albert
9777e5e432 Remove dead code. 2022-11-25 15:29:49 -08:00
Dan Albert
5f74fd81eb Unfilter the custom waypoint targets.
There doesn't appear to be any reason for us to be poking at
implementation details here aside from changing the name from "unit" to
"building" for that case. Just iterate over the known strike targets.

Making this change uncovered some latent type errors.

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

(cherry picked from commit 5e7e5e2636)
2022-11-25 14:22:11 -08:00
Dan Albert
b57e30a13c Add radios for the MB-339A.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2511.

(cherry picked from commit e208df16b2)
2022-11-25 14:15:23 -08:00
Dan Albert
789c863922 Update RoleplayingPleb's campaigns.
https://github.com/dcs-liberation/dcs_liberation/issues/2558
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2561.

(cherry picked from commit 11632b0ef1)
2022-11-25 13:15:50 -08:00
Dan Albert
3cbd4c7dd4 Make the casualty report scrollable.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2567.

(cherry picked from commit b0bc46f539)
2022-11-25 13:15:50 -08:00
SnappyComebacks
9d0c75d199 Add banner for MB-339A.
(cherry picked from commit 627ed45065)
2022-11-25 12:14:52 -07:00
RndName
8e63aa4d72 Remove dcs capture event from state json
With the latest change we added capture zones and corresponding trigger rules for all Airfields as well so we do not need to rely on the dcs capture event S_EVENT_BASE_CAPTURED anymore.

cherry-pick from fc9ad5b519
2022-11-24 11:29:16 +01:00
RndName
06a4dce555 Add Airfield to list of capture zone types
This will create capture zones and the trigger rules to check for a base capture. Will fix an issue where the dcs capture event is not fired and therefore the capture not recognized by liberation

cherry-pick from 40ddad1d9a
2022-11-24 11:29:16 +01:00
RndName
65a232a0c7 Fix carrier group generation
cherry-pick from eb997db703
2022-11-24 11:29:16 +01:00
MetalStormGhost
bde22b52ea Attempt at fixing Carrier killed in state.json but not being removed from game, issue #2405. GenericCarrierGenerator.generate() will now generate the ship group with an array that only contains alive ship units, just like GroundObjectGenerator.generate() has previously done.
Carrier groups will now also show up as destroyed/damaged on the map when the carrier is sunk.

cherry-pick from e53dc5b80b
2022-11-24 11:29:16 +01:00
RndName
bfed69573f Validate primary and secondary nodes for iads network
cherry-pick from ab64655f05
2022-11-21 12:23:40 +01:00
RndName
9ba717fd82 Fix IADS network error caused by dead groups
Fixed an error which would occur when dead units which are non static would be added as secondary node during the skynet lua data generation. This should in general not be possible as connection nodes and power sources are currently most of the time static.

cherry-pick from e1b530e4fc
2022-11-21 12:23:40 +01:00
SnappyComebacks
f2e8a77862 Update pydcs.
Added ice halo generation.

(cherry picked from commit 4414853e45)
2022-11-20 18:03:58 -07:00
93 changed files with 1498 additions and 2187 deletions

76
.gitattributes vendored
View File

@@ -14,3 +14,79 @@
*.pyo binary export-ignore *.pyo binary export-ignore
*.pyd binary *.pyd binary
unshipped_data/arcgis_maps/ filter=lfs diff=lfs merge=lfs -text unshipped_data/arcgis_maps/ filter=lfs diff=lfs merge=lfs -text
# https://github.com/alexkaratarakis/gitattributes/blob/master/Common.gitattributes
# Documents
*.bibtex text diff=bibtex
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain
*.md text diff=markdown
*.mdx text diff=markdown
*.tex text diff=tex
*.adoc text
*.textile text
*.mustache text
*.csv text
*.tab text
*.tsv text
*.txt text
*.sql text
*.epub diff=astextplain
# Graphics
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.tif binary
*.tiff binary
*.ico binary
# SVG treated as text by default.
*.svg text
# If you want to treat it as binary,
# use the following line instead.
# *.svg binary
*.eps binary
# Scripts
*.bash text eol=lf
*.fish text eol=lf
*.sh text eol=lf
*.zsh text eol=lf
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.ps1 text eol=crlf
# Serialisation
*.json text
*.toml text
*.xml text
*.yaml text
*.yml text
# Archives
*.7z binary
*.gz binary
*.tar binary
*.tgz binary
*.zip binary
# Text files where line endings should be preserved
*.patch -text
#
# Exclude files from exporting
#
.gitattributes export-ignore
.gitignore export-ignore
.gitkeep export-ignore

View File

@@ -31,12 +31,13 @@ body:
If the bug was found in a development build, select "Development build" If the bug was found in a development build, select "Development build"
and provide a link to the build in the field below. and provide a link to the build in the field below.
options: options:
- 5.2.0 - 6.0.0
- Development build - Development build
- type: textarea - type: textarea
attributes: attributes:
label: Build information label: Build information
description: The build information from the Help -> Report an issue window. description:
The build information from the Help -> Report an issue window.
- type: textarea - type: textarea
attributes: attributes:
label: Description label: Description
@@ -52,18 +53,17 @@ body:
description: > description: >
Attach any files needed to reproduce the bug here. **A save game is Attach any files needed to reproduce the bug here. **A save game is
required.** We typically cannot help without a save game (the required.** We typically cannot help without a save game (the
`.liberation` file found in `.liberation` file found in `%USERPROFILE%/Saved
`%USERPROFILE%/Saved Games/DCS/Liberation/Saves`), so most bugs filed Games/DCS/Liberation/Saves`), so most bugs filed without saved games
without saved games will be closed without investigation. will be closed without investigation.
Other useful files to include are: Other useful files to include are:
The Liberation log file. The log file is located at The Liberation log file. The log file is located at `<Liberation install
`<Liberation install directory>/logs/liberation.log`. The log often directory>/logs/liberation.log`. The log often includes data about
includes data about non-fatal errors that could be the root cause of the non-fatal errors that could be the root cause of the problem.
problem.
The `liberation_nextturn.miz` or a track file. This should always be The `liberation_nextturn.miz` or a track file. This should always be
@@ -73,11 +73,12 @@ body:
The `state.json` file for the most recently completed turn, located at The `state.json` file for the most recently completed turn, located at
`<Liberation install directory>/state.json`. This file is essential for `<Liberation install directory>/state.json`. This file is essential for
investigating any issues with end-of-turn results processing. investigating any issues with end-of-turn results processing. **If you
include this file, also include `last_turn.liberation`.**
You can attach files to the bug by dragging and dropping the file You can attach files to the bug by dragging and dropping the file into
into this text box. GitHub will not allow uploads of all file types, so this text box. GitHub will not allow uploads of all file types, so
attach a zip of the files if needed. attach a zip of the files if needed.
validations: validations:
required: true required: true

View File

@@ -39,12 +39,13 @@ body:
If the bug was found in a development build, select "Development build" If the bug was found in a development build, select "Development build"
and provide a link to the build in the field below. and provide a link to the build in the field below.
options: options:
- 5.2.0 - 6.0.0
- Development build - Development build
- type: textarea - type: textarea
attributes: attributes:
label: Build information label: Build information
description: The build information from the Help -> Report an issue window. description:
The build information from the Help -> Report an issue window.
- type: input - type: input
attributes: attributes:
label: Campaign name label: Campaign name
@@ -59,8 +60,8 @@ body:
label: Blue faction label: Blue faction
description: > description: >
The name of the blue faction you selected. If the bug only occurs with a The name of the blue faction you selected. If the bug only occurs with a
custom faction (or modifications to a stock faction), upload the custom faction (or modifications to a stock faction), upload the faction
faction file as an attachment to the bug description field. file as an attachment to the bug description field.
validations: validations:
required: true required: true
- type: input - type: input
@@ -68,8 +69,8 @@ body:
label: Red faction label: Red faction
description: > description: >
The name of the red faction you selected. If the bug only occurs with a The name of the red faction you selected. If the bug only occurs with a
custom faction (or modifications to a stock faction), upload the custom faction (or modifications to a stock faction), upload the faction
faction file as an attachment to the bug description field. file as an attachment to the bug description field.
validations: validations:
required: true required: true
- type: textarea - type: textarea
@@ -102,11 +103,11 @@ body:
attributes: attributes:
label: Log file label: Log file
description: > description: >
Attach the Liberation log file. The log file is located at Attach the Liberation log file. The log file is located at `<Liberation
`<Liberation install directory>/logs/liberation.log`. install directory>/logs/liberation.log`.
You can attach files to the bug by dragging and dropping the file You can attach files to the bug by dragging and dropping the file into
into this text box. this text box.
validations: validations:
required: true required: true

View File

@@ -11,6 +11,7 @@ jobs:
- uses: actions/setup-python@v2 - uses: actions/setup-python@v2
- uses: psf/black@stable - uses: psf/black@stable
with: with:
version: ~=22.12
src: "." src: "."
options: "--check" options: "--check"

View File

@@ -1,3 +1,36 @@
# 6.1.1
## Fixes
* **[Data]** Fixed unit ID for the KS-19 AAA. KS-19 would not previously generate correctly in missions. A new game is required for this fix to take effect.
* **[Flight Planning]** Automatic flight planning will no longer accidentally plan a recovery tanker instead of a theater refueling package. This fixes a potential crash during mission generation when opfor plans a refueling task at a sunk carrier. You'll need to skip the current turn to force opfor to replan their flights to get the fix.
* **[Mission Generation]** Using heliports (airports without any runways) will no longer cause mission generation to fail.
* **[Mission Generation]** Prevent helicopters from spawning into collisions at FARPs when more than one flight uses the same FARP.
# 6.1.0
Saves from 6.0.0 are compatible with 6.1.0
## Features/Improvements
* **[Engine]** Support for DCS 2.8.1.34437, including Blackshark 3.
* **[Factions]** Defaulted bluefor modern to use Georgian and Ukrainian liveries for Russian aircraft.
* **[Factions]** Added Peru.
* **[Flight Planning]** AEW&C and Refueling flights are now plannable on LHA carriers.
* **[Flight Planning]** Refueling flights planned on aircraft carriers will act as a recovery tanker for the carrier.
* **[Loadouts]** Adjusted F-15E loadouts.
* **[Mission Generation]** The previous turn will now be saved as last_turn.liberation when submitting mission results. This is often essential for debugging bug reports. **Include this file in the bug report whenever it is available.**
* **[Modding]** Added support for the HMS Ariadne, Achilles, and Castle class.
* **[Modding]** Added HMS Invincible to the game data as a helicopter carrier.
## Fixes
* **[Flight Planning]** Fixes CAS flights not having landing waypoints.
* **[Mission Generation]** Airbase and FOB capture is no longer blocked by grounded aircraft / helicopters.
* **[Squadrons]** Fixed the livery for the VF-33 F-14A squadron.
* **[Theaters]** Fixed Channel campaigns not having data for land/sea/obstacle boundaries, causing front lines to extend into forests and water. Requires a new campaign to get the fix.
* **[UI]** Fixed an issue where manual submit of mission results did not end the mission correctly.
# 6.0.0 # 6.0.0
Saves from 5.x are not compatible with 6.0. Saves from 5.x are not compatible with 6.0.
@@ -16,6 +49,7 @@ Saves from 5.x are not compatible with 6.0.
* **[Mission Generation]** Added performance option to not cull IADS when culling would affect how mission is played at target area. * **[Mission Generation]** Added performance option to not cull IADS when culling would affect how mission is played at target area.
* **[Mission Generation]** Reworked the ground object generation which now uses a new layout system * **[Mission Generation]** Reworked the ground object generation which now uses a new layout system
* **[Mission Generation]** Added information about the modulation (AM/FM) of the assigned frequencies to the kneeboard and assign AM modulation instead of FM for JTAC. * **[Mission Generation]** Added information about the modulation (AM/FM) of the assigned frequencies to the kneeboard and assign AM modulation instead of FM for JTAC.
* **[Mission Generation]** Added ice halos.
* **[Mission Generation]** Adjusted wind speeds. Wind speeds at high altitude are generally higher now. * **[Mission Generation]** Adjusted wind speeds. Wind speeds at high altitude are generally higher now.
* **[Mission Generation]** Added turbulence. Higher in Summer and Winter, also higher at day time than at nighttime. * **[Mission Generation]** Added turbulence. Higher in Summer and Winter, also higher at day time than at nighttime.
* **[Modding]** Updated UH-60L mod version support to 1.3.1 * **[Modding]** Updated UH-60L mod version support to 1.3.1

View File

@@ -10,6 +10,7 @@ from dcs.helicopters import (
CH_47D, CH_47D,
CH_53E, CH_53E,
Ka_50, Ka_50,
Ka_50_3,
Mi_24P, Mi_24P,
Mi_24V, Mi_24V,
Mi_26, Mi_26,
@@ -237,6 +238,7 @@ CAS_CAPABLE = [
OH_58D, OH_58D,
SA342M, SA342M,
SA342L, SA342L,
Ka_50_3,
Ka_50, Ka_50,
Mi_28N, Mi_28N,
Mi_24P, Mi_24P,
@@ -308,6 +310,7 @@ SEAD_CAPABLE = [
# Aircraft used for DEAD tasks. Must be capable of the CAS DCS task. # Aircraft used for DEAD tasks. Must be capable of the CAS DCS task.
DEAD_CAPABLE = SEAD_CAPABLE + [ DEAD_CAPABLE = SEAD_CAPABLE + [
AJS37, AJS37,
F_15E,
F_14B, F_14B,
F_14A_135_GR, F_14A_135_GR,
JAS39Gripen_AG, JAS39Gripen_AG,

View File

@@ -29,7 +29,7 @@ class CasLayout(PatrollingLayout):
yield self.target yield self.target
yield self.patrol_end yield self.patrol_end
yield from self.nav_from yield from self.nav_from
yield self.departure yield self.arrival
if self.divert is not None: if self.divert is not None:
yield self.divert yield self.divert
yield self.bullseye yield self.bullseye

View File

@@ -3,6 +3,8 @@ from __future__ import annotations
from typing import Any, TYPE_CHECKING, Type from typing import Any, TYPE_CHECKING, Type
from game.ato import FlightType from game.ato import FlightType
from game.theater.controlpoint import NavalControlPoint
from game.theater.frontline import FrontLine
from .aewc import AewcFlightPlan from .aewc import AewcFlightPlan
from .airassault import AirAssaultFlightPlan from .airassault import AirAssaultFlightPlan
from .airlift import AirliftFlightPlan from .airlift import AirliftFlightPlan
@@ -19,6 +21,7 @@ from .ocarunway import OcaRunwayFlightPlan
from .packagerefueling import PackageRefuelingFlightPlan from .packagerefueling import PackageRefuelingFlightPlan
from .planningerror import PlanningError from .planningerror import PlanningError
from .sead import SeadFlightPlan from .sead import SeadFlightPlan
from .shiprecoverytanker import RecoveryTankerFlightPlan
from .strike import StrikeFlightPlan from .strike import StrikeFlightPlan
from .sweep import SweepFlightPlan from .sweep import SweepFlightPlan
from .tarcap import TarCapFlightPlan from .tarcap import TarCapFlightPlan
@@ -26,15 +29,19 @@ from .theaterrefueling import TheaterRefuelingFlightPlan
if TYPE_CHECKING: if TYPE_CHECKING:
from game.ato import Flight from game.ato import Flight
from game.theater import FrontLine
class FlightPlanBuilderTypes: class FlightPlanBuilderTypes:
@staticmethod @staticmethod
def for_flight(flight: Flight) -> Type[IBuilder[Any, Any]]: def for_flight(flight: Flight) -> Type[IBuilder[Any, Any]]:
if flight.flight_type is FlightType.REFUELING: if flight.flight_type is FlightType.REFUELING:
if flight.package.target.is_friendly(flight.squadron.player) or isinstance( target = flight.package.target
flight.package.target, FrontLine if target.is_friendly(flight.squadron.player) and isinstance(
target, NavalControlPoint
):
return RecoveryTankerFlightPlan.builder_type()
if target.is_friendly(flight.squadron.player) or isinstance(
target, FrontLine
): ):
return TheaterRefuelingFlightPlan.builder_type() return TheaterRefuelingFlightPlan.builder_type()
return PackageRefuelingFlightPlan.builder_type() return PackageRefuelingFlightPlan.builder_type()

View File

@@ -0,0 +1,91 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import timedelta
from typing import Iterator, Type
from game.ato.flightplans.standard import StandardFlightPlan, StandardLayout
from game.ato.flightplans.ibuilder import IBuilder
from game.ato.flightplans.standard import StandardLayout
from game.ato.flightplans.waypointbuilder import WaypointBuilder
from game.ato.flightwaypoint import FlightWaypoint
@dataclass(frozen=True)
class RecoveryTankerLayout(StandardLayout):
nav_to: list[FlightWaypoint]
recovery_ship: FlightWaypoint
nav_from: list[FlightWaypoint]
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
yield self.departure
yield from self.nav_to
yield self.recovery_ship
yield from self.nav_from
yield self.arrival
if self.divert is not None:
yield self.divert
yield self.bullseye
class RecoveryTankerFlightPlan(StandardFlightPlan[RecoveryTankerLayout]):
@staticmethod
def builder_type() -> Type[Builder]:
return Builder
@property
def tot_waypoint(self) -> FlightWaypoint:
return self.layout.recovery_ship
@property
def mission_departure_time(self) -> timedelta:
return self.patrol_end_time
@property
def patrol_start_time(self) -> timedelta:
return self.package.time_over_target
@property
def patrol_end_time(self) -> timedelta:
return self.tot + timedelta(hours=2)
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
if waypoint == self.tot_waypoint:
return self.tot
return None
def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> timedelta | None:
if waypoint == self.tot_waypoint:
return self.mission_departure_time
return None
class Builder(IBuilder[RecoveryTankerFlightPlan, RecoveryTankerLayout]):
def layout(self) -> RecoveryTankerLayout:
builder = WaypointBuilder(self.flight, self.coalition)
# TODO: Propagate the ship position to the Tanker's TOT,
# so that we minimize the tanker's need to catch up to the carrier.
recovery_ship = self.package.target.position
recovery_tanker = builder.recovery_tanker(recovery_ship)
# We don't have per aircraft cruise altitudes, so just reuse patrol altitude?
tanker_type = self.flight.unit_type
nav_cruise_altitude = tanker_type.preferred_patrol_altitude
return RecoveryTankerLayout(
departure=builder.takeoff(self.flight.departure),
nav_to=builder.nav_path(
self.flight.departure.position, recovery_ship, nav_cruise_altitude
),
nav_from=builder.nav_path(
recovery_ship, self.flight.arrival.position, nav_cruise_altitude
),
recovery_ship=recovery_tanker,
arrival=builder.land(self.flight.arrival),
divert=builder.divert(self.flight.divert),
bullseye=builder.bullseye(),
)
def build(self) -> RecoveryTankerFlightPlan:
return RecoveryTankerFlightPlan(self.flight, self.layout())

View File

@@ -23,7 +23,7 @@ from game.theater import (
TheaterGroundObject, TheaterGroundObject,
TheaterUnit, TheaterUnit,
) )
from game.utils import Distance, meters, nautical_miles from game.utils import Distance, feet, meters, nautical_miles
if TYPE_CHECKING: if TYPE_CHECKING:
from game.coalition import Coalition from game.coalition import Coalition
@@ -204,6 +204,19 @@ class WaypointBuilder:
pretty_name="Refuel", pretty_name="Refuel",
) )
def recovery_tanker(self, position: Point) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO"
return FlightWaypoint(
"RECOVERY",
FlightWaypointType.RECOVERY_TANKER,
position,
feet(6000),
alt_type,
description="Recovery tanker for aircraft carriers",
pretty_name="Recovery",
)
def split(self, position: Point) -> FlightWaypoint: def split(self, position: Point) -> FlightWaypoint:
alt_type: AltitudeReference = "BARO" alt_type: AltitudeReference = "BARO"
if self.is_helo: if self.is_helo:

View File

@@ -1,6 +1,5 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Sequence
from dataclasses import dataclass, field from dataclasses import dataclass, field
from datetime import timedelta from datetime import timedelta
from typing import Literal, TYPE_CHECKING from typing import Literal, TYPE_CHECKING
@@ -12,8 +11,7 @@ from game.theater.theatergroup import TheaterUnit
from game.utils import Distance, meters from game.utils import Distance, meters
if TYPE_CHECKING: if TYPE_CHECKING:
from game.theater import ControlPoint, MissionTarget from game.theater import ControlPoint
AltitudeReference = Literal["BARO", "RADIO"] AltitudeReference = Literal["BARO", "RADIO"]
@@ -32,7 +30,7 @@ class FlightWaypoint:
# having three names. A short and long form is enough. # having three names. A short and long form is enough.
description: str = "" description: str = ""
targets: Sequence[MissionTarget | TheaterUnit] = field(default_factory=list) targets: list[TheaterUnit] = field(default_factory=list)
obj_name: str = "" obj_name: str = ""
pretty_name: str = "" pretty_name: str = ""
only_for_player: bool = False only_for_player: bool = False

View File

@@ -49,3 +49,4 @@ class FlightWaypointType(IntEnum):
REFUEL = 29 # Should look for nearby tanker to refuel from. REFUEL = 29 # Should look for nearby tanker to refuel from.
CARGO_STOP = 30 # Stopover landing point using the LandingReFuAr waypoint type CARGO_STOP = 30 # Stopover landing point using the LandingReFuAr waypoint type
INGRESS_AIR_ASSAULT = 31 INGRESS_AIR_ASSAULT = 31
RECOVERY_TANKER = 32

View File

@@ -5,6 +5,7 @@ import operator
from collections.abc import Iterable, Iterator from collections.abc import Iterable, Iterator
from typing import TYPE_CHECKING, TypeVar from typing import TYPE_CHECKING, TypeVar
from game.ato.closestairfields import ClosestAirfields, ObjectiveDistanceCache
from game.theater import ( from game.theater import (
Airfield, Airfield,
ControlPoint, ControlPoint,
@@ -15,12 +16,11 @@ from game.theater import (
) )
from game.theater.theatergroundobject import ( from game.theater.theatergroundobject import (
BuildingGroundObject, BuildingGroundObject,
IadsBuildingGroundObject,
IadsGroundObject, IadsGroundObject,
NavalGroundObject, NavalGroundObject,
IadsBuildingGroundObject,
) )
from game.utils import meters, nautical_miles from game.utils import meters, nautical_miles
from game.ato.closestairfields import ClosestAirfields, ObjectiveDistanceCache
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
@@ -209,22 +209,20 @@ class ObjectiveFinder:
raise RuntimeError("Found no friendly control points. You probably lost.") raise RuntimeError("Found no friendly control points. You probably lost.")
return farthest return farthest
def closest_friendly_control_point(self) -> ControlPoint: def preferred_theater_refueling_control_point(self) -> ControlPoint | None:
"""Finds the friendly control point that is closest to any threats.""" """Finds the friendly control point that is closest to any threats."""
threat_zones = self.game.threat_zone_for(not self.is_player) threat_zones = self.game.threat_zone_for(not self.is_player)
closest = None closest = None
min_distance = meters(math.inf) min_distance = meters(math.inf)
for cp in self.friendly_control_points(): for cp in self.friendly_control_points():
if isinstance(cp, OffMapSpawn): if isinstance(cp, OffMapSpawn) or cp.is_fleet:
continue continue
distance = threat_zones.distance_to_threat(cp.position) distance = threat_zones.distance_to_threat(cp.position)
if distance < min_distance: if distance < min_distance:
closest = cp closest = cp
min_distance = distance min_distance = distance
if closest is None:
raise RuntimeError("Found no friendly control points. You probably lost.")
return closest return closest
def enemy_control_points(self) -> Iterator[ControlPoint]: def enemy_control_points(self) -> Iterator[ControlPoint]:

View File

@@ -153,6 +153,11 @@ class TheaterState(WorldState["TheaterState"]):
barcap_duration = coalition.doctrine.cap_duration.total_seconds() barcap_duration = coalition.doctrine.cap_duration.total_seconds()
barcap_rounds = math.ceil(mission_duration / barcap_duration) barcap_rounds = math.ceil(mission_duration / barcap_duration)
refueling_targets: list[MissionTarget] = []
theater_refuling_point = finder.preferred_theater_refueling_control_point()
if theater_refuling_point is not None:
refueling_targets.append(theater_refuling_point)
return TheaterState( return TheaterState(
context=context, context=context,
barcaps_needed={ barcaps_needed={
@@ -162,7 +167,7 @@ class TheaterState(WorldState["TheaterState"]):
front_line_stances={f: None for f in finder.front_lines()}, front_line_stances={f: None for f in finder.front_lines()},
vulnerable_front_lines=list(finder.front_lines()), vulnerable_front_lines=list(finder.front_lines()),
aewc_targets=[finder.farthest_friendly_control_point()], aewc_targets=[finder.farthest_friendly_control_point()],
refueling_targets=[finder.closest_friendly_control_point()], refueling_targets=refueling_targets,
enemy_air_defenses=list(finder.enemy_air_defenses()), enemy_air_defenses=list(finder.enemy_air_defenses()),
threatening_air_defenses=[], threatening_air_defenses=[],
detecting_air_defenses=[], detecting_air_defenses=[],

View File

@@ -338,9 +338,7 @@ class Debriefing:
seen = set() seen = set()
captures = [] captures = []
for capture in reversed(self.state_data.base_capture_events): for capture in reversed(self.state_data.base_capture_events):
# The ID string in the JSON file will be an airport ID for airport captures # The ID string in the JSON file will be the UUID generated from liberation
# but will be a UUID for all other types, since DCS doesn't know the UUIDs
# for the captured FOBs.
cp_id, new_owner_id_str, _name = capture.split("||") cp_id, new_owner_id_str, _name = capture.split("||")
# Only the most recent capture event matters. # Only the most recent capture event matters.
@@ -349,13 +347,8 @@ class Debriefing:
seen.add(cp_id) seen.add(cp_id)
try: try:
control_point = self.game.theater.find_control_point_by_airport_id(
int(cp_id)
)
except ValueError:
# The CP ID could not be converted to an int, so it's a UUID.
control_point = self.game.theater.find_control_point_by_id(UUID(cp_id)) control_point = self.game.theater.find_control_point_by_id(UUID(cp_id))
except KeyError: except (KeyError, ValueError):
# Captured base is not a part of the campaign. This happens when neutral # Captured base is not a part of the campaign. This happens when neutral
# bases are near the conflict. Nothing to do. # bases are near the conflict. Nothing to do.
continue continue

View File

@@ -332,6 +332,8 @@ class Game:
from .server import EventStream from .server import EventStream
from .sim import GameUpdateEvents from .sim import GameUpdateEvents
persistency.save_last_turn_state(self)
events = GameUpdateEvents() events = GameUpdateEvents()
logging.info("Pass turn") logging.info("Pass turn")

View File

@@ -24,6 +24,7 @@ from dcs.unitgroup import FlyingGroup
from game.ato import Flight, FlightType from game.ato import Flight, FlightType
from game.ato.flightplans.aewc import AewcFlightPlan from game.ato.flightplans.aewc import AewcFlightPlan
from game.ato.flightplans.shiprecoverytanker import RecoveryTankerFlightPlan
from game.ato.flightplans.theaterrefueling import TheaterRefuelingFlightPlan from game.ato.flightplans.theaterrefueling import TheaterRefuelingFlightPlan
@@ -246,7 +247,10 @@ class AircraftBehavior:
def configure_refueling(self, group: FlyingGroup[Any], flight: Flight) -> None: def configure_refueling(self, group: FlyingGroup[Any], flight: Flight) -> None:
group.task = Refueling.name group.task = Refueling.name
if not isinstance(flight.flight_plan, TheaterRefuelingFlightPlan): if not (
isinstance(flight.flight_plan, TheaterRefuelingFlightPlan)
or isinstance(flight.flight_plan, RecoveryTankerFlightPlan)
):
logging.error( logging.error(
f"Cannot configure racetrack refueling tasks for {flight} because it " f"Cannot configure racetrack refueling tasks for {flight} because it "
"does not have an racetrack refueling flight plan." "does not have an racetrack refueling flight plan."

View File

@@ -26,6 +26,7 @@ from game.settings import Settings
from game.theater.controlpoint import ( from game.theater.controlpoint import (
Airfield, Airfield,
ControlPoint, ControlPoint,
Fob,
) )
from game.unitmap import UnitMap from game.unitmap import UnitMap
from .aircraftpainter import AircraftPainter from .aircraftpainter import AircraftPainter
@@ -177,6 +178,15 @@ class AircraftGenerator:
self.mission_data, self.mission_data,
dynamic_runways, dynamic_runways,
self.use_client, self.use_client,
self.unit_map,
).configure() ).configure()
) )
wpt = group.waypoint("LANDING")
if flight.is_helo and isinstance(flight.arrival, Fob) and wpt:
hpad = self.helipads[flight.arrival].units.pop(0)
wpt.helipad_id = hpad.id
wpt.link_unit = hpad.id
self.helipads[flight.arrival].units.append(hpad)
return group return group

View File

@@ -10,6 +10,7 @@ from dcs.unit import Skill
from dcs.unitgroup import FlyingGroup from dcs.unitgroup import FlyingGroup
from game.ato import Flight, FlightType from game.ato import Flight, FlightType
from game.ato.flightplans.shiprecoverytanker import RecoveryTankerFlightPlan
from game.callsigns import callsign_for_support_unit from game.callsigns import callsign_for_support_unit
from game.data.weapons import Pylon, WeaponType as WeaponTypeEnum from game.data.weapons import Pylon, WeaponType as WeaponTypeEnum
from game.missiongenerator.missiondata import MissionData, AwacsInfo, TankerInfo from game.missiongenerator.missiondata import MissionData, AwacsInfo, TankerInfo
@@ -19,6 +20,7 @@ from game.radio.radios import RadioFrequency, RadioRegistry
from game.radio.tacan import TacanBand, TacanRegistry, TacanUsage from game.radio.tacan import TacanBand, TacanRegistry, TacanUsage
from game.runways import RunwayData from game.runways import RunwayData
from game.squadrons import Pilot from game.squadrons import Pilot
from game.unitmap import UnitMap
from .aircraftbehavior import AircraftBehavior from .aircraftbehavior import AircraftBehavior
from .aircraftpainter import AircraftPainter from .aircraftpainter import AircraftPainter
from .flightdata import FlightData from .flightdata import FlightData
@@ -44,6 +46,7 @@ class FlightGroupConfigurator:
mission_data: MissionData, mission_data: MissionData,
dynamic_runways: dict[str, RunwayData], dynamic_runways: dict[str, RunwayData],
use_client: bool, use_client: bool,
unit_map: UnitMap,
) -> None: ) -> None:
self.flight = flight self.flight = flight
self.group = group self.group = group
@@ -56,6 +59,7 @@ class FlightGroupConfigurator:
self.mission_data = mission_data self.mission_data = mission_data
self.dynamic_runways = dynamic_runways self.dynamic_runways = dynamic_runways
self.use_client = use_client self.use_client = use_client
self.unit_map = unit_map
def configure(self) -> FlightData: def configure(self) -> FlightData:
AircraftBehavior(self.flight.flight_type).apply_to(self.flight, self.group) AircraftBehavior(self.flight.flight_type).apply_to(self.flight, self.group)
@@ -97,6 +101,7 @@ class FlightGroupConfigurator:
self.time, self.time,
self.game.settings, self.game.settings,
self.mission_data, self.mission_data,
self.unit_map,
).create_waypoints() ).create_waypoints()
return FlightData( return FlightData(
@@ -156,7 +161,9 @@ class FlightGroupConfigurator:
blue=self.flight.departure.captured, blue=self.flight.departure.captured,
) )
) )
elif isinstance(self.flight.flight_plan, TheaterRefuelingFlightPlan): elif isinstance(
self.flight.flight_plan, TheaterRefuelingFlightPlan
) or isinstance(self.flight.flight_plan, RecoveryTankerFlightPlan):
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y, TacanUsage.AirToAir) tacan = self.tacan_registry.alloc_for_band(TacanBand.Y, TacanUsage.AirToAir)
self.mission_data.tankers.append( self.mission_data.tankers.append(
TankerInfo( TankerInfo(

View File

@@ -230,16 +230,22 @@ class FlightGroupSpawner:
def _generate_at_cp_helipad(self, name: str, cp: ControlPoint) -> FlyingGroup[Any]: def _generate_at_cp_helipad(self, name: str, cp: ControlPoint) -> FlyingGroup[Any]:
try: try:
helipad = self.helipads[cp] helipad = self.helipads[cp]
except IndexError as ex: except IndexError:
raise NoParkingSlotError() raise NoParkingSlotError()
group = self._generate_at_group(name, helipad) group = self._generate_at_group(name, helipad)
if self.start_type is not StartType.COLD: if self.start_type is StartType.WARM:
group.points[0].type = "TakeOffParkingHot" group.points[0].type = "TakeOffParkingHot"
hpad = helipad.units[0]
for i in range(self.flight.count): for i in range(self.flight.count):
group.units[i].position = helipad.units[i].position group.units[i].position = hpad.position
group.units[i].heading = helipad.units[i].heading group.units[i].heading = hpad.heading
# pydcs has just `parking_id = None`, so mypy thinks str is invalid. Ought
# to fix pydcs, but that's not the kind of change we want to pull into the
# 6.1 branch, and frankly we should probably just improve pydcs's handling
# of FARPs instead.
group.units[i].parking_id = str(i + 1) # type: ignore
return group return group
def dcs_start_type(self) -> DcsStartType: def dcs_start_type(self) -> DcsStartType:

View File

@@ -12,6 +12,7 @@ from game.ato import Flight, FlightWaypoint
from game.ato.flightwaypointtype import FlightWaypointType from game.ato.flightwaypointtype import FlightWaypointType
from game.missiongenerator.missiondata import MissionData from game.missiongenerator.missiondata import MissionData
from game.theater import MissionTarget, TheaterUnit from game.theater import MissionTarget, TheaterUnit
from game.unitmap import UnitMap
TARGET_WAYPOINTS = ( TARGET_WAYPOINTS = (
FlightWaypointType.TARGET_GROUP_LOC, FlightWaypointType.TARGET_GROUP_LOC,
@@ -29,6 +30,7 @@ class PydcsWaypointBuilder:
mission: Mission, mission: Mission,
elapsed_mission_time: timedelta, elapsed_mission_time: timedelta,
mission_data: MissionData, mission_data: MissionData,
unit_map: UnitMap,
) -> None: ) -> None:
self.waypoint = waypoint self.waypoint = waypoint
self.group = group self.group = group
@@ -37,6 +39,7 @@ class PydcsWaypointBuilder:
self.mission = mission self.mission = mission
self.elapsed_mission_time = elapsed_mission_time self.elapsed_mission_time = elapsed_mission_time
self.mission_data = mission_data self.mission_data = mission_data
self.unit_map = unit_map
def build(self) -> MovingPoint: def build(self) -> MovingPoint:
waypoint = self.group.add_waypoint( waypoint = self.group.add_waypoint(

View File

@@ -0,0 +1,60 @@
from dcs.point import MovingPoint
from dcs.task import ActivateBeaconCommand, RecoveryTanker
from game.ato import FlightType
from game.utils import feet, knots
from .pydcswaypointbuilder import PydcsWaypointBuilder
class RecoveryTankerBuilder(PydcsWaypointBuilder):
def add_tasks(self, waypoint: MovingPoint) -> None:
assert self.flight.flight_type == FlightType.REFUELING
group_id = self._get_carrier_group_id()
speed = knots(250).meters_per_second
altitude = feet(6000).meters
# Last waypoint has index of 1.
# Give the tanker a end condition of the last carrier waypoint.
# If the carrier ever gets more than one waypoint this approach needs to change.
last_waypoint = 2
recovery_tanker = RecoveryTanker(group_id, speed, altitude, last_waypoint)
waypoint.add_task(recovery_tanker)
self.configure_tanker_tacan(waypoint)
def _get_carrier_group_id(self) -> int:
name = self.package.target.name
carrier_position = self.package.target.position
theater_objects = self.unit_map.theater_objects
for key, value in theater_objects.items():
# Check name and position in case there are multiple of same carrier.
if name in key and value.theater_unit.position == carrier_position:
return value.dcs_group_id
raise RuntimeError(
f"Could not find a carrier in the mission matching {name} at "
f"({carrier_position.x}, {carrier_position.y})"
)
def configure_tanker_tacan(self, waypoint: MovingPoint) -> None:
if self.flight.unit_type.dcs_unit_type.tacan:
tanker_info = self.mission_data.tankers[-1]
tacan = tanker_info.tacan
tacan_callsign = {
"Texaco": "TEX",
"Arco": "ARC",
"Shell": "SHL",
}.get(tanker_info.callsign)
waypoint.add_task(
ActivateBeaconCommand(
tacan.number,
tacan.band.value,
tacan_callsign,
bearing=True,
unit_id=self.group.units[0].id,
aa=True,
)
)

View File

@@ -1,8 +1,9 @@
import copy import copy
from dcs import Point
from dcs.planes import B_17G, B_52H, Tu_22M3 from dcs.planes import B_17G, B_52H, Tu_22M3
from dcs.point import MovingPoint from dcs.point import MovingPoint
from dcs.task import Bombing, OptFormation, WeaponType, Expend from dcs.task import Bombing, Expend, OptFormation, WeaponType
from .pydcswaypointbuilder import PydcsWaypointBuilder from .pydcswaypointbuilder import PydcsWaypointBuilder
@@ -21,7 +22,7 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
if not targets: if not targets:
return return
center = copy.copy(targets[0].position) center: Point = copy.copy(targets[0].position)
for target in targets[1:]: for target in targets[1:]:
center += target.position center += target.position
center /= len(targets) center /= len(targets)

View File

@@ -17,8 +17,12 @@ from game.ato.flightstate import InFlight, WaitingForStart
from game.ato.flightwaypointtype import FlightWaypointType from game.ato.flightwaypointtype import FlightWaypointType
from game.ato.starttype import StartType from game.ato.starttype import StartType
from game.missiongenerator.aircraft.waypoints.cargostop import CargoStopBuilder from game.missiongenerator.aircraft.waypoints.cargostop import CargoStopBuilder
from game.missiongenerator.aircraft.waypoints.recoverytanker import (
RecoveryTankerBuilder,
)
from game.missiongenerator.missiondata import MissionData from game.missiongenerator.missiondata import MissionData
from game.settings import Settings from game.settings import Settings
from game.unitmap import UnitMap
from game.utils import pairwise from game.utils import pairwise
from .baiingress import BaiIngressBuilder from .baiingress import BaiIngressBuilder
from .landingzone import LandingZoneBuilder from .landingzone import LandingZoneBuilder
@@ -50,6 +54,7 @@ class WaypointGenerator:
time: datetime, time: datetime,
settings: Settings, settings: Settings,
mission_data: MissionData, mission_data: MissionData,
unit_map: UnitMap,
) -> None: ) -> None:
self.flight = flight self.flight = flight
self.group = group self.group = group
@@ -58,6 +63,7 @@ class WaypointGenerator:
self.time = time self.time = time
self.settings = settings self.settings = settings
self.mission_data = mission_data self.mission_data = mission_data
self.unit_map = unit_map
def create_waypoints(self) -> tuple[timedelta, list[FlightWaypoint]]: def create_waypoints(self) -> tuple[timedelta, list[FlightWaypoint]]:
for waypoint in self.flight.points: for waypoint in self.flight.points:
@@ -135,6 +141,7 @@ class WaypointGenerator:
FlightWaypointType.PICKUP_ZONE: LandingZoneBuilder, FlightWaypointType.PICKUP_ZONE: LandingZoneBuilder,
FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder, FlightWaypointType.DROPOFF_ZONE: LandingZoneBuilder,
FlightWaypointType.REFUEL: RefuelPointBuilder, FlightWaypointType.REFUEL: RefuelPointBuilder,
FlightWaypointType.RECOVERY_TANKER: RecoveryTankerBuilder,
FlightWaypointType.CARGO_STOP: CargoStopBuilder, FlightWaypointType.CARGO_STOP: CargoStopBuilder,
} }
builder = builders.get(waypoint.waypoint_type, DefaultWaypointBuilder) builder = builders.get(waypoint.waypoint_type, DefaultWaypointBuilder)
@@ -145,6 +152,7 @@ class WaypointGenerator:
self.mission, self.mission,
self.elapsed_mission_time, self.elapsed_mission_time,
self.mission_data, self.mission_data,
self.unit_map,
) )
def _estimate_min_fuel_for(self, waypoints: list[FlightWaypoint]) -> None: def _estimate_min_fuel_for(self, waypoints: list[FlightWaypoint]) -> None:

View File

@@ -104,6 +104,7 @@ class GroundObjectGenerator:
self.add_trigger_zone_for_scenery(unit) self.add_trigger_zone_for_scenery(unit)
if ( if (
self.game.settings.plugin_option("skynetiads") self.game.settings.plugin_option("skynetiads")
and self.game.theater.iads_network.advanced_iads
and isinstance(group, IadsGroundGroup) and isinstance(group, IadsGroundGroup)
and group.iads_role.participate and group.iads_role.participate
): ):
@@ -147,7 +148,7 @@ class GroundObjectGenerator:
vehicle_unit.position = unit.position vehicle_unit.position = unit.position
vehicle_unit.heading = unit.position.heading.degrees vehicle_unit.heading = unit.position.heading.degrees
vehicle_group.add_unit(vehicle_unit) vehicle_group.add_unit(vehicle_unit)
self._register_theater_unit(unit, vehicle_group.units[-1]) self._register_theater_unit(vehicle_group.id, unit, vehicle_group.units[-1])
if vehicle_group is None: if vehicle_group is None:
raise RuntimeError(f"Error creating VehicleGroup for {group_name}") raise RuntimeError(f"Error creating VehicleGroup for {group_name}")
return vehicle_group return vehicle_group
@@ -180,7 +181,7 @@ class GroundObjectGenerator:
ship_unit.position = unit.position ship_unit.position = unit.position
ship_unit.heading = unit.position.heading.degrees ship_unit.heading = unit.position.heading.degrees
ship_group.add_unit(ship_unit) ship_group.add_unit(ship_unit)
self._register_theater_unit(unit, ship_group.units[-1]) self._register_theater_unit(ship_group.id, unit, ship_group.units[-1])
if ship_group is None: if ship_group is None:
raise RuntimeError(f"Error creating ShipGroup for {group_name}") raise RuntimeError(f"Error creating ShipGroup for {group_name}")
return ship_group return ship_group
@@ -194,7 +195,7 @@ class GroundObjectGenerator:
heading=unit.position.heading.degrees, heading=unit.position.heading.degrees,
dead=not unit.alive, dead=not unit.alive,
) )
self._register_theater_unit(unit, static_group.units[0]) self._register_theater_unit(static_group.id, unit, static_group.units[0])
@staticmethod @staticmethod
def enable_eplrs(group: VehicleGroup, unit_type: Type[VehicleType]) -> None: def enable_eplrs(group: VehicleGroup, unit_type: Type[VehicleType]) -> None:
@@ -209,10 +210,11 @@ class GroundObjectGenerator:
def _register_theater_unit( def _register_theater_unit(
self, self,
dcs_group_id: int,
theater_unit: TheaterUnit, theater_unit: TheaterUnit,
dcs_unit: Unit, dcs_unit: Unit,
) -> None: ) -> None:
self.unit_map.add_theater_unit_mapping(theater_unit, dcs_unit) self.unit_map.add_theater_unit_mapping(dcs_group_id, theater_unit, dcs_unit)
def add_trigger_zone_for_scenery(self, scenery: SceneryUnit) -> None: def add_trigger_zone_for_scenery(self, scenery: SceneryUnit) -> None:
# Align the trigger zones to the faction color on the DCS briefing/F10 map. # Align the trigger zones to the faction color on the DCS briefing/F10 map.
@@ -291,13 +293,13 @@ class GroundObjectGenerator:
self.m.triggerrules.triggers.append(t) self.m.triggerrules.triggers.append(t)
def generate_iads_command_unit(self, unit: SceneryUnit) -> None: def generate_iads_command_unit(self, unit: SceneryUnit) -> None:
# Creates a static Infantry Unit next to a scenery object. This is needed # Creates a static Unit (tyre with red flag) next to a scenery object. This is
# because skynet can not use map objects as Comms, Power or Command and needs a # needed because skynet can not use map objects as Comms, Power or Command and
# "real" unit to function correctly # needs a "real" unit to function correctly
self.m.static_group( self.m.static_group(
country=self.country, country=self.country,
name=unit.unit_name, name=unit.unit_name,
_type=dcs.vehicles.Infantry.Soldier_M4, _type=dcs.statics.Fortification.Black_Tyre_RF,
position=unit.position, position=unit.position,
heading=unit.position.heading.degrees, heading=unit.position.heading.degrees,
dead=not unit.alive, # Also spawn as dead! dead=not unit.alive, # Also spawn as dead!
@@ -405,7 +407,17 @@ class GenericCarrierGenerator(GroundObjectGenerator):
logging.warning(f"Found empty carrier group in {self.control_point}") logging.warning(f"Found empty carrier group in {self.control_point}")
continue continue
ship_group = self.create_ship_group(group.group_name, group.units, atc) ship_units = []
for unit in group.units:
if unit.alive:
# All alive Ships
ship_units.append(unit)
if not ship_units:
# No alive units in this group, continue with next group
continue
ship_group = self.create_ship_group(group.group_name, ship_units, atc)
# Always steam into the wind, even if the carrier is being moved. # Always steam into the wind, even if the carrier is being moved.
# There are multiple unsimulated hours between turns, so we can # There are multiple unsimulated hours between turns, so we can
@@ -414,7 +426,7 @@ class GenericCarrierGenerator(GroundObjectGenerator):
brc = self.steam_into_wind(ship_group) brc = self.steam_into_wind(ship_group)
# Set Carrier Specific Options # Set Carrier Specific Options
if g_id == 0: if g_id == 0 and self.control_point.runway_is_operational():
# Get Correct unit type for the carrier. # Get Correct unit type for the carrier.
# This will upgrade to super carrier if option is enabled # This will upgrade to super carrier if option is enabled
carrier_type = self.carrier_type carrier_type = self.carrier_type

View File

@@ -44,7 +44,7 @@ class Silence(Option):
class TriggerGenerator: class TriggerGenerator:
capture_zone_types = (Fob,) capture_zone_types = (Fob, Airfield)
capture_zone_flag = 600 capture_zone_flag = 600
def __init__(self, mission: Mission, game: Game) -> None: def __init__(self, mission: Mission, game: Game) -> None:
@@ -159,7 +159,9 @@ class TriggerGenerator:
flag = self.get_capture_zone_flag() flag = self.get_capture_zone_flag()
capture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger") capture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger")
capture_trigger.add_condition( capture_trigger.add_condition(
AllOfCoalitionOutsideZone(defending_coalition, trigger_zone.id) AllOfCoalitionOutsideZone(
defending_coalition, trigger_zone.id, unit_type="GROUND"
)
) )
capture_trigger.add_condition( capture_trigger.add_condition(
PartOfCoalitionInZone( PartOfCoalitionInZone(
@@ -176,7 +178,9 @@ class TriggerGenerator:
recapture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger") recapture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger")
recapture_trigger.add_condition( recapture_trigger.add_condition(
AllOfCoalitionOutsideZone(attacking_coalition, trigger_zone.id) AllOfCoalitionOutsideZone(
attacking_coalition, trigger_zone.id, unit_type="GROUND"
)
) )
recapture_trigger.add_condition( recapture_trigger.add_condition(
PartOfCoalitionInZone( PartOfCoalitionInZone(

View File

@@ -31,8 +31,8 @@ def save_dir() -> Path:
return Path(base_path()) / "Liberation" / "Saves" return Path(base_path()) / "Liberation" / "Saves"
def _temporary_save_file() -> str: def _temporary_save_file() -> Path:
return str(save_dir() / "tmpsave.liberation") return save_dir() / "tmpsave.liberation"
def _autosave_path() -> str: def _autosave_path() -> str:
@@ -54,16 +54,18 @@ def load_game(path: str) -> Optional[Game]:
return None return None
def save_game(game: Game) -> bool: def save_game(game: Game, destination: Path | None = None) -> None:
if destination is None:
destination = Path(game.savepath)
temp_save_file = _temporary_save_file()
with logged_duration("Saving game"): with logged_duration("Saving game"):
try: try:
with open(_temporary_save_file(), "wb") as f: with temp_save_file.open("wb") as f:
pickle.dump(game, f) pickle.dump(game, f)
shutil.copy(_temporary_save_file(), game.savepath) shutil.copy(temp_save_file, destination)
return True
except Exception: except Exception:
logging.exception("Could not save game") logging.exception("Could not save game")
return False
def autosave(game: Game) -> bool: def autosave(game: Game) -> bool:
@@ -79,3 +81,7 @@ def autosave(game: Game) -> bool:
except Exception: except Exception:
logging.exception("Could not save game") logging.exception("Could not save game")
return False return False
def save_last_turn_state(game: Game) -> None:
save_game(game, save_dir() / "last_turn.liberation")

View File

@@ -6,6 +6,7 @@ import logging
import re import re
from dataclasses import dataclass from dataclasses import dataclass
from typing import Dict, FrozenSet, Iterator, List, Set, Tuple from typing import Dict, FrozenSet, Iterator, List, Set, Tuple
from dcs.task import Modulation from dcs.task import Modulation
@@ -246,6 +247,51 @@ RADIOS: List[Radio] = [
), ),
), ),
Radio("UHF TRAP 137B", (RadioRange(MHz(225), MHz(400), kHz(25), Modulation.AM),)), Radio("UHF TRAP 137B", (RadioRange(MHz(225), MHz(400), kHz(25), Modulation.AM),)),
Radio(
"AN/ARC-150(V) 2",
(
RadioRange(
MHz(225),
MHz(400),
MHz(1),
Modulation.AM,
frozenset((MHz(243),)),
),
),
),
Radio(
"SRT-651/N",
(
RadioRange(
MHz(30),
MHz(88),
MHz(1),
Modulation.FM,
frozenset((MHz(40, 500),)),
),
RadioRange(
MHz(108),
MHz(156),
MHz(1),
Modulation.AM,
frozenset((MHz(121, 500),)),
),
RadioRange(
MHz(156),
MHz(174),
MHz(1),
Modulation.FM,
frozenset((MHz(156, 800),)),
),
RadioRange(
MHz(225),
MHz(400),
MHz(1),
Modulation.AM, # Actually AM/FM, but we can't represent that.
frozenset((MHz(243),)),
),
),
),
] ]

View File

@@ -34,8 +34,13 @@ class IadsConnectionJs(BaseModel):
iads_connections = [] iads_connections = []
tgo = network_node.group.ground_object tgo = network_node.group.ground_object
for id, connection in network_node.connections.items(): for id, connection in network_node.connections.items():
if connection.ground_object.is_friendly(True) != tgo.is_friendly(True): if (
continue # Skip connections which are not from same coalition not connection.iads_role.is_secondary_node
or connection.ground_object.is_friendly(True) != tgo.is_friendly(True)
):
# Skip connections to non secondary nodes (for example PD)
# and connections which are not from same coalition
continue
iads_connections.append( iads_connections.append(
IadsConnectionJs( IadsConnectionJs(
id=id, id=id,
@@ -67,10 +72,8 @@ class IadsNetworkJs(BaseModel):
@staticmethod @staticmethod
def from_network(network: IadsNetwork) -> IadsNetworkJs: def from_network(network: IadsNetwork) -> IadsNetworkJs:
iads_connections = [] iads_connections = []
for connection in network.nodes: for primary_node in network.nodes:
if not connection.group.iads_role.participate: iads_connections.extend(IadsConnectionJs.connections_for_node(primary_node))
continue # Skip
iads_connections.extend(IadsConnectionJs.connections_for_node(connection))
return IadsNetworkJs( return IadsNetworkJs(
advanced=network.advanced_iads, connections=iads_connections advanced=network.advanced_iads, connections=iads_connections
) )

View File

@@ -5,6 +5,7 @@ from datetime import timedelta
from pathlib import Path from pathlib import Path
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from game import persistency
from game.debriefing import Debriefing from game.debriefing import Debriefing
from game.missiongenerator import MissionGenerator from game.missiongenerator import MissionGenerator
from game.unitmap import UnitMap from game.unitmap import UnitMap
@@ -73,6 +74,7 @@ class MissionSimulation:
"was generated." "was generated."
) )
persistency.save_last_turn_state(self.game)
MissionResultsProcessor(self.game).commit(debriefing, events) MissionResultsProcessor(self.game).commit(debriefing, events)
def finish(self) -> None: def finish(self) -> None:

View File

@@ -33,6 +33,7 @@ from dcs.ships import (
CVN_75, CVN_75,
CV_1143_5, CV_1143_5,
Forrestal, Forrestal,
Hms_invincible,
KUZNECOW, KUZNECOW,
LHA_Tarawa, LHA_Tarawa,
Stennis, Stennis,
@@ -877,6 +878,11 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
) -> RunwayData: ) -> RunwayData:
... ...
def stub_runway_data(self) -> RunwayData:
return RunwayData(
self.full_name, runway_heading=Heading.from_degrees(0), runway_name=""
)
@property @property
def airdrome_id_for_landing(self) -> Optional[int]: def airdrome_id_for_landing(self) -> Optional[int]:
return None return None
@@ -1133,6 +1139,14 @@ class Airfield(ControlPoint):
conditions: Conditions, conditions: Conditions,
dynamic_runways: Dict[str, RunwayData], dynamic_runways: Dict[str, RunwayData],
) -> RunwayData: ) -> RunwayData:
if not self.airport.runways:
# Some airfields are heliports and don't have any runways. This isn't really
# the best fix, since we should still try to generate partial data for TACAN
# beacons, but it'll do for a bug fix, and the proper fix probably involves
# making heliports their own CP type.
# https://github.com/dcs-liberation/dcs_liberation/issues/2710
return self.stub_runway_data()
assigner = RunwayAssigner(conditions) assigner = RunwayAssigner(conditions)
return assigner.get_preferred_runway(theater, self.airport) return assigner.get_preferred_runway(theater, self.airport)
@@ -1176,6 +1190,8 @@ class NavalControlPoint(ControlPoint, ABC):
if self.is_friendly(for_player): if self.is_friendly(for_player):
yield from [ yield from [
FlightType.AEWC,
FlightType.REFUELING,
# TODO: FlightType.INTERCEPTION # TODO: FlightType.INTERCEPTION
# TODO: Buddy tanking for the A-4? # TODO: Buddy tanking for the A-4?
# TODO: Rescue chopper? # TODO: Rescue chopper?
@@ -1200,11 +1216,12 @@ class NavalControlPoint(ControlPoint, ABC):
# while its escorts are still alive. # while its escorts are still alive.
for group in self.find_main_tgo().groups: for group in self.find_main_tgo().groups:
for u in group.units: for u in group.units:
if u.type in [ if u.alive and u.type in [
Forrestal, Forrestal,
Stennis, Hms_invincible,
LHA_Tarawa,
KUZNECOW, KUZNECOW,
LHA_Tarawa,
Stennis,
Type_071, Type_071,
]: ]:
return True return True
@@ -1265,13 +1282,10 @@ class Carrier(NavalControlPoint):
return SymbolSet.SEA_SURFACE, SeaSurfaceEntity.CARRIER return SymbolSet.SEA_SURFACE, SeaSurfaceEntity.CARRIER
def mission_types(self, for_player: bool) -> Iterator[FlightType]: def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from game.ato import FlightType
yield from super().mission_types(for_player) yield from super().mission_types(for_player)
if self.is_friendly(for_player): if self.is_friendly(for_player):
yield from [ yield from [
FlightType.AEWC, # Nothing yet.
FlightType.REFUELING,
] ]
def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None: def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None:
@@ -1372,9 +1386,7 @@ class OffMapSpawn(ControlPoint):
dynamic_runways: Dict[str, RunwayData], dynamic_runways: Dict[str, RunwayData],
) -> RunwayData: ) -> RunwayData:
logging.warning("TODO: Off map spawns have no runways.") logging.warning("TODO: Off map spawns have no runways.")
return RunwayData( return self.stub_runway_data()
self.full_name, runway_heading=Heading.from_degrees(0), runway_name=""
)
@property @property
def runway_status(self) -> RunwayStatus: def runway_status(self) -> RunwayStatus:
@@ -1416,9 +1428,7 @@ class Fob(ControlPoint):
dynamic_runways: Dict[str, RunwayData], dynamic_runways: Dict[str, RunwayData],
) -> RunwayData: ) -> RunwayData:
logging.warning("TODO: FOBs have no runways.") logging.warning("TODO: FOBs have no runways.")
return RunwayData( return self.stub_runway_data()
self.full_name, runway_heading=Heading.from_degrees(0), runway_name=""
)
@property @property
def runway_status(self) -> RunwayStatus: def runway_status(self) -> RunwayStatus:

View File

@@ -44,15 +44,14 @@ class SkynetNode:
IadsRole.POWER_SOURCE, IadsRole.POWER_SOURCE,
]: ]:
# Use UnitName for EWR, CommandCenter, Comms, Power # Use UnitName for EWR, CommandCenter, Comms, Power
is_dead = group.alive_units == 0
for unit in group.units: for unit in group.units:
# Check for alive units in the group if unit.alive or (is_dead and unit.is_static):
if unit.alive: # Return first alive unit within the group or otherwise return the
# first static object as these will still be added to the mission
return unit.unit_name return unit.unit_name
if group.units[0].is_static: # Raise error if there is no skynet capable unit in this group
# Statics will be placed as dead unit raise IadsNetworkException(f"Group {group.name} has no skynet usable units")
return group.units[0].unit_name
# If no alive unit is available and not static raise error
raise IadsNetworkException("Group has no skynet usable units")
else: else:
# Use the GroupName for SAMs, SAMAsEWR and PDs # Use the GroupName for SAMs, SAMAsEWR and PDs
return group.group_name return group.group_name
@@ -80,10 +79,10 @@ class IadsNetworkNode:
def __str__(self) -> str: def __str__(self) -> str:
return self.group.group_name return self.group.group_name
def add_connection_for_tgo(self, tgo: TheaterGroundObject) -> None: def add_secondary_node(self, tgo: TheaterGroundObject) -> None:
"""Add all possible connections for the given TGO to the node""" """Add all possible connections for the given secondary node to this node"""
for group in tgo.groups: for group in tgo.groups:
if isinstance(group, IadsGroundGroup) and group.iads_role.participate: if isinstance(group, IadsGroundGroup) and group.iads_role.is_secondary_node:
self.add_connection_for_group(group) self.add_connection_for_group(group)
def add_connection_for_group(self, group: IadsGroundGroup) -> None: def add_connection_for_group(self, group: IadsGroundGroup) -> None:
@@ -134,11 +133,11 @@ class IadsNetwork:
# Skip culled ground objects # Skip culled ground objects
continue continue
# HOTFIX! Skip non-static nodes with no alive units left if node.group.alive_units == 0 and not node.group.has_statics:
# Delete this as soon as PRs #2285, #2286 & #2287 are merged # Skip non-static nodes with no alive units left
unit_count = len(node.group.units) # Dead static nodes can be added to skynet as these are added to the
is_static = node.group.units[0].is_static if unit_count > 0 else False # mission as dead unit. Non static will not be added to the mission and
if node.group.alive_units == 0 and not is_static: # are therefore not accessible by skynet
continue continue
# SkynetNode.from_group(node.group) may raise an exception # SkynetNode.from_group(node.group) may raise an exception
@@ -146,6 +145,9 @@ class IadsNetwork:
# but if it does, we want to know because it's supposed to be impossible afaict # but if it does, we want to know because it's supposed to be impossible afaict
skynet_node = SkynetNode.from_group(node.group) skynet_node = SkynetNode.from_group(node.group)
for connection in node.connections.values(): for connection in node.connections.values():
if connection.alive_units == 0 and not connection.has_statics:
# Skip non static and dead connection nodes. See comment above
continue
if connection.ground_object.is_friendly( if connection.ground_object.is_friendly(
skynet_node.player skynet_node.player
) and not game.iads_considerate_culling(connection.ground_object): ) and not game.iads_considerate_culling(connection.ground_object):
@@ -191,34 +193,27 @@ class IadsNetwork:
for primary_node in primary_nodes: for primary_node in primary_nodes:
self.update_tgo(primary_node, events) self.update_tgo(primary_node, events)
def node_for_group(self, group: IadsGroundGroup) -> IadsNetworkNode:
"""Get existing node from the iads network or create a new node"""
for cn in self.nodes:
if cn.group == group:
return cn
node = IadsNetworkNode(group)
self.nodes.append(node)
return node
def node_for_tgo(self, tgo: TheaterGroundObject) -> Optional[IadsNetworkNode]: def node_for_tgo(self, tgo: TheaterGroundObject) -> Optional[IadsNetworkNode]:
"""Get existing node from the iads network or create a new node""" """Create Primary node for the TGO or return existing one"""
for cn in self.nodes: for cn in self.nodes:
if cn.group.ground_object == tgo: if cn.group.ground_object == tgo:
return cn return cn
return self._new_node_for_tgo(tgo) return self._new_node_for_tgo(tgo)
def _new_node_for_tgo(self, tgo: TheaterGroundObject) -> Optional[IadsNetworkNode]: def _new_node_for_tgo(self, tgo: TheaterGroundObject) -> Optional[IadsNetworkNode]:
# Create new connection_node if none exists """Create a new primary node for the given TGO.
Will return None if the given TGO is not capable of being a primary node.
This will also add any PointDefense of this TGO to the primary node"""
node: Optional[IadsNetworkNode] = None node: Optional[IadsNetworkNode] = None
for group in tgo.groups: for group in tgo.groups:
# TODO Cleanup
if isinstance(group, IadsGroundGroup): if isinstance(group, IadsGroundGroup):
# The first IadsGroundGroup is always the primary Group # The first IadsGroundGroup is always the primary Group
if not node and group.iads_role.participate: if node is None and group.iads_role.is_primary_node:
# Primary Node # Create Primary Node
node = self.node_for_group(group) node = IadsNetworkNode(group)
elif node and group.iads_role == IadsRole.POINT_DEFENSE: self.nodes.append(node)
elif node is not None and group.iads_role == IadsRole.POINT_DEFENSE:
# Point Defense Node for this TGO # Point Defense Node for this TGO
node.add_connection_for_group(group) node.add_connection_for_group(group)
@@ -280,7 +275,13 @@ class IadsNetwork:
connections = self.iads_config[primary_node] connections = self.iads_config[primary_node]
for secondary_node in connections: for secondary_node in connections:
try: try:
node.add_connection_for_tgo(self.ground_objects[secondary_node]) nearby_go = self.ground_objects[secondary_node]
if IadsRole.for_category(nearby_go.category).is_secondary_node:
node.add_secondary_node(nearby_go)
else:
logging.error(
f"IADS: {secondary_node} is not a valid secondary node"
)
except KeyError: except KeyError:
logging.exception( logging.exception(
f"IADS: No ground object found for connection {secondary_node}" f"IADS: No ground object found for connection {secondary_node}"
@@ -296,15 +297,11 @@ class IadsNetwork:
continue continue
nearby_iads_role = IadsRole.for_category(nearby_go.category) nearby_iads_role = IadsRole.for_category(nearby_go.category)
if ( if (
nearby_iads_role nearby_iads_role.is_secondary_node
in [
IadsRole.POWER_SOURCE,
IadsRole.CONNECTION_NODE,
]
and nearby_go.position.distance_to_point(primary_tgo.position) and nearby_go.position.distance_to_point(primary_tgo.position)
<= nearby_iads_role.connection_range.meters <= nearby_iads_role.connection_range.meters
): ):
node.add_connection_for_tgo(nearby_go) node.add_secondary_node(nearby_go)
def initialize_network_from_range(self) -> None: def initialize_network_from_range(self) -> None:
"""Initialize the IADS Network by range""" """Initialize the IADS Network by range"""

View File

@@ -79,3 +79,16 @@ class IadsRole(Enum):
IadsRole.NO_BEHAVIOR, IadsRole.NO_BEHAVIOR,
IadsRole.POINT_DEFENSE, IadsRole.POINT_DEFENSE,
] ]
@property
def is_primary_node(self) -> bool:
return self in [
IadsRole.SAM,
IadsRole.SAM_AS_EWR,
IadsRole.EWR,
IadsRole.COMMAND_CENTER,
]
@property
def is_secondary_node(self) -> bool:
return self in [IadsRole.CONNECTION_NODE, IadsRole.POWER_SOURCE]

View File

@@ -185,6 +185,10 @@ class TheaterGroup:
def alive_units(self) -> int: def alive_units(self) -> int:
return sum(unit.alive for unit in self.units) return sum(unit.alive for unit in self.units)
@property
def has_statics(self) -> bool:
return any(unit.is_static for unit in self.units)
def max_detection_range(self) -> Distance: def max_detection_range(self) -> Distance:
"""Calculate the maximum detection range of the TheaterGroup""" """Calculate the maximum detection range of the TheaterGroup"""
ranges = (u.detection_range for u in self.units if u.is_anti_air) ranges = (u.detection_range for u in self.units if u.is_anti_air)

View File

@@ -34,6 +34,7 @@ class FrontLineUnit:
@dataclass(frozen=True) @dataclass(frozen=True)
class TheaterUnitMapping: class TheaterUnitMapping:
dcs_group_id: int
theater_unit: TheaterUnit theater_unit: TheaterUnit
dcs_unit: Unit dcs_unit: Unit
@@ -104,14 +105,16 @@ class UnitMap:
return self.front_line_units.get(name, None) return self.front_line_units.get(name, None)
def add_theater_unit_mapping( def add_theater_unit_mapping(
self, theater_unit: TheaterUnit, dcs_unit: Unit self, dcs_group_id: int, theater_unit: TheaterUnit, dcs_unit: Unit
) -> None: ) -> None:
# Deaths for units at TGOs are recorded in the corresponding GroundUnit within # Deaths for units at TGOs are recorded in the corresponding GroundUnit within
# the GroundGroup, so we have to match the dcs unit with the liberation unit # the GroundGroup, so we have to match the dcs unit with the liberation unit
name = str(dcs_unit.name) name = str(dcs_unit.name)
if name in self.theater_objects: if name in self.theater_objects:
raise RuntimeError(f"Duplicate TGO unit: {name}") raise RuntimeError(f"Duplicate TGO unit: {name}")
self.theater_objects[name] = TheaterUnitMapping(theater_unit, dcs_unit) self.theater_objects[name] = TheaterUnitMapping(
dcs_group_id, theater_unit, dcs_unit
)
def theater_units(self, name: str) -> Optional[TheaterUnitMapping]: def theater_units(self, name: str) -> Optional[TheaterUnitMapping]:
return self.theater_objects.get(name, None) return self.theater_objects.get(name, None)

View File

@@ -2,8 +2,8 @@ from pathlib import Path
MAJOR_VERSION = 6 MAJOR_VERSION = 6
MINOR_VERSION = 0 MINOR_VERSION = 1
MICRO_VERSION = 0 MICRO_VERSION = 1
VERSION_NUMBER = ".".join(str(v) for v in (MAJOR_VERSION, MINOR_VERSION, MICRO_VERSION)) VERSION_NUMBER = ".".join(str(v) for v in (MAJOR_VERSION, MINOR_VERSION, MICRO_VERSION))

View File

@@ -7,7 +7,6 @@ from game.missiongenerator.frontlineconflictdescription import (
FrontLineConflictDescription, FrontLineConflictDescription,
) )
from game.theater.controlpoint import ControlPointType from game.theater.controlpoint import ControlPointType
from game.theater.theatergroundobject import BuildingGroundObject, IadsGroundObject
from game.utils import Distance from game.utils import Distance
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
@@ -81,66 +80,25 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
wpt.description = "Frontline" wpt.description = "Frontline"
i = add_model_item(i, model, wpt.pretty_name, wpt) i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_targets: for tgo in self.game.theater.ground_objects:
for cp in self.game.theater.controlpoints: for target_idx, target in enumerate(tgo.strike_targets):
if (self.include_enemy and not cp.captured) or ( wptname = f"[{tgo.obj_name}] : {target.name} #{target_idx}"
self.include_friendly and cp.captured wpt = FlightWaypoint(
): wptname,
for ground_object in cp.ground_objects: FlightWaypointType.CUSTOM,
if not ground_object.is_dead and isinstance( target.position,
ground_object, BuildingGroundObject Distance.from_meters(0),
): )
wpt = FlightWaypoint( wpt.alt_type = "RADIO"
ground_object.waypoint_name, wpt.pretty_name = wptname
FlightWaypointType.CUSTOM, wpt.targets.append(target)
ground_object.position, wpt.obj_name = tgo.obj_name
Distance.from_meters(0), wpt.waypoint_type = FlightWaypointType.CUSTOM
) if tgo.is_friendly(to_player=True):
wpt.alt_type = "RADIO" wpt.description = f"Friendly unit: {target.name}"
wpt.pretty_name = wpt.name else:
wpt.obj_name = ground_object.obj_name wpt.description = f"Enemy unit: {target.name}"
wpt.targets.append(ground_object) i = add_model_item(i, model, wpt.pretty_name, wpt)
if cp.captured:
wpt.description = "Friendly Building"
else:
wpt.description = "Enemy Building"
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_units:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (
self.include_friendly and cp.captured
):
for ground_object in cp.ground_objects:
if not ground_object.is_dead and (
isinstance(ground_object, IadsGroundObject)
):
for g in ground_object.groups:
for j, u in enumerate(g.units):
wptname = (
"["
+ str(ground_object.obj_name)
+ "] : "
+ u.name
+ " #"
+ str(j)
)
wpt = FlightWaypoint(
wptname,
FlightWaypointType.CUSTOM,
u.position,
Distance.from_meters(0),
)
wpt.alt_type = "RADIO"
wpt.pretty_name = wptname
wpt.targets.append(u)
wpt.obj_name = ground_object.obj_name
wpt.waypoint_type = FlightWaypointType.CUSTOM
if cp.captured:
wpt.description = "Friendly unit: " + u.name
else:
wpt.description = "Enemy unit: " + u.name
i = add_model_item(i, model, wpt.pretty_name, wpt)
if self.include_airbases: if self.include_airbases:
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:

View File

@@ -1,71 +0,0 @@
from PySide2.QtGui import QStandardItem, QStandardItemModel
from game import Game
from game.theater import SamGroundObject
from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox
class StrikeTargetInfo:
def __init__(self):
self.name = ""
self.location = None
self.units = []
self.buildings = []
class QStrikeTargetSelectionComboBox(QFilteredComboBox):
def __init__(self, game: Game, parent=None):
super(QStrikeTargetSelectionComboBox, self).__init__(parent)
self.game = game
self.find_possible_strike_targets()
for t in self.targets:
print(t.name + " - " + str(len(t.units)) + " " + str(len(t.buildings)))
def get_selected_target(self) -> StrikeTargetInfo:
n = self.currentText()
for target in self.targets:
if target.name == n:
return target
def find_possible_strike_targets(self):
self.targets = []
i = 0
model = QStandardItemModel()
def add_model_item(i, model, target):
item = QStandardItem(target.name)
model.setItem(i, 0, item)
self.targets.append(target)
return i + 1
for cp in self.game.theater.controlpoints:
if cp.captured:
continue
added_obj_names = []
for g in cp.ground_objects:
if g.obj_name in added_obj_names:
continue
target = StrikeTargetInfo()
target.location = g
target.name = g.obj_name
if isinstance(g, SamGroundObject):
target.name = g.obj_name + " [units]"
for group in g.groups:
for u in group.units:
target.units.append(u)
else:
target.name = g.obj_name + " [" + g.category + "]"
for g2 in cp.ground_objects:
if g2 is not g and g2.obj_name == g.obj_name:
target.buildings.append(g2)
i = add_model_item(i, model, target)
added_obj_names.append(g.obj_name)
self.setModel(model)

View File

@@ -1,73 +0,0 @@
import random
from PySide2.QtGui import QStandardItemModel, QStandardItem
from PySide2.QtWidgets import (
QGroupBox,
QLabel,
QWidget,
QVBoxLayout,
QListView,
QAbstractItemView,
)
from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import StrikeTargetInfo
class QStrikeTargetInfoView(QGroupBox):
"""
UI Component to display info about a strike target
"""
def __init__(self, strike_target_infos: StrikeTargetInfo):
if strike_target_infos is None:
strike_target_infos = StrikeTargetInfo()
super(QStrikeTargetInfoView, self).__init__(
"Target : " + strike_target_infos.name
)
self.strike_target_infos = strike_target_infos
self.listView = QListView()
self.init_ui()
def init_ui(self):
layout = QVBoxLayout(self)
layout.setSpacing(0)
layout.addWidget(self.listView)
self.setLayout(layout)
def setTarget(self, target):
self.setTitle(target.name)
self.strike_target_infos = target
model = QStandardItemModel()
self.listView.setSelectionMode(QAbstractItemView.NoSelection)
if len(self.strike_target_infos.units) > 0:
dic = {}
for u in self.strike_target_infos.units:
if u.type.id in dic.keys():
dic[u.type.id] = dic[u.type.id] + 1
else:
dic[u.type.id] = 1
for k, v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
else:
dic = {}
for b in self.strike_target_infos.buildings:
id = b.dcs_identifier
if b.is_dead:
id = id + "[Destroyed]"
if id in dic.keys():
dic[id] = dic[id] + 1
else:
dic[id] = 1
for k, v in dic.items():
model.appendRow(QStandardItem(k + " x " + str(v)))
print(k + " x " + str(v))
self.listView.setModel(model)

View File

@@ -8,12 +8,13 @@ from PySide2.QtWidgets import (
QGroupBox, QGroupBox,
QLabel, QLabel,
QPushButton, QPushButton,
QScrollArea,
QVBoxLayout, QVBoxLayout,
QWidget,
) )
from game.debriefing import Debriefing from game.debriefing import Debriefing
T = TypeVar("T") T = TypeVar("T")
@@ -52,6 +53,19 @@ class LossGrid(QGridLayout):
self.addWidget(QLabel(str(count)), row, 1) self.addWidget(QLabel(str(count)), row, 1)
class ScrollingCasualtyReportContainer(QGroupBox):
def __init__(self, debriefing: Debriefing, player: bool) -> None:
country = debriefing.player_country if player else debriefing.enemy_country
super().__init__(f"{country}'s lost units:")
scroll_content = QWidget()
scroll_content.setLayout(LossGrid(debriefing, player))
scroll_area = QScrollArea()
scroll_area.setWidget(scroll_content)
layout = QVBoxLayout()
layout.addWidget(scroll_area)
self.setLayout(layout)
class QDebriefingWindow(QDialog): class QDebriefingWindow(QDialog):
def __init__(self, debriefing: Debriefing): def __init__(self, debriefing: Debriefing):
super(QDebriefingWindow, self).__init__() super(QDebriefingWindow, self).__init__()
@@ -75,12 +89,10 @@ class QDebriefingWindow(QDialog):
title = QLabel("<b>Casualty report</b>") title = QLabel("<b>Casualty report</b>")
layout.addWidget(title) layout.addWidget(title)
player_lost_units = QGroupBox(f"{self.debriefing.player_country}'s lost units:") player_lost_units = ScrollingCasualtyReportContainer(debriefing, player=True)
player_lost_units.setLayout(LossGrid(debriefing, player=True))
layout.addWidget(player_lost_units) layout.addWidget(player_lost_units)
enemy_lost_units = QGroupBox(f"{self.debriefing.enemy_country}'s lost units:") enemy_lost_units = ScrollingCasualtyReportContainer(debriefing, player=False)
enemy_lost_units.setLayout(LossGrid(debriefing, player=False))
layout.addWidget(enemy_lost_units) layout.addWidget(enemy_lost_units)
okay = QPushButton("Okay") okay = QPushButton("Okay")

View File

@@ -238,5 +238,5 @@ class QWaitingForMissionResultWindow(QDialog):
# Stop the current waiting thread as we manually submit the results # Stop the current waiting thread as we manually submit the results
self.wait_thread.stop() self.wait_thread.stop()
self.on_debriefing_update( self.on_debriefing_update(
self.sim_controller.debrief_current_state(Path(file[0], force_end=True)) self.sim_controller.debrief_current_state(Path(file[0]), force_end=True)
) )

View File

@@ -4,7 +4,7 @@ asgiref==3.5.2
atomicwrites==1.4.1 atomicwrites==1.4.1
attrs==21.4.0 attrs==21.4.0
black==22.6.0 black==22.6.0
certifi==2022.6.15 certifi==2022.12.7
cfgv==3.3.1 cfgv==3.3.1
click==8.1.3 click==8.1.3
colorama==0.4.5 colorama==0.4.5
@@ -32,7 +32,7 @@ pluggy==1.0.0
pre-commit==2.19.0 pre-commit==2.19.0
py==1.11.0 py==1.11.0
pydantic==1.9.1 pydantic==1.9.1
-e git+https://github.com/pydcs/dcs@324e4471eb32af69eee8c103ad5260fe1ba13ba9#egg=pydcs -e git+https://github.com/pydcs/dcs@9c7b47b6c2b4870bf563bf880315dac019f6df1f#egg=pydcs
pyinstaller==5.2 pyinstaller==5.2
pyinstaller-hooks-contrib==2022.8 pyinstaller-hooks-contrib==2022.8
pyparsing==3.0.9 pyparsing==3.0.9

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ recommended_enemy_faction: Germany 1944
recommended_start_date: 1944-07-04 recommended_start_date: 1944-07-04
miz: caen_to_evreux.miz miz: caen_to_evreux.miz
performance: 1 performance: 1
version: "9.0" version: "10.0"
squadrons: squadrons:
# Evreux # Evreux
26: 26:

View File

@@ -1,103 +0,0 @@
---
name: Marianas - Guam - Mount Barrigada
theater: MarianaIslands
authors: Ghosti
description: <p>The objective of this campaign is the capture of Guam. Blue side, having landed their forces on the beaches of Agat, controls Antonio B. Won Pat airfield and are pushing towards the enemy stronghold of Mount Barrigada.</p>
miz: marianas_guam_barrigada.miz
performance: 2
version: "9.0"
squadrons:
Blue CV:
- primary: BARCAP
secondary: air-to-air
- primary: BARCAP
secondary: any
- primary: Refueling
- primary: Strike
secondary: any
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
- primary: AEW&C
Blue LHA:
- primary: Strike
secondary: any
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
Red CV:
- primary: BARCAP
secondary: air-to-air
- primary: BARCAP
secondary: any
- primary: Refueling
- primary: Strike
secondary: any
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
- primary: AEW&C
Red LHA:
- primary: Strike
secondary: any
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
# Andersen AFB
6:
- primary: BARCAP
secondary: air-to-air
- primary: BARCAP
secondary: air-to-air
- primary: Strike
secondary: any
- primary: Anti-ship
secondary: any
- primary: BAI
secondary: any
- primary: CAS
secondary: air-to-ground
- primary: AEW&C
- primary: Refueling
- primary: Transport
# Antonio B. Won Pat Intl
4:
- primary: BARCAP
secondary: air-to-air
- primary: BARCAP
secondary: air-to-air
- primary: Strike
secondary: any
- primary: Anti-ship
secondary: any
- primary: BAI
secondary: any
- primary: CAS
secondary: any
- primary: CAS
secondary: air-to-ground
- primary: AEW&C
- primary: Refueling
- primary: Transport
Andersen AFB Northwest Field:
- primary: Strike
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground
Orote Point:
- primary: Strike
secondary: air-to-ground
- primary: BAI
secondary: air-to-ground
- primary: CAS
secondary: air-to-ground

View File

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

View File

@@ -1,84 +0,0 @@
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: 9.0
squadrons:
# Mozdok
28:
- primary: BARCAP
secondary: air-to-air
aircraft:
- Su-27 Flanker-B
- primary: BARCAP
secondary: any
aircraft:
- MiG-29S Fulcrum-C
- primary: SEAD
secondary: air-to-ground
aircraft:
- Su-25T Frogfoot
- primary: Strike
aircraft:
- Tu-160 Blackjack
- primary: AEW&C
aircraft:
- A-50
- primary: Refueling
aircraft:
- IL-78M
- primary: Transport
aircraft:
- IL-76MD
# Cherkessk FARP
Zarechnoe FARP:
- primary: CAS
secondary: air-to-ground
aircraft:
- Mi-24P Hind-F
- Mi-24V Hind-E
- primary: CAS
secondary: air-to-ground
aircraft:
- Ka-50 Hokum
# Mineralvodye
26:
- primary: BARCAP
secondary: any
aircraft:
- F-16CM Fighting Falcon (Block 50)
- primary: CAS
secondary: air-to-ground
aircraft:
- AV-8B Harrier II Night Attack
# Cherkessk FARP
Cherkessk FARP:
- primary: CAS
secondary: air-to-ground
aircraft:
- AH-64A Apache
# Maykop
16:
- primary: BARCAP
secondary: any
aircraft:
- F-15C Eagle
- primary: BARCAP
secondary: any
aircraft:
- F/A-18C Hornet (Lot 20)
- primary: Strike
aircraft:
- B-52H Stratofortress
- primary: CAS
secondary: air-to-ground
aircraft:
- A-10A Thunderbolt II
- primary: AEW&C
aircraft:
- E-2C Hawkeye

View File

@@ -8,7 +8,7 @@ recommended_enemy_faction: Russia 1975
recommended_start_date: 1995-06-13 recommended_start_date: 1995-06-13
miz: northern_russia.miz miz: northern_russia.miz
performance: 2 performance: 2
version: "9.1" version: "10.5"
squadrons: squadrons:
# Kutaisi # Kutaisi
25: 25:

View File

@@ -6,7 +6,7 @@ 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>" 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>"
miz: "operation_dynamo.miz" miz: "operation_dynamo.miz"
performance: 1 performance: 1
version: "9.0" version: "10.0"
squadrons: squadrons:
# Manston # Manston
5: 5:

View File

@@ -8,7 +8,7 @@ recommended_enemy_faction: Syria 2012'ish
recommended_start_date: 2012-06-05 recommended_start_date: 2012-06-05
miz: syria_full_map.miz miz: syria_full_map.miz
performance: 3 performance: 3
version: "9.1" version: "10.5"
squadrons: squadrons:
# Incirlik # Incirlik
16: 16:

Binary file not shown.

View File

@@ -2,36 +2,36 @@ local unitPayloads = {
["name"] = "F-15E", ["name"] = "F-15E",
["payloads"] = { ["payloads"] = {
[1] = { [1] = {
["displayName"] = "Liberation CAS", ["displayName"] = "CAS",
["name"] = "Liberation CAS", ["name"] = "CAS",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{444BA8AE-82A7-4345-842E-76154EFCCA46}",
["num"] = 18,
},
[2] = {
["CLSID"] = "{444BA8AE-82A7-4345-842E-76154EFCCA46}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 1, ["num"] = 1,
}, },
[4] = { [2] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 3, ["num"] = 3,
}, },
[4] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 4,
},
[5] = { [5] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 17, ["num"] = 6,
}, },
[6] = { [6] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}", ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 19, ["num"] = 7,
}, },
[7] = { [7] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}", ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 10, ["num"] = 9,
}, },
[8] = { [8] = {
["CLSID"] = "{CBU_105}", ["CLSID"] = "{CBU_105}",
@@ -39,47 +39,23 @@ local unitPayloads = {
}, },
[9] = { [9] = {
["CLSID"] = "{CBU_105}", ["CLSID"] = "{CBU_105}",
["num"] = 9, ["num"] = 12,
}, },
[10] = { [10] = {
["CLSID"] = "{CBU_105}",
["num"] = 8,
},
[11] = {
["CLSID"] = "{CBU_105}",
["num"] = 7,
},
[12] = {
["CLSID"] = "{CBU_105}",
["num"] = 12,
},
[13] = {
["CLSID"] = "{CBU_105}", ["CLSID"] = "{CBU_105}",
["num"] = 13, ["num"] = 13,
}, },
[14] = { [11] = {
["CLSID"] = "{Mk82AIR}", ["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 6, ["num"] = 18,
}, },
[15] = { [12] = {
["CLSID"] = "{Mk82AIR}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 5, ["num"] = 19,
}, },
[16] = { [13] = {
["CLSID"] = "{Mk82AIR}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 4, ["num"] = 17,
},
[17] = {
["CLSID"] = "{Mk82AIR}",
["num"] = 14,
},
[18] = {
["CLSID"] = "{Mk82AIR}",
["num"] = 15,
},
[19] = {
["CLSID"] = "{Mk82AIR}",
["num"] = 16,
}, },
}, },
["tasks"] = { ["tasks"] = {
@@ -90,54 +66,46 @@ local unitPayloads = {
["name"] = "STRIKE", ["name"] = "STRIKE",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 1, ["num"] = 1,
}, },
[2] = { [2] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 3, ["num"] = 3,
}, },
[3] = {
["CLSID"] = "{GBU-38}",
["num"] = 4,
},
[4] = { [4] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{GBU-38}",
["num"] = 6, ["num"] = 11,
}, },
[5] = { [5] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{GBU-31}",
["num"] = 7, ["num"] = 7,
}, },
[6] = { [6] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{GBU-38}",
["num"] = 9, ["num"] = 13,
}, },
[7] = { [7] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}", ["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 10, ["num"] = 18,
}, },
[8] = { [8] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{GBU-38}",
["num"] = 11, ["num"] = 12,
}, },
[9] = { [9] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 13,
},
[10] = {
["CLSID"] = "{GBU-38}",
["num"] = 14,
},
[11] = {
["CLSID"] = "{GBU-38}",
["num"] = 16,
},
[12] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}",
["num"] = 19, ["num"] = 19,
}, },
[13] = { [10] = {
["CLSID"] = "{GBU-31}",
["num"] = 9,
},
[11] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 17, ["num"] = 17,
}, },
@@ -150,32 +118,44 @@ local unitPayloads = {
["name"] = "CAP", ["name"] = "CAP",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 1, ["num"] = 1,
}, },
[2] = { [2] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 3, ["num"] = 3,
}, },
[3] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 10,
},
[4] = { [4] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 19, ["num"] = 11,
}, },
[5] = { [5] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 17, ["num"] = 17,
}, },
[6] = { [6] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 18, ["num"] = 13,
}, },
[7] = { [7] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 7,
},
[8] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 19,
},
[9] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 9,
},
[10] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}", ["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2, ["num"] = 18,
}, },
}, },
["tasks"] = { ["tasks"] = {
@@ -186,48 +166,44 @@ local unitPayloads = {
["name"] = "ANTISHIP", ["name"] = "ANTISHIP",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 1, ["num"] = 1,
}, },
[2] = { [2] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 3, ["num"] = 3,
}, },
[3] = {
["CLSID"] = "{GBU-38}",
["num"] = 9,
},
[4] = { [4] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}", ["CLSID"] = "{51F9AAE5-964F-4D21-83FB-502E3BFE5F8A}",
["num"] = 10,
},
[5] = {
["CLSID"] = "{GBU-38}",
["num"] = 11, ["num"] = 11,
}, },
[5] = {
["CLSID"] = "{51F9AAE5-964F-4D21-83FB-502E3BFE5F8A}",
["num"] = 7,
},
[6] = { [6] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}", ["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 19,
},
[7] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 17,
},
[8] = {
["CLSID"] = "{444BA8AE-82A7-4345-842E-76154EFCCA46}",
["num"] = 18, ["num"] = 18,
}, },
[7] = {
["CLSID"] = "{51F9AAE5-964F-4D21-83FB-502E3BFE5F8A}",
["num"] = 13,
},
[8] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 19,
},
[9] = { [9] = {
["CLSID"] = "{444BA8AE-82A7-4345-842E-76154EFCCA46}", ["CLSID"] = "{51F9AAE5-964F-4D21-83FB-502E3BFE5F8A}",
["num"] = 2, ["num"] = 9,
}, },
[10] = { [10] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 12, ["num"] = 17,
},
[11] = {
["CLSID"] = "{GBU-38}",
["num"] = 8,
}, },
}, },
["tasks"] = { ["tasks"] = {
@@ -235,117 +211,169 @@ local unitPayloads = {
}, },
}, },
[5] = { [5] = {
["name"] = "SEAD", ["name"] = "DEAD",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 1, ["num"] = 1,
}, },
[2] = { [2] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 3, ["num"] = 3,
}, },
[3] = {
["CLSID"] = "{GBU-38}",
["num"] = 9,
},
[4] = { [4] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}", ["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 10, ["num"] = 6,
}, },
[5] = { [5] = {
["CLSID"] = "{GBU-38}",
["num"] = 11,
},
[6] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}",
["num"] = 19,
},
[7] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 17, ["num"] = 17,
}, },
[8] = { [6] = {
["CLSID"] = "{9BCC2A2B-5708-4860-B1F1-053A18442067}", ["CLSID"] = "{GBU-38}",
["num"] = 11,
},
[7] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 18, ["num"] = 18,
}, },
[9] = { [8] = {
["CLSID"] = "{9BCC2A2B-5708-4860-B1F1-053A18442067}",
["num"] = 2,
},
[10] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{GBU-38}",
["num"] = 12, ["num"] = 12,
}, },
[9] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 19,
},
[10] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 9,
},
[11] = { [11] = {
["CLSID"] = "{GBU-38}",
["num"] = 8,
},
[12] = {
["CLSID"] = "{GBU-38}", ["CLSID"] = "{GBU-38}",
["num"] = 13, ["num"] = 13,
}, },
[13] = {
["CLSID"] = "{GBU-38}",
["num"] = 7,
},
}, },
["tasks"] = { ["tasks"] = {
[1] = 32, [1] = 32,
}, },
}, },
[6] = { [6] = {
["name"] = "Liberation OCA/Runway", ["name"] = "OCA/Runway",
["pylons"] = { ["pylons"] = {
[1] = { [1] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 1, ["num"] = 1,
}, },
[2] = { [2] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 3, ["num"] = 3,
}, },
[3] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 4,
},
[4] = { [4] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{GBU-31}",
["num"] = 6, ["num"] = 11,
}, },
[5] = { [5] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{GBU-31}",
["num"] = 7, ["num"] = 7,
}, },
[6] = { [6] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{GBU-31}",
["num"] = 9, ["num"] = 10,
}, },
[7] = { [7] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}", ["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 10, ["num"] = 18,
}, },
[8] = { [8] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 11, ["num"] = 17,
}, },
[9] = { [9] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}", ["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 13,
},
[10] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 14,
},
[11] = {
["CLSID"] = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}",
["num"] = 16,
},
[12] = {
["CLSID"] = "{C8E06185-7CD6-4C90-959F-044679E90751}",
["num"] = 19, ["num"] = 19,
}, },
[10] = {
["CLSID"] = "{GBU-31}",
["num"] = 9,
},
[11] = {
["CLSID"] = "{GBU-31}",
["num"] = 13,
},
},
["tasks"] = {
[1] = 11,
},
},
[7] = {
["name"] = "OCA/Aircraft",
["pylons"] = {
[1] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 1,
},
[2] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 2,
},
[3] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 3,
},
[4] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 4,
},
[5] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 6,
},
[6] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 7,
},
[7] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 9,
},
[8] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 10,
},
[9] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 11,
},
[10] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 13,
},
[11] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 14,
},
[12] = {
["CLSID"] = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}",
["num"] = 16,
},
[13] = { [13] = {
["CLSID"] = "{E1F29B21-F291-4589-9FD8-3272EEC69506}",
["num"] = 18,
},
[14] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 19,
},
[15] = {
["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}", ["CLSID"] = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}",
["num"] = 17, ["num"] = 17,
}, },

View File

@@ -0,0 +1,107 @@
local unitPayloads = {
["name"] = "Ka-50_3",
["payloads"] = {
[1] = {
["name"] = "Liberation CAS",
["pylons"] = {
[1] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 6,
},
[2] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 5,
},
[3] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}",
["num"] = 1,
},
[5] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}",
["num"] = 3,
},
[6] = {
["CLSID"] = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}",
["num"] = 2,
},
},
["tasks"] = {
[1] = 31,
},
},
[2] = {
["displayName"] = "Liberation BAI",
["name"] = "Liberation BAI",
["pylons"] = {
[1] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 6,
},
[2] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 5,
},
[3] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 4,
},
[4] = {
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
["num"] = 1,
},
[5] = {
["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 3,
},
[6] = {
["CLSID"] = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}",
["num"] = 2,
},
},
["tasks"] = {
[1] = 31,
},
},
[3] = {
["displayName"] = "Liberation OCA/Aircraft",
["name"] = "Liberation OCA/Aircraft",
["pylons"] = {
[1] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 6,
},
[2] = {
["CLSID"] = "{9S846_2xIGLA}",
["num"] = 5,
},
[3] = {
["CLSID"] = "B_8V20A_OFP2",
["num"] = 4,
},
[4] = {
["CLSID"] = "B_8V20A_OFP2",
["num"] = 1,
},
[5] = {
["CLSID"] = "B_8V20A_OFP2",
["num"] = 3,
},
[6] = {
["CLSID"] = "B_8V20A_OFP2",
["num"] = 2,
},
},
["tasks"] = {
[1] = 31,
},
},
},
["tasks"] = {
},
["unitType"] = "Ka-50_3",
}
return unitPayloads

View File

@@ -24,6 +24,7 @@
"F/A-18C Hornet (Lot 20)", "F/A-18C Hornet (Lot 20)",
"JF-17 Thunder", "JF-17 Thunder",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"Mirage 2000C", "Mirage 2000C",
"Mirage-F1B", "Mirage-F1B",
"Mirage-F1BE", "Mirage-F1BE",
@@ -35,6 +36,7 @@
"Mirage-F1M-EE", "Mirage-F1M-EE",
"Mi-24P Hind-F", "Mi-24P Hind-F",
"Mi-8MTV2 Hip", "Mi-8MTV2 Hip",
"MiG-29S Fulcrum-C",
"SA 342L Gazelle", "SA 342L Gazelle",
"SA 342M Gazelle", "SA 342M Gazelle",
"Su-25T Frogfoot", "Su-25T Frogfoot",
@@ -109,5 +111,13 @@
], ],
"has_jtac": true, "has_jtac": true,
"jtac_unit": "MQ-9 Reaper", "jtac_unit": "MQ-9 Reaper",
"unrestricted_satnav": true "unrestricted_satnav": true,
"liveries_overrides": {
"Ka-50 Hokum": ["georgia camo"],
"Mi-8MTV2 Hip": ["Ukraine"],
"Mi-24P Hind-F": ["Ukrainian Army Aviation"],
"MiG-29S Fulcrum-C": ["Air Force Ukraine Standard"],
"Su-25T Frogfoot": ["af standard 101"],
"Su-27 Flanker-B": ["Air Force Ukraine Standard"]
}
} }

View File

@@ -0,0 +1,62 @@
{
"country": "Peru",
"name": "Peru 1995",
"authors": "Dillie",
"description": "<p>Peru during the 1995 Cenepa War.</p>",
"locales": [
"es_ES"
],
"aircrafts": [
"Mirage 2000C",
"Mi-24V Hind-E",
"Mi-8MTV2 Hip",
"MB-339A",
"Su-22M4 Fitter-K"
],
"frontline_units": [
"T-55A",
"M113",
"BRDM-2",
"BTR-80",
"M1043 HMMWV (M2 HMG)",
"ZSU-23-4 Shilka"
],
"artillery_units": [
"BM-21 Grad",
"M109A6 Paladin"
],
"logistics_units": [
"Truck Ural-4320T"
],
"infantry_units": [
"Infantry AK-74",
"Infantry RPG",
"Infantry M249",
"MANPADS SA-18 Igla \"Grouse\""
],
"missiles": [],
"preset_groups": [
"SA-3/S-125"
],
"naval_units": [],
"air_defense_units": [
"AAA ZU-23 Emplacement",
"Bofors 40 mm Gun",
"SAM P19 \"Flat Face\" SR (SA-2/3)",
"ZSU-23-4 Shilka"
],
"carrier_names": [],
"helicopter_carrier_names": [],
"cruiser": [],
"requirements": {},
"has_jtac": false,
"doctrine": "modern",
"liveries_overrides": {
"Mirage 2000C": [
"Peru052"
],
"MB-339A": [
"MB339AP 'PERU'"
]
}
}

View File

@@ -8,6 +8,7 @@
], ],
"aircrafts": [ "aircrafts": [
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"L-39ZA Albatros", "L-39ZA Albatros",
"Mi-24V Hind-E", "Mi-24V Hind-E",
"Mi-24P Hind-F", "Mi-24P Hind-F",

View File

@@ -10,6 +10,7 @@
"J-15 Flanker X-2", "J-15 Flanker X-2",
"J-7B", "J-7B",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"L-39ZA Albatros", "L-39ZA Albatros",
"Mi-24V Hind-E", "Mi-24V Hind-E",
"Mi-24P Hind-F", "Mi-24P Hind-F",

View File

@@ -11,6 +11,7 @@
"J-11A Flanker-L", "J-11A Flanker-L",
"JF-17 Thunder", "JF-17 Thunder",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"L-39ZA Albatros", "L-39ZA Albatros",
"Mi-24V Hind-E", "Mi-24V Hind-E",
"Mi-24P Hind-F", "Mi-24P Hind-F",

View File

@@ -9,6 +9,7 @@
"aircrafts": [ "aircrafts": [
"IL-76MD", "IL-76MD",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"Mi-24V Hind-E", "Mi-24V Hind-E",
"Mi-24P Hind-F", "Mi-24P Hind-F",
"Mi-8MTV2 Hip", "Mi-8MTV2 Hip",

View File

@@ -9,6 +9,7 @@
"aircrafts": [ "aircrafts": [
"IL-76MD", "IL-76MD",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"L-39ZA Albatros", "L-39ZA Albatros",
"Mi-24V Hind-E", "Mi-24V Hind-E",
"Mi-24P Hind-F", "Mi-24P Hind-F",

View File

@@ -9,6 +9,7 @@
"aircrafts": [ "aircrafts": [
"IL-76MD", "IL-76MD",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"L-39ZA Albatros", "L-39ZA Albatros",
"Mi-24V Hind-E", "Mi-24V Hind-E",
"Mi-24P Hind-F", "Mi-24P Hind-F",

View File

@@ -48,11 +48,15 @@
"Rapier" "Rapier"
], ],
"naval_units": [ "naval_units": [
"FFG Oliver Hazard Perry",
"DDG Arleigh Burke IIa",
"CG Ticonderoga", "CG Ticonderoga",
"LHA-1 Tarawa", "CVN-74 John C. Stennis",
"CVN-74 John C. Stennis" "Castle Class",
"DDG Arleigh Burke IIa",
"FFG Oliver Hazard Perry",
"HMS Achilles (F12)",
"HMS Andromeda (F57)",
"HMS Ariadne (F72)",
"HMS Invincible (R05)"
], ],
"missiles": [], "missiles": [],
"air_defense_units": [ "air_defense_units": [

View File

@@ -24,6 +24,7 @@
"F-16CM Fighting Falcon (Block 50)", "F-16CM Fighting Falcon (Block 50)",
"F/A-18C Hornet (Lot 20)", "F/A-18C Hornet (Lot 20)",
"Ka-50 Hokum", "Ka-50 Hokum",
"Ka-50 Hokum (Blackshark 3)",
"MB-339A", "MB-339A",
"Mi-24P Hind-F", "Mi-24P Hind-F",
"SH-60B Seahawk", "SH-60B Seahawk",

View File

@@ -160,11 +160,6 @@ local function onEvent(event)
write_state() write_state()
end end
if event.id == world.event.S_EVENT_BASE_CAPTURED and event.place then
base_capture_events[#base_capture_events + 1] = event.place.getID(event.place) .. "||" .. event.place.getCoalition(event.place) .. "||" .. event.place.getName(event.place)
write_state()
end
if event.id == world.event.S_EVENT_MISSION_END then if event.id == world.event.S_EVENT_MISSION_END then
mission_ended = true mission_ended = true
write_state() write_state()

View File

@@ -5,7 +5,7 @@ female_pilot_percentage: 0
country: USA country: USA
role: Strike Fighter role: Strike Fighter
aircraft: F-14A Tomcat (Block 135-GR Late) aircraft: F-14A Tomcat (Block 135-GR Late)
livery: VF-33 Starfighters 201 livery: VF-33 Starfighters AB201(Dale Snodgrass)
mission_types: mission_types:
- BAI - BAI
- BARCAP - BARCAP

View File

@@ -0,0 +1,18 @@
---
name: Escuadrón Aéreo 412
nickname: Halcones
country: Peru
role: Fighter
aircraft: Mirage 2000C
livery: Peru052
mission_types:
- BARCAP
- Escort
- Intercept
- Fighter sweep
- TARCAP
- BAI
- CAS
- OCA/Aircraft
- OCA/Runway
- Strike

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,26 @@
class: Helicopter
always_keeps_gun: true
carrier_capable: true
description:
"The Ka-50 Black Shark (NATO reporting name: Hokum) is a unique and deadly
single-seat, Russian attack helicopter that has seen combat in the Northern
Caucasus. It combines a high performance dual rotor system with a deadly
weapons payload of guided missiles, rockets, bombs, and a 30mm cannon. The
Ka-50 is also unique in that it has an ejection seat."
introduced: 1995
lha_capable: true
cabin_size: 0 # Can not transport troops
can_carry_crates: true
manufacturer: Kamov
origin: USSR/Russia
price: 20
role: Attack
variants:
Ka-50 Hokum (Blackshark 3): {}
radios:
intra_flight: R-800L1
inter_flight: R-800L1
# The R-800L1 doesn't have preset channels, and the other radio is for
# communications with FAC and ground units, which don't currently have
# radios assigned, so no channels to configure.
kneeboard_units: "metric"

View File

@@ -6,3 +6,17 @@ role: Light Attack
max_range: 200 max_range: 200
variants: variants:
MB-339A: {} MB-339A: {}
radios:
intra_flight: SRT-651/N
inter_flight: AN/ARC-150(V) 2
channels:
# The common allocator is sufficient for Liberation's purposes. There are
# more than 20 channels available on COMM2 (manual says 100, ME says 30,
# presumably only 30 can be truly *pre* set, and the other 70 can be set in
# the cockpit). We never need that many though, so no sense customizing
# further.
type: common
# COMM1 us UHF only. COMM2 is V/UHF. We prefer allocating intra-flight on
# VHF because it's less contested, so intra-flight goes to COMM2.
intra_flight_radio_index: 2
inter_flight_radio_index: 1

View File

@@ -0,0 +1,4 @@
class: Boat
price: 0
variants:
Castle Class: null

View File

@@ -0,0 +1,4 @@
class: HelicopterCarrier
price: 0
variants:
HMS Invincible (R05): null

View File

@@ -0,0 +1,4 @@
class: Frigate
price: 0
variants:
HMS Achilles (F12): null

View File

@@ -0,0 +1,4 @@
class: Frigate
price: 0
variants:
HMS Andromeda (F57): null

View File

@@ -0,0 +1,4 @@
class: Frigate
price: 0
variants:
HMS Ariadne (F72): null