Compare commits

..

2 Commits

Author SHA1 Message Date
Dan Albert
8c1f3e8116 Use python, not py.
py is a shortcut that launches the *latest* version of Python on the
machine. https://stackoverflow.com/a/50896577/632035

The build machines were updated to include python 3.9, so we were
doing everything with 3.9 instead of 3.8. pyproj doesn't have a binary
wheel for 3.9 on pypi yet, so we were falling back to building it from
source, which we aren't able to do, breaking the build.
2020-10-28 00:06:56 -07:00
Dan Albert
df00059d3f Fix initial frequencies for support aircraft.
Vaicom (a mod that adds voice control for the communications menus)
isn't able to follow the waypoint frequency change that normally sets
the radio channel for the AWACS/tanker flights. Set the group's
frequency correctly to start so it works.

(cherry picked from commit 4abf806837)
2020-10-17 15:56:48 -07:00
421 changed files with 8891 additions and 30734 deletions

View File

@@ -27,25 +27,6 @@ jobs:
# For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead # For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead
Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force
- name: mypy game
run: |
./venv/scripts/activate
mypy game
- name: mypy gen
run: |
./venv/scripts/activate
mypy gen
- name: mypy theater
run: |
./venv/scripts/activate
mypy theater
- name: update build number
run: |
[IO.File]::WriteAllLines($pwd.path + "\resources\buildnumber", $env:GITHUB_RUN_NUMBER)
- name: Build binaries - name: Build binaries
run: | run: |
./venv/scripts/activate ./venv/scripts/activate

View File

@@ -29,25 +29,6 @@ jobs:
# For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead # For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead
Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force
- name: Finalize version
run: |
New-Item -ItemType file resources\final
- name: mypy game
run: |
./venv/scripts/activate
mypy game
- name: mypy gen
run: |
./venv/scripts/activate
mypy gen
- name: mypy theater
run: |
./venv/scripts/activate
mypy theater
- name: Build binaries - name: Build binaries
run: | run: |
./venv/scripts/activate ./venv/scripts/activate

4
.gitignore vendored
View File

@@ -8,6 +8,7 @@ logs.txt
dist/** dist/**
a.py a.py
resources/tools/a.miz resources/tools/a.miz
tests/**
# User-specific stuff # User-specific stuff
.idea/ .idea/
@@ -15,8 +16,9 @@ resources/tools/a.miz
/liberation_preferences.json /liberation_preferences.json
/state.json /state.json
logs/ logs/liberation.log
qt_ui/logs/liberation.log qt_ui/logs/liberation.log
*.psd *.psd
resources/scripts/plugins/*

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"python.pythonPath": "g:\\python\\dcs_liberation\\venv\\Scripts\\python.exe",
"vsintellicode.python.completionsEnabled": true
}

View File

@@ -12,10 +12,10 @@
![GitHub stars](https://img.shields.io/github/stars/khopa/dcs_liberation?style=social) ![GitHub stars](https://img.shields.io/github/stars/khopa/dcs_liberation?style=social)
## About DCS Liberation ## About DCS Liberation
DCS Liberation is a [DCS World](https://www.digitalcombatsimulator.com/en/products/world/) turn based single-player or co-op dynamic campaign. DCS Liberation is a [DCS World](https://www.digitalcombatsimulator.com/en/products/world/) turn based single-player semi dynamic campaign.
It is an external program that generates full and complex DCS missions and manage a persistent combat environment. It is an external program that generates full and complex DCS missions and manage a persistent combat environment.
![Logo](https://i.imgur.com/4hq0rLq.png) ![Logo](https://imgur.com/B6tvlBJ.png)
## Downloads ## Downloads

View File

@@ -1,46 +1,3 @@
# 2.2.0
## Features/Improvements :
* **[Campaign Generator]** Added early warning radar generation
* **[Campaign Generator]** Added scud launcher sites
* **[Cheat Menu]** Added ability to capture base from mission planner
* **[Cheat Menu]** Added ability to show red ATO
* **[Factions]** Added WW2 factions that do not depend on WW2 asset pack
* **[Factions]** Cold War / Middle eastern factions will use Flak sites
* **[Flight Planner]** Flight planner overhaul, with package and TOT system
* **[Flight Planner]** Pick runways and ascent/descent based on headwind
* **[Map]** Added polygon debug mode display
* **[Map]** Highlight the selected flight path on the map
* **[Map]** Improved SAM display settings
* **[Map]** Improved flight plan display settings
* **[Map]** Caucasus and The Channel map use a new system to generate SAM and strike target location to reduce probability of targets generated in the middle of a forests
* **[Misc]** Flexible Dedicated Hosting Options for Mission Files via environment variables
* **[Moddability]** Custom campaigns can be designed through json files
* **[Moddability]** LUA plugins can now be injected into Liberation missions.
* **[Moddability]** Optional Skynet IADS lua plugin now included
* **[New Game]** Starting budget can be freely selected
* **[New Game]** Exanded information for faction and campaign selection in the new game wizard
* **[UI]** Add double and right click actions to many UI elements.
* **[UI]** Add polygon drawing mode for map background
* **[UI]** Added a warning if you press takeoff with no player enabled flights
* **[UI]** Packages and flights now visible in the main window sidebar
* **[Units/Factions]** Added bombers to some coalitions
* **[Units/Factions]** Added support for SU-57 mod by Cubanace
* **[Units]** Added Freya EWR sites to german WW2 factions
* **[Units]** Added support for many bombers (B-52H, B-1B, Tu-22, Tu-142)
* **[Units]** Added support for new P-47 variants
## Fixes :
* **[Campaign Generator]** Big airbases could end up without any airbase defense.
* **[Campaign generator]** Ship group and offshore buildings should not be generated on land anymore
* **[Flight Planner]** Fix waypoint alitudes for helicopters
* **[Flight Planner]** Fixed CAS aircraft wandering away from frontline
* **[Maps]** Incirlik airbase was missing exclusions zones, so SAMS could end up being generated on the runway
* **[Mission Generator]** Fixed player/client confusion when a flight had only one player slot.
* **[Radios]** Fix A-10C radio
* **[UI]** Many missing unit icons were added
* **[UI]** Missing TER weapons in custom payload now selectable.
# 2.1.5 # 2.1.5
## Features/Improvements : ## Features/Improvements :
@@ -53,7 +10,9 @@
# 2.1.4 # 2.1.4
## Fixes : ## Fixes :
* **[UI]** Fixed an issue that prevented generating the mission (take off button no working) on old savegames. * **[UI]** Fixed an issue that prevent generating the mission (take off button no working) on old savegames.
# 2.1.3
## Features/Improvements : ## Features/Improvements :
* **[Units/Factions]** Added A-10C_2 to USA 2005 and Bluefor modern factions * **[Units/Factions]** Added A-10C_2 to USA 2005 and Bluefor modern factions
@@ -317,4 +276,4 @@ Sorry :(
* **[Mission Generator]** Planned flights will spawn even if their home base has been captured or is being contested by enemy ground units. * **[Mission Generator]** Planned flights will spawn even if their home base has been captured or is being contested by enemy ground units.
* **[Campaign Generator]** Base defenses would not be generated on Normandy map and in some rare cases on others maps as well * **[Campaign Generator]** Base defenses would not be generated on Normandy map and in some rare cases on others maps as well
* **[Mission Planning]** CAS waypoints created from the "Predefined waypoint selector" would not be at the exact location of the frontline * **[Mission Planning]** CAS waypoints created from the "Predefined waypoint selector" would not be at the exact location of the frontline
* **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP) * **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP)

View File

@@ -1,3 +1,2 @@
from .game import Game from .game import Game
from . import db from . import db
from .version import VERSION

View File

@@ -1,11 +1,10 @@
import inspect import inspect
import dcs import dcs
DEFAULT_AVAILABLE_BUILDINGS = ['fuel', 'ammo', 'comms', 'oil', 'ware', 'farp', 'fob', 'power', 'factory', 'derrick'] DEFAULT_AVAILABLE_BUILDINGS = ['fuel', 'ammo', 'comms', 'oil', 'ware', 'farp', 'fob', 'power', 'factory', 'derrick', 'aa']
WW2_FREE = ['fuel', 'factory', 'ware'] WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp', 'aa']
WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp'] WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'aa']
WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp']
FORTIFICATION_BUILDINGS = ['Siegfried Line', 'Concertina wire', 'Concertina Wire', 'Czech hedgehogs 1', 'Czech hedgehogs 2', FORTIFICATION_BUILDINGS = ['Siegfried Line', 'Concertina wire', 'Concertina Wire', 'Czech hedgehogs 1', 'Czech hedgehogs 2',
'Dragonteeth 1', 'Dragonteeth 2', 'Dragonteeth 3', 'Dragonteeth 4', 'Dragonteeth 5', 'Dragonteeth 1', 'Dragonteeth 2', 'Dragonteeth 3', 'Dragonteeth 4', 'Dragonteeth 5',

View File

@@ -1,24 +1,4 @@
from dcs.planes import ( from dcs.planes import *
Bf_109K_4,
C_101CC,
FW_190A8,
FW_190D9,
F_5E_3,
F_86F_Sabre,
I_16,
L_39ZA,
MiG_15bis,
MiG_19P,
MiG_21Bis,
P_47D_30,
P_47D_30bl1,
P_47D_40,
P_51D,
P_51D_30_NA,
SpitfireLFMkIX,
SpitfireLFMkIXCW
)
from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.a4ec.a4ec import A_4E_C
""" """
@@ -43,8 +23,6 @@ GUNFIGHTERS = [
P_51D_30_NA, P_51D_30_NA,
P_51D, P_51D,
P_47D_30, P_47D_30,
P_47D_30bl1,
P_47D_40,
SpitfireLFMkIXCW, SpitfireLFMkIXCW,
SpitfireLFMkIX, SpitfireLFMkIX,
Bf_109K_4, Bf_109K_4,

View File

@@ -1,111 +1,95 @@
from dataclasses import dataclass
from datetime import timedelta
from game.utils import nm_to_meter, feet_to_meter from game.utils import nm_to_meter, feet_to_meter
MODERN_DOCTRINE = {
@dataclass(frozen=True) "GENERATORS": {
class Doctrine: "CAS": True,
cas: bool "CAP": True,
cap: bool "SEAD": True,
sead: bool "STRIKE": True,
strike: bool "ANTISHIP": True,
antiship: bool },
strike_max_range: int "STRIKE_MAX_RANGE": 1500000,
sead_max_range: int "SEAD_MAX_RANGE": 1500000,
rendezvous_altitude: int "CAP_EVERY_X_MINUTES": 20,
join_distance: int "CAS_EVERY_X_MINUTES": 30,
split_distance: int "SEAD_EVERY_X_MINUTES": 40,
ingress_egress_distance: int "STRIKE_EVERY_X_MINUTES": 40,
ingress_altitude: int
egress_altitude: int
min_patrol_altitude: int "INGRESS_EGRESS_DISTANCE": nm_to_meter(45),
max_patrol_altitude: int "INGRESS_ALT": feet_to_meter(20000),
pattern_altitude: int "EGRESS_ALT": feet_to_meter(20000),
"PATROL_ALT_RANGE": (feet_to_meter(15000), feet_to_meter(33000)),
"PATTERN_ALTITUDE": feet_to_meter(5000),
cap_duration: timedelta "CAP_PATTERN_LENGTH": (nm_to_meter(15), nm_to_meter(40)),
cap_min_track_length: int "FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(6), nm_to_meter(15)),
cap_max_track_length: int "CAP_DISTANCE_FROM_CP": (nm_to_meter(10), nm_to_meter(40)),
cap_min_distance_from_cp: int
cap_max_distance_from_cp: int
cas_duration: timedelta "MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
}
COLDWAR_DOCTRINE = {
MODERN_DOCTRINE = Doctrine( "GENERATORS": {
cap=True, "CAS": True,
cas=True, "CAP": True,
sead=True, "SEAD": True,
strike=True, "STRIKE": True,
antiship=True, "ANTISHIP": True,
strike_max_range=1500000, },
sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(25000),
join_distance=nm_to_meter(20),
split_distance=nm_to_meter(20),
ingress_egress_distance=nm_to_meter(45),
ingress_altitude=feet_to_meter(20000),
egress_altitude=feet_to_meter(20000),
min_patrol_altitude=feet_to_meter(15000),
max_patrol_altitude=feet_to_meter(33000),
pattern_altitude=feet_to_meter(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nm_to_meter(15),
cap_max_track_length=nm_to_meter(40),
cap_min_distance_from_cp=nm_to_meter(10),
cap_max_distance_from_cp=nm_to_meter(40),
cas_duration=timedelta(minutes=30),
)
COLDWAR_DOCTRINE = Doctrine( "STRIKE_MAX_RANGE": 1500000,
cap=True, "SEAD_MAX_RANGE": 1500000,
cas=True,
sead=True,
strike=True,
antiship=True,
strike_max_range=1500000,
sead_max_range=1500000,
rendezvous_altitude=feet_to_meter(22000),
join_distance=nm_to_meter(10),
split_distance=nm_to_meter(10),
ingress_egress_distance=nm_to_meter(30),
ingress_altitude=feet_to_meter(18000),
egress_altitude=feet_to_meter(18000),
min_patrol_altitude=feet_to_meter(10000),
max_patrol_altitude=feet_to_meter(24000),
pattern_altitude=feet_to_meter(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nm_to_meter(12),
cap_max_track_length=nm_to_meter(24),
cap_min_distance_from_cp=nm_to_meter(8),
cap_max_distance_from_cp=nm_to_meter(25),
cas_duration=timedelta(minutes=30),
)
WWII_DOCTRINE = Doctrine( "CAP_EVERY_X_MINUTES": 20,
cap=True, "CAS_EVERY_X_MINUTES": 30,
cas=True, "SEAD_EVERY_X_MINUTES": 40,
sead=False, "STRIKE_EVERY_X_MINUTES": 40,
strike=True,
antiship=True, "INGRESS_EGRESS_DISTANCE": nm_to_meter(30),
strike_max_range=1500000, "INGRESS_ALT": feet_to_meter(18000),
sead_max_range=1500000, "EGRESS_ALT": feet_to_meter(18000),
join_distance=nm_to_meter(5), "PATROL_ALT_RANGE": (feet_to_meter(10000), feet_to_meter(24000)),
split_distance=nm_to_meter(5), "PATTERN_ALTITUDE": feet_to_meter(5000),
rendezvous_altitude=feet_to_meter(10000),
ingress_egress_distance=nm_to_meter(7), "CAP_PATTERN_LENGTH": (nm_to_meter(12), nm_to_meter(24)),
ingress_altitude=feet_to_meter(8000), "FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(2), nm_to_meter(8)),
egress_altitude=feet_to_meter(8000), "CAP_DISTANCE_FROM_CP": (nm_to_meter(8), nm_to_meter(25)),
min_patrol_altitude=feet_to_meter(4000),
max_patrol_altitude=feet_to_meter(15000), "MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
pattern_altitude=feet_to_meter(5000), }
cap_duration=timedelta(minutes=30),
cap_min_track_length=nm_to_meter(8), WWII_DOCTRINE = {
cap_max_track_length=nm_to_meter(18),
cap_min_distance_from_cp=nm_to_meter(0), "GENERATORS": {
cap_max_distance_from_cp=nm_to_meter(5), "CAS": True,
cas_duration=timedelta(minutes=30), "CAP": True,
) "SEAD": False,
"STRIKE": True,
"ANTISHIP": True,
},
"STRIKE_MAX_RANGE": 1500000,
"SEAD_MAX_RANGE": 1500000,
"CAP_EVERY_X_MINUTES": 20,
"CAS_EVERY_X_MINUTES": 30,
"SEAD_EVERY_X_MINUTES": 40,
"STRIKE_EVERY_X_MINUTES": 40,
"INGRESS_EGRESS_DISTANCE": nm_to_meter(7),
"INGRESS_ALT": feet_to_meter(8000),
"EGRESS_ALT": feet_to_meter(8000),
"PATROL_ALT_RANGE": (feet_to_meter(4000), feet_to_meter(15000)),
"PATTERN_ALTITUDE": feet_to_meter(5000),
"CAP_PATTERN_LENGTH": (nm_to_meter(8), nm_to_meter(18)),
"FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(1), nm_to_meter(6)),
"CAP_DISTANCE_FROM_CP": (nm_to_meter(0), nm_to_meter(5)),
"MAX_NUMBER_OF_INTERCEPTION_GROUP": 3,
}

View File

@@ -1,26 +1,5 @@
from dcs.ships import (
CGN_1144_2_Pyotr_Velikiy,
CG_1164_Moskva,
CVN_70_Carl_Vinson,
CVN_71_Theodore_Roosevelt,
CVN_72_Abraham_Lincoln,
CVN_73_George_Washington,
CVN_74_John_C__Stennis,
CV_1143_5_Admiral_Kuznetsov,
CV_1143_5_Admiral_Kuznetsov_2017,
FFG_11540_Neustrashimy,
FFL_1124_4_Grisha,
FF_1135M_Rezky,
FSG_1241_1MP_Molniya,
LHA_1_Tarawa,
Oliver_Hazzard_Perry_class,
Ticonderoga_class,
Type_052B_Destroyer,
Type_052C_Destroyer,
Type_054A_Frigate,
USS_Arleigh_Burke_IIa,
)
from dcs.vehicles import AirDefence from dcs.vehicles import AirDefence
from dcs.ships import *
UNITS_WITH_RADAR = [ UNITS_WITH_RADAR = [

View File

@@ -1,173 +1,77 @@
import typing
import enum
from datetime import datetime from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional, Tuple, Type, Union
from dcs import Mission from dcs.countries import get_by_id, country_dict
from dcs.countries import country_dict from dcs.vehicles import *
from dcs.country import Country from dcs.ships import *
from dcs.helicopters import ( from dcs.planes import *
AH_1W, from dcs.helicopters import *
AH_64A,
AH_64D, from dcs.task import *
HelicopterType, from dcs.unit import *
Ka_50, from dcs.unittype import *
Mi_24V, from dcs.unitgroup import *
Mi_28N,
Mi_8MT, from game.factions.australia_2005 import Australia_2005
OH_58D, from game.factions.bluefor_coldwar import BLUEFOR_COLDWAR
SA342L, from game.factions.bluefor_coldwar_a4 import BLUEFOR_COLDWAR_A4
SA342M, from game.factions.bluefor_coldwar_mods import BLUEFOR_COLDWAR_MODS
SA342Minigun, from game.factions.canada_2005 import Canada_2005
SA342Mistral, from game.factions.china_2010 import China_2010
UH_1H, from game.factions.france_1995 import France_1995
UH_60A, from game.factions.france_2005 import France_2005
helicopter_map, from game.factions.france_modded import France_2005_Modded
) from game.factions.germany_1944_easy import Germany_1944_Easy
from dcs.mapping import Point from game.factions.germany_1990 import Germany_1990
# mypy can't resolve these if they're wildcard imports for some reason. from game.factions.insurgent import Insurgent
from dcs.planes import ( from game.factions.insurgent_modded import Insurgent_modded
AJS37, from game.factions.iran_2015 import Iran_2015
AV8BNA, from game.factions.israel_1948 import Israel_1948
A_10A, from game.factions.israel_1973 import Israel_1973, Israel_1973_NO_WW2_UNITS, Israel_1982
A_10C, from game.factions.israel_2000 import Israel_2000
A_10C_2, from game.factions.italy_1990 import Italy_1990
A_20G, from game.factions.italy_1990_mb339 import Italy_1990_MB339
A_50, from game.factions.japan_2005 import Japan_2005
An_26B, from game.factions.libya_2011 import Libya_2011
An_30M, from game.factions.netherlands_1990 import Netherlands_1990
B_17G, from game.factions.north_korea_2000 import NorthKorea_2000
B_1B, from game.factions.pakistan_2015 import Pakistan_2015
B_52H, from game.factions.private_miltary_companies import PMC_WESTERN_B, PMC_RUSSIAN, PMC_WESTERN_A
Bf_109K_4, from game.factions.russia_1975 import Russia_1975
C_101CC, from game.factions.germany_1944 import Germany_1944
C_130, from game.factions.india_2010 import India_2010
E_3A, from game.factions.russia_1955 import Russia_1955
FA_18C_hornet, from game.factions.russia_1965 import Russia_1965
FW_190A8, from game.factions.russia_1990 import Russia_1990
FW_190D9, from game.factions.russia_2010 import Russia_2010
F_117A, from game.factions.spain_1990 import Spain_1990
F_14B, from game.factions.sweden_1990 import Sweden_1990
F_15C, from game.factions.syria import Syria_2011, Syria_1967, Syria_1967_WW2_Weapons, Syria_1973, Arab_Armies_1948, Syria_1982
F_15E, from game.factions.turkey_2005 import Turkey_2005
F_16A, from game.factions.uae_2005 import UAE_2005
F_16C_50, from game.factions.uk_1944 import UK_1944
F_4E, from game.factions.uk_1990 import UnitedKingdom_1990
F_5E_3, from game.factions.ukraine_2010 import Ukraine_2010
F_86F_Sabre, from game.factions.us_aggressors import US_Aggressors
F_A_18C, from game.factions.usa_1944 import USA_1944, ALLIES_1944
IL_76MD, from game.factions.usa_1955 import USA_1955
IL_78M, from game.factions.usa_1960 import USA_1960
JF_17, from game.factions.usa_1965 import USA_1965
J_11A, from game.factions.usa_1990 import USA_1990
Ju_88A4, from game.factions.usa_2005 import USA_2005
KC130, from game.factions.bluefor_modern import BLUEFOR_MODERN
KC_135,
KJ_2000,
L_39C,
L_39ZA,
MQ_9_Reaper,
M_2000C,
MiG_15bis,
MiG_19P,
MiG_21Bis,
MiG_23MLD,
MiG_25PD,
MiG_27K,
MiG_29A,
MiG_29G,
MiG_29S,
MiG_31,
Mirage_2000_5,
P_47D_30,
P_47D_30bl1,
P_47D_40,
P_51D,
P_51D_30_NA,
PlaneType,
RQ_1A_Predator,
S_3B_Tanker,
SpitfireLFMkIX,
SpitfireLFMkIXCW,
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_25TM,
Su_27,
Su_30,
Su_33,
Su_34,
Tornado_GR4,
Tornado_IDS,
Tu_160,
Tu_22M3,
Tu_95MS,
WingLoong_I,
Yak_40,
plane_map,
)
from dcs.ships import (
Armed_speedboat,
Bulk_cargo_ship_Yakushev,
CVN_71_Theodore_Roosevelt,
CVN_72_Abraham_Lincoln,
CVN_73_George_Washington,
CVN_74_John_C__Stennis,
CV_1143_5_Admiral_Kuznetsov,
CV_1143_5_Admiral_Kuznetsov_2017,
Dry_cargo_ship_Ivanov,
LHA_1_Tarawa,
Tanker_Elnya_160,
ship_map,
)
from dcs.task import (
AWACS,
AntishipStrike,
CAP,
CAS,
CargoTransportation,
Embarking,
Escort,
GroundAttack,
Intercept,
MainTask,
Nothing,
PinpointStrike,
Reconnaissance,
Refueling,
SEAD,
Task,
Transport,
)
from dcs.terrain.terrain import Airport
from dcs.unit import Ship, Unit, Vehicle
from dcs.unitgroup import ShipGroup, StaticGroup
from dcs.unittype import FlyingType, ShipType, UnitType, VehicleType
from dcs.vehicles import (
AirDefence,
Armor,
Artillery,
Carriage,
Infantry,
Unarmed,
vehicle_map,
)
import pydcs_extensions.frenchpack.frenchpack as frenchpack
from game.factions.faction import Faction
# PATCH pydcs data with MODS # PATCH pydcs data with MODS
from game.factions.faction_loader import FactionLoader
from pydcs_extensions.a4ec.a4ec import A_4E_C from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.mb339.mb339 import MB_339PAN from pydcs_extensions.mb339.mb339 import MB_339PAN
import pydcs_extensions.frenchpack.frenchpack as frenchpack
from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M
from pydcs_extensions.su57.su57 import Su_57
plane_map["A-4E-C"] = A_4E_C plane_map["A-4E-C"] = A_4E_C
plane_map["MB-339PAN"] = MB_339PAN plane_map["MB-339PAN"] = MB_339PAN
plane_map["Rafale_M"] = Rafale_M plane_map["Rafale_M"] = Rafale_M
plane_map["Rafale_A_S"] = Rafale_A_S plane_map["Rafale_A_S"] = Rafale_A_S
plane_map["Su-57"] = Su_57
vehicle_map["FieldHL"] = frenchpack._FIELD_HIDE vehicle_map["FieldHL"] = frenchpack._FIELD_HIDE
vehicle_map["HARRIERH"] = frenchpack._FIELD_HIDE_SMALL vehicle_map["HARRIERH"] = frenchpack._FIELD_HIDE_SMALL
@@ -244,7 +148,6 @@ PRICES = {
J_11A: 26, J_11A: 26,
JF_17: 20, JF_17: 20,
Su_30: 24, Su_30: 24,
Su_57: 40,
SpitfireLFMkIX: 14, SpitfireLFMkIX: 14,
SpitfireLFMkIXCW: 14, SpitfireLFMkIXCW: 14,
@@ -309,10 +212,6 @@ PRICES = {
# Bombers # Bombers
B_52H: 35, B_52H: 35,
B_1B: 50, B_1B: 50,
F_117A: 100,
Tu_160: 50,
Tu_22M3: 40,
Tu_95MS: 35,
# special # special
IL_76MD: 30, IL_76MD: 30,
@@ -404,27 +303,24 @@ PRICES = {
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24, Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24,
Armor.MT_Pz_Kpfw_IV_Ausf_H:16, Armor.MT_Pz_Kpfw_IV_Ausf_H:16,
Armor.HT_Pz_Kpfw_VI_Tiger_I:24, Armor.HT_Pz_Kpfw_VI_Tiger_I:24,
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II:26, Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:26,
Armor.TD_Jagdpanther_G1: 18, Armor.TD_Jagdpanther_G1: 18,
Armor.TD_Jagdpanzer_IV: 11, Armor.TD_Jagdpanzer_IV: 11,
Armor.Sd_Kfz_184_Elefant: 18, Armor.Sd_Kfz_184_Elefant: 18,
Armor.APC_Sd_Kfz_251:4, Armor.APC_Sd_Kfz_251:4,
Armor.AC_Sd_Kfz_234_2_Puma:8, Armor.IFV_Sd_Kfz_234_2_Puma:8,
Armor.MT_M4_Sherman:12, Armor.MT_M4_Sherman:12,
Armor.MT_M4A4_Sherman_Firefly:16, Armor.MT_M4A4_Sherman_Firefly:16,
Armor.CT_Cromwell_IV:12, Armor.CT_Cromwell_IV:12,
Armor.M30_Cargo_Carrier:2, Armor.M30_Cargo_Carrier:2,
Armor.APC_M2A1:4, Armor.APC_M2A1:4,
Armor.CT_Centaur_IV: 10, Armor.ST_Centaur_IV: 10,
Armor.HIT_Churchill_VII: 16, Armor.HIT_Churchill_VII: 16,
Armor.LAC_M8_Greyhound: 8, Armor.LAC_M8_Greyhound: 8,
Armor.TD_M10_GMC: 14, Armor.TD_M10_GMC: 14,
Armor.StuG_III_Ausf__G: 12, Armor.StuG_III_Ausf__G: 12,
Artillery.M12_GMC: 10, Artillery.M12_GMC: 10,
Artillery.Sturmpanzer_IV_Brummbär: 10, Artillery.Sturmpanzer_IV_Brummbär: 10,
Armor.Daimler_Armoured_Car: 8,
Armor.LT_Mk_VII_Tetrarch: 8,
Armor.M4_Tractor: 2,
# ship # ship
CV_1143_5_Admiral_Kuznetsov: 100, CV_1143_5_Admiral_Kuznetsov: 100,
@@ -503,16 +399,12 @@ PRICES = {
AirDefence.AAA_Flak_38: 6, AirDefence.AAA_Flak_38: 6,
AirDefence.AAA_8_8cm_Flak_36: 8, AirDefence.AAA_8_8cm_Flak_36: 8,
AirDefence.AAA_8_8cm_Flak_37: 9, AirDefence.AAA_8_8cm_Flak_37: 9,
AirDefence.AAA_Flak_Vierling_38: 5, AirDefence.AAA_Flak_Vierling_38:6,
AirDefence.AAA_Kdo_G_40: 8, AirDefence.AAA_Kdo_G_40: 8,
AirDefence.Flak_Searchlight_37: 4, AirDefence.Flak_Searchlight_37: 4,
AirDefence.Maschinensatz_33: 10, AirDefence.Maschinensatz_33: 10,
AirDefence.AAA_8_8cm_Flak_41: 10, AirDefence.AAA_8_8cm_Flak_41: 10,
AirDefence.EWR_FuMG_401_Freya_LZ: 25,
AirDefence.AAA_Bofors_40mm: 8, AirDefence.AAA_Bofors_40mm: 8,
AirDefence.AAA_M1_37mm: 7,
AirDefence.AAA_M45_Quadmount: 4,
AirDefence.AA_gun_QF_3_7: 10,
# FRENCH PACK MOD # FRENCH PACK MOD
frenchpack.AMX_10RCR: 10, frenchpack.AMX_10RCR: 10,
@@ -567,7 +459,6 @@ UNIT_BY_TASK = {
F_5E_3, F_5E_3,
Su_27, Su_27,
Su_33, Su_33,
Su_57,
MiG_19P, MiG_19P,
MiG_21Bis, MiG_21Bis,
MiG_23MLD, MiG_23MLD,
@@ -600,53 +491,49 @@ UNIT_BY_TASK = {
SA342Mistral SA342Mistral
], ],
CAS: [ CAS: [
AH_1W, F_15E,
AH_64A, F_86F_Sabre,
AH_64D, MiG_15bis,
AJS37, L_39ZA,
AV8BNA, AV8BNA,
AJS37,
A_10A, A_10A,
A_10C, A_10C,
A_10C_2, A_10C_2,
A_20G,
B_17G,
B_1B,
B_52H,
F_117A,
F_15E,
F_86F_Sabre,
Ju_88A4,
Ka_50,
L_39ZA,
MB_339PAN,
MQ_9_Reaper,
MiG_15bis,
MiG_27K,
Mi_24V,
Mi_28N,
Mi_8MT,
OH_58D,
P_47D_30,
P_47D_30bl1,
P_47D_40,
RQ_1A_Predator,
Rafale_A_S,
SA342L,
SA342M,
SA342Minigun,
Su_17M4, Su_17M4,
Su_24M,
Su_24MR,
Su_25, Su_25,
Su_25T, Su_25T,
Su_34, Su_34,
Tornado_GR4, Ka_50,
SA342M,
SA342L,
SA342Minigun,
Su_24M,
Su_24MR,
AH_64A,
AH_64D,
OH_58D,
B_52H,
B_1B,
Tornado_IDS, Tornado_IDS,
Tu_160, Tornado_GR4,
Tu_22M3,
Tu_95MS,
UH_1H, UH_1H,
Mi_8MT,
Mi_28N,
Mi_24V,
MiG_27K,
A_20G,
P_47D_30,
P_47D_30bl1,
P_47D_40,
Ju_88A4,
B_17G,
MB_339PAN,
Rafale_A_S,
WingLoong_I, WingLoong_I,
MQ_9_Reaper,
RQ_1A_Predator,
AH_1W
], ],
Transport: [ Transport: [
IL_76MD, IL_76MD,
@@ -752,13 +639,13 @@ UNIT_BY_TASK = {
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G, Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
Armor.MT_Pz_Kpfw_IV_Ausf_H, Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.HT_Pz_Kpfw_VI_Tiger_I, Armor.HT_Pz_Kpfw_VI_Tiger_I,
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II, Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II,
Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251,
Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251,
Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251,
Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251,
Armor.AC_Sd_Kfz_234_2_Puma, Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.AC_Sd_Kfz_234_2_Puma, Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.MT_M4_Sherman, Armor.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly, Armor.MT_M4A4_Sherman_Firefly,
Armor.CT_Cromwell_IV, Armor.CT_Cromwell_IV,
@@ -771,12 +658,12 @@ UNIT_BY_TASK = {
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G, Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
Armor.MT_Pz_Kpfw_IV_Ausf_H, Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.HT_Pz_Kpfw_VI_Tiger_I, Armor.HT_Pz_Kpfw_VI_Tiger_I,
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II, Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II,
Armor.TD_Jagdpanther_G1, Armor.TD_Jagdpanther_G1,
Armor.TD_Jagdpanzer_IV, Armor.TD_Jagdpanzer_IV,
Armor.Sd_Kfz_184_Elefant, Armor.Sd_Kfz_184_Elefant,
Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251,
Armor.AC_Sd_Kfz_234_2_Puma, Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.MT_M4_Sherman, Armor.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly, Armor.MT_M4A4_Sherman_Firefly,
Armor.CT_Cromwell_IV, Armor.CT_Cromwell_IV,
@@ -785,8 +672,8 @@ UNIT_BY_TASK = {
Armor.M30_Cargo_Carrier, Armor.M30_Cargo_Carrier,
Armor.APC_M2A1, Armor.APC_M2A1,
Armor.APC_M2A1, Armor.APC_M2A1,
Armor.CT_Centaur_IV, Armor.ST_Centaur_IV,
Armor.CT_Centaur_IV, Armor.ST_Centaur_IV,
Armor.HIT_Churchill_VII, Armor.HIT_Churchill_VII,
Armor.LAC_M8_Greyhound, Armor.LAC_M8_Greyhound,
Armor.LAC_M8_Greyhound, Armor.LAC_M8_Greyhound,
@@ -906,13 +793,13 @@ SAM_CONVERT = {
""" """
Units that will always be spawned in the air Units that will always be spawned in the air
""" """
TAKEOFF_BAN: List[Type[FlyingType]] = [ TAKEOFF_BAN = [
] ]
""" """
Units that will be always spawned in the air if launched from the carrier Units that will be always spawned in the air if launched from the carrier
""" """
CARRIER_TAKEOFF_BAN: List[Type[FlyingType]] = [ CARRIER_TAKEOFF_BAN = [
Su_33, # Kuznecow is bugged in a way that only 2 aircraft could be spawned Su_33, # Kuznecow is bugged in a way that only 2 aircraft could be spawned
] ]
@@ -920,7 +807,98 @@ CARRIER_TAKEOFF_BAN: List[Type[FlyingType]] = [
Units separated by country. Units separated by country.
country : DCS Country name country : DCS Country name
""" """
FACTIONS = FactionLoader() FACTIONS = {
"Bluefor Modern": BLUEFOR_MODERN,
"Bluefor Cold War 1970s": BLUEFOR_COLDWAR,
"Bluefor Cold War (with A-4)": BLUEFOR_COLDWAR_A4,
"Bluefor Cold War (with A-4 and MB339)": BLUEFOR_COLDWAR_MODS,
"USA 1955 (WW2 Pack)": USA_1955,
"USA 1960": USA_1960,
"USA 1965": USA_1965,
"USA 1990": USA_1990,
"USA 2005": USA_2005,
"USA Aggressors 2005": US_Aggressors,
"Russia 1955": Russia_1955,
"Russia 1965": Russia_1965,
"Russia 1975": Russia_1975,
"Russia 1990": Russia_1990,
"Russia 2010": Russia_2010,
"France 1995": France_1995,
"France 2005": France_2005,
"France 2005 (Modded)": France_2005_Modded,
"Germany 1990": Germany_1990,
"Netherlands 1990": Netherlands_1990,
"United Kingdom 1990": UnitedKingdom_1990,
"Spain 1990": Spain_1990,
"Italy 1990": Italy_1990,
"Italy 1990 (With MB339)": Italy_1990_MB339,
"Israel 2000": Israel_2000,
"Israel 1982": Israel_1982,
"Israel 1973 (WW2 Pack)": Israel_1973,
"Israel 1973": Israel_1973_NO_WW2_UNITS,
"Israel 1948": Israel_1948,
"Arab Armies 1982": Syria_1982,
"Arab Armies 1973": Syria_1973,
"Arab Armies 1967 (WW2 Pack)": Syria_1967_WW2_Weapons,
"Arab Armies 1967": Syria_1967,
"Arab League 1948": Arab_Armies_1948,
"China 2010": China_2010,
"Sweden 1990": Sweden_1990,
"Australia 2005": Australia_2005,
"Canada 2005": Canada_2005,
"Japan 2005": Japan_2005,
"Turkey 2005": Turkey_2005,
"United Arab Emirates 2005": UAE_2005,
"Ukraine 2010": Ukraine_2010,
"India 2010": India_2010,
"Libya 2011": Libya_2011,
"Syria 2011": Syria_2011,
"Pakistan 2015": Pakistan_2015,
"Iran 2015": Iran_2015,
"North Korea 2000": NorthKorea_2000,
"Insurgent": Insurgent,
"Insurgent (Modded)": Insurgent_modded,
"PMC (American)": PMC_WESTERN_A,
"PMC (American) - MB339": PMC_WESTERN_B,
"PMC (Russian)": PMC_RUSSIAN,
"Allies 1944 (WW2 Pack)": USA_1944,
"USA 1944 (WW2 Pack)": ALLIES_1944,
"UK 1944 (WW2 Pack)": UK_1944,
"Germany 1944 (WW2 Pack)": Germany_1944,
"Germany 1944 Easy (WW2 Pack)": Germany_1944_Easy,
}
CARRIER_TYPE_BY_PLANE = { CARRIER_TYPE_BY_PLANE = {
FA_18C_hornet: CVN_74_John_C__Stennis, FA_18C_hornet: CVN_74_John_C__Stennis,
@@ -956,16 +934,11 @@ COMMON_OVERRIDE = {
PinpointStrike: "STRIKE", PinpointStrike: "STRIKE",
SEAD: "SEAD", SEAD: "SEAD",
AntishipStrike: "ANTISHIP", AntishipStrike: "ANTISHIP",
GroundAttack: "STRIKE", GroundAttack: "STRIKE"
Escort: "CAP",
} }
PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = { PLANE_PAYLOAD_OVERRIDES = {
B_1B: COMMON_OVERRIDE,
B_52H: COMMON_OVERRIDE,
F_117A: COMMON_OVERRIDE,
F_15E: COMMON_OVERRIDE,
FA_18C_hornet: { FA_18C_hornet: {
CAP: "CAP HEAVY", CAP: "CAP HEAVY",
Intercept: "CAP HEAVY", Intercept: "CAP HEAVY",
@@ -973,8 +946,7 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
PinpointStrike: "STRIKE", PinpointStrike: "STRIKE",
SEAD: "SEAD", SEAD: "SEAD",
AntishipStrike: "ANTISHIP", AntishipStrike: "ANTISHIP",
GroundAttack: "STRIKE", GroundAttack: "STRIKE"
Escort: "CAP HEAVY",
}, },
F_A_18C: { F_A_18C: {
CAP: "CAP HEAVY", CAP: "CAP HEAVY",
@@ -983,14 +955,8 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
PinpointStrike: "STRIKE", PinpointStrike: "STRIKE",
SEAD: "SEAD", SEAD: "SEAD",
AntishipStrike: "ANTISHIP", AntishipStrike: "ANTISHIP",
GroundAttack: "STRIKE", GroundAttack: "STRIKE"
Escort: "CAP HEAVY",
}, },
Tu_160: {
PinpointStrike: "Kh-65*12",
},
Tu_22M3: COMMON_OVERRIDE,
Tu_95MS: COMMON_OVERRIDE,
A_10A: COMMON_OVERRIDE, A_10A: COMMON_OVERRIDE,
A_10C: COMMON_OVERRIDE, A_10C: COMMON_OVERRIDE,
A_10C_2: COMMON_OVERRIDE, A_10C_2: COMMON_OVERRIDE,
@@ -999,6 +965,7 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
F_5E_3: COMMON_OVERRIDE, F_5E_3: COMMON_OVERRIDE,
F_14B: COMMON_OVERRIDE, F_14B: COMMON_OVERRIDE,
F_15C: COMMON_OVERRIDE, F_15C: COMMON_OVERRIDE,
F_15E: COMMON_OVERRIDE,
F_16C_50: COMMON_OVERRIDE, F_16C_50: COMMON_OVERRIDE,
JF_17: COMMON_OVERRIDE, JF_17: COMMON_OVERRIDE,
M_2000C: COMMON_OVERRIDE, M_2000C: COMMON_OVERRIDE,
@@ -1016,7 +983,6 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = {
Su_24M:COMMON_OVERRIDE, Su_24M:COMMON_OVERRIDE,
Su_30: COMMON_OVERRIDE, Su_30: COMMON_OVERRIDE,
Su_34: COMMON_OVERRIDE, Su_34: COMMON_OVERRIDE,
Su_57: COMMON_OVERRIDE,
MiG_23MLD: COMMON_OVERRIDE, MiG_23MLD: COMMON_OVERRIDE,
MiG_27K: COMMON_OVERRIDE, MiG_27K: COMMON_OVERRIDE,
Tornado_GR4: COMMON_OVERRIDE, Tornado_GR4: COMMON_OVERRIDE,
@@ -1133,7 +1099,6 @@ CARRIER_CAPABLE = [
AV8BNA, AV8BNA,
Su_33, Su_33,
A_4E_C, A_4E_C,
Rafale_M,
UH_1H, UH_1H,
Mi_8MT, Mi_8MT,
@@ -1169,17 +1134,17 @@ LHA_CAPABLE = [
---------- END OF CONFIGURATION SECTION ---------- END OF CONFIGURATION SECTION
""" """
UnitsDict = Dict[UnitType, int] UnitsDict = typing.Dict[UnitType, int]
PlaneDict = Dict[FlyingType, int] PlaneDict = typing.Dict[FlyingType, int]
HeliDict = Dict[HelicopterType, int] HeliDict = typing.Dict[HelicopterType, int]
ArmorDict = Dict[VehicleType, int] ArmorDict = typing.Dict[VehicleType, int]
ShipDict = Dict[ShipType, int] ShipDict = typing.Dict[ShipType, int]
AirDefenseDict = Dict[AirDefence, int] AirDefenseDict = typing.Dict[AirDefence, int]
AssignedUnitsDict = Dict[Type[UnitType], Tuple[int, int]] AssignedUnitsDict = typing.Dict[typing.Type[UnitType], typing.Tuple[int, int]]
TaskForceDict = Dict[Type[MainTask], AssignedUnitsDict] TaskForceDict = typing.Dict[typing.Type[Task], AssignedUnitsDict]
StartingPosition = Union[ShipGroup, StaticGroup, Airport, Point] StartingPosition = typing.Optional[typing.Union[ShipGroup, StaticGroup, Airport, Point]]
def upgrade_to_supercarrier(unit, name: str): def upgrade_to_supercarrier(unit, name: str):
@@ -1197,8 +1162,7 @@ def upgrade_to_supercarrier(unit, name: str):
else: else:
return unit return unit
def unit_task(unit: UnitType) -> Task:
def unit_task(unit: UnitType) -> Optional[Task]:
for task, units in UNIT_BY_TASK.items(): for task, units in UNIT_BY_TASK.items():
if unit in units: if unit in units:
return task return task
@@ -1209,12 +1173,10 @@ def unit_task(unit: UnitType) -> Optional[Task]:
print(unit.name + " cause issue") print(unit.name + " cause issue")
return None return None
def find_unittype(for_task: Task, country_name: str) -> typing.List[UnitType]:
return [x for x in UNIT_BY_TASK[for_task] if x in FACTIONS[country_name]["units"]]
def find_unittype(for_task: Task, country_name: str) -> List[UnitType]: def find_infantry(country_name: str) -> typing.List[UnitType]:
return [x for x in UNIT_BY_TASK[for_task] if x in FACTIONS[country_name].units]
def find_infantry(country_name: str) -> List[UnitType]:
inf = [ inf = [
Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS,
Infantry.Soldier_RPG, Infantry.Soldier_RPG,
@@ -1229,18 +1191,15 @@ def find_infantry(country_name: str) -> List[UnitType]:
Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand,
Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents
] ]
return [x for x in inf if x in FACTIONS[country_name].infantry_units] return [x for x in inf if x in FACTIONS[country_name]["units"]]
def unit_type_name(unit_type) -> str: def unit_type_name(unit_type) -> str:
return unit_type.id and unit_type.id or unit_type.name return unit_type.id and unit_type.id or unit_type.name
def unit_type_name_2(unit_type) -> str: def unit_type_name_2(unit_type) -> str:
return unit_type.name and unit_type.name or unit_type.id return unit_type.name and unit_type.name or unit_type.id
def unit_type_from_name(name: str) -> UnitType:
def unit_type_from_name(name: str) -> Optional[UnitType]:
if name in vehicle_map: if name in vehicle_map:
return vehicle_map[name] return vehicle_map[name]
elif name in plane_map: elif name in plane_map:
@@ -1273,7 +1232,7 @@ def task_name(task) -> str:
return task.name return task.name
def choose_units(for_task: Task, factor: float, count: int, country: str) -> List[UnitType]: def choose_units(for_task: Task, factor: float, count: int, country: str) -> typing.Collection[UnitType]:
suitable_unittypes = find_unittype(for_task, country) suitable_unittypes = find_unittype(for_task, country)
suitable_unittypes = [x for x in suitable_unittypes if x not in helicopter_map.values()] suitable_unittypes = [x for x in suitable_unittypes if x not in helicopter_map.values()]
suitable_unittypes.sort(key=lambda x: PRICES[x]) suitable_unittypes.sort(key=lambda x: PRICES[x])
@@ -1299,7 +1258,7 @@ def unitdict_merge(a: UnitsDict, b: UnitsDict) -> UnitsDict:
def unitdict_split(unit_dict: UnitsDict, count: int): def unitdict_split(unit_dict: UnitsDict, count: int):
buffer_dict: Dict[UnitType, int] = {} buffer_dict = {}
for unit_type, unit_count in unit_dict.items(): for unit_type, unit_count in unit_dict.items():
for _ in range(unit_count): for _ in range(unit_count):
unitdict_append(buffer_dict, unit_type, 1) unitdict_append(buffer_dict, unit_type, 1)
@@ -1322,7 +1281,7 @@ def unitdict_restrict_count(unit_dict: UnitsDict, total_count: int) -> UnitsDict
return {} return {}
def assigned_units_split(fd: AssignedUnitsDict) -> Tuple[PlaneDict, PlaneDict]: def assigned_units_split(fd: AssignedUnitsDict) -> typing.Tuple[PlaneDict, PlaneDict]:
return {k: v1 for k, (v1, v2) in fd.items()}, {k: v2 for k, (v1, v2) in fd.items()}, return {k: v1 for k, (v1, v2) in fd.items()}, {k: v2 for k, (v1, v2) in fd.items()},
@@ -1331,7 +1290,7 @@ def assigned_units_from(d: PlaneDict) -> AssignedUnitsDict:
def assignedunits_split_to_count(dict: AssignedUnitsDict, count: int): def assignedunits_split_to_count(dict: AssignedUnitsDict, count: int):
buffer_dict: Dict[Type[UnitType], Tuple[int, int]] = {} buffer_dict = {}
for unit_type, (unit_count, client_count) in dict.items(): for unit_type, (unit_count, client_count) in dict.items():
for _ in range(unit_count): for _ in range(unit_count):
new_count, new_client_count = buffer_dict.get(unit_type, (0, 0)) new_count, new_client_count = buffer_dict.get(unit_type, (0, 0))
@@ -1380,7 +1339,6 @@ class DefaultLiveries:
class Default(Enum): class Default(Enum):
af_standard = "" af_standard = ""
OH_58D.Liveries = DefaultLiveries OH_58D.Liveries = DefaultLiveries
F_16C_50.Liveries = DefaultLiveries F_16C_50.Liveries = DefaultLiveries
P_51D_30_NA.Liveries = DefaultLiveries P_51D_30_NA.Liveries = DefaultLiveries

View File

@@ -1,22 +1,24 @@
from __future__ import annotations import typing
import logging import logging
import math
from typing import Dict, List, Optional, Type, TYPE_CHECKING
from dcs.mapping import Point from dcs.action import Coalition
from dcs.task import Task from dcs.unittype import UnitType
from dcs.task import *
from dcs.vehicles import AirDefence
from dcs.unittype import UnitType from dcs.unittype import UnitType
from game import db, persistency from game import *
from game.debriefing import Debriefing
from game.infos.information import Information from game.infos.information import Information
from game.operation.operation import Operation from theater import *
from gen.ground_forces.combat_stance import CombatStance from gen.environmentgen import EnvironmentSettings
from theater import ControlPoint from gen.conflictgen import Conflict
from game.db import assigned_units_from, unitdict_from
from theater.start_generator import generate_airbase_defense_group
if TYPE_CHECKING: from userdata.debriefing import Debriefing
from ..game import Game from userdata import persistency
import game.db as db
DIFFICULTY_LOG_BASE = 1.1 DIFFICULTY_LOG_BASE = 1.1
EVENT_DEPARTURE_MAX_DISTANCE = 340000 EVENT_DEPARTURE_MAX_DISTANCE = 340000
@@ -26,7 +28,6 @@ MINOR_DEFEAT_INFLUENCE = 0.1
DEFEAT_INFLUENCE = 0.3 DEFEAT_INFLUENCE = 0.3
STRONG_DEFEAT_INFLUENCE = 0.5 STRONG_DEFEAT_INFLUENCE = 0.5
class Event: class Event:
silent = False silent = False
informational = False informational = False
@@ -36,15 +37,17 @@ class Event:
game = None # type: Game game = None # type: Game
location = None # type: Point location = None # type: Point
from_cp = None # type: ControlPoint from_cp = None # type: ControlPoint
departure_cp = None # type: ControlPoint
to_cp = None # type: ControlPoint to_cp = None # type: ControlPoint
operation = None # type: Operation operation = None # type: Operation
difficulty = 1 # type: int difficulty = 1 # type: int
environment_settings = None # type: EnvironmentSettings
BONUS_BASE = 5 BONUS_BASE = 5
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str): def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str):
self.game = game self.game = game
self.departure_cp: Optional[ControlPoint] = None self.departure_cp = None
self.from_cp = from_cp self.from_cp = from_cp
self.to_cp = target_cp self.to_cp = target_cp
self.location = location self.location = location
@@ -56,14 +59,14 @@ class Event:
return self.attacker_name == self.game.player_name return self.attacker_name == self.game.player_name
@property @property
def enemy_cp(self) -> Optional[ControlPoint]: def enemy_cp(self) -> ControlPoint:
if self.attacker_name == self.game.player_name: if self.attacker_name == self.game.player_name:
return self.to_cp return self.to_cp
else: else:
return self.departure_cp return self.departure_cp
@property @property
def tasks(self) -> List[Type[Task]]: def tasks(self) -> typing.Collection[typing.Type[Task]]:
return [] return []
@property @property
@@ -88,6 +91,18 @@ class Event:
def is_successfull(self, debriefing: Debriefing) -> bool: def is_successfull(self, debriefing: Debriefing) -> bool:
return self.operation.is_successfull(debriefing) return self.operation.is_successfull(debriefing)
def player_attacking(self, cp: ControlPoint, flights: db.TaskForceDict):
if self.is_player_attacking:
self.departure_cp = cp
else:
self.to_cp = cp
def player_defending(self, cp: ControlPoint, flights: db.TaskForceDict):
if self.is_player_attacking:
self.departure_cp = cp
else:
self.to_cp = cp
def generate(self): def generate(self):
self.operation.is_awacs_enabled = self.is_awacs_enabled self.operation.is_awacs_enabled = self.is_awacs_enabled
self.operation.ca_slots = self.ca_slots self.operation.ca_slots = self.ca_slots
@@ -144,13 +159,9 @@ class Event:
for i, ground_object in enumerate(cp.ground_objects): for i, ground_object in enumerate(cp.ground_objects):
if ground_object.is_dead: if ground_object.is_dead:
continue continue
if ( if ground_object.matches_string_identifier(destroyed_ground_unit_name):
(ground_object.group_name == destroyed_ground_unit_name) logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
or
(ground_object.is_same_group(destroyed_ground_unit_name))
):
logging.info("cp {} killing ground object {}".format(cp, ground_object.group_name))
cp.ground_objects[i].is_dead = True cp.ground_objects[i].is_dead = True
info = Information("Building destroyed", info = Information("Building destroyed",
@@ -165,7 +176,7 @@ class Event:
"", "",
self.game.turn) self.game.turn)
for i, ground_object in enumerate(cp.ground_objects): for i, ground_object in enumerate(cp.ground_objects):
if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA", "EWR"]: if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
for g in ground_object.groups: for g in ground_object.groups:
if not hasattr(g, "units_losts"): if not hasattr(g, "units_losts"):
g.units_losts = [] g.units_losts = []
@@ -198,19 +209,29 @@ class Event:
if cp.id == id: if cp.id == id:
if cp.captured and new_owner_coalition != coalition: if cp.captured and new_owner_coalition != coalition:
for_player = False cp.captured = False
info = Information(cp.name + " lost !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn) info = Information(cp.name + " lost !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn)
self.game.informations.append(info) self.game.informations.append(info)
pname = self.game.enemy_name
captured_cps.append(cp) captured_cps.append(cp)
elif not(cp.captured) and new_owner_coalition == coalition: elif not(cp.captured) and new_owner_coalition == coalition:
for_player = True cp.captured = True
info = Information(cp.name + " captured !", "We took control of " + cp.name + "! Great job !", self.game.turn) info = Information(cp.name + " captured !", "We took control of " + cp.name + "! Great job !", self.game.turn)
self.game.informations.append(info) self.game.informations.append(info)
pname = self.game.player_name
captured_cps.append(cp) captured_cps.append(cp)
else: else:
continue continue
cp.capture(self.game, for_player) cp.base.aircraft = {}
cp.base.armor = {}
airbase_def_id = 0
for g in cp.ground_objects:
g.groups = []
if g.airbase_group and pname != "":
generate_airbase_defense_group(airbase_def_id, g, pname, self.game, cp)
airbase_def_id = airbase_def_id + 1
for cp in captured_cps: for cp in captured_cps:
logging.info("Will run redeploy for " + cp.name) logging.info("Will run redeploy for " + cp.name)
@@ -232,7 +253,7 @@ class Event:
for enemy_cp in enemy_cps: for enemy_cp in enemy_cps:
print("Compute frontline progression for : " + cp.name + " to " + enemy_cp.name) print("Compute frontline progression for : " + cp.name + " to " + enemy_cp.name)
delta = 0.0 delta = 0
player_won = True player_won = True
ally_casualties = killed_unit_count_by_cp[cp.id] ally_casualties = killed_unit_count_by_cp[cp.id]
enemy_casualties = killed_unit_count_by_cp[enemy_cp.id] enemy_casualties = killed_unit_count_by_cp[enemy_cp.id]
@@ -355,6 +376,7 @@ class Event:
class UnitsDeliveryEvent(Event): class UnitsDeliveryEvent(Event):
informational = True informational = True
units = None # type: typing.Dict[UnitType, int]
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game): def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
super(UnitsDeliveryEvent, self).__init__(game=game, super(UnitsDeliveryEvent, self).__init__(game=game,
@@ -364,12 +386,12 @@ class UnitsDeliveryEvent(Event):
attacker_name=attacker_name, attacker_name=attacker_name,
defender_name=defender_name) defender_name=defender_name)
self.units: Dict[UnitType, int] = {} self.units = {}
def __str__(self): def __str__(self):
return "Pending delivery to {}".format(self.to_cp) return "Pending delivery to {}".format(self.to_cp)
def deliver(self, units: Dict[UnitType, int]): def deliver(self, units: typing.Dict[UnitType, int]):
for k, v in units.items(): for k, v in units.items():
self.units[k] = self.units.get(k, 0) + v self.units[k] = self.units.get(k, 0) + v

View File

@@ -1,17 +1,12 @@
from typing import List, Type from game.event import *
from dcs.task import CAP, CAS, Task
from game import db
from game.operation.frontlineattack import FrontlineAttackOperation from game.operation.frontlineattack import FrontlineAttackOperation
from .event import Event from userdata.debriefing import Debriefing
from ..debriefing import Debriefing
class FrontlineAttackEvent(Event): class FrontlineAttackEvent(Event):
@property @property
def tasks(self) -> List[Type[Task]]: def tasks(self) -> typing.Collection[typing.Type[Task]]:
if self.is_player_attacking: if self.is_player_attacking:
return [CAS, CAP] return [CAS, CAP]
else: else:
@@ -39,7 +34,6 @@ class FrontlineAttackEvent(Event):
self.to_cp.base.affect_strength(-0.1) self.to_cp.base.affect_strength(-0.1)
def player_attacking(self, flights: db.TaskForceDict): def player_attacking(self, flights: db.TaskForceDict):
assert self.departure_cp is not None
op = FrontlineAttackOperation(game=self.game, op = FrontlineAttackOperation(game=self.game,
attacker_name=self.attacker_name, attacker_name=self.attacker_name,
defender_name=self.defender_name, defender_name=self.defender_name,
@@ -47,3 +41,13 @@ class FrontlineAttackEvent(Event):
departure_cp=self.departure_cp, departure_cp=self.departure_cp,
to_cp=self.to_cp) to_cp=self.to_cp)
self.operation = op self.operation = op
def player_defending(self, flights: db.TaskForceDict):
op = FrontlineAttackOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp)
self.operation = op

View File

@@ -0,0 +1,50 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Australia_2005 = {
"country": "Australia",
"side": "blue",
"units": [
FA_18C_hornet,
KC_135,
KC130,
C_130,
E_3A,
Armor.MBT_M1A2_Abrams,
Armor.MBT_Leopard_1A3,
Armor.APC_M113,
Armor.IFV_LAV_25,
Armor.IFV_MCV_80,
UH_1H,
AH_1W, # Standing as EC Tiger
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.Rapier_FSA_Launcher,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.Rapier_FSA_Launcher,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
], "lhanames": [
"HMAS Canberra",
"HMAS Adelaide"
], "boat":[
"ArleighBurkeGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,58 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
BLUEFOR_COLDWAR = {
"country": "Combined Joint Task Forces Blue",
"side": "blue",
"units": [
F_14B,
F_4E,
F_5E_3,
A_10A,
AJS37,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
SA342M,
SA342L,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Chaparral_M48,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.AAA_Vulcan_M163,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat": [
], "has_jtac": True
}

View File

@@ -0,0 +1,65 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from pydcs_extensions.a4ec.a4ec import A_4E_C
BLUEFOR_COLDWAR_A4 = {
"country": "Combined Joint Task Forces Blue",
"side": "blue",
"units": [
F_14B,
F_4E,
F_5E_3,
A_10A,
AJS37,
A_4E_C,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
SA342M,
SA342L,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Chaparral_M48,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.AAA_Vulcan_M163,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat": [
], "requirements": {
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
}, "has_jtac": True
}

View File

@@ -0,0 +1,68 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.mb339.mb339 import MB_339PAN
BLUEFOR_COLDWAR_MODS = {
"country": "USA",
"side": "blue",
"units": [
F_14B,
F_4E,
F_5E_3,
A_10A,
AJS37,
A_4E_C,
MB_339PAN,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
SA342M,
SA342L,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Chaparral_M48,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.AAA_Vulcan_M163,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat": [
], "requirements": {
"MB-339A": "http://www.freccetricolorivirtuali.net/",
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
}, "has_jtac": True
}

View File

@@ -0,0 +1,83 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
BLUEFOR_MODERN = {
"country": "Combined Joint Task Forces Blue",
"side": "blue",
"units": [
F_15C,
F_14B,
FA_18C_hornet,
F_16C_50,
JF_17,
M_2000C,
F_5E_3,
Su_27,
Su_25T,
A_10A,
A_10C,
A_10C_2,
AV8BNA,
AJS37,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
AH_64D,
Ka_50,
SA342M,
SA342L,
Armor.MBT_M1A2_Abrams,
Armor.MBT_Leopard_2,
Armor.ATGM_M1134_Stryker,
Armor.IFV_M2A2_Bradley,
Armor.IFV_Marder,
Armor.APC_M1043_HMMWV_Armament,
Artillery.MLRS_M270,
Artillery.SPH_M109_Paladin,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Patriot_EPP_III,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.SAM_Avenger_M1097,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,43 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Canada_2005 = {
"country": "Canada",
"side": "blue",
"units": [
FA_18C_hornet,
KC_135,
KC130,
C_130,
E_3A,
Armor.MBT_Leopard_1A3,
Armor.MBT_Leopard_2,
Armor.IFV_LAV_25,
Armor.APC_M113,
Armor.IFV_MCV_80,
UH_1H,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Avenger_M1097,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.SAM_Avenger_M1097,
], "destroyer": [
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
], "boat":[
"ArleighBurkeGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,80 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
China_2010 = {
"country": "China",
"side": "red",
"units": [
MiG_21Bis, # Standing as J-7
Su_30,
Su_33,
J_11A,
JF_17,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
KJ_2000,
Mi_8MT,
Mi_28N,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C, # Standing as HQ-9+
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.HQ_7_Self_Propelled_LN,
Armor.ZTZ_96B,
Armor.MBT_T_55,
Armor.ZBD_04A,
Armor.IFV_BMP_1,
Artillery.MLRS_9A52_Smerch,
Artillery.SPH_2S9_Nona,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160
],
"shorad":[
AirDefence.SPAAA_ZSU_23_4_Shilka,
AirDefence.Rapier_FSA_Launcher, # Standing as PL-9C Shorad
AirDefence.HQ_7_Self_Propelled_LN
], "aircraft_carrier": [
CV_1143_5_Admiral_Kuznetsov,
], "destroyer": [
Type_052B_Destroyer,
Type_052C_Destroyer
], "cruiser": [
Type_054A_Frigate,
], "helicopter_carrier": [
Type_071_Amphibious_Transport_Dock,
], "lhanames": [
"Kunlun Shan",
"Jinggang Shan",
"Changbai Shan",
"Yimeng Shan",
"Longhu Shan",
"Wuzhi Shan",
"Wudang Shan"
], "carrier_names": [
"001 Liaoning",
"002 Shandong",
], "boat":[
"Type54GroupGenerator"
],
"has_jtac": True,
"jtac_unit": WingLoong_I
}

View File

@@ -1,258 +0,0 @@
from __future__ import annotations
import logging
from dataclasses import dataclass, field
from typing import Optional, Dict, Type, List, Any, cast
import dcs
from dcs.countries import country_dict
from dcs.planes import plane_map
from dcs.unittype import FlyingType, ShipType, VehicleType, UnitType
from dcs.vehicles import Armor, Unarmed, Infantry, Artillery, AirDefence
from game.data.building_data import WW2_ALLIES_BUILDINGS, DEFAULT_AVAILABLE_BUILDINGS, WW2_GERMANY_BUILDINGS, WW2_FREE
from game.data.doctrine import Doctrine, MODERN_DOCTRINE, COLDWAR_DOCTRINE, WWII_DOCTRINE
from pydcs_extensions.mod_units import MODDED_VEHICLES, MODDED_AIRPLANES
@dataclass
class Faction:
# Country used by this faction
country: str = field(default="")
# Nice name of the faction
name: str = field(default="")
# List of faction file authors
authors: str = field(default="")
# A description of the faction
description: str = field(default="")
# Available aircraft
aircrafts: List[UnitType] = field(default_factory=list)
# Available awacs aircraft
awacs: List[UnitType] = field(default_factory=list)
# Available tanker aircraft
tankers: List[UnitType] = field(default_factory=list)
# Available frontline units
frontline_units: List[VehicleType] = field(default_factory=list)
# Available artillery units
artillery_units: List[VehicleType] = field(default_factory=list)
# Infantry units used
infantry_units: List[VehicleType] = field(default_factory=list)
# Logistics units used
logistics_units: List[VehicleType] = field(default_factory=list)
# List of units that can be deployed as SHORAD
shorads: List[str] = field(default_factory=list)
# Possible SAMS site generators for this faction
sams: List[str] = field(default_factory=list)
# Possible EWR generators for this faction.
ewrs: List[str] = field(default_factory=list)
# Possible Missile site generators for this faction
missiles: List[str] = field(default_factory=list)
# Required mods or asset packs
requirements: Dict[str, str] = field(default_factory=dict)
# possible aircraft carrier units
aircraft_carrier: List[UnitType] = field(default_factory=list)
# possible helicopter carrier units
helicopter_carrier: List[UnitType] = field(default_factory=list)
# Possible carrier names
carrier_names: List[str] = field(default_factory=list)
# Possible helicopter carrier names
helicopter_carrier_names: List[str] = field(default_factory=list)
# Navy group generators
navy_generators: List[str] = field(default_factory=list)
# Available destroyers
destroyers: List[str] = field(default_factory=list)
# Available cruisers
cruisers: List[str] = field(default_factory=list)
# How many navy group should we try to generate per CP on startup for this faction
navy_group_count: int = field(default=1)
# How many missiles group should we try to generate per CP on startup for this faction
missiles_group_count: int = field(default=1)
# Whether this faction has JTAC access
has_jtac: bool = field(default=False)
# Unit to use as JTAC for this faction
jtac_unit: Optional[FlyingType] = field(default=None)
# doctrine
doctrine: Doctrine = field(default=MODERN_DOCTRINE)
# List of available buildings for this faction
building_set: List[str] = field(default_factory=list)
@classmethod
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction:
faction = Faction()
faction.country = json.get("country", "/")
if faction.country not in [c.name for c in country_dict.values()]:
raise AssertionError("Faction's country (\"{}\") is not a valid DCS country ID".format(faction.country))
faction.name = json.get("name", "")
if not faction.name:
raise AssertionError("Faction has no valid name")
faction.authors = json.get("authors", "")
faction.description = json.get("description", "")
faction.aircrafts = load_all_aircraft(json.get("aircrafts", []))
faction.awacs = load_all_aircraft(json.get("awacs", []))
faction.tankers = load_all_aircraft(json.get("tankers", []))
faction.frontline_units = load_all_vehicles(
json.get("frontline_units", []))
faction.artillery_units = load_all_vehicles(
json.get("artillery_units", []))
faction.infantry_units = load_all_vehicles(
json.get("infantry_units", []))
faction.logistics_units = load_all_vehicles(
json.get("logistics_units", []))
faction.sams = json.get("sams", [])
faction.ewrs = json.get("ewrs", [])
faction.shorads = json.get("shorads", [])
faction.missiles = json.get("missiles", [])
faction.requirements = json.get("requirements", {})
faction.carrier_names = json.get("carrier_names", [])
faction.helicopter_carrier_names = json.get(
"helicopter_carrier_names", [])
faction.navy_generators = json.get("navy_generators", [])
faction.aircraft_carrier = load_all_ships(
json.get("aircraft_carrier", []))
faction.helicopter_carrier = load_all_ships(
json.get("helicopter_carrier", []))
faction.destroyers = load_all_ships(json.get("destroyers", []))
faction.cruisers = load_all_ships(json.get("cruisers", []))
faction.has_jtac = json.get("has_jtac", False)
jtac_name = json.get("jtac_unit", None)
if jtac_name is not None:
faction.jtac_unit = load_aircraft(jtac_name)
else:
faction.jtac_unit = None
faction.navy_group_count = int(json.get("navy_group_count", 1))
faction.missiles_group_count = int(json.get("missiles_group_count", 0))
# Load doctrine
doctrine = json.get("doctrine", "modern")
if doctrine == "modern":
faction.doctrine = MODERN_DOCTRINE
elif doctrine == "coldwar":
faction.doctrine = COLDWAR_DOCTRINE
elif doctrine == "ww2":
faction.doctrine = WWII_DOCTRINE
else:
faction.doctrine = MODERN_DOCTRINE
# Load the building set
building_set = json.get("building_set", "default")
if building_set == "default":
faction.building_set = DEFAULT_AVAILABLE_BUILDINGS
elif building_set == "ww2free":
faction.building_set = WW2_FREE
elif building_set == "ww2ally":
faction.building_set = WW2_ALLIES_BUILDINGS
elif building_set == "ww2germany":
faction.building_set = WW2_GERMANY_BUILDINGS
else:
faction.building_set = DEFAULT_AVAILABLE_BUILDINGS
return faction
@property
def units(self) -> List[UnitType]:
return (self.infantry_units + self.aircrafts + self.awacs +
self.artillery_units + self.frontline_units +
self.tankers + self.logistics_units)
def unit_loader(unit: str, class_repository: List[Any]) -> Optional[UnitType]:
"""
Find unit by name
:param unit: Unit name as string
:param class_repository: Repository of classes (Either a module, a class, or a list of classes)
:return: The unit as a PyDCS type
"""
if unit is None:
return None
elif unit in plane_map.keys():
return plane_map[unit]
else:
for mother_class in class_repository:
if getattr(mother_class, unit, None) is not None:
return getattr(mother_class, unit)
if type(mother_class) is list:
for m in mother_class:
if m.__name__ == unit:
return m
logging.error(f"FACTION ERROR : Unable to find {unit} in pydcs")
return None
def load_aircraft(name: str) -> Optional[FlyingType]:
return cast(Optional[FlyingType], unit_loader(
name, [dcs.planes, dcs.helicopters, MODDED_AIRPLANES]
))
def load_all_aircraft(data) -> List[FlyingType]:
items = []
for name in data:
item = load_aircraft(name)
if item is not None:
items.append(item)
return items
def load_vehicle(name: str) -> Optional[VehicleType]:
return cast(Optional[FlyingType], unit_loader(
name, [Infantry, Unarmed, Armor, AirDefence, Artillery, MODDED_VEHICLES]
))
def load_all_vehicles(data) -> List[VehicleType]:
items = []
for name in data:
item = load_vehicle(name)
if item is not None:
items.append(item)
return items
def load_ship(name: str) -> Optional[ShipType]:
return cast(Optional[FlyingType], unit_loader(name, [dcs.ships]))
def load_all_ships(data) -> List[ShipType]:
items = []
for name in data:
item = load_ship(name)
if item is not None:
items.append(item)
return items

View File

@@ -1,46 +0,0 @@
from __future__ import annotations
import json
import logging
from pathlib import Path
from typing import Dict, Iterator, Optional, Type
from game.factions.faction import Faction
FACTION_DIRECTORY = Path("./resources/factions/")
class FactionLoader:
def __init__(self) -> None:
self._factions: Optional[Dict[str, Faction]] = None
@property
def factions(self) -> Dict[str, Faction]:
self.initialize()
assert self._factions is not None
return self._factions
def initialize(self) -> None:
if self._factions is None:
self._factions = self.load_factions()
@classmethod
def load_factions(cls: Type[FactionLoader]) -> Dict[str, Faction]:
files = [f for f in FACTION_DIRECTORY.glob("*.json") if f.is_file()]
factions = {}
for f in files:
try:
with f.open("r", encoding="utf-8") as fdata:
data = json.load(fdata, encoding="utf-8")
factions[data["name"]] = Faction.from_json(data)
logging.info("Loaded faction : " + str(f))
except Exception:
logging.exception(f"Unable to load faction : {f}")
return factions
def __getitem__(self, name: str) -> Faction:
return self.factions[name]
def __iter__(self) -> Iterator[str]:
return iter(self.factions.keys())

View File

@@ -0,0 +1,47 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
France_1995 = {
"country": "France",
"side": "blue",
"units": [
M_2000C,
Mirage_2000_5,
KC_135,
KC130,
C_130,
E_3A,
SA342M,
SA342L,
SA342Mistral,
Armor.MBT_Leclerc,
Armor.TPz_Fuchs, # Standing as VAB
Armor.APC_Cobra, # Standing as VBL
Armor.ATGM_M1134_Stryker, # Standing as VAB Mephisto
Artillery.SPH_M109_Paladin, # Standing as AMX30 AuF1
Artillery.MLRS_M270,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Roland_ADS,
AirDefence.SAM_Hawk_PCP,
AirDefence.HQ_7_Self_Propelled_LN, # Standing as Crotale
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_Roland_ADS
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,62 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
France_2005 = {
"country": "France",
"side": "blue",
"units":[
M_2000C,
Mirage_2000_5,
FA_18C_hornet, # Standing as Rafale M
KC_135,
KC130,
C_130,
E_3A,
SA342M,
SA342L,
SA342Mistral,
Armor.MBT_Leclerc,
Armor.TPz_Fuchs, # Standing as VAB
Armor.APC_Cobra, # Standing as VBL
Armor.ATGM_M1134_Stryker, # Standing as VAB Mephisto
Artillery.SPH_M109_Paladin, # Standing as AMX30 AuF1
Artillery.MLRS_M270,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Roland_ADS,
AirDefence.SAM_Hawk_PCP,
AirDefence.HQ_7_Self_Propelled_LN, # Standing as Crotale
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad":[
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_Roland_ADS
], "aircraft_carrier": [
CVN_74_John_C__Stennis, # Standing as CDG Aircraft Carrier
], "helicopter_carrier": [
LHA_1_Tarawa, # Standing as Mistral Class
], "destroyer": [
Oliver_Hazzard_Perry_class,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"PA Charles de Gaulle",
], "lhanames": [
"L9013 Mistral",
"L9014 Tonerre",
"L9015 Dixmude"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,80 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
import pydcs_extensions.frenchpack.frenchpack as frenchpack
from pydcs_extensions.rafale.rafale import Rafale_M, Rafale_A_S
France_2005_Modded = {
"country": "France",
"side": "blue",
"units": [
M_2000C,
Mirage_2000_5,
Rafale_M,
Rafale_A_S,
KC_135,
KC130,
C_130,
E_3A,
SA342M,
SA342L,
SA342Mistral,
Armor.MBT_Leclerc,
Artillery.SPH_M109_Paladin, # Standing as AMX30 AuF1
Artillery.MLRS_M270,
frenchpack.AMX_10RCR,
frenchpack.AMX_10RCR_SEPAR,
frenchpack.ERC_90,
frenchpack.TRM_2000_PAMELA,
frenchpack.VAB__50,
frenchpack.VAB_MEPHISTO,
frenchpack.VAB_T20_13,
frenchpack.VBL__50,
frenchpack.VBL_AANF1,
frenchpack.VBAE_CRAB,
frenchpack.VBAE_CRAB_MMP,
frenchpack.AMX_30B2,
frenchpack.Leclerc_Serie_XXI,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Roland_ADS,
AirDefence.SAM_Hawk_PCP,
AirDefence.HQ_7_Self_Propelled_LN, # Standing as Crotale
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_Roland_ADS
], "aircraft_carrier": [
CVN_74_John_C__Stennis, # Standing as CDG Aircraft Carrier
], "helicopter_carrier": [
LHA_1_Tarawa, # Standing as Mistral Class
], "destroyer": [
Oliver_Hazzard_Perry_class,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"PA Charles de Gaulle",
], "lhanames": [
"L9013 Mistral",
"L9014 Tonerre",
"L9015 Dixmude"
], "boat": [
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
], "requirements": {
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974",
"RAFALE 2.5.5": "https://www.digitalcombatsimulator.com/fr/files/3307478/",
}, "has_jtac": True
}

View File

@@ -0,0 +1,47 @@
from dcs.planes import *
from dcs.vehicles import *
from game.data.building_data import WW2_GERMANY_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
Germany_1944 = {
"country": "Third Reich",
"side": "red",
"units": [
FW_190A8,
FW_190D9,
Bf_109K_4,
Ju_88A4,
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.HT_Pz_Kpfw_VI_Tiger_I,
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II,
Armor.APC_Sd_Kfz_251,
Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.Sd_Kfz_184_Elefant,
Armor.TD_Jagdpanther_G1,
Armor.TD_Jagdpanzer_IV,
Artillery.Sturmpanzer_IV_Brummbär,
Unarmed.Sd_Kfz_2,
Unarmed.Sd_Kfz_7,
Unarmed.Kübelwagen_82,
Infantry.Infantry_Mauser_98,
AirDefence.AAA_8_8cm_Flak_36,
],"requirements":{
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"shorad": [
AirDefence.AAA_8_8cm_Flak_36,
],
"objects": WW2_GERMANY_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["UBoatGroupGenerator", "SchnellbootGroupGenerator"],
"boat_count": 2,
"missiles": ["V1GroupGenerator"],
"missiles_count": 1
}

View File

@@ -0,0 +1,40 @@
from dcs.planes import *
from dcs.vehicles import *
from game.data.building_data import WW2_GERMANY_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
Germany_1944_Easy = {
"country": "Third Reich",
"side": "red",
"units": [
FW_190A8,
FW_190D9,
Bf_109K_4,
Ju_88A4,
Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.APC_Sd_Kfz_251,
Armor.IFV_Sd_Kfz_234_2_Puma,
Artillery.Sturmpanzer_IV_Brummbär,
Unarmed.Sd_Kfz_2,
Unarmed.Sd_Kfz_7,
Unarmed.Kübelwagen_82,
Infantry.Infantry_Mauser_98,
AirDefence.AAA_8_8cm_Flak_36,
],"requirements":{
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"shorad":[
AirDefence.AAA_8_8cm_Flak_36,
],
"objects": WW2_GERMANY_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["UBoatGroupGenerator", "SchnellbootGroupGenerator"],
"boat_count": 1,
"missiles": ["V1GroupGenerator"],
"missiles_count": 1
}

View File

@@ -0,0 +1,45 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Germany_1990 = {
"country": "Germany",
"side": "blue",
"units":[
MiG_29G,
Tornado_IDS,
F_4E,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
SA342M,
SA342L,
Armor.TPz_Fuchs,
Armor.MBT_Leopard_1A3,
Armor.MBT_Leopard_2,
Armor.IFV_Marder,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Roland_ADS,
AirDefence.SAM_Hawk_PCP,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad":[
AirDefence.SPAAA_Gepard,
AirDefence.SAM_Roland_ADS,
], "boat":[
"OliverHazardPerryGroupGenerator"
]
}

View File

@@ -0,0 +1,55 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
India_2010 = {
"country": "India",
"side": "blue",
"units": [
Mirage_2000_5,
M_2000C,
MiG_27K,
MiG_21Bis,
MiG_29S,
Su_30,
KC_135,
KC130,
C_130,
E_3A,
AH_64A,
Mi_8MT,
Armor.MBT_T_90,
Armor.MBT_T_72B,
Armor.IFV_BMP_2,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_3_S_125_LN_5P73,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad":[
AirDefence.SAM_SA_8_Osa_9A33,
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
AirDefence.SAM_SA_8_Osa_9A33,
AirDefence.SAM_SA_19_Tunguska_2S6
], "aircraft_carrier": [
CV_1143_5_Admiral_Kuznetsov,
], "destroyer": [
FSG_1241_1MP_Molniya,
], "carrier_names": [
"INS Vikramaditya"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator", "MolniyaGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,27 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Insurgent = {
"country": "Insurgents",
"side": "red",
"units": [
AirDefence.AAA_ZU_23_Insurgent_Closed,
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
Armor.APC_Cobra,
Armor.APC_MTLB,
Armor.ARV_BRDM_2,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Infantry_Soldier_Insurgents,
Infantry.Soldier_RPG,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160
]
}

View File

@@ -0,0 +1,33 @@
from dcs.ships import *
from dcs.vehicles import *
from pydcs_extensions.frenchpack.frenchpack import DIM__TOYOTA_BLUE, DIM__TOYOTA_DESERT, DIM__TOYOTA_GREEN, \
DIM__KAMIKAZE
Insurgent_modded = {
"country": "Insurgents",
"side": "red",
"units": [
AirDefence.AAA_ZU_23_Insurgent_Closed,
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
DIM__TOYOTA_BLUE,
DIM__TOYOTA_DESERT,
DIM__TOYOTA_GREEN,
DIM__KAMIKAZE,
Armor.ARV_BRDM_2,
Armor.APC_Cobra,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
Infantry.Infantry_Soldier_Insurgents,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160
], "requirements": {
"frenchpack V3.5": "https://forums.eagle.ru/showthread.php?t=279974",
}
}

View File

@@ -0,0 +1,58 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Iran_2015 = {
"country": "Iran",
"side": "red",
"units": [
MiG_29A,
F_4E,
F_14B,
F_5E_3,
MiG_21Bis,
Su_24M,
Su_25,
Su_17M4,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Mi_28N,
Mi_24V,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_SA_2_LN_SM_90,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
Armor.APC_M113,
Armor.APC_BTR_80,
Armor.MBT_M60A3_Patton,
Armor.MBT_T_72B,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160
],
"shorad":[
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.AAA_ZU_23_Insurgent_Closed
], "boat":[
"GrishaGroupGenerator", "MolniyaGroupGenerator", "KiloSubGroupGenerator"
]
}

View File

@@ -0,0 +1,33 @@
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Israel_1948 = {
"country": "Israel",
"side": "blue",
"units":[
SpitfireLFMkIXCW,
SpitfireLFMkIX,
P_51D,
P_51D_30_NA,
Bf_109K_4, # Standing as Avia S-199
B_17G,
Armor.MT_M4A4_Sherman_Firefly,
Armor.APC_M2A1,
Armor.MT_M4_Sherman,
Armor.LAC_M8_Greyhound,
Unarmed.Transport_M818,
Infantry.Infantry_SMLE_No_4_Mk_1,
AirDefence.AAA_Bofors_40mm,
Armed_speedboat,
],"requirements":{
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"shorad": [
AirDefence.AAA_Bofors_40mm
], "boat": [
], "has_jtac": False
}

View File

@@ -0,0 +1,112 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from pydcs_extensions.a4ec.a4ec import A_4E_C
Israel_1973 = {
"country": "Israel",
"side": "blue",
"units":[
F_4E,
A_4E_C,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
Armor.MT_M4A4_Sherman_Firefly,
Armor.APC_M2A1,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Hawk_PCP,
AirDefence.AAA_Bofors_40mm,
AirDefence.SAM_Chaparral_M48,
Armed_speedboat,
], "requirements": {
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
}, "shorad": [
AirDefence.SAM_Chaparral_M48,
AirDefence.AAA_Bofors_40mm
], "boat": [
], "has_jtac": True
}
Israel_1973_NO_WW2_UNITS = {
"country": "Israel",
"side": "blue",
"units":[
F_4E,
A_4E_C,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Chaparral_M48,
Armed_speedboat,
], "requirements": {
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
}, "shorad": [
AirDefence.SAM_Chaparral_M48,
], "boat": [
], "has_jtac": True
}
Israel_1982 = {
"country": "Israel",
"side": "blue",
"units":[
F_4E,
A_4E_C,
F_15C,
F_16A,
F_16C_50,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
AH_1W,
Armor.APC_M113,
Armor.MBT_M60A3_Patton,
Armor.MBT_Merkava_Mk__4,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Chaparral_M48,
Armed_speedboat,
], "requirements": {
"Community A-4E": "https://heclak.github.io/community-a4e-c/",
}, "shorad": [
AirDefence.SAM_Chaparral_M48,
], "boat": [
], "has_jtac": True
}

View File

@@ -0,0 +1,44 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Israel_2000 = {
"country": "Israel",
"side": "blue",
"units":[
F_16C_50,
F_15C,
F_15E,
F_4E,
KC_135,
KC130,
C_130,
E_3A,
AH_1W,
AH_64D,
Armor.MBT_Merkava_Mk__4,
Armor.APC_M113,
Armor.APC_M1043_HMMWV_Armament,
Armor.ATGM_M1045_HMMWV_TOW,
Artillery.SPH_M109_Paladin,
Artillery.MLRS_M270,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Patriot_EPP_III,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad": [
AirDefence.SAM_Avenger_M1097
], "boat": [
"ArleighBurkeGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,48 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Italy_1990 = {
"country": "Italy",
"side": "blue",
"units": [
Tornado_IDS,
AV8BNA,
# MB339,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
AH_1W,
UH_1H,
Armor.MBT_Leopard_1A3, # OF-40 MBT
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Avenger_M1097,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad":[
AirDefence.SAM_Avenger_M1097,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
], "cruiser": [
Ticonderoga_class,
], "lhanames": [
"Giuseppe Garibaldi",
"Cavour",
], "boat":[
"OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,52 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from pydcs_extensions.mb339.mb339 import MB_339PAN
Italy_1990_MB339 = {
"country": "Italy",
"side": "blue",
"units": [
Tornado_IDS,
AV8BNA,
MB_339PAN,
KC_135,
S_3B_Tanker,
C_130,
E_3A,
AH_1W,
UH_1H,
Armor.MBT_Leopard_1A3, # OF-40 MBT
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Avenger_M1097,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad":[
AirDefence.SAM_Avenger_M1097,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
], "cruiser": [
Ticonderoga_class,
], "lhanames": [
"Giuseppe Garibaldi",
"Cavour",
], "boat": [
"OliverHazardPerryGroupGenerator"
], "requirements": {
"MB-339A": "http://www.freccetricolorivirtuali.net/",
}, "has_jtac": True
}

View File

@@ -0,0 +1,54 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Japan_2005 = {
"country": "Japan",
"side": "blue",
"units": [
F_15C, # F-15J/DJ
F_16C_50, # F-2A/B
F_4E, # F-4EJ
KC_135,
KC130,
C_130,
E_3A,
AH_1W,
AH_64D,
Armor.MBT_Merkava_Mk__4, # Standing as Type 10 MBT
Armor.MBT_M1A2_Abrams, # Standing as Type 90 MBT
Armor.IFV_Marder, # Standing as Type 89 IFV
Armor.TPz_Fuchs, # Standing as Type 96 APC
Armor.IFV_LAV_25, # Standing as Type 16 or Type 87
Armor.APC_M1043_HMMWV_Armament,
Artillery.MLRS_M270,
Artillery.SPH_M109_Paladin, # Standing as Type 99 SPH
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Patriot_EPP_III,
LHA_1_Tarawa,
], "shorad": [
AirDefence.SPAAA_Gepard, # Type 87 SPAG
], "helicopter_carrier": [
LHA_1_Tarawa, # Standing as Hyuga-class helicopter carrier
], "destroyer": [
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
], "lhanames": [
"Hyuga",
"Ise",
], "boat":[
"ArleighBurkeGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,49 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.vehicles import *
Libya_2011 = {
"country": "Libya",
"side": "red",
"units": [
MiG_21Bis,
MiG_23MLD,
Su_24M,
Su_17M4,
Mi_24V,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
AirDefence.SAM_SA_8_Osa_9A33,
AirDefence.SAM_SA_2_LN_SM_90,
AirDefence.SAM_SA_3_S_125_LN_5P73,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.HQ_7_Self_Propelled_LN,
Armor.IFV_BMP_1,
Armor.FDDM_Grad,
Armor.ARV_BRDM_2,
Armor.MBT_T_55,
Armor.MBT_T_72B,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Paratrooper_RPG_16,
Infantry.Infantry_Soldier_Insurgents
],
"shorad":[
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.SAM_SA_8_Osa_9A33,
], "boat": [
"GrishaGroupGenerator", "MolniyaGroupGenerator"
]
}

View File

@@ -0,0 +1,38 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Netherlands_1990 = {
"country": "The Netherlands",
"side": "blue",
"units": [
F_16C_50,
F_5E_3,
KC_135,
KC130,
C_130,
E_3A,
AH_64A,
Armor.APC_M113,
Armor.MBT_Leopard_1A3,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Avenger_M1097,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad": [
AirDefence.SAM_Avenger_M1097
], "boat": [
"OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,53 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
NorthKorea_2000 = {
"country": "North Korea",
"side": "red",
"units":[
MiG_29A,
Su_25,
MiG_15bis,
MiG_21Bis,
MiG_23MLD,
MiG_19P,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Mi_8MT,
Mi_24V,
Armor.MBT_T_55,
Armor.MBT_T_72B,
Armor.MBT_T_80U,
Armor.IFV_BMP_1,
Armor.APC_BTR_80,
Armor.ARV_BRDM_2,
Unarmed.Transport_M818,
Infantry.Soldier_AK,
AirDefence.SAM_SA_2_LN_SM_90,
AirDefence.SAM_SA_3_S_125_LN_5P73,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160
],
"shorad":[
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka
],
"boat": [
"GrishaGroupGenerator", "MolniyaGroupGenerator"
]
}

View File

@@ -0,0 +1,42 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Pakistan_2015 = {
"country": "Pakistan",
"side": "blue",
"units": [
JF_17,
F_16C_50,
MiG_21Bis, # Standing as J-7
MiG_19P, # Standing as J-6
IL_78M,
E_3A,
UH_1H,
AH_1W,
Armor.MBT_T_80U,
Armor.MBT_T_55, # Standing as Al-Zarrar / Type 59 MBT
Armor.ZBD_04A,
Armor.APC_BTR_80,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_SA_2_LN_SM_90, # Standing as HQ-2
AirDefence.SAM_SA_10_S_300PS_LN_5P85C, # Standing as HQ-9
Armed_speedboat,
], "shorad": [
AirDefence.HQ_7_Self_Propelled_LN,
AirDefence.AAA_ZU_23_Insurgent_on_Ural_375,
AirDefence.AAA_ZU_23_Closed
], "boat": [
"Type54GroupGenerator", "OliverHazardPerryGroupGenerator"
],
"has_jtac": True,
"jtac_unit": WingLoong_I
}

View File

@@ -0,0 +1,91 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from pydcs_extensions.mb339.mb339 import MB_339PAN
PMC_WESTERN_A = {
"country": "USA",
"side": "blue",
"units": [
C_101CC,
UH_1H,
Mi_8MT,
OH_58D,
SA342M,
Armor.APC_M1043_HMMWV_Armament,
Armor.IFV_MCV_80,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Avenger_M1097,
Armed_speedboat,
], "shorad":[
AirDefence.SAM_Avenger_M1097,
], "has_jtac": True
}
PMC_WESTERN_B = {
"country": "USA",
"side": "blue",
"units": [
MB_339PAN,
C_101CC,
UH_1H,
Mi_8MT,
OH_58D,
SA342M,
Armor.APC_M1043_HMMWV_Armament,
Armor.IFV_MCV_80,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Avenger_M1097,
Armed_speedboat,
], "shorad":[
AirDefence.SAM_Avenger_M1097,
], "has_jtac": True,
"requirements": {
"MB-339A": "http://www.freccetricolorivirtuali.net/",
}
}
PMC_RUSSIAN = {
"country": "Russia",
"side": "blue",
"units": [
L_39C,
L_39ZA,
Mi_8MT,
Mi_24V,
Ka_50,
Armor.APC_Cobra,
Armor.APC_BTR_80,
Armor.ARV_BRDM_2,
Unarmed.Transport_Ural_375,
Infantry.Paratrooper_AKS,
Infantry.Paratrooper_RPG_16,
AirDefence.AAA_ZU_23_on_Ural_375,
Armed_speedboat,
], "shorad":[
AirDefence.AAA_ZU_23_on_Ural_375,
AirDefence.AAA_ZU_23_Closed,
], "has_jtac": True
}

View File

@@ -0,0 +1,38 @@
from dcs.planes import MiG_15bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40
from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
Russia_1955 = {
"country": "Russia",
"side": "red",
"units": [
MiG_15bis,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
AirDefence.AAA_ZU_23_Closed,
AirDefence.AAA_ZU_23_on_Ural_375,
Armor.ARV_BRDM_2,
Armor.FDDM_Grad,
Armor.APC_MTLB,
Armor.MBT_T_55,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
]
}

View File

@@ -0,0 +1,55 @@
from dcs.helicopters import Mi_8MT
from dcs.planes import MiG_15bis, MiG_19P, MiG_21Bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40, A_50
from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
Russia_1965 = {
"country": "Russia",
"side": "red",
"units": [
MiG_15bis,
MiG_19P,
MiG_21Bis,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Mi_8MT,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_2_LN_SM_90,
AirDefence.SAM_SA_3_S_125_LN_5P73,
Armor.ARV_BRDM_2,
Armor.APC_BTR_80,
Armor.ARV_BTR_RD,
Armor.IFV_BMD_1,
Armor.IFV_BMP_1,
Armor.MBT_T_55,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
],
"shorad":[
AirDefence.AAA_ZU_23_Closed
], "boat": [
"GrishaGroupGenerator"
]
}

View File

@@ -0,0 +1,71 @@
from dcs.helicopters import Mi_8MT, Mi_24V
from dcs.planes import MiG_21Bis, MiG_23MLD, MiG_25PD, MiG_29A, Su_17M4, Su_24M, Su_25, IL_76MD, IL_78M, An_26B, An_30M, \
Yak_40, A_50
from dcs.ships import *
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
Russia_1975 = {
"country": "Russia",
"side": "red",
"units": [
MiG_21Bis,
MiG_23MLD,
MiG_25PD,
MiG_29A,
Su_17M4,
Su_24M,
Su_25,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Mi_8MT,
Mi_24V,
AirDefence.AAA_ZU_23_Closed,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_3_S_125_LN_5P73,
Armor.ARV_BRDM_2,
Armor.APC_BTR_80,
Armor.IFV_BMD_1,
Armor.IFV_BMP_1,
Armor.MBT_T_55,
Artillery.SPH_2S9_Nona,
Artillery.SPH_2S1_Gvozdika,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
],
"shorad": [
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka
], "aircraft_carrier": [
CV_1143_5_Admiral_Kuznetsov,
], "destroyer": [
FF_1135M_Rezky,
], "cruiser": [
CGN_1144_2_Pyotr_Velikiy,
], "boat": [
"RussianNavyGroupGenerator", "KiloSubGroupGenerator", "MolniyaGroupGenerator"
]
}

View File

@@ -0,0 +1,74 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Russia_1990 = {
"country": "Russia",
"side": "red",
"units": [
MiG_23MLD,
MiG_25PD,
MiG_29A,
MiG_29S,
MiG_31,
Su_27,
Su_24M,
Su_25,
Ka_50,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Mi_8MT,
Mi_24V,
AirDefence.AAA_ZU_23_Closed,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_3_S_125_LN_5P73,
Armor.ARV_BRDM_2,
Armor.APC_BTR_80,
Armor.IFV_BMD_1,
Armor.IFV_BMP_1,
Armor.MBT_T_55,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.SPH_2S19_Msta,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
],
"shorad":[
AirDefence.SAM_SA_9_Strela_1_9P31,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
AirDefence.SPAAA_ZSU_23_4_Shilka
], "carrier_names": [
"Admiral Kuznetov",
"Admiral Gorshkov"
], "aircraft_carrier": [
CV_1143_5_Admiral_Kuznetsov,
], "destroyer": [
FF_1135M_Rezky,
], "cruiser": [
FSG_1241_1MP_Molniya,
], "boat":[
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
]
}

View File

@@ -0,0 +1,77 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Russia_2010 = {
"country": "Russia",
"side": "red",
"units": [
Su_27,
Su_30,
Su_33,
MiG_29S,
MiG_31,
Su_25,
Su_25T,
Su_34,
Su_24M,
L_39ZA,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Ka_50,
Mi_8MT,
Mi_24V,
Mi_28N,
AirDefence.SAM_SA_19_Tunguska_2S6,
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
Armor.APC_BTR_80,
Armor.MBT_T_90,
Armor.MBT_T_80U,
Armor.MBT_T_72B,
Armor.IFV_BMP_1,
Armor.IFV_BMP_2,
Armor.IFV_BMP_3,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.SPH_2S19_Msta,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
],
"shorad":[
AirDefence.SAM_SA_19_Tunguska_2S6,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3
], "aircraft_carrier": [
CV_1143_5_Admiral_Kuznetsov,
], "carrier_names": [
"Admiral Kuznetov"
], "destroyer": [
FF_1135M_Rezky,
], "cruiser": [
FSG_1241_1MP_Molniya,
], "boat": [
"RussianNavyGroupGenerator", "KiloSubGroupGenerator"
]
}

View File

@@ -0,0 +1,50 @@
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Spain_1990 = {
"country": "Spain",
"side": "blue",
"units": [
FA_18C_hornet,
AV8BNA,
F_5E_3,
C_101CC,
KC_135,
KC130,
C_130,
E_3A,
Armor.MBT_M60A3_Patton,
Armor.MBT_Leopard_2,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Avenger_M1097,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad":[
AirDefence.SAM_Avenger_M1097,
], "aircraft_carrier": [
CVN_74_John_C__Stennis, # Standing as Principe de Asturias
], "helicopter_carrier": [
LHA_1_Tarawa, # Standing as Juan Carlos
], "destroyer": [
Oliver_Hazzard_Perry_class,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"Principe de Asturias",
], "lhanames": [
"Juan Carlos I",
], "boat":[
"OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,31 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
Sweden_1990 = {
"country": "Sweden",
"side": "blue",
"units": [
AJS37,
UH_1H,
AirDefence.SAM_Hawk_PCP,
Armor.IFV_MCV_80, # Standing as Strf 90
Armor.MBT_Leopard_2,
Armor.APC_M1126_Stryker_ICV, # Closest thing available
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
],
"shorad": [
AirDefence.SAM_Avenger_M1097
], "has_jtac": True
}

267
game/factions/syria.py Normal file
View File

@@ -0,0 +1,267 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.vehicles import *
Syria_2011 = {
"country": "Syria",
"side": "red",
"units": [
MiG_21Bis,
MiG_23MLD,
MiG_25PD,
MiG_29S,
Su_17M4,
Su_24M,
L_39ZA,
Mi_24V,
Mi_8MT,
SA342M,
SA342L,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_3_S_125_LN_5P73,
AirDefence.SAM_SA_2_LN_SM_90,
AirDefence.SAM_SA_8_Osa_9A33,
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
Armor.IFV_BMP_1,
Armor.IFV_BMP_2,
Armor.APC_BTR_80,
Armor.ARV_BRDM_2,
Armor.APC_MTLB,
Armor.APC_Cobra,
Armor.MBT_T_55,
Armor.MBT_T_72B,
Armor.MBT_T_90,
Artillery.MLRS_BM_21_Grad,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.SPH_2S1_Gvozdika,
Artillery.SPH_2S9_Nona,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Paratrooper_RPG_16,
Infantry.Soldier_AK
],
"shorad": [
AirDefence.SAM_SA_8_Osa_9A33,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
AirDefence.SAM_SA_9_Strela_1_9P31,
AirDefence.SAM_SA_19_Tunguska_2S6,
AirDefence.AAA_ZU_23_on_Ural_375,
], "boat": [
"GrishaGroupGenerator", "MolniyaGroupGenerator"
]
}
Syria_1973 = {
"country": "Syria",
"side": "red",
"units": [
MiG_21Bis,
MiG_19P,
MiG_15bis, # Standing as Mig-17
Su_17M4, # Standing as Su-7
Mi_8MT,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_3_S_125_LN_5P73,
AirDefence.SAM_SA_2_LN_SM_90,
Armor.IFV_BMP_1,
Armor.APC_MTLB,
Armor.MBT_T_55,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Paratrooper_RPG_16,
Infantry.Soldier_AK
],
"shorad": [
AirDefence.AAA_ZU_23_on_Ural_375,
], "boat": [
"GrishaGroupGenerator"
]
}
Syria_1982 = {
"country": "Syria",
"side": "red",
"units": [
MiG_21Bis,
MiG_23MLD,
MiG_25PD,
MiG_19P,
Su_17M4, # Standing as Su-7
Mi_8MT,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_3_S_125_LN_5P73,
AirDefence.SAM_SA_2_LN_SM_90,
Armor.IFV_BMP_1,
Armor.APC_MTLB,
Armor.MBT_T_55,
Armor.MBT_T_72B,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Paratrooper_RPG_16,
Infantry.Soldier_AK
],
"shorad": [
AirDefence.AAA_ZU_23_on_Ural_375,
], "boat": [
"GrishaGroupGenerator"
]
}
Syria_1967 = {
"country": "Syria",
"side": "red",
"units": [
MiG_21Bis,
MiG_19P,
MiG_15bis, # Standing as Mig-17
Su_17M4, # Standing as Su-7
Mi_8MT,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
AirDefence.SAM_SA_2_LN_SM_90,
Armor.ARV_BRDM_2,
Armor.MBT_T_55,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Paratrooper_RPG_16,
Infantry.Soldier_AK
],
"shorad": [
AirDefence.AAA_ZU_23_on_Ural_375,
], "boat": [
"GrishaGroupGenerator"
]
}
Syria_1967_WW2_Weapons = {
"country": "Syria",
"side": "red",
"units": [
MiG_21Bis,
MiG_19P,
MiG_15bis, # Standing as Mig-17
Su_17M4, # Standing as Su-7
Mi_8MT,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
AirDefence.SAM_SA_2_LN_SM_90,
Armor.ARV_BRDM_2,
Armor.MBT_T_55,
Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.StuG_III_Ausf__G,
Armor.TD_Jagdpanzer_IV,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Soldier_RPG,
Infantry.Soldier_AK
], "requirements": {
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"shorad": [
AirDefence.AAA_ZU_23_on_Ural_375,
], "boat": [
"GrishaGroupGenerator"
]
}
Arab_Armies_1948 = {
"country": "Syria",
"side": "red",
"units": [
SpitfireLFMkIX,
SpitfireLFMkIXCW,
AirDefence.SAM_SA_2_LN_SM_90,
Armor.MT_M4_Sherman,
Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.APC_Sd_Kfz_251,
Armor.IFV_Sd_Kfz_234_2_Puma,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Infantry_SMLE_No_4_Mk_1,
AirDefence.AAA_8_8cm_Flak_36,
], "requirements": {
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"shorad": [
AirDefence.AAA_8_8cm_Flak_36,
], "boat": [
"GrishaGroupGenerator"
]
}

View File

@@ -0,0 +1,41 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Turkey_2005 = {
"country": "Turkey",
"side": "blue",
"units":[
F_16C_50,
F_4E,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
AH_1W,
Armor.MBT_Leopard_2,
Armor.MBT_Leopard_1A3,
Armor.MBT_M60A3_Patton,
Armor.APC_Cobra,
Armor.APC_BTR_80,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.SAM_Avenger_M1097,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad":[
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZSU_23_4_Shilka
], "boat":[
"OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

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

@@ -0,0 +1,38 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
UAE_2005 = {
"country": "United Arab Emirates",
"side": "blue",
"units":[
M_2000C,
Mirage_2000_5,
F_16C_50,
KC_135,
KC130,
C_130,
E_3A,
AH_64D,
Armor.MBT_Leclerc,
Armor.IFV_BMP_3,
Armor.TPz_Fuchs,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.Rapier_FSA_Launcher,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "boat":[
"OliverHazardPerryGroupGenerator"
],
"has_jtac": True,
"jtac_unit": WingLoong_I
}

44
game/factions/uk_1944.py Normal file
View File

@@ -0,0 +1,44 @@
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from game.data.building_data import WW2_ALLIES_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
UK_1944 = {
"country": "UK",
"side": "blue",
"units": [
P_51D,
P_51D_30_NA,
P_47D_30,
SpitfireLFMkIX,
SpitfireLFMkIXCW,
A_20G,
B_17G,
Armor.MT_M4A4_Sherman_Firefly,
Armor.MT_M4_Sherman,
Armor.APC_M2A1,
Armor.CT_Cromwell_IV,
Armor.ST_Centaur_IV,
Armor.HIT_Churchill_VII,
Infantry.Infantry_SMLE_No_4_Mk_1,
LS_Samuel_Chase,
LST_Mk_II,
LCVP__Higgins_boat,
Unarmed.CCKW_353,
AirDefence.AAA_Bofors_40mm,
], "shorad":[
AirDefence.AAA_Bofors_40mm,
],"requirements":{
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"objects": WW2_ALLIES_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["WW2LSTGroupGenerator"],
"boat_count": 1
}

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

@@ -0,0 +1,52 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
UnitedKingdom_1990 = {
"country": "UK",
"side": "blue",
"units":[
AV8BNA, # Standing as BAE Harrier 2
Tornado_GR4,
F_4E,
KC_135,
KC130,
C_130,
E_3A,
SA342M,
AH_64A,
Armor.MBT_Challenger_II,
Armor.IFV_MCV_80,
Armor.APC_M1043_HMMWV_Armament,
Armor.ATGM_M1045_HMMWV_TOW,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.Rapier_FSA_Launcher,
AirDefence.SAM_Avenger_M1097, # Standing as Starstreak
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad":[
AirDefence.SAM_Avenger_M1097,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
], "cruiser": [
Ticonderoga_class,
], "lhanames": [
"HMS Invincible",
"HMS Illustrious",
"HMS Ark Royal",
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

View File

@@ -0,0 +1,53 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
Ukraine_2010 = {
"country": "Ukraine",
"side": "blue",
"units": [
Su_25,
Su_25T,
Su_24M,
Su_27,
MiG_29S,
L_39ZA,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Mi_8MT,
Mi_24V,
AirDefence.SAM_SA_3_S_125_LN_5P73,
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
Armor.APC_M1043_HMMWV_Armament,
Armor.IFV_BMP_3,
Armor.IFV_BMP_2,
Armor.APC_BTR_80,
Armor.MBT_T_80U,
Armor.MBT_T_72B,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160,
],
"shorad":[
AirDefence.SAM_SA_19_Tunguska_2S6,
AirDefence.SAM_SA_13_Strela_10M3_9A35M3,
AirDefence.AAA_ZU_23_on_Ural_375
], "boat":[
"GrishaGroupGenerator"
]
}

View File

@@ -0,0 +1,72 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
US_Aggressors = {
"country": "USAF Aggressors",
"side": "red",
"units": [
F_15C,
F_5E_3,
FA_18C_hornet,
F_16C_50,
Su_27,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
AH_64D,
Ka_50,
SA342M,
SA342L,
Armor.MBT_M1A2_Abrams,
Armor.MBT_Leopard_2,
Armor.ATGM_M1134_Stryker,
Armor.IFV_M2A2_Bradley,
Armor.APC_M1043_HMMWV_Armament,
Artillery.MLRS_M270,
Artillery.SPH_M109_Paladin,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Patriot_EPP_III,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.SAM_Avenger_M1097,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
]
}

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

@@ -0,0 +1,86 @@
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
from game.data.building_data import WW2_ALLIES_BUILDINGS
from game.data.doctrine import WWII_DOCTRINE
USA_1944 = {
"country": "USA",
"side": "blue",
"units": [
P_51D,
P_51D_30_NA,
P_47D_30,
A_20G,
B_17G,
Armor.MT_M4_Sherman,
Armor.M30_Cargo_Carrier,
Armor.APC_M2A1,
Armor.LAC_M8_Greyhound,
Armor.TD_M10_GMC,
Artillery.M12_GMC,
Infantry.Infantry_M1_Garand,
LS_Samuel_Chase,
LST_Mk_II,
LCVP__Higgins_boat,
Unarmed.CCKW_353,
AirDefence.AAA_Bofors_40mm,
], "shorad":[
AirDefence.AAA_Bofors_40mm,
],"requirements":{
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"objects": WW2_ALLIES_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["WW2LSTGroupGenerator"],
"boat_count": 2
}
ALLIES_1944 = {
"country": "USA",
"side": "blue",
"units": [
P_51D,
P_51D_30_NA,
P_47D_30,
SpitfireLFMkIX,
SpitfireLFMkIXCW,
A_20G,
B_17G,
Armor.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly,
Armor.CT_Cromwell_IV,
Armor.M30_Cargo_Carrier,
Armor.APC_M2A1,
Armor.CT_Cromwell_IV,
Armor.ST_Centaur_IV,
Armor.HIT_Churchill_VII,
Armor.LAC_M8_Greyhound,
Armor.TD_M10_GMC,
Artillery.M12_GMC,
Infantry.Infantry_M1_Garand,
Infantry.Infantry_SMLE_No_4_Mk_1,
LS_Samuel_Chase,
LST_Mk_II,
LCVP__Higgins_boat,
Unarmed.CCKW_353,
AirDefence.AAA_Bofors_40mm,
], "shorad":[
AirDefence.AAA_Bofors_40mm,
],"requirements":{
"WW2 Asset Pack": "https://www.digitalcombatsimulator.com/en/products/other/wwii_assets_pack/",
},
"objects": WW2_ALLIES_BUILDINGS,
"doctrine": WWII_DOCTRINE,
"boat": ["WW2LSTGroupGenerator"],
"boat_count": 2
}

33
game/factions/usa_1955.py Normal file
View File

@@ -0,0 +1,33 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
USA_1955 = {
"country": "USA",
"side": "blue",
"units": [
F_86F_Sabre,
P_51D,
KC_135,
KC130,
C_130,
E_3A,
Armor.MT_M4A4_Sherman_Firefly,
Armor.MT_M4_Sherman,
Armor.MBT_M60A3_Patton,
Armor.APC_M2A1,
Armor.M30_Cargo_Carrier,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.AAA_Bofors_40mm,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
]
}

36
game/factions/usa_1960.py Normal file
View File

@@ -0,0 +1,36 @@
from dcs.vehicles import *
from dcs.ships import *
from dcs.planes import *
from dcs.helicopters import *
USA_1960 = {
"country": "USA",
"side": "blue",
"units": [
F_86F_Sabre,
P_51D,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.AAA_Vulcan_M163,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad":[
AirDefence.AAA_Vulcan_M163
]
}

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

@@ -0,0 +1,41 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_1965 = {
"country": "USA",
"side": "blue",
"units": [
F_5E_3,
F_4E,
KC_135,
KC130,
C_130,
E_3A,
B_52H,
UH_1H,
Armor.MBT_M60A3_Patton,
Armor.APC_M113,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Chaparral_M48,
AirDefence.SAM_Hawk_PCP,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
],
"shorad":[
AirDefence.AAA_Vulcan_M163,
AirDefence.SAM_Chaparral_M48
], "boat":[
]
}

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

@@ -0,0 +1,65 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_1990 = {
"country": "USA",
"side": "blue",
"units": [
F_15C,
F_15E,
F_14B,
FA_18C_hornet,
F_16C_50,
A_10A,
AV8BNA,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
AH_64A,
Armor.MBT_M1A2_Abrams,
Armor.IFV_LAV_25,
Armor.APC_M1043_HMMWV_Armament,
Armor.ATGM_M1045_HMMWV_TOW,
Armor.ATGM_M1134_Stryker,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad":[
AirDefence.SAM_Avenger_M1097,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
Oliver_Hazzard_Perry_class,
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"CVN-72 Abraham Lincoln",
"CVN-73 Georges Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator", "OliverHazardPerryGroupGenerator"
], "has_jtac": True
}

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

@@ -0,0 +1,73 @@
from dcs.helicopters import *
from dcs.planes import *
from dcs.ships import *
from dcs.vehicles import *
USA_2005 = {
"country": "USA",
"side": "blue",
"units": [
F_15C,
F_15E,
F_14B,
FA_18C_hornet,
F_16C_50,
A_10C,
A_10C_2,
AV8BNA,
MQ_9_Reaper,
KC_135,
KC130,
C_130,
E_3A,
UH_1H,
AH_64D,
Armor.MBT_M1A2_Abrams,
Armor.ATGM_M1134_Stryker,
Armor.APC_M1126_Stryker_ICV,
Armor.IFV_M2A2_Bradley,
Armor.IFV_LAV_25,
Armor.APC_M1043_HMMWV_Armament,
Armor.ATGM_M1045_HMMWV_TOW,
Artillery.MLRS_M270,
Artillery.SPH_M109_Paladin,
Unarmed.Transport_M818,
Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Patriot_EPP_III,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
Armed_speedboat,
], "shorad": [
AirDefence.SAM_Avenger_M1097,
], "aircraft_carrier": [
CVN_74_John_C__Stennis,
], "helicopter_carrier": [
LHA_1_Tarawa,
], "destroyer": [
USS_Arleigh_Burke_IIa,
], "cruiser": [
Ticonderoga_class,
], "carrier_names": [
"CVN-71 Theodore Roosevelt",
"CVN-72 Abraham Lincoln",
"CVN-73 George Washington",
"CVN-74 John C. Stennis",
], "lhanames": [
"LHA-1 Tarawa",
"LHA-2 Saipan",
"LHA-3 Belleau Wood",
"LHA-4 Nassau",
"LHA-5 Peleliu"
], "boat":[
"ArleighBurkeGroupGenerator"
], "has_jtac": True
}

View File

@@ -1,36 +1,11 @@
import logging from datetime import datetime, timedelta
import math
import random
import sys
from datetime import date, datetime, timedelta
from typing import Dict, List
from dcs.action import Coalition from game.db import REWARDS, PLAYER_BUDGET_BASE, sys
from dcs.mapping import Point
from dcs.task import CAP, CAS, PinpointStrike, Task
from dcs.unittype import UnitType
from dcs.vehicles import AirDefence
from game import db
from game.db import PLAYER_BUDGET_BASE, REWARDS
from game.inventory import GlobalAircraftInventory
from game.models.game_stats import GameStats from game.models.game_stats import GameStats
from game.plugins import LuaPluginManager from gen.flights.ai_flight_planner import FlightPlanner
from gen.ato import AirTaskingOrder
from gen.conflictgen import Conflict
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
from gen.flights.closestairfields import ObjectiveDistanceCache
from gen.ground_forces.ai_ground_planner import GroundPlanner from gen.ground_forces.ai_ground_planner import GroundPlanner
from theater import ConflictTheater, ControlPoint from .event import *
from theater.conflicttheater import IMPORTANCE_HIGH, IMPORTANCE_LOW
from . import persistency
from .debriefing import Debriefing
from .event.event import Event, UnitsDeliveryEvent
from .event.frontlineattack import FrontlineAttackEvent
from .factions.faction import Faction
from .infos.information import Information
from .settings import Settings from .settings import Settings
from .weather import Conditions, TimeOfDay
COMMISION_UNIT_VARIETY = 4 COMMISION_UNIT_VARIETY = 4
COMMISION_LIMITS_SCALE = 1.5 COMMISION_LIMITS_SCALE = 1.5
@@ -70,45 +45,41 @@ PLAYER_BUDGET_IMPORTANCE_LOG = 2
class Game: class Game:
def __init__(self, player_name: str, enemy_name: str, settings = None # type: Settings
theater: ConflictTheater, start_date: datetime, budget = PLAYER_BUDGET_INITIAL
settings: Settings): events = None # type: typing.List[Event]
pending_transfers = None # type: typing.Dict[]
ignored_cps = None # type: typing.Collection[ControlPoint]
turn = 0
game_stats: GameStats = None
current_unit_id = 0
current_group_id = 0
def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater, start_date: datetime, settings):
self.settings = settings self.settings = settings
self.events: List[Event] = [] self.events = []
self.theater = theater self.theater = theater
self.player_name = player_name self.player_name = player_name
self.player_country = db.FACTIONS[player_name].country self.player_country = db.FACTIONS[player_name]["country"]
self.enemy_name = enemy_name self.enemy_name = enemy_name
self.enemy_country = db.FACTIONS[enemy_name].country self.enemy_country = db.FACTIONS[enemy_name]["country"]
self.turn = 0 self.turn = 0
self.date = date(start_date.year, start_date.month, start_date.day) self.date = datetime(start_date.year, start_date.month, start_date.day)
self.game_stats = GameStats() self.game_stats = GameStats()
self.game_stats.update(self) self.game_stats.update(self)
self.ground_planners: Dict[int, GroundPlanner] = {} self.planners = {}
self.ground_planners = {}
self.informations = [] self.informations = []
self.informations.append(Information("Game Start", "-" * 40, 0)) self.informations.append(Information("Game Start", "-" * 40, 0))
self.__culling_points = self.compute_conflicts_position() self.__culling_points = self.compute_conflicts_position()
self.__destroyed_units: List[str] = [] self.__frontlineData = []
self.__destroyed_units = []
self.jtacs = []
self.savepath = "" self.savepath = ""
self.budget = PLAYER_BUDGET_INITIAL
self.current_unit_id = 0
self.current_group_id = 0
self.conditions = self.generate_conditions()
self.blue_ato = AirTaskingOrder()
self.red_ato = AirTaskingOrder()
self.aircraft_inventory = GlobalAircraftInventory(
self.theater.controlpoints
)
self.sanitize_sides() self.sanitize_sides()
self.on_load()
def generate_conditions(self) -> Conditions:
return Conditions.generate(self.theater, self.date,
self.current_turn_time_of_day, self.settings)
def sanitize_sides(self): def sanitize_sides(self):
""" """
@@ -124,11 +95,11 @@ class Game:
self.enemy_country = "Russia" self.enemy_country = "Russia"
@property @property
def player_faction(self) -> Faction: def player_faction(self):
return db.FACTIONS[self.player_name] return db.FACTIONS[self.player_name]
@property @property
def enemy_faction(self) -> Faction: def enemy_faction(self):
return db.FACTIONS[self.enemy_name] return db.FACTIONS[self.enemy_name]
def _roll(self, prob, mult): def _roll(self, prob, mult):
@@ -142,10 +113,32 @@ class Game:
self.events.append(event_class(self, player_cp, enemy_cp, enemy_cp.position, self.player_name, self.enemy_name)) self.events.append(event_class(self, player_cp, enemy_cp, enemy_cp.position, self.player_name, self.enemy_name))
def _generate_events(self): def _generate_events(self):
for front_line in self.theater.conflicts(True): for player_cp, enemy_cp in self.theater.conflicts(True):
self._generate_player_event(FrontlineAttackEvent, self._generate_player_event(FrontlineAttackEvent, player_cp, enemy_cp)
front_line.control_point_a,
front_line.control_point_b) def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW)
if for_task == AirDefence and not self.settings.sams:
return [x for x in db.find_unittype(AirDefence, self.enemy_name) if x not in db.SAM_BAN]
else:
return db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy_name)
def _commision_units(self, cp: ControlPoint):
for for_task in [CAS, CAP, AirDefence]:
limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance,
COMMISION_LIMITS_SCALE) * self.settings.multiplier
missing_units = limit - cp.base.total_units(for_task)
if missing_units > 0:
awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance,
COMMISION_AMOUNTS_SCALE) * self.settings.multiplier
points_to_spend = cp.base.append_commision_points(for_task, awarded_points)
if points_to_spend > 0:
unittypes = self.commision_unit_types(cp, for_task)
if len(unittypes) > 0:
d = {random.choice(unittypes): points_to_spend}
logging.info("Commision {}: {}".format(cp, d))
cp.base.commision_units(d)
@property @property
def budget_reward_amount(self): def budget_reward_amount(self):
@@ -201,20 +194,11 @@ class Game:
else: else:
return event and event.name and event.name == self.player_name return event and event.name and event.name == self.player_name
def on_load(self) -> None: def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
LuaPluginManager.load_settings(self.settings)
ObjectiveDistanceCache.set_theater(self.theater)
# Save game compatibility.
# TODO: Remove in 2.3.
if not hasattr(self, "conditions"):
self.conditions = self.generate_conditions()
def pass_turn(self, no_action: bool = False) -> None:
logging.info("Pass turn") logging.info("Pass turn")
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0)) self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
self.turn += 1 self.turn = self.turn + 1
for event in self.events: for event in self.events:
if self.settings.version == "dev": if self.settings.version == "dev":
@@ -235,37 +219,34 @@ class Game:
if not cp.is_carrier and not cp.is_lha: if not cp.is_carrier and not cp.is_lha:
cp.base.affect_strength(-PLAYER_BASE_STRENGTH_RECOVERY) cp.base.affect_strength(-PLAYER_BASE_STRENGTH_RECOVERY)
self.conditions = self.generate_conditions() self.ignored_cps = []
if ignored_cps:
self.ignored_cps = ignored_cps
self.initialize_turn() self.events = [] # type: typing.List[Event]
# Autosave progress
persistency.autosave(self)
def initialize_turn(self) -> None:
self.events = []
self._generate_events() self._generate_events()
# Update statistics # Update statistics
self.game_stats.update(self) self.game_stats.update(self)
self.aircraft_inventory.reset()
for cp in self.theater.controlpoints:
self.aircraft_inventory.set_from_control_point(cp)
# Plan flights & combat for next turn # Plan flights & combat for next turn
self.__culling_points = self.compute_conflicts_position() self.__culling_points = self.compute_conflicts_position()
self.planners = {}
self.ground_planners = {} self.ground_planners = {}
self.blue_ato.clear()
self.red_ato.clear()
CoalitionMissionPlanner(self, is_player=True).plan_missions()
CoalitionMissionPlanner(self, is_player=False).plan_missions()
for cp in self.theater.controlpoints: for cp in self.theater.controlpoints:
if cp.has_runway():
planner = FlightPlanner(cp, self)
planner.plan_flights()
self.planners[cp.id] = planner
if cp.has_frontline: if cp.has_frontline:
gplanner = GroundPlanner(cp, self) gplanner = GroundPlanner(cp, self)
gplanner.plan_groundwar() gplanner.plan_groundwar()
self.ground_planners[cp.id] = gplanner self.ground_planners[cp.id] = gplanner
# Autosave progress
persistency.autosave(self)
def _enemy_reinforcement(self): def _enemy_reinforcement(self):
""" """
Compute and commision reinforcement for enemy bases Compute and commision reinforcement for enemy bases
@@ -293,7 +274,7 @@ class Game:
potential_cp_armor = self.theater.enemy_points() potential_cp_armor = self.theater.enemy_points()
i = 0 i = 0
potential_units = db.FACTIONS[self.enemy_name].frontline_units potential_units = [u for u in db.FACTIONS[self.enemy_name]["units"] if u in db.UNIT_BY_TASK[PinpointStrike]]
print("Enemy Recruiting") print("Enemy Recruiting")
print(potential_cp_armor) print(potential_cp_armor)
@@ -319,9 +300,8 @@ class Game:
if budget_for_armored_units > 0: if budget_for_armored_units > 0:
budget_for_aircraft += budget_for_armored_units budget_for_aircraft += budget_for_armored_units
potential_units = [u for u in db.FACTIONS[self.enemy_name].aircrafts potential_units = [u for u in db.FACTIONS[self.enemy_name]["units"] if
if u in db.UNIT_BY_TASK[CAS] or u in db.UNIT_BY_TASK[CAP]] u in db.UNIT_BY_TASK[CAS] or u in db.UNIT_BY_TASK[CAP]]
if len(potential_units) > 0 and len(potential_cp_armor) > 0: if len(potential_units) > 0 and len(potential_cp_armor) > 0:
while budget_for_aircraft > 0: while budget_for_aircraft > 0:
i = i + 1 i = i + 1
@@ -339,11 +319,11 @@ class Game:
self.informations.append(info) self.informations.append(info)
@property @property
def current_turn_time_of_day(self) -> TimeOfDay: def current_turn_daytime(self):
return list(TimeOfDay)[self.turn % 4] return ["dawn", "day", "dusk", "night"][self.turn % 4]
@property @property
def current_day(self) -> date: def current_day(self):
return self.date + timedelta(days=self.turn // 4) return self.date + timedelta(days=self.turn // 4)
def next_unit_id(self): def next_unit_id(self):
@@ -368,13 +348,10 @@ class Game:
points = [] points = []
# By default, use the existing frontline conflict position # By default, use the existing frontline conflict position
for front_line in self.theater.conflicts(): for conflict in self.theater.conflicts():
position = Conflict.frontline_position(self.theater, points.append(Conflict.frontline_position(self.theater, conflict[0], conflict[1])[0])
front_line.control_point_a, points.append(conflict[0].position)
front_line.control_point_b) points.append(conflict[1].position)
points.append(position[0])
points.append(front_line.control_point_a.position)
points.append(front_line.control_point_b.position)
# If there is no conflict take the center point between the two nearest opposing bases # If there is no conflict take the center point between the two nearest opposing bases
if len(points) == 0: if len(points) == 0:
@@ -423,13 +400,6 @@ class Game:
return False return False
return True return True
def get_culling_points(self):
"""
Check culling points
:return: List of culling points
"""
return self.__culling_points
# 1 = red, 2 = blue # 1 = red, 2 = blue
def get_player_coalition_id(self): def get_player_coalition_id(self):
return 2 return 2
@@ -438,10 +408,10 @@ class Game:
return 1 return 1
def get_player_coalition(self): def get_player_coalition(self):
return Coalition.Blue return dcs.action.Coalition.Blue
def get_enemy_coalition(self): def get_enemy_coalition(self):
return Coalition.Red return dcs.action.Coalition.Red
def get_player_color(self): def get_player_color(self):
return "blue" return "blue"

View File

@@ -1,123 +0,0 @@
"""Inventory management APIs."""
from collections import defaultdict
from typing import Dict, Iterable, Iterator, Set, Tuple
from dcs.unittype import UnitType
from gen.flights.flight import Flight
from theater import ControlPoint
class ControlPointAircraftInventory:
"""Aircraft inventory for a single control point."""
def __init__(self, control_point: ControlPoint) -> None:
self.control_point = control_point
self.inventory: Dict[UnitType, int] = defaultdict(int)
def add_aircraft(self, aircraft: UnitType, count: int) -> None:
"""Adds aircraft to the inventory.
Args:
aircraft: The type of aircraft to add.
count: The number of aircraft to add.
"""
self.inventory[aircraft] += count
def remove_aircraft(self, aircraft: UnitType, count: int) -> None:
"""Removes aircraft from the inventory.
Args:
aircraft: The type of aircraft to remove.
count: The number of aircraft to remove.
Raises:
ValueError: The control point cannot fulfill the requested number of
aircraft.
"""
available = self.inventory[aircraft]
if available < count:
raise ValueError(
f"Cannot remove {count} {aircraft.id} from "
f"{self.control_point.name}. Only have {available}."
)
self.inventory[aircraft] -= count
def available(self, aircraft: UnitType) -> int:
"""Returns the number of available aircraft of the given type.
Args:
aircraft: The type of aircraft to query.
"""
try:
return self.inventory[aircraft]
except KeyError:
return 0
@property
def types_available(self) -> Iterator[UnitType]:
"""Iterates over all available aircraft types."""
for aircraft, count in self.inventory.items():
if count > 0:
yield aircraft
@property
def all_aircraft(self) -> Iterator[Tuple[UnitType, int]]:
"""Iterates over all available aircraft types, including amounts."""
for aircraft, count in self.inventory.items():
if count > 0:
yield aircraft, count
def clear(self) -> None:
"""Clears all aircraft from the inventory."""
self.inventory.clear()
class GlobalAircraftInventory:
"""Game-wide aircraft inventory."""
def __init__(self, control_points: Iterable[ControlPoint]) -> None:
self.inventories: Dict[ControlPoint, ControlPointAircraftInventory] = {
cp: ControlPointAircraftInventory(cp) for cp in control_points
}
def reset(self) -> None:
"""Clears all control points and their inventories."""
for inventory in self.inventories.values():
inventory.clear()
def set_from_control_point(self, control_point: ControlPoint) -> None:
"""Set the control point's aircraft inventory.
If the inventory for the given control point has already been set for
the turn, it will be overwritten.
"""
inventory = self.inventories[control_point]
for aircraft, count in control_point.base.aircraft.items():
inventory.add_aircraft(aircraft, count)
def for_control_point(
self,
control_point: ControlPoint) -> ControlPointAircraftInventory:
"""Returns the inventory specific to the given control point."""
return self.inventories[control_point]
@property
def available_types_for_player(self) -> Iterator[UnitType]:
"""Iterates over all aircraft types available to the player."""
seen: Set[UnitType] = set()
for control_point, inventory in self.inventories.items():
if control_point.captured:
for aircraft in inventory.types_available:
if aircraft not in seen:
seen.add(aircraft)
yield aircraft
def claim_for_flight(self, flight: Flight) -> None:
"""Removes aircraft from the inventory for the given flight."""
inventory = self.for_control_point(flight.from_cp)
inventory.remove_aircraft(flight.unit_type, flight.count)
def return_from_flight(self, flight: Flight) -> None:
"""Returns a flight's aircraft to the inventory."""
inventory = self.for_control_point(flight.from_cp)
inventory.add_aircraft(flight.unit_type, flight.count)

View File

@@ -1,5 +1,3 @@
from typing import List
class FactionTurnMetadata: class FactionTurnMetadata:
""" """
Store metadata about a faction Store metadata about a faction
@@ -33,8 +31,10 @@ class GameStats:
Store statistics for the current game Store statistics for the current game
""" """
data_per_turn: [GameTurnMetadata] = []
def __init__(self): def __init__(self):
self.data_per_turn: List[GameTurnMetadata] = [] self.data_per_turn = []
def update(self, game): def update(self, game):
""" """

View File

@@ -1,8 +1,7 @@
from dcs.terrain.terrain import Terrain from game.db import assigned_units_split
from .operation import *
from gen.conflictgen import Conflict
from .operation import Operation
from .. import db
MAX_DISTANCE_BETWEEN_GROUPS = 12000 MAX_DISTANCE_BETWEEN_GROUPS = 12000
@@ -35,4 +34,6 @@ class FrontlineAttackOperation(Operation):
conflict=conflict) conflict=conflict)
def generate(self): def generate(self):
self.briefinggen.title = "Frontline CAS"
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
super(FrontlineAttackOperation, self).generate() super(FrontlineAttackOperation, self).generate()

View File

@@ -1,51 +1,30 @@
import logging from typing import Set
import os
from pathlib import Path
from typing import List, Optional, Set
from dcs import Mission from gen import *
from dcs.action import DoScript, DoScriptFile from gen.airfields import AIRFIELD_DATA
from dcs.coalition import Coalition from gen.beacons import load_beacons_for_terrain
from gen.radios import RadioRegistry
from gen.tacan import TacanRegistry
from dcs.countries import country_dict from dcs.countries import country_dict
from dcs.lua.parse import loads from dcs.lua.parse import loads
from dcs.mapping import Point
from dcs.terrain.terrain import Terrain from dcs.terrain.terrain import Terrain
from dcs.translation import String from userdata.debriefing import *
from dcs.triggers import TriggerStart
from dcs.unittype import UnitType
from game.plugins import LuaPluginManager
from gen import Conflict, FlightType, VisualGenerator
from gen.aircraft import AIRCRAFT_DATA, AircraftConflictGenerator, FlightData
from gen.airfields import AIRFIELD_DATA
from gen.airsupportgen import AirSupport, AirSupportConflictGenerator
from gen.armor import GroundConflictGenerator, JtacInfo
from gen.beacons import load_beacons_for_terrain
from gen.briefinggen import BriefingGenerator, MissionInfoGenerator
from gen.environmentgen import EnvironmentGenerator
from gen.forcedoptionsgen import ForcedOptionsGenerator
from gen.groundobjectsgen import GroundObjectsGenerator
from gen.kneeboard import KneeboardGenerator
from gen.radios import RadioFrequency, RadioRegistry
from gen.tacan import TacanRegistry
from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
from theater import ControlPoint
from .. import db
from ..debriefing import Debriefing
class Operation: class Operation:
attackers_starting_position = None # type: db.StartingPosition attackers_starting_position = None # type: db.StartingPosition
defenders_starting_position = None # type: db.StartingPosition defenders_starting_position = None # type: db.StartingPosition
current_mission = None # type: Mission current_mission = None # type: dcs.Mission
regular_mission = None # type: Mission regular_mission = None # type: dcs.Mission
quick_mission = None # type: Mission quick_mission = None # type: dcs.Mission
conflict = None # type: Conflict conflict = None # type: Conflict
armorgen = None # type: ArmorConflictGenerator
airgen = None # type: AircraftConflictGenerator airgen = None # type: AircraftConflictGenerator
triggersgen = None # type: TriggersGenerator triggersgen = None # type: TriggersGenerator
airsupportgen = None # type: AirSupportConflictGenerator airsupportgen = None # type: AirSupportConflictGenerator
visualgen = None # type: VisualGenerator visualgen = None # type: VisualGenerator
envgen = None # type: EnvironmentGenerator
groundobjectgen = None # type: GroundObjectsGenerator groundobjectgen = None # type: GroundObjectsGenerator
briefinggen = None # type: BriefingGenerator briefinggen = None # type: BriefingGenerator
forcedoptionsgen = None # type: ForcedOptionsGenerator forcedoptionsgen = None # type: ForcedOptionsGenerator
@@ -64,20 +43,19 @@ class Operation:
defender_name: str, defender_name: str,
from_cp: ControlPoint, from_cp: ControlPoint,
departure_cp: ControlPoint, departure_cp: ControlPoint,
to_cp: ControlPoint): to_cp: ControlPoint = None):
self.game = game self.game = game
self.attacker_name = attacker_name self.attacker_name = attacker_name
self.attacker_country = db.FACTIONS[attacker_name].country self.attacker_country = db.FACTIONS[attacker_name]["country"]
self.defender_name = defender_name self.defender_name = defender_name
self.defender_country = db.FACTIONS[defender_name].country self.defender_country = db.FACTIONS[defender_name]["country"]
print(self.defender_country, self.attacker_country) print(self.defender_country, self.attacker_country)
self.from_cp = from_cp self.from_cp = from_cp
self.departure_cp = departure_cp self.departure_cp = departure_cp
self.to_cp = to_cp self.to_cp = to_cp
self.is_quick = False self.is_quick = False
self.plugin_scripts: List[str] = []
def units_of(self, country_name: str) -> List[UnitType]: def units_of(self, country_name: str) -> typing.Collection[UnitType]:
return [] return []
def is_successfull(self, debriefing: Debriefing) -> bool: def is_successfull(self, debriefing: Debriefing) -> bool:
@@ -90,13 +68,14 @@ class Operation:
def initialize(self, mission: Mission, conflict: Conflict): def initialize(self, mission: Mission, conflict: Conflict):
self.current_mission = mission self.current_mission = mission
self.conflict = conflict self.conflict = conflict
# self.briefinggen = BriefingGenerator(self.current_mission, self.game) Is it safe to remove this, or does it also break save compat? self.briefinggen = BriefingGenerator(self.current_mission,
self.conflict, self.game)
def prepare(self, terrain: Terrain, is_quick: bool): def prepare(self, terrain: Terrain, is_quick: bool):
with open("resources/default_options.lua", "r") as f: with open("resources/default_options.lua", "r") as f:
options_dict = loads(f.read())["options"] options_dict = loads(f.read())["options"]
self.current_mission = Mission(terrain) self.current_mission = dcs.Mission(terrain)
print(self.game.player_country) print(self.game.player_country)
print(country_dict[db.country_id_from_name(self.game.player_country)]) print(country_dict[db.country_id_from_name(self.game.player_country)])
@@ -127,74 +106,7 @@ class Operation:
self.defenders_starting_position = None self.defenders_starting_position = None
else: else:
self.attackers_starting_position = self.departure_cp.at self.attackers_starting_position = self.departure_cp.at
# TODO: Is this possible? self.defenders_starting_position = self.to_cp.at
if self.to_cp is not None:
self.defenders_starting_position = self.to_cp.at
else:
self.defenders_starting_position = None
def inject_lua_trigger(self, contents: str, comment: str) -> None:
trigger = TriggerStart(comment=comment)
trigger.add_action(DoScript(String(contents)))
self.current_mission.triggerrules.triggers.append(trigger)
def bypass_plugin_script(self, mnemonic: str) -> None:
self.plugin_scripts.append(mnemonic)
def inject_plugin_script(self, plugin_mnemonic: str, script: str,
script_mnemonic: str) -> None:
if script_mnemonic in self.plugin_scripts:
logging.debug(
f"Skipping already loaded {script} for {plugin_mnemonic}"
)
else:
self.plugin_scripts.append(script_mnemonic)
plugin_path = Path("./resources/plugins", plugin_mnemonic)
script_path = Path(plugin_path, script)
if not script_path.exists():
logging.error(
f"Cannot find {script_path} for plugin {plugin_mnemonic}"
)
return
trigger = TriggerStart(comment=f"Load {script_mnemonic}")
filename = script_path.resolve()
fileref = self.current_mission.map_resource.add_resource_file(filename)
trigger.add_action(DoScriptFile(fileref))
self.current_mission.triggerrules.triggers.append(trigger)
def notify_info_generators(
self,
groundobjectgen: GroundObjectsGenerator,
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo],
airgen: AircraftConflictGenerator,
):
"""Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)
"""
gens: List[MissionInfoGenerator] = [
KneeboardGenerator(self.current_mission, self.game),
BriefingGenerator(self.current_mission, self.game)
]
for gen in gens:
for dynamic_runway in groundobjectgen.runways.values():
gen.add_dynamic_runway(dynamic_runway)
for tanker in airsupportgen.air_support.tankers:
gen.add_tanker(tanker)
if self.is_awacs_enabled:
for awacs in airsupportgen.air_support.awacs:
gen.add_awacs(awacs)
for jtac in jtacs:
gen.add_jtac(jtac)
for flight in airgen.flights:
gen.add_flight(flight)
gen.generate()
def generate(self): def generate(self):
radio_registry = RadioRegistry() radio_registry = RadioRegistry()
@@ -225,9 +137,13 @@ class Operation:
for frequency in unique_map_frequencies: for frequency in unique_map_frequencies:
radio_registry.reserve(frequency) radio_registry.reserve(frequency)
# Set mission time and weather conditions. # Generate meteo
EnvironmentGenerator(self.current_mission, envgen = EnviromentGenerator(self.current_mission, self.conflict,
self.game.conditions).generate() self.game)
if self.environment_settings is None:
self.environment_settings = envgen.generate()
else:
envgen.load(self.environment_settings)
# Generate ground object first # Generate ground object first
@@ -269,23 +185,23 @@ class Operation:
airgen = AircraftConflictGenerator( airgen = AircraftConflictGenerator(
self.current_mission, self.conflict, self.game.settings, self.game, self.current_mission, self.conflict, self.game.settings, self.game,
radio_registry) radio_registry)
for cp in self.game.theater.controlpoints:
airgen.generate_flights( side = cp.captured
self.current_mission.country(self.game.player_country), if side:
self.game.blue_ato, country = self.current_mission.country(self.game.player_country)
groundobjectgen.runways else:
) country = self.current_mission.country(self.game.enemy_country)
airgen.generate_flights( if cp.id in self.game.planners.keys():
self.current_mission.country(self.game.enemy_country), airgen.generate_flights(
self.game.red_ato, cp,
groundobjectgen.runways country,
) self.game.planners[cp.id],
groundobjectgen.runways
)
# Generate ground units on frontline everywhere # Generate ground units on frontline everywhere
jtacs: List[JtacInfo] = [] jtacs: List[JtacInfo] = []
for front_line in self.game.theater.conflicts(True): for player_cp, enemy_cp in self.game.theater.conflicts(True):
player_cp = front_line.control_point_a
enemy_cp = front_line.control_point_b
conflict = Conflict.frontline_cas_conflict(self.attacker_name, self.defender_name, conflict = Conflict.frontline_cas_conflict(self.attacker_name, self.defender_name,
self.current_mission.country(self.attacker_country), self.current_mission.country(self.attacker_country),
self.current_mission.country(self.defender_country), self.current_mission.country(self.defender_country),
@@ -320,167 +236,126 @@ class Operation:
if self.game.settings.perf_smoke_gen: if self.game.settings.perf_smoke_gen:
visualgen.generate() visualgen.generate()
luaData = {} # Inject Plugins Lua Scripts
luaData["AircraftCarriers"] = {} listOfPluginsScripts = []
luaData["Tankers"] = {} plugin_file_path = Path("./resources/scripts/plugins/__plugins.lst")
luaData["AWACs"] = {} if plugin_file_path.exists():
luaData["JTACs"] = {} for line in plugin_file_path.read_text().splitlines():
luaData["TargetPoints"] = {} name = line.strip()
if not name.startswith( '#' ):
trigger = TriggerStart(comment="Load " + name)
listOfPluginsScripts.append(name)
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/plugins/" + name)
trigger.add_action(DoScriptFile(fileref))
self.current_mission.triggerrules.triggers.append(trigger)
else:
logging.info(
f"Not loading plugins, {plugin_file_path} does not exist")
self.assign_channels_to_flights(airgen.flights, # Inject Mist Script if not done already in the plugins
airsupportgen.air_support) if not "mist.lua" in listOfPluginsScripts and not "mist_4_3_74.lua" in listOfPluginsScripts: # don't load the script twice
trigger = TriggerStart(comment="Load Mist Lua framework")
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/mist_4_3_74.lua")
trigger.add_action(DoScriptFile(fileref))
self.current_mission.triggerrules.triggers.append(trigger)
for tanker in airsupportgen.air_support.tankers: # Inject JSON library if not done already in the plugins
luaData["Tankers"][tanker.callsign] = { if not "json.lua" in listOfPluginsScripts : # don't load the script twice
"dcsGroupName": tanker.dcsGroupName, trigger = TriggerStart(comment="Load JSON Lua library")
"callsign": tanker.callsign, fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/json.lua")
"variant": tanker.variant, trigger.add_action(DoScriptFile(fileref))
"radio": tanker.freq.mhz, self.current_mission.triggerrules.triggers.append(trigger)
"tacan": str(tanker.tacan.number) + tanker.tacan.band.name
}
if self.is_awacs_enabled: # Inject Ciribob's JTACAutoLase if not done already in the plugins
for awacs in airsupportgen.air_support.awacs: if not "JTACAutoLase.lua" in listOfPluginsScripts : # don't load the script twice
luaData["AWACs"][awacs.callsign] = { trigger = TriggerStart(comment="Load JTACAutoLase.lua script")
"dcsGroupName": awacs.dcsGroupName, fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/JTACAutoLase.lua")
"callsign": awacs.callsign, trigger.add_action(DoScriptFile(fileref))
"radio": awacs.freq.mhz self.current_mission.triggerrules.triggers.append(trigger)
}
for jtac in jtacs:
luaData["JTACs"][jtac.callsign] = {
"dcsGroupName": jtac.dcsGroupName,
"callsign": jtac.callsign,
"zone": jtac.region,
"dcsUnit": jtac.unit_name,
"laserCode": jtac.code
}
for flight in airgen.flights:
if flight.friendly and flight.flight_type in [FlightType.ANTISHIP, FlightType.DEAD, FlightType.SEAD, FlightType.STRIKE]:
flightType = flight.flight_type.name
flightTarget = flight.package.target
if flightTarget:
flightTargetName = None
flightTargetType = None
if hasattr(flightTarget, 'obj_name'):
flightTargetName = flightTarget.obj_name
flightTargetType = flightType + f" TGT ({flightTarget.category})"
elif hasattr(flightTarget, 'name'):
flightTargetName = flightTarget.name
flightTargetType = flightType + " TGT (Airbase)"
luaData["TargetPoints"][flightTargetName] = {
"name": flightTargetName,
"type": flightTargetType,
"position": { "x": flightTarget.position.x, "y": flightTarget.position.y}
}
# set a LUA table with data from Liberation that we want to set # set a LUA table with data from Liberation that we want to set
# at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function # at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function
# later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts # later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts
state_location = "[[" + os.path.abspath(".") + "]]" state_location = "[[" + os.path.abspath("state.json") + "]]"
lua = """ lua = """
-- setting configuration table -- setting configuration table
env.info("DCSLiberation|: setting configuration table") env.info("DCSLiberation|: setting configuration table")
-- all data in this table is overridable. -- all data in this table is overridable.
dcsLiberation = {} dcsLiberation = {}
-- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory -- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory
dcsLiberation.installPath=""" + state_location + """ dcsLiberation.installPath=""" + state_location + """
""" -- you can override dcsLiberation.JTACAutoLase to make it use your own function ; it will be called with these parameters : ({jtac.unit_name}, {jtac.code}, {smoke}, 'vehicle') for all JTACs
# Process the tankers if ctld then
lua += """ dcsLiberation.JTACAutoLase=ctld.JTACAutoLase
elseif JTACAutoLase then
-- list the tankers generated by Liberation dcsLiberation.JTACAutoLase=JTACAutoLase
dcsLiberation.Tankers = { end
"""
for key in luaData["Tankers"]: -- later, we'll add more data to the table
data = luaData["Tankers"][key] --dcsLiberation.POIs = {}
dcsGroupName= data["dcsGroupName"] --dcsLiberation.BASEs = {}
callsign = data["callsign"] --dcsLiberation.JTACs = {}
variant = data["variant"] """
tacan = data["tacan"]
radio = data["radio"]
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', variant='{variant}', tacan='{tacan}', radio='{radio}' }}, \n"
#lua += f" {{name='{dcsGroupName}', description='{callsign} ({variant})', information='Tacan:{tacan} Radio:{radio}' }}, \n"
lua += "}"
# Process the AWACSes
lua += """
-- list the AWACs generated by Liberation
dcsLiberation.AWACs = {
"""
for key in luaData["AWACs"]:
data = luaData["AWACs"][key]
dcsGroupName= data["dcsGroupName"]
callsign = data["callsign"]
radio = data["radio"]
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', radio='{radio}' }}, \n"
#lua += f" {{name='{dcsGroupName}', description='{callsign} (AWACS)', information='Radio:{radio}' }}, \n"
lua += "}"
# Process the JTACs
lua += """
-- list the JTACs generated by Liberation
dcsLiberation.JTACs = {
"""
for key in luaData["JTACs"]:
data = luaData["JTACs"][key]
dcsGroupName= data["dcsGroupName"]
callsign = data["callsign"]
zone = data["zone"]
laserCode = data["laserCode"]
dcsUnit = data["dcsUnit"]
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', zone='{zone}', laserCode='{laserCode}', dcsUnit='{dcsUnit}' }}, \n"
#lua += f" {{name='{dcsGroupName}', description='JTAC {callsign} ', information='Laser:{laserCode}', jtac={laserCode} }}, \n"
lua += "}"
# Process the Target Points
lua += """
-- list the target points generated by Liberation
dcsLiberation.TargetPoints = {
"""
for key in luaData["TargetPoints"]:
data = luaData["TargetPoints"][key]
name = data["name"]
pointType = data["type"]
positionX = data["position"]["x"]
positionY = data["position"]["y"]
lua += f" {{name='{name}', pointType='{pointType}', positionX='{positionX}', positionY='{positionY}' }}, \n"
#lua += f" {{name='{pointType} {name}', point{{x={positionX}, z={positionY} }} }}, \n"
lua += "}"
lua += """
-- list the airbases generated by Liberation
-- dcsLiberation.Airbases = {}
-- list the aircraft carriers generated by Liberation
-- dcsLiberation.Carriers = {}
-- later, we'll add more data to the table
"""
trigger = TriggerStart(comment="Set DCS Liberation data") trigger = TriggerStart(comment="Set DCS Liberation data")
trigger.add_action(DoScript(String(lua))) trigger.add_action(DoScript(String(lua)))
self.current_mission.triggerrules.triggers.append(trigger) self.current_mission.triggerrules.triggers.append(trigger)
# Inject Plugins Lua Scripts and data # Inject DCS-Liberation script if not done already in the plugins
for plugin in LuaPluginManager.plugins(): if not "dcs_liberation.lua" in listOfPluginsScripts : # don't load the script twice
if plugin.enabled: trigger = TriggerStart(comment="Load DCS Liberation script")
plugin.inject_scripts(self) fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/dcs_liberation.lua")
plugin.inject_configuration(self) trigger.add_action(DoScriptFile(fileref))
self.current_mission.triggerrules.triggers.append(trigger)
# add a configuration for JTACAutoLase and start lasing for all JTACs
smoke = "true"
if hasattr(self.game.settings, "jtac_smoke_on"):
if not self.game.settings.jtac_smoke_on:
smoke = "false"
lua = """
-- setting and starting JTACs
env.info("DCSLiberation|: setting and starting JTACs")
"""
for jtac in jtacs:
lua += f"if dcsLiberation.JTACAutoLase then dcsLiberation.JTACAutoLase('{jtac.unit_name}', {jtac.code}, {smoke}, 'vehicle') end\n"
trigger = TriggerStart(comment="Start JTACs")
trigger.add_action(DoScript(String(lua)))
self.current_mission.triggerrules.triggers.append(trigger)
self.assign_channels_to_flights(airgen.flights, self.assign_channels_to_flights(airgen.flights,
airsupportgen.air_support) airsupportgen.air_support)
self.notify_info_generators(groundobjectgen, airsupportgen, jtacs, airgen)
kneeboard_generator = KneeboardGenerator(self.current_mission)
for dynamic_runway in groundobjectgen.runways.values():
self.briefinggen.add_dynamic_runway(dynamic_runway)
for tanker in airsupportgen.air_support.tankers:
self.briefinggen.add_tanker(tanker)
kneeboard_generator.add_tanker(tanker)
if self.is_awacs_enabled:
for awacs in airsupportgen.air_support.awacs:
self.briefinggen.add_awacs(awacs)
kneeboard_generator.add_awacs(awacs)
for jtac in jtacs:
self.briefinggen.add_jtac(jtac)
kneeboard_generator.add_jtac(jtac)
for flight in airgen.flights:
self.briefinggen.add_flight(flight)
kneeboard_generator.add_flight(flight)
self.briefinggen.generate()
kneeboard_generator.generate()
def assign_channels_to_flights(self, flights: List[FlightData], def assign_channels_to_flights(self, flights: List[FlightData],
air_support: AirSupport) -> None: air_support: AirSupport) -> None:
@@ -501,7 +376,6 @@ dcsLiberation.TargetPoints = {
logging.warning(f"No aircraft data for {airframe.id}") logging.warning(f"No aircraft data for {airframe.id}")
return return
if aircraft_data.channel_allocator is not None: aircraft_data.channel_allocator.assign_channels_for_flight(
aircraft_data.channel_allocator.assign_channels_for_flight( flight, air_support
flight, air_support )
)

View File

@@ -1,2 +0,0 @@
from .luaplugin import LuaPlugin
from .manager import LuaPluginManager

View File

@@ -1,180 +0,0 @@
from __future__ import annotations
import json
import logging
import textwrap
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, TYPE_CHECKING
from game.settings import Settings
if TYPE_CHECKING:
from game.operation.operation import Operation
class LuaPluginWorkOrder:
def __init__(self, parent_mnemonic: str, filename: str, mnemonic: str,
disable: bool) -> None:
self.parent_mnemonic = parent_mnemonic
self.filename = filename
self.mnemonic = mnemonic
self.disable = disable
def work(self, operation: Operation) -> None:
if self.disable:
operation.bypass_plugin_script(self.mnemonic)
else:
operation.inject_plugin_script(self.parent_mnemonic, self.filename,
self.mnemonic)
class PluginSettings:
def __init__(self, identifier: str, enabled_by_default: bool) -> None:
self.identifier = identifier
self.enabled_by_default = enabled_by_default
self.settings = Settings()
self.initialize_settings()
def set_settings(self, settings: Settings):
self.settings = settings
self.initialize_settings()
def initialize_settings(self) -> None:
# Plugin options are saved in the game's Settings, but it's possible for
# plugins to change across loads. If new plugins are added or new
# options added to those plugins, initialize the new settings.
self.settings.initialize_plugin_option(self.identifier,
self.enabled_by_default)
@property
def enabled(self) -> bool:
return self.settings.plugin_option(self.identifier)
def set_enabled(self, enabled: bool) -> None:
self.settings.set_plugin_option(self.identifier, enabled)
class LuaPluginOption(PluginSettings):
def __init__(self, identifier: str, name: str,
enabled_by_default: bool) -> None:
super().__init__(identifier, enabled_by_default)
self.name = name
@dataclass(frozen=True)
class LuaPluginDefinition:
identifier: str
name: str
present_in_ui: bool
enabled_by_default: bool
options: List[LuaPluginOption]
work_orders: List[LuaPluginWorkOrder]
config_work_orders: List[LuaPluginWorkOrder]
@classmethod
def from_json(cls, name: str, path: Path) -> LuaPluginDefinition:
data = json.loads(path.read_text())
options = []
for option in data.get("specificOptions"):
option_id = option["mnemonic"]
options.append(LuaPluginOption(
identifier=f"{name}.{option_id}",
name=option.get("nameInUI", name),
enabled_by_default=option.get("defaultValue")
))
work_orders = []
for work_order in data.get("scriptsWorkOrders"):
work_orders.append(LuaPluginWorkOrder(
name, work_order.get("file"), work_order["mnemonic"],
work_order.get("disable", False)
))
config_work_orders = []
for work_order in data.get("configurationWorkOrders"):
config_work_orders.append(LuaPluginWorkOrder(
name, work_order.get("file"), work_order["mnemonic"],
work_order.get("disable", False)
))
return cls(
identifier=name,
name=data["nameInUI"],
present_in_ui=not data.get("skipUI", False),
enabled_by_default=data.get("defaultValue", False),
options=options,
work_orders=work_orders,
config_work_orders=config_work_orders
)
class LuaPlugin(PluginSettings):
def __init__(self, definition: LuaPluginDefinition) -> None:
self.definition = definition
super().__init__(self.definition.identifier,
self.definition.enabled_by_default)
@property
def name(self) -> str:
return self.definition.name
@property
def show_in_ui(self) -> bool:
return self.definition.present_in_ui
@property
def options(self) -> List[LuaPluginOption]:
return self.definition.options
@classmethod
def from_json(cls, name: str, path: Path) -> Optional[LuaPlugin]:
try:
definition = LuaPluginDefinition.from_json(name, path)
except KeyError:
logging.exception("Required plugin configuration value missing")
return None
return cls(definition)
def set_settings(self, settings: Settings):
super().set_settings(settings)
for option in self.definition.options:
option.set_settings(self.settings)
def inject_scripts(self, operation: Operation) -> None:
for work_order in self.definition.work_orders:
work_order.work(operation)
def inject_configuration(self, operation: Operation) -> None:
# inject the plugin options
if self.options:
option_decls = []
for option in self.options:
enabled = str(option.enabled).lower()
name = option.identifier
option_decls.append(
f" dcsLiberation.plugins.{name} = {enabled}")
joined_options = "\n".join(option_decls)
lua = textwrap.dedent(f"""\
-- {self.identifier} plugin configuration.
if dcsLiberation then
if not dcsLiberation.plugins then
dcsLiberation.plugins = {{}}
end
dcsLiberation.plugins.{self.identifier} = {{}}
{joined_options}
end
""")
operation.inject_lua_trigger(
lua, f"{self.identifier} plugin configuration")
for work_order in self.definition.config_work_orders:
work_order.work(operation)

View File

@@ -1,50 +0,0 @@
import json
import logging
from pathlib import Path
from typing import Dict, List, Optional
from game.settings import Settings
from game.plugins.luaplugin import LuaPlugin
class LuaPluginManager:
_plugins_loaded = False
_plugins: Dict[str, LuaPlugin] = {}
@classmethod
def _load_plugins(cls) -> None:
plugins_path = Path("resources/plugins")
path = plugins_path / "plugins.json"
if not path.exists():
raise RuntimeError(f"{path} does not exist. Cannot continue.")
logging.info(f"Reading plugins list from {path}")
data = json.loads(path.read_text())
for name in data:
plugin_path = plugins_path / name / "plugin.json"
if not plugin_path.exists():
raise RuntimeError(
f"Invalid plugin configuration: required plugin {name} "
f"does not exist at {plugin_path}")
logging.info(f"Loading plugin {name} from {plugin_path}")
plugin = LuaPlugin.from_json(name, plugin_path)
if plugin is not None:
cls._plugins[name] = plugin
cls._plugins_loaded = True
@classmethod
def _get_plugins(cls) -> Dict[str, LuaPlugin]:
if not cls._plugins_loaded:
cls._load_plugins()
return cls._plugins
@classmethod
def plugins(cls) -> List[LuaPlugin]:
return list(cls._get_plugins().values())
@classmethod
def load_settings(cls, settings: Settings) -> None:
for plugin in cls.plugins():
plugin.set_settings(settings)

View File

@@ -1,5 +1,3 @@
from typing import Dict
class Settings: class Settings:
@@ -26,6 +24,8 @@ class Settings:
self.sams = True # Legacy parameter do not use self.sams = True # Legacy parameter do not use
self.cold_start = False # Legacy parameter do not use self.cold_start = False # Legacy parameter do not use
self.version = None self.version = None
self.include_jtac_if_available = True
self.jtac_smoke_on = True
# Performance oriented # Performance oriented
self.perf_red_alert_state = True self.perf_red_alert_state = True
@@ -40,37 +40,4 @@ class Settings:
self.perf_culling = False self.perf_culling = False
self.perf_culling_distance = 100 self.perf_culling_distance = 100
# LUA Plugins system
self.plugins: Dict[str, bool] = {}
# Cheating
self.show_red_ato = False
self.never_delay_player_flights = False
@staticmethod
def plugin_settings_key(identifier: str) -> str:
return f"plugins.{identifier}"
def initialize_plugin_option(self, identifier: str,
default_value: bool) -> None:
try:
self.plugin_option(identifier)
except KeyError:
self.set_plugin_option(identifier, default_value)
def plugin_option(self, identifier: str) -> bool:
return self.plugins[self.plugin_settings_key(identifier)]
def set_plugin_option(self, identifier: str, enabled: bool) -> None:
self.plugins[self.plugin_settings_key(identifier)] = enabled
def __setstate__(self, state) -> None:
# __setstate__ is called with the dict of the object being unpickled. We
# can provide save compatibility for new settings options (which
# normally would not be present in the unpickled object) by creating a
# new settings object, updating it with the unpickled state, and
# updating our dict with that.
new_state = Settings().__dict__
new_state.update(state)
self.__dict__.update(new_state)

View File

@@ -1,14 +1,14 @@
def meter_to_feet(value_in_meter: float) -> int: def meter_to_feet(value_in_meter):
return int(3.28084 * value_in_meter) return int(3.28084 * value_in_meter)
def feet_to_meter(value_in_feet: float) -> int: def feet_to_meter(value_in_feet):
return int(value_in_feet / 3.28084) return int(float(value_in_feet)/3.048)
def meter_to_nm(value_in_meter: float) -> int: def meter_to_nm(value_in_meter):
return int(value_in_meter / 1852) return int(float(value_in_meter)*0.000539957)
def nm_to_meter(value_in_nm: float) -> int: def nm_to_meter(value_in_nm):
return int(value_in_nm * 1852) return int(float(value_in_nm)*1852)

View File

@@ -1,18 +0,0 @@
from pathlib import Path
def _build_version_string() -> str:
components = ["2.2.0"]
build_number_path = Path("resources/buildnumber")
if build_number_path.exists():
with build_number_path.open("r") as build_number_file:
components.append(build_number_file.readline())
if not Path("resources/final").exists():
components.append("preview")
return "-".join(components)
#: Current version of Liberation.
VERSION = _build_version_string()

View File

@@ -1,183 +0,0 @@
from __future__ import annotations
import datetime
import logging
import random
from dataclasses import dataclass
from enum import Enum
from typing import Optional
from dcs.weather import Weather as PydcsWeather, Wind
from game.settings import Settings
from theater import ConflictTheater
class TimeOfDay(Enum):
Dawn = "dawn"
Day = "day"
Dusk = "dusk"
Night = "night"
@dataclass(frozen=True)
class WindConditions:
at_0m: Wind
at_2000m: Wind
at_8000m: Wind
@dataclass(frozen=True)
class Clouds:
base: int
density: int
thickness: int
precipitation: PydcsWeather.Preceptions
@dataclass(frozen=True)
class Fog:
visibility: int
thickness: int
class Weather:
def __init__(self) -> None:
self.clouds = self.generate_clouds()
self.fog = self.generate_fog()
self.wind = self.generate_wind()
def generate_clouds(self) -> Optional[Clouds]:
raise NotImplementedError
def generate_fog(self) -> Optional[Fog]:
if random.randrange(5) != 0:
return None
return Fog(
visibility=random.randint(2500, 5000),
thickness=random.randint(100, 500)
)
def generate_wind(self) -> WindConditions:
raise NotImplementedError
@staticmethod
def random_wind(minimum: int, maximum) -> WindConditions:
wind_direction = random.randint(0, 360)
at_0m_factor = 1
at_2000m_factor = 2
at_8000m_factor = 3
base_wind = random.randint(minimum, maximum)
return WindConditions(
# Always some wind to make the smoke move a bit.
at_0m=Wind(wind_direction, max(1, base_wind * at_0m_factor)),
at_2000m=Wind(wind_direction, base_wind * at_2000m_factor),
at_8000m=Wind(wind_direction, base_wind * at_8000m_factor)
)
@staticmethod
def random_cloud_base() -> int:
return random.randint(2000, 3000)
@staticmethod
def random_cloud_thickness() -> int:
return random.randint(100, 400)
class ClearSkies(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return None
def generate_fog(self) -> Optional[Fog]:
return None
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 0)
class Cloudy(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return Clouds(
base=self.random_cloud_base(),
density=random.randint(1, 8),
thickness=self.random_cloud_thickness(),
precipitation=PydcsWeather.Preceptions.None_
)
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 4)
class Raining(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return Clouds(
base=self.random_cloud_base(),
density=random.randint(5, 8),
thickness=self.random_cloud_thickness(),
precipitation=PydcsWeather.Preceptions.Rain
)
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 6)
class Thunderstorm(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return Clouds(
base=self.random_cloud_base(),
density=random.randint(9, 10),
thickness=self.random_cloud_thickness(),
precipitation=PydcsWeather.Preceptions.Thunderstorm
)
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 8)
@dataclass
class Conditions:
time_of_day: TimeOfDay
start_time: datetime.datetime
weather: Weather
@classmethod
def generate(cls, theater: ConflictTheater, day: datetime.date,
time_of_day: TimeOfDay, settings: Settings) -> Conditions:
return cls(
time_of_day=time_of_day,
start_time=cls.generate_start_time(
theater, day, time_of_day, settings.night_disabled
),
weather=cls.generate_weather()
)
@classmethod
def generate_start_time(cls, theater: ConflictTheater, day: datetime.date,
time_of_day: TimeOfDay,
night_disabled: bool) -> datetime.datetime:
if night_disabled:
logging.info("Skip Night mission due to user settings")
time_range = {
TimeOfDay.Dawn: (8, 9),
TimeOfDay.Day: (10, 12),
TimeOfDay.Dusk: (12, 14),
TimeOfDay.Night: (14, 17),
}[time_of_day]
else:
time_range = theater.daytime_map[time_of_day.value]
time = datetime.time(hour=random.randint(*time_range))
return datetime.datetime.combine(day, time)
@classmethod
def generate_weather(cls) -> Weather:
chances = {
Thunderstorm: 1,
Raining: 20,
Cloudy: 60,
ClearSkies: 20,
}
weather_type = random.choices(list(chances.keys()),
weights=list(chances.values()))[0]
return weather_type()

View File

@@ -1,3 +1,4 @@
from .aaa import *
from .aircraft import * from .aircraft import *
from .armor import * from .armor import *
from .airsupportgen import * from .airsupportgen import *
@@ -11,3 +12,4 @@ from .forcedoptionsgen import *
from .kneeboard import * from .kneeboard import *
from . import naming from . import naming

51
gen/aaa.py Normal file
View File

@@ -0,0 +1,51 @@
from .conflictgen import *
from .naming import *
from dcs.mission import *
from dcs.mission import *
from .conflictgen import *
from .naming import *
DISTANCE_FACTOR = 0.5, 1
EXTRA_AA_MIN_DISTANCE = 50000
EXTRA_AA_MAX_DISTANCE = 150000
EXTRA_AA_POSITION_FROM_CP = 550
class ExtraAAConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game, player_country: Country, enemy_country: Country):
self.mission = mission
self.game = game
self.conflict = conflict
self.player_country = player_country
self.enemy_country = enemy_country
def generate(self):
for cp in self.game.theater.controlpoints:
if cp.is_global:
continue
if cp.position.distance_to_point(self.conflict.position) < EXTRA_AA_MIN_DISTANCE:
continue
if cp.position.distance_to_point(self.conflict.from_cp.position) < EXTRA_AA_MIN_DISTANCE:
continue
if cp.position.distance_to_point(self.conflict.to_cp.position) < EXTRA_AA_MIN_DISTANCE:
continue
if cp.position.distance_to_point(self.conflict.position) > EXTRA_AA_MAX_DISTANCE:
continue
country_name = cp.captured and self.player_country or self.enemy_country
position = cp.position.point_from_heading(0, EXTRA_AA_POSITION_FROM_CP)
self.mission.vehicle_group(
country=self.mission.country(country_name),
name=namegen.next_basedefense_name(),
_type=db.EXTRA_AA[country_name],
position=position,
group_size=1
)

File diff suppressed because it is too large Load Diff

View File

@@ -3,11 +3,11 @@
Remove once https://github.com/pydcs/dcs/issues/69 tracks getting the missing Remove once https://github.com/pydcs/dcs/issues/69 tracks getting the missing
data added to pydcs. Until then, missing data can be manually filled in here. data added to pydcs. Until then, missing data can be manually filled in here.
""" """
from __future__ import annotations
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Dict, Optional, Tuple import logging
from typing import Dict, Iterator, Optional, Tuple
from dcs.terrain.terrain import Airport
from .radios import MHz, RadioFrequency from .radios import MHz, RadioFrequency
from .tacan import TacanBand, TacanChannel from .tacan import TacanBand, TacanChannel
@@ -195,12 +195,10 @@ AIRFIELD_DATA = {
runway_length=8623, runway_length=8623,
atc=AtcData(MHz(3, 750), MHz(121, 0), MHz(38, 400), MHz(250, 0)), atc=AtcData(MHz(3, 750), MHz(121, 0), MHz(38, 400), MHz(250, 0)),
outer_ndb={ outer_ndb={
"22": ("AP", MHz(443, 0)), "22": ("AP", MHz(443, 0)), "4": "443.00 (AN)"
"04": ("AN", MHz(443)),
}, },
inner_ndb={ inner_ndb={
"22": ("P", MHz(215, 0)), "22": ("P", MHz(215, 0)), "4": "215.00 (N)"
"04": ("N", MHz(215)),
}, },
), ),
@@ -1503,3 +1501,61 @@ AIRFIELD_DATA = {
atc=AtcData(MHz(3, 775), MHz(118, 50), MHz(38, 450), MHz(250, 50)), atc=AtcData(MHz(3, 775), MHz(118, 50), MHz(38, 450), MHz(250, 50)),
), ),
} }
@dataclass(frozen=True)
class RunwayData:
airfield_name: str
runway_name: str
atc: Optional[RadioFrequency] = None
tacan: Optional[TacanChannel] = None
tacan_callsign: Optional[str] = None
ils: Optional[RadioFrequency] = None
icls: Optional[int] = None
@classmethod
def for_airfield(cls, airport: Airport, runway: str) -> "RunwayData":
"""Creates RunwayData for the given runway of an airfield.
Args:
airport: The airfield the runway belongs to.
runway: Identifier of the runway to use. e.g. "03" or "20L".
"""
atc: Optional[RadioFrequency] = None
tacan: Optional[TacanChannel] = None
tacan_callsign: Optional[str] = None
ils: Optional[RadioFrequency] = None
try:
airfield = AIRFIELD_DATA[airport.name]
if airfield.atc is not None:
atc = airfield.atc.uhf
else:
atc = None
tacan = airfield.tacan
tacan_callsign = airfield.tacan_callsign
ils = airfield.ils_freq(runway)
except KeyError:
logging.warning(f"No airfield data for {airport.name}")
return cls(
airfield_name=airport.name,
runway_name=runway,
atc=atc,
tacan=tacan,
tacan_callsign=tacan_callsign,
ils=ils
)
@classmethod
def for_pydcs_airport(cls, airport: Airport) -> Iterator["RunwayData"]:
for runway in airport.runways:
runway_number = runway.heading // 10
runway_side = ["", "L", "R"][runway.leftright]
runway_name = f"{runway_number:02}{runway_side}"
yield cls.for_airfield(airport, runway_name)
# pydcs only exposes one runway per physical runway, so to expose
# both sides of the runway we need to generate the other.
runway_number = ((runway.heading + 180) % 360) // 10
runway_side = ["", "R", "L"][runway.leftright]
runway_name = f"{runway_number:02}{runway_side}"
yield cls.for_airfield(airport, runway_name)

View File

@@ -1,21 +1,8 @@
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List, Type
from dcs.mission import Mission, StartType
from dcs.planes import IL_78M
from dcs.task import (
AWACS,
ActivateBeaconCommand,
MainTask,
Refueling,
SetImmortalCommand,
SetInvisibleCommand,
)
from game import db
from .naming import namegen
from .callsigns import callsign_for_support_unit from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict from .conflictgen import *
from .naming import *
from .radios import RadioFrequency, RadioRegistry from .radios import RadioFrequency, RadioRegistry
from .tacan import TacanBand, TacanChannel, TacanRegistry from .tacan import TacanBand, TacanChannel, TacanRegistry
@@ -30,7 +17,6 @@ AWACS_ALT = 13000
@dataclass @dataclass
class AwacsInfo: class AwacsInfo:
"""AWACS information for the kneeboard.""" """AWACS information for the kneeboard."""
dcsGroupName: str
callsign: str callsign: str
freq: RadioFrequency freq: RadioFrequency
@@ -38,7 +24,6 @@ class AwacsInfo:
@dataclass @dataclass
class TankerInfo: class TankerInfo:
"""Tanker information for the kneeboard.""" """Tanker information for the kneeboard."""
dcsGroupName: str
callsign: str callsign: str
variant: str variant: str
freq: RadioFrequency freq: RadioFrequency
@@ -64,7 +49,7 @@ class AirSupportConflictGenerator:
self.tacan_registry = tacan_registry self.tacan_registry = tacan_registry
@classmethod @classmethod
def support_tasks(cls) -> List[Type[MainTask]]: def support_tasks(cls) -> typing.Collection[typing.Type[MainTask]]:
return [Refueling, AWACS] return [Refueling, AWACS]
def generate(self, is_awacs_enabled): def generate(self, is_awacs_enabled):
@@ -118,7 +103,7 @@ class AirSupportConflictGenerator:
tanker_group.points[0].tasks.append(SetInvisibleCommand(True)) tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
tanker_group.points[0].tasks.append(SetImmortalCommand(True)) tanker_group.points[0].tasks.append(SetImmortalCommand(True))
self.air_support.tankers.append(TankerInfo(str(tanker_group.name), callsign, variant, freq, tacan)) self.air_support.tankers.append(TankerInfo(callsign, variant, freq, tacan))
if is_awacs_enabled: if is_awacs_enabled:
try: try:
@@ -140,6 +125,6 @@ class AirSupportConflictGenerator:
awacs_flight.points[0].tasks.append(SetImmortalCommand(True)) awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
self.air_support.awacs.append(AwacsInfo( self.air_support.awacs.append(AwacsInfo(
str(awacs_flight.name), callsign_for_support_unit(awacs_flight), freq)) callsign_for_support_unit(awacs_flight), freq))
except: except:
print("No AWACS for faction") print("No AWACS for faction")

View File

@@ -1,40 +1,13 @@
import logging
import random
from dataclasses import dataclass from dataclasses import dataclass
from typing import List
from dcs import Mission
from dcs.action import AITaskPush from dcs.action import AITaskPush
from dcs.condition import GroupLifeLess, Or, TimeAfter, UnitDamaged from dcs.condition import TimeAfter, UnitDamaged, Or, GroupLifeLess
from dcs.country import Country from dcs.triggers import TriggerOnce, Event
from dcs.mapping import Point
from dcs.planes import MQ_9_Reaper
from dcs.point import PointAction
from dcs.task import (
AttackGroup,
ControlledTask,
EPLRS,
FireAtPoint,
GoToWaypoint,
Hold,
OrbitAction,
SetImmortalCommand,
SetInvisibleCommand,
)
from dcs.triggers import Event, TriggerOnce
from dcs.unit import Vehicle
from dcs.unittype import VehicleType
from game import db from gen import namegen
from .naming import namegen from gen.ground_forces.ai_ground_planner import CombatGroupRole, DISTANCE_FROM_FRONTLINE
from gen.ground_forces.ai_ground_planner import (
CombatGroupRole,
DISTANCE_FROM_FRONTLINE,
)
from .callsigns import callsign_for_support_unit from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict from .conflictgen import *
from .ground_forces.combat_stance import CombatStance
from game.plugins import LuaPluginManager
SPREAD_DISTANCE_FACTOR = 0.1, 0.3 SPREAD_DISTANCE_FACTOR = 0.1, 0.3
SPREAD_DISTANCE_SIZE_FACTOR = 0.1 SPREAD_DISTANCE_SIZE_FACTOR = 0.1
@@ -55,7 +28,6 @@ RANDOM_OFFSET_ATTACK = 250
@dataclass(frozen=True) @dataclass(frozen=True)
class JtacInfo: class JtacInfo:
"""JTAC information.""" """JTAC information."""
dcsGroupName: str
unit_name: str unit_name: str
callsign: str callsign: str
region: str region: str
@@ -76,7 +48,7 @@ class GroundConflictGenerator:
self.jtacs: List[JtacInfo] = [] self.jtacs: List[JtacInfo] = []
def _group_point(self, point) -> Point: def _group_point(self, point) -> Point:
distance = random.randint( distance = randint(
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]), int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]), int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
) )
@@ -140,13 +112,13 @@ class GroundConflictGenerator:
self.plan_action_for_groups(self.enemy_stance, enemy_groups, player_groups, self.conflict.heading - 90, self.conflict.to_cp, self.conflict.from_cp) self.plan_action_for_groups(self.enemy_stance, enemy_groups, player_groups, self.conflict.heading - 90, self.conflict.to_cp, self.conflict.from_cp)
# Add JTAC # Add JTAC
if self.game.player_faction.has_jtac: if "has_jtac" in self.game.player_faction and self.game.player_faction["has_jtac"] and self.game.settings.include_jtac_if_available:
n = "JTAC" + str(self.conflict.from_cp.id) + str(self.conflict.to_cp.id) n = "JTAC" + str(self.conflict.from_cp.id) + str(self.conflict.to_cp.id)
code = 1688 - len(self.jtacs) code = 1688 - len(self.jtacs)
utype = MQ_9_Reaper utype = MQ_9_Reaper
if self.game.player_faction.jtac_unit is not None: if "jtac_unit" in self.game.player_faction:
utype = self.game.player_faction.jtac_unit utype = self.game.player_faction["jtac_unit"]
jtac = self.mission.flight_group(country=self.mission.country(self.game.player_country), jtac = self.mission.flight_group(country=self.mission.country(self.game.player_country),
name=n, name=n,
@@ -160,7 +132,7 @@ class GroundConflictGenerator:
frontline = f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}" frontline = f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}"
# Note: Will need to change if we ever add ground based JTAC. # Note: Will need to change if we ever add ground based JTAC.
callsign = callsign_for_support_unit(jtac) callsign = callsign_for_support_unit(jtac)
self.jtacs.append(JtacInfo(str(jtac.name), n, callsign, frontline, str(code))) self.jtacs.append(JtacInfo(n, callsign, frontline, str(code)))
def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading): def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading):
@@ -193,7 +165,7 @@ class GroundConflictGenerator:
heading=forward_heading, heading=forward_heading,
move_formation=PointAction.OffRoad) move_formation=PointAction.OffRoad)
for i in range(random.randint(3, 10)): for i in range(randint(3, 10)):
u = random.choice(possible_infantry_units) u = random.choice(possible_infantry_units)
position = infantry_position.random_point_within(55, 5) position = infantry_position.random_point_within(55, 5)
self.mission.vehicle_group( self.mission.vehicle_group(

View File

@@ -1,205 +0,0 @@
"""Air Tasking Orders.
The classes of the Air Tasking Order (ATO) define all of the missions that have
been planned, and which aircraft have been assigned to them. Each planned
mission, or "package" is composed of individual flights. The package may contain
dissimilar aircraft performing different roles, but all for the same goal. For
example, the package to strike an enemy airfield may contain an escort flight,
a SEAD flight, and the strike aircraft themselves. CAP packages may contain only
the single CAP flight.
"""
import logging
from collections import defaultdict
from dataclasses import dataclass, field
from datetime import timedelta
from typing import Dict, List, Optional
from dcs.mapping import Point
from theater.missiontarget import MissionTarget
from .flights.flight import Flight, FlightType
from .flights.flightplan import FormationFlightPlan
@dataclass(frozen=True)
class Task:
"""The main task of a flight or package."""
#: The type of task.
task_type: FlightType
#: The location of the objective.
location: str
@dataclass(frozen=True)
class PackageWaypoints:
join: Point
ingress: Point
egress: Point
split: Point
@dataclass
class Package:
"""A mission package."""
#: The mission target. Currently can be either a ControlPoint or a
#: TheaterGroundObject (non-ControlPoint map objectives).
target: MissionTarget
#: The set of flights in the package.
flights: List[Flight] = field(default_factory=list)
delay: int = field(default=0)
#: Desired TOT as an offset from mission start.
time_over_target: timedelta = field(default=timedelta())
waypoints: Optional[PackageWaypoints] = field(default=None)
@property
def formation_speed(self) -> Optional[int]:
"""The speed of the package when in formation.
If none of the flights in the package will join a formation, this
returns None. This is nto uncommon, since only strike-like (strike,
DEAD, anti-ship, BAI, etc.) flights and their escorts fly in formation.
Others (CAP and CAS, currently) will coordinate in target timing but
fly their own path to the target.
"""
speeds = []
for flight in self.flights:
if isinstance(flight.flight_plan, FormationFlightPlan):
speeds.append(flight.flight_plan.best_flight_formation_speed)
if not speeds:
return None
return min(speeds)
# TODO: Should depend on the type of escort.
# SEAD might be able to leave before CAP.
@property
def escort_start_time(self) -> Optional[timedelta]:
times = []
for flight in self.flights:
waypoint = flight.flight_plan.request_escort_at()
if waypoint is None:
continue
tot = flight.flight_plan.tot_for_waypoint(waypoint)
if tot is None:
logging.error(
f"{flight} requested escort at {waypoint} but that "
"waypoint has no TOT. It may not be escorted.")
continue
times.append(tot)
if times:
return min(times)
return None
@property
def escort_end_time(self) -> Optional[timedelta]:
times = []
for flight in self.flights:
waypoint = flight.flight_plan.dismiss_escort_at()
if waypoint is None:
continue
tot = flight.flight_plan.tot_for_waypoint(waypoint)
if tot is None:
tot = flight.flight_plan.depart_time_for_waypoint(waypoint)
if tot is None:
logging.error(
f"{flight} dismissed escort at {waypoint} but that "
"waypoint has no TOT or departure time. It may not be "
"escorted.")
continue
times.append(tot)
if times:
return max(times)
return None
def add_flight(self, flight: Flight) -> None:
"""Adds a flight to the package."""
self.flights.append(flight)
def remove_flight(self, flight: Flight) -> None:
"""Removes a flight from the package."""
self.flights.remove(flight)
if not self.flights:
self.waypoints = None
@property
def primary_task(self) -> Optional[FlightType]:
if not self.flights:
return None
flight_counts: Dict[FlightType, int] = defaultdict(lambda: 0)
for flight in self.flights:
flight_counts[flight.flight_type] += 1
# The package will contain a mix of mission types, but in general we can
# determine the goal of the mission because some mission types are more
# likely to be the main task than others. For example, a package with
# only CAP flights is a CAP package, a flight with CAP and strike is a
# strike package, a flight with CAP and DEAD is a DEAD package, and a
# flight with strike and SEAD is an OCA/Strike package. The type of
# package is determined by the highest priority flight in the package.
task_priorities = [
FlightType.CAS,
FlightType.STRIKE,
FlightType.ANTISHIP,
FlightType.BAI,
FlightType.EVAC,
FlightType.TROOP_TRANSPORT,
FlightType.RECON,
FlightType.ELINT,
FlightType.DEAD,
FlightType.SEAD,
FlightType.LOGISTICS,
FlightType.INTERCEPTION,
FlightType.TARCAP,
FlightType.CAP,
FlightType.BARCAP,
FlightType.EWAR,
FlightType.ESCORT,
]
for task in task_priorities:
if flight_counts[task]:
return task
# If we get here, our task_priorities list above is incomplete. Log the
# issue and return the type of *any* flight in the package.
some_mission = next(iter(self.flights)).flight_type
logging.warning(f"Unhandled mission type: {some_mission}")
return some_mission
@property
def package_description(self) -> str:
"""Generates a package description based on flight composition."""
task = self.primary_task
if task is None:
return "No mission"
return task.name
def __hash__(self) -> int:
# TODO: Far from perfect. Number packages?
return hash(self.target.name)
@dataclass
class AirTaskingOrder:
"""The entire ATO for one coalition."""
#: The set of all planned packages in the ATO.
packages: List[Package] = field(default_factory=list)
def add_package(self, package: Package) -> None:
"""Adds a package to the ATO."""
self.packages.append(package)
def remove_package(self, package: Package) -> None:
"""Removes a package from the ATO."""
self.packages.remove(package)
def clear(self) -> None:
"""Removes all packages from the ATO."""
self.packages.clear()

View File

@@ -1,26 +1,19 @@
"""
Briefing generation logic
"""
from __future__ import annotations
import os import os
import random from collections import defaultdict
import logging
from dataclasses import dataclass from dataclasses import dataclass
from theater.frontline import FrontLine import random
from typing import List, Dict, TYPE_CHECKING from typing import List
from jinja2 import Environment, FileSystemLoader, select_autoescape
from game import db
from dcs.mission import Mission from dcs.mission import Mission
from .aircraft import FlightData from .aircraft import FlightData
from .airfields import RunwayData
from .airsupportgen import AwacsInfo, TankerInfo from .airsupportgen import AwacsInfo, TankerInfo
from .armor import JtacInfo from .armor import JtacInfo
from theater import ControlPoint from .conflictgen import Conflict
from .ground_forces.combat_stance import CombatStance from .ground_forces.combat_stance import CombatStance
from .radios import RadioFrequency from .radios import RadioFrequency
from .runways import RunwayData
if TYPE_CHECKING:
from game import Game
@dataclass @dataclass
class CommInfo: class CommInfo:
@@ -29,33 +22,19 @@ class CommInfo:
freq: RadioFrequency freq: RadioFrequency
class FrontLineInfo:
def __init__(self, front_line: FrontLine):
self.front_line: FrontLine = front_line
self.player_base: ControlPoint = front_line.control_point_a
self.enemy_base: ControlPoint = front_line.control_point_b
self.player_zero: bool = self.player_base.base.total_armor == 0
self.enemy_zero: bool = self.enemy_base.base.total_armor == 0
self.advantage: bool = self.player_base.base.total_armor > self.enemy_base.base.total_armor
self.stance: CombatStance = self.player_base.stances[self.enemy_base.id]
self.combat_stances = CombatStance
class MissionInfoGenerator: class MissionInfoGenerator:
"""Base type for generators of mission information for the player. """Base type for generators of mission information for the player.
Examples of subtypes include briefing generators, kneeboard generators, etc. Examples of subtypes include briefing generators, kneeboard generators, etc.
""" """
def __init__(self, mission: Mission, game: Game) -> None: def __init__(self, mission: Mission) -> None:
self.mission = mission self.mission = mission
self.game = game
self.awacs: List[AwacsInfo] = [] self.awacs: List[AwacsInfo] = []
self.comms: List[CommInfo] = [] self.comms: List[CommInfo] = []
self.flights: List[FlightData] = [] self.flights: List[FlightData] = []
self.jtacs: List[JtacInfo] = [] self.jtacs: List[JtacInfo] = []
self.tankers: List[TankerInfo] = [] self.tankers: List[TankerInfo] = []
self.frontlines: List[FrontLineInfo] = []
self.dynamic_runways: List[RunwayData] = []
def add_awacs(self, awacs: AwacsInfo) -> None: def add_awacs(self, awacs: AwacsInfo) -> None:
"""Adds an AWACS/GCI to the mission. """Adds an AWACS/GCI to the mission.
@@ -98,13 +77,20 @@ class MissionInfoGenerator:
""" """
self.tankers.append(tanker) self.tankers.append(tanker)
def add_frontline(self, frontline: FrontLineInfo) -> None: def generate(self) -> None:
"""Adds a frontline to the briefing """Generates the mission information."""
raise NotImplementedError
Arguments:
frontline: Frontline conflict information class BriefingGenerator(MissionInfoGenerator):
"""
self.frontlines.append(frontline) def __init__(self, mission: Mission, conflict: Conflict, game):
super().__init__(mission)
self.conflict = conflict
self.game = game
self.title = ""
self.description = ""
self.dynamic_runways: List[RunwayData] = []
def add_dynamic_runway(self, runway: RunwayData) -> None: def add_dynamic_runway(self, runway: RunwayData) -> None:
"""Adds a dynamically generated runway to the briefing. """Adds a dynamically generated runway to the briefing.
@@ -114,51 +100,148 @@ class MissionInfoGenerator:
""" """
self.dynamic_runways.append(runway) self.dynamic_runways.append(runway)
def generate(self) -> None: def add_flight_description(self, flight: FlightData):
"""Generates the mission information.""" assert flight.client_units
raise NotImplementedError
aircraft = flight.aircraft_type
flight_unit_name = db.unit_type_name(aircraft)
self.description += "-" * 50 + "\n"
self.description += f"{flight_unit_name} x {flight.size + 2}\n\n"
class BriefingGenerator(MissionInfoGenerator): for i, wpt in enumerate(flight.waypoints):
self.description += f"#{i + 1} -- {wpt.name} : {wpt.description}\n"
self.description += f"#{len(flight.waypoints) + 1} -- RTB\n\n"
def __init__(self, mission: Mission, game: Game): def add_ally_flight_description(self, flight: FlightData):
super().__init__(mission, game) assert not flight.client_units
self.allied_flights_by_departure: Dict[str, List[FlightData]] = {} aircraft = flight.aircraft_type
env = Environment( flight_unit_name = db.unit_type_name(aircraft)
loader=FileSystemLoader("resources/briefing/templates"), self.description += (
autoescape=select_autoescape( f"{flight.flight_type.name} {flight_unit_name} x {flight.size}, "
disabled_extensions=("",), f"departing in {flight.departure_delay} minutes\n"
default_for_string=True, )
default=True,
),
trim_blocks=True,
lstrip_blocks=True,
)
self.template = env.get_template("briefingtemplate_EN.j2")
def generate(self) -> None: def generate(self):
"""Generate the mission briefing self.description = ""
"""
self._generate_frontline_info()
self.generate_allied_flights_by_departure()
self.mission.set_description_text(self.template.render(vars(self)))
self.mission.add_picture_blue(os.path.abspath(
"./resources/ui/splash_screen.png"))
def _generate_frontline_info(self) -> None: self.description += "DCS Liberation turn #" + str(self.game.turn) + "\n"
"""Build FrontLineInfo objects from FrontLine type and append to briefing. self.description += "=" * 15 + "\n\n"
"""
for front_line in self.game.theater.conflicts(from_player=True):
self.add_frontline(FrontLineInfo(front_line))
# TODO: This should determine if runway is friendly through a method more robust than the existing string match self.generate_ongoing_war_text()
def generate_allied_flights_by_departure(self) -> None:
"""Create iterable to display allied flights grouped by departure airfield. self.description += "\n"*2
""" self.description += "Your flights:" + "\n"
self.description += "=" * 15 + "\n\n"
for flight in self.flights:
if flight.client_units:
self.add_flight_description(flight)
self.description += "\n"*2
self.description += "Planned ally flights:" + "\n"
self.description += "=" * 15 + "\n"
allied_flights_by_departure = defaultdict(list)
for flight in self.flights: for flight in self.flights:
if not flight.client_units and flight.friendly: if not flight.client_units and flight.friendly:
name = flight.departure.airfield_name name = flight.departure.airfield_name
if name in self.allied_flights_by_departure: # where else can we get this? allied_flights_by_departure[name].append(flight)
self.allied_flights_by_departure[name].append(flight) for departure, flights in allied_flights_by_departure.items():
else: self.description += f"\nFrom {departure}\n"
self.allied_flights_by_departure[name] = [flight] self.description += "-" * 50 + "\n\n"
for flight in flights:
self.add_ally_flight_description(flight)
if self.comms:
self.description += "\n\nComms Frequencies:\n"
self.description += "=" * 15 + "\n"
for comm_info in self.comms:
self.description += f"{comm_info.name}: {comm_info.freq}\n"
self.description += ("-" * 50) + "\n"
for runway in self.dynamic_runways:
self.description += f"{runway.airfield_name}\n"
self.description += f"RADIO : {runway.atc}\n"
if runway.tacan is not None:
self.description += f"TACAN : {runway.tacan} {runway.tacan_callsign}\n"
if runway.icls is not None:
self.description += f"ICLS Channel : {runway.icls}\n"
self.description += "-" * 50 + "\n"
self.description += "JTACS [F-10 Menu] : \n"
self.description += "===================\n\n"
for jtac in self.jtacs:
self.description += f"{jtac.region} -- Code : {jtac.code}\n"
self.mission.set_description_text(self.description)
self.mission.add_picture_blue(os.path.abspath(
"./resources/ui/splash_screen.png"))
def generate_ongoing_war_text(self):
self.description += "Current situation:\n"
self.description += "=" * 15 + "\n\n"
conflict_number = 0
for c in self.game.theater.conflicts():
conflict_number = conflict_number + 1
if c[0].captured:
player_base = c[0]
enemy_base = c[1]
else:
player_base = c[1]
enemy_base = c[0]
has_numerical_superiority = player_base.base.total_armor > enemy_base.base.total_armor
self.description += self.__random_frontline_sentence(player_base.name, enemy_base.name)
if enemy_base.id in player_base.stances.keys():
stance = player_base.stances[enemy_base.id]
if player_base.base.total_armor == 0:
self.description += "We do not have a single vehicle available to hold our position, the situation is critical, and we will lose ground inevitably.\n"
elif enemy_base.base.total_armor == 0:
self.description += "The enemy forces have been crushed, we will be able to make significant progress toward " + enemy_base.name + ". \n"
if stance == CombatStance.AGGRESSIVE:
if has_numerical_superiority:
self.description += "On this location, our ground forces will try to make progress against the enemy"
self.description += ". As the enemy is outnumbered, our forces should have no issue making progress.\n"
elif has_numerical_superiority:
self.description += "On this location, our ground forces will try an audacious assault against enemies in superior numbers. The operation is risky, and the enemy might counter attack.\n"
elif stance == CombatStance.ELIMINATION:
if has_numerical_superiority:
self.description += "On this location, our ground forces will focus on the destruction of enemy assets, before attempting to make progress toward " + enemy_base.name + ". "
self.description += "The enemy is already outnumbered, and this maneuver might draw a final blow to their forces.\n"
elif has_numerical_superiority:
self.description += "On this location, our ground forces will try an audacious assault against enemies in superior numbers. The operation is risky, and the enemy might counter attack.\n"
elif stance == CombatStance.BREAKTHROUGH:
if has_numerical_superiority:
self.description += "On this location, our ground forces will focus on progression toward " + enemy_base.name + ".\n"
elif has_numerical_superiority:
self.description += "On this location, our ground forces have been ordered to rush toward " + enemy_base.name + ". Wish them luck... We are also expecting a counter attack.\n"
elif stance in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]:
if has_numerical_superiority:
self.description += "On this location, our ground forces will hold position. We are not expecting an enemy assault.\n"
elif has_numerical_superiority:
self.description += "On this location, our ground forces have been ordered to hold still, and defend against enemy attacks. An enemy assault might be iminent.\n"
if conflict_number == 0:
self.description += "There are currently no fights on the ground.\n"
self.description += "\n\n"
def __random_frontline_sentence(self, player_base_name, enemy_base_name):
templates = [
"There are combats between {} and {}. ",
"The war on the ground is still going on between {} and {}. ",
"Our ground forces in {} are opposed to enemy forces based in {}. ",
"Our forces from {} are fighting enemies based in {}. ",
"There is an active frontline between {} and {}. ",
]
return random.choice(templates).format(player_base_name, enemy_base_name)

View File

@@ -1,11 +1,21 @@
import logging import logging
import random import typing
from typing import Tuple import pdb
import dcs
from dcs.country import Country from random import randint
from dcs.mapping import Point from dcs import Mission
from theater import ConflictTheater, ControlPoint from dcs.mission import *
from dcs.vehicles import *
from dcs.unitgroup import *
from dcs.unittype import *
from dcs.mapping import *
from dcs.point import *
from dcs.task import *
from dcs.country import *
from theater import *
AIR_DISTANCE = 40000 AIR_DISTANCE = 40000
@@ -55,6 +65,24 @@ def _heading_sum(h, a) -> int:
class Conflict: class Conflict:
attackers_side = None # type: str
defenders_side = None # type: str
attackers_country = None # type: Country
defenders_country = None # type: Country
from_cp = None # type: ControlPoint
to_cp = None # type: ControlPoint
position = None # type: Point
size = None # type: int
radials = None # type: typing.List[int]
heading = None # type: int
distance = None # type: int
ground_attackers_location = None # type: Point
ground_defenders_location = None # type: Point
air_attackers_location = None # type: Point
air_defenders_location = None # type: Point
def __init__(self, def __init__(self,
theater: ConflictTheater, theater: ConflictTheater,
from_cp: ControlPoint, from_cp: ControlPoint,
@@ -127,7 +155,7 @@ class Conflict:
else: else:
return self.position return self.position
def find_ground_position(self, at: Point, heading: int, max_distance: int = 40000) -> Point: def find_ground_position(self, at: Point, heading: int, max_distance: int = 40000) -> typing.Optional[Point]:
return Conflict._find_ground_position(at, max_distance, heading, self.theater) return Conflict._find_ground_position(at, max_distance, heading, self.theater)
@classmethod @classmethod
@@ -135,7 +163,7 @@ class Conflict:
return from_cp.has_frontline and to_cp.has_frontline return from_cp.has_frontline and to_cp.has_frontline
@classmethod @classmethod
def frontline_position(cls, theater: ConflictTheater, from_cp: ControlPoint, to_cp: ControlPoint) -> Tuple[Point, int]: def frontline_position(cls, theater: ConflictTheater, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[typing.Tuple[Point, int]]:
attack_heading = from_cp.position.heading_between_point(to_cp.position) attack_heading = from_cp.position.heading_between_point(to_cp.position)
attack_distance = from_cp.position.distance_to_point(to_cp.position) attack_distance = from_cp.position.distance_to_point(to_cp.position)
middle_point = from_cp.position.point_from_heading(attack_heading, attack_distance / 2) middle_point = from_cp.position.point_from_heading(attack_heading, attack_distance / 2)
@@ -146,7 +174,9 @@ class Conflict:
@classmethod @classmethod
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> Tuple[Point, int, int]: def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
initial, heading = cls.frontline_position(theater, from_cp, to_cp)
""" """
probe_end_point = initial.point_from_heading(heading, FRONTLINE_LENGTH) probe_end_point = initial.point_from_heading(heading, FRONTLINE_LENGTH)
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ]) probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ])
@@ -163,6 +193,9 @@ class Conflict:
return Point(*intersection.xy[0]), _heading_sum(heading, 90), intersection.length return Point(*intersection.xy[0]), _heading_sum(heading, 90), intersection.length
""" """
frontline = cls.frontline_position(theater, from_cp, to_cp) frontline = cls.frontline_position(theater, from_cp, to_cp)
if not frontline:
return None
center_position, heading = frontline center_position, heading = frontline
left_position, right_position = None, None left_position, right_position = None, None
@@ -210,7 +243,7 @@ class Conflict:
""" """
@classmethod @classmethod
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> typing.Optional[Point]:
pos = initial pos = initial
for _ in range(0, int(max_distance), 500): for _ in range(0, int(max_distance), 500):
if theater.is_on_land(pos): if theater.is_on_land(pos):
@@ -269,14 +302,10 @@ class Conflict:
distance = to_cp.size * GROUND_DISTANCE_FACTOR distance = to_cp.size * GROUND_DISTANCE_FACTOR
attackers_location = position.point_from_heading(attack_heading, distance) attackers_location = position.point_from_heading(attack_heading, distance)
attackers_location = Conflict._find_ground_position( attackers_location = Conflict._find_ground_position(attackers_location, distance * 2, _heading_sum(attack_heading, 180), theater)
attackers_location, int(distance * 2),
_heading_sum(attack_heading, 180), theater)
defenders_location = position.point_from_heading(defense_heading, distance) defenders_location = position.point_from_heading(defense_heading, distance)
defenders_location = Conflict._find_ground_position( defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
defenders_location, int(distance * 2),
_heading_sum(defense_heading, 180), theater)
return cls( return cls(
position=position, position=position,
@@ -400,7 +429,7 @@ class Conflict:
assert cls.has_frontline_between(from_cp, to_cp) assert cls.has_frontline_between(from_cp, to_cp)
position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater) position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
attack_position = position.point_from_heading(heading, random.randint(0, int(distance))) attack_position = position.point_from_heading(heading, randint(0, int(distance)))
attackers_position = attack_position.point_from_heading(heading - 90, AIR_DISTANCE) attackers_position = attack_position.point_from_heading(heading - 90, AIR_DISTANCE)
defenders_position = attack_position.point_from_heading(heading + 90, random.randint(*CAP_CAS_DISTANCE)) defenders_position = attack_position.point_from_heading(heading + 90, random.randint(*CAP_CAS_DISTANCE))
@@ -427,9 +456,7 @@ class Conflict:
distance = to_cp.size * GROUND_DISTANCE_FACTOR distance = to_cp.size * GROUND_DISTANCE_FACTOR
defenders_location = position.point_from_heading(defense_heading, distance) defenders_location = position.point_from_heading(defense_heading, distance)
defenders_location = Conflict._find_ground_position( defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
defenders_location, int(distance * 2),
_heading_sum(defense_heading, 180), theater)
return cls( return cls(
position=position, position=position,

View File

@@ -11,7 +11,8 @@ def generate_armor_group(faction:str, game, ground_object):
This generate a group of ground units This generate a group of ground units
:return: Generated group :return: Generated group
""" """
possible_unit = [u for u in db.FACTIONS[faction].frontline_units if u in Armor.__dict__.values()]
possible_unit = [u for u in db.FACTIONS[faction]["units"] if u in Armor.__dict__.values()]
if len(possible_unit) > 0: if len(possible_unit) > 0:
unit_type = random.choice(possible_unit) unit_type = random.choice(possible_unit)
return generate_armor_group_of_type(game, ground_object, unit_type) return generate_armor_group_of_type(game, ground_object, unit_type)

View File

@@ -1,36 +1,156 @@
from typing import Optional import logging
import typing
import random
from datetime import datetime, timedelta, time
from dcs.mission import Mission from dcs.mission import Mission
from dcs.triggers import *
from dcs.condition import *
from dcs.action import *
from dcs.unit import Skill
from dcs.point import MovingPoint, PointProperties
from dcs.action import *
from dcs.weather import *
from game.weather import Clouds, Fog, Conditions, WindConditions from game import db
from theater import *
from gen import *
WEATHER_CLOUD_BASE = 2000, 3000
WEATHER_CLOUD_DENSITY = 1, 8
WEATHER_CLOUD_THICKNESS = 100, 400
WEATHER_CLOUD_BASE_MIN = 1600
WEATHER_FOG_CHANCE = 20
WEATHER_FOG_VISIBILITY = 2500, 5000
WEATHER_FOG_THICKNESS = 100, 500
RANDOM_TIME = {
"night": 7,
"dusk": 40,
"dawn": 40,
"day": 100,
}
RANDOM_WEATHER = {
1: 0, # thunderstorm
2: 20, # rain
3: 80, # clouds
4: 100, # clear
}
class EnvironmentGenerator: class EnvironmentSettings:
def __init__(self, mission: Mission, conditions: Conditions) -> None: weather_dict = None
start_time = None
class EnviromentGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game):
self.mission = mission self.mission = mission
self.conditions = conditions self.conflict = conflict
self.game = game
def set_clouds(self, clouds: Optional[Clouds]) -> None: def _gen_time(self):
if clouds is None:
return
self.mission.weather.clouds_base = clouds.base
self.mission.weather.clouds_thickness = clouds.thickness
self.mission.weather.clouds_density = clouds.density
self.mission.weather.clouds_iprecptns = clouds.precipitation
def set_fog(self, fog: Optional[Fog]) -> None: start_time = self.game.current_day
if fog is None:
return
self.mission.weather.fog_visibility = fog.visibility
self.mission.weather.fog_thickness = fog.thickness
def set_wind(self, wind: WindConditions) -> None: daytime = self.game.current_turn_daytime
self.mission.weather.wind_at_ground = wind.at_0m logging.info("Mission time will be {}".format(daytime))
self.mission.weather.wind_at_2000 = wind.at_2000m if self.game.settings.night_disabled:
self.mission.weather.wind_at_8000 = wind.at_8000m logging.info("Skip Night mission due to user settings")
if daytime == "dawn":
time_range = (8, 9)
elif daytime == "day":
time_range = (10, 12)
elif daytime == "dusk":
time_range = (12, 14)
elif daytime == "night":
time_range = (14, 17)
else:
time_range = (10, 12)
else:
time_range = self.game.theater.daytime_map[daytime]
start_time += timedelta(hours=random.randint(*time_range))
logging.info("time - {}, slot - {}, night skipped - {}".format(
str(start_time),
str(time_range),
self.game.settings.night_disabled))
self.mission.start_time = start_time
def _generate_wind(self, wind_speed, wind_direction=None):
# wind
if not wind_direction:
wind_direction = random.randint(0, 360)
self.mission.weather.wind_at_ground = Wind(wind_direction, wind_speed)
self.mission.weather.wind_at_2000 = Wind(wind_direction, wind_speed * 2)
self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3)
def _generate_base_weather(self):
# clouds
self.mission.weather.clouds_base = random.randint(*WEATHER_CLOUD_BASE)
self.mission.weather.clouds_density = random.randint(*WEATHER_CLOUD_DENSITY)
self.mission.weather.clouds_thickness = random.randint(*WEATHER_CLOUD_THICKNESS)
# wind
self._generate_wind(random.randint(0, 4))
# fog
if random.randint(0, 100) < WEATHER_FOG_CHANCE:
self.mission.weather.fog_visibility = random.randint(*WEATHER_FOG_VISIBILITY)
self.mission.weather.fog_thickness = random.randint(*WEATHER_FOG_THICKNESS)
def _gen_random_weather(self):
weather_type = None
for k, v in RANDOM_WEATHER.items():
if random.randint(0, 100) <= v:
weather_type = k
break
logging.info("generated weather {}".format(weather_type))
if weather_type == 1:
# thunderstorm
self._generate_base_weather()
self._generate_wind(random.randint(0, 8))
self.mission.weather.clouds_density = random.randint(9, 10)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Thunderstorm
elif weather_type == 2:
# rain
self._generate_base_weather()
self.mission.weather.clouds_density = random.randint(5, 8)
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Rain
self._generate_wind(random.randint(0, 6))
elif weather_type == 3:
# clouds
self._generate_base_weather()
elif weather_type == 4:
# clear
pass
if self.mission.weather.clouds_density > 0:
# sometimes clouds are randomized way too low and need to be fixed
self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN)
if self.mission.weather.wind_at_ground.speed == 0:
# frontline smokes look silly w/o any wind
self._generate_wind(1)
def generate(self) -> EnvironmentSettings:
self._gen_time()
self._gen_random_weather()
settings = EnvironmentSettings()
settings.start_time = self.mission.start_time
settings.weather_dict = self.mission.weather.dict()
return settings
def load(self, settings: EnvironmentSettings):
self.mission.start_time = settings.start_time
self.mission.weather.load_from_dict(settings.weather_dict)
def generate(self):
self.mission.start_time = self.conditions.start_time
self.set_clouds(self.conditions.weather.clouds)
self.set_fog(self.conditions.weather.fog)
self.set_wind(self.conditions.weather.wind)

View File

@@ -1,26 +1,34 @@
import random import random
from gen.sam.group_generator import ShipGroupGenerator from gen.sam.group_generator import GroupGenerator
class CarrierGroupGenerator(ShipGroupGenerator): class CarrierGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(CarrierGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self): def generate(self):
# Add carrier # Add carrier
if len(self.faction.aircraft_carrier) > 0: if "aircraft_carrier" in self.faction.keys():
carrier_type = random.choice(self.faction.aircraft_carrier)
if "supercarrier" in self.faction.keys() and self.game.settings.supercarrier:
carrier_type = random.choice(self.faction["supercarrier"])
else:
carrier_type = random.choice(self.faction["aircraft_carrier"])
self.add_unit(carrier_type, "Carrier", self.position.x, self.position.y, self.heading) self.add_unit(carrier_type, "Carrier", self.position.x, self.position.y, self.heading)
else: else:
return return
# Add destroyers escort # Add destroyers escort
if len(self.faction.destroyers) > 0: if "destroyer" in self.faction.keys():
dd_type = random.choice(self.faction.destroyers) dd_type = random.choice(self.faction["destroyer"])
self.add_unit(dd_type, "DD1", self.position.x + 2500, self.position.y + 4500, self.heading) self.add_unit(dd_type, "DD1", self.position.x + 2500, self.position.y + 4500, self.heading)
self.add_unit(dd_type, "DD2", self.position.x + 2500, self.position.y - 4500, self.heading) self.add_unit(dd_type, "DD2", self.position.x + 2500, self.position.y - 4500, self.heading)
self.add_unit(dd_type, "DD3", self.position.x + 4500, self.position.y + 8500, self.heading) self.add_unit(dd_type, "DD3", self.position.x + 4500, self.position.y + 8500, self.heading)
self.add_unit(dd_type, "DD4", self.position.x + 4500, self.position.y - 8500, self.heading) self.add_unit(dd_type, "DD4", self.position.x + 4500, self.position.y - 8500, self.heading)
self.get_generated_group().points[0].speed = 20 self.get_generated_group().points[0].speed = 20

View File

@@ -1,26 +1,15 @@
from __future__ import annotations
import random import random
from typing import TYPE_CHECKING
from dcs.ships import (
Type_052C_Destroyer,
Type_052B_Destroyer,
Type_054A_Frigate,
CGN_1144_2_Pyotr_Velikiy,
)
from game.factions.faction import Faction
from gen.fleet.dd_group import DDGroupGenerator from gen.fleet.dd_group import DDGroupGenerator
from gen.sam.group_generator import ShipGroupGenerator from gen.sam.group_generator import GroupGenerator
from theater.theatergroundobject import TheaterGroundObject from dcs.ships import *
if TYPE_CHECKING:
from game.game import Game
class ChineseNavyGroupGenerator(ShipGroupGenerator): class ChineseNavyGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(ChineseNavyGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self): def generate(self):
@@ -49,5 +38,5 @@ class ChineseNavyGroupGenerator(ShipGroupGenerator):
class Type54GroupGenerator(DDGroupGenerator): class Type54GroupGenerator(DDGroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): def __init__(self, game, ground_object, faction):
super(Type54GroupGenerator, self).__init__(game, ground_object, faction, Type_054A_Frigate) super(Type54GroupGenerator, self).__init__(game, ground_object, faction, Type_054A_Frigate)

View File

@@ -1,21 +1,14 @@
from __future__ import annotations import random
from typing import TYPE_CHECKING
from game.factions.faction import Faction from gen.sam.group_generator import GroupGenerator
from theater.theatergroundobject import TheaterGroundObject from dcs.ships import *
from gen.sam.group_generator import ShipGroupGenerator
from dcs.unittype import ShipType
from dcs.ships import Oliver_Hazzard_Perry_class, USS_Arleigh_Burke_IIa
if TYPE_CHECKING:
from game.game import Game
class DDGroupGenerator(ShipGroupGenerator): class DDGroupGenerator(GroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction, ddtype: ShipType): def __init__(self, game, ground_object, faction, ddtype):
super(DDGroupGenerator, self).__init__(game, ground_object, faction) super(DDGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
self.ddtype = ddtype self.ddtype = ddtype
def generate(self): def generate(self):
@@ -25,10 +18,10 @@ class DDGroupGenerator(ShipGroupGenerator):
class OliverHazardPerryGroupGenerator(DDGroupGenerator): class OliverHazardPerryGroupGenerator(DDGroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): def __init__(self, game, ground_object, faction):
super(OliverHazardPerryGroupGenerator, self).__init__(game, ground_object, faction, Oliver_Hazzard_Perry_class) super(OliverHazardPerryGroupGenerator, self).__init__(game, ground_object, faction, Oliver_Hazzard_Perry_class)
class ArleighBurkeGroupGenerator(DDGroupGenerator): class ArleighBurkeGroupGenerator(DDGroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): def __init__(self, game, ground_object, faction):
super(ArleighBurkeGroupGenerator, self).__init__(game, ground_object, faction, USS_Arleigh_Burke_IIa) super(ArleighBurkeGroupGenerator, self).__init__(game, ground_object, faction, USS_Arleigh_Burke_IIa)

View File

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

View File

@@ -1,29 +1,15 @@
from __future__ import annotations
import random import random
from typing import TYPE_CHECKING
from dcs.ships import (
FFL_1124_4_Grisha,
FSG_1241_1MP_Molniya,
FFG_11540_Neustrashimy,
FF_1135M_Rezky,
CG_1164_Moskva,
CGN_1144_2_Pyotr_Velikiy,
SSK_877,
SSK_641B
)
from gen.fleet.dd_group import DDGroupGenerator from gen.fleet.dd_group import DDGroupGenerator
from gen.sam.group_generator import ShipGroupGenerator from gen.sam.group_generator import GroupGenerator
from game.factions.faction import Faction from dcs.ships import *
from theater.theatergroundobject import TheaterGroundObject
if TYPE_CHECKING: class RussianNavyGroupGenerator(GroupGenerator):
from game.game import Game
def __init__(self, game, ground_object, faction):
class RussianNavyGroupGenerator(ShipGroupGenerator): super(RussianNavyGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self): def generate(self):
@@ -53,20 +39,21 @@ class RussianNavyGroupGenerator(ShipGroupGenerator):
class GrishaGroupGenerator(DDGroupGenerator): class GrishaGroupGenerator(DDGroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): def __init__(self, game, ground_object, faction):
super(GrishaGroupGenerator, self).__init__(game, ground_object, faction, FFL_1124_4_Grisha) super(GrishaGroupGenerator, self).__init__(game, ground_object, faction, FFL_1124_4_Grisha)
class MolniyaGroupGenerator(DDGroupGenerator): class MolniyaGroupGenerator(DDGroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): def __init__(self, game, ground_object, faction):
super(MolniyaGroupGenerator, self).__init__(game, ground_object, faction, FSG_1241_1MP_Molniya) super(MolniyaGroupGenerator, self).__init__(game, ground_object, faction, FSG_1241_1MP_Molniya)
class KiloSubGroupGenerator(DDGroupGenerator): class KiloSubGroupGenerator(DDGroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): def __init__(self, game, ground_object, faction):
super(KiloSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_877) super(KiloSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_877)
class TangoSubGroupGenerator(DDGroupGenerator): class TangoSubGroupGenerator(DDGroupGenerator):
def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): def __init__(self, game, ground_object, faction):
super(TangoSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_641B) super(TangoSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_641B)

View File

@@ -2,14 +2,18 @@ import random
from dcs.ships import Schnellboot_type_S130 from dcs.ships import Schnellboot_type_S130
from gen.sam.group_generator import ShipGroupGenerator from gen.sam.group_generator import GroupGenerator
class SchnellbootGroupGenerator(ShipGroupGenerator): class SchnellbootGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(SchnellbootGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self): def generate(self):
for i in range(random.randint(2, 4)): for i in range(random.randint(2, 4)):
self.add_unit(Schnellboot_type_S130, "Schnellboot" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading) self.add_unit(Schnellboot_type_S130, "Schnellboot" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading)
self.get_generated_group().points[0].speed = 20 self.get_generated_group().points[0].speed = 20

View File

@@ -12,7 +12,6 @@ from gen.fleet.schnellboot import SchnellbootGroupGenerator
from gen.fleet.uboat import UBoatGroupGenerator from gen.fleet.uboat import UBoatGroupGenerator
from gen.fleet.ww2lst import WW2LSTGroupGenerator from gen.fleet.ww2lst import WW2LSTGroupGenerator
SHIP_MAP = { SHIP_MAP = {
"SchnellbootGroupGenerator": SchnellbootGroupGenerator, "SchnellbootGroupGenerator": SchnellbootGroupGenerator,
"WW2LSTGroupGenerator": WW2LSTGroupGenerator, "WW2LSTGroupGenerator": WW2LSTGroupGenerator,
@@ -29,24 +28,26 @@ SHIP_MAP = {
} }
def generate_ship_group(game, ground_object, faction_name: str): def generate_ship_group(game, ground_object, faction:str):
""" """
This generate a ship group This generate a ship group
:return: Nothing, but put the group reference inside the ground object :return: Nothing, but put the group reference inside the ground object
""" """
faction = db.FACTIONS[faction_name] faction = db.FACTIONS[faction]
if len(faction.navy_generators) > 0: if "boat" in faction.keys():
gen = random.choice(faction.navy_generators) generators = faction["boat"]
if gen in SHIP_MAP.keys(): if len(generators) > 0:
generator = SHIP_MAP[gen](game, ground_object, faction) gen = random.choice(generators)
generator.generate() if gen in SHIP_MAP.keys():
return generator.get_generated_group() generator = SHIP_MAP[gen](game, ground_object, faction)
else: generator.generate()
logging.info("Unable to generate ship group, generator : " + str(gen) + "does not exists") return generator.get_generated_group()
else:
logging.info("Unable to generate ship group, generator : " + str(gen) + "does not exists")
return None return None
def generate_carrier_group(faction: str, game, ground_object): def generate_carrier_group(faction:str, game, ground_object):
""" """
This generate a carrier group This generate a carrier group
:param parentCp: The parent control point :param parentCp: The parent control point
@@ -59,7 +60,7 @@ def generate_carrier_group(faction: str, game, ground_object):
return generator.get_generated_group() return generator.get_generated_group()
def generate_lha_group(faction: str, game, ground_object): def generate_lha_group(faction:str, game, ground_object):
""" """
This generate a lha carrier group This generate a lha carrier group
:param parentCp: The parent control point :param parentCp: The parent control point
@@ -69,4 +70,4 @@ def generate_lha_group(faction: str, game, ground_object):
""" """
generator = LHAGroupGenerator(game, ground_object, db.FACTIONS[faction]) generator = LHAGroupGenerator(game, ground_object, db.FACTIONS[faction])
generator.generate() generator.generate()
return generator.get_generated_group() return generator.get_generated_group()

View File

@@ -2,10 +2,14 @@ import random
from dcs.ships import Uboat_VIIC_U_flak from dcs.ships import Uboat_VIIC_U_flak
from gen.sam.group_generator import ShipGroupGenerator from gen.sam.group_generator import GroupGenerator
class UBoatGroupGenerator(ShipGroupGenerator): class UBoatGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(UBoatGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self): def generate(self):

View File

@@ -2,10 +2,14 @@ import random
from dcs.ships import LS_Samuel_Chase, LST_Mk_II from dcs.ships import LS_Samuel_Chase, LST_Mk_II
from gen.sam.group_generator import ShipGroupGenerator from gen.sam.group_generator import GroupGenerator
class WW2LSTGroupGenerator(ShipGroupGenerator): class WW2LSTGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(WW2LSTGroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self): def generate(self):

File diff suppressed because it is too large Load Diff

View File

@@ -1,84 +1,5 @@
from dcs.helicopters import ( from dcs.planes import *
AH_1W, from dcs.helicopters import *
AH_64A,
AH_64D,
Ka_50,
Mi_24V,
Mi_28N,
Mi_8MT,
OH_58D,
SA342L,
SA342M,
UH_1H,
)
from dcs.planes import (
AJS37,
AV8BNA,
A_10A,
A_10C,
A_10C_2,
A_20G,
B_17G,
B_1B,
B_52H,
Bf_109K_4,
C_101CC,
FA_18C_hornet,
FW_190A8,
FW_190D9,
F_117A,
F_14B,
F_15C,
F_15E,
F_16A,
F_16C_50,
F_4E,
F_5E_3,
F_86F_Sabre,
F_A_18C,
JF_17,
J_11A,
Ju_88A4,
L_39ZA,
MQ_9_Reaper,
M_2000C,
MiG_15bis,
MiG_19P,
MiG_21Bis,
MiG_23MLD,
MiG_25PD,
MiG_27K,
MiG_29A,
MiG_29G,
MiG_29K,
MiG_29S,
MiG_31,
Mirage_2000_5,
P_47D_30,
P_47D_30bl1,
P_47D_40,
P_51D,
P_51D_30_NA,
RQ_1A_Predator,
SpitfireLFMkIX,
SpitfireLFMkIXCW,
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_25TM,
Su_27,
Su_30,
Su_33,
Su_34,
Tornado_GR4,
Tornado_IDS,
Tu_160,
Tu_22M3,
Tu_95MS,
WingLoong_I,
)
# Interceptor are the aircraft prioritized for interception tasks # Interceptor are the aircraft prioritized for interception tasks
# If none is available, the AI will use regular CAP-capable aircraft instead # If none is available, the AI will use regular CAP-capable aircraft instead
@@ -86,11 +7,6 @@ from pydcs_extensions.a4ec.a4ec import A_4E_C
from pydcs_extensions.mb339.mb339 import MB_339PAN from pydcs_extensions.mb339.mb339 import MB_339PAN
from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M from pydcs_extensions.rafale.rafale import Rafale_A_S, Rafale_M
# TODO: These lists really ought to be era (faction) dependent.
# Factions which have F-5s, F-86s, and A-4s will should prefer F-5s for CAP, but
# factions that also have F-4s should not.
from pydcs_extensions.su57.su57 import Su_57
INTERCEPT_CAPABLE = [ INTERCEPT_CAPABLE = [
MiG_21Bis, MiG_21Bis,
MiG_25PD, MiG_25PD,
@@ -127,7 +43,6 @@ CAP_CAPABLE = [
JF_17, JF_17,
Su_30, Su_30,
Su_33, Su_33,
Su_57,
M_2000C, M_2000C,
Mirage_2000_5, Mirage_2000_5,
@@ -162,43 +77,6 @@ CAP_CAPABLE = [
Rafale_M, Rafale_M,
] ]
CAP_PREFERRED = [
MiG_15bis,
MiG_19P,
MiG_21Bis,
MiG_23MLD,
MiG_25PD,
MiG_29A,
MiG_29G,
MiG_29S,
MiG_31,
Su_27,
J_11A,
Su_30,
Su_33,
Su_57,
M_2000C,
Mirage_2000_5,
F_86F_Sabre,
F_14B,
F_15C,
P_51D_30_NA,
P_51D,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
Rafale_M,
]
# Used for CAS (Close air support) and BAI (Battlefield Interdiction) # Used for CAS (Close air support) and BAI (Battlefield Interdiction)
CAS_CAPABLE = [ CAS_CAPABLE = [
@@ -232,8 +110,6 @@ CAS_CAPABLE = [
F_16C_50, F_16C_50,
FA_18C_hornet, FA_18C_hornet,
B_1B,
Tornado_IDS, Tornado_IDS,
Tornado_GR4, Tornado_GR4,
@@ -279,65 +155,12 @@ CAS_CAPABLE = [
RQ_1A_Predator RQ_1A_Predator
] ]
CAS_PREFERRED = [
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_25TM,
Su_34,
JF_17,
A_10A,
A_10C,
A_10C_2,
AV8BNA,
F_15E,
Tornado_GR4,
C_101CC,
MB_339PAN,
L_39ZA,
AJS37,
SA342M,
SA342L,
OH_58D,
AH_64A,
AH_64D,
AH_1W,
UH_1H,
Mi_8MT,
Mi_28N,
Mi_24V,
Ka_50,
P_47D_30,
P_47D_30bl1,
P_47D_40,
A_20G,
A_4E_C,
Rafale_A_S,
WingLoong_I,
MQ_9_Reaper,
RQ_1A_Predator
]
# Aircraft used for SEAD / DEAD tasks # Aircraft used for SEAD / DEAD tasks
SEAD_CAPABLE = [ SEAD_CAPABLE = [
F_4E, F_4E,
FA_18C_hornet, FA_18C_hornet,
F_15E, F_15E,
F_16C_50, # F_16C_50, Not yet
AV8BNA, AV8BNA,
JF_17, JF_17,
@@ -356,12 +179,6 @@ SEAD_CAPABLE = [
Rafale_A_S Rafale_A_S
] ]
SEAD_PREFERRED = [
F_4E,
Su_25T,
Tornado_IDS,
]
# Aircraft used for Strike mission # Aircraft used for Strike mission
STRIKE_CAPABLE = [ STRIKE_CAPABLE = [
MiG_15bis, MiG_15bis,
@@ -375,10 +192,6 @@ STRIKE_CAPABLE = [
Su_25T, Su_25T,
Su_34, Su_34,
Tu_160,
Tu_22M3,
Tu_95MS,
JF_17, JF_17,
M_2000C, M_2000C,
@@ -396,10 +209,6 @@ STRIKE_CAPABLE = [
F_16C_50, F_16C_50,
FA_18C_hornet, FA_18C_hornet,
B_1B,
B_52H,
F_117A,
Tornado_IDS, Tornado_IDS,
Tornado_GR4, Tornado_GR4,
@@ -427,20 +236,6 @@ STRIKE_CAPABLE = [
] ]
STRIKE_PREFERRED = [
AJS37,
A_20G,
B_17G,
B_1B,
B_52H,
F_117A,
F_15E,
Tornado_GR4,
Tu_160,
Tu_22M3,
Tu_95MS,
]
ANTISHIP_CAPABLE = [ ANTISHIP_CAPABLE = [
Su_24M, Su_24M,
Su_17M4, Su_17M4,

View File

@@ -1,51 +0,0 @@
"""Objective adjacency lists."""
from typing import Dict, Iterator, List, Optional
from theater import ConflictTheater, ControlPoint, MissionTarget
class ClosestAirfields:
"""Precalculates which control points are closes to the given target."""
def __init__(self, target: MissionTarget,
all_control_points: List[ControlPoint]) -> None:
self.target = target
self.closest_airfields: List[ControlPoint] = sorted(
all_control_points, key=lambda c: self.target.distance_to(c)
)
def airfields_within(self, meters: int) -> Iterator[ControlPoint]:
"""Iterates over all airfields within the given range of the target.
Note that this iterates over *all* airfields, not just friendly
airfields.
"""
for cp in self.closest_airfields:
if cp.distance_to(self.target) < meters:
yield cp
else:
break
class ObjectiveDistanceCache:
theater: Optional[ConflictTheater] = None
closest_airfields: Dict[str, ClosestAirfields] = {}
@classmethod
def set_theater(cls, theater: ConflictTheater) -> None:
if cls.theater is not None:
cls.closest_airfields = {}
cls.theater = theater
@classmethod
def get_closest_airfields(cls, location: MissionTarget) -> ClosestAirfields:
if cls.theater is None:
raise RuntimeError(
"Call ObjectiveDistanceCache.set_theater before using"
)
if location.name not in cls.closest_airfields:
cls.closest_airfields[location.name] = ClosestAirfields(
location, cls.theater.controlpoints
)
return cls.closest_airfields[location.name]

View File

@@ -1,23 +1,14 @@
from __future__ import annotations
from datetime import timedelta
from enum import Enum from enum import Enum
from typing import Dict, List, Optional, TYPE_CHECKING from typing import List
from dcs.mapping import Point
from dcs.point import MovingPoint, PointAction
from dcs.unittype import FlyingType
from game import db from game import db
from theater.controlpoint import ControlPoint, MissionTarget from dcs.unittype import UnitType
from dcs.point import MovingPoint, PointAction
if TYPE_CHECKING: from theater.controlpoint import ControlPoint
from gen.ato import Package
from gen.flights.flightplan import FlightPlan
class FlightType(Enum): class FlightType(Enum):
CAP = 0 # Do not use. Use BARCAP or TARCAP. CAP = 0
TARCAP = 1 TARCAP = 1
BARCAP = 2 BARCAP = 2
CAS = 3 CAS = 3
@@ -56,26 +47,23 @@ class FlightWaypointType(Enum):
TARGET_GROUP_LOC = 13 # A target group approximate location TARGET_GROUP_LOC = 13 # A target group approximate location
TARGET_SHIP = 14 # A target ship known location TARGET_SHIP = 14 # A target ship known location
CUSTOM = 15 # User waypoint (no specific behaviour) CUSTOM = 15 # User waypoint (no specific behaviour)
JOIN = 16
SPLIT = 17
LOITER = 18 class PredefinedWaypointCategory(Enum):
INGRESS_ESCORT = 19 NOT_PREDEFINED = 0
INGRESS_DEAD = 20 ALLY_CP = 1
ENEMY_CP = 2
FRONTLINE = 3
ENEMY_BUILDING = 4
ENEMY_UNIT = 5
ALLY_BUILDING = 6
ALLY_UNIT = 7
class FlightWaypoint: class FlightWaypoint:
def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float, def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float,
alt: int = 0) -> None: alt: int = 0) -> None:
"""Creates a flight waypoint.
Args:
waypoint_type: The waypoint type.
x: X cooidinate of the waypoint.
y: Y coordinate of the waypoint.
alt: Altitude of the waypoint. By default this is AGL, but it can be
changed to MSL by setting alt_type to "RADIO".
"""
self.waypoint_type = waypoint_type self.waypoint_type = waypoint_type
self.x = x self.x = x
self.y = y self.y = y
@@ -83,27 +71,20 @@ class FlightWaypoint:
self.alt_type = "BARO" self.alt_type = "BARO"
self.name = "" self.name = ""
self.description = "" self.description = ""
self.targets: List[MissionTarget] = [] self.targets = []
self.targetGroup = None
self.obj_name = "" self.obj_name = ""
self.pretty_name = "" self.pretty_name = ""
self.category: PredefinedWaypointCategory = PredefinedWaypointCategory.NOT_PREDEFINED
self.only_for_player = False self.only_for_player = False
self.data = None
# These are set very late by the air conflict generator (part of mission
# generation). We do it late so that we don't need to propagate changes
# to waypoint times whenever the player alters the package TOT or the
# flight's offset in the UI.
self.tot: Optional[timedelta] = None
self.departure_time: Optional[timedelta] = None
@property
def position(self) -> Point:
return Point(self.x, self.y)
@classmethod @classmethod
def from_pydcs(cls, point: MovingPoint, def from_pydcs(cls, point: MovingPoint,
from_cp: ControlPoint) -> "FlightWaypoint": from_cp: ControlPoint) -> "FlightWaypoint":
waypoint = FlightWaypoint(FlightWaypointType.NAV, point.position.x, waypoint = FlightWaypoint(point.position.x, point.position.y,
point.position.y, point.alt) point.alt)
waypoint.alt_type = point.alt_type waypoint.alt_type = point.alt_type
# Other actions exist... but none of them *should* be the first # Other actions exist... but none of them *should* be the first
# waypoint for a flight. # waypoint for a flight.
@@ -127,36 +108,43 @@ class FlightWaypoint:
class Flight: class Flight:
unit_type: UnitType = None
from_cp = None
points: List[FlightWaypoint] = []
flight_type: FlightType = None
count: int = 0
client_count: int = 0
targets = []
use_custom_loadout = False
loadout = {}
preset_loadout_name = ""
start_type = "Runway"
group = False # Contains DCS Mission group data after mission has been generated
def __init__(self, package: Package, unit_type: FlyingType, count: int, # How long before this flight should take off
from_cp: ControlPoint, flight_type: FlightType, scheduled_in = 0
start_type: str) -> None:
self.package = package def __init__(self, unit_type: UnitType, count: int, from_cp, flight_type: FlightType):
self.unit_type = unit_type self.unit_type = unit_type
self.count = count self.count = count
self.from_cp = from_cp self.from_cp = from_cp
self.flight_type = flight_type self.flight_type = flight_type
# TODO: Replace with FlightPlan. self.points = []
self.targets: List[MissionTarget] = [] self.targets = []
self.loadout: Dict[str, str] = {} self.loadout = {}
self.start_type = start_type self.start_type = "Runway"
self.use_custom_loadout = False
self.client_count = 0
# Will be replaced with a more appropriate FlightPlan by
# FlightPlanBuilder, but an empty flight plan the flight begins with an
# empty flight plan.
from gen.flights.flightplan import CustomFlightPlan
self.flight_plan: FlightPlan = CustomFlightPlan(
package=package,
flight=self,
custom_waypoints=[]
)
@property
def points(self) -> List[FlightWaypoint]:
return self.flight_plan.waypoints[1:]
def __repr__(self): def __repr__(self):
return self.flight_type.name + " | " + str(self.count) + "x" + db.unit_type_name(self.unit_type) \ return self.flight_type.name + " | " + str(self.count) + "x" + db.unit_type_name(self.unit_type) \
+ " (" + str(len(self.points)) + " wpt)" + " in " + str(self.scheduled_in) + " minutes (" + str(len(self.points)) + " wpt)"
# Test
if __name__ == '__main__':
from pydcs.dcs.planes import A_10C
from theater import ControlPoint, Point, List
from_cp = ControlPoint(0, "AA", Point(0, 0), None, [], 0, 0)
f = Flight(A_10C(), 4, from_cp, FlightType.CAS)
f.scheduled_in = 50
print(f)

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