diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..e74d8c24 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Black +a47bef1f1336fd264d0b175f4421758339a30acb \ No newline at end of file diff --git a/.github/workflows/black.yml b/.github/workflows/black.yml new file mode 100644 index 00000000..994337fa --- /dev/null +++ b/.github/workflows/black.yml @@ -0,0 +1,13 @@ +name: Lint + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - uses: psf/black@stable + with: + black_args: ". --check" \ No newline at end of file diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..14d8171c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,6 @@ +repos: + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + language_version: python3 \ No newline at end of file diff --git a/game/data/aaa_db.py b/game/data/aaa_db.py index 1aa7d597..d7e7d101 100644 --- a/game/data/aaa_db.py +++ b/game/data/aaa_db.py @@ -17,5 +17,5 @@ AAA_UNITS = [ AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_Kdo_G_40, AirDefence.AAA_8_8cm_Flak_41, - AirDefence.AAA_Bofors_40mm -] \ No newline at end of file + AirDefence.AAA_Bofors_40mm, +] diff --git a/game/data/building_data.py b/game/data/building_data.py index ae4e2236..5331ebbd 100644 --- a/game/data/building_data.py +++ b/game/data/building_data.py @@ -1,17 +1,70 @@ import inspect 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", +] -WW2_FREE = ['fuel', 'factory', 'ware', 'fob'] -WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp', 'fob'] -WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'fob'] +WW2_FREE = ["fuel", "factory", "ware", "fob"] +WW2_GERMANY_BUILDINGS = [ + "fuel", + "factory", + "ww2bunker", + "ww2bunker", + "ww2bunker", + "allycamp", + "allycamp", + "fob", +] +WW2_ALLIES_BUILDINGS = [ + "fuel", + "factory", + "allycamp", + "allycamp", + "allycamp", + "allycamp", + "allycamp", + "fob", +] -FORTIFICATION_BUILDINGS = ['Siegfried Line', 'Concertina wire', 'Concertina Wire', 'Czech hedgehogs 1', 'Czech hedgehogs 2', - 'Dragonteeth 1', 'Dragonteeth 2', 'Dragonteeth 3', 'Dragonteeth 4', 'Dragonteeth 5', - 'Haystack 1', 'Haystack 2', 'Haystack 3', 'Haystack 4', 'Hemmkurvenvenhindernis', - 'Log posts 1', 'Log posts 2', 'Log posts 3', 'Log ramps 1', 'Log ramps 2', 'Log ramps 3', - 'Belgian Gate', 'Container white'] +FORTIFICATION_BUILDINGS = [ + "Siegfried Line", + "Concertina wire", + "Concertina Wire", + "Czech hedgehogs 1", + "Czech hedgehogs 2", + "Dragonteeth 1", + "Dragonteeth 2", + "Dragonteeth 3", + "Dragonteeth 4", + "Dragonteeth 5", + "Haystack 1", + "Haystack 2", + "Haystack 3", + "Haystack 4", + "Hemmkurvenvenhindernis", + "Log posts 1", + "Log posts 2", + "Log posts 3", + "Log ramps 1", + "Log ramps 2", + "Log ramps 3", + "Belgian Gate", + "Container white", +] -FORTIFICATION_UNITS = [c for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)] -FORTIFICATION_UNITS_ID = [c.id for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)] \ No newline at end of file +FORTIFICATION_UNITS = [ + c for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c) +] +FORTIFICATION_UNITS_ID = [ + c.id for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c) +] diff --git a/game/data/cap_capabilities_db.py b/game/data/cap_capabilities_db.py index 1ee3f075..160faede 100644 --- a/game/data/cap_capabilities_db.py +++ b/game/data/cap_capabilities_db.py @@ -16,7 +16,7 @@ from dcs.planes import ( P_51D, P_51D_30_NA, SpitfireLFMkIX, - SpitfireLFMkIXCW + SpitfireLFMkIXCW, ) from pydcs_extensions.a4ec.a4ec import A_4E_C @@ -26,7 +26,6 @@ This list contains the aircraft that do not use the guns as the last resort weap They'll RTB when they don't have gun ammo left """ GUNFIGHTERS = [ - # Cold War MiG_15bis, MiG_19P, @@ -34,11 +33,9 @@ GUNFIGHTERS = [ F_86F_Sabre, A_4E_C, F_5E_3, - # Trainers C_101CC, L_39ZA, - # WW2 P_51D_30_NA, P_51D, @@ -51,5 +48,4 @@ GUNFIGHTERS = [ FW_190D9, FW_190A8, I_16, - -] \ No newline at end of file +] diff --git a/game/data/radar_db.py b/game/data/radar_db.py index 4e90d56c..10e38f42 100644 --- a/game/data/radar_db.py +++ b/game/data/radar_db.py @@ -23,7 +23,6 @@ from dcs.ships import ( from dcs.vehicles import AirDefence UNITS_WITH_RADAR = [ - # Radars AirDefence.SAM_SA_15_Tor_9A331, AirDefence.SAM_SA_11_Buk_CC_9S470M1, @@ -49,7 +48,6 @@ UNITS_WITH_RADAR = [ AirDefence.SAM_SA_3_S_125_TR_SNR, AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song, AirDefence.HQ_7_Self_Propelled_STR, - # Ships CVN_70_Carl_Vinson, Oliver_Hazzard_Perry_class, @@ -70,5 +68,5 @@ UNITS_WITH_RADAR = [ LHA_1_Tarawa, Type_052B_Destroyer, Type_054A_Frigate, - Type_052C_Destroyer -] \ No newline at end of file + Type_052C_Destroyer, +] diff --git a/game/data/weapons.py b/game/data/weapons.py index acbd9456..b42112fa 100644 --- a/game/data/weapons.py +++ b/game/data/weapons.py @@ -28,7 +28,8 @@ class Weapon: introduction_year = WEAPON_INTRODUCTION_YEARS.get(self) if introduction_year is None: logging.warning( - f"No introduction year for {self}, assuming always available") + f"No introduction year for {self}, assuming always available" + ) return True return date >= datetime.date(introduction_year, 1, 1) @@ -52,7 +53,7 @@ class Weapon: return cls( cast(str, weapon_data["clsid"]), cast(str, weapon_data["name"]), - cast(int, weapon_data["weight"]) + cast(int, weapon_data["weight"]), ) @classmethod @@ -88,8 +89,11 @@ class Pylon: def for_aircraft(cls, aircraft: Type[FlyingType], number: int) -> Pylon: # In pydcs these are all arbitrary inner classes of the aircraft type. # The only way to identify them is by their name. - pylons = [v for v in aircraft.__dict__.values() if - inspect.isclass(v) and v.__name__.startswith("Pylon")] + pylons = [ + v + for v in aircraft.__dict__.values() + if inspect.isclass(v) and v.__name__.startswith("Pylon") + ] # And that Pylon class has members with irrelevant names that have # values of (pylon number, allowed weapon). @@ -117,112 +121,92 @@ _WEAPON_FALLBACKS = [ (Weapons.ADM_141A_, None), (Weapons.ADM_141A__, None), (Weapons.ADM_141B, None), - # AGM-114K Hellfire - (Weapons.AGM114x2_OH_58, Weapons.M260_HYDRA), # assuming OH-58 and not MQ-9 - (Weapons.AGM_114K, None), # Only for RQ-1 + (Weapons.AGM114x2_OH_58, Weapons.M260_HYDRA), # assuming OH-58 and not MQ-9 + (Weapons.AGM_114K, None), # Only for RQ-1 (Weapons.AGM_114K___4, Weapons.LAU_61___19_2_75__rockets_MK151_HE), - # AGM-119 Penguin (Weapons.AGM_119B_Penguin, Weapons.Mk_82), - # AGM-122 Sidearm - (Weapons.AGM_122, None), # No known aircraft carries this - (Weapons.AGM_122_Sidearm, Weapons.GBU_12), # outer pylons harrier - (Weapons.AGM_122_Sidearm_, Weapons.LAU_117_AGM_65E), # internal pylons harrier - + (Weapons.AGM_122, None), # No known aircraft carries this + (Weapons.AGM_122_Sidearm, Weapons.GBU_12), # outer pylons harrier + (Weapons.AGM_122_Sidearm_, Weapons.LAU_117_AGM_65E), # internal pylons harrier # AGM-154 JSOW (Weapons.AGM_154A, Weapons.GBU_12), (Weapons.BRU_55___2_x_AGM_154A, Weapons.BRU_33___2_x_GBU_12), - (Weapons.BRU_57___2_x_AGM_154A, None), # doesn't exist on any aircraft yet - + (Weapons.BRU_57___2_x_AGM_154A, None), # doesn't exist on any aircraft yet (Weapons.AGM_154B, Weapons.CBU_105), - (Weapons.AGM_154C, Weapons.GBU_12), (Weapons.AGM_154C_4, Weapons.GBU_31_8), (Weapons.BRU_55___2_x_AGM_154C, Weapons.BRU_33___2_x_GBU_12), - # AGM-45 Shrike (Weapons.AGM_45A, None), (Weapons.AGM_45B, Weapons.AGM_45A), (Weapons.AGM_45B_, Weapons.AGM_45A), - # AGM-62 Walleye (Weapons.AGM_62, Weapons.Mk_84), - # AGM-65 Maverick - (Weapons.AGM_65D, None), # doesn't exist - (Weapons.AGM_65E, None), # doesn't exist - (Weapons.AGM_65K, None), # doesn't exist - (Weapons.LAU_117_AGM_65A, None), # doesn't exist - (Weapons.LAU_117_AGM_65B, None), # doesn't exist - - (Weapons.LAU_117_AGM_65D, Weapons.AGM_62), # Walleye is the predecessor to the maverick + (Weapons.AGM_65D, None), # doesn't exist + (Weapons.AGM_65E, None), # doesn't exist + (Weapons.AGM_65K, None), # doesn't exist + (Weapons.LAU_117_AGM_65A, None), # doesn't exist + (Weapons.LAU_117_AGM_65B, None), # doesn't exist + ( + Weapons.LAU_117_AGM_65D, + Weapons.AGM_62, + ), # Walleye is the predecessor to the maverick (Weapons.LAU_117_AGM_65E, None), (Weapons.LAU_117_AGM_65F, Weapons.LAU_117_AGM_65D), (Weapons.LAU_117_AGM_65G, Weapons.LAU_117_AGM_65D), (Weapons.LAU_117_AGM_65H, Weapons.LAU_117_AGM_65D), (Weapons.LAU_117_AGM_65K, Weapons.LAU_117_AGM_65D), (Weapons.LAU_117_AGM_65L, None), - (Weapons.LAU_88_AGM_65D_2, None), (Weapons.LAU_88_AGM_65D_2_, None), (Weapons.LAU_88_AGM_65D_3, None), (Weapons.LAU_88_AGM_65D_ONE, None), - (Weapons.LAU_88_AGM_65E_2, Weapons.LAU_88_AGM_65D_2), (Weapons.LAU_88_AGM_65E_2_, Weapons.LAU_88_AGM_65D_2_), (Weapons.LAU_88_AGM_65E_3, Weapons.LAU_88_AGM_65D_3), - (Weapons.LAU_88_AGM_65H, Weapons.LAU_88_AGM_65D_2), (Weapons.LAU_88_AGM_65H_2_L, Weapons.LAU_88_AGM_65D_2_), (Weapons.LAU_88_AGM_65H_2_R, Weapons.LAU_88_AGM_65D_2_), (Weapons.LAU_88_AGM_65H_3, Weapons.LAU_88_AGM_65D_3), - (Weapons.LAU_88_AGM_65K_2, Weapons.LAU_88_AGM_65D_2), (Weapons.LAU_88_AGM_65K_2_, Weapons.LAU_88_AGM_65D_2_), (Weapons.LAU_88_AGM_65K_3, Weapons.LAU_88_AGM_65D_3), - # AGM-84 Harpoon - (Weapons.AGM_84, None), # doesn't exist + (Weapons.AGM_84, None), # doesn't exist (Weapons.AGM_84A, Weapons.Mk_82), (Weapons.AGM_84A_8, Weapons._27_Mk_82), (Weapons.AGM_84D, Weapons.AGM_62), (Weapons.AGM_84E, Weapons.LAU_117_AGM_65F), (Weapons.AGM_84H, Weapons.AGM_84E), - # AGM-86 ALCM (Weapons.AGM_86C, Weapons._27_Mk_82), (Weapons.AGM_86C_20, Weapons._27_Mk_82), (Weapons.AGM_86C_8, Weapons._27_Mk_82), (Weapons.MER_6_AGM_86C, Weapons.MER_12_Mk_82), - # AGM-88 HARM (Weapons.AGM_88C, Weapons.AGM_65D), (Weapons.AGM_88C_, Weapons.AGM_65D), - - # AIM-120 AMRAAM + # AIM-120 AMRAAM (Weapons.AIM_120B, Weapons.AIM_7MH), (Weapons.LAU_115___AIM_120B, Weapons.LAU_115C_AIM_7MH), (Weapons.LAU_115_2_LAU_127_AIM_120B, Weapons.LAU_115C_AIM_7MH), - (Weapons.AIM_120C, Weapons.AIM_120B), (Weapons.LAU_115___AIM_120C, Weapons.LAU_115___AIM_120B), (Weapons.LAU_115_2_LAU_127_AIM_120C, Weapons.LAU_115_2_LAU_127_AIM_120B), - # AIM-54 Phoenix (Weapons.AIM_54A_Mk47, None), (Weapons.AIM_54A_Mk47_, None), (Weapons.AIM_54A_Mk47__, None), - (Weapons.AIM_54A_Mk60, Weapons.AIM_54A_Mk47), (Weapons.AIM_54A_Mk60_, Weapons.AIM_54A_Mk47_), (Weapons.AIM_54A_Mk60__, Weapons.AIM_54A_Mk47__), - (Weapons.AIM_54C_Mk47, Weapons.AIM_54A_Mk60), (Weapons.AIM_54C_Mk47_, Weapons.AIM_54A_Mk60_), (Weapons.AIM_54C_Mk47__, Weapons.AIM_54A_Mk60__), - # AIM-7 Sparrow (Weapons.AIM_7E, None), (Weapons.AIM_7F, Weapons.AIM_7E), @@ -234,62 +218,61 @@ _WEAPON_FALLBACKS = [ (Weapons.AIM_7MH, Weapons.AIM_7M), (Weapons.AIM_7MH_, Weapons.AIM_7M_), (Weapons.AIM_7MH__, Weapons.AIM_7M__), - (Weapons.LAU_115C_AIM_7E, None), (Weapons.LAU_115C_AIM_7F, Weapons.LAU_115C_AIM_7E), (Weapons.LAU_115___AIM_7M, Weapons.LAU_115C_AIM_7F), (Weapons.LAU_115C_AIM_7MH, Weapons.LAU_115___AIM_7M), - # AIM-9 Sidewinder (Weapons.AIM_9L_Sidewinder_IR_AAM, None), (Weapons.AIM_9M_Sidewinder_IR_AAM, Weapons.AIM_9P5_Sidewinder_IR_AAM), (Weapons.AIM_9P5_Sidewinder_IR_AAM, Weapons.AIM_9P_Sidewinder_IR_AAM), (Weapons.AIM_9P_Sidewinder_IR_AAM, Weapons.AIM_9L_Sidewinder_IR_AAM), (Weapons.AIM_9X_Sidewinder_IR_AAM, Weapons.AIM_9P_Sidewinder_IR_AAM), - (Weapons.LAU_105_1_AIM_9L_L, None), (Weapons.LAU_105_1_AIM_9L_R, None), (Weapons.LAU_105_1_AIM_9M_L, Weapons.LAU_105_1_AIM_9L_L), (Weapons.LAU_105_1_AIM_9M_R, Weapons.LAU_105_1_AIM_9L_R), - (Weapons.LAU_105_2_AIM_9L, None), (Weapons.LAU_105_2_AIM_9P5, Weapons.LAU_105___2_AIM_9P_Sidewinder_IR_AAM), - (Weapons.LAU_105___2_AIM_9M_Sidewinder_IR_AAM, Weapons.LAU_105_2_AIM_9L), - (Weapons.LAU_105___2_AIM_9P_Sidewinder_IR_AAM, Weapons.LAU_105___2_AIM_9M_Sidewinder_IR_AAM), - + ( + Weapons.LAU_105___2_AIM_9P_Sidewinder_IR_AAM, + Weapons.LAU_105___2_AIM_9M_Sidewinder_IR_AAM, + ), (Weapons.LAU_115_2_LAU_127_AIM_9L, None), (Weapons.LAU_115_2_LAU_127_AIM_9M, Weapons.LAU_115_2_LAU_127_AIM_9L), (Weapons.LAU_115_2_LAU_127_AIM_9X, Weapons.LAU_115_2_LAU_127_AIM_9M), - (Weapons.LAU_115_LAU_127_AIM_9L, None), (Weapons.LAU_115_LAU_127_AIM_9M, Weapons.LAU_115_LAU_127_AIM_9L), (Weapons.LAU_115_LAU_127_AIM_9X, Weapons.LAU_115_LAU_127_AIM_9M), - (Weapons.LAU_127_AIM_9L, None), (Weapons.LAU_127_AIM_9M, Weapons.LAU_127_AIM_9L), (Weapons.LAU_127_AIM_9X, Weapons.LAU_127_AIM_9M), - (Weapons.LAU_138_AIM_9L, None), (Weapons.LAU_138_AIM_9M, Weapons.LAU_138_AIM_9L), - (Weapons.LAU_7_AIM_9L, None), (Weapons.LAU_7_AIM_9M, Weapons.LAU_7_AIM_9L), (Weapons.LAU_7_AIM_9M_Sidewinder_IR_AAM, Weapons.LAU_7_AIM_9P5_Sidewinder_IR_AAM), (Weapons.LAU_7_AIM_9P5_Sidewinder_IR_AAM, Weapons.LAU_7_AIM_9P_Sidewinder_IR_AAM), (Weapons.LAU_7_AIM_9P_Sidewinder_IR_AAM, Weapons.LAU_7_AIM_9L), (Weapons.LAU_7_AIM_9X_Sidewinder_IR_AAM, Weapons.LAU_7_AIM_9M_Sidewinder_IR_AAM), - (Weapons.LAU_7___2_AIM_9L_Sidewinder_IR_AAM, None), - (Weapons.LAU_7___2_AIM_9M_Sidewinder_IR_AAM, Weapons.LAU_7___2_AIM_9P5_Sidewinder_IR_AAM), - (Weapons.LAU_7___2_AIM_9P5_Sidewinder_IR_AAM, Weapons.LAU_7___2_AIM_9P_Sidewinder_IR_AAM), - (Weapons.LAU_7___2_AIM_9P_Sidewinder_IR_AAM, Weapons.LAU_7___2_AIM_9L_Sidewinder_IR_AAM), - + ( + Weapons.LAU_7___2_AIM_9M_Sidewinder_IR_AAM, + Weapons.LAU_7___2_AIM_9P5_Sidewinder_IR_AAM, + ), + ( + Weapons.LAU_7___2_AIM_9P5_Sidewinder_IR_AAM, + Weapons.LAU_7___2_AIM_9P_Sidewinder_IR_AAM, + ), + ( + Weapons.LAU_7___2_AIM_9P_Sidewinder_IR_AAM, + Weapons.LAU_7___2_AIM_9L_Sidewinder_IR_AAM, + ), # ALQ ECM Pods (Weapons.ALQ_131, None), (Weapons.ALQ_184, Weapons.ALQ_131), (Weapons.AN_ALQ_164_DECM_Pod, None), - # TGP Pods (Weapons.AN_AAQ_28_LITENING_, None), (Weapons.AN_AAQ_28_LITENING, Weapons.Lantirn_F_16), @@ -300,17 +283,14 @@ _WEAPON_FALLBACKS = [ (Weapons.Lantirn_F_16, None), (Weapons.Lantirn_Target_Pod, None), (Weapons.Pavetack_F_111, None), - # BLU-107 (Weapons.BLU_107, None), (Weapons.MER_6_BLU_107, Weapons.MER_6_Mk_82), - # GBU-10 LGB (Weapons.DIS_GBU_10, Weapons.Mk_84), (Weapons.GBU_10, Weapons.Mk_84), (Weapons.GBU_10_, Weapons.Mk_84), (Weapons.GBU_10_2, Weapons.Mk_84), - # GBU-12 LGB (Weapons.AUF2_GBU_12_x_2, None), (Weapons.BRU_33___2_x_GBU_12, Weapons.BRU_33___2_x_Mk_82_), @@ -328,29 +308,23 @@ _WEAPON_FALLBACKS = [ (Weapons._2_GBU_12, Weapons._2_Mk_82), (Weapons._2_GBU_12_, Weapons._2_Mk_82_), (Weapons._3_GBU_12, Weapons._3_Mk_82_), - # GBU-15 LGB (Weapons.GBU_15, Weapons.Mk_84), - # GBU-16 LGB (Weapons.BRU_33___2_x_GBU_16, None), - (Weapons.DIS_GBU_16, Weapons.Mk_83), - (Weapons.GBU_16, Weapons.Mk_83), - (Weapons.GBU_16_, Weapons.Mk_83_), - + (Weapons.DIS_GBU_16, Weapons.Mk_83), + (Weapons.GBU_16, Weapons.Mk_83), + (Weapons.GBU_16_, Weapons.Mk_83_), # GBU-24 LGB (Weapons.GBU_24, Weapons.GBU_10), (Weapons.GBU_24_, Weapons.GBU_10_), (Weapons.GBU_24__, Weapons.GBU_10_), - # GBU-27 LGB (Weapons.GBU_24, Weapons.GBU_10), (Weapons.GBU_24_, Weapons.GBU_10_), (Weapons.GBU_24__, Weapons.GBU_10_), - # GBU-28 LGB (Weapons.GBU_28, Weapons.GBU_15), - # GBU-31 JDAM (Weapons.GBU_31V3B_8, Weapons.B_1B_Mk_84_8), (Weapons.GBU_31_8, Weapons.B_1B_Mk_84_8), @@ -358,150 +332,127 @@ _WEAPON_FALLBACKS = [ (Weapons.GBU_31_V_2_B, Weapons.GBU_24_), (Weapons.GBU_31_V_3_B, Weapons.GBU_24_), (Weapons.GBU_31_V_4_B, Weapons.GBU_24_), - # GBU-32 JDAM (Weapons.GBU_32_V_2_B, Weapons.GBU_16), - # GBU-32 JDAM (Weapons.BRU_55___2_x_GBU_38, Weapons.BRU_33___2_x_Mk_82_), - (Weapons.BRU_57___2_x_GBU_38, None), # Doesn't exist + (Weapons.BRU_57___2_x_GBU_38, None), # Doesn't exist (Weapons.GBU_38, Weapons.Mk_82), (Weapons.GBU_38_16, Weapons.MK_82_28), (Weapons._2_GBU_38, Weapons._2_Mk_82), (Weapons._2_GBU_38_, Weapons._2_Mk_82_), (Weapons._3_GBU_38, Weapons._3_Mk_82_), - # GBU-54 LJDAM (Weapons.GBU_54_V_1_B, Weapons.GBU_38), (Weapons._2_GBU_54_V_1_B, Weapons._2_GBU_38), (Weapons._2_GBU_54_V_1_B_, Weapons._2_GBU_38_), (Weapons._3_GBU_54_V_1_B, Weapons._3_GBU_38), - # CBU-52 (Weapons.CBU_52B, None), - # CBU-87 CEM (Weapons.CBU_87, Weapons.Mk_82), (Weapons.TER_9A___2_x_CBU_87, Weapons.TER_9A___2_x_Mk_82), (Weapons.TER_9A___2_x_CBU_87_, Weapons.TER_9A___2_x_Mk_82_), (Weapons.TER_9A___3_x_CBU_87, Weapons.TER_9A___3_x_Mk_82), - # CBU-97 (Weapons.CBU_97, Weapons.Mk_82), (Weapons.TER_9A___2_x_CBU_97, Weapons.TER_9A___2_x_Mk_82), (Weapons.TER_9A___2_x_CBU_97_, Weapons.TER_9A___2_x_Mk_82_), (Weapons.TER_9A___3_x_CBU_97, Weapons.TER_9A___3_x_Mk_82), - # CBU-99 (It's a bomb made in 1968, I'm not bothering right now with backups) - # CBU-103 - (Weapons.BRU_57___2_x_CBU_103, None), # doesn't exist... + (Weapons.BRU_57___2_x_CBU_103, None), # doesn't exist... (Weapons.CBU_103, Weapons.CBU_87), - # CBU-105 - (Weapons.BRU_57___2_x_CBU_105, None), # doesn't exist... + (Weapons.BRU_57___2_x_CBU_105, None), # doesn't exist... (Weapons.CBU_105, Weapons.CBU_97), - - (Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M151___HE_APKWS, Weapons.LAU_131___7_2_75__rockets_M151__HE_), - (Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M282___MPP_APKWS, Weapons.LAU_131___7_2_75__rockets_M151__HE_), - (Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M151___HE_APKWS, Weapons.LAU_68_3___7_2_75__rockets_M151__HE_), - (Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M282___MPP_APKWS, Weapons.LAU_68_3___7_2_75__rockets_M151__HE_), - + ( + Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M151___HE_APKWS, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ), + ( + Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M282___MPP_APKWS, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ), + ( + Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M151___HE_APKWS, + Weapons.LAU_68_3___7_2_75__rockets_M151__HE_, + ), + ( + Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M282___MPP_APKWS, + Weapons.LAU_68_3___7_2_75__rockets_M151__HE_, + ), # Russia # KAB-1500 (Weapons.KAB_1500Kr, None), (Weapons.KAB_1500LG_Pr, Weapons.KAB_1500Kr), (Weapons.KAB_1500L, Weapons.KAB_1500LG_Pr), - # KAB-500 (Weapons.KAB_500kr, Weapons.FAB_500_M62), (Weapons.KAB_500L, Weapons.KAB_500kr), (Weapons.KAB_500S, Weapons.KAB_500L), - # KH Series (Weapons.Kh_22N, None), (Weapons.Kh_23L, None), - (Weapons.Kh_25ML, None), (Weapons.Kh_25ML_, None), (Weapons.Kh_25ML__, None), - (Weapons.Kh_25MP, None), (Weapons.Kh_25MPU, Weapons.Kh_25MP), - (Weapons.Kh_25MR, None), (Weapons.Kh_25MR_, None), - (Weapons.Kh_28__AS_9_Kyle_, None), - (Weapons.Kh_29L, Weapons.Kh_25ML), (Weapons.Kh_29L_, Weapons.Kh_25ML_), (Weapons.Kh_29L__, Weapons.Kh_25ML__), (Weapons.Kh_29T, Weapons.Kh_25MR), (Weapons.Kh_29T_, Weapons.Kh_25MR_), (Weapons.Kh_29T_, Weapons.Kh_25MR_), - (Weapons.Kh_31A, None), (Weapons.Kh_31A_, None), (Weapons.Kh_31A__, None), (Weapons.Kh_31P, Weapons.Kh_25MP), (Weapons.Kh_31P_, Weapons.Kh_25MP), (Weapons.Kh_31P__, Weapons.Kh_25MP), - (Weapons.Kh_35, Weapons.Kh_31A), (Weapons.Kh_35_, Weapons.Kh_31A_), (Weapons.Kh_35_6, None), - (Weapons.Kh_41, None), - (Weapons.Kh_58U, Weapons.Kh_31P), (Weapons.Kh_58U_, Weapons.Kh_31P_), - (Weapons.Kh_59M, Weapons.Kh_31A), - (Weapons.Kh_65, None), (Weapons.Kh_65_6, None), (Weapons.Kh_65_8, None), - (Weapons.Kh_66_Grom__21__APU_68, None), - # ECM (Weapons.L175V_Khibiny_ECM_pod, None), - # R-13 (Weapons.R_13M, None), (Weapons.R_13M1, Weapons.R_13M), - # R-24 (Weapons.R_24R, None), (Weapons.R_24T, None), - # R-27 (Weapons.R_27T, Weapons.R_24T), (Weapons.R_27R, Weapons.R_24R), (Weapons.R_27ER, Weapons.R_27R), (Weapons.R_27ET, Weapons.R_27T), - # R-33 (Weapons.R_33, None), - # R-3 (Weapons.R_3S, Weapons.R_13M), (Weapons.R_3R, Weapons.R_3S), - # R-40 (Weapons.R_40R, None), (Weapons.R_40T, None), - # R-55 (Weapons.R_55, None), (Weapons.RS2US, None), - # R-60 (Weapons.R_60, Weapons.R_13M1), (Weapons.R_60_x_2, Weapons.R_13M1), (Weapons.R_60_x_2_, Weapons.R_13M1), - (Weapons.APU_60_1_R_60M, Weapons.R_3S), (Weapons.R_60M, Weapons.R_60), (Weapons.R_60M_, Weapons.R_60), @@ -509,44 +460,39 @@ _WEAPON_FALLBACKS = [ (Weapons.R_60M_2_, Weapons.R_60M), (Weapons.R_60M_x_2, Weapons.R_60M), (Weapons.R_60M_x_2_, Weapons.R_60M), - # R-73 (Weapons.R_73, Weapons.R_60M), (Weapons.R_73_, None), - # R-77 (Weapons.R_77, Weapons.R_27ER), (Weapons.R_77_, None), - # UK # ALARM (Weapons.ALARM, None), (Weapons.ALARM_2, None), - # France # BLG-66 Belouga (Weapons.AUF2_BLG_66_AC_x_2, Weapons.AUF2_MK_82_x_2), (Weapons.BLG_66_AC_Belouga, Weapons.Mk_82), (Weapons.BLG_66_AC_Belouga_, Weapons.Mk_82), - # HOT-3 (Weapons.HOT3, None), (Weapons.HOT3_, None), - # Magic 2 (Weapons.Matra_Magic_II, None), (Weapons.R_550_Magic_2, None), - # Super 530D (Weapons.Matra_Super_530D, Weapons.Matra_Magic_II), - (Weapons.Super_530D, None) - + (Weapons.Super_530D, None), ] WEAPON_FALLBACK_MAP: Dict[Weapon, Optional[Weapon]] = defaultdict( lambda: cast(Optional[Weapon], None), - ((Weapon.from_pydcs(a), b if b is None else Weapon.from_pydcs(b)) - for a, b in _WEAPON_FALLBACKS)) + ( + (Weapon.from_pydcs(a), b if b is None else Weapon.from_pydcs(b)) + for a, b in _WEAPON_FALLBACKS + ), +) WEAPON_INTRODUCTION_YEARS = { @@ -556,44 +502,34 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons.ADM_141A_): 1987, Weapon.from_pydcs(Weapons.ADM_141A__): 1987, Weapon.from_pydcs(Weapons.ADM_141B): 1987, - # AGM-114K Hellfire Weapon.from_pydcs(Weapons.AGM114x2_OH_58): 1993, Weapon.from_pydcs(Weapons.AGM_114K): 1993, Weapon.from_pydcs(Weapons.AGM_114K___4): 1993, - # AGM-119 Penguin Weapon.from_pydcs(Weapons.AGM_119B_Penguin): 1972, - # AGM-122 Sidearm Weapon.from_pydcs(Weapons.AGM_122): 1986, Weapon.from_pydcs(Weapons.AGM_122_Sidearm): 1986, Weapon.from_pydcs(Weapons.AGM_122_Sidearm_): 1986, - # AGM-154 JSOW Weapon.from_pydcs(Weapons.AGM_154A): 1998, Weapon.from_pydcs(Weapons.BRU_55___2_x_AGM_154A): 1998, Weapon.from_pydcs(Weapons.BRU_57___2_x_AGM_154A): 1998, - Weapon.from_pydcs(Weapons.AGM_154B): 2005, - Weapon.from_pydcs(Weapons.AGM_154C): 2005, Weapon.from_pydcs(Weapons.AGM_154C_4): 2005, Weapon.from_pydcs(Weapons.BRU_55___2_x_AGM_154C): 2005, - # AGM-45 Shrike Weapon.from_pydcs(Weapons.AGM_45A): 1965, Weapon.from_pydcs(Weapons.AGM_45B): 1970, Weapon.from_pydcs(Weapons.AGM_45B_): 1970, - # AGM-62 Walleye Weapon.from_pydcs(Weapons.AGM_62): 1972, - # AGM-65 Maverick Weapon.from_pydcs(Weapons.AGM_65D): 1983, Weapon.from_pydcs(Weapons.AGM_65E): 1985, Weapon.from_pydcs(Weapons.AGM_65K): 2007, - Weapon.from_pydcs(Weapons.LAU_117_AGM_65A): 1972, Weapon.from_pydcs(Weapons.LAU_117_AGM_65B): 1972, Weapon.from_pydcs(Weapons.LAU_117_AGM_65D): 1986, @@ -603,25 +539,20 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons.LAU_117_AGM_65H): 2002, Weapon.from_pydcs(Weapons.LAU_117_AGM_65K): 2002, Weapon.from_pydcs(Weapons.LAU_117_AGM_65L): 1985, - Weapon.from_pydcs(Weapons.LAU_88_AGM_65D_2): 1983, Weapon.from_pydcs(Weapons.LAU_88_AGM_65D_2_): 1983, Weapon.from_pydcs(Weapons.LAU_88_AGM_65D_3): 1983, Weapon.from_pydcs(Weapons.LAU_88_AGM_65D_ONE): 1983, - Weapon.from_pydcs(Weapons.LAU_88_AGM_65E_2): 1985, Weapon.from_pydcs(Weapons.LAU_88_AGM_65E_2_): 1985, Weapon.from_pydcs(Weapons.LAU_88_AGM_65E_3): 1985, - Weapon.from_pydcs(Weapons.LAU_88_AGM_65H): 2007, Weapon.from_pydcs(Weapons.LAU_88_AGM_65H_2_L): 2007, Weapon.from_pydcs(Weapons.LAU_88_AGM_65H_2_R): 2007, Weapon.from_pydcs(Weapons.LAU_88_AGM_65H_3): 2007, - Weapon.from_pydcs(Weapons.LAU_88_AGM_65K_2): 2007, Weapon.from_pydcs(Weapons.LAU_88_AGM_65K_2_): 2007, Weapon.from_pydcs(Weapons.LAU_88_AGM_65K_3): 2007, - # AGM-84 Harpoon Weapon.from_pydcs(Weapons.AGM_84): 1979, Weapon.from_pydcs(Weapons.AGM_84A): 1979, @@ -630,41 +561,33 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons.AGM_84E): 1990, Weapon.from_pydcs(Weapons.AGM_84E_SLAM): 1990, Weapon.from_pydcs(Weapons.AGM_84H): 1998, - # AGM-86 ALCM Weapon.from_pydcs(Weapons.AGM_86C): 1986, Weapon.from_pydcs(Weapons.AGM_86C_20): 1986, Weapon.from_pydcs(Weapons.AGM_86C_8): 1986, Weapon.from_pydcs(Weapons.MER_6_AGM_86C): 1986, - # AGM-88 HARM Weapon.from_pydcs(Weapons.AGM_88C): 1983, Weapon.from_pydcs(Weapons.AGM_88C_): 1983, # for future reference: 1983 is the A model IOC. B model in 1986 and C model in 1994. - # AIM-120 AMRAAM Weapon.from_pydcs(Weapons.AIM_120B): 1994, Weapon.from_pydcs(Weapons.AIM_120C): 1996, - Weapon.from_pydcs(Weapons.LAU_115_2_LAU_127_AIM_120B): 1994, Weapon.from_pydcs(Weapons.LAU_115___AIM_120B): 1994, Weapon.from_pydcs(Weapons.LAU_115_2_LAU_127_AIM_120C): 1996, Weapon.from_pydcs(Weapons.LAU_115___AIM_120C): 1996, - # AIM-54 Phoenix Weapon.from_pydcs(Weapons.AIM_54A_Mk47): 1974, Weapon.from_pydcs(Weapons.AIM_54A_Mk47_): 1974, Weapon.from_pydcs(Weapons.AIM_54A_Mk47__): 1974, - Weapon.from_pydcs(Weapons.AIM_54A_Mk60): 1974, Weapon.from_pydcs(Weapons.AIM_54A_Mk60_): 1974, Weapon.from_pydcs(Weapons.AIM_54A_Mk60__): 1974, - Weapon.from_pydcs(Weapons.AIM_54C): 1974, Weapon.from_pydcs(Weapons.AIM_54C_Mk47): 1974, Weapon.from_pydcs(Weapons.AIM_54C_Mk47_): 1974, Weapon.from_pydcs(Weapons.AIM_54C_Mk47__): 1974, - # AIM-7 Sparrow Weapon.from_pydcs(Weapons.AIM_7E): 1963, Weapon.from_pydcs(Weapons.AIM_7F): 1976, @@ -676,62 +599,49 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons.AIM_7MH): 1987, Weapon.from_pydcs(Weapons.AIM_7MH_): 1987, Weapon.from_pydcs(Weapons.AIM_7MH__): 1987, - Weapon.from_pydcs(Weapons.LAU_115C_AIM_7E): 1963, Weapon.from_pydcs(Weapons.LAU_115C_AIM_7F): 1976, Weapon.from_pydcs(Weapons.LAU_115___AIM_7M): 1982, Weapon.from_pydcs(Weapons.LAU_115C_AIM_7MH): 1987, - # AIM-9 Sidewinder Weapon.from_pydcs(Weapons.AIM_9L_Sidewinder_IR_AAM): 1977, Weapon.from_pydcs(Weapons.AIM_9M_Sidewinder_IR_AAM): 1982, Weapon.from_pydcs(Weapons.AIM_9P5_Sidewinder_IR_AAM): 1980, Weapon.from_pydcs(Weapons.AIM_9P_Sidewinder_IR_AAM): 1978, Weapon.from_pydcs(Weapons.AIM_9X_Sidewinder_IR_AAM): 2003, - Weapon.from_pydcs(Weapons.LAU_105_1_AIM_9L_L): 1977, Weapon.from_pydcs(Weapons.LAU_105_1_AIM_9L_R): 1977, Weapon.from_pydcs(Weapons.LAU_105_1_AIM_9M_L): 1982, Weapon.from_pydcs(Weapons.LAU_105_1_AIM_9M_R): 1982, - Weapon.from_pydcs(Weapons.LAU_105_2_AIM_9L): 1977, Weapon.from_pydcs(Weapons.LAU_105_2_AIM_9P5): 1980, - Weapon.from_pydcs(Weapons.LAU_105___2_AIM_9M_Sidewinder_IR_AAM): 1982, Weapon.from_pydcs(Weapons.LAU_105___2_AIM_9P_Sidewinder_IR_AAM): 1978, - Weapon.from_pydcs(Weapons.LAU_115_2_LAU_127_AIM_9L): 1977, Weapon.from_pydcs(Weapons.LAU_115_2_LAU_127_AIM_9M): 1982, Weapon.from_pydcs(Weapons.LAU_115_2_LAU_127_AIM_9X): 2003, - Weapon.from_pydcs(Weapons.LAU_115_LAU_127_AIM_9L): 1977, Weapon.from_pydcs(Weapons.LAU_115_LAU_127_AIM_9M): 1982, Weapon.from_pydcs(Weapons.LAU_115_LAU_127_AIM_9X): 2003, - Weapon.from_pydcs(Weapons.LAU_127_AIM_9L): 1977, Weapon.from_pydcs(Weapons.LAU_127_AIM_9M): 1982, Weapon.from_pydcs(Weapons.LAU_127_AIM_9X): 2003, - Weapon.from_pydcs(Weapons.LAU_138_AIM_9L): 1977, Weapon.from_pydcs(Weapons.LAU_138_AIM_9M): 1982, - Weapon.from_pydcs(Weapons.LAU_7_AIM_9L): 1977, Weapon.from_pydcs(Weapons.LAU_7_AIM_9M): 1982, Weapon.from_pydcs(Weapons.LAU_7_AIM_9M_Sidewinder_IR_AAM): 1982, Weapon.from_pydcs(Weapons.LAU_7_AIM_9P5_Sidewinder_IR_AAM): 1980, Weapon.from_pydcs(Weapons.LAU_7_AIM_9P_Sidewinder_IR_AAM): 1978, Weapon.from_pydcs(Weapons.LAU_7_AIM_9X_Sidewinder_IR_AAM): 2003, - Weapon.from_pydcs(Weapons.LAU_7___2_AIM_9L_Sidewinder_IR_AAM): 1977, Weapon.from_pydcs(Weapons.LAU_7___2_AIM_9M_Sidewinder_IR_AAM): 1982, Weapon.from_pydcs(Weapons.LAU_7___2_AIM_9P5_Sidewinder_IR_AAM): 1980, Weapon.from_pydcs(Weapons.LAU_7___2_AIM_9P_Sidewinder_IR_AAM): 1978, - # ALQ ECM Pods Weapon.from_pydcs(Weapons.ALQ_131): 1970, Weapon.from_pydcs(Weapons.ALQ_184): 1989, Weapon.from_pydcs(Weapons.AN_ALQ_164_DECM_Pod): 1984, - # TGP Pods Weapon.from_pydcs(Weapons.AN_AAQ_28_LITENING): 1995, Weapon.from_pydcs(Weapons.AN_AAQ_28_LITENING_): 1995, @@ -742,17 +652,14 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons.Lantirn_F_16): 1985, Weapon.from_pydcs(Weapons.Lantirn_Target_Pod): 1985, Weapon.from_pydcs(Weapons.Pavetack_F_111): 1982, - # BLU-107 Weapon.from_pydcs(Weapons.BLU_107): 1983, Weapon.from_pydcs(Weapons.MER_6_BLU_107): 1983, - # GBU-10 LGB Weapon.from_pydcs(Weapons.DIS_GBU_10): 1976, Weapon.from_pydcs(Weapons.GBU_10): 1976, Weapon.from_pydcs(Weapons.GBU_10_): 1976, Weapon.from_pydcs(Weapons.GBU_10_2): 1976, - # GBU-12 LGB Weapon.from_pydcs(Weapons.AUF2_GBU_12_x_2): 1976, Weapon.from_pydcs(Weapons.BRU_33___2_x_GBU_12): 1976, @@ -770,10 +677,8 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons._2_GBU_12): 1976, Weapon.from_pydcs(Weapons._2_GBU_12_): 1976, Weapon.from_pydcs(Weapons._3_GBU_12): 1976, - # GBU-15 LGB Weapon.from_pydcs(Weapons.GBU_15): 1975, - # GBU-16 LGB Weapon.from_pydcs(Weapons.BRU_33___2_x_GBU_16): 1976, Weapon.from_pydcs(Weapons.DIS_GBU_16): 1976, @@ -783,20 +688,16 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons._2_GBU_16_): 1976, Weapon.from_pydcs(Weapons._3_GBU_16): 1976, Weapon.from_pydcs(Weapons._3_GBU_16_): 1976, - # GBU-24 LGB Weapon.from_pydcs(Weapons.GBU_24): 1986, Weapon.from_pydcs(Weapons.GBU_24_): 1986, Weapon.from_pydcs(Weapons.GBU_24__): 1986, - # GBU-27 LGB Weapon.from_pydcs(Weapons.GBU_27): 1991, Weapon.from_pydcs(Weapons.GBU_27_2): 1991, Weapon.from_pydcs(Weapons.GBU_27_4): 1991, - # GBU-28 Weapon.from_pydcs(Weapons.GBU_28): 1991, - # GBU-31 JDAM Weapon.from_pydcs(Weapons.GBU_31V3B_8): 2001, Weapon.from_pydcs(Weapons.GBU_31_8): 2001, @@ -804,10 +705,8 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons.GBU_31_V_2_B): 2001, Weapon.from_pydcs(Weapons.GBU_31_V_3_B): 2001, Weapon.from_pydcs(Weapons.GBU_31_V_4_B): 2001, - # GBU-32 JDAM Weapon.from_pydcs(Weapons.GBU_32_V_2_B): 2002, - # GBU-38 JDAM Weapon.from_pydcs(Weapons.BRU_55___2_x_GBU_38): 2005, Weapon.from_pydcs(Weapons.BRU_57___2_x_GBU_38): 2005, @@ -816,53 +715,40 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons._2_GBU_38): 2005, Weapon.from_pydcs(Weapons._2_GBU_38_): 2005, Weapon.from_pydcs(Weapons._3_GBU_38): 2005, - # GBU-54 LJDAM Weapon.from_pydcs(Weapons.GBU_54_V_1_B): 2008, Weapon.from_pydcs(Weapons._2_GBU_54_V_1_B): 2008, Weapon.from_pydcs(Weapons._2_GBU_54_V_1_B_): 2008, Weapon.from_pydcs(Weapons._3_GBU_54_V_1_B): 2008, - # CBU-52 Weapon.from_pydcs(Weapons.CBU_52B): 1970, - # CBU-87 CEM Weapon.from_pydcs(Weapons.CBU_87): 1986, Weapon.from_pydcs(Weapons.TER_9A___2_x_CBU_87): 1986, Weapon.from_pydcs(Weapons.TER_9A___2_x_CBU_87_): 1986, Weapon.from_pydcs(Weapons.TER_9A___3_x_CBU_87): 1986, - # CBU-97 Weapon.from_pydcs(Weapons.CBU_97): 1992, Weapon.from_pydcs(Weapons.TER_9A___2_x_CBU_97): 1992, Weapon.from_pydcs(Weapons.TER_9A___2_x_CBU_97_): 1992, Weapon.from_pydcs(Weapons.TER_9A___3_x_CBU_97): 1992, - # CBU-99 Weapon.from_pydcs(Weapons.BRU_33___2_x_CBU_99): 1968, Weapon.from_pydcs(Weapons.CBU_99): 1968, - Weapon.from_pydcs(Weapons.BRU_33___2_x_Mk_20_Rockeye): 1968, - Weapon.from_pydcs(Weapons.DIS_MK_20): 1968, Weapon.from_pydcs(Weapons.DIS_MK_20_DUAL_L): 1968, Weapon.from_pydcs(Weapons.DIS_MK_20_DUAL_R): 1968, - Weapon.from_pydcs(Weapons.HSAB_9_Mk_20_Rockeye): 1968, - Weapon.from_pydcs(Weapons.MAK79_2_MK_20): 1968, Weapon.from_pydcs(Weapons.MAK79_2_MK_20_): 1968, - Weapon.from_pydcs(Weapons.MAK79_MK_20): 1968, Weapon.from_pydcs(Weapons.MAK79_MK_20_): 1968, - Weapon.from_pydcs(Weapons.MER_6_Mk_20_Rockeye): 1968, - Weapon.from_pydcs(Weapons.Mk_20): 1968, Weapon.from_pydcs(Weapons.Mk_20_): 1968, Weapon.from_pydcs(Weapons.Mk_20_18): 1968, Weapon.from_pydcs(Weapons.Mk_20_Rockeye__6): 1968, - Weapon.from_pydcs(Weapons._2_MK_20): 1968, Weapon.from_pydcs(Weapons._2_MK_20_): 1968, Weapon.from_pydcs(Weapons._2_MK_20__): 1968, @@ -872,123 +758,100 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons._2_Mk_20_Rockeye): 1968, Weapon.from_pydcs(Weapons._2_Mk_20_Rockeye_): 1968, Weapon.from_pydcs(Weapons._2_Mk_20_Rockeye__): 1968, - Weapon.from_pydcs(Weapons._3_Mk_20_Rockeye): 1968, Weapon.from_pydcs(Weapons._3_Mk_20_Rockeye_): 1968, - # CBU-103 Weapon.from_pydcs(Weapons.BRU_57___2_x_CBU_103): 2000, Weapon.from_pydcs(Weapons.CBU_103): 2000, - # CBU-105 Weapon.from_pydcs(Weapons.BRU_57___2_x_CBU_105): 2000, Weapon.from_pydcs(Weapons.CBU_105): 2000, - # APKWS - Weapon.from_pydcs(Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M151___HE_APKWS): 2016, - Weapon.from_pydcs(Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M282___MPP_APKWS): 2016, - Weapon.from_pydcs(Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M151___HE_APKWS): 2016, - Weapon.from_pydcs(Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M282___MPP_APKWS): 2016, - + Weapon.from_pydcs( + Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M151___HE_APKWS + ): 2016, + Weapon.from_pydcs( + Weapons.LAU_131_pod___7_x_2_75__Hydra___Laser_Guided_Rkts_M282___MPP_APKWS + ): 2016, + Weapon.from_pydcs( + Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M151___HE_APKWS + ): 2016, + Weapon.from_pydcs( + Weapons._3_x_LAU_131_pods___21_x_2_75__Hydra___Laser_Guided_M282___MPP_APKWS + ): 2016, # Russia - # KAB-1500 Weapon.from_pydcs(Weapons.KAB_1500Kr): 1985, Weapon.from_pydcs(Weapons.KAB_1500L): 1995, Weapon.from_pydcs(Weapons.KAB_1500LG_Pr): 1990, - # KAB-500 Weapon.from_pydcs(Weapons.KAB_500kr): 1980, Weapon.from_pydcs(Weapons.KAB_500L): 1995, Weapon.from_pydcs(Weapons.KAB_500S): 2000, - # Kh Series Weapon.from_pydcs(Weapons.Kh_22N): 1962, Weapon.from_pydcs(Weapons.Kh_23L): 1975, - Weapon.from_pydcs(Weapons.Kh_25ML): 1975, Weapon.from_pydcs(Weapons.Kh_25ML_): 1975, Weapon.from_pydcs(Weapons.Kh_25ML__): 1975, - Weapon.from_pydcs(Weapons.Kh_25MP): 1975, - Weapon.from_pydcs(Weapons.Kh_25MPU): 1980, Weapon.from_pydcs(Weapons.Kh_25MPU_): 1980, Weapon.from_pydcs(Weapons.Kh_25MPU__): 1980, - Weapon.from_pydcs(Weapons.Kh_25MR): 1975, Weapon.from_pydcs(Weapons.Kh_25MR_): 1975, - Weapon.from_pydcs(Weapons.Kh_28__AS_9_Kyle_): 1973, - Weapon.from_pydcs(Weapons.Kh_29L): 1980, Weapon.from_pydcs(Weapons.Kh_29L_): 1980, Weapon.from_pydcs(Weapons.Kh_29L__): 1980, Weapon.from_pydcs(Weapons.Kh_29T): 1980, Weapon.from_pydcs(Weapons.Kh_29T_): 1980, Weapon.from_pydcs(Weapons.Kh_29T__): 1980, - Weapon.from_pydcs(Weapons.Kh_31A): 1980, Weapon.from_pydcs(Weapons.Kh_31A_): 1980, Weapon.from_pydcs(Weapons.Kh_31A__): 1980, Weapon.from_pydcs(Weapons.Kh_31P): 1980, Weapon.from_pydcs(Weapons.Kh_31P_): 1980, Weapon.from_pydcs(Weapons.Kh_31P__): 1980, - Weapon.from_pydcs(Weapons.Kh_35): 2003, Weapon.from_pydcs(Weapons.Kh_35_): 2003, Weapon.from_pydcs(Weapons.Kh_35_6): 2003, - Weapon.from_pydcs(Weapons.Kh_41): 1984, - Weapon.from_pydcs(Weapons.Kh_58U): 1985, Weapon.from_pydcs(Weapons.Kh_58U_): 1985, - Weapon.from_pydcs(Weapons.Kh_59M): 1990, - Weapon.from_pydcs(Weapons.Kh_65): 1992, Weapon.from_pydcs(Weapons.Kh_65_6): 1992, Weapon.from_pydcs(Weapons.Kh_65_8): 1992, - Weapon.from_pydcs(Weapons.Kh_66_Grom__21__APU_68): 1968, - # ECM Weapon.from_pydcs(Weapons.L175V_Khibiny_ECM_pod): 1982, - # R-13 Weapon.from_pydcs(Weapons.R_13M): 1961, Weapon.from_pydcs(Weapons.R_13M1): 1965, - # R-24 Weapon.from_pydcs(Weapons.R_24R): 1981, Weapon.from_pydcs(Weapons.R_24T): 1981, - # R-27 Weapon.from_pydcs(Weapons.R_27ER): 1983, Weapon.from_pydcs(Weapons.R_27ET): 1986, Weapon.from_pydcs(Weapons.R_27R): 1983, Weapon.from_pydcs(Weapons.R_27T): 1983, - # R-33 Weapon.from_pydcs(Weapons.R_33): 1981, - # R-3 Weapon.from_pydcs(Weapons.R_3R): 1966, Weapon.from_pydcs(Weapons.R_3S): 1962, - # R-40 Weapon.from_pydcs(Weapons.R_40R): 1976, Weapon.from_pydcs(Weapons.R_40T): 1976, - # R-55 Weapon.from_pydcs(Weapons.R_55): 1957, Weapon.from_pydcs(Weapons.RS2US): 1957, - # R-60 Weapon.from_pydcs(Weapons.R_60): 1973, Weapon.from_pydcs(Weapons.R_60_x_2): 1973, Weapon.from_pydcs(Weapons.R_60_x_2_): 1973, - Weapon.from_pydcs(Weapons.APU_60_1_R_60M): 1982, Weapon.from_pydcs(Weapons.R_60M): 1982, Weapon.from_pydcs(Weapons.R_60M_): 1982, @@ -996,36 +859,28 @@ WEAPON_INTRODUCTION_YEARS = { Weapon.from_pydcs(Weapons.R_60M_2_): 1982, Weapon.from_pydcs(Weapons.R_60M_x_2): 1982, Weapon.from_pydcs(Weapons.R_60M_x_2_): 1982, - # R-73 Weapon.from_pydcs(Weapons.R_73): 1984, Weapon.from_pydcs(Weapons.R_73_): 1984, - # R-77 Weapon.from_pydcs(Weapons.R_77): 2002, Weapon.from_pydcs(Weapons.R_77_): 2002, - # UK # ALARM Weapon.from_pydcs(Weapons.ALARM): 1990, Weapon.from_pydcs(Weapons.ALARM_2): 1990, - # France # BLG-66 Belouga Weapon.from_pydcs(Weapons.AUF2_BLG_66_AC_x_2): 1979, Weapon.from_pydcs(Weapons.BLG_66_AC_Belouga): 1979, Weapon.from_pydcs(Weapons.BLG_66_AC_Belouga_): 1979, - # HOT-3 Weapon.from_pydcs(Weapons.HOT3): 1998, Weapon.from_pydcs(Weapons.HOT3_): 1998, - # Magic 2 Weapon.from_pydcs(Weapons.Matra_Magic_II): 1986, Weapon.from_pydcs(Weapons.R_550_Magic_2): 1986, - # Super 530D Weapon.from_pydcs(Weapons.Matra_Super_530D): 1988, Weapon.from_pydcs(Weapons.Super_530D): 1988, - } diff --git a/game/db.py b/game/db.py index d0d04f01..82a80fed 100644 --- a/game/db.py +++ b/game/db.py @@ -25,6 +25,7 @@ from dcs.helicopters import ( helicopter_map, ) from dcs.mapping import Point + # mypy can't resolve these if they're wildcard imports for some reason. from dcs.planes import ( AJS37, @@ -112,7 +113,7 @@ from dcs.planes import ( WingLoong_I, Yak_40, plane_map, - I_16 + I_16, ) from dcs.ships import ( Armed_speedboat, @@ -166,6 +167,7 @@ from dcs.vehicles import ( import pydcs_extensions.frenchpack.frenchpack as frenchpack import pydcs_extensions.highdigitsams.highdigitsams as highdigitsams + # PATCH pydcs data with MODS from game.factions.faction_loader import FactionLoader from pydcs_extensions.a4ec.a4ec import A_4E_C @@ -222,46 +224,122 @@ vehicle_map["Kamikaze"] = frenchpack.DIM__KAMIKAZE vehicle_map[highdigitsams.AAA_SON_9_Fire_Can.id] = highdigitsams.AAA_SON_9_Fire_Can vehicle_map[highdigitsams.AAA_100mm_KS_19.id] = highdigitsams.AAA_100mm_KS_19 -vehicle_map[highdigitsams.SAM_SA_10B_S_300PS_54K6_CP.id] = highdigitsams.SAM_SA_10B_S_300PS_54K6_CP -vehicle_map[highdigitsams.SAM_SA_10B_S_300PS_5P85SE_LN.id] = highdigitsams.SAM_SA_10B_S_300PS_5P85SE_LN -vehicle_map[highdigitsams.SAM_SA_10B_S_300PS_5P85SU_LN.id] = highdigitsams.SAM_SA_10B_S_300PS_5P85SU_LN -vehicle_map[highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85CE.id] = highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85CE -vehicle_map[highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85DE.id] = highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85DE -vehicle_map[highdigitsams.SAM_SA_10B_S_300PS_30N6_TR.id] = highdigitsams.SAM_SA_10B_S_300PS_30N6_TR -vehicle_map[highdigitsams.SAM_SA_10B_S_300PS_40B6M_TR.id] = highdigitsams.SAM_SA_10B_S_300PS_40B6M_TR -vehicle_map[highdigitsams.SAM_SA_10B_S_300PS_40B6MD_SR.id] = highdigitsams.SAM_SA_10B_S_300PS_40B6MD_SR -vehicle_map[highdigitsams.SAM_SA_10B_S_300PS_64H6E_SR.id] = highdigitsams.SAM_SA_10B_S_300PS_64H6E_SR -vehicle_map[highdigitsams.SAM_SA_20_S_300PMU1_CP_54K6.id] = highdigitsams.SAM_SA_20_S_300PMU1_CP_54K6 -vehicle_map[highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E.id] = highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E -vehicle_map[highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E_truck.id] = highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E_truck -vehicle_map[highdigitsams.SAM_SA_20_S_300PMU1_SR_5N66E.id] = highdigitsams.SAM_SA_20_S_300PMU1_SR_5N66E -vehicle_map[highdigitsams.SAM_SA_20_S_300PMU1_SR_64N6E.id] = highdigitsams.SAM_SA_20_S_300PMU1_SR_64N6E -vehicle_map[highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85CE.id] = highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85CE -vehicle_map[highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85DE.id] = highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85DE -vehicle_map[highdigitsams.SAM_SA_20B_S_300PMU2_CP_54K6E2.id] = highdigitsams.SAM_SA_20B_S_300PMU2_CP_54K6E2 -vehicle_map[highdigitsams.SAM_SA_20B_S_300PMU2_TR_92H6E_truck.id] = highdigitsams.SAM_SA_20B_S_300PMU2_TR_92H6E_truck -vehicle_map[highdigitsams.SAM_SA_20B_S_300PMU2_SR_64N6E2.id] = highdigitsams.SAM_SA_20B_S_300PMU2_SR_64N6E2 -vehicle_map[highdigitsams.SAM_SA_20B_S_300PMU2_LN_5P85SE2.id] = highdigitsams.SAM_SA_20B_S_300PMU2_LN_5P85SE2 -vehicle_map[highdigitsams.SAM_SA_12_S_300V_9S457_CP.id] = highdigitsams.SAM_SA_12_S_300V_9S457_CP -vehicle_map[highdigitsams.SAM_SA_12_S_300V_9A82_LN.id] = highdigitsams.SAM_SA_12_S_300V_9A82_LN -vehicle_map[highdigitsams.SAM_SA_12_S_300V_9A83_LN.id] = highdigitsams.SAM_SA_12_S_300V_9A83_LN -vehicle_map[highdigitsams.SAM_SA_12_S_300V_9S15_SR.id] = highdigitsams.SAM_SA_12_S_300V_9S15_SR -vehicle_map[highdigitsams.SAM_SA_12_S_300V_9S19_SR.id] = highdigitsams.SAM_SA_12_S_300V_9S19_SR -vehicle_map[highdigitsams.SAM_SA_12_S_300V_9S32_TR.id] = highdigitsams.SAM_SA_12_S_300V_9S32_TR -vehicle_map[highdigitsams.SAM_SA_23_S_300VM_9S457ME_CP.id] = highdigitsams.SAM_SA_23_S_300VM_9S457ME_CP -vehicle_map[highdigitsams.SAM_SA_23_S_300VM_9S15M2_SR.id] = highdigitsams.SAM_SA_23_S_300VM_9S15M2_SR -vehicle_map[highdigitsams.SAM_SA_23_S_300VM_9S19M2_SR.id] = highdigitsams.SAM_SA_23_S_300VM_9S19M2_SR -vehicle_map[highdigitsams.SAM_SA_23_S_300VM_9S32ME_TR.id] = highdigitsams.SAM_SA_23_S_300VM_9S32ME_TR -vehicle_map[highdigitsams.SAM_SA_23_S_300VM_9A83ME_LN.id] = highdigitsams.SAM_SA_23_S_300VM_9A83ME_LN -vehicle_map[highdigitsams.SAM_SA_23_S_300VM_9A82ME_LN.id] = highdigitsams.SAM_SA_23_S_300VM_9A82ME_LN -vehicle_map[highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2.id] = highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2 -vehicle_map[highdigitsams.SAM_SA_2__V759__LN_SM_90.id] = highdigitsams.SAM_SA_2__V759__LN_SM_90 +vehicle_map[ + highdigitsams.SAM_SA_10B_S_300PS_54K6_CP.id +] = highdigitsams.SAM_SA_10B_S_300PS_54K6_CP +vehicle_map[ + highdigitsams.SAM_SA_10B_S_300PS_5P85SE_LN.id +] = highdigitsams.SAM_SA_10B_S_300PS_5P85SE_LN +vehicle_map[ + highdigitsams.SAM_SA_10B_S_300PS_5P85SU_LN.id +] = highdigitsams.SAM_SA_10B_S_300PS_5P85SU_LN +vehicle_map[ + highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85CE.id +] = highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85CE +vehicle_map[ + highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85DE.id +] = highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85DE +vehicle_map[ + highdigitsams.SAM_SA_10B_S_300PS_30N6_TR.id +] = highdigitsams.SAM_SA_10B_S_300PS_30N6_TR +vehicle_map[ + highdigitsams.SAM_SA_10B_S_300PS_40B6M_TR.id +] = highdigitsams.SAM_SA_10B_S_300PS_40B6M_TR +vehicle_map[ + highdigitsams.SAM_SA_10B_S_300PS_40B6MD_SR.id +] = highdigitsams.SAM_SA_10B_S_300PS_40B6MD_SR +vehicle_map[ + highdigitsams.SAM_SA_10B_S_300PS_64H6E_SR.id +] = highdigitsams.SAM_SA_10B_S_300PS_64H6E_SR +vehicle_map[ + highdigitsams.SAM_SA_20_S_300PMU1_CP_54K6.id +] = highdigitsams.SAM_SA_20_S_300PMU1_CP_54K6 +vehicle_map[ + highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E.id +] = highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E +vehicle_map[ + highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E_truck.id +] = highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E_truck +vehicle_map[ + highdigitsams.SAM_SA_20_S_300PMU1_SR_5N66E.id +] = highdigitsams.SAM_SA_20_S_300PMU1_SR_5N66E +vehicle_map[ + highdigitsams.SAM_SA_20_S_300PMU1_SR_64N6E.id +] = highdigitsams.SAM_SA_20_S_300PMU1_SR_64N6E +vehicle_map[ + highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85CE.id +] = highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85CE +vehicle_map[ + highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85DE.id +] = highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85DE +vehicle_map[ + highdigitsams.SAM_SA_20B_S_300PMU2_CP_54K6E2.id +] = highdigitsams.SAM_SA_20B_S_300PMU2_CP_54K6E2 +vehicle_map[ + highdigitsams.SAM_SA_20B_S_300PMU2_TR_92H6E_truck.id +] = highdigitsams.SAM_SA_20B_S_300PMU2_TR_92H6E_truck +vehicle_map[ + highdigitsams.SAM_SA_20B_S_300PMU2_SR_64N6E2.id +] = highdigitsams.SAM_SA_20B_S_300PMU2_SR_64N6E2 +vehicle_map[ + highdigitsams.SAM_SA_20B_S_300PMU2_LN_5P85SE2.id +] = highdigitsams.SAM_SA_20B_S_300PMU2_LN_5P85SE2 +vehicle_map[ + highdigitsams.SAM_SA_12_S_300V_9S457_CP.id +] = highdigitsams.SAM_SA_12_S_300V_9S457_CP +vehicle_map[ + highdigitsams.SAM_SA_12_S_300V_9A82_LN.id +] = highdigitsams.SAM_SA_12_S_300V_9A82_LN +vehicle_map[ + highdigitsams.SAM_SA_12_S_300V_9A83_LN.id +] = highdigitsams.SAM_SA_12_S_300V_9A83_LN +vehicle_map[ + highdigitsams.SAM_SA_12_S_300V_9S15_SR.id +] = highdigitsams.SAM_SA_12_S_300V_9S15_SR +vehicle_map[ + highdigitsams.SAM_SA_12_S_300V_9S19_SR.id +] = highdigitsams.SAM_SA_12_S_300V_9S19_SR +vehicle_map[ + highdigitsams.SAM_SA_12_S_300V_9S32_TR.id +] = highdigitsams.SAM_SA_12_S_300V_9S32_TR +vehicle_map[ + highdigitsams.SAM_SA_23_S_300VM_9S457ME_CP.id +] = highdigitsams.SAM_SA_23_S_300VM_9S457ME_CP +vehicle_map[ + highdigitsams.SAM_SA_23_S_300VM_9S15M2_SR.id +] = highdigitsams.SAM_SA_23_S_300VM_9S15M2_SR +vehicle_map[ + highdigitsams.SAM_SA_23_S_300VM_9S19M2_SR.id +] = highdigitsams.SAM_SA_23_S_300VM_9S19M2_SR +vehicle_map[ + highdigitsams.SAM_SA_23_S_300VM_9S32ME_TR.id +] = highdigitsams.SAM_SA_23_S_300VM_9S32ME_TR +vehicle_map[ + highdigitsams.SAM_SA_23_S_300VM_9A83ME_LN.id +] = highdigitsams.SAM_SA_23_S_300VM_9A83ME_LN +vehicle_map[ + highdigitsams.SAM_SA_23_S_300VM_9A82ME_LN.id +] = highdigitsams.SAM_SA_23_S_300VM_9A82ME_LN +vehicle_map[ + highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2.id +] = highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2 +vehicle_map[ + highdigitsams.SAM_SA_2__V759__LN_SM_90.id +] = highdigitsams.SAM_SA_2__V759__LN_SM_90 vehicle_map[highdigitsams.SAM_HQ_2_LN_SM_90.id] = highdigitsams.SAM_HQ_2_LN_SM_90 -vehicle_map[highdigitsams.SAM_SA_3__V_601P__LN_5P73.id] = highdigitsams.SAM_SA_3__V_601P__LN_5P73 -vehicle_map[highdigitsams.SAM_SA_24_Igla_S_manpad.id] = highdigitsams.SAM_SA_24_Igla_S_manpad -vehicle_map[highdigitsams.SAM_SA_14_Strela_3_manpad.id] = highdigitsams.SAM_SA_14_Strela_3_manpad +vehicle_map[ + highdigitsams.SAM_SA_3__V_601P__LN_5P73.id +] = highdigitsams.SAM_SA_3__V_601P__LN_5P73 +vehicle_map[ + highdigitsams.SAM_SA_24_Igla_S_manpad.id +] = highdigitsams.SAM_SA_24_Igla_S_manpad +vehicle_map[ + highdigitsams.SAM_SA_14_Strela_3_manpad.id +] = highdigitsams.SAM_SA_14_Strela_3_manpad vehicle_map[highdigitsams.Polyana_D4M1_C2_node.id] = highdigitsams.Polyana_D4M1_C2_node -vehicle_map[highdigitsams._34Ya6E_Gazetchik_E_decoy.id] = highdigitsams._34Ya6E_Gazetchik_E_decoy +vehicle_map[ + highdigitsams._34Ya6E_Gazetchik_E_decoy.id +] = highdigitsams._34Ya6E_Gazetchik_E_decoy """ ---------- BEGINNING OF CONFIGURATION SECTION @@ -308,7 +386,6 @@ PRICES = { JF_17: 20, Su_30: 24, Su_57: 40, - SpitfireLFMkIX: 14, SpitfireLFMkIXCW: 14, I_16: 10, @@ -317,7 +394,6 @@ PRICES = { FW_190A8: 14, A_20G: 22, Ju_88A4: 24, - F_5E_3: 8, MiG_15bis: 4, MiG_19P: 6, @@ -328,7 +404,6 @@ PRICES = { C_101CC: 6, A_4E_C: 8, MB_339PAN: 6, - AV8BNA: 14, M_2000C: 16, Mirage_2000_5: 20, @@ -342,7 +417,6 @@ PRICES = { F_22A: 40, Tornado_IDS: 20, Tornado_GR4: 20, - # bomber Su_17M4: 10, Su_25: 15, @@ -352,12 +426,10 @@ PRICES = { Su_24M: 20, Su_24MR: 24, MiG_27K: 20, - A_10A: 16, A_10C: 22, A_10C_2: 24, S_3B: 10, - # heli Ka_50: 13, SA342M: 8, @@ -373,7 +445,6 @@ PRICES = { AH_64D: 30, OH_58D: 6, SH_60B: 6, - # Bombers B_52H: 35, B_1B: 50, @@ -382,7 +453,6 @@ PRICES = { Tu_22M3: 40, Tu_95MS: 35, F_111F: 21, - # special IL_76MD: 30, An_26B: 25, @@ -393,14 +463,12 @@ PRICES = { KC_135: 25, KC130: 25, KC135MPRS: 25, - A_50: 50, KJ_2000: 50, E_3A: 50, E_2C: 50, C_130: 25, Hercules: 25, - # WW2 P_51D_30_NA: 18, P_51D: 16, @@ -408,17 +476,14 @@ PRICES = { P_47D_30bl1: 16, P_47D_40: 18, B_17G: 30, - # Drones MQ_9_Reaper: 12, RQ_1A_Predator: 6, WingLoong_I: 6, - # Modded Rafale_M: 26, Rafale_A_S: 26, Rafale_B: 26, - # armor Armor.APC_MTLB: 4, Armor.FDDM_Grad: 4, @@ -437,7 +502,6 @@ PRICES = { Armor.IFV_BMP_3: 18, Armor.ZBD_04A: 12, Armor.ZTZ_96B: 30, - Armor.APC_Cobra: 4, Armor.APC_M113: 6, Armor.APC_M1043_HMMWV_Armament: 2, @@ -457,10 +521,8 @@ PRICES = { Armor.IFV_Marder: 10, Armor.IFV_MCV_80: 10, Armor.IFV_LAV_25: 7, - Artillery.MLRS_M270: 55, Artillery.SPH_M109_Paladin: 25, - Artillery.SPH_2S9_Nona: 12, Artillery.SPH_2S1_Gvozdika: 18, Artillery.SPH_2S3_Akatsia: 24, @@ -470,14 +532,11 @@ PRICES = { Artillery.MLRS_9A52_Smerch: 40, Artillery._2B11_mortar: 4, Artillery.SpGH_Dana: 26, - Unarmed.Transport_UAZ_469: 3, Unarmed.Transport_Ural_375: 3, Infantry.Infantry_M4: 1, Infantry.Soldier_AK: 1, - Unarmed.Transport_M818: 3, - # WW2 Armor.MT_Pz_Kpfw_V_Panther_Ausf_G: 24, Armor.MT_Pz_Kpfw_IV_Ausf_H: 16, @@ -504,22 +563,18 @@ PRICES = { Armor.Daimler_Armoured_Car: 8, Armor.LT_Mk_VII_Tetrarch: 8, Armor.M4_Tractor: 2, - # ship CV_1143_5_Admiral_Kuznetsov: 100, CVN_74_John_C__Stennis: 100, LHA_1_Tarawa: 50, - Bulk_cargo_ship_Yakushev: 10, Armed_speedboat: 10, Dry_cargo_ship_Ivanov: 10, Tanker_Elnya_160: 10, - # Air Defence units AirDefence.SAM_SA_19_Tunguska_2S6: 30, AirDefence.SAM_SA_6_Kub_LN_2P25: 20, AirDefence.SAM_SA_3_S_125_LN_5P73: 6, - AirDefence.SAM_SA_11_Buk_LN_9A310M1: 30, AirDefence.SAM_SA_11_Buk_CC_9S470M1: 25, AirDefence.SAM_SA_11_Buk_SR_9S18M1: 28, @@ -558,7 +613,6 @@ PRICES = { AirDefence.SAM_SA_18_Igla_S_comm: 8, AirDefence.EWR_1L13: 30, AirDefence.SAM_SA_6_Kub_STR_9S91: 22, - AirDefence.EWR_55G6: 30, AirDefence.CP_9S80M1_Sborka: 10, AirDefence.SAM_Hawk_TR_AN_MPQ_46: 14, @@ -589,7 +643,6 @@ PRICES = { AirDefence.AAA_M1_37mm: 7, AirDefence.AAA_M45_Quadmount: 4, AirDefence.AA_gun_QF_3_7: 10, - # FRENCH PACK MOD frenchpack.AMX_10RCR: 10, frenchpack.AMX_10RCR_SEPAR: 12, @@ -619,7 +672,6 @@ PRICES = { frenchpack.DIM__TOYOTA_GREEN: 2, frenchpack.DIM__TOYOTA_DESERT: 2, frenchpack.DIM__KAMIKAZE: 6, - # SA-10 AirDefence.SAM_SA_10_S_300PS_CP_54K6: 18, AirDefence.SAM_SA_10_S_300PS_TR_30N6: 24, @@ -627,11 +679,9 @@ PRICES = { AirDefence.SAM_SA_10_S_300PS_SR_64H6E: 30, AirDefence.SAM_SA_10_S_300PS_LN_5P85C: 22, AirDefence.SAM_SA_10_S_300PS_LN_5P85D: 22, - # High digit sams mod highdigitsams.AAA_SON_9_Fire_Can: 8, highdigitsams.AAA_100mm_KS_19: 10, - highdigitsams.SAM_SA_10B_S_300PS_54K6_CP: 20, highdigitsams.SAM_SA_10B_S_300PS_5P85SE_LN: 24, highdigitsams.SAM_SA_10B_S_300PS_5P85SU_LN: 24, @@ -641,14 +691,12 @@ PRICES = { highdigitsams.SAM_SA_10B_S_300PS_40B6M_TR: 26, highdigitsams.SAM_SA_10B_S_300PS_40B6MD_SR: 32, highdigitsams.SAM_SA_10B_S_300PS_64H6E_SR: 32, - highdigitsams.SAM_SA_12_S_300V_9S457_CP: 22, highdigitsams.SAM_SA_12_S_300V_9A82_LN: 26, highdigitsams.SAM_SA_12_S_300V_9A83_LN: 26, highdigitsams.SAM_SA_12_S_300V_9S15_SR: 34, highdigitsams.SAM_SA_12_S_300V_9S19_SR: 34, highdigitsams.SAM_SA_12_S_300V_9S32_TR: 28, - highdigitsams.SAM_SA_20_S_300PMU1_CP_54K6: 26, highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E: 30, highdigitsams.SAM_SA_20_S_300PMU1_TR_30N6E_truck: 32, @@ -656,21 +704,17 @@ PRICES = { highdigitsams.SAM_SA_20_S_300PMU1_SR_64N6E: 38, highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85CE: 28, highdigitsams.SAM_SA_20_S_300PMU1_LN_5P85DE: 28, - highdigitsams.SAM_SA_20B_S_300PMU2_CP_54K6E2: 27, highdigitsams.SAM_SA_20B_S_300PMU2_TR_92H6E_truck: 33, highdigitsams.SAM_SA_20B_S_300PMU2_SR_64N6E2: 40, highdigitsams.SAM_SA_20B_S_300PMU2_LN_5P85SE2: 30, - highdigitsams.SAM_SA_23_S_300VM_9S457ME_CP: 30, highdigitsams.SAM_SA_23_S_300VM_9S15M2_SR: 45, highdigitsams.SAM_SA_23_S_300VM_9S19M2_SR: 45, highdigitsams.SAM_SA_23_S_300VM_9S32ME_TR: 35, highdigitsams.SAM_SA_23_S_300VM_9A83ME_LN: 32, highdigitsams.SAM_SA_23_S_300VM_9A82ME_LN: 32, - highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2: 40, - } """ @@ -728,7 +772,7 @@ UNIT_BY_TASK = { SpitfireLFMkIX, A_4E_C, Rafale_M, - SA342Mistral + SA342Mistral, ], CAS: [ AH_1W, @@ -779,18 +823,12 @@ UNIT_BY_TASK = { Tu_160, Tu_22M3, Tu_95MS, - UH_1H, + UH_1H, SH_60B, WingLoong_I, - Hercules - ], - Transport: [ - IL_76MD, - An_26B, - An_30M, - Yak_40, - C_130 + Hercules, ], + Transport: [IL_76MD, An_26B, An_30M, Yak_40, C_130], Refueling: [ IL_78M, KC_135, @@ -798,12 +836,7 @@ UNIT_BY_TASK = { S_3B_Tanker, KC135MPRS, ], - AWACS: [ - E_3A, - E_2C, - A_50, - KJ_2000 - ], + AWACS: [E_3A, E_2C, A_50, KJ_2000], PinpointStrike: [ Armor.APC_MTLB, Armor.APC_MTLB, @@ -851,7 +884,6 @@ UNIT_BY_TASK = { Armor.MBT_T_80U, Armor.MBT_T_90, Armor.ZTZ_96B, - Armor.APC_Cobra, Armor.APC_Cobra, Armor.APC_Cobra, @@ -895,7 +927,6 @@ UNIT_BY_TASK = { Armor.MBT_Leopard_2, Armor.MBT_Challenger_II, Armor.MBT_Merkava_Mk__4, - Armor.MT_Pz_Kpfw_V_Panther_Ausf_G, Armor.MT_Pz_Kpfw_IV_Ausf_H, Armor.HT_Pz_Kpfw_VI_Tiger_I, @@ -945,7 +976,6 @@ UNIT_BY_TASK = { Artillery.Sturmpanzer_IV_Brummbär, Armor.Daimler_Armoured_Car, Armor.LT_Mk_VII_Tetrarch, - Artillery.MLRS_M270, Artillery.SPH_M109_Paladin, Artillery.SPH_2S9_Nona, @@ -959,7 +989,6 @@ UNIT_BY_TASK = { Artillery.SpGH_Dana, Artillery.M12_GMC, Artillery.Sturmpanzer_IV_Brummbär, - AirDefence.AAA_ZU_23_on_Ural_375, AirDefence.AAA_ZU_23_Insurgent_on_Ural_375, AirDefence.AAA_ZSU_57_2, @@ -983,12 +1012,10 @@ UNIT_BY_TASK = { AirDefence.AAA_Bofors_40mm, AirDefence.AAA_M1_37mm, AirDefence.AA_gun_QF_3_7, - frenchpack.DIM__TOYOTA_BLUE, frenchpack.DIM__TOYOTA_DESERT, frenchpack.DIM__TOYOTA_GREEN, frenchpack.DIM__KAMIKAZE, - frenchpack.AMX_10RCR, frenchpack.AMX_10RCR_SEPAR, frenchpack.ERC_90, @@ -1006,15 +1033,29 @@ UNIT_BY_TASK = { frenchpack.DIM__TOYOTA_GREEN, frenchpack.DIM__TOYOTA_DESERT, frenchpack.DIM__KAMIKAZE, - ], - AirDefence: [ + AirDefence: [], + Reconnaissance: [ + Unarmed.Transport_M818, + Unarmed.Transport_Ural_375, + Unarmed.Transport_UAZ_469, + ], + Nothing: [ + Infantry.Infantry_M4, + Infantry.Soldier_AK, ], - Reconnaissance: [Unarmed.Transport_M818, Unarmed.Transport_Ural_375, Unarmed.Transport_UAZ_469], - Nothing: [Infantry.Infantry_M4, Infantry.Soldier_AK, ], Embarking: [], - Carriage: [CVN_74_John_C__Stennis, LHA_1_Tarawa, CV_1143_5_Admiral_Kuznetsov, ], - CargoTransportation: [Dry_cargo_ship_Ivanov, Bulk_cargo_ship_Yakushev, Tanker_Elnya_160, Armed_speedboat, ] + Carriage: [ + CVN_74_John_C__Stennis, + LHA_1_Tarawa, + CV_1143_5_Admiral_Kuznetsov, + ], + CargoTransportation: [ + Dry_cargo_ship_Ivanov, + Bulk_cargo_ship_Yakushev, + Tanker_Elnya_160, + Armed_speedboat, + ], } """ @@ -1022,7 +1063,6 @@ Units from AirDefense category of UNIT_BY_TASK that will be removed from use if """ SAM_BAN = [ AirDefence.SAM_Linebacker_M6, - AirDefence.SAM_SA_9_Strela_1_9P31, AirDefence.SAM_SA_8_Osa_9A33, AirDefence.SAM_SA_19_Tunguska_2S6, @@ -1051,20 +1091,19 @@ SAM_CONVERT = { AirDefence.SAM_Hawk_TR_AN_MPQ_46: AirDefence.SAM_Hawk_PCP, AirDefence.SAM_Hawk_SR_AN_MPQ_50: AirDefence.SAM_Hawk_PCP, AirDefence.SAM_Hawk_LN_M192: AirDefence.SAM_Hawk_PCP, - 'except': { + "except": { # this radar is shared between the two S300's. if we attempt to find a SAM site at a base and can't find one # model, we can safely assume the other was deployed # well, perhaps not safely, but we'll make the assumption anyway :p AirDefence.SAM_SA_10_S_300PS_TR_30N6: AirDefence.SAM_SA_10_S_300PS_CP_54K6, - AirDefence.SAM_SR_P_19: AirDefence.SAM_SA_2_LN_SM_90 - } + AirDefence.SAM_SR_P_19: AirDefence.SAM_SA_2_LN_SM_90, + }, } """ Units that will always be spawned in the air """ -TAKEOFF_BAN: List[Type[FlyingType]] = [ -] +TAKEOFF_BAN: List[Type[FlyingType]] = [] """ Units that will be always spawned in the air if launched from the carrier @@ -1135,19 +1174,18 @@ EXPANDED_TASK_PAYLOAD_OVERRIDE = { "BARCAP": ("CAP HEAVY", "CAP"), "CAS": ("CAS MAVERICK F", "CAS"), "INTERCEPTION": ("CAP HEAVY", "CAP"), - "STRIKE": ("STRIKE",), + "STRIKE": ("STRIKE",), "ANTISHIP": ("ANTISHIP",), "SEAD": ("SEAD",), "DEAD": ("SEAD",), "ESCORT": ("CAP HEAVY", "CAP"), - "BAI": ( "BAI", "CAS MAVERICK F", "CAS"), + "BAI": ("BAI", "CAS MAVERICK F", "CAS"), "SWEEP": ("CAP HEAVY", "CAP"), - "OCA_RUNWAY": ("RUNWAY_ATTACK","RUNWAY_STRIKE","STRIKE"), - "OCA_AIRCRAFT": ("OCA","CAS MAVERICK F", "CAS") + "OCA_RUNWAY": ("RUNWAY_ATTACK", "RUNWAY_STRIKE", "STRIKE"), + "OCA_AIRCRAFT": ("OCA", "CAS MAVERICK F", "CAS"), } PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = { - B_1B: COMMON_OVERRIDE, B_52H: COMMON_OVERRIDE, F_117A: COMMON_OVERRIDE, @@ -1254,11 +1292,9 @@ PLANE_PAYLOAD_OVERRIDES: Dict[Type[PlaneType], Dict[Type[Task], str]] = { AH_64A: COMMON_OVERRIDE, SH_60B: COMMON_OVERRIDE, Hercules: COMMON_OVERRIDE, - Su_25TM: { SEAD: "Kh-31P*2_Kh-25ML*4_R-73*2_L-081_MPS410", }, - } """ @@ -1319,9 +1355,17 @@ TIME_PERIODS = { } REWARDS = { - "power": 4, "warehouse": 2, "ware": 2, "fuel": 2, "ammo": 2, - "farp": 1, "fob": 1, "factory": 10, "comms": 10, "oil": 10, - "derrick": 8 + "power": 4, + "warehouse": 2, + "ware": 2, + "fuel": 2, + "ammo": 2, + "farp": 1, + "fob": 1, + "factory": 10, + "comms": 10, + "oil": 10, + "derrick": 8, } CARRIER_CAPABLE = [ @@ -1334,7 +1378,6 @@ CARRIER_CAPABLE = [ Rafale_M, S_3B, E_2C, - UH_1H, Mi_8MT, Ka_50, @@ -1342,7 +1385,6 @@ CARRIER_CAPABLE = [ OH_58D, UH_60A, SH_60B, - SA342L, SA342M, SA342Minigun, @@ -1351,7 +1393,6 @@ CARRIER_CAPABLE = [ LHA_CAPABLE = [ AV8BNA, - UH_1H, Mi_8MT, Ka_50, @@ -1359,11 +1400,10 @@ LHA_CAPABLE = [ OH_58D, UH_60A, SH_60B, - SA342L, SA342M, SA342Minigun, - SA342Mistral + SA342Mistral, ] """ @@ -1422,27 +1462,50 @@ def find_unittype(for_task: Task, country_name: str) -> List[Type[UnitType]]: MANPADS: List[VehicleType] = [ AirDefence.SAM_SA_18_Igla_MANPADS, AirDefence.SAM_SA_18_Igla_S_MANPADS, - AirDefence.Stinger_MANPADS + AirDefence.Stinger_MANPADS, ] INFANTRY: List[VehicleType] = [ - 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.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, + Infantry.Infantry_M4, + Infantry.Infantry_M4, + Infantry.Infantry_M4, + Infantry.Infantry_M4, + Infantry.Infantry_M4, Infantry.Soldier_M249, Artillery._2B11_mortar, - Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, + Infantry.Soldier_AK, + Infantry.Soldier_AK, + Infantry.Soldier_AK, + Infantry.Soldier_AK, + Infantry.Soldier_AK, Infantry.Paratrooper_RPG_16, - Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, - Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, + Infantry.Georgian_soldier_with_M4, + Infantry.Georgian_soldier_with_M4, + Infantry.Georgian_soldier_with_M4, Infantry.Infantry_Soldier_Rus, - Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1, - Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, + Infantry.Infantry_Soldier_Rus, + Infantry.Infantry_Soldier_Rus, + Infantry.Infantry_Soldier_Rus, + Infantry.Infantry_SMLE_No_4_Mk_1, + Infantry.Infantry_SMLE_No_4_Mk_1, + Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_Mauser_98, - 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_Mauser_98, + Infantry.Infantry_Mauser_98, + Infantry.Infantry_Mauser_98, + Infantry.Infantry_M1_Garand, + Infantry.Infantry_M1_Garand, + Infantry.Infantry_M1_Garand, + Infantry.Infantry_Soldier_Insurgents, + Infantry.Infantry_Soldier_Insurgents, + Infantry.Infantry_Soldier_Insurgents, ] @@ -1465,6 +1528,7 @@ def unit_type_name(unit_type) -> str: def unit_type_name_2(unit_type) -> str: return unit_type.name and unit_type.name or unit_type.id + def unit_get_expanded_info(country_name: str, unit_type, request_type: str) -> str: original_name = unit_type.name and unit_type.name or unit_type.id default_value = None @@ -1493,6 +1557,7 @@ def unit_get_expanded_info(country_name: str, unit_type, request_type: str) -> s return default_value return faction_value + def unit_type_from_name(name: str) -> Optional[Type[UnitType]]: if name in vehicle_map: return vehicle_map[name] @@ -1526,9 +1591,13 @@ def task_name(task) -> str: 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 +) -> List[UnitType]: 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]) idx = int(len(suitable_unittypes) * factor) @@ -1576,7 +1645,10 @@ def unitdict_restrict_count(unit_dict: UnitsDict, total_count: int) -> UnitsDict def assigned_units_split(fd: AssignedUnitsDict) -> 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()}, + ) def assigned_units_from(d: PlaneDict) -> AssignedUnitsDict: @@ -1620,7 +1692,9 @@ def _validate_db(): total_set = set() for t, unit_collection in UNIT_BY_TASK.items(): for unit_type in set(unit_collection): - assert unit_type not in total_set, "{} is duplicate for task {}".format(unit_type, t) + assert unit_type not in total_set, "{} is duplicate for task {}".format( + unit_type, t + ) total_set.add(unit_type) # check prices diff --git a/game/debriefing.py b/game/debriefing.py index 59477ba4..c0221838 100644 --- a/game/debriefing.py +++ b/game/debriefing.py @@ -96,13 +96,14 @@ class StateData: # them when they've already dead. Dedup. killed_ground_units=list(set(data["killed_ground_units"])), destroyed_statics=data["destroyed_objects_positions"], - base_capture_events=data["base_capture_events"] + base_capture_events=data["base_capture_events"], ) class Debriefing: - def __init__(self, state_data: Dict[str, Any], game: Game, - unit_map: UnitMap) -> None: + def __init__( + self, state_data: Dict[str, Any], game: Game, unit_map: UnitMap + ) -> None: self.state_data = StateData.from_json(state_data) self.unit_map = unit_map @@ -135,12 +136,9 @@ class Debriefing: yield from self.ground_losses.enemy_airfields def casualty_count(self, control_point: ControlPoint) -> int: - return len( - [x for x in self.front_line_losses if x.origin == control_point] - ) + return len([x for x in self.front_line_losses if x.origin == control_point]) - def front_line_losses_by_type( - self, player: bool) -> Dict[Type[UnitType], int]: + def front_line_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]: losses_by_type: Dict[Type[UnitType], int] = defaultdict(int) if player: losses = self.ground_losses.player_front_line @@ -221,8 +219,10 @@ class Debriefing: # deaths, so we expect to see quite a few unclaimed dead ground # units. We should start tracking those and covert this to a # warning. - logging.debug(f"Death of untracked ground unit {unit_name} will " - "have no effect. This may be normal behavior.") + logging.debug( + f"Death of untracked ground unit {unit_name} will " + "have no effect. This may be normal behavior." + ) return losses @@ -234,15 +234,16 @@ class Debriefing: for idx, base in enumerate(i.split("||")[0] for i in reversed_captures): if base not in [x[1] for x in last_base_cap_indexes]: last_base_cap_indexes.append((idx, base)) - return [reversed_captures[idx[0]] for idx in last_base_cap_indexes] + return [reversed_captures[idx[0]] for idx in last_base_cap_indexes] class PollDebriefingFileThread(threading.Thread): """Thread class with a stop() method. The thread itself has to check regularly for the stopped() condition.""" - def __init__(self, callback: Callable[[Debriefing], None], - game: Game, unit_map: UnitMap) -> None: + def __init__( + self, callback: Callable[[Debriefing], None], game: Game, unit_map: UnitMap + ) -> None: super().__init__() self._stop_event = threading.Event() self.callback = callback @@ -261,7 +262,10 @@ class PollDebriefingFileThread(threading.Thread): else: last_modified = 0 while not self.stopped(): - if os.path.isfile("state.json") and os.path.getmtime("state.json") > last_modified: + if ( + os.path.isfile("state.json") + and os.path.getmtime("state.json") > last_modified + ): with open("state.json", "r") as json_file: json_data = json.load(json_file) debriefing = Debriefing(json_data, self.game, self.unit_map) @@ -270,8 +274,9 @@ class PollDebriefingFileThread(threading.Thread): time.sleep(5) -def wait_for_debriefing(callback: Callable[[Debriefing], None], - game: Game, unit_map) -> PollDebriefingFileThread: +def wait_for_debriefing( + callback: Callable[[Debriefing], None], game: Game, unit_map +) -> PollDebriefingFileThread: thread = PollDebriefingFileThread(callback, game, unit_map) thread.start() return thread diff --git a/game/event/event.py b/game/event/event.py index 92e646b8..ea7f0e17 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -37,7 +37,15 @@ class Event: to_cp = None # type: ControlPoint difficulty = 1 # type: int - 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.from_cp = from_cp self.to_cp = target_cp @@ -57,12 +65,14 @@ class Event: Operation.prepare(self.game) unit_map = Operation.generate() Operation.current_mission.save( - persistency.mission_path_for("liberation_nextturn.miz")) + persistency.mission_path_for("liberation_nextturn.miz") + ) return unit_map @staticmethod - def _transfer_aircraft(ato: AirTaskingOrder, losses: AirLosses, - for_player: bool) -> None: + def _transfer_aircraft( + ato: AirTaskingOrder, losses: AirLosses, for_player: bool + ) -> None: for package in ato.packages: for flight in package.flights: # No need to transfer to the same location. @@ -77,13 +87,16 @@ class Event: if flight.arrival.captured != for_player: logging.info( f"Not transferring {flight} because {flight.arrival} " - "was captured") + "was captured" + ) continue transfer_count = losses.surviving_flight_members(flight) if transfer_count < 0: - logging.error(f"{flight} had {flight.count} aircraft but " - f"{transfer_count} losses were recorded.") + logging.error( + f"{flight} had {flight.count} aircraft but " + f"{transfer_count} losses were recorded." + ) continue aircraft = flight.unit_type @@ -91,7 +104,8 @@ class Event: if available < transfer_count: logging.error( f"Found killed {aircraft} from {flight.departure} but " - f"that airbase has only {available} available.") + f"that airbase has only {available} available." + ) continue flight.departure.base.aircraft[aircraft] -= transfer_count @@ -101,10 +115,12 @@ class Event: flight.arrival.base.aircraft[aircraft] += transfer_count def complete_aircraft_transfers(self, debriefing: Debriefing) -> None: - self._transfer_aircraft(self.game.blue_ato, debriefing.air_losses, - for_player=True) - self._transfer_aircraft(self.game.red_ato, debriefing.air_losses, - for_player=False) + self._transfer_aircraft( + self.game.blue_ato, debriefing.air_losses, for_player=True + ) + self._transfer_aircraft( + self.game.red_ato, debriefing.air_losses, for_player=False + ) @staticmethod def commit_air_losses(debriefing: Debriefing) -> None: @@ -115,7 +131,8 @@ class Event: if available <= 0: logging.error( f"Found killed {aircraft} from {cp} but that airbase has " - "none available.") + "none available." + ) continue logging.info(f"{aircraft} destroyed from {cp}") @@ -130,7 +147,8 @@ class Event: if available <= 0: logging.error( f"Found killed {unit_type} from {control_point} but that " - "airbase has none available.") + "airbase has none available." + ) continue logging.info(f"{unit_type} destroyed from {control_point}") @@ -149,11 +167,14 @@ class Event: def commit_building_losses(self, debriefing: Debriefing) -> None: for loss in debriefing.building_losses: loss.ground_object.kill() - self.game.informations.append(Information( - "Building destroyed", - f"{loss.ground_object.dcs_identifier} has been destroyed at " - f"location {loss.ground_object.obj_name}", self.game.turn - )) + self.game.informations.append( + Information( + "Building destroyed", + f"{loss.ground_object.dcs_identifier} has been destroyed at " + f"location {loss.ground_object.obj_name}", + self.game.turn, + ) + ) @staticmethod def commit_damaged_runways(debriefing: Debriefing) -> None: @@ -171,9 +192,9 @@ class Event: # ------------------------------ # Captured bases - #if self.game.player_country in db.BLUEFOR_FACTIONS: - coalition = 2 # Value in DCS mission event for BLUE - #else: + # if self.game.player_country in db.BLUEFOR_FACTIONS: + coalition = 2 # Value in DCS mission event for BLUE + # else: # coalition = 1 # Value in DCS mission event for RED for captured in debriefing.base_capture_events: @@ -187,12 +208,22 @@ class Event: if cp.captured and new_owner_coalition != coalition: for_player = 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) 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 - 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) captured_cps.append(cp) else: @@ -218,7 +249,12 @@ class Event: for cp in self.game.theater.player_points(): enemy_cps = [e for e in cp.connected_points if not e.captured] 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 player_won = True @@ -234,7 +270,11 @@ class Event: ratio = (1.0 + enemy_casualties) / (1.0 + ally_casualties) - player_aggresive = cp.stances[enemy_cp.id] in [CombatStance.AGGRESSIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH] + player_aggresive = cp.stances[enemy_cp.id] in [ + CombatStance.AGGRESSIVE, + CombatStance.ELIMINATION, + CombatStance.BREAKTHROUGH, + ] if ally_units_alive == 0: player_won = False @@ -259,11 +299,17 @@ class Event: delta = DEFEAT_INFLUENCE elif ally_casualties > enemy_casualties: - if ally_units_alive > 2*enemy_units_alive and player_aggresive: + if ( + ally_units_alive > 2 * enemy_units_alive + and player_aggresive + ): # Even with casualties if the enemy is overwhelmed, they are going to lose ground player_won = True delta = MINOR_DEFEAT_INFLUENCE - elif ally_units_alive > 3*enemy_units_alive and player_aggresive: + elif ( + ally_units_alive > 3 * enemy_units_alive + and player_aggresive + ): player_won = True delta = STRONG_DEFEAT_INFLUENCE else: @@ -275,7 +321,10 @@ class Event: delta = STRONG_DEFEAT_INFLUENCE # No progress with defensive strategies - if player_won and cp.stances[enemy_cp.id] in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]: + if player_won and cp.stances[enemy_cp.id] in [ + CombatStance.DEFENSIVE, + CombatStance.AMBUSH, + ]: print("Defensive stance, progress is limited") delta = MINOR_DEFEAT_INFLUENCE @@ -283,28 +332,40 @@ class Event: print(cp.name + " won ! factor > " + str(delta)) cp.base.affect_strength(delta) enemy_cp.base.affect_strength(-delta) - info = Information("Frontline Report", - "Our ground forces from " + cp.name + " are making progress toward " + enemy_cp.name, - self.game.turn) + info = Information( + "Frontline Report", + "Our ground forces from " + + cp.name + + " are making progress toward " + + enemy_cp.name, + self.game.turn, + ) self.game.informations.append(info) else: print(cp.name + " lost ! factor > " + str(delta)) enemy_cp.base.affect_strength(delta) cp.base.affect_strength(-delta) - info = Information("Frontline Report", - "Our ground forces from " + cp.name + " are losing ground against the enemy forces from " + enemy_cp.name, - self.game.turn) + info = Information( + "Frontline Report", + "Our ground forces from " + + cp.name + + " are losing ground against the enemy forces from " + + enemy_cp.name, + self.game.turn, + ) self.game.informations.append(info) def redeploy_units(self, cp: ControlPoint) -> None: - """" + """ " Auto redeploy units to newly captured base """ - ally_connected_cps = [ocp for ocp in cp.connected_points if - cp.captured == ocp.captured] - enemy_connected_cps = [ocp for ocp in cp.connected_points if - cp.captured != ocp.captured] + ally_connected_cps = [ + ocp for ocp in cp.connected_points if cp.captured == ocp.captured + ] + enemy_connected_cps = [ + ocp for ocp in cp.connected_points if cp.captured != ocp.captured + ] # If the newly captured cp does not have enemy connected cp, # then it is not necessary to redeploy frontline units there. @@ -315,8 +376,7 @@ class Event: for ally_cp in ally_connected_cps: self.redeploy_between(cp, ally_cp) - def redeploy_between(self, destination: ControlPoint, - source: ControlPoint) -> None: + def redeploy_between(self, destination: ControlPoint, source: ControlPoint) -> None: total_units_redeployed = 0 moved_units = {} @@ -333,8 +393,7 @@ class Event: for frontline_unit, count in source.base.armor.items(): moved_units[frontline_unit] = int(count * move_factor) - total_units_redeployed = total_units_redeployed + int( - count * move_factor) + total_units_redeployed = total_units_redeployed + int(count * move_factor) destination.base.commision_units(moved_units) source.base.commit_losses(moved_units) @@ -362,7 +421,6 @@ class Event: class UnitsDeliveryEvent: - def __init__(self, control_point: ControlPoint) -> None: self.to_cp = control_point self.units: Dict[Type[UnitType], int] = {} @@ -390,8 +448,7 @@ class UnitsDeliveryEvent: logging.error(f"Could not refund {unit_type.id}, price unknown") continue - logging.info( - f"Refunding {count} {unit_type.id} at {self.to_cp.name}") + logging.info(f"Refunding {count} {unit_type.id} at {self.to_cp.name}") game.adjust_budget(price * count, player=self.to_cp.captured) def available_next_turn(self, unit_type: Type[UnitType]) -> int: @@ -409,13 +466,13 @@ class UnitsDeliveryEvent: aircraft = unit_type.id name = self.to_cp.name if count >= 0: - bought_units[unit_type] = count + bought_units[unit_type] = count game.message( - f"{coalition} reinforcements: {aircraft} x {count} at {name}") + f"{coalition} reinforcements: {aircraft} x {count} at {name}" + ) else: sold_units[unit_type] = -count - game.message( - f"{coalition} sold: {aircraft} x {-count} at {name}") + game.message(f"{coalition} sold: {aircraft} x {-count} at {name}") self.to_cp.base.commision_units(bought_units) self.to_cp.base.commit_losses(sold_units) self.units = {} diff --git a/game/event/frontlineattack.py b/game/event/frontlineattack.py index 6dab825d..d7749a2a 100644 --- a/game/event/frontlineattack.py +++ b/game/event/frontlineattack.py @@ -7,5 +7,6 @@ class FrontlineAttackEvent(Event): Currently the same as its parent, but here for legacy compatibility as well as to allow for future unique Event handling """ + def __str__(self): return "Frontline attack" diff --git a/game/factions/faction.py b/game/factions/faction.py index 0c6371f0..b1cecc55 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -10,8 +10,18 @@ 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 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 @@ -109,8 +119,7 @@ class Faction: building_set: List[str] = field(default_factory=list) # List of default livery overrides - liveries_overrides: Dict[Type[UnitType], List[str]] = field( - default_factory=dict) + liveries_overrides: Dict[Type[UnitType], List[str]] = field(default_factory=dict) #: Set to True if the faction should force the "Unrestricted satnav" option #: for the mission. This option enables GPS for capable aircraft regardless @@ -128,7 +137,11 @@ class 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)) + raise AssertionError( + 'Faction\'s country ("{}") is not a valid DCS country ID'.format( + faction.country + ) + ) faction.name = json.get("name", "") if not faction.name: @@ -141,14 +154,10 @@ class Faction: 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.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.ewrs = json.get("ewrs", []) @@ -163,13 +172,10 @@ class Faction: faction.requirements = json.get("requirements", {}) faction.carrier_names = json.get("carrier_names", []) - faction.helicopter_carrier_names = json.get( - "helicopter_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.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) @@ -220,13 +226,18 @@ class Faction: @property def units(self) -> List[Type[UnitType]]: - return (self.infantry_units + self.aircrafts + self.awacs + - self.artillery_units + self.frontline_units + - self.tankers + self.logistics_units) + 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[Type[UnitType]]: +def unit_loader(unit: str, class_repository: List[Any]) -> Optional[Type[UnitType]]: """ Find unit by name :param unit: Unit name as string @@ -250,9 +261,10 @@ def unit_loader( def load_aircraft(name: str) -> Optional[Type[FlyingType]]: - return cast(Optional[FlyingType], unit_loader( - name, [dcs.planes, dcs.helicopters, MODDED_AIRPLANES] - )) + return cast( + Optional[FlyingType], + unit_loader(name, [dcs.planes, dcs.helicopters, MODDED_AIRPLANES]), + ) def load_all_aircraft(data) -> List[Type[FlyingType]]: @@ -265,9 +277,12 @@ def load_all_aircraft(data) -> List[Type[FlyingType]]: def load_vehicle(name: str) -> Optional[Type[VehicleType]]: - return cast(Optional[FlyingType], unit_loader( - name, [Infantry, Unarmed, Armor, AirDefence, Artillery, MODDED_VEHICLES] - )) + return cast( + Optional[FlyingType], + unit_loader( + name, [Infantry, Unarmed, Armor, AirDefence, Artillery, MODDED_VEHICLES] + ), + ) def load_all_vehicles(data) -> List[Type[VehicleType]]: diff --git a/game/game.py b/game/game.py index 2ec47e4d..743a5380 100644 --- a/game/game.py +++ b/game/game.py @@ -78,10 +78,16 @@ class TurnState(Enum): class Game: - def __init__(self, player_name: str, enemy_name: str, - theater: ConflictTheater, start_date: datetime, - settings: Settings, player_budget: float, - enemy_budget: float) -> None: + def __init__( + self, + player_name: str, + enemy_name: str, + theater: ConflictTheater, + start_date: datetime, + settings: Settings, + player_budget: float, + enemy_budget: float, + ) -> None: self.settings = settings self.events: List[Event] = [] self.theater = theater @@ -112,9 +118,7 @@ class Game: self.blue_ato = AirTaskingOrder() self.red_ato = AirTaskingOrder() - self.aircraft_inventory = GlobalAircraftInventory( - self.theater.controlpoints - ) + self.aircraft_inventory = GlobalAircraftInventory(self.theater.controlpoints) self.sanitize_sides() @@ -147,8 +151,9 @@ class Game: self.on_load() def generate_conditions(self) -> Conditions: - return Conditions.generate(self.theater, self.date, - self.current_turn_time_of_day, self.settings) + return Conditions.generate( + self.theater, self.date, self.current_turn_time_of_day, self.settings + ) def sanitize_sides(self): """ @@ -184,13 +189,24 @@ class Game: return random.randint(1, 100) <= prob * mult def _generate_player_event(self, event_class, player_cp, enemy_cp): - 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): for front_line in self.theater.conflicts(True): - self._generate_player_event(FrontlineAttackEvent, - front_line.control_point_a, - front_line.control_point_b) + self._generate_player_event( + FrontlineAttackEvent, + front_line.control_point_a, + front_line.control_point_b, + ) def adjust_budget(self, amount: float, player: bool) -> None: if player: @@ -208,7 +224,7 @@ class Game: self.enemy_budget += Income(self, player=False).total def initiate_event(self, event: Event) -> UnitMap: - #assert event in self.events + # assert event in self.events logging.info("Generating {} (regular)".format(event)) return event.generate() @@ -223,7 +239,11 @@ class Game: def is_player_attack(self, event): if isinstance(event, Event): - return event and event.attacker_name and event.attacker_name == self.player_name + return ( + event + and event.attacker_name + and event.attacker_name == self.player_name + ) else: raise RuntimeError(f"{event} was passed when an Event type was expected") @@ -235,7 +255,9 @@ class Game: def pass_turn(self, no_action: bool = False) -> None: 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 for control_point in self.theater.controlpoints: @@ -281,7 +303,7 @@ class Game: # Check for win or loss condition turn_state = self.check_win_loss() - if turn_state in (TurnState.LOSS,TurnState.WIN): + if turn_state in (TurnState.LOSS, TurnState.WIN): return self.process_win_loss(turn_state) # Plan flights & combat for next turn @@ -305,8 +327,11 @@ class Game: self.plan_procurement(blue_planner, red_planner) - def plan_procurement(self, blue_planner: CoalitionMissionPlanner, - red_planner: CoalitionMissionPlanner) -> None: + def plan_procurement( + self, + blue_planner: CoalitionMissionPlanner, + red_planner: CoalitionMissionPlanner, + ) -> None: # The first turn needs to buy a *lot* of aircraft to fill CAPs, so it # gets much more of the budget that turn. Otherwise budget (after # repairs) is split evenly between air and ground. For the default @@ -320,7 +345,7 @@ class Game: manage_runways=self.settings.automate_runway_repair, manage_front_line=self.settings.automate_front_line_reinforcements, manage_aircraft=self.settings.automate_aircraft_reinforcements, - front_line_budget_share=ground_portion + front_line_budget_share=ground_portion, ).spend_budget(self.budget, blue_planner.procurement_requests) self.enemy_budget = ProcurementAi( @@ -330,7 +355,7 @@ class Game: manage_runways=True, manage_front_line=True, manage_aircraft=True, - front_line_budget_share=ground_portion + front_line_budget_share=ground_portion, ).spend_budget(self.enemy_budget, red_planner.procurement_requests) def message(self, text: str) -> None: @@ -361,10 +386,12 @@ class Game: def compute_threat_zones(self) -> None: self.blue_threat_zone = ThreatZones.for_faction(self, player=True) self.red_threat_zone = ThreatZones.for_faction(self, player=False) - self.blue_navmesh = NavMesh.from_threat_zones(self.red_threat_zone, - self.theater) - self.red_navmesh = NavMesh.from_threat_zones(self.blue_threat_zone, - self.theater) + self.blue_navmesh = NavMesh.from_threat_zones( + self.red_threat_zone, self.theater + ) + self.red_navmesh = NavMesh.from_threat_zones( + self.blue_threat_zone, self.theater + ) def threat_zone_for(self, player: bool) -> ThreatZones: if player: @@ -386,9 +413,9 @@ class Game: # By default, use the existing frontline conflict position for front_line in self.theater.conflicts(): - position = Conflict.frontline_position(front_line.control_point_a, - front_line.control_point_b, - self.theater) + position = Conflict.frontline_position( + front_line.control_point_a, front_line.control_point_b, self.theater + ) zones.append(position[0]) zones.append(front_line.control_point_a.position) zones.append(front_line.control_point_b.position) @@ -413,7 +440,10 @@ class Game: d = cp.position.distance_to_point(cp2.position) if d < min_distance: min_distance = d - cpoint = Point((cp.position.x + cp2.position.x) / 2, (cp.position.y + cp2.position.y) / 2) + cpoint = Point( + (cp.position.x + cp2.position.x) / 2, + (cp.position.y + cp2.position.y) / 2, + ) zones.append(cp.position) zones.append(cp2.position) break @@ -422,8 +452,7 @@ class Game: if cpoint is not None: zones.append(cpoint) - packages = itertools.chain(self.blue_ato.packages, - self.red_ato.packages) + packages = itertools.chain(self.blue_ato.packages, self.red_ato.packages) for package in packages: if package.primary_task is FlightType.BARCAP: # BARCAPs will be planned at most locations on smaller theaters, @@ -460,7 +489,10 @@ class Game: return False else: for z in self.__culling_zones: - if z.distance_to_point(pos) < self.settings.perf_culling_distance * 1000: + if ( + z.distance_to_point(pos) + < self.settings.perf_culling_distance * 1000 + ): return False for p in self.__culling_points: if p.distance_to_point(pos) < 2500: @@ -502,6 +534,10 @@ class Game: def process_win_loss(self, turn_state: TurnState): if turn_state is TurnState.WIN: - return self.message("Congratulations, you are victorious! Start a new campaign to continue.") + return self.message( + "Congratulations, you are victorious! Start a new campaign to continue." + ) elif turn_state is TurnState.LOSS: - return self.message("Game Over, you lose. Start a new campaign to continue.") + return self.message( + "Game Over, you lose. Start a new campaign to continue." + ) diff --git a/game/income.py b/game/income.py index d4e05993..dd2be887 100644 --- a/game/income.py +++ b/game/income.py @@ -46,10 +46,10 @@ class Income: for tgo in tgos: if not tgo.is_dead: count += 1 - self.buildings.append(BuildingIncome(name, category, count, - REWARDS[category])) + self.buildings.append( + BuildingIncome(name, category, count, REWARDS[category]) + ) self.from_bases = sum(cp.income_per_turn for cp in self.control_points) self.total_buildings = sum(b.income for b in self.buildings) - self.total = ((self.total_buildings + self.from_bases) * - self.multiplier) + self.total = (self.total_buildings + self.from_bases) * self.multiplier diff --git a/game/infos/information.py b/game/infos/information.py index 35e94f92..1c132d46 100644 --- a/game/infos/information.py +++ b/game/infos/information.py @@ -1,7 +1,7 @@ import datetime -class Information(): +class Information: def __init__(self, title="", text="", turn=0): self.title = title self.text = text @@ -9,9 +9,11 @@ class Information(): self.timestamp = datetime.datetime.now() def __str__(self): - return '[{}][{}] {} {}'.format( - self.timestamp.strftime("%Y-%m-%d %H:%M:%S") if self.timestamp is not None else '', + return "[{}][{}] {} {}".format( + self.timestamp.strftime("%Y-%m-%d %H:%M:%S") + if self.timestamp is not None + else "", self.turn, self.title, - self.text - ) \ No newline at end of file + self.text, + ) diff --git a/game/inventory.py b/game/inventory.py index b369ad8b..651b80a8 100644 --- a/game/inventory.py +++ b/game/inventory.py @@ -79,6 +79,7 @@ class ControlPointAircraftInventory: 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 @@ -100,8 +101,8 @@ class GlobalAircraftInventory: inventory.add_aircraft(aircraft, count) def for_control_point( - self, - control_point: ControlPoint) -> ControlPointAircraftInventory: + self, control_point: ControlPoint + ) -> ControlPointAircraftInventory: """Returns the inventory specific to the given control point.""" return self.inventories[control_point] diff --git a/game/models/destroyed_units.py b/game/models/destroyed_units.py index ade75cc4..7d0de042 100644 --- a/game/models/destroyed_units.py +++ b/game/models/destroyed_units.py @@ -7,8 +7,7 @@ class DestroyedUnit: y: int name: str - def __init__(self, x , y, name): + def __init__(self, x, y, name): self.x = x self.y = y self.name = name - diff --git a/game/models/frontline_data.py b/game/models/frontline_data.py index 586ebd58..f60d5ccd 100644 --- a/game/models/frontline_data.py +++ b/game/models/frontline_data.py @@ -6,7 +6,7 @@ class FrontlineData: This Data structure will store information about an existing frontline """ - def __init__(self, from_cp:ControlPoint, to_cp: ControlPoint): + def __init__(self, from_cp: ControlPoint, to_cp: ControlPoint): self.to_cp = to_cp self.from_cp = from_cp self.enemy_units_position = [] diff --git a/game/models/game_stats.py b/game/models/game_stats.py index e6d628f4..a4d8e623 100644 --- a/game/models/game_stats.py +++ b/game/models/game_stats.py @@ -1,5 +1,6 @@ from typing import List + class FactionTurnMetadata: """ Store metadata about a faction @@ -20,8 +21,8 @@ class GameTurnMetadata: Store metadata about a game turn """ - allied_units:FactionTurnMetadata - enemy_units:FactionTurnMetadata + allied_units: FactionTurnMetadata + enemy_units: FactionTurnMetadata def __init__(self): self.allied_units = FactionTurnMetadata() @@ -53,4 +54,3 @@ class GameStats: turn_data.enemy_units.vehicles_count += sum(cp.base.armor.values()) self.data_per_turn.append(turn_data) - diff --git a/game/navmesh.py b/game/navmesh.py index c1ac6ab0..bcef8191 100644 --- a/game/navmesh.py +++ b/game/navmesh.py @@ -114,9 +114,11 @@ class NavMesh: return self.travel_cost(a, b) @staticmethod - def reconstruct_path(came_from: Dict[NavPoint, Optional[NavPoint]], - origin: NavPoint, - destination: NavPoint) -> List[Point]: + def reconstruct_path( + came_from: Dict[NavPoint, Optional[NavPoint]], + origin: NavPoint, + destination: NavPoint, + ) -> List[Point]: current = destination path: List[Point] = [] while current != origin: @@ -141,16 +143,14 @@ class NavMesh: raise ValueError(f"Origin point {origin} is outside the navmesh") destination_poly = self.localize(destination) if destination_poly is None: - raise ValueError( - f"Origin point {destination} is outside the navmesh") + raise ValueError(f"Origin point {destination} is outside the navmesh") return self._shortest_path( NavPoint(self.dcs_to_shapely_point(origin), origin_poly), - NavPoint(self.dcs_to_shapely_point(destination), destination_poly) + NavPoint(self.dcs_to_shapely_point(destination), destination_poly), ) - def _shortest_path(self, origin: NavPoint, - destination: NavPoint) -> List[Point]: + def _shortest_path(self, origin: NavPoint, destination: NavPoint) -> List[Point]: # Adapted from # https://www.redblobgames.com/pathfinding/a-star/implementation.py. frontier = NavFrontier() @@ -167,9 +167,7 @@ class NavMesh: if current.poly == destination.poly: # Made it to the correct nav poly. Add the leg from the border # to the target. - cost = best_known[current] + self.travel_cost( - current, destination - ) + cost = best_known[current] + self.travel_cost(current, destination) if cost < best_known[destination]: best_known[destination] = cost estimated = cost @@ -185,14 +183,10 @@ class NavMesh: raise RuntimeError _, neighbor_point = nearest_points(current.point, boundary) neighbor_nav = NavPoint(neighbor_point, neighbor) - cost = best_known[current] + self.travel_cost( - current, neighbor_nav - ) + cost = best_known[current] + self.travel_cost(current, neighbor_nav) if cost < best_known[neighbor_nav]: best_known[neighbor_nav] = cost - estimated = cost + self.travel_heuristic( - neighbor_nav, destination - ) + estimated = cost + self.travel_heuristic(neighbor_nav, destination) frontier.push(neighbor_nav, estimated) came_from[neighbor_nav] = current @@ -209,13 +203,16 @@ class NavMesh: # threatened airbases at the map edges have room to retreat from the # threat without running off the navmesh. return box(*LineString(points).bounds).buffer( - nautical_miles(100).meters, resolution=1) + nautical_miles(100).meters, resolution=1 + ) @staticmethod - def create_navpolys(polys: List[Polygon], - threat_zones: ThreatZones) -> List[NavMeshPoly]: - return [NavMeshPoly(i, p, threat_zones.threatened(p)) - for i, p in enumerate(polys)] + def create_navpolys( + polys: List[Polygon], threat_zones: ThreatZones + ) -> List[NavMeshPoly]: + return [ + NavMeshPoly(i, p, threat_zones.threatened(p)) for i, p in enumerate(polys) + ] @staticmethod def associate_neighbors(polys: List[NavMeshPoly]) -> None: @@ -234,8 +231,7 @@ class NavMesh: point = (int(x), int(y)) neighbors = {} for potential_neighbor in points_map[point]: - intersection = navpoly.poly.intersection( - potential_neighbor.poly) + intersection = navpoly.poly.intersection(potential_neighbor.poly) if not intersection.is_empty: potential_neighbor.neighbors[navpoly] = intersection neighbors[potential_neighbor] = intersection @@ -243,8 +239,9 @@ class NavMesh: points_map[point].add(navpoly) @classmethod - def from_threat_zones(cls, threat_zones: ThreatZones, - theater: ConflictTheater) -> NavMesh: + def from_threat_zones( + cls, threat_zones: ThreatZones, theater: ConflictTheater + ) -> NavMesh: # Simplify the threat poly to reduce the number of nav zones. Increase # the size of the zone and then simplify it with the buffer size as the # error margin. This will create a simpler poly around the threat zone. diff --git a/game/operation/operation.py b/game/operation/operation.py index 98aea571..fc190510 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -41,6 +41,7 @@ if TYPE_CHECKING: class Operation: """Static class for managing the final Mission generation""" + current_mission = None # type: Mission airgen = None # type: AircraftConflictGenerator triggersgen = None # type: TriggersGenerator @@ -84,7 +85,7 @@ class Operation: cls.game.enemy_name, cls.game.player_country, cls.game.enemy_country, - frontline.position + frontline.position, ) @classmethod @@ -93,7 +94,7 @@ class Operation: player_cp, enemy_cp = cls.game.theater.closest_opposing_control_points() mid_point = player_cp.position.point_from_heading( player_cp.position.heading_between_point(enemy_cp.position), - player_cp.position.distance_to_point(enemy_cp.position) / 2 + player_cp.position.distance_to_point(enemy_cp.position) / 2, ) return Conflict( cls.game.theater, @@ -103,7 +104,7 @@ class Operation: cls.game.enemy_name, cls.game.player_country, cls.game.enemy_country, - mid_point + mid_point, ) @classmethod @@ -118,9 +119,11 @@ class Operation: p_country = cls.game.player_country e_country = cls.game.enemy_country cls.current_mission.coalition["blue"].add_country( - country_dict[db.country_id_from_name(p_country)]()) + country_dict[db.country_id_from_name(p_country)]() + ) cls.current_mission.coalition["red"].add_country( - country_dict[db.country_id_from_name(e_country)]()) + country_dict[db.country_id_from_name(e_country)]() + ) @classmethod def inject_lua_trigger(cls, contents: str, comment: str) -> None: @@ -133,12 +136,11 @@ class Operation: cls.plugin_scripts.append(mnemonic) @classmethod - def inject_plugin_script(cls, plugin_mnemonic: str, script: str, - script_mnemonic: str) -> None: + def inject_plugin_script( + cls, plugin_mnemonic: str, script: str, script_mnemonic: str + ) -> None: if script_mnemonic in cls.plugin_scripts: - logging.debug( - f"Skipping already loaded {script} for {plugin_mnemonic}" - ) + logging.debug(f"Skipping already loaded {script} for {plugin_mnemonic}") else: cls.plugin_scripts.append(script_mnemonic) @@ -146,15 +148,12 @@ class Operation: script_path = Path(plugin_path, script) if not script_path.exists(): - logging.error( - f"Cannot find {script_path} for plugin {plugin_mnemonic}" - ) + logging.error(f"Cannot find {script_path} for plugin {plugin_mnemonic}") return trigger = TriggerStart(comment=f"Load {script_mnemonic}") filename = script_path.resolve() - fileref = cls.current_mission.map_resource.add_resource_file( - filename) + fileref = cls.current_mission.map_resource.add_resource_file(filename) trigger.add_action(DoScriptFile(fileref)) cls.current_mission.triggerrules.triggers.append(trigger) @@ -166,11 +165,10 @@ class Operation: jtacs: List[JtacInfo], airgen: AircraftConflictGenerator, ): - """Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings) - """ + """Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)""" gens: List[MissionInfoGenerator] = [ KneeboardGenerator(cls.current_mission, cls.game), - BriefingGenerator(cls.current_mission, cls.game) + BriefingGenerator(cls.current_mission, cls.game), ] for gen in gens: for dynamic_runway in groundobjectgen.runways.values(): @@ -208,8 +206,9 @@ class Operation: cls.radio_registry.reserve(frequency) @classmethod - def assign_channels_to_flights(cls, flights: List[FlightData], - air_support: AirSupport) -> None: + def assign_channels_to_flights( + cls, flights: List[FlightData], air_support: AirSupport + ) -> None: """Assigns preset radio channels for client flights.""" for flight in flights: if not flight.client_units: @@ -217,8 +216,7 @@ class Operation: cls.assign_channels_to_flight(flight, air_support) @staticmethod - def assign_channels_to_flight(flight: FlightData, - air_support: AirSupport) -> None: + def assign_channels_to_flight(flight: FlightData, air_support: AirSupport) -> None: """Assigns preset radio channels for a client flight.""" airframe = flight.aircraft_type @@ -234,7 +232,9 @@ class Operation: ) @classmethod - def _create_tacan_registry(cls, unique_map_frequencies: Set[RadioFrequency]) -> None: + def _create_tacan_registry( + cls, unique_map_frequencies: Set[RadioFrequency] + ) -> None: """ Dedup beacon/radio frequencies, since some maps have some frequencies used multiple times. @@ -246,13 +246,14 @@ class Operation: unique_map_frequencies.add(beacon.frequency) if beacon.is_tacan: if beacon.channel is None: - logging.error( - f"TACAN beacon has no channel: {beacon.callsign}") + logging.error(f"TACAN beacon has no channel: {beacon.callsign}") else: cls.tacan_registry.reserve(beacon.tacan_channel) @classmethod - def _create_radio_registry(cls, unique_map_frequencies: Set[RadioFrequency]) -> None: + def _create_radio_registry( + cls, unique_map_frequencies: Set[RadioFrequency] + ) -> None: cls.radio_registry = RadioRegistry() for data in AIRFIELD_DATA.values(): if data.theater == cls.game.theater.terrain.name and data.atc: @@ -270,7 +271,7 @@ class Operation: cls.game, cls.radio_registry, cls.tacan_registry, - cls.unit_map + cls.unit_map, ) cls.groundobjectgen.generate() @@ -284,10 +285,13 @@ class Operation: continue pos = Point(d["x"], d["z"]) - if utype is not None and not cls.game.position_culled(pos) and cls.game.settings.perf_destroyed_units: + if ( + utype is not None + and not cls.game.position_culled(pos) + and cls.game.settings.perf_destroyed_units + ): cls.current_mission.static_group( - country=cls.current_mission.country( - cls.game.player_country), + country=cls.current_mission.country(cls.game.player_country), name="", _type=utype, hidden=True, @@ -302,13 +306,13 @@ class Operation: cls.create_unit_map() cls.create_radio_registries() # Set mission time and weather conditions. - EnvironmentGenerator(cls.current_mission, - cls.game.conditions).generate() + EnvironmentGenerator(cls.current_mission, cls.game.conditions).generate() cls._generate_ground_units() cls._generate_destroyed_units() cls._generate_air_units() - cls.assign_channels_to_flights(cls.airgen.flights, - cls.airsupportgen.air_support) + cls.assign_channels_to_flights( + cls.airgen.flights, cls.airsupportgen.air_support + ) cls._generate_ground_conflicts() # Triggers @@ -317,14 +321,16 @@ class Operation: # Setup combined arms parameters cls.current_mission.groundControl.pilot_can_control_vehicles = cls.ca_slots > 0 - if cls.game.player_country in [country.name for country in cls.current_mission.coalition["blue"].countries.values()]: + if cls.game.player_country in [ + country.name + for country in cls.current_mission.coalition["blue"].countries.values() + ]: cls.current_mission.groundControl.blue_tactical_commander = cls.ca_slots else: cls.current_mission.groundControl.red_tactical_commander = cls.ca_slots # Options - forcedoptionsgen = ForcedOptionsGenerator( - cls.current_mission, cls.game) + forcedoptionsgen = ForcedOptionsGenerator(cls.current_mission, cls.game) forcedoptionsgen.generate() # Generate Visuals Smoke Effects @@ -341,13 +347,11 @@ class Operation: plugin.inject_scripts(cls) plugin.inject_configuration(cls) - cls.assign_channels_to_flights(cls.airgen.flights, - cls.airsupportgen.air_support) + cls.assign_channels_to_flights( + cls.airgen.flights, cls.airsupportgen.air_support + ) cls.notify_info_generators( - cls.groundobjectgen, - cls.airsupportgen, - cls.jtacs, - cls.airgen + cls.groundobjectgen, cls.airsupportgen, cls.jtacs, cls.airgen ) cls.reset_naming_ids() return cls.unit_map @@ -359,29 +363,38 @@ class Operation: # Air Support (Tanker & Awacs) assert cls.radio_registry and cls.tacan_registry cls.airsupportgen = AirSupportConflictGenerator( - cls.current_mission, cls.air_conflict(), cls.game, cls.radio_registry, - cls.tacan_registry) + cls.current_mission, + cls.air_conflict(), + cls.game, + cls.radio_registry, + cls.tacan_registry, + ) cls.airsupportgen.generate() # Generate Aircraft Activity on the map cls.airgen = AircraftConflictGenerator( - cls.current_mission, cls.game.settings, cls.game, - cls.radio_registry, cls.unit_map) + cls.current_mission, + cls.game.settings, + cls.game, + cls.radio_registry, + cls.unit_map, + ) cls.airgen.clear_parking_slots() cls.airgen.generate_flights( cls.current_mission.country(cls.game.player_country), cls.game.blue_ato, - cls.groundobjectgen.runways + cls.groundobjectgen.runways, ) cls.airgen.generate_flights( cls.current_mission.country(cls.game.enemy_country), cls.game.red_ato, - cls.groundobjectgen.runways + cls.groundobjectgen.runways, ) cls.airgen.spawn_unused_aircraft( cls.current_mission.country(cls.game.player_country), - cls.current_mission.country(cls.game.enemy_country)) + cls.current_mission.country(cls.game.enemy_country), + ) @classmethod def _generate_ground_conflicts(cls) -> None: @@ -396,17 +409,19 @@ class Operation: cls.current_mission.country(cls.game.enemy_country), player_cp, enemy_cp, - cls.game.theater + cls.game.theater, ) # Generate frontline ops player_gp = cls.game.ground_planners[player_cp.id].units_per_cp[enemy_cp.id] enemy_gp = cls.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id] ground_conflict_gen = GroundConflictGenerator( cls.current_mission, - conflict, cls.game, - player_gp, enemy_gp, + conflict, + cls.game, + player_gp, + enemy_gp, player_cp.stances[enemy_cp.id], - cls.unit_map + cls.unit_map, ) ground_conflict_gen.generate() cls.jtacs.extend(ground_conflict_gen.jtacs) @@ -416,9 +431,12 @@ class Operation: namegen.reset_numbers() @classmethod - def generate_lua(cls, airgen: AircraftConflictGenerator, - airsupportgen: AirSupportConflictGenerator, - jtacs: List[JtacInfo]) -> None: + def generate_lua( + cls, + airgen: AircraftConflictGenerator, + airsupportgen: AirSupportConflictGenerator, + jtacs: List[JtacInfo], + ) -> None: # TODO: Refactor this luaData = { "AircraftCarriers": {}, @@ -434,7 +452,7 @@ class Operation: "callsign": tanker.callsign, "variant": tanker.variant, "radio": tanker.freq.mhz, - "tacan": str(tanker.tacan.number) + tanker.tacan.band.name + "tacan": str(tanker.tacan.number) + tanker.tacan.band.name, } if airsupportgen.air_support.awacs: @@ -442,7 +460,7 @@ class Operation: luaData["AWACs"][awacs.callsign] = { "dcsGroupName": awacs.dcsGroupName, "callsign": awacs.callsign, - "radio": awacs.freq.mhz + "radio": awacs.freq.mhz, } for jtac in jtacs: @@ -451,14 +469,16 @@ class Operation: "callsign": jtac.callsign, "zone": jtac.region, "dcsUnit": jtac.unit_name, - "laserCode": jtac.code + "laserCode": jtac.code, } for flight in airgen.flights: - if flight.friendly and flight.flight_type in [FlightType.ANTISHIP, - FlightType.DEAD, - FlightType.SEAD, - FlightType.STRIKE]: + if flight.friendly and flight.flight_type in [ + FlightType.ANTISHIP, + FlightType.DEAD, + FlightType.SEAD, + FlightType.STRIKE, + ]: flightType = str(flight.flight_type) flightTarget = flight.package.target if flightTarget: @@ -466,23 +486,27 @@ class Operation: flightTargetType = None if isinstance(flightTarget, TheaterGroundObject): flightTargetName = flightTarget.obj_name - flightTargetType = flightType + \ - f" TGT ({flightTarget.category})" - elif hasattr(flightTarget, '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} + "position": { + "x": flightTarget.position.x, + "y": flightTarget.position.y, + }, } # 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 # 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(".") + "]]" - lua = """ + lua = ( + """ -- setting configuration table env.info("DCSLiberation|: setting configuration table") @@ -490,9 +514,12 @@ class Operation: dcsLiberation = {} -- 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 + + """ """ + ) # Process the tankers lua += """ diff --git a/game/persistency.py b/game/persistency.py index 38bd5550..8e15ece3 100644 --- a/game/persistency.py +++ b/game/persistency.py @@ -67,4 +67,3 @@ def autosave(game) -> bool: except Exception: logging.exception("Could not save game") return False - diff --git a/game/plugins/luaplugin.py b/game/plugins/luaplugin.py index f14d9e08..b58446a9 100644 --- a/game/plugins/luaplugin.py +++ b/game/plugins/luaplugin.py @@ -14,9 +14,9 @@ if TYPE_CHECKING: class LuaPluginWorkOrder: - - def __init__(self, parent_mnemonic: str, filename: str, mnemonic: str, - disable: bool) -> None: + def __init__( + self, parent_mnemonic: str, filename: str, mnemonic: str, disable: bool + ) -> None: self.parent_mnemonic = parent_mnemonic self.filename = filename self.mnemonic = mnemonic @@ -26,8 +26,9 @@ class LuaPluginWorkOrder: if self.disable: operation.bypass_plugin_script(self.mnemonic) else: - operation.inject_plugin_script(self.parent_mnemonic, self.filename, - self.mnemonic) + operation.inject_plugin_script( + self.parent_mnemonic, self.filename, self.mnemonic + ) class PluginSettings: @@ -45,8 +46,7 @@ class PluginSettings: # 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) + self.settings.initialize_plugin_option(self.identifier, self.enabled_by_default) @property def enabled(self) -> bool: @@ -57,8 +57,7 @@ class PluginSettings: class LuaPluginOption(PluginSettings): - def __init__(self, identifier: str, name: str, - enabled_by_default: bool) -> None: + def __init__(self, identifier: str, name: str, enabled_by_default: bool) -> None: super().__init__(identifier, enabled_by_default) self.name = name @@ -80,24 +79,34 @@ class LuaPluginDefinition: 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") - )) + 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) - )) + 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) - )) + config_work_orders.append( + LuaPluginWorkOrder( + name, + work_order.get("file"), + work_order["mnemonic"], + work_order.get("disable", False), + ) + ) return cls( identifier=name, @@ -106,16 +115,14 @@ class LuaPluginDefinition: enabled_by_default=data.get("defaultValue", False), options=options, work_orders=work_orders, - config_work_orders=config_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) + super().__init__(self.definition.identifier, self.definition.enabled_by_default) @property def name(self) -> str: @@ -155,12 +162,12 @@ class LuaPlugin(PluginSettings): for option in self.options: enabled = str(option.enabled).lower() name = option.identifier - option_decls.append( - f" dcsLiberation.plugins.{name} = {enabled}") + option_decls.append(f" dcsLiberation.plugins.{name} = {enabled}") joined_options = "\n".join(option_decls) - lua = textwrap.dedent(f"""\ + lua = textwrap.dedent( + f"""\ -- {self.identifier} plugin configuration. if dcsLiberation then @@ -171,10 +178,10 @@ class LuaPlugin(PluginSettings): {joined_options} end - """) + """ + ) - operation.inject_lua_trigger( - lua, f"{self.identifier} plugin configuration") + operation.inject_lua_trigger(lua, f"{self.identifier} plugin configuration") for work_order in self.definition.config_work_orders: work_order.work(operation) diff --git a/game/plugins/manager.py b/game/plugins/manager.py index 19b87118..7b6eabba 100644 --- a/game/plugins/manager.py +++ b/game/plugins/manager.py @@ -27,7 +27,8 @@ class LuaPluginManager: if not plugin_path.exists(): raise RuntimeError( f"Invalid plugin configuration: required plugin {name} " - f"does not exist at {plugin_path}") + 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: diff --git a/game/point_with_heading.py b/game/point_with_heading.py index 06a844b1..fa322723 100644 --- a/game/point_with_heading.py +++ b/game/point_with_heading.py @@ -2,7 +2,6 @@ from dcs import Point class PointWithHeading(Point): - def __init__(self): super(PointWithHeading, self).__init__(0, 0) self.heading = 0 @@ -13,4 +12,4 @@ class PointWithHeading(Point): p.x = point.x p.y = point.y p.heading = heading - return p \ No newline at end of file + return p diff --git a/game/procurement.py b/game/procurement.py index 0122ab59..0ff83155 100644 --- a/game/procurement.py +++ b/game/procurement.py @@ -35,9 +35,16 @@ class AircraftProcurementRequest: class ProcurementAi: - def __init__(self, game: Game, for_player: bool, faction: Faction, - manage_runways: bool, manage_front_line: bool, - manage_aircraft: bool, front_line_budget_share: float) -> None: + def __init__( + self, + game: Game, + for_player: bool, + faction: Faction, + manage_runways: bool, + manage_front_line: bool, + manage_aircraft: bool, + front_line_budget_share: float, + ) -> None: if front_line_budget_share > 1.0: raise ValueError @@ -51,8 +58,8 @@ class ProcurementAi: self.threat_zones = self.game.threat_zone_for(not self.is_player) def spend_budget( - self, budget: float, - aircraft_requests: List[AircraftProcurementRequest]) -> float: + self, budget: float, aircraft_requests: List[AircraftProcurementRequest] + ) -> float: if self.manage_runways: budget = self.repair_runways(budget) if self.manage_front_line: @@ -100,25 +107,30 @@ class ProcurementAi: budget -= db.RUNWAY_REPAIR_COST if self.is_player: self.game.message( - "OPFOR has begun repairing the runway at " - f"{control_point}" + "OPFOR has begun repairing the runway at " f"{control_point}" ) else: self.game.message( - "We have begun repairing the runway at " - f"{control_point}" + "We have begun repairing the runway at " f"{control_point}" ) return budget def random_affordable_ground_unit( - self, budget: float, - cp: ControlPoint) -> Optional[Type[VehicleType]]: - affordable_units = [u for u in self.faction.frontline_units + self.faction.artillery_units if - db.PRICES[u] <= budget] + self, budget: float, cp: ControlPoint + ) -> Optional[Type[VehicleType]]: + affordable_units = [ + u + for u in self.faction.frontline_units + self.faction.artillery_units + if db.PRICES[u] <= budget + ] - total_number_aa = cp.base.total_frontline_aa + cp.pending_frontline_aa_deliveries_count - total_non_aa = cp.base.total_armor + cp.pending_deliveries_count - total_number_aa - max_aa = math.ceil(total_non_aa/8) + total_number_aa = ( + cp.base.total_frontline_aa + cp.pending_frontline_aa_deliveries_count + ) + total_non_aa = ( + cp.base.total_armor + cp.pending_deliveries_count - total_number_aa + ) + max_aa = math.ceil(total_non_aa / 8) # Limit the number of AA units the AI will buy if not total_number_aa < max_aa: @@ -150,8 +162,12 @@ class ProcurementAi: return budget def _affordable_aircraft_of_types( - self, types: List[Type[FlyingType]], airbase: ControlPoint, - number: int, max_price: float) -> Optional[Type[FlyingType]]: + self, + types: List[Type[FlyingType]], + airbase: ControlPoint, + number: int, + max_price: float, + ) -> Optional[Type[FlyingType]]: best_choice: Optional[Type[FlyingType]] = None for unit in [u for u in self.faction.aircrafts if u in types]: if db.PRICES[unit] * number > max_price: @@ -168,15 +184,15 @@ class ProcurementAi: return best_choice def affordable_aircraft_for( - self, request: AircraftProcurementRequest, - airbase: ControlPoint, budget: float) -> Optional[Type[FlyingType]]: + self, request: AircraftProcurementRequest, airbase: ControlPoint, budget: float + ) -> Optional[Type[FlyingType]]: return self._affordable_aircraft_of_types( - aircraft_for_task(request.task_capability), - airbase, request.number, budget) + aircraft_for_task(request.task_capability), airbase, request.number, budget + ) def purchase_aircraft( - self, budget: float, - aircraft_requests: List[AircraftProcurementRequest]) -> float: + self, budget: float, aircraft_requests: List[AircraftProcurementRequest] + ) -> float: for request in aircraft_requests: for airbase in self.best_airbases_for(request): unit = self.affordable_aircraft_for(request, airbase, budget) @@ -201,11 +217,9 @@ class ProcurementAi: return self.game.theater.enemy_points() def best_airbases_for( - self, - request: AircraftProcurementRequest) -> Iterator[ControlPoint]: - distance_cache = ObjectiveDistanceCache.get_closest_airfields( - request.near - ) + self, request: AircraftProcurementRequest + ) -> Iterator[ControlPoint]: + distance_cache = ObjectiveDistanceCache.get_closest_airfields(request.near) threatened = [] for cp in distance_cache.airfields_within(request.range): if not cp.is_friendly(self.is_player): diff --git a/game/settings.py b/game/settings.py index 431ec361..4602bd55 100644 --- a/game/settings.py +++ b/game/settings.py @@ -59,8 +59,7 @@ class Settings: def plugin_settings_key(identifier: str) -> str: return f"plugins.{identifier}" - def initialize_plugin_option(self, identifier: str, - default_value: bool) -> None: + def initialize_plugin_option(self, identifier: str, default_value: bool) -> None: try: self.plugin_option(identifier) except KeyError: diff --git a/game/theater/base.py b/game/theater/base.py index 65c822c4..5db7bba7 100644 --- a/game/theater/base.py +++ b/game/theater/base.py @@ -22,7 +22,6 @@ BASE_MIN_STRENGTH = 0 class Base: - def __init__(self): self.aircraft: Dict[Type[FlyingType], int] = {} self.armor: Dict[Type[VehicleType], int] = {} @@ -57,23 +56,43 @@ class Base: return sum(self.aa.values()) def total_units(self, task: Task) -> int: - return sum([c for t, c in itertools.chain(self.aircraft.items(), self.armor.items(), self.aa.items()) if t in db.UNIT_BY_TASK[task]]) + return sum( + [ + c + for t, c in itertools.chain( + self.aircraft.items(), self.armor.items(), self.aa.items() + ) + if t in db.UNIT_BY_TASK[task] + ] + ) def total_units_of_type(self, unit_type) -> int: - return sum([c for t, c in itertools.chain(self.aircraft.items(), self.armor.items(), self.aa.items()) if t == unit_type]) + return sum( + [ + c + for t, c in itertools.chain( + self.aircraft.items(), self.armor.items(), self.aa.items() + ) + if t == unit_type + ] + ) @property def all_units(self): - return itertools.chain(self.aircraft.items(), self.armor.items(), self.aa.items()) + return itertools.chain( + self.aircraft.items(), self.armor.items(), self.aa.items() + ) - def _find_best_unit(self, available_units: Dict[UnitType, int], - for_type: Task, count: int) -> Dict[UnitType, int]: + def _find_best_unit( + self, available_units: Dict[UnitType, int], for_type: Task, count: int + ) -> Dict[UnitType, int]: if count <= 0: logging.warning("{}: no units for {}".format(self, for_type)) return {} - sorted_units = [key for key in available_units if - key in db.UNIT_BY_TASK[for_type]] + sorted_units = [ + key for key in available_units if key in db.UNIT_BY_TASK[for_type] + ] sorted_units.sort(key=lambda x: db.PRICES[x], reverse=True) result: Dict[UnitType, int] = {} @@ -94,14 +113,18 @@ class Base: logging.info("{} for {} ({}): {}".format(self, for_type, count, result)) return result - def _find_best_planes(self, for_type: Task, count: int) -> typing.Dict[FlyingType, int]: + def _find_best_planes( + self, for_type: Task, count: int + ) -> typing.Dict[FlyingType, int]: return self._find_best_unit(self.aircraft, for_type, count) def _find_best_armor(self, for_type: Task, count: int) -> typing.Dict[Armor, int]: return self._find_best_unit(self.armor, for_type, count) def append_commision_points(self, for_type, points: float) -> int: - self.commision_points[for_type] = self.commision_points.get(for_type, 0) + points + self.commision_points[for_type] = ( + self.commision_points.get(for_type, 0) + points + ) points = self.commision_points[for_type] if points >= 1: self.commision_points[for_type] = points - math.floor(points) @@ -110,7 +133,9 @@ class Base: return 0 def filter_units(self, applicable_units: typing.Collection): - self.aircraft = {k: v for k, v in self.aircraft.items() if k in applicable_units} + self.aircraft = { + k: v for k, v in self.aircraft.items() if k in applicable_units + } self.armor = {k: v for k, v in self.armor.items() if k in applicable_units} def commision_units(self, units: typing.Dict[typing.Any, int]): @@ -122,7 +147,12 @@ class Base: for_task = db.unit_task(unit_type) target_dict = None - if for_task == AWACS or for_task == CAS or for_task == CAP or for_task == Embarking: + if ( + for_task == AWACS + or for_task == CAS + or for_task == CAP + or for_task == Embarking + ): target_dict = self.aircraft elif for_task == PinpointStrike: target_dict = self.armor @@ -149,7 +179,7 @@ class Base: if unit_type not in target_array: print("Base didn't find event type {}".format(unit_type)) continue - + target_array[unit_type] = max(target_array[unit_type] - count, 0) if target_array[unit_type] == 0: del target_array[unit_type] @@ -166,12 +196,20 @@ class Base: def scramble_count(self, multiplier: float, task: Task = None) -> int: if task: - count = sum([v for k, v in self.aircraft.items() if db.unit_task(k) == task]) + count = sum( + [v for k, v in self.aircraft.items() if db.unit_task(k) == task] + ) else: count = self.total_aircraft count = int(math.ceil(count * PLANES_SCRAMBLE_FACTOR * self.strength)) - return min(min(max(count, PLANES_SCRAMBLE_MIN_BASE), int(PLANES_SCRAMBLE_MAX_BASE * multiplier)), count) + return min( + min( + max(count, PLANES_SCRAMBLE_MIN_BASE), + int(PLANES_SCRAMBLE_MAX_BASE * multiplier), + ), + count, + ) def assemble_count(self): return int(self.total_armor * 0.5) @@ -202,4 +240,8 @@ class Base: return self._find_best_armor(PinpointStrike, count) def assemble_aa(self, count=None) -> typing.Dict[AirDefence, int]: - return self._find_best_unit(self.aa, AirDefence, count and min(count, self.total_aa) or self.assemble_aa_count()) + return self._find_best_unit( + self.aa, + AirDefence, + count and min(count, self.total_aa) or self.assemble_aa_count(), + ) diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py index b21b9d16..9220c3db 100644 --- a/game/theater/conflicttheater.py +++ b/game/theater/conflicttheater.py @@ -158,7 +158,8 @@ class MizCampaignLoader: def country(self, blue: bool) -> Country: country = self.mission.country( - self.BLUE_COUNTRY.name if blue else self.RED_COUNTRY.name) + self.BLUE_COUNTRY.name if blue else self.RED_COUNTRY.name + ) # Should be guaranteed because we initialized them. assert country return country @@ -255,22 +256,23 @@ class MizCampaignLoader: for blue in (False, True): for group in self.off_map_spawns(blue): - control_point = OffMapSpawn(next(self.control_point_id), - str(group.name), group.position) + control_point = OffMapSpawn( + next(self.control_point_id), str(group.name), group.position + ) control_point.captured = blue control_point.captured_invert = group.late_activation control_points[control_point.id] = control_point for group in self.carriers(blue): # TODO: Name the carrier. control_point = Carrier( - "carrier", group.position, next(self.control_point_id)) + "carrier", group.position, next(self.control_point_id) + ) control_point.captured = blue control_point.captured_invert = group.late_activation control_points[control_point.id] = control_point for group in self.lhas(blue): # TODO: Name the LHA. - control_point = Lha( - "lha", group.position, next(self.control_point_id)) + control_point = Lha("lha", group.position, next(self.control_point_id)) control_point.captured = blue control_point.captured_invert = group.late_activation control_points[control_point.id] = control_point @@ -302,21 +304,21 @@ class MizCampaignLoader: origin = self.theater.closest_control_point(waypoints[0]) if origin is None: raise RuntimeError( - f"No control point near the first waypoint of {group.name}") + f"No control point near the first waypoint of {group.name}" + ) destination = self.theater.closest_control_point(waypoints[-1]) if destination is None: raise RuntimeError( - f"No control point near the final waypoint of {group.name}") + f"No control point near the final waypoint of {group.name}" + ) # Snap the begin and end points to the control points. waypoints[0] = origin.position waypoints[-1] = destination.position front_line_id = f"{origin.id}|{destination.id}" front_lines[front_line_id] = ComplexFrontLine(origin, waypoints) - self.control_points[origin.id].connect( - self.control_points[destination.id]) - self.control_points[destination.id].connect( - self.control_points[origin.id]) + self.control_points[origin.id].connect(self.control_points[destination.id]) + self.control_points[destination.id].connect(self.control_points[origin.id]) return front_lines def objective_info(self, group: Group) -> Tuple[ControlPoint, Distance]: @@ -329,52 +331,63 @@ class MizCampaignLoader: closest, distance = self.objective_info(group) if distance < self.BASE_DEFENSE_RADIUS: closest.preset_locations.base_garrisons.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) else: - logging.warning( - f"Found garrison unit too far from base: {group.name}") + logging.warning(f"Found garrison unit too far from base: {group.name}") for group in self.sams: closest, distance = self.objective_info(group) if distance < self.BASE_DEFENSE_RADIUS: closest.preset_locations.base_air_defense.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) else: closest.preset_locations.strike_locations.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) for group in self.ewrs: closest, distance = self.objective_info(group) - closest.preset_locations.ewrs.append(PointWithHeading.from_point(group.position, group.units[0].heading)) + closest.preset_locations.ewrs.append( + PointWithHeading.from_point(group.position, group.units[0].heading) + ) for group in self.offshore_strike_targets: closest, distance = self.objective_info(group) closest.preset_locations.offshore_strike_locations.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) for group in self.ships: closest, distance = self.objective_info(group) - closest.preset_locations.ships.append(PointWithHeading.from_point(group.position, group.units[0].heading)) + closest.preset_locations.ships.append( + PointWithHeading.from_point(group.position, group.units[0].heading) + ) for group in self.missile_sites: closest, distance = self.objective_info(group) closest.preset_locations.missile_sites.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) for group in self.coastal_defenses: closest, distance = self.objective_info(group) closest.preset_locations.coastal_defenses.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) for group in self.required_long_range_sams: closest, distance = self.objective_info(group) closest.preset_locations.required_long_range_sams.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) for group in self.required_medium_range_sams: closest, distance = self.objective_info(group) closest.preset_locations.required_medium_range_sams.append( - PointWithHeading.from_point(group.position, group.units[0].heading)) + PointWithHeading.from_point(group.position, group.units[0].heading) + ) def populate_theater(self) -> None: for control_point in self.control_points.values(): @@ -428,8 +441,9 @@ class ConflictTheater: logging.warning("Replacing existing frontline data") self._frontline_data = data - def add_controlpoint(self, point: ControlPoint, - connected_to: Optional[List[ControlPoint]] = None): + def add_controlpoint( + self, point: ControlPoint, connected_to: Optional[List[ControlPoint]] = None + ): if connected_to is None: connected_to = [] for connected_point in connected_to: @@ -503,7 +517,7 @@ class ConflictTheater: nearest_point = Point(nearest_point.x, nearest_point.y) new_point = point.point_from_heading( point.heading_between_point(nearest_point), - point.distance_to_point(nearest_point) + extend_dist + point.distance_to_point(nearest_point) + extend_dist, ) return new_point @@ -517,7 +531,9 @@ class ConflictTheater: def conflicts(self, from_player=True) -> Iterator[FrontLine]: for cp in [x for x in self.controlpoints if x.captured == from_player]: - for connected_point in [x for x in cp.connected_points if x.captured != from_player]: + for connected_point in [ + x for x in cp.connected_points if x.captured != from_player + ]: yield FrontLine(cp, connected_point, self) def enemy_points(self) -> List[ControlPoint]: @@ -572,17 +588,22 @@ class ConflictTheater: distances[cp.id] = dist closest_cp_id = min(distances, key=distances.get) # type: ignore - all_cp_min_distances[(control_point.id, closest_cp_id)] = distances[closest_cp_id] + all_cp_min_distances[(control_point.id, closest_cp_id)] = distances[ + closest_cp_id + ] closest_opposing_cps = [ self.find_control_point_by_id(i) - for i - in min(all_cp_min_distances, key=all_cp_min_distances.get) # type: ignore + for i in min( + all_cp_min_distances, key=all_cp_min_distances.get + ) # type: ignore ] # type: List[ControlPoint] assert len(closest_opposing_cps) == 2 if closest_opposing_cps[0].captured: return cast(Tuple[ControlPoint, ControlPoint], tuple(closest_opposing_cps)) else: - return cast(Tuple[ControlPoint, ControlPoint], tuple(reversed(closest_opposing_cps))) + return cast( + Tuple[ControlPoint, ControlPoint], tuple(reversed(closest_opposing_cps)) + ) def find_control_point_by_id(self, id: int) -> ControlPoint: for i in self.controlpoints: @@ -677,8 +698,7 @@ class PersianGulfTheater(ConflictTheater): terrain = persiangulf.PersianGulf() overview_image = "persiangulf.gif" reference_points = ( - ReferencePoint(persiangulf.Jiroft_Airport.position, - Point(1692, 1343)), + ReferencePoint(persiangulf.Jiroft_Airport.position, Point(1692, 1343)), ReferencePoint(persiangulf.Liwa_Airbase.position, Point(358, 3238)), ) landmap = load_landmap("resources\\gulflandmap.p") @@ -727,7 +747,7 @@ class TheChannelTheater(ConflictTheater): overview_image = "thechannel.gif" reference_points = ( ReferencePoint(thechannel.Abbeville_Drucat.position, Point(2005, 2390)), - ReferencePoint(thechannel.Detling.position, Point(706, 382)) + ReferencePoint(thechannel.Detling.position, Point(706, 382)), ) landmap = load_landmap("resources\\channellandmap.p") daytime_map = { @@ -793,10 +813,10 @@ class FrontLine(MissionTarget): """ def __init__( - self, - control_point_a: ControlPoint, - control_point_b: ControlPoint, - theater: ConflictTheater + self, + control_point_a: ControlPoint, + control_point_b: ControlPoint, + theater: ConflictTheater, ) -> None: self.control_point_a = control_point_a self.control_point_b = control_point_b @@ -882,7 +902,7 @@ class FrontLine(MissionTarget): according to the current strength of each control point """ total_strength = ( - self.control_point_a.base.strength + self.control_point_b.base.strength + self.control_point_a.base.strength + self.control_point_b.base.strength ) if self.control_point_a.base.strength == 0: return self._adjust_for_min_dist(0) @@ -897,11 +917,11 @@ class FrontLine(MissionTarget): constant of either end control point. """ if (distance > self.attack_distance / 2) and ( - distance + FRONTLINE_MIN_CP_DISTANCE > self.attack_distance + distance + FRONTLINE_MIN_CP_DISTANCE > self.attack_distance ): distance = self.attack_distance - FRONTLINE_MIN_CP_DISTANCE elif (distance < self.attack_distance / 2) and ( - distance < FRONTLINE_MIN_CP_DISTANCE + distance < FRONTLINE_MIN_CP_DISTANCE ): distance = FRONTLINE_MIN_CP_DISTANCE return distance @@ -916,8 +936,8 @@ class FrontLine(MissionTarget): ) complex_frontlines = self.theater.frontline_data if (complex_frontlines) and ( - (control_point_ids in complex_frontlines) - or (reversed_cp_ids in complex_frontlines) + (control_point_ids in complex_frontlines) + or (reversed_cp_ids in complex_frontlines) ): # The frontline segments must be stored in the correct order for the distance algorithms to work. # The points in the frontline are ordered from the id before the | to the id after. @@ -943,7 +963,7 @@ class FrontLine(MissionTarget): @staticmethod def load_json_frontlines( - theater: ConflictTheater + theater: ConflictTheater, ) -> Optional[Dict[str, ComplexFrontLine]]: """Load complex frontlines from json""" try: diff --git a/game/theater/controlpoint.py b/game/theater/controlpoint.py index ffdb5239..4b2013a0 100644 --- a/game/theater/controlpoint.py +++ b/game/theater/controlpoint.py @@ -71,6 +71,7 @@ class LocationType(Enum): Shorad = "SHORAD" StrikeTarget = "strike target" + @dataclass class PresetLocations: """Defines the preset locations loaded from the campaign mission file.""" @@ -230,10 +231,17 @@ class ControlPoint(MissionTarget, ABC): # TODO: Only airbases have IDs. # TODO: has_frontline is only reasonable for airbases. # TODO: cptype is obsolete. - def __init__(self, cp_id: int, name: str, position: Point, - at: db.StartingPosition, size: int, - importance: float, has_frontline=True, - cptype=ControlPointType.AIRBASE): + def __init__( + self, + cp_id: int, + name: str, + position: Point, + at: db.StartingPosition, + size: int, + importance: float, + has_frontline=True, + cptype=ControlPointType.AIRBASE, + ): super().__init__(name, position) # TODO: Should be Airbase specific. self.id = cp_id @@ -256,17 +264,17 @@ class ControlPoint(MissionTarget, ABC): # TODO: Should be Airbase specific. self.stances: Dict[int, CombatStance] = {} from ..event import UnitsDeliveryEvent + self.pending_unit_deliveries = UnitsDeliveryEvent(self) self.target_position: Optional[Point] = None - + def __repr__(self): return f"<{__class__}: {self.name}>" @property def ground_objects(self) -> List[TheaterGroundObject]: - return list( - itertools.chain(self.connected_objectives, self.base_defenses)) + return list(itertools.chain(self.connected_objectives, self.base_defenses)) @property @abstractmethod @@ -341,15 +349,18 @@ class ControlPoint(MissionTarget, ABC): Get the carrier group name if the airbase is a carrier :return: Carrier group name """ - if self.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, - ControlPointType.LHA_GROUP]: + if self.cptype in [ + ControlPointType.AIRCRAFT_CARRIER_GROUP, + ControlPointType.LHA_GROUP, + ]: for g in self.ground_objects: if g.dcs_identifier == "CARRIER": for group in g.groups: for u in group.units: if db.unit_type_from_name(u.type) in [ - CVN_74_John_C__Stennis, - CV_1143_5_Admiral_Kuznetsov]: + CVN_74_John_C__Stennis, + CV_1143_5_Admiral_Kuznetsov, + ]: return group.name elif g.dcs_identifier == "LHA": for group in g.groups: @@ -385,7 +396,8 @@ class ControlPoint(MissionTarget, ABC): else: logging.error( "Could not determine preset location type for " - f"{base_defense}. Assuming garrison type.") + f"{base_defense}. Assuming garrison type." + ) self.preset_locations.base_garrisons.append(p) self.base_defenses = [] @@ -395,15 +407,18 @@ class ControlPoint(MissionTarget, ABC): game.adjust_budget(total, player=not self.captured) game.message( f"{self.name} is not connected to any friendly points. Ground " - f"vehicles have been captured and sold for ${total}M.") + f"vehicles have been captured and sold for ${total}M." + ) def retreat_ground_units(self, game: Game): # When there are multiple valid destinations, deliver units to whichever # base is least defended first. The closest approximation of unit # strength we have is price - destinations = [GroundUnitDestination(cp) - for cp in self.connected_points - if cp.captured == self.captured] + destinations = [ + GroundUnitDestination(cp) + for cp in self.connected_points + if cp.captured == self.captured + ] if not destinations: self.capture_equipment(game) return @@ -416,8 +431,9 @@ class ControlPoint(MissionTarget, ABC): destination.control_point.base.commision_units({unit_type: 1}) destination = heapq.heappushpop(destinations, destination) - def capture_aircraft(self, game: Game, airframe: Type[FlyingType], - count: int) -> None: + def capture_aircraft( + self, game: Game, airframe: Type[FlyingType], count: int + ) -> None: try: value = PRICES[airframe] * count except KeyError: @@ -428,11 +444,12 @@ class ControlPoint(MissionTarget, ABC): game.message( f"No valid retreat destination in range of {self.name} for " f"{airframe.id}. {count} aircraft have been captured and sold for " - f"${value}M.") + f"${value}M." + ) def aircraft_retreat_destination( - self, game: Game, - airframe: Type[FlyingType]) -> Optional[ControlPoint]: + self, game: Game, airframe: Type[FlyingType] + ) -> Optional[ControlPoint]: closest = ObjectiveDistanceCache.get_closest_airfields(self) # TODO: Should be airframe dependent. max_retreat_distance = nautical_miles(200) @@ -448,8 +465,9 @@ class ControlPoint(MissionTarget, ABC): return airbase return None - def _retreat_air_units(self, game: Game, airframe: Type[FlyingType], - count: int) -> None: + def _retreat_air_units( + self, game: Game, airframe: Type[FlyingType], count: int + ) -> None: while count: logging.debug(f"Retreating {count} {airframe.id} from {self.name}") destination = self.aircraft_retreat_destination(game, airframe) @@ -482,6 +500,7 @@ class ControlPoint(MissionTarget, ABC): self.clear_base_defenses() from .start_generator import BaseDefenseGenerator + BaseDefenseGenerator(game, self).generate() @abstractmethod @@ -511,16 +530,19 @@ class ControlPoint(MissionTarget, ABC): if issubclass(unit_bought, FlyingType): on_order += self.pending_unit_deliveries.units[unit_bought] - return PendingOccupancy(self.base.total_aircraft, on_order, - self.aircraft_transferring(game)) + return PendingOccupancy( + self.base.total_aircraft, on_order, self.aircraft_transferring(game) + ) def unclaimed_parking(self, game: Game) -> int: - return (self.total_aircraft_parking - - self.expected_aircraft_next_turn(game).total) + return ( + self.total_aircraft_parking - self.expected_aircraft_next_turn(game).total + ) @abstractmethod - def active_runway(self, conditions: Conditions, - dynamic_runways: Dict[str, RunwayData]) -> RunwayData: + def active_runway( + self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData] + ) -> RunwayData: ... @property @@ -571,7 +593,13 @@ class ControlPoint(MissionTarget, ABC): Get number of pending frontline aa units """ if self.pending_unit_deliveries: - return sum([v for k,v in self.pending_unit_deliveries.units.items() if k in TYPE_SHORAD]) + return sum( + [ + v + for k, v in self.pending_unit_deliveries.units.items() + if k in TYPE_SHORAD + ] + ) else: return 0 @@ -595,9 +623,12 @@ class ControlPoint(MissionTarget, ABC): continue on_order += self.pending_unit_deliveries.units[unit_bought] - return PendingOccupancy(self.base.total_armor, on_order, - # Ground unit transfers not yet implemented. - transferring=0) + return PendingOccupancy( + self.base.total_armor, + on_order, + # Ground unit transfers not yet implemented. + transferring=0, + ) @property def income_per_turn(self) -> int: @@ -605,6 +636,7 @@ class ControlPoint(MissionTarget, ABC): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if self.is_friendly(for_player): yield from [ FlightType.AEWC, @@ -613,17 +645,23 @@ class ControlPoint(MissionTarget, ABC): @property def has_active_frontline(self) -> bool: - return any( - not c.is_friendly(self.captured) for c in self.connected_points) + return any(not c.is_friendly(self.captured) for c in self.connected_points) class Airfield(ControlPoint): - - def __init__(self, airport: Airport, size: int, - importance: float, has_frontline=True): - super().__init__(airport.id, airport.name, airport.position, airport, - size, importance, has_frontline, - cptype=ControlPointType.AIRBASE) + def __init__( + self, airport: Airport, size: int, importance: float, has_frontline=True + ): + super().__init__( + airport.id, + airport.name, + airport.position, + airport, + size, + importance, + has_frontline, + cptype=ControlPointType.AIRBASE, + ) self.airport = airport self._runway_status = RunwayStatus() @@ -637,6 +675,7 @@ class Airfield(ControlPoint): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if self.is_friendly(for_player): yield from [ # TODO: FlightType.INTERCEPTION @@ -667,8 +706,9 @@ class Airfield(ControlPoint): def damage_runway(self) -> None: self.runway_status.damage() - def active_runway(self, conditions: Conditions, - dynamic_runways: Dict[str, RunwayData]) -> RunwayData: + def active_runway( + self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData] + ) -> RunwayData: assigner = RunwayAssigner(conditions) return assigner.get_preferred_runway(self.airport) @@ -686,13 +726,13 @@ class Airfield(ControlPoint): class NavalControlPoint(ControlPoint, ABC): - @property def is_fleet(self) -> bool: return True def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if self.is_friendly(for_player): yield from [ # TODO: FlightType.INTERCEPTION @@ -716,14 +756,17 @@ class NavalControlPoint(ControlPoint, ABC): for group in g.groups: for u in group.units: if db.unit_type_from_name(u.type) in [ - CVN_74_John_C__Stennis, LHA_1_Tarawa, - CV_1143_5_Admiral_Kuznetsov, - Type_071_Amphibious_Transport_Dock]: + CVN_74_John_C__Stennis, + LHA_1_Tarawa, + CV_1143_5_Admiral_Kuznetsov, + Type_071_Amphibious_Transport_Dock, + ]: return True return False - def active_runway(self, conditions: Conditions, - dynamic_runways: Dict[str, RunwayData]) -> RunwayData: + def active_runway( + self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData] + ) -> RunwayData: # TODO: Assign TACAN and ICLS earlier so we don't need this. fallback = RunwayData(self.full_name, runway_heading=0, runway_name="") return dynamic_runways.get(self.name, fallback) @@ -746,12 +789,19 @@ class NavalControlPoint(ControlPoint, ABC): class Carrier(NavalControlPoint): - def __init__(self, name: str, at: Point, cp_id: int): import game.theater.conflicttheater - super().__init__(cp_id, name, at, at, - game.theater.conflicttheater.SIZE_SMALL, 1, - has_frontline=False, cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP) + + super().__init__( + cp_id, + name, + at, + at, + game.theater.conflicttheater.SIZE_SMALL, + 1, + has_frontline=False, + cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP, + ) def capture(self, game: Game, for_player: bool) -> None: raise RuntimeError("Carriers cannot be captured") @@ -769,12 +819,19 @@ class Carrier(NavalControlPoint): class Lha(NavalControlPoint): - def __init__(self, name: str, at: Point, cp_id: int): import game.theater.conflicttheater - super().__init__(cp_id, name, at, at, - game.theater.conflicttheater.SIZE_SMALL, 1, - has_frontline=False, cptype=ControlPointType.LHA_GROUP) + + super().__init__( + cp_id, + name, + at, + at, + game.theater.conflicttheater.SIZE_SMALL, + 1, + has_frontline=False, + cptype=ControlPointType.LHA_GROUP, + ) def capture(self, game: Game, for_player: bool) -> None: raise RuntimeError("LHAs cannot be captured") @@ -792,15 +849,22 @@ class Lha(NavalControlPoint): class OffMapSpawn(ControlPoint): - def runway_is_operational(self) -> bool: return True def __init__(self, cp_id: int, name: str, position: Point): from . import IMPORTANCE_MEDIUM, SIZE_REGULAR - super().__init__(cp_id, name, position, at=position, - size=SIZE_REGULAR, importance=IMPORTANCE_MEDIUM, - has_frontline=False, cptype=ControlPointType.OFF_MAP) + + super().__init__( + cp_id, + name, + position, + at=position, + size=SIZE_REGULAR, + importance=IMPORTANCE_MEDIUM, + has_frontline=False, + cptype=ControlPointType.OFF_MAP, + ) def capture(self, game: Game, for_player: bool) -> None: raise RuntimeError("Off map control points cannot be captured") @@ -819,8 +883,9 @@ class OffMapSpawn(ControlPoint): def heading(self) -> int: return 0 - def active_runway(self, conditions: Conditions, - dynamic_runways: Dict[str, RunwayData]) -> RunwayData: + def active_runway( + self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData] + ) -> RunwayData: logging.warning("TODO: Off map spawns have no runways.") return RunwayData(self.full_name, runway_heading=0, runway_name="") @@ -834,19 +899,27 @@ class OffMapSpawn(ControlPoint): class Fob(ControlPoint): - def __init__(self, name: str, at: Point, cp_id: int): import game.theater.conflicttheater - super().__init__(cp_id, name, at, at, - game.theater.conflicttheater.SIZE_SMALL, 1, - has_frontline=True, cptype=ControlPointType.FOB) + + super().__init__( + cp_id, + name, + at, + at, + game.theater.conflicttheater.SIZE_SMALL, + 1, + has_frontline=True, + cptype=ControlPointType.FOB, + ) self.name = name - + def runway_is_operational(self) -> bool: return False - - def active_runway(self, conditions: Conditions, - dynamic_runways: Dict[str, RunwayData]) -> RunwayData: + + def active_runway( + self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData] + ) -> RunwayData: logging.warning("TODO: FOBs have no runways.") return RunwayData(self.full_name, runway_heading=0, runway_name="") @@ -856,6 +929,7 @@ class Fob(ControlPoint): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if self.is_friendly(for_player): yield from [ FlightType.BARCAP, diff --git a/game/theater/landmap.py b/game/theater/landmap.py index c5b422ad..29d551b3 100644 --- a/game/theater/landmap.py +++ b/game/theater/landmap.py @@ -46,4 +46,3 @@ def poly_centroid(poly) -> Tuple[float, float]: x = sum(x_list) / len(poly) y = sum(y_list) / len(poly) return (x, y) - diff --git a/game/theater/missiontarget.py b/game/theater/missiontarget.py index c442fe42..9a5fe8cd 100644 --- a/game/theater/missiontarget.py +++ b/game/theater/missiontarget.py @@ -29,6 +29,7 @@ class MissionTarget: def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if self.is_friendly(for_player): yield FlightType.BARCAP else: diff --git a/game/theater/start_generator.py b/game/theater/start_generator.py index 221d86fa..85fb806d 100644 --- a/game/theater/start_generator.py +++ b/game/theater/start_generator.py @@ -22,7 +22,8 @@ from game.theater.theatergroundobject import ( MissileSiteGroundObject, SamGroundObject, ShipGroundObject, - VehicleGroupGroundObject, CoastalSiteGroundObject, + VehicleGroupGroundObject, + CoastalSiteGroundObject, ) from game.version import VERSION from gen import namegen @@ -77,9 +78,14 @@ class GeneratorSettings: class GameGenerator: - def __init__(self, player: str, enemy: str, theater: ConflictTheater, - settings: Settings, - generator_settings: GeneratorSettings) -> None: + def __init__( + self, + player: str, + enemy: str, + theater: ConflictTheater, + settings: Settings, + generator_settings: GeneratorSettings, + ) -> None: self.player = player self.enemy = enemy self.theater = theater @@ -97,7 +103,7 @@ class GameGenerator: start_date=self.generator_settings.start_date, settings=self.settings, player_budget=self.generator_settings.player_budget, - enemy_budget=self.generator_settings.enemy_budget + enemy_budget=self.generator_settings.enemy_budget, ) GroundObjectGenerator(game, self.generator_settings).generate() @@ -109,7 +115,7 @@ class GameGenerator: # Auto-capture half the bases if midgame. if self.generator_settings.midgame: control_points = self.theater.controlpoints - for control_point in control_points[:len(control_points) // 2]: + for control_point in control_points[: len(control_points) // 2]: control_point.captured = True # Remove carrier and lha, invert situation if needed @@ -141,28 +147,37 @@ class LocationFinder: self.game = game self.control_point = control_point self.miz_data = MizDataLocationFinder.compute_possible_locations( - game.theater.terrain.name, control_point.full_name) + game.theater.terrain.name, control_point.full_name + ) def location_for(self, location_type: LocationType) -> Optional[PointWithHeading]: position = self.control_point.preset_locations.random_for(location_type) if position is not None: return position - logging.warning(f"No campaign location for %s Mat %s", - location_type.value, self.control_point) + logging.warning( + f"No campaign location for %s Mat %s", + location_type.value, + self.control_point, + ) position = self.random_from_miz_data( - location_type == LocationType.OffshoreStrikeTarget) + location_type == LocationType.OffshoreStrikeTarget + ) if position is not None: return position - logging.debug(f"No mizdata location for %s at %s", location_type.value, - self.control_point) + logging.debug( + f"No mizdata location for %s at %s", location_type.value, self.control_point + ) position = self.random_position(location_type) if position is not None: return position - logging.error(f"Could not find position for %s at %s", - location_type.value, self.control_point) + logging.error( + f"Could not find position for %s at %s", + location_type.value, + self.control_point, + ) return None def random_from_miz_data(self, offshore: bool) -> Optional[PointWithHeading]: @@ -176,15 +191,20 @@ class LocationFinder: return PointWithHeading.from_point(preset.position, preset.heading) return None - def random_position(self, location_type: LocationType) -> Optional[PointWithHeading]: + def random_position( + self, location_type: LocationType + ) -> Optional[PointWithHeading]: # TODO: Flesh out preset locations so we never hit this case. if location_type == LocationType.Coastal: # No coastal locations generated randomly return None - logging.warning("Falling back to random location for %s at %s", - location_type.value, self.control_point) + logging.warning( + "Falling back to random location for %s at %s", + location_type.value, + self.control_point, + ) is_base_defense = location_type in { LocationType.BaseAirDefense, @@ -218,23 +238,28 @@ class LocationFinder: min_range = 10000 max_range = 40000 - position = self._find_random_position(min_range, max_range, - on_land, is_base_defense, - avoid_others) + position = self._find_random_position( + min_range, max_range, on_land, is_base_defense, avoid_others + ) # Retry once, searching a bit further (On some big airbases, 3200 is too # short (Ex : Incirlik)), but searching farther on every base would be # problematic, as some base defense units would end up very far away # from small airfields. if position is None and is_base_defense: - position = self._find_random_position(3200, 4800, - on_land, is_base_defense, - avoid_others) + position = self._find_random_position( + 3200, 4800, on_land, is_base_defense, avoid_others + ) return position - def _find_random_position(self, min_range: int, max_range: int, - on_ground: bool, is_base_defense: bool, - avoid_others: bool) -> Optional[PointWithHeading]: + def _find_random_position( + self, + min_range: int, + max_range: int, + on_ground: bool, + is_base_defense: bool, + avoid_others: bool, + ) -> Optional[PointWithHeading]: """ Find a valid ground object location :param on_ground: Whether it should be on ground or on sea (True = on @@ -278,15 +303,21 @@ class LocationFinder: for _ in range(300): # Check if on land or sea - p = PointWithHeading.from_point(near.random_point_within(max_range, min_range), random.randint(0, 360)) + p = PointWithHeading.from_point( + near.random_point_within(max_range, min_range), random.randint(0, 360) + ) if is_valid(p): return p return None class ControlPointGroundObjectGenerator: - def __init__(self, game: Game, generator_settings: GeneratorSettings, - control_point: ControlPoint) -> None: + def __init__( + self, + game: Game, + generator_settings: GeneratorSettings, + control_point: ControlPoint, + ) -> None: self.game = game self.generator_settings = generator_settings self.control_point = control_point @@ -327,15 +358,15 @@ class ControlPointGroundObjectGenerator: self.generate_ship() def generate_ship(self) -> None: - point = self.location_finder.location_for( - LocationType.OffshoreStrikeTarget) + point = self.location_finder.location_for(LocationType.OffshoreStrikeTarget) if point is None: return group_id = self.game.next_group_id() - g = ShipGroundObject(namegen.random_objective_name(), group_id, point, - self.control_point) + g = ShipGroundObject( + namegen.random_objective_name(), group_id, point, self.control_point + ) group = generate_ship_group(self.game, g, self.faction_name) g.groups = [] @@ -358,13 +389,15 @@ class CarrierGroundObjectGenerator(ControlPointGroundObjectGenerator): if not carrier_names: logging.info( f"Skipping generation of {self.control_point.name} because " - f"{self.faction_name} has no carriers") + f"{self.faction_name} has no carriers" + ) return False # Create ground object group group_id = self.game.next_group_id() - g = CarrierGroundObject(namegen.random_objective_name(), group_id, - self.control_point) + g = CarrierGroundObject( + namegen.random_objective_name(), group_id, self.control_point + ) group = generate_carrier_group(self.faction_name, self.game, g) g.groups = [] if group is not None: @@ -383,13 +416,15 @@ class LhaGroundObjectGenerator(ControlPointGroundObjectGenerator): if not lha_names: logging.info( f"Skipping generation of {self.control_point.name} because " - f"{self.faction_name} has no LHAs") + f"{self.faction_name} has no LHAs" + ) return False # Create ground object group group_id = self.game.next_group_id() - g = LhaGroundObject(namegen.random_objective_name(), group_id, - self.control_point) + g = LhaGroundObject( + namegen.random_objective_name(), group_id, self.control_point + ) group = generate_lha_group(self.faction_name, self.game, g) g.groups = [] if group is not None: @@ -428,8 +463,9 @@ class BaseDefenseGenerator: group_id = self.game.next_group_id() - g = EwrGroundObject(namegen.random_objective_name(), group_id, - position, self.control_point) + g = EwrGroundObject( + namegen.random_objective_name(), group_id, position, self.control_point + ) group = generate_ewr_group(self.game, g, self.faction) if group is None: @@ -460,28 +496,35 @@ class BaseDefenseGenerator: group_id = self.game.next_group_id() - g = VehicleGroupGroundObject(namegen.random_objective_name(), group_id, - position, self.control_point, - for_airbase=True) + g = VehicleGroupGroundObject( + namegen.random_objective_name(), + group_id, + position, + self.control_point, + for_airbase=True, + ) group = generate_armor_group(self.faction_name, self.game, g) if group is None: - logging.error( - f"Could not generate garrison at {self.control_point}") + logging.error(f"Could not generate garrison at {self.control_point}") return g.groups.append(group) self.control_point.base_defenses.append(g) def generate_sam(self) -> None: - position = self.location_finder.location_for( - LocationType.BaseAirDefense) + position = self.location_finder.location_for(LocationType.BaseAirDefense) if position is None: return group_id = self.game.next_group_id() - g = SamGroundObject(namegen.random_objective_name(), group_id, - position, self.control_point, for_airbase=True) + g = SamGroundObject( + namegen.random_objective_name(), + group_id, + position, + self.control_point, + for_airbase=True, + ) groups = generate_anti_air_group(self.game, g, self.faction) if not groups: @@ -491,21 +534,25 @@ class BaseDefenseGenerator: self.control_point.base_defenses.append(g) def generate_shorad(self) -> None: - position = self.location_finder.location_for( - LocationType.BaseAirDefense) + position = self.location_finder.location_for(LocationType.BaseAirDefense) if position is None: return group_id = self.game.next_group_id() - g = SamGroundObject(namegen.random_objective_name(), group_id, - position, self.control_point, for_airbase=True) + g = SamGroundObject( + namegen.random_objective_name(), + group_id, + position, + self.control_point, + for_airbase=True, + ) - groups = generate_anti_air_group(self.game, g, self.faction, - ranges=[{AirDefenseRange.Short}]) + groups = generate_anti_air_group( + self.game, g, self.faction, ranges=[{AirDefenseRange.Short}] + ) if not groups: - logging.error( - f"Could not generate SHORAD group at {self.control_point}") + logging.error(f"Could not generate SHORAD group at {self.control_point}") return g.groups = groups self.control_point.base_defenses.append(g) @@ -515,7 +562,7 @@ class FobDefenseGenerator(BaseDefenseGenerator): def generate(self) -> None: self.generate_garrison() self.generate_fob_defenses() - + def generate_fob_defenses(self): # First group has a 1/2 chance of being a SHORAD, # and a 1/2 chance of a garrison. @@ -534,9 +581,13 @@ class FobDefenseGenerator(BaseDefenseGenerator): class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): - def __init__(self, game: Game, generator_settings: GeneratorSettings, - control_point: ControlPoint, - templates: GroundObjectTemplates) -> None: + def __init__( + self, + game: Game, + generator_settings: GeneratorSettings, + control_point: ControlPoint, + templates: GroundObjectTemplates, + ) -> None: super().__init__(game, generator_settings, control_point) self.templates = templates @@ -585,18 +636,25 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): """ presets = self.control_point.preset_locations for position in presets.required_long_range_sams: - self.generate_aa_at(position, ranges=[ - {AirDefenseRange.Long}, - {AirDefenseRange.Medium}, - {AirDefenseRange.Short}, - ]) + self.generate_aa_at( + position, + ranges=[ + {AirDefenseRange.Long}, + {AirDefenseRange.Medium}, + {AirDefenseRange.Short}, + ], + ) for position in presets.required_medium_range_sams: - self.generate_aa_at(position, ranges=[ - {AirDefenseRange.Medium}, - {AirDefenseRange.Short}, - ]) - return (len(presets.required_long_range_sams) + - len(presets.required_medium_range_sams)) + self.generate_aa_at( + position, + ranges=[ + {AirDefenseRange.Medium}, + {AirDefenseRange.Short}, + ], + ) + return len(presets.required_long_range_sams) + len( + presets.required_medium_range_sams + ) def generate_ground_point(self) -> None: try: @@ -627,8 +685,15 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): template_point = Point(unit["offset"].x, unit["offset"].y) g = BuildingGroundObject( - obj_name, category, group_id, object_id, point + template_point, - unit["heading"], self.control_point, unit["type"]) + obj_name, + category, + group_id, + object_id, + point + template_point, + unit["heading"], + self.control_point, + unit["type"], + ) self.control_point.connected_objectives.append(g) @@ -636,23 +701,34 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): position = self.location_finder.location_for(LocationType.Sam) if position is None: return - self.generate_aa_at(position, ranges=[ - # Prefer to use proper SAMs, but fall back to SHORADs if needed. - {AirDefenseRange.Long, AirDefenseRange.Medium}, - {AirDefenseRange.Short}, - ]) + self.generate_aa_at( + position, + ranges=[ + # Prefer to use proper SAMs, but fall back to SHORADs if needed. + {AirDefenseRange.Long, AirDefenseRange.Medium}, + {AirDefenseRange.Short}, + ], + ) def generate_aa_at( - self, position: Point, - ranges: Iterable[Set[AirDefenseRange]]) -> None: + self, position: Point, ranges: Iterable[Set[AirDefenseRange]] + ) -> None: group_id = self.game.next_group_id() - g = SamGroundObject(namegen.random_objective_name(), group_id, - position, self.control_point, for_airbase=False) + g = SamGroundObject( + namegen.random_objective_name(), + group_id, + position, + self.control_point, + for_airbase=False, + ) groups = generate_anti_air_group(self.game, g, self.faction, ranges) if not groups: - logging.error("Could not generate air defense group for %s at %s", - g.name, self.control_point) + logging.error( + "Could not generate air defense group for %s at %s", + g.name, + self.control_point, + ) return g.groups = groups self.control_point.connected_objectives.append(g) @@ -668,8 +744,9 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): group_id = self.game.next_group_id() - g = MissileSiteGroundObject(namegen.random_objective_name(), group_id, - position, self.control_point) + g = MissileSiteGroundObject( + namegen.random_objective_name(), group_id, position, self.control_point + ) group = generate_missile_group(self.game, g, self.faction_name) g.groups = [] if group is not None: @@ -688,8 +765,13 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): group_id = self.game.next_group_id() - g = CoastalSiteGroundObject(namegen.random_objective_name(), group_id, - position, self.control_point, position.heading) + g = CoastalSiteGroundObject( + namegen.random_objective_name(), + group_id, + position, + self.control_point, + position.heading, + ) group = generate_coastal_group(self.game, g, self.faction_name) g.groups = [] if group is not None: @@ -707,7 +789,7 @@ class FobGroundObjectGenerator(AirbaseGroundObjectGenerator): def generate_fob(self) -> None: try: - category = self.faction.building_set[self.faction.building_set.index('fob')] + category = self.faction.building_set[self.faction.building_set.index("fob")] except IndexError: logging.exception("Faction has no fob buildings defined") return @@ -725,14 +807,21 @@ class FobGroundObjectGenerator(AirbaseGroundObjectGenerator): template_point = Point(unit["offset"].x, unit["offset"].y) g = BuildingGroundObject( - obj_name, category, group_id, object_id, point + template_point, - unit["heading"], self.control_point, unit["type"], airbase_group=True) + obj_name, + category, + group_id, + object_id, + point + template_point, + unit["heading"], + self.control_point, + unit["type"], + airbase_group=True, + ) self.control_point.connected_objectives.append(g) class GroundObjectGenerator: - def __init__(self, game: Game, - generator_settings: GeneratorSettings) -> None: + def __init__(self, game: Game, generator_settings: GeneratorSettings) -> None: self.game = game self.generator_settings = generator_settings with open("resources/groundobject_templates.p", "rb") as f: @@ -750,19 +839,22 @@ class GroundObjectGenerator: generator: ControlPointGroundObjectGenerator if control_point.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP: generator = CarrierGroundObjectGenerator( - self.game, self.generator_settings, control_point) + self.game, self.generator_settings, control_point + ) elif control_point.cptype == ControlPointType.LHA_GROUP: generator = LhaGroundObjectGenerator( - self.game, self.generator_settings, control_point) + self.game, self.generator_settings, control_point + ) elif isinstance(control_point, OffMapSpawn): generator = NoOpGroundObjectGenerator( - self.game, self.generator_settings, control_point) + self.game, self.generator_settings, control_point + ) elif isinstance(control_point, Fob): generator = FobGroundObjectGenerator( - self.game, self.generator_settings, control_point, - self.templates) + self.game, self.generator_settings, control_point, self.templates + ) else: generator = AirbaseGroundObjectGenerator( - self.game, self.generator_settings, control_point, - self.templates) + self.game, self.generator_settings, control_point, self.templates + ) return generator.generate() diff --git a/game/theater/theatergroundobject.py b/game/theater/theatergroundobject.py index 2a660cf3..7c6581da 100644 --- a/game/theater/theatergroundobject.py +++ b/game/theater/theatergroundobject.py @@ -33,7 +33,7 @@ NAME_BY_CATEGORY = { "ww2bunker": "Bunker", "village": "Village", "allycamp": "Camp", - "EWR":"EWR", + "EWR": "EWR", } ABBREV_NAME = { @@ -54,34 +54,59 @@ ABBREV_NAME = { } CATEGORY_MAP = { - # Special cases "CARRIER": ["CARRIER"], "LHA": ["LHA"], "aa": ["AA"], - # Buildings - "power": ["Workshop A", "Electric power box", "Garage small A", "Farm B", "Repair workshop", "Garage B"], + "power": [ + "Workshop A", + "Electric power box", + "Garage small A", + "Farm B", + "Repair workshop", + "Garage B", + ], "ware": ["Warehouse", "Hangar A"], "fuel": ["Tank", "Tank 2", "Tank 3", "Fuel tank"], "ammo": [".Ammunition depot", "Hangar B"], - "farp": ["FARP Tent", "FARP Ammo Dump Coating", "FARP Fuel Depot", "FARP Command Post", "FARP CP Blindage"], + "farp": [ + "FARP Tent", + "FARP Ammo Dump Coating", + "FARP Fuel Depot", + "FARP Command Post", + "FARP CP Blindage", + ], "fob": ["Bunker 2", "Bunker 1", "Garage small B", ".Command Center", "Barracks 2"], "factory": ["Tech combine", "Tech hangar A"], "comms": ["TV tower", "Comms tower M"], "oil": ["Oil platform"], "derrick": ["Oil derrick", "Pump station", "Subsidiary structure 2"], - "ww2bunker": ["Siegfried Line", "Fire Control Bunker", "SK_C_28_naval_gun", "Concertina Wire", "Czech hedgehogs 1"], + "ww2bunker": [ + "Siegfried Line", + "Fire Control Bunker", + "SK_C_28_naval_gun", + "Concertina Wire", + "Czech hedgehogs 1", + ], "village": ["Small house 1B", "Small House 1A", "Small warehouse 1"], "allycamp": [], } class TheaterGroundObject(MissionTarget): - - def __init__(self, name: str, category: str, group_id: int, position: Point, - heading: int, control_point: ControlPoint, dcs_identifier: str, - airbase_group: bool, sea_object: bool) -> None: + def __init__( + self, + name: str, + category: str, + group_id: int, + position: Point, + heading: int, + control_point: ControlPoint, + dcs_identifier: str, + airbase_group: bool, + sea_object: bool, + ) -> None: super().__init__(name, position) self.category = category self.group_id = group_id @@ -131,6 +156,7 @@ class TheaterGroundObject(MissionTarget): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if self.is_friendly(for_player): yield from [ # TODO: FlightType.LOGISTICS @@ -193,9 +219,18 @@ class TheaterGroundObject(MissionTarget): class BuildingGroundObject(TheaterGroundObject): - def __init__(self, name: str, category: str, group_id: int, object_id: int, - position: Point, heading: int, control_point: ControlPoint, - dcs_identifier: str, airbase_group=False) -> None: + def __init__( + self, + name: str, + category: str, + group_id: int, + object_id: int, + position: Point, + heading: int, + control_point: ControlPoint, + dcs_identifier: str, + airbase_group=False, + ) -> None: super().__init__( name=name, category=category, @@ -205,7 +240,7 @@ class BuildingGroundObject(TheaterGroundObject): control_point=control_point, dcs_identifier=dcs_identifier, airbase_group=airbase_group, - sea_object=False + sea_object=False, ) self.object_id = object_id # Other TGOs track deadness based on the number of alive units, but @@ -234,6 +269,7 @@ class BuildingGroundObject(TheaterGroundObject): class NavalGroundObject(TheaterGroundObject): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if not self.is_friendly(for_player): yield FlightType.ANTISHIP yield from super().mission_types(for_player) @@ -249,8 +285,7 @@ class GenericCarrierGroundObject(NavalGroundObject): # TODO: Why is this both a CP and a TGO? class CarrierGroundObject(GenericCarrierGroundObject): - def __init__(self, name: str, group_id: int, - control_point: ControlPoint) -> None: + def __init__(self, name: str, group_id: int, control_point: ControlPoint) -> None: super().__init__( name=name, category="CARRIER", @@ -260,7 +295,7 @@ class CarrierGroundObject(GenericCarrierGroundObject): control_point=control_point, dcs_identifier="CARRIER", airbase_group=True, - sea_object=True + sea_object=True, ) @property @@ -272,8 +307,7 @@ class CarrierGroundObject(GenericCarrierGroundObject): # TODO: Why is this both a CP and a TGO? class LhaGroundObject(GenericCarrierGroundObject): - def __init__(self, name: str, group_id: int, - control_point: ControlPoint) -> None: + def __init__(self, name: str, group_id: int, control_point: ControlPoint) -> None: super().__init__( name=name, category="LHA", @@ -283,7 +317,7 @@ class LhaGroundObject(GenericCarrierGroundObject): control_point=control_point, dcs_identifier="LHA", airbase_group=True, - sea_object=True + sea_object=True, ) @property @@ -294,8 +328,9 @@ class LhaGroundObject(GenericCarrierGroundObject): class MissileSiteGroundObject(TheaterGroundObject): - def __init__(self, name: str, group_id: int, position: Point, - control_point: ControlPoint) -> None: + def __init__( + self, name: str, group_id: int, position: Point, control_point: ControlPoint + ) -> None: super().__init__( name=name, category="aa", @@ -305,13 +340,19 @@ class MissileSiteGroundObject(TheaterGroundObject): control_point=control_point, dcs_identifier="AA", airbase_group=False, - sea_object=False + sea_object=False, ) class CoastalSiteGroundObject(TheaterGroundObject): - def __init__(self, name: str, group_id: int, position: Point, - control_point: ControlPoint, heading) -> None: + def __init__( + self, + name: str, + group_id: int, + position: Point, + control_point: ControlPoint, + heading, + ) -> None: super().__init__( name=name, category="aa", @@ -321,11 +362,10 @@ class CoastalSiteGroundObject(TheaterGroundObject): control_point=control_point, dcs_identifier="AA", airbase_group=False, - sea_object=False + sea_object=False, ) - class BaseDefenseGroundObject(TheaterGroundObject): """Base type for all base defenses.""" @@ -334,8 +374,14 @@ class BaseDefenseGroundObject(TheaterGroundObject): # This type gets used both for AA sites (SAM, AAA, or SHORAD). These should each # be split into their own types. class SamGroundObject(BaseDefenseGroundObject): - def __init__(self, name: str, group_id: int, position: Point, - control_point: ControlPoint, for_airbase: bool) -> None: + def __init__( + self, + name: str, + group_id: int, + position: Point, + control_point: ControlPoint, + for_airbase: bool, + ) -> None: super().__init__( name=name, category="aa", @@ -345,7 +391,7 @@ class SamGroundObject(BaseDefenseGroundObject): control_point=control_point, dcs_identifier="AA", airbase_group=for_airbase, - sea_object=False + sea_object=False, ) # Set by the SAM unit generator if the generated group is compatible # with Skynet. @@ -362,6 +408,7 @@ class SamGroundObject(BaseDefenseGroundObject): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if not self.is_friendly(for_player): yield FlightType.DEAD yield from super().mission_types(for_player) @@ -372,8 +419,14 @@ class SamGroundObject(BaseDefenseGroundObject): class VehicleGroupGroundObject(BaseDefenseGroundObject): - def __init__(self, name: str, group_id: int, position: Point, - control_point: ControlPoint, for_airbase: bool) -> None: + def __init__( + self, + name: str, + group_id: int, + position: Point, + control_point: ControlPoint, + for_airbase: bool, + ) -> None: super().__init__( name=name, category="aa", @@ -383,13 +436,14 @@ class VehicleGroupGroundObject(BaseDefenseGroundObject): control_point=control_point, dcs_identifier="AA", airbase_group=for_airbase, - sea_object=False + sea_object=False, ) class EwrGroundObject(BaseDefenseGroundObject): - def __init__(self, name: str, group_id: int, position: Point, - control_point: ControlPoint) -> None: + def __init__( + self, name: str, group_id: int, position: Point, control_point: ControlPoint + ) -> None: super().__init__( name=name, category="EWR", @@ -399,7 +453,7 @@ class EwrGroundObject(BaseDefenseGroundObject): control_point=control_point, dcs_identifier="EWR", airbase_group=True, - sea_object=False + sea_object=False, ) @property @@ -409,6 +463,7 @@ class EwrGroundObject(BaseDefenseGroundObject): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType + if not self.is_friendly(for_player): yield FlightType.DEAD yield from super().mission_types(for_player) @@ -419,8 +474,9 @@ class EwrGroundObject(BaseDefenseGroundObject): class ShipGroundObject(NavalGroundObject): - def __init__(self, name: str, group_id: int, position: Point, - control_point: ControlPoint) -> None: + def __init__( + self, name: str, group_id: int, position: Point, control_point: ControlPoint + ) -> None: super().__init__( name=name, category="aa", @@ -430,7 +486,7 @@ class ShipGroundObject(NavalGroundObject): control_point=control_point, dcs_identifier="AA", airbase_group=False, - sea_object=True + sea_object=True, ) @property diff --git a/game/threatzones.py b/game/threatzones.py index 17f4df96..865acf2f 100644 --- a/game/threatzones.py +++ b/game/threatzones.py @@ -32,8 +32,9 @@ class ThreatZones: self.all = unary_union([airbases, air_defenses]) def closest_boundary(self, point: DcsPoint) -> DcsPoint: - boundary, _ = nearest_points(self.all.boundary, - self.dcs_to_shapely_point(point)) + boundary, _ = nearest_points( + self.all.boundary, self.dcs_to_shapely_point(point) + ) return DcsPoint(boundary.x, boundary.y) @singledispatchmethod @@ -49,8 +50,9 @@ class ThreatZones: return self.all.intersects(self.dcs_to_shapely_point(position)) def path_threatened(self, a: DcsPoint, b: DcsPoint) -> bool: - return self.threatened(LineString( - [self.dcs_to_shapely_point(a), self.dcs_to_shapely_point(b)])) + return self.threatened( + LineString([self.dcs_to_shapely_point(a), self.dcs_to_shapely_point(b)]) + ) @singledispatchmethod def threatened_by_aircraft(self, target) -> bool: @@ -62,9 +64,9 @@ class ThreatZones: @threatened_by_aircraft.register def _threatened_by_aircraft_flight(self, flight: Flight) -> bool: - return self.threatened_by_aircraft(LineString(( - self.dcs_to_shapely_point(p.position) for p in flight.points - ))) + return self.threatened_by_aircraft( + LineString((self.dcs_to_shapely_point(p.position) for p in flight.points)) + ) @singledispatchmethod def threatened_by_air_defense(self, target) -> bool: @@ -76,13 +78,14 @@ class ThreatZones: @threatened_by_air_defense.register def _threatened_by_air_defense_flight(self, flight: Flight) -> bool: - return self.threatened_by_air_defense(LineString(( - self.dcs_to_shapely_point(p.position) for p in flight.points - ))) + return self.threatened_by_air_defense( + LineString((self.dcs_to_shapely_point(p.position) for p in flight.points)) + ) @classmethod - def closest_enemy_airbase(cls, location: ControlPoint, - max_distance: Distance) -> Optional[ControlPoint]: + def closest_enemy_airbase( + cls, location: ControlPoint, max_distance: Distance + ) -> Optional[ControlPoint]: airfields = ObjectiveDistanceCache.get_closest_airfields(location) for airfield in airfields.airfields_within(max_distance): if airfield.captured != location.captured: @@ -90,13 +93,14 @@ class ThreatZones: return None @classmethod - def barcap_threat_range(cls, game: Game, - control_point: ControlPoint) -> Distance: + def barcap_threat_range(cls, game: Game, control_point: ControlPoint) -> Distance: doctrine = game.faction_for(control_point.captured).doctrine - cap_threat_range = (doctrine.cap_max_distance_from_cp + - doctrine.cap_engagement_range) - opposing_airfield = cls.closest_enemy_airbase(control_point, - cap_threat_range * 2) + cap_threat_range = ( + doctrine.cap_max_distance_from_cp + doctrine.cap_engagement_range + ) + opposing_airfield = cls.closest_enemy_airbase( + control_point, cap_threat_range * 2 + ) if opposing_airfield is None: return cap_threat_range @@ -133,8 +137,7 @@ class ThreatZones: if control_point.captured != player: continue if control_point.runway_is_operational(): - point = ShapelyPoint(control_point.position.x, - control_point.position.y) + point = ShapelyPoint(control_point.position.x, control_point.position.y) cap_threat_range = cls.barcap_threat_range(game, control_point) airbases.append(point.buffer(cap_threat_range.meters)) @@ -149,10 +152,9 @@ class ThreatZones: air_defenses.append(threat_zone) return cls( - airbases=unary_union(airbases), - air_defenses=unary_union(air_defenses) + airbases=unary_union(airbases), air_defenses=unary_union(air_defenses) ) @staticmethod def dcs_to_shapely_point(point: DcsPoint) -> ShapelyPoint: - return ShapelyPoint(point.x, point.y) \ No newline at end of file + return ShapelyPoint(point.x, point.y) diff --git a/game/unitmap.py b/game/unitmap.py index d8aeaa65..149cba40 100644 --- a/game/unitmap.py +++ b/game/unitmap.py @@ -70,15 +70,19 @@ class UnitMap: raise RuntimeError(f"Unknown unit type: {unit.type}") if not issubclass(unit_type, VehicleType): raise RuntimeError( - f"{name} is a {unit_type.__name__}, expected a VehicleType") + f"{name} is a {unit_type.__name__}, expected a VehicleType" + ) self.front_line_units[name] = FrontLineUnit(unit_type, origin) def front_line_unit(self, name: str) -> Optional[FrontLineUnit]: return self.front_line_units.get(name, None) - def add_ground_object_units(self, ground_object: TheaterGroundObject, - persistence_group: Group, - miz_group: Group) -> None: + def add_ground_object_units( + self, + ground_object: TheaterGroundObject, + persistence_group: Group, + miz_group: Group, + ) -> None: """Adds a group associated with a TGO to the unit map. Args: @@ -103,13 +107,13 @@ class UnitMap: if name in self.ground_object_units: raise RuntimeError(f"Duplicate TGO unit: {name}") self.ground_object_units[name] = GroundObjectUnit( - ground_object, persistence_group, persistent_unit) + ground_object, persistence_group, persistent_unit + ) def ground_object_unit(self, name: str) -> Optional[GroundObjectUnit]: return self.ground_object_units.get(name, None) - def add_building(self, ground_object: BuildingGroundObject, - group: Group) -> None: + def add_building(self, ground_object: BuildingGroundObject, group: Group) -> None: # The actual name is a String (the pydcs translatable string), which # doesn't define __eq__. # The name of the initiator in the DCS dead event will have " object" @@ -119,8 +123,9 @@ class UnitMap: raise RuntimeError(f"Duplicate TGO unit: {name}") self.buildings[name] = Building(ground_object) - def add_fortification(self, ground_object: BuildingGroundObject, - group: VehicleGroup) -> None: + def add_fortification( + self, ground_object: BuildingGroundObject, group: VehicleGroup + ) -> None: if len(group.units) != 1: raise ValueError("Fortification groups must have exactly one unit.") unit = group.units[0] diff --git a/game/weather.py b/game/weather.py index ae9a6fd2..c7d33c39 100644 --- a/game/weather.py +++ b/game/weather.py @@ -58,7 +58,7 @@ class Weather: return None return Fog( visibility=meters(random.randint(2500, 5000)), - thickness=random.randint(100, 500) + thickness=random.randint(100, 500), ) def generate_wind(self) -> WindConditions: @@ -76,7 +76,7 @@ class Weather: # 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) + at_8000m=Wind(wind_direction, base_wind * at_8000m_factor), ) @staticmethod @@ -105,7 +105,7 @@ class Cloudy(Weather): base=self.random_cloud_base(), density=random.randint(1, 8), thickness=self.random_cloud_thickness(), - precipitation=PydcsWeather.Preceptions.None_ + precipitation=PydcsWeather.Preceptions.None_, ) def generate_wind(self) -> WindConditions: @@ -118,7 +118,7 @@ class Raining(Weather): base=self.random_cloud_base(), density=random.randint(5, 8), thickness=self.random_cloud_thickness(), - precipitation=PydcsWeather.Preceptions.Rain + precipitation=PydcsWeather.Preceptions.Rain, ) def generate_wind(self) -> WindConditions: @@ -131,7 +131,7 @@ class Thunderstorm(Weather): base=self.random_cloud_base(), density=random.randint(9, 10), thickness=self.random_cloud_thickness(), - precipitation=PydcsWeather.Preceptions.Thunderstorm + precipitation=PydcsWeather.Preceptions.Thunderstorm, ) def generate_wind(self) -> WindConditions: @@ -145,20 +145,29 @@ class Conditions: weather: Weather @classmethod - def generate(cls, theater: ConflictTheater, day: datetime.date, - time_of_day: TimeOfDay, settings: Settings) -> Conditions: + 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() + weather=cls.generate_weather(), ) @classmethod - def generate_start_time(cls, theater: ConflictTheater, day: datetime.date, - time_of_day: TimeOfDay, - night_disabled: bool) -> datetime.datetime: + 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 = { @@ -181,6 +190,7 @@ class Conditions: Cloudy: 60, ClearSkies: 20, } - weather_type = random.choices(list(chances.keys()), - weights=list(chances.values()))[0] + weather_type = random.choices( + list(chances.keys()), weights=list(chances.values()) + )[0] return weather_type() diff --git a/gen/aircraft.py b/gen/aircraft.py index 044b6eff..97fb75c4 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -303,14 +303,23 @@ class FlightData: joker_fuel: Optional[int] - def __init__(self, package: Package, country: str, flight_type: FlightType, - units: List[FlyingUnit], size: int, friendly: bool, - departure_delay: timedelta, departure: RunwayData, - arrival: RunwayData, divert: Optional[RunwayData], - waypoints: List[FlightWaypoint], - intra_flight_channel: RadioFrequency, - bingo_fuel: Optional[int], - joker_fuel: Optional[int]) -> None: + def __init__( + self, + package: Package, + country: str, + flight_type: FlightType, + units: List[FlyingUnit], + size: int, + friendly: bool, + departure_delay: timedelta, + departure: RunwayData, + arrival: RunwayData, + divert: Optional[RunwayData], + waypoints: List[FlightWaypoint], + intra_flight_channel: RadioFrequency, + bingo_fuel: Optional[int], + joker_fuel: Optional[int], + ) -> None: self.package = package self.country = country self.flight_type = flight_type @@ -343,13 +352,13 @@ class FlightData: # Note: pydcs only initializes the radio presets for client slots. return self.client_units[0].num_radio_channels(radio_id) - def channel_for( - self, frequency: RadioFrequency) -> Optional[ChannelAssignment]: + def channel_for(self, frequency: RadioFrequency) -> Optional[ChannelAssignment]: """Returns the radio and channel number for the given frequency.""" return self.frequency_to_channel_map.get(frequency, None) - def assign_channel(self, radio_id: int, channel_id: int, - frequency: RadioFrequency) -> None: + def assign_channel( + self, radio_id: int, channel_id: int, frequency: RadioFrequency + ) -> None: """Assigns a preset radio channel to the given frequency.""" for unit in self.client_units: unit.set_radio_channel_preset(radio_id, channel_id, frequency.mhz) @@ -366,8 +375,9 @@ class FlightData: class RadioChannelAllocator: """Base class for radio channel allocators.""" - def assign_channels_for_flight(self, flight: FlightData, - air_support: AirSupport) -> None: + def assign_channels_for_flight( + self, flight: FlightData, air_support: AirSupport + ) -> None: """Assigns mission frequencies to preset channels for the flight.""" raise NotImplementedError @@ -389,11 +399,13 @@ class CommonRadioChannelAllocator(RadioChannelAllocator): #: index of the panel_radio field of the pydcs.dcs.planes object. intra_flight_radio_index: Optional[int] - def assign_channels_for_flight(self, flight: FlightData, - air_support: AirSupport) -> None: + def assign_channels_for_flight( + self, flight: FlightData, air_support: AirSupport + ) -> None: if self.intra_flight_radio_index is not None: flight.assign_channel( - self.intra_flight_radio_index, 1, flight.intra_flight_channel) + self.intra_flight_radio_index, 1, flight.intra_flight_channel + ) if self.inter_flight_radio_index is None: return @@ -411,26 +423,22 @@ class CommonRadioChannelAllocator(RadioChannelAllocator): channel_alloc = iter(range(first_channel, last_channel + 1)) if flight.departure.atc is not None: - flight.assign_channel(radio_id, next(channel_alloc), - flight.departure.atc) + flight.assign_channel(radio_id, next(channel_alloc), flight.departure.atc) # TODO: If there ever are multiple AWACS, limit to mission relevant. for awacs in air_support.awacs: flight.assign_channel(radio_id, next(channel_alloc), awacs.freq) if flight.arrival != flight.departure and flight.arrival.atc is not None: - flight.assign_channel(radio_id, next(channel_alloc), - flight.arrival.atc) + flight.assign_channel(radio_id, next(channel_alloc), flight.arrival.atc) try: # TODO: Skip incompatible tankers. for tanker in air_support.tankers: - flight.assign_channel( - radio_id, next(channel_alloc), tanker.freq) + flight.assign_channel(radio_id, next(channel_alloc), tanker.freq) if flight.divert is not None and flight.divert.atc is not None: - flight.assign_channel(radio_id, next(channel_alloc), - flight.divert.atc) + flight.assign_channel(radio_id, next(channel_alloc), flight.divert.atc) except StopIteration: # Any remaining channels are nice-to-haves, but not necessary for # the few aircraft with a small number of channels available. @@ -441,8 +449,9 @@ class CommonRadioChannelAllocator(RadioChannelAllocator): class NoOpChannelAllocator(RadioChannelAllocator): """Channel allocator for aircraft that don't support preset channels.""" - def assign_channels_for_flight(self, flight: FlightData, - air_support: AirSupport) -> None: + def assign_channels_for_flight( + self, flight: FlightData, air_support: AirSupport + ) -> None: pass @@ -450,8 +459,9 @@ class NoOpChannelAllocator(RadioChannelAllocator): class FarmerRadioChannelAllocator(RadioChannelAllocator): """Preset channel allocator for the MiG-19P.""" - def assign_channels_for_flight(self, flight: FlightData, - air_support: AirSupport) -> None: + def assign_channels_for_flight( + self, flight: FlightData, air_support: AirSupport + ) -> None: # The Farmer only has 6 preset channels. It also only has a VHF radio, # and currently our ATC data and AWACS are only in the UHF band. radio_id = 1 @@ -464,8 +474,9 @@ class FarmerRadioChannelAllocator(RadioChannelAllocator): class ViggenRadioChannelAllocator(RadioChannelAllocator): """Preset channel allocator for the AJS37.""" - def assign_channels_for_flight(self, flight: FlightData, - air_support: AirSupport) -> None: + def assign_channels_for_flight( + self, flight: FlightData, air_support: AirSupport + ) -> None: # The Viggen's preset channels are handled differently from other # aircraft. The aircraft automatically configures channels for every # allied flight in the game (including AWACS) and for every airfield. As @@ -489,8 +500,9 @@ class ViggenRadioChannelAllocator(RadioChannelAllocator): class SCR522RadioChannelAllocator(RadioChannelAllocator): """Preset channel allocator for the SCR522 WW2 radios. (4 channels)""" - def assign_channels_for_flight(self, flight: FlightData, - air_support: AirSupport) -> None: + def assign_channels_for_flight( + self, flight: FlightData, air_support: AirSupport + ) -> None: radio_id = 1 flight.assign_channel(radio_id, 1, flight.intra_flight_channel) if flight.departure.atc is not None: @@ -527,9 +539,8 @@ AIRCRAFT_DATA: Dict[str, AircraftData] = { # VHF for intraflight is not accepted anymore by DCS # (see https://forums.eagle.ru/showthread.php?p=4499738). intra_flight_radio=get_radio("AN/ARC-164"), - channel_allocator=NoOpChannelAllocator() + channel_allocator=NoOpChannelAllocator(), ), - "AJS37": AircraftData( # The AJS37 has somewhat unique radio configuration. Two backup radio # (FR 24) can only operate simultaneously with the main radio in guard @@ -538,40 +549,33 @@ AIRCRAFT_DATA: Dict[str, AircraftData] = { inter_flight_radio=get_radio("FR 22"), intra_flight_radio=get_radio("FR 22"), channel_allocator=ViggenRadioChannelAllocator(), - channel_namer=ViggenChannelNamer + channel_namer=ViggenChannelNamer, ), - "AV8BNA": AircraftData( inter_flight_radio=get_radio("AN/ARC-210"), intra_flight_radio=get_radio("AN/ARC-210"), channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=2, - intra_flight_radio_index=1 - ) + inter_flight_radio_index=2, intra_flight_radio_index=1 + ), ), - "F-14B": AircraftData( inter_flight_radio=get_radio("AN/ARC-159"), intra_flight_radio=get_radio("AN/ARC-182"), channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=1, - intra_flight_radio_index=2 + inter_flight_radio_index=1, intra_flight_radio_index=2 ), - channel_namer=TomcatChannelNamer + channel_namer=TomcatChannelNamer, ), - "F-16C_50": AircraftData( inter_flight_radio=get_radio("AN/ARC-164"), intra_flight_radio=get_radio("AN/ARC-222"), # COM2 is the AN/ARC-222, which is the VHF radio we want to use for # intra-flight communication to leave COM1 open for UHF inter-flight. channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=1, - intra_flight_radio_index=2 + inter_flight_radio_index=1, intra_flight_radio_index=2 ), - channel_namer=ViperChannelNamer + channel_namer=ViperChannelNamer, ), - "FA-18C_hornet": AircraftData( inter_flight_radio=get_radio("AN/ARC-210"), intra_flight_radio=get_radio("AN/ARC-210"), @@ -580,22 +584,18 @@ AIRCRAFT_DATA: Dict[str, AircraftData] = { # AN/ARC-210s, radio 1 will be compatible regardless of which frequency # is assigned, so we must use radio 1 for the intra-flight radio. channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=2, - intra_flight_radio_index=1 - ) + inter_flight_radio_index=2, intra_flight_radio_index=1 + ), ), - "JF-17": AircraftData( inter_flight_radio=get_radio("R&S M3AR UHF"), intra_flight_radio=get_radio("R&S M3AR VHF"), channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=1, - intra_flight_radio_index=1 + inter_flight_radio_index=1, intra_flight_radio_index=1 ), # Same naming pattern as the Viper, so just reuse that. - channel_namer=ViperChannelNamer + channel_namer=ViperChannelNamer, ), - "Ka-50": AircraftData( inter_flight_radio=get_radio("R-800L1"), intra_flight_radio=get_radio("R-800L1"), @@ -604,50 +604,41 @@ AIRCRAFT_DATA: Dict[str, AircraftData] = { # radios assigned, so no channels to configure. channel_allocator=NoOpChannelAllocator(), ), - "M-2000C": AircraftData( inter_flight_radio=get_radio("TRT ERA 7000 V/UHF"), intra_flight_radio=get_radio("TRT ERA 7200 UHF"), channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=1, - intra_flight_radio_index=2 + inter_flight_radio_index=1, intra_flight_radio_index=2 ), - channel_namer=MirageChannelNamer + channel_namer=MirageChannelNamer, ), - "MiG-15bis": AircraftData( inter_flight_radio=get_radio("RSI-6K HF"), intra_flight_radio=get_radio("RSI-6K HF"), channel_allocator=NoOpChannelAllocator(), ), - "MiG-19P": AircraftData( inter_flight_radio=get_radio("RSIU-4V"), intra_flight_radio=get_radio("RSIU-4V"), channel_allocator=FarmerRadioChannelAllocator(), - channel_namer=SingleRadioChannelNamer + channel_namer=SingleRadioChannelNamer, ), - "MiG-21Bis": AircraftData( inter_flight_radio=get_radio("RSIU-5V"), intra_flight_radio=get_radio("RSIU-5V"), channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=1, - intra_flight_radio_index=1 + inter_flight_radio_index=1, intra_flight_radio_index=1 ), channel_namer=SingleRadioChannelNamer, ), - "P-51D": AircraftData( inter_flight_radio=get_radio("SCR522"), intra_flight_radio=get_radio("SCR522"), channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=1, - intra_flight_radio_index=1 + inter_flight_radio_index=1, intra_flight_radio_index=1 ), - channel_namer=SCR522ChannelNamer + channel_namer=SCR522ChannelNamer, ), - "UH-1H": AircraftData( inter_flight_radio=get_radio("AN/ARC-51BX"), # Ideally this would use the AN/ARC-131 because that radio is supposed @@ -655,11 +646,10 @@ AIRCRAFT_DATA: Dict[str, AircraftData] = { # frequency, nor will it allow the AN/ARC-134. intra_flight_radio=get_radio("AN/ARC-51BX"), channel_allocator=CommonRadioChannelAllocator( - inter_flight_radio_index=1, - intra_flight_radio_index=1 + inter_flight_radio_index=1, intra_flight_radio_index=1 ), - channel_namer=HueyChannelNamer - ) + channel_namer=HueyChannelNamer, + ), } AIRCRAFT_DATA["A-10C_2"] = AIRCRAFT_DATA["A-10C"] AIRCRAFT_DATA["P-51D-30-NA"] = AIRCRAFT_DATA["P-51D"] @@ -667,8 +657,14 @@ AIRCRAFT_DATA["P-47D-30"] = AIRCRAFT_DATA["P-51D"] class AircraftConflictGenerator: - def __init__(self, mission: Mission, settings: Settings, game: Game, - radio_registry: RadioRegistry, unit_map: UnitMap) -> None: + def __init__( + self, + mission: Mission, + settings: Settings, + game: Game, + radio_registry: RadioRegistry, + unit_map: UnitMap, + ) -> None: self.m = mission self.game = game self.settings = settings @@ -702,8 +698,7 @@ class AircraftConflictGenerator: """ try: aircraft_data = AIRCRAFT_DATA[airframe.id] - return self.radio_registry.alloc_for_radio( - aircraft_data.intra_flight_radio) + return self.radio_registry.alloc_for_radio(aircraft_data.intra_flight_radio) except KeyError: return get_fallback_channel(airframe) @@ -715,9 +710,14 @@ class AircraftConflictGenerator: return StartType.Cold return StartType.Warm - def _setup_group(self, group: FlyingGroup, for_task: Type[Task], - package: Package, flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def _setup_group( + self, + group: FlyingGroup, + for_task: Type[Task], + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: did_load_loadout = False unit_type = group.units[0].unit_type @@ -733,13 +733,20 @@ class AircraftConflictGenerator: if not group.units[0].pylons and for_task == RunwayAttack: if PinpointStrike in db.PLANE_PAYLOAD_OVERRIDES[unit_type]: logging.warning( - "No loadout for \"Runway Attack\" for the {}, defaulting to Strike loadout".format( - str(unit_type))) - payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][PinpointStrike] + 'No loadout for "Runway Attack" for the {}, defaulting to Strike loadout'.format( + str(unit_type) + ) + ) + payload_name = db.PLANE_PAYLOAD_OVERRIDES[unit_type][ + PinpointStrike + ] group.load_loadout(payload_name) did_load_loadout = True logging.info( - "Loaded overridden payload for {} - {} for task {}".format(unit_type, payload_name, for_task)) + "Loaded overridden payload for {} - {} for task {}".format( + unit_type, payload_name, for_task + ) + ) if not did_load_loadout: group.load_task_default_loadout(for_task) @@ -774,45 +781,55 @@ class AircraftConflictGenerator: if unit_type is F_14B: unit.set_property(F_14B.Properties.INSAlignmentStored.id, True) - group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)) + group.points[0].tasks.append( + OptReactOnThreat(OptReactOnThreat.Values.EvadeFire) + ) channel = self.get_intra_flight_channel(unit_type) group.set_frequency(channel.mhz) divert = None if flight.divert is not None: - divert = flight.divert.active_runway(self.game.conditions, - dynamic_runways) + divert = flight.divert.active_runway(self.game.conditions, dynamic_runways) - self.flights.append(FlightData( - package=package, - country=faction.country, - flight_type=flight.flight_type, - units=group.units, - size=len(group.units), - friendly=flight.from_cp.captured, - # Set later. - departure_delay=timedelta(), - departure=flight.departure.active_runway(self.game.conditions, - dynamic_runways), - arrival=flight.arrival.active_runway(self.game.conditions, - dynamic_runways), - divert=divert, - # Waypoints are added later, after they've had their TOTs set. - waypoints=[], - intra_flight_channel=channel, - bingo_fuel=flight.flight_plan.bingo_fuel, - joker_fuel=flight.flight_plan.joker_fuel - )) + self.flights.append( + FlightData( + package=package, + country=faction.country, + flight_type=flight.flight_type, + units=group.units, + size=len(group.units), + friendly=flight.from_cp.captured, + # Set later. + departure_delay=timedelta(), + departure=flight.departure.active_runway( + self.game.conditions, dynamic_runways + ), + arrival=flight.arrival.active_runway( + self.game.conditions, dynamic_runways + ), + divert=divert, + # Waypoints are added later, after they've had their TOTs set. + waypoints=[], + intra_flight_channel=channel, + bingo_fuel=flight.flight_plan.bingo_fuel, + joker_fuel=flight.flight_plan.joker_fuel, + ) + ) # Special case so Su 33 and C101 can take off if unit_type in [Su_33, C_101EB, C_101CC]: self.set_reduced_fuel(flight, group, unit_type) - def _generate_at_airport(self, name: str, side: Country, - unit_type: Type[FlyingType], count: int, - start_type: str, - airport: Optional[Airport] = None) -> FlyingGroup: + def _generate_at_airport( + self, + name: str, + side: Country, + unit_type: Type[FlyingType], + count: int, + start_type: str, + airport: Optional[Airport] = None, + ) -> FlyingGroup: assert count > 0 logging.info("airgen: {} for {} at {}".format(unit_type, side.id, airport)) @@ -824,10 +841,12 @@ class AircraftConflictGenerator: maintask=None, start_type=self._start_type(start_type), group_size=count, - parking_slots=None) + parking_slots=None, + ) - def _generate_inflight(self, name: str, side: Country, flight: Flight, - origin: ControlPoint) -> FlyingGroup: + def _generate_inflight( + self, name: str, side: Country, flight: Flight, origin: ControlPoint + ) -> FlyingGroup: assert flight.count > 0 at = origin.position @@ -845,8 +864,10 @@ class AircraftConflictGenerator: pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000)) logging.info( - "airgen: {} for {} at {} at {}".format(flight.unit_type, side.id, - alt, int(speed.kph))) + "airgen: {} for {} at {} at {}".format( + flight.unit_type, side.id, alt, int(speed.kph) + ) + ) group = self.m.flight_group( country=side, name=name, @@ -856,15 +877,21 @@ class AircraftConflictGenerator: altitude=alt.meters, speed=speed.kph, maintask=None, - group_size=flight.count) + group_size=flight.count, + ) group.points[0].alt_type = alt_type return group - def _generate_at_group(self, name: str, side: Country, - unit_type: Type[FlyingType], count: int, - start_type: str, - at: Union[ShipGroup, StaticGroup]) -> FlyingGroup: + def _generate_at_group( + self, + name: str, + side: Country, + unit_type: Type[FlyingType], + count: int, + start_type: str, + at: Union[ShipGroup, StaticGroup], + ) -> FlyingGroup: assert count > 0 logging.info("airgen: {} for {} at unit {}".format(unit_type, side.id, at)) @@ -875,17 +902,22 @@ class AircraftConflictGenerator: pad_group=at, maintask=None, start_type=self._start_type(start_type), - group_size=count) + group_size=count, + ) - def _add_radio_waypoint(self, group: FlyingGroup, position, - altitude: Distance, - airspeed: int = 600) -> MovingPoint: + def _add_radio_waypoint( + self, group: FlyingGroup, position, altitude: Distance, airspeed: int = 600 + ) -> MovingPoint: point = group.add_waypoint(position, altitude.meters, airspeed) point.alt_type = "RADIO" return point - def _rtb_for(self, group: FlyingGroup, cp: ControlPoint, - at: Optional[db.StartingPosition] = None): + def _rtb_for( + self, + group: FlyingGroup, + cp: ControlPoint, + at: Optional[db.StartingPosition] = None, + ): if at is None: at = cp.at position = at if isinstance(at, Point) else at.position @@ -896,8 +928,7 @@ class AircraftConflictGenerator: tod_location = position.point_from_heading(heading, RTB_DISTANCE) self._add_radio_waypoint(group, tod_location, last_waypoint.alt) - destination_waypoint = self._add_radio_waypoint(group, position, - RTB_ALTITUDE) + destination_waypoint = self._add_radio_waypoint(group, position, RTB_ALTITUDE) if isinstance(at, Airport): group.land_at(at) return destination_waypoint @@ -927,8 +958,7 @@ class AircraftConflictGenerator: pylon = Pylon.for_aircraft(flight.unit_type, pylon_number) pylon.equip(group, weapon) - def _degrade_payload_to_era(self, flight: Flight, - group: FlyingGroup) -> None: + def _degrade_payload_to_era(self, flight: Flight, group: FlyingGroup) -> None: loadout = dict(group.units[0].pylons) for pylon_number, clsid in loadout.items(): weapon = Weapon.from_clsid(clsid["CLSID"]) @@ -951,22 +981,23 @@ class AircraftConflictGenerator: for parking_slot in cp.parking_slots: parking_slot.unit_id = None - def generate_flights(self, country, ato: AirTaskingOrder, - dynamic_runways: Dict[str, RunwayData]) -> None: + def generate_flights( + self, country, ato: AirTaskingOrder, dynamic_runways: Dict[str, RunwayData] + ) -> None: for package in ato.packages: if not package.flights: continue for flight in package.flights: logging.info(f"Generating flight: {flight.unit_type}") - group = self.generate_planned_flight(flight.from_cp, country, - flight) + group = self.generate_planned_flight(flight.from_cp, country, flight) self.unit_map.add_aircraft(group, flight) self.setup_flight_group(group, package, flight, dynamic_runways) self.create_waypoints(group, package, flight) - def spawn_unused_aircraft(self, player_country: Country, - enemy_country: Country) -> None: + def spawn_unused_aircraft( + self, player_country: Country, enemy_country: Country + ) -> None: inventories = self.game.aircraft_inventory.inventories for control_point, inventory in inventories.items(): if not isinstance(control_point, Airfield): @@ -981,30 +1012,45 @@ class AircraftConflictGenerator: for aircraft, available in inventory.all_aircraft: try: - self._spawn_unused_at(control_point, country, faction, aircraft, - available) + self._spawn_unused_at( + control_point, country, faction, aircraft, available + ) except NoParkingSlotError: # If we run out of parking, stop spawning aircraft. return - def _spawn_unused_at(self, control_point: Airfield, country: Country, faction: Faction, - aircraft: Type[FlyingType], number: int) -> None: + def _spawn_unused_at( + self, + control_point: Airfield, + country: Country, + faction: Faction, + aircraft: Type[FlyingType], + number: int, + ) -> None: for _ in range(number): # Creating a flight even those this isn't a fragged mission lets us # reuse the existing debriefing code. # TODO: Special flight type? - flight = Flight(Package(control_point), faction.country, aircraft, 1, - FlightType.BARCAP, "Cold", departure=control_point, - arrival=control_point, divert=None) + flight = Flight( + Package(control_point), + faction.country, + aircraft, + 1, + FlightType.BARCAP, + "Cold", + departure=control_point, + arrival=control_point, + divert=None, + ) group = self._generate_at_airport( - name=namegen.next_aircraft_name(country, control_point.id, - flight), + name=namegen.next_aircraft_name(country, control_point.id, flight), side=country, unit_type=aircraft, count=1, start_type="Cold", - airport=control_point.airport) + airport=control_point.airport, + ) if aircraft in faction.liveries_overrides: livery = random.choice(faction.liveries_overrides[aircraft]) @@ -1014,8 +1060,9 @@ class AircraftConflictGenerator: group.uncontrolled = True self.unit_map.add_aircraft(group, flight) - def set_activation_time(self, flight: Flight, group: FlyingGroup, - delay: timedelta) -> None: + def set_activation_time( + self, flight: Flight, group: FlyingGroup, delay: timedelta + ) -> None: # Note: Late activation causes the waypoint TOTs to look *weird* in the # mission editor. Waypoint times will be relative to the group # activation time rather than in absolute local time. A flight delayed @@ -1024,31 +1071,31 @@ class AircraftConflictGenerator: group.late_activation = True activation_trigger = TriggerOnce( - Event.NoEvent, f"FlightLateActivationTrigger{group.id}") - activation_trigger.add_condition( - TimeAfter(seconds=int(delay.total_seconds()))) + Event.NoEvent, f"FlightLateActivationTrigger{group.id}" + ) + activation_trigger.add_condition(TimeAfter(seconds=int(delay.total_seconds()))) self.prevent_spawn_at_hostile_airbase(flight, activation_trigger) activation_trigger.add_action(ActivateGroup(group.id)) self.m.triggerrules.triggers.append(activation_trigger) - def set_startup_time(self, flight: Flight, group: FlyingGroup, - delay: timedelta) -> None: + def set_startup_time( + self, flight: Flight, group: FlyingGroup, delay: timedelta + ) -> None: # Uncontrolled causes the AI unit to spawn, but not begin startup. group.uncontrolled = True - activation_trigger = TriggerOnce(Event.NoEvent, - f"FlightStartTrigger{group.id}") - activation_trigger.add_condition( - TimeAfter(seconds=int(delay.total_seconds()))) + activation_trigger = TriggerOnce(Event.NoEvent, f"FlightStartTrigger{group.id}") + activation_trigger.add_condition(TimeAfter(seconds=int(delay.total_seconds()))) self.prevent_spawn_at_hostile_airbase(flight, activation_trigger) group.add_trigger_action(StartCommand()) activation_trigger.add_action(AITaskPush(group.id, len(group.tasks))) self.m.triggerrules.triggers.append(activation_trigger) - def prevent_spawn_at_hostile_airbase(self, flight: Flight, - trigger: TriggerRule) -> None: + def prevent_spawn_at_hostile_airbase( + self, flight: Flight, trigger: TriggerRule + ) -> None: # Prevent delayed flights from spawning at airbases if they were # captured before they've spawned. if flight.from_cp.cptype != ControlPointType.AIRBASE: @@ -1059,18 +1106,15 @@ class AircraftConflictGenerator: else: coalition = self.game.get_enemy_coalition_id() - trigger.add_condition( - CoalitionHasAirdrome(coalition, flight.from_cp.id)) + trigger.add_condition(CoalitionHasAirdrome(coalition, flight.from_cp.id)) def generate_planned_flight(self, cp, country, flight: Flight): name = namegen.next_aircraft_name(country, cp.id, flight) try: if flight.start_type == "In Flight": group = self._generate_inflight( - name=name, - side=country, - flight=flight, - origin=cp) + name=name, side=country, flight=flight, origin=cp + ) elif isinstance(cp, NavalControlPoint): group_name = cp.get_carrier_group_name() group = self._generate_at_group( @@ -1079,34 +1123,39 @@ class AircraftConflictGenerator: unit_type=flight.unit_type, count=flight.count, start_type=flight.start_type, - at=self.m.find_group(group_name)) + at=self.m.find_group(group_name), + ) else: if not isinstance(cp, Airfield): raise RuntimeError( - f"Attempted to spawn at airfield for non-airfield {cp}") + f"Attempted to spawn at airfield for non-airfield {cp}" + ) group = self._generate_at_airport( name=name, side=country, unit_type=flight.unit_type, count=flight.count, start_type=flight.start_type, - airport=cp.airport) + airport=cp.airport, + ) except Exception as e: # Generated when there is no place on Runway or on Parking Slots logging.error(e) - logging.warning("No room on runway or parking slots. Starting from the air.") + logging.warning( + "No room on runway or parking slots. Starting from the air." + ) flight.start_type = "In Flight" group = self._generate_inflight( - name=name, - side=country, - flight=flight, - origin=cp) + name=name, side=country, flight=flight, origin=cp + ) group.points[0].alt = 1500 return group @staticmethod - def set_reduced_fuel(flight: Flight, group: FlyingGroup, unit_type: Type[PlaneType]) -> None: + def set_reduced_fuel( + flight: Flight, group: FlyingGroup, unit_type: Type[PlaneType] + ) -> None: if unit_type is Su_33: for unit in group.units: if flight.flight_type is not CAP: @@ -1121,11 +1170,12 @@ class AircraftConflictGenerator: @staticmethod def configure_behavior( - group: FlyingGroup, - react_on_threat: Optional[OptReactOnThreat.Values] = None, - roe: Optional[OptROE.Values] = None, - rtb_winchester: Optional[OptRTBOnOutOfAmmo.Values] = None, - restrict_jettison: Optional[bool] = None) -> None: + group: FlyingGroup, + react_on_threat: Optional[OptReactOnThreat.Values] = None, + roe: Optional[OptROE.Values] = None, + rtb_winchester: Optional[OptRTBOnOutOfAmmo.Values] = None, + restrict_jettison: Optional[bool] = None, + ) -> None: group.points[0].tasks.clear() if react_on_threat is not None: group.points[0].tasks.append(OptReactOnThreat(react_on_threat)) @@ -1142,13 +1192,17 @@ class AircraftConflictGenerator: @staticmethod def configure_eplrs(group: FlyingGroup, flight: Flight) -> None: - if hasattr(flight.unit_type, 'eplrs'): + if hasattr(flight.unit_type, "eplrs"): if flight.unit_type.eplrs: group.points[0].tasks.append(EPLRS(group.id)) - def configure_cap(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_cap( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = CAP.name self._setup_group(group, CAP, package, flight, dynamic_runways) @@ -1159,9 +1213,13 @@ class AircraftConflictGenerator: self.configure_behavior(group, rtb_winchester=ammo_type) - def configure_sweep(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_sweep( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = FighterSweep.name self._setup_group(group, FighterSweep, package, flight, dynamic_runways) @@ -1172,9 +1230,13 @@ class AircraftConflictGenerator: self.configure_behavior(group, rtb_winchester=ammo_type) - def configure_cas(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_cas( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = CAS.name self._setup_group(group, CAS, package, flight, dynamic_runways) self.configure_behavior( @@ -1182,11 +1244,16 @@ class AircraftConflictGenerator: react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.OpenFire, rtb_winchester=OptRTBOnOutOfAmmo.Values.Unguided, - restrict_jettison=True) + restrict_jettison=True, + ) - def configure_dead(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_dead( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = SEAD.name self._setup_group(group, SEAD, package, flight, dynamic_runways) self.configure_behavior( @@ -1194,11 +1261,16 @@ class AircraftConflictGenerator: react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.OpenFire, rtb_winchester=OptRTBOnOutOfAmmo.Values.ASM, - restrict_jettison=True) + restrict_jettison=True, + ) - def configure_sead(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_sead( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = SEAD.name self._setup_group(group, SEAD, package, flight, dynamic_runways) self.configure_behavior( @@ -1206,86 +1278,122 @@ class AircraftConflictGenerator: react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.OpenFire, rtb_winchester=OptRTBOnOutOfAmmo.Values.ASM, - restrict_jettison=True) + restrict_jettison=True, + ) - def configure_strike(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_strike( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = GroundAttack.name self._setup_group(group, GroundAttack, package, flight, dynamic_runways) self.configure_behavior( group, react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.OpenFire, - restrict_jettison=True) + restrict_jettison=True, + ) - def configure_anti_ship(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_anti_ship( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = AntishipStrike.name - self._setup_group(group, AntishipStrike, package, flight, - dynamic_runways) + self._setup_group(group, AntishipStrike, package, flight, dynamic_runways) self.configure_behavior( group, react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.OpenFire, - restrict_jettison=True) + restrict_jettison=True, + ) def configure_runway_attack( - self, group: FlyingGroup, package: Package, flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = RunwayAttack.name self._setup_group(group, RunwayAttack, package, flight, dynamic_runways) self.configure_behavior( group, react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.OpenFire, - restrict_jettison=True) + restrict_jettison=True, + ) def configure_oca_strike( - self, group: FlyingGroup, package: Package, flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = CAS.name self._setup_group(group, CAS, package, flight, dynamic_runways) self.configure_behavior( group, react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.OpenFire, - restrict_jettison=True) + restrict_jettison=True, + ) def configure_awacs( - self, group: FlyingGroup, package: Package, flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: group.task = AWACS.name self._setup_group(group, AWACS, package, flight, dynamic_runways) self.configure_behavior( group, react_on_threat=OptReactOnThreat.Values.EvadeFire, roe=OptROE.Values.WeaponHold, - restrict_jettison=True) + restrict_jettison=True, + ) - def configure_escort(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def configure_escort( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: # Escort groups are actually given the CAP task so they can perform the # Search Then Engage task, which we have to use instead of the Escort # task for the reasons explained in JoinPointBuilder. group.task = CAP.name self._setup_group(group, CAP, package, flight, dynamic_runways) - self.configure_behavior(group, roe=OptROE.Values.OpenFire, - restrict_jettison=True) + self.configure_behavior( + group, roe=OptROE.Values.OpenFire, restrict_jettison=True + ) - def configure_unknown_task(self, group: FlyingGroup, - flight: Flight) -> None: + def configure_unknown_task(self, group: FlyingGroup, flight: Flight) -> None: logging.error(f"Unhandled flight type: {flight.flight_type}") self.configure_behavior(group) - def setup_flight_group(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + def setup_flight_group( + self, + group: FlyingGroup, + package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData], + ) -> None: flight_type = flight.flight_type - if flight_type in [FlightType.BARCAP, FlightType.TARCAP, - FlightType.INTERCEPTION]: + if flight_type in [ + FlightType.BARCAP, + FlightType.TARCAP, + FlightType.INTERCEPTION, + ]: self.configure_cap(group, package, flight, dynamic_runways) elif flight_type == FlightType.SWEEP: self.configure_sweep(group, package, flight, dynamic_runways) @@ -1304,8 +1412,7 @@ class AircraftConflictGenerator: elif flight_type == FlightType.ESCORT: self.configure_escort(group, package, flight, dynamic_runways) elif flight_type == FlightType.OCA_RUNWAY: - self.configure_runway_attack(group, package, flight, - dynamic_runways) + self.configure_runway_attack(group, package, flight, dynamic_runways) elif flight_type == FlightType.OCA_AIRCRAFT: self.configure_oca_strike(group, package, flight, dynamic_runways) else: @@ -1314,13 +1421,13 @@ class AircraftConflictGenerator: self.configure_eplrs(group, flight) def create_waypoints( - self, group: FlyingGroup, package: Package, flight: Flight) -> None: + self, group: FlyingGroup, package: Package, flight: Flight + ) -> None: for waypoint in flight.points: waypoint.tot = None - takeoff_point = FlightWaypoint.from_pydcs(group.points[0], - flight.from_cp) + takeoff_point = FlightWaypoint.from_pydcs(group.points[0], flight.from_cp) self.set_takeoff_time(takeoff_point, package, flight, group) filtered_points = [] # type: List[FlightWaypoint] @@ -1336,13 +1443,20 @@ class AircraftConflictGenerator: # this could be updated to make it pick the "best" two targets in the group. if flight.unit_type is AJS37 and flight.client_count: viggen_target_points = [ - (idx, point) for idx, point in enumerate(filtered_points) if point.waypoint_type in TARGET_WAYPOINTS + (idx, point) + for idx, point in enumerate(filtered_points) + if point.waypoint_type in TARGET_WAYPOINTS ] if viggen_target_points: - keep_target = viggen_target_points[random.randint(0, len(viggen_target_points) - 1)] + keep_target = viggen_target_points[ + random.randint(0, len(viggen_target_points) - 1) + ] filtered_points = [ - point for idx, point in enumerate(filtered_points) if ( - point.waypoint_type not in TARGET_WAYPOINTS or idx == keep_target[0] + point + for idx, point in enumerate(filtered_points) + if ( + point.waypoint_type not in TARGET_WAYPOINTS + or idx == keep_target[0] ) ] @@ -1358,8 +1472,7 @@ class AircraftConflictGenerator: if self.game.settings.restrict_weapons_by_date: self._degrade_payload_to_era(flight, group) - def should_delay_flight(self, flight: Flight, - start_time: timedelta) -> bool: + def should_delay_flight(self, flight: Flight, start_time: timedelta) -> bool: if start_time.total_seconds() <= 0: return False @@ -1374,8 +1487,13 @@ class AircraftConflictGenerator: return not self.settings.never_delay_player_flights - def set_takeoff_time(self, waypoint: FlightWaypoint, package: Package, - flight: Flight, group: FlyingGroup) -> None: + def set_takeoff_time( + self, + waypoint: FlightWaypoint, + package: Package, + flight: Flight, + group: FlyingGroup, + ) -> None: estimator = TotEstimator(package) start_time = estimator.mission_start_time(flight) @@ -1414,9 +1532,14 @@ class AircraftConflictGenerator: class PydcsWaypointBuilder: - def __init__(self, waypoint: FlightWaypoint, group: FlyingGroup, - package: Package, flight: Flight, - mission: Mission) -> None: + def __init__( + self, + waypoint: FlightWaypoint, + group: FlyingGroup, + package: Package, + flight: Flight, + mission: Mission, + ) -> None: self.waypoint = waypoint self.group = group self.package = package @@ -1427,7 +1550,8 @@ class PydcsWaypointBuilder: waypoint = self.group.add_waypoint( Point(self.waypoint.x, self.waypoint.y), self.waypoint.alt.meters, - name=self.mission.string(self.waypoint.name)) + name=self.mission.string(self.waypoint.name), + ) if self.waypoint.flyover: waypoint.action = PointAction.FlyOverPoint @@ -1449,9 +1573,14 @@ class PydcsWaypointBuilder: waypoint.speed_locked = False @classmethod - def for_waypoint(cls, waypoint: FlightWaypoint, group: FlyingGroup, - package: Package, flight: Flight, - mission: Mission) -> PydcsWaypointBuilder: + def for_waypoint( + cls, + waypoint: FlightWaypoint, + group: FlyingGroup, + package: Package, + flight: Flight, + mission: Mission, + ) -> PydcsWaypointBuilder: builders = { FlightWaypointType.INGRESS_BAI: BaiIngressBuilder, FlightWaypointType.INGRESS_CAS: CasIngressBuilder, @@ -1474,9 +1603,8 @@ class PydcsWaypointBuilder: """Viggen player aircraft consider any waypoint with a TOT set to be a target ("M") waypoint. If the flight is a player controlled Viggen flight, no TOT should be set on any waypoint except actual target waypoints. """ - if ( - (self.flight.client_count > 0 and self.flight.unit_type == AJS37) and - (self.waypoint.waypoint_type not in TARGET_WAYPOINTS) + if (self.flight.client_count > 0 and self.flight.unit_type == AJS37) and ( + self.waypoint.waypoint_type not in TARGET_WAYPOINTS ): return True else: @@ -1498,10 +1626,9 @@ class DefaultWaypointBuilder(PydcsWaypointBuilder): class HoldPointBuilder(PydcsWaypointBuilder): def build(self) -> MovingPoint: waypoint = super().build() - loiter = ControlledTask(OrbitAction( - altitude=waypoint.alt, - pattern=OrbitAction.OrbitPattern.Circle - )) + loiter = ControlledTask( + OrbitAction(altitude=waypoint.alt, pattern=OrbitAction.OrbitPattern.Circle) + ) if not isinstance(self.flight.flight_plan, LoiterFlightPlan): flight_plan_type = self.flight.flight_plan.__class__.__name__ logging.error( @@ -1532,11 +1659,14 @@ class BaiIngressBuilder(PydcsWaypointBuilder): task.params["groupAttack"] = True waypoint.tasks.append(task) else: - logging.error("Could not find group for BAI mission %s", - target_group.group_name) + logging.error( + "Could not find group for BAI mission %s", target_group.group_name + ) else: - logging.error("Unexpected target type for BAI mission: %s", - target_group.__class__.__name__) + logging.error( + "Unexpected target type for BAI mission: %s", + target_group.__class__.__name__, + ) return waypoint @@ -1544,25 +1674,28 @@ class CasIngressBuilder(PydcsWaypointBuilder): def build(self) -> MovingPoint: waypoint = super().build() if isinstance(self.flight.flight_plan, CasFlightPlan): - waypoint.add_task(EngageTargetsInZone( - position=self.flight.flight_plan.target, - radius=int(self.flight.flight_plan.engagement_distance.meters), - targets=[ - Targets.All.GroundUnits.GroundVehicles, - Targets.All.GroundUnits.AirDefence.AAA, - Targets.All.GroundUnits.Infantry, - ]) + waypoint.add_task( + EngageTargetsInZone( + position=self.flight.flight_plan.target, + radius=int(self.flight.flight_plan.engagement_distance.meters), + targets=[ + Targets.All.GroundUnits.GroundVehicles, + Targets.All.GroundUnits.AirDefence.AAA, + Targets.All.GroundUnits.Infantry, + ], + ) ) else: - logging.error( - "No CAS waypoint found. Falling back to search and engage") - waypoint.add_task(EngageTargets( - max_distance=int(nautical_miles(10).meters), - targets=[ - Targets.All.GroundUnits.GroundVehicles, - Targets.All.GroundUnits.AirDefence.AAA, - Targets.All.GroundUnits.Infantry, - ]) + logging.error("No CAS waypoint found. Falling back to search and engage") + waypoint.add_task( + EngageTargets( + max_distance=int(nautical_miles(10).meters), + targets=[ + Targets.All.GroundUnits.GroundVehicles, + Targets.All.GroundUnits.AirDefence.AAA, + Targets.All.GroundUnits.Infantry, + ], + ) ) return waypoint @@ -1583,7 +1716,9 @@ class DeadIngressBuilder(PydcsWaypointBuilder): task.params["groupAttack"] = True waypoint.tasks.append(task) else: - logging.error(f"Could not find group for DEAD mission {target_group.group_name}") + logging.error( + f"Could not find group for DEAD mission {target_group.group_name}" + ) self.register_special_waypoints(self.waypoint.targets) return waypoint @@ -1596,7 +1731,8 @@ class OcaAircraftIngressBuilder(PydcsWaypointBuilder): if not isinstance(target, Airfield): logging.error( "Unexpected target type for OCA Strike mission: %s", - target.__class__.__name__) + target.__class__.__name__, + ) return waypoint task = EngageTargetsInZone( @@ -1604,7 +1740,7 @@ class OcaAircraftIngressBuilder(PydcsWaypointBuilder): # Al Dhafra is 4 nm across at most. Add a little wiggle room in case # the airport position from DCS is not centered. radius=int(nautical_miles(3).meters), - targets=[Targets.All.Air] + targets=[Targets.All.Air], ) task.params["attackQtyLimit"] = False task.params["directionEnabled"] = False @@ -1622,11 +1758,13 @@ class OcaRunwayIngressBuilder(PydcsWaypointBuilder): if not isinstance(target, Airfield): logging.error( "Unexpected target type for runway bombing mission: %s", - target.__class__.__name__) + target.__class__.__name__, + ) return waypoint waypoint.tasks.append( - BombingRunway(airport_id=target.airport.id, group_attack=True)) + BombingRunway(airport_id=target.airport.id, group_attack=True) + ) return waypoint @@ -1638,15 +1776,19 @@ class SeadIngressBuilder(PydcsWaypointBuilder): if isinstance(target_group, TheaterGroundObject): tgroup = self.mission.find_group(target_group.group_name) if tgroup is not None: - waypoint.add_task(EngageTargetsInZone( - position=tgroup.position, - radius=int(nautical_miles(30).meters), - targets=[ - Targets.All.GroundUnits.AirDefence, - ]) + waypoint.add_task( + EngageTargetsInZone( + position=tgroup.position, + radius=int(nautical_miles(30).meters), + targets=[ + Targets.All.GroundUnits.AirDefence, + ], + ) ) else: - logging.error(f"Could not find group for DEAD mission {target_group.group_name}") + logging.error( + f"Could not find group for DEAD mission {target_group.group_name}" + ) self.register_special_waypoints(self.waypoint.targets) return waypoint @@ -1718,12 +1860,16 @@ class SweepIngressBuilder(PydcsWaypointBuilder): flight_plan_type = self.flight.flight_plan.__class__.__name__ logging.error( f"Cannot create sweep for {self.flight} because " - f"{flight_plan_type} is not a sweep flight plan.") + f"{flight_plan_type} is not a sweep flight plan." + ) return waypoint - waypoint.tasks.append(EngageTargets( - max_distance=int(nautical_miles(50).meters), - targets=[Targets.All.Air.Planes.Fighters])) + waypoint.tasks.append( + EngageTargets( + max_distance=int(nautical_miles(50).meters), + targets=[Targets.All.Air.Planes.Fighters], + ) + ) return waypoint @@ -1763,11 +1909,15 @@ class JoinPointBuilder(PydcsWaypointBuilder): # the strike aircraft finish their attack task. # # https://forums.eagle.ru/forum/english/digital-combat-simulator/dcs-world-2-5/bugs-and-problems-ai/ai-ad/250183-task-follow-and-escort-temporarily-aborted - waypoint.add_task(ControlledTask(EngageTargets( - # TODO: From doctrine. - max_distance=int(nautical_miles(30).meters), - targets=[Targets.All.Air.Planes.Fighters] - ))) + waypoint.add_task( + ControlledTask( + EngageTargets( + # TODO: From doctrine. + max_distance=int(nautical_miles(30).meters), + targets=[Targets.All.Air.Planes.Fighters], + ) + ) + ) # We could set this task to end at the split point. pydcs doesn't # currently support that task end condition though, and we don't really @@ -1792,7 +1942,8 @@ class RaceTrackBuilder(PydcsWaypointBuilder): flight_plan_type = flight_plan.__class__.__name__ logging.error( f"Cannot create race track for {self.flight} because " - f"{flight_plan_type} does not define a patrol.") + f"{flight_plan_type} does not define a patrol." + ) return waypoint # NB: It's important that the engage task comes before the orbit task. @@ -1809,16 +1960,18 @@ class RaceTrackBuilder(PydcsWaypointBuilder): if self.flight.flight_type in cap_types: engagement_distance = int(flight_plan.engagement_distance.meters) waypoint.tasks.append( - EngageTargets(max_distance=engagement_distance, - targets=[Targets.All.Air])) + EngageTargets( + max_distance=engagement_distance, targets=[Targets.All.Air] + ) + ) - racetrack = ControlledTask(OrbitAction( - altitude=waypoint.alt, - pattern=OrbitAction.OrbitPattern.RaceTrack - )) + racetrack = ControlledTask( + OrbitAction( + altitude=waypoint.alt, pattern=OrbitAction.OrbitPattern.RaceTrack + ) + ) self.set_waypoint_tot(waypoint, flight_plan.patrol_start_time) - racetrack.stop_after_time( - int(flight_plan.patrol_end_time.total_seconds())) + racetrack.stop_after_time(int(flight_plan.patrol_end_time.total_seconds())) waypoint.add_task(racetrack) return waypoint @@ -1832,7 +1985,8 @@ class RaceTrackEndBuilder(PydcsWaypointBuilder): flight_plan_type = self.flight.flight_plan.__class__.__name__ logging.error( f"Cannot create race track for {self.flight} because " - f"{flight_plan_type} does not define a patrol.") + f"{flight_plan_type} does not define a patrol." + ) return waypoint self.waypoint.departure_time = self.flight.flight_plan.patrol_end_time diff --git a/gen/airfields.py b/gen/airfields.py index 14a40840..d0bb4d54 100644 --- a/gen/airfields.py +++ b/gen/airfields.py @@ -23,6 +23,7 @@ class AtcData: @dataclass class AirfieldData: """Additional airfield data not included in pydcs.""" + #: Name of the theater the airport is in. theater: str @@ -73,7 +74,6 @@ class AirfieldData: # TODO: Add more airfields. AIRFIELD_DATA = { # Caucasus - "Batumi": AirfieldData( theater="Caucasus", icao="UGSB", @@ -86,7 +86,6 @@ AIRFIELD_DATA = { "13": ("ILU", MHz(110, 300)), }, ), - "Kobuleti": AirfieldData( theater="Caucasus", icao="UG5X", @@ -105,7 +104,6 @@ AIRFIELD_DATA = { "07": ("T", MHz(490, 0)), }, ), - "Senaki-Kolkhi": AirfieldData( theater="Caucasus", icao="UGKS", @@ -124,7 +122,6 @@ AIRFIELD_DATA = { "09": ("I", MHz(688, 0)), }, ), - "Kutaisi": AirfieldData( theater="Caucasus", icao="UGKO", @@ -137,7 +134,6 @@ AIRFIELD_DATA = { "08": ("IKS", MHz(109, 750)), }, ), - "Sukhumi-Babushara": AirfieldData( theater="Caucasus", icao="UGSS", @@ -151,7 +147,6 @@ AIRFIELD_DATA = { "30": ("A", MHz(995, 0)), }, ), - "Gudauta": AirfieldData( theater="Caucasus", icao="UG23", @@ -159,7 +154,6 @@ AIRFIELD_DATA = { runway_length=7839, atc=AtcData(MHz(4, 200), MHz(120, 0), MHz(40, 200), MHz(259, 0)), ), - "Sochi-Adler": AirfieldData( theater="Caucasus", icao="URSS", @@ -170,7 +164,6 @@ AIRFIELD_DATA = { "06": ("ISO", MHz(111, 100)), }, ), - "Gelendzhik": AirfieldData( theater="Caucasus", icao="URKG", @@ -179,7 +172,6 @@ AIRFIELD_DATA = { vor=("GN", MHz(114, 30)), atc=AtcData(MHz(4, 0), MHz(126, 0), MHz(39, 400), MHz(255, 0)), ), - "Novorossiysk": AirfieldData( theater="Caucasus", icao="URKN", @@ -187,7 +179,6 @@ AIRFIELD_DATA = { runway_length=5639, atc=AtcData(MHz(3, 850), MHz(123, 0), MHz(38, 800), MHz(252, 0)), ), - "Anapa-Vityazevo": AirfieldData( theater="Caucasus", icao="URKA", @@ -203,7 +194,6 @@ AIRFIELD_DATA = { "04": ("N", MHz(215)), }, ), - "Krymsk": AirfieldData( theater="Caucasus", icao="URKW", @@ -224,7 +214,6 @@ AIRFIELD_DATA = { "22": ("K", MHz(803, 0)), }, ), - "Krasnodar-Center": AirfieldData( theater="Caucasus", icao="URKL", @@ -244,7 +233,6 @@ AIRFIELD_DATA = { "27": ("C", MHz(303, 0)), }, ), - "Krasnodar-Pashkovsky": AirfieldData( theater="Caucasus", icao="URKK", @@ -261,7 +249,6 @@ AIRFIELD_DATA = { "05": ("K", MHz(240, 0)), }, ), - "Maykop-Khanskaya": AirfieldData( theater="Caucasus", icao="URKH", @@ -281,7 +268,6 @@ AIRFIELD_DATA = { "22": ("R", MHz(591, 0)), }, ), - "Mineralnye Vody": AirfieldData( theater="Caucasus", icao="URMM", @@ -302,7 +288,6 @@ AIRFIELD_DATA = { "12": ("D", MHz(283, 0)), }, ), - "Nalchik": AirfieldData( theater="Caucasus", icao="URMN", @@ -319,7 +304,6 @@ AIRFIELD_DATA = { "24": ("N", MHz(350, 0)), }, ), - "Mozdok": AirfieldData( theater="Caucasus", icao="XRMF", @@ -338,9 +322,8 @@ AIRFIELD_DATA = { inner_ndb={ "26": ("R", MHz(1, 6)), "8": ("D", MHz(1, 6)), - } + }, ), - "Beslan": AirfieldData( theater="Caucasus", icao="URMO", @@ -355,9 +338,8 @@ AIRFIELD_DATA = { }, inner_ndb={ "10": ("C", MHz(250, 0)), - } + }, ), - "Tbilisi-Lochini": AirfieldData( theater="Caucasus", icao="UGTB", @@ -379,7 +361,6 @@ AIRFIELD_DATA = { "30": ("N", MHz(435, 0)), }, ), - "Soganlung": AirfieldData( theater="Caucasus", icao="UG24", @@ -389,7 +370,6 @@ AIRFIELD_DATA = { tacan_callsign="GTB", atc=AtcData(MHz(4, 650), MHz(139, 0), MHz(42, 0), MHz(268, 0)), ), - "Vaziani": AirfieldData( theater="Caucasus", icao="UG27", @@ -403,7 +383,6 @@ AIRFIELD_DATA = { "31": ("IVZ", MHz(108, 750)), }, ), - # TODO : PERSIAN GULF MAP "Liwa Airbase": AirfieldData( theater="Persian Gulf", @@ -412,10 +391,9 @@ AIRFIELD_DATA = { runway_length=10768, tacan=TacanChannel(121, TacanBand.X), tacan_callsign="OMLW", - vor=("OMLW", MHz(117,400)), + vor=("OMLW", MHz(117, 400)), atc=AtcData(MHz(4, 225), MHz(39, 350), MHz(119, 300), MHz(250, 950)), ), - "Al Dhafra AB": AirfieldData( theater="Persian Gulf", icao="OMAM", @@ -430,7 +408,6 @@ AIRFIELD_DATA = { "31": ("IMA", MHz(109, 100)), }, ), - "Al-Bateen Airport": AirfieldData( theater="Persian Gulf", icao="OMAD", @@ -439,7 +416,6 @@ AIRFIELD_DATA = { vor=("ALB", MHz(114, 0)), atc=AtcData(MHz(4, 25), MHz(38, 950), MHz(119, 900), MHz(250, 550)), ), - "Sas Al Nakheel Airport": AirfieldData( theater="Persian Gulf", icao="OMNK", @@ -448,7 +424,6 @@ AIRFIELD_DATA = { vor=("SAS", MHz(128, 930)), atc=AtcData(MHz(3, 975), MHz(38, 850), MHz(128, 900), MHz(250, 450)), ), - "Abu Dhabi International Airport": AirfieldData( theater="Persian Gulf", icao="OMAA", @@ -457,7 +432,6 @@ AIRFIELD_DATA = { vor=("ADV", MHz(114, 250)), atc=AtcData(MHz(4, 000), MHz(38, 900), MHz(119, 200), MHz(250, 500)), ), - "Al Ain International Airport": AirfieldData( theater="Persian Gulf", icao="OMAL", @@ -466,7 +440,6 @@ AIRFIELD_DATA = { vor=("ALN", MHz(112, 600)), atc=AtcData(MHz(4, 75), MHz(39, 50), MHz(119, 850), MHz(250, 650)), ), - "Al Maktoum Intl": AirfieldData( theater="Persian Gulf", icao="OMDW", @@ -478,7 +451,6 @@ AIRFIELD_DATA = { "12": ("IMA", MHz(111, 750)), }, ), - "Al Minhad Intl": AirfieldData( theater="Persian Gulf", icao="OMDM", @@ -492,7 +464,6 @@ AIRFIELD_DATA = { "9": ("IMNW", MHz(110, 700)), }, ), - "Dubai Intl": AirfieldData( theater="Persian Gulf", icao="OMDB", @@ -504,7 +475,6 @@ AIRFIELD_DATA = { "12": ("IDBR", MHz(110, 100)), }, ), - "Sharjah Intl": AirfieldData( theater="Persian Gulf", icao="OMSJ", @@ -516,7 +486,6 @@ AIRFIELD_DATA = { "12": ("ISRE", MHz(108, 550)), }, ), - "Fujairah Intl": AirfieldData( theater="Persian Gulf", icao="OMFJ", @@ -528,7 +497,6 @@ AIRFIELD_DATA = { "29": ("IFJR", MHz(111, 500)), }, ), - "Ras AL Khaimah": AirfieldData( theater="Persian Gulf", icao="OMRK", @@ -537,7 +505,6 @@ AIRFIELD_DATA = { vor=("OMRK", MHz(113, 600)), atc=AtcData(MHz(4, 150), MHz(39, 200), MHz(121, 600), MHz(250, 800)), ), - "Khasab": AirfieldData( theater="Persian Gulf", icao="OOKB", @@ -548,14 +515,9 @@ AIRFIELD_DATA = { "19": ("IBKS", MHz(110, 300)), }, ), - "Sir Abu Nuayr": AirfieldData( - theater="Persian Gulf", - icao="OMSN", - elevation=25, - runway_length=2229 + theater="Persian Gulf", icao="OMSN", elevation=25, runway_length=2229 ), - "Sirri Island": AirfieldData( theater="Persian Gulf", icao="OIBS", @@ -564,7 +526,6 @@ AIRFIELD_DATA = { vor=("SIR", MHz(113, 750)), atc=AtcData(MHz(3, 875), MHz(38, 650), MHz(135, 50), MHz(250, 250)), ), - "Abu Musa Island Airport": AirfieldData( theater="Persian Gulf", icao="OIBA", @@ -572,7 +533,6 @@ AIRFIELD_DATA = { runway_length=7616, atc=AtcData(MHz(3, 950), MHz(38, 800), MHz(122, 900), MHz(250, 400)), ), - "Tunb Kochak": AirfieldData( theater="Persian Gulf", icao="OITK", @@ -581,14 +541,12 @@ AIRFIELD_DATA = { tacan=TacanChannel(89, TacanBand.X), tacan_callsign="KCK", ), - "Tunb Island AFB": AirfieldData( theater="Persian Gulf", icao="OIGI", elevation=42, runway_length=6099, ), - "Qeshm Island": AirfieldData( theater="Persian Gulf", icao="OIKQ", @@ -597,7 +555,6 @@ AIRFIELD_DATA = { vor=("KHM", MHz(117, 100)), atc=AtcData(MHz(3, 825), MHz(38, 550), MHz(118, 50), MHz(250, 150)), ), - "Bandar-e-Jask airfield": AirfieldData( theater="Persian Gulf", icao="OIZJ", @@ -606,7 +563,6 @@ AIRFIELD_DATA = { vor=("KHM", MHz(116, 300)), atc=AtcData(MHz(3, 825), MHz(38, 550), MHz(118, 50), MHz(250, 150)), ), - "Bandar Lengeh": AirfieldData( theater="Persian Gulf", icao="OIBL", @@ -615,7 +571,6 @@ AIRFIELD_DATA = { vor=("LEN", MHz(114, 800)), atc=AtcData(MHz(4, 225), MHz(39, 350), MHz(121, 700), MHz(250, 950)), ), - "Kish International Airport": AirfieldData( theater="Persian Gulf", icao="OIBK", @@ -625,7 +580,6 @@ AIRFIELD_DATA = { tacan_callsign="KIH", atc=AtcData(MHz(4, 50), MHz(39, 000), MHz(121, 650), MHz(250, 600)), ), - "Lavan Island Airport": AirfieldData( theater="Persian Gulf", icao="OIBV", @@ -634,7 +588,6 @@ AIRFIELD_DATA = { vor=("LVA", MHz(116, 850)), atc=AtcData(MHz(4, 100), MHz(39, 100), MHz(128, 550), MHz(250, 700)), ), - "Lar Airbase": AirfieldData( theater="Persian Gulf", icao="OISL", @@ -643,7 +596,6 @@ AIRFIELD_DATA = { vor=("LAR", MHz(117, 900)), atc=AtcData(MHz(3, 775), MHz(38, 450), MHz(127, 350), MHz(250, 50)), ), - "Havadarya": AirfieldData( theater="Persian Gulf", icao="OIKP", @@ -656,7 +608,6 @@ AIRFIELD_DATA = { "8": ("IBHD", MHz(108, 900)), }, ), - "Bandar Abbas Intl": AirfieldData( theater="Persian Gulf", icao="OIKB", @@ -670,7 +621,6 @@ AIRFIELD_DATA = { "21": ("IBND", MHz(333, 800)), }, ), - "Jiroft Airport": AirfieldData( theater="Persian Gulf", icao="OIKJ", @@ -678,7 +628,6 @@ AIRFIELD_DATA = { runway_length=9160, atc=AtcData(MHz(4, 125), MHz(39, 120), MHz(136, 0), MHz(250, 750)), ), - "Kerman Airport": AirfieldData( theater="Persian Gulf", icao="OIKK", @@ -689,7 +638,6 @@ AIRFIELD_DATA = { vor=("KER", MHz(112, 0)), atc=AtcData(MHz(3, 900), MHz(38, 700), MHz(118, 250), MHz(250, 300)), ), - "Shiraz International Airport": AirfieldData( theater="Persian Gulf", icao="OISS", @@ -700,7 +648,6 @@ AIRFIELD_DATA = { vor=("SYZ", MHz(112, 0)), atc=AtcData(MHz(3, 925), MHz(38, 750), MHz(121, 900), MHz(250, 350)), ), - # Syria Map "Adana Sakirpasa": AirfieldData( theater="Syria", @@ -713,7 +660,6 @@ AIRFIELD_DATA = { "05": ("IADA", MHz(108, 700)), }, ), - "Incirlik": AirfieldData( theater="Syria", icao="LTAG", @@ -728,7 +674,6 @@ AIRFIELD_DATA = { "23": ("DANM", MHz(111, 700)), }, ), - "Minakh": AirfieldData( theater="Syria", icao="OS71", @@ -736,7 +681,6 @@ AIRFIELD_DATA = { runway_length=4648, atc=AtcData(MHz(4, 125), MHz(39, 150), MHz(120, 600), MHz(250, 700)), ), - "Hatay": AirfieldData( theater="Syria", icao="LTDA", @@ -749,7 +693,6 @@ AIRFIELD_DATA = { "04": ("IHAT", MHz(108, 900)), }, ), - "Kuweires": AirfieldData( theater="Syria", icao="OS66", @@ -757,7 +700,6 @@ AIRFIELD_DATA = { runway_length=6662, atc=AtcData(MHz(4, 275), MHz(39, 450), MHz(120, 500), MHz(251)), ), - "Aleppo": AirfieldData( theater="Syria", icao="OSAP", @@ -769,7 +711,6 @@ AIRFIELD_DATA = { "23": ("DANM", MHz(111, 700)), }, ), - "Jirah": AirfieldData( theater="Syria", icao="OS62", @@ -777,14 +718,12 @@ AIRFIELD_DATA = { runway_length=9090, atc=AtcData(MHz(3, 875), MHz(38, 650), MHz(118, 100), MHz(250, 200)), ), - "Taftanaz": AirfieldData( theater="Syria", elevation=1020, runway_length=2705, atc=AtcData(MHz(4, 375), MHz(39, 650), MHz(122, 800), MHz(251, 200)), ), - "Tabqa": AirfieldData( theater="Syria", icao="OS59", @@ -792,7 +731,6 @@ AIRFIELD_DATA = { runway_length=9036, atc=AtcData(MHz(4, 350), MHz(39, 600), MHz(118, 500), MHz(251, 150)), ), - "Abu al-Dahur": AirfieldData( theater="Syria", icao="OS57", @@ -800,7 +738,6 @@ AIRFIELD_DATA = { runway_length=8728, atc=AtcData(MHz(3, 950), MHz(38, 800), MHz(122, 200), MHz(250, 350)), ), - "Bassel Al-Assad": AirfieldData( theater="Syria", icao="OSLK", @@ -812,7 +749,6 @@ AIRFIELD_DATA = { "17": ("IBA", MHz(109, 100)), }, ), - "Hama": AirfieldData( theater="Syria", icao="OS58", @@ -820,7 +756,6 @@ AIRFIELD_DATA = { runway_length=7957, atc=AtcData(MHz(3, 800), MHz(38, 500), MHz(118, 50), MHz(250, 100)), ), - "Rene Mouawad": AirfieldData( theater="Syria", icao="OLKA", @@ -828,7 +763,6 @@ AIRFIELD_DATA = { runway_length=8614, atc=AtcData(MHz(4, 325), MHz(39, 550), MHz(129, 500), MHz(251, 100)), ), - "Al Quasayr": AirfieldData( theater="Syria", icao="OS70", @@ -836,7 +770,6 @@ AIRFIELD_DATA = { runway_length=8585, atc=AtcData(MHz(4, 400), MHz(39, 700), MHz(119, 200), MHz(251, 250)), ), - "Palmyra": AirfieldData( theater="Syria", icao="OSPR", @@ -844,7 +777,6 @@ AIRFIELD_DATA = { runway_length=8704, atc=AtcData(MHz(4, 175), MHz(39, 250), MHz(121, 900), MHz(250, 800)), ), - "Wujah Al Hajar": AirfieldData( theater="Syria", icao="Z19O", @@ -853,7 +785,6 @@ AIRFIELD_DATA = { vor=("CAK", MHz(116, 200)), atc=AtcData(MHz(4, 425), MHz(39, 750), MHz(121, 500), MHz(251, 300)), ), - "An Nasiriyah": AirfieldData( theater="Syria", icao="OS64", @@ -861,7 +792,6 @@ AIRFIELD_DATA = { runway_length=8172, atc=AtcData(MHz(4, 450), MHz(39, 800), MHz(122, 300), MHz(251, 350)), ), - "Rayak": AirfieldData( theater="Syria", icao="OLRA", @@ -870,7 +800,6 @@ AIRFIELD_DATA = { vor=("HTY", MHz(124, 400)), atc=AtcData(MHz(4, 300), MHz(39, 500), MHz(124, 400), MHz(251, 50)), ), - "Beirut-Rafic Hariri": AirfieldData( theater="Syria", icao="OLBA", @@ -882,7 +811,6 @@ AIRFIELD_DATA = { "17": ("BIL", MHz(109, 500)), }, ), - "Al-Dumayr": AirfieldData( theater="Syria", icao="OS61", @@ -890,21 +818,18 @@ AIRFIELD_DATA = { runway_length=8902, atc=AtcData(MHz(4, 550), MHz(40), MHz(120, 300), MHz(251, 550)), ), - "Marj as Sultan North": AirfieldData( theater="Syria", elevation=2007, runway_length=268, atc=AtcData(MHz(4, 25), MHz(38, 950), MHz(122, 700), MHz(250, 500)), ), - "Marj as Sultan South": AirfieldData( theater="Syria", elevation=2007, runway_length=166, atc=AtcData(MHz(4, 525), MHz(39, 950), MHz(122, 900), MHz(251, 500)), ), - "Mezzeh": AirfieldData( theater="Syria", icao="OS67", @@ -912,14 +837,12 @@ AIRFIELD_DATA = { runway_length=7522, atc=AtcData(MHz(4, 100), MHz(39, 100), MHz(120, 700), MHz(250, 650)), ), - "Qabr as Sitt": AirfieldData( theater="Syria", elevation=2134, runway_length=489, atc=AtcData(MHz(4, 200), MHz(39, 300), MHz(122, 600), MHz(250, 850)), ), - "Damascus": AirfieldData( theater="Syria", icao="OSDI", @@ -931,7 +854,6 @@ AIRFIELD_DATA = { "24": ("IDA", MHz(109, 900)), }, ), - "Marj Ruhayyil": AirfieldData( theater="Syria", icao="OS63", @@ -939,7 +861,6 @@ AIRFIELD_DATA = { runway_length=7576, atc=AtcData(MHz(4, 50), MHz(39), MHz(120, 800), MHz(250, 550)), ), - "Kiryat Shmona": AirfieldData( theater="Syria", icao="LLKS", @@ -947,7 +868,6 @@ AIRFIELD_DATA = { runway_length=3258, atc=AtcData(MHz(3, 975), MHz(38, 850), MHz(118, 400), MHz(250, 400)), ), - "Khalkhalah": AirfieldData( theater="Syria", icao="OS69", @@ -955,7 +875,6 @@ AIRFIELD_DATA = { runway_length=8248, atc=AtcData(MHz(3, 900), MHz(38, 700), MHz(122, 500), MHz(250, 250)), ), - "Haifa": AirfieldData( theater="Syria", icao="LLHA", @@ -963,7 +882,6 @@ AIRFIELD_DATA = { runway_length=3253, atc=AtcData(MHz(3, 775), MHz(38, 450), MHz(127, 800), MHz(250, 50)), ), - "Ramat David": AirfieldData( theater="Syria", icao="LLRD", @@ -971,7 +889,6 @@ AIRFIELD_DATA = { runway_length=7037, atc=AtcData(MHz(4, 250), MHz(39, 400), MHz(118, 600), MHz(250, 950)), ), - "Megiddo": AirfieldData( theater="Syria", icao="LLMG", @@ -979,7 +896,6 @@ AIRFIELD_DATA = { runway_length=6098, atc=AtcData(MHz(4, 75), MHz(39, 50), MHz(119, 900), MHz(250, 600)), ), - "Eyn Shemer": AirfieldData( theater="Syria", icao="LLES", @@ -987,7 +903,6 @@ AIRFIELD_DATA = { runway_length=3562, atc=AtcData(MHz(3, 750), MHz(38, 400), MHz(123, 400), MHz(250)), ), - "King Hussein Air College": AirfieldData( theater="Syria", icao="OJMF", @@ -995,21 +910,18 @@ AIRFIELD_DATA = { runway_length=8595, atc=AtcData(MHz(3, 925), MHz(38, 750), MHz(118, 300), MHz(250, 300)), ), - # NTTR "Mina Airport 3Q0": AirfieldData( theater="NTTR", elevation=4562, runway_length=4222, ), - "Tonopah Airport": AirfieldData( theater="NTTR", icao="KTPH", elevation=5394, runway_length=6715, ), - "Tonopah Test Range Airfield": AirfieldData( theater="NTTR", icao="KTNX", @@ -1023,20 +935,17 @@ AIRFIELD_DATA = { "14": ("I-RVP", MHz(108, 300)), }, ), - "Beatty Airport": AirfieldData( theater="NTTR", icao="KBTY", elevation=3173, runway_length=5380, ), - "Pahute Mesa Airstrip": AirfieldData( theater="NTTR", elevation=5056, runway_length=5420, ), - "Groom Lake AFB": AirfieldData( theater="NTTR", icao="KXTA", @@ -1049,20 +958,17 @@ AIRFIELD_DATA = { "32": ("GLRI", MHz(109, 300)), }, ), - "Lincoln County": AirfieldData( theater="NTTR", elevation=4815, runway_length=4408, ), - "Mesquite": AirfieldData( theater="NTTR", icao="67L", elevation=1858, runway_length=4937, ), - "Creech AFB": AirfieldData( theater="NTTR", icao="KINS", @@ -1075,7 +981,6 @@ AIRFIELD_DATA = { "8": ("ICRR", MHz(108, 700)), }, ), - "Echo Bay": AirfieldData( theater="NTTR", icao="OL9", @@ -1085,7 +990,6 @@ AIRFIELD_DATA = { tacan_callsign="INS", atc=AtcData(MHz(3, 825), MHz(118, 300), MHz(38, 550), MHz(360, 600)), ), - "Nellis AFB": AirfieldData( theater="NTTR", icao="KLSV", @@ -1098,7 +1002,6 @@ AIRFIELD_DATA = { "21": ("IDIQ", MHz(109, 100)), }, ), - "North Las Vegas": AirfieldData( theater="NTTR", icao="KVGT", @@ -1106,7 +1009,6 @@ AIRFIELD_DATA = { runway_length=4734, atc=AtcData(MHz(3, 775), MHz(125, 700), MHz(38, 450), MHz(360, 750)), ), - "McCarran International Airport": AirfieldData( theater="NTTR", icao="KLAS", @@ -1119,7 +1021,6 @@ AIRFIELD_DATA = { "25": ("I-LAS", MHz(110, 300)), }, ), - "Henderson Executive Airport": AirfieldData( theater="NTTR", icao="KHND", @@ -1127,20 +1028,17 @@ AIRFIELD_DATA = { runway_length=5999, atc=AtcData(MHz(3, 925), MHz(125, 100), MHz(38, 750), MHz(250, 100)), ), - "Boulder City Airport": AirfieldData( theater="NTTR", icao="KBVU", elevation=2121, runway_length=4612, ), - "Jean Airport": AirfieldData( theater="NTTR", elevation=2824, runway_length=4053, ), - "Laughlin Airport": AirfieldData( theater="NTTR", icao="KIFP", @@ -1148,44 +1046,37 @@ AIRFIELD_DATA = { runway_length=7139, atc=AtcData(MHz(3, 750), MHz(123, 900), MHz(38, 400), MHz(250, 0)), ), - # Normandy - "Needs Oar Point": AirfieldData( theater="Normandy", elevation=30, runway_length=5259, atc=AtcData(MHz(4, 225), MHz(118, 950), MHz(39, 350), MHz(250, 950)), ), - "Funtington": AirfieldData( theater="Normandy", elevation=164, runway_length=5080, atc=AtcData(MHz(4, 250), MHz(119, 000), MHz(39, 400), MHz(251, 000)), ), - "Tangmere": AirfieldData( theater="Normandy", elevation=47, runway_length=4296, atc=AtcData(MHz(4, 300), MHz(119, 100), MHz(39, 500), MHz(251, 100)), ), - "Ford_AF": AirfieldData( theater="Normandy", elevation=29, runway_length=4296, atc=AtcData(MHz(4, 325), MHz(119, 150), MHz(39, 550), MHz(251, 150)), ), - "Chailey": AirfieldData( theater="Normandy", elevation=134, runway_length=5080, atc=AtcData(MHz(4, 200), MHz(118, 900), MHz(39, 300), MHz(250, 900)), ), - "Maupertus": AirfieldData( theater="Normandy", icao="A-15", @@ -1193,7 +1084,6 @@ AIRFIELD_DATA = { runway_length=4666, atc=AtcData(MHz(4, 550), MHz(119, 600), MHz(40, 000), MHz(251, 600)), ), - "Azeville": AirfieldData( theater="Normandy", icao="A-7", @@ -1201,7 +1091,6 @@ AIRFIELD_DATA = { runway_length=3357, atc=AtcData(MHz(3, 875), MHz(118, 250), MHz(38, 650), MHz(250, 250)), ), - "Biniville": AirfieldData( theater="Normandy", icao="A-24", @@ -1209,7 +1098,6 @@ AIRFIELD_DATA = { runway_length=3283, atc=AtcData(MHz(3, 750), MHz(118, 000), MHz(38, 400), MHz(250, 000)), ), - "Beuzeville": AirfieldData( theater="Normandy", icao="A-6", @@ -1217,7 +1105,6 @@ AIRFIELD_DATA = { runway_length=3840, atc=AtcData(MHz(3, 850), MHz(118, 200), MHz(38, 600), MHz(250, 200)), ), - "Picauville": AirfieldData( theater="Normandy", icao="A-8", @@ -1225,7 +1112,6 @@ AIRFIELD_DATA = { runway_length=3840, atc=AtcData(MHz(3, 900), MHz(118, 300), MHz(38, 700), MHz(250, 300)), ), - "Brucheville": AirfieldData( theater="Normandy", icao="A-16", @@ -1233,7 +1119,6 @@ AIRFIELD_DATA = { runway_length=3413, atc=AtcData(MHz(4, 575), MHz(119, 650), MHz(40, 50), MHz(251, 650)), ), - "Cretteville": AirfieldData( theater="Normandy", icao="A-14", @@ -1241,7 +1126,6 @@ AIRFIELD_DATA = { runway_length=4594, atc=AtcData(MHz(4, 500), MHz(119, 500), MHz(39, 900), MHz(251, 500)), ), - "Meautis": AirfieldData( theater="Normandy", icao="A-17", @@ -1249,7 +1133,6 @@ AIRFIELD_DATA = { runway_length=3840, atc=AtcData(MHz(4, 600), MHz(119, 700), MHz(40, 100), MHz(251, 700)), ), - "Lessay": AirfieldData( theater="Normandy", icao="A-20", @@ -1257,7 +1140,6 @@ AIRFIELD_DATA = { runway_length=5080, atc=AtcData(MHz(4, 650), MHz(119, 800), MHz(40, 200), MHz(251, 800)), ), - "Cardonville": AirfieldData( theater="Normandy", icao="A-3", @@ -1265,7 +1147,6 @@ AIRFIELD_DATA = { runway_length=4541, atc=AtcData(MHz(3, 775), MHz(118, 50), MHz(38, 450), MHz(250, 50)), ), - "Cricqueville-en-Bessin": AirfieldData( theater="Normandy", icao="A-2", @@ -1273,7 +1154,6 @@ AIRFIELD_DATA = { runway_length=3459, atc=AtcData(MHz(4, 625), MHz(119, 750), MHz(40, 150), MHz(251, 750)), ), - "Deux Jumeaux": AirfieldData( theater="Normandy", icao="A-4", @@ -1281,7 +1161,6 @@ AIRFIELD_DATA = { runway_length=4628, atc=AtcData(MHz(3, 800), MHz(118, 100), MHz(38, 500), MHz(250, 100)), ), - "Saint Pierre du Mont": AirfieldData( theater="Normandy", icao="A-1", @@ -1289,7 +1168,6 @@ AIRFIELD_DATA = { runway_length=4737, atc=AtcData(MHz(4, 000), MHz(118, 500), MHz(38, 900), MHz(250, 500)), ), - "Sainte-Laurent-sur-Mer": AirfieldData( theater="Normandy", icao="A-21", @@ -1297,7 +1175,6 @@ AIRFIELD_DATA = { runway_length=4561, atc=AtcData(MHz(4, 675), MHz(119, 850), MHz(40, 250), MHz(251, 850)), ), - "Longues-sur-Mer": AirfieldData( theater="Normandy", icao="B-11", @@ -1305,7 +1182,6 @@ AIRFIELD_DATA = { runway_length=3155, atc=AtcData(MHz(3, 950), MHz(118, 400), MHz(38, 800), MHz(250, 400)), ), - "Chippelle": AirfieldData( theater="Normandy", icao="A-5", @@ -1313,7 +1189,6 @@ AIRFIELD_DATA = { runway_length=4643, atc=AtcData(MHz(3, 825), MHz(118, 150), MHz(38, 550), MHz(250, 150)), ), - "Le Molay": AirfieldData( theater="Normandy", icao="A-9", @@ -1321,7 +1196,6 @@ AIRFIELD_DATA = { runway_length=3840, atc=AtcData(MHz(3, 925), MHz(118, 350), MHz(38, 750), MHz(250, 350)), ), - "Lignerolles": AirfieldData( theater="Normandy", icao="A-12", @@ -1329,7 +1203,6 @@ AIRFIELD_DATA = { runway_length=3436, atc=AtcData(MHz(4, 275), MHz(119, 50), MHz(39, 450), MHz(251, 50)), ), - "Sommervieu": AirfieldData( theater="Normandy", icao="B-8", @@ -1337,7 +1210,6 @@ AIRFIELD_DATA = { runway_length=3840, atc=AtcData(MHz(4, 125), MHz(118, 750), MHz(39, 150), MHz(250, 750)), ), - "Bazenville": AirfieldData( theater="Normandy", icao="B-2", @@ -1345,7 +1217,6 @@ AIRFIELD_DATA = { runway_length=3800, atc=AtcData(MHz(4, 25), MHz(118, 550), MHz(38, 950), MHz(250, 550)), ), - "Rucqueville": AirfieldData( theater="Normandy", icao="B-7", @@ -1353,7 +1224,6 @@ AIRFIELD_DATA = { runway_length=4561, atc=AtcData(MHz(4, 100), MHz(118, 700), MHz(39, 100), MHz(250, 700)), ), - "Lantheuil": AirfieldData( theater="Normandy", icao="B-9", @@ -1361,7 +1231,6 @@ AIRFIELD_DATA = { runway_length=3597, atc=AtcData(MHz(4, 150), MHz(118, 800), MHz(39, 200), MHz(250, 800)), ), - "Sainte-Croix-sur-Mer": AirfieldData( theater="Normandy", icao="B-3", @@ -1369,7 +1238,6 @@ AIRFIELD_DATA = { runway_length=3840, atc=AtcData(MHz(4, 50), MHz(118, 600), MHz(39, 000), MHz(250, 600)), ), - "Beny-sur-Mer": AirfieldData( theater="Normandy", icao="B-4", @@ -1377,7 +1245,6 @@ AIRFIELD_DATA = { runway_length=3155, atc=AtcData(MHz(4, 75), MHz(118, 650), MHz(39, 50), MHz(250, 650)), ), - "Carpiquet": AirfieldData( theater="Normandy", icao="B-17", @@ -1385,63 +1252,54 @@ AIRFIELD_DATA = { runway_length=3799, atc=AtcData(MHz(3, 975), MHz(118, 450), MHz(38, 850), MHz(250, 450)), ), - "Goulet": AirfieldData( theater="Normandy", elevation=616, runway_length=3283, atc=AtcData(MHz(4, 375), MHz(119, 250), MHz(39, 650), MHz(251, 250)), ), - "Argentan": AirfieldData( theater="Normandy", elevation=639, runway_length=3283, atc=AtcData(MHz(4, 350), MHz(119, 200), MHz(39, 600), MHz(251, 200)), ), - "Vrigny": AirfieldData( theater="Normandy", elevation=590, runway_length=3283, atc=AtcData(MHz(4, 475), MHz(119, 450), MHz(39, 850), MHz(251, 450)), ), - "Hauterive": AirfieldData( theater="Normandy", elevation=476, runway_length=3283, atc=AtcData(MHz(4, 450), MHz(119, 400), MHz(39, 800), MHz(251, 400)), ), - "Essay": AirfieldData( theater="Normandy", elevation=507, runway_length=3283, atc=AtcData(MHz(4, 425), MHz(119, 350), MHz(39, 750), MHz(251, 350)), ), - "Barville": AirfieldData( theater="Normandy", elevation=462, runway_length=3493, atc=AtcData(MHz(4, 400), MHz(119, 300), MHz(39, 700), MHz(251, 300)), ), - "Conches": AirfieldData( theater="Normandy", elevation=541, runway_length=4199, atc=AtcData(MHz(4, 525), MHz(119, 550), MHz(39, 950), MHz(251, 550)), ), - "Evreux": AirfieldData( theater="Normandy", elevation=423, runway_length=4296, atc=AtcData(MHz(4, 175), MHz(118, 850), MHz(39, 250), MHz(250, 850)), ), - # Channel Map "Detling": AirfieldData( theater="Channel", @@ -1449,56 +1307,48 @@ AIRFIELD_DATA = { runway_length=2557, atc=AtcData(MHz(3, 950), MHz(118, 400), MHz(38, 800), MHz(250, 400)), ), - "High Halden": AirfieldData( theater="Channel", elevation=104, runway_length=3296, atc=AtcData(MHz(3, 750), MHz(118, 800), MHz(38, 400), MHz(250, 0)), ), - "Lympne": AirfieldData( theater="Channel", elevation=351, runway_length=2548, atc=AtcData(MHz(3, 925), MHz(118, 350), MHz(38, 750), MHz(250, 350)), ), - "Hawkinge": AirfieldData( theater="Channel", elevation=524, runway_length=3013, atc=AtcData(MHz(3, 900), MHz(118, 300), MHz(38, 700), MHz(250, 300)), ), - "Manston": AirfieldData( theater="Channel", elevation=160, runway_length=8626, atc=AtcData(MHz(3, 875), MHz(118, 250), MHz(38, 650), MHz(250, 250)), ), - "Dunkirk Mardyck": AirfieldData( theater="Channel", elevation=16, runway_length=1737, atc=AtcData(MHz(3, 850), MHz(118, 200), MHz(38, 600), MHz(250, 200)), ), - "Saint Omer Longuenesse": AirfieldData( theater="Channel", elevation=219, runway_length=1929, atc=AtcData(MHz(3, 825), MHz(118, 150), MHz(38, 550), MHz(250, 150)), ), - "Merville Calonne": AirfieldData( theater="Channel", elevation=52, runway_length=7580, atc=AtcData(MHz(3, 800), MHz(118, 100), MHz(38, 500), MHz(250, 100)), ), - "Abbeville Drucat": AirfieldData( theater="Channel", elevation=183, diff --git a/gen/airsupportgen.py b/gen/airsupportgen.py index 71c54c42..88520374 100644 --- a/gen/airsupportgen.py +++ b/gen/airsupportgen.py @@ -22,8 +22,6 @@ from .radios import RadioFrequency, RadioRegistry from .tacan import TacanBand, TacanChannel, TacanRegistry - - TANKER_DISTANCE = 15000 TANKER_ALT = 4572 TANKER_HEADING_OFFSET = 45 @@ -35,6 +33,7 @@ AWACS_ALT = 13000 @dataclass class AwacsInfo: """AWACS information for the kneeboard.""" + dcsGroupName: str callsign: str freq: RadioFrequency @@ -43,6 +42,7 @@ class AwacsInfo: @dataclass class TankerInfo: """Tanker information for the kneeboard.""" + dcsGroupName: str callsign: str variant: str @@ -57,10 +57,14 @@ class AirSupport: class AirSupportConflictGenerator: - - def __init__(self, mission: Mission, conflict: Conflict, game, - radio_registry: RadioRegistry, - tacan_registry: TacanRegistry) -> None: + def __init__( + self, + mission: Mission, + conflict: Conflict, + game, + radio_registry: RadioRegistry, + tacan_registry: TacanRegistry, + ) -> None: self.mission = mission self.conflict = conflict self.game = game @@ -81,22 +85,37 @@ class AirSupportConflictGenerator: elif unit_type is KC135MPRS: return (TANKER_ALT + 500, 596) return (TANKER_ALT, 574) - + def generate(self): - player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp + player_cp = ( + self.conflict.from_cp + if self.conflict.from_cp.captured + else self.conflict.to_cp + ) fallback_tanker_number = 0 - for i, tanker_unit_type in enumerate(db.find_unittype(Refueling, self.conflict.attackers_side)): + for i, tanker_unit_type in enumerate( + db.find_unittype(Refueling, self.conflict.attackers_side) + ): alt, airspeed = self._get_tanker_params(tanker_unit_type) - variant = db.unit_type_name(tanker_unit_type) + variant = db.unit_type_name(tanker_unit_type) freq = self.radio_registry.alloc_uhf() tacan = self.tacan_registry.alloc_for_band(TacanBand.Y) - tanker_heading = self.conflict.to_cp.position.heading_between_point(self.conflict.from_cp.position) + TANKER_HEADING_OFFSET * i - tanker_position = player_cp.position.point_from_heading(tanker_heading, TANKER_DISTANCE) + tanker_heading = ( + self.conflict.to_cp.position.heading_between_point( + self.conflict.from_cp.position + ) + + TANKER_HEADING_OFFSET * i + ) + tanker_position = player_cp.position.point_from_heading( + tanker_heading, TANKER_DISTANCE + ) tanker_group = self.mission.refuel_flight( country=self.mission.country(self.game.player_country), - name=namegen.next_tanker_name(self.mission.country(self.game.player_country), tanker_unit_type), + name=namegen.next_tanker_name( + self.mission.country(self.game.player_country), tanker_unit_type + ), airport=None, plane_type=tanker_unit_type, position=tanker_position, @@ -127,14 +146,23 @@ class AirSupportConflictGenerator: if tanker_unit_type != IL_78M: # Override PyDCS tacan channel. tanker_group.points[0].tasks.pop() - tanker_group.points[0].tasks.append(ActivateBeaconCommand( - tacan.number, tacan.band.value, tacan_callsign, True, - tanker_group.units[0].id, True)) + tanker_group.points[0].tasks.append( + ActivateBeaconCommand( + tacan.number, + tacan.band.value, + tacan_callsign, + True, + tanker_group.units[0].id, + True, + ) + ) tanker_group.points[0].tasks.append(SetInvisibleCommand(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(str(tanker_group.name), callsign, variant, freq, tacan) + ) if not self.game.settings.disable_legacy_aewc: possible_awacs = db.find_unittype(AWACS, self.conflict.attackers_side) @@ -145,11 +173,15 @@ class AirSupportConflictGenerator: awacs_flight = self.mission.awacs_flight( country=self.mission.country(self.game.player_country), - name=namegen.next_awacs_name(self.mission.country(self.game.player_country)), + name=namegen.next_awacs_name( + self.mission.country(self.game.player_country) + ), plane_type=awacs_unit, altitude=AWACS_ALT, airport=None, - position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE), + position=self.conflict.position.random_point_within( + AWACS_DISTANCE, AWACS_DISTANCE + ), frequency=freq.mhz, start_type=StartType.Warm, ) @@ -158,7 +190,12 @@ class AirSupportConflictGenerator: awacs_flight.points[0].tasks.append(SetInvisibleCommand(True)) awacs_flight.points[0].tasks.append(SetImmortalCommand(True)) - self.air_support.awacs.append(AwacsInfo( - str(awacs_flight.name), callsign_for_support_unit(awacs_flight), freq)) + self.air_support.awacs.append( + AwacsInfo( + str(awacs_flight.name), + callsign_for_support_unit(awacs_flight), + freq, + ) + ) else: - logging.warning("No AWACS for faction") \ No newline at end of file + logging.warning("No AWACS for faction") diff --git a/gen/armor.py b/gen/armor.py index 48311bdc..8f075d38 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -12,9 +12,17 @@ from dcs.country import Country from dcs.mapping import Point from dcs.planes import MQ_9_Reaper from dcs.point import PointAction -from dcs.task import (EPLRS, AttackGroup, ControlledTask, FireAtPoint, - GoToWaypoint, Hold, OrbitAction, SetImmortalCommand, - SetInvisibleCommand) +from dcs.task import ( + EPLRS, + AttackGroup, + ControlledTask, + FireAtPoint, + GoToWaypoint, + Hold, + OrbitAction, + SetImmortalCommand, + SetInvisibleCommand, +) from dcs.triggers import Event, TriggerOnce from dcs.unit import Vehicle from dcs.unitgroup import VehicleGroup @@ -24,8 +32,11 @@ from game.unitmap import UnitMap from game.utils import heading_sum, opposite_heading from game.theater.controlpoint import ControlPoint -from gen.ground_forces.ai_ground_planner import (DISTANCE_FROM_FRONTLINE, - CombatGroup, CombatGroupRole) +from gen.ground_forces.ai_ground_planner import ( + DISTANCE_FROM_FRONTLINE, + CombatGroup, + CombatGroupRole, +) from .callsigns import callsign_for_support_unit from .conflictgen import Conflict @@ -56,6 +67,7 @@ INFANTRY_GROUP_SIZE = 5 @dataclass(frozen=True) class JtacInfo: """JTAC information.""" + dcsGroupName: str unit_name: str callsign: str @@ -65,16 +77,16 @@ class JtacInfo: class GroundConflictGenerator: - def __init__( - self, - mission: Mission, - conflict: Conflict, - game: Game, - player_planned_combat_groups: List[CombatGroup], - enemy_planned_combat_groups: List[CombatGroup], - player_stance: CombatStance, - unit_map: UnitMap) -> None: + self, + mission: Mission, + conflict: Conflict, + game: Game, + player_planned_combat_groups: List[CombatGroup], + enemy_planned_combat_groups: List[CombatGroup], + player_stance: CombatStance, + unit_map: UnitMap, + ) -> None: self.mission = mission self.conflict = conflict self.enemy_planned_combat_groups = enemy_planned_combat_groups @@ -87,14 +99,16 @@ class GroundConflictGenerator: def _enemy_stance(self): """Picks the enemy stance according to the number of planned groups on the frontline for each side""" - if len(self.enemy_planned_combat_groups) > len(self.player_planned_combat_groups): + if len(self.enemy_planned_combat_groups) > len( + self.player_planned_combat_groups + ): return random.choice( [ CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.AGGRESSIVE, CombatStance.ELIMINATION, - CombatStance.BREAKTHROUGH + CombatStance.BREAKTHROUGH, ] ) else: @@ -104,31 +118,37 @@ class GroundConflictGenerator: CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.AMBUSH, - CombatStance.AGGRESSIVE + CombatStance.AGGRESSIVE, ] ) @staticmethod def _group_point(point: Point, base_distance) -> Point: distance = random.randint( - int(base_distance * SPREAD_DISTANCE_FACTOR[0]), - int(base_distance * SPREAD_DISTANCE_FACTOR[1]), - ) - return point.random_point_within(distance, base_distance * SPREAD_DISTANCE_SIZE_FACTOR) + int(base_distance * SPREAD_DISTANCE_FACTOR[0]), + int(base_distance * SPREAD_DISTANCE_FACTOR[1]), + ) + return point.random_point_within( + distance, base_distance * SPREAD_DISTANCE_SIZE_FACTOR + ) def generate(self): - position = Conflict.frontline_position(self.conflict.from_cp, self.conflict.to_cp, self.game.theater) + position = Conflict.frontline_position( + self.conflict.from_cp, self.conflict.to_cp, self.game.theater + ) frontline_vector = Conflict.frontline_vector( - self.conflict.from_cp, - self.conflict.to_cp, - self.game.theater - ) + self.conflict.from_cp, self.conflict.to_cp, self.game.theater + ) # Create player groups at random position - player_groups = self._generate_groups(self.player_planned_combat_groups, frontline_vector, True) + player_groups = self._generate_groups( + self.player_planned_combat_groups, frontline_vector, True + ) # Create enemy groups at random position - enemy_groups = self._generate_groups(self.enemy_planned_combat_groups, frontline_vector, False) + enemy_groups = self._generate_groups( + self.enemy_planned_combat_groups, frontline_vector, False + ) # Plan combat actions for groups self.plan_action_for_groups( @@ -137,7 +157,7 @@ class GroundConflictGenerator: enemy_groups, self.conflict.heading + 90, self.conflict.from_cp, - self.conflict.to_cp + self.conflict.to_cp, ) self.plan_action_for_groups( self.enemy_stance, @@ -145,7 +165,7 @@ class GroundConflictGenerator: player_groups, self.conflict.heading - 90, self.conflict.to_cp, - self.conflict.from_cp + self.conflict.from_cp, ) # Add JTAC @@ -157,34 +177,38 @@ class GroundConflictGenerator: if self.game.player_faction.jtac_unit is not None: utype = self.game.player_faction.jtac_unit - jtac = self.mission.flight_group(country=self.mission.country(self.game.player_country), - name=n, - aircraft_type=utype, - position=position[0], - airport=None, - altitude=5000) + jtac = self.mission.flight_group( + country=self.mission.country(self.game.player_country), + name=n, + aircraft_type=utype, + position=position[0], + airport=None, + altitude=5000, + ) jtac.points[0].tasks.append(SetInvisibleCommand(True)) jtac.points[0].tasks.append(SetImmortalCommand(True)) - jtac.points[0].tasks.append(OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle)) - frontline = f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}" + jtac.points[0].tasks.append( + OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle) + ) + 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. callsign = callsign_for_support_unit(jtac) - self.jtacs.append(JtacInfo(str(jtac.name), n, callsign, frontline, str(code))) + self.jtacs.append( + JtacInfo(str(jtac.name), n, callsign, frontline, str(code)) + ) def gen_infantry_group_for_group( - self, - group: VehicleGroup, - is_player: bool, - side: Country, - forward_heading: int + self, group: VehicleGroup, is_player: bool, side: Country, forward_heading: int ) -> None: infantry_position = self.conflict.find_ground_position( group.points[0].position.random_point_within(250, 50), 500, forward_heading, - self.conflict.theater - ) + self.conflict.theater, + ) if not infantry_position: logging.warning("Could not find infantry position") return @@ -208,44 +232,50 @@ class GroundConflictGenerator: u = random.choice(manpads) self.mission.vehicle_group( side, - namegen.next_infantry_name(side, cp.id, u), u, + namegen.next_infantry_name(side, cp.id, u), + u, position=infantry_position, group_size=1, heading=forward_heading, - move_formation=PointAction.OffRoad) + move_formation=PointAction.OffRoad, + ) return - possible_infantry_units = db.find_infantry(faction, allow_manpad=self.game.settings.manpads) + possible_infantry_units = db.find_infantry( + faction, allow_manpad=self.game.settings.manpads + ) if len(possible_infantry_units) == 0: return u = random.choice(possible_infantry_units) self.mission.vehicle_group( - side, - namegen.next_infantry_name(side, cp.id, u), u, - position=infantry_position, - group_size=1, - heading=forward_heading, - move_formation=PointAction.OffRoad) + side, + namegen.next_infantry_name(side, cp.id, u), + u, + position=infantry_position, + group_size=1, + heading=forward_heading, + move_formation=PointAction.OffRoad, + ) for i in range(INFANTRY_GROUP_SIZE): u = random.choice(possible_infantry_units) position = infantry_position.random_point_within(55, 5) self.mission.vehicle_group( side, - namegen.next_infantry_name(side, cp.id, u), u, + namegen.next_infantry_name(side, cp.id, u), + u, position=position, group_size=1, heading=forward_heading, - move_formation=PointAction.OffRoad) + move_formation=PointAction.OffRoad, + ) def _set_reform_waypoint( - self, - dcs_group: VehicleGroup, - forward_heading: int + self, dcs_group: VehicleGroup, forward_heading: int ) -> None: """Setting a waypoint close to the spawn position allows the group to reform gracefully - rather than spin + rather than spin """ reform_point = dcs_group.position.point_from_heading(forward_heading, 50) dcs_group.add_waypoint(reform_point) @@ -256,7 +286,7 @@ class GroundConflictGenerator: gen_group: CombatGroup, dcs_group: VehicleGroup, forward_heading: int, - target: Point + target: Point, ) -> bool: """ Handles adding the DCS tasks for artillery groups for all combat stances. @@ -269,7 +299,9 @@ class GroundConflictGenerator: dcs_group.add_trigger_action(hold_task) # Artillery strike random start - artillery_trigger = TriggerOnce(Event.NoEvent, "ArtilleryFireTask #" + str(dcs_group.id)) + artillery_trigger = TriggerOnce( + Event.NoEvent, "ArtilleryFireTask #" + str(dcs_group.id) + ) artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45) * 60)) # TODO: Update to fire at group instead of point fire_task = FireAtPoint(target, len(gen_group.units) * 10, 100) @@ -283,12 +315,19 @@ class GroundConflictGenerator: # Hold position dcs_group.points[1].tasks.append(Hold()) - retreat = self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE/3)) - dcs_group.add_waypoint(dcs_group.position.point_from_heading(forward_heading, 1), PointAction.OffRoad) + retreat = self.find_retreat_point( + dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 3) + ) + dcs_group.add_waypoint( + dcs_group.position.point_from_heading(forward_heading, 1), + PointAction.OffRoad, + ) dcs_group.points[2].tasks.append(Hold()) dcs_group.add_waypoint(retreat, PointAction.OffRoad) - artillery_fallback = TriggerOnce(Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id)) + artillery_fallback = TriggerOnce( + Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id) + ) for i, u in enumerate(dcs_group.units): artillery_fallback.add_condition(UnitDamaged(u.id)) if i < len(dcs_group.units) - 1: @@ -302,7 +341,9 @@ class GroundConflictGenerator: retreat_task.number = 4 dcs_group.add_trigger_action(retreat_task) - artillery_fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) + artillery_fallback.add_action( + AITaskPush(dcs_group.id, len(dcs_group.tasks)) + ) self.mission.triggerrules.triggers.append(artillery_fallback) for u in dcs_group.units: @@ -330,12 +371,8 @@ class GroundConflictGenerator: target = self.find_nearest_enemy_group(dcs_group, enemy_groups) if target is not None: rand_offset = Point( - random.randint( - -RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK - ), - random.randint( - -RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK - ) + random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), + random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), ) target_point = self.conflict.theater.nearest_land_pos( target.points[0].position + rand_offset @@ -345,8 +382,7 @@ class GroundConflictGenerator: if ( to_cp.position.distance_to_point(dcs_group.points[0].position) - <= - AGGRESIVE_MOVE_DISTANCE + <= AGGRESIVE_MOVE_DISTANCE ): attack_point = self.conflict.theater.nearest_land_pos( to_cp.position.random_point_within(500, 0) @@ -358,16 +394,16 @@ class GroundConflictGenerator: if offset_heading < 0: offset_heading = 358 attack_point = self.find_offensive_point( - dcs_group, - offset_heading, - AGGRESIVE_MOVE_DISTANCE + dcs_group, offset_heading, AGGRESIVE_MOVE_DISTANCE ) dcs_group.add_waypoint(attack_point, PointAction.OffRoad) elif stance == CombatStance.BREAKTHROUGH: # In breakthrough mode, the units will move forward # If the enemy base is close enough, the units will attack the base - if to_cp.position.distance_to_point( - dcs_group.points[0].position) <= BREAKTHROUGH_OFFENSIVE_DISTANCE: + if ( + to_cp.position.distance_to_point(dcs_group.points[0].position) + <= BREAKTHROUGH_OFFENSIVE_DISTANCE + ): attack_point = self.conflict.theater.nearest_land_pos( to_cp.position.random_point_within(500, 0) ) @@ -377,27 +413,27 @@ class GroundConflictGenerator: offset_heading = forward_heading - 1 if offset_heading < 0: offset_heading = 359 - attack_point = self.find_offensive_point(dcs_group, offset_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE) + attack_point = self.find_offensive_point( + dcs_group, offset_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE + ) dcs_group.add_waypoint(attack_point, PointAction.OffRoad) elif stance == CombatStance.ELIMINATION: # In elimination mode, the units focus on destroying as much enemy groups as possible targets = self.find_n_nearest_enemy_groups(dcs_group, enemy_groups, 3) for i, target in enumerate(targets, start=1): rand_offset = Point( - random.randint( - -RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK - ), - random.randint( - -RANDOM_OFFSET_ATTACK, - RANDOM_OFFSET_ATTACK - ) + random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), + random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), ) target_point = self.conflict.theater.nearest_land_pos( - target.points[0].position+rand_offset + target.points[0].position + rand_offset ) dcs_group.add_waypoint(target_point, PointAction.OffRoad) dcs_group.points[i + 1].tasks.append(AttackGroup(target.id)) - if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE: + if ( + to_cp.position.distance_to_point(dcs_group.points[0].position) + <= AGGRESIVE_MOVE_DISTANCE + ): attack_point = self.conflict.theater.nearest_land_pos( to_cp.position.random_point_within(500, 0) ) @@ -420,12 +456,23 @@ class GroundConflictGenerator: Returns True if tasking was added, returns False if the stance was not a combat stance. """ self._set_reform_waypoint(dcs_group, forward_heading) - if stance in [CombatStance.AGGRESSIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]: + if stance in [ + CombatStance.AGGRESSIVE, + CombatStance.BREAKTHROUGH, + CombatStance.ELIMINATION, + ]: # APC & ATGM will never move too much forward, but will follow along any offensive - if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE: - attack_point = self.conflict.theater.nearest_land_pos(to_cp.position.random_point_within(500, 0)) + if ( + to_cp.position.distance_to_point(dcs_group.points[0].position) + <= AGGRESIVE_MOVE_DISTANCE + ): + attack_point = self.conflict.theater.nearest_land_pos( + to_cp.position.random_point_within(500, 0) + ) else: - attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE) + attack_point = self.find_offensive_point( + dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE + ) dcs_group.add_waypoint(attack_point, PointAction.OffRoad) if stance != CombatStance.RETREAT: @@ -434,29 +481,36 @@ class GroundConflictGenerator: return False def plan_action_for_groups( - self, stance: CombatStance, + self, + stance: CombatStance, ally_groups: List[Tuple[VehicleGroup, CombatGroup]], enemy_groups: List[Tuple[VehicleGroup, CombatGroup]], forward_heading: int, from_cp: ControlPoint, - to_cp: ControlPoint + to_cp: ControlPoint, ) -> None: if not self.game.settings.perf_moving_units: return for dcs_group, group in ally_groups: - if hasattr(group.units[0], 'eplrs') and group.units[0].eplrs: + if hasattr(group.units[0], "eplrs") and group.units[0].eplrs: dcs_group.points[0].tasks.append(EPLRS(dcs_group.id)) if group.role == CombatGroupRole.ARTILLERY: if self.game.settings.perf_artillery: - target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups) + target = self.get_artillery_target_in_range( + dcs_group, group, enemy_groups + ) if target is not None: - self._plan_artillery_action(stance, group, dcs_group, forward_heading, target) + self._plan_artillery_action( + stance, group, dcs_group, forward_heading, target + ) elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]: - self._plan_tank_ifv_action(stance, enemy_groups, dcs_group, forward_heading, to_cp) + self._plan_tank_ifv_action( + stance, enemy_groups, dcs_group, forward_heading, to_cp + ) elif group.role in [CombatGroupRole.APC, CombatGroupRole.ATGM]: self._plan_apc_atgm_action(stance, dcs_group, forward_heading, to_cp) @@ -464,11 +518,16 @@ class GroundConflictGenerator: if stance == CombatStance.RETREAT: # In retreat mode, the units will fall back # If the ally base is close enough, the units will even regroup there - if from_cp.position.distance_to_point(dcs_group.points[0].position) <= RETREAT_DISTANCE: + if ( + from_cp.position.distance_to_point(dcs_group.points[0].position) + <= RETREAT_DISTANCE + ): retreat_point = from_cp.position.random_point_within(500, 250) else: retreat_point = self.find_retreat_point(dcs_group, forward_heading) - reposition_point = retreat_point.point_from_heading(forward_heading, 10) # Another point to make the unit face the enemy + reposition_point = retreat_point.point_from_heading( + forward_heading, 10 + ) # Another point to make the unit face the enemy dcs_group.add_waypoint(retreat_point, PointAction.OffRoad) dcs_group.add_waypoint(reposition_point, PointAction.OffRoad) @@ -490,8 +549,10 @@ class GroundConflictGenerator: # We add a new retreat waypoint dcs_group.add_waypoint( - self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 8)), - PointAction.OffRoad + self.find_retreat_point( + dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 8) + ), + PointAction.OffRoad, ) # Fallback task @@ -515,7 +576,7 @@ class GroundConflictGenerator: self, dcs_group: VehicleGroup, frontline_heading: int, - distance: int = RETREAT_DISTANCE + distance: int = RETREAT_DISTANCE, ) -> Point: """ Find a point to retreat to @@ -523,17 +584,15 @@ class GroundConflictGenerator: :param frontline_heading: Heading of the frontline :return: dcs.mapping.Point object with the desired position """ - desired_point = dcs_group.points[0].position.point_from_heading(heading_sum(frontline_heading, +180), distance) + desired_point = dcs_group.points[0].position.point_from_heading( + heading_sum(frontline_heading, +180), distance + ) if self.conflict.theater.is_on_land(desired_point): return desired_point return self.conflict.theater.nearest_land_pos(desired_point) - def find_offensive_point( - self, - dcs_group: VehicleGroup, - frontline_heading: int, - distance: int + self, dcs_group: VehicleGroup, frontline_heading: int, distance: int ) -> Point: """ Find a point to attack @@ -542,7 +601,9 @@ class GroundConflictGenerator: :param distance: Distance of the offensive (how far unit should move) :return: dcs.mapping.Point object with the desired position """ - desired_point = dcs_group.points[0].position.point_from_heading(frontline_heading, distance) + desired_point = dcs_group.points[0].position.point_from_heading( + frontline_heading, distance + ) if self.conflict.theater.is_on_land(desired_point): return desired_point return self.conflict.theater.nearest_land_pos(desired_point) @@ -551,7 +612,7 @@ class GroundConflictGenerator: def find_n_nearest_enemy_groups( player_group: VehicleGroup, enemy_groups: List[Tuple[VehicleGroup, CombatGroup]], - n: int + n: int, ) -> List[VehicleGroup]: """ Return the nearest enemy group for the player group @@ -562,7 +623,9 @@ class GroundConflictGenerator: targets = [] # type: List[Optional[VehicleGroup]] sorted_list = sorted( enemy_groups, - key=lambda group: player_group.points[0].position.distance_to_point(group[0].points[0].position) + key=lambda group: player_group.points[0].position.distance_to_point( + group[0].points[0].position + ), ) for i in range(n): # TODO: Is this supposed to return no groups if enemy_groups is less than n? @@ -574,8 +637,7 @@ class GroundConflictGenerator: @staticmethod def find_nearest_enemy_group( - player_group: VehicleGroup, - enemy_groups: List[Tuple[VehicleGroup, CombatGroup]] + player_group: VehicleGroup, enemy_groups: List[Tuple[VehicleGroup, CombatGroup]] ) -> Optional[VehicleGroup]: """ Search the enemy groups for a potential target suitable to armored assault @@ -585,7 +647,9 @@ class GroundConflictGenerator: min_distance = 99999999 target = None for dcs_group, _ in enemy_groups: - dist = player_group.points[0].position.distance_to_point(dcs_group.points[0].position) + dist = player_group.points[0].position.distance_to_point( + dcs_group.points[0].position + ) if dist < min_distance: min_distance = dist target = dcs_group @@ -595,7 +659,7 @@ class GroundConflictGenerator: def get_artillery_target_in_range( dcs_group: VehicleGroup, group: CombatGroup, - enemy_groups: List[Tuple[VehicleGroup, CombatGroup]] + enemy_groups: List[Tuple[VehicleGroup, CombatGroup]], ) -> Optional[Point]: """ Search the enemy groups for a potential target suitable to an artillery unit @@ -606,7 +670,9 @@ class GroundConflictGenerator: return None for _ in range(10): potential_target = random.choice(enemy_groups)[0] - distance_to_target = dcs_group.points[0].position.distance_to_point(potential_target.points[0].position) + distance_to_target = dcs_group.points[0].position.distance_to_point( + potential_target.points[0].position + ) if distance_to_target < rng: return potential_target.points[0].position return None @@ -620,12 +686,12 @@ class GroundConflictGenerator: if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]: rg = random.randint( DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][0], - DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1] + DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1], ) elif rg < DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]: rg = random.randint( DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK][0], - DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK][1] + DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK][1], ) return rg @@ -635,42 +701,46 @@ class GroundConflictGenerator: combat_width: int, distance_from_frontline: int, heading: int, - spawn_heading: int + spawn_heading: int, ): - shifted = conflict_position.point_from_heading(heading, random.randint(0, combat_width)) - desired_point = shifted.point_from_heading( - spawn_heading, - distance_from_frontline + shifted = conflict_position.point_from_heading( + heading, random.randint(0, combat_width) + ) + desired_point = shifted.point_from_heading( + spawn_heading, distance_from_frontline + ) + return Conflict.find_ground_position( + desired_point, combat_width, heading, self.conflict.theater ) - return Conflict.find_ground_position(desired_point, combat_width, heading, self.conflict.theater) - def _generate_groups( self, groups: List[CombatGroup], frontline_vector: Tuple[Point, int, int], - is_player: bool + is_player: bool, ) -> List[Tuple[VehicleGroup, CombatGroup]]: """Finds valid positions for planned groups and generates a pydcs group for them""" positioned_groups = [] position, heading, combat_width = frontline_vector - spawn_heading = int(heading_sum(heading, -90)) if is_player else int(heading_sum(heading, 90)) + spawn_heading = ( + int(heading_sum(heading, -90)) + if is_player + else int(heading_sum(heading, 90)) + ) country = self.game.player_country if is_player else self.game.enemy_country for group in groups: if group.role == CombatGroupRole.ARTILLERY: - distance_from_frontline = self.get_artilery_group_distance_from_frontline(group) + distance_from_frontline = ( + self.get_artilery_group_distance_from_frontline(group) + ) else: distance_from_frontline = random.randint( - DISTANCE_FROM_FRONTLINE[group.role][0], - DISTANCE_FROM_FRONTLINE[group.role][1] + DISTANCE_FROM_FRONTLINE[group.role][0], + DISTANCE_FROM_FRONTLINE[group.role][1], ) final_position = self.get_valid_position_for_group( - position, - combat_width, - distance_from_frontline, - heading, - spawn_heading + position, combat_width, distance_from_frontline, heading, spawn_heading ) if final_position is not None: @@ -693,7 +763,7 @@ class GroundConflictGenerator: g, is_player, self.mission.country(country), - opposite_heading(spawn_heading) + opposite_heading(spawn_heading), ) else: logging.warning(f"Unable to get valid position for {group}") @@ -718,12 +788,14 @@ class GroundConflictGenerator: logging.info("armorgen: {} for {}".format(unit, side.id)) group = self.mission.vehicle_group( - side, - namegen.next_unit_name(side, cp.id, unit), unit, - position=at, - group_size=count, - heading=heading, - move_formation=move_formation) + side, + namegen.next_unit_name(side, cp.id, unit), + unit, + position=at, + group_size=count, + heading=heading, + move_formation=move_formation, + ) self.unit_map.add_front_line_units(group, cp) diff --git a/gen/ato.py b/gen/ato.py index 7d541f77..da58430f 100644 --- a/gen/ato.py +++ b/gen/ato.py @@ -96,7 +96,8 @@ class Package: if tot is None: logging.error( f"{flight} requested escort at {waypoint} but that " - "waypoint has no TOT. It may not be escorted.") + "waypoint has no TOT. It may not be escorted." + ) continue times.append(tot) if times: @@ -117,7 +118,8 @@ class Package: logging.error( f"{flight} dismissed escort at {waypoint} but that " "waypoint has no TOT or departure time. It may not be " - "escorted.") + "escorted." + ) continue times.append(tot) if times: diff --git a/gen/briefinggen.py b/gen/briefinggen.py index 14cef8de..b1246c78 100644 --- a/gen/briefinggen.py +++ b/gen/briefinggen.py @@ -23,9 +23,11 @@ from .runways import RunwayData if TYPE_CHECKING: from game import Game + @dataclass class CommInfo: """Communications information for the kneeboard.""" + name: str freq: RadioFrequency @@ -37,10 +39,13 @@ class FrontLineInfo: 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.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: """Base type for generators of mission information for the player. @@ -131,7 +136,6 @@ def format_waypoint_time(waypoint: FlightWaypoint, depart_prefix: str) -> str: class BriefingGenerator(MissionInfoGenerator): - def __init__(self, mission: Mission, game: Game): super().__init__(mission, game) self.allied_flights_by_departure: Dict[str, List[FlightData]] = {} @@ -141,36 +145,36 @@ class BriefingGenerator(MissionInfoGenerator): disabled_extensions=("",), default_for_string=True, default=True, - ), + ), trim_blocks=True, lstrip_blocks=True, - ) + ) env.filters["waypoint_timing"] = format_waypoint_time self.template = env.get_template("briefingtemplate_EN.j2") def generate(self) -> None: - """Generate the mission briefing - """ + """Generate the mission briefing""" 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")) + self.mission.add_picture_blue( + os.path.abspath("./resources/ui/splash_screen.png") + ) def _generate_frontline_info(self) -> None: - """Build FrontLineInfo objects from FrontLine type and append to briefing. - """ + """Build FrontLineInfo objects from FrontLine type and append to briefing.""" 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 def generate_allied_flights_by_departure(self) -> None: - """Create iterable to display allied flights grouped by departure airfield. - """ + """Create iterable to display allied flights grouped by departure airfield.""" for flight in self.flights: if not flight.client_units and flight.friendly: name = flight.departure.airfield_name - if name in self.allied_flights_by_departure: # where else can we get this? + if ( + name in self.allied_flights_by_departure + ): # where else can we get this? self.allied_flights_by_departure[name].append(flight) else: self.allied_flights_by_departure[name] = [flight] diff --git a/gen/coastal/coastal_group_generator.py b/gen/coastal/coastal_group_generator.py index 63ac4a03..160712e0 100644 --- a/gen/coastal/coastal_group_generator.py +++ b/gen/coastal/coastal_group_generator.py @@ -23,5 +23,9 @@ def generate_coastal_group(game, ground_object, faction_name: str): generator.generate() return generator.get_generated_group() else: - logging.info("Unable to generate missile group, generator : " + str(gen) + "does not exists") - return None \ No newline at end of file + logging.info( + "Unable to generate missile group, generator : " + + str(gen) + + "does not exists" + ) + return None diff --git a/gen/coastal/silkworm.py b/gen/coastal/silkworm.py index e194ccc9..8ccd7637 100644 --- a/gen/coastal/silkworm.py +++ b/gen/coastal/silkworm.py @@ -4,7 +4,6 @@ from gen.sam.group_generator import GroupGenerator class SilkwormGenerator(GroupGenerator): - def __init__(self, game, ground_object, faction): super(SilkwormGenerator, self).__init__(game, ground_object) self.faction = faction @@ -13,20 +12,47 @@ class SilkwormGenerator(GroupGenerator): positions = self.get_circular_position(5, launcher_distance=120, coverage=180) - self.add_unit(MissilesSS.Silkworm_Radar, "SR#0", self.position.x, self.position.y, self.heading) + self.add_unit( + MissilesSS.Silkworm_Radar, + "SR#0", + self.position.x, + self.position.y, + self.heading, + ) # Launchers for i, p in enumerate(positions): - self.add_unit(MissilesSS.SS_N_2_Silkworm, "Missile#" + str(i), p[0], p[1], self.heading) + self.add_unit( + MissilesSS.SS_N_2_Silkworm, + "Missile#" + str(i), + p[0], + p[1], + self.heading, + ) # Commander - self.add_unit(Unarmed.Transport_KAMAZ_43101, "KAMAZ#0", self.position.x - 35, self.position.y - 20, - self.heading) + self.add_unit( + Unarmed.Transport_KAMAZ_43101, + "KAMAZ#0", + self.position.x - 35, + self.position.y - 20, + self.heading, + ) # Shorad - self.add_unit(AirDefence.SPAAA_ZSU_23_4_Shilka, "SHILKA#0", self.position.x - 55, self.position.y - 38, - self.heading) + self.add_unit( + AirDefence.SPAAA_ZSU_23_4_Shilka, + "SHILKA#0", + self.position.x - 55, + self.position.y - 38, + self.heading, + ) # Shorad 2 - self.add_unit(AirDefence.SAM_SA_9_Strela_1_9P31, "STRELA#0", - self.position.x + 200, self.position.y + 15, 90) \ No newline at end of file + self.add_unit( + AirDefence.SAM_SA_9_Strela_1_9P31, + "STRELA#0", + self.position.x + 200, + self.position.y + 15, + 90, + ) diff --git a/gen/conflictgen.py b/gen/conflictgen.py index b050e028..25e1fed3 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -12,19 +12,21 @@ from game.utils import heading_sum, opposite_heading FRONTLINE_LENGTH = 80000 + class Conflict: - def __init__(self, - theater: ConflictTheater, - from_cp: ControlPoint, - to_cp: ControlPoint, - attackers_side: str, - defenders_side: str, - attackers_country: Country, - defenders_country: Country, - position: Point, - heading: Optional[int] = None, - size: Optional[int] = None - ): + def __init__( + self, + theater: ConflictTheater, + from_cp: ControlPoint, + to_cp: ControlPoint, + attackers_side: str, + defenders_side: str, + attackers_country: Country, + defenders_country: Country, + position: Point, + heading: Optional[int] = None, + size: Optional[int] = None, + ): self.attackers_side = attackers_side self.defenders_side = defenders_side @@ -43,27 +45,49 @@ class Conflict: return from_cp.has_frontline and to_cp.has_frontline @classmethod - def frontline_position(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> Tuple[Point, int]: + def frontline_position( + cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater + ) -> Tuple[Point, int]: frontline = FrontLine(from_cp, to_cp, theater) attack_heading = frontline.attack_heading - position = cls.find_ground_position(frontline.position, FRONTLINE_LENGTH, heading_sum(attack_heading, 90), theater) + position = cls.find_ground_position( + frontline.position, + FRONTLINE_LENGTH, + heading_sum(attack_heading, 90), + theater, + ) return position, opposite_heading(attack_heading) @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 + ) -> Tuple[Point, int, int]: """ Returns a vector for a valid frontline location avoiding exclusion zones. """ center_position, heading = cls.frontline_position(from_cp, to_cp, theater) left_heading = heading_sum(heading, -90) right_heading = heading_sum(heading, 90) - left_position = cls.extend_ground_position(center_position, int(FRONTLINE_LENGTH / 2), left_heading, theater) - right_position = cls.extend_ground_position(center_position, int(FRONTLINE_LENGTH / 2), right_heading, theater) + left_position = cls.extend_ground_position( + center_position, int(FRONTLINE_LENGTH / 2), left_heading, theater + ) + right_position = cls.extend_ground_position( + center_position, int(FRONTLINE_LENGTH / 2), right_heading, theater + ) distance = int(left_position.distance_to_point(right_position)) return left_position, right_heading, distance @classmethod - def frontline_cas_conflict(cls, attacker_name: str, defender_name: str, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): + def frontline_cas_conflict( + cls, + attacker_name: str, + defender_name: str, + attacker: Country, + defender: Country, + from_cp: ControlPoint, + to_cp: ControlPoint, + theater: ConflictTheater, + ): assert cls.has_frontline_between(from_cp, to_cp) position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater) conflict = cls( @@ -76,12 +100,14 @@ class Conflict: defenders_side=defender_name, attackers_country=attacker, defenders_country=defender, - size=distance + size=distance, ) return conflict @classmethod - def extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: + def extend_ground_position( + cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater + ) -> Point: """Finds the first intersection with an exclusion zone in one heading from an initial point up to max_distance""" extended = initial.point_from_heading(heading, max_distance) if theater.landmap is None: @@ -92,8 +118,7 @@ class Conflict: p1 = ShapelyPoint(extended.x, extended.y) line = LineString([p0, p1]) - intersection = line.intersection( - theater.landmap.inclusion_zone_only.boundary) + intersection = line.intersection(theater.landmap.inclusion_zone_only.boundary) if intersection.is_empty: # Max extent does not intersect with the boundary of the inclusion # zone, so the full front line is usable. This does assume that the @@ -104,7 +129,14 @@ class Conflict: return initial.point_from_heading(heading, p0.distance(intersection)) @classmethod - def find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater, coerce=True) -> Optional[Point]: + def find_ground_position( + cls, + initial: Point, + max_distance: int, + heading: int, + theater: ConflictTheater, + coerce=True, + ) -> Optional[Point]: """ Finds the nearest valid ground position along a provided heading and it's inverse up to max_distance. `coerce=True` will return the closest land position to `initial` regardless of heading or distance @@ -123,4 +155,3 @@ class Conflict: return pos logging.error("Didn't find ground position ({})!".format(initial)) return None - diff --git a/gen/defenses/armor_group_generator.py b/gen/defenses/armor_group_generator.py index c8bc472c..fe429e72 100644 --- a/gen/defenses/armor_group_generator.py +++ b/gen/defenses/armor_group_generator.py @@ -3,15 +3,20 @@ import random from dcs.vehicles import Armor from game import db -from gen.defenses.armored_group_generator import ArmoredGroupGenerator, FixedSizeArmorGroupGenerator +from gen.defenses.armored_group_generator import ( + ArmoredGroupGenerator, + FixedSizeArmorGroupGenerator, +) -def generate_armor_group(faction:str, game, ground_object): +def generate_armor_group(faction: str, game, ground_object): """ This generate a group of ground units :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].frontline_units if u in Armor.__dict__.values() + ] if len(possible_unit) > 0: unit_type = random.choice(possible_unit) return generate_armor_group_of_type(game, ground_object, unit_type) @@ -36,4 +41,3 @@ def generate_armor_group_of_type_and_size(game, ground_object, unit_type, size: generator = FixedSizeArmorGroupGenerator(game, ground_object, unit_type, size) generator.generate() return generator.get_generated_group() - diff --git a/gen/defenses/armored_group_generator.py b/gen/defenses/armored_group_generator.py index 3b81a1dd..e25f5c74 100644 --- a/gen/defenses/armored_group_generator.py +++ b/gen/defenses/armored_group_generator.py @@ -4,7 +4,6 @@ from gen.sam.group_generator import GroupGenerator class ArmoredGroupGenerator(GroupGenerator): - def __init__(self, game, ground_object, unit_type): super(ArmoredGroupGenerator, self).__init__(game, ground_object) self.unit_type = unit_type @@ -20,13 +19,16 @@ class ArmoredGroupGenerator(GroupGenerator): for i in range(grid_x): for j in range(grid_y): index = index + 1 - self.add_unit(self.unit_type, "Armor#" + str(index), - self.position.x + spacing * i, - self.position.y + spacing * j, self.heading) + self.add_unit( + self.unit_type, + "Armor#" + str(index), + self.position.x + spacing * i, + self.position.y + spacing * j, + self.heading, + ) class FixedSizeArmorGroupGenerator(GroupGenerator): - def __init__(self, game, ground_object, unit_type, size): super(FixedSizeArmorGroupGenerator, self).__init__(game, ground_object) self.unit_type = unit_type @@ -38,7 +40,10 @@ class FixedSizeArmorGroupGenerator(GroupGenerator): index = 0 for i in range(self.size): index = index + 1 - self.add_unit(self.unit_type, "Armor#" + str(index), - self.position.x + spacing * i, - self.position.y, self.heading) - + self.add_unit( + self.unit_type, + "Armor#" + str(index), + self.position.x + spacing * i, + self.position.y, + self.heading, + ) diff --git a/gen/fleet/carrier_group.py b/gen/fleet/carrier_group.py index c8be8c7d..518c89b7 100644 --- a/gen/fleet/carrier_group.py +++ b/gen/fleet/carrier_group.py @@ -2,54 +2,122 @@ import random from gen.sam.group_generator import ShipGroupGenerator -from dcs.ships import ( - USS_Arleigh_Burke_IIa, - Ticonderoga_class -) +from dcs.ships import USS_Arleigh_Burke_IIa, Ticonderoga_class + class CarrierGroupGenerator(ShipGroupGenerator): - def generate(self): - #Carrier Strike Group 8 + # Carrier Strike Group 8 if self.faction.carrier_names[0] == "Carrier Strike Group 8": carrier_type = random.choice(self.faction.aircraft_carrier) - self.add_unit(carrier_type, "CVN-75 Harry S. Truman", self.position.x, self.position.y, self.heading) + self.add_unit( + carrier_type, + "CVN-75 Harry S. Truman", + self.position.x, + self.position.y, + self.heading, + ) # Add Arleigh Burke escort - self.add_unit(USS_Arleigh_Burke_IIa, "USS Ramage", self.position.x + 6482, self.position.y + 6667, self.heading) + self.add_unit( + USS_Arleigh_Burke_IIa, + "USS Ramage", + self.position.x + 6482, + self.position.y + 6667, + self.heading, + ) - self.add_unit(USS_Arleigh_Burke_IIa, "USS Mitscher", self.position.x - 7963, self.position.y + 7037, self.heading) + self.add_unit( + USS_Arleigh_Burke_IIa, + "USS Mitscher", + self.position.x - 7963, + self.position.y + 7037, + self.heading, + ) - self.add_unit(USS_Arleigh_Burke_IIa, "USS Forrest Sherman", self.position.x - 7408, self.position.y - 7408, self.heading) + self.add_unit( + USS_Arleigh_Burke_IIa, + "USS Forrest Sherman", + self.position.x - 7408, + self.position.y - 7408, + self.heading, + ) - self.add_unit(USS_Arleigh_Burke_IIa, "USS Lassen", self.position.x + 8704, self.position.y - 6296, self.heading) + self.add_unit( + USS_Arleigh_Burke_IIa, + "USS Lassen", + self.position.x + 8704, + self.position.y - 6296, + self.heading, + ) # Add Ticonderoga escort if self.heading >= 180: - self.add_unit(Ticonderoga_class, "USS Hué City", self.position.x + 2222, self.position.y - 3333, self.heading) + self.add_unit( + Ticonderoga_class, + "USS Hué City", + self.position.x + 2222, + self.position.y - 3333, + self.heading, + ) else: - self.add_unit(Ticonderoga_class, "USS Hué City", self.position.x - 3333, self.position.y + 2222, self.heading) - + self.add_unit( + Ticonderoga_class, + "USS Hué City", + self.position.x - 3333, + self.position.y + 2222, + self.heading, + ) self.get_generated_group().points[0].speed = 20 - ################################################################################################## + ################################################################################################## # Add carrier for normal generation else: if len(self.faction.aircraft_carrier) > 0: 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: return # Add destroyers escort if len(self.faction.destroyers) > 0: dd_type = random.choice(self.faction.destroyers) - 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, + "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, "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, + "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.get_generated_group().points[0].speed = 20 \ No newline at end of file + self.get_generated_group().points[0].speed = 20 diff --git a/gen/fleet/cn_dd_group.py b/gen/fleet/cn_dd_group.py index 8a20ae10..0878e61b 100644 --- a/gen/fleet/cn_dd_group.py +++ b/gen/fleet/cn_dd_group.py @@ -20,7 +20,6 @@ if TYPE_CHECKING: class ChineseNavyGroupGenerator(ShipGroupGenerator): - def generate(self): include_frigate = random.choice([True, True, False]) @@ -30,17 +29,45 @@ class ChineseNavyGroupGenerator(ShipGroupGenerator): include_frigate = True if include_frigate: - self.add_unit(Type_054A_Frigate, "FF1", self.position.x + 1200, self.position.y + 900, self.heading) - self.add_unit(Type_054A_Frigate, "FF2", self.position.x + 1200, self.position.y - 900, self.heading) + self.add_unit( + Type_054A_Frigate, + "FF1", + self.position.x + 1200, + self.position.y + 900, + self.heading, + ) + self.add_unit( + Type_054A_Frigate, + "FF2", + self.position.x + 1200, + self.position.y - 900, + self.heading, + ) if include_dd: dd_type = random.choice([Type_052C_Destroyer, Type_052B_Destroyer]) - self.add_unit(dd_type, "DD1", self.position.x + 2400, self.position.y + 900, self.heading) - self.add_unit(dd_type, "DD2", self.position.x + 2400, self.position.y - 900, self.heading) + self.add_unit( + dd_type, + "DD1", + self.position.x + 2400, + self.position.y + 900, + self.heading, + ) + self.add_unit( + dd_type, + "DD2", + self.position.x + 2400, + self.position.y - 900, + self.heading, + ) self.get_generated_group().points[0].speed = 20 class Type54GroupGenerator(DDGroupGenerator): - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): - super(Type54GroupGenerator, self).__init__(game, ground_object, faction, Type_054A_Frigate) + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): + super(Type54GroupGenerator, self).__init__( + game, ground_object, faction, Type_054A_Frigate + ) diff --git a/gen/fleet/dd_group.py b/gen/fleet/dd_group.py index c6a3e115..767eb52a 100644 --- a/gen/fleet/dd_group.py +++ b/gen/fleet/dd_group.py @@ -13,22 +13,47 @@ if TYPE_CHECKING: class DDGroupGenerator(ShipGroupGenerator): - - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction, ddtype: ShipType): + def __init__( + self, + game: Game, + ground_object: TheaterGroundObject, + faction: Faction, + ddtype: ShipType, + ): super(DDGroupGenerator, self).__init__(game, ground_object, faction) self.ddtype = ddtype def generate(self): - self.add_unit(self.ddtype, "DD1", self.position.x + 500, self.position.y + 900, self.heading) - self.add_unit(self.ddtype, "DD2", self.position.x + 500, self.position.y - 900, self.heading) + self.add_unit( + self.ddtype, + "DD1", + self.position.x + 500, + self.position.y + 900, + self.heading, + ) + self.add_unit( + self.ddtype, + "DD2", + self.position.x + 500, + self.position.y - 900, + self.heading, + ) self.get_generated_group().points[0].speed = 20 class OliverHazardPerryGroupGenerator(DDGroupGenerator): - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): - super(OliverHazardPerryGroupGenerator, self).__init__(game, ground_object, faction, Oliver_Hazzard_Perry_class) + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): + super(OliverHazardPerryGroupGenerator, self).__init__( + game, ground_object, faction, Oliver_Hazzard_Perry_class + ) class ArleighBurkeGroupGenerator(DDGroupGenerator): - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): - super(ArleighBurkeGroupGenerator, self).__init__(game, ground_object, faction, USS_Arleigh_Burke_IIa) + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): + super(ArleighBurkeGroupGenerator, self).__init__( + game, ground_object, faction, USS_Arleigh_Burke_IIa + ) diff --git a/gen/fleet/lha_group.py b/gen/fleet/lha_group.py index cfbafcbb..a1a78d37 100644 --- a/gen/fleet/lha_group.py +++ b/gen/fleet/lha_group.py @@ -4,18 +4,31 @@ from gen.sam.group_generator import ShipGroupGenerator class LHAGroupGenerator(ShipGroupGenerator): - def generate(self): # Add carrier if len(self.faction.helicopter_carrier) > 0: 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 if len(self.faction.destroyers) > 0: dd_type = random.choice(self.faction.destroyers) - 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, + "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.get_generated_group().points[0].speed = 20 diff --git a/gen/fleet/ru_dd_group.py b/gen/fleet/ru_dd_group.py index ee08577f..e13c8bb2 100644 --- a/gen/fleet/ru_dd_group.py +++ b/gen/fleet/ru_dd_group.py @@ -9,7 +9,7 @@ from dcs.ships import ( FF_1135M_Rezky, CG_1164_Moskva, SSK_877, - SSK_641B + SSK_641B, ) from gen.fleet.dd_group import DDGroupGenerator @@ -23,7 +23,6 @@ if TYPE_CHECKING: class RussianNavyGroupGenerator(ShipGroupGenerator): - def generate(self): include_frigate = random.choice([True, True, False]) @@ -39,37 +38,79 @@ class RussianNavyGroupGenerator(ShipGroupGenerator): if include_frigate: frigate_type = random.choice([FFL_1124_4_Grisha, FSG_1241_1MP_Molniya]) - self.add_unit(frigate_type, "FF1", self.position.x + 1200, self.position.y + 900, self.heading) - self.add_unit(frigate_type, "FF2", self.position.x + 1200, self.position.y - 900, self.heading) + self.add_unit( + frigate_type, + "FF1", + self.position.x + 1200, + self.position.y + 900, + self.heading, + ) + self.add_unit( + frigate_type, + "FF2", + self.position.x + 1200, + self.position.y - 900, + self.heading, + ) if include_dd: dd_type = random.choice([FFG_11540_Neustrashimy, FF_1135M_Rezky]) - self.add_unit(dd_type, "DD1", self.position.x + 2400, self.position.y + 900, self.heading) - self.add_unit(dd_type, "DD2", self.position.x + 2400, self.position.y - 900, self.heading) + self.add_unit( + dd_type, + "DD1", + self.position.x + 2400, + self.position.y + 900, + self.heading, + ) + self.add_unit( + dd_type, + "DD2", + self.position.x + 2400, + self.position.y - 900, + self.heading, + ) if include_cc: # Only include the Moskva for now, the Pyotry Velikiy is an unkillable monster. # See https://github.com/Khopa/dcs_liberation/issues/567 - self.add_unit(CG_1164_Moskva, "CC1", self.position.x, self.position.y, self.heading) + self.add_unit( + CG_1164_Moskva, "CC1", self.position.x, self.position.y, self.heading + ) self.get_generated_group().points[0].speed = 20 class GrishaGroupGenerator(DDGroupGenerator): - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): - super(GrishaGroupGenerator, self).__init__(game, ground_object, faction, FFL_1124_4_Grisha) + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): + super(GrishaGroupGenerator, self).__init__( + game, ground_object, faction, FFL_1124_4_Grisha + ) class MolniyaGroupGenerator(DDGroupGenerator): - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): - super(MolniyaGroupGenerator, self).__init__(game, ground_object, faction, FSG_1241_1MP_Molniya) + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): + super(MolniyaGroupGenerator, self).__init__( + game, ground_object, faction, FSG_1241_1MP_Molniya + ) class KiloSubGroupGenerator(DDGroupGenerator): - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): - super(KiloSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_877) + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): + super(KiloSubGroupGenerator, self).__init__( + game, ground_object, faction, SSK_877 + ) class TangoSubGroupGenerator(DDGroupGenerator): - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): - super(TangoSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_641B) + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): + super(TangoSubGroupGenerator, self).__init__( + game, ground_object, faction, SSK_641B + ) diff --git a/gen/fleet/schnellboot.py b/gen/fleet/schnellboot.py index 3de8783e..83a83fdf 100644 --- a/gen/fleet/schnellboot.py +++ b/gen/fleet/schnellboot.py @@ -6,10 +6,15 @@ from gen.sam.group_generator import ShipGroupGenerator class SchnellbootGroupGenerator(ShipGroupGenerator): - def generate(self): 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 diff --git a/gen/fleet/ship_group_generator.py b/gen/fleet/ship_group_generator.py index ad5bf457..024d6e03 100644 --- a/gen/fleet/ship_group_generator.py +++ b/gen/fleet/ship_group_generator.py @@ -4,10 +4,18 @@ import random from game import db from gen.fleet.carrier_group import CarrierGroupGenerator from gen.fleet.cn_dd_group import ChineseNavyGroupGenerator, Type54GroupGenerator -from gen.fleet.dd_group import ArleighBurkeGroupGenerator, OliverHazardPerryGroupGenerator +from gen.fleet.dd_group import ( + ArleighBurkeGroupGenerator, + OliverHazardPerryGroupGenerator, +) from gen.fleet.lha_group import LHAGroupGenerator -from gen.fleet.ru_dd_group import RussianNavyGroupGenerator, GrishaGroupGenerator, MolniyaGroupGenerator, \ - KiloSubGroupGenerator, TangoSubGroupGenerator +from gen.fleet.ru_dd_group import ( + RussianNavyGroupGenerator, + GrishaGroupGenerator, + MolniyaGroupGenerator, + KiloSubGroupGenerator, + TangoSubGroupGenerator, +) from gen.fleet.schnellboot import SchnellbootGroupGenerator from gen.fleet.uboat import UBoatGroupGenerator from gen.fleet.ww2lst import WW2LSTGroupGenerator @@ -25,7 +33,7 @@ SHIP_MAP = { "MolniyaGroupGenerator": MolniyaGroupGenerator, "KiloSubGroupGenerator": KiloSubGroupGenerator, "TangoSubGroupGenerator": TangoSubGroupGenerator, - "Type54GroupGenerator": Type54GroupGenerator + "Type54GroupGenerator": Type54GroupGenerator, } @@ -43,7 +51,11 @@ def generate_ship_group(game, ground_object, faction_name: str): generator.generate() return generator.get_generated_group() else: - logging.info("Unable to generate ship group, generator : " + str(gen) + "does not exists") + logging.info( + "Unable to generate ship group, generator : " + + str(gen) + + "does not exists" + ) return None diff --git a/gen/fleet/uboat.py b/gen/fleet/uboat.py index 8d2e9cc0..bb736d1e 100644 --- a/gen/fleet/uboat.py +++ b/gen/fleet/uboat.py @@ -6,10 +6,15 @@ from gen.sam.group_generator import ShipGroupGenerator class UBoatGroupGenerator(ShipGroupGenerator): - def generate(self): for i in range(random.randint(1, 4)): - self.add_unit(Uboat_VIIC_U_flak, "Uboat" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading) + self.add_unit( + Uboat_VIIC_U_flak, + "Uboat" + 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 \ No newline at end of file + self.get_generated_group().points[0].speed = 20 diff --git a/gen/fleet/ww2lst.py b/gen/fleet/ww2lst.py index e0512009..db7deb69 100644 --- a/gen/fleet/ww2lst.py +++ b/gen/fleet/ww2lst.py @@ -6,13 +6,24 @@ from gen.sam.group_generator import ShipGroupGenerator class WW2LSTGroupGenerator(ShipGroupGenerator): - def generate(self): # Add LS Samuel Chase - self.add_unit(LS_Samuel_Chase, "SamuelChase", self.position.x, self.position.y, self.heading) + self.add_unit( + LS_Samuel_Chase, + "SamuelChase", + self.position.x, + self.position.y, + self.heading, + ) for i in range(1, random.randint(3, 4)): - self.add_unit(LST_Mk_II, "LST" + str(i), self.position.x + i * random.randint(800, 1200), self.position.y, self.heading) + self.add_unit( + LST_Mk_II, + "LST" + str(i), + self.position.x + i * random.randint(800, 1200), + self.position.y, + self.heading, + ) - self.get_generated_group().points[0].speed = 20 \ No newline at end of file + self.get_generated_group().points[0].speed = 20 diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 09be5d2c..944f2479 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -109,22 +109,25 @@ class ProposedMission: flights: List[ProposedFlight] def __str__(self) -> str: - flights = ', '.join([str(f) for f in self.flights]) + flights = ", ".join([str(f) for f in self.flights]) return f"{self.location.name}: {flights}" class AircraftAllocator: """Finds suitable aircraft for proposed missions.""" - def __init__(self, closest_airfields: ClosestAirfields, - global_inventory: GlobalAircraftInventory, - is_player: bool) -> None: + def __init__( + self, + closest_airfields: ClosestAirfields, + global_inventory: GlobalAircraftInventory, + is_player: bool, + ) -> None: self.closest_airfields = closest_airfields self.global_inventory = global_inventory self.is_player = is_player def find_aircraft_for_flight( - self, flight: ProposedFlight + self, flight: ProposedFlight ) -> Optional[Tuple[ControlPoint, Type[FlyingType]]]: """Finds aircraft suitable for the given mission. @@ -144,12 +147,12 @@ class AircraftAllocator: on subsequent calls. If the found aircraft are not used, the caller is responsible for returning them to the inventory. """ - return self.find_aircraft_of_type( - flight, aircraft_for_task(flight.task) - ) + return self.find_aircraft_of_type(flight, aircraft_for_task(flight.task)) def find_aircraft_of_type( - self, flight: ProposedFlight, types: List[Type[FlyingType]], + self, + flight: ProposedFlight, + types: List[Type[FlyingType]], ) -> Optional[Tuple[ControlPoint, Type[FlyingType]]]: airfields_in_range = self.closest_airfields.airfields_within( flight.max_distance @@ -171,18 +174,22 @@ class AircraftAllocator: class PackageBuilder: """Builds a Package for the flights it receives.""" - def __init__(self, location: MissionTarget, - closest_airfields: ClosestAirfields, - global_inventory: GlobalAircraftInventory, - is_player: bool, - package_country: str, - start_type: str) -> None: + def __init__( + self, + location: MissionTarget, + closest_airfields: ClosestAirfields, + global_inventory: GlobalAircraftInventory, + is_player: bool, + package_country: str, + start_type: str, + ) -> None: self.closest_airfields = closest_airfields self.is_player = is_player self.package_country = package_country self.package = Package(location) - self.allocator = AircraftAllocator(closest_airfields, global_inventory, - is_player) + self.allocator = AircraftAllocator( + closest_airfields, global_inventory, is_player + ) self.global_inventory = global_inventory self.start_type = start_type @@ -203,14 +210,23 @@ class PackageBuilder: else: start_type = self.start_type - flight = Flight(self.package, self.package_country, aircraft, plan.num_aircraft, plan.task, - start_type, departure=airfield, arrival=airfield, - divert=self.find_divert_field(aircraft, airfield)) + flight = Flight( + self.package, + self.package_country, + aircraft, + plan.num_aircraft, + plan.task, + start_type, + departure=airfield, + arrival=airfield, + divert=self.find_divert_field(aircraft, airfield), + ) self.package.add_flight(flight) return True - def find_divert_field(self, aircraft: Type[FlyingType], - arrival: ControlPoint) -> Optional[ControlPoint]: + def find_divert_field( + self, aircraft: Type[FlyingType], arrival: ControlPoint + ) -> Optional[ControlPoint]: divert_limit = nautical_miles(150) for airfield in self.closest_airfields.airfields_within(divert_limit): if airfield.captured != self.is_player: @@ -323,8 +339,8 @@ class ObjectiveFinder: return self._targets_by_range(self.enemy_ships()) def _targets_by_range( - self, - targets: Iterable[MissionTarget]) -> Iterator[MissionTarget]: + self, targets: Iterable[MissionTarget] + ) -> Iterator[MissionTarget]: target_ranges: List[Tuple[MissionTarget, int]] = [] for target in targets: ranges: List[int] = [] @@ -430,8 +446,9 @@ class ObjectiveFinder: def friendly_control_points(self) -> Iterator[ControlPoint]: """Iterates over all friendly control points.""" - return (c for c in self.game.theater.controlpoints if - c.is_friendly(self.is_player)) + return ( + c for c in self.game.theater.controlpoints if c.is_friendly(self.is_player) + ) def farthest_friendly_control_point(self) -> ControlPoint: """ @@ -451,8 +468,11 @@ class ObjectiveFinder: def enemy_control_points(self) -> Iterator[ControlPoint]: """Iterates over all enemy control points.""" - return (c for c in self.game.theater.controlpoints if - not c.is_friendly(self.is_player)) + return ( + c + for c in self.game.theater.controlpoints + if not c.is_friendly(self.is_player) + ) def all_possible_targets(self) -> Iterator[MissionTarget]: """Iterates over all possible mission targets in the theater. @@ -524,33 +544,46 @@ class CoalitionMissionPlanner: eliminated this turn. """ - #Find farthest, friendly CP for AEWC + # Find farthest, friendly CP for AEWC cp = self.objective_finder.farthest_friendly_control_point() - yield ProposedMission(cp, [ - ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE) - ]) + yield ProposedMission( + cp, [ProposedFlight(FlightType.AEWC, 1, self.MAX_AWEC_RANGE)] + ) # Find friendly CPs within 100 nmi from an enemy airfield, plan CAP. for cp in self.objective_finder.vulnerable_control_points(): # Plan three rounds of CAP to give ~90 minutes coverage. Spacing # these out appropriately is done in stagger_missions. - yield ProposedMission(cp, [ - ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE), - ]) - yield ProposedMission(cp, [ - ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE), - ]) - yield ProposedMission(cp, [ - ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE), - ]) + yield ProposedMission( + cp, + [ + ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE), + ], + ) + yield ProposedMission( + cp, + [ + ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE), + ], + ) + yield ProposedMission( + cp, + [ + ProposedFlight(FlightType.BARCAP, 2, self.MAX_CAP_RANGE), + ], + ) # Find front lines, plan CAS. for front_line in self.objective_finder.front_lines(): - yield ProposedMission(front_line, [ - ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE), - ProposedFlight(FlightType.TARCAP, 2, self.MAX_CAP_RANGE, - EscortType.AirToAir), - ]) + yield ProposedMission( + front_line, + [ + ProposedFlight(FlightType.CAS, 2, self.MAX_CAS_RANGE), + ProposedFlight( + FlightType.TARCAP, 2, self.MAX_CAP_RANGE, EscortType.AirToAir + ), + ], + ) def propose_missions(self) -> Iterator[ProposedMission]: """Identifies and iterates over potential mission in priority order.""" @@ -561,30 +594,46 @@ class CoalitionMissionPlanner: # Find enemy SAM sites with ranges that extend to within 50 nmi of # friendly CPs, front, lines, or objects, plan DEAD. for sam in self.objective_finder.threatening_sams(): - yield ProposedMission(sam, [ - ProposedFlight(FlightType.DEAD, 2, self.MAX_SEAD_RANGE), - # TODO: Max escort range. - ProposedFlight(FlightType.ESCORT, 2, self.MAX_SEAD_RANGE, - EscortType.AirToAir), - ]) + yield ProposedMission( + sam, + [ + ProposedFlight(FlightType.DEAD, 2, self.MAX_SEAD_RANGE), + # TODO: Max escort range. + ProposedFlight( + FlightType.ESCORT, 2, self.MAX_SEAD_RANGE, EscortType.AirToAir + ), + ], + ) for group in self.objective_finder.threatening_ships(): - yield ProposedMission(group, [ - ProposedFlight(FlightType.ANTISHIP, 2, self.MAX_ANTISHIP_RANGE), - # TODO: Max escort range. - ProposedFlight(FlightType.ESCORT, 2, self.MAX_ANTISHIP_RANGE, - EscortType.AirToAir), - ]) + yield ProposedMission( + group, + [ + ProposedFlight(FlightType.ANTISHIP, 2, self.MAX_ANTISHIP_RANGE), + # TODO: Max escort range. + ProposedFlight( + FlightType.ESCORT, + 2, + self.MAX_ANTISHIP_RANGE, + EscortType.AirToAir, + ), + ], + ) for group in self.objective_finder.threatening_vehicle_groups(): - yield ProposedMission(group, [ - ProposedFlight(FlightType.BAI, 2, self.MAX_BAI_RANGE), - # TODO: Max escort range. - ProposedFlight(FlightType.ESCORT, 2, self.MAX_BAI_RANGE, - EscortType.AirToAir), - ProposedFlight(FlightType.SEAD, 2, self.MAX_OCA_RANGE, - EscortType.Sead), - ]) + yield ProposedMission( + group, + [ + ProposedFlight(FlightType.BAI, 2, self.MAX_BAI_RANGE), + # TODO: Max escort range. + ProposedFlight( + FlightType.ESCORT, 2, self.MAX_BAI_RANGE, EscortType.AirToAir + ), + ProposedFlight( + FlightType.SEAD, 2, self.MAX_OCA_RANGE, EscortType.Sead + ), + ], + ) for target in self.objective_finder.oca_targets(min_aircraft=20): flights = [ @@ -593,27 +642,37 @@ class CoalitionMissionPlanner: if self.game.settings.default_start_type == "Cold": # Only schedule if the default start type is Cold. If the player # has set anything else there are no targets to hit. - flights.append(ProposedFlight(FlightType.OCA_AIRCRAFT, 2, - self.MAX_OCA_RANGE)) - flights.extend([ - # TODO: Max escort range. - ProposedFlight(FlightType.ESCORT, 2, self.MAX_OCA_RANGE, - EscortType.AirToAir), - ProposedFlight(FlightType.SEAD, 2, self.MAX_OCA_RANGE, - EscortType.Sead), - ]) + flights.append( + ProposedFlight(FlightType.OCA_AIRCRAFT, 2, self.MAX_OCA_RANGE) + ) + flights.extend( + [ + # TODO: Max escort range. + ProposedFlight( + FlightType.ESCORT, 2, self.MAX_OCA_RANGE, EscortType.AirToAir + ), + ProposedFlight( + FlightType.SEAD, 2, self.MAX_OCA_RANGE, EscortType.Sead + ), + ] + ) yield ProposedMission(target, flights) # Plan strike missions. for target in self.objective_finder.strike_targets(): - yield ProposedMission(target, [ - ProposedFlight(FlightType.STRIKE, 2, self.MAX_STRIKE_RANGE), - # TODO: Max escort range. - ProposedFlight(FlightType.ESCORT, 2, self.MAX_STRIKE_RANGE, - EscortType.AirToAir), - ProposedFlight(FlightType.SEAD, 2, self.MAX_STRIKE_RANGE, - EscortType.Sead), - ]) + yield ProposedMission( + target, + [ + ProposedFlight(FlightType.STRIKE, 2, self.MAX_STRIKE_RANGE), + # TODO: Max escort range. + ProposedFlight( + FlightType.ESCORT, 2, self.MAX_STRIKE_RANGE, EscortType.AirToAir + ), + ProposedFlight( + FlightType.SEAD, 2, self.MAX_STRIKE_RANGE, EscortType.Sead + ), + ], + ) def plan_missions(self) -> None: """Identifies and plans mission for the turn.""" @@ -628,19 +687,23 @@ class CoalitionMissionPlanner: for cp in self.objective_finder.friendly_control_points(): inventory = self.game.aircraft_inventory.for_control_point(cp) for aircraft, available in inventory.all_aircraft: - self.message("Unused aircraft", - f"{available} {aircraft.id} from {cp}") + self.message("Unused aircraft", f"{available} {aircraft.id} from {cp}") - def plan_flight(self, mission: ProposedMission, flight: ProposedFlight, - builder: PackageBuilder, missing_types: Set[FlightType], - for_reserves: bool) -> None: + def plan_flight( + self, + mission: ProposedMission, + flight: ProposedFlight, + builder: PackageBuilder, + missing_types: Set[FlightType], + for_reserves: bool, + ) -> None: if not builder.plan_flight(flight): missing_types.add(flight.task) purchase_order = AircraftProcurementRequest( near=mission.location, range=flight.max_distance, task_capability=flight.task, - number=flight.num_aircraft + number=flight.num_aircraft, ) if for_reserves: # Reserves are planned for critical missions, so prioritize @@ -650,26 +713,28 @@ class CoalitionMissionPlanner: self.procurement_requests.append(purchase_order) def scrub_mission_missing_aircraft( - self, mission: ProposedMission, builder: PackageBuilder, - missing_types: Set[FlightType], - not_attempted: Iterable[ProposedFlight], - reserves: bool) -> None: + self, + mission: ProposedMission, + builder: PackageBuilder, + missing_types: Set[FlightType], + not_attempted: Iterable[ProposedFlight], + reserves: bool, + ) -> None: # Try to plan the rest of the mission just so we can count the missing # types to buy. for flight in not_attempted: self.plan_flight(mission, flight, builder, missing_types, reserves) - missing_types_str = ", ".join( - sorted([t.name for t in missing_types])) + missing_types_str = ", ".join(sorted([t.name for t in missing_types])) builder.release_planned_aircraft() desc = "reserve aircraft" if reserves else "aircraft" self.message( "Insufficient aircraft", f"Not enough {desc} in range for {mission.location.name} " - f"capable of: {missing_types_str}") + f"capable of: {missing_types_str}", + ) - def check_needed_escorts( - self, builder: PackageBuilder) -> Dict[EscortType, bool]: + def check_needed_escorts(self, builder: PackageBuilder) -> Dict[EscortType, bool]: threats = defaultdict(bool) for flight in builder.package.flights: if self.threat_zones.threatened_by_aircraft(flight): @@ -678,8 +743,7 @@ class CoalitionMissionPlanner: threats[EscortType.Sead] = True return threats - def plan_mission(self, mission: ProposedMission, - reserves: bool = False) -> None: + def plan_mission(self, mission: ProposedMission, reserves: bool = False) -> None: """Allocates aircraft for a proposed mission and adds it to the ATO.""" if self.is_player: @@ -693,7 +757,7 @@ class CoalitionMissionPlanner: self.game.aircraft_inventory, self.is_player, package_country, - self.game.settings.default_start_type + self.game.settings.default_start_type, ) # Attempt to plan all the main elements of the mission first. Escorts @@ -707,12 +771,12 @@ class CoalitionMissionPlanner: # If the package does not need escorts they may be pruned. escorts.append(proposed_flight) continue - self.plan_flight(mission, proposed_flight, builder, missing_types, - reserves) + self.plan_flight(mission, proposed_flight, builder, missing_types, reserves) if missing_types: - self.scrub_mission_missing_aircraft(mission, builder, missing_types, - escorts, reserves) + self.scrub_mission_missing_aircraft( + mission, builder, missing_types, escorts, reserves + ) return # Create flight plans for the main flights of the package so we can @@ -721,8 +785,9 @@ class CoalitionMissionPlanner: # flights that will rendezvous with their package will be affected by # the other flights in the package. Escorts will not be able to # contribute to this. - flight_plan_builder = FlightPlanBuilder(self.game, builder.package, - self.is_player) + flight_plan_builder = FlightPlanBuilder( + self.game, builder.package, self.is_player + ) for flight in builder.package.flights: flight_plan_builder.populate_flight_plan(flight) @@ -732,14 +797,14 @@ class CoalitionMissionPlanner: # impossible. assert escort.escort_type is not None if needed_escorts[escort.escort_type]: - self.plan_flight(mission, escort, builder, missing_types, - reserves) + self.plan_flight(mission, escort, builder, missing_types, reserves) # Check again for unavailable aircraft. If the escort was required and # none were found, scrub the mission. if missing_types: - self.scrub_mission_missing_aircraft(mission, builder, missing_types, - escorts, reserves) + self.scrub_mission_missing_aircraft( + mission, builder, missing_types, escorts, reserves + ) return if reserves: @@ -756,8 +821,9 @@ class CoalitionMissionPlanner: self.ato.add_package(package) def stagger_missions(self) -> None: - def start_time_generator(count: int, earliest: int, latest: int, - margin: int) -> Iterator[timedelta]: + def start_time_generator( + count: int, earliest: int, latest: int, margin: int + ) -> Iterator[timedelta]: interval = (latest - earliest) // count for time in range(earliest, latest, interval): error = random.randint(-margin, margin) @@ -768,17 +834,13 @@ class CoalitionMissionPlanner: FlightType.TARCAP, } - previous_cap_end_time: Dict[MissionTarget, timedelta] = defaultdict( - timedelta - ) - non_dca_packages = [p for p in self.ato.packages if - p.primary_task not in dca_types] + previous_cap_end_time: Dict[MissionTarget, timedelta] = defaultdict(timedelta) + non_dca_packages = [ + p for p in self.ato.packages if p.primary_task not in dca_types + ] start_time = start_time_generator( - count=len(non_dca_packages), - earliest=5, - latest=90, - margin=5 + count=len(non_dca_packages), earliest=5, latest=90, margin=5 ) for package in self.ato.packages: tot = TotEstimator(package).earliest_tot() @@ -795,8 +857,7 @@ class CoalitionMissionPlanner: departure_time = package.mission_departure_time # Should be impossible for CAPs if departure_time is None: - logging.error( - f"Could not determine mission end time for {package}") + logging.error(f"Could not determine mission end time for {package}") continue previous_cap_end_time[package.target] = departure_time else: @@ -815,8 +876,6 @@ class CoalitionMissionPlanner: message to the info panel. """ if self.is_player: - self.game.informations.append( - Information(title, text, self.game.turn) - ) + self.game.informations.append(Information(title, text, self.game.turn)) else: logging.info(f"{title}: {text}") diff --git a/gen/flights/ai_flight_planner_db.py b/gen/flights/ai_flight_planner_db.py index a29ac6cd..67a38d3d 100644 --- a/gen/flights/ai_flight_planner_db.py +++ b/gen/flights/ai_flight_planner_db.py @@ -370,11 +370,7 @@ TRANSPORT_CAPABLE = [ UH_1H, ] -DRONES = [ - MQ_9_Reaper, - RQ_1A_Predator, - WingLoong_I -] +DRONES = [MQ_9_Reaper, RQ_1A_Predator, WingLoong_I] AEWC_CAPABLE = [ E_3A, diff --git a/gen/flights/closestairfields.py b/gen/flights/closestairfields.py index 7dfab22f..76662c81 100644 --- a/gen/flights/closestairfields.py +++ b/gen/flights/closestairfields.py @@ -12,8 +12,9 @@ if TYPE_CHECKING: class ClosestAirfields: """Precalculates which control points are closes to the given target.""" - def __init__(self, target: MissionTarget, - all_control_points: List[ControlPoint]) -> None: + def __init__( + self, target: MissionTarget, all_control_points: List[ControlPoint] + ) -> None: self.target = target # This cache is configured once on load, so it's important that it is # complete and deterministic to avoid different behaviors across loads. @@ -52,9 +53,7 @@ class ObjectiveDistanceCache: @classmethod def get_closest_airfields(cls, location: MissionTarget) -> ClosestAirfields: if cls.theater is None: - raise RuntimeError( - "Call ObjectiveDistanceCache.set_theater before using" - ) + raise RuntimeError("Call ObjectiveDistanceCache.set_theater before using") if location.name not in cls.closest_airfields: cls.closest_airfields[location.name] = ClosestAirfields( diff --git a/gen/flights/flight.py b/gen/flights/flight.py index 3cae97df..bfcf9f28 100644 --- a/gen/flights/flight.py +++ b/gen/flights/flight.py @@ -28,6 +28,7 @@ class FlightType(Enum): each flight and thus a part of the ATO, so changing these values will break save compat. """ + TARCAP = "TARCAP" BARCAP = "BARCAP" CAS = "CAS" @@ -48,22 +49,22 @@ class FlightType(Enum): class FlightWaypointType(Enum): - TAKEOFF = 0 # Take off point - ASCEND_POINT = 1 # Ascension point after take off - PATROL = 2 # Patrol point - PATROL_TRACK = 3 # Patrol race track - NAV = 4 # Nav point - INGRESS_STRIKE = 5 # Ingress strike (For generator, means that this should have bombing on next TARGET_POINT points) - INGRESS_SEAD = 6 # Ingress sead (For generator, means that this should attack groups on TARGET_GROUP_LOC points) - INGRESS_CAS = 7 # Ingress cas (should start CAS task) - CAS = 8 # Should do CAS there - EGRESS = 9 # Should stop attack - DESCENT_POINT = 10 # Should start descending to pattern alt - LANDING_POINT = 11 # Should land there - TARGET_POINT = 12 # A target building or static object, position - TARGET_GROUP_LOC = 13 # A target group approximate location - TARGET_SHIP = 14 # A target ship known location - CUSTOM = 15 # User waypoint (no specific behaviour) + TAKEOFF = 0 # Take off point + ASCEND_POINT = 1 # Ascension point after take off + PATROL = 2 # Patrol point + PATROL_TRACK = 3 # Patrol race track + NAV = 4 # Nav point + INGRESS_STRIKE = 5 # Ingress strike (For generator, means that this should have bombing on next TARGET_POINT points) + INGRESS_SEAD = 6 # Ingress sead (For generator, means that this should attack groups on TARGET_GROUP_LOC points) + INGRESS_CAS = 7 # Ingress cas (should start CAS task) + CAS = 8 # Should do CAS there + EGRESS = 9 # Should stop attack + DESCENT_POINT = 10 # Should start descending to pattern alt + LANDING_POINT = 11 # Should land there + TARGET_POINT = 12 # A target building or static object, position + TARGET_GROUP_LOC = 13 # A target group approximate location + TARGET_SHIP = 14 # A target ship known location + CUSTOM = 15 # User waypoint (no specific behaviour) JOIN = 16 SPLIT = 17 LOITER = 18 @@ -77,9 +78,13 @@ class FlightWaypointType(Enum): class FlightWaypoint: - - def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float, - alt: Distance = meters(0)) -> None: + def __init__( + self, + waypoint_type: FlightWaypointType, + x: float, + y: float, + alt: Distance = meters(0), + ) -> None: """Creates a flight waypoint. Args: @@ -117,10 +122,13 @@ class FlightWaypoint: return Point(self.x, self.y) @classmethod - def from_pydcs(cls, point: MovingPoint, - from_cp: ControlPoint) -> "FlightWaypoint": - waypoint = FlightWaypoint(FlightWaypointType.NAV, point.position.x, - point.position.y, meters(point.alt)) + def from_pydcs(cls, point: MovingPoint, from_cp: ControlPoint) -> "FlightWaypoint": + waypoint = FlightWaypoint( + FlightWaypointType.NAV, + point.position.x, + point.position.y, + meters(point.alt), + ) waypoint.alt_type = point.alt_type # Other actions exist... but none of them *should* be the first # waypoint for a flight. @@ -144,12 +152,19 @@ class FlightWaypoint: class Flight: - - def __init__(self, package: Package, country: str, unit_type: Type[FlyingType], - count: int, flight_type: FlightType, start_type: str, - departure: ControlPoint, arrival: ControlPoint, - divert: Optional[ControlPoint], - custom_name: Optional[str] = None) -> None: + def __init__( + self, + package: Package, + country: str, + unit_type: Type[FlyingType], + count: int, + flight_type: FlightType, + start_type: str, + departure: ControlPoint, + arrival: ControlPoint, + divert: Optional[ControlPoint], + custom_name: Optional[str] = None, + ) -> None: self.package = package self.country = country self.unit_type = unit_type @@ -170,10 +185,9 @@ class Flight: # 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=[] + package=package, flight=self, custom_waypoints=[] ) @property @@ -191,7 +205,7 @@ class Flight: return f"[{self.flight_type}] {self.count} x {name}" def __str__(self): - name = db.unit_get_expanded_info(self.country, self.unit_type, 'name') + name = db.unit_get_expanded_info(self.country, self.unit_type, "name") if self.custom_name: return f"{self.custom_name} {self.count} x {name}" return f"[{self.flight_type}] {self.count} x {name}" diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index 743eeaaa..80a577ed 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -15,12 +15,7 @@ from datetime import timedelta from functools import cached_property from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple -from dcs.planes import ( - E_3A, - E_2C, - A_50, - KJ_2000 -) +from dcs.planes import E_3A, E_2C, A_50, KJ_2000 from dcs.mapping import Point from dcs.unit import Unit @@ -82,7 +77,7 @@ class FlightPlan: raise NotImplementedError def edges( - self, until: Optional[FlightWaypoint] = None + self, until: Optional[FlightWaypoint] = None ) -> Iterator[Tuple[FlightWaypoint, FlightWaypoint]]: """A list of all paths between waypoints, in order.""" waypoints = self.waypoints @@ -93,8 +88,9 @@ class FlightPlan: return zip(self.waypoints[:last_index], self.waypoints[1:last_index]) - def best_speed_between_waypoints(self, a: FlightWaypoint, - b: FlightWaypoint) -> Speed: + def best_speed_between_waypoints( + self, a: FlightWaypoint, b: FlightWaypoint + ) -> Speed: """Desired ground speed between points a and b.""" factor = 1.0 if b.waypoint_type == FlightWaypointType.ASCEND_POINT: @@ -115,8 +111,7 @@ class FlightPlan: # near 2000 ft MSL. return GroundSpeed.for_flight(self.flight, min(a.alt, b.alt)) * factor - def speed_between_waypoints(self, a: FlightWaypoint, - b: FlightWaypoint) -> Speed: + def speed_between_waypoints(self, a: FlightWaypoint, b: FlightWaypoint) -> Speed: return self.best_speed_between_waypoints(a, b) @property @@ -131,8 +126,7 @@ class FlightPlan: @cached_property def bingo_fuel(self) -> int: - """Bingo fuel value for the FlightPlan - """ + """Bingo fuel value for the FlightPlan""" distance_to_arrival = self.max_distance_from(self.flight.arrival) bingo = 1000.0 # Minimum Emergency Fuel @@ -149,8 +143,7 @@ class FlightPlan: @cached_property def joker_fuel(self) -> int: - """Joker fuel value for the FlightPlan - """ + """Joker fuel value for the FlightPlan""" return self.bingo_fuel + 1000 def max_distance_from(self, cp: ControlPoint) -> Distance: @@ -159,8 +152,9 @@ class FlightPlan: """ if not self.waypoints: return meters(0) - return max([meters(cp.position.distance_to_point(w.position)) for w in - self.waypoints]) + return max( + [meters(cp.position.distance_to_point(w.position)) for w in self.waypoints] + ) @property def tot_offset(self) -> timedelta: @@ -171,30 +165,30 @@ class FlightPlan: """ return timedelta() - def _travel_time_to_waypoint( - self, destination: FlightWaypoint) -> timedelta: + def _travel_time_to_waypoint(self, destination: FlightWaypoint) -> timedelta: total = timedelta() if destination not in self.waypoints: raise PlanningError( f"Did not find destination waypoint {destination} in " - f"waypoints for {self.flight}") + f"waypoints for {self.flight}" + ) for previous_waypoint, waypoint in self.edges(until=destination): - total += self.travel_time_between_waypoints(previous_waypoint, - waypoint) + total += self.travel_time_between_waypoints(previous_waypoint, waypoint) return total - def travel_time_between_waypoints(self, a: FlightWaypoint, - b: FlightWaypoint) -> timedelta: - return TravelTime.between_points(a.position, b.position, - self.speed_between_waypoints(a, b)) + def travel_time_between_waypoints( + self, a: FlightWaypoint, b: FlightWaypoint + ) -> timedelta: + return TravelTime.between_points( + a.position, b.position, self.speed_between_waypoints(a, b) + ) def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: raise NotImplementedError - def depart_time_for_waypoint( - self, waypoint: FlightWaypoint) -> Optional[timedelta]: + def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: raise NotImplementedError def request_escort_at(self) -> Optional[FlightWaypoint]: @@ -219,8 +213,7 @@ class FlightPlan: if takeoff_time is None: return None - start_time = (takeoff_time - self.estimate_startup() - - self.estimate_ground_ops()) + start_time = takeoff_time - self.estimate_startup() - self.estimate_ground_ops() # In case FP math has given us some barely below zero time, round to # zero. @@ -276,14 +269,14 @@ class LoiterFlightPlan(FlightPlan): def push_time(self) -> timedelta: raise NotImplementedError - def depart_time_for_waypoint( - self, waypoint: FlightWaypoint) -> Optional[timedelta]: + def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: if waypoint == self.hold: return self.push_time return None - def travel_time_between_waypoints(self, a: FlightWaypoint, - b: FlightWaypoint) -> timedelta: + def travel_time_between_waypoints( + self, a: FlightWaypoint, b: FlightWaypoint + ) -> timedelta: travel_time = super().travel_time_between_waypoints(a, b) if a != self.hold: return travel_time @@ -328,12 +321,12 @@ class FormationFlightPlan(LoiterFlightPlan): speeds = [] for previous_waypoint, waypoint in self.edges(): if waypoint in self.package_speed_waypoints: - speeds.append(self.best_speed_between_waypoints( - previous_waypoint, waypoint)) + speeds.append( + self.best_speed_between_waypoints(previous_waypoint, waypoint) + ) return min(speeds) - def speed_between_waypoints(self, a: FlightWaypoint, - b: FlightWaypoint) -> Speed: + def speed_between_waypoints(self, a: FlightWaypoint, b: FlightWaypoint) -> Speed: if b in self.package_speed_waypoints: # Should be impossible, as any package with at least one # FormationFlightPlan flight needs a formation speed. @@ -366,7 +359,7 @@ class FormationFlightPlan(LoiterFlightPlan): return self.join_time - TravelTime.between_points( self.hold.position, self.join.position, - GroundSpeed.for_flight(self.flight, self.hold.alt) + GroundSpeed.for_flight(self.flight, self.hold.alt), ) @property @@ -406,8 +399,7 @@ class PatrollingFlightPlan(FlightPlan): return self.patrol_start_time return None - def depart_time_for_waypoint( - self, waypoint: FlightWaypoint) -> Optional[timedelta]: + def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: if waypoint == self.patrol_end: return self.patrol_end_time return None @@ -497,8 +489,7 @@ class TarCapFlightPlan(PatrollingFlightPlan): def tot_offset(self) -> timedelta: return -self.lead_time - def depart_time_for_waypoint( - self, waypoint: FlightWaypoint) -> Optional[timedelta]: + def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: if waypoint == self.patrol_end: return self.patrol_end_time return super().depart_time_for_waypoint(waypoint) @@ -549,13 +540,12 @@ class StrikeFlightPlan(FormationFlightPlan): @property def package_speed_waypoints(self) -> Set[FlightWaypoint]: return { - self.ingress, - self.egress, - self.split, - } | set(self.targets) + self.ingress, + self.egress, + self.split, + } | set(self.targets) - def speed_between_waypoints(self, a: FlightWaypoint, - b: FlightWaypoint) -> Speed: + def speed_between_waypoints(self, a: FlightWaypoint, b: FlightWaypoint) -> Speed: # FlightWaypoint is only comparable by identity, so adding # target_area_waypoint to package_speed_waypoints is useless. if b.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC: @@ -571,10 +561,12 @@ class StrikeFlightPlan(FormationFlightPlan): @property def target_area_waypoint(self) -> FlightWaypoint: - return FlightWaypoint(FlightWaypointType.TARGET_GROUP_LOC, - self.package.target.position.x, - self.package.target.position.y, - meters(0)) + return FlightWaypoint( + FlightWaypointType.TARGET_GROUP_LOC, + self.package.target.position.x, + self.package.target.position.y, + meters(0), + ) @property def travel_time_to_target(self) -> timedelta: @@ -588,14 +580,15 @@ class StrikeFlightPlan(FormationFlightPlan): # package we need to use the travel time to the same position as # the others. total += self.travel_time_between_waypoints( - previous_waypoint, self.target_area_waypoint) + previous_waypoint, self.target_area_waypoint + ) break - total += self.travel_time_between_waypoints(previous_waypoint, - waypoint) + total += self.travel_time_between_waypoints(previous_waypoint, waypoint) else: raise PlanningError( f"Did not find destination waypoint {destination} in " - f"waypoints for {self.flight}") + f"waypoints for {self.flight}" + ) return total @property @@ -604,28 +597,28 @@ class StrikeFlightPlan(FormationFlightPlan): @property def join_time(self) -> timedelta: - travel_time = self.travel_time_between_waypoints( - self.join, self.ingress) + travel_time = self.travel_time_between_waypoints(self.join, self.ingress) return self.ingress_time - travel_time @property def split_time(self) -> timedelta: - travel_time = self.travel_time_between_waypoints( - self.egress, self.split) + travel_time = self.travel_time_between_waypoints(self.egress, self.split) return self.egress_time + travel_time @property def ingress_time(self) -> timedelta: tot = self.package.time_over_target travel_time = self.travel_time_between_waypoints( - self.ingress, self.target_area_waypoint) + self.ingress, self.target_area_waypoint + ) return tot - travel_time @property def egress_time(self) -> timedelta: tot = self.package.time_over_target travel_time = self.travel_time_between_waypoints( - self.target_area_waypoint, self.egress) + self.target_area_waypoint, self.egress + ) return tot + travel_time def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: @@ -671,7 +664,8 @@ class SweepFlightPlan(LoiterFlightPlan): @property def sweep_start_time(self) -> timedelta: travel_time = self.travel_time_between_waypoints( - self.sweep_start, self.sweep_end) + self.sweep_start, self.sweep_end + ) return self.sweep_end_time - travel_time @property @@ -685,8 +679,7 @@ class SweepFlightPlan(LoiterFlightPlan): return self.sweep_end_time return None - def depart_time_for_waypoint( - self, waypoint: FlightWaypoint) -> Optional[timedelta]: + def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: if waypoint == self.hold: return self.push_time return None @@ -696,7 +689,7 @@ class SweepFlightPlan(LoiterFlightPlan): return self.sweep_end_time - TravelTime.between_points( self.hold.position, self.sweep_end.position, - GroundSpeed.for_flight(self.flight, self.hold.alt) + GroundSpeed.for_flight(self.flight, self.hold.alt), ) def mission_departure_time(self) -> timedelta: @@ -763,8 +756,7 @@ class CustomFlightPlan(FlightPlan): return self.package.time_over_target return None - def depart_time_for_waypoint( - self, waypoint: FlightWaypoint) -> Optional[timedelta]: + def depart_time_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: return None @property @@ -794,9 +786,11 @@ class FlightPlanBuilder: self.threat_zones = self.game.threat_zone_for(not self.is_player) def populate_flight_plan( - self, flight: Flight, - # TODO: Custom targets should be an attribute of the flight. - custom_targets: Optional[List[Unit]] = None) -> None: + self, + flight: Flight, + # TODO: Custom targets should be an attribute of the flight. + custom_targets: Optional[List[Unit]] = None, + ) -> None: """Creates a default flight plan for the given mission.""" if flight not in self.package.flights: raise RuntimeError("Flight must be a part of the package") @@ -805,8 +799,8 @@ class FlightPlanBuilder: flight.flight_plan = self.generate_flight_plan(flight, custom_targets) def generate_flight_plan( - self, flight: Flight, - custom_targets: Optional[List[Unit]]) -> FlightPlan: + self, flight: Flight, custom_targets: Optional[List[Unit]] + ) -> FlightPlan: # TODO: Flesh out mission types. task = flight.flight_type if task == FlightType.ANTISHIP: @@ -835,8 +829,7 @@ class FlightPlanBuilder: return self.generate_tarcap(flight) elif task == FlightType.AEWC: return self.generate_aewc(flight) - raise PlanningError( - f"{task} flight plan generation not implemented") + raise PlanningError(f"{task} flight plan generation not implemented") def regenerate_package_waypoints(self) -> None: # The simple case is where the target is greater than the ingress @@ -873,6 +866,7 @@ class FlightPlanBuilder: # | | | | # +--------------+ +---------------+ from gen.ato import PackageWaypoints + target = self.package.target.position join_point = self.preferred_join_point() @@ -906,10 +900,9 @@ class FlightPlanBuilder: def legacy_package_waypoints_impl(self) -> None: from gen.ato import PackageWaypoints - ingress_point = self._ingress_point( - self._target_heading_to_package_airfield()) - egress_point = self._egress_point( - self._target_heading_to_package_airfield()) + + ingress_point = self._ingress_point(self._target_heading_to_package_airfield()) + egress_point = self._egress_point(self._target_heading_to_package_airfield()) join_point = self._rendezvous_point(ingress_point) split_point = self._rendezvous_point(egress_point) self.package.waypoints = PackageWaypoints( @@ -921,7 +914,8 @@ class FlightPlanBuilder: def preferred_join_point(self) -> Optional[Point]: path = self.game.navmesh_for(self.is_player).shortest_path( - self.package_airfield().position, self.package.target.position) + self.package_airfield().position, self.package.target.position + ) for point in reversed(path): if not self.threat_zones.threatened(point): return point @@ -961,16 +955,16 @@ class FlightPlanBuilder: targets.append(StrikeTarget(building.category, building)) - return self.strike_flightplan(flight, location, - FlightWaypointType.INGRESS_STRIKE, - targets) + return self.strike_flightplan( + flight, location, FlightWaypointType.INGRESS_STRIKE, targets + ) def generate_aewc(self, flight: Flight) -> AwacsFlightPlan: """Generate a AWACS flight at a given location. - Args: - flight: The flight to generate the flight plan for. - """ + Args: + flight: The flight to generate the flight plan for. + """ location = self.package.target start = self.aewc_orbit(location) @@ -994,10 +988,12 @@ class FlightPlanBuilder: package=self.package, flight=flight, takeoff=builder.takeoff(flight.departure), - nav_to=builder.nav_path(flight.departure.position, start.position, - patrol_alt), - nav_from=builder.nav_path(start.position, flight.arrival.position, - patrol_alt), + nav_to=builder.nav_path( + flight.departure.position, start.position, patrol_alt + ), + nav_from=builder.nav_path( + start.position, flight.arrival.position, patrol_alt + ), land=builder.land(flight.arrival), divert=builder.divert(flight.divert), hold=start, @@ -1017,11 +1013,11 @@ class FlightPlanBuilder: targets: List[StrikeTarget] = [] for group in location.groups: - targets.append( - StrikeTarget(f"{group.name} at {location.name}", group)) + targets.append(StrikeTarget(f"{group.name} at {location.name}", group)) - return self.strike_flightplan(flight, location, - FlightWaypointType.INGRESS_BAI, targets) + return self.strike_flightplan( + flight, location, FlightWaypointType.INGRESS_BAI, targets + ) def generate_anti_ship(self, flight: Flight) -> StrikeFlightPlan: """Generates an anti-ship flight plan. @@ -1043,11 +1039,11 @@ class FlightPlanBuilder: targets: List[StrikeTarget] = [] for group in location.groups: - targets.append( - StrikeTarget(f"{group.name} at {location.name}", group)) + targets.append(StrikeTarget(f"{group.name} at {location.name}", group)) - return self.strike_flightplan(flight, location, - FlightWaypointType.INGRESS_BAI, targets) + return self.strike_flightplan( + flight, location, FlightWaypointType.INGRESS_BAI, targets + ) def generate_barcap(self, flight: Flight) -> BarCapFlightPlan: """Generate a BARCAP flight at a given location. @@ -1061,10 +1057,12 @@ class FlightPlanBuilder: raise InvalidObjectiveLocation(flight.flight_type, location) start, end = self.racetrack_for_objective(location, barcap=True) - patrol_alt = meters(random.randint( - int(self.doctrine.min_patrol_altitude.meters), - int(self.doctrine.max_patrol_altitude.meters) - )) + patrol_alt = meters( + random.randint( + int(self.doctrine.min_patrol_altitude.meters), + int(self.doctrine.max_patrol_altitude.meters), + ) + ) builder = WaypointBuilder(flight, self.game, self.is_player) start, end = builder.race_track(start, end, patrol_alt) @@ -1075,14 +1073,16 @@ class FlightPlanBuilder: patrol_duration=self.doctrine.cap_duration, engagement_distance=self.doctrine.cap_engagement_range, takeoff=builder.takeoff(flight.departure), - nav_to=builder.nav_path(flight.departure.position, start.position, - patrol_alt), - nav_from=builder.nav_path(end.position, flight.arrival.position, - patrol_alt), + nav_to=builder.nav_path( + flight.departure.position, start.position, patrol_alt + ), + nav_from=builder.nav_path( + end.position, flight.arrival.position, patrol_alt + ), patrol_start=start, patrol_end=end, land=builder.land(flight.arrival), - divert=builder.divert(flight.divert) + divert=builder.divert(flight.divert), ) def generate_sweep(self, flight: Flight) -> SweepFlightPlan: @@ -1095,12 +1095,10 @@ class FlightPlanBuilder: target = self.package.target.position heading = self.package.waypoints.join.heading_between_point(target) - start = target.point_from_heading(heading, - -self.doctrine.sweep_distance.meters) + start = target.point_from_heading(heading, -self.doctrine.sweep_distance.meters) builder = WaypointBuilder(flight, self.game, self.is_player) - start, end = builder.sweep(start, target, - self.doctrine.ingress_altitude) + start, end = builder.sweep(start, target, self.doctrine.ingress_altitude) hold = builder.hold(self._hold_point(flight)) @@ -1111,18 +1109,21 @@ class FlightPlanBuilder: takeoff=builder.takeoff(flight.departure), hold=hold, hold_duration=timedelta(minutes=5), - nav_to=builder.nav_path(hold.position, start.position, - self.doctrine.ingress_altitude), - nav_from=builder.nav_path(end.position, flight.arrival.position, - self.doctrine.ingress_altitude), + nav_to=builder.nav_path( + hold.position, start.position, self.doctrine.ingress_altitude + ), + nav_from=builder.nav_path( + end.position, flight.arrival.position, self.doctrine.ingress_altitude + ), sweep_start=start, sweep_end=end, land=builder.land(flight.arrival), - divert=builder.divert(flight.divert) + divert=builder.divert(flight.divert), ) - def racetrack_for_objective(self, location: MissionTarget, - barcap: bool) -> Tuple[Point, Point]: + def racetrack_for_objective( + self, location: MissionTarget, barcap: bool + ) -> Tuple[Point, Point]: closest_cache = ObjectiveDistanceCache.get_closest_airfields(location) for airfield in closest_cache.operational_airfields: # If the mission is a BARCAP of an enemy airfield, find the *next* @@ -1135,20 +1136,21 @@ class FlightPlanBuilder: else: raise PlanningError("Could not find any enemy airfields") - heading = location.position.heading_between_point( - closest_airfield.position - ) + heading = location.position.heading_between_point(closest_airfield.position) - position = ShapelyPoint(self.package.target.position.x, - self.package.target.position.y) + position = ShapelyPoint( + self.package.target.position.x, self.package.target.position.y + ) if barcap: # BARCAPs should remain far enough back from the enemy that their # commit range does not enter the enemy's threat zone. Include a 5nm # buffer. - distance_to_no_fly = meters( - position.distance(self.threat_zones.all) - ) - self.doctrine.cap_engagement_range - nautical_miles(5) + distance_to_no_fly = ( + meters(position.distance(self.threat_zones.all)) + - self.doctrine.cap_engagement_range + - nautical_miles(5) + ) else: # Other race tracks (TARCAPs, currently) just try to keep some # distance from the nearest enemy airbase, but since they are by @@ -1158,22 +1160,24 @@ class FlightPlanBuilder: distance_to_airfield = meters( closest_airfield.position.distance_to_point( self.package.target.position - )) + ) + ) distance_to_no_fly = distance_to_airfield - min_distance_from_enemy - min_cap_distance = min(self.doctrine.cap_min_distance_from_cp, - distance_to_no_fly) - max_cap_distance = min(self.doctrine.cap_max_distance_from_cp, - distance_to_no_fly) + min_cap_distance = min( + self.doctrine.cap_min_distance_from_cp, distance_to_no_fly + ) + max_cap_distance = min( + self.doctrine.cap_max_distance_from_cp, distance_to_no_fly + ) end = location.position.point_from_heading( heading, - random.randint(int(min_cap_distance.meters), - int(max_cap_distance.meters)) + random.randint(int(min_cap_distance.meters), int(max_cap_distance.meters)), ) diameter = random.randint( int(self.doctrine.cap_min_track_length.meters), - int(self.doctrine.cap_max_track_length.meters) + int(self.doctrine.cap_max_track_length.meters), ) start = end.point_from_heading(heading - 180, diameter) return start, end @@ -1185,13 +1189,11 @@ class FlightPlanBuilder: # Place this either over the target or as close as possible outside the # threat zone: https://github.com/Khopa/dcs_liberation/issues/842. heading = location.position.heading_between_point(closest_airfield.position) - return location.position.point_from_heading( - heading, - 5000 - ) + return location.position.point_from_heading(heading, 5000) - def racetrack_for_frontline(self, origin: Point, - front_line: FrontLine) -> Tuple[Point, Point]: + def racetrack_for_frontline( + self, origin: Point, front_line: FrontLine + ) -> Tuple[Point, Point]: ally_cp, enemy_cp = front_line.control_points # Find targets waypoints @@ -1200,8 +1202,10 @@ class FlightPlanBuilder: ) center = ingress.point_from_heading(heading, distance / 2) orbit_center = center.point_from_heading( - heading - 90, random.randint(int(nautical_miles(6).meters), - int(nautical_miles(15).meters)) + heading - 90, + random.randint( + int(nautical_miles(6).meters), int(nautical_miles(15).meters) + ), ) combat_width = distance / 2 @@ -1227,18 +1231,21 @@ class FlightPlanBuilder: location = self.package.target patrol_alt = meters( - random.randint(int(self.doctrine.min_patrol_altitude.meters), - int(self.doctrine.max_patrol_altitude.meters))) + random.randint( + int(self.doctrine.min_patrol_altitude.meters), + int(self.doctrine.max_patrol_altitude.meters), + ) + ) # Create points builder = WaypointBuilder(flight, self.game, self.is_player) if isinstance(location, FrontLine): orbit0p, orbit1p = self.racetrack_for_frontline( - flight.departure.position, location) + flight.departure.position, location + ) else: - orbit0p, orbit1p = self.racetrack_for_objective(location, - barcap=False) + orbit0p, orbit1p = self.racetrack_for_objective(location, barcap=False) start, end = builder.race_track(orbit0p, orbit1p, patrol_alt) return TarCapFlightPlan( @@ -1252,18 +1259,17 @@ class FlightPlanBuilder: patrol_duration=self.doctrine.cap_duration, engagement_distance=self.doctrine.cap_engagement_range, takeoff=builder.takeoff(flight.departure), - nav_to=builder.nav_path(flight.departure.position, orbit0p, - patrol_alt), - nav_from=builder.nav_path(orbit1p, flight.arrival.position, - patrol_alt), + nav_to=builder.nav_path(flight.departure.position, orbit0p, patrol_alt), + nav_from=builder.nav_path(orbit1p, flight.arrival.position, patrol_alt), patrol_start=start, patrol_end=end, land=builder.land(flight.arrival), - divert=builder.divert(flight.divert) + divert=builder.divert(flight.divert), ) - def generate_dead(self, flight: Flight, - custom_targets: Optional[List[Unit]]) -> StrikeFlightPlan: + def generate_dead( + self, flight: Flight, custom_targets: Optional[List[Unit]] + ) -> StrikeFlightPlan: """Generate a DEAD flight at a given location. Args: @@ -1276,7 +1282,8 @@ class FlightPlanBuilder: is_sam = isinstance(location, SamGroundObject) if not is_ewr and not is_sam: logging.exception( - f"Invalid Objective Location for DEAD flight {flight=} at {location=}") + f"Invalid Objective Location for DEAD flight {flight=} at {location=}" + ) raise InvalidObjectiveLocation(flight.flight_type, location) # TODO: Unify these. @@ -1288,8 +1295,9 @@ class FlightPlanBuilder: for target in custom_targets: targets.append(StrikeTarget(location.name, target)) - return self.strike_flightplan(flight, location, - FlightWaypointType.INGRESS_DEAD, targets) + return self.strike_flightplan( + flight, location, FlightWaypointType.INGRESS_DEAD, targets + ) def generate_oca_strike(self, flight: Flight) -> StrikeFlightPlan: """Generate an OCA Strike flight plan at a given location. @@ -1302,11 +1310,13 @@ class FlightPlanBuilder: if not isinstance(location, Airfield): logging.exception( f"Invalid Objective Location for OCA Strike flight " - f"{flight=} at {location=}.") + f"{flight=} at {location=}." + ) raise InvalidObjectiveLocation(flight.flight_type, location) - return self.strike_flightplan(flight, location, - FlightWaypointType.INGRESS_OCA_AIRCRAFT) + return self.strike_flightplan( + flight, location, FlightWaypointType.INGRESS_OCA_AIRCRAFT + ) def generate_runway_attack(self, flight: Flight) -> StrikeFlightPlan: """Generate a runway attack flight plan at a given location. @@ -1319,14 +1329,17 @@ class FlightPlanBuilder: if not isinstance(location, Airfield): logging.exception( f"Invalid Objective Location for runway bombing flight " - f"{flight=} at {location=}.") + f"{flight=} at {location=}." + ) raise InvalidObjectiveLocation(flight.flight_type, location) - return self.strike_flightplan(flight, location, - FlightWaypointType.INGRESS_OCA_RUNWAY) + return self.strike_flightplan( + flight, location, FlightWaypointType.INGRESS_OCA_RUNWAY + ) - def generate_sead(self, flight: Flight, - custom_targets: Optional[List[Unit]]) -> StrikeFlightPlan: + def generate_sead( + self, flight: Flight, custom_targets: Optional[List[Unit]] + ) -> StrikeFlightPlan: """Generate a SEAD flight at a given location. Args: @@ -1344,16 +1357,19 @@ class FlightPlanBuilder: for target in custom_targets: targets.append(StrikeTarget(location.name, target)) - return self.strike_flightplan(flight, location, - FlightWaypointType.INGRESS_SEAD, targets) + return self.strike_flightplan( + flight, location, FlightWaypointType.INGRESS_SEAD, targets + ) def generate_escort(self, flight: Flight) -> StrikeFlightPlan: assert self.package.waypoints is not None builder = WaypointBuilder(flight, self.game, self.is_player) ingress, target, egress = builder.escort( - self.package.waypoints.ingress, self.package.target, - self.package.waypoints.egress) + self.package.waypoints.ingress, + self.package.target, + self.package.waypoints.egress, + ) hold = builder.hold(self._hold_point(flight)) join = builder.join(self.package.waypoints.join) split = builder.split(self.package.waypoints.split) @@ -1364,17 +1380,19 @@ class FlightPlanBuilder: takeoff=builder.takeoff(flight.departure), hold=hold, hold_duration=timedelta(minutes=5), - nav_to=builder.nav_path(hold.position, join.position, - self.doctrine.ingress_altitude), + nav_to=builder.nav_path( + hold.position, join.position, self.doctrine.ingress_altitude + ), join=join, ingress=ingress, targets=[target], egress=egress, split=split, - nav_from=builder.nav_path(split.position, flight.arrival.position, - self.doctrine.ingress_altitude), + nav_from=builder.nav_path( + split.position, flight.arrival.position, self.doctrine.ingress_altitude + ), land=builder.land(flight.arrival), - divert=builder.divert(flight.divert) + divert=builder.divert(flight.divert), ) def generate_cas(self, flight: Flight) -> CasFlightPlan: @@ -1389,8 +1407,7 @@ class FlightPlanBuilder: raise InvalidObjectiveLocation(flight.flight_type, location) ingress, heading, distance = Conflict.frontline_vector( - location.control_points[0], location.control_points[1], - self.game.theater + location.control_points[0], location.control_points[1], self.game.theater ) center = ingress.point_from_heading(heading, distance / 2) egress = ingress.point_from_heading(heading, distance) @@ -1407,22 +1424,26 @@ class FlightPlanBuilder: flight=flight, patrol_duration=self.doctrine.cas_duration, takeoff=builder.takeoff(flight.departure), - nav_to=builder.nav_path(flight.departure.position, ingress, - self.doctrine.ingress_altitude), - nav_from=builder.nav_path(egress, flight.arrival.position, - self.doctrine.ingress_altitude), - patrol_start=builder.ingress(FlightWaypointType.INGRESS_CAS, - ingress, location), + nav_to=builder.nav_path( + flight.departure.position, ingress, self.doctrine.ingress_altitude + ), + nav_from=builder.nav_path( + egress, flight.arrival.position, self.doctrine.ingress_altitude + ), + patrol_start=builder.ingress( + FlightWaypointType.INGRESS_CAS, ingress, location + ), engagement_distance=meters(FRONTLINE_LENGTH) / 2, target=builder.cas(center), patrol_end=builder.egress(egress, location), land=builder.land(flight.arrival), - divert=builder.divert(flight.divert) + divert=builder.divert(flight.divert), ) @staticmethod - def target_waypoint(flight: Flight, builder: WaypointBuilder, - target: StrikeTarget) -> FlightWaypoint: + def target_waypoint( + flight: Flight, builder: WaypointBuilder, target: StrikeTarget + ) -> FlightWaypoint: if flight.flight_type in {FlightType.ANTISHIP, FlightType.BAI}: return builder.bai_group(target) elif flight.flight_type == FlightType.DEAD: @@ -1433,8 +1454,9 @@ class FlightPlanBuilder: return builder.strike_point(target) @staticmethod - def target_area_waypoint(flight: Flight, location: MissionTarget, - builder: WaypointBuilder) -> FlightWaypoint: + def target_area_waypoint( + flight: Flight, location: MissionTarget, builder: WaypointBuilder + ) -> FlightWaypoint: if flight.flight_type == FlightType.DEAD: return builder.dead_area(location) elif flight.flight_type == FlightType.SEAD: @@ -1455,12 +1477,14 @@ class FlightPlanBuilder: # If the origin airfield is closer to the target than the join # point, plan the hold point such that it retreats from the origin # airfield. - return join.point_from_heading(target.heading_between_point(origin), - self.doctrine.push_distance.meters) + return join.point_from_heading( + target.heading_between_point(origin), self.doctrine.push_distance.meters + ) heading_to_join = origin.heading_between_point(join) hold_point = origin.point_from_heading( - heading_to_join, self.doctrine.push_distance.meters) + heading_to_join, self.doctrine.push_distance.meters + ) hold_distance = meters(hold_point.distance_to_point(join)) if hold_distance >= self.doctrine.push_distance: # Hold point is between the origin airfield and the join point and @@ -1474,26 +1498,27 @@ class FlightPlanBuilder: # properly. origin_to_join = origin.distance_to_point(join) cos_theta = ( - (self.doctrine.hold_distance.meters ** 2 + - origin_to_join ** 2 - - self.doctrine.join_distance.meters ** 2) / - (2 * self.doctrine.hold_distance.meters * origin_to_join) - ) + self.doctrine.hold_distance.meters ** 2 + + origin_to_join ** 2 + - self.doctrine.join_distance.meters ** 2 + ) / (2 * self.doctrine.hold_distance.meters * origin_to_join) try: theta = math.acos(cos_theta) except ValueError: # No solution that maintains hold and join distances. Extend the # hold point away from the target. return origin.point_from_heading( - target.heading_between_point(origin), - self.doctrine.hold_distance.meters) + target.heading_between_point(origin), self.doctrine.hold_distance.meters + ) - return origin.point_from_heading(heading_to_join - theta, - self.doctrine.hold_distance.meters) + return origin.point_from_heading( + heading_to_join - theta, self.doctrine.hold_distance.meters + ) # TODO: Make a model for the waypoint builder and use that in the UI. - def generate_rtb_waypoint(self, flight: Flight, - arrival: ControlPoint) -> FlightWaypoint: + def generate_rtb_waypoint( + self, flight: Flight, arrival: ControlPoint + ) -> FlightWaypoint: """Generate RTB landing point. Args: @@ -1504,20 +1529,23 @@ class FlightPlanBuilder: return builder.land(arrival) def strike_flightplan( - self, flight: Flight, location: MissionTarget, - ingress_type: FlightWaypointType, - targets: Optional[List[StrikeTarget]] = None) -> StrikeFlightPlan: + self, + flight: Flight, + location: MissionTarget, + ingress_type: FlightWaypointType, + targets: Optional[List[StrikeTarget]] = None, + ) -> StrikeFlightPlan: assert self.package.waypoints is not None builder = WaypointBuilder(flight, self.game, self.is_player, targets) target_waypoints: List[FlightWaypoint] = [] if targets is not None: for target in targets: - target_waypoints.append( - self.target_waypoint(flight, builder, target)) + target_waypoints.append(self.target_waypoint(flight, builder, target)) else: target_waypoints.append( - self.target_area_waypoint(flight, location, builder)) + self.target_area_waypoint(flight, location, builder) + ) hold = builder.hold(self._hold_point(flight)) join = builder.join(self.package.waypoints.join) @@ -1529,32 +1557,38 @@ class FlightPlanBuilder: takeoff=builder.takeoff(flight.departure), hold=hold, hold_duration=timedelta(minutes=5), - nav_to=builder.nav_path(hold.position, join.position, - self.doctrine.ingress_altitude), + nav_to=builder.nav_path( + hold.position, join.position, self.doctrine.ingress_altitude + ), join=join, - ingress=builder.ingress(ingress_type, - self.package.waypoints.ingress, location), + ingress=builder.ingress( + ingress_type, self.package.waypoints.ingress, location + ), targets=target_waypoints, egress=builder.egress(self.package.waypoints.egress, location), split=split, - nav_from=builder.nav_path(split.position, flight.arrival.position, - self.doctrine.ingress_altitude), + nav_from=builder.nav_path( + split.position, flight.arrival.position, self.doctrine.ingress_altitude + ), land=builder.land(flight.arrival), - divert=builder.divert(flight.divert) + divert=builder.divert(flight.divert), ) def _retreating_rendezvous_point(self, attack_transition: Point) -> Point: """Creates a rendezvous point that retreats from the origin airfield.""" return attack_transition.point_from_heading( self.package.target.position.heading_between_point( - self.package_airfield().position), - self.doctrine.join_distance.meters) + self.package_airfield().position + ), + self.doctrine.join_distance.meters, + ) def _advancing_rendezvous_point(self, attack_transition: Point) -> Point: """Creates a rendezvous point that advances toward the target.""" heading = self._heading_to_package_airfield(attack_transition) return attack_transition.point_from_heading( - heading, -self.doctrine.join_distance.meters) + heading, -self.doctrine.join_distance.meters + ) def _rendezvous_should_retreat(self, attack_transition: Point) -> bool: transition_target_distance = attack_transition.distance_to_point( @@ -1609,13 +1643,9 @@ class FlightPlanBuilder: # The package airfield is either the flight's airfield (when there is no # package) or the closest airfield to the objective that is the # departure airfield for some flight in the package. - cache = ObjectiveDistanceCache.get_closest_airfields( - self.package.target - ) + cache = ObjectiveDistanceCache.get_closest_airfields(self.package.target) for airfield in cache.operational_airfields: for flight in self.package.flights: if flight.departure == airfield: return airfield - raise RuntimeError( - "Could not find any airfield assigned to this package" - ) + raise RuntimeError("Could not find any airfield assigned to this package") diff --git a/gen/flights/traveltime.py b/gen/flights/traveltime.py index 3311fe22..98e74fac 100644 --- a/gen/flights/traveltime.py +++ b/gen/flights/traveltime.py @@ -23,7 +23,6 @@ if TYPE_CHECKING: class GroundSpeed: - @classmethod def for_flight(cls, flight: Flight, altitude: Distance) -> Speed: if not issubclass(flight.unit_type, FlyingType): @@ -55,13 +54,11 @@ class TravelTime: def between_points(a: Point, b: Point, speed: Speed) -> timedelta: error_factor = 1.1 distance = meters(a.distance_to_point(b)) - return timedelta( - hours=distance.nautical_miles / speed.knots * error_factor) + return timedelta(hours=distance.nautical_miles / speed.knots * error_factor) # TODO: Most if not all of this should move into FlightPlan. class TotEstimator: - def __init__(self, package: Package) -> None: self.package = package @@ -75,9 +72,9 @@ class TotEstimator: return startup_time def earliest_tot(self) -> timedelta: - earliest_tot = max(( - self.earliest_tot_for_flight(f) for f in self.package.flights - )) + earliest_tot = max( + (self.earliest_tot_for_flight(f) for f in self.package.flights) + ) # Trim microseconds. DCS doesn't handle sub-second resolution for tasks, # and they're not interesting from a mission planning perspective so we diff --git a/gen/flights/waypointbuilder.py b/gen/flights/waypointbuilder.py index 96e17c69..69ffac81 100644 --- a/gen/flights/waypointbuilder.py +++ b/gen/flights/waypointbuilder.py @@ -36,8 +36,13 @@ class StrikeTarget: class WaypointBuilder: - def __init__(self, flight: Flight, game: Game, player: bool, - targets: Optional[List[StrikeTarget]] = None) -> None: + def __init__( + self, + flight: Flight, + game: Game, + player: bool, + targets: Optional[List[StrikeTarget]] = None, + ) -> None: self.flight = flight self.conditions = game.conditions self.doctrine = game.faction_for(player).doctrine @@ -65,9 +70,7 @@ class WaypointBuilder: FlightWaypointType.NAV, position.x, position.y, - meters( - 500 - ) if self.is_helo else self.doctrine.rendezvous_altitude + meters(500) if self.is_helo else self.doctrine.rendezvous_altitude, ) waypoint.name = "NAV" waypoint.alt_type = "BARO" @@ -75,10 +78,7 @@ class WaypointBuilder: waypoint.pretty_name = "Enter theater" else: waypoint = FlightWaypoint( - FlightWaypointType.TAKEOFF, - position.x, - position.y, - meters(0) + FlightWaypointType.TAKEOFF, position.x, position.y, meters(0) ) waypoint.name = "TAKEOFF" waypoint.alt_type = "RADIO" @@ -98,9 +98,7 @@ class WaypointBuilder: FlightWaypointType.NAV, position.x, position.y, - meters( - 500 - ) if self.is_helo else self.doctrine.rendezvous_altitude + meters(500) if self.is_helo else self.doctrine.rendezvous_altitude, ) waypoint.name = "NAV" waypoint.alt_type = "BARO" @@ -108,10 +106,7 @@ class WaypointBuilder: waypoint.pretty_name = "Exit theater" else: waypoint = FlightWaypoint( - FlightWaypointType.LANDING_POINT, - position.x, - position.y, - meters(0) + FlightWaypointType.LANDING_POINT, position.x, position.y, meters(0) ) waypoint.name = "LANDING" waypoint.alt_type = "RADIO" @@ -119,8 +114,7 @@ class WaypointBuilder: waypoint.pretty_name = "Land" return waypoint - def divert(self, - divert: Optional[ControlPoint]) -> Optional[FlightWaypoint]: + def divert(self, divert: Optional[ControlPoint]) -> Optional[FlightWaypoint]: """Create divert waypoint for the given arrival airfield or carrier. Args: @@ -141,10 +135,7 @@ class WaypointBuilder: altitude_type = "RADIO" waypoint = FlightWaypoint( - FlightWaypointType.DIVERT, - position.x, - position.y, - altitude + FlightWaypointType.DIVERT, position.x, position.y, altitude ) waypoint.alt_type = altitude_type waypoint.name = "DIVERT" @@ -158,9 +149,7 @@ class WaypointBuilder: FlightWaypointType.LOITER, position.x, position.y, - meters( - 500 - ) if self.is_helo else self.doctrine.rendezvous_altitude + meters(500) if self.is_helo else self.doctrine.rendezvous_altitude, ) waypoint.pretty_name = "Hold" waypoint.description = "Wait until push time" @@ -172,9 +161,7 @@ class WaypointBuilder: FlightWaypointType.JOIN, position.x, position.y, - meters( - 80 - ) if self.is_helo else self.doctrine.ingress_altitude + meters(80) if self.is_helo else self.doctrine.ingress_altitude, ) if self.is_helo: waypoint.alt_type = "RADIO" @@ -188,9 +175,7 @@ class WaypointBuilder: FlightWaypointType.SPLIT, position.x, position.y, - meters( - 80 - ) if self.is_helo else self.doctrine.ingress_altitude + meters(80) if self.is_helo else self.doctrine.ingress_altitude, ) if self.is_helo: waypoint.alt_type = "RADIO" @@ -199,15 +184,17 @@ class WaypointBuilder: waypoint.name = "SPLIT" return waypoint - def ingress(self, ingress_type: FlightWaypointType, position: Point, - objective: MissionTarget) -> FlightWaypoint: + def ingress( + self, + ingress_type: FlightWaypointType, + position: Point, + objective: MissionTarget, + ) -> FlightWaypoint: waypoint = FlightWaypoint( ingress_type, position.x, position.y, - meters( - 50 - ) if self.is_helo else self.doctrine.ingress_altitude + meters(50) if self.is_helo else self.doctrine.ingress_altitude, ) if self.is_helo: waypoint.alt_type = "RADIO" @@ -223,9 +210,7 @@ class WaypointBuilder: FlightWaypointType.EGRESS, position.x, position.y, - meters( - 50 - ) if self.is_helo else self.doctrine.ingress_altitude + meters(50) if self.is_helo else self.doctrine.ingress_altitude, ) if self.is_helo: waypoint.alt_type = "RADIO" @@ -252,7 +237,7 @@ class WaypointBuilder: FlightWaypointType.TARGET_POINT, target.target.position.x, target.target.position.y, - meters(0) + meters(0), ) waypoint.description = description waypoint.pretty_name = description @@ -277,13 +262,14 @@ class WaypointBuilder: return self._target_area(f"ATTACK {target.name}", target, flyover=True) @staticmethod - def _target_area(name: str, location: MissionTarget, - flyover: bool = False) -> FlightWaypoint: + def _target_area( + name: str, location: MissionTarget, flyover: bool = False + ) -> FlightWaypoint: waypoint = FlightWaypoint( FlightWaypointType.TARGET_GROUP_LOC, location.position.x, location.position.y, - meters(0) + meters(0), ) waypoint.description = name waypoint.pretty_name = name @@ -308,7 +294,7 @@ class WaypointBuilder: FlightWaypointType.CAS, position.x, position.y, - meters(50) if self.is_helo else meters(1000) + meters(50) if self.is_helo else meters(1000), ) waypoint.alt_type = "RADIO" waypoint.description = "Provide CAS" @@ -325,10 +311,7 @@ class WaypointBuilder: altitude: Altitude of the racetrack. """ waypoint = FlightWaypoint( - FlightWaypointType.PATROL_TRACK, - position.x, - position.y, - altitude + FlightWaypointType.PATROL_TRACK, position.x, position.y, altitude ) waypoint.name = "RACETRACK START" waypoint.description = "Orbit between this point and the next point" @@ -344,18 +327,16 @@ class WaypointBuilder: altitude: Altitude of the racetrack. """ waypoint = FlightWaypoint( - FlightWaypointType.PATROL, - position.x, - position.y, - altitude + FlightWaypointType.PATROL, position.x, position.y, altitude ) waypoint.name = "RACETRACK END" waypoint.description = "Orbit between this point and the previous point" waypoint.pretty_name = "Race-track end" return waypoint - def race_track(self, start: Point, end: Point, - altitude: Distance) -> Tuple[FlightWaypoint, FlightWaypoint]: + def race_track( + self, start: Point, end: Point, altitude: Distance + ) -> Tuple[FlightWaypoint, FlightWaypoint]: """Creates two waypoint for a racetrack orbit. Args: @@ -363,8 +344,10 @@ class WaypointBuilder: end: The ending racetrack waypoint. altitude: The racetrack altitude. """ - return (self.race_track_start(start, altitude), - self.race_track_end(end, altitude)) + return ( + self.race_track_start(start, altitude), + self.race_track_end(end, altitude), + ) @staticmethod def orbit(start: Point, altitude: Distance) -> FlightWaypoint: @@ -375,12 +358,7 @@ class WaypointBuilder: altitude: Altitude of the racetrack. """ - waypoint = FlightWaypoint( - FlightWaypointType.LOITER, - start.x, - start.y, - altitude - ) + waypoint = FlightWaypoint(FlightWaypointType.LOITER, start.x, start.y, altitude) waypoint.name = "ORBIT" waypoint.description = "Anchor and hold at this point" waypoint.pretty_name = "Orbit" @@ -395,10 +373,7 @@ class WaypointBuilder: altitude: Altitude of the sweep in meters. """ waypoint = FlightWaypoint( - FlightWaypointType.INGRESS_SWEEP, - position.x, - position.y, - altitude + FlightWaypointType.INGRESS_SWEEP, position.x, position.y, altitude ) waypoint.name = "SWEEP START" waypoint.description = "Proceed to the target and engage enemy aircraft" @@ -414,18 +389,16 @@ class WaypointBuilder: altitude: Altitude of the sweep in meters. """ waypoint = FlightWaypoint( - FlightWaypointType.EGRESS, - position.x, - position.y, - altitude + FlightWaypointType.EGRESS, position.x, position.y, altitude ) waypoint.name = "SWEEP END" waypoint.description = "End of sweep" waypoint.pretty_name = "Sweep end" return waypoint - def sweep(self, start: Point, end: Point, - altitude: Distance) -> Tuple[FlightWaypoint, FlightWaypoint]: + def sweep( + self, start: Point, end: Point, altitude: Distance + ) -> Tuple[FlightWaypoint, FlightWaypoint]: """Creates two waypoint for a racetrack orbit. Args: @@ -433,11 +406,11 @@ class WaypointBuilder: end: The end of the sweep. altitude: The sweep altitude. """ - return (self.sweep_start(start, altitude), - self.sweep_end(end, altitude)) + return (self.sweep_start(start, altitude), self.sweep_end(end, altitude)) - def escort(self, ingress: Point, target: MissionTarget, egress: Point) -> \ - Tuple[FlightWaypoint, FlightWaypoint, FlightWaypoint]: + def escort( + self, ingress: Point, target: MissionTarget, egress: Point + ) -> Tuple[FlightWaypoint, FlightWaypoint, FlightWaypoint]: """Creates the waypoints needed to escort the package. Args: @@ -451,16 +424,13 @@ class WaypointBuilder: # description in gen.aircraft.JoinPointBuilder), so instead we give # the escort flights a flight plan including the ingress point, target # area, and egress point. - ingress = self.ingress(FlightWaypointType.INGRESS_ESCORT, ingress, - target) + ingress = self.ingress(FlightWaypointType.INGRESS_ESCORT, ingress, target) waypoint = FlightWaypoint( FlightWaypointType.TARGET_GROUP_LOC, target.position.x, target.position.y, - meters( - 50 - ) if self.is_helo else self.doctrine.ingress_altitude + meters(50) if self.is_helo else self.doctrine.ingress_altitude, ) if self.is_helo: waypoint.alt_type = "RADIO" @@ -480,18 +450,14 @@ class WaypointBuilder: altitude: Altitude of the waypoint. """ waypoint = FlightWaypoint( - FlightWaypointType.NAV, - position.x, - position.y, - altitude + FlightWaypointType.NAV, position.x, position.y, altitude ) waypoint.name = "NAV" waypoint.description = "NAV" waypoint.pretty_name = "Nav" return waypoint - def nav_path(self, a: Point, b: Point, - altitude: Distance) -> List[FlightWaypoint]: + def nav_path(self, a: Point, b: Point, altitude: Distance) -> List[FlightWaypoint]: path = self.clean_nav_points(self.navmesh.shortest_path(a, b)) return [self.nav(self.perturb(p), altitude) for p in path] @@ -518,10 +484,8 @@ class WaypointBuilder: previous = current current = nxt - def nav_point_prunable(self, previous: Point, current: Point, - nxt: Point) -> bool: - previous_threatened = self.threat_zones.path_threatened(previous, - current) + def nav_point_prunable(self, previous: Point, current: Point, nxt: Point) -> bool: + previous_threatened = self.threat_zones.path_threatened(previous, current) next_threatened = self.threat_zones.path_threatened(current, nxt) pruned_threatened = self.threat_zones.path_threatened(previous, nxt) previous_distance = meters(previous.distance_to_point(current)) diff --git a/gen/forcedoptionsgen.py b/gen/forcedoptionsgen.py index 19421942..bbae0eb2 100644 --- a/gen/forcedoptionsgen.py +++ b/gen/forcedoptionsgen.py @@ -15,11 +15,15 @@ class ForcedOptionsGenerator: self.game = game def _set_options_view(self) -> None: - self.mission.forced_options.options_view = self.game.settings.map_coalition_visibility + self.mission.forced_options.options_view = ( + self.game.settings.map_coalition_visibility + ) def _set_external_views(self) -> None: if not self.game.settings.external_views_allowed: - self.mission.forced_options.external_views = self.game.settings.external_views_allowed + self.mission.forced_options.external_views = ( + self.game.settings.external_views_allowed + ) def _set_labels(self) -> None: # TODO: Fix settings to use the real type. diff --git a/gen/ground_forces/ai_ground_planner.py b/gen/ground_forces/ai_ground_planner.py index e98d82d2..777efd01 100644 --- a/gen/ground_forces/ai_ground_planner.py +++ b/gen/ground_forces/ai_ground_planner.py @@ -39,12 +39,11 @@ GROUP_SIZES_BY_COMBAT_STANCE = { CombatStance.RETREAT: [2, 4, 6, 8], CombatStance.BREAKTHROUGH: [4, 6, 6, 8], CombatStance.ELIMINATION: [2, 4, 4, 4, 6], - CombatStance.AMBUSH: [1, 1, 2, 2, 2, 2, 4] + CombatStance.AMBUSH: [1, 1, 2, 2, 2, 2, 4], } class CombatGroup: - def __init__(self, role: CombatGroupRole): self.units: List[VehicleType] = [] self.role = role @@ -60,11 +59,12 @@ class CombatGroup: class GroundPlanner: - - def __init__(self, cp:ControlPoint, game): + def __init__(self, cp: ControlPoint, game): self.cp = cp self.game = game - self.connected_enemy_cp = [cp for cp in self.cp.connected_points if cp.captured != self.cp.captured] + self.connected_enemy_cp = [ + cp for cp in self.cp.connected_points if cp.captured != self.cp.captured + ] self.tank_groups: List[CombatGroup] = [] self.apc_group: List[CombatGroup] = [] self.ifv_group: List[CombatGroup] = [] @@ -80,7 +80,7 @@ class GroundPlanner: def plan_groundwar(self): - if hasattr(self.cp, 'stance'): + if hasattr(self.cp, "stance"): group_size_choice = GROUP_SIZES_BY_COMBAT_STANCE[self.cp.stance] else: self.cp.stance = CombatStance.DEFENSIVE @@ -152,12 +152,3 @@ class GroundPlanner: print("For : #" + str(key)) for group in self.units_per_cp[key]: print(str(group)) - - - - - - - - - diff --git a/gen/ground_forces/ai_ground_planner_db.py b/gen/ground_forces/ai_ground_planner_db.py index c448611d..b34b055b 100644 --- a/gen/ground_forces/ai_ground_planner_db.py +++ b/gen/ground_forces/ai_ground_planner_db.py @@ -16,7 +16,6 @@ TYPE_TANKS = [ Armor.MBT_M60A3_Patton, Armor.MBT_Merkava_Mk__4, Armor.ZTZ_96B, - # WW2 Armor.MT_Pz_Kpfw_V_Panther_Ausf_G, Armor.MT_Pz_Kpfw_IV_Ausf_H, @@ -29,36 +28,30 @@ TYPE_TANKS = [ Armor.CT_Cromwell_IV, Armor.HIT_Churchill_VII, Armor.LT_Mk_VII_Tetrarch, - # Mods frenchpack.DIM__TOYOTA_BLUE, frenchpack.DIM__TOYOTA_GREEN, frenchpack.DIM__TOYOTA_DESERT, frenchpack.DIM__KAMIKAZE, - frenchpack.AMX_10RCR, frenchpack.AMX_10RCR_SEPAR, frenchpack.AMX_30B2, frenchpack.Leclerc_Serie_XXI, - ] TYPE_ATGM = [ Armor.ATGM_M1045_HMMWV_TOW, Armor.ATGM_M1134_Stryker, Armor.IFV_BMP_2, - # WW2 (Tank Destroyers) Armor.M30_Cargo_Carrier, Armor.TD_Jagdpanzer_IV, Armor.TD_Jagdpanther_G1, Armor.TD_M10_GMC, - # Mods frenchpack.VBAE_CRAB_MMP, frenchpack.VAB_MEPHISTO, frenchpack.TRM_2000_PAMELA, - ] TYPE_IFV = [ @@ -73,17 +66,14 @@ TYPE_IFV = [ Armor.IFV_M2A2_Bradley, Armor.IFV_BMD_1, Armor.ZBD_04A, - # WW2 Armor.AC_Sd_Kfz_234_2_Puma, Armor.LAC_M8_Greyhound, Armor.Daimler_Armoured_Car, - # Mods frenchpack.ERC_90, frenchpack.VBAE_CRAB, - frenchpack.VAB_T20_13 - + frenchpack.VAB_T20_13, ] TYPE_APC = [ @@ -101,16 +91,13 @@ TYPE_APC = [ Armor.ARV_BRDM_2, Armor.ARV_BTR_RD, Armor.FDDM_Grad, - # WW2 Armor.APC_M2A1, Armor.APC_Sd_Kfz_251, - # Mods frenchpack.VAB__50, frenchpack.VBL__50, frenchpack.VBL_AANF1, - ] TYPE_ARTILLERY = [ @@ -125,10 +112,9 @@ TYPE_ARTILLERY = [ Artillery.SpGH_Dana, Artillery.SPH_2S19_Msta, Artillery.MLRS_FDDM, - # WW2 Artillery.Sturmpanzer_IV_Brummbär, - Artillery.M12_GMC + Artillery.M12_GMC, ] TYPE_LOGI = [ @@ -147,11 +133,9 @@ TYPE_LOGI = [ Unarmed.Willys_MB, Unarmed.Land_Rover_109_S3, Unarmed.Land_Rover_101_FC, - # Mods frenchpack.VBL, frenchpack.VAB, - ] TYPE_INFANTRY = [ @@ -179,7 +163,6 @@ TYPE_SHORAD = [ AirDefence.SAM_SA_13_Strela_10M3_9A35M3, AirDefence.SAM_SA_15_Tor_9A331, AirDefence.SAM_SA_19_Tunguska_2S6, - AirDefence.SPAAA_Gepard, AirDefence.AAA_Vulcan_M163, AirDefence.SAM_Linebacker_M6, @@ -187,7 +170,6 @@ TYPE_SHORAD = [ AirDefence.SAM_Avenger_M1097, AirDefence.SAM_Roland_ADS, AirDefence.HQ_7_Self_Propelled_LN, - AirDefence.AAA_8_8cm_Flak_18, AirDefence.AAA_8_8cm_Flak_36, AirDefence.AAA_8_8cm_Flak_37, @@ -195,5 +177,4 @@ TYPE_SHORAD = [ AirDefence.AAA_Bofors_40mm, AirDefence.AAA_M1_37mm, AirDefence.AA_gun_QF_3_7, - ] diff --git a/gen/ground_forces/combat_stance.py b/gen/ground_forces/combat_stance.py index 604ec508..26e959eb 100644 --- a/gen/ground_forces/combat_stance.py +++ b/gen/ground_forces/combat_stance.py @@ -2,10 +2,11 @@ from enum import Enum class CombatStance(Enum): - DEFENSIVE = 0 # Unit will adopt defensive stance with medium group of units - AGGRESSIVE = 1 # Unit will attempt to make progress with medium sized group of units - RETREAT = 2 # Unit will retreat - BREAKTHROUGH = 3 # Unit will attempt a breakthrough, rushing forward very aggresively with big group of armored units, and even less armored units will move aggresively + DEFENSIVE = 0 # Unit will adopt defensive stance with medium group of units + AGGRESSIVE = ( + 1 # Unit will attempt to make progress with medium sized group of units + ) + RETREAT = 2 # Unit will retreat + BREAKTHROUGH = 3 # Unit will attempt a breakthrough, rushing forward very aggresively with big group of armored units, and even less armored units will move aggresively ELIMINATION = 4 # Unit will progress aggresively toward anemy units, attempting to eliminate the ennemy force - AMBUSH = 5 # Units will adopt a defensive stance a bit different from 'DEFENSIVE', ATGM & INFANTRY with RPG will be located on frontline with the armored units. (The groups of units will be smaller) - + AMBUSH = 5 # Units will adopt a defensive stance a bit different from 'DEFENSIVE', ATGM & INFANTRY with RPG will be located on frontline with the armored units. (The groups of units will be smaller) diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index ced430e5..524af93e 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -18,7 +18,8 @@ from dcs.task import ( ActivateBeaconCommand, ActivateICLSCommand, EPLRS, - OptAlarmState, FireAtPoint, + OptAlarmState, + FireAtPoint, ) from dcs.unit import Ship, Unit, Vehicle from dcs.unitgroup import Group, ShipGroup, StaticGroup, VehicleGroup @@ -30,9 +31,12 @@ from game.data.building_data import FORTIFICATION_UNITS, FORTIFICATION_UNITS_ID from game.db import unit_type_from_name from game.theater import ControlPoint, TheaterGroundObject from game.theater.theatergroundobject import ( - BuildingGroundObject, CarrierGroundObject, + BuildingGroundObject, + CarrierGroundObject, GenericCarrierGroundObject, - LhaGroundObject, ShipGroundObject, MissileSiteGroundObject, + LhaGroundObject, + ShipGroundObject, + MissileSiteGroundObject, ) from game.unitmap import UnitMap from game.utils import knots, mps @@ -53,8 +57,15 @@ class GenericGroundObjectGenerator: Currently used only for SAM """ - def __init__(self, ground_object: TheaterGroundObject, country: Country, - game: Game, mission: Mission, unit_map: UnitMap) -> None: + + def __init__( + self, + ground_object: TheaterGroundObject, + country: Country, + game: Game, + mission: Mission, + unit_map: UnitMap, + ) -> None: self.ground_object = ground_object self.country = country self.game = game @@ -72,18 +83,22 @@ class GenericGroundObjectGenerator: unit_type = unit_type_from_name(group.units[0].type) if unit_type is None: - raise RuntimeError( - f"Unrecognized unit type: {group.units[0].type}") + raise RuntimeError(f"Unrecognized unit type: {group.units[0].type}") - vg = self.m.vehicle_group(self.country, group.name, unit_type, - position=group.position, - heading=group.units[0].heading) + vg = self.m.vehicle_group( + self.country, + group.name, + unit_type, + position=group.position, + heading=group.units[0].heading, + ) vg.units[0].name = self.m.string(group.units[0].name) vg.units[0].player_can_drive = True for i, u in enumerate(group.units): if i > 0: - vehicle = Vehicle(self.m.next_unit_id(), - self.m.string(u.name), u.type) + vehicle = Vehicle( + self.m.next_unit_id(), self.m.string(u.name), u.type + ) vehicle.position.x = u.position.x vehicle.position.y = u.position.y vehicle.heading = u.heading @@ -96,7 +111,7 @@ class GenericGroundObjectGenerator: @staticmethod def enable_eplrs(group: Group, unit_type: Type[UnitType]) -> None: - if hasattr(unit_type, 'eplrs'): + if hasattr(unit_type, "eplrs"): if unit_type.eplrs: group.points[0].tasks.append(EPLRS(group.id)) @@ -106,14 +121,13 @@ class GenericGroundObjectGenerator: else: group.points[0].tasks.append(OptAlarmState(1)) - def _register_unit_group(self, persistence_group: Group, - miz_group: Group) -> None: - self.unit_map.add_ground_object_units(self.ground_object, - persistence_group, miz_group) + def _register_unit_group(self, persistence_group: Group, miz_group: Group) -> None: + self.unit_map.add_ground_object_units( + self.ground_object, persistence_group, miz_group + ) class MissileSiteGenerator(GenericGroundObjectGenerator): - def generate(self) -> None: super(MissileSiteGenerator, self).generate() # Note : Only the SCUD missiles group can fire (V1 site cannot fire in game right now) @@ -125,13 +139,19 @@ class MissileSiteGenerator(GenericGroundObjectGenerator): targets = self.possible_missile_targets(vg) if targets: target = random.choice(targets) - real_target = target.point_from_heading(random.randint(0, 360), random.randint(0, 2500)) + real_target = target.point_from_heading( + random.randint(0, 360), random.randint(0, 2500) + ) vg.points[0].add_task(FireAtPoint(real_target)) logging.info("Set up fire task for missile group.") else: - logging.info("Couldn't setup missile site to fire, no valid target in range.") + logging.info( + "Couldn't setup missile site to fire, no valid target in range." + ) else: - logging.info("Couldn't setup missile site to fire, group was not generated.") + logging.info( + "Couldn't setup missile site to fire, group was not generated." + ) def possible_missile_targets(self, vg: Group) -> List[Point]: """ @@ -190,7 +210,8 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator): break else: logging.error( - f"{self.ground_object.dcs_identifier} not found in static maps") + f"{self.ground_object.dcs_identifier} not found in static maps" + ) def generate_vehicle_group(self, unit_type: UnitType) -> None: if not self.ground_object.is_dead: @@ -228,11 +249,20 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): Used by both CV(N) groups and LHA groups. """ - def __init__(self, ground_object: GenericCarrierGroundObject, - control_point: ControlPoint, country: Country, game: Game, - mission: Mission, radio_registry: RadioRegistry, - tacan_registry: TacanRegistry, icls_alloc: Iterator[int], - runways: Dict[str, RunwayData], unit_map: UnitMap) -> None: + + def __init__( + self, + ground_object: GenericCarrierGroundObject, + control_point: ControlPoint, + country: Country, + game: Game, + mission: Mission, + radio_registry: RadioRegistry, + tacan_registry: TacanRegistry, + icls_alloc: Iterator[int], + runways: Dict[str, RunwayData], + unit_map: UnitMap, + ) -> None: super().__init__(ground_object, country, game, mission, unit_map) self.ground_object = ground_object self.control_point = control_point @@ -245,8 +275,7 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): # TODO: Require single group? for group in self.ground_object.groups: if not group.units: - logging.warning( - f"Found empty carrier group in {self.control_point}") + logging.warning(f"Found empty carrier group in {self.control_point}") continue atc = self.radio_registry.alloc_uhf() @@ -270,25 +299,29 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): def get_carrier_type(self, group: Group) -> Type[UnitType]: unit_type = unit_type_from_name(group.units[0].type) if unit_type is None: - raise RuntimeError( - f"Unrecognized carrier name: {group.units[0].type}") + raise RuntimeError(f"Unrecognized carrier name: {group.units[0].type}") return unit_type - def configure_carrier(self, group: Group, - atc_channel: RadioFrequency) -> ShipGroup: + def configure_carrier(self, group: Group, atc_channel: RadioFrequency) -> ShipGroup: unit_type = self.get_carrier_type(group) - ship_group = self.m.ship_group(self.country, group.name, unit_type, - position=group.position, - heading=group.units[0].heading) + ship_group = self.m.ship_group( + self.country, + group.name, + unit_type, + position=group.position, + heading=group.units[0].heading, + ) ship_group.set_frequency(atc_channel.hertz) ship_group.units[0].name = self.m.string(group.units[0].name) return ship_group def create_ship(self, unit: Unit, atc_channel: RadioFrequency) -> Ship: - ship = Ship(self.m.next_unit_id(), - self.m.string(unit.name), - unit_type_from_name(unit.type)) + ship = Ship( + self.m.next_unit_id(), + self.m.string(unit.name), + unit_type_from_name(unit.type), + ) ship.position.x = unit.position.x ship.position.y = unit.position.y ship.heading = unit.heading @@ -303,7 +336,8 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): carrier_speed = knots(25) - mps(wind.speed) for attempt in range(5): point = group.points[0].position.point_from_heading( - brc, 100000 - attempt * 20000) + brc, 100000 - attempt * 20000 + ) if self.game.theater.is_in_sea(point): group.points[0].speed = carrier_speed.meters_per_second group.add_waypoint(point, carrier_speed.kph) @@ -314,21 +348,30 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): raise NotImplementedError @staticmethod - def activate_beacons(group: ShipGroup, tacan: TacanChannel, - callsign: str, icls: int) -> None: - group.points[0].tasks.append(ActivateBeaconCommand( - channel=tacan.number, - modechannel=tacan.band.value, - callsign=callsign, - unit_id=group.units[0].id, - aa=False - )) - group.points[0].tasks.append(ActivateICLSCommand( - icls, unit_id=group.units[0].id - )) + def activate_beacons( + group: ShipGroup, tacan: TacanChannel, callsign: str, icls: int + ) -> None: + group.points[0].tasks.append( + ActivateBeaconCommand( + channel=tacan.number, + modechannel=tacan.band.value, + callsign=callsign, + unit_id=group.units[0].id, + aa=False, + ) + ) + group.points[0].tasks.append( + ActivateICLSCommand(icls, unit_id=group.units[0].id) + ) - def add_runway_data(self, brc: int, atc: RadioFrequency, - tacan: TacanChannel, callsign: str, icls: int) -> None: + def add_runway_data( + self, + brc: int, + atc: RadioFrequency, + tacan: TacanChannel, + callsign: str, + icls: int, + ) -> None: # TODO: Make unit name usable. # This relies on one control point mapping exactly # to one LHA, carrier, or other usable "runway". @@ -354,8 +397,7 @@ class CarrierGenerator(GenericCarrierGenerator): def get_carrier_type(self, group: Group) -> UnitType: unit_type = super().get_carrier_type(group) if self.game.settings.supercarrier: - unit_type = db.upgrade_to_supercarrier(unit_type, - self.control_point.name) + unit_type = db.upgrade_to_supercarrier(unit_type, self.control_point.name) return unit_type def tacan_callsign(self) -> str: @@ -363,18 +405,20 @@ class CarrierGenerator(GenericCarrierGenerator): if self.control_point.name == "Carrier Strike Group 8": return "TRU" else: - return random.choice([ - "STE", - "CVN", - "CVH", - "CCV", - "ACC", - "ARC", - "GER", - "ABR", - "LIN", - "TRU", - ]) + return random.choice( + [ + "STE", + "CVN", + "CVH", + "CCV", + "ACC", + "ARC", + "GER", + "ABR", + "LIN", + "TRU", + ] + ) class LhaGenerator(GenericCarrierGenerator): @@ -382,14 +426,16 @@ class LhaGenerator(GenericCarrierGenerator): def tacan_callsign(self) -> str: # TODO: Assign these properly. - return random.choice([ - "LHD", - "LHA", - "LHB", - "LHC", - "LHD", - "LDS", - ]) + return random.choice( + [ + "LHD", + "LHA", + "LHB", + "LHC", + "LHD", + "LDS", + ] + ) class ShipObjectGenerator(GenericGroundObjectGenerator): @@ -406,22 +452,23 @@ class ShipObjectGenerator(GenericGroundObjectGenerator): unit_type = unit_type_from_name(group.units[0].type) if unit_type is None: - raise RuntimeError( - f"Unrecognized unit type: {group.units[0].type}") + raise RuntimeError(f"Unrecognized unit type: {group.units[0].type}") self.generate_group(group, unit_type) - def generate_group(self, group_def: Group, - first_unit_type: Type[UnitType]) -> None: - group = self.m.ship_group(self.country, group_def.name, first_unit_type, - position=group_def.position, - heading=group_def.units[0].heading) + def generate_group(self, group_def: Group, first_unit_type: Type[UnitType]) -> None: + group = self.m.ship_group( + self.country, + group_def.name, + first_unit_type, + position=group_def.position, + heading=group_def.units[0].heading, + ) group.units[0].name = self.m.string(group_def.units[0].name) # TODO: Skipping the first unit looks like copy pasta from the carrier. for unit in group_def.units[1:]: unit_type = unit_type_from_name(unit.type) - ship = Ship(self.m.next_unit_id(), - self.m.string(unit.name), unit_type) + ship = Ship(self.m.next_unit_id(), self.m.string(unit.name), unit_type) ship.position.x = unit.position.x ship.position.y = unit.position.y ship.heading = unit.heading @@ -439,9 +486,14 @@ class GroundObjectsGenerator: the appropriate generators. """ - def __init__(self, mission: Mission, game: Game, - radio_registry: RadioRegistry, tacan_registry: TacanRegistry, - unit_map: UnitMap) -> None: + def __init__( + self, + mission: Mission, + game: Game, + radio_registry: RadioRegistry, + tacan_registry: TacanRegistry, + unit_map: UnitMap, + ) -> None: self.m = mission self.game = game self.radio_registry = radio_registry @@ -461,28 +513,44 @@ class GroundObjectsGenerator: for ground_object in cp.ground_objects: if isinstance(ground_object, BuildingGroundObject): generator = BuildingSiteGenerator( - ground_object, country, self.game, self.m, - self.unit_map) + ground_object, country, self.game, self.m, self.unit_map + ) elif isinstance(ground_object, CarrierGroundObject): generator = CarrierGenerator( - ground_object, cp, country, self.game, self.m, - self.radio_registry, self.tacan_registry, - self.icls_alloc, self.runways, self.unit_map) + ground_object, + cp, + country, + self.game, + self.m, + self.radio_registry, + self.tacan_registry, + self.icls_alloc, + self.runways, + self.unit_map, + ) elif isinstance(ground_object, LhaGroundObject): generator = CarrierGenerator( - ground_object, cp, country, self.game, self.m, - self.radio_registry, self.tacan_registry, - self.icls_alloc, self.runways, self.unit_map) + ground_object, + cp, + country, + self.game, + self.m, + self.radio_registry, + self.tacan_registry, + self.icls_alloc, + self.runways, + self.unit_map, + ) elif isinstance(ground_object, ShipGroundObject): generator = ShipObjectGenerator( - ground_object, country, self.game, self.m, - self.unit_map) + ground_object, country, self.game, self.m, self.unit_map + ) elif isinstance(ground_object, MissileSiteGroundObject): generator = MissileSiteGenerator( - ground_object, country, self.game, self.m, - self.unit_map) + ground_object, country, self.game, self.m, self.unit_map + ) else: generator = GenericGroundObjectGenerator( - ground_object, country, self.game, self.m, - self.unit_map) + ground_object, country, self.game, self.m, self.unit_map + ) generator.generate() diff --git a/gen/kneeboard.py b/gen/kneeboard.py index 97442c59..cfc8416e 100644 --- a/gen/kneeboard.py +++ b/gen/kneeboard.py @@ -49,7 +49,7 @@ class KneeboardPageWriter: """Creates kneeboard images.""" def __init__(self, page_margin: int = 24, line_spacing: int = 12) -> None: - self.image = Image.new('RGB', (768, 1024), (0xff, 0xff, 0xff)) + self.image = Image.new("RGB", (768, 1024), (0xFF, 0xFF, 0xFF)) # These font sizes create a relatively full page for current sorties. If # we start generating more complicated flight plans, or start including # more information in the comm ladder (the latter of which we should @@ -58,8 +58,7 @@ class KneeboardPageWriter: self.title_font = ImageFont.truetype("arial.ttf", 32) self.heading_font = ImageFont.truetype("arial.ttf", 24) self.content_font = ImageFont.truetype("arial.ttf", 20) - self.table_font = ImageFont.truetype( - "resources/fonts/Inconsolata.otf", 20) + self.table_font = ImageFont.truetype("resources/fonts/Inconsolata.otf", 20) self.draw = ImageDraw.Draw(self.image) self.x = page_margin self.y = page_margin @@ -69,8 +68,9 @@ class KneeboardPageWriter: def position(self) -> Tuple[int, int]: return self.x, self.y - def text(self, text: str, font=None, - fill: Tuple[int, int, int] = (0, 0, 0)) -> None: + def text( + self, text: str, font=None, fill: Tuple[int, int, int] = (0, 0, 0) + ) -> None: if font is None: font = self.content_font @@ -84,8 +84,9 @@ class KneeboardPageWriter: def heading(self, text: str) -> None: self.text(text, font=self.heading_font) - def table(self, cells: List[List[str]], - headers: Optional[List[str]] = None) -> None: + def table( + self, cells: List[List[str]], headers: Optional[List[str]] = None + ) -> None: if headers is None: headers = [] table = tabulate(cells, headers=headers, numalign="right") @@ -157,29 +158,34 @@ class FlightPlanBuilder: first_waypoint_num = self.target_points[0].number last_waypoint_num = self.target_points[-1].number - self.rows.append([ - f"{first_waypoint_num}-{last_waypoint_num}", - "Target points", - "0", - self._waypoint_distance(self.target_points[0].waypoint), - self._ground_speed(self.target_points[0].waypoint), - self._format_time(self.target_points[0].waypoint.tot), - self._format_time(self.target_points[0].waypoint.departure_time), - ]) + self.rows.append( + [ + f"{first_waypoint_num}-{last_waypoint_num}", + "Target points", + "0", + self._waypoint_distance(self.target_points[0].waypoint), + self._ground_speed(self.target_points[0].waypoint), + self._format_time(self.target_points[0].waypoint.tot), + self._format_time(self.target_points[0].waypoint.departure_time), + ] + ) self.last_waypoint = self.target_points[-1].waypoint def add_waypoint_row(self, waypoint: NumberedWaypoint) -> None: - self.rows.append([ - str(waypoint.number), - KneeboardPageWriter.wrap_line( - waypoint.waypoint.pretty_name, - FlightPlanBuilder.WAYPOINT_DESC_MAX_LEN), - str(int(waypoint.waypoint.alt.feet)), - self._waypoint_distance(waypoint.waypoint), - self._ground_speed(waypoint.waypoint), - self._format_time(waypoint.waypoint.tot), - self._format_time(waypoint.waypoint.departure_time), - ]) + self.rows.append( + [ + str(waypoint.number), + KneeboardPageWriter.wrap_line( + waypoint.waypoint.pretty_name, + FlightPlanBuilder.WAYPOINT_DESC_MAX_LEN, + ), + str(int(waypoint.waypoint.alt.feet)), + self._waypoint_distance(waypoint.waypoint), + self._ground_speed(waypoint.waypoint), + self._format_time(waypoint.waypoint.tot), + self._format_time(waypoint.waypoint.departure_time), + ] + ) def _format_time(self, time: Optional[datetime.timedelta]) -> str: if time is None: @@ -191,9 +197,9 @@ class FlightPlanBuilder: if self.last_waypoint is None: return "-" - distance = meters(self.last_waypoint.position.distance_to_point( - waypoint.position - )) + distance = meters( + self.last_waypoint.position.distance_to_point(waypoint.position) + ) return f"{distance.nautical_miles:.1f} NM" def _ground_speed(self, waypoint: FlightWaypoint) -> str: @@ -210,9 +216,9 @@ class FlightPlanBuilder: else: return "-" - distance = meters(self.last_waypoint.position.distance_to_point( - waypoint.position - )) + distance = meters( + self.last_waypoint.position.distance_to_point(waypoint.position) + ) duration = (waypoint.tot - last_time).total_seconds() / 3600 return f"{int(distance.nautical_miles / duration)} kt" @@ -222,9 +228,16 @@ class FlightPlanBuilder: class BriefingPage(KneeboardPage): """A kneeboard page containing briefing information.""" - def __init__(self, flight: FlightData, comms: List[CommInfo], - awacs: List[AwacsInfo], tankers: List[TankerInfo], - jtacs: List[JtacInfo], start_time: datetime.datetime) -> None: + + def __init__( + self, + flight: FlightData, + comms: List[CommInfo], + awacs: List[AwacsInfo], + tankers: List[TankerInfo], + jtacs: List[JtacInfo], + start_time: datetime.datetime, + ) -> None: self.flight = flight self.comms = list(comms) self.awacs = awacs @@ -239,49 +252,59 @@ class BriefingPage(KneeboardPage): # TODO: Handle carriers. writer.heading("Airfield Info") - writer.table([ - self.airfield_info_row("Departure", self.flight.departure), - self.airfield_info_row("Arrival", self.flight.arrival), - self.airfield_info_row("Divert", self.flight.divert), - ], headers=["", "Airbase", "ATC", "TCN", "I(C)LS", "RWY"]) + writer.table( + [ + self.airfield_info_row("Departure", self.flight.departure), + self.airfield_info_row("Arrival", self.flight.arrival), + self.airfield_info_row("Divert", self.flight.divert), + ], + headers=["", "Airbase", "ATC", "TCN", "I(C)LS", "RWY"], + ) writer.heading("Flight Plan") flight_plan_builder = FlightPlanBuilder(self.start_time) for num, waypoint in enumerate(self.flight.waypoints): flight_plan_builder.add_waypoint(num, waypoint) - writer.table(flight_plan_builder.build(), headers=[ - "#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure" - ]) + writer.table( + flight_plan_builder.build(), + headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"], + ) flight_plan_builder - writer.table([ - ["{}lbs".format(self.flight.bingo_fuel), "{}lbs".format(self.flight.joker_fuel)] - ], ['Bingo', 'Joker']) + writer.table( + [ + [ + "{}lbs".format(self.flight.bingo_fuel), + "{}lbs".format(self.flight.joker_fuel), + ] + ], + ["Bingo", "Joker"], + ) # Package Section writer.heading("Comm ladder") comm_ladder = [] for comm in self.comms: - comm_ladder.append([comm.name, '', '', '', self.format_frequency(comm.freq)]) + comm_ladder.append( + [comm.name, "", "", "", self.format_frequency(comm.freq)] + ) for a in self.awacs: - comm_ladder.append([ - a.callsign, - 'AWACS', - '', - '', - self.format_frequency(a.freq) - ]) + comm_ladder.append( + [a.callsign, "AWACS", "", "", self.format_frequency(a.freq)] + ) for tanker in self.tankers: - comm_ladder.append([ - tanker.callsign, - "Tanker", - tanker.variant, - str(tanker.tacan), - self.format_frequency(tanker.freq), - ]) - - writer.table(comm_ladder, headers=["Callsign","Task", "Type", "TACAN", "FREQ"]) + comm_ladder.append( + [ + tanker.callsign, + "Tanker", + tanker.variant, + str(tanker.tacan), + self.format_frequency(tanker.freq), + ] + ) + + writer.table(comm_ladder, headers=["Callsign", "Task", "Type", "TACAN", "FREQ"]) writer.heading("JTAC") jtacs = [] @@ -291,8 +314,9 @@ class BriefingPage(KneeboardPage): writer.write(path) - def airfield_info_row(self, row_title: str, - runway: Optional[RunwayData]) -> List[str]: + def airfield_info_row( + self, row_title: str, runway: Optional[RunwayData] + ) -> List[str]: """Creates a table row for a given airfield. Args: @@ -372,7 +396,8 @@ class KneeboardGenerator(MissionInfoGenerator): if not flight.client_units: continue all_flights[flight.aircraft_type].extend( - self.generate_flight_kneeboard(flight)) + self.generate_flight_kneeboard(flight) + ) return all_flights def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]: @@ -384,6 +409,6 @@ class KneeboardGenerator(MissionInfoGenerator): self.awacs, self.tankers, self.jtacs, - self.mission.start_time + self.mission.start_time, ), ] diff --git a/gen/locations/preset_location_finder.py b/gen/locations/preset_location_finder.py index 4df32466..94202294 100644 --- a/gen/locations/preset_location_finder.py +++ b/gen/locations/preset_location_finder.py @@ -9,9 +9,10 @@ from gen.locations.preset_locations import PresetLocation class MizDataLocationFinder: - @staticmethod - def compute_possible_locations(terrain_name: str, cp_name: str) -> PresetControlPointLocations: + def compute_possible_locations( + terrain_name: str, cp_name: str + ) -> PresetControlPointLocations: """ Extract the list of preset locations from miz data :param terrain_name: Terrain/Map name @@ -32,28 +33,54 @@ class MizDataLocationFinder: for vehicle_group in m.country("USA").vehicle_group: if len(vehicle_group.units) > 0: - ashore_locations.append(PresetLocation(vehicle_group.position, - vehicle_group.units[0].heading, - vehicle_group.name)) + ashore_locations.append( + PresetLocation( + vehicle_group.position, + vehicle_group.units[0].heading, + vehicle_group.name, + ) + ) for ship_group in m.country("USA").ship_group: - if len(ship_group.units) > 0 and ship_group.units[0].type == ships.Oliver_Hazzard_Perry_class.id: - offshore_locations.append(PresetLocation(ship_group.position, - ship_group.units[0].heading, - ship_group.name)) + if ( + len(ship_group.units) > 0 + and ship_group.units[0].type == ships.Oliver_Hazzard_Perry_class.id + ): + offshore_locations.append( + PresetLocation( + ship_group.position, + ship_group.units[0].heading, + ship_group.name, + ) + ) for static_group in m.country("USA").static_group: if len(static_group.units) > 0: - powerplants_locations.append(PresetLocation(static_group.position, - static_group.units[0].heading, - static_group.name)) + powerplants_locations.append( + PresetLocation( + static_group.position, + static_group.units[0].heading, + static_group.name, + ) + ) if m.country("Iran") is not None: for vehicle_group in m.country("Iran").vehicle_group: - if len(vehicle_group.units) > 0 and vehicle_group.units[0].type == MissilesSS.SS_N_2_Silkworm.id: - antiship_locations.append(PresetLocation(vehicle_group.position, - vehicle_group.units[0].heading, - vehicle_group.name)) + if ( + len(vehicle_group.units) > 0 + and vehicle_group.units[0].type == MissilesSS.SS_N_2_Silkworm.id + ): + antiship_locations.append( + PresetLocation( + vehicle_group.position, + vehicle_group.units[0].heading, + vehicle_group.name, + ) + ) - return PresetControlPointLocations(ashore_locations, offshore_locations, - antiship_locations, powerplants_locations) + return PresetControlPointLocations( + ashore_locations, + offshore_locations, + antiship_locations, + powerplants_locations, + ) diff --git a/gen/locations/preset_locations.py b/gen/locations/preset_locations.py index 2d8872c3..89bdffbc 100644 --- a/gen/locations/preset_locations.py +++ b/gen/locations/preset_locations.py @@ -6,10 +6,16 @@ from dcs import Point @dataclass class PresetLocation: """A preset location""" + position: Point heading: int id: str def __str__(self): - return "-" * 10 + "X: {}\n Y: {}\nHdg: {}°\nId: {}".format(self.position.x, self.position.y, self.heading, - self.id) + "-" * 10 + return ( + "-" * 10 + + "X: {}\n Y: {}\nHdg: {}°\nId: {}".format( + self.position.x, self.position.y, self.heading, self.id + ) + + "-" * 10 + ) diff --git a/gen/missiles/missiles_group_generator.py b/gen/missiles/missiles_group_generator.py index 0e2ab2ec..72251516 100644 --- a/gen/missiles/missiles_group_generator.py +++ b/gen/missiles/missiles_group_generator.py @@ -4,10 +4,7 @@ from game import db from gen.missiles.scud_site import ScudGenerator from gen.missiles.v1_group import V1GroupGenerator -MISSILES_MAP = { - "V1GroupGenerator": V1GroupGenerator, - "ScudGenerator": ScudGenerator -} +MISSILES_MAP = {"V1GroupGenerator": V1GroupGenerator, "ScudGenerator": ScudGenerator} def generate_missile_group(game, ground_object, faction_name: str): @@ -25,5 +22,9 @@ def generate_missile_group(game, ground_object, faction_name: str): generator.generate() return generator.get_generated_group() else: - logging.info("Unable to generate missile group, generator : " + str(gen) + "does not exists") - return None \ No newline at end of file + logging.info( + "Unable to generate missile group, generator : " + + str(gen) + + "does not exists" + ) + return None diff --git a/gen/missiles/scud_site.py b/gen/missiles/scud_site.py index 6b050c84..eb548c19 100644 --- a/gen/missiles/scud_site.py +++ b/gen/missiles/scud_site.py @@ -6,7 +6,6 @@ from gen.sam.group_generator import GroupGenerator class ScudGenerator(GroupGenerator): - def __init__(self, game, ground_object, faction): super(ScudGenerator, self).__init__(game, ground_object) self.faction = faction @@ -14,17 +13,50 @@ class ScudGenerator(GroupGenerator): def generate(self): # Scuds - self.add_unit(MissilesSS.SRBM_SS_1C_Scud_B_9K72_LN_9P117M, "V1#0", self.position.x, self.position.y + random.randint(1, 8), self.heading) - self.add_unit(MissilesSS.SRBM_SS_1C_Scud_B_9K72_LN_9P117M, "V1#1", self.position.x + 50, self.position.y + random.randint(1, 8), self.heading) - self.add_unit(MissilesSS.SRBM_SS_1C_Scud_B_9K72_LN_9P117M, "V1#2", self.position.x + 100, self.position.y + random.randint(1, 8), self.heading) + self.add_unit( + MissilesSS.SRBM_SS_1C_Scud_B_9K72_LN_9P117M, + "V1#0", + self.position.x, + self.position.y + random.randint(1, 8), + self.heading, + ) + self.add_unit( + MissilesSS.SRBM_SS_1C_Scud_B_9K72_LN_9P117M, + "V1#1", + self.position.x + 50, + self.position.y + random.randint(1, 8), + self.heading, + ) + self.add_unit( + MissilesSS.SRBM_SS_1C_Scud_B_9K72_LN_9P117M, + "V1#2", + self.position.x + 100, + self.position.y + random.randint(1, 8), + self.heading, + ) # Commander - self.add_unit(Unarmed.Transport_UAZ_469, "Kubel#0", self.position.x - 35, self.position.y - 20, - self.heading) + self.add_unit( + Unarmed.Transport_UAZ_469, + "Kubel#0", + self.position.x - 35, + self.position.y - 20, + self.heading, + ) # Shorad - self.add_unit(AirDefence.SPAAA_ZSU_23_4_Shilka, "SHILKA#0", self.position.x - 55, self.position.y - 38, - self.heading) + self.add_unit( + AirDefence.SPAAA_ZSU_23_4_Shilka, + "SHILKA#0", + self.position.x - 55, + self.position.y - 38, + self.heading, + ) - self.add_unit(AirDefence.SAM_SA_9_Strela_1_9P31, "STRELA#0", - self.position.x + 200, self.position.y + 15, 90) \ No newline at end of file + self.add_unit( + AirDefence.SAM_SA_9_Strela_1_9P31, + "STRELA#0", + self.position.x + 200, + self.position.y + 15, + 90, + ) diff --git a/gen/missiles/v1_group.py b/gen/missiles/v1_group.py index a099da08..bf1511a6 100644 --- a/gen/missiles/v1_group.py +++ b/gen/missiles/v1_group.py @@ -6,7 +6,6 @@ from gen.sam.group_generator import GroupGenerator class V1GroupGenerator(GroupGenerator): - def __init__(self, game, ground_object, faction): super(V1GroupGenerator, self).__init__(game, ground_object) self.faction = faction @@ -14,19 +13,54 @@ class V1GroupGenerator(GroupGenerator): def generate(self): # Ramps - self.add_unit(MissilesSS.V_1_ramp, "V1#0", self.position.x, self.position.y + random.randint(1, 8), self.heading) - self.add_unit(MissilesSS.V_1_ramp, "V1#1", self.position.x + 50, self.position.y + random.randint(1, 8), self.heading) - self.add_unit(MissilesSS.V_1_ramp, "V1#2", self.position.x + 100, self.position.y + random.randint(1, 8), self.heading) + self.add_unit( + MissilesSS.V_1_ramp, + "V1#0", + self.position.x, + self.position.y + random.randint(1, 8), + self.heading, + ) + self.add_unit( + MissilesSS.V_1_ramp, + "V1#1", + self.position.x + 50, + self.position.y + random.randint(1, 8), + self.heading, + ) + self.add_unit( + MissilesSS.V_1_ramp, + "V1#2", + self.position.x + 100, + self.position.y + random.randint(1, 8), + self.heading, + ) # Commander - self.add_unit(Unarmed.Kübelwagen_82, "Kubel#0", self.position.x - 35, self.position.y - 20, - self.heading) + self.add_unit( + Unarmed.Kübelwagen_82, + "Kubel#0", + self.position.x - 35, + self.position.y - 20, + self.heading, + ) # Self defense flak - flak_unit = random.choice([AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_Flak_38]) + flak_unit = random.choice( + [AirDefence.AAA_Flak_Vierling_38, AirDefence.AAA_Flak_38] + ) - self.add_unit(flak_unit, "FLAK#0", self.position.x - 55, self.position.y - 38, - self.heading) + self.add_unit( + flak_unit, + "FLAK#0", + self.position.x - 55, + self.position.y - 38, + self.heading, + ) - self.add_unit(Unarmed.Blitz_3_6_6700A, "Blitz#0", - self.position.x + 200, self.position.y + 15, 90) \ No newline at end of file + self.add_unit( + Unarmed.Blitz_3_6_6700A, + "Blitz#0", + self.position.x + 200, + self.position.y + 15, + 90, + ) diff --git a/gen/naming.py b/gen/naming.py index 3e59bbc7..0f65d024 100644 --- a/gen/naming.py +++ b/gen/naming.py @@ -9,39 +9,242 @@ from game import db from gen.flights.flight import Flight -ALPHA_MILITARY = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", - "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", - "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", - "Tango", "Uniform", "Victor", "Whisky", "XRay", "Yankee", - "Zulu", "Zero"] +ALPHA_MILITARY = [ + "Alpha", + "Bravo", + "Charlie", + "Delta", + "Echo", + "Foxtrot", + "Golf", + "Hotel", + "India", + "Juliet", + "Kilo", + "Lima", + "Mike", + "November", + "Oscar", + "Papa", + "Quebec", + "Romeo", + "Sierra", + "Tango", + "Uniform", + "Victor", + "Whisky", + "XRay", + "Yankee", + "Zulu", + "Zero", +] ANIMALS = [ - "SHARK", "TORTOISE", "BAT", "PANGOLIN", "AARDWOLF", - "MONKEY", "BUFFALO", "DOG", "BOBCAT", "LYNX", "PANTHER", "TIGER", - "LION", "OWL", "BUTTERFLY", "BISON", "DUCK", "COBRA", "MAMBA", - "DOLPHIN", "PHEASANT", "ARMADILLLO", "RACOON", "ZEBRA", "COW", "COYOTE", "FOX", - "LIGHTFOOT", "COTTONMOUTH", "TAURUS", "VIPER", "CASTOR", "GIRAFFE", "SNAKE", - "MONSTER", "ALBATROSS", "HAWK", "DOVE", "MOCKINGBIRD", "GECKO", "ORYX", "GORILLA", - "HARAMBE", "GOOSE", "MAVERICK", "HARE", "JACKAL", "LEOPARD", "CAT", "MUSK", "ORCA", - "OCELOT", "BEAR", "PANDA", "GULL", "PENGUIN", "PYTHON", "RAVEN", "DEER", "MOOSE", - "REINDEER", "SHEEP", "GAZELLE", "INSECT", "VULTURE", "WALLABY", "KANGAROO", "KOALA", - "KIWI", "WHALE", "FISH", "RHINO", "HIPPO", "RAT", "WOODPECKER", "WORM", "BABOON", - "YAK", "SCORPIO", "HORSE", "POODLE", "CENTIPEDE", "CHICKEN", "CHEETAH", "CHAMELEON", - "CATFISH", "CATERPILLAR", "CARACAL", "CAMEL", "CAIMAN", "BARRACUDA", "BANDICOOT", - "ALLIGATOR", "BONGO", "CORAL", "ELEPHANT", "ANTELOPE", "CRAB", "DACHSHUND", "DODO", - "FLAMINGO", "FERRET", "FALCON", "BULLDOG", "DONKEY", "IGUANA", "TAMARIN", "HARRIER", - "GRIZZLY", "GREYHOUND", "GRASSHOPPER", "JAGUAR", "LADYBUG", "KOMODO", "DRAGON", "LIZARD", - "LLAMA", "LOBSTER", "OCTOPUS", "MANATEE", "MAGPIE", "MACAW", "OSTRICH", "OYSTER", - "MOLE", "MULE", "MOTH", "MONGOOSE", "MOLLY", "MEERKAT", "MOUSE", "PEACOCK", "PIKE", "ROBIN", - "RAGDOLL", "PLATYPUS", "PELICAN", "PARROT", "PORCUPINE", "PIRANHA", "PUMA", "PUG", "TAPIR", - "TERMITE", "URCHIN", "SHRIMP", "TURKEY", "TOUCAN", "TETRA", "HUSKY", "STARFISH", "SWAN", - "FROG", "SQUIRREL", "WALRUS", "WARTHOG", "CORGI", "WEASEL", "WOMBAT", "WOLVERINE", "MAMMOTH", - "TOAD", "WOLF", "ZEBU", "SEAL", "SKATE", "JELLYFISH", "MOSQUITO", "LOCUST", "SLUG", "SNAIL", - "HEDGEHOG", "PIGLET", "FENNEC", "BADGER", "ALPACA", "DINGO", "COLT", "SKUNK", "BUNNY", "IMPALA", - "GUANACO", "CAPYBARA", "ELK", "MINK", "PRONGHORN", "CROW", "BUMBLEBEE", "FAWN", "OTTER", "WATERBUCK", - "JERBOA", "KITTEN", "ARGALI", "OX", "MARE", "FINCH", "BASILISK", "GOPHER", "HAMSTER", "CANARY", "WOODCHUCK", - "ANACONDA" - ] + "SHARK", + "TORTOISE", + "BAT", + "PANGOLIN", + "AARDWOLF", + "MONKEY", + "BUFFALO", + "DOG", + "BOBCAT", + "LYNX", + "PANTHER", + "TIGER", + "LION", + "OWL", + "BUTTERFLY", + "BISON", + "DUCK", + "COBRA", + "MAMBA", + "DOLPHIN", + "PHEASANT", + "ARMADILLLO", + "RACOON", + "ZEBRA", + "COW", + "COYOTE", + "FOX", + "LIGHTFOOT", + "COTTONMOUTH", + "TAURUS", + "VIPER", + "CASTOR", + "GIRAFFE", + "SNAKE", + "MONSTER", + "ALBATROSS", + "HAWK", + "DOVE", + "MOCKINGBIRD", + "GECKO", + "ORYX", + "GORILLA", + "HARAMBE", + "GOOSE", + "MAVERICK", + "HARE", + "JACKAL", + "LEOPARD", + "CAT", + "MUSK", + "ORCA", + "OCELOT", + "BEAR", + "PANDA", + "GULL", + "PENGUIN", + "PYTHON", + "RAVEN", + "DEER", + "MOOSE", + "REINDEER", + "SHEEP", + "GAZELLE", + "INSECT", + "VULTURE", + "WALLABY", + "KANGAROO", + "KOALA", + "KIWI", + "WHALE", + "FISH", + "RHINO", + "HIPPO", + "RAT", + "WOODPECKER", + "WORM", + "BABOON", + "YAK", + "SCORPIO", + "HORSE", + "POODLE", + "CENTIPEDE", + "CHICKEN", + "CHEETAH", + "CHAMELEON", + "CATFISH", + "CATERPILLAR", + "CARACAL", + "CAMEL", + "CAIMAN", + "BARRACUDA", + "BANDICOOT", + "ALLIGATOR", + "BONGO", + "CORAL", + "ELEPHANT", + "ANTELOPE", + "CRAB", + "DACHSHUND", + "DODO", + "FLAMINGO", + "FERRET", + "FALCON", + "BULLDOG", + "DONKEY", + "IGUANA", + "TAMARIN", + "HARRIER", + "GRIZZLY", + "GREYHOUND", + "GRASSHOPPER", + "JAGUAR", + "LADYBUG", + "KOMODO", + "DRAGON", + "LIZARD", + "LLAMA", + "LOBSTER", + "OCTOPUS", + "MANATEE", + "MAGPIE", + "MACAW", + "OSTRICH", + "OYSTER", + "MOLE", + "MULE", + "MOTH", + "MONGOOSE", + "MOLLY", + "MEERKAT", + "MOUSE", + "PEACOCK", + "PIKE", + "ROBIN", + "RAGDOLL", + "PLATYPUS", + "PELICAN", + "PARROT", + "PORCUPINE", + "PIRANHA", + "PUMA", + "PUG", + "TAPIR", + "TERMITE", + "URCHIN", + "SHRIMP", + "TURKEY", + "TOUCAN", + "TETRA", + "HUSKY", + "STARFISH", + "SWAN", + "FROG", + "SQUIRREL", + "WALRUS", + "WARTHOG", + "CORGI", + "WEASEL", + "WOMBAT", + "WOLVERINE", + "MAMMOTH", + "TOAD", + "WOLF", + "ZEBU", + "SEAL", + "SKATE", + "JELLYFISH", + "MOSQUITO", + "LOCUST", + "SLUG", + "SNAIL", + "HEDGEHOG", + "PIGLET", + "FENNEC", + "BADGER", + "ALPACA", + "DINGO", + "COLT", + "SKUNK", + "BUNNY", + "IMPALA", + "GUANACO", + "CAPYBARA", + "ELK", + "MINK", + "PRONGHORN", + "CROW", + "BUMBLEBEE", + "FAWN", + "OTTER", + "WATERBUCK", + "JERBOA", + "KITTEN", + "ARGALI", + "OX", + "MARE", + "FINCH", + "BASILISK", + "GOPHER", + "HAMSTER", + "CANARY", + "WOODCHUCK", + "ANACONDA", +] + class NameGenerator: number = 0 @@ -72,21 +275,36 @@ class NameGenerator: name_str = flight.custom_name else: name_str = "{} {}".format( - flight.package.target.name, flight.flight_type) + flight.package.target.name, flight.flight_type + ) except AttributeError: # Here to maintain save compatibility with 2.3 - name_str = "{} {}".format( - flight.package.target.name, flight.flight_type) - return "{}|{}|{}|{}|{}|".format(name_str, country.id, cls.aircraft_number, parent_base_id, db.unit_type_name(flight.unit_type)) + name_str = "{} {}".format(flight.package.target.name, flight.flight_type) + return "{}|{}|{}|{}|{}|".format( + name_str, + country.id, + cls.aircraft_number, + parent_base_id, + db.unit_type_name(flight.unit_type), + ) @classmethod def next_unit_name(cls, country: Country, parent_base_id: int, unit_type: UnitType): cls.number += 1 - return "unit|{}|{}|{}|{}|".format(country.id, cls.number, parent_base_id, db.unit_type_name(unit_type)) + return "unit|{}|{}|{}|{}|".format( + country.id, cls.number, parent_base_id, db.unit_type_name(unit_type) + ) @classmethod - def next_infantry_name(cls, country: Country, parent_base_id: int, unit_type: UnitType): + def next_infantry_name( + cls, country: Country, parent_base_id: int, unit_type: UnitType + ): cls.infantry_number += 1 - return "infantry|{}|{}|{}|{}|".format(country.id, cls.infantry_number, parent_base_id, db.unit_type_name(unit_type)) + return "infantry|{}|{}|{}|{}|".format( + country.id, + cls.infantry_number, + parent_base_id, + db.unit_type_name(unit_type), + ) @staticmethod def next_basedefense_name(): @@ -100,7 +318,9 @@ class NameGenerator: @classmethod def next_tanker_name(cls, country: Country, unit_type: UnitType): cls.number += 1 - return "tanker|{}|{}|0|{}".format(country.id, cls.number, db.unit_type_name(unit_type)) + return "tanker|{}|{}|0|{}".format( + country.id, cls.number, db.unit_type_name(unit_type) + ) @classmethod def next_carrier_name(cls, country: Country): @@ -112,7 +332,11 @@ class NameGenerator: if len(cls.ANIMALS) == 0: for i in range(10): new_name_generated = True - alpha_mil_name = random.choice(ALPHA_MILITARY).upper() + "#" + str(random.randint(0, 100)) + alpha_mil_name = ( + random.choice(ALPHA_MILITARY).upper() + + "#" + + str(random.randint(0, 100)) + ) for existing_name in cls.existing_alphas: if existing_name == alpha_mil_name: new_name_generated = False diff --git a/gen/radios.py b/gen/radios.py index e6d413cc..ae72922b 100644 --- a/gen/radios.py +++ b/gen/radios.py @@ -68,9 +68,10 @@ class Radio: def range(self) -> Iterator[RadioFrequency]: """Returns an iterator over the usable frequencies of this radio.""" - return (RadioFrequency(x) for x in range( - self.minimum.hertz, self.maximum.hertz, self.step.hertz - )) + return ( + RadioFrequency(x) + for x in range(self.minimum.hertz, self.maximum.hertz, self.step.hertz) + ) @property def last_channel(self) -> RadioFrequency: @@ -99,14 +100,12 @@ RADIOS: List[Radio] = [ Radio("SCR-522", MHz(100), MHz(156), step=MHz(1)), Radio("A.R.I. 1063", MHz(100), MHz(156), step=MHz(1)), Radio("BC-1206", kHz(200), kHz(400), step=kHz(10)), - # Note: The M2000C V/UHF can operate in both ranges, but has a gap between # 150 MHz and 225 MHz. We can't allocate in that gap, and the current # system doesn't model gaps, so just pretend it ends at 150 MHz for now. We # can model gaps later if needed. Radio("TRT ERA 7000 V/UHF", MHz(118), MHz(150), step=MHz(1)), Radio("TRT ERA 7200 UHF", MHz(225), MHz(400), step=MHz(1)), - # Tomcat radios # # https://www.heatblur.se/F-14Manual/general.html#an-arc-159-uhf-1-radio Radio("AN/ARC-159", MHz(225), MHz(400), step=MHz(1)), @@ -114,31 +113,23 @@ RADIOS: List[Radio] = [ # to 400 MHz range, but we can't model gaps with the current implementation. # https://www.heatblur.se/F-14Manual/general.html#an-arc-182-v-uhf-2-radio Radio("AN/ARC-182", MHz(108), MHz(174), step=MHz(1)), - # Also capable of [103, 156) at 25 kHz intervals, but we can't do gaps. Radio("FR 22", MHz(225), MHz(400), step=kHz(50)), - # P-51 / P-47 Radio # 4 preset channels (A/B/C/D) Radio("SCR522", MHz(100), MHz(156), step=kHz(25)), - Radio("R&S M3AR VHF", MHz(120), MHz(174), step=MHz(1)), Radio("R&S M3AR UHF", MHz(225), MHz(400), step=MHz(1)), - # MiG-15bis Radio("RSI-6K HF", MHz(3, 750), MHz(5), step=kHz(25)), - # MiG-19P Radio("RSIU-4V", MHz(100), MHz(150), step=MHz(1)), - # MiG-21bis Radio("RSIU-5V", MHz(118), MHz(140), step=MHz(1)), - # Ka-50 # Note: Also capable of 100MHz-150MHz, but we can't model gaps. Radio("R-800L1", MHz(220), MHz(400), step=kHz(25)), Radio("R-828", MHz(20), MHz(60), step=kHz(25)), - # UH-1H Radio("AN/ARC-51BX", MHz(225), MHz(400), step=kHz(50)), Radio("AN/ARC-131", MHz(30), MHz(76), step=kHz(50)), @@ -218,7 +209,8 @@ class RadioRegistry: # https://github.com/Khopa/dcs_liberation/issues/598 channel = radio.last_channel logging.warning( - f"No more free channels for {radio.name}. Reusing {channel}.") + f"No more free channels for {radio.name}. Reusing {channel}." + ) return channel def alloc_uhf(self) -> RadioFrequency: diff --git a/gen/runways.py b/gen/runways.py index ab150720..dfb0cebe 100644 --- a/gen/runways.py +++ b/gen/runways.py @@ -25,8 +25,9 @@ class RunwayData: icls: Optional[int] = None @classmethod - def for_airfield(cls, airport: Airport, runway_heading: int, - runway_name: str) -> RunwayData: + def for_airfield( + cls, airport: Airport, runway_heading: int, runway_name: str + ) -> RunwayData: """Creates RunwayData for the given runway of an airfield. Args: @@ -56,7 +57,7 @@ class RunwayData: atc=atc, tacan=tacan, tacan_callsign=tacan_callsign, - ils=ils + ils=ils, ) @classmethod diff --git a/gen/sam/aaa_bofors.py b/gen/sam/aaa_bofors.py index 1d7d18c4..919ec249 100644 --- a/gen/sam/aaa_bofors.py +++ b/gen/sam/aaa_bofors.py @@ -20,15 +20,19 @@ class BoforsGenerator(AirDefenseGroupGenerator): grid_x = random.randint(2, 3) grid_y = random.randint(2, 3) - spacing = random.randint(10,40) + spacing = random.randint(10, 40) index = 0 for i in range(grid_x): for j in range(grid_y): - index = index+1 - self.add_unit(AirDefence.AAA_Bofors_40mm, "AAA#" + str(index), - self.position.x + spacing*i, - self.position.y + spacing*j, self.heading) + index = index + 1 + self.add_unit( + AirDefence.AAA_Bofors_40mm, + "AAA#" + str(index), + self.position.x + spacing * i, + self.position.y + spacing * j, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/aaa_flak.py b/gen/sam/aaa_flak.py index a6acc45a..d3386700 100644 --- a/gen/sam/aaa_flak.py +++ b/gen/sam/aaa_flak.py @@ -37,34 +37,64 @@ class FlakGenerator(AirDefenseGroupGenerator): for i in range(grid_x): for j in range(grid_y): - index = index+1 - self.add_unit(unit_type, "AAA#" + str(index), - self.position.x + spacing*i + random.randint(1,5), - self.position.y + spacing*j + random.randint(1,5), self.heading) + index = index + 1 + self.add_unit( + unit_type, + "AAA#" + str(index), + self.position.x + spacing * i + random.randint(1, 5), + self.position.y + spacing * j + random.randint(1, 5), + self.heading, + ) - if(mixed): + if mixed: unit_type = random.choice(GFLAK) # Search lights - search_pos = self.get_circular_position(random.randint(2,3), 80) + search_pos = self.get_circular_position(random.randint(2, 3), 80) for index, pos in enumerate(search_pos): - self.add_unit(AirDefence.Flak_Searchlight_37, "SearchLight#" + str(index), pos[0], pos[1], self.heading) + self.add_unit( + AirDefence.Flak_Searchlight_37, + "SearchLight#" + str(index), + pos[0], + pos[1], + self.heading, + ) # Support - self.add_unit(AirDefence.Maschinensatz_33, "MC33#", self.position.x-20, self.position.y-20, self.heading) - self.add_unit(AirDefence.AAA_Kdo_G_40, "KDO#", self.position.x - 25, self.position.y - 20, - self.heading) + self.add_unit( + AirDefence.Maschinensatz_33, + "MC33#", + self.position.x - 20, + self.position.y - 20, + self.heading, + ) + self.add_unit( + AirDefence.AAA_Kdo_G_40, + "KDO#", + self.position.x - 25, + self.position.y - 20, + self.heading, + ) # Commander - self.add_unit(Unarmed.Kübelwagen_82, "Kubel#", self.position.x - 35, self.position.y - 20, - self.heading) + self.add_unit( + Unarmed.Kübelwagen_82, + "Kubel#", + self.position.x - 35, + self.position.y - 20, + self.heading, + ) # Some Opel Blitz trucks - for i in range(int(max(1,grid_x/2))): - for j in range(int(max(1,grid_x/2))): - self.add_unit(Unarmed.Blitz_3_6_6700A, "BLITZ#" + str(index), - self.position.x + 125 + 15*i + random.randint(1,5), - self.position.y + 15*j + random.randint(1,5), 75) + for i in range(int(max(1, grid_x / 2))): + for j in range(int(max(1, grid_x / 2))): + self.add_unit( + Unarmed.Blitz_3_6_6700A, + "BLITZ#" + str(index), + self.position.x + 125 + 15 * i + random.randint(1, 5), + self.position.y + 15 * j + random.randint(1, 5), + 75, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/aaa_flak18.py b/gen/sam/aaa_flak18.py index 0716f05a..8dfda229 100644 --- a/gen/sam/aaa_flak18.py +++ b/gen/sam/aaa_flak18.py @@ -24,12 +24,22 @@ class Flak18Generator(AirDefenseGroupGenerator): for i in range(3): for j in range(2): index = index + 1 - self.add_unit(AirDefence.AAA_8_8cm_Flak_18, "AAA#" + str(index), - self.position.x + spacing * i + random.randint(1, 5), - self.position.y + spacing * j + random.randint(1, 5), self.heading) + self.add_unit( + AirDefence.AAA_8_8cm_Flak_18, + "AAA#" + str(index), + self.position.x + spacing * i + random.randint(1, 5), + self.position.y + spacing * j + random.randint(1, 5), + self.heading, + ) # Add a commander truck - self.add_unit(Unarmed.Blitz_3_6_6700A, "Blitz#", self.position.x - 35, self.position.y - 20, self.heading) + self.add_unit( + Unarmed.Blitz_3_6_6700A, + "Blitz#", + self.position.x - 35, + self.position.y - 20, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/aaa_ks19.py b/gen/sam/aaa_ks19.py index c0eb50ab..c733b468 100644 --- a/gen/sam/aaa_ks19.py +++ b/gen/sam/aaa_ks19.py @@ -19,15 +19,25 @@ class KS19Generator(AirDefenseGroupGenerator): spacing = random.randint(10, 40) - self.add_unit(highdigitsams.AAA_SON_9_Fire_Can, "TR", self.position.x - 20, self.position.y - 20, self.heading) + self.add_unit( + highdigitsams.AAA_SON_9_Fire_Can, + "TR", + self.position.x - 20, + self.position.y - 20, + self.heading, + ) index = 0 for i in range(3): for j in range(3): index = index + 1 - self.add_unit(highdigitsams.AAA_100mm_KS_19, "AAA#" + str(index), - self.position.x + spacing * i, - self.position.y + spacing * j, self.heading) + self.add_unit( + highdigitsams.AAA_100mm_KS_19, + "AAA#" + str(index), + self.position.x + spacing * i, + self.position.y + spacing * j, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/aaa_ww2_ally_flak.py b/gen/sam/aaa_ww2_ally_flak.py index 6c2fed26..1d1d06f4 100644 --- a/gen/sam/aaa_ww2_ally_flak.py +++ b/gen/sam/aaa_ww2_ally_flak.py @@ -20,21 +20,63 @@ class AllyWW2FlakGenerator(AirDefenseGroupGenerator): positions = self.get_circular_position(4, launcher_distance=30, coverage=360) for i, position in enumerate(positions): - self.add_unit(AirDefence.AA_gun_QF_3_7, "AA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AA_gun_QF_3_7, + "AA#" + str(i), + position[0], + position[1], + position[2], + ) positions = self.get_circular_position(8, launcher_distance=60, coverage=360) for i, position in enumerate(positions): - self.add_unit(AirDefence.AAA_M1_37mm, "AA#" + str(4 + i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AAA_M1_37mm, + "AA#" + str(4 + i), + position[0], + position[1], + position[2], + ) positions = self.get_circular_position(8, launcher_distance=90, coverage=360) for i, position in enumerate(positions): - self.add_unit(AirDefence.AAA_M45_Quadmount, "AA#" + str(12 + i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AAA_M45_Quadmount, + "AA#" + str(12 + i), + position[0], + position[1], + position[2], + ) # Add a commander truck - self.add_unit(Unarmed.Willys_MB, "CMD#1", self.position.x, self.position.y - 20, random.randint(0, 360)) - self.add_unit(Armor.M30_Cargo_Carrier, "LOG#1", self.position.x, self.position.y + 20, random.randint(0, 360)) - self.add_unit(Armor.M4_Tractor, "LOG#2", self.position.x + 20, self.position.y, random.randint(0, 360)) - self.add_unit(Unarmed.Bedford_MWD, "LOG#3", self.position.x - 20, self.position.y, random.randint(0, 360)) + self.add_unit( + Unarmed.Willys_MB, + "CMD#1", + self.position.x, + self.position.y - 20, + random.randint(0, 360), + ) + self.add_unit( + Armor.M30_Cargo_Carrier, + "LOG#1", + self.position.x, + self.position.y + 20, + random.randint(0, 360), + ) + self.add_unit( + Armor.M4_Tractor, + "LOG#2", + self.position.x + 20, + self.position.y, + random.randint(0, 360), + ) + self.add_unit( + Unarmed.Bedford_MWD, + "LOG#3", + self.position.x - 20, + self.position.y, + random.randint(0, 360), + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/aaa_zsu57.py b/gen/sam/aaa_zsu57.py index 45847018..3c877ee0 100644 --- a/gen/sam/aaa_zsu57.py +++ b/gen/sam/aaa_zsu57.py @@ -16,9 +16,17 @@ class ZSU57Generator(AirDefenseGroupGenerator): def generate(self): num_launchers = 5 - positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=110, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.AAA_ZSU_57_2, "SPAA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AAA_ZSU_57_2, + "SPAA#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/aaa_zu23_insurgent.py b/gen/sam/aaa_zu23_insurgent.py index 85d65290..78ac4218 100644 --- a/gen/sam/aaa_zu23_insurgent.py +++ b/gen/sam/aaa_zu23_insurgent.py @@ -20,15 +20,19 @@ class ZU23InsurgentGenerator(AirDefenseGroupGenerator): grid_x = random.randint(2, 3) grid_y = random.randint(2, 3) - spacing = random.randint(10,40) + spacing = random.randint(10, 40) index = 0 for i in range(grid_x): for j in range(grid_y): - index = index+1 - self.add_unit(AirDefence.AAA_ZU_23_Insurgent_Closed, "AAA#" + str(index), - self.position.x + spacing*i, - self.position.y + spacing*j, self.heading) + index = index + 1 + self.add_unit( + AirDefence.AAA_ZU_23_Insurgent_Closed, + "AAA#" + str(index), + self.position.x + spacing * i, + self.position.y + spacing * j, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/airdefensegroupgenerator.py b/gen/sam/airdefensegroupgenerator.py index 20096046..39d61e8f 100644 --- a/gen/sam/airdefensegroupgenerator.py +++ b/gen/sam/airdefensegroupgenerator.py @@ -28,8 +28,9 @@ class AirDefenseGroupGenerator(GroupGenerator, ABC): self.auxiliary_groups: List[VehicleGroup] = [] def add_auxiliary_group(self, name_suffix: str) -> VehicleGroup: - group = VehicleGroup(self.game.next_group_id(), - "|".join([self.go.group_name, name_suffix])) + group = VehicleGroup( + self.game.next_group_id(), "|".join([self.go.group_name, name_suffix]) + ) self.auxiliary_groups.append(group) return group @@ -37,7 +38,8 @@ class AirDefenseGroupGenerator(GroupGenerator, ABC): raise RuntimeError( "Deprecated call to AirDefenseGroupGenerator.get_generated_group " "misses auxiliary groups. Use AirDefenseGroupGenerator.groups " - "instead.") + "instead." + ) @property def groups(self) -> Iterator[VehicleGroup]: diff --git a/gen/sam/cold_war_flak.py b/gen/sam/cold_war_flak.py index ce1b71d9..81a184f7 100644 --- a/gen/sam/cold_war_flak.py +++ b/gen/sam/cold_war_flak.py @@ -29,18 +29,38 @@ class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator): for i in range(3): for j in range(2): index = index + 1 - self.add_unit(AirDefence.AAA_8_8cm_Flak_18, "AAA#" + str(index), - self.position.x + spacing * i + random.randint(1, 5), - self.position.y + spacing * j + random.randint(1, 5), self.heading) + self.add_unit( + AirDefence.AAA_8_8cm_Flak_18, + "AAA#" + str(index), + self.position.x + spacing * i + random.randint(1, 5), + self.position.y + spacing * j + random.randint(1, 5), + self.heading, + ) # Short range guns - self.add_unit(AirDefence.AAA_Bofors_40mm, "SHO#1", - self.position.x - 40, self.position.y - 40, self.heading + 180), - self.add_unit(AirDefence.AAA_Bofors_40mm, "SHO#2", - self.position.x + spacing * 2 + 40, self.position.y + spacing + 40, self.heading), + self.add_unit( + AirDefence.AAA_Bofors_40mm, + "SHO#1", + self.position.x - 40, + self.position.y - 40, + self.heading + 180, + ), + self.add_unit( + AirDefence.AAA_Bofors_40mm, + "SHO#2", + self.position.x + spacing * 2 + 40, + self.position.y + spacing + 40, + self.heading, + ), # Add a truck - self.add_unit(Unarmed.Transport_KAMAZ_43101, "Truck#", self.position.x - 60, self.position.y - 20, self.heading) + self.add_unit( + Unarmed.Transport_KAMAZ_43101, + "Truck#", + self.position.x - 60, + self.position.y - 20, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: @@ -66,18 +86,38 @@ class ColdWarFlakGenerator(AirDefenseGroupGenerator): for i in range(3): for j in range(2): index = index + 1 - self.add_unit(AirDefence.AAA_8_8cm_Flak_18, "AAA#" + str(index), - self.position.x + spacing * i + random.randint(1, 5), - self.position.y + spacing * j + random.randint(1, 5), self.heading) + self.add_unit( + AirDefence.AAA_8_8cm_Flak_18, + "AAA#" + str(index), + self.position.x + spacing * i + random.randint(1, 5), + self.position.y + spacing * j + random.randint(1, 5), + self.heading, + ) # Short range guns - self.add_unit(AirDefence.AAA_ZU_23_Closed, "SHO#1", - self.position.x - 40, self.position.y - 40, self.heading + 180), - self.add_unit(AirDefence.AAA_ZU_23_Closed, "SHO#2", - self.position.x + spacing * 2 + 40, self.position.y + spacing + 40, self.heading), + self.add_unit( + AirDefence.AAA_ZU_23_Closed, + "SHO#1", + self.position.x - 40, + self.position.y - 40, + self.heading + 180, + ), + self.add_unit( + AirDefence.AAA_ZU_23_Closed, + "SHO#2", + self.position.x + spacing * 2 + 40, + self.position.y + spacing + 40, + self.heading, + ), # Add a P19 Radar for EWR - self.add_unit(AirDefence.SAM_SR_P_19, "SR#0", self.position.x - 60, self.position.y - 20, self.heading) + self.add_unit( + AirDefence.SAM_SR_P_19, + "SR#0", + self.position.x - 60, + self.position.y - 20, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/ewrs.py b/gen/sam/ewrs.py index 2c60af22..ef94f8b2 100644 --- a/gen/sam/ewrs.py +++ b/gen/sam/ewrs.py @@ -10,8 +10,9 @@ class EwrGenerator(GroupGenerator): raise NotImplementedError def generate(self) -> None: - self.add_unit(self.unit_type, "EWR", self.position.x, self.position.y, - self.heading) + self.add_unit( + self.unit_type, "EWR", self.position.x, self.position.y, self.heading + ) class BoxSpringGenerator(EwrGenerator): diff --git a/gen/sam/freya_ewr.py b/gen/sam/freya_ewr.py index f244482b..af637b91 100644 --- a/gen/sam/freya_ewr.py +++ b/gen/sam/freya_ewr.py @@ -17,27 +17,93 @@ class FreyaGenerator(AirDefenseGroupGenerator): def generate(self): # TODO : would be better with the Concrete structure that is supposed to protect it - self.add_unit(AirDefence.EWR_FuMG_401_Freya_LZ, "EWR#1", self.position.x, self.position.y, self.heading) + self.add_unit( + AirDefence.EWR_FuMG_401_Freya_LZ, + "EWR#1", + self.position.x, + self.position.y, + self.heading, + ) positions = self.get_circular_position(4, launcher_distance=50, coverage=360) for i, position in enumerate(positions): - self.add_unit(AirDefence.AAA_Flak_Vierling_38, "AA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AAA_Flak_Vierling_38, + "AA#" + str(i), + position[0], + position[1], + position[2], + ) positions = self.get_circular_position(4, launcher_distance=100, coverage=360) for i, position in enumerate(positions): - self.add_unit(AirDefence.AAA_8_8cm_Flak_18, "AA#" + str(4+i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AAA_8_8cm_Flak_18, + "AA#" + str(4 + i), + position[0], + position[1], + position[2], + ) # Command/Logi - self.add_unit(Unarmed.Kübelwagen_82, "Kubel#1", self.position.x - 20, self.position.y - 20, self.heading) - self.add_unit(Unarmed.Sd_Kfz_7, "Sdkfz#1", self.position.x + 20, self.position.y + 22, self.heading) - self.add_unit(Unarmed.Sd_Kfz_2, "Sdkfz#2", self.position.x - 22, self.position.y + 20, self.heading) + self.add_unit( + Unarmed.Kübelwagen_82, + "Kubel#1", + self.position.x - 20, + self.position.y - 20, + self.heading, + ) + self.add_unit( + Unarmed.Sd_Kfz_7, + "Sdkfz#1", + self.position.x + 20, + self.position.y + 22, + self.heading, + ) + self.add_unit( + Unarmed.Sd_Kfz_2, + "Sdkfz#2", + self.position.x - 22, + self.position.y + 20, + self.heading, + ) # Maschinensatz_33 and Kdo.g 40 Telemeter - self.add_unit(AirDefence.Maschinensatz_33, "Energy#1", self.position.x + 20, self.position.y - 20, self.heading) - self.add_unit(AirDefence.AAA_Kdo_G_40, "Telemeter#1", self.position.x + 20, self.position.y - 10, self.heading) - self.add_unit(Infantry.Infantry_Mauser_98, "Inf#1", self.position.x + 20, self.position.y - 14, self.heading) - self.add_unit(Infantry.Infantry_Mauser_98, "Inf#2", self.position.x + 20, self.position.y - 22, self.heading) - self.add_unit(Infantry.Infantry_Mauser_98, "Inf#3", self.position.x + 20, self.position.y - 24, self.heading + 45) + self.add_unit( + AirDefence.Maschinensatz_33, + "Energy#1", + self.position.x + 20, + self.position.y - 20, + self.heading, + ) + self.add_unit( + AirDefence.AAA_Kdo_G_40, + "Telemeter#1", + self.position.x + 20, + self.position.y - 10, + self.heading, + ) + self.add_unit( + Infantry.Infantry_Mauser_98, + "Inf#1", + self.position.x + 20, + self.position.y - 14, + self.heading, + ) + self.add_unit( + Infantry.Infantry_Mauser_98, + "Inf#2", + self.position.x + 20, + self.position.y - 22, + self.heading, + ) + self.add_unit( + Infantry.Infantry_Mauser_98, + "Inf#3", + self.position.x + 20, + self.position.y - 24, + self.heading + 45, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/group_generator.py b/gen/sam/group_generator.py index be63b777..65eb0b50 100644 --- a/gen/sam/group_generator.py +++ b/gen/sam/group_generator.py @@ -23,14 +23,12 @@ if TYPE_CHECKING: # care about in the format we want if we just generate our own group description # types rather than pydcs groups. class GroupGenerator: - def __init__(self, game: Game, ground_object: TheaterGroundObject) -> None: self.game = game self.go = ground_object self.position = ground_object.position self.heading = random.randint(0, 359) - self.vg = unitgroup.VehicleGroup(self.game.next_group_id(), - self.go.group_name) + self.vg = unitgroup.VehicleGroup(self.game.next_group_id(), self.go.group_name) wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0) wp.ETA_locked = True @@ -40,16 +38,27 @@ class GroupGenerator: def get_generated_group(self) -> unitgroup.VehicleGroup: return self.vg - def add_unit(self, unit_type: Type[VehicleType], name: str, pos_x: float, - pos_y: float, heading: int) -> Vehicle: - return self.add_unit_to_group(self.vg, unit_type, name, - Point(pos_x, pos_y), heading) + def add_unit( + self, + unit_type: Type[VehicleType], + name: str, + pos_x: float, + pos_y: float, + heading: int, + ) -> Vehicle: + return self.add_unit_to_group( + self.vg, unit_type, name, Point(pos_x, pos_y), heading + ) - def add_unit_to_group(self, group: unitgroup.VehicleGroup, - unit_type: Type[VehicleType], name: str, - position: Point, heading: int) -> Vehicle: - unit = Vehicle(self.game.next_unit_id(), - f"{group.name}|{name}", unit_type.id) + def add_unit_to_group( + self, + group: unitgroup.VehicleGroup, + unit_type: Type[VehicleType], + name: str, + position: Point, + heading: int, + ) -> Vehicle: + unit = Vehicle(self.game.next_unit_id(), f"{group.name}|{name}", unit_type.id) unit.position = position unit.heading = heading group.add_unit(unit) @@ -82,31 +91,36 @@ class GroupGenerator: current_offset = self.heading current_offset -= outer_offset * (math.ceil(num_units / 2) - 1) for x in range(1, num_units + 1): - positions.append(( - self.position.x + launcher_distance * math.cos(math.radians(current_offset)), - self.position.y + launcher_distance * math.sin(math.radians(current_offset)), - current_offset, - )) + positions.append( + ( + self.position.x + + launcher_distance * math.cos(math.radians(current_offset)), + self.position.y + + launcher_distance * math.sin(math.radians(current_offset)), + current_offset, + ) + ) current_offset += outer_offset return positions class ShipGroupGenerator(GroupGenerator): """Abstract class for other ship generator classes""" - def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): + + def __init__( + self, game: Game, ground_object: TheaterGroundObject, faction: Faction + ): self.game = game self.go = ground_object self.position = ground_object.position self.heading = random.randint(0, 359) self.faction = faction - self.vg = unitgroup.ShipGroup(self.game.next_group_id(), - self.go.group_name) + self.vg = unitgroup.ShipGroup(self.game.next_group_id(), self.go.group_name) wp = self.vg.add_waypoint(self.position, 0) wp.ETA_locked = True - + def add_unit(self, unit_type, name, pos_x, pos_y, heading) -> Ship: - unit = Ship(self.game.next_unit_id(), - f"{self.go.group_name}|{name}", unit_type) + unit = Ship(self.game.next_unit_id(), f"{self.go.group_name}|{name}", unit_type) unit.position.x = pos_x unit.position.y = pos_y unit.heading = heading diff --git a/gen/sam/sam_avenger.py b/gen/sam/sam_avenger.py index 32d1c228..982b9471 100644 --- a/gen/sam/sam_avenger.py +++ b/gen/sam/sam_avenger.py @@ -19,10 +19,24 @@ class AvengerGenerator(AirDefenseGroupGenerator): def generate(self): num_launchers = random.randint(2, 3) - self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading) - positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180) + self.add_unit( + Unarmed.Transport_M818, + "TRUCK", + self.position.x, + self.position.y, + self.heading, + ) + positions = self.get_circular_position( + num_launchers, launcher_distance=110, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_Avenger_M1097, "SPAA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_Avenger_M1097, + "SPAA#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_chaparral.py b/gen/sam/sam_chaparral.py index 1e768bf4..a0b1ece2 100644 --- a/gen/sam/sam_chaparral.py +++ b/gen/sam/sam_chaparral.py @@ -19,10 +19,24 @@ class ChaparralGenerator(AirDefenseGroupGenerator): def generate(self): num_launchers = random.randint(2, 4) - self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading) - positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180) + self.add_unit( + Unarmed.Transport_M818, + "TRUCK", + self.position.x, + self.position.y, + self.heading, + ) + positions = self.get_circular_position( + num_launchers, launcher_distance=110, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_Chaparral_M48, "SPAA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_Chaparral_M48, + "SPAA#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_gepard.py b/gen/sam/sam_gepard.py index 7e8ef223..df16df31 100644 --- a/gen/sam/sam_gepard.py +++ b/gen/sam/sam_gepard.py @@ -17,10 +17,28 @@ class GepardGenerator(AirDefenseGroupGenerator): price = 50 def generate(self): - self.add_unit(AirDefence.SPAAA_Gepard, "SPAAA", self.position.x, self.position.y, self.heading) + self.add_unit( + AirDefence.SPAAA_Gepard, + "SPAAA", + self.position.x, + self.position.y, + self.heading, + ) if random.randint(0, 1) == 1: - self.add_unit(AirDefence.SPAAA_Gepard, "SPAAA2", self.position.x, self.position.y, self.heading) - self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x + 80, self.position.y, self.heading) + self.add_unit( + AirDefence.SPAAA_Gepard, + "SPAAA2", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + Unarmed.Transport_M818, + "TRUCK", + self.position.x + 80, + self.position.y, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_group_generator.py b/gen/sam/sam_group_generator.py index dfda4048..9d8545fb 100644 --- a/gen/sam/sam_group_generator.py +++ b/gen/sam/sam_group_generator.py @@ -49,7 +49,12 @@ from gen.sam.sam_roland import RolandGenerator from gen.sam.sam_sa10 import ( SA10Generator, Tier2SA10Generator, - Tier3SA10Generator, SA10BGenerator, SA12Generator, SA20Generator, SA20BGenerator, SA23Generator, + Tier3SA10Generator, + SA10BGenerator, + SA12Generator, + SA20Generator, + SA20BGenerator, + SA23Generator, ) from gen.sam.sam_sa11 import SA11Generator from gen.sam.sam_sa13 import SA13Generator @@ -103,7 +108,6 @@ SAM_MAP: Dict[str, Type[AirDefenseGroupGenerator]] = { "FreyaGenerator": FreyaGenerator, "AllyWW2FlakGenerator": AllyWW2FlakGenerator, "ZSU57Generator": ZSU57Generator, - "KS19Generator": KS19Generator, "SA10BGenerator": SA10BGenerator, "SA12Generator": SA12Generator, @@ -145,7 +149,7 @@ SAM_PRICES = { AirDefence.SAM_SA_13_Strela_10M3_9A35M3: 30, AirDefence.SAM_SA_15_Tor_9A331: 40, AirDefence.SAM_SA_19_Tunguska_2S6: 35, - AirDefence.HQ_7_Self_Propelled_LN: 35 + AirDefence.HQ_7_Self_Propelled_LN: 35, } EWR_MAP = { @@ -163,7 +167,8 @@ EWR_MAP = { def get_faction_possible_sams_generator( - faction: Faction) -> List[Type[AirDefenseGroupGenerator]]: + faction: Faction, +) -> List[Type[AirDefenseGroupGenerator]]: """ Return the list of possible SAM generator for the given faction :param faction: Faction name to search units for @@ -180,8 +185,10 @@ def get_faction_possible_ewrs_generator(faction: Faction) -> List[Type[GroupGene def _generate_anti_air_from( - generators: Sequence[Type[AirDefenseGroupGenerator]], game: Game, - ground_object: SamGroundObject) -> List[VehicleGroup]: + generators: Sequence[Type[AirDefenseGroupGenerator]], + game: Game, + ground_object: SamGroundObject, +) -> List[VehicleGroup]: if not generators: return [] sam_generator_class = random.choice(generators) @@ -191,8 +198,10 @@ def _generate_anti_air_from( def generate_anti_air_group( - game: Game, ground_object: SamGroundObject, faction: Faction, - ranges: Optional[Iterable[Set[AirDefenseRange]]] = None + game: Game, + ground_object: SamGroundObject, + faction: Faction, + ranges: Optional[Iterable[Set[AirDefenseRange]]] = None, ) -> List[VehicleGroup]: """ This generate a SAM group @@ -213,24 +222,25 @@ def generate_anti_air_group( """ generators = get_faction_possible_sams_generator(faction) if ranges is None: - ranges = [{ - AirDefenseRange.Long, - AirDefenseRange.Medium, - AirDefenseRange.Short, - }] + ranges = [ + { + AirDefenseRange.Long, + AirDefenseRange.Medium, + AirDefenseRange.Short, + } + ] for range_options in ranges: - generators_for_range = [g for g in generators if - g.range() in range_options] - groups = _generate_anti_air_from(generators_for_range, game, - ground_object) + generators_for_range = [g for g in generators if g.range() in range_options] + groups = _generate_anti_air_from(generators_for_range, game, ground_object) if groups: return groups return [] -def generate_ewr_group(game: Game, ground_object: TheaterGroundObject, - faction: Faction) -> Optional[VehicleGroup]: +def generate_ewr_group( + game: Game, ground_object: TheaterGroundObject, faction: Faction +) -> Optional[VehicleGroup]: """Generates an early warning radar group. :param game: The Game. diff --git a/gen/sam/sam_hawk.py b/gen/sam/sam_hawk.py index 0d526301..651531c0 100644 --- a/gen/sam/sam_hawk.py +++ b/gen/sam/sam_hawk.py @@ -18,20 +18,51 @@ class HawkGenerator(AirDefenseGroupGenerator): price = 115 def generate(self): - self.add_unit(AirDefence.SAM_Hawk_SR_AN_MPQ_50, "SR", self.position.x + 20, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_Hawk_PCP, "PCP", self.position.x, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_Hawk_TR_AN_MPQ_46, "TR", self.position.x + 40, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_Hawk_SR_AN_MPQ_50, + "SR", + self.position.x + 20, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_Hawk_PCP, + "PCP", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_Hawk_TR_AN_MPQ_46, + "TR", + self.position.x + 40, + self.position.y, + self.heading, + ) # Triple A for close range defense aa_group = self.add_auxiliary_group("AA") - self.add_unit_to_group(aa_group, AirDefence.AAA_Vulcan_M163, "AAA", - self.position + Point(20, 30), self.heading) + self.add_unit_to_group( + aa_group, + AirDefence.AAA_Vulcan_M163, + "AAA", + self.position + Point(20, 30), + self.heading, + ) num_launchers = random.randint(3, 6) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_Hawk_LN_M192, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_Hawk_LN_M192, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_hq7.py b/gen/sam/sam_hq7.py index a9f6eb59..06bba42f 100644 --- a/gen/sam/sam_hq7.py +++ b/gen/sam/sam_hq7.py @@ -18,23 +18,51 @@ class HQ7Generator(AirDefenseGroupGenerator): price = 120 def generate(self): - self.add_unit(AirDefence.HQ_7_Self_Propelled_STR, "STR", self.position.x, self.position.y, self.heading) - self.add_unit(AirDefence.HQ_7_Self_Propelled_LN, "LN", self.position.x + 20, self.position.y, self.heading) + self.add_unit( + AirDefence.HQ_7_Self_Propelled_STR, + "STR", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.HQ_7_Self_Propelled_LN, + "LN", + self.position.x + 20, + self.position.y, + self.heading, + ) # Triple A for close range defense aa_group = self.add_auxiliary_group("AA") - self.add_unit_to_group(aa_group, AirDefence.AAA_ZU_23_on_Ural_375, - "AAA1", self.position + Point(20, 30), - self.heading) - self.add_unit_to_group(aa_group, AirDefence.AAA_ZU_23_on_Ural_375, - "AAA2", self.position - Point(20, 30), - self.heading) + self.add_unit_to_group( + aa_group, + AirDefence.AAA_ZU_23_on_Ural_375, + "AAA1", + self.position + Point(20, 30), + self.heading, + ) + self.add_unit_to_group( + aa_group, + AirDefence.AAA_ZU_23_on_Ural_375, + "AAA2", + self.position - Point(20, 30), + self.heading, + ) num_launchers = random.randint(0, 3) if num_launchers > 0: - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.HQ_7_Self_Propelled_LN, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.HQ_7_Self_Propelled_LN, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_linebacker.py b/gen/sam/sam_linebacker.py index e2dae5a1..1fa89ca2 100644 --- a/gen/sam/sam_linebacker.py +++ b/gen/sam/sam_linebacker.py @@ -19,10 +19,24 @@ class LinebackerGenerator(AirDefenseGroupGenerator): def generate(self): num_launchers = random.randint(2, 4) - self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading) - positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180) + self.add_unit( + Unarmed.Transport_M818, + "TRUCK", + self.position.x, + self.position.y, + self.heading, + ) + positions = self.get_circular_position( + num_launchers, launcher_distance=110, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_Linebacker_M6, "M6#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_Linebacker_M6, + "M6#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_patriot.py b/gen/sam/sam_patriot.py index 45fcce1a..21f78b36 100644 --- a/gen/sam/sam_patriot.py +++ b/gen/sam/sam_patriot.py @@ -19,24 +19,65 @@ class PatriotGenerator(AirDefenseGroupGenerator): def generate(self): # Command Post - self.add_unit(AirDefence.SAM_Patriot_STR_AN_MPQ_53, "STR", self.position.x + 30, self.position.y + 30, self.heading) - self.add_unit(AirDefence.SAM_Patriot_AMG_AN_MRC_137, "MRC", self.position.x, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_Patriot_ECS_AN_MSQ_104, "MSQ", self.position.x + 30, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_Patriot_ICC, "ICC", self.position.x + 60, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_Patriot_EPP_III, "EPP", self.position.x, self.position.y + 30, self.heading) + self.add_unit( + AirDefence.SAM_Patriot_STR_AN_MPQ_53, + "STR", + self.position.x + 30, + self.position.y + 30, + self.heading, + ) + self.add_unit( + AirDefence.SAM_Patriot_AMG_AN_MRC_137, + "MRC", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_Patriot_ECS_AN_MSQ_104, + "MSQ", + self.position.x + 30, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_Patriot_ICC, + "ICC", + self.position.x + 60, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_Patriot_EPP_III, + "EPP", + self.position.x, + self.position.y + 30, + self.heading, + ) num_launchers = random.randint(3, 4) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_Patriot_LN_M901, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_Patriot_LN_M901, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) # Short range protection for high value site aa_group = self.add_auxiliary_group("AA") num_launchers = random.randint(3, 4) - positions = self.get_circular_position(num_launchers, launcher_distance=200, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=200, coverage=360 + ) for i, (x, y, heading) in enumerate(positions): - self.add_unit_to_group(aa_group, AirDefence.AAA_Vulcan_M163, - f"SPAAA#{i}", Point(x, y), heading) + self.add_unit_to_group( + aa_group, AirDefence.AAA_Vulcan_M163, f"SPAAA#{i}", Point(x, y), heading + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_rapier.py b/gen/sam/sam_rapier.py index 5b4dbaa9..1ff8f9ce 100644 --- a/gen/sam/sam_rapier.py +++ b/gen/sam/sam_rapier.py @@ -17,14 +17,34 @@ class RapierGenerator(AirDefenseGroupGenerator): price = 50 def generate(self): - self.add_unit(AirDefence.Rapier_FSA_Blindfire_Tracker, "BT", self.position.x, self.position.y, self.heading) - self.add_unit(AirDefence.Rapier_FSA_Optical_Tracker, "OT", self.position.x + 20, self.position.y, self.heading) + self.add_unit( + AirDefence.Rapier_FSA_Blindfire_Tracker, + "BT", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.Rapier_FSA_Optical_Tracker, + "OT", + self.position.x + 20, + self.position.y, + self.heading, + ) num_launchers = random.randint(3, 6) - positions = self.get_circular_position(num_launchers, launcher_distance=80, coverage=240) + positions = self.get_circular_position( + num_launchers, launcher_distance=80, coverage=240 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.Rapier_FSA_Launcher, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.Rapier_FSA_Launcher, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_roland.py b/gen/sam/sam_roland.py index 3c2685c7..26c1bcdf 100644 --- a/gen/sam/sam_roland.py +++ b/gen/sam/sam_roland.py @@ -15,9 +15,27 @@ class RolandGenerator(AirDefenseGroupGenerator): price = 40 def generate(self): - self.add_unit(AirDefence.SAM_Roland_EWR, "EWR", self.position.x + 40, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_Roland_ADS, "ADS", self.position.x, self.position.y, self.heading) - self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x + 80, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_Roland_EWR, + "EWR", + self.position.x + 40, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_Roland_ADS, + "ADS", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + Unarmed.Transport_M818, + "TRUCK", + self.position.x + 80, + self.position.y, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa10.py b/gen/sam/sam_sa10.py index 9a7e7b02..66ad9ca0 100644 --- a/gen/sam/sam_sa10.py +++ b/gen/sam/sam_sa10.py @@ -33,28 +33,41 @@ class SA10Generator(AirDefenseGroupGenerator): def generate(self): # Search Radar - self.add_unit(self.sr1, "SR1", self.position.x, self.position.y + 40, self.heading) + self.add_unit( + self.sr1, "SR1", self.position.x, self.position.y + 40, self.heading + ) # Search radar for missiles (optionnal) - self.add_unit(self.sr2, "SR2", self.position.x - 40, self.position.y, self.heading) + self.add_unit( + self.sr2, "SR2", self.position.x - 40, self.position.y, self.heading + ) # Command Post self.add_unit(self.cp, "CP", self.position.x, self.position.y, self.heading) # 2 Tracking radars - self.add_unit(self.tr1, "TR1", self.position.x - 40, self.position.y - 40, self.heading) + self.add_unit( + self.tr1, "TR1", self.position.x - 40, self.position.y - 40, self.heading + ) - self.add_unit(self.tr2, "TR2", self.position.x + 40, self.position.y - 40, - self.heading) + self.add_unit( + self.tr2, "TR2", self.position.x + 40, self.position.y - 40, self.heading + ) # 2 different launcher type (C & D) num_launchers = random.randint(6, 8) - positions = self.get_circular_position(num_launchers, launcher_distance=100, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=100, coverage=360 + ) for i, position in enumerate(positions): if i % 2 == 0: - self.add_unit(self.ln1, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + self.ln1, "LN#" + str(i), position[0], position[1], position[2] + ) else: - self.add_unit(self.ln2, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + self.ln2, "LN#" + str(i), position[0], position[1], position[2] + ) self.generate_defensive_groups() @@ -67,10 +80,16 @@ class SA10Generator(AirDefenseGroupGenerator): aa_group = self.add_auxiliary_group("AA") num_launchers = random.randint(6, 8) positions = self.get_circular_position( - num_launchers, launcher_distance=210, coverage=360) + num_launchers, launcher_distance=210, coverage=360 + ) for i, (x, y, heading) in enumerate(positions): - self.add_unit_to_group(aa_group, AirDefence.SPAAA_ZSU_23_4_Shilka, - f"AA#{i}", Point(x, y), heading) + self.add_unit_to_group( + aa_group, + AirDefence.SPAAA_ZSU_23_4_Shilka, + f"AA#{i}", + Point(x, y), + heading, + ) class Tier2SA10Generator(SA10Generator): @@ -86,10 +105,16 @@ class Tier2SA10Generator(SA10Generator): pd_group = self.add_auxiliary_group("PD") num_launchers = random.randint(2, 4) positions = self.get_circular_position( - num_launchers, launcher_distance=140, coverage=360) + num_launchers, launcher_distance=140, coverage=360 + ) for i, (x, y, heading) in enumerate(positions): - self.add_unit_to_group(pd_group, AirDefence.SAM_SA_15_Tor_9A331, - f"PD#{i}", Point(x, y), heading) + self.add_unit_to_group( + pd_group, + AirDefence.SAM_SA_15_Tor_9A331, + f"PD#{i}", + Point(x, y), + heading, + ) class Tier3SA10Generator(SA10Generator): @@ -102,19 +127,31 @@ class Tier3SA10Generator(SA10Generator): aa_group = self.add_auxiliary_group("AA") num_launchers = random.randint(6, 8) positions = self.get_circular_position( - num_launchers, launcher_distance=210, coverage=360) + num_launchers, launcher_distance=210, coverage=360 + ) for i, (x, y, heading) in enumerate(positions): - self.add_unit_to_group(aa_group, AirDefence.SAM_SA_19_Tunguska_2S6, - f"AA#{i}", Point(x, y), heading) + self.add_unit_to_group( + aa_group, + AirDefence.SAM_SA_19_Tunguska_2S6, + f"AA#{i}", + Point(x, y), + heading, + ) # SA-15 for both shorter range targets and point defense. pd_group = self.add_auxiliary_group("PD") num_launchers = random.randint(2, 4) positions = self.get_circular_position( - num_launchers, launcher_distance=140, coverage=360) + num_launchers, launcher_distance=140, coverage=360 + ) for i, (x, y, heading) in enumerate(positions): - self.add_unit_to_group(pd_group, AirDefence.SAM_SA_15_Tor_9A331, - f"PD#{i}", Point(x, y), heading) + self.add_unit_to_group( + pd_group, + AirDefence.SAM_SA_15_Tor_9A331, + f"PD#{i}", + Point(x, y), + heading, + ) class SA10BGenerator(Tier3SA10Generator): @@ -194,4 +231,4 @@ class SA23Generator(Tier3SA10Generator): self.tr1 = highdigitsams.SAM_SA_23_S_300VM_9S32ME_TR self.tr2 = highdigitsams.SAM_SA_23_S_300VM_9S32ME_TR self.ln1 = highdigitsams.SAM_SA_23_S_300VM_9A82ME_LN - self.ln2 = highdigitsams.SAM_SA_23_S_300VM_9A83ME_LN \ No newline at end of file + self.ln2 = highdigitsams.SAM_SA_23_S_300VM_9A83ME_LN diff --git a/gen/sam/sam_sa11.py b/gen/sam/sam_sa11.py index 2fd5a08f..adf0362e 100644 --- a/gen/sam/sam_sa11.py +++ b/gen/sam/sam_sa11.py @@ -17,14 +17,34 @@ class SA11Generator(AirDefenseGroupGenerator): price = 180 def generate(self): - self.add_unit(AirDefence.SAM_SA_11_Buk_SR_9S18M1, "SR", self.position.x+20, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_SA_11_Buk_CC_9S470M1, "CC", self.position.x, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SA_11_Buk_SR_9S18M1, + "SR", + self.position.x + 20, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_SA_11_Buk_CC_9S470M1, + "CC", + self.position.x, + self.position.y, + self.heading, + ) num_launchers = random.randint(2, 4) - positions = self.get_circular_position(num_launchers, launcher_distance=140, coverage=180) + positions = self.get_circular_position( + num_launchers, launcher_distance=140, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_SA_11_Buk_LN_9A310M1, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_SA_11_Buk_LN_9A310M1, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa13.py b/gen/sam/sam_sa13.py index ec7b3693..dec20085 100644 --- a/gen/sam/sam_sa13.py +++ b/gen/sam/sam_sa13.py @@ -17,13 +17,33 @@ class SA13Generator(AirDefenseGroupGenerator): price = 50 def generate(self): - self.add_unit(Unarmed.Transport_UAZ_469, "UAZ", self.position.x, self.position.y, self.heading) - self.add_unit(Unarmed.Transport_KAMAZ_43101, "TRUCK", self.position.x+40, self.position.y, self.heading) + self.add_unit( + Unarmed.Transport_UAZ_469, + "UAZ", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + Unarmed.Transport_KAMAZ_43101, + "TRUCK", + self.position.x + 40, + self.position.y, + self.heading, + ) num_launchers = random.randint(2, 3) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_SA_13_Strela_10M3_9A35M3, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_SA_13_Strela_10M3_9A35M3, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa15.py b/gen/sam/sam_sa15.py index 30eaabfe..ffd45752 100644 --- a/gen/sam/sam_sa15.py +++ b/gen/sam/sam_sa15.py @@ -15,10 +15,28 @@ class SA15Generator(AirDefenseGroupGenerator): price = 55 def generate(self): - self.add_unit(AirDefence.SAM_SA_15_Tor_9A331, "ADS", self.position.x, self.position.y, self.heading) - self.add_unit(Unarmed.Transport_UAZ_469, "EWR", self.position.x + 40, self.position.y, self.heading) - self.add_unit(Unarmed.Transport_KAMAZ_43101, "TRUCK", self.position.x + 80, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SA_15_Tor_9A331, + "ADS", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + Unarmed.Transport_UAZ_469, + "EWR", + self.position.x + 40, + self.position.y, + self.heading, + ) + self.add_unit( + Unarmed.Transport_KAMAZ_43101, + "TRUCK", + self.position.x + 80, + self.position.y, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: - return AirDefenseRange.Medium \ No newline at end of file + return AirDefenseRange.Medium diff --git a/gen/sam/sam_sa17.py b/gen/sam/sam_sa17.py index eced94bf..52922646 100644 --- a/gen/sam/sam_sa17.py +++ b/gen/sam/sam_sa17.py @@ -16,14 +16,31 @@ class SA17Generator(AirDefenseGroupGenerator): price = 180 def generate(self): - self.add_unit(AirDefence.SAM_SA_11_Buk_SR_9S18M1, "SR", self.position.x + 20, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_SA_11_Buk_CC_9S470M1, "CC", self.position.x, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SA_11_Buk_SR_9S18M1, + "SR", + self.position.x + 20, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_SA_11_Buk_CC_9S470M1, + "CC", + self.position.x, + self.position.y, + self.heading, + ) positions = self.get_circular_position(3, launcher_distance=140, coverage=180) for i, position in enumerate(positions): - self.add_unit(highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2, "LN#" + str(i), position[0], position[1], - position[2]) + self.add_unit( + highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa19.py b/gen/sam/sam_sa19.py index 298ae91c..02a3ff24 100644 --- a/gen/sam/sam_sa19.py +++ b/gen/sam/sam_sa19.py @@ -20,11 +20,25 @@ class SA19Generator(AirDefenseGroupGenerator): num_launchers = random.randint(1, 3) if num_launchers == 1: - self.add_unit(AirDefence.SAM_SA_19_Tunguska_2S6, "LN#0", self.position.x, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SA_19_Tunguska_2S6, + "LN#0", + self.position.x, + self.position.y, + self.heading, + ) else: - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_SA_19_Tunguska_2S6, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_SA_19_Tunguska_2S6, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa2.py b/gen/sam/sam_sa2.py index c95da151..4307371e 100644 --- a/gen/sam/sam_sa2.py +++ b/gen/sam/sam_sa2.py @@ -17,14 +17,30 @@ class SA2Generator(AirDefenseGroupGenerator): price = 74 def generate(self): - self.add_unit(AirDefence.SAM_SR_P_19, "SR", self.position.x, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song, "TR", self.position.x + 20, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SR_P_19, "SR", self.position.x, self.position.y, self.heading + ) + self.add_unit( + AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song, + "TR", + self.position.x + 20, + self.position.y, + self.heading, + ) num_launchers = random.randint(3, 6) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_SA_2_LN_SM_90, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_SA_2_LN_SM_90, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa3.py b/gen/sam/sam_sa3.py index 8ab5cad3..eb689de5 100644 --- a/gen/sam/sam_sa3.py +++ b/gen/sam/sam_sa3.py @@ -17,14 +17,30 @@ class SA3Generator(AirDefenseGroupGenerator): price = 80 def generate(self): - self.add_unit(AirDefence.SAM_SR_P_19, "SR", self.position.x, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_SA_3_S_125_TR_SNR, "TR", self.position.x + 20, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SR_P_19, "SR", self.position.x, self.position.y, self.heading + ) + self.add_unit( + AirDefence.SAM_SA_3_S_125_TR_SNR, + "TR", + self.position.x + 20, + self.position.y, + self.heading, + ) num_launchers = random.randint(3, 6) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_SA_3_S_125_LN_5P73, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_SA_3_S_125_LN_5P73, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa6.py b/gen/sam/sam_sa6.py index fab5f01b..eb084aa1 100644 --- a/gen/sam/sam_sa6.py +++ b/gen/sam/sam_sa6.py @@ -17,13 +17,27 @@ class SA6Generator(AirDefenseGroupGenerator): price = 102 def generate(self): - self.add_unit(AirDefence.SAM_SA_6_Kub_STR_9S91, "STR", self.position.x, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SA_6_Kub_STR_9S91, + "STR", + self.position.x, + self.position.y, + self.heading, + ) num_launchers = random.randint(2, 4) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_SA_6_Kub_LN_2P25, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_SA_6_Kub_LN_2P25, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa8.py b/gen/sam/sam_sa8.py index 2dd104ee..3bf14f2e 100644 --- a/gen/sam/sam_sa8.py +++ b/gen/sam/sam_sa8.py @@ -15,8 +15,20 @@ class SA8Generator(AirDefenseGroupGenerator): price = 55 def generate(self): - self.add_unit(AirDefence.SAM_SA_8_Osa_9A33, "OSA", self.position.x, self.position.y, self.heading) - self.add_unit(AirDefence.SAM_SA_8_Osa_LD_9T217, "LD", self.position.x + 20, self.position.y, self.heading) + self.add_unit( + AirDefence.SAM_SA_8_Osa_9A33, + "OSA", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + AirDefence.SAM_SA_8_Osa_LD_9T217, + "LD", + self.position.x + 20, + self.position.y, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_sa9.py b/gen/sam/sam_sa9.py index f1cfaff7..0051a03a 100644 --- a/gen/sam/sam_sa9.py +++ b/gen/sam/sam_sa9.py @@ -17,13 +17,33 @@ class SA9Generator(AirDefenseGroupGenerator): price = 40 def generate(self): - self.add_unit(Unarmed.Transport_UAZ_469, "UAZ", self.position.x, self.position.y, self.heading) - self.add_unit(Unarmed.Transport_KAMAZ_43101, "TRUCK", self.position.x+40, self.position.y, self.heading) + self.add_unit( + Unarmed.Transport_UAZ_469, + "UAZ", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + Unarmed.Transport_KAMAZ_43101, + "TRUCK", + self.position.x + 40, + self.position.y, + self.heading, + ) num_launchers = random.randint(2, 3) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SAM_SA_9_Strela_1_9P31, "LN#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SAM_SA_9_Strela_1_9P31, + "LN#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_vulcan.py b/gen/sam/sam_vulcan.py index 5b67d878..44364189 100644 --- a/gen/sam/sam_vulcan.py +++ b/gen/sam/sam_vulcan.py @@ -17,12 +17,29 @@ class VulcanGenerator(AirDefenseGroupGenerator): price = 25 def generate(self): - self.add_unit(AirDefence.AAA_Vulcan_M163, "SPAAA", self.position.x, self.position.y, self.heading) + self.add_unit( + AirDefence.AAA_Vulcan_M163, + "SPAAA", + self.position.x, + self.position.y, + self.heading, + ) if random.randint(0, 1) == 1: - self.add_unit(AirDefence.AAA_Vulcan_M163, "SPAAA2", self.position.x, self.position.y, self.heading) - self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x + 80, self.position.y, self.heading) + self.add_unit( + AirDefence.AAA_Vulcan_M163, + "SPAAA2", + self.position.x, + self.position.y, + self.heading, + ) + self.add_unit( + Unarmed.Transport_M818, + "TRUCK", + self.position.x + 80, + self.position.y, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: return AirDefenseRange.Short - diff --git a/gen/sam/sam_zsu23.py b/gen/sam/sam_zsu23.py index c25a9295..aa70e54b 100644 --- a/gen/sam/sam_zsu23.py +++ b/gen/sam/sam_zsu23.py @@ -19,9 +19,17 @@ class ZSU23Generator(AirDefenseGroupGenerator): def generate(self): num_launchers = random.randint(4, 5) - positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + positions = self.get_circular_position( + num_launchers, launcher_distance=120, coverage=180 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.SPAAA_ZSU_23_4_Shilka, "SPAA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.SPAAA_ZSU_23_4_Shilka, + "SPAA#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_zu23.py b/gen/sam/sam_zu23.py index 494c436d..b17367f4 100644 --- a/gen/sam/sam_zu23.py +++ b/gen/sam/sam_zu23.py @@ -20,15 +20,19 @@ class ZU23Generator(AirDefenseGroupGenerator): grid_x = random.randint(2, 3) grid_y = random.randint(2, 3) - spacing = random.randint(10,40) + spacing = random.randint(10, 40) index = 0 for i in range(grid_x): for j in range(grid_y): - index = index+1 - self.add_unit(AirDefence.AAA_ZU_23_Closed, "AAA#" + str(index), - self.position.x + spacing*i, - self.position.y + spacing*j, self.heading) + index = index + 1 + self.add_unit( + AirDefence.AAA_ZU_23_Closed, + "AAA#" + str(index), + self.position.x + spacing * i, + self.position.y + spacing * j, + self.heading, + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_zu23_ural.py b/gen/sam/sam_zu23_ural.py index 2f26436b..5f075f49 100644 --- a/gen/sam/sam_zu23_ural.py +++ b/gen/sam/sam_zu23_ural.py @@ -19,9 +19,17 @@ class ZU23UralGenerator(AirDefenseGroupGenerator): def generate(self): num_launchers = random.randint(2, 8) - positions = self.get_circular_position(num_launchers, launcher_distance=80, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=80, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.AAA_ZU_23_on_Ural_375, "SPAA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AAA_ZU_23_on_Ural_375, + "SPAA#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: diff --git a/gen/sam/sam_zu23_ural_insurgent.py b/gen/sam/sam_zu23_ural_insurgent.py index d8c26995..3e8282e0 100644 --- a/gen/sam/sam_zu23_ural_insurgent.py +++ b/gen/sam/sam_zu23_ural_insurgent.py @@ -19,11 +19,18 @@ class ZU23UralInsurgentGenerator(AirDefenseGroupGenerator): def generate(self): num_launchers = random.randint(2, 8) - positions = self.get_circular_position(num_launchers, launcher_distance=80, coverage=360) + positions = self.get_circular_position( + num_launchers, launcher_distance=80, coverage=360 + ) for i, position in enumerate(positions): - self.add_unit(AirDefence.AAA_ZU_23_Insurgent_on_Ural_375, "SPAA#" + str(i), position[0], position[1], position[2]) + self.add_unit( + AirDefence.AAA_ZU_23_Insurgent_on_Ural_375, + "SPAA#" + str(i), + position[0], + position[1], + position[2], + ) @classmethod def range(cls) -> AirDefenseRange: return AirDefenseRange.Short - diff --git a/gen/triggergen.py b/gen/triggergen.py index 6a24d703..7df4838f 100644 --- a/gen/triggergen.py +++ b/gen/triggergen.py @@ -2,18 +2,13 @@ from __future__ import annotations from typing import TYPE_CHECKING -from dcs.action import ( - MarkToAll, - SetFlag, - DoScript, - ClearFlag -) +from dcs.action import MarkToAll, SetFlag, DoScript, ClearFlag from dcs.condition import ( TimeAfter, AllOfCoalitionOutsideZone, PartOfCoalitionInZone, FlagIsFalse, - FlagIsTrue + FlagIsTrue, ) from dcs.unitgroup import FlyingGroup from dcs.mission import Mission @@ -56,7 +51,7 @@ class Silence(Option): class TriggersGenerator: - capture_zone_types = (Fob, ) + capture_zone_types = (Fob,) capture_zone_flag = 600 def __init__(self, mission: Mission, game: Game): @@ -82,16 +77,18 @@ class TriggersGenerator: airport.operating_level_air = 0 airport.operating_level_equipment = 0 airport.operating_level_fuel = 0 - + for airport in self.mission.terrain.airport_list(): if airport.id not in cp_ids: airport.unlimited_fuel = True airport.unlimited_munitions = True airport.unlimited_aircrafts = True - + for cp in self.game.theater.controlpoints: if isinstance(cp, Airfield): - self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition) + self.mission.terrain.airport_by_id(cp.at.id).set_coalition( + cp.captured and player_coalition or enemy_coalition + ) def _set_skill(self, player_coalition: str, enemy_coalition: str): """ @@ -99,17 +96,28 @@ class TriggersGenerator: """ for coalition_name, coalition in self.mission.coalition.items(): if coalition_name == player_coalition: - skill_level = self.game.settings.player_skill, self.game.settings.player_skill + skill_level = ( + self.game.settings.player_skill, + self.game.settings.player_skill, + ) elif coalition_name == enemy_coalition: - skill_level = self.game.settings.enemy_skill, self.game.settings.enemy_vehicle_skill + skill_level = ( + self.game.settings.enemy_skill, + self.game.settings.enemy_vehicle_skill, + ) else: continue for country in coalition.countries.values(): - flying_groups = country.plane_group + country.helicopter_group # type: FlyingGroup + flying_groups = ( + country.plane_group + country.helicopter_group + ) # type: FlyingGroup for flying_group in flying_groups: for plane_unit in flying_group.units: - if plane_unit.skill != Skill.Client and plane_unit.skill != Skill.Player: + if ( + plane_unit.skill != Skill.Client + and plane_unit.skill != Skill.Player + ): plane_unit.skill = Skill(skill_level[0]) for vehicle_group in country.vehicle_group: @@ -127,7 +135,9 @@ class TriggersGenerator: added = [] for ground_object in cp.ground_objects: if ground_object.obj_name not in added: - zone = self.mission.triggers.add_triggerzone(ground_object.position, radius=10, hidden=True, name="MARK") + zone = self.mission.triggers.add_triggerzone( + ground_object.position, radius=10, hidden=True, name="MARK" + ) if cp.captured: name = ground_object.obj_name + " [ALLY]" else: @@ -137,7 +147,9 @@ class TriggersGenerator: added.append(ground_object.obj_name) self.mission.triggerrules.triggers.append(mark_trigger) - def _generate_capture_triggers(self, player_coalition: str, enemy_coalition: str) -> None: + def _generate_capture_triggers( + self, player_coalition: str, enemy_coalition: str + ) -> None: """Creates a pair of triggers for each control point of `cls.capture_zone_types`. One for the initial capture of a control point, and one if it is recaptured. Directly appends to the global `base_capture_events` var declared by `dcs_libaration.lua` @@ -155,33 +167,41 @@ class TriggersGenerator: defending_coalition = enemy_coalition defend_coalition_int = 1 - trigger_zone = self.mission.triggers.add_triggerzone(cp.position, radius=3000, hidden=False, name="CAPTURE") + trigger_zone = self.mission.triggers.add_triggerzone( + cp.position, radius=3000, hidden=False, name="CAPTURE" + ) flag = self.get_capture_zone_flag() capture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger") - capture_trigger.add_condition(AllOfCoalitionOutsideZone(defending_coalition, trigger_zone.id)) - capture_trigger.add_condition(PartOfCoalitionInZone(attacking_coalition, trigger_zone.id, unit_type="GROUND")) + capture_trigger.add_condition( + AllOfCoalitionOutsideZone(defending_coalition, trigger_zone.id) + ) + capture_trigger.add_condition( + PartOfCoalitionInZone( + attacking_coalition, trigger_zone.id, unit_type="GROUND" + ) + ) capture_trigger.add_condition(FlagIsFalse(flag=flag)) script_string = String( f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{attack_coalition_int}||{cp.full_name}"' ) - capture_trigger.add_action(DoScript( - script_string - ) - ) + capture_trigger.add_action(DoScript(script_string)) capture_trigger.add_action(SetFlag(flag=flag)) self.mission.triggerrules.triggers.append(capture_trigger) recapture_trigger = TriggerCondition(Event.NoEvent, "Capture Trigger") - recapture_trigger.add_condition(AllOfCoalitionOutsideZone(attacking_coalition, trigger_zone.id)) - recapture_trigger.add_condition(PartOfCoalitionInZone(defending_coalition, trigger_zone.id, unit_type="GROUND")) + recapture_trigger.add_condition( + AllOfCoalitionOutsideZone(attacking_coalition, trigger_zone.id) + ) + recapture_trigger.add_condition( + PartOfCoalitionInZone( + defending_coalition, trigger_zone.id, unit_type="GROUND" + ) + ) recapture_trigger.add_condition(FlagIsTrue(flag=flag)) script_string = String( f'base_capture_events[#base_capture_events + 1] = "{cp.id}||{defend_coalition_int}||{cp.full_name}"' ) - recapture_trigger.add_action(DoScript( - script_string - ) - ) + recapture_trigger.add_action(DoScript(script_string)) recapture_trigger.add_action(ClearFlag(flag=flag)) self.mission.triggerrules.triggers.append(recapture_trigger) @@ -190,10 +210,14 @@ class TriggersGenerator: enemy_coalition = "red" player_cp, enemy_cp = self.game.theater.closest_opposing_control_points() - self.mission.coalition["blue"].bullseye = {"x": enemy_cp.position.x, - "y": enemy_cp.position.y} - self.mission.coalition["red"].bullseye = {"x": player_cp.position.x, - "y": player_cp.position.y} + self.mission.coalition["blue"].bullseye = { + "x": enemy_cp.position.x, + "y": enemy_cp.position.y, + } + self.mission.coalition["red"].bullseye = { + "x": player_cp.position.x, + "y": player_cp.position.y, + } self._set_skill(player_coalition, enemy_coalition) self._set_allegiances(player_coalition, enemy_coalition) diff --git a/gen/units.py b/gen/units.py index 005e1576..cfd16ab8 100644 --- a/gen/units.py +++ b/gen/units.py @@ -3,4 +3,4 @@ def meters_to_feet(meters: float) -> float: """Convers meters to feet.""" - return meters * 3.28084 \ No newline at end of file + return meters * 3.28084 diff --git a/gen/visualgen.py b/gen/visualgen.py index e03da467..8c1982fa 100644 --- a/gen/visualgen.py +++ b/gen/visualgen.py @@ -103,7 +103,9 @@ class VisualGenerator: if from_cp.is_global or to_cp.is_global: continue - plane_start, heading, distance = Conflict.frontline_vector(from_cp, to_cp, self.game.theater) + plane_start, heading, distance = Conflict.frontline_vector( + from_cp, to_cp, self.game.theater + ) if not plane_start: continue @@ -112,7 +114,9 @@ class VisualGenerator: for k, v in FRONT_SMOKE_TYPE_CHANCES.items(): if random.randint(0, 100) <= k: - pos = position.random_point_within(FRONT_SMOKE_RANDOM_SPREAD, FRONT_SMOKE_RANDOM_SPREAD) + pos = position.random_point_within( + FRONT_SMOKE_RANDOM_SPREAD, FRONT_SMOKE_RANDOM_SPREAD + ) if not self.game.theater.is_on_land(pos): break @@ -120,7 +124,8 @@ class VisualGenerator: self.mission.country(self.game.enemy_country), "", _type=v, - position=pos) + position=pos, + ) break def _generate_stub_planes(self): @@ -138,7 +143,14 @@ class VisualGenerator: def generate_target_smokes(self, target): spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR - for _ in range(0, int(target.size * DESTINATION_SMOKE_AMOUNT_FACTOR * (1.1 - target.base.strength))): + for _ in range( + 0, + int( + target.size + * DESTINATION_SMOKE_AMOUNT_FACTOR + * (1.1 - target.base.strength) + ), + ): for k, v in DESTINATION_SMOKE_TYPE_CHANCES.items(): if random.randint(0, 100) <= k: position = target.position.random_point_within(0, spread) @@ -149,7 +161,8 @@ class VisualGenerator: self.mission.country(self.game.enemy_country), "", _type=v, - position=position) + position=position, + ) break def generate_transportation_marker(self, at: Point): @@ -157,7 +170,7 @@ class VisualGenerator: self.mission.country(self.game.player_country), "", _type=MarkerSmoke, - position=at + position=at, ) def generate_transportation_destination(self, at: Point): @@ -166,7 +179,7 @@ class VisualGenerator: self.mission.country(self.game.player_country), "", _type=Outpost, - position=at + position=at, ) def generate(self): diff --git a/pydcs_extensions/a4ec/a4ec.py b/pydcs_extensions/a4ec/a4ec.py index 01e0c88f..07acdcf5 100644 --- a/pydcs_extensions/a4ec/a4ec.py +++ b/pydcs_extensions/a4ec/a4ec.py @@ -6,99 +6,412 @@ from dcs.weapons_data import Weapons class WeaponsA4EC: - AN_M57__2__TER_ = {"clsid": "{AN-M57_TER_2_L}", "name": "AN-M57 *2 (TER)", "weight": 273.6} - AN_M57__2__TER__ = {"clsid": "{AN-M57_TER_2_R}", "name": "AN-M57 *2 (TER)", "weight": 273.6} - AN_M57__3__TER_ = {"clsid": "{AN-M57_TER_3_C}", "name": "AN-M57 *3 (TER)", "weight": 386.6} - AN_M57__5__MER_ = {"clsid": "{AN-M57_MER_5_L}", "name": "AN-M57 *5 (MER)", "weight": 664.8} - AN_M57__5__MER__ = {"clsid": "{AN-M57_MER_5_R}", "name": "AN-M57 *5 (MER)", "weight": 664.8} - AN_M57__6__MER_ = {"clsid": "{AN-M57_MER_6_C}", "name": "AN-M57 *6 (MER)", "weight": 777.8} + AN_M57__2__TER_ = { + "clsid": "{AN-M57_TER_2_L}", + "name": "AN-M57 *2 (TER)", + "weight": 273.6, + } + AN_M57__2__TER__ = { + "clsid": "{AN-M57_TER_2_R}", + "name": "AN-M57 *2 (TER)", + "weight": 273.6, + } + AN_M57__3__TER_ = { + "clsid": "{AN-M57_TER_3_C}", + "name": "AN-M57 *3 (TER)", + "weight": 386.6, + } + AN_M57__5__MER_ = { + "clsid": "{AN-M57_MER_5_L}", + "name": "AN-M57 *5 (MER)", + "weight": 664.8, + } + AN_M57__5__MER__ = { + "clsid": "{AN-M57_MER_5_R}", + "name": "AN-M57 *5 (MER)", + "weight": 664.8, + } + AN_M57__6__MER_ = { + "clsid": "{AN-M57_MER_6_C}", + "name": "AN-M57 *6 (MER)", + "weight": 777.8, + } AN_M66A2 = {"clsid": "{AN-M66A2}", "name": "AN-M66A2", "weight": 970.68688} AN_M81 = {"clsid": "{AN-M81}", "name": "AN-M81", "weight": 117.93392} - AN_M81__5__MER_ = {"clsid": "{AN-M81_MER_5_L}", "name": "AN-M81 *5 (MER)", "weight": 689.3} - AN_M81__5__MER__ = {"clsid": "{AN-M81_MER_5_R}", "name": "AN-M81 *5 (MER)", "weight": 689.3} - AN_M81__6__MER_ = {"clsid": "{AN-M81_MER_6_C}", "name": "AN-M81 *6 (MER)", "weight": 807.2} + AN_M81__5__MER_ = { + "clsid": "{AN-M81_MER_5_L}", + "name": "AN-M81 *5 (MER)", + "weight": 689.3, + } + AN_M81__5__MER__ = { + "clsid": "{AN-M81_MER_5_R}", + "name": "AN-M81 *5 (MER)", + "weight": 689.3, + } + AN_M81__6__MER_ = { + "clsid": "{AN-M81_MER_6_C}", + "name": "AN-M81 *6 (MER)", + "weight": 807.2, + } AN_M88 = {"clsid": "{AN-M88}", "name": "AN-M88", "weight": 98.0665904} - AN_M88__5__MER_ = {"clsid": "{AN-M88_MER_5_L}", "name": "AN-M88 *5 (MER)", "weight": 589.8} - AN_M88__5__MER__ = {"clsid": "{AN-M88_MER_5_R}", "name": "AN-M88 *5 (MER)", "weight": 589.8} - AN_M88__6__MER_ = {"clsid": "{AN-M88_MER_6_C}", "name": "AN-M88 *6 (MER)", "weight": 687.8} + AN_M88__5__MER_ = { + "clsid": "{AN-M88_MER_5_L}", + "name": "AN-M88 *5 (MER)", + "weight": 589.8, + } + AN_M88__5__MER__ = { + "clsid": "{AN-M88_MER_5_R}", + "name": "AN-M88 *5 (MER)", + "weight": 589.8, + } + AN_M88__6__MER_ = { + "clsid": "{AN-M88_MER_6_C}", + "name": "AN-M88 *6 (MER)", + "weight": 687.8, + } CBU_1_A = {"clsid": "{CBU-1/A}", "name": "CBU-1/A", "weight": 458.921706} - CBU_1_A__2 = {"clsid": "{CBU-1/A_TER_2_L}", "name": "CBU-1/A *2", "weight": 713.473056} - CBU_1_A__2_ = {"clsid": "{CBU-1/A_TER_2_R}", "name": "CBU-1/A *2", "weight": 713.473056} + CBU_1_A__2 = { + "clsid": "{CBU-1/A_TER_2_L}", + "name": "CBU-1/A *2", + "weight": 713.473056, + } + CBU_1_A__2_ = { + "clsid": "{CBU-1/A_TER_2_R}", + "name": "CBU-1/A *2", + "weight": 713.473056, + } CBU_2B_A = {"clsid": "{CBU-2B/A}", "name": "CBU-2B/A", "weight": 379.543106} - CBU_2B_A__2 = {"clsid": "{CBU-2B/A_TER_2_L}", "name": "CBU-2B/A *2", "weight": 806.686212} - CBU_2B_A__2_ = {"clsid": "{CBU-2B/A_TER_2_R}", "name": "CBU-2B/A *2", "weight": 806.686212} + CBU_2B_A__2 = { + "clsid": "{CBU-2B/A_TER_2_L}", + "name": "CBU-2B/A *2", + "weight": 806.686212, + } + CBU_2B_A__2_ = { + "clsid": "{CBU-2B/A_TER_2_R}", + "name": "CBU-2B/A *2", + "weight": 806.686212, + } CBU_2_A = {"clsid": "{CBU-2/A}", "name": "CBU-2/A", "weight": 343.822736} - CBU_2_A__2 = {"clsid": "{CBU-2/A_TER_2_L}", "name": "CBU-2/A *2", "weight": 735.245472} - CBU_2_A__2_ = {"clsid": "{CBU-2/A_TER_2_R}", "name": "CBU-2/A *2", "weight": 735.245472} - D_704_Refueling_Pod = {"clsid": "{D-704_BUDDY_POD}", "name": "D-704 Refueling Pod", "weight": 1234.532648} - Fuel_Tank_150_gallons = {"clsid": "{DFT-150gal}", "name": "Fuel Tank 150 gallons", "weight": 515.888512} - Fuel_Tank_300_gallons = {"clsid": "{DFT-300gal}", "name": "Fuel Tank 300 gallons", "weight": 991.407336} - Fuel_Tank_300_gallons_ = {"clsid": "{DFT-300gal_LR}", "name": "Fuel Tank 300 gallons", "weight": 998.664808} - Fuel_Tank_400_gallons = {"clsid": "{DFT-400gal}", "name": "Fuel Tank 400 gallons", "weight": 1320.06208} - LAU_10_2___4_ZUNI_MK_71 = {"clsid": "{LAU-10 ZUNI_TER_2_C}", "name": "LAU-10*2 - 4 ZUNI MK 71", "weight": 927.6} - LAU_10_2___4_ZUNI_MK_71_ = {"clsid": "{LAU-10 ZUNI_TER_2_L}", "name": "LAU-10*2 - 4 ZUNI MK 71", "weight": 927.6} - LAU_10_2___4_ZUNI_MK_71__ = {"clsid": "{LAU-10 ZUNI_TER_2_R}", "name": "LAU-10*2 - 4 ZUNI MK 71", "weight": 927.6} - LAU_10_3___4_ZUNI_MK_71 = {"clsid": "{LAU-10 ZUNI_TER_3_C}", "name": "LAU-10*3 - 4 ZUNI MK 71", "weight": 1367.6} - LAU_3_2___19_FFAR_M156_WP = {"clsid": "{LAU-3 FFAR WP156_TER_2_C}", "name": "LAU-3*2 - 19 FFAR M156 WP", "weight": 673.3414512} - LAU_3_2___19_FFAR_M156_WP_ = {"clsid": "{LAU-3 FFAR WP156_TER_2_L}", "name": "LAU-3*2 - 19 FFAR M156 WP", "weight": 673.3414512} - LAU_3_2___19_FFAR_M156_WP__ = {"clsid": "{LAU-3 FFAR WP156_TER_2_R}", "name": "LAU-3*2 - 19 FFAR M156 WP", "weight": 673.3414512} - LAU_3_2___19_FFAR_Mk1_HE = {"clsid": "{LAU-3 FFAR Mk1 HE_TER_2_C}", "name": "LAU-3*2 - 19 FFAR Mk1 HE", "weight": 618.184664} - LAU_3_2___19_FFAR_Mk1_HE_ = {"clsid": "{LAU-3 FFAR Mk1 HE_TER_2_L}", "name": "LAU-3*2 - 19 FFAR Mk1 HE", "weight": 618.184664} - LAU_3_2___19_FFAR_Mk1_HE__ = {"clsid": "{LAU-3 FFAR Mk1 HE_TER_2_R}", "name": "LAU-3*2 - 19 FFAR Mk1 HE", "weight": 618.184664} - LAU_3_2___19_FFAR_Mk5_HEAT = {"clsid": "{LAU-3 FFAR Mk5 HEAT_TER_2_C}", "name": "LAU-3*2 - 19 FFAR Mk5 HEAT", "weight": 619.9083136} - LAU_3_2___19_FFAR_Mk5_HEAT_ = {"clsid": "{LAU-3 FFAR Mk5 HEAT_TER_2_L}", "name": "LAU-3*2 - 19 FFAR Mk5 HEAT", "weight": 619.9083136} - LAU_3_2___19_FFAR_Mk5_HEAT__ = {"clsid": "{LAU-3 FFAR Mk5 HEAT_TER_2_R}", "name": "LAU-3*2 - 19 FFAR Mk5 HEAT", "weight": 619.9083136} - LAU_3_3___19_FFAR_M156_WP = {"clsid": "{LAU-3 FFAR WP156_TER_3_C}", "name": "LAU-3*3 - 19 FFAR M156 WP", "weight": 986.2121768} - LAU_3_3___19_FFAR_Mk1_HE = {"clsid": "{LAU-3 FFAR Mk1 HE_TER_3_C}", "name": "LAU-3*3 - 19 FFAR Mk1 HE", "weight": 903.476996} - LAU_3_3___19_FFAR_Mk5_HEAT = {"clsid": "{LAU-3 FFAR Mk5 HEAT_TER_3_C}", "name": "LAU-3*3 - 19 FFAR Mk5 HEAT", "weight": 906.0624704} - LAU_68_2___7_FFAR_M156_WP = {"clsid": "{LAU-68 FFAR WP156_TER_2_C}", "name": "LAU-68*2 - 7 FFAR M156 WP", "weight": 287.9121136} - LAU_68_2___7_FFAR_M156_WP_ = {"clsid": "{LAU-68 FFAR WP156_TER_2_L}", "name": "LAU-68*2 - 7 FFAR M156 WP", "weight": 287.9121136} - LAU_68_2___7_FFAR_M156_WP__ = {"clsid": "{LAU-68 FFAR WP156_TER_2_R}", "name": "LAU-68*2 - 7 FFAR M156 WP", "weight": 287.9121136} - LAU_68_2___7_FFAR_Mk1_HE = {"clsid": "{LAU-68 FFAR Mk1 HE_TER_2_C}", "name": "LAU-68*2 - 7 FFAR Mk1 HE", "weight": 267.591192} - LAU_68_2___7_FFAR_Mk1_HE_ = {"clsid": "{LAU-68 FFAR Mk1 HE_TER_2_L}", "name": "LAU-68*2 - 7 FFAR Mk1 HE", "weight": 267.591192} - LAU_68_2___7_FFAR_Mk1_HE__ = {"clsid": "{LAU-68 FFAR Mk1 HE_TER_2_R}", "name": "LAU-68*2 - 7 FFAR Mk1 HE", "weight": 267.591192} - LAU_68_2___7_FFAR_Mk5_HEAT = {"clsid": "{LAU-68 FFAR Mk5 HEAT_TER_2_C}", "name": "LAU-68*2 - 7 FFAR Mk5 HEAT", "weight": 268.2262208} - LAU_68_2___7_FFAR_Mk5_HEAT_ = {"clsid": "{LAU-68 FFAR Mk5 HEAT_TER_2_L}", "name": "LAU-68*2 - 7 FFAR Mk5 HEAT", "weight": 268.2262208} - LAU_68_2___7_FFAR_Mk5_HEAT__ = {"clsid": "{LAU-68 FFAR Mk5 HEAT_TER_2_R}", "name": "LAU-68*2 - 7 FFAR Mk5 HEAT", "weight": 268.2262208} - LAU_68_3___7_FFAR_M156_WP = {"clsid": "{LAU-68 FFAR WP156_TER_3_C}", "name": "LAU-68*3 - 7 FFAR M156 WP", "weight": 408.0681704} - LAU_68_3___7_FFAR_Mk1_HE = {"clsid": "{LAU-68 FFAR Mk1 HE_TER_3_C}", "name": "LAU-68*3 - 7 FFAR Mk1 HE", "weight": 377.586788} - LAU_68_3___7_FFAR_Mk5_HEAT = {"clsid": "{LAU-68 FFAR Mk5 HEAT_TER_3_C}", "name": "LAU-68*3 - 7 FFAR Mk5 HEAT", "weight": 378.5393312} + CBU_2_A__2 = { + "clsid": "{CBU-2/A_TER_2_L}", + "name": "CBU-2/A *2", + "weight": 735.245472, + } + CBU_2_A__2_ = { + "clsid": "{CBU-2/A_TER_2_R}", + "name": "CBU-2/A *2", + "weight": 735.245472, + } + D_704_Refueling_Pod = { + "clsid": "{D-704_BUDDY_POD}", + "name": "D-704 Refueling Pod", + "weight": 1234.532648, + } + Fuel_Tank_150_gallons = { + "clsid": "{DFT-150gal}", + "name": "Fuel Tank 150 gallons", + "weight": 515.888512, + } + Fuel_Tank_300_gallons = { + "clsid": "{DFT-300gal}", + "name": "Fuel Tank 300 gallons", + "weight": 991.407336, + } + Fuel_Tank_300_gallons_ = { + "clsid": "{DFT-300gal_LR}", + "name": "Fuel Tank 300 gallons", + "weight": 998.664808, + } + Fuel_Tank_400_gallons = { + "clsid": "{DFT-400gal}", + "name": "Fuel Tank 400 gallons", + "weight": 1320.06208, + } + LAU_10_2___4_ZUNI_MK_71 = { + "clsid": "{LAU-10 ZUNI_TER_2_C}", + "name": "LAU-10*2 - 4 ZUNI MK 71", + "weight": 927.6, + } + LAU_10_2___4_ZUNI_MK_71_ = { + "clsid": "{LAU-10 ZUNI_TER_2_L}", + "name": "LAU-10*2 - 4 ZUNI MK 71", + "weight": 927.6, + } + LAU_10_2___4_ZUNI_MK_71__ = { + "clsid": "{LAU-10 ZUNI_TER_2_R}", + "name": "LAU-10*2 - 4 ZUNI MK 71", + "weight": 927.6, + } + LAU_10_3___4_ZUNI_MK_71 = { + "clsid": "{LAU-10 ZUNI_TER_3_C}", + "name": "LAU-10*3 - 4 ZUNI MK 71", + "weight": 1367.6, + } + LAU_3_2___19_FFAR_M156_WP = { + "clsid": "{LAU-3 FFAR WP156_TER_2_C}", + "name": "LAU-3*2 - 19 FFAR M156 WP", + "weight": 673.3414512, + } + LAU_3_2___19_FFAR_M156_WP_ = { + "clsid": "{LAU-3 FFAR WP156_TER_2_L}", + "name": "LAU-3*2 - 19 FFAR M156 WP", + "weight": 673.3414512, + } + LAU_3_2___19_FFAR_M156_WP__ = { + "clsid": "{LAU-3 FFAR WP156_TER_2_R}", + "name": "LAU-3*2 - 19 FFAR M156 WP", + "weight": 673.3414512, + } + LAU_3_2___19_FFAR_Mk1_HE = { + "clsid": "{LAU-3 FFAR Mk1 HE_TER_2_C}", + "name": "LAU-3*2 - 19 FFAR Mk1 HE", + "weight": 618.184664, + } + LAU_3_2___19_FFAR_Mk1_HE_ = { + "clsid": "{LAU-3 FFAR Mk1 HE_TER_2_L}", + "name": "LAU-3*2 - 19 FFAR Mk1 HE", + "weight": 618.184664, + } + LAU_3_2___19_FFAR_Mk1_HE__ = { + "clsid": "{LAU-3 FFAR Mk1 HE_TER_2_R}", + "name": "LAU-3*2 - 19 FFAR Mk1 HE", + "weight": 618.184664, + } + LAU_3_2___19_FFAR_Mk5_HEAT = { + "clsid": "{LAU-3 FFAR Mk5 HEAT_TER_2_C}", + "name": "LAU-3*2 - 19 FFAR Mk5 HEAT", + "weight": 619.9083136, + } + LAU_3_2___19_FFAR_Mk5_HEAT_ = { + "clsid": "{LAU-3 FFAR Mk5 HEAT_TER_2_L}", + "name": "LAU-3*2 - 19 FFAR Mk5 HEAT", + "weight": 619.9083136, + } + LAU_3_2___19_FFAR_Mk5_HEAT__ = { + "clsid": "{LAU-3 FFAR Mk5 HEAT_TER_2_R}", + "name": "LAU-3*2 - 19 FFAR Mk5 HEAT", + "weight": 619.9083136, + } + LAU_3_3___19_FFAR_M156_WP = { + "clsid": "{LAU-3 FFAR WP156_TER_3_C}", + "name": "LAU-3*3 - 19 FFAR M156 WP", + "weight": 986.2121768, + } + LAU_3_3___19_FFAR_Mk1_HE = { + "clsid": "{LAU-3 FFAR Mk1 HE_TER_3_C}", + "name": "LAU-3*3 - 19 FFAR Mk1 HE", + "weight": 903.476996, + } + LAU_3_3___19_FFAR_Mk5_HEAT = { + "clsid": "{LAU-3 FFAR Mk5 HEAT_TER_3_C}", + "name": "LAU-3*3 - 19 FFAR Mk5 HEAT", + "weight": 906.0624704, + } + LAU_68_2___7_FFAR_M156_WP = { + "clsid": "{LAU-68 FFAR WP156_TER_2_C}", + "name": "LAU-68*2 - 7 FFAR M156 WP", + "weight": 287.9121136, + } + LAU_68_2___7_FFAR_M156_WP_ = { + "clsid": "{LAU-68 FFAR WP156_TER_2_L}", + "name": "LAU-68*2 - 7 FFAR M156 WP", + "weight": 287.9121136, + } + LAU_68_2___7_FFAR_M156_WP__ = { + "clsid": "{LAU-68 FFAR WP156_TER_2_R}", + "name": "LAU-68*2 - 7 FFAR M156 WP", + "weight": 287.9121136, + } + LAU_68_2___7_FFAR_Mk1_HE = { + "clsid": "{LAU-68 FFAR Mk1 HE_TER_2_C}", + "name": "LAU-68*2 - 7 FFAR Mk1 HE", + "weight": 267.591192, + } + LAU_68_2___7_FFAR_Mk1_HE_ = { + "clsid": "{LAU-68 FFAR Mk1 HE_TER_2_L}", + "name": "LAU-68*2 - 7 FFAR Mk1 HE", + "weight": 267.591192, + } + LAU_68_2___7_FFAR_Mk1_HE__ = { + "clsid": "{LAU-68 FFAR Mk1 HE_TER_2_R}", + "name": "LAU-68*2 - 7 FFAR Mk1 HE", + "weight": 267.591192, + } + LAU_68_2___7_FFAR_Mk5_HEAT = { + "clsid": "{LAU-68 FFAR Mk5 HEAT_TER_2_C}", + "name": "LAU-68*2 - 7 FFAR Mk5 HEAT", + "weight": 268.2262208, + } + LAU_68_2___7_FFAR_Mk5_HEAT_ = { + "clsid": "{LAU-68 FFAR Mk5 HEAT_TER_2_L}", + "name": "LAU-68*2 - 7 FFAR Mk5 HEAT", + "weight": 268.2262208, + } + LAU_68_2___7_FFAR_Mk5_HEAT__ = { + "clsid": "{LAU-68 FFAR Mk5 HEAT_TER_2_R}", + "name": "LAU-68*2 - 7 FFAR Mk5 HEAT", + "weight": 268.2262208, + } + LAU_68_3___7_FFAR_M156_WP = { + "clsid": "{LAU-68 FFAR WP156_TER_3_C}", + "name": "LAU-68*3 - 7 FFAR M156 WP", + "weight": 408.0681704, + } + LAU_68_3___7_FFAR_Mk1_HE = { + "clsid": "{LAU-68 FFAR Mk1 HE_TER_3_C}", + "name": "LAU-68*3 - 7 FFAR Mk1 HE", + "weight": 377.586788, + } + LAU_68_3___7_FFAR_Mk5_HEAT = { + "clsid": "{LAU-68 FFAR Mk5 HEAT_TER_3_C}", + "name": "LAU-68*3 - 7 FFAR Mk5 HEAT", + "weight": 378.5393312, + } MAK79_2_MK_20 = {"clsid": "{MAK79_MK20 2L}", "name": "MAK79 2 MK-20", "weight": 464} - MAK79_2_MK_20_ = {"clsid": "{MAK79_MK20 2R}", "name": "MAK79 2 MK-20", "weight": 464} + MAK79_2_MK_20_ = { + "clsid": "{MAK79_MK20 2R}", + "name": "MAK79 2 MK-20", + "weight": 464, + } MAK79_MK_20 = {"clsid": "{MAK79_MK20 1R}", "name": "MAK79 MK-20", "weight": 232} MAK79_MK_20_ = {"clsid": "{MAK79_MK20 1L}", "name": "MAK79 MK-20", "weight": 232} Mk4_HIPEG = {"clsid": "{Mk4 HIPEG}", "name": "Mk4 HIPEG", "weight": 612.35} - Mk_20__2__TER_ = {"clsid": "{Mk-20_TER_2_L}", "name": "Mk-20 *2 (TER)", "weight": 491.6} - Mk_20__2__TER__ = {"clsid": "{Mk-20_TER_2_R}", "name": "Mk-20 *2 (TER)", "weight": 491.6} - Mk_20__2__TER___ = {"clsid": "{Mk-20_TER_2_C}", "name": "Mk-20 *2 (TER)", "weight": 491.6} - Mk_20__3__TER_ = {"clsid": "{Mk-20_TER_3_C}", "name": "Mk-20 *3 (TER)", "weight": 713.6} + Mk_20__2__TER_ = { + "clsid": "{Mk-20_TER_2_L}", + "name": "Mk-20 *2 (TER)", + "weight": 491.6, + } + Mk_20__2__TER__ = { + "clsid": "{Mk-20_TER_2_R}", + "name": "Mk-20 *2 (TER)", + "weight": 491.6, + } + Mk_20__2__TER___ = { + "clsid": "{Mk-20_TER_2_C}", + "name": "Mk-20 *2 (TER)", + "weight": 491.6, + } + Mk_20__3__TER_ = { + "clsid": "{Mk-20_TER_3_C}", + "name": "Mk-20 *3 (TER)", + "weight": 713.6, + } Mk_77_mod_0 = {"clsid": "{mk77mod0}", "name": "Mk-77 mod 0", "weight": 340} Mk_77_mod_1 = {"clsid": "{mk77mod1}", "name": "Mk-77 mod 1", "weight": 230} - Mk_77_mod_1__2__TER_ = {"clsid": "{Mk-77 mod 1_TER_2_L}", "name": "Mk-77 mod 1 *2 (TER)", "weight": 507.6} - Mk_77_mod_1__2__TER__ = {"clsid": "{Mk-77 mod 1_TER_2_R}", "name": "Mk-77 mod 1 *2 (TER)", "weight": 507.6} - Mk_77_mod_1__2__TER___ = {"clsid": "{Mk-77 mod 1_TER_2_C}", "name": "Mk-77 mod 1 *2 (TER)", "weight": 507.6} - Mk_77_mod_1__4__MER_ = {"clsid": "{Mk-77 mod 1_MER_4_C}", "name": "Mk-77 mod 1 *4 (MER)", "weight": 1019.8} + Mk_77_mod_1__2__TER_ = { + "clsid": "{Mk-77 mod 1_TER_2_L}", + "name": "Mk-77 mod 1 *2 (TER)", + "weight": 507.6, + } + Mk_77_mod_1__2__TER__ = { + "clsid": "{Mk-77 mod 1_TER_2_R}", + "name": "Mk-77 mod 1 *2 (TER)", + "weight": 507.6, + } + Mk_77_mod_1__2__TER___ = { + "clsid": "{Mk-77 mod 1_TER_2_C}", + "name": "Mk-77 mod 1 *2 (TER)", + "weight": 507.6, + } + Mk_77_mod_1__4__MER_ = { + "clsid": "{Mk-77 mod 1_MER_4_C}", + "name": "Mk-77 mod 1 *4 (MER)", + "weight": 1019.8, + } Mk_81SE = {"clsid": "{MK-81SE}", "name": "Mk-81SE", "weight": 113.398} - Mk_81SE__5__MER_ = {"clsid": "{Mk-81SE_MER_5_L}", "name": "Mk-81SE *5 (MER)", "weight": 689.8} - Mk_81SE__5__MER__ = {"clsid": "{Mk-81SE_MER_5_R}", "name": "Mk-81SE *5 (MER)", "weight": 689.8} - Mk_81SE__6__MER_ = {"clsid": "{Mk-81SE_MER_6_C}", "name": "Mk-81SE *6 (MER)", "weight": 807.8} - Mk_81__5__MER_ = {"clsid": "{Mk-81_MER_5_L}", "name": "Mk-81 *5 (MER)", "weight": 689.8} - Mk_81__5__MER__ = {"clsid": "{Mk-81_MER_5_R}", "name": "Mk-81 *5 (MER)", "weight": 689.8} - Mk_81__6__MER_ = {"clsid": "{Mk-81_MER_6_C}", "name": "Mk-81 *6 (MER)", "weight": 807.8} - Mk_82_Snakeye__2__TER_ = {"clsid": "{Mk-82 Snakeye_TER_2_L}", "name": "Mk-82 Snakeye *2 (TER)", "weight": 529.6} - Mk_82_Snakeye__2__TER__ = {"clsid": "{Mk-82 Snakeye_TER_2_R}", "name": "Mk-82 Snakeye *2 (TER)", "weight": 529.6} - Mk_82_Snakeye__3__TER_ = {"clsid": "{Mk-82 Snakeye_TER_3_C}", "name": "Mk-82 Snakeye *3 (TER)", "weight": 770.6} - Mk_82_Snakeye__4__MER_ = {"clsid": "{Mk-82 Snakeye_MER_4_C}", "name": "Mk-82 Snakeye *4 (MER)", "weight": 1063.8} - Mk_82_Snakeye__6__MER_ = {"clsid": "{Mk-82 Snakeye_MER_6_C}", "name": "Mk-82 Snakeye *6 (MER)", "weight": 1545.8} - Mk_82__2__TER_ = {"clsid": "{Mk-82_TER_2_L}", "name": "Mk-82 *2 (TER)", "weight": 529.6} - Mk_82__2__TER__ = {"clsid": "{Mk-82_TER_2_R}", "name": "Mk-82 *2 (TER)", "weight": 529.6} - Mk_82__3__TER_ = {"clsid": "{Mk-82_TER_3_C}", "name": "Mk-82 *3 (TER)", "weight": 770.6} - Mk_82__4__MER_ = {"clsid": "{Mk-82_MER_4_C}", "name": "Mk-82 *4 (MER)", "weight": 1063.8} - Mk_82__6__MER_ = {"clsid": "{Mk-82_MER_6_C}", "name": "Mk-82 *6 (MER)", "weight": 1545.8} - Mk_83__2__TER_ = {"clsid": "{Mk-83_TER_2_C}", "name": "Mk-83 *2 (TER)", "weight": 941.6} - Mk_83__3__TER_ = {"clsid": "{Mk-83_TER_3_C}", "name": "Mk-83 *3 (TER)", "weight": 1388.6} + Mk_81SE__5__MER_ = { + "clsid": "{Mk-81SE_MER_5_L}", + "name": "Mk-81SE *5 (MER)", + "weight": 689.8, + } + Mk_81SE__5__MER__ = { + "clsid": "{Mk-81SE_MER_5_R}", + "name": "Mk-81SE *5 (MER)", + "weight": 689.8, + } + Mk_81SE__6__MER_ = { + "clsid": "{Mk-81SE_MER_6_C}", + "name": "Mk-81SE *6 (MER)", + "weight": 807.8, + } + Mk_81__5__MER_ = { + "clsid": "{Mk-81_MER_5_L}", + "name": "Mk-81 *5 (MER)", + "weight": 689.8, + } + Mk_81__5__MER__ = { + "clsid": "{Mk-81_MER_5_R}", + "name": "Mk-81 *5 (MER)", + "weight": 689.8, + } + Mk_81__6__MER_ = { + "clsid": "{Mk-81_MER_6_C}", + "name": "Mk-81 *6 (MER)", + "weight": 807.8, + } + Mk_82_Snakeye__2__TER_ = { + "clsid": "{Mk-82 Snakeye_TER_2_L}", + "name": "Mk-82 Snakeye *2 (TER)", + "weight": 529.6, + } + Mk_82_Snakeye__2__TER__ = { + "clsid": "{Mk-82 Snakeye_TER_2_R}", + "name": "Mk-82 Snakeye *2 (TER)", + "weight": 529.6, + } + Mk_82_Snakeye__3__TER_ = { + "clsid": "{Mk-82 Snakeye_TER_3_C}", + "name": "Mk-82 Snakeye *3 (TER)", + "weight": 770.6, + } + Mk_82_Snakeye__4__MER_ = { + "clsid": "{Mk-82 Snakeye_MER_4_C}", + "name": "Mk-82 Snakeye *4 (MER)", + "weight": 1063.8, + } + Mk_82_Snakeye__6__MER_ = { + "clsid": "{Mk-82 Snakeye_MER_6_C}", + "name": "Mk-82 Snakeye *6 (MER)", + "weight": 1545.8, + } + Mk_82__2__TER_ = { + "clsid": "{Mk-82_TER_2_L}", + "name": "Mk-82 *2 (TER)", + "weight": 529.6, + } + Mk_82__2__TER__ = { + "clsid": "{Mk-82_TER_2_R}", + "name": "Mk-82 *2 (TER)", + "weight": 529.6, + } + Mk_82__3__TER_ = { + "clsid": "{Mk-82_TER_3_C}", + "name": "Mk-82 *3 (TER)", + "weight": 770.6, + } + Mk_82__4__MER_ = { + "clsid": "{Mk-82_MER_4_C}", + "name": "Mk-82 *4 (MER)", + "weight": 1063.8, + } + Mk_82__6__MER_ = { + "clsid": "{Mk-82_MER_6_C}", + "name": "Mk-82 *6 (MER)", + "weight": 1545.8, + } + Mk_83__2__TER_ = { + "clsid": "{Mk-83_TER_2_C}", + "name": "Mk-83 *2 (TER)", + "weight": 941.6, + } + Mk_83__3__TER_ = { + "clsid": "{Mk-83_TER_3_C}", + "name": "Mk-83 *3 (TER)", + "weight": 1388.6, + } _3_LAU_61 = {"clsid": "{TER,LAU-61*3}", "name": "3*LAU-61", "weight": 98} + class A_4E_C(PlaneType): id = "A-4E-C" flyable = True @@ -112,7 +425,7 @@ class A_4E_C(PlaneType): charge_total = 60 chaff_charge_size = 1 flare_charge_size = 1 - category = "Interceptor" #{78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} + category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} radio_frequency = 254 panel_radio = { @@ -137,7 +450,7 @@ class A_4E_C(PlaneType): 7: 257, 14: 263, 19: 266, - 15: 261 + 15: 261, }, }, } @@ -153,7 +466,6 @@ class A_4E_C(PlaneType): } class Properties: - class HideECMPanel: id = "HideECMPanel" @@ -219,7 +531,6 @@ class A_4E_C(PlaneType): _14_seconds = 7 class Liveries: - class Georgia(Enum): Unmarked = "Unmarked" Community_A_4E = "Community A-4E" @@ -425,7 +736,9 @@ class A_4E_C(PlaneType): Aggressor_USN_TopGun = "Aggressor USN TopGun" Aggressor_USN_VF_126_Bandits = "Aggressor USN VF-126 Bandits" Aggressor_USN_VF_127_Royal_Blues = "Aggressor USN VF-127 Royal Blues" - Aggressor_USN_VFA_127_Cyclons__Forest = "Aggressor USN VFA-127 Cyclons (Forest)" + Aggressor_USN_VFA_127_Cyclons__Forest = ( + "Aggressor USN VFA-127 Cyclons (Forest)" + ) Aggressor_USN_VFA_127_Cyclons__Sea = "Aggressor USN VFA-127 Cyclons (Sea)" class USA(Enum): @@ -476,7 +789,9 @@ class A_4E_C(PlaneType): Trainer_USN_VC_8_Redtails = "Trainer USN VC-8 Redtails" Aggressor_USN_VF_126_Bandits = "Aggressor USN VF-126 Bandits" Aggressor_USN_VF_127_Royal_Blues = "Aggressor USN VF-127 Royal Blues" - Aggressor_USN_VFA_127_Cyclons__Forest = "Aggressor USN VFA-127 Cyclons (Forest)" + Aggressor_USN_VFA_127_Cyclons__Forest = ( + "Aggressor USN VFA-127 Cyclons (Forest)" + ) Aggressor_USN_VFA_127_Cyclons__Sea = "Aggressor USN VFA-127 Cyclons (Sea)" Trainer_USN_VT_7_Eagles = "Trainer USN VT-7 Eagles" @@ -524,7 +839,7 @@ class A_4E_C(PlaneType): LAU_68___7_FFAR_Mk1_HE = (1, Weapons.LAU_68___7_FFAR_Mk1_HE) LAU_68___7_FFAR_Mk5_HEAT = (1, Weapons.LAU_68___7_FFAR_Mk5_HEAT) AGM_45A = (1, Weapons.AGM_45A) -#ERRR {AGM12_B} + # ERRR {AGM12_B} Mk_20 = (1, Weapons.Mk_20) Mk_81 = (1, Weapons.Mk_81) Mk_81SE = (1, WeaponsA4EC.Mk_81SE) @@ -536,14 +851,18 @@ class A_4E_C(PlaneType): AN_M64 = (1, Weapons.AN_M64) AN_M81 = (1, WeaponsA4EC.AN_M81) AN_M88 = (1, WeaponsA4EC.AN_M88) - LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = (1, Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_) + LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = ( + 1, + Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_, + ) Smokewinder___red = (1, Weapons.Smokewinder___red) Smokewinder___green = (1, Weapons.Smokewinder___green) Smokewinder___blue = (1, Weapons.Smokewinder___blue) Smokewinder___white = (1, Weapons.Smokewinder___white) Smokewinder___yellow = (1, Weapons.Smokewinder___yellow) Smokewinder___orange = (1, Weapons.Smokewinder___orange) -#ERRR + + # ERRR class Pylon2: Fuel_Tank_300_gallons_ = (2, WeaponsA4EC.Fuel_Tank_300_gallons_) @@ -566,8 +885,8 @@ class A_4E_C(PlaneType): LAU_68_2___7_FFAR_Mk1_HE_ = (2, WeaponsA4EC.LAU_68_2___7_FFAR_Mk1_HE_) LAU_68_2___7_FFAR_Mk5_HEAT_ = (2, WeaponsA4EC.LAU_68_2___7_FFAR_Mk5_HEAT_) AGM_45A = (2, Weapons.AGM_45A) -#ERRR {AGM12_C} -#ERRR {AGM12_B} + # ERRR {AGM12_C} + # ERRR {AGM12_B} AGM_62 = (2, Weapons.AGM_62) Mk_20 = (2, Weapons.Mk_20) Mk_81 = (2, Weapons.Mk_81) @@ -607,15 +926,19 @@ class A_4E_C(PlaneType): Smokewinder___white = (2, Weapons.Smokewinder___white) Smokewinder___yellow = (2, Weapons.Smokewinder___yellow) Smokewinder___orange = (2, Weapons.Smokewinder___orange) -#ERRR + + # ERRR class Pylon3: Fuel_Tank_400_gallons = (3, WeaponsA4EC.Fuel_Tank_400_gallons) Fuel_Tank_300_gallons = (3, WeaponsA4EC.Fuel_Tank_300_gallons) Fuel_Tank_150_gallons = (3, WeaponsA4EC.Fuel_Tank_150_gallons) D_704_Refueling_Pod = (3, WeaponsA4EC.D_704_Refueling_Pod) -#ERRR {3*LAU-61} - LAU_68_3___7_2_75__rockets_MK5__HE_ = (3, Weapons.LAU_68_3___7_2_75__rockets_MK5__HE_) + # ERRR {3*LAU-61} + LAU_68_3___7_2_75__rockets_MK5__HE_ = ( + 3, + Weapons.LAU_68_3___7_2_75__rockets_MK5__HE_, + ) LAU_10___4_ZUNI_MK_71 = (3, Weapons.LAU_10___4_ZUNI_MK_71) LAU_10_2___4_ZUNI_MK_71 = (3, WeaponsA4EC.LAU_10_2___4_ZUNI_MK_71) LAU_10_3___4_ZUNI_MK_71 = (3, WeaponsA4EC.LAU_10_3___4_ZUNI_MK_71) @@ -637,7 +960,7 @@ class A_4E_C(PlaneType): LAU_68_3___7_FFAR_M156_WP = (3, WeaponsA4EC.LAU_68_3___7_FFAR_M156_WP) LAU_68_3___7_FFAR_Mk1_HE = (3, WeaponsA4EC.LAU_68_3___7_FFAR_Mk1_HE) LAU_68_3___7_FFAR_Mk5_HEAT = (3, WeaponsA4EC.LAU_68_3___7_FFAR_Mk5_HEAT) -#ERRR {AGM12_B} + # ERRR {AGM12_B} AGM_62 = (3, Weapons.AGM_62) Mk_20 = (3, Weapons.Mk_20) Mk_81 = (3, Weapons.Mk_81) @@ -680,7 +1003,8 @@ class A_4E_C(PlaneType): Smokewinder___white = (3, Weapons.Smokewinder___white) Smokewinder___yellow = (3, Weapons.Smokewinder___yellow) Smokewinder___orange = (3, Weapons.Smokewinder___orange) -#ERRR + + # ERRR class Pylon4: Fuel_Tank_300_gallons_ = (4, WeaponsA4EC.Fuel_Tank_300_gallons_) @@ -703,8 +1027,8 @@ class A_4E_C(PlaneType): LAU_68_2___7_FFAR_Mk1_HE__ = (4, WeaponsA4EC.LAU_68_2___7_FFAR_Mk1_HE__) LAU_68_2___7_FFAR_Mk5_HEAT__ = (4, WeaponsA4EC.LAU_68_2___7_FFAR_Mk5_HEAT__) AGM_45A = (4, Weapons.AGM_45A) -#ERRR {AGM12_C} -#ERRR {AGM12_B} + # ERRR {AGM12_C} + # ERRR {AGM12_B} AGM_62 = (4, Weapons.AGM_62) Mk_20 = (4, Weapons.Mk_20) Mk_81 = (4, Weapons.Mk_81) @@ -744,7 +1068,8 @@ class A_4E_C(PlaneType): Smokewinder___white = (4, Weapons.Smokewinder___white) Smokewinder___yellow = (4, Weapons.Smokewinder___yellow) Smokewinder___orange = (4, Weapons.Smokewinder___orange) -#ERRR + + # ERRR class Pylon5: LAU_10___4_ZUNI_MK_71 = (5, Weapons.LAU_10___4_ZUNI_MK_71) @@ -755,7 +1080,7 @@ class A_4E_C(PlaneType): LAU_68___7_FFAR_Mk1_HE = (5, Weapons.LAU_68___7_FFAR_Mk1_HE) LAU_68___7_FFAR_Mk5_HEAT = (5, Weapons.LAU_68___7_FFAR_Mk5_HEAT) AGM_45A = (5, Weapons.AGM_45A) -#ERRR {AGM12_B} + # ERRR {AGM12_B} Mk_20 = (5, Weapons.Mk_20) Mk_81 = (5, Weapons.Mk_81) Mk_81SE = (5, WeaponsA4EC.Mk_81SE) @@ -767,16 +1092,27 @@ class A_4E_C(PlaneType): AN_M64 = (5, Weapons.AN_M64) AN_M81 = (5, WeaponsA4EC.AN_M81) AN_M88 = (5, WeaponsA4EC.AN_M88) - LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = (5, Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_) + LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = ( + 5, + Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_, + ) Smokewinder___red = (5, Weapons.Smokewinder___red) Smokewinder___green = (5, Weapons.Smokewinder___green) Smokewinder___blue = (5, Weapons.Smokewinder___blue) Smokewinder___white = (5, Weapons.Smokewinder___white) Smokewinder___yellow = (5, Weapons.Smokewinder___yellow) Smokewinder___orange = (5, Weapons.Smokewinder___orange) -#ERRR + + # ERRR pylons = {1, 2, 3, 4, 5} - tasks = [task.CAP, task.CAS, task.SEAD, task.GroundAttack, task.AFAC, task.Refueling] - task_default = task.CAS \ No newline at end of file + tasks = [ + task.CAP, + task.CAS, + task.SEAD, + task.GroundAttack, + task.AFAC, + task.Refueling, + ] + task_default = task.CAS diff --git a/pydcs_extensions/f22a/f22a.py b/pydcs_extensions/f22a/f22a.py index f4230f91..10e289d4 100644 --- a/pydcs_extensions/f22a/f22a.py +++ b/pydcs_extensions/f22a/f22a.py @@ -19,11 +19,10 @@ class F_22A(PlaneType): chaff_charge_size = 1 flare_charge_size = 2 eplrs = True - category = "Interceptor" #{78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} + category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} radio_frequency = 127.5 class Liveries: - class USSR(Enum): default = "default" @@ -353,5 +352,11 @@ class F_22A(PlaneType): pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} - tasks = [task.CAP, task.Escort, task.FighterSweep, task.Intercept, task.Reconnaissance] + tasks = [ + task.CAP, + task.Escort, + task.FighterSweep, + task.Intercept, + task.Reconnaissance, + ] task_default = task.CAP diff --git a/pydcs_extensions/frenchpack/frenchpack.py b/pydcs_extensions/frenchpack/frenchpack.py index 2d5f5433..2f02f9e0 100644 --- a/pydcs_extensions/frenchpack/frenchpack.py +++ b/pydcs_extensions/frenchpack/frenchpack.py @@ -3,6 +3,7 @@ # from dcs import unittype + class AMX_10RCR(unittype.VehicleType): id = "AMX10RCR" name = "AMX-10RCR" @@ -150,8 +151,10 @@ class DIM__KAMIKAZE(unittype.VehicleType): air_weapon_dist = 50 eplrs = True + ## FORTIFICATION + class _FIELD_HIDE(unittype.VehicleType): id = "FieldHL" name = "*FIELD HIDE" @@ -159,6 +162,7 @@ class _FIELD_HIDE(unittype.VehicleType): threat_range = 0 air_weapon_dist = 0 + class _FIELD_HIDE_SMALL(unittype.VehicleType): id = "HARRIERH" name = "*FIELD HIDE SMALL" @@ -166,6 +170,7 @@ class _FIELD_HIDE_SMALL(unittype.VehicleType): threat_range = 0 air_weapon_dist = 0 + class SmokeD1(unittype.VehicleType): id = "SmokeD1" name = "SmokeD1" @@ -173,6 +178,7 @@ class SmokeD1(unittype.VehicleType): threat_range = 0 air_weapon_dist = 0 + class SmokeD3(unittype.VehicleType): id = "SmokeD3" name = "SmokeD3" @@ -189,6 +195,7 @@ class TRM_2000(unittype.VehicleType): air_weapon_dist = 0 eplrs = True + class TRM_2000_Fuel(unittype.VehicleType): id = "TRM2000_Citerne" name = "TRM-2000 Fuel" @@ -197,6 +204,7 @@ class TRM_2000_Fuel(unittype.VehicleType): air_weapon_dist = 0 eplrs = True + class VAB_MEDICAL(unittype.VehicleType): id = "VABH" name = "VAB MEDICAL" @@ -205,6 +213,7 @@ class VAB_MEDICAL(unittype.VehicleType): air_weapon_dist = 0 eplrs = True + class VAB(unittype.VehicleType): id = "VAB_RADIO" name = "VAB" @@ -213,6 +222,7 @@ class VAB(unittype.VehicleType): air_weapon_dist = 0 eplrs = True + class VBL(unittype.VehicleType): id = "VBL-Radio" name = "VBL" @@ -221,6 +231,7 @@ class VBL(unittype.VehicleType): air_weapon_dist = 0 eplrs = True + class Tracma_TD_1500(unittype.VehicleType): id = "Tracma" name = "Tracma TD 1500" @@ -228,8 +239,10 @@ class Tracma_TD_1500(unittype.VehicleType): threat_range = 0 air_weapon_dist = 0 + ## AIRDEFENCE + class SMOKE_SAM_IR(unittype.VehicleType): id = "SMOKESAM" name = "SMOKE SAM IR" @@ -238,6 +251,7 @@ class SMOKE_SAM_IR(unittype.VehicleType): air_weapon_dist = 20000 eplrs = True + class _53T2(unittype.VehicleType): id = "AA20" name = "53T2" @@ -245,6 +259,7 @@ class _53T2(unittype.VehicleType): threat_range = 2000 air_weapon_dist = 2000 + class TRM_2000_53T2(unittype.VehicleType): id = "TRM2000_AA20" name = "TRM-2000 53T2" @@ -253,6 +268,7 @@ class TRM_2000_53T2(unittype.VehicleType): air_weapon_dist = 2000 eplrs = True + class TRM_2000_PAMELA(unittype.VehicleType): id = "TRMMISTRAL" name = "TRM-2000 PAMELA" @@ -261,8 +277,10 @@ class TRM_2000_PAMELA(unittype.VehicleType): air_weapon_dist = 10000 eplrs = True + ## INFANTRY + class Infantry_Soldier_JTAC(unittype.VehicleType): id = "JTACFP" name = "Infantry Soldier JTAC" @@ -270,8 +288,10 @@ class Infantry_Soldier_JTAC(unittype.VehicleType): threat_range = 500 air_weapon_dist = 500 + ## ARTILERY + class MO_120_RT(unittype.VehicleType): id = "M120" name = "MO 120 RT" @@ -279,10 +299,11 @@ class MO_120_RT(unittype.VehicleType): threat_range = 15000 air_weapon_dist = 15000 + class VAB_MORTIER(unittype.VehicleType): id = "VAB_MORTIER" name = "VAB MORTIER" detection_range = 0 threat_range = 15000 air_weapon_dist = 15000 - eplrs = True \ No newline at end of file + eplrs = True diff --git a/pydcs_extensions/hercules/hercules.py b/pydcs_extensions/hercules/hercules.py index e400e8bd..262c5319 100644 --- a/pydcs_extensions/hercules/hercules.py +++ b/pydcs_extensions/hercules/hercules.py @@ -6,118 +6,563 @@ from dcs.weapons_data import Weapons class HerculesWeapons: - GAU_23A_Chain_Gun__30mm_ = {"clsid": "{Herc_GAU_23A_Chain_Gun}", "name": "GAU 23A Chain Gun (30mm)", "weight": 595.9426} - Herc_AAA_GEPARD = {"clsid": "Herc_AAA_GEPARD", "name": "AAA GEPARD [34720lb]", "weight": 15782} - Herc_AAA_Vulcan_M163 = {"clsid": "Herc_AAA_Vulcan_M163", "name": "AAA Vulcan M163 [21666lb]", "weight": 9848} - Herc_Ammo_AGM_154C_missiles = {"clsid": "Herc_Ammo_AGM_154C_missiles", "name": "Ammo AGM-154C*10 [10648lb]", "weight": 4960} - Herc_Ammo_AGM_65D_missiles = {"clsid": "Herc_Ammo_AGM_65D_missiles", "name": "Ammo AGM-65D*10 [4800lb]", "weight": 2300} - Herc_Ammo_AGM_65E_missiles = {"clsid": "Herc_Ammo_AGM_65E_missiles", "name": "Ammo AGM-65E*10 [6292lb]", "weight": 2980} - Herc_Ammo_AGM_65G_missiles = {"clsid": "Herc_Ammo_AGM_65G_missiles", "name": "Ammo AGM-65G*10 [6622lb]", "weight": 3130} - Herc_Ammo_AGM_65H_missiles = {"clsid": "Herc_Ammo_AGM_65H_missiles", "name": "Ammo AGM-65H*10 [4570lb]", "weight": 2200} - Herc_Ammo_AGM_65K_missiles = {"clsid": "Herc_Ammo_AGM_65K_missiles", "name": "Ammo AGM-65K*10 [7920lb]", "weight": 3720} - Herc_Ammo_AGM_84A_missiles = {"clsid": "Herc_Ammo_AGM_84A_missiles", "name": "Ammo AGM-84A*8 [11651lb]", "weight": 5408} - Herc_Ammo_AGM_84E_missiles = {"clsid": "Herc_Ammo_AGM_84E_missiles", "name": "Ammo AGM-84E*8 [11651lb]", "weight": 5408} - Herc_Ammo_AGM_88C_missiles = {"clsid": "Herc_Ammo_AGM_88C_missiles", "name": "Ammo AGM-88C*10 [7920lb]", "weight": 3730} - Herc_Ammo_AIM120B_missiles = {"clsid": "Herc_Ammo_AIM120B_missiles", "name": "Ammo AIM-120B*24 [11193lb]", "weight": 5208} - Herc_Ammo_AIM120C_missiles = {"clsid": "Herc_Ammo_AIM120C_missiles", "name": "Ammo AIM-120C*24 [10665lb]", "weight": 5208} - Herc_Ammo_AIM54C_missiles = {"clsid": "Herc_Ammo_AIM54C_missiles", "name": "Ammo AIM-54C*18 [18335lb]", "weight": 8454} - Herc_Ammo_AIM7M_missiles = {"clsid": "Herc_Ammo_AIM7M_missiles", "name": "Ammo AIM-7M*24 [14995lb]", "weight": 6936} - Herc_Ammo_AIM9M_missiles = {"clsid": "Herc_Ammo_AIM9M_missiles", "name": "Ammo AIM-9M*30 [7128lb]", "weight": 4860} - Herc_Ammo_AIM9P5_missiles = {"clsid": "Herc_Ammo_AIM9P5_missiles", "name": "Ammo AIM-9P5*30 [5676lb]", "weight": 2700} - Herc_Ammo_AIM9X_missiles = {"clsid": "Herc_Ammo_AIM9X_missiles", "name": "Ammo AIM-9X*30 [5676lb]", "weight": 2700} - Herc_Ammo_BETAB500SP_bombs = {"clsid": "Herc_Ammo_BETAB500SP_bombs", "name": "Ammo BetAB-500ShP*10 [9328lb]", "weight": 4360} - Herc_Ammo_BETAB500_bombs = {"clsid": "Herc_Ammo_BETAB500_bombs", "name": "Ammo BetAB-500*10 [9460lb]", "weight": 4420} - Herc_Ammo_CBU_103_bombs = {"clsid": "Herc_Ammo_CBU_103_bombs", "name": "Ammo CBU-103*10 [10142lb]", "weight": 4730} - Herc_Ammo_CBU_105_bombs = {"clsid": "Herc_Ammo_CBU_105_bombs", "name": "Ammo CBU-105*10 [11022lb]", "weight": 5130} - Herc_Ammo_CBU_87_bombs = {"clsid": "Herc_Ammo_CBU_87_bombs", "name": "Ammo CBU-87*10 [9460lb]", "weight": 4420} - Herc_Ammo_CBU_97_bombs = {"clsid": "Herc_Ammo_CBU_97_bombs", "name": "Ammo CBU-97*10 [10362lb]", "weight": 4830} - Herc_Ammo_FAB100_bombs = {"clsid": "Herc_Ammo_FAB100_bombs", "name": "Ammo FAB-100*20 [4400lb", "weight": 2120} - Herc_Ammo_FAB250_bombs = {"clsid": "Herc_Ammo_FAB250_bombs", "name": "Ammo FAB-250*20 [11000lb]", "weight": 5120} - Herc_Ammo_FAB500_bombs = {"clsid": "Herc_Ammo_FAB500_bombs", "name": "Ammo FAB-500*10 [11000lb]", "weight": 5120} - Herc_Ammo_GBU_10_bombs = {"clsid": "Herc_Ammo_GBU_10_bombs", "name": "Ammo GBU-10*6 [15340lb]", "weight": 7092} - Herc_Ammo_GBU_12_bombs = {"clsid": "Herc_Ammo_GBU_12_bombs", "name": "Ammo GBU-12*16 [9680lb]", "weight": 4520} - Herc_Ammo_GBU_16_bombs = {"clsid": "Herc_Ammo_GBU_16_bombs", "name": "Ammo GBU-16*10 [12408lb]", "weight": 5760} - Herc_Ammo_GBU_31_V3B_bombs = {"clsid": "Herc_Ammo_GBU_31_V3B_bombs", "name": "Ammo GBU-31V3B*6 [12949lb]", "weight": 6006} - Herc_Ammo_GBU_31_VB_bombs = {"clsid": "Herc_Ammo_GBU_31_VB_bombs", "name": "Ammo GBU-31V/B*6 [12328lb]", "weight": 5724} - Herc_Ammo_GBU_38_bombs = {"clsid": "Herc_Ammo_GBU_38_bombs", "name": "Ammo GBU-38*10 [6028lb]", "weight": 2860} - Herc_Ammo_hydra_HE_rockets = {"clsid": "Herc_Ammo_hydra_HE_rockets", "name": "Ammo M151 Hydra HE*80 [4752lb]", "weight": 2280} - Herc_Ammo_hydra_WP_rockets = {"clsid": "Herc_Ammo_hydra_WP_rockets", "name": "Ammo M156 Hydra WP*80 [4752lb]", "weight": 2280} - Herc_Ammo_KAB500KR_bombs = {"clsid": "Herc_Ammo_KAB500KR_bombs", "name": "Ammo KAB-500kr*10 [12320lb]", "weight": 5720} - Herc_Ammo_KH25ML_missiles = {"clsid": "Herc_Ammo_KH25ML_missiles", "name": "Ammo Kh-25ML*10 [7920lb]", "weight": 3720} - Herc_Ammo_KH25MPU_missiles = {"clsid": "Herc_Ammo_KH25MPU_missiles", "name": "Ammo Kh-25MPU*10 [8140lb]", "weight": 3820} - Herc_Ammo_KH29L_missiles = {"clsid": "Herc_Ammo_KH29L_missiles", "name": "Ammo Kh-29L*10 [16434lb]", "weight": 7590} - Herc_Ammo_KH29T_missiles = {"clsid": "Herc_Ammo_KH29T_missiles", "name": "Ammo Kh-29T*10 [16720lb]", "weight": 7720} - Herc_Ammo_KH58U_missiles = {"clsid": "Herc_Ammo_KH58U_missiles", "name": "Ammo Kh-58U*10 [16060lb]", "weight": 7420} - Herc_Ammo_KMGU296AO25KO_bombs = {"clsid": "Herc_Ammo_KMGU296AO25KO_bombs", "name": "Ammo KMGU-2 - 96 PTAB-2.5KO*10 [11440lb]", "weight": 5320} - Herc_Ammo_KMGU296AO25RT_bombs = {"clsid": "Herc_Ammo_KMGU296AO25RT_bombs", "name": "Ammo KMGU-2 - 96 AO-2.5RT*10 [11440lb]", "weight": 5320} - Herc_Ammo_M117_bombs = {"clsid": "Herc_Ammo_M117_bombs", "name": "Ammo M117*16 [11968lb]", "weight": 5560} - Herc_Ammo_MAGIC2_missiles = {"clsid": "Herc_Ammo_MAGIC2_missiles", "name": "Ammo Magic2*30 [5676lb]", "weight": 2700} - Herc_Ammo_MK20_bombs = {"clsid": "Herc_Ammo_MK20_bombs", "name": "Ammo MK20*20 [9768lb]", "weight": 4560} - Herc_Ammo_Mk_82AIR_bombs = {"clsid": "Herc_Ammo_Mk_82AIR_bombs", "name": "Ammo Mk-82AIR*20 [11044lb]", "weight": 4940} - Herc_Ammo_Mk_82Snake_bombs = {"clsid": "Herc_Ammo_Mk_82Snake_bombs", "name": "Ammo Mk-82Snakeye*20 [11880lb]", "weight": 4940} - Herc_Ammo_Mk_82_bombs = {"clsid": "Herc_Ammo_Mk_82_bombs", "name": "Ammo Mk-82*20 [10560lb]", "weight": 4940} - Herc_Ammo_Mk_83_bombs = {"clsid": "Herc_Ammo_Mk_83_bombs", "name": "Ammo Mk-83*10 [9834lb]", "weight": 4590} - Herc_Ammo_Mk_84_bombs = {"clsid": "Herc_Ammo_Mk_84_bombs", "name": "Ammo Mk-84*8 [15735b]", "weight": 7272} - Herc_Ammo_R27ER_missiles = {"clsid": "Herc_Ammo_R27ER_missiles", "name": "Ammo R-27ER*24 [18480lb]", "weight": 8520} - Herc_Ammo_R27ET_missiles = {"clsid": "Herc_Ammo_R27ET_missiles", "name": "Ammo R-27ET*24 [18480lb", "weight": 8496} - Herc_Ammo_R27R_missiles = {"clsid": "Herc_Ammo_R27R_missiles", "name": "Ammo R-27R*24 [13359lb]", "weight": 6192} - Herc_Ammo_R27T_missiles = {"clsid": "Herc_Ammo_R27T_missiles", "name": "Ammo R-27T*24 [13359lb]", "weight": 6192} - Herc_Ammo_R60M_missiles = {"clsid": "Herc_Ammo_R60M_missiles", "name": "Ammo R-60M*30 [2904lb]", "weight": 1440} - Herc_Ammo_R77_missiles = {"clsid": "Herc_Ammo_R77_missiles", "name": "Ammo R-77*24 [9240lb]", "weight": 4320} - Herc_Ammo_RBK250PTAB25M_bombs = {"clsid": "Herc_Ammo_RBK250PTAB25M_bombs", "name": "Ammo RBK-250 PTAB-2.5M*20 [12012lb]", "weight": 5580} - Herc_Ammo_RBK500255PTAB105_bombs = {"clsid": "Herc_Ammo_RBK500255PTAB105_bombs", "name": "Ammo RBK-500-255 PTAB-10-5*10 [9394lb]", "weight": 4390} - Herc_Ammo_RBK500PTAB1M_bombs = {"clsid": "Herc_Ammo_RBK500PTAB1M_bombs", "name": "Ammo RBK-500 PTAB-1M*10 [9394lb]", "weight": 4390} - Herc_Ammo_S24B_missiles = {"clsid": "Herc_Ammo_S24B_missiles", "name": "Ammo S-24B*20 [10340lb]", "weight": 4820} - Herc_Ammo_S25L_missiles = {"clsid": "Herc_Ammo_S25L_missiles", "name": "Ammo S-25L*10 [11000b]", "weight": 5120} - Herc_Ammo_S25OFM_missiles = {"clsid": "Herc_Ammo_S25OFM_missiles", "name": "Ammo S-25OFM*10 [10890lb]", "weight": 5070} - Herc_Ammo_S530D_missiles = {"clsid": "Herc_Ammo_S530D_missiles", "name": "Ammo Super 530D*24 [6480lb]", "weight": 6600} - Herc_Ammo_SAB100_bombs = {"clsid": "Herc_Ammo_SAB100_bombs", "name": "Ammo SAB-100*20 [11000lb]", "weight": 2120} - Herc_Ammo_Vikhr_missiles = {"clsid": "Herc_Ammo_Vikhr_missiles", "name": "Ammo Vikhr*48 [5808lb]", "weight": 2760} - Herc_APC_BTR_80 = {"clsid": "Herc_APC_BTR_80", "name": "APC BTR-80 [23936lb]", "weight": 10880} - Herc_APC_COBRA = {"clsid": "Herc_APC_COBRA", "name": "APC Cobra [10912lb]", "weight": 4960} - Herc_APC_LAV_25 = {"clsid": "Herc_APC_LAV_25", "name": "APC LAV-25 [22514lb]", "weight": 10234} - Herc_APC_M1025_HMMWV = {"clsid": "Herc_APC_M1025_HMMWV", "name": "M1025 HMMWV [6160lb]", "weight": 2800} - Herc_APC_M1043_HMMWV_Armament = {"clsid": "Herc_APC_M1043_HMMWV_Armament", "name": "APC M1043 HMMWV Armament [7023lb]", "weight": 3192} - Herc_APC_M113 = {"clsid": "Herc_APC_M113", "name": "APC M113 [21624lb]", "weight": 9830} - Herc_APC_MTLB = {"clsid": "Herc_APC_MTLB", "name": "APC MTLB [26000lb]", "weight": 12000} - Herc_ART_GVOZDIKA = {"clsid": "Herc_ART_GVOZDIKA", "name": "ART GVOZDIKA [34720lb]", "weight": 15782} - Herc_ART_NONA = {"clsid": "Herc_ART_NONA", "name": "ART 2S9 NONA [19140lb]", "weight": 8700} - Herc_ARV_BRDM_2 = {"clsid": "Herc_ARV_BRDM_2", "name": "ARV BRDM-2 [12320lb]", "weight": 5600} - Herc_ATGM_M1045_HMMWV_TOW = {"clsid": "Herc_ATGM_M1045_HMMWV_TOW", "name": "ATGM M1045 HMMWV TOW [7183lb]", "weight": 3265} - Herc_ATGM_M1134_Stryker = {"clsid": "Herc_ATGM_M1134_Stryker", "name": "ATGM M1134 Stryker [30337lb]", "weight": 13790} - Herc_BattleStation = {"clsid": "Herc_BattleStation", "name": "Battle Station", "weight": 0} - Herc_Ext_Fuel_Tank = {"clsid": "Herc_Ext_Fuel_Tank", "name": "External Fuel Tank", "weight": 4131} - Herc_GEN_CRATE = {"clsid": "Herc_GEN_CRATE", "name": "Generic Crate [20000lb]", "weight": 9071} - Herc_HEMTT_TFFT = {"clsid": "Herc_HEMTT_TFFT", "name": "HEMTT TFFT [34400lb]", "weight": 15634} - Herc_IFV_BMD1 = {"clsid": "Herc_IFV_BMD1", "name": "IFV BMD-1 [18040lb]", "weight": 8200} - Herc_IFV_BMP_1 = {"clsid": "Herc_IFV_BMP_1", "name": "IFV BMP-1 [23232lb]", "weight": 10560} - Herc_IFV_BMP_2 = {"clsid": "Herc_IFV_BMP_2", "name": "IFV BMP-2 [25168lb]", "weight": 11440} - Herc_IFV_BMP_3 = {"clsid": "Herc_IFV_BMP_3", "name": "IFV BMP-3 [32912lb]", "weight": 14960} - Herc_IFV_BTRD = {"clsid": "Herc_IFV_BTRD", "name": "IFV BTR-D [18040lb]", "weight": 8200} - Herc_IFV_M2A2_Bradley = {"clsid": "Herc_IFV_M2A2_Bradley", "name": "IFV M2A2 Bradley [34720lb]", "weight": 15782} - Herc_IFV_MARDER = {"clsid": "Herc_IFV_MARDER", "name": "IFV MARDER [34720lb]", "weight": 15782} - Herc_IFV_MCV80_Warrior = {"clsid": "Herc_IFV_MCV80_Warrior", "name": "IFV MCV-80 [34720lb]", "weight": 15782} - Herc_IFV_TPZ = {"clsid": "Herc_IFV_TPZ", "name": "IFV TPZ FUCH [33440lb]", "weight": 15200} + GAU_23A_Chain_Gun__30mm_ = { + "clsid": "{Herc_GAU_23A_Chain_Gun}", + "name": "GAU 23A Chain Gun (30mm)", + "weight": 595.9426, + } + Herc_AAA_GEPARD = { + "clsid": "Herc_AAA_GEPARD", + "name": "AAA GEPARD [34720lb]", + "weight": 15782, + } + Herc_AAA_Vulcan_M163 = { + "clsid": "Herc_AAA_Vulcan_M163", + "name": "AAA Vulcan M163 [21666lb]", + "weight": 9848, + } + Herc_Ammo_AGM_154C_missiles = { + "clsid": "Herc_Ammo_AGM_154C_missiles", + "name": "Ammo AGM-154C*10 [10648lb]", + "weight": 4960, + } + Herc_Ammo_AGM_65D_missiles = { + "clsid": "Herc_Ammo_AGM_65D_missiles", + "name": "Ammo AGM-65D*10 [4800lb]", + "weight": 2300, + } + Herc_Ammo_AGM_65E_missiles = { + "clsid": "Herc_Ammo_AGM_65E_missiles", + "name": "Ammo AGM-65E*10 [6292lb]", + "weight": 2980, + } + Herc_Ammo_AGM_65G_missiles = { + "clsid": "Herc_Ammo_AGM_65G_missiles", + "name": "Ammo AGM-65G*10 [6622lb]", + "weight": 3130, + } + Herc_Ammo_AGM_65H_missiles = { + "clsid": "Herc_Ammo_AGM_65H_missiles", + "name": "Ammo AGM-65H*10 [4570lb]", + "weight": 2200, + } + Herc_Ammo_AGM_65K_missiles = { + "clsid": "Herc_Ammo_AGM_65K_missiles", + "name": "Ammo AGM-65K*10 [7920lb]", + "weight": 3720, + } + Herc_Ammo_AGM_84A_missiles = { + "clsid": "Herc_Ammo_AGM_84A_missiles", + "name": "Ammo AGM-84A*8 [11651lb]", + "weight": 5408, + } + Herc_Ammo_AGM_84E_missiles = { + "clsid": "Herc_Ammo_AGM_84E_missiles", + "name": "Ammo AGM-84E*8 [11651lb]", + "weight": 5408, + } + Herc_Ammo_AGM_88C_missiles = { + "clsid": "Herc_Ammo_AGM_88C_missiles", + "name": "Ammo AGM-88C*10 [7920lb]", + "weight": 3730, + } + Herc_Ammo_AIM120B_missiles = { + "clsid": "Herc_Ammo_AIM120B_missiles", + "name": "Ammo AIM-120B*24 [11193lb]", + "weight": 5208, + } + Herc_Ammo_AIM120C_missiles = { + "clsid": "Herc_Ammo_AIM120C_missiles", + "name": "Ammo AIM-120C*24 [10665lb]", + "weight": 5208, + } + Herc_Ammo_AIM54C_missiles = { + "clsid": "Herc_Ammo_AIM54C_missiles", + "name": "Ammo AIM-54C*18 [18335lb]", + "weight": 8454, + } + Herc_Ammo_AIM7M_missiles = { + "clsid": "Herc_Ammo_AIM7M_missiles", + "name": "Ammo AIM-7M*24 [14995lb]", + "weight": 6936, + } + Herc_Ammo_AIM9M_missiles = { + "clsid": "Herc_Ammo_AIM9M_missiles", + "name": "Ammo AIM-9M*30 [7128lb]", + "weight": 4860, + } + Herc_Ammo_AIM9P5_missiles = { + "clsid": "Herc_Ammo_AIM9P5_missiles", + "name": "Ammo AIM-9P5*30 [5676lb]", + "weight": 2700, + } + Herc_Ammo_AIM9X_missiles = { + "clsid": "Herc_Ammo_AIM9X_missiles", + "name": "Ammo AIM-9X*30 [5676lb]", + "weight": 2700, + } + Herc_Ammo_BETAB500SP_bombs = { + "clsid": "Herc_Ammo_BETAB500SP_bombs", + "name": "Ammo BetAB-500ShP*10 [9328lb]", + "weight": 4360, + } + Herc_Ammo_BETAB500_bombs = { + "clsid": "Herc_Ammo_BETAB500_bombs", + "name": "Ammo BetAB-500*10 [9460lb]", + "weight": 4420, + } + Herc_Ammo_CBU_103_bombs = { + "clsid": "Herc_Ammo_CBU_103_bombs", + "name": "Ammo CBU-103*10 [10142lb]", + "weight": 4730, + } + Herc_Ammo_CBU_105_bombs = { + "clsid": "Herc_Ammo_CBU_105_bombs", + "name": "Ammo CBU-105*10 [11022lb]", + "weight": 5130, + } + Herc_Ammo_CBU_87_bombs = { + "clsid": "Herc_Ammo_CBU_87_bombs", + "name": "Ammo CBU-87*10 [9460lb]", + "weight": 4420, + } + Herc_Ammo_CBU_97_bombs = { + "clsid": "Herc_Ammo_CBU_97_bombs", + "name": "Ammo CBU-97*10 [10362lb]", + "weight": 4830, + } + Herc_Ammo_FAB100_bombs = { + "clsid": "Herc_Ammo_FAB100_bombs", + "name": "Ammo FAB-100*20 [4400lb", + "weight": 2120, + } + Herc_Ammo_FAB250_bombs = { + "clsid": "Herc_Ammo_FAB250_bombs", + "name": "Ammo FAB-250*20 [11000lb]", + "weight": 5120, + } + Herc_Ammo_FAB500_bombs = { + "clsid": "Herc_Ammo_FAB500_bombs", + "name": "Ammo FAB-500*10 [11000lb]", + "weight": 5120, + } + Herc_Ammo_GBU_10_bombs = { + "clsid": "Herc_Ammo_GBU_10_bombs", + "name": "Ammo GBU-10*6 [15340lb]", + "weight": 7092, + } + Herc_Ammo_GBU_12_bombs = { + "clsid": "Herc_Ammo_GBU_12_bombs", + "name": "Ammo GBU-12*16 [9680lb]", + "weight": 4520, + } + Herc_Ammo_GBU_16_bombs = { + "clsid": "Herc_Ammo_GBU_16_bombs", + "name": "Ammo GBU-16*10 [12408lb]", + "weight": 5760, + } + Herc_Ammo_GBU_31_V3B_bombs = { + "clsid": "Herc_Ammo_GBU_31_V3B_bombs", + "name": "Ammo GBU-31V3B*6 [12949lb]", + "weight": 6006, + } + Herc_Ammo_GBU_31_VB_bombs = { + "clsid": "Herc_Ammo_GBU_31_VB_bombs", + "name": "Ammo GBU-31V/B*6 [12328lb]", + "weight": 5724, + } + Herc_Ammo_GBU_38_bombs = { + "clsid": "Herc_Ammo_GBU_38_bombs", + "name": "Ammo GBU-38*10 [6028lb]", + "weight": 2860, + } + Herc_Ammo_hydra_HE_rockets = { + "clsid": "Herc_Ammo_hydra_HE_rockets", + "name": "Ammo M151 Hydra HE*80 [4752lb]", + "weight": 2280, + } + Herc_Ammo_hydra_WP_rockets = { + "clsid": "Herc_Ammo_hydra_WP_rockets", + "name": "Ammo M156 Hydra WP*80 [4752lb]", + "weight": 2280, + } + Herc_Ammo_KAB500KR_bombs = { + "clsid": "Herc_Ammo_KAB500KR_bombs", + "name": "Ammo KAB-500kr*10 [12320lb]", + "weight": 5720, + } + Herc_Ammo_KH25ML_missiles = { + "clsid": "Herc_Ammo_KH25ML_missiles", + "name": "Ammo Kh-25ML*10 [7920lb]", + "weight": 3720, + } + Herc_Ammo_KH25MPU_missiles = { + "clsid": "Herc_Ammo_KH25MPU_missiles", + "name": "Ammo Kh-25MPU*10 [8140lb]", + "weight": 3820, + } + Herc_Ammo_KH29L_missiles = { + "clsid": "Herc_Ammo_KH29L_missiles", + "name": "Ammo Kh-29L*10 [16434lb]", + "weight": 7590, + } + Herc_Ammo_KH29T_missiles = { + "clsid": "Herc_Ammo_KH29T_missiles", + "name": "Ammo Kh-29T*10 [16720lb]", + "weight": 7720, + } + Herc_Ammo_KH58U_missiles = { + "clsid": "Herc_Ammo_KH58U_missiles", + "name": "Ammo Kh-58U*10 [16060lb]", + "weight": 7420, + } + Herc_Ammo_KMGU296AO25KO_bombs = { + "clsid": "Herc_Ammo_KMGU296AO25KO_bombs", + "name": "Ammo KMGU-2 - 96 PTAB-2.5KO*10 [11440lb]", + "weight": 5320, + } + Herc_Ammo_KMGU296AO25RT_bombs = { + "clsid": "Herc_Ammo_KMGU296AO25RT_bombs", + "name": "Ammo KMGU-2 - 96 AO-2.5RT*10 [11440lb]", + "weight": 5320, + } + Herc_Ammo_M117_bombs = { + "clsid": "Herc_Ammo_M117_bombs", + "name": "Ammo M117*16 [11968lb]", + "weight": 5560, + } + Herc_Ammo_MAGIC2_missiles = { + "clsid": "Herc_Ammo_MAGIC2_missiles", + "name": "Ammo Magic2*30 [5676lb]", + "weight": 2700, + } + Herc_Ammo_MK20_bombs = { + "clsid": "Herc_Ammo_MK20_bombs", + "name": "Ammo MK20*20 [9768lb]", + "weight": 4560, + } + Herc_Ammo_Mk_82AIR_bombs = { + "clsid": "Herc_Ammo_Mk_82AIR_bombs", + "name": "Ammo Mk-82AIR*20 [11044lb]", + "weight": 4940, + } + Herc_Ammo_Mk_82Snake_bombs = { + "clsid": "Herc_Ammo_Mk_82Snake_bombs", + "name": "Ammo Mk-82Snakeye*20 [11880lb]", + "weight": 4940, + } + Herc_Ammo_Mk_82_bombs = { + "clsid": "Herc_Ammo_Mk_82_bombs", + "name": "Ammo Mk-82*20 [10560lb]", + "weight": 4940, + } + Herc_Ammo_Mk_83_bombs = { + "clsid": "Herc_Ammo_Mk_83_bombs", + "name": "Ammo Mk-83*10 [9834lb]", + "weight": 4590, + } + Herc_Ammo_Mk_84_bombs = { + "clsid": "Herc_Ammo_Mk_84_bombs", + "name": "Ammo Mk-84*8 [15735b]", + "weight": 7272, + } + Herc_Ammo_R27ER_missiles = { + "clsid": "Herc_Ammo_R27ER_missiles", + "name": "Ammo R-27ER*24 [18480lb]", + "weight": 8520, + } + Herc_Ammo_R27ET_missiles = { + "clsid": "Herc_Ammo_R27ET_missiles", + "name": "Ammo R-27ET*24 [18480lb", + "weight": 8496, + } + Herc_Ammo_R27R_missiles = { + "clsid": "Herc_Ammo_R27R_missiles", + "name": "Ammo R-27R*24 [13359lb]", + "weight": 6192, + } + Herc_Ammo_R27T_missiles = { + "clsid": "Herc_Ammo_R27T_missiles", + "name": "Ammo R-27T*24 [13359lb]", + "weight": 6192, + } + Herc_Ammo_R60M_missiles = { + "clsid": "Herc_Ammo_R60M_missiles", + "name": "Ammo R-60M*30 [2904lb]", + "weight": 1440, + } + Herc_Ammo_R77_missiles = { + "clsid": "Herc_Ammo_R77_missiles", + "name": "Ammo R-77*24 [9240lb]", + "weight": 4320, + } + Herc_Ammo_RBK250PTAB25M_bombs = { + "clsid": "Herc_Ammo_RBK250PTAB25M_bombs", + "name": "Ammo RBK-250 PTAB-2.5M*20 [12012lb]", + "weight": 5580, + } + Herc_Ammo_RBK500255PTAB105_bombs = { + "clsid": "Herc_Ammo_RBK500255PTAB105_bombs", + "name": "Ammo RBK-500-255 PTAB-10-5*10 [9394lb]", + "weight": 4390, + } + Herc_Ammo_RBK500PTAB1M_bombs = { + "clsid": "Herc_Ammo_RBK500PTAB1M_bombs", + "name": "Ammo RBK-500 PTAB-1M*10 [9394lb]", + "weight": 4390, + } + Herc_Ammo_S24B_missiles = { + "clsid": "Herc_Ammo_S24B_missiles", + "name": "Ammo S-24B*20 [10340lb]", + "weight": 4820, + } + Herc_Ammo_S25L_missiles = { + "clsid": "Herc_Ammo_S25L_missiles", + "name": "Ammo S-25L*10 [11000b]", + "weight": 5120, + } + Herc_Ammo_S25OFM_missiles = { + "clsid": "Herc_Ammo_S25OFM_missiles", + "name": "Ammo S-25OFM*10 [10890lb]", + "weight": 5070, + } + Herc_Ammo_S530D_missiles = { + "clsid": "Herc_Ammo_S530D_missiles", + "name": "Ammo Super 530D*24 [6480lb]", + "weight": 6600, + } + Herc_Ammo_SAB100_bombs = { + "clsid": "Herc_Ammo_SAB100_bombs", + "name": "Ammo SAB-100*20 [11000lb]", + "weight": 2120, + } + Herc_Ammo_Vikhr_missiles = { + "clsid": "Herc_Ammo_Vikhr_missiles", + "name": "Ammo Vikhr*48 [5808lb]", + "weight": 2760, + } + Herc_APC_BTR_80 = { + "clsid": "Herc_APC_BTR_80", + "name": "APC BTR-80 [23936lb]", + "weight": 10880, + } + Herc_APC_COBRA = { + "clsid": "Herc_APC_COBRA", + "name": "APC Cobra [10912lb]", + "weight": 4960, + } + Herc_APC_LAV_25 = { + "clsid": "Herc_APC_LAV_25", + "name": "APC LAV-25 [22514lb]", + "weight": 10234, + } + Herc_APC_M1025_HMMWV = { + "clsid": "Herc_APC_M1025_HMMWV", + "name": "M1025 HMMWV [6160lb]", + "weight": 2800, + } + Herc_APC_M1043_HMMWV_Armament = { + "clsid": "Herc_APC_M1043_HMMWV_Armament", + "name": "APC M1043 HMMWV Armament [7023lb]", + "weight": 3192, + } + Herc_APC_M113 = { + "clsid": "Herc_APC_M113", + "name": "APC M113 [21624lb]", + "weight": 9830, + } + Herc_APC_MTLB = { + "clsid": "Herc_APC_MTLB", + "name": "APC MTLB [26000lb]", + "weight": 12000, + } + Herc_ART_GVOZDIKA = { + "clsid": "Herc_ART_GVOZDIKA", + "name": "ART GVOZDIKA [34720lb]", + "weight": 15782, + } + Herc_ART_NONA = { + "clsid": "Herc_ART_NONA", + "name": "ART 2S9 NONA [19140lb]", + "weight": 8700, + } + Herc_ARV_BRDM_2 = { + "clsid": "Herc_ARV_BRDM_2", + "name": "ARV BRDM-2 [12320lb]", + "weight": 5600, + } + Herc_ATGM_M1045_HMMWV_TOW = { + "clsid": "Herc_ATGM_M1045_HMMWV_TOW", + "name": "ATGM M1045 HMMWV TOW [7183lb]", + "weight": 3265, + } + Herc_ATGM_M1134_Stryker = { + "clsid": "Herc_ATGM_M1134_Stryker", + "name": "ATGM M1134 Stryker [30337lb]", + "weight": 13790, + } + Herc_BattleStation = { + "clsid": "Herc_BattleStation", + "name": "Battle Station", + "weight": 0, + } + Herc_Ext_Fuel_Tank = { + "clsid": "Herc_Ext_Fuel_Tank", + "name": "External Fuel Tank", + "weight": 4131, + } + Herc_GEN_CRATE = { + "clsid": "Herc_GEN_CRATE", + "name": "Generic Crate [20000lb]", + "weight": 9071, + } + Herc_HEMTT_TFFT = { + "clsid": "Herc_HEMTT_TFFT", + "name": "HEMTT TFFT [34400lb]", + "weight": 15634, + } + Herc_IFV_BMD1 = { + "clsid": "Herc_IFV_BMD1", + "name": "IFV BMD-1 [18040lb]", + "weight": 8200, + } + Herc_IFV_BMP_1 = { + "clsid": "Herc_IFV_BMP_1", + "name": "IFV BMP-1 [23232lb]", + "weight": 10560, + } + Herc_IFV_BMP_2 = { + "clsid": "Herc_IFV_BMP_2", + "name": "IFV BMP-2 [25168lb]", + "weight": 11440, + } + Herc_IFV_BMP_3 = { + "clsid": "Herc_IFV_BMP_3", + "name": "IFV BMP-3 [32912lb]", + "weight": 14960, + } + Herc_IFV_BTRD = { + "clsid": "Herc_IFV_BTRD", + "name": "IFV BTR-D [18040lb]", + "weight": 8200, + } + Herc_IFV_M2A2_Bradley = { + "clsid": "Herc_IFV_M2A2_Bradley", + "name": "IFV M2A2 Bradley [34720lb]", + "weight": 15782, + } + Herc_IFV_MARDER = { + "clsid": "Herc_IFV_MARDER", + "name": "IFV MARDER [34720lb]", + "weight": 15782, + } + Herc_IFV_MCV80_Warrior = { + "clsid": "Herc_IFV_MCV80_Warrior", + "name": "IFV MCV-80 [34720lb]", + "weight": 15782, + } + Herc_IFV_TPZ = { + "clsid": "Herc_IFV_TPZ", + "name": "IFV TPZ FUCH [33440lb]", + "weight": 15200, + } Herc_JATO = {"clsid": "Herc_JATO", "name": "JATO", "weight": 0} - Herc_M_818 = {"clsid": "Herc_M_818", "name": "Transport M818 [16000lb]", "weight": 7272} - Herc_SAM_13 = {"clsid": "Herc_SAM_13", "name": "SAM SA-13 STRELA [21624lb]", "weight": 9830} - Herc_SAM_19 = {"clsid": "Herc_SAM_19", "name": "SAM SA-19 Tunguska 2S6 [34720lb]", "weight": 15782} - Herc_SAM_CHAPARRAL = {"clsid": "Herc_SAM_CHAPARRAL", "name": "SAM CHAPARRAL [21624lb]", "weight": 9830} - Herc_SAM_LINEBACKER = {"clsid": "Herc_SAM_LINEBACKER", "name": "SAM LINEBACKER [34720lb]", "weight": 15782} - Herc_SAM_M1097_HMMWV = {"clsid": "Herc_SAM_M1097_HMMWV", "name": "SAM Avenger M1097 [7200lb]", "weight": 3273} - Herc_SAM_ROLAND_ADS = {"clsid": "Herc_SAM_ROLAND_ADS", "name": "SAM ROLAND ADS [34720lb]", "weight": 15782} - Herc_SAM_ROLAND_LN = {"clsid": "Herc_SAM_ROLAND_LN", "name": "SAM ROLAND LN [34720b]", "weight": 15782} - Herc_Soldier_Squad = {"clsid": "Herc_Soldier_Squad", "name": "Squad 30 x Soldier [7950lb]", "weight": 120} - Herc_SPG_M1126_Stryker_ICV = {"clsid": "Herc_SPG_M1126_Stryker_ICV", "name": "APC M1126 Stryker ICV [29542lb]", "weight": 13429} - Herc_SPG_M1128_Stryker_MGS = {"clsid": "Herc_SPG_M1128_Stryker_MGS", "name": "SPG M1128 Stryker MGS [33036lb]", "weight": 15016} - Herc_Tanker_HEMTT = {"clsid": "Herc_Tanker_HEMTT", "name": "Tanker M978 HEMTT [34000lb]", "weight": 15455} - Herc_TIGR_233036 = {"clsid": "Herc_TIGR_233036", "name": "Transport Tigr [15900lb]", "weight": 7200} - Herc_UAZ_469 = {"clsid": "Herc_UAZ_469", "name": "Transport UAZ-469 [3747lb]", "weight": 1700} - Herc_URAL_375 = {"clsid": "Herc_URAL_375", "name": "Transport URAL-375 [14815lb]", "weight": 6734} - Herc_ZSU_23_4 = {"clsid": "Herc_ZSU_23_4", "name": "AAA ZSU-23-4 Shilka [32912lb]", "weight": 14960} - M61_Vulcan_Rotary_Cannon__20mm_ = {"clsid": "{Herc_M61_Vulcan_Rotary_Cannon}", "name": "M61 Vulcan Rotary Cannon (20mm)", "weight": 595.9426} - _105mm_Howitzer = {"clsid": "{Herc_105mm_Howitzer}", "name": "105mm Howitzer", "weight": 595.9426} - Herc_GBU_43_B_MOAB_ = {"clsid": "Herc_GBU-43/B(MOAB)", "name": "GBU-43/B(MOAB)", "weight": 9800} + Herc_M_818 = { + "clsid": "Herc_M_818", + "name": "Transport M818 [16000lb]", + "weight": 7272, + } + Herc_SAM_13 = { + "clsid": "Herc_SAM_13", + "name": "SAM SA-13 STRELA [21624lb]", + "weight": 9830, + } + Herc_SAM_19 = { + "clsid": "Herc_SAM_19", + "name": "SAM SA-19 Tunguska 2S6 [34720lb]", + "weight": 15782, + } + Herc_SAM_CHAPARRAL = { + "clsid": "Herc_SAM_CHAPARRAL", + "name": "SAM CHAPARRAL [21624lb]", + "weight": 9830, + } + Herc_SAM_LINEBACKER = { + "clsid": "Herc_SAM_LINEBACKER", + "name": "SAM LINEBACKER [34720lb]", + "weight": 15782, + } + Herc_SAM_M1097_HMMWV = { + "clsid": "Herc_SAM_M1097_HMMWV", + "name": "SAM Avenger M1097 [7200lb]", + "weight": 3273, + } + Herc_SAM_ROLAND_ADS = { + "clsid": "Herc_SAM_ROLAND_ADS", + "name": "SAM ROLAND ADS [34720lb]", + "weight": 15782, + } + Herc_SAM_ROLAND_LN = { + "clsid": "Herc_SAM_ROLAND_LN", + "name": "SAM ROLAND LN [34720b]", + "weight": 15782, + } + Herc_Soldier_Squad = { + "clsid": "Herc_Soldier_Squad", + "name": "Squad 30 x Soldier [7950lb]", + "weight": 120, + } + Herc_SPG_M1126_Stryker_ICV = { + "clsid": "Herc_SPG_M1126_Stryker_ICV", + "name": "APC M1126 Stryker ICV [29542lb]", + "weight": 13429, + } + Herc_SPG_M1128_Stryker_MGS = { + "clsid": "Herc_SPG_M1128_Stryker_MGS", + "name": "SPG M1128 Stryker MGS [33036lb]", + "weight": 15016, + } + Herc_Tanker_HEMTT = { + "clsid": "Herc_Tanker_HEMTT", + "name": "Tanker M978 HEMTT [34000lb]", + "weight": 15455, + } + Herc_TIGR_233036 = { + "clsid": "Herc_TIGR_233036", + "name": "Transport Tigr [15900lb]", + "weight": 7200, + } + Herc_UAZ_469 = { + "clsid": "Herc_UAZ_469", + "name": "Transport UAZ-469 [3747lb]", + "weight": 1700, + } + Herc_URAL_375 = { + "clsid": "Herc_URAL_375", + "name": "Transport URAL-375 [14815lb]", + "weight": 6734, + } + Herc_ZSU_23_4 = { + "clsid": "Herc_ZSU_23_4", + "name": "AAA ZSU-23-4 Shilka [32912lb]", + "weight": 14960, + } + M61_Vulcan_Rotary_Cannon__20mm_ = { + "clsid": "{Herc_M61_Vulcan_Rotary_Cannon}", + "name": "M61 Vulcan Rotary Cannon (20mm)", + "weight": 595.9426, + } + _105mm_Howitzer = { + "clsid": "{Herc_105mm_Howitzer}", + "name": "105mm Howitzer", + "weight": 595.9426, + } + Herc_GBU_43_B_MOAB_ = { + "clsid": "Herc_GBU-43/B(MOAB)", + "name": "GBU-43/B(MOAB)", + "weight": 9800, + } + class Hercules(PlaneType): id = "Hercules" @@ -156,13 +601,12 @@ class Hercules(PlaneType): 7: 124, 14: 131, 19: 136, - 15: 132 + 15: 132, }, }, } class Liveries: - class USSR(Enum): default = "default" @@ -413,7 +857,10 @@ class Hercules(PlaneType): Herc_JATO = (1, HerculesWeapons.Herc_JATO) class Pylon2: - LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = (2, Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_) + LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = ( + 2, + Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_, + ) Smokewinder___red = (2, Weapons.Smokewinder___red) Smokewinder___green = (2, Weapons.Smokewinder___green) Smokewinder___blue = (2, Weapons.Smokewinder___blue) @@ -423,7 +870,10 @@ class Hercules(PlaneType): Herc_Ext_Fuel_Tank = (2, HerculesWeapons.Herc_Ext_Fuel_Tank) class Pylon3: - LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = (3, Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_) + LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = ( + 3, + Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_, + ) Smokewinder___red = (3, Weapons.Smokewinder___red) Smokewinder___green = (3, Weapons.Smokewinder___green) Smokewinder___blue = (3, Weapons.Smokewinder___blue) @@ -433,7 +883,10 @@ class Hercules(PlaneType): Herc_Ext_Fuel_Tank = (3, HerculesWeapons.Herc_Ext_Fuel_Tank) class Pylon4: - LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = (4, Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_) + LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = ( + 4, + Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_, + ) Smokewinder___red = (4, Weapons.Smokewinder___red) Smokewinder___green = (4, Weapons.Smokewinder___green) Smokewinder___blue = (4, Weapons.Smokewinder___blue) @@ -443,7 +896,10 @@ class Hercules(PlaneType): Herc_Ext_Fuel_Tank = (4, HerculesWeapons.Herc_Ext_Fuel_Tank) class Pylon5: - LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = (5, Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_) + LAU_68___7_2_75__rockets_M257__Parachute_illumination_ = ( + 5, + Weapons.LAU_68___7_2_75__rockets_M257__Parachute_illumination_, + ) Smokewinder___red = (5, Weapons.Smokewinder___red) Smokewinder___green = (5, Weapons.Smokewinder___green) Smokewinder___blue = (5, Weapons.Smokewinder___blue) @@ -453,7 +909,10 @@ class Hercules(PlaneType): Herc_Ext_Fuel_Tank = (5, HerculesWeapons.Herc_Ext_Fuel_Tank) class Pylon6: - M61_Vulcan_Rotary_Cannon__20mm_ = (6, HerculesWeapons.M61_Vulcan_Rotary_Cannon__20mm_) + M61_Vulcan_Rotary_Cannon__20mm_ = ( + 6, + HerculesWeapons.M61_Vulcan_Rotary_Cannon__20mm_, + ) class Pylon7: GAU_23A_Chain_Gun__30mm_ = (7, HerculesWeapons.GAU_23A_Chain_Gun__30mm_) @@ -482,7 +941,7 @@ class Hercules(PlaneType): Herc_Ammo_S24B_missiles = (10, HerculesWeapons.Herc_Ammo_S24B_missiles) Herc_Ammo_S25OFM_missiles = (10, HerculesWeapons.Herc_Ammo_S25OFM_missiles) Herc_Ammo_S25L_missiles = (10, HerculesWeapons.Herc_Ammo_S25L_missiles) - #Herc_Ammo_TOW_missiles = (10, HerculesWeapons.Herc_Ammo_TOW_missiles) + # Herc_Ammo_TOW_missiles = (10, HerculesWeapons.Herc_Ammo_TOW_missiles) Herc_Ammo_GBU_10_bombs = (10, HerculesWeapons.Herc_Ammo_GBU_10_bombs) Herc_Ammo_GBU_12_bombs = (10, HerculesWeapons.Herc_Ammo_GBU_12_bombs) Herc_Ammo_GBU_16_bombs = (10, HerculesWeapons.Herc_Ammo_GBU_16_bombs) @@ -504,12 +963,27 @@ class Hercules(PlaneType): Herc_Ammo_BETAB500_bombs = (10, HerculesWeapons.Herc_Ammo_BETAB500_bombs) Herc_Ammo_BETAB500SP_bombs = (10, HerculesWeapons.Herc_Ammo_BETAB500SP_bombs) Herc_Ammo_KAB500KR_bombs = (10, HerculesWeapons.Herc_Ammo_KAB500KR_bombs) - Herc_Ammo_RBK250PTAB25M_bombs = (10, HerculesWeapons.Herc_Ammo_RBK250PTAB25M_bombs) - Herc_Ammo_RBK500255PTAB105_bombs = (10, HerculesWeapons.Herc_Ammo_RBK500255PTAB105_bombs) - Herc_Ammo_RBK500PTAB1M_bombs = (10, HerculesWeapons.Herc_Ammo_RBK500PTAB1M_bombs) -#ERRR Herc_Ammo_Herc_Ammo_M117_bombs_bombs - Herc_Ammo_KMGU296AO25RT_bombs = (10, HerculesWeapons.Herc_Ammo_KMGU296AO25RT_bombs) - Herc_Ammo_KMGU296AO25KO_bombs = (10, HerculesWeapons.Herc_Ammo_KMGU296AO25KO_bombs) + Herc_Ammo_RBK250PTAB25M_bombs = ( + 10, + HerculesWeapons.Herc_Ammo_RBK250PTAB25M_bombs, + ) + Herc_Ammo_RBK500255PTAB105_bombs = ( + 10, + HerculesWeapons.Herc_Ammo_RBK500255PTAB105_bombs, + ) + Herc_Ammo_RBK500PTAB1M_bombs = ( + 10, + HerculesWeapons.Herc_Ammo_RBK500PTAB1M_bombs, + ) + # ERRR Herc_Ammo_Herc_Ammo_M117_bombs_bombs + Herc_Ammo_KMGU296AO25RT_bombs = ( + 10, + HerculesWeapons.Herc_Ammo_KMGU296AO25RT_bombs, + ) + Herc_Ammo_KMGU296AO25KO_bombs = ( + 10, + HerculesWeapons.Herc_Ammo_KMGU296AO25KO_bombs, + ) Herc_Ammo_MK20_bombs = (10, HerculesWeapons.Herc_Ammo_MK20_bombs) Herc_Ammo_SAB100_bombs = (10, HerculesWeapons.Herc_Ammo_SAB100_bombs) Herc_Ammo_hydra_HE_rockets = (10, HerculesWeapons.Herc_Ammo_hydra_HE_rockets) @@ -526,11 +1000,14 @@ class Hercules(PlaneType): Herc_Ammo_R27ER_missiles = (10, HerculesWeapons.Herc_Ammo_R27ER_missiles) Herc_Ammo_R27T_missiles = (10, HerculesWeapons.Herc_Ammo_R27T_missiles) Herc_Ammo_R27ET_missiles = (10, HerculesWeapons.Herc_Ammo_R27ET_missiles) -#ERRR Herc_Ammo_R27_missiles + # ERRR Herc_Ammo_R27_missiles Herc_Ammo_S530D_missiles = (10, HerculesWeapons.Herc_Ammo_S530D_missiles) Herc_Ammo_AIM54C_missiles = (10, HerculesWeapons.Herc_Ammo_AIM54C_missiles) - Herc_APC_M1043_HMMWV_Armament = (10, HerculesWeapons.Herc_APC_M1043_HMMWV_Armament) - #Herc_ATGM_M1045_HMMWV_TOW = (10, HerculesWeapons.Herc_ATGM_M1045_HMMWV_TOW) + Herc_APC_M1043_HMMWV_Armament = ( + 10, + HerculesWeapons.Herc_APC_M1043_HMMWV_Armament, + ) + # Herc_ATGM_M1045_HMMWV_TOW = (10, HerculesWeapons.Herc_ATGM_M1045_HMMWV_TOW) Herc_APC_M1025_HMMWV = (10, HerculesWeapons.Herc_APC_M1025_HMMWV) Herc_SAM_M1097_HMMWV = (10, HerculesWeapons.Herc_SAM_M1097_HMMWV) Herc_APC_COBRA = (10, HerculesWeapons.Herc_APC_COBRA) @@ -559,7 +1036,7 @@ class Hercules(PlaneType): Herc_Ammo_S24B_missiles = (11, HerculesWeapons.Herc_Ammo_S24B_missiles) Herc_Ammo_S25OFM_missiles = (11, HerculesWeapons.Herc_Ammo_S25OFM_missiles) Herc_Ammo_S25L_missiles = (11, HerculesWeapons.Herc_Ammo_S25L_missiles) - #Herc_Ammo_TOW_missiles = (11, HerculesWeapons.Herc_Ammo_TOW_missiles) + # Herc_Ammo_TOW_missiles = (11, HerculesWeapons.Herc_Ammo_TOW_missiles) Herc_GBU_43_B_MOAB_ = (11, HerculesWeapons.Herc_GBU_43_B_MOAB_) Herc_Ammo_GBU_10_bombs = (11, HerculesWeapons.Herc_Ammo_GBU_10_bombs) Herc_Ammo_GBU_12_bombs = (11, HerculesWeapons.Herc_Ammo_GBU_12_bombs) @@ -582,12 +1059,27 @@ class Hercules(PlaneType): Herc_Ammo_BETAB500_bombs = (11, HerculesWeapons.Herc_Ammo_BETAB500_bombs) Herc_Ammo_BETAB500SP_bombs = (11, HerculesWeapons.Herc_Ammo_BETAB500SP_bombs) Herc_Ammo_KAB500KR_bombs = (11, HerculesWeapons.Herc_Ammo_KAB500KR_bombs) - Herc_Ammo_RBK250PTAB25M_bombs = (11, HerculesWeapons.Herc_Ammo_RBK250PTAB25M_bombs) - Herc_Ammo_RBK500255PTAB105_bombs = (11, HerculesWeapons.Herc_Ammo_RBK500255PTAB105_bombs) - Herc_Ammo_RBK500PTAB1M_bombs = (11, HerculesWeapons.Herc_Ammo_RBK500PTAB1M_bombs) -#ERRR Herc_Ammo_Herc_Ammo_M117_bombs_bombs - Herc_Ammo_KMGU296AO25RT_bombs = (11, HerculesWeapons.Herc_Ammo_KMGU296AO25RT_bombs) - Herc_Ammo_KMGU296AO25KO_bombs = (11, HerculesWeapons.Herc_Ammo_KMGU296AO25KO_bombs) + Herc_Ammo_RBK250PTAB25M_bombs = ( + 11, + HerculesWeapons.Herc_Ammo_RBK250PTAB25M_bombs, + ) + Herc_Ammo_RBK500255PTAB105_bombs = ( + 11, + HerculesWeapons.Herc_Ammo_RBK500255PTAB105_bombs, + ) + Herc_Ammo_RBK500PTAB1M_bombs = ( + 11, + HerculesWeapons.Herc_Ammo_RBK500PTAB1M_bombs, + ) + # ERRR Herc_Ammo_Herc_Ammo_M117_bombs_bombs + Herc_Ammo_KMGU296AO25RT_bombs = ( + 11, + HerculesWeapons.Herc_Ammo_KMGU296AO25RT_bombs, + ) + Herc_Ammo_KMGU296AO25KO_bombs = ( + 11, + HerculesWeapons.Herc_Ammo_KMGU296AO25KO_bombs, + ) Herc_Ammo_MK20_bombs = (11, HerculesWeapons.Herc_Ammo_MK20_bombs) Herc_Ammo_SAB100_bombs = (11, HerculesWeapons.Herc_Ammo_SAB100_bombs) Herc_Ammo_hydra_HE_rockets = (11, HerculesWeapons.Herc_Ammo_hydra_HE_rockets) @@ -604,10 +1096,13 @@ class Hercules(PlaneType): Herc_Ammo_R27ER_missiles = (11, HerculesWeapons.Herc_Ammo_R27ER_missiles) Herc_Ammo_R27T_missiles = (11, HerculesWeapons.Herc_Ammo_R27T_missiles) Herc_Ammo_R27ET_missiles = (11, HerculesWeapons.Herc_Ammo_R27ET_missiles) -#ERRR Herc_Ammo_R27_missiles + # ERRR Herc_Ammo_R27_missiles Herc_Ammo_S530D_missiles = (11, HerculesWeapons.Herc_Ammo_S530D_missiles) Herc_Ammo_AIM54C_missiles = (11, HerculesWeapons.Herc_Ammo_AIM54C_missiles) - Herc_APC_M1043_HMMWV_Armament = (11, HerculesWeapons.Herc_APC_M1043_HMMWV_Armament) + Herc_APC_M1043_HMMWV_Armament = ( + 11, + HerculesWeapons.Herc_APC_M1043_HMMWV_Armament, + ) Herc_ATGM_M1045_HMMWV_TOW = (11, HerculesWeapons.Herc_ATGM_M1045_HMMWV_TOW) Herc_AAA_Vulcan_M163 = (11, HerculesWeapons.Herc_AAA_Vulcan_M163) Herc_SPG_M1126_Stryker_ICV = (11, HerculesWeapons.Herc_SPG_M1126_Stryker_ICV) @@ -667,7 +1162,7 @@ class Hercules(PlaneType): Herc_Ammo_S24B_missiles = (12, HerculesWeapons.Herc_Ammo_S24B_missiles) Herc_Ammo_S25OFM_missiles = (12, HerculesWeapons.Herc_Ammo_S25OFM_missiles) Herc_Ammo_S25L_missiles = (12, HerculesWeapons.Herc_Ammo_S25L_missiles) - #Herc_Ammo_TOW_missiles = (12, HerculesWeapons.Herc_Ammo_TOW_missiles) + # Herc_Ammo_TOW_missiles = (12, HerculesWeapons.Herc_Ammo_TOW_missiles) Herc_Ammo_GBU_10_bombs = (12, HerculesWeapons.Herc_Ammo_GBU_10_bombs) Herc_Ammo_GBU_12_bombs = (12, HerculesWeapons.Herc_Ammo_GBU_12_bombs) Herc_Ammo_GBU_16_bombs = (12, HerculesWeapons.Herc_Ammo_GBU_16_bombs) @@ -689,12 +1184,27 @@ class Hercules(PlaneType): Herc_Ammo_BETAB500_bombs = (12, HerculesWeapons.Herc_Ammo_BETAB500_bombs) Herc_Ammo_BETAB500SP_bombs = (12, HerculesWeapons.Herc_Ammo_BETAB500SP_bombs) Herc_Ammo_KAB500KR_bombs = (12, HerculesWeapons.Herc_Ammo_KAB500KR_bombs) - Herc_Ammo_RBK250PTAB25M_bombs = (12, HerculesWeapons.Herc_Ammo_RBK250PTAB25M_bombs) - Herc_Ammo_RBK500255PTAB105_bombs = (12, HerculesWeapons.Herc_Ammo_RBK500255PTAB105_bombs) - Herc_Ammo_RBK500PTAB1M_bombs = (12, HerculesWeapons.Herc_Ammo_RBK500PTAB1M_bombs) -#ERRR Herc_Ammo_Herc_Ammo_M117_bombs_bombs - Herc_Ammo_KMGU296AO25RT_bombs = (12, HerculesWeapons.Herc_Ammo_KMGU296AO25RT_bombs) - Herc_Ammo_KMGU296AO25KO_bombs = (12, HerculesWeapons.Herc_Ammo_KMGU296AO25KO_bombs) + Herc_Ammo_RBK250PTAB25M_bombs = ( + 12, + HerculesWeapons.Herc_Ammo_RBK250PTAB25M_bombs, + ) + Herc_Ammo_RBK500255PTAB105_bombs = ( + 12, + HerculesWeapons.Herc_Ammo_RBK500255PTAB105_bombs, + ) + Herc_Ammo_RBK500PTAB1M_bombs = ( + 12, + HerculesWeapons.Herc_Ammo_RBK500PTAB1M_bombs, + ) + # ERRR Herc_Ammo_Herc_Ammo_M117_bombs_bombs + Herc_Ammo_KMGU296AO25RT_bombs = ( + 12, + HerculesWeapons.Herc_Ammo_KMGU296AO25RT_bombs, + ) + Herc_Ammo_KMGU296AO25KO_bombs = ( + 12, + HerculesWeapons.Herc_Ammo_KMGU296AO25KO_bombs, + ) Herc_Ammo_MK20_bombs = (12, HerculesWeapons.Herc_Ammo_MK20_bombs) Herc_Ammo_SAB100_bombs = (12, HerculesWeapons.Herc_Ammo_SAB100_bombs) Herc_Ammo_hydra_HE_rockets = (12, HerculesWeapons.Herc_Ammo_hydra_HE_rockets) @@ -711,10 +1221,13 @@ class Hercules(PlaneType): Herc_Ammo_R27ER_missiles = (12, HerculesWeapons.Herc_Ammo_R27ER_missiles) Herc_Ammo_R27T_missiles = (12, HerculesWeapons.Herc_Ammo_R27T_missiles) Herc_Ammo_R27ET_missiles = (12, HerculesWeapons.Herc_Ammo_R27ET_missiles) -#ERRR Herc_Ammo_R27_missiles + # ERRR Herc_Ammo_R27_missiles Herc_Ammo_S530D_missiles = (12, HerculesWeapons.Herc_Ammo_S530D_missiles) Herc_Ammo_AIM54C_missiles = (12, HerculesWeapons.Herc_Ammo_AIM54C_missiles) - Herc_APC_M1043_HMMWV_Armament = (12, HerculesWeapons.Herc_APC_M1043_HMMWV_Armament) + Herc_APC_M1043_HMMWV_Armament = ( + 12, + HerculesWeapons.Herc_APC_M1043_HMMWV_Armament, + ) Herc_ATGM_M1045_HMMWV_TOW = (12, HerculesWeapons.Herc_ATGM_M1045_HMMWV_TOW) Herc_AAA_Vulcan_M163 = (12, HerculesWeapons.Herc_AAA_Vulcan_M163) Herc_APC_LAV_25 = (12, HerculesWeapons.Herc_APC_LAV_25) diff --git a/pydcs_extensions/highdigitsams/highdigitsams.py b/pydcs_extensions/highdigitsams/highdigitsams.py index a3cc4de7..a7e365dd 100644 --- a/pydcs_extensions/highdigitsams/highdigitsams.py +++ b/pydcs_extensions/highdigitsams/highdigitsams.py @@ -335,4 +335,3 @@ class _34Ya6E_Gazetchik_E_decoy(unittype.VehicleType): detection_range = 20000 threat_range = 0 air_weapon_dist = 0 - diff --git a/pydcs_extensions/mb339/mb339.py b/pydcs_extensions/mb339/mb339.py index 39a63f76..896a33b0 100644 --- a/pydcs_extensions/mb339/mb339.py +++ b/pydcs_extensions/mb339/mb339.py @@ -7,32 +7,104 @@ from dcs.weapons_data import Weapons class MB_339PAN_Weapons: ARF8M3_TP = {"clsid": "{ARF8M3_TP}", "name": "ARF8M3 TP", "weight": None} - BRD_4_250_4_MK_76_2_ARF_8M3TP_ = {"clsid": "{BRD-4-250}", "name": "BRD-4-250(4*MK.76+2*ARF-8M3TP)", "weight": 137.6} + BRD_4_250_4_MK_76_2_ARF_8M3TP_ = { + "clsid": "{BRD-4-250}", + "name": "BRD-4-250(4*MK.76+2*ARF-8M3TP)", + "weight": 137.6, + } Color_Oil_Tank = {"clsid": "{COLOR-TANK}", "name": "Color Oil Tank", "weight": 183} Empty_Pylon = {"clsid": "{VOID-PYLON-MB339A}", "name": "Empty Pylon", "weight": 20} - Fuel_Tank_330lt = {"clsid": "{FUEL-SUBAL_TANK-330}", "name": "Fuel Tank 330lt", "weight": 315} + Fuel_Tank_330lt = { + "clsid": "{FUEL-SUBAL_TANK-330}", + "name": "Fuel Tank 330lt", + "weight": 315, + } GunPod_AN_M3 = {"clsid": "{MB339-AN-M3_L}", "name": "GunPod AN/M3", "weight": 75} GunPod_AN_M3_ = {"clsid": "{MB339-AN-M3_R}", "name": "GunPod AN/M3", "weight": 75} - GunPod_DEFA553 = {"clsid": "{MB339-DEFA553_L}", "name": "GunPod DEFA553", "weight": 190} - GunPod_DEFA553_ = {"clsid": "{MB339-DEFA553_R}", "name": "GunPod DEFA553", "weight": 190} - LAU_10___4_ZUNI_MK_71___ = {"clsid": "{LAU-10}", "name": "LAU-10 - 4 ZUNI MK 71", "weight": 308} - LR_25___25_ARF_8M3_API_ = {"clsid": "{LR-25API}", "name": "LR-25 - 25 ARF/8M3(API)", "weight": 141} - LR_25___25_ARF_8M3_HEI_ = {"clsid": "{LR-25HEI}", "name": "LR-25 - 25 ARF/8M3(HEI)", "weight": 161} + GunPod_DEFA553 = { + "clsid": "{MB339-DEFA553_L}", + "name": "GunPod DEFA553", + "weight": 190, + } + GunPod_DEFA553_ = { + "clsid": "{MB339-DEFA553_R}", + "name": "GunPod DEFA553", + "weight": 190, + } + LAU_10___4_ZUNI_MK_71___ = { + "clsid": "{LAU-10}", + "name": "LAU-10 - 4 ZUNI MK 71", + "weight": 308, + } + LR_25___25_ARF_8M3_API_ = { + "clsid": "{LR-25API}", + "name": "LR-25 - 25 ARF/8M3(API)", + "weight": 141, + } + LR_25___25_ARF_8M3_HEI_ = { + "clsid": "{LR-25HEI}", + "name": "LR-25 - 25 ARF/8M3(HEI)", + "weight": 161, + } MAK79_2_MK_20 = {"clsid": "{MAK79_MK20 2L}", "name": "MAK79 2 MK-20", "weight": 464} - MAK79_2_MK_20_ = {"clsid": "{MAK79_MK20 2R}", "name": "MAK79 2 MK-20", "weight": 464} + MAK79_2_MK_20_ = { + "clsid": "{MAK79_MK20 2R}", + "name": "MAK79 2 MK-20", + "weight": 464, + } MAK79_MK_20 = {"clsid": "{MAK79_MK20 1R}", "name": "MAK79 MK-20", "weight": 232} MAK79_MK_20_ = {"clsid": "{MAK79_MK20 1L}", "name": "MAK79 MK-20", "weight": 232} - MB339_Black_Smoke = {"clsid": "{SMOKE-BLACK-MB339}", "name": "MB339 Black Smoke", "weight": 1} - MB339_Green_Smoke = {"clsid": "{SMOKE-GREEN-MB339}", "name": "MB339 Green Smoke", "weight": 1} - MB339_ORANGE_Smoke = {"clsid": "{SMOKE-ORANGE-MB339}", "name": "MB339 ORANGE Smoke", "weight": 1} - MB339_Red_Smoke = {"clsid": "{SMOKE-RED-MB339}", "name": "MB339 Red Smoke", "weight": 1} - MB339_White_Smoke = {"clsid": "{SMOKE-WHITE-MB339}", "name": "MB339 White Smoke", "weight": 1} - MB339_YELLOW_Smoke = {"clsid": "{SMOKE-YELLOW-MB339}", "name": "MB339 YELLOW Smoke", "weight": 1} + MB339_Black_Smoke = { + "clsid": "{SMOKE-BLACK-MB339}", + "name": "MB339 Black Smoke", + "weight": 1, + } + MB339_Green_Smoke = { + "clsid": "{SMOKE-GREEN-MB339}", + "name": "MB339 Green Smoke", + "weight": 1, + } + MB339_ORANGE_Smoke = { + "clsid": "{SMOKE-ORANGE-MB339}", + "name": "MB339 ORANGE Smoke", + "weight": 1, + } + MB339_Red_Smoke = { + "clsid": "{SMOKE-RED-MB339}", + "name": "MB339 Red Smoke", + "weight": 1, + } + MB339_White_Smoke = { + "clsid": "{SMOKE-WHITE-MB339}", + "name": "MB339 White Smoke", + "weight": 1, + } + MB339_YELLOW_Smoke = { + "clsid": "{SMOKE-YELLOW-MB339}", + "name": "MB339 YELLOW Smoke", + "weight": 1, + } MK76 = {"clsid": "{MK76}", "name": "MK76", "weight": 11.3} - Tip_Fuel_Tank_500lt = {"clsid": "{FUEL-TIP-TANK-500-L}", "name": "Tip Fuel Tank 500lt", "weight": 471} - Tip_Fuel_Tank_500lt_ = {"clsid": "{FUEL-TIP-TANK-500-R}", "name": "Tip Fuel Tank 500lt", "weight": 471} - Tip_Fuel_Tank_Ellittici_320lt = {"clsid": "{FUEL-TIP-ELLITTIC-L}", "name": "Tip Fuel Tank Ellittici 320lt", "weight": 314.2} - Tip_Fuel_Tank_Ellittici_320lt_ = {"clsid": "{FUEL-TIP-ELLITTIC-R}", "name": "Tip Fuel Tank Ellittici 320lt", "weight": 314.2} + Tip_Fuel_Tank_500lt = { + "clsid": "{FUEL-TIP-TANK-500-L}", + "name": "Tip Fuel Tank 500lt", + "weight": 471, + } + Tip_Fuel_Tank_500lt_ = { + "clsid": "{FUEL-TIP-TANK-500-R}", + "name": "Tip Fuel Tank 500lt", + "weight": 471, + } + Tip_Fuel_Tank_Ellittici_320lt = { + "clsid": "{FUEL-TIP-ELLITTIC-L}", + "name": "Tip Fuel Tank Ellittici 320lt", + "weight": 314.2, + } + Tip_Fuel_Tank_Ellittici_320lt_ = { + "clsid": "{FUEL-TIP-ELLITTIC-R}", + "name": "Tip Fuel Tank Ellittici 320lt", + "weight": 314.2, + } class MB_339PAN(PlaneType): @@ -43,7 +115,7 @@ class MB_339PAN(PlaneType): length = 12.13 fuel_max = 626 max_speed = 763.2 - category = "Interceptor" #{78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} + category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} radio_frequency = 124 panel_radio = { @@ -68,7 +140,7 @@ class MB_339PAN(PlaneType): 7: 262, 14: 266, 19: 268, - 15: 265 + 15: 265, }, }, 2: { @@ -102,7 +174,7 @@ class MB_339PAN(PlaneType): 28: 257, 23: 260, 29: 253, - 15: 265 + 15: 265, }, }, } @@ -113,7 +185,6 @@ class MB_339PAN(PlaneType): } class Properties: - class SoloFlight: id = "SoloFlight" @@ -127,7 +198,6 @@ class MB_339PAN(PlaneType): Equally_Responsible = -2 class Liveries: - class Georgia(Enum): MB339AA__ARMADA____Crippa = "MB339AA 'ARMADA' - Crippa" MB339AA__ARMADA____Yellow_Band = "MB339AA 'ARMADA' - Yellow Band" @@ -370,7 +440,10 @@ class MB_339PAN(PlaneType): class Pylon1: Tip_Fuel_Tank_500lt = (1, MB_339PAN_Weapons.Tip_Fuel_Tank_500lt) - Tip_Fuel_Tank_Ellittici_320lt = (1, MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt) + Tip_Fuel_Tank_Ellittici_320lt = ( + 1, + MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt, + ) class Pylon2: Empty_Pylon = (2, MB_339PAN_Weapons.Empty_Pylon) @@ -386,7 +459,10 @@ class MB_339PAN(PlaneType): LR_25___25_ARF_8M3_API_ = (3, MB_339PAN_Weapons.LR_25___25_ARF_8M3_API_) Mk_82 = (3, Weapons.Mk_82) LAU_10___4_ZUNI_MK_71___ = (3, MB_339PAN_Weapons.LAU_10___4_ZUNI_MK_71___) - BRD_4_250_4_MK_76_2_ARF_8M3TP_ = (3, MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_) + BRD_4_250_4_MK_76_2_ARF_8M3TP_ = ( + 3, + MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_, + ) Matra_Type_155_Rocket_Pod = (3, Weapons.Matra_Type_155_Rocket_Pod) class Pylon4: @@ -427,7 +503,10 @@ class MB_339PAN(PlaneType): Mk_82 = (8, Weapons.Mk_82) LAU_10___4_ZUNI_MK_71___ = (8, MB_339PAN_Weapons.LAU_10___4_ZUNI_MK_71___) Matra_Type_155_Rocket_Pod = (8, Weapons.Matra_Type_155_Rocket_Pod) - BRD_4_250_4_MK_76_2_ARF_8M3TP_ = (8, MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_) + BRD_4_250_4_MK_76_2_ARF_8M3TP_ = ( + 8, + MB_339PAN_Weapons.BRD_4_250_4_MK_76_2_ARF_8M3TP_, + ) class Pylon9: Empty_Pylon = (9, MB_339PAN_Weapons.Empty_Pylon) @@ -438,9 +517,18 @@ class MB_339PAN(PlaneType): class Pylon10: Tip_Fuel_Tank_500lt_ = (10, MB_339PAN_Weapons.Tip_Fuel_Tank_500lt_) - Tip_Fuel_Tank_Ellittici_320lt_ = (10, MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt_) + Tip_Fuel_Tank_Ellittici_320lt_ = ( + 10, + MB_339PAN_Weapons.Tip_Fuel_Tank_Ellittici_320lt_, + ) pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - tasks = [task.GroundAttack, task.RunwayAttack, task.CAS, task.AntishipStrike, task.Reconnaissance] - task_default = task.Nothing \ No newline at end of file + tasks = [ + task.GroundAttack, + task.RunwayAttack, + task.CAS, + task.AntishipStrike, + task.Reconnaissance, + ] + task_default = task.Nothing diff --git a/pydcs_extensions/mod_units.py b/pydcs_extensions/mod_units.py index 5dd1e93c..ec1580a0 100644 --- a/pydcs_extensions/mod_units.py +++ b/pydcs_extensions/mod_units.py @@ -7,7 +7,16 @@ from pydcs_extensions.rafale.rafale import Rafale_M, Rafale_A_S, Rafale_B from pydcs_extensions.su57.su57 import Su_57 import pydcs_extensions.frenchpack.frenchpack as frenchpack -MODDED_AIRPLANES = [A_4E_C, MB_339PAN, Rafale_A_S, Rafale_M, Rafale_B, Su_57, F_22A, Hercules] +MODDED_AIRPLANES = [ + A_4E_C, + MB_339PAN, + Rafale_A_S, + Rafale_M, + Rafale_B, + Su_57, + F_22A, + Hercules, +] MODDED_VEHICLES = [ frenchpack._FIELD_HIDE, frenchpack._FIELD_HIDE_SMALL, @@ -84,5 +93,5 @@ MODDED_VEHICLES = [ highdigitsams.SAM_SA_24_Igla_S_manpad, highdigitsams.SAM_SA_14_Strela_3_manpad, highdigitsams.Polyana_D4M1_C2_node, - highdigitsams._34Ya6E_Gazetchik_E_decoy -] \ No newline at end of file + highdigitsams._34Ya6E_Gazetchik_E_decoy, +] diff --git a/pydcs_extensions/rafale/rafale.py b/pydcs_extensions/rafale/rafale.py index 458a6e8e..ff1829e3 100644 --- a/pydcs_extensions/rafale/rafale.py +++ b/pydcs_extensions/rafale/rafale.py @@ -4,6 +4,7 @@ from dcs import task from dcs.planes import PlaneType from dcs.weapons_data import Weapons + class RafaleWeapons: SCALP = {"clsid": "{SCALP}", "name": "SCALP", "weight": None} AS_30L = {"clsid": "{AS_30L}", "name": "AS_30L", "weight": 292} @@ -25,16 +26,36 @@ class RafaleWeapons: RPL751_ = {"clsid": "{RPL751}", "name": "RPL751", "weight": 70} RPL751__ = {"clsid": "{RPL751}", "name": "RPL751", "weight": 70} METEOR = {"clsid": "{RAFALE_MBDA_METEOR}", "name": "METEOR", "weight": 199} - METEOR_x2 = {"clsid": "{LAU-115_2xLAU-127_MBDA_METEOR}", "name": "METEOR x2", "weight": 445} + METEOR_x2 = { + "clsid": "{LAU-115_2xLAU-127_MBDA_METEOR}", + "name": "METEOR x2", + "weight": 445, + } GBU_49 = {"clsid": "{GBU_49}", "name": "GBU_49", "weight": 525} GBU12PII = {"clsid": "{GBU12PII}", "name": "GBU12PII", "weight": 525} AASM_250 = {"clsid": "{AASM_250}", "name": "AASM_250", "weight": 250} AASM_250_L = {"clsid": "{AASM_250_L}", "name": "AASM_250_L", "weight": 500} AASM_250_R = {"clsid": "{AASM_250_R}", "name": "AASM_250_R", "weight": 500} - AASM_250_RIGHT = {"clsid": "{AASM_250_RIGHT}", "name": "AASM_250_RIGHT", "weight": 250} - _2_GBU_54_V_1_B = {"clsid": "{BRU-70A_2*GBU-54_LEFT}", "name": "2 GBU-54(V)1/B", "weight": 566} - _2_GBU_54_V_1_B_ = {"clsid": "{BRU-70A_2*GBU-54_RIGHT}", "name": "2 GBU-54(V)1/B", "weight": 566} - _3_GBU_54_V_1_B = {"clsid": "{BRU-70A_3*GBU-54}", "name": "3 GBU-54(V)1/B", "weight": 819} + AASM_250_RIGHT = { + "clsid": "{AASM_250_RIGHT}", + "name": "AASM_250_RIGHT", + "weight": 250, + } + _2_GBU_54_V_1_B = { + "clsid": "{BRU-70A_2*GBU-54_LEFT}", + "name": "2 GBU-54(V)1/B", + "weight": 566, + } + _2_GBU_54_V_1_B_ = { + "clsid": "{BRU-70A_2*GBU-54_RIGHT}", + "name": "2 GBU-54(V)1/B", + "weight": 566, + } + _3_GBU_54_V_1_B = { + "clsid": "{BRU-70A_3*GBU-54}", + "name": "3 GBU-54(V)1/B", + "weight": 819, + } class Rafale_A_S(PlaneType): @@ -50,11 +71,10 @@ class Rafale_A_S(PlaneType): charge_total = 96 chaff_charge_size = 1 flare_charge_size = 1 - category = "Interceptor" #{78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} + category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} radio_frequency = 127.5 class Liveries: - class USSR(Enum): _01_marine_12_f = "01 marine 12 f" _02_rafale_export = "02 rafale export" @@ -896,7 +916,10 @@ class Rafale_A_S(PlaneType): _3_Mk_20_Rockeye = (2, Weapons._3_Mk_20_Rockeye) Mk_84 = (2, Weapons.Mk_84) GBU_24 = (2, Weapons.GBU_24) - LAU_131___7_2_75__rockets_M151__HE_ = (2, Weapons.LAU_131___7_2_75__rockets_M151__HE_) + LAU_131___7_2_75__rockets_M151__HE_ = ( + 2, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ) LAU3_HE151 = (2, Weapons.LAU3_HE151) LAU3_WP156 = (2, Weapons.LAU3_WP156) LAU3_HE5 = (2, Weapons.LAU3_HE5) @@ -923,7 +946,10 @@ class Rafale_A_S(PlaneType): AIM_9P_Sidewinder_IR_AAM = (4, Weapons.AIM_9P_Sidewinder_IR_AAM) MICA_IR = (4, Weapons.MICA_IR) LAU_10___4_ZUNI_MK_71 = (4, Weapons.LAU_10___4_ZUNI_MK_71) - LAU_61___19_2_75__rockets_MK151_HE = (4, Weapons.LAU_61___19_2_75__rockets_MK151_HE) + LAU_61___19_2_75__rockets_MK151_HE = ( + 4, + Weapons.LAU_61___19_2_75__rockets_MK151_HE, + ) LAU3_HE151 = (4, Weapons.LAU3_HE151) class Pylon5: @@ -937,7 +963,10 @@ class Rafale_A_S(PlaneType): class Pylon6: LAU_10___4_ZUNI_MK_71 = (6, Weapons.LAU_10___4_ZUNI_MK_71) - LAU_61___19_2_75__rockets_MK151_HE = (6, Weapons.LAU_61___19_2_75__rockets_MK151_HE) + LAU_61___19_2_75__rockets_MK151_HE = ( + 6, + Weapons.LAU_61___19_2_75__rockets_MK151_HE, + ) AIM_9M_Sidewinder_IR_AAM = (6, Weapons.AIM_9M_Sidewinder_IR_AAM) AIM_9P_Sidewinder_IR_AAM = (6, Weapons.AIM_9P_Sidewinder_IR_AAM) MICA_IR = (6, Weapons.MICA_IR) @@ -973,7 +1002,10 @@ class Rafale_A_S(PlaneType): Mk_84 = (9, Weapons.Mk_84) _3_Mk_82 = (9, Weapons._3_Mk_82) AGM_88C_ = (9, Weapons.AGM_88C_) - LAU_131___7_2_75__rockets_M151__HE_ = (9, Weapons.LAU_131___7_2_75__rockets_M151__HE_) + LAU_131___7_2_75__rockets_M151__HE_ = ( + 9, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ) LAU3_HE151 = (9, Weapons.LAU3_HE151) LAU3_WP156 = (9, Weapons.LAU3_WP156) LAU3_HE5 = (9, Weapons.LAU3_HE5) @@ -993,7 +1025,18 @@ class Rafale_A_S(PlaneType): pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - tasks = [task.CAP, task.Escort, task.FighterSweep, task.GroundAttack, task.CAS, task.AFAC, task.RunwayAttack, task.AntishipStrike, task.SEAD, task.PinpointStrike] + tasks = [ + task.CAP, + task.Escort, + task.FighterSweep, + task.GroundAttack, + task.CAS, + task.AFAC, + task.RunwayAttack, + task.AntishipStrike, + task.SEAD, + task.PinpointStrike, + ] task_default = task.CAP @@ -1010,11 +1053,10 @@ class Rafale_M(PlaneType): charge_total = 96 chaff_charge_size = 1 flare_charge_size = 1 - category = "Interceptor" #{78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} + category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} radio_frequency = 127.5 class Liveries: - class USSR(Enum): _01_marine_12_f = "01 marine 12 f" _02_rafale_export = "02 rafale export" @@ -1851,7 +1893,10 @@ class Rafale_M(PlaneType): MER_2_MK_83 = (2, Weapons.MER_2_MK_83) MER_2_MK_82 = (2, Weapons.MER_2_MK_82) _3_Mk_82 = (2, Weapons._3_Mk_82) - LAU_131___7_2_75__rockets_M151__HE_ = (2, Weapons.LAU_131___7_2_75__rockets_M151__HE_) + LAU_131___7_2_75__rockets_M151__HE_ = ( + 2, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ) LAU3_HE151 = (2, Weapons.LAU3_HE151) LAU3_WP156 = (2, Weapons.LAU3_WP156) LAU3_HE5 = (2, Weapons.LAU3_HE5) @@ -1887,7 +1932,10 @@ class Rafale_M(PlaneType): AIM_9P_Sidewinder_IR_AAM = (4, Weapons.AIM_9P_Sidewinder_IR_AAM) LAU3_WP156 = (4, Weapons.LAU3_WP156) LAU_10___4_ZUNI_MK_71 = (4, Weapons.LAU_10___4_ZUNI_MK_71) - LAU_61___19_2_75__rockets_MK151_HE = (4, Weapons.LAU_61___19_2_75__rockets_MK151_HE) + LAU_61___19_2_75__rockets_MK151_HE = ( + 4, + Weapons.LAU_61___19_2_75__rockets_MK151_HE, + ) Mk_82 = (4, Weapons.Mk_82) class Pylon5: @@ -1906,7 +1954,10 @@ class Rafale_M(PlaneType): AIM_9P_Sidewinder_IR_AAM = (6, Weapons.AIM_9P_Sidewinder_IR_AAM) LAU3_WP156 = (6, Weapons.LAU3_WP156) LAU_10___4_ZUNI_MK_71 = (6, Weapons.LAU_10___4_ZUNI_MK_71) - LAU_61___19_2_75__rockets_MK151_HE = (6, Weapons.LAU_61___19_2_75__rockets_MK151_HE) + LAU_61___19_2_75__rockets_MK151_HE = ( + 6, + Weapons.LAU_61___19_2_75__rockets_MK151_HE, + ) Mk_82 = (6, Weapons.Mk_82) class Pylon7: @@ -1936,7 +1987,10 @@ class Rafale_M(PlaneType): MER_2_MK_83 = (9, Weapons.MER_2_MK_83) MER_2_MK_82 = (9, Weapons.MER_2_MK_82) _3_Mk_82 = (9, Weapons._3_Mk_82) - LAU_131___7_2_75__rockets_M151__HE_ = (9, Weapons.LAU_131___7_2_75__rockets_M151__HE_) + LAU_131___7_2_75__rockets_M151__HE_ = ( + 9, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ) LAU3_HE151 = (9, Weapons.LAU3_HE151) LAU3_WP156 = (9, Weapons.LAU3_WP156) LAU3_HE5 = (9, Weapons.LAU3_HE5) @@ -1961,7 +2015,18 @@ class Rafale_M(PlaneType): pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - tasks = [task.CAP, task.Escort, task.FighterSweep, task.GroundAttack, task.CAS, task.AFAC, task.RunwayAttack, task.AntishipStrike, task.Reconnaissance, task.Intercept] + tasks = [ + task.CAP, + task.Escort, + task.FighterSweep, + task.GroundAttack, + task.CAS, + task.AFAC, + task.RunwayAttack, + task.AntishipStrike, + task.Reconnaissance, + task.Intercept, + ] task_default = task.CAP @@ -1978,11 +2043,10 @@ class Rafale_B(PlaneType): charge_total = 96 chaff_charge_size = 1 flare_charge_size = 1 - category = "Interceptor" #{78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} + category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} radio_frequency = 127.5 class Liveries: - class USSR(Enum): _01_rafale_b_lafayette = "01 rafale b lafayette" _02_rafale_b_mt_de_marsan = "02 rafale b mt de marsan" @@ -2414,7 +2478,10 @@ class Rafale_B(PlaneType): _3_Mk_20_Rockeye = (2, Weapons._3_Mk_20_Rockeye) Mk_84 = (2, Weapons.Mk_84) GBU_24 = (2, Weapons.GBU_24) - LAU_131___7_2_75__rockets_M151__HE_ = (2, Weapons.LAU_131___7_2_75__rockets_M151__HE_) + LAU_131___7_2_75__rockets_M151__HE_ = ( + 2, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ) LAU3_HE151 = (2, Weapons.LAU3_HE151) LAU3_WP156 = (2, Weapons.LAU3_WP156) LAU3_HE5 = (2, Weapons.LAU3_HE5) @@ -2442,7 +2509,10 @@ class Rafale_B(PlaneType): AIM_9P_Sidewinder_IR_AAM = (4, Weapons.AIM_9P_Sidewinder_IR_AAM) MICA_IR = (4, Weapons.MICA_IR) LAU_10___4_ZUNI_MK_71 = (4, Weapons.LAU_10___4_ZUNI_MK_71) - LAU_61___19_2_75__rockets_MK151_HE = (4, Weapons.LAU_61___19_2_75__rockets_MK151_HE) + LAU_61___19_2_75__rockets_MK151_HE = ( + 4, + Weapons.LAU_61___19_2_75__rockets_MK151_HE, + ) LAU3_HE151 = (4, Weapons.LAU3_HE151) class Pylon5: @@ -2459,7 +2529,10 @@ class Rafale_B(PlaneType): class Pylon6: LAU_10___4_ZUNI_MK_71 = (6, Weapons.LAU_10___4_ZUNI_MK_71) - LAU_61___19_2_75__rockets_MK151_HE = (6, Weapons.LAU_61___19_2_75__rockets_MK151_HE) + LAU_61___19_2_75__rockets_MK151_HE = ( + 6, + Weapons.LAU_61___19_2_75__rockets_MK151_HE, + ) AIM_9M_Sidewinder_IR_AAM = (6, Weapons.AIM_9M_Sidewinder_IR_AAM) AIM_9P_Sidewinder_IR_AAM = (6, Weapons.AIM_9P_Sidewinder_IR_AAM) MICA_IR = (6, Weapons.MICA_IR) @@ -2496,7 +2569,10 @@ class Rafale_B(PlaneType): Mk_84 = (9, Weapons.Mk_84) _3_Mk_82 = (9, Weapons._3_Mk_82) AGM_88C_ = (9, Weapons.AGM_88C_) - LAU_131___7_2_75__rockets_M151__HE_ = (9, Weapons.LAU_131___7_2_75__rockets_M151__HE_) + LAU_131___7_2_75__rockets_M151__HE_ = ( + 9, + Weapons.LAU_131___7_2_75__rockets_M151__HE_, + ) LAU3_HE151 = (9, Weapons.LAU3_HE151) LAU3_WP156 = (9, Weapons.LAU3_WP156) LAU3_HE5 = (9, Weapons.LAU3_HE5) @@ -2516,7 +2592,18 @@ class Rafale_B(PlaneType): pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - tasks = [task.CAP, task.Escort, task.FighterSweep, task.GroundAttack, task.CAS, task.AFAC, task.RunwayAttack, task.AntishipStrike, task.SEAD, task.PinpointStrike] + tasks = [ + task.CAP, + task.Escort, + task.FighterSweep, + task.GroundAttack, + task.CAS, + task.AFAC, + task.RunwayAttack, + task.AntishipStrike, + task.SEAD, + task.PinpointStrike, + ] task_default = task.GroundAttack @@ -2534,11 +2621,10 @@ class Rafale_M_NOUNOU(PlaneType): chaff_charge_size = 1 flare_charge_size = 1 tacan = True - category = "Tankers" #{8A302789-A55D-4897-B647-66493FA6826F} + category = "Tankers" # {8A302789-A55D-4897-B647-66493FA6826F} radio_frequency = 127.5 class Liveries: - class USSR(Enum): _01_marine_12_f = "01 marine 12 f" _02_rafale_export = "02 rafale export" @@ -2977,4 +3063,3 @@ class Rafale_M_NOUNOU(PlaneType): tasks = [task.Refueling] task_default = task.Refueling - diff --git a/pydcs_extensions/su57/su57.py b/pydcs_extensions/su57/su57.py index a586222e..9b09d295 100644 --- a/pydcs_extensions/su57/su57.py +++ b/pydcs_extensions/su57/su57.py @@ -11,7 +11,11 @@ class Su57Weapons: RVV_BD = {"clsid": "{RVV-BD}", "name": "RVV-BD", "weight": 600} RVV_L = {"clsid": "{RVV-L}", "name": "RVV-L", "weight": 748} RVV_M = {"clsid": "{RVV-M}", "name": "RVV-M", "weight": 190} - Su_57_Fuel_Tank = {"clsid": "{SU_57Tank}", "name": "Su-57 Fuel Tank", "weight": 1561.421} + Su_57_Fuel_Tank = { + "clsid": "{SU_57Tank}", + "name": "Su-57 Fuel Tank", + "weight": 1561.421, + } class Su_57(PlaneType): @@ -27,10 +31,9 @@ class Su_57(PlaneType): charge_total = 200 chaff_charge_size = 1 flare_charge_size = 1 - category = "Interceptor" #{78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} + category = "Interceptor" # {78EFB7A2-FD52-4b57-A6A6-3BF0E1D6555F} class Liveries: - class USSR(Enum): _22 = "22" _20 = "20" @@ -1703,8 +1706,8 @@ class Su_57(PlaneType): FAB_1500_M54 = (11, Weapons.FAB_1500_M54) KAB_1500L = (11, Weapons.KAB_1500L) MER_6_FAB_250 = (11, Weapons.MER_6_FAB_250) -#ERRR {R-33} - RVV_BD = (11,Su57Weapons.RVV_BD) + # ERRR {R-33} + RVV_BD = (11, Su57Weapons.RVV_BD) RVV_AE = (11, Su57Weapons.RVV_AE) RVV_M = (11, Su57Weapons.RVV_M) RVV_L = (11, Su57Weapons.RVV_L) @@ -1732,5 +1735,15 @@ class Su_57(PlaneType): pylons = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} - tasks = [task.CAP, task.Intercept, task.Escort, task.FighterSweep, task.AFAC, task.GroundAttack, task.RunwayAttack, task.AntishipStrike, task.CAS] + tasks = [ + task.CAP, + task.Intercept, + task.Escort, + task.FighterSweep, + task.AFAC, + task.GroundAttack, + task.RunwayAttack, + task.AntishipStrike, + task.CAS, + ] task_default = task.CAP diff --git a/qt_ui/dialogs.py b/qt_ui/dialogs.py index 09fd2b93..bf9166e1 100644 --- a/qt_ui/dialogs.py +++ b/qt_ui/dialogs.py @@ -37,10 +37,7 @@ class Dialog: def open_new_package_dialog(cls, mission_target: MissionTarget, parent=None): """Opens the dialog to create a new package with the given target.""" cls.new_package_dialog = QNewPackageDialog( - cls.game_model, - cls.game_model.ato_model, - mission_target, - parent=parent + cls.game_model, cls.game_model.ato_model, mission_target, parent=parent ) cls.new_package_dialog.show() @@ -48,20 +45,16 @@ class Dialog: def open_edit_package_dialog(cls, package_model: PackageModel): """Opens the dialog to edit the given package.""" cls.edit_package_dialog = QEditPackageDialog( - cls.game_model, - cls.game_model.ato_model, - package_model + cls.game_model, cls.game_model.ato_model, package_model ) cls.edit_package_dialog.show() @classmethod - def open_edit_flight_dialog(cls, package_model: PackageModel, - flight: Flight, parent=None) -> None: + def open_edit_flight_dialog( + cls, package_model: PackageModel, flight: Flight, parent=None + ) -> None: """Opens the dialog to edit the given flight.""" cls.edit_flight_dialog = QEditFlightDialog( - cls.game_model, - package_model, - flight, - parent=parent + cls.game_model, package_model, flight, parent=parent ) cls.edit_flight_dialog.show() diff --git a/qt_ui/displayoptions.py b/qt_ui/displayoptions.py index 7b894b1d..423fe77f 100644 --- a/qt_ui/displayoptions.py +++ b/qt_ui/displayoptions.py @@ -20,6 +20,7 @@ class DisplayRule: @value.setter def value(self, value: bool) -> None: from qt_ui.widgets.map.QLiberationMap import QLiberationMap + self._value = value if QLiberationMap.instance is not None: QLiberationMap.instance.reload_scene() @@ -52,14 +53,16 @@ class FlightPathOptions(DisplayGroup): class ThreatZoneOptions(DisplayGroup): def __init__(self, coalition_name: str) -> None: super().__init__(f"{coalition_name} Threat Zones") - self.none = DisplayRule( - f"Hide {coalition_name.lower()} threat zones", True) + self.none = DisplayRule(f"Hide {coalition_name.lower()} threat zones", True) self.all = DisplayRule( - f"Show full {coalition_name.lower()} threat zones", False) + f"Show full {coalition_name.lower()} threat zones", False + ) self.aircraft = DisplayRule( - f"Show {coalition_name.lower()} aircraft threat tones", False) + f"Show {coalition_name.lower()} aircraft threat tones", False + ) self.air_defenses = DisplayRule( - f"Show {coalition_name.lower()} air defenses threat zones", False) + f"Show {coalition_name.lower()} air defenses threat zones", False + ) class NavMeshOptions(DisplayGroup): @@ -99,10 +102,8 @@ class DisplayOptions: map_poly = DisplayRule("Map Polygon Debug Mode", False) waypoint_info = DisplayRule("Waypoint Information", True) culling = DisplayRule("Display Culling Zones", False) - actual_frontline_pos = DisplayRule("Display Actual Frontline Location", - False) - barcap_commit_range = DisplayRule("Display selected BARCAP commit range", - False) + actual_frontline_pos = DisplayRule("Display Actual Frontline Location", False) + barcap_commit_range = DisplayRule("Display selected BARCAP commit range", False) flight_paths = FlightPathOptions() blue_threat_zones = ThreatZoneOptions("Blue") red_threat_zones = ThreatZoneOptions("Red") diff --git a/qt_ui/liberation_install.py b/qt_ui/liberation_install.py index a8044363..164c9323 100644 --- a/qt_ui/liberation_install.py +++ b/qt_ui/liberation_install.py @@ -20,7 +20,7 @@ def init(): if os.path.isfile(PREFERENCES_FILE_PATH): try: - with(open(PREFERENCES_FILE_PATH)) as prefs: + with (open(PREFERENCES_FILE_PATH)) as prefs: pref_data = json.loads(prefs.read()) __dcs_saved_game_directory = pref_data["saved_game_dir"] __dcs_installation_directory = pref_data["dcs_install_dir"] @@ -37,9 +37,13 @@ def init(): else: __last_save_file = "" try: - __dcs_saved_game_directory = dcs.installation.get_dcs_saved_games_directory() + __dcs_saved_game_directory = ( + dcs.installation.get_dcs_saved_games_directory() + ) if os.path.exists(__dcs_saved_game_directory + ".openbeta"): - __dcs_saved_game_directory = dcs.installation.get_dcs_saved_games_directory() + ".openbeta" + __dcs_saved_game_directory = ( + dcs.installation.get_dcs_saved_games_directory() + ".openbeta" + ) except: __dcs_saved_game_directory = "" try: @@ -69,10 +73,12 @@ def save_config(): global __dcs_saved_game_directory global __dcs_installation_directory global __last_save_file - pref_data = {"saved_game_dir": __dcs_saved_game_directory, - "dcs_install_dir": __dcs_installation_directory, - "last_save_file": __last_save_file} - with(open(PREFERENCES_FILE_PATH, "w")) as prefs: + pref_data = { + "saved_game_dir": __dcs_saved_game_directory, + "dcs_install_dir": __dcs_installation_directory, + "last_save_file": __last_save_file, + } + with (open(PREFERENCES_FILE_PATH, "w")) as prefs: prefs.write(json.dumps(pref_data)) @@ -97,7 +103,9 @@ def get_last_save_file(): def replace_mission_scripting_file(): install_dir = get_dcs_install_directory() - mission_scripting_path = os.path.join(install_dir, "Scripts", "MissionScripting.lua") + mission_scripting_path = os.path.join( + install_dir, "Scripts", "MissionScripting.lua" + ) liberation_scripting_path = "./resources/scripts/MissionScripting.lua" backup_scripting_path = "./resources/scripts/MissionScripting.original.lua" if os.path.isfile(mission_scripting_path): @@ -116,9 +124,10 @@ def replace_mission_scripting_file(): def restore_original_mission_scripting(): install_dir = get_dcs_install_directory() - mission_scripting_path = os.path.join(install_dir, "Scripts", "MissionScripting.lua") + mission_scripting_path = os.path.join( + install_dir, "Scripts", "MissionScripting.lua" + ) backup_scripting_path = "./resources/scripts/MissionScripting.original.lua" if os.path.isfile(backup_scripting_path) and os.path.isfile(mission_scripting_path): copyfile(backup_scripting_path, mission_scripting_path) - diff --git a/qt_ui/liberation_theme.py b/qt_ui/liberation_theme.py index 79714209..0bc7dbb3 100644 --- a/qt_ui/liberation_theme.py +++ b/qt_ui/liberation_theme.py @@ -12,16 +12,16 @@ DEFAULT_THEME_INDEX = 1 # new themes can be added here THEMES: Dict[int, Dict[str, str]] = { - 0: {'themeName': 'Vanilla', - 'themeFile': 'windows-style.css', - 'themeIcons': 'medium', - }, - - 1: {'themeName': 'DCS World', - 'themeFile': 'style-dcs.css', - 'themeIcons': 'light', - }, - + 0: { + "themeName": "Vanilla", + "themeFile": "windows-style.css", + "themeIcons": "medium", + }, + 1: { + "themeName": "DCS World", + "themeFile": "style-dcs.css", + "themeIcons": "light", + }, } @@ -32,7 +32,7 @@ def init(): if os.path.isfile(THEME_PREFERENCES_FILE_PATH): try: - with(open(THEME_PREFERENCES_FILE_PATH)) as prefs: + with (open(THEME_PREFERENCES_FILE_PATH)) as prefs: pref_data = json.loads(prefs.read()) __theme_index = pref_data["theme_index"] set_theme_index(__theme_index) @@ -64,26 +64,24 @@ def get_theme_index(): # get theme name based on current index def get_theme_name(): - theme_name = THEMES[get_theme_index()]['themeName'] + theme_name = THEMES[get_theme_index()]["themeName"] return theme_name # get theme icon sub-folder name based on current index def get_theme_icons(): - theme_icons = THEMES[get_theme_index()]['themeIcons'] + theme_icons = THEMES[get_theme_index()]["themeIcons"] return str(theme_icons) # get theme stylesheet css based on current index def get_theme_css_file(): - theme_file = THEMES[get_theme_index()]['themeFile'] + theme_file = THEMES[get_theme_index()]["themeFile"] return str(theme_file) # save current theme index to json file def save_theme_config(): - pref_data = { - "theme_index": get_theme_index() - } - with(open(THEME_PREFERENCES_FILE_PATH, "w")) as prefs: + pref_data = {"theme_index": get_theme_index()} + with (open(THEME_PREFERENCES_FILE_PATH, "w")) as prefs: prefs.write(json.dumps(pref_data)) diff --git a/qt_ui/logging_config.py b/qt_ui/logging_config.py index 739e8ac0..486c6bd5 100644 --- a/qt_ui/logging_config.py +++ b/qt_ui/logging_config.py @@ -13,7 +13,7 @@ def init_logging(version: str) -> None: logging.basicConfig(level=logging.DEBUG, format=fmt) logger = logging.getLogger() - handler = RotatingFileHandler('./logs/liberation.log', 'a', 5000000, 1) + handler = RotatingFileHandler("./logs/liberation.log", "a", 5000000, 1) handler.setLevel(logging.INFO) handler.setFormatter(logging.Formatter(fmt)) diff --git a/qt_ui/main.py b/qt_ui/main.py index b8984a2a..95b07f3d 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -31,28 +31,36 @@ from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.QLiberationWindow import QLiberationWindow from qt_ui.windows.newgame.QCampaignList import Campaign from qt_ui.windows.newgame.QNewGameWizard import DEFAULT_BUDGET -from qt_ui.windows.preferences.QLiberationFirstStartWindow import \ - QLiberationFirstStartWindow +from qt_ui.windows.preferences.QLiberationFirstStartWindow import ( + QLiberationFirstStartWindow, +) def run_ui(game: Optional[Game] = None) -> None: - os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # Potential fix for 4K screens + os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1" # Potential fix for 4K screens app = QApplication(sys.argv) # init the theme and load the stylesheet based on the theme index liberation_theme.init() - with open("./resources/stylesheets/"+liberation_theme.get_theme_css_file()) as stylesheet: - logging.info('Loading stylesheet: %s', liberation_theme.get_theme_css_file()) + with open( + "./resources/stylesheets/" + liberation_theme.get_theme_css_file() + ) as stylesheet: + logging.info("Loading stylesheet: %s", liberation_theme.get_theme_css_file()) app.setStyleSheet(stylesheet.read()) # Inject custom payload in pydcs framework - custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads") + custom_payloads = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "..\\resources\\customized_payloads", + ) if os.path.exists(custom_payloads): dcs.unittype.FlyingType.payload_dirs.append(custom_payloads) else: # For release version the path is different. - custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), - "resources\\customized_payloads") + custom_payloads = os.path.join( + os.path.dirname(os.path.realpath(__file__)), + "resources\\customized_payloads", + ) if os.path.exists(custom_payloads): dcs.unittype.FlyingType.payload_dirs.append(custom_payloads) @@ -62,7 +70,11 @@ def run_ui(game: Optional[Game] = None) -> None: window.exec_() logging.info("Using {} as 'Saved Game Folder'".format(persistency.base_path())) - logging.info("Using {} as 'DCS installation folder'".format(liberation_install.get_dcs_install_directory())) + logging.info( + "Using {} as 'DCS installation folder'".format( + liberation_install.get_dcs_install_directory() + ) + ) # Splash screen setup pixmap = QPixmap("./resources/ui/splash_screen.png") @@ -83,7 +95,9 @@ def run_ui(game: Optional[Game] = None) -> None: except: error_dialog = QtWidgets.QErrorMessage() error_dialog.setWindowTitle("Wrong DCS installation directory.") - error_dialog.showMessage("Unable to modify Mission Scripting file. Possible issues with rights. Try running as admin, or please perform the modification of the MissionScripting file manually.") + error_dialog.showMessage( + "Unable to modify Mission Scripting file. Possible issues with rights. Try running as admin, or please perform the modification of the MissionScripting file manually." + ) error_dialog.exec_() # Apply CSS (need works) @@ -113,15 +127,15 @@ def parse_args() -> argparse.Namespace: return path parser.add_argument( - "--warn-missing-weapon-data", action="store_true", - help="Emits a warning for weapons without date or fallback information." + "--warn-missing-weapon-data", + action="store_true", + help="Emits a warning for weapons without date or fallback information.", ) new_game = subparsers.add_parser("new-game") new_game.add_argument( - "campaign", type=path_arg, - help="Path to the campaign to start." + "campaign", type=path_arg, help="Path to the campaign to start." ) new_game.add_argument( @@ -133,34 +147,38 @@ def parse_args() -> argparse.Namespace: ) new_game.add_argument( - "--supercarrier", action="store_true", - help="Use the supercarrier module." + "--supercarrier", action="store_true", help="Use the supercarrier module." ) new_game.add_argument( - "--auto-procurement", action="store_true", - help="Automate bluefor procurement." + "--auto-procurement", action="store_true", help="Automate bluefor procurement." ) new_game.add_argument( - "--inverted", action="store_true", - help="Invert the campaign." + "--inverted", action="store_true", help="Invert the campaign." ) return parser.parse_args() -def create_game(campaign_path: Path, blue: str, red: str, - supercarrier: bool, auto_procurement: bool, - inverted: bool) -> Game: +def create_game( + campaign_path: Path, + blue: str, + red: str, + supercarrier: bool, + auto_procurement: bool, + inverted: bool, +) -> Game: campaign = Campaign.from_json(campaign_path) generator = GameGenerator( - blue, red, campaign.load_theater(), + blue, + red, + campaign.load_theater(), Settings( supercarrier=supercarrier, automate_runway_repair=auto_procurement, automate_front_line_reinforcements=auto_procurement, - automate_aircraft_reinforcements=auto_procurement + automate_aircraft_reinforcements=auto_procurement, ), GeneratorSettings( start_date=datetime.today(), @@ -171,8 +189,8 @@ def create_game(campaign_path: Path, blue: str, red: str, no_carrier=False, no_lha=False, no_player_navy=False, - no_enemy_navy=False - ) + no_enemy_navy=False, + ), ) return generator.generate() @@ -201,9 +219,14 @@ def main(): lint_weapon_data() if args.subcommand == "new-game": - game = create_game(args.campaign, args.blue, args.red, - args.supercarrier, args.auto_procurement, - args.inverted) + game = create_game( + args.campaign, + args.blue, + args.red, + args.supercarrier, + args.auto_procurement, + args.inverted, + ) run_ui(game) diff --git a/qt_ui/models.py b/qt_ui/models.py index 670c52ad..cdc594d6 100644 --- a/qt_ui/models.py +++ b/qt_ui/models.py @@ -125,7 +125,8 @@ class PackageModel(QAbstractListModel): """Returns the text that should be displayed for the flight.""" estimator = TotEstimator(self.package) delay = datetime.timedelta( - seconds=int(estimator.mission_start_time(flight).total_seconds())) + seconds=int(estimator.mission_start_time(flight).total_seconds()) + ) origin = flight.from_cp.name return f"{flight} from {origin} in {delay}" @@ -290,6 +291,7 @@ class GameModel: This isn't a real Qt data model, but simplifies management of the game and its ATO objects. """ + def __init__(self, game: Optional[Game]) -> None: self.game: Optional[Game] = game if self.game is None: diff --git a/qt_ui/uiconstants.py b/qt_ui/uiconstants.py index 04320a7d..b1016692 100644 --- a/qt_ui/uiconstants.py +++ b/qt_ui/uiconstants.py @@ -7,11 +7,11 @@ from game.theater.theatergroundobject import CATEGORY_MAP from .liberation_theme import get_theme_icons -URLS : Dict[str, str] = { +URLS: Dict[str, str] = { "Manual": "https://github.com/khopa/dcs_liberation/wiki", "Repository": "https://github.com/khopa/dcs_liberation", "ForumThread": "https://forums.eagle.ru/showthread.php?t=214834", - "Issues": "https://github.com/khopa/dcs_liberation/issues" + "Issues": "https://github.com/khopa/dcs_liberation/issues", } LABELS_OPTIONS = ["Full", "Abbreviated", "Dot Only", "Off"] @@ -28,46 +28,36 @@ FONT_MAP = QFont(FONT_NAME, 10, weight=75, italic=False) COLORS: Dict[str, QColor] = { "white": QColor(255, 255, 255), "white_transparent": QColor(255, 255, 255, 35), - "light_red": QColor(231, 92, 83, 90), "red": QColor(200, 80, 80), "dark_red": QColor(140, 20, 20), "red_transparent": QColor(227, 32, 0, 20), "transparent": QColor(255, 255, 255, 0), - "light_blue": QColor(105, 182, 240, 90), "blue": QColor(0, 132, 255), "dark_blue": QColor(45, 62, 80), "sea_blue": QColor(52, 68, 85), "sea_blue_transparent": QColor(52, 68, 85, 150), "blue_transparent": QColor(0, 132, 255, 20), - "purple": QColor(187, 137, 255), "yellow": QColor(238, 225, 123), - "bright_red": QColor(150, 80, 80), "super_red": QColor(227, 32, 0), - "green": QColor(128, 186, 128), "light_green": QColor(223, 255, 173), "light_green_transparent": QColor(180, 255, 140, 50), "bright_green": QColor(64, 200, 64), - "black": QColor(0, 0, 0), "black_transparent": QColor(0, 0, 0, 5), - "orange": QColor(254, 125, 10), - "night_overlay": QColor(12, 20, 69), "dawn_dust_overlay": QColor(46, 38, 85), - "grey": QColor(150, 150, 150), "grey_transparent": QColor(150, 150, 150, 150), "dark_grey": QColor(75, 75, 75), "dark_grey_transparent": QColor(75, 75, 75, 150), "dark_dark_grey": QColor(48, 48, 48), "dark_dark_grey_transparent": QColor(48, 48, 48, 150), - } CP_SIZE = 12 @@ -78,29 +68,44 @@ VEHICLE_BANNERS: Dict[str, QPixmap] = {} VEHICLES_ICONS: Dict[str, QPixmap] = {} ICONS: Dict[str, QPixmap] = {} + def load_icons(): - ICONS["New"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/new.png") - ICONS["Open"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/open.png") - ICONS["Save"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/save.png") - ICONS["Discord"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/discord.png") - ICONS["Github"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/github.png") + ICONS["New"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/new.png") + ICONS["Open"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/open.png") + ICONS["Save"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/save.png") + ICONS["Discord"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/discord.png" + ) + ICONS["Github"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/github.png" + ) - - ICONS["Control Points"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/circle.png") - ICONS["Ground Objects"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/industry.png") - ICONS["Lines"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/arrows-h.png") - ICONS["Waypoint Information"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/info.png") - ICONS["Map Polygon Debug Mode"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/map.png") + ICONS["Control Points"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/circle.png" + ) + ICONS["Ground Objects"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/industry.png" + ) + ICONS["Lines"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/arrows-h.png" + ) + ICONS["Waypoint Information"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/info.png" + ) + ICONS["Map Polygon Debug Mode"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/map.png" + ) ICONS["Ally SAM Threat Range"] = QPixmap("./resources/ui/misc/blue-sam.png") ICONS["Enemy SAM Threat Range"] = QPixmap("./resources/ui/misc/red-sam.png") ICONS["SAM Detection Range"] = QPixmap("./resources/ui/misc/detection-sam.png") - ICONS["Display Culling Zones"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/eraser.png") + ICONS["Display Culling Zones"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/eraser.png" + ) ICONS["Hide Flight Paths"] = QPixmap("./resources/ui/misc/hide-flight-path.png") ICONS["Show Selected Flight Path"] = QPixmap("./resources/ui/misc/flight-path.png") ICONS["Show All Flight Paths"] = QPixmap("./resources/ui/misc/all-flight-paths.png") - ICONS["Hangar"] = QPixmap("./resources/ui/misc/hangar.png") ICONS["Terrain_Caucasus"] = QPixmap("./resources/ui/terrain_caucasus.gif") @@ -115,18 +120,32 @@ def load_icons(): ICONS["Dusk"] = QPixmap("./resources/ui/conditions/timeofday/dusk.png") ICONS["Night"] = QPixmap("./resources/ui/conditions/timeofday/night.png") - ICONS["Money"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/money_icon.png") - ICONS["PassTurn"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/hourglass.png") - ICONS["Proceed"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/proceed.png") - ICONS["Settings"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/settings.png") - ICONS["Statistics"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/statistics.png") - ICONS["Ordnance"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/ordnance_icon.png") + ICONS["Money"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/money_icon.png" + ) + ICONS["PassTurn"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/hourglass.png" + ) + ICONS["Proceed"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/proceed.png" + ) + ICONS["Settings"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/settings.png" + ) + ICONS["Statistics"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/statistics.png" + ) + ICONS["Ordnance"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/ordnance_icon.png" + ) ICONS["target"] = QPixmap("./resources/ui/ground_assets/target.png") ICONS["cleared"] = QPixmap("./resources/ui/ground_assets/cleared.png") for category in CATEGORY_MAP.keys(): ICONS[category] = QPixmap("./resources/ui/ground_assets/" + category + ".png") - ICONS[category + "_blue"] = QPixmap("./resources/ui/ground_assets/" + category + "_blue.png") + ICONS[category + "_blue"] = QPixmap( + "./resources/ui/ground_assets/" + category + "_blue.png" + ) ICONS["destroyed"] = QPixmap("./resources/ui/ground_assets/destroyed.png") ICONS["ship"] = QPixmap("./resources/ui/ground_assets/ship.png") ICONS["ship_blue"] = QPixmap("./resources/ui/ground_assets/ship_blue.png") @@ -137,11 +156,19 @@ def load_icons(): ICONS["coastal"] = QPixmap("./resources/ui/ground_assets/coastal.png") ICONS["coastal_blue"] = QPixmap("./resources/ui/ground_assets/coastal_blue.png") - ICONS["Generator"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/generator.png") - ICONS["Missile"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/missile.png") - ICONS["Cheat"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/cheat.png") - ICONS["Plugins"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/plugins.png") - ICONS["PluginsOptions"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/pluginsoptions.png") + ICONS["Generator"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/generator.png" + ) + ICONS["Missile"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/missile.png" + ) + ICONS["Cheat"] = QPixmap("./resources/ui/misc/" + get_theme_icons() + "/cheat.png") + ICONS["Plugins"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/plugins.png" + ) + ICONS["PluginsOptions"] = QPixmap( + "./resources/ui/misc/" + get_theme_icons() + "/pluginsoptions.png" + ) ICONS["TaskCAS"] = QPixmap("./resources/ui/tasks/cas.png") ICONS["TaskCAP"] = QPixmap("./resources/ui/tasks/cap.png") @@ -152,20 +179,46 @@ def load_icons(): Weather Icons """ ICONS["Weather_winds"] = QPixmap("./resources/ui/conditions/weather/winds.png") - ICONS["Weather_day-clear"] = QPixmap("./resources/ui/conditions/weather/day-clear.png") - ICONS["Weather_day-cloudy-fog"] = QPixmap("./resources/ui/conditions/weather/day-cloudy-fog.png") + ICONS["Weather_day-clear"] = QPixmap( + "./resources/ui/conditions/weather/day-clear.png" + ) + ICONS["Weather_day-cloudy-fog"] = QPixmap( + "./resources/ui/conditions/weather/day-cloudy-fog.png" + ) ICONS["Weather_day-fog"] = QPixmap("./resources/ui/conditions/weather/day-fog.png") - ICONS["Weather_day-partly-cloudy"] = QPixmap("./resources/ui/conditions/weather/day-partly-cloudy.png") - ICONS["Weather_day-rain"] = QPixmap("./resources/ui/conditions/weather/day-rain.png") - ICONS["Weather_day-thunderstorm"] = QPixmap("./resources/ui/conditions/weather/day-thunderstorm.png") - ICONS["Weather_day-totally-cloud"] = QPixmap("./resources/ui/conditions/weather/day-totally-cloud.png") - ICONS["Weather_night-clear"] = QPixmap("./resources/ui/conditions/weather/night-clear.png") - ICONS["Weather_night-cloudy-fog"] = QPixmap("./resources/ui/conditions/weather/night-cloudy-fog.png") - ICONS["Weather_night-fog"] = QPixmap("./resources/ui/conditions/weather/night-fog.png") - ICONS["Weather_night-partly-cloudy"] = QPixmap("./resources/ui/conditions/weather/night-partly-cloudy.png") - ICONS["Weather_night-rain"] = QPixmap("./resources/ui/conditions/weather/night-rain.png") - ICONS["Weather_night-thunderstorm"] = QPixmap("./resources/ui/conditions/weather/night-thunderstorm.png") - ICONS["Weather_night-totally-cloud"] = QPixmap("./resources/ui/conditions/weather/night-totally-cloud.png") + ICONS["Weather_day-partly-cloudy"] = QPixmap( + "./resources/ui/conditions/weather/day-partly-cloudy.png" + ) + ICONS["Weather_day-rain"] = QPixmap( + "./resources/ui/conditions/weather/day-rain.png" + ) + ICONS["Weather_day-thunderstorm"] = QPixmap( + "./resources/ui/conditions/weather/day-thunderstorm.png" + ) + ICONS["Weather_day-totally-cloud"] = QPixmap( + "./resources/ui/conditions/weather/day-totally-cloud.png" + ) + ICONS["Weather_night-clear"] = QPixmap( + "./resources/ui/conditions/weather/night-clear.png" + ) + ICONS["Weather_night-cloudy-fog"] = QPixmap( + "./resources/ui/conditions/weather/night-cloudy-fog.png" + ) + ICONS["Weather_night-fog"] = QPixmap( + "./resources/ui/conditions/weather/night-fog.png" + ) + ICONS["Weather_night-partly-cloudy"] = QPixmap( + "./resources/ui/conditions/weather/night-partly-cloudy.png" + ) + ICONS["Weather_night-rain"] = QPixmap( + "./resources/ui/conditions/weather/night-rain.png" + ) + ICONS["Weather_night-thunderstorm"] = QPixmap( + "./resources/ui/conditions/weather/night-thunderstorm.png" + ) + ICONS["Weather_night-totally-cloud"] = QPixmap( + "./resources/ui/conditions/weather/night-totally-cloud.png" + ) EVENT_ICONS: Dict[str, QPixmap] = {} @@ -174,12 +227,17 @@ EVENT_ICONS: Dict[str, QPixmap] = {} def load_event_icons(): for image in os.listdir("./resources/ui/events/"): if image.endswith(".PNG"): - EVENT_ICONS[image[:-4]] = QPixmap(os.path.join("./resources/ui/events/", image)) + EVENT_ICONS[image[:-4]] = QPixmap( + os.path.join("./resources/ui/events/", image) + ) + def load_aircraft_icons(): for aircraft in os.listdir("./resources/ui/units/aircrafts/icons/"): if aircraft.endswith(".jpg"): - AIRCRAFT_ICONS[aircraft[:-7]] = QPixmap(os.path.join("./resources/ui/units/aircrafts/icons/", aircraft)) + AIRCRAFT_ICONS[aircraft[:-7]] = QPixmap( + os.path.join("./resources/ui/units/aircrafts/icons/", aircraft) + ) AIRCRAFT_ICONS["F-16C_50"] = AIRCRAFT_ICONS["F-16C"] AIRCRAFT_ICONS["FA-18C_hornet"] = AIRCRAFT_ICONS["FA-18C"] AIRCRAFT_ICONS["A-10C_2"] = AIRCRAFT_ICONS["A-10C"] @@ -188,14 +246,22 @@ def load_aircraft_icons(): def load_vehicle_icons(): for vehicle in os.listdir("./resources/ui/units/vehicles/icons/"): if vehicle.endswith(".jpg"): - VEHICLES_ICONS[vehicle[:-7]] = QPixmap(os.path.join("./resources/ui/units/vehicles/icons/", vehicle)) + VEHICLES_ICONS[vehicle[:-7]] = QPixmap( + os.path.join("./resources/ui/units/vehicles/icons/", vehicle) + ) + def load_aircraft_banners(): for aircraft in os.listdir("./resources/ui/units/aircrafts/banners/"): if aircraft.endswith(".jpg"): - AIRCRAFT_BANNERS[aircraft[:-7]] = QPixmap(os.path.join("./resources/ui/units/aircrafts/banners/", aircraft)) + AIRCRAFT_BANNERS[aircraft[:-7]] = QPixmap( + os.path.join("./resources/ui/units/aircrafts/banners/", aircraft) + ) + def load_vehicle_banners(): for aircraft in os.listdir("./resources/ui/units/vehicles/banners/"): if aircraft.endswith(".jpg"): - VEHICLE_BANNERS[aircraft[:-7]] = QPixmap(os.path.join("./resources/ui/units/vehicles/banners/", aircraft)) \ No newline at end of file + VEHICLE_BANNERS[aircraft[:-7]] = QPixmap( + os.path.join("./resources/ui/units/vehicles/banners/", aircraft) + ) diff --git a/qt_ui/widgets/QBudgetBox.py b/qt_ui/widgets/QBudgetBox.py index c8ba24dd..fe20ab44 100644 --- a/qt_ui/widgets/QBudgetBox.py +++ b/qt_ui/widgets/QBudgetBox.py @@ -35,7 +35,9 @@ class QBudgetBox(QGroupBox): :param budget: Current money available :param reward: Planned reward for next turn """ - self.money_amount.setText(str(round(budget,2)) + "M (+" + str(round(reward,2)) + "M)") + self.money_amount.setText( + str(round(budget, 2)) + "M (+" + str(round(reward, 2)) + "M)" + ) def setGame(self, game): if game is None: @@ -47,4 +49,4 @@ class QBudgetBox(QGroupBox): def openFinances(self): self.subwindow = QFinancesMenu(self.game) - self.subwindow.show() \ No newline at end of file + self.subwindow.show() diff --git a/qt_ui/widgets/QConditionsWidget.py b/qt_ui/widgets/QConditionsWidget.py index 89cf2cdb..661905f4 100644 --- a/qt_ui/widgets/QConditionsWidget.py +++ b/qt_ui/widgets/QConditionsWidget.py @@ -18,10 +18,12 @@ class QTimeTurnWidget(QGroupBox): """ UI Component to display current turn and time info """ - + def __init__(self): super(QTimeTurnWidget, self).__init__("Turn") - self.setStyleSheet('padding: 0px; margin-left: 5px; margin-right: 0px; margin-top: 1ex; margin-bottom: 5px; border-right: 0px') + self.setStyleSheet( + "padding: 0px; margin-left: 5px; margin-right: 0px; margin-top: 1ex; margin-bottom: 5px; border-right: 0px" + ) self.icons = { TimeOfDay.Dawn: CONST.ICONS["Dawn"], @@ -55,20 +57,21 @@ class QTimeTurnWidget(QGroupBox): """ self.daytime_icon.setPixmap(self.icons[conditions.time_of_day]) self.date_display.setText(conditions.start_time.strftime("%d %b %Y")) - self.time_display.setText( - conditions.start_time.strftime("%H:%M:%S Local")) + self.time_display.setText(conditions.start_time.strftime("%H:%M:%S Local")) self.setTitle(f"Turn {turn}") + class QWeatherWidget(QGroupBox): """ UI Component to display current weather forecast """ + turn = None conditions = None def __init__(self): super(QWeatherWidget, self).__init__("") - self.setProperty('style', 'QWeatherWidget') + self.setProperty("style", "QWeatherWidget") self.icons = { TimeOfDay.Dawn: CONST.ICONS["Dawn"], @@ -83,17 +86,15 @@ class QWeatherWidget(QGroupBox): self.makeWeatherIcon() self.makeCloudRainFogWidget() self.makeWindsWidget() - + def makeWeatherIcon(self): - """Makes the Weather Icon Widget - """ + """Makes the Weather Icon Widget""" self.weather_icon = QLabel() self.weather_icon.setPixmap(self.icons[TimeOfDay.Dawn]) self.layout.addWidget(self.weather_icon) def makeCloudRainFogWidget(self): - """Makes the Cloud, Rain, Fog Widget - """ + """Makes the Cloud, Rain, Fog Widget""" self.textLayout = QVBoxLayout() self.layout.addLayout(self.textLayout) @@ -107,43 +108,41 @@ class QWeatherWidget(QGroupBox): self.textLayout.addWidget(self.forecastFog) def makeWindsWidget(self): - """Factory for the winds widget. - """ + """Factory for the winds widget.""" windsLayout = QGridLayout() self.layout.addLayout(windsLayout) - windsLayout.addWidget(self.makeIcon(CONST.ICONS['Weather_winds']), 0, 0, 3, 1) + windsLayout.addWidget(self.makeIcon(CONST.ICONS["Weather_winds"]), 0, 0, 3, 1) - windsLayout.addWidget(self.makeLabel('At GL'), 0, 1) - windsLayout.addWidget(self.makeLabel('At FL08'), 1, 1) - windsLayout.addWidget(self.makeLabel('At FL26'), 2, 1) + windsLayout.addWidget(self.makeLabel("At GL"), 0, 1) + windsLayout.addWidget(self.makeLabel("At FL08"), 1, 1) + windsLayout.addWidget(self.makeLabel("At FL26"), 2, 1) - self.windGLSpeedLabel = self.makeLabel('0kts') - self.windGLDirLabel = self.makeLabel('0º') + self.windGLSpeedLabel = self.makeLabel("0kts") + self.windGLDirLabel = self.makeLabel("0º") windsLayout.addWidget(self.windGLSpeedLabel, 0, 2) windsLayout.addWidget(self.windGLDirLabel, 0, 3) - - self.windFL08SpeedLabel = self.makeLabel('0kts') - self.windFL08DirLabel = self.makeLabel('0º') + self.windFL08SpeedLabel = self.makeLabel("0kts") + self.windFL08DirLabel = self.makeLabel("0º") windsLayout.addWidget(self.windFL08SpeedLabel, 1, 2) windsLayout.addWidget(self.windFL08DirLabel, 1, 3) - self.windFL26SpeedLabel = self.makeLabel('0kts') - self.windFL26DirLabel = self.makeLabel('0º') + self.windFL26SpeedLabel = self.makeLabel("0kts") + self.windFL26DirLabel = self.makeLabel("0º") windsLayout.addWidget(self.windFL26SpeedLabel, 2, 2) windsLayout.addWidget(self.windFL26DirLabel, 2, 3) - def makeLabel(self, text: str = '') -> QLabel: + def makeLabel(self, text: str = "") -> QLabel: """Shorthand to generate a QLabel with widget standard style :arg pixmap QPixmap for the icon. """ label = QLabel(text) - label.setProperty('style', 'text-sm') + label.setProperty("style", "text-sm") return label - + def makeIcon(self, pixmap: QPixmap) -> QLabel: """Shorthand to generate a QIcon with pixmap. @@ -167,26 +166,28 @@ class QWeatherWidget(QGroupBox): self.updateWinds() def updateWinds(self): - """Updates the UI with the current conditions wind info. - """ + """Updates the UI with the current conditions wind info.""" windGlSpeed = mps(self.conditions.weather.wind.at_0m.speed or 0) - windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, '0') - self.windGLSpeedLabel.setText(f'{int(windGlSpeed.knots)}kts') - self.windGLDirLabel.setText(f'{windGlDir}º') + windGlDir = str(self.conditions.weather.wind.at_0m.direction or 0).rjust(3, "0") + self.windGLSpeedLabel.setText(f"{int(windGlSpeed.knots)}kts") + self.windGLDirLabel.setText(f"{windGlDir}º") windFL08Speed = mps(self.conditions.weather.wind.at_2000m.speed or 0) - windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust(3, '0') - self.windFL08SpeedLabel.setText(f'{int(windFL08Speed.knots)}kts') - self.windFL08DirLabel.setText(f'{windFL08Dir}º') + windFL08Dir = str(self.conditions.weather.wind.at_2000m.direction or 0).rjust( + 3, "0" + ) + self.windFL08SpeedLabel.setText(f"{int(windFL08Speed.knots)}kts") + self.windFL08DirLabel.setText(f"{windFL08Dir}º") windFL26Speed = mps(self.conditions.weather.wind.at_8000m.speed or 0) - windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust(3, '0') - self.windFL26SpeedLabel.setText(f'{int(windFL26Speed.knots)}kts') - self.windFL26DirLabel.setText(f'{windFL26Dir}º') + windFL26Dir = str(self.conditions.weather.wind.at_8000m.direction or 0).rjust( + 3, "0" + ) + self.windFL26SpeedLabel.setText(f"{int(windFL26Speed.knots)}kts") + self.windFL26DirLabel.setText(f"{windFL26Dir}º") def updateForecast(self): - """Updates the Forecast Text and icon with the current conditions wind info. - """ + """Updates the Forecast Text and icon with the current conditions wind info.""" icon = [] if self.conditions.weather.clouds is None: cloudDensity = 0 @@ -197,44 +198,44 @@ class QWeatherWidget(QGroupBox): fog = self.conditions.weather.fog or None is_night = self.conditions.time_of_day == TimeOfDay.Night - time = 'night' if is_night else 'day' + time = "night" if is_night else "day" if cloudDensity <= 0: - self.forecastClouds.setText('Sunny') - icon = [time, 'clear'] - + self.forecastClouds.setText("Sunny") + icon = [time, "clear"] + if cloudDensity > 0 and cloudDensity < 3: - self.forecastClouds.setText('Partly Cloudy') - icon = [time, 'partly-cloudy'] + self.forecastClouds.setText("Partly Cloudy") + icon = [time, "partly-cloudy"] if cloudDensity >= 3 and cloudDensity < 5: - self.forecastClouds.setText('Mostly Cloudy') - icon = [time, 'partly-cloudy'] + self.forecastClouds.setText("Mostly Cloudy") + icon = [time, "partly-cloudy"] if cloudDensity >= 5: - self.forecastClouds.setText('Totally Cloudy') - icon = [time, 'partly-cloudy'] + self.forecastClouds.setText("Totally Cloudy") + icon = [time, "partly-cloudy"] if precipitation == PydcsWeather.Preceptions.Rain: - self.forecastRain.setText('Rain') - icon = [time, 'rain'] + self.forecastRain.setText("Rain") + icon = [time, "rain"] elif precipitation == PydcsWeather.Preceptions.Thunderstorm: - self.forecastRain.setText('Thunderstorm') - icon = [time, 'thunderstorm'] - + self.forecastRain.setText("Thunderstorm") + icon = [time, "thunderstorm"] + else: - self.forecastRain.setText('No Rain') + self.forecastRain.setText("No Rain") if not fog: - self.forecastFog.setText('No fog') - else: + self.forecastFog.setText("No fog") + else: visibility = round(fog.visibility.nautical_miles, 1) - self.forecastFog.setText(f'Fog vis: {visibility}nm') - icon = [time, ('cloudy' if cloudDensity > 1 else None), 'fog'] + self.forecastFog.setText(f"Fog vis: {visibility}nm") + icon = [time, ("cloudy" if cloudDensity > 1 else None), "fog"] - icon_key = "Weather_{}".format('-'.join(filter(None.__ne__, icon))) - icon = CONST.ICONS.get(icon_key) or CONST.ICONS['Weather_night-partly-cloudy'] + icon_key = "Weather_{}".format("-".join(filter(None.__ne__, icon))) + icon = CONST.ICONS.get(icon_key) or CONST.ICONS["Weather_night-partly-cloudy"] self.weather_icon.setPixmap(icon) @@ -245,7 +246,7 @@ class QConditionsWidget(QFrame): def __init__(self): super(QConditionsWidget, self).__init__() - self.setProperty('style', 'QConditionsWidget') + self.setProperty("style", "QConditionsWidget") self.layout = QGridLayout() self.layout.setContentsMargins(0, 0, 0, 0) @@ -254,11 +255,13 @@ class QConditionsWidget(QFrame): self.setLayout(self.layout) self.time_turn_widget = QTimeTurnWidget() - self.time_turn_widget.setStyleSheet('QGroupBox { margin-right: 0px; }') + self.time_turn_widget.setStyleSheet("QGroupBox { margin-right: 0px; }") self.layout.addWidget(self.time_turn_widget, 0, 0) self.weather_widget = QWeatherWidget() - self.weather_widget.setStyleSheet('QGroupBox { margin-top: 5px; margin-left: 0px; border-left: 0px; }') + self.weather_widget.setStyleSheet( + "QGroupBox { margin-top: 5px; margin-left: 0px; border-left: 0px; }" + ) self.weather_widget.hide() self.layout.addWidget(self.weather_widget, 0, 1) @@ -271,4 +274,3 @@ class QConditionsWidget(QFrame): self.time_turn_widget.setCurrentTurn(turn, conditions) self.weather_widget.setCurrentTurn(turn, conditions) self.weather_widget.show() - diff --git a/qt_ui/widgets/QDebriefingInformation.py b/qt_ui/widgets/QDebriefingInformation.py index abf6caea..9c7ddcef 100644 --- a/qt_ui/widgets/QDebriefingInformation.py +++ b/qt_ui/widgets/QDebriefingInformation.py @@ -8,4 +8,4 @@ class QDebriefingInformation(QFrame): def __init__(self): super(QDebriefingInformation, self).__init__() - self.init_ui() \ No newline at end of file + self.init_ui() diff --git a/qt_ui/widgets/QFactionsInfos.py b/qt_ui/widgets/QFactionsInfos.py index 79b5848d..40369356 100644 --- a/qt_ui/widgets/QFactionsInfos.py +++ b/qt_ui/widgets/QFactionsInfos.py @@ -16,10 +16,10 @@ class QFactionsInfos(QGroupBox): self.layout = QGridLayout() self.layout.setSpacing(0) - self.layout.addWidget(QLabel("Player : "),0,0) - self.layout.addWidget(self.player_name,0,1) - self.layout.addWidget(QLabel("Enemy : "),1,0) - self.layout.addWidget(self.enemy_name,1,1) + self.layout.addWidget(QLabel("Player : "), 0, 0) + self.layout.addWidget(self.player_name, 0, 1) + self.layout.addWidget(QLabel("Enemy : "), 1, 0) + self.layout.addWidget(self.enemy_name, 1, 1) self.setLayout(self.layout) def setGame(self, game: Game): @@ -29,4 +29,3 @@ class QFactionsInfos(QGroupBox): else: self.player_name.setText("") self.enemy_name.setText("") - diff --git a/qt_ui/widgets/QFlightSizeSpinner.py b/qt_ui/widgets/QFlightSizeSpinner.py index a2619507..30cb8002 100644 --- a/qt_ui/widgets/QFlightSizeSpinner.py +++ b/qt_ui/widgets/QFlightSizeSpinner.py @@ -5,8 +5,9 @@ from PySide2.QtWidgets import QSpinBox class QFlightSizeSpinner(QSpinBox): """Spin box for selecting the number of aircraft in a flight.""" - def __init__(self, min_size: int = 1, max_size: int = 4, - default_size: int = 2) -> None: + def __init__( + self, min_size: int = 1, max_size: int = 4, default_size: int = 2 + ) -> None: super().__init__() self.setMinimum(min_size) self.setMaximum(max_size) diff --git a/qt_ui/widgets/QIntelBox.py b/qt_ui/widgets/QIntelBox.py index 3925baf3..2e3eb7e8 100644 --- a/qt_ui/widgets/QIntelBox.py +++ b/qt_ui/widgets/QIntelBox.py @@ -94,12 +94,16 @@ class QIntelBox(QGroupBox): data = self.game.game_stats.data_per_turn[-1] - self.air_strength.setText(self.forces_strength_text( - data.allied_units.aircraft_count, - data.enemy_units.aircraft_count)) - self.ground_strength.setText(self.forces_strength_text( - data.allied_units.vehicles_count, - data.enemy_units.vehicles_count)) + self.air_strength.setText( + self.forces_strength_text( + data.allied_units.aircraft_count, data.enemy_units.aircraft_count + ) + ) + self.ground_strength.setText( + self.forces_strength_text( + data.allied_units.vehicles_count, data.enemy_units.vehicles_count + ) + ) self.economic_strength.setText(self.economic_strength_text()) def open_details_window(self) -> None: diff --git a/qt_ui/widgets/QLabeledWidget.py b/qt_ui/widgets/QLabeledWidget.py index 91bb52bb..547f8b7b 100644 --- a/qt_ui/widgets/QLabeledWidget.py +++ b/qt_ui/widgets/QLabeledWidget.py @@ -12,8 +12,9 @@ class QLabeledWidget(QHBoxLayout): label is used to name the input. """ - def __init__(self, text: str, widget: QWidget, - tooltip: Optional[str] = None) -> None: + def __init__( + self, text: str, widget: QWidget, tooltip: Optional[str] = None + ) -> None: super().__init__() label = QLabel(text) self.addWidget(label) diff --git a/qt_ui/widgets/QTopPanel.py b/qt_ui/widgets/QTopPanel.py index a01b2953..68834d28 100644 --- a/qt_ui/widgets/QTopPanel.py +++ b/qt_ui/widgets/QTopPanel.py @@ -22,15 +22,13 @@ from qt_ui.widgets.QFactionsInfos import QFactionsInfos from qt_ui.widgets.QIntelBox import QIntelBox from qt_ui.widgets.clientslots import MaxPlayerCount from qt_ui.windows.GameUpdateSignal import GameUpdateSignal -from qt_ui.windows.QWaitingForMissionResultWindow import \ - QWaitingForMissionResultWindow +from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow from qt_ui.windows.settings.QSettingsWindow import QSettingsWindow from qt_ui.windows.stats.QStatsWindow import QStatsWindow from qt_ui.widgets.QConditionsWidget import QConditionsWidget class QTopPanel(QFrame): - def __init__(self, game_model: GameModel): super(QTopPanel, self).__init__() self.game_model = game_model @@ -43,7 +41,7 @@ class QTopPanel(QFrame): def game(self) -> Optional[Game]: return self.game_model.game - def init_ui(self): + def init_ui(self): self.conditionsWidget = QConditionsWidget() self.budgetBox = QBudgetBox(self.game) @@ -85,14 +83,13 @@ class QTopPanel(QFrame): self.proceedBox = QGroupBox("Proceed") self.proceedBoxLayout = QHBoxLayout() - self.proceedBoxLayout.addLayout( - MaxPlayerCount(self.game_model.ato_model)) + self.proceedBoxLayout.addLayout(MaxPlayerCount(self.game_model.ato_model)) self.proceedBoxLayout.addWidget(self.passTurnButton) self.proceedBoxLayout.addWidget(self.proceedButton) self.proceedBox.setLayout(self.proceedBoxLayout) self.layout = QHBoxLayout() - + self.layout.addWidget(self.factionsInfos) self.layout.addWidget(self.conditionsWidget) self.layout.addWidget(self.budgetBox) @@ -101,8 +98,8 @@ class QTopPanel(QFrame): self.layout.addStretch(1) self.layout.addWidget(self.proceedBox) - self.layout.setContentsMargins(0,0,0,0) - + self.layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(self.layout) def setGame(self, game: Optional[Game]): @@ -169,48 +166,50 @@ class QTopPanel(QFrame): result = QMessageBox.question( self, "Continue without client slots?", - ("No client slots have been created for players. Continuing will " - "allow the AI to perform the mission, but players will be unable " - "to participate.
" - "
" - "To add client slots for players, select a package from the " - "Packages panel on the left of the main window, and then a flight " - "from the Flights panel below the Packages panel. The edit button " - "below the Flights panel will allow you to edit the number of " - "client slots in the flight. Each client slot allows one player.
" - "
Click 'Yes' to continue with an AI only mission" - "
Click 'No' if you'd like to make more changes."), + ( + "No client slots have been created for players. Continuing will " + "allow the AI to perform the mission, but players will be unable " + "to participate.
" + "
" + "To add client slots for players, select a package from the " + "Packages panel on the left of the main window, and then a flight " + "from the Flights panel below the Packages panel. The edit button " + "below the Flights panel will allow you to edit the number of " + "client slots in the flight. Each client slot allows one player.
" + "
Click 'Yes' to continue with an AI only mission" + "
Click 'No' if you'd like to make more changes." + ), QMessageBox.No, - QMessageBox.Yes + QMessageBox.Yes, ) return result == QMessageBox.Yes - def confirm_negative_start_time(self, - negative_starts: List[Package]) -> bool: - formatted = '
'.join( + def confirm_negative_start_time(self, negative_starts: List[Package]) -> bool: + formatted = "
".join( [f"{p.primary_task} {p.target.name}" for p in negative_starts] ) mbox = QMessageBox( QMessageBox.Question, "Continue with past start times?", - ("Some flights in the following packages have start times set " - "earlier than mission start time:
" - "
" - f"{formatted}
" - "
" - "Flight start times are estimated based on the package TOT, so it " - "is possible that not all flights will be able to reach the " - "target area at their assigned times.
" - "
" - "You can either continue with the mission as planned, with the " - "misplanned flights potentially flying too fast and/or missing " - "their rendezvous; automatically fix negative TOTs; or cancel " - "mission start and fix the packages manually."), - parent=self + ( + "Some flights in the following packages have start times set " + "earlier than mission start time:
" + "
" + f"{formatted}
" + "
" + "Flight start times are estimated based on the package TOT, so it " + "is possible that not all flights will be able to reach the " + "target area at their assigned times.
" + "
" + "You can either continue with the mission as planned, with the " + "misplanned flights potentially flying too fast and/or missing " + "their rendezvous; automatically fix negative TOTs; or cancel " + "mission start and fix the packages manually." + ), + parent=self, ) auto = mbox.addButton("Fix TOTs automatically", QMessageBox.ActionRole) - ignore = mbox.addButton("Continue without fixing", - QMessageBox.DestructiveRole) + ignore = mbox.addButton("Continue without fixing", QMessageBox.DestructiveRole) cancel = mbox.addButton(QMessageBox.Cancel) mbox.setEscapeButton(cancel) mbox.exec_() @@ -238,12 +237,12 @@ class QTopPanel(QFrame): closest_cps[1], self.game.theater.controlpoints[0].position, self.game.player_name, - self.game.enemy_name) + self.game.enemy_name, + ) unit_map = self.game.initiate_event(game_event) - waiting = QWaitingForMissionResultWindow(game_event, self.game, - unit_map) + waiting = QWaitingForMissionResultWindow(game_event, self.game, unit_map) waiting.show() - def budget_update(self, game:Game): + def budget_update(self, game: Game): self.budgetBox.setGame(game) diff --git a/qt_ui/widgets/ato.py b/qt_ui/widgets/ato.py index aece9ee1..fa5e7072 100644 --- a/qt_ui/widgets/ato.py +++ b/qt_ui/widgets/ato.py @@ -71,34 +71,38 @@ class FlightDelegate(QStyledItemDelegate): return f"From {origin} to {flight.arrival.name}" return f"From {origin}" - def paint(self, painter: QPainter, option: QStyleOptionViewItem, - index: QModelIndex) -> None: + def paint( + self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex + ) -> None: # Draw the list item with all the default selection styling, but with an # invalid index so text formatting is left to us. super().paint(painter, option, QModelIndex()) - rect = option.rect.adjusted(self.HMARGIN, self.VMARGIN, -self.HMARGIN, - -self.VMARGIN) + rect = option.rect.adjusted( + self.HMARGIN, self.VMARGIN, -self.HMARGIN, -self.VMARGIN + ) with painter_context(painter): painter.setFont(self.get_font(option)) icon: Optional[QIcon] = index.data(Qt.DecorationRole) if icon is not None: - icon.paint(painter, rect, Qt.AlignLeft | Qt.AlignVCenter, - self.icon_mode(option), - self.icon_state(option)) + icon.paint( + painter, + rect, + Qt.AlignLeft | Qt.AlignVCenter, + self.icon_mode(option), + self.icon_state(option), + ) - rect = rect.adjusted(self.icon_size(option).width() + self.HMARGIN, - 0, 0, 0) + rect = rect.adjusted(self.icon_size(option).width() + self.HMARGIN, 0, 0, 0) painter.drawText(rect, Qt.AlignLeft, self.first_row_text(index)) line2 = rect.adjusted(0, rect.height() / 2, 0, rect.height() / 2) painter.drawText(line2, Qt.AlignLeft, self.second_row_text(index)) clients = self.num_clients(index) if clients: - painter.drawText(rect, Qt.AlignRight, - f"Player Slots: {clients}") + painter.drawText(rect, Qt.AlignRight, f"Player Slots: {clients}") def num_clients(self, index: QModelIndex) -> int: flight = self.flight(index) @@ -126,22 +130,24 @@ class FlightDelegate(QStyledItemDelegate): else: return icon_size - def sizeHint(self, option: QStyleOptionViewItem, - index: QModelIndex) -> QSize: + def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize: left = self.icon_size(option).width() + self.HMARGIN metrics = QFontMetrics(self.get_font(option)) first = metrics.size(0, self.first_row_text(index)) second = metrics.size(0, self.second_row_text(index)) text_width = max(first.width(), second.width()) - return QSize(left + text_width + 2 * self.HMARGIN, - first.height() + second.height() + 2 * self.VMARGIN) + return QSize( + left + text_width + 2 * self.HMARGIN, + first.height() + second.height() + 2 * self.VMARGIN, + ) class QFlightList(QListView): """List view for displaying the flights of a package.""" - def __init__(self, game_model: GameModel, - package_model: Optional[PackageModel]) -> None: + def __init__( + self, game_model: GameModel, package_model: Optional[PackageModel] + ) -> None: super().__init__() self.game_model = game_model self.package_model = package_model @@ -163,8 +169,7 @@ class QFlightList(QListView): # noinspection PyUnresolvedReferences model.deleted.connect(self.disconnect_model) self.selectionModel().setCurrentIndex( - model.index(0, 0, QModelIndex()), - QItemSelectionModel.Select + model.index(0, 0, QModelIndex()), QItemSelectionModel.Select ) def disconnect_model(self) -> None: @@ -192,14 +197,15 @@ class QFlightList(QListView): def edit_flight(self, index: QModelIndex) -> None: from qt_ui.dialogs import Dialog + Dialog.open_edit_flight_dialog( - self.package_model, self.package_model.flight_at_index(index), - parent=self.window() + self.package_model, + self.package_model.flight_at_index(index), + parent=self.window(), ) def delete_flight(self, index: QModelIndex) -> None: - self.game_model.game.aircraft_inventory.return_from_flight( - self.selected_item) + self.game_model.game.aircraft_inventory.return_from_flight(self.selected_item) self.package_model.delete_flight_at_index(index) GameUpdateSignal.get_instance().redraw_flight_paths() @@ -226,8 +232,9 @@ class QFlightPanel(QGroupBox): delete buttons for flight management. """ - def __init__(self, game_model: GameModel, - package_model: Optional[PackageModel] = None) -> None: + def __init__( + self, game_model: GameModel, package_model: Optional[PackageModel] = None + ) -> None: super().__init__("Flights") self.game_model = game_model self.package_model = package_model @@ -336,14 +343,16 @@ class PackageDelegate(QStyledItemDelegate): package = self.package(index) return f"TOT T+{package.time_over_target}" - def paint(self, painter: QPainter, option: QStyleOptionViewItem, - index: QModelIndex) -> None: + def paint( + self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex + ) -> None: # Draw the list item with all the default selection styling, but with an # invalid index so text formatting is left to us. super().paint(painter, option, QModelIndex()) - rect = option.rect.adjusted(self.HMARGIN, self.VMARGIN, -self.HMARGIN, - -self.VMARGIN) + rect = option.rect.adjusted( + self.HMARGIN, self.VMARGIN, -self.HMARGIN, -self.VMARGIN + ) with painter_context(painter): painter.setFont(self.get_font(option)) @@ -354,20 +363,20 @@ class PackageDelegate(QStyledItemDelegate): clients = self.num_clients(index) if clients: - painter.drawText(rect, Qt.AlignRight, - f"Player Slots: {clients}") + painter.drawText(rect, Qt.AlignRight, f"Player Slots: {clients}") def num_clients(self, index: QModelIndex) -> int: package = self.package(index) return sum(f.client_count for f in package.flights) - def sizeHint(self, option: QStyleOptionViewItem, - index: QModelIndex) -> QSize: + def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex) -> QSize: metrics = QFontMetrics(self.get_font(option)) left = metrics.size(0, self.left_text(index)) right = metrics.size(0, self.right_text(index)) - return QSize(max(left.width(), right.width()) + 2 * self.HMARGIN, - left.height() + right.height() + 2 * self.VMARGIN) + return QSize( + max(left.width(), right.width()) + 2 * self.HMARGIN, + left.height() + right.height() + 2 * self.VMARGIN, + ) class QPackageList(QListView): @@ -393,19 +402,20 @@ class QPackageList(QListView): def edit_package(self, index: QModelIndex) -> None: from qt_ui.dialogs import Dialog + Dialog.open_edit_package_dialog(self.ato_model.get_package_model(index)) def delete_package(self, index: QModelIndex) -> None: self.ato_model.delete_package_at_index(index) GameUpdateSignal.get_instance().redraw_flight_paths() - def on_new_packages(self, _parent: QModelIndex, first: int, - _last: int) -> None: + def on_new_packages(self, _parent: QModelIndex, first: int, _last: int) -> None: # Select the newly created pacakges. This should only ever happen due to # the player saving a new package, so selecting it helps them view/edit # it faster. - self.selectionModel().setCurrentIndex(self.model().index(first, 0), - QItemSelectionModel.Select) + self.selectionModel().setCurrentIndex( + self.model().index(first, 0), QItemSelectionModel.Select + ) def on_double_click(self, index: QModelIndex) -> None: if not index.isValid(): @@ -533,8 +543,6 @@ class QAirTaskingOrderPanel(QSplitter): """Sets the newly selected flight for display in the bottom panel.""" index = self.package_panel.package_list.currentIndex() if index.isValid(): - self.flight_panel.set_package( - self.ato_model.get_package_model(index) - ) + self.flight_panel.set_package(self.ato_model.get_package_model(index)) else: self.flight_panel.set_package(None) diff --git a/qt_ui/widgets/clientslots.py b/qt_ui/widgets/clientslots.py index 1c9fff9b..231ed61a 100644 --- a/qt_ui/widgets/clientslots.py +++ b/qt_ui/widgets/clientslots.py @@ -11,9 +11,12 @@ class MaxPlayerCount(QLabeledWidget): self.slots_label = QLabel(str(self.count_client_slots)) self.ato_model.client_slots_changed.connect(self.update_count) super().__init__( - "Max Players:", self.slots_label, - ("Total number of client slots. To add client slots, edit a flight " - "using the panel on the left.") + "Max Players:", + self.slots_label, + ( + "Total number of client slots. To add client slots, edit a flight " + "using the panel on the left." + ), ) @property diff --git a/qt_ui/widgets/combos/QAircraftTypeSelector.py b/qt_ui/widgets/combos/QAircraftTypeSelector.py index dc478c67..5afa3761 100644 --- a/qt_ui/widgets/combos/QAircraftTypeSelector.py +++ b/qt_ui/widgets/combos/QAircraftTypeSelector.py @@ -11,10 +11,16 @@ import gen.flights.ai_flight_planner_db from game import Game, db + class QAircraftTypeSelector(QComboBox): """Combo box for selecting among the given aircraft types.""" - def __init__(self, aircraft_types: Iterable[Type[FlyingType]], country: str, mission_type: str) -> None: + def __init__( + self, + aircraft_types: Iterable[Type[FlyingType]], + country: str, + mission_type: str, + ) -> None: super().__init__() self.model().sort(0) @@ -26,32 +32,72 @@ class QAircraftTypeSelector(QComboBox): current_aircraft = self.currentData() self.clear() for aircraft in aircraft_types: - if mission_type in [FlightType.BARCAP, FlightType.ESCORT, FlightType.INTERCEPTION, FlightType.SWEEP, FlightType.TARCAP]: + if mission_type in [ + FlightType.BARCAP, + FlightType.ESCORT, + FlightType.INTERCEPTION, + FlightType.SWEEP, + FlightType.TARCAP, + ]: if aircraft in gen.flights.ai_flight_planner_db.CAP_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) - elif mission_type in [FlightType.CAS, FlightType.BAI, FlightType.OCA_AIRCRAFT]: - if aircraft in gen.flights.ai_flight_planner_db.CAS_CAPABLE or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) + elif mission_type in [ + FlightType.CAS, + FlightType.BAI, + FlightType.OCA_AIRCRAFT, + ]: + if ( + aircraft in gen.flights.ai_flight_planner_db.CAS_CAPABLE + or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE + ): + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) elif mission_type in [FlightType.SEAD]: if aircraft in gen.flights.ai_flight_planner_db.SEAD_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) elif mission_type in [FlightType.DEAD]: if aircraft in gen.flights.ai_flight_planner_db.DEAD_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) elif mission_type in [FlightType.STRIKE]: - if aircraft in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) + if ( + aircraft in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE + or aircraft in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE + ): + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) elif mission_type in [FlightType.ANTISHIP]: if aircraft in gen.flights.ai_flight_planner_db.ANTISHIP_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) elif mission_type in [FlightType.OCA_RUNWAY]: if aircraft in gen.flights.ai_flight_planner_db.RUNWAY_ATTACK_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) elif mission_type in [FlightType.AEWC]: if aircraft in gen.flights.ai_flight_planner_db.AEWC_CAPABLE: - self.addItem(f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", userData=aircraft) + self.addItem( + f"{db.unit_get_expanded_info(self.country, aircraft, 'name')}", + userData=aircraft, + ) current_aircraft_index = self.findData(current_aircraft) if current_aircraft_index != -1: self.setCurrentIndex(current_aircraft_index) if self.count() == 0: - self.addItem("No capable aircraft available", userData=None) \ No newline at end of file + self.addItem("No capable aircraft available", userData=None) diff --git a/qt_ui/widgets/combos/QArrivalAirfieldSelector.py b/qt_ui/widgets/combos/QArrivalAirfieldSelector.py index 22097b34..e4075f57 100644 --- a/qt_ui/widgets/combos/QArrivalAirfieldSelector.py +++ b/qt_ui/widgets/combos/QArrivalAirfieldSelector.py @@ -14,8 +14,12 @@ class QArrivalAirfieldSelector(QComboBox): aircraft type is able to land at. """ - def __init__(self, destinations: Iterable[ControlPoint], - aircraft: Type[FlyingType], optional_text: str) -> None: + def __init__( + self, + destinations: Iterable[ControlPoint], + aircraft: Type[FlyingType], + optional_text: str, + ) -> None: super().__init__() self.destinations = list(destinations) self.aircraft = aircraft diff --git a/qt_ui/widgets/combos/QFilteredComboBox.py b/qt_ui/widgets/combos/QFilteredComboBox.py index 7d152b2e..1fae0cf8 100644 --- a/qt_ui/widgets/combos/QFilteredComboBox.py +++ b/qt_ui/widgets/combos/QFilteredComboBox.py @@ -3,9 +3,16 @@ from PySide2.QtWidgets import QComboBox, QCompleter class QFilteredComboBox(QComboBox): - - def __init__(self, parent=None, include_targets=True, include_airbases=True, - include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True): + def __init__( + self, + parent=None, + include_targets=True, + include_airbases=True, + include_frontlines=True, + include_units=True, + include_enemy=True, + include_friendly=True, + ): super(QFilteredComboBox, self).__init__(parent) self.setFocusPolicy(Qt.StrongFocus) diff --git a/qt_ui/widgets/combos/QOriginAirfieldSelector.py b/qt_ui/widgets/combos/QOriginAirfieldSelector.py index 364f8b04..75342fd1 100644 --- a/qt_ui/widgets/combos/QOriginAirfieldSelector.py +++ b/qt_ui/widgets/combos/QOriginAirfieldSelector.py @@ -18,9 +18,12 @@ class QOriginAirfieldSelector(QComboBox): availability_changed = Signal(int) - def __init__(self, global_inventory: GlobalAircraftInventory, - origins: Iterable[ControlPoint], - aircraft: Type[FlyingType]) -> None: + def __init__( + self, + global_inventory: GlobalAircraftInventory, + origins: Iterable[ControlPoint], + aircraft: Type[FlyingType], + ) -> None: super().__init__() self.global_inventory = global_inventory self.origins = list(origins) diff --git a/qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py b/qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py index 8bb72d81..5de3527e 100644 --- a/qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py +++ b/qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py @@ -9,9 +9,17 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox class QPredefinedWaypointSelectionComboBox(QFilteredComboBox): - - def __init__(self, game: Game, parent=None, include_targets=True, include_airbases=True, - include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True): + def __init__( + self, + game: Game, + parent=None, + include_targets=True, + include_airbases=True, + include_frontlines=True, + include_units=True, + include_enemy=True, + include_friendly=True, + ): super(QPredefinedWaypointSelectionComboBox, self).__init__(parent) self.game = game self.include_targets = include_targets @@ -34,7 +42,11 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox): waypoints = [first_waypoint] if include_all_from_same_location: for w in self.wpts: - if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name: + if ( + w is not first_waypoint + and w.obj_name + and w.obj_name == first_waypoint.obj_name + ): waypoints.append(w) return waypoints @@ -53,14 +65,19 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox): if self.include_frontlines: for cp in self.game.theater.controlpoints: if cp.captured: - enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured] + enemy_cp = [ + ecp + for ecp in cp.connected_points + if ecp.captured != cp.captured + ] for ecp in enemy_cp: pos = Conflict.frontline_position(cp, ecp, self.game.theater)[0] wpt = FlightWaypoint( FlightWaypointType.CUSTOM, pos.x, pos.y, - Distance.from_meters(800)) + Distance.from_meters(800), + ) wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]" wpt.alt_type = "RADIO" wpt.pretty_name = wpt.name @@ -69,14 +86,18 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox): if self.include_targets: for cp in self.game.theater.controlpoints: - if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured): + if (self.include_enemy and not cp.captured) or ( + self.include_friendly and cp.captured + ): for ground_object in cp.ground_objects: - if not ground_object.is_dead and isinstance(ground_object, BuildingGroundObject): + if not ground_object.is_dead and isinstance( + ground_object, BuildingGroundObject + ): wpt = FlightWaypoint( FlightWaypointType.CUSTOM, ground_object.position.x, ground_object.position.y, - Distance.from_meters(0) + Distance.from_meters(0), ) wpt.alt_type = "RADIO" wpt.name = ground_object.waypoint_name @@ -91,19 +112,31 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox): if self.include_units: for cp in self.game.theater.controlpoints: - if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured): + if (self.include_enemy and not cp.captured) or ( + self.include_friendly and cp.captured + ): for ground_object in cp.ground_objects: - if not ground_object.is_dead and ground_object.dcs_identifier == "AA": + if ( + not ground_object.is_dead + and ground_object.dcs_identifier == "AA" + ): for g in ground_object.groups: for j, u in enumerate(g.units): wpt = FlightWaypoint( FlightWaypointType.CUSTOM, u.position.x, u.position.y, - Distance.from_meters(0) + Distance.from_meters(0), ) wpt.alt_type = "RADIO" - wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j) + wpt.name = wpt.name = ( + "[" + + str(ground_object.obj_name) + + "] : " + + u.type + + " #" + + str(j) + ) wpt.pretty_name = wpt.name wpt.targets.append(u) wpt.obj_name = ground_object.obj_name @@ -116,17 +149,21 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox): if self.include_airbases: for cp in self.game.theater.controlpoints: - if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured): + if (self.include_enemy and not cp.captured) or ( + self.include_friendly and cp.captured + ): wpt = FlightWaypoint( FlightWaypointType.CUSTOM, cp.position.x, cp.position.y, - Distance.from_meters(0) + Distance.from_meters(0), ) wpt.alt_type = "RADIO" wpt.name = cp.name if cp.captured: - wpt.description = "Position of " + cp.name + " [Friendly Airbase]" + wpt.description = ( + "Position of " + cp.name + " [Friendly Airbase]" + ) else: wpt.description = "Position of " + cp.name + " [Enemy Airbase]" diff --git a/qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py b/qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py index a037d1b4..3cd46bf9 100644 --- a/qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py +++ b/qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py @@ -7,7 +7,6 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox class SEADTargetInfo: - def __init__(self): self.name = "" self.location = None @@ -15,8 +14,8 @@ class SEADTargetInfo: self.threat_range = 0 self.detection_range = 0 -class QSEADTargetSelectionComboBox(QFilteredComboBox): +class QSEADTargetSelectionComboBox(QFilteredComboBox): def __init__(self, game: Game, parent=None): super(QSEADTargetSelectionComboBox, self).__init__(parent) self.game = game @@ -41,7 +40,8 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox): return i + 1 for cp in self.game.theater.controlpoints: - if cp.captured: continue + if cp.captured: + continue for g in cp.ground_objects: radars = [] @@ -53,7 +53,10 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox): utype = db.unit_type_from_name(u.type) if utype in UNITS_WITH_RADAR: - if hasattr(utype, "detection_range") and utype.detection_range > 1000: + if ( + hasattr(utype, "detection_range") + and utype.detection_range > 1000 + ): if utype.detection_range > detection_range: detection_range = utype.detection_range radars.append(u) @@ -63,9 +66,18 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox): threat_range = utype.threat_range if len(radars) > 0: tgt_info = SEADTargetInfo() - tgt_info.name = g.obj_name + " [" + ",".join([db.unit_type_from_name(u.type).id for u in radars]) + " ]" + tgt_info.name = ( + g.obj_name + + " [" + + ",".join( + [db.unit_type_from_name(u.type).id for u in radars] + ) + + " ]" + ) if len(tgt_info.name) > 25: - tgt_info.name = g.obj_name + " [" + str(len(radars)) + " units]" + tgt_info.name = ( + g.obj_name + " [" + str(len(radars)) + " units]" + ) tgt_info.radars = radars tgt_info.location = g tgt_info.threat_range = threat_range @@ -73,5 +85,3 @@ class QSEADTargetSelectionComboBox(QFilteredComboBox): i = add_model_item(i, model, tgt_info) self.setModel(model) - - diff --git a/qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py b/qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py index b5fcc57d..d31c501c 100644 --- a/qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py +++ b/qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py @@ -5,7 +5,6 @@ from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox class StrikeTargetInfo: - def __init__(self): self.name = "" self.location = None @@ -14,17 +13,14 @@ class StrikeTargetInfo: class QStrikeTargetSelectionComboBox(QFilteredComboBox): - def __init__(self, game: Game, parent=None): super(QStrikeTargetSelectionComboBox, self).__init__(parent) self.game = game self.find_possible_strike_targets() - for t in self.targets: print(t.name + " - " + str(len(t.units)) + " " + str(len(t.buildings))) - def get_selected_target(self) -> StrikeTargetInfo: n = self.currentText() for target in self.targets: @@ -44,12 +40,14 @@ class QStrikeTargetSelectionComboBox(QFilteredComboBox): return i + 1 for cp in self.game.theater.controlpoints: - if cp.captured: continue + if cp.captured: + continue added_obj_names = [] for g in cp.ground_objects: - if g.obj_name in added_obj_names: continue + if g.obj_name in added_obj_names: + continue target = StrikeTargetInfo() target.location = g @@ -70,5 +68,3 @@ class QStrikeTargetSelectionComboBox(QFilteredComboBox): added_obj_names.append(g.obj_name) self.setModel(model) - - diff --git a/qt_ui/widgets/floatspinners.py b/qt_ui/widgets/floatspinners.py index 058b3516..54f4dccd 100644 --- a/qt_ui/widgets/floatspinners.py +++ b/qt_ui/widgets/floatspinners.py @@ -4,9 +4,12 @@ from PySide2.QtWidgets import QSpinBox class TenthsSpinner(QSpinBox): - def __init__(self, minimum: Optional[int] = None, - maximum: Optional[int] = None, - initial: Optional[int] = None) -> None: + def __init__( + self, + minimum: Optional[int] = None, + maximum: Optional[int] = None, + initial: Optional[int] = None, + ) -> None: super().__init__() if minimum is not None: diff --git a/qt_ui/widgets/map/QFrontLine.py b/qt_ui/widgets/map/QFrontLine.py index 9f99ab13..41edcaea 100644 --- a/qt_ui/widgets/map/QFrontLine.py +++ b/qt_ui/widgets/map/QFrontLine.py @@ -27,8 +27,15 @@ class QFrontLine(QGraphicsLineItem): change the mouse cursor on hover. """ - def __init__(self, x1: float, y1: float, x2: float, y2: float, - mission_target: FrontLine, game_model: GameModel) -> None: + def __init__( + self, + x1: float, + y1: float, + x2: float, + y2: float, + mission_target: FrontLine, + game_model: GameModel, + ) -> None: super().__init__(x1, y1, x2, y2) self.mission_target = mission_target self.game_model = game_model @@ -98,10 +105,9 @@ class QFrontLine(QGraphicsLineItem): self.mission_target.control_point_b.base.affect_strength(-0.1) self.game_model.game.initialize_turn() GameUpdateSignal.get_instance().updateGame(self.game_model.game) - + def cheat_backward(self) -> None: self.mission_target.control_point_a.base.affect_strength(-0.1) self.mission_target.control_point_b.base.affect_strength(0.1) self.game_model.game.initialize_turn() GameUpdateSignal.get_instance().updateGame(self.game_model.game) - \ No newline at end of file diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index 1f171843..2be5e520 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -70,10 +70,10 @@ from qt_ui.windows.GameUpdateSignal import GameUpdateSignal MAX_SHIP_DISTANCE = nautical_miles(80) + def binomial(i: int, n: int) -> float: """Binomial coefficient""" - return math.factorial(n) / float( - math.factorial(i) * math.factorial(n - i)) + return math.factorial(n) / float(math.factorial(i) * math.factorial(n - i)) def bernstein(t: float, i: int, n: int) -> float: @@ -92,7 +92,9 @@ def bezier(t: float, points: Iterable[Tuple[float, float]]) -> Tuple[float, floa return x, y -def bezier_curve_range(n: int, points: Iterable[Tuple[float, float]]) -> Iterator[Tuple[float, float]]: +def bezier_curve_range( + n: int, points: Iterable[Tuple[float, float]] +) -> Iterator[Tuple[float, float]]: """Range of points in a curve bezier""" for i in range(n): t = i / float(n - 1) @@ -133,7 +135,9 @@ class QLiberationMap(QGraphicsView): self.setGame(game_model.game) # Object displayed when unit is selected - self.movement_line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(QPointF(0,0),QPointF(0,0))) + self.movement_line = QtWidgets.QGraphicsLineItem( + QtCore.QLineF(QPointF(0, 0), QPointF(0, 0)) + ) self.movement_line.setPen(QPen(CONST.COLORS["orange"], width=10.0)) self.selected_cp: QMapControlPoint = None @@ -162,8 +166,7 @@ class QLiberationMap(QGraphicsView): # update_flight_selection will be called in when the last # package is removed. If no flight is selected, it's not a # problem to also have no package selected. - logging.error( - "Flight was selected with no package selected") + logging.error("Flight was selected with no package selected") return # Optional[int] isn't a valid type for a Qt signal. None will be @@ -183,7 +186,6 @@ class QLiberationMap(QGraphicsView): self.navmesh_highlight: Optional[QPolygonF] = None self.shortest_path_segments: List[QLineF] = [] - def init_scene(self): scene = QLiberationScene(self) self.setScene(scene) @@ -203,6 +205,7 @@ class QLiberationMap(QGraphicsView): """ Uncomment to set up theather reference points""" + def keyPressEvent(self, event): modifiers = QtWidgets.QApplication.keyboardModifiers() if not self.reference_point_setup_mode: @@ -225,40 +228,39 @@ class QLiberationMap(QGraphicsView): if event.key() == QtCore.Qt.Key_Down: self.update_reference_point( - self.game.theater.reference_points[0], - Point(0, distance)) + self.game.theater.reference_points[0], Point(0, distance) + ) if event.key() == QtCore.Qt.Key_Up: self.update_reference_point( - self.game.theater.reference_points[0], - Point(0, -distance)) + self.game.theater.reference_points[0], Point(0, -distance) + ) if event.key() == QtCore.Qt.Key_Left: self.update_reference_point( - self.game.theater.reference_points[0], - Point(-distance, 0)) + self.game.theater.reference_points[0], Point(-distance, 0) + ) if event.key() == QtCore.Qt.Key_Right: self.update_reference_point( - self.game.theater.reference_points[0], - Point(distance, 0)) + self.game.theater.reference_points[0], Point(distance, 0) + ) if event.key() == QtCore.Qt.Key_S: self.update_reference_point( - self.game.theater.reference_points[1], - Point(0, distance)) + self.game.theater.reference_points[1], Point(0, distance) + ) if event.key() == QtCore.Qt.Key_W: self.update_reference_point( - self.game.theater.reference_points[1], - Point(0, -distance)) + self.game.theater.reference_points[1], Point(0, -distance) + ) if event.key() == QtCore.Qt.Key_A: self.update_reference_point( - self.game.theater.reference_points[1], - Point(-distance, 0)) + self.game.theater.reference_points[1], Point(-distance, 0) + ) if event.key() == QtCore.Qt.Key_D: self.update_reference_point( - self.game.theater.reference_points[1], - Point(distance, 0)) + self.game.theater.reference_points[1], Point(distance, 0) + ) - logging.debug( - f"Reference points: {self.game.theater.reference_points}") + logging.debug(f"Reference points: {self.game.theater.reference_points}") self.reload_scene() @staticmethod @@ -275,16 +277,33 @@ class QLiberationMap(QGraphicsView): distance_point = self._transform_point(culling_distance_point) transformed = self._transform_point(point) radius = distance_point[0] - transformed[0] - scene.addEllipse(transformed[0]-radius, transformed[1]-radius, 2*radius, 2*radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"]) + scene.addEllipse( + transformed[0] - radius, + transformed[1] - radius, + 2 * radius, + 2 * radius, + CONST.COLORS["transparent"], + CONST.COLORS["light_green_transparent"], + ) for zone in culling_zones: - culling_distance_zone = Point(zone.x + culling_distance*1000, zone.y + culling_distance*1000) + culling_distance_zone = Point( + zone.x + culling_distance * 1000, zone.y + culling_distance * 1000 + ) distance_zone = self._transform_point(culling_distance_zone) transformed = self._transform_point(zone) radius = distance_zone[0] - transformed[0] - scene.addEllipse(transformed[0]-radius, transformed[1]-radius, 2*radius, 2*radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"]) + scene.addEllipse( + transformed[0] - radius, + transformed[1] - radius, + 2 * radius, + 2 * radius, + CONST.COLORS["transparent"], + CONST.COLORS["light_green_transparent"], + ) - def draw_shapely_poly(self, scene: QGraphicsScene, poly: Polygon, pen: QPen, - brush: QBrush) -> Optional[QPolygonF]: + def draw_shapely_poly( + self, scene: QGraphicsScene, poly: Polygon, pen: QPen, brush: QBrush + ) -> Optional[QPolygonF]: if poly.is_empty: return None points = [] @@ -293,16 +312,18 @@ class QLiberationMap(QGraphicsView): points.append(QPointF(x, y)) return scene.addPolygon(QPolygonF(points), pen, brush) - def draw_threat_zone(self, scene: QGraphicsScene, poly: Polygon, - player: bool) -> None: + def draw_threat_zone( + self, scene: QGraphicsScene, poly: Polygon, player: bool + ) -> None: if player: brush = QColor(0, 132, 255, 100) else: brush = QColor(227, 32, 0, 100) self.draw_shapely_poly(scene, poly, CONST.COLORS["transparent"], brush) - def display_threat_zones(self, scene: QGraphicsScene, - options: ThreatZoneOptions, player: bool) -> None: + def display_threat_zones( + self, scene: QGraphicsScene, options: ThreatZoneOptions, player: bool + ) -> None: """Draws the threat zones on the map.""" threat_zones = self.game.threat_zone_for(player) if options.all: @@ -321,33 +342,44 @@ class QLiberationMap(QGraphicsView): for poly in polys: self.draw_threat_zone(scene, poly, player) - def draw_navmesh_neighbor_line(self, scene: QGraphicsScene, poly: Polygon, - begin: ShapelyPoint) -> None: + def draw_navmesh_neighbor_line( + self, scene: QGraphicsScene, poly: Polygon, begin: ShapelyPoint + ) -> None: vertex = Point(begin.x, begin.y) centroid = poly.centroid direction = Point(centroid.x, centroid.y) - end = vertex.point_from_heading(vertex.heading_between_point(direction), - nautical_miles(2).meters) + end = vertex.point_from_heading( + vertex.heading_between_point(direction), nautical_miles(2).meters + ) - scene.addLine(QLineF(QPointF(*self._transform_point(vertex)), - QPointF(*self._transform_point(end))), - CONST.COLORS["yellow"]) + scene.addLine( + QLineF( + QPointF(*self._transform_point(vertex)), + QPointF(*self._transform_point(end)), + ), + CONST.COLORS["yellow"], + ) @singledispatchmethod - def draw_navmesh_border(self, intersection, scene: QGraphicsScene, - poly: Polygon) -> None: - raise NotImplementedError("draw_navmesh_border not implemented for %s", - intersection.__class__.__name__) + def draw_navmesh_border( + self, intersection, scene: QGraphicsScene, poly: Polygon + ) -> None: + raise NotImplementedError( + "draw_navmesh_border not implemented for %s", + intersection.__class__.__name__, + ) @draw_navmesh_border.register - def draw_navmesh_point_border(self, intersection: ShapelyPoint, - scene: QGraphicsScene, poly: Polygon) -> None: + def draw_navmesh_point_border( + self, intersection: ShapelyPoint, scene: QGraphicsScene, poly: Polygon + ) -> None: # Draw a line from the vertex toward the center of the polygon. self.draw_navmesh_neighbor_line(scene, poly, intersection) @draw_navmesh_border.register - def draw_navmesh_edge_border(self, intersection: LineString, - scene: QGraphicsScene, poly: Polygon) -> None: + def draw_navmesh_edge_border( + self, intersection: LineString, scene: QGraphicsScene, poly: Polygon + ) -> None: # Draw a line from the center of the edge toward the center of the # polygon. edge_center = intersection.interpolate(0.5, normalized=True) @@ -355,13 +387,16 @@ class QLiberationMap(QGraphicsView): def display_navmesh(self, scene: QGraphicsScene, player: bool) -> None: for navpoly in self.game.navmesh_for(player).polys: - self.draw_shapely_poly(scene, navpoly.poly, CONST.COLORS["black"], - CONST.COLORS["transparent"]) + self.draw_shapely_poly( + scene, navpoly.poly, CONST.COLORS["black"], CONST.COLORS["transparent"] + ) position = self._transform_point( - Point(navpoly.poly.centroid.x, navpoly.poly.centroid.y)) - text = scene.addSimpleText(f"Navmesh {navpoly.ident}", - self.waypoint_info_font) + Point(navpoly.poly.centroid.x, navpoly.poly.centroid.y) + ) + text = scene.addSimpleText( + f"Navmesh {navpoly.ident}", self.waypoint_info_font + ) text.setBrush(QColor(255, 255, 255)) text.setPen(QColor(255, 255, 255)) text.moveBy(position[0] + 8, position[1]) @@ -370,8 +405,9 @@ class QLiberationMap(QGraphicsView): for border in navpoly.neighbors.values(): self.draw_navmesh_border(border, scene, navpoly.poly) - def highlight_mouse_navmesh(self, scene: QGraphicsScene, navmesh: NavMesh, - mouse_position: Point) -> None: + def highlight_mouse_navmesh( + self, scene: QGraphicsScene, navmesh: NavMesh, mouse_position: Point + ) -> None: if self.navmesh_highlight is not None: try: scene.removeItem(self.navmesh_highlight) @@ -381,11 +417,15 @@ class QLiberationMap(QGraphicsView): if navpoly is None: return self.navmesh_highlight = self.draw_shapely_poly( - scene, navpoly.poly, CONST.COLORS["transparent"], - CONST.COLORS["light_green_transparent"]) + scene, + navpoly.poly, + CONST.COLORS["transparent"], + CONST.COLORS["light_green_transparent"], + ) - def draw_shortest_path(self, scene: QGraphicsScene, navmesh: NavMesh, - destination: Point, player: bool) -> None: + def draw_shortest_path( + self, scene: QGraphicsScene, navmesh: NavMesh, destination: Point, player: bool + ) -> None: for line in self.shortest_path_segments: try: scene.removeItem(line) @@ -407,21 +447,36 @@ class QLiberationMap(QGraphicsView): flight_path_pen = self.flight_path_pen(player, selected=True) # Draw the line to the *middle* of the waypoint. offset = self.WAYPOINT_SIZE // 2 - self.shortest_path_segments.append(scene.addLine( - prev_pos[0] + offset, prev_pos[1] + offset, - new_pos[0] + offset, new_pos[1] + offset, - flight_path_pen - )) + self.shortest_path_segments.append( + scene.addLine( + prev_pos[0] + offset, + prev_pos[1] + offset, + new_pos[0] + offset, + new_pos[1] + offset, + flight_path_pen, + ) + ) - self.shortest_path_segments.append(scene.addEllipse( - new_pos[0], new_pos[1], self.WAYPOINT_SIZE, - self.WAYPOINT_SIZE, flight_path_pen, flight_path_pen - )) + self.shortest_path_segments.append( + scene.addEllipse( + new_pos[0], + new_pos[1], + self.WAYPOINT_SIZE, + self.WAYPOINT_SIZE, + flight_path_pen, + flight_path_pen, + ) + ) prev_pos = new_pos - def draw_test_flight_plan(self, scene: QGraphicsScene, task: FlightType, - point_near_target: Point, player: bool) -> None: + def draw_test_flight_plan( + self, + scene: QGraphicsScene, + task: FlightType, + point_near_target: Point, + player: bool, + ) -> None: for line in self.shortest_path_segments: try: scene.removeItem(line) @@ -438,8 +493,16 @@ class QLiberationMap(QGraphicsView): origin = self.game.theater.enemy_points()[0] package = Package(target) - flight = Flight(package, F_16C_50, 2, task, start_type="Warm", - departure=origin, arrival=origin, divert=None) + flight = Flight( + package, + F_16C_50, + 2, + task, + start_type="Warm", + departure=origin, + arrival=origin, + divert=None, + ) package.add_flight(flight) planner = FlightPlanBuilder(self.game, package, is_player=player) try: @@ -452,31 +515,49 @@ class QLiberationMap(QGraphicsView): @staticmethod def should_display_ground_objects_at(cp: ControlPoint) -> bool: - return ((DisplayOptions.sam_ranges and cp.captured) or - (DisplayOptions.enemy_sam_ranges and not cp.captured)) + return (DisplayOptions.sam_ranges and cp.captured) or ( + DisplayOptions.enemy_sam_ranges and not cp.captured + ) - def draw_threat_range(self, scene: QGraphicsScene, group: Group, ground_object: TheaterGroundObject, cp: ControlPoint) -> None: + def draw_threat_range( + self, + scene: QGraphicsScene, + group: Group, + ground_object: TheaterGroundObject, + cp: ControlPoint, + ) -> None: go_pos = self._transform_point(ground_object.position) detection_range = ground_object.detection_range(group) threat_range = ground_object.threat_range(group) if threat_range: threat_pos = self._transform_point( - ground_object.position + Point(threat_range.meters, - threat_range.meters)) + ground_object.position + Point(threat_range.meters, threat_range.meters) + ) threat_radius = Point(*go_pos).distance_to_point(Point(*threat_pos)) # Add threat range circle - scene.addEllipse(go_pos[0] - threat_radius / 2 + 7, go_pos[1] - threat_radius / 2 + 6, - threat_radius, threat_radius, self.threat_pen(cp.captured)) + scene.addEllipse( + go_pos[0] - threat_radius / 2 + 7, + go_pos[1] - threat_radius / 2 + 6, + threat_radius, + threat_radius, + self.threat_pen(cp.captured), + ) if detection_range and DisplayOptions.detection_range: # Add detection range circle detection_pos = self._transform_point( - ground_object.position + Point(detection_range.meters, - detection_range.meters)) + ground_object.position + + Point(detection_range.meters, detection_range.meters) + ) detection_radius = Point(*go_pos).distance_to_point(Point(*detection_pos)) - scene.addEllipse(go_pos[0] - detection_radius/2 + 7, go_pos[1] - detection_radius/2 + 6, - detection_radius, detection_radius, self.detection_pen(cp.captured)) + scene.addEllipse( + go_pos[0] - detection_radius / 2 + 7, + go_pos[1] - detection_radius / 2 + 6, + detection_radius, + detection_radius, + self.detection_pen(cp.captured), + ) def draw_ground_objects(self, scene: QGraphicsScene, cp: ControlPoint) -> None: added_objects = [] @@ -486,8 +567,22 @@ class QLiberationMap(QGraphicsView): go_pos = self._transform_point(ground_object.position) if not ground_object.airbase_group: - buildings = self.game.theater.find_ground_objects_by_obj_name(ground_object.obj_name) - scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 14, 12, cp, ground_object, self.game, buildings)) + buildings = self.game.theater.find_ground_objects_by_obj_name( + ground_object.obj_name + ) + scene.addItem( + QMapGroundObject( + self, + go_pos[0], + go_pos[1], + 14, + 12, + cp, + ground_object, + self.game, + buildings, + ) + ) should_display = self.should_display_ground_objects_at(cp) if ground_object.might_have_aa and should_display: @@ -508,10 +603,8 @@ class QLiberationMap(QGraphicsView): if DisplayOptions.culling and self.game.settings.perf_culling: self.display_culling(scene) - self.display_threat_zones(scene, DisplayOptions.blue_threat_zones, - player=True) - self.display_threat_zones(scene, DisplayOptions.red_threat_zones, - player=False) + self.display_threat_zones(scene, DisplayOptions.blue_threat_zones, player=True) + self.display_threat_zones(scene, DisplayOptions.red_threat_zones, player=False) if DisplayOptions.navmeshes.blue_navmesh: self.display_navmesh(scene, player=True) @@ -522,24 +615,33 @@ class QLiberationMap(QGraphicsView): pos = self._transform_point(cp.position) - scene.addItem(QMapControlPoint(self, pos[0] - CONST.CP_SIZE / 2, - pos[1] - CONST.CP_SIZE / 2, - CONST.CP_SIZE, - CONST.CP_SIZE, cp, self.game_model)) + scene.addItem( + QMapControlPoint( + self, + pos[0] - CONST.CP_SIZE / 2, + pos[1] - CONST.CP_SIZE / 2, + CONST.CP_SIZE, + CONST.CP_SIZE, + cp, + self.game_model, + ) + ) if cp.captured: pen = QPen(brush=CONST.COLORS[playerColor]) - brush = CONST.COLORS[playerColor+"_transparent"] + brush = CONST.COLORS[playerColor + "_transparent"] else: pen = QPen(brush=CONST.COLORS[enemyColor]) - brush = CONST.COLORS[enemyColor+"_transparent"] + brush = CONST.COLORS[enemyColor + "_transparent"] self.draw_ground_objects(scene, cp) if cp.target_position is not None: proj = self._transform_point(cp.target_position) - scene.addLine(QLineF(QPointF(pos[0], pos[1]), QPointF(proj[0], proj[1])), - QPen(CONST.COLORS["green"], width=10, s=Qt.DashDotLine)) + scene.addLine( + QLineF(QPointF(pos[0], pos[1]), QPointF(proj[0], proj[1])), + QPen(CONST.COLORS["green"], width=10, s=Qt.DashDotLine), + ) for cp in self.game.theater.enemy_points(): if DisplayOptions.lines: @@ -585,8 +687,9 @@ class QLiberationMap(QGraphicsView): continue self.draw_flight_plan(scene, flight, selected) - def draw_flight_plan(self, scene: QGraphicsScene, flight: Flight, - selected: bool) -> None: + def draw_flight_plan( + self, scene: QGraphicsScene, flight: Flight, selected: bool + ) -> None: is_player = flight.from_cp.captured pos = self._transform_point(flight.from_cp.position) @@ -604,8 +707,7 @@ class QLiberationMap(QGraphicsView): continue new_pos = self._transform_point(Point(point.x, point.y)) - self.draw_flight_path(scene, prev_pos, new_pos, is_player, - selected) + self.draw_flight_path(scene, prev_pos, new_pos, is_player, selected) self.draw_waypoint(scene, new_pos, is_player, selected) if selected and DisplayOptions.waypoint_info: if point.waypoint_type in target_types: @@ -613,44 +715,63 @@ class QLiberationMap(QGraphicsView): # Don't draw dozens of targets over each other. continue drew_target = True - self.draw_waypoint_info(scene, idx + 1, point, new_pos, - flight.flight_plan) + self.draw_waypoint_info( + scene, idx + 1, point, new_pos, flight.flight_plan + ) prev_pos = tuple(new_pos) if selected and DisplayOptions.barcap_commit_range: self.draw_barcap_commit_range(scene, flight) - def draw_barcap_commit_range(self, scene: QGraphicsScene, - flight: Flight) -> None: + def draw_barcap_commit_range(self, scene: QGraphicsScene, flight: Flight) -> None: if flight.flight_type is not FlightType.BARCAP: return if not isinstance(flight.flight_plan, BarCapFlightPlan): return start = flight.flight_plan.patrol_start end = flight.flight_plan.patrol_end - line = LineString([ - ShapelyPoint(start.x, start.y), - ShapelyPoint(end.x, end.y), - ]) + line = LineString( + [ + ShapelyPoint(start.x, start.y), + ShapelyPoint(end.x, end.y), + ] + ) doctrine = self.game.faction_for(flight.departure.captured).doctrine bubble = line.buffer(doctrine.cap_engagement_range.meters) - self.flight_path_items.append(self.draw_shapely_poly( - scene, bubble, CONST.COLORS["yellow"], CONST.COLORS["transparent"] - )) + self.flight_path_items.append( + self.draw_shapely_poly( + scene, bubble, CONST.COLORS["yellow"], CONST.COLORS["transparent"] + ) + ) - def draw_waypoint(self, scene: QGraphicsScene, - position: Tuple[float, float], player: bool, - selected: bool) -> None: + def draw_waypoint( + self, + scene: QGraphicsScene, + position: Tuple[float, float], + player: bool, + selected: bool, + ) -> None: waypoint_pen = self.waypoint_pen(player, selected) waypoint_brush = self.waypoint_brush(player, selected) - self.flight_path_items.append(scene.addEllipse( - position[0], position[1], self.WAYPOINT_SIZE, - self.WAYPOINT_SIZE, waypoint_pen, waypoint_brush - )) + self.flight_path_items.append( + scene.addEllipse( + position[0], + position[1], + self.WAYPOINT_SIZE, + self.WAYPOINT_SIZE, + waypoint_pen, + waypoint_brush, + ) + ) - def draw_waypoint_info(self, scene: QGraphicsScene, number: int, - waypoint: FlightWaypoint, position: Tuple[int, int], - flight_plan: FlightPlan) -> None: + def draw_waypoint_info( + self, + scene: QGraphicsScene, + number: int, + waypoint: FlightWaypoint, + position: Tuple[int, int], + flight_plan: FlightPlan, + ) -> None: altitude = int(waypoint.alt.feet) altitude_type = "AGL" if waypoint.alt_type == "RADIO" else "MSL" @@ -669,11 +790,13 @@ class QLiberationMap(QGraphicsView): pen = QPen(QColor("black"), 0.3) brush = QColor("white") - text = "\n".join([ - f"{number} {waypoint.name}", - f"{altitude} ft {altitude_type}", - tot, - ]) + text = "\n".join( + [ + f"{number} {waypoint.name}", + f"{altitude} ft {altitude_type}", + tot, + ] + ) item = scene.addSimpleText(text, self.waypoint_info_font) item.setFlag(QGraphicsItem.ItemIgnoresTransformations) @@ -683,19 +806,30 @@ class QLiberationMap(QGraphicsView): item.setZValue(2) self.flight_path_items.append(item) - def draw_flight_path(self, scene: QGraphicsScene, pos0: Tuple[float, float], - pos1: Tuple[float, float], player: bool, - selected: bool) -> None: + def draw_flight_path( + self, + scene: QGraphicsScene, + pos0: Tuple[float, float], + pos1: Tuple[float, float], + player: bool, + selected: bool, + ) -> None: flight_path_pen = self.flight_path_pen(player, selected) # Draw the line to the *middle* of the waypoint. offset = self.WAYPOINT_SIZE // 2 - self.flight_path_items.append(scene.addLine( - pos0[0] + offset, pos0[1] + offset, - pos1[0] + offset, pos1[1] + offset, - flight_path_pen - )) + self.flight_path_items.append( + scene.addLine( + pos0[0] + offset, + pos0[1] + offset, + pos1[0] + offset, + pos1[1] + offset, + flight_path_pen, + ) + ) - def draw_bezier_frontline(self, scene: QGraphicsScene, pen:QPen, frontline: FrontLine) -> None: + def draw_bezier_frontline( + self, scene: QGraphicsScene, pen: QPen, frontline: FrontLine + ) -> None: """ Thanks to Alquimista for sharing a python implementation of the bezier algorithm this is adapted from. https://gist.github.com/Alquimista/1274149#file-bezdraw-py @@ -706,7 +840,9 @@ class QLiberationMap(QGraphicsView): bezier_fixed_points.append(self._transform_point(segment.point_b)) old_point = bezier_fixed_points[0] - for point in bezier_curve_range(int(len(bezier_fixed_points) * 2), bezier_fixed_points): + for point in bezier_curve_range( + int(len(bezier_fixed_points) * 2), bezier_fixed_points + ): scene.addLine(old_point[0], old_point[1], point[0], point[1], pen=pen) old_point = point @@ -715,14 +851,18 @@ class QLiberationMap(QGraphicsView): for connected_cp in cp.connected_points: pos2 = self._transform_point(connected_cp.position) if not cp.captured: - color = CONST.COLORS["dark_"+enemyColor] + color = CONST.COLORS["dark_" + enemyColor] else: - color = CONST.COLORS["dark_"+playerColor] + color = CONST.COLORS["dark_" + playerColor] pen = QPen(brush=color) pen.setColor(color) pen.setWidth(6) frontline = FrontLine(cp, connected_cp, self.game.theater) - if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp): + if ( + cp.captured + and not connected_cp.captured + and Conflict.has_frontline_between(cp, connected_cp) + ): if DisplayOptions.actual_frontline_pos: self.draw_actual_frontline(frontline, scene, pen) else: @@ -730,35 +870,30 @@ class QLiberationMap(QGraphicsView): else: self.draw_bezier_frontline(scene, pen, frontline) - def draw_frontline_approximation(self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen) -> None: + def draw_frontline_approximation( + self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen + ) -> None: posx = frontline.position h = frontline.attack_heading pos2 = self._transform_point(posx) self.draw_bezier_frontline(scene, pen, frontline) - p1 = point_from_heading(pos2[0], pos2[1], h+180, 25) + p1 = point_from_heading(pos2[0], pos2[1], h + 180, 25) p2 = point_from_heading(pos2[0], pos2[1], h, 25) scene.addItem( - QFrontLine( - p1[0], - p1[1], - p2[0], - p2[1], - frontline, - self.game_model - ) + QFrontLine(p1[0], p1[1], p2[0], p2[1], frontline, self.game_model) ) - def draw_actual_frontline(self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen) -> None: + def draw_actual_frontline( + self, frontline: FrontLine, scene: QGraphicsScene, pen: QPen + ) -> None: self.draw_bezier_frontline(scene, pen, frontline) vector = Conflict.frontline_vector( - frontline.control_point_a, - frontline.control_point_b, - self.game.theater + frontline.control_point_a, frontline.control_point_b, self.game.theater ) left_pos = self._transform_point(vector[0]) right_pos = self._transform_point( - vector[0].point_from_heading(vector[1], vector[2]) - ) + vector[0].point_from_heading(vector[1], vector[2]) + ) scene.addItem( QFrontLine( left_pos[0], @@ -766,7 +901,7 @@ class QLiberationMap(QGraphicsView): right_pos[0], right_pos[1], frontline, - self.game_model + self.game_model, ) ) @@ -779,26 +914,48 @@ class QLiberationMap(QGraphicsView): SMALL_LINE = 2 dist = self.distance_to_pixels(nautical_miles(scale_distance_nm)) - self.scene().addRect(POS_X, POS_Y-PADDING, PADDING*2 + dist, BIG_LINE*2+3*PADDING, pen=CONST.COLORS["black"], brush=CONST.COLORS["black"]) - l = self.scene().addLine(POS_X + PADDING, POS_Y + BIG_LINE*2, POS_X + PADDING + dist, POS_Y + BIG_LINE*2) + self.scene().addRect( + POS_X, + POS_Y - PADDING, + PADDING * 2 + dist, + BIG_LINE * 2 + 3 * PADDING, + pen=CONST.COLORS["black"], + brush=CONST.COLORS["black"], + ) + l = self.scene().addLine( + POS_X + PADDING, + POS_Y + BIG_LINE * 2, + POS_X + PADDING + dist, + POS_Y + BIG_LINE * 2, + ) - text = self.scene().addText("0nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False)) - text.setPos(POS_X, POS_Y + BIG_LINE*2) + text = self.scene().addText( + "0nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False) + ) + text.setPos(POS_X, POS_Y + BIG_LINE * 2) text.setDefaultTextColor(Qt.white) - text2 = self.scene().addText(str(scale_distance_nm) + "nm", font=QFont("Trebuchet MS", 6, weight=5, italic=False)) + text2 = self.scene().addText( + str(scale_distance_nm) + "nm", + font=QFont("Trebuchet MS", 6, weight=5, italic=False), + ) text2.setPos(POS_X + dist, POS_Y + BIG_LINE * 2) text2.setDefaultTextColor(Qt.white) l.setPen(CONST.COLORS["white"]) - for i in range(number_of_points+1): - d = float(i)/float(number_of_points) + for i in range(number_of_points + 1): + d = float(i) / float(number_of_points) if i == 0 or i == number_of_points: h = BIG_LINE else: h = SMALL_LINE - l = self.scene().addLine(POS_X + PADDING + d * dist, POS_Y + BIG_LINE*2, POS_X + PADDING + d * dist, POS_Y + BIG_LINE - h) + l = self.scene().addLine( + POS_X + PADDING + d * dist, + POS_Y + BIG_LINE * 2, + POS_X + PADDING + d * dist, + POS_Y + BIG_LINE - h, + ) l.setPen(CONST.COLORS["white"]) def wheelEvent(self, event: QWheelEvent): @@ -828,7 +985,8 @@ class QLiberationMap(QGraphicsView): point_b = self.game.theater.reference_points[1] world_distance = self._transpose_point( - point_b.world_coordinates - point_a.world_coordinates) + point_b.world_coordinates - point_a.world_coordinates + ) image_distance = point_b.image_coordinates - point_a.image_coordinates x_scale = image_distance.x / world_distance.x @@ -870,8 +1028,7 @@ class QLiberationMap(QGraphicsView): scale = self._scaling_factor() offset = point_a.image_coordinates - scene_point - scaled = self._transpose_point( - Point(offset.x / scale.x, offset.y / scale.y)) + scaled = self._transpose_point(Point(offset.x / scale.x, offset.y / scale.y)) return point_a.world_coordinates - scaled def distance_to_pixels(self, distance: Distance) -> int: @@ -955,7 +1112,12 @@ class QLiberationMap(QGraphicsView): for sea_zone in self.game.theater.landmap.sea_zones: print(sea_zone) - poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone.exterior.coords]) + poly = QPolygonF( + [ + QPointF(*self._transform_point(Point(point[0], point[1]))) + for point in sea_zone.exterior.coords + ] + ) if self.reference_point_setup_mode: color = "sea_blue_transparent" else: @@ -963,18 +1125,40 @@ class QLiberationMap(QGraphicsView): scene.addPolygon(poly, CONST.COLORS[color], CONST.COLORS[color]) for inclusion_zone in self.game.theater.landmap.inclusion_zones: - poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone.exterior.coords]) + poly = QPolygonF( + [ + QPointF(*self._transform_point(Point(point[0], point[1]))) + for point in inclusion_zone.exterior.coords + ] + ) if self.reference_point_setup_mode: - scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_grey_transparent"]) + scene.addPolygon( + poly, + CONST.COLORS["grey_transparent"], + CONST.COLORS["dark_grey_transparent"], + ) else: - scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"]) + scene.addPolygon( + poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"] + ) for exclusion_zone in self.game.theater.landmap.exclusion_zones: - poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone.exterior.coords]) + poly = QPolygonF( + [ + QPointF(*self._transform_point(Point(point[0], point[1]))) + for point in exclusion_zone.exterior.coords + ] + ) if self.reference_point_setup_mode: - scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_dark_grey_transparent"]) + scene.addPolygon( + poly, + CONST.COLORS["grey_transparent"], + CONST.COLORS["dark_dark_grey_transparent"], + ) else: - scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"]) + scene.addPolygon( + poly, CONST.COLORS["grey"], CONST.COLORS["dark_dark_grey"] + ) # Uncomment to display plan projection test # self.projection_test() @@ -983,15 +1167,18 @@ class QLiberationMap(QGraphicsView): if self.reference_point_setup_mode: for i, point in enumerate(self.game.theater.reference_points): self.scene().addRect( - QRectF(point.image_coordinates.x, point.image_coordinates.y, - 25, 25), pen=CONST.COLORS["red"], - brush=CONST.COLORS["red"]) + QRectF( + point.image_coordinates.x, point.image_coordinates.y, 25, 25 + ), + pen=CONST.COLORS["red"], + brush=CONST.COLORS["red"], + ) text = self.scene().addText( f"P{i} = {point.image_coordinates}", - font=QFont("Trebuchet MS", 14, weight=8, italic=False)) + font=QFont("Trebuchet MS", 14, weight=8, italic=False), + ) text.setDefaultTextColor(CONST.COLORS["red"]) - text.setPos(point.image_coordinates.x + 26, - point.image_coordinates.y) + text.setPos(point.image_coordinates.x + 26, point.image_coordinates.y) # Set to True to visually debug _transform_point. draw_transformed = False @@ -1000,10 +1187,12 @@ class QLiberationMap(QGraphicsView): self.scene().addRect( QRectF(x, y, 25, 25), pen=CONST.COLORS["red"], - brush=CONST.COLORS["red"]) + brush=CONST.COLORS["red"], + ) text = self.scene().addText( f"P{i}' = {x}, {y}", - font=QFont("Trebuchet MS", 14, weight=8, italic=False)) + font=QFont("Trebuchet MS", 14, weight=8, italic=False), + ) text.setDefaultTextColor(CONST.COLORS["red"]) text.setPos(x + 26, y) @@ -1023,7 +1212,9 @@ class QLiberationMap(QGraphicsView): self.state = QLiberationMapState.MOVING_UNIT self.selected_cp = selected_cp position = self._transform_point(selected_cp.control_point.position) - self.movement_line = QtWidgets.QGraphicsLineItem(QLineF(QPointF(*position), QPointF(*position))) + self.movement_line = QtWidgets.QGraphicsLineItem( + QLineF(QPointF(*position), QPointF(*position)) + ) self.scene().addItem(self.movement_line) def is_valid_ship_pos(self, scene_position: Point) -> bool: @@ -1043,7 +1234,8 @@ class QLiberationMap(QGraphicsView): if self.state == QLiberationMapState.MOVING_UNIT: self.setCursor(Qt.PointingHandCursor) self.movement_line.setLine( - QLineF(self.movement_line.line().p1(), event.scenePos())) + QLineF(self.movement_line.line().p1(), event.scenePos()) + ) if self.is_valid_ship_pos(mouse_position): self.movement_line.setPen(CONST.COLORS["green"]) @@ -1053,21 +1245,28 @@ class QLiberationMap(QGraphicsView): mouse_world_pos = self._scene_to_dcs_coords(mouse_position) if DisplayOptions.navmeshes.blue_navmesh: self.highlight_mouse_navmesh( - self.scene(), self.game.blue_navmesh, - self._scene_to_dcs_coords(mouse_position)) + self.scene(), + self.game.blue_navmesh, + self._scene_to_dcs_coords(mouse_position), + ) if DisplayOptions.path_debug.shortest_path: - self.draw_shortest_path(self.scene(), self.game.blue_navmesh, - mouse_world_pos, player=True) + self.draw_shortest_path( + self.scene(), self.game.blue_navmesh, mouse_world_pos, player=True + ) if DisplayOptions.navmeshes.red_navmesh: self.highlight_mouse_navmesh( - self.scene(), self.game.red_navmesh, mouse_world_pos) + self.scene(), self.game.red_navmesh, mouse_world_pos + ) debug_blue = DisplayOptions.path_debug_faction.blue if DisplayOptions.path_debug.shortest_path: self.draw_shortest_path( - self.scene(), self.game.navmesh_for(player=debug_blue), - mouse_world_pos, player=False) + self.scene(), + self.game.navmesh_for(player=debug_blue), + mouse_world_pos, + player=False, + ) elif not DisplayOptions.path_debug.hide: if DisplayOptions.path_debug.barcap: task = FlightType.BARCAP @@ -1080,10 +1279,10 @@ class QLiberationMap(QGraphicsView): elif DisplayOptions.path_debug.tarcap: task = FlightType.TARCAP else: - raise ValueError( - "Unexpected value for DisplayOptions.path_debug") - self.draw_test_flight_plan(self.scene(), task, mouse_world_pos, - player=debug_blue) + raise ValueError("Unexpected value for DisplayOptions.path_debug") + self.draw_test_flight_plan( + self.scene(), task, mouse_world_pos, player=debug_blue + ) def sceneMousePressEvent(self, event: QGraphicsSceneMouseEvent): if self.state == QLiberationMapState.MOVING_UNIT: @@ -1109,4 +1308,4 @@ class QLiberationMap(QGraphicsView): self.scene().removeItem(self.movement_line) except: pass - self.selected_cp = None \ No newline at end of file + self.selected_cp = None diff --git a/qt_ui/widgets/map/QLiberationScene.py b/qt_ui/widgets/map/QLiberationScene.py index 5ad08d5e..fff8c379 100644 --- a/qt_ui/widgets/map/QLiberationScene.py +++ b/qt_ui/widgets/map/QLiberationScene.py @@ -4,17 +4,18 @@ import qt_ui.uiconstants as CONST class QLiberationScene(QGraphicsScene): - def __init__(self, parent): super().__init__(parent) - item = self.addText("Go to \"File/New Game\" to setup a new campaign or go to \"File/Open\" to load an existing save game.", - CONST.FONT_PRIMARY) + item = self.addText( + 'Go to "File/New Game" to setup a new campaign or go to "File/Open" to load an existing save game.', + CONST.FONT_PRIMARY, + ) item.setDefaultTextColor(CONST.COLORS["white"]) def mouseMoveEvent(self, event: QGraphicsSceneMouseEvent): super(QLiberationScene, self).mouseMoveEvent(event) self.parent().sceneMouseMovedEvent(event) - def mousePressEvent(self, event:QGraphicsSceneMouseEvent): + def mousePressEvent(self, event: QGraphicsSceneMouseEvent): super(QLiberationScene, self).mousePressEvent(event) self.parent().sceneMousePressEvent(event) diff --git a/qt_ui/widgets/map/QMapControlPoint.py b/qt_ui/widgets/map/QMapControlPoint.py index 035db95e..12bfce6a 100644 --- a/qt_ui/widgets/map/QMapControlPoint.py +++ b/qt_ui/widgets/map/QMapControlPoint.py @@ -13,8 +13,16 @@ from ...windows.GameUpdateSignal import GameUpdateSignal class QMapControlPoint(QMapObject): - def __init__(self, parent, x: float, y: float, w: float, h: float, - control_point: ControlPoint, game_model: GameModel) -> None: + def __init__( + self, + parent, + x: float, + y: float, + w: float, + h: float, + control_point: ControlPoint, + game_model: GameModel, + ) -> None: super().__init__(x, y, w, h, mission_target=control_point) self.game_model = game_model self.control_point = control_point @@ -22,8 +30,7 @@ class QMapControlPoint(QMapObject): self.setZValue(1) self.setToolTip(self.control_point.name) self.base_details_dialog: Optional[QBaseMenu2] = None - self.capture_action = QAction( - f"CHEAT: Capture {self.control_point.name}") + self.capture_action = QAction(f"CHEAT: Capture {self.control_point.name}") self.capture_action.triggered.connect(self.cheat_capture) self.move_action = QAction("Move") @@ -69,9 +76,7 @@ class QMapControlPoint(QMapObject): def on_click(self) -> None: self.base_details_dialog = QBaseMenu2( - self.window(), - self.control_point, - self.game_model + self.window(), self.control_point, self.game_model ) self.base_details_dialog.show() @@ -89,7 +94,10 @@ class QMapControlPoint(QMapObject): return for connected in self.control_point.connected_points: - if connected.captured and self.game_model.game.settings.enable_base_capture_cheat: + if ( + connected.captured + and self.game_model.game.settings.enable_base_capture_cheat + ): menu.addAction(self.capture_action) break @@ -105,7 +113,7 @@ class QMapControlPoint(QMapObject): def cancel_move(self): self.control_point.target_position = None GameUpdateSignal.get_instance().updateGame(self.game_model.game) - + def open_new_package_dialog(self) -> None: """Extends the default packagedialog to redirect to base menu for red air base.""" is_navy = isinstance(self.control_point, NavalControlPoint) diff --git a/qt_ui/widgets/map/QMapGroundObject.py b/qt_ui/widgets/map/QMapGroundObject.py index 22f41bdc..10c52d09 100644 --- a/qt_ui/widgets/map/QMapGroundObject.py +++ b/qt_ui/widgets/map/QMapGroundObject.py @@ -9,17 +9,28 @@ from game import Game from game.data.building_data import FORTIFICATION_BUILDINGS from game.db import REWARDS from game.theater import ControlPoint, TheaterGroundObject -from game.theater.theatergroundobject import MissileSiteGroundObject, CoastalSiteGroundObject +from game.theater.theatergroundobject import ( + MissileSiteGroundObject, + CoastalSiteGroundObject, +) from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu from .QMapObject import QMapObject from ...displayoptions import DisplayOptions class QMapGroundObject(QMapObject): - def __init__(self, parent, x: float, y: float, w: float, h: float, - control_point: ControlPoint, - ground_object: TheaterGroundObject, game: Game, - buildings: Optional[List[TheaterGroundObject]] = None) -> None: + def __init__( + self, + parent, + x: float, + y: float, + w: float, + h: float, + control_point: ControlPoint, + ground_object: TheaterGroundObject, + game: Game, + buildings: Optional[List[TheaterGroundObject]] = None, + ) -> None: super().__init__(x, y, w, h, mission_target=ground_object) self.ground_object = ground_object self.control_point = control_point @@ -42,7 +53,7 @@ class QMapGroundObject(QMapObject): for g in self.ground_object.groups: for u in g.units: if u.type in units: - units[u.type] = units[u.type]+1 + units[u.type] = units[u.type] + 1 else: units[u.type] = 1 @@ -80,8 +91,12 @@ class QMapGroundObject(QMapObject): if isinstance(self.ground_object, CoastalSiteGroundObject): cat = "coastal" - rect = QRect(option.rect.x() + 2, option.rect.y(), - option.rect.width() - 2, option.rect.height()) + rect = QRect( + option.rect.x() + 2, + option.rect.y(), + option.rect.width() - 2, + option.rect.height(), + ) is_dead = self.ground_object.is_dead for building in self.buildings: @@ -98,7 +113,7 @@ class QMapGroundObject(QMapObject): if not is_dead and not self.control_point.captured: if cat == "aa" and not has_threat: painter.drawPixmap(rect, const.ICONS["nothreat" + enemy_icons]) - else: + else: painter.drawPixmap(rect, const.ICONS[cat + enemy_icons]) elif not is_dead: if cat == "aa" and not has_threat: @@ -130,13 +145,22 @@ class QMapGroundObject(QMapObject): units_dead += len(g.units_losts) if units_dead + units_alive > 0: - ratio = float(units_alive)/(float(units_dead) + float(units_alive)) + ratio = float(units_alive) / (float(units_dead) + float(units_alive)) bar_height = ratio * option.rect.height() - painter.fillRect(option.rect.x(), option.rect.y(), 2, - option.rect.height(), - QBrush(const.COLORS["dark_red"])) - painter.fillRect(option.rect.x(), option.rect.y(), 2, bar_height, - QBrush(const.COLORS["green"])) + painter.fillRect( + option.rect.x(), + option.rect.y(), + 2, + option.rect.height(), + QBrush(const.COLORS["dark_red"]), + ) + painter.fillRect( + option.rect.x(), + option.rect.y(), + 2, + bar_height, + QBrush(const.COLORS["green"]), + ) def on_click(self) -> None: self.ground_object_dialog = QGroundObjectMenu( @@ -144,6 +168,6 @@ class QMapGroundObject(QMapObject): self.ground_object, self.buildings, self.control_point, - self.game + self.game, ) self.ground_object_dialog.show() diff --git a/qt_ui/widgets/map/QMapObject.py b/qt_ui/widgets/map/QMapObject.py index a12feb33..f4b0bfb6 100644 --- a/qt_ui/widgets/map/QMapObject.py +++ b/qt_ui/widgets/map/QMapObject.py @@ -23,8 +23,9 @@ class QMapObject(QGraphicsRectItem): change the mouse cursor on hover. """ - def __init__(self, x: float, y: float, w: float, h: float, - mission_target: MissionTarget) -> None: + def __init__( + self, x: float, y: float, w: float, h: float, mission_target: MissionTarget + ) -> None: super().__init__(x, y, w, h) self.mission_target = mission_target self.new_package_dialog: Optional[QNewPackageDialog] = None diff --git a/qt_ui/widgets/spinsliders.py b/qt_ui/widgets/spinsliders.py index 1b63862b..90623a6d 100644 --- a/qt_ui/widgets/spinsliders.py +++ b/qt_ui/widgets/spinsliders.py @@ -5,8 +5,7 @@ from qt_ui.widgets.floatspinners import TenthsSpinner class TenthsSpinSlider(QGridLayout): - def __init__(self, label: str, minimum: int, maximum: int, - initial: int) -> None: + def __init__(self, label: str, minimum: int, maximum: int, initial: int) -> None: super().__init__() self.addWidget(QLabel(label), 0, 0) diff --git a/qt_ui/widgets/views/QSeadTargetInfoView.py b/qt_ui/widgets/views/QSeadTargetInfoView.py index 06dcdfc3..0fc40b22 100644 --- a/qt_ui/widgets/views/QSeadTargetInfoView.py +++ b/qt_ui/widgets/views/QSeadTargetInfoView.py @@ -31,12 +31,3 @@ class QSeadTargetInfoView(QGroupBox): for r in self.sead_target_infos.radars: radar_list_model.appendRow(QStandardItem(r.type)) self.radar_list.setModel(radar_list_model) - - - - - - - - - diff --git a/qt_ui/widgets/views/QStrikeTargetInfoView.py b/qt_ui/widgets/views/QStrikeTargetInfoView.py index efa707f9..cb24f0a5 100644 --- a/qt_ui/widgets/views/QStrikeTargetInfoView.py +++ b/qt_ui/widgets/views/QStrikeTargetInfoView.py @@ -1,7 +1,14 @@ import random from PySide2.QtGui import QStandardItemModel, QStandardItem -from PySide2.QtWidgets import QGroupBox, QLabel, QWidget, QVBoxLayout, QListView, QAbstractItemView +from PySide2.QtWidgets import ( + QGroupBox, + QLabel, + QWidget, + QVBoxLayout, + QListView, + QAbstractItemView, +) from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import StrikeTargetInfo @@ -16,7 +23,9 @@ class QStrikeTargetInfoView(QGroupBox): if strike_target_infos is None: strike_target_infos = StrikeTargetInfo() - super(QStrikeTargetInfoView, self).__init__("Target : " + strike_target_infos.name) + super(QStrikeTargetInfoView, self).__init__( + "Target : " + strike_target_infos.name + ) self.strike_target_infos = strike_target_infos @@ -24,14 +33,12 @@ class QStrikeTargetInfoView(QGroupBox): self.init_ui() - def init_ui(self): layout = QVBoxLayout(self) layout.setSpacing(0) layout.addWidget(self.listView) self.setLayout(layout) - def setTarget(self, target): self.setTitle(target.name) @@ -40,36 +47,27 @@ class QStrikeTargetInfoView(QGroupBox): self.listView.setSelectionMode(QAbstractItemView.NoSelection) if len(self.strike_target_infos.units) > 0: - dic = {} - for u in self.strike_target_infos.units: - if u.type in dic.keys(): - dic[u.type] = dic[u.type] + 1 - else: - dic[u.type] = 1 - for k,v in dic.items(): - model.appendRow(QStandardItem(k + " x " + str(v))) - print(k + " x " + str(v)) + dic = {} + for u in self.strike_target_infos.units: + if u.type in dic.keys(): + dic[u.type] = dic[u.type] + 1 + else: + dic[u.type] = 1 + for k, v in dic.items(): + model.appendRow(QStandardItem(k + " x " + str(v))) + print(k + " x " + str(v)) else: - dic = {} - for b in self.strike_target_infos.buildings: - id = b.dcs_identifier - if b.is_dead: - id = id + "[Destroyed]" - if id in dic.keys(): - dic[id] = dic[id] + 1 - else: - dic[id] = 1 - for k, v in dic.items(): - model.appendRow(QStandardItem(k + " x " + str(v))) - print(k + " x " + str(v)) + dic = {} + for b in self.strike_target_infos.buildings: + id = b.dcs_identifier + if b.is_dead: + id = id + "[Destroyed]" + if id in dic.keys(): + dic[id] = dic[id] + 1 + else: + dic[id] = 1 + for k, v in dic.items(): + model.appendRow(QStandardItem(k + " x " + str(v))) + print(k + " x " + str(v)) self.listView.setModel(model) - - - - - - - - - diff --git a/qt_ui/windows/QDebriefingWindow.py b/qt_ui/windows/QDebriefingWindow.py index f54530a0..d4fa312b 100644 --- a/qt_ui/windows/QDebriefingWindow.py +++ b/qt_ui/windows/QDebriefingWindow.py @@ -15,7 +15,6 @@ from game.debriefing import Debriefing class QDebriefingWindow(QDialog): - def __init__(self, debriefing: Debriefing): super(QDebriefingWindow, self).__init__() self.debriefing = debriefing @@ -42,8 +41,7 @@ class QDebriefingWindow(QDialog): self.layout.addWidget(title) # Player lost units - lostUnits = QGroupBox( - f"{self.debriefing.player_country}'s lost units:") + lostUnits = QGroupBox(f"{self.debriefing.player_country}'s lost units:") lostUnitsLayout = QGridLayout() lostUnits.setLayout(lostUnitsLayout) @@ -52,25 +50,27 @@ class QDebriefingWindow(QDialog): for unit_type, count in player_air_losses.items(): try: lostUnitsLayout.addWidget( - QLabel(db.unit_get_expanded_info(self.debriefing.player_country, unit_type, 'name')), row, 0) + QLabel( + db.unit_get_expanded_info( + self.debriefing.player_country, unit_type, "name" + ) + ), + row, + 0, + ) lostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: - logging.exception( - f"Issue adding {unit_type} to debriefing information") + logging.exception(f"Issue adding {unit_type} to debriefing information") - front_line_losses = self.debriefing.front_line_losses_by_type( - player=True - ) + front_line_losses = self.debriefing.front_line_losses_by_type(player=True) for unit_type, count in front_line_losses.items(): try: - lostUnitsLayout.addWidget( - QLabel(db.unit_type_name(unit_type)), row, 0) + lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) lostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: - logging.exception( - f"Issue adding {unit_type} to debriefing information") + logging.exception(f"Issue adding {unit_type} to debriefing information") building_losses = self.debriefing.building_losses_by_type(player=True) for building, count in building_losses.items(): @@ -79,14 +79,12 @@ class QDebriefingWindow(QDialog): lostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: - logging.exception( - f"Issue adding {building} to debriefing information") + logging.exception(f"Issue adding {building} to debriefing information") self.layout.addWidget(lostUnits) # Enemy lost units - enemylostUnits = QGroupBox( - f"{self.debriefing.enemy_country}'s lost units:") + enemylostUnits = QGroupBox(f"{self.debriefing.enemy_country}'s lost units:") enemylostUnitsLayout = QGridLayout() enemylostUnits.setLayout(enemylostUnitsLayout) @@ -94,16 +92,20 @@ class QDebriefingWindow(QDialog): for unit_type, count in enemy_air_losses.items(): try: enemylostUnitsLayout.addWidget( - QLabel(db.unit_get_expanded_info(self.debriefing.enemy_country, unit_type, 'name')), row, 0) + QLabel( + db.unit_get_expanded_info( + self.debriefing.enemy_country, unit_type, "name" + ) + ), + row, + 0, + ) enemylostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: - logging.exception( - f"Issue adding {unit_type} to debriefing information") + logging.exception(f"Issue adding {unit_type} to debriefing information") - front_line_losses = self.debriefing.front_line_losses_by_type( - player=False - ) + front_line_losses = self.debriefing.front_line_losses_by_type(player=False) for unit_type, count in front_line_losses.items(): if count == 0: continue @@ -118,8 +120,7 @@ class QDebriefingWindow(QDialog): enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 except AttributeError: - logging.exception( - f"Issue adding {building} to debriefing information") + logging.exception(f"Issue adding {building} to debriefing information") self.layout.addWidget(enemylostUnits) diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index bd12559e..32957a04 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -7,7 +7,8 @@ from PySide2.QtCore import Qt from PySide2.QtGui import QCloseEvent, QIcon from PySide2.QtWidgets import ( QAction, - QActionGroup, QDesktopWidget, + QActionGroup, + QDesktopWidget, QFileDialog, QMainWindow, QMessageBox, @@ -31,12 +32,12 @@ from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.QDebriefingWindow import QDebriefingWindow from qt_ui.windows.infos.QInfoPanel import QInfoPanel from qt_ui.windows.newgame.QNewGameWizard import NewGameWizard -from qt_ui.windows.preferences.QLiberationPreferencesWindow import \ - QLiberationPreferencesWindow +from qt_ui.windows.preferences.QLiberationPreferencesWindow import ( + QLiberationPreferencesWindow, +) class QLiberationWindow(QMainWindow): - def __init__(self, game: Optional[Game]) -> None: super(QLiberationWindow, self).__init__() @@ -50,7 +51,7 @@ class QLiberationWindow(QMainWindow): self.setGeometry(300, 100, 270, 100) self.setWindowTitle(f"DCS Liberation - v{VERSION}") self.setWindowIcon(QIcon("./resources/icon.png")) - self.statusBar().showMessage('Ready') + self.statusBar().showMessage("Ready") self.initUi() self.initActions() @@ -106,22 +107,22 @@ class QLiberationWindow(QMainWindow): self.newGameAction = QAction("&New Game", self) self.newGameAction.setIcon(QIcon(CONST.ICONS["New"])) self.newGameAction.triggered.connect(self.newGame) - self.newGameAction.setShortcut('CTRL+N') + self.newGameAction.setShortcut("CTRL+N") self.openAction = QAction("&Open", self) self.openAction.setIcon(QIcon(CONST.ICONS["Open"])) self.openAction.triggered.connect(self.openFile) - self.openAction.setShortcut('CTRL+O') + self.openAction.setShortcut("CTRL+O") self.saveGameAction = QAction("&Save", self) self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveGameAction.triggered.connect(self.saveGame) - self.saveGameAction.setShortcut('CTRL+S') + self.saveGameAction.setShortcut("CTRL+S") self.saveAsAction = QAction("Save &As", self) self.saveAsAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveAsAction.triggered.connect(self.saveGameAs) - self.saveAsAction.setShortcut('CTRL+A') + self.saveAsAction.setShortcut("CTRL+A") self.showAboutDialogAction = QAction("&About DCS Liberation", self) self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about")) @@ -133,11 +134,17 @@ class QLiberationWindow(QMainWindow): self.openDiscordAction = QAction("&Discord Server", self) self.openDiscordAction.setIcon(CONST.ICONS["Discord"]) - self.openDiscordAction.triggered.connect(lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) + self.openDiscordAction.triggered.connect( + lambda: webbrowser.open_new_tab( + "https://" + "discord.gg" + "/" + "bKrt" + "rkJ" + ) + ) self.openGithubAction = QAction("&Github Repo", self) self.openGithubAction.setIcon(CONST.ICONS["Github"]) - self.openGithubAction.triggered.connect(lambda: webbrowser.open_new_tab("https://github.com/khopa/dcs_liberation")) + self.openGithubAction.triggered.connect( + lambda: webbrowser.open_new_tab("https://github.com/khopa/dcs_liberation") + ) def initToolbar(self): self.tool_bar = self.addToolBar("File") @@ -151,7 +158,6 @@ class QLiberationWindow(QMainWindow): self.display_bar = self.addToolBar("Display") - def initMenuBar(self): self.menu = self.menuBar() @@ -168,7 +174,6 @@ class QLiberationWindow(QMainWindow): displayMenu = self.menu.addMenu("&Display") - last_was_group = False for item in DisplayOptions.menu_items(): if isinstance(item, DisplayRule): @@ -194,17 +199,29 @@ class QLiberationWindow(QMainWindow): help_menu = self.menu.addMenu("&Help") help_menu.addAction(self.openDiscordAction) help_menu.addAction(self.openGithubAction) - help_menu.addAction("&Releases", lambda: webbrowser.open_new_tab("https://github.com/Khopa/dcs_liberation/releases")) - help_menu.addAction("&Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) - help_menu.addAction("&ED Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"])) - help_menu.addAction("Report an &issue", lambda: webbrowser.open_new_tab(URLS["Issues"])) + help_menu.addAction( + "&Releases", + lambda: webbrowser.open_new_tab( + "https://github.com/Khopa/dcs_liberation/releases" + ), + ) + help_menu.addAction( + "&Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"]) + ) + help_menu.addAction( + "&ED Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"]) + ) + help_menu.addAction( + "Report an &issue", lambda: webbrowser.open_new_tab(URLS["Issues"]) + ) help_menu.addSeparator() help_menu.addAction(self.showAboutDialogAction) @staticmethod def make_display_rule_action( - display_rule, group: Optional[QActionGroup] = None) -> QAction: + display_rule, group: Optional[QActionGroup] = None + ) -> QAction: def make_check_closure(): def closure(): display_rule.value = action.isChecked() @@ -227,9 +244,12 @@ class QLiberationWindow(QMainWindow): wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame)) def openFile(self): - file = QFileDialog.getOpenFileName(self, "Select game file to open", - dir=persistency._dcs_saved_game_folder, - filter="*.liberation") + file = QFileDialog.getOpenFileName( + self, + "Select game file to open", + dir=persistency._dcs_saved_game_folder, + filter="*.liberation", + ) if file is not None: game = persistency.load_game(file[0]) GameUpdateSignal.get_instance().updateGame(game) @@ -246,7 +266,12 @@ class QLiberationWindow(QMainWindow): self.saveGameAs() def saveGameAs(self): - file = QFileDialog.getSaveFileName(self, "Save As", dir=persistency._dcs_saved_game_folder, filter="*.liberation") + file = QFileDialog.getSaveFileName( + self, + "Save As", + dir=persistency._dcs_saved_game_folder, + filter="*.liberation", + ) if file is not None: self.game.savepath = file[0] persistency.save_game(self.game) @@ -275,23 +300,27 @@ class QLiberationWindow(QMainWindow): "version of DCS Liberation.\n" "\n" f"{traceback.format_exc()}", - QMessageBox.Ok + QMessageBox.Ok, ) GameUpdateSignal.get_instance().updateGame(None) def showAboutDialog(self): - text = "

DCS Liberation " + VERSION + "

" + \ - "Source code : https://github.com/khopa/dcs_liberation" + \ - "

Authors

" + \ - "

DCS Liberation was originally developed by shdwp, DCS Liberation 2.0 is a partial rewrite based on this work by Khopa." \ - "

Contributors

" + \ - "shdwp, Khopa, ColonelPanic, Roach, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57, Plob, Hawkmoon" + \ - "

Special Thanks :

" \ - "rp- for the pydcs framework
"\ - "Grimes (mrSkortch) & Speed for the MIST framework
"\ - "Ciribob for the JTACAutoLase.lua script
"\ - "Walder for the Skynet-IADS script
"\ - "Anubis Yinepu for the Hercules Cargo script
" + text = ( + "

DCS Liberation " + + VERSION + + "

" + + "Source code : https://github.com/khopa/dcs_liberation" + + "

Authors

" + + "

DCS Liberation was originally developed by shdwp, DCS Liberation 2.0 is a partial rewrite based on this work by Khopa." + "

Contributors

" + + "shdwp, Khopa, ColonelPanic, Roach, Wrycu, calvinmorrow, JohanAberg, Deus, root0fall, Captain Cody, steveveepee, pedromagueija, parithon, bwRavencl, davidp57, Plob, Hawkmoon" + + "

Special Thanks :

" + "rp- for the pydcs framework
" + "Grimes (mrSkortch) & Speed for the MIST framework
" + "Ciribob for the JTACAutoLase.lua script
" + "Walder for the Skynet-IADS script
" + "Anubis Yinepu for the Hercules Cargo script
" + ) about = QMessageBox() about.setWindowTitle("About DCS Liberation") about.setIcon(QMessageBox.Icon.Information) @@ -310,9 +339,10 @@ class QLiberationWindow(QMainWindow): def closeEvent(self, event: QCloseEvent) -> None: result = QMessageBox.question( - self, "Quit Liberation?", + self, + "Quit Liberation?", "Are you sure you want to quit? All unsaved progress will be lost.", - QMessageBox.Yes | QMessageBox.No + QMessageBox.Yes | QMessageBox.No, ) if result == QMessageBox.Yes: super().closeEvent(event) diff --git a/qt_ui/windows/QUnitInfoWindow.py b/qt_ui/windows/QUnitInfoWindow.py index 71735496..541f208b 100644 --- a/qt_ui/windows/QUnitInfoWindow.py +++ b/qt_ui/windows/QUnitInfoWindow.py @@ -28,13 +28,14 @@ from gen.flights.flight import FlightType class QUnitInfoWindow(QDialog): - def __init__(self, game: Game, unit_type: Type[UnitType]) -> None: super(QUnitInfoWindow, self).__init__() self.setModal(True) self.game = game self.unit_type = unit_type - self.setWindowTitle(f"Unit Info: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'name')}") + self.setWindowTitle( + f"Unit Info: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'name')}" + ) self.setWindowIcon(QIcon("./resources/icon.png")) self.setMinimumHeight(570) self.setMaximumWidth(640) @@ -47,7 +48,10 @@ class QUnitInfoWindow(QDialog): header = QLabel(self) header.setGeometry(0, 0, 720, 360) - if dcs.planes.plane_map.get(self.unit_type.id) is not None or dcs.helicopters.helicopter_map.get(self.unit_type.id) is not None: + if ( + dcs.planes.plane_map.get(self.unit_type.id) is not None + or dcs.helicopters.helicopter_map.get(self.unit_type.id) is not None + ): pixmap = AIRCRAFT_BANNERS.get(self.unit_type.id) elif dcs.vehicles.vehicle_map.get(self.unit_type.id) is not None: pixmap = VEHICLE_BANNERS.get(self.unit_type.id) @@ -57,24 +61,32 @@ class QUnitInfoWindow(QDialog): self.layout.addWidget(header, 0, 0) self.gridLayout = QGridLayout() - + # Build the topmost details grid. self.details_grid = QFrame() self.details_grid_layout = QGridLayout() self.details_grid_layout.setMargin(0) - self.name_box = QLabel(f"Name: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'manufacturer')} {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'name')}") + self.name_box = QLabel( + f"Name: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'manufacturer')} {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'name')}" + ) self.name_box.setProperty("style", "info-element") - - self.country_box = QLabel(f"Country of Origin: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'country-of-origin')}") + + self.country_box = QLabel( + f"Country of Origin: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'country-of-origin')}" + ) self.country_box.setProperty("style", "info-element") - - self.role_box = QLabel(f"Role: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'role')}") + + self.role_box = QLabel( + f"Role: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'role')}" + ) self.role_box.setProperty("style", "info-element") - - self.year_box = QLabel(f"Variant Introduction: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'year-of-variant-introduction')}") + + self.year_box = QLabel( + f"Variant Introduction: {db.unit_get_expanded_info(self.game.player_country, self.unit_type, 'year-of-variant-introduction')}" + ) self.year_box.setProperty("style", "info-element") - + self.details_grid_layout.addWidget(self.name_box, 0, 0) self.details_grid_layout.addWidget(self.country_box, 0, 1) self.details_grid_layout.addWidget(self.role_box, 1, 0) @@ -83,17 +95,24 @@ class QUnitInfoWindow(QDialog): self.details_grid.setLayout(self.details_grid_layout) self.gridLayout.addWidget(self.details_grid, 1, 0) - + # If it's an aircraft, include the task list. - if dcs.planes.plane_map.get(self.unit_type.id) is not None or dcs.helicopters.helicopter_map.get(self.unit_type.id) is not None: - self.tasks_box = QLabel(f"In-Game Tasks: {self.generateAircraftTasks()}") + if ( + dcs.planes.plane_map.get(self.unit_type.id) is not None + or dcs.helicopters.helicopter_map.get(self.unit_type.id) is not None + ): + self.tasks_box = QLabel( + f"In-Game Tasks: {self.generateAircraftTasks()}" + ) self.tasks_box.setProperty("style", "info-element") self.gridLayout.addWidget(self.tasks_box, 2, 0) # Finally, add the description box. self.details_text = QTextBrowser() self.details_text.setProperty("style", "info-desc") - self.details_text.setText(db.unit_get_expanded_info(self.game.player_country, self.unit_type, "text")) + self.details_text.setText( + db.unit_get_expanded_info(self.game.player_country, self.unit_type, "text") + ) self.gridLayout.addWidget(self.details_text, 3, 0) self.layout.addLayout(self.gridLayout, 1, 0) @@ -102,9 +121,18 @@ class QUnitInfoWindow(QDialog): def generateAircraftTasks(self) -> str: aircraft_tasks = "" if self.unit_type in gen.flights.ai_flight_planner_db.CAP_CAPABLE: - aircraft_tasks = aircraft_tasks + f"{FlightType.BARCAP}, {FlightType.ESCORT}, {FlightType.INTERCEPTION}, {FlightType.SWEEP}, {FlightType.TARCAP}, " - if self.unit_type in gen.flights.ai_flight_planner_db.CAS_CAPABLE or self.unit_type in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE: - aircraft_tasks = aircraft_tasks + f"{FlightType.CAS}, {FlightType.BAI}, {FlightType.OCA_AIRCRAFT}, " + aircraft_tasks = ( + aircraft_tasks + + f"{FlightType.BARCAP}, {FlightType.ESCORT}, {FlightType.INTERCEPTION}, {FlightType.SWEEP}, {FlightType.TARCAP}, " + ) + if ( + self.unit_type in gen.flights.ai_flight_planner_db.CAS_CAPABLE + or self.unit_type in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE + ): + aircraft_tasks = ( + aircraft_tasks + + f"{FlightType.CAS}, {FlightType.BAI}, {FlightType.OCA_AIRCRAFT}, " + ) if self.unit_type in gen.flights.ai_flight_planner_db.SEAD_CAPABLE: aircraft_tasks = aircraft_tasks + f"{FlightType.SEAD}, " if self.unit_type in gen.flights.ai_flight_planner_db.DEAD_CAPABLE: @@ -113,6 +141,9 @@ class QUnitInfoWindow(QDialog): aircraft_tasks = aircraft_tasks + f"{FlightType.ANTISHIP}, " if self.unit_type in gen.flights.ai_flight_planner_db.RUNWAY_ATTACK_CAPABLE: aircraft_tasks = aircraft_tasks + f"{FlightType.OCA_RUNWAY}, " - if self.unit_type in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE or self.unit_type in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE: + if ( + self.unit_type in gen.flights.ai_flight_planner_db.STRIKE_CAPABLE + or self.unit_type in gen.flights.ai_flight_planner_db.TRANSPORT_CAPABLE + ): aircraft_tasks = aircraft_tasks + f"{FlightType.STRIKE}, " - return aircraft_tasks[:-2] \ No newline at end of file + return aircraft_tasks[:-2] diff --git a/qt_ui/windows/QWaitingForMissionResultWindow.py b/qt_ui/windows/QWaitingForMissionResultWindow.py index db8e9f54..2efa730b 100644 --- a/qt_ui/windows/QWaitingForMissionResultWindow.py +++ b/qt_ui/windows/QWaitingForMissionResultWindow.py @@ -49,7 +49,6 @@ DebriefingFileWrittenSignal() class QWaitingForMissionResultWindow(QDialog): - def __init__(self, gameEvent: Event, game: Game, unit_map: UnitMap) -> None: super(QWaitingForMissionResultWindow, self).__init__() self.setModal(True) @@ -61,10 +60,14 @@ class QWaitingForMissionResultWindow(QDialog): self.setMinimumHeight(570) self.initUi() - DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(self.updateLayout) + DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect( + self.updateLayout + ) self.wait_thread = wait_for_debriefing( - lambda debriefing: self.on_debriefing_update(debriefing), self.game, - self.unit_map) + lambda debriefing: self.on_debriefing_update(debriefing), + self.game, + self.unit_map, + ) def initUi(self): self.layout = QGridLayout() @@ -89,7 +92,8 @@ class QWaitingForMissionResultWindow(QDialog): ) self.instructions_text = QTextBrowser() self.instructions_text.setHtml( - jinja.get_template("mission_start_EN.j2").render()) + jinja.get_template("mission_start_EN.j2").render() + ) self.instructions_text.setOpenExternalLinks(True) self.gridLayout.addWidget(self.instructions_text, 1, 0) @@ -110,7 +114,6 @@ class QWaitingForMissionResultWindow(QDialog): self.actions_layout.addWidget(self.cancel) self.gridLayout.addWidget(self.actions, 2, 0) - self.actions2 = QGroupBox("Actions :") self.actions2_layout = QHBoxLayout() self.actions2.setLayout(self.actions2_layout) @@ -137,26 +140,24 @@ class QWaitingForMissionResultWindow(QDialog): updateLayout.addWidget(QLabel("Aircraft destroyed"), 0, 0) updateLayout.addWidget( - QLabel(str(len(list(debriefing.air_losses.losses)))), 0, 1) + QLabel(str(len(list(debriefing.air_losses.losses)))), 0, 1 + ) + updateLayout.addWidget(QLabel("Front line units destroyed"), 1, 0) updateLayout.addWidget( - QLabel("Front line units destroyed"), 1, 0) - updateLayout.addWidget( - QLabel(str(len(list(debriefing.front_line_losses)))), 1, 1) + QLabel(str(len(list(debriefing.front_line_losses)))), 1, 1 + ) + updateLayout.addWidget(QLabel("Other ground units destroyed"), 2, 0) updateLayout.addWidget( - QLabel("Other ground units destroyed"), 2, 0) - updateLayout.addWidget( - QLabel(str(len(list(debriefing.ground_object_losses)))), 2, 1) + QLabel(str(len(list(debriefing.ground_object_losses)))), 2, 1 + ) - updateLayout.addWidget( - QLabel("Buildings destroyed"), 3, 0) - updateLayout.addWidget( - QLabel(str(len(list(debriefing.building_losses)))), 3, 1) + updateLayout.addWidget(QLabel("Buildings destroyed"), 3, 0) + updateLayout.addWidget(QLabel(str(len(list(debriefing.building_losses)))), 3, 1) updateLayout.addWidget(QLabel("Base Capture Events"), 4, 0) - updateLayout.addWidget( - QLabel(str(len(debriefing.base_capture_events))), 4, 1) + updateLayout.addWidget(QLabel(str(len(debriefing.base_capture_events))), 4, 1) # Clear previous content of the window for i in reversed(range(self.gridLayout.count())): @@ -183,7 +184,8 @@ class QWaitingForMissionResultWindow(QDialog): except Exception: logging.exception("Got an error while sending debriefing") self.wait_thread = wait_for_debriefing( - lambda d: self.on_debriefing_update(d), self.game, self.unit_map) + lambda d: self.on_debriefing_update(d), self.game, self.unit_map + ) def process_debriefing(self): start = timeit.default_timer() @@ -205,7 +207,9 @@ class QWaitingForMissionResultWindow(QDialog): self.wait_thread.stop() def submit_manually(self): - file = QFileDialog.getOpenFileName(self, "Select game file to open", filter="json(*.json)", dir=".") + file = QFileDialog.getOpenFileName( + self, "Select game file to open", filter="json(*.json)", dir="." + ) print(file) try: with open(file[0], "r") as json_file: @@ -223,4 +227,3 @@ class QWaitingForMissionResultWindow(QDialog): msg.setWindowFlags(Qt.WindowStaysOnTopHint) msg.exec_() return - diff --git a/qt_ui/windows/basemenu/QBaseMenu2.py b/qt_ui/windows/basemenu/QBaseMenu2.py index 5b399c82..65b49710 100644 --- a/qt_ui/windows/basemenu/QBaseMenu2.py +++ b/qt_ui/windows/basemenu/QBaseMenu2.py @@ -22,7 +22,6 @@ from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour class QBaseMenu2(QDialog): - def __init__(self, parent, cp: ControlPoint, game_model: GameModel): super(QBaseMenu2, self).__init__(parent) @@ -95,8 +94,7 @@ class QBaseMenu2(QDialog): self.budget_display.setAlignment(Qt.AlignRight | Qt.AlignBottom) self.budget_display.setProperty("style", "budget-label") bottom_row.addWidget(self.budget_display) - GameUpdateSignal.get_instance().budgetupdated.connect( - self.update_budget) + GameUpdateSignal.get_instance().budgetupdated.connect(self.update_budget) self.setLayout(main_layout) @property @@ -114,13 +112,16 @@ class QBaseMenu2(QDialog): "Cannot repair runway", f"Runway repair costs ${db.RUNWAY_REPAIR_COST}M but you have " f"only ${self.game_model.game.budget}M available.", - QMessageBox.Ok) + QMessageBox.Ok, + ) return if not self.can_repair_runway: QMessageBox.critical( self, "Cannot repair runway", - f"Cannot repair this runway.", QMessageBox.Ok) + f"Cannot repair this runway.", + QMessageBox.Ok, + ) return self.cp.begin_runway_repair() @@ -144,7 +145,8 @@ class QBaseMenu2(QDialog): return else: self.repair_button.setText( - f"Cannot afford repair ${db.RUNWAY_REPAIR_COST}M") + f"Cannot afford repair ${db.RUNWAY_REPAIR_COST}M" + ) self.repair_button.setDisabled(True) return @@ -152,11 +154,15 @@ class QBaseMenu2(QDialog): self.repair_button.setDisabled(True) def update_intel_summary(self) -> None: - self.intel_summary.setText("\n".join([ - f"{self.cp.base.total_aircraft} aircraft", - f"{self.cp.base.total_armor} ground units", - str(self.cp.runway_status) - ])) + self.intel_summary.setText( + "\n".join( + [ + f"{self.cp.base.total_aircraft} aircraft", + f"{self.cp.base.total_armor} ground units", + str(self.cp.runway_status), + ] + ) + ) def closeEvent(self, close_event: QCloseEvent): GameUpdateSignal.get_instance().updateGame(self.game_model.game) @@ -173,5 +179,4 @@ class QBaseMenu2(QDialog): Dialog.open_new_package_dialog(self.cp, parent=self.window()) def update_budget(self, game: Game) -> None: - self.budget_display.setText( - QRecruitBehaviour.BUDGET_FORMAT.format(game.budget)) + self.budget_display.setText(QRecruitBehaviour.BUDGET_FORMAT.format(game.budget)) diff --git a/qt_ui/windows/basemenu/QBaseMenuTabs.py b/qt_ui/windows/basemenu/QBaseMenuTabs.py index b7bb551a..b33cc733 100644 --- a/qt_ui/windows/basemenu/QBaseMenuTabs.py +++ b/qt_ui/windows/basemenu/QBaseMenuTabs.py @@ -9,7 +9,6 @@ from qt_ui.windows.basemenu.intel.QIntelInfo import QIntelInfo class QBaseMenuTabs(QTabWidget): - def __init__(self, cp: ControlPoint, game_model: GameModel): super(QBaseMenuTabs, self).__init__() @@ -30,4 +29,4 @@ class QBaseMenuTabs(QTabWidget): self.ground_forces_hq = QGroundForcesHQ(cp, game_model) self.addTab(self.ground_forces_hq, "Ground Forces HQ") self.base_defenses_hq = QBaseDefensesHQ(cp, game_model.game) - self.addTab(self.base_defenses_hq, "Base Defenses") \ No newline at end of file + self.addTab(self.base_defenses_hq, "Base Defenses") diff --git a/qt_ui/windows/basemenu/QRecruitBehaviour.py b/qt_ui/windows/basemenu/QRecruitBehaviour.py index 799c526b..97ef9b2a 100644 --- a/qt_ui/windows/basemenu/QRecruitBehaviour.py +++ b/qt_ui/windows/basemenu/QRecruitBehaviour.py @@ -48,8 +48,13 @@ class QRecruitBehaviour: def budget(self, value: int) -> None: self.game_model.game.budget = value - def add_purchase_row(self, unit_type: Type[UnitType], layout: QLayout, - row: int, disabled: bool = False) -> int: + def add_purchase_row( + self, + unit_type: Type[UnitType], + layout: QLayout, + row: int, + disabled: bool = False, + ) -> int: exist = QGroupBox() exist.setProperty("style", "buy-box") exist.setMaximumHeight(36) @@ -60,8 +65,16 @@ class QRecruitBehaviour: existing_units = self.cp.base.total_units_of_type(unit_type) scheduled_units = self.pending_deliveries.units.get(unit_type, 0) - unitName = QLabel("" + db.unit_get_expanded_info(self.game_model.game.player_country, unit_type, 'name') + "") - unitName.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) + unitName = QLabel( + "" + + db.unit_get_expanded_info( + self.game_model.game.player_country, unit_type, "name" + ) + + "" + ) + unitName.setSizePolicy( + QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + ) existing_units = QLabel(str(existing_units)) existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) @@ -104,7 +117,7 @@ class QRecruitBehaviour: info.setMinimumHeight(36) infolayout = QHBoxLayout() info.setLayout(infolayout) - + unitInfo = QPushButton("i") unitInfo.setProperty("style", "btn-info") unitInfo.setDisabled(disabled) @@ -114,9 +127,13 @@ class QRecruitBehaviour: unitInfo.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) existLayout.addWidget(unitName) - existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) + existLayout.addItem( + QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum) + ) existLayout.addWidget(existing_units) - existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum)) + existLayout.addItem( + QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum) + ) existLayout.addWidget(price) buysellayout.addWidget(sell) @@ -133,13 +150,17 @@ class QRecruitBehaviour: def _update_count_label(self, unit_type: Type[UnitType]): - self.bought_amount_labels[unit_type].setText("{}".format( - unit_type in self.pending_deliveries.units and "{}".format(self.pending_deliveries.units[unit_type]) or "0" - )) + self.bought_amount_labels[unit_type].setText( + "{}".format( + unit_type in self.pending_deliveries.units + and "{}".format(self.pending_deliveries.units[unit_type]) + or "0" + ) + ) - self.existing_units_labels[unit_type].setText("{}".format( - self.cp.base.total_units_of_type(unit_type) - )) + self.existing_units_labels[unit_type].setText( + "{}".format(self.cp.base.total_units_of_type(unit_type)) + ) def update_available_budget(self) -> None: GameUpdateSignal.get_instance().updateBudget(self.game_model.game) diff --git a/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py b/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py index b2820a5e..d5da57af 100644 --- a/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py +++ b/qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py @@ -65,11 +65,19 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour): continue unit_types.add(unit) - sorted_units = sorted(unit_types, key=lambda u: db.unit_get_expanded_info(self.game_model.game.player_country, u, 'name')) + sorted_units = sorted( + unit_types, + key=lambda u: db.unit_get_expanded_info( + self.game_model.game.player_country, u, "name" + ), + ) for unit_type in sorted_units: row = self.add_purchase_row( - unit_type, task_box_layout, row, - disabled=not self.cp.can_operate(unit_type)) + unit_type, + task_box_layout, + row, + disabled=not self.cp.can_operate(unit_type), + ) stretch = QVBoxLayout() stretch.addStretch() task_box_layout.addLayout(stretch, row, 0) @@ -89,8 +97,11 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour): if self.cp.unclaimed_parking(self.game_model.game) <= 0: logging.debug(f"No space for additional aircraft at {self.cp}.") QMessageBox.warning( - self, "No space for additional aircraft", - f"There is no parking space left at {self.cp.name} to accommodate another plane.", QMessageBox.Ok) + self, + "No space for additional aircraft", + f"There is no parking space left at {self.cp.name} to accommodate another plane.", + QMessageBox.Ok, + ) return # If we change our mind about selling, we want the aircraft to be put # back in the inventory immediately. @@ -98,7 +109,7 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour): global_inventory = self.game_model.game.aircraft_inventory inventory = global_inventory.for_control_point(self.cp) inventory.add_aircraft(unit_type, 1) - + super().buy(unit_type) self.hangar_status.update_label() @@ -112,19 +123,20 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour): inventory.remove_aircraft(unit_type, 1) except ValueError: QMessageBox.critical( - self, "Could not sell aircraft", + self, + "Could not sell aircraft", f"Attempted to sell one {unit_type.id} at {self.cp.name} " "but none are available. Are all aircraft currently " - "assigned to a mission?", QMessageBox.Ok) + "assigned to a mission?", + QMessageBox.Ok, + ) return super().sell(unit_type) self.hangar_status.update_label() class QHangarStatus(QHBoxLayout): - - def __init__(self, game_model: GameModel, - control_point: ControlPoint) -> None: + def __init__(self, game_model: GameModel, control_point: ControlPoint) -> None: super().__init__() self.game_model = game_model self.control_point = control_point @@ -140,8 +152,7 @@ class QHangarStatus(QHBoxLayout): self.setAlignment(Qt.AlignLeft) def update_label(self) -> None: - next_turn = self.control_point.expected_aircraft_next_turn( - self.game_model.game) + next_turn = self.control_point.expected_aircraft_next_turn(self.game_model.game) max_amount = self.control_point.total_aircraft_parking components = [f"{next_turn.present} present"] @@ -158,4 +169,5 @@ class QHangarStatus(QHBoxLayout): details = ", ".join(components) self.text.setText( - f"{next_turn.total}/{max_amount} ({details})") + f"{next_turn.total}/{max_amount} ({details})" + ) diff --git a/qt_ui/windows/basemenu/airfield/QAirfieldCommand.py b/qt_ui/windows/basemenu/airfield/QAirfieldCommand.py index cac85b83..289fd8f8 100644 --- a/qt_ui/windows/basemenu/airfield/QAirfieldCommand.py +++ b/qt_ui/windows/basemenu/airfield/QAirfieldCommand.py @@ -2,14 +2,14 @@ from PySide2.QtWidgets import QFrame, QGridLayout, QGroupBox, QVBoxLayout from game.theater import ControlPoint from qt_ui.models import GameModel -from qt_ui.windows.basemenu.airfield.QAircraftRecruitmentMenu import \ - QAircraftRecruitmentMenu +from qt_ui.windows.basemenu.airfield.QAircraftRecruitmentMenu import ( + QAircraftRecruitmentMenu, +) from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView class QAirfieldCommand(QFrame): - - def __init__(self, cp:ControlPoint, game_model: GameModel): + def __init__(self, cp: ControlPoint, game_model: GameModel): super(QAirfieldCommand, self).__init__() self.cp = cp self.game_model = game_model @@ -22,9 +22,7 @@ class QAirfieldCommand(QFrame): planned = QGroupBox("Planned Flights") planned_layout = QVBoxLayout() - planned_layout.addWidget( - QPlannedFlightsView(self.game_model, self.cp) - ) + planned_layout.addWidget(QPlannedFlightsView(self.game_model, self.cp)) planned.setLayout(planned_layout) layout.addWidget(planned, 0, 1) diff --git a/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py b/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py index 16aa2a90..618e62dd 100644 --- a/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py +++ b/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py @@ -16,13 +16,14 @@ from dcs import vehicles class QBaseDefenseGroupInfo(QGroupBox): - def __init__(self, cp: ControlPoint, ground_object: TheaterGroundObject, game): super(QBaseDefenseGroupInfo, self).__init__("Group : " + ground_object.obj_name) self.ground_object = ground_object self.cp = cp self.game = game - self.buildings = game.theater.find_ground_objects_by_obj_name(self.ground_object.obj_name) + self.buildings = game.theater.find_ground_objects_by_obj_name( + self.ground_object.obj_name + ) self.main_layout = QVBoxLayout() self.unit_layout = QGridLayout() @@ -76,24 +77,30 @@ class QBaseDefenseGroupInfo(QGroupBox): unit_display_name = k unit_type = vehicles.vehicle_map.get(k) if unit_type is not None: - unit_display_name = db.unit_get_expanded_info(self.game.enemy_country, unit_type, 'name') - self.unit_layout.addWidget(QLabel(str(v) + " x " + "" + unit_display_name + ""), i, 1) + unit_display_name = db.unit_get_expanded_info( + self.game.enemy_country, unit_type, "name" + ) + self.unit_layout.addWidget( + QLabel(str(v) + " x " + "" + unit_display_name + ""), + i, + 1, + ) i = i + 1 if len(unit_dict.items()) == 0: self.unit_layout.addWidget(QLabel("/"), 0, 0) - - self.setLayout(self.main_layout) - + def onAttack(self): Dialog.open_new_package_dialog(self.ground_object, parent=self.window()) def onManage(self): - self.edition_menu = QGroundObjectMenu(self.window(), self.ground_object, self.buildings, self.cp, self.game) + self.edition_menu = QGroundObjectMenu( + self.window(), self.ground_object, self.buildings, self.cp, self.game + ) self.edition_menu.show() self.edition_menu.changed.connect(self.onEdition) def onEdition(self): - self.buildLayout() \ No newline at end of file + self.buildLayout() diff --git a/qt_ui/windows/basemenu/base_defenses/QBaseDefensesHQ.py b/qt_ui/windows/basemenu/base_defenses/QBaseDefensesHQ.py index 75a45eb0..f1d99d67 100644 --- a/qt_ui/windows/basemenu/base_defenses/QBaseDefensesHQ.py +++ b/qt_ui/windows/basemenu/base_defenses/QBaseDefensesHQ.py @@ -2,13 +2,11 @@ from PySide2.QtWidgets import QFrame, QGridLayout from game import Game from game.theater import ControlPoint -from qt_ui.windows.basemenu.base_defenses.QBaseInformation import \ - QBaseInformation +from qt_ui.windows.basemenu.base_defenses.QBaseInformation import QBaseInformation class QBaseDefensesHQ(QFrame): - - def __init__(self, cp:ControlPoint, game:Game): + def __init__(self, cp: ControlPoint, game: Game): super(QBaseDefensesHQ, self).__init__() self.cp = cp self.game = game @@ -19,4 +17,3 @@ class QBaseDefensesHQ(QFrame): layout = QGridLayout() layout.addWidget(QBaseInformation(self.cp, airport, self.game)) self.setLayout(layout) - diff --git a/qt_ui/windows/basemenu/base_defenses/QBaseInformation.py b/qt_ui/windows/basemenu/base_defenses/QBaseInformation.py index 7db68d8c..f2b874dd 100644 --- a/qt_ui/windows/basemenu/base_defenses/QBaseInformation.py +++ b/qt_ui/windows/basemenu/base_defenses/QBaseInformation.py @@ -9,13 +9,13 @@ from PySide2.QtWidgets import ( from game.theater import Airport, ControlPoint, Fob from game.theater.theatergroundobject import BuildingGroundObject -from qt_ui.windows.basemenu.base_defenses.QBaseDefenseGroupInfo import \ - QBaseDefenseGroupInfo +from qt_ui.windows.basemenu.base_defenses.QBaseDefenseGroupInfo import ( + QBaseDefenseGroupInfo, +) class QBaseInformation(QFrame): - - def __init__(self, cp:ControlPoint, airport:Airport, game): + def __init__(self, cp: ControlPoint, airport: Airport, game): super(QBaseInformation, self).__init__() self.cp = cp self.airport = airport @@ -53,4 +53,4 @@ class QBaseInformation(QFrame): self.mainLayout.addWidget(scroll) - self.setLayout(self.mainLayout) \ No newline at end of file + self.setLayout(self.mainLayout) diff --git a/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py b/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py index 3f089036..46536204 100644 --- a/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py +++ b/qt_ui/windows/basemenu/ground_forces/QArmorRecruitmentMenu.py @@ -17,7 +17,6 @@ from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour): - def __init__(self, cp: ControlPoint, game_model: GameModel): QFrame.__init__(self) self.cp = cp @@ -32,8 +31,9 @@ class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour): main_layout = QVBoxLayout() units = { - PinpointStrike: db.find_unittype(PinpointStrike, - self.game_model.game.player_name), + PinpointStrike: db.find_unittype( + PinpointStrike, self.game_model.game.player_name + ), } scroll_content = QWidget() @@ -43,8 +43,13 @@ class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour): for task_type in units.keys(): units_column = list(set(units[task_type])) - if len(units_column) == 0: continue - units_column.sort(key=lambda u: db.unit_get_expanded_info(self.game_model.game.player_country, u, 'name')) + if len(units_column) == 0: + continue + units_column.sort( + key=lambda u: db.unit_get_expanded_info( + self.game_model.game.player_country, u, "name" + ) + ) for unit_type in units_column: row = self.add_purchase_row(unit_type, task_box_layout, row) stretch = QVBoxLayout() @@ -63,8 +68,11 @@ class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour): def sell(self, unit_type: UnitType): if self.pending_deliveries.available_next_turn(unit_type) <= 0: QMessageBox.critical( - self, "Could not sell ground unit", + self, + "Could not sell ground unit", f"Attempted to sell one {unit_type.id} at {self.cp.name} " - "but none are available.", QMessageBox.Ok) + "but none are available.", + QMessageBox.Ok, + ) return - super().sell(unit_type) \ No newline at end of file + super().sell(unit_type) diff --git a/qt_ui/windows/basemenu/ground_forces/QGroundForcesHQ.py b/qt_ui/windows/basemenu/ground_forces/QGroundForcesHQ.py index 39cba843..f6304fcd 100644 --- a/qt_ui/windows/basemenu/ground_forces/QGroundForcesHQ.py +++ b/qt_ui/windows/basemenu/ground_forces/QGroundForcesHQ.py @@ -2,14 +2,15 @@ from PySide2.QtWidgets import QFrame, QGridLayout from game.theater import ControlPoint from qt_ui.models import GameModel -from qt_ui.windows.basemenu.ground_forces.QArmorRecruitmentMenu import \ - QArmorRecruitmentMenu -from qt_ui.windows.basemenu.ground_forces.QGroundForcesStrategy import \ - QGroundForcesStrategy +from qt_ui.windows.basemenu.ground_forces.QArmorRecruitmentMenu import ( + QArmorRecruitmentMenu, +) +from qt_ui.windows.basemenu.ground_forces.QGroundForcesStrategy import ( + QGroundForcesStrategy, +) class QGroundForcesHQ(QFrame): - def __init__(self, cp: ControlPoint, game_model: GameModel) -> None: super(QGroundForcesHQ, self).__init__() self.cp = cp @@ -19,6 +20,5 @@ class QGroundForcesHQ(QFrame): def init_ui(self): layout = QGridLayout() layout.addWidget(QArmorRecruitmentMenu(self.cp, self.game_model), 0, 0) - layout.addWidget(QGroundForcesStrategy(self.cp, self.game_model.game), - 0, 1) + layout.addWidget(QGroundForcesStrategy(self.cp, self.game_model.game), 0, 1) self.setLayout(layout) diff --git a/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategy.py b/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategy.py index 3aee8c50..c903413b 100644 --- a/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategy.py +++ b/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategy.py @@ -2,13 +2,13 @@ from PySide2.QtWidgets import QGroupBox, QLabel, QVBoxLayout from game import Game from game.theater import ControlPoint -from qt_ui.windows.basemenu.ground_forces.QGroundForcesStrategySelector import \ - QGroundForcesStrategySelector +from qt_ui.windows.basemenu.ground_forces.QGroundForcesStrategySelector import ( + QGroundForcesStrategySelector, +) class QGroundForcesStrategy(QGroupBox): - - def __init__(self, cp:ControlPoint, game:Game): + def __init__(self, cp: ControlPoint, game: Game): super(QGroundForcesStrategy, self).__init__("Frontline operations :") self.cp = cp self.game = game diff --git a/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategySelector.py b/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategySelector.py index 4acd8731..9cd23eaa 100644 --- a/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategySelector.py +++ b/qt_ui/windows/basemenu/ground_forces/QGroundForcesStrategySelector.py @@ -4,8 +4,7 @@ from game.theater import CombatStance, ControlPoint class QGroundForcesStrategySelector(QComboBox): - - def __init__(self, cp:ControlPoint, enemy_cp:ControlPoint): + def __init__(self, cp: ControlPoint, enemy_cp: ControlPoint): super(QGroundForcesStrategySelector, self).__init__() self.cp = cp self.enemy_cp = enemy_cp diff --git a/qt_ui/windows/basemenu/intel/QIntelInfo.py b/qt_ui/windows/basemenu/intel/QIntelInfo.py index 6c40d5f9..ea5dfa4c 100644 --- a/qt_ui/windows/basemenu/intel/QIntelInfo.py +++ b/qt_ui/windows/basemenu/intel/QIntelInfo.py @@ -15,8 +15,7 @@ from game.theater import ControlPoint class QIntelInfo(QFrame): - - def __init__(self, cp:ControlPoint, game:Game): + def __init__(self, cp: ControlPoint, game: Game): super(QIntelInfo, self).__init__() self.cp = cp self.game = game @@ -27,8 +26,6 @@ class QIntelInfo(QFrame): scroll_content = QWidget() intelLayout = QVBoxLayout() - - units = { CAP: db.find_unittype(CAP, self.game.enemy_name), Embarking: db.find_unittype(Embarking, self.game.enemy_name), @@ -50,7 +47,17 @@ class QIntelInfo(QFrame): existing_units = self.cp.base.total_units_of_type(unit_type) if existing_units == 0: continue - groupLayout.addWidget(QLabel("" + db.unit_get_expanded_info(self.game.enemy_country, unit_type, 'name') + ""), row, 0) + groupLayout.addWidget( + QLabel( + "" + + db.unit_get_expanded_info( + self.game.enemy_country, unit_type, "name" + ) + + "" + ), + row, + 0, + ) groupLayout.addWidget(QLabel(str(existing_units)), row, 1) row += 1 @@ -64,5 +71,5 @@ class QIntelInfo(QFrame): scroll.setWidget(scroll_content) layout.addWidget(scroll) - - self.setLayout(layout) \ No newline at end of file + + self.setLayout(layout) diff --git a/qt_ui/windows/finances/QFinancesMenu.py b/qt_ui/windows/finances/QFinancesMenu.py index e1151b64..ea008c10 100644 --- a/qt_ui/windows/finances/QFinancesMenu.py +++ b/qt_ui/windows/finances/QFinancesMenu.py @@ -16,7 +16,6 @@ from game.theater import ControlPoint class QHorizontalSeparationLine(QFrame): - def __init__(self): super().__init__() self.setMinimumWidth(1) @@ -34,7 +33,8 @@ class FinancesLayout(QGridLayout): income = Income(game, player) control_points = reversed( - sorted(income.control_points, key=lambda c: c.income_per_turn)) + sorted(income.control_points, key=lambda c: c.income_per_turn) + ) for control_point in control_points: self.add_control_point(control_point) @@ -46,8 +46,10 @@ class FinancesLayout(QGridLayout): self.add_line() - self.add_row(middle=f"Income multiplier: {income.multiplier:.1f}", - right=f"{income.total}M") + self.add_row( + middle=f"Income multiplier: {income.multiplier:.1f}", + right=f"{income.total}M", + ) if player: budget = game.budget @@ -57,8 +59,12 @@ class FinancesLayout(QGridLayout): self.add_row(middle="Balance", right=f"{budget}M") self.setRowStretch(next(self.row), 1) - def add_row(self, left: Optional[str] = None, middle: Optional[str] = None, - right: Optional[str] = None) -> None: + def add_row( + self, + left: Optional[str] = None, + middle: Optional[str] = None, + right: Optional[str] = None, + ) -> None: if not any([left, middle, right]): raise ValueError @@ -71,17 +77,21 @@ class FinancesLayout(QGridLayout): self.addWidget(QLabel(right), row, 2) def add_control_point(self, control_point: ControlPoint) -> None: - self.add_row(left=f"{control_point.name}", - right=f"{control_point.income_per_turn}M") + self.add_row( + left=f"{control_point.name}", + right=f"{control_point.income_per_turn}M", + ) def add_building(self, building: BuildingIncome) -> None: row = next(self.row) self.addWidget( - QLabel(f"{building.category.upper()} [{building.name}]"), - row, 0) - self.addWidget(QLabel( - f"{building.number} buildings x {building.income_per_building}M"), - row, 1) + QLabel(f"{building.category.upper()} [{building.name}]"), row, 0 + ) + self.addWidget( + QLabel(f"{building.number} buildings x {building.income_per_building}M"), + row, + 1, + ) rlabel = QLabel(f"{building.income}M") rlabel.setProperty("style", "green") self.addWidget(rlabel, row, 2) @@ -91,7 +101,6 @@ class FinancesLayout(QGridLayout): class QFinancesMenu(QDialog): - def __init__(self, game: Game): super(QFinancesMenu, self).__init__() diff --git a/qt_ui/windows/groundobject/QBuildingInfo.py b/qt_ui/windows/groundobject/QBuildingInfo.py index fcf6366b..3f484f15 100644 --- a/qt_ui/windows/groundobject/QBuildingInfo.py +++ b/qt_ui/windows/groundobject/QBuildingInfo.py @@ -4,8 +4,8 @@ from PySide2.QtGui import QPixmap from PySide2.QtWidgets import QGroupBox, QHBoxLayout, QVBoxLayout, QLabel from game.db import REWARDS -class QBuildingInfo(QGroupBox): +class QBuildingInfo(QGroupBox): def __init__(self, building, ground_object): super(QBuildingInfo, self).__init__() self.building = building @@ -14,7 +14,9 @@ class QBuildingInfo(QGroupBox): def init_ui(self): self.header = QLabel() - path = os.path.join("./resources/ui/units/buildings/" + self.building.dcs_identifier + ".png") + path = os.path.join( + "./resources/ui/units/buildings/" + self.building.dcs_identifier + ".png" + ) if self.building.is_dead: pixmap = QPixmap("./resources/ui/units/buildings/dead.png") elif os.path.isfile(path): @@ -22,7 +24,10 @@ class QBuildingInfo(QGroupBox): else: pixmap = QPixmap("./resources/ui/units/buildings/missing.png") self.header.setPixmap(pixmap) - name = "{} {}".format(self.building.dcs_identifier[0:18], "[DEAD]" if self.building.is_dead else "") + name = "{} {}".format( + self.building.dcs_identifier[0:18], + "[DEAD]" if self.building.is_dead else "", + ) self.name = QLabel(name) self.name.setProperty("style", "small") layout = QVBoxLayout() @@ -30,9 +35,9 @@ class QBuildingInfo(QGroupBox): layout.addWidget(self.name) if self.building.category in REWARDS.keys(): - income_label_text = 'Value: ' + str(REWARDS[self.building.category]) + "M" + income_label_text = "Value: " + str(REWARDS[self.building.category]) + "M" if self.building.is_dead: - income_label_text = '' + income_label_text + '' + income_label_text = "" + income_label_text + "" self.reward = QLabel(income_label_text) layout.addWidget(self.reward) diff --git a/qt_ui/windows/groundobject/QGroundObjectMenu.py b/qt_ui/windows/groundobject/QGroundObjectMenu.py index d979c657..13abb5b3 100644 --- a/qt_ui/windows/groundobject/QGroundObjectMenu.py +++ b/qt_ui/windows/groundobject/QGroundObjectMenu.py @@ -22,8 +22,7 @@ from game.data.building_data import FORTIFICATION_BUILDINGS from game.db import PRICES, PinpointStrike, REWARDS, unit_type_of from game.theater import ControlPoint, TheaterGroundObject from game.theater.theatergroundobject import NavalGroundObject -from gen.defenses.armor_group_generator import \ - generate_armor_group_of_type_and_size +from gen.defenses.armor_group_generator import generate_armor_group_of_type_and_size from gen.sam.sam_group_generator import get_faction_possible_sams_generator from qt_ui.uiconstants import EVENT_ICONS from qt_ui.widgets.QBudgetBox import QBudgetBox @@ -36,9 +35,14 @@ class QGroundObjectMenu(QDialog): changed = QtCore.Signal() - def __init__(self, parent, ground_object: TheaterGroundObject, - buildings: Optional[List[TheaterGroundObject]], - cp: ControlPoint, game: Game): + def __init__( + self, + parent, + ground_object: TheaterGroundObject, + buildings: Optional[List[TheaterGroundObject]], + cp: ControlPoint, + game: Game, + ): super().__init__(parent) self.setMinimumWidth(350) self.ground_object = ground_object @@ -105,8 +109,20 @@ class QGroundObjectMenu(QDialog): unit_display_name = u.type unit_type = vehicles.vehicle_map.get(u.type) if unit_type is not None: - unit_display_name = db.unit_get_expanded_info(self.game.enemy_country, unit_type, 'name') - self.intelLayout.addWidget(QLabel("Unit #" + str(u.id) + " - " + str(unit_display_name) + ""), i, 0) + unit_display_name = db.unit_get_expanded_info( + self.game.enemy_country, unit_type, "name" + ) + self.intelLayout.addWidget( + QLabel( + "Unit #" + + str(u.id) + + " - " + + str(unit_display_name) + + "" + ), + i, + 0, + ) i = i + 1 for u in g.units_losts: @@ -117,11 +133,19 @@ class QGroundObjectMenu(QDialog): else: price = 6 - self.intelLayout.addWidget(QLabel("Unit #" + str(u.id) + " - " + str(u.type) + " [DEAD]"), i, 0) + self.intelLayout.addWidget( + QLabel( + "Unit #" + str(u.id) + " - " + str(u.type) + " [DEAD]" + ), + i, + 0, + ) if self.cp.captured: repair = QPushButton("Repair [" + str(price) + "M]") repair.setProperty("style", "btn-success") - repair.clicked.connect(lambda u=u, g=g, p=price: self.repair_unit(g, u, p)) + repair.clicked.connect( + lambda u=u, g=g, p=price: self.repair_unit(g, u, p) + ) self.intelLayout.addWidget(repair, i, 1) i = i + 1 stretch = QVBoxLayout() @@ -136,7 +160,9 @@ class QGroundObjectMenu(QDialog): received_income = 0 for i, building in enumerate(self.buildings): if building.dcs_identifier not in FORTIFICATION_BUILDINGS: - self.buildingsLayout.addWidget(QBuildingInfo(building, self.ground_object), j/3, j%3) + self.buildingsLayout.addWidget( + QBuildingInfo(building, self.ground_object), j / 3, j % 3 + ) j = j + 1 if building.category in REWARDS.keys(): @@ -148,8 +174,12 @@ class QGroundObjectMenu(QDialog): self.financesBox = QGroupBox("Finances: ") self.financesBoxLayout = QGridLayout() - self.financesBoxLayout.addWidget(QLabel("Available: " + str(total_income) + "M"), 2, 1) - self.financesBoxLayout.addWidget(QLabel("Receiving: " + str(received_income) + "M"), 2, 2) + self.financesBoxLayout.addWidget( + QLabel("Available: " + str(total_income) + "M"), 2, 1 + ) + self.financesBoxLayout.addWidget( + QLabel("Receiving: " + str(received_income) + "M"), 2, 2 + ) self.financesBox.setLayout(self.financesBoxLayout) self.buildingBox.setLayout(self.buildingsLayout) @@ -224,17 +254,25 @@ class QGroundObjectMenu(QDialog): GameUpdateSignal.get_instance().updateBudget(self.game) def buy_group(self): - self.subwindow = QBuyGroupForGroundObjectDialog(self, self.ground_object, self.cp, self.game, self.total_value) + self.subwindow = QBuyGroupForGroundObjectDialog( + self, self.ground_object, self.cp, self.game, self.total_value + ) self.subwindow.changed.connect(self.do_refresh_layout) self.subwindow.show() - class QBuyGroupForGroundObjectDialog(QDialog): changed = QtCore.Signal() - def __init__(self, parent, ground_object: TheaterGroundObject, cp: ControlPoint, game: Game, current_group_value: int): + def __init__( + self, + parent, + ground_object: TheaterGroundObject, + cp: ControlPoint, + game: Game, + current_group_value: int, + ): super(QBuyGroupForGroundObjectDialog, self).__init__(parent) self.setMinimumWidth(350) @@ -256,8 +294,6 @@ class QBuyGroupForGroundObjectDialog(QDialog): self.buySamBox = QGroupBox("Buy SAM site :") self.buyArmorBox = QGroupBox("Buy defensive position :") - - self.init_ui() def init_ui(self): @@ -267,7 +303,9 @@ class QBuyGroupForGroundObjectDialog(QDialog): possible_sams = get_faction_possible_sams_generator(faction) for sam in possible_sams: - self.samCombo.addItem(sam.name + " [$" + str(sam.price) + "M]", userData=sam) + self.samCombo.addItem( + sam.name + " [$" + str(sam.price) + "M]", userData=sam + ) self.samCombo.currentIndexChanged.connect(self.samComboChanged) self.buySamLayout.addWidget(QLabel("Site Type :"), 0, 0, Qt.AlignLeft) @@ -281,9 +319,14 @@ class QBuyGroupForGroundObjectDialog(QDialog): # Armored units - armored_units = db.find_unittype(PinpointStrike, faction.name) # Todo : refactor this legacy nonsense + armored_units = db.find_unittype( + PinpointStrike, faction.name + ) # Todo : refactor this legacy nonsense for unit in set(armored_units): - self.buyArmorCombo.addItem(db.unit_type_name_2(unit) + " [$" + str(db.PRICES[unit]) + "M]", userData=unit) + self.buyArmorCombo.addItem( + db.unit_type_name_2(unit) + " [$" + str(db.PRICES[unit]) + "M]", + userData=unit, + ) self.buyArmorCombo.currentIndexChanged.connect(self.armorComboChanged) self.amount.setMinimum(2) @@ -293,9 +336,13 @@ class QBuyGroupForGroundObjectDialog(QDialog): self.buyArmorLayout.addWidget(QLabel("Unit type :"), 0, 0, Qt.AlignLeft) self.buyArmorLayout.addWidget(self.buyArmorCombo, 0, 1, alignment=Qt.AlignRight) - self.buyArmorLayout.addWidget(QLabel("Group size :"), 1, 0, alignment=Qt.AlignLeft) + self.buyArmorLayout.addWidget( + QLabel("Group size :"), 1, 0, alignment=Qt.AlignLeft + ) self.buyArmorLayout.addWidget(self.amount, 1, 1, alignment=Qt.AlignRight) - self.buyArmorLayout.addWidget(self.buyArmorButton, 2, 1, alignment=Qt.AlignRight) + self.buyArmorLayout.addWidget( + self.buyArmorButton, 2, 1, alignment=Qt.AlignRight + ) stretch2 = QVBoxLayout() stretch2.addStretch() self.buyArmorLayout.addLayout(stretch2, 3, 0) @@ -321,13 +368,36 @@ class QBuyGroupForGroundObjectDialog(QDialog): pass def samComboChanged(self, index): - self.buySamButton.setText("Buy [$" + str(self.samCombo.itemData(index).price) + "M] [-$" + str(self.current_group_value) + "M]") + self.buySamButton.setText( + "Buy [$" + + str(self.samCombo.itemData(index).price) + + "M] [-$" + + str(self.current_group_value) + + "M]" + ) def armorComboChanged(self, index): - self.buyArmorButton.setText("Buy [$" + str(db.PRICES[self.buyArmorCombo.itemData(index)] * self.amount.value()) + "M][-$" + str(self.current_group_value) + "M]") + self.buyArmorButton.setText( + "Buy [$" + + str(db.PRICES[self.buyArmorCombo.itemData(index)] * self.amount.value()) + + "M][-$" + + str(self.current_group_value) + + "M]" + ) def amountComboChanged(self): - self.buyArmorButton.setText("Buy [$" + str(db.PRICES[self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())] * self.amount.value()) + "M][-$" + str(self.current_group_value) + "M]") + self.buyArmorButton.setText( + "Buy [$" + + str( + db.PRICES[ + self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex()) + ] + * self.amount.value() + ) + + "M][-$" + + str(self.current_group_value) + + "M]" + ) def buyArmor(self): logging.info("Buying Armor ") @@ -342,7 +412,9 @@ class QBuyGroupForGroundObjectDialog(QDialog): self.game.budget -= price # Generate Armor - group = generate_armor_group_of_type_and_size(self.game, self.ground_object, utype, int(self.amount.value())) + group = generate_armor_group_of_type_and_size( + self.game, self.ground_object, utype, int(self.amount.value()) + ) self.ground_object.groups = [group] GameUpdateSignal.get_instance().updateBudget(self.game) @@ -377,4 +449,4 @@ class QBuyGroupForGroundObjectDialog(QDialog): msg.setStandardButtons(QMessageBox.Ok) msg.setWindowFlags(Qt.WindowStaysOnTopHint) msg.exec_() - self.close() \ No newline at end of file + self.close() diff --git a/qt_ui/windows/infos/QInfoItem.py b/qt_ui/windows/infos/QInfoItem.py index f8d63bf2..b15c5f1c 100644 --- a/qt_ui/windows/infos/QInfoItem.py +++ b/qt_ui/windows/infos/QInfoItem.py @@ -4,7 +4,6 @@ from game.infos.information import Information class QInfoItem(QStandardItem): - def __init__(self, info: Information): super(QInfoItem, self).__init__() self.info = info diff --git a/qt_ui/windows/infos/QInfoList.py b/qt_ui/windows/infos/QInfoList.py index e2f934c0..7614d21f 100644 --- a/qt_ui/windows/infos/QInfoList.py +++ b/qt_ui/windows/infos/QInfoList.py @@ -7,15 +7,16 @@ from qt_ui.windows.infos.QInfoItem import QInfoItem class QInfoList(QListView): - - def __init__(self, game:Game): + def __init__(self, game: Game): super(QInfoList, self).__init__() self.model = QStandardItemModel(self) self.setModel(self.model) self.game = game self.update_list() - self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) + self.selectionModel().setCurrentIndex( + self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select + ) self.selectionModel().selectionChanged.connect(self.on_selected_info_changed) def on_selected_info_changed(self): @@ -26,8 +27,10 @@ class QInfoList(QListView): if self.game is not None: for i, info in enumerate(reversed(self.game.informations)): self.model.appendRow(QInfoItem(info)) - self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) + self.selectionModel().setCurrentIndex( + self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select + ) def setGame(self, game): self.game = game - self.update_list() \ No newline at end of file + self.update_list() diff --git a/qt_ui/windows/infos/QInfoPanel.py b/qt_ui/windows/infos/QInfoPanel.py index 6b31afa3..2bb021ab 100644 --- a/qt_ui/windows/infos/QInfoPanel.py +++ b/qt_ui/windows/infos/QInfoPanel.py @@ -5,7 +5,6 @@ from qt_ui.windows.infos.QInfoList import QInfoList class QInfoPanel(QGroupBox): - def __init__(self, game: Game): super(QInfoPanel, self).__init__("Info Panel") self.informations_list = QInfoList(game) @@ -24,5 +23,3 @@ class QInfoPanel(QGroupBox): layout.setSpacing(0) layout.setContentsMargins(0, 20, 0, 0) self.setLayout(layout) - - diff --git a/qt_ui/windows/infos/QInfoWidget.py b/qt_ui/windows/infos/QInfoWidget.py index dfa8336a..5f76b422 100644 --- a/qt_ui/windows/infos/QInfoWidget.py +++ b/qt_ui/windows/infos/QInfoWidget.py @@ -4,7 +4,6 @@ from game.infos.information import Information class QInfoWidget(QFrame): - def __init__(self, info: Information): super(QInfoWidget, self).__init__() self.info = info @@ -14,8 +13,6 @@ class QInfoWidget(QFrame): def init_ui(self): layout = QGridLayout() - layout.addWidget(self.titleLabel,0,0) - layout.addWidget(self.textLabel,1,0) + layout.addWidget(self.titleLabel, 0, 0) + layout.addWidget(self.textLabel, 1, 0) self.setLayout(layout) - - diff --git a/qt_ui/windows/intel.py b/qt_ui/windows/intel.py index b2db5421..5e8784cd 100644 --- a/qt_ui/windows/intel.py +++ b/qt_ui/windows/intel.py @@ -58,7 +58,9 @@ class IntelTableLayout(QGridLayout): def add_spacer(self) -> None: self.addItem( QSpacerItem(0, 0, QSizePolicy.Preferred, QSizePolicy.Expanding), - next(self.row), 0) + next(self.row), + 0, + ) def add_row(self, text: str, count: int) -> None: row = next(self.row) @@ -81,7 +83,10 @@ class AircraftIntelLayout(IntelTableLayout): for airframe, count in base.aircraft.items(): if not count: continue - self.add_row(db.unit_get_expanded_info(game.enemy_country, airframe, 'name'), count) + self.add_row( + db.unit_get_expanded_info(game.enemy_country, airframe, "name"), + count, + ) self.add_spacer() self.add_row("Total", total) @@ -121,7 +126,6 @@ class ArmyIntelTab(ScrollingFrame): class IntelTabs(QTabWidget): - def __init__(self, game: Game): super().__init__() @@ -131,7 +135,6 @@ class IntelTabs(QTabWidget): class IntelWindow(QDialog): - def __init__(self, game: Game): super().__init__() diff --git a/qt_ui/windows/mission/QEditFlightDialog.py b/qt_ui/windows/mission/QEditFlightDialog.py index a6893a47..028b088d 100644 --- a/qt_ui/windows/mission/QEditFlightDialog.py +++ b/qt_ui/windows/mission/QEditFlightDialog.py @@ -14,8 +14,13 @@ from qt_ui.windows.mission.flight.QFlightPlanner import QFlightPlanner class QEditFlightDialog(QDialog): """Dialog window for editing flight plans and loadouts.""" - def __init__(self, game_model: GameModel, package_model: PackageModel, - flight: Flight, parent=None) -> None: + def __init__( + self, + game_model: GameModel, + package_model: PackageModel, + flight: Flight, + parent=None, + ) -> None: super().__init__(parent=parent) self.game_model = game_model @@ -25,8 +30,7 @@ class QEditFlightDialog(QDialog): layout = QVBoxLayout() - self.flight_planner = QFlightPlanner(package_model, flight, - game_model.game) + self.flight_planner = QFlightPlanner(package_model, flight, game_model.game) layout.addWidget(self.flight_planner) self.setLayout(layout) diff --git a/qt_ui/windows/mission/QFlightItem.py b/qt_ui/windows/mission/QFlightItem.py index 3ba73a64..d8d9cb44 100644 --- a/qt_ui/windows/mission/QFlightItem.py +++ b/qt_ui/windows/mission/QFlightItem.py @@ -9,13 +9,15 @@ from qt_ui.uiconstants import AIRCRAFT_ICONS # TODO: Replace with QFlightList. class QFlightItem(QStandardItem): - def __init__(self, package: Package, flight: Flight): super(QFlightItem, self).__init__() self.package = package self.flight = flight - if db.unit_type_name(self.flight.unit_type).replace("/", " ") in AIRCRAFT_ICONS.keys(): + if ( + db.unit_type_name(self.flight.unit_type).replace("/", " ") + in AIRCRAFT_ICONS.keys() + ): icon = QIcon((AIRCRAFT_ICONS[db.unit_type_name(self.flight.unit_type)])) self.setIcon(icon) self.setEditable(False) diff --git a/qt_ui/windows/mission/QPackageDialog.py b/qt_ui/windows/mission/QPackageDialog.py index 10acc4fe..3497140f 100644 --- a/qt_ui/windows/mission/QPackageDialog.py +++ b/qt_ui/windows/mission/QPackageDialog.py @@ -60,9 +60,9 @@ class QPackageDialog(QDialog): self.package_type_label = QLabel("Package Type:") self.package_type_text = QLabel(self.package_model.description) # noinspection PyUnresolvedReferences - self.package_changed.connect(lambda: self.package_type_text.setText( - self.package_model.description - )) + self.package_changed.connect( + lambda: self.package_type_text.setText(self.package_model.description) + ) self.package_type_column.addWidget(self.package_type_label) self.package_type_column.addWidget(self.package_type_text) @@ -91,7 +91,9 @@ class QPackageDialog(QDialog): self.auto_asap.toggled.connect(self.set_asap) self.tot_column.addWidget(self.auto_asap) - self.tot_help_label = QLabel("Help") + self.tot_help_label = QLabel( + 'Help' + ) self.tot_help_label.setAlignment(Qt.AlignCenter) self.tot_help_label.setOpenExternalLinks(True) self.tot_column.addWidget(self.tot_help_label) @@ -159,16 +161,17 @@ class QPackageDialog(QDialog): def update_tot(self) -> None: self.tot_spinner.setTime(self.tot_qtime()) - def on_selection_changed(self, selected: QItemSelection, - _deselected: QItemSelection) -> None: + def on_selection_changed( + self, selected: QItemSelection, _deselected: QItemSelection + ) -> None: """Updates the state of the delete button.""" self.delete_flight_button.setEnabled(not selected.empty()) def on_add_flight(self) -> None: """Opens the new flight dialog.""" - self.add_flight_dialog = QFlightCreator(self.game, - self.package_model.package, - parent=self.window()) + self.add_flight_dialog = QFlightCreator( + self.game, self.package_model.package, parent=self.window() + ) self.add_flight_dialog.created.connect(self.add_flight) self.add_flight_dialog.show() @@ -176,8 +179,9 @@ class QPackageDialog(QDialog): """Adds the new flight to the package.""" self.game.aircraft_inventory.claim_for_flight(flight) self.package_model.add_flight(flight) - planner = FlightPlanBuilder(self.game, self.package_model.package, - is_player=True) + planner = FlightPlanBuilder( + self.game, self.package_model.package, is_player=True + ) try: planner.populate_flight_plan(flight) except PlanningError as ex: @@ -209,8 +213,9 @@ class QNewPackageDialog(QPackageDialog): New packages do not affect the ATO model until they are saved. """ - def __init__(self, game_model: GameModel, model: AtoModel, - target: MissionTarget, parent=None) -> None: + def __init__( + self, game_model: GameModel, model: AtoModel, target: MissionTarget, parent=None + ) -> None: super().__init__(game_model, PackageModel(Package(target)), parent=parent) self.ato_model = model @@ -240,8 +245,9 @@ class QEditPackageDialog(QPackageDialog): Changes to existing packages occur immediately. """ - def __init__(self, game_model: GameModel, model: AtoModel, - package: PackageModel) -> None: + def __init__( + self, game_model: GameModel, model: AtoModel, package: PackageModel + ) -> None: super().__init__(game_model, package) self.ato_model = model diff --git a/qt_ui/windows/mission/QPlannedFlightsView.py b/qt_ui/windows/mission/QPlannedFlightsView.py index 1ca6e845..302003ad 100644 --- a/qt_ui/windows/mission/QPlannedFlightsView.py +++ b/qt_ui/windows/mission/QPlannedFlightsView.py @@ -8,7 +8,6 @@ from game.theater.controlpoint import ControlPoint class QPlannedFlightsView(QListView): - def __init__(self, game_model: GameModel, cp: ControlPoint) -> None: super(QPlannedFlightsView, self).__init__() self.game_model = game_model diff --git a/qt_ui/windows/mission/flight/QFlightCreator.py b/qt_ui/windows/mission/flight/QFlightCreator.py index eafd3c90..f4aa743c 100644 --- a/qt_ui/windows/mission/flight/QFlightCreator.py +++ b/qt_ui/windows/mission/flight/QFlightCreator.py @@ -20,8 +20,7 @@ from qt_ui.uiconstants import EVENT_ICONS from qt_ui.widgets.QFlightSizeSpinner import QFlightSizeSpinner from qt_ui.widgets.QLabeledWidget import QLabeledWidget from qt_ui.widgets.combos.QAircraftTypeSelector import QAircraftTypeSelector -from qt_ui.widgets.combos.QArrivalAirfieldSelector import \ - QArrivalAirfieldSelector +from qt_ui.widgets.combos.QArrivalAirfieldSelector import QArrivalAirfieldSelector from qt_ui.widgets.combos.QFlightTypeComboBox import QFlightTypeComboBox from qt_ui.widgets.combos.QOriginAirfieldSelector import QOriginAirfieldSelector @@ -42,26 +41,24 @@ class QFlightCreator(QDialog): layout = QVBoxLayout() - self.task_selector = QFlightTypeComboBox( - self.game.theater, package.target - ) + self.task_selector = QFlightTypeComboBox(self.game.theater, package.target) self.task_selector.setCurrentIndex(0) - self.task_selector.currentTextChanged.connect( - self.on_task_changed) + self.task_selector.currentTextChanged.connect(self.on_task_changed) layout.addLayout(QLabeledWidget("Task:", self.task_selector)) self.aircraft_selector = QAircraftTypeSelector( - self.game.aircraft_inventory.available_types_for_player, self.game.player_country, self.task_selector.currentData() + self.game.aircraft_inventory.available_types_for_player, + self.game.player_country, + self.task_selector.currentData(), ) self.aircraft_selector.setCurrentIndex(0) - self.aircraft_selector.currentIndexChanged.connect( - self.on_aircraft_changed) + self.aircraft_selector.currentIndexChanged.connect(self.on_aircraft_changed) layout.addLayout(QLabeledWidget("Aircraft:", self.aircraft_selector)) self.departure = QOriginAirfieldSelector( self.game.aircraft_inventory, [cp for cp in game.theater.controlpoints if cp.captured], - self.aircraft_selector.currentData() + self.aircraft_selector.currentData(), ) self.departure.availability_changed.connect(self.update_max_size) self.departure.currentIndexChanged.connect(self.on_departure_changed) @@ -70,14 +67,14 @@ class QFlightCreator(QDialog): self.arrival = QArrivalAirfieldSelector( [cp for cp in game.theater.controlpoints if cp.captured], self.aircraft_selector.currentData(), - "Same as departure" + "Same as departure", ) layout.addLayout(QLabeledWidget("Arrival:", self.arrival)) self.divert = QArrivalAirfieldSelector( [cp for cp in game.theater.controlpoints if cp.captured], self.aircraft_selector.currentData(), - "None" + "None", ) layout.addLayout(QLabeledWidget("Divert:", self.divert)) @@ -86,15 +83,12 @@ class QFlightCreator(QDialog): layout.addLayout(QLabeledWidget("Size:", self.flight_size_spinner)) self.client_slots_spinner = QFlightSizeSpinner( - min_size=0, - max_size=self.flight_size_spinner.value(), - default_size=0 + min_size=0, max_size=self.flight_size_spinner.value(), default_size=0 ) self.flight_size_spinner.valueChanged.connect( lambda v: self.client_slots_spinner.setMaximum(v) ) - layout.addLayout( - QLabeledWidget("Client Slots:", self.client_slots_spinner)) + layout.addLayout(QLabeledWidget("Client Slots:", self.client_slots_spinner)) # When an off-map spawn overrides the start type to in-flight, we save # the selected type into this value. If a non-off-map spawn is selected @@ -103,14 +97,20 @@ class QFlightCreator(QDialog): self.start_type = QComboBox() self.start_type.addItems(["Cold", "Warm", "Runway", "In Flight"]) self.start_type.setCurrentText(self.game.settings.default_start_type) - layout.addLayout(QLabeledWidget( - "Start type:", self.start_type, - tooltip="Selects the start type for this flight.")) - layout.addWidget(QLabel( - "Any option other than Cold will make this flight " + - "non-targetable
by OCA/Aircraft missions. This will affect " + - "game balance." - )) + layout.addLayout( + QLabeledWidget( + "Start type:", + self.start_type, + tooltip="Selects the start type for this flight.", + ) + ) + layout.addWidget( + QLabel( + "Any option other than Cold will make this flight " + + "non-targetable
by OCA/Aircraft missions. This will affect " + + "game balance." + ) + ) self.custom_name = QLineEdit() self.custom_name.textChanged.connect(self.set_custom_name_text) @@ -159,8 +159,7 @@ class QFlightCreator(QDialog): def create_flight(self) -> None: error = self.verify_form() if error is not None: - QMessageBox.critical(self, "Could not create flight", error, - QMessageBox.Ok) + QMessageBox.critical(self, "Could not create flight", error, QMessageBox.Ok) return task = self.task_selector.currentData() @@ -173,9 +172,18 @@ class QFlightCreator(QDialog): if arrival is None: arrival = origin - flight = Flight(self.package, self.country, aircraft, size, task, - self.start_type.currentText(), origin, arrival, divert, - custom_name=self.custom_name_text) + flight = Flight( + self.package, + self.country, + aircraft, + size, + task, + self.start_type.currentText(), + origin, + arrival, + divert, + custom_name=self.custom_name_text, + ) flight.client_count = self.client_slots_spinner.value() # noinspection PyUnresolvedReferences @@ -203,7 +211,10 @@ class QFlightCreator(QDialog): self.restore_start_type = None def on_task_changed(self) -> None: - self.aircraft_selector.updateItems(self.task_selector.currentData(), self.game.aircraft_inventory.available_types_for_player) + self.aircraft_selector.updateItems( + self.task_selector.currentData(), + self.game.aircraft_inventory.available_types_for_player, + ) def update_max_size(self, available: int) -> None: self.flight_size_spinner.setMaximum(min(available, 4)) diff --git a/qt_ui/windows/mission/flight/QFlightPlanner.py b/qt_ui/windows/mission/flight/QFlightPlanner.py index 71904b48..0c1b9e03 100644 --- a/qt_ui/windows/mission/flight/QFlightPlanner.py +++ b/qt_ui/windows/mission/flight/QFlightPlanner.py @@ -3,16 +3,14 @@ from PySide2.QtWidgets import QTabWidget from game import Game from gen.flights.flight import Flight from qt_ui.models import PackageModel -from qt_ui.windows.mission.flight.payload.QFlightPayloadTab import \ - QFlightPayloadTab -from qt_ui.windows.mission.flight.settings.QGeneralFlightSettingsTab import \ - QGeneralFlightSettingsTab -from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import \ - QFlightWaypointTab +from qt_ui.windows.mission.flight.payload.QFlightPayloadTab import QFlightPayloadTab +from qt_ui.windows.mission.flight.settings.QGeneralFlightSettingsTab import ( + QGeneralFlightSettingsTab, +) +from qt_ui.windows.mission.flight.waypoints.QFlightWaypointTab import QFlightWaypointTab class QFlightPlanner(QTabWidget): - def __init__(self, package_model: PackageModel, flight: Flight, game: Game): super().__init__() @@ -20,8 +18,7 @@ class QFlightPlanner(QTabWidget): game, package_model, flight ) self.payload_tab = QFlightPayloadTab(flight, game) - self.waypoint_tab = QFlightWaypointTab(game, package_model.package, - flight) + self.waypoint_tab = QFlightWaypointTab(game, package_model.package, flight) self.addTab(self.general_settings_tab, "General Flight settings") self.addTab(self.payload_tab, "Payload") self.addTab(self.waypoint_tab, "Waypoints") diff --git a/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py b/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py index 9ad63330..b61fe432 100644 --- a/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py +++ b/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py @@ -7,7 +7,6 @@ from qt_ui.windows.mission.flight.payload.QLoadoutEditor import QLoadoutEditor class QFlightPayloadTab(QFrame): - def __init__(self, flight: Flight, game: Game): super(QFlightPayloadTab, self).__init__() self.flight = flight @@ -18,7 +17,9 @@ class QFlightPayloadTab(QFrame): layout = QGridLayout() # Docs Link - docsText = QLabel("How to create your own default loadout") + docsText = QLabel( + 'How to create your own default loadout' + ) docsText.setAlignment(Qt.AlignCenter) docsText.setOpenExternalLinks(True) diff --git a/qt_ui/windows/mission/flight/payload/QLoadoutEditor.py b/qt_ui/windows/mission/flight/payload/QLoadoutEditor.py index 75c0f7ec..4d675e42 100644 --- a/qt_ui/windows/mission/flight/payload/QLoadoutEditor.py +++ b/qt_ui/windows/mission/flight/payload/QLoadoutEditor.py @@ -13,7 +13,6 @@ from qt_ui.windows.mission.flight.payload.QPylonEditor import QPylonEditor class QLoadoutEditor(QGroupBox): - def __init__(self, flight: Flight, game: Game) -> None: super().__init__("Use custom loadout") self.flight = flight @@ -28,8 +27,7 @@ class QLoadoutEditor(QGroupBox): for i, pylon in enumerate(Pylon.iter_pylons(self.flight.unit_type)): label = QLabel(f"{pylon.number}") - label.setSizePolicy( - QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) + label.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) layout.addWidget(label, i, 0) layout.addWidget(QPylonEditor(game, flight, pylon), i, 1) diff --git a/qt_ui/windows/mission/flight/payload/QPylonEditor.py b/qt_ui/windows/mission/flight/payload/QPylonEditor.py index 9d95bc8c..eb0314cb 100644 --- a/qt_ui/windows/mission/flight/payload/QPylonEditor.py +++ b/qt_ui/windows/mission/flight/payload/QPylonEditor.py @@ -11,7 +11,6 @@ from dcs import weapons_data class QPylonEditor(QComboBox): - def __init__(self, game: Game, flight: Flight, pylon: Pylon) -> None: super().__init__() self.flight = flight @@ -30,7 +29,7 @@ class QPylonEditor(QComboBox): self.addItem(weapon.name, weapon) if current == weapon: self.setCurrentIndex(i + 1) - + self.currentIndexChanged.connect(self.on_pylon_change) def on_pylon_change(self): @@ -40,8 +39,7 @@ class QPylonEditor(QComboBox): if selected is None: logging.debug(f"Pylon {self.pylon.number} emptied") else: - logging.debug( - f"Pylon {self.pylon.number} changed to {selected.name}") + logging.debug(f"Pylon {self.pylon.number} changed to {selected.name}") def default_loadout(self) -> None: self.flight.unit_type.load_payloads() @@ -51,7 +49,9 @@ class QPylonEditor(QComboBox): loadout = None # Iterate through each possible payload type for a given aircraft. # Some aircraft have custom loadouts that in aren't the standard set. - for payload_override in db.EXPANDED_TASK_PAYLOAD_OVERRIDE.get(self.flight.flight_type.name): + for payload_override in db.EXPANDED_TASK_PAYLOAD_OVERRIDE.get( + self.flight.flight_type.name + ): if loadout is None: loadout = self.flight.unit_type.loadout_by_name(payload_override) if loadout is not None: @@ -75,6 +75,12 @@ class QPylonEditor(QComboBox): else: historical_weapon = orig_weapon if historical_weapon is not None: - self.setCurrentText(weapons_data.weapon_ids.get(historical_weapon.cls_id).get("name")) + self.setCurrentText( + weapons_data.weapon_ids.get(historical_weapon.cls_id).get( + "name" + ) + ) else: - self.setCurrentText(weapons_data.weapon_ids.get(pylon_default_weapon).get("name")) \ No newline at end of file + self.setCurrentText( + weapons_data.weapon_ids.get(pylon_default_weapon).get("name") + ) diff --git a/qt_ui/windows/mission/flight/settings/FlightAirfieldDisplay.py b/qt_ui/windows/mission/flight/settings/FlightAirfieldDisplay.py index 4b56cf1b..282df1ce 100644 --- a/qt_ui/windows/mission/flight/settings/FlightAirfieldDisplay.py +++ b/qt_ui/windows/mission/flight/settings/FlightAirfieldDisplay.py @@ -1,6 +1,6 @@ import logging -from PySide2.QtWidgets import (QGroupBox, QLabel, QMessageBox, QVBoxLayout) +from PySide2.QtWidgets import QGroupBox, QLabel, QMessageBox, QVBoxLayout from game import Game from gen.flights.flight import Flight @@ -8,14 +8,11 @@ from gen.flights.flightplan import FlightPlanBuilder, PlanningError from gen.flights.traveltime import TotEstimator from qt_ui.models import PackageModel from qt_ui.widgets.QLabeledWidget import QLabeledWidget -from qt_ui.widgets.combos.QArrivalAirfieldSelector import \ - QArrivalAirfieldSelector +from qt_ui.widgets.combos.QArrivalAirfieldSelector import QArrivalAirfieldSelector class FlightAirfieldDisplay(QGroupBox): - - def __init__(self, game: Game, package_model: PackageModel, - flight: Flight) -> None: + def __init__(self, game: Game, package_model: PackageModel, flight: Flight) -> None: super().__init__("Departure/Arrival") self.game = game self.package_model = package_model @@ -24,18 +21,25 @@ class FlightAirfieldDisplay(QGroupBox): layout = QVBoxLayout() self.departure_time = QLabel() - layout.addLayout(QLabeledWidget( - f"Departing from {flight.from_cp.name}", - self.departure_time)) + layout.addLayout( + QLabeledWidget( + f"Departing from {flight.from_cp.name}", self.departure_time + ) + ) self.package_model.tot_changed.connect(self.update_departure_time) self.update_departure_time() - layout.addWidget(QLabel("Determined based on the package TOT. Edit the " - "package to adjust the TOT.")) + layout.addWidget( + QLabel( + "Determined based on the package TOT. Edit the " + "package to adjust the TOT." + ) + ) self.arrival = QArrivalAirfieldSelector( [cp for cp in game.theater.controlpoints if cp.captured], - flight.unit_type, "Same as departure" + flight.unit_type, + "Same as departure", ) self.arrival.currentIndexChanged.connect(self.set_arrival) if flight.arrival != flight.departure: @@ -44,7 +48,8 @@ class FlightAirfieldDisplay(QGroupBox): self.divert = QArrivalAirfieldSelector( [cp for cp in game.theater.controlpoints if cp.captured], - flight.unit_type, "None" + flight.unit_type, + "None", ) self.divert.currentIndexChanged.connect(self.set_divert) if flight.divert is not None: @@ -94,6 +99,7 @@ class FlightAirfieldDisplay(QGroupBox): ) def update_flight_plan(self) -> None: - planner = FlightPlanBuilder(self.game, self.package_model.package, - is_player=True) + planner = FlightPlanBuilder( + self.game, self.package_model.package, is_player=True + ) planner.populate_flight_plan(self.flight) diff --git a/qt_ui/windows/mission/flight/settings/QCustomName.py b/qt_ui/windows/mission/flight/settings/QCustomName.py index 7a22e48d..f0e04130 100644 --- a/qt_ui/windows/mission/flight/settings/QCustomName.py +++ b/qt_ui/windows/mission/flight/settings/QCustomName.py @@ -4,7 +4,6 @@ from gen.flights.flight import Flight class QFlightCustomName(QGroupBox): - def __init__(self, flight: Flight): super(QFlightCustomName, self).__init__() diff --git a/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py b/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py index f21b07b0..bf078bfe 100644 --- a/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py +++ b/qt_ui/windows/mission/flight/settings/QFlightSlotEditor.py @@ -17,9 +17,7 @@ class QFlightSlotEditor(QGroupBox): self.package_model = package_model self.flight = flight self.game = game - self.inventory = self.game.aircraft_inventory.for_control_point( - flight.from_cp - ) + self.inventory = self.game.aircraft_inventory.for_control_point(flight.from_cp) available = self.inventory.available(self.flight.unit_type) max_count = self.flight.count + available if max_count > 4: @@ -67,7 +65,8 @@ class QFlightSlotEditor(QGroupBox): logging.error( f"Could not add {difference} additional aircraft to " f"{self.flight} because {self.flight.from_cp} has only " - f"{available} {self.flight.unit_type} remaining") + f"{available} {self.flight.unit_type} remaining" + ) self.flight.count = old_count self.game.aircraft_inventory.claim_for_flight(self.flight) self.changed.emit() @@ -81,4 +80,4 @@ class QFlightSlotEditor(QGroupBox): def _cap_client_count(self): if self.flight.client_count > self.flight.count: self.flight.client_count = self.flight.count - self.client_count_spinner.setValue(self.flight.client_count) \ No newline at end of file + self.client_count_spinner.setValue(self.flight.client_count) diff --git a/qt_ui/windows/mission/flight/settings/QFlightStartType.py b/qt_ui/windows/mission/flight/settings/QFlightStartType.py index 17fbe042..ccab3deb 100644 --- a/qt_ui/windows/mission/flight/settings/QFlightStartType.py +++ b/qt_ui/windows/mission/flight/settings/QFlightStartType.py @@ -11,7 +11,6 @@ from qt_ui.models import PackageModel class QFlightStartType(QGroupBox): - def __init__(self, package_model: PackageModel, flight: Flight): super().__init__() self.package_model = package_model @@ -32,10 +31,12 @@ class QFlightStartType(QGroupBox): self.main_row.addWidget(self.start_type) self.layout.addLayout(self.main_row) - self.layout.addWidget(QLabel( - "Any option other than Cold will make this flight non-targetable " + - "by OCA/Aircraft missions. This will affect game balance." - )) + self.layout.addWidget( + QLabel( + "Any option other than Cold will make this flight non-targetable " + + "by OCA/Aircraft missions. This will affect game balance." + ) + ) self.setLayout(self.layout) def _on_start_type_selected(self): diff --git a/qt_ui/windows/mission/flight/settings/QFlightTypeTaskInfo.py b/qt_ui/windows/mission/flight/settings/QFlightTypeTaskInfo.py index 76e7f815..c05a0fa6 100644 --- a/qt_ui/windows/mission/flight/settings/QFlightTypeTaskInfo.py +++ b/qt_ui/windows/mission/flight/settings/QFlightTypeTaskInfo.py @@ -5,7 +5,6 @@ from qt_ui.uiconstants import AIRCRAFT_ICONS class QFlightTypeTaskInfo(QGroupBox): - def __init__(self, flight): super(QFlightTypeTaskInfo, self).__init__("Flight") self.flight = flight @@ -14,7 +13,9 @@ class QFlightTypeTaskInfo(QGroupBox): self.aircraft_icon = QLabel() if db.unit_type_name(self.flight.unit_type) in AIRCRAFT_ICONS: - self.aircraft_icon.setPixmap(AIRCRAFT_ICONS[db.unit_type_name(self.flight.unit_type)]) + self.aircraft_icon.setPixmap( + AIRCRAFT_ICONS[db.unit_type_name(self.flight.unit_type)] + ) self.task = QLabel("Task:") self.task_type = QLabel(str(flight.flight_type)) diff --git a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py index 804fe8dc..0b207356 100644 --- a/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py +++ b/qt_ui/windows/mission/flight/settings/QGeneralFlightSettingsTab.py @@ -4,16 +4,15 @@ from PySide2.QtWidgets import QFrame, QGridLayout, QVBoxLayout from game import Game from gen.flights.flight import Flight from qt_ui.models import PackageModel -from qt_ui.windows.mission.flight.settings.FlightAirfieldDisplay import \ - FlightAirfieldDisplay -from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import \ - QFlightSlotEditor -from qt_ui.windows.mission.flight.settings.QFlightStartType import \ - QFlightStartType -from qt_ui.windows.mission.flight.settings.QFlightTypeTaskInfo import \ - QFlightTypeTaskInfo -from qt_ui.windows.mission.flight.settings.QCustomName import \ - QFlightCustomName +from qt_ui.windows.mission.flight.settings.FlightAirfieldDisplay import ( + FlightAirfieldDisplay, +) +from qt_ui.windows.mission.flight.settings.QFlightSlotEditor import QFlightSlotEditor +from qt_ui.windows.mission.flight.settings.QFlightStartType import QFlightStartType +from qt_ui.windows.mission.flight.settings.QFlightTypeTaskInfo import ( + QFlightTypeTaskInfo, +) +from qt_ui.windows.mission.flight.settings.QCustomName import QFlightCustomName class QGeneralFlightSettingsTab(QFrame): @@ -24,8 +23,7 @@ class QGeneralFlightSettingsTab(QFrame): layout = QGridLayout() layout.addWidget(QFlightTypeTaskInfo(flight), 0, 0) - layout.addWidget(FlightAirfieldDisplay(game, package_model, flight), 1, - 0) + layout.addWidget(FlightAirfieldDisplay(game, package_model, flight), 1, 0) layout.addWidget(QFlightSlotEditor(package_model, flight, game), 2, 0) layout.addWidget(QFlightStartType(package_model, flight), 3, 0) layout.addWidget(QFlightCustomName(flight), 4, 0) diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py index 137b70b7..29834c01 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py @@ -4,12 +4,11 @@ from gen.flights.flight import FlightWaypoint class QFlightWaypointInfoBox(QGroupBox): - - def __init__(self, flight_wpt:FlightWaypoint = None): + def __init__(self, flight_wpt: FlightWaypoint = None): super(QFlightWaypointInfoBox, self).__init__("Waypoint") self.flight_wpt = flight_wpt if flight_wpt is None: - self.flight_wpt = FlightWaypoint(0,0,0) + self.flight_wpt = FlightWaypoint(0, 0, 0) self.x_position_label = QLabel(str(self.flight_wpt.x)) self.y_position_label = QLabel(str(self.flight_wpt.y)) self.alt_label = QLabel(str(int(self.flight_wpt.alt.feet))) @@ -46,7 +45,7 @@ class QFlightWaypointInfoBox(QGroupBox): desc_layout.addWidget(self.desc_label) desc_layout.addStretch() - #layout.addLayout(name_layout) + # layout.addLayout(name_layout) layout.addLayout(x_pos_layout) layout.addLayout(y_pos_layout) layout.addLayout(alt_layout) @@ -54,10 +53,10 @@ class QFlightWaypointInfoBox(QGroupBox): self.setLayout(layout) - def set_flight_waypoint(self, flight_wpt:FlightWaypoint): + def set_flight_waypoint(self, flight_wpt: FlightWaypoint): self.flight_wpt = flight_wpt if flight_wpt is None: - self.flight_wpt = FlightWaypoint(0,0,0) + self.flight_wpt = FlightWaypoint(0, 0, 0) self.x_position_label.setText(str(self.flight_wpt.x)) self.y_position_label.setText(str(self.flight_wpt.y)) self.alt_label.setText(str(int(self.flight_wpt.alt.feet))) diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py index d3c596b1..c970bb94 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py @@ -6,10 +6,8 @@ from gen.flights.flight import FlightWaypoint class QWaypointItem(QStandardItem): - def __init__(self, point: FlightWaypoint, number): super(QWaypointItem, self).__init__() self.number = number - self.setText('{:<16}'.format(point.pretty_name)) + self.setText("{:<16}".format(point.pretty_name)) self.setEditable(False) - diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py index 28c63d59..b9ba7444 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py @@ -6,12 +6,10 @@ from PySide2.QtWidgets import QHeaderView, QTableView from gen.ato import Package from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType -from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import \ - QWaypointItem +from qt_ui.windows.mission.flight.waypoints.QFlightWaypointItem import QWaypointItem class QFlightWaypointList(QTableView): - def __init__(self, package: Package, flight: Flight): super().__init__() self.package = package @@ -28,7 +26,9 @@ class QFlightWaypointList(QTableView): self.selectedPoint = self.flight.points[0] self.update_list() - self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) + self.selectionModel().setCurrentIndex( + self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select + ) def update_list(self): self.model.clear() @@ -38,16 +38,18 @@ class QFlightWaypointList(QTableView): waypoints = self.flight.flight_plan.waypoints for row, waypoint in enumerate(waypoints): self.add_waypoint_row(row, self.flight, waypoint) - self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), - QItemSelectionModel.Select) + self.selectionModel().setCurrentIndex( + self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select + ) self.resizeColumnsToContents() total_column_width = self.verticalHeader().width() + self.lineWidth() for i in range(0, self.model.columnCount()): total_column_width += self.columnWidth(i) + self.lineWidth() self.setFixedWidth(total_column_width) - def add_waypoint_row(self, row: int, flight: Flight, - waypoint: FlightWaypoint) -> None: + def add_waypoint_row( + self, row: int, flight: Flight, waypoint: FlightWaypoint + ) -> None: self.model.insertRow(self.model.rowCount()) self.model.setItem(row, 0, QWaypointItem(waypoint, row)) diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py index abd1733d..95ede450 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py @@ -20,15 +20,15 @@ from gen.flights.flightplan import ( PlanningError, StrikeFlightPlan, ) -from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import \ - QFlightWaypointList -from qt_ui.windows.mission.flight.waypoints \ - .QPredefinedWaypointSelectionWindow import \ - QPredefinedWaypointSelectionWindow +from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import ( + QFlightWaypointList, +) +from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import ( + QPredefinedWaypointSelectionWindow, +) class QFlightWaypointTab(QFrame): - def __init__(self, game: Game, package: Package, flight: Flight): super(QFlightWaypointTab, self).__init__() self.game = game @@ -46,8 +46,7 @@ class QFlightWaypointTab(QFrame): def init_ui(self): layout = QGridLayout() - self.flight_waypoint_list = QFlightWaypointList(self.package, - self.flight) + self.flight_waypoint_list = QFlightWaypointList(self.package, self.flight) layout.addWidget(self.flight_waypoint_list, 0, 0) rlayout = QVBoxLayout() @@ -58,10 +57,13 @@ class QFlightWaypointTab(QFrame): self.recreate_buttons.clear() for task in self.package.target.mission_types(for_player=True): + def make_closure(arg): def closure(): return self.confirm_recreate(arg) + return closure + button = QPushButton(f"Recreate as {task}") button.clicked.connect(make_closure(task)) rlayout.addWidget(button) @@ -106,7 +108,9 @@ class QFlightWaypointTab(QFrame): self.flight.flight_plan.custom_waypoints.remove(waypoint) def on_fast_waypoint(self): - self.subwindow = QPredefinedWaypointSelectionWindow(self.game, self.flight, self.flight_waypoint_list) + self.subwindow = QPredefinedWaypointSelectionWindow( + self.game, self.flight, self.flight_waypoint_list + ) self.subwindow.waypoints_added.connect(self.on_waypoints_added) self.subwindow.show() @@ -120,8 +124,7 @@ class QFlightWaypointTab(QFrame): self.on_change() def on_rtb_waypoint(self): - rtb = self.planner.generate_rtb_waypoint(self.flight, - self.flight.from_cp) + rtb = self.planner.generate_rtb_waypoint(self.flight, self.flight.from_cp) self.degrade_to_custom_flight_plan() assert isinstance(self.flight.flight_plan, CustomFlightPlan) self.flight.flight_plan.custom_waypoints.append(rtb) @@ -133,17 +136,19 @@ class QFlightWaypointTab(QFrame): self.flight.flight_plan = CustomFlightPlan( package=self.flight.package, flight=self.flight, - custom_waypoints=self.flight.flight_plan.waypoints + custom_waypoints=self.flight.flight_plan.waypoints, ) def confirm_recreate(self, task: FlightType) -> None: result = QMessageBox.question( self, "Regenerate flight?", - ("Changing the flight type will reset its flight plan. Do you want " - "to continue?"), + ( + "Changing the flight type will reset its flight plan. Do you want " + "to continue?" + ), QMessageBox.No, - QMessageBox.Yes + QMessageBox.Yes, ) original_task = self.flight.flight_type if result == QMessageBox.Yes: diff --git a/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py b/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py index ccec5034..5bc71271 100644 --- a/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py +++ b/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py @@ -11,16 +11,18 @@ from PySide2.QtWidgets import ( from game import Game from gen.flights.flight import Flight from qt_ui.uiconstants import EVENT_ICONS -from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import \ - QPredefinedWaypointSelectionComboBox -from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import \ - QFlightWaypointInfoBox +from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import ( + QPredefinedWaypointSelectionComboBox, +) +from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import ( + QFlightWaypointInfoBox, +) PREDEFINED_WAYPOINT_CATEGORIES = [ "Frontline (CAS AREA)", "Building", "Units", - "Airbase" + "Airbase", ] @@ -56,7 +58,6 @@ class QPredefinedWaypointSelectionWindow(QDialog): self.init_ui() self.on_select_wpt_changed() - def init_ui(self): layout = QVBoxLayout() @@ -79,7 +80,9 @@ class QPredefinedWaypointSelectionWindow(QDialog): self.setLayout(layout) def on_select_wpt_changed(self): - self.selected_waypoints = self.wpt_selection_box.get_selected_waypoints(self.include_all.isChecked()) + self.selected_waypoints = self.wpt_selection_box.get_selected_waypoints( + self.include_all.isChecked() + ) if self.selected_waypoints is None or len(self.selected_waypoints) <= 0: self.add_button.setDisabled(True) else: diff --git a/qt_ui/windows/newgame/QCampaignList.py b/qt_ui/windows/newgame/QCampaignList.py index 7c9e7cec..b97f2ef0 100644 --- a/qt_ui/windows/newgame/QCampaignList.py +++ b/qt_ui/windows/newgame/QCampaignList.py @@ -19,6 +19,7 @@ PERF_MEDIUM = 1 PERF_HARD = 2 PERF_NASA = 3 + @dataclass(frozen=True) class Campaign: name: str @@ -46,7 +47,7 @@ class Campaign: data.get("recommended_enemy_faction", "Russia 1990"), data.get("performance", 0), data, - path + path, ) def load_theater(self) -> ConflictTheater: @@ -68,7 +69,6 @@ def load_campaigns() -> List[Campaign]: class QCampaignItem(QStandardItem): - def __init__(self, campaign: Campaign) -> None: super(QCampaignItem, self).__init__() self.setIcon(QtGui.QIcon(CONST.ICONS[campaign.icon_name])) @@ -77,7 +77,6 @@ class QCampaignItem(QStandardItem): class QCampaignList(QListView): - def __init__(self, campaigns: List[Campaign]) -> None: super(QCampaignList, self).__init__() self.model = QStandardItemModel(self) @@ -105,4 +104,4 @@ class QCampaignList(QListView): self.repaint() def clear_layout(self): - self.model.removeRows(0, self.model.rowCount()) \ No newline at end of file + self.model.removeRows(0, self.model.rowCount()) diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index 4d7bb5cb..866db587 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -47,8 +47,10 @@ class NewGameWizard(QtWidgets.QWizard): self.addPage(DifficultyAndAutomationOptions()) self.addPage(ConclusionPage()) - self.setPixmap(QtWidgets.QWizard.WatermarkPixmap, - QtGui.QPixmap('./resources/ui/wizard/watermark1.png')) + self.setPixmap( + QtWidgets.QWizard.WatermarkPixmap, + QtGui.QPixmap("./resources/ui/wizard/watermark1.png"), + ) self.setWizardStyle(QtWidgets.QWizard.ModernStyle) self.setWindowTitle("New Game") @@ -63,21 +65,19 @@ class NewGameWizard(QtWidgets.QWizard): campaign = self.campaigns[0] settings = Settings( - player_income_multiplier=self.field( - "player_income_multiplier") / 10, + player_income_multiplier=self.field("player_income_multiplier") / 10, enemy_income_multiplier=self.field("enemy_income_multiplier") / 10, automate_runway_repair=self.field("automate_runway_repairs"), automate_front_line_reinforcements=self.field( "automate_front_line_purchases" ), - automate_aircraft_reinforcements=self.field( - "automate_aircraft_purchases" - ), - supercarrier=self.field("supercarrier") + automate_aircraft_reinforcements=self.field("automate_aircraft_purchases"), + supercarrier=self.field("supercarrier"), ) generator_settings = GeneratorSettings( start_date=db.TIME_PERIODS[ - list(db.TIME_PERIODS.keys())[self.field("timePeriod")]], + list(db.TIME_PERIODS.keys())[self.field("timePeriod")] + ], player_budget=int(self.field("starting_money")), enemy_budget=int(self.field("enemy_starting_money")), # QSlider forces integers, so we use 1 to 50 and divide by 10 to @@ -87,7 +87,7 @@ class NewGameWizard(QtWidgets.QWizard): no_carrier=self.field("no_carrier"), no_lha=self.field("no_lha"), no_player_navy=self.field("no_player_navy"), - no_enemy_navy=self.field("no_enemy_navy") + no_enemy_navy=self.field("no_enemy_navy"), ) blue_faction = [c for c in db.FACTIONS][self.field("blueFaction")] @@ -97,7 +97,7 @@ class NewGameWizard(QtWidgets.QWizard): red_faction, campaign.load_theater(), settings, - generator_settings + generator_settings, ) self.generatedGame = generator.generate() @@ -109,11 +109,15 @@ class IntroPage(QtWidgets.QWizardPage): super(IntroPage, self).__init__(parent) self.setTitle("Introduction") - self.setPixmap(QtWidgets.QWizard.WatermarkPixmap, - QtGui.QPixmap('./resources/ui/wizard/watermark1.png')) + self.setPixmap( + QtWidgets.QWizard.WatermarkPixmap, + QtGui.QPixmap("./resources/ui/wizard/watermark1.png"), + ) - label = QtWidgets.QLabel("This wizard will help you setup a new game.\n\n" - "Please make sure you saved and backed up your previous game before going through.") + label = QtWidgets.QLabel( + "This wizard will help you setup a new game.\n\n" + "Please make sure you saved and backed up your previous game before going through." + ) label.setWordWrap(True) layout = QtWidgets.QVBoxLayout() @@ -126,9 +130,13 @@ class FactionSelection(QtWidgets.QWizardPage): super(FactionSelection, self).__init__(parent) self.setTitle("Faction selection") - self.setSubTitle("\nChoose the two opposing factions and select the player side.") - self.setPixmap(QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap('./resources/ui/misc/generator.png')) + self.setSubTitle( + "\nChoose the two opposing factions and select the player side." + ) + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/misc/generator.png"), + ) self.setMinimumHeight(250) @@ -178,19 +186,21 @@ class FactionSelection(QtWidgets.QWizardPage): # Create required mod layout self.requiredModsGroup = QtWidgets.QGroupBox("Required Mods") self.requiredModsGroupLayout = QtWidgets.QHBoxLayout() - self.requiredMods = QtWidgets.QLabel("
  • None
") + self.requiredMods = QtWidgets.QLabel("
  • None
") self.requiredMods.setOpenExternalLinks(True) self.requiredModsGroupLayout.addWidget(self.requiredMods) self.requiredModsGroup.setLayout(self.requiredModsGroupLayout) # Docs Link - docsText = QtWidgets.QLabel("How to create your own faction") + docsText = QtWidgets.QLabel( + 'How to create your own faction' + ) docsText.setAlignment(Qt.AlignCenter) docsText.setOpenExternalLinks(True) # Link form fields - self.registerField('blueFaction', self.blueFactionSelect) - self.registerField('redFaction', self.redFactionSelect) + self.registerField("blueFaction", self.blueFactionSelect) + self.registerField("redFaction", self.redFactionSelect) # Build layout layout = QtWidgets.QVBoxLayout() @@ -203,8 +213,7 @@ class FactionSelection(QtWidgets.QWizardPage): self.blueFactionSelect.activated.connect(self.updateUnitRecap) self.redFactionSelect.activated.connect(self.updateUnitRecap) - - def setDefaultFactions(self, campaign:Campaign): + def setDefaultFactions(self, campaign: Campaign): """ Set default faction for selected campaign """ self.blueFactionSelect.clear() @@ -242,16 +251,30 @@ class FactionSelection(QtWidgets.QWizardPage): has_mod = True for mod in red_faction.requirements.keys(): self.requiredMods.setText( - self.requiredMods.text() + "\n
  • " + mod + ": " + - red_faction.requirements[mod] + "
  • ") + self.requiredMods.text() + + "\n
  • " + + mod + + ': ' + + red_faction.requirements[mod] + + "
  • " + ) if len(blue_faction.requirements.keys()) > 0: has_mod = True for mod in blue_faction.requirements.keys(): if mod not in red_faction.requirements.keys(): self.requiredMods.setText( - self.requiredMods.text() + "\n
  • " + mod + ": " + blue_faction.requirements[mod] + "
  • ") + self.requiredMods.text() + + "\n
  • " + + mod + + ': ' + + blue_faction.requirements[mod] + + "
  • " + ) if has_mod: self.requiredMods.setText(self.requiredMods.text() + "\n\n") @@ -260,18 +283,27 @@ class FactionSelection(QtWidgets.QWizardPage): class TheaterConfiguration(QtWidgets.QWizardPage): - def __init__(self, campaigns: List[Campaign], faction_selection: FactionSelection, parent=None) -> None: + def __init__( + self, + campaigns: List[Campaign], + faction_selection: FactionSelection, + parent=None, + ) -> None: super().__init__(parent) self.faction_selection = faction_selection self.setTitle("Theater configuration") self.setSubTitle("\nChoose a terrain and time period for this game.") - self.setPixmap(QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap('./resources/ui/wizard/logo1.png')) + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/wizard/logo1.png"), + ) - self.setPixmap(QtWidgets.QWizard.WatermarkPixmap, - QtGui.QPixmap('./resources/ui/wizard/watermark3.png')) + self.setPixmap( + QtWidgets.QWizard.WatermarkPixmap, + QtGui.QPixmap("./resources/ui/wizard/watermark3.png"), + ) # List of campaigns campaignList = QCampaignList(campaigns) @@ -288,22 +320,28 @@ class TheaterConfiguration(QtWidgets.QWizardPage): def on_campaign_selected(): template = jinja_env.get_template("campaigntemplate_EN.j2") - template_perf = jinja_env.get_template("campaign_performance_template_EN.j2") + template_perf = jinja_env.get_template( + "campaign_performance_template_EN.j2" + ) index = campaignList.selectionModel().currentIndex().row() campaign = campaignList.campaigns[index] self.setField("selectedCampaign", campaign) self.campaignMapDescription.setText(template.render({"campaign": campaign})) self.faction_selection.setDefaultFactions(campaign) - self.performanceText.setText(template_perf.render({"performance": campaign.performance})) + self.performanceText.setText( + template_perf.render({"performance": campaign.performance}) + ) - campaignList.selectionModel().setCurrentIndex(campaignList.indexAt(QPoint(1, 1)), QItemSelectionModel.Rows) + campaignList.selectionModel().setCurrentIndex( + campaignList.indexAt(QPoint(1, 1)), QItemSelectionModel.Rows + ) campaignList.selectionModel().selectionChanged.connect(on_campaign_selected) on_campaign_selected() # Campaign settings mapSettingsGroup = QtWidgets.QGroupBox("Map Settings") invertMap = QtWidgets.QCheckBox() - self.registerField('invertMap', invertMap) + self.registerField("invertMap", invertMap) mapSettingsLayout = QtWidgets.QGridLayout() mapSettingsLayout.addWidget(QtWidgets.QLabel("Invert Map"), 0, 0) mapSettingsLayout.addWidget(invertMap, 0, 1) @@ -319,12 +357,14 @@ class TheaterConfiguration(QtWidgets.QWizardPage): timePeriodSelect.setCurrentIndex(21) # Docs Link - docsText = QtWidgets.QLabel("How to create your own theater") + docsText = QtWidgets.QLabel( + 'How to create your own theater' + ) docsText.setAlignment(Qt.AlignCenter) docsText.setOpenExternalLinks(True) # Register fields - self.registerField('timePeriod', timePeriodSelect) + self.registerField("timePeriod", timePeriodSelect) timeGroupLayout = QtWidgets.QGridLayout() timeGroupLayout.addWidget(timePeriod, 0, 0) @@ -343,9 +383,12 @@ class TheaterConfiguration(QtWidgets.QWizardPage): class CurrencySpinner(QtWidgets.QSpinBox): - def __init__(self, minimum: Optional[int] = None, - maximum: Optional[int] = None, - initial: Optional[int] = None) -> None: + def __init__( + self, + minimum: Optional[int] = None, + maximum: Optional[int] = None, + initial: Optional[int] = None, + ) -> None: super().__init__() if minimum is not None: @@ -385,10 +428,13 @@ class DifficultyAndAutomationOptions(QtWidgets.QWizardPage): super().__init__(parent) self.setTitle("Difficulty and automation options") - self.setSubTitle("\nOptions controlling game difficulty and level of " - "player involvement.") - self.setPixmap(QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap('./resources/ui/wizard/logo1.png')) + self.setSubTitle( + "\nOptions controlling game difficulty and level of " "player involvement." + ) + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/wizard/logo1.png"), + ) layout = QtWidgets.QVBoxLayout() @@ -406,7 +452,7 @@ class DifficultyAndAutomationOptions(QtWidgets.QWizardPage): economy_layout.addLayout(enemy_income) player_budget = BudgetInputs("Player starting budget") - self.registerField('starting_money', player_budget.starting_money) + self.registerField("starting_money", player_budget.starting_money) economy_layout.addLayout(player_budget) enemy_budget = BudgetInputs("Enemy starting budget") @@ -418,20 +464,17 @@ class DifficultyAndAutomationOptions(QtWidgets.QWizardPage): assist_layout = QtWidgets.QGridLayout() assist_group.setLayout(assist_layout) - assist_layout.addWidget( - QtWidgets.QLabel("Automate runway repairs"), 0, 0) + assist_layout.addWidget(QtWidgets.QLabel("Automate runway repairs"), 0, 0) runway_repairs = QtWidgets.QCheckBox() self.registerField("automate_runway_repairs", runway_repairs) assist_layout.addWidget(runway_repairs, 0, 1, Qt.AlignRight) - assist_layout.addWidget( - QtWidgets.QLabel("Automate front-line purchases"), 1, 0) + assist_layout.addWidget(QtWidgets.QLabel("Automate front-line purchases"), 1, 0) front_line = QtWidgets.QCheckBox() self.registerField("automate_front_line_purchases", front_line) assist_layout.addWidget(front_line, 1, 1, Qt.AlignRight) - assist_layout.addWidget( - QtWidgets.QLabel("Automate aircraft purchases"), 2, 0) + assist_layout.addWidget(QtWidgets.QLabel("Automate aircraft purchases"), 2, 0) aircraft = QtWidgets.QCheckBox() self.registerField("automate_aircraft_purchases", aircraft) assist_layout.addWidget(aircraft, 2, 1, Qt.AlignRight) @@ -444,21 +487,23 @@ class GeneratorOptions(QtWidgets.QWizardPage): super().__init__(parent) self.setTitle("Generator settings") self.setSubTitle("\nOptions affecting the generation of the game.") - self.setPixmap(QtWidgets.QWizard.LogoPixmap, - QtGui.QPixmap('./resources/ui/wizard/logo1.png')) + self.setPixmap( + QtWidgets.QWizard.LogoPixmap, + QtGui.QPixmap("./resources/ui/wizard/logo1.png"), + ) # Campaign settings generatorSettingsGroup = QtWidgets.QGroupBox("Generator Settings") no_carrier = QtWidgets.QCheckBox() - self.registerField('no_carrier', no_carrier) + self.registerField("no_carrier", no_carrier) no_lha = QtWidgets.QCheckBox() - self.registerField('no_lha', no_lha) + self.registerField("no_lha", no_lha) supercarrier = QtWidgets.QCheckBox() - self.registerField('supercarrier', supercarrier) + self.registerField("supercarrier", supercarrier) no_player_navy = QtWidgets.QCheckBox() - self.registerField('no_player_navy', no_player_navy) + self.registerField("no_player_navy", no_player_navy) no_enemy_navy = QtWidgets.QCheckBox() - self.registerField('no_enemy_navy', no_enemy_navy) + self.registerField("no_enemy_navy", no_enemy_navy) generatorLayout = QtWidgets.QGridLayout() generatorLayout.addWidget(QtWidgets.QLabel("No Aircraft Carriers"), 1, 0) @@ -484,10 +529,14 @@ class ConclusionPage(QtWidgets.QWizardPage): self.setTitle("Conclusion") self.setSubTitle("\n\n") - self.setPixmap(QtWidgets.QWizard.WatermarkPixmap, - QtGui.QPixmap('./resources/ui/wizard/watermark2.png')) + self.setPixmap( + QtWidgets.QWizard.WatermarkPixmap, + QtGui.QPixmap("./resources/ui/wizard/watermark2.png"), + ) - self.label = QtWidgets.QLabel("Click 'Finish' to generate and start the new game.") + self.label = QtWidgets.QLabel( + "Click 'Finish' to generate and start the new game." + ) self.label.setWordWrap(True) layout = QtWidgets.QVBoxLayout() diff --git a/qt_ui/windows/preferences/QLiberationFirstStartWindow.py b/qt_ui/windows/preferences/QLiberationFirstStartWindow.py index f441f2d3..4a300f35 100644 --- a/qt_ui/windows/preferences/QLiberationFirstStartWindow.py +++ b/qt_ui/windows/preferences/QLiberationFirstStartWindow.py @@ -1,11 +1,17 @@ from PySide2.QtGui import QIcon, Qt -from PySide2.QtWidgets import QDialog, QVBoxLayout, QPushButton, QHBoxLayout, QPlainTextEdit, QTextEdit +from PySide2.QtWidgets import ( + QDialog, + QVBoxLayout, + QPushButton, + QHBoxLayout, + QPlainTextEdit, + QTextEdit, +) from qt_ui.windows.preferences.QLiberationPreferences import QLiberationPreferences class QLiberationFirstStartWindow(QDialog): - def __init__(self): super(QLiberationFirstStartWindow, self).__init__() @@ -59,7 +65,7 @@ class QLiberationFirstStartWindow(QDialog): self.warning_text = QTextEdit(WARN_TEXT) self.warning_text.setReadOnly(True) self.apply_button = QPushButton("I have read everything and I Accept") - self.apply_button.clicked.connect(lambda : self.apply()) + self.apply_button.clicked.connect(lambda: self.apply()) self.initUI() def initUI(self): @@ -77,4 +83,3 @@ class QLiberationFirstStartWindow(QDialog): print("Applying changes") if self.preferences.apply(): self.close() - diff --git a/qt_ui/windows/preferences/QLiberationPreferences.py b/qt_ui/windows/preferences/QLiberationPreferences.py index 3e8db6a7..0d41b298 100644 --- a/qt_ui/windows/preferences/QLiberationPreferences.py +++ b/qt_ui/windows/preferences/QLiberationPreferences.py @@ -18,7 +18,6 @@ from qt_ui.liberation_theme import THEMES, get_theme_index, set_theme_index class QLiberationPreferences(QFrame): - def __init__(self): super(QLiberationPreferences, self).__init__() self.saved_game_dir = "" @@ -38,17 +37,27 @@ class QLiberationPreferences(QFrame): self.browse_install_dir = QPushButton("Browse...") self.browse_install_dir.clicked.connect(self.on_browse_installation_dir) self.themeSelect = QComboBox() - [self.themeSelect.addItem(y['themeName']) for x, y in THEMES.items()] + [self.themeSelect.addItem(y["themeName"]) for x, y in THEMES.items()] self.initUi() def initUi(self): main_layout = QVBoxLayout() layout = QGridLayout() - layout.addWidget(QLabel("DCS saved game directory:"), 0, 0, alignment=Qt.AlignLeft) + layout.addWidget( + QLabel("DCS saved game directory:"), + 0, + 0, + alignment=Qt.AlignLeft, + ) layout.addWidget(self.edit_saved_game_dir, 1, 0, alignment=Qt.AlignRight) layout.addWidget(self.browse_saved_game, 1, 1, alignment=Qt.AlignRight) - layout.addWidget(QLabel("DCS installation directory:"), 2, 0, alignment=Qt.AlignLeft) + layout.addWidget( + QLabel("DCS installation directory:"), + 2, + 0, + alignment=Qt.AlignLeft, + ) layout.addWidget(self.edit_dcs_install_dir, 3, 0, alignment=Qt.AlignRight) layout.addWidget(self.browse_install_dir, 3, 1, alignment=Qt.AlignRight) layout.addWidget(QLabel("Theme (Requires Restart)"), 4, 0) @@ -61,13 +70,17 @@ class QLiberationPreferences(QFrame): self.setLayout(main_layout) def on_browse_saved_games(self): - saved_game_dir = str(QFileDialog.getExistingDirectory(self, "Select DCS Saved Game Directory")) + saved_game_dir = str( + QFileDialog.getExistingDirectory(self, "Select DCS Saved Game Directory") + ) if saved_game_dir: self.saved_game_dir = saved_game_dir self.edit_saved_game_dir.setText(saved_game_dir) def on_browse_installation_dir(self): - install_dir = str(QFileDialog.getExistingDirectory(self, "Select DCS Installation Directory")) + install_dir = str( + QFileDialog.getExistingDirectory(self, "Select DCS Installation Directory") + ) if install_dir: self.dcs_install_dir = install_dir self.edit_dcs_install_dir.setText(install_dir) @@ -80,24 +93,34 @@ class QLiberationPreferences(QFrame): set_theme_index(self.themeSelect.currentIndex()) if not os.path.isdir(self.saved_game_dir): - error_dialog = QMessageBox.critical(self, "Wrong DCS Saved Games directory.", - self.saved_game_dir + " is not a valid directory", - QMessageBox.StandardButton.Ok) + error_dialog = QMessageBox.critical( + self, + "Wrong DCS Saved Games directory.", + self.saved_game_dir + " is not a valid directory", + QMessageBox.StandardButton.Ok, + ) error_dialog.exec_() return False if not os.path.isdir(self.dcs_install_dir): - error_dialog = QMessageBox.critical(self, "Wrong DCS installation directory.", - self.dcs_install_dir + " is not a valid directory", - QMessageBox.StandardButton.Ok) + error_dialog = QMessageBox.critical( + self, + "Wrong DCS installation directory.", + self.dcs_install_dir + " is not a valid directory", + QMessageBox.StandardButton.Ok, + ) error_dialog.exec_() return False - if not os.path.isdir(os.path.join(self.dcs_install_dir, "Scripts")) and os.path.isfile( - os.path.join(self.dcs_install_dir, "bin", "DCS.exe")): - error_dialog = QMessageBox.critical(self, "Wrong DCS installation directory.", - self.dcs_install_dir + " is not a valid DCS installation directory", - QMessageBox.StandardButton.Ok) + if not os.path.isdir( + os.path.join(self.dcs_install_dir, "Scripts") + ) and os.path.isfile(os.path.join(self.dcs_install_dir, "bin", "DCS.exe")): + error_dialog = QMessageBox.critical( + self, + "Wrong DCS installation directory.", + self.dcs_install_dir + " is not a valid DCS installation directory", + QMessageBox.StandardButton.Ok, + ) error_dialog.exec_() return False diff --git a/qt_ui/windows/preferences/QLiberationPreferencesWindow.py b/qt_ui/windows/preferences/QLiberationPreferencesWindow.py index ce5a65ff..e23a3c08 100644 --- a/qt_ui/windows/preferences/QLiberationPreferencesWindow.py +++ b/qt_ui/windows/preferences/QLiberationPreferencesWindow.py @@ -5,7 +5,6 @@ from qt_ui.windows.preferences.QLiberationPreferences import QLiberationPreferen class QLiberationPreferencesWindow(QDialog): - def __init__(self): super(QLiberationPreferencesWindow, self).__init__() @@ -15,7 +14,7 @@ class QLiberationPreferencesWindow(QDialog): self.setWindowIcon(QIcon("./resources/icon.png")) self.preferences = QLiberationPreferences() self.apply_button = QPushButton("Apply") - self.apply_button.clicked.connect(lambda : self.apply()) + self.apply_button.clicked.connect(lambda: self.apply()) self.initUI() def initUI(self): @@ -34,4 +33,3 @@ class QLiberationPreferencesWindow(QDialog): self.close() else: print("Not Closing") - diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index 3f39e6a1..af9f6d3f 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -46,14 +46,20 @@ class CheatSettingsBox(QGroupBox): self.frontline_cheat_checkbox.toggled.connect(apply_settings) self.base_capture_cheat_checkbox = QCheckBox() - self.base_capture_cheat_checkbox.setChecked(game.settings.enable_base_capture_cheat) + self.base_capture_cheat_checkbox.setChecked( + game.settings.enable_base_capture_cheat + ) self.base_capture_cheat_checkbox.toggled.connect(apply_settings) self.red_ato = QLabeledWidget("Show Red ATO:", self.red_ato_checkbox) self.main_layout.addLayout(self.red_ato) - self.frontline_cheat = QLabeledWidget("Enable Frontline Cheats:", self.frontline_cheat_checkbox) + self.frontline_cheat = QLabeledWidget( + "Enable Frontline Cheats:", self.frontline_cheat_checkbox + ) self.main_layout.addLayout(self.frontline_cheat) - self.base_capture_cheat = QLabeledWidget("Enable Base Capture Cheat:", self.base_capture_cheat_checkbox) + self.base_capture_cheat = QLabeledWidget( + "Enable Base Capture Cheat:", self.base_capture_cheat_checkbox + ) self.main_layout.addLayout(self.base_capture_cheat) @property @@ -85,7 +91,6 @@ class StartTypeComboBox(QComboBox): class QSettingsWindow(QDialog): - def __init__(self, game: Game): super(QSettingsWindow, self).__init__() @@ -163,9 +168,12 @@ class QSettingsWindow(QDialog): self.categoryList.setSelectionBehavior(QAbstractItemView.SelectRows) self.categoryList.setModel(self.categoryModel) - self.categoryList.selectionModel().setCurrentIndex(self.categoryList.indexAt(QPoint(1,1)), QItemSelectionModel.Select) - self.categoryList.selectionModel().selectionChanged.connect(self.onSelectionChanged) - + self.categoryList.selectionModel().setCurrentIndex( + self.categoryList.indexAt(QPoint(1, 1)), QItemSelectionModel.Select + ) + self.categoryList.selectionModel().selectionChanged.connect( + self.onSelectionChanged + ) self.layout.addWidget(self.categoryList, 0, 0, 1, 1) self.layout.addLayout(self.right_layout, 0, 1, 5, 1) @@ -193,17 +201,29 @@ class QSettingsWindow(QDialog): self.enemyCoalitionSkill.addItem(skill) self.enemyAASkill.addItem(skill) - self.playerCoalitionSkill.setCurrentIndex(CONST.SKILL_OPTIONS.index(self.game.settings.player_skill)) - self.enemyCoalitionSkill.setCurrentIndex(CONST.SKILL_OPTIONS.index(self.game.settings.enemy_skill)) - self.enemyAASkill.setCurrentIndex(CONST.SKILL_OPTIONS.index(self.game.settings.enemy_vehicle_skill)) + self.playerCoalitionSkill.setCurrentIndex( + CONST.SKILL_OPTIONS.index(self.game.settings.player_skill) + ) + self.enemyCoalitionSkill.setCurrentIndex( + CONST.SKILL_OPTIONS.index(self.game.settings.enemy_skill) + ) + self.enemyAASkill.setCurrentIndex( + CONST.SKILL_OPTIONS.index(self.game.settings.enemy_vehicle_skill) + ) self.player_income = TenthsSpinSlider( - "Player income multiplier", 1, 50, - int(self.game.settings.player_income_multiplier * 10)) + "Player income multiplier", + 1, + 50, + int(self.game.settings.player_income_multiplier * 10), + ) self.player_income.spinner.valueChanged.connect(self.applySettings) self.enemy_income = TenthsSpinSlider( - "Enemy income multiplier", 1, 50, - int(self.game.settings.enemy_income_multiplier * 10)) + "Enemy income multiplier", + 1, + 50, + int(self.game.settings.enemy_income_multiplier * 10), + ) self.enemy_income.spinner.valueChanged.connect(self.applySettings) self.playerCoalitionSkill.currentIndexChanged.connect(self.applySettings) @@ -228,7 +248,9 @@ class QSettingsWindow(QDialog): self.difficultyLabel = QComboBox() [self.difficultyLabel.addItem(t) for t in CONST.LABELS_OPTIONS] - self.difficultyLabel.setCurrentIndex(CONST.LABELS_OPTIONS.index(self.game.settings.labels)) + self.difficultyLabel.setCurrentIndex( + CONST.LABELS_OPTIONS.index(self.game.settings.labels) + ) self.difficultyLabel.currentIndexChanged.connect(self.applySettings) self.mapVisibiitySelection = QComboBox() @@ -238,11 +260,21 @@ class QSettingsWindow(QDialog): self.mapVisibiitySelection.addItem("Fog of War", ForcedOptions.Views.Allies) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.Allies: self.mapVisibiitySelection.setCurrentIndex(1) - self.mapVisibiitySelection.addItem("Allies Only", ForcedOptions.Views.OnlyAllies) - if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyAllies: + self.mapVisibiitySelection.addItem( + "Allies Only", ForcedOptions.Views.OnlyAllies + ) + if ( + self.game.settings.map_coalition_visibility + == ForcedOptions.Views.OnlyAllies + ): self.mapVisibiitySelection.setCurrentIndex(2) - self.mapVisibiitySelection.addItem("Own Aircraft Only", ForcedOptions.Views.MyAircraft) - if self.game.settings.map_coalition_visibility == ForcedOptions.Views.MyAircraft: + self.mapVisibiitySelection.addItem( + "Own Aircraft Only", ForcedOptions.Views.MyAircraft + ) + if ( + self.game.settings.map_coalition_visibility + == ForcedOptions.Views.MyAircraft + ): self.mapVisibiitySelection.setCurrentIndex(3) self.mapVisibiitySelection.addItem("Map Only", ForcedOptions.Views.OnlyMap) if self.game.settings.map_coalition_visibility == ForcedOptions.Views.OnlyMap: @@ -254,7 +286,9 @@ class QSettingsWindow(QDialog): self.ext_views.toggled.connect(self.applySettings) self.aiDifficultyLayout.addWidget(QLabel("Player coalition skill"), 0, 0) - self.aiDifficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1, Qt.AlignRight) + self.aiDifficultyLayout.addWidget( + self.playerCoalitionSkill, 0, 1, Qt.AlignRight + ) self.aiDifficultyLayout.addWidget(QLabel("Enemy coalition skill"), 1, 0) self.aiDifficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1, Qt.AlignRight) self.aiDifficultyLayout.addWidget(QLabel("Enemy AA and vehicles skill"), 2, 0) @@ -272,9 +306,13 @@ class QSettingsWindow(QDialog): self.difficultyLayout.addWidget(self.missionSettings) self.missionRestrictionsLayout.addWidget(QLabel("In Game Labels"), 0, 0) - self.missionRestrictionsLayout.addWidget(self.difficultyLabel, 0, 1, Qt.AlignRight) + self.missionRestrictionsLayout.addWidget( + self.difficultyLabel, 0, 1, Qt.AlignRight + ) self.missionRestrictionsLayout.addWidget(QLabel("Map visibility options"), 1, 0) - self.missionRestrictionsLayout.addWidget(self.mapVisibiitySelection, 1, 1, Qt.AlignRight) + self.missionRestrictionsLayout.addWidget( + self.mapVisibiitySelection, 1, 1, Qt.AlignRight + ) self.missionRestrictionsLayout.addWidget(QLabel("Allow external views"), 2, 0) self.missionRestrictionsLayout.addWidget(self.ext_views, 2, 1, Qt.AlignRight) self.missionRestrictionsSettings.setLayout(self.missionRestrictionsLayout) @@ -323,9 +361,7 @@ class QSettingsWindow(QDialog): ) old_awac.setToolTip(old_awec_info) - old_awac_label = QLabel( - "Disable invulnerable, always-available AEW&C (WIP)" - ) + old_awac_label = QLabel("Disable invulnerable, always-available AEW&C (WIP)") old_awac_label.setToolTip(old_awec_info) general_layout.addWidget(old_awac_label, 1, 0) @@ -347,25 +383,21 @@ class QSettingsWindow(QDialog): self.game.settings.automate_aircraft_reinforcements = value runway_repair = QCheckBox() - runway_repair.setChecked( - self.game.settings.automate_runway_repair) + runway_repair.setChecked(self.game.settings.automate_runway_repair) runway_repair.toggled.connect(set_runway_automation) automation_layout.addWidget(QLabel("Automate runway repairs"), 0, 0) automation_layout.addWidget(runway_repair, 0, 1, Qt.AlignRight) front_line = QCheckBox() - front_line.setChecked( - self.game.settings.automate_front_line_reinforcements) + front_line.setChecked(self.game.settings.automate_front_line_reinforcements) front_line.toggled.connect(set_front_line_automation) - automation_layout.addWidget( - QLabel("Automate front-line purchases"), 1, 0) + automation_layout.addWidget(QLabel("Automate front-line purchases"), 1, 0) automation_layout.addWidget(front_line, 1, 1, Qt.AlignRight) aircraft = QCheckBox() - aircraft.setChecked( - self.game.settings.automate_aircraft_reinforcements) + aircraft.setChecked(self.game.settings.automate_aircraft_reinforcements) aircraft.toggled.connect(set_aircraft_automation) automation_layout.addWidget(QLabel("Automate aircraft purchases"), 2, 0) @@ -392,7 +424,8 @@ class QSettingsWindow(QDialog): self.never_delay_players = QCheckBox() self.never_delay_players.setChecked( - self.game.settings.never_delay_player_flights) + self.game.settings.never_delay_player_flights + ) self.never_delay_players.toggled.connect(self.applySettings) self.never_delay_players.setToolTip( "When checked, player flights with a delayed start time will be " @@ -403,14 +436,12 @@ class QSettingsWindow(QDialog): self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight) self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0) self.gameplayLayout.addWidget(self.generate_marks, 1, 1, Qt.AlignRight) - self.gameplayLayout.addWidget( - QLabel("Never delay player flights"), 2, 0) - self.gameplayLayout.addWidget(self.never_delay_players, 2, 1, - Qt.AlignRight) + self.gameplayLayout.addWidget(QLabel("Never delay player flights"), 2, 0) + self.gameplayLayout.addWidget(self.never_delay_players, 2, 1, Qt.AlignRight) start_type_label = QLabel( - "Default start type for AI aircraft:
    Warning: " + - "Any option other than Cold breaks OCA/Aircraft missions." + "Default start type for AI aircraft:
    Warning: " + + "Any option other than Cold breaks OCA/Aircraft missions." ) start_type_label.setToolTip(START_TYPE_TOOLTIP) start_type = StartTypeComboBox(self.game.settings) @@ -459,35 +490,58 @@ class QSettingsWindow(QDialog): self.culling_distance.valueChanged.connect(self.applySettings) self.culling_do_not_cull_carrier = QCheckBox() - self.culling_do_not_cull_carrier.setChecked(self.game.settings.perf_do_not_cull_carrier) + self.culling_do_not_cull_carrier.setChecked( + self.game.settings.perf_do_not_cull_carrier + ) self.culling_do_not_cull_carrier.toggled.connect(self.applySettings) - self.performanceLayout.addWidget(QLabel("Smoke visual effect on frontline"), 0, 0) + self.performanceLayout.addWidget( + QLabel("Smoke visual effect on frontline"), 0, 0 + ) self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("SAM starts in RED alert mode"), 1, 0) self.performanceLayout.addWidget(self.red_alert, 1, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Artillery strikes"), 2, 0) self.performanceLayout.addWidget(self.arti, 2, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Moving ground units"), 3, 0) - self.performanceLayout.addWidget(self.moving_units, 3, 1, alignment=Qt.AlignRight) - self.performanceLayout.addWidget(QLabel("Generate infantry squads along vehicles"), 4, 0) + self.performanceLayout.addWidget( + self.moving_units, 3, 1, alignment=Qt.AlignRight + ) + self.performanceLayout.addWidget( + QLabel("Generate infantry squads along vehicles"), 4, 0 + ) self.performanceLayout.addWidget(self.infantry, 4, 1, alignment=Qt.AlignRight) - self.performanceLayout.addWidget(QLabel("Include destroyed units carcass"), 6, 0) - self.performanceLayout.addWidget(self.destroyed_units, 6, 1, alignment=Qt.AlignRight) + self.performanceLayout.addWidget( + QLabel("Include destroyed units carcass"), 6, 0 + ) + self.performanceLayout.addWidget( + self.destroyed_units, 6, 1, alignment=Qt.AlignRight + ) self.performanceLayout.addWidget(QHorizontalSeparationLine(), 7, 0, 1, 2) - self.performanceLayout.addWidget(QLabel("Culling of distant units enabled"), 8, 0) + self.performanceLayout.addWidget( + QLabel("Culling of distant units enabled"), 8, 0 + ) self.performanceLayout.addWidget(self.culling, 8, 1, alignment=Qt.AlignRight) self.performanceLayout.addWidget(QLabel("Culling distance (km)"), 9, 0) - self.performanceLayout.addWidget(self.culling_distance, 9, 1, alignment=Qt.AlignRight) - self.performanceLayout.addWidget(QLabel("Do not cull carrier's surroundings"), 10, 0) - self.performanceLayout.addWidget(self.culling_do_not_cull_carrier, 10, 1, alignment=Qt.AlignRight) + self.performanceLayout.addWidget( + self.culling_distance, 9, 1, alignment=Qt.AlignRight + ) + self.performanceLayout.addWidget( + QLabel("Do not cull carrier's surroundings"), 10, 0 + ) + self.performanceLayout.addWidget( + self.culling_do_not_cull_carrier, 10, 1, alignment=Qt.AlignRight + ) self.generatorLayout.addWidget(self.gameplay) - self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience.")) + self.generatorLayout.addWidget( + QLabel( + "Disabling settings below may improve performance, but will impact the overall quality of the experience." + ) + ) self.generatorLayout.addWidget(self.performance) - def initCheatLayout(self): self.cheatPage = QWidget() @@ -511,7 +565,7 @@ class QSettingsWindow(QDialog): btn = QPushButton("Cheat " + str(amount) + "M") btn.setProperty("style", "btn-danger") btn.clicked.connect(self.cheatLambda(amount)) - self.moneyCheatBoxLayout.addWidget(btn, i/2, i%2) + self.moneyCheatBoxLayout.addWidget(btn, i / 2, i % 2) self.cheatLayout.addWidget(self.moneyCheatBox, stretch=1) def cheatLambda(self, amount): @@ -521,24 +575,44 @@ class QSettingsWindow(QDialog): logging.info("CHEATING FOR AMOUNT : " + str(amount) + "M") self.game.budget += amount if amount > 0: - self.game.informations.append(Information("CHEATER", "You are a cheater and you should feel bad", self.game.turn)) + self.game.informations.append( + Information( + "CHEATER", + "You are a cheater and you should feel bad", + self.game.turn, + ) + ) else: - self.game.informations.append(Information("CHEATER", "You are still a cheater !", self.game.turn)) + self.game.informations.append( + Information("CHEATER", "You are still a cheater !", self.game.turn) + ) GameUpdateSignal.get_instance().updateGame(self.game) def applySettings(self): - self.game.settings.player_skill = CONST.SKILL_OPTIONS[self.playerCoalitionSkill.currentIndex()] - self.game.settings.enemy_skill = CONST.SKILL_OPTIONS[self.enemyCoalitionSkill.currentIndex()] - self.game.settings.enemy_vehicle_skill = CONST.SKILL_OPTIONS[self.enemyAASkill.currentIndex()] + self.game.settings.player_skill = CONST.SKILL_OPTIONS[ + self.playerCoalitionSkill.currentIndex() + ] + self.game.settings.enemy_skill = CONST.SKILL_OPTIONS[ + self.enemyCoalitionSkill.currentIndex() + ] + self.game.settings.enemy_vehicle_skill = CONST.SKILL_OPTIONS[ + self.enemyAASkill.currentIndex() + ] self.game.settings.player_income_multiplier = self.player_income.value self.game.settings.enemy_income_multiplier = self.enemy_income.value self.game.settings.manpads = self.manpads.isChecked() - self.game.settings.labels = CONST.LABELS_OPTIONS[self.difficultyLabel.currentIndex()] + self.game.settings.labels = CONST.LABELS_OPTIONS[ + self.difficultyLabel.currentIndex() + ] self.game.settings.night_disabled = self.noNightMission.isChecked() - self.game.settings.map_coalition_visibility = self.mapVisibiitySelection.currentData() + self.game.settings.map_coalition_visibility = ( + self.mapVisibiitySelection.currentData() + ) self.game.settings.external_views_allowed = self.ext_views.isChecked() self.game.settings.generate_marks = self.generate_marks.isChecked() - self.game.settings.never_delay_player_flights = self.never_delay_players.isChecked() + self.game.settings.never_delay_player_flights = ( + self.never_delay_players.isChecked() + ) self.game.settings.supercarrier = self.supercarrier.isChecked() @@ -551,15 +625,21 @@ class QSettingsWindow(QDialog): self.game.settings.perf_culling = self.culling.isChecked() self.game.settings.perf_culling_distance = int(self.culling_distance.value()) - self.game.settings.perf_do_not_cull_carrier = self.culling_do_not_cull_carrier.isChecked() + self.game.settings.perf_do_not_cull_carrier = ( + self.culling_do_not_cull_carrier.isChecked() + ) self.game.settings.show_red_ato = self.cheat_options.show_red_ato - self.game.settings.enable_frontline_cheats = self.cheat_options.show_frontline_cheat - self.game.settings.enable_base_capture_cheat = self.cheat_options.show_base_capture_cheat + self.game.settings.enable_frontline_cheats = ( + self.cheat_options.show_frontline_cheat + ) + self.game.settings.enable_base_capture_cheat = ( + self.cheat_options.show_base_capture_cheat + ) self.game.compute_conflicts_position() GameUpdateSignal.get_instance().updateGame(self.game) def onSelectionChanged(self): index = self.categoryList.selectionModel().currentIndex().row() - self.right_layout.setCurrentIndex(index) \ No newline at end of file + self.right_layout.setCurrentIndex(index) diff --git a/qt_ui/windows/settings/plugins.py b/qt_ui/windows/settings/plugins.py index ca3f6e35..f4973f77 100644 --- a/qt_ui/windows/settings/plugins.py +++ b/qt_ui/windows/settings/plugins.py @@ -3,7 +3,8 @@ from PySide2.QtWidgets import ( QCheckBox, QGridLayout, QGroupBox, - QLabel, QVBoxLayout, + QLabel, + QVBoxLayout, QWidget, ) diff --git a/qt_ui/windows/stats/QAircraftChart.py b/qt_ui/windows/stats/QAircraftChart.py index e1c4db14..6c8d1db9 100644 --- a/qt_ui/windows/stats/QAircraftChart.py +++ b/qt_ui/windows/stats/QAircraftChart.py @@ -6,7 +6,6 @@ from game import Game class QAircraftChart(QFrame): - def __init__(self, game: Game): super(QAircraftChart, self).__init__() self.game = game @@ -19,8 +18,12 @@ class QAircraftChart(QFrame): def generateUnitCharts(self): - self.alliedAircraft = [d.allied_units.aircraft_count for d in self.game.game_stats.data_per_turn] - self.enemyAircraft = [d.enemy_units.aircraft_count for d in self.game.game_stats.data_per_turn] + self.alliedAircraft = [ + d.allied_units.aircraft_count for d in self.game.game_stats.data_per_turn + ] + self.enemyAircraft = [ + d.enemy_units.aircraft_count for d in self.game.game_stats.data_per_turn + ] self.alliedAircraftSerie = QtCharts.QLineSeries() self.alliedAircraftSerie.setName("Allied aircraft count") @@ -40,7 +43,9 @@ class QAircraftChart(QFrame): self.chart.createDefaultAxes() self.chart.axisX().setRange(0, len(self.alliedAircraft)) - self.chart.axisY().setRange(0, max(max(self.alliedAircraft), max(self.enemyAircraft)) + 10) + self.chart.axisY().setRange( + 0, max(max(self.alliedAircraft), max(self.enemyAircraft)) + 10 + ) self.chartView = QtCharts.QChartView(self.chart) self.chartView.setRenderHint(QPainter.Antialiasing) diff --git a/qt_ui/windows/stats/QArmorChart.py b/qt_ui/windows/stats/QArmorChart.py index cf008ab5..09c272fa 100644 --- a/qt_ui/windows/stats/QArmorChart.py +++ b/qt_ui/windows/stats/QArmorChart.py @@ -6,7 +6,6 @@ from game import Game class QArmorChart(QFrame): - def __init__(self, game: Game): super(QArmorChart, self).__init__() self.game = game @@ -19,8 +18,12 @@ class QArmorChart(QFrame): def generateUnitCharts(self): - self.alliedArmor = [d.allied_units.vehicles_count for d in self.game.game_stats.data_per_turn] - self.enemyArmor = [d.enemy_units.vehicles_count for d in self.game.game_stats.data_per_turn] + self.alliedArmor = [ + d.allied_units.vehicles_count for d in self.game.game_stats.data_per_turn + ] + self.enemyArmor = [ + d.enemy_units.vehicles_count for d in self.game.game_stats.data_per_turn + ] self.alliedArmorSerie = QtCharts.QLineSeries() self.alliedArmorSerie.setName("Allied vehicle count") @@ -40,7 +43,9 @@ class QArmorChart(QFrame): self.chart.createDefaultAxes() self.chart.axisX().setRange(0, len(self.alliedArmor)) - self.chart.axisY().setRange(0, max(max(self.alliedArmor), max(self.enemyArmor)) + 10) + self.chart.axisY().setRange( + 0, max(max(self.alliedArmor), max(self.enemyArmor)) + 10 + ) self.chartView = QtCharts.QChartView(self.chart) self.chartView.setRenderHint(QPainter.Antialiasing) diff --git a/qt_ui/windows/stats/QStatsWindow.py b/qt_ui/windows/stats/QStatsWindow.py index a7cf34de..7d4fda07 100644 --- a/qt_ui/windows/stats/QStatsWindow.py +++ b/qt_ui/windows/stats/QStatsWindow.py @@ -7,7 +7,6 @@ from qt_ui.windows.stats.QArmorChart import QArmorChart class QStatsWindow(QDialog): - def __init__(self, game: Game): super(QStatsWindow, self).__init__() diff --git a/requirements.txt b/requirements.txt index 702ac87f..f537f6cd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,10 +1,31 @@ -Pyside2>=5.15.2 -pyinstaller==3.6 - -Pillow~=7.2.0 -tabulate~=0.8.7 - +altgraph==0.17 +appdirs==1.4.4 +black==20.8b1 +cfgv==3.2.0 +click==7.1.2 +distlib==0.3.1 +filelock==3.0.12 +future==0.18.2 +identify==1.5.13 +Jinja2==2.11.3 +MarkupSafe==1.1.1 mypy==0.782 mypy-extensions==0.4.3 -jinja2>=2.11.2 -shapely==1.7.1 +nodeenv==1.5.0 +pathspec==0.8.1 +pefile==2019.4.18 +Pillow==7.2.0 +pre-commit==2.10.1 +PyInstaller==3.6 +PySide2==5.15.2 +pywin32-ctypes==0.2.0 +PyYAML==5.4.1 +regex==2020.11.13 +Shapely==1.7.1 +shiboken2==5.15.2 +six==1.15.0 +tabulate==0.8.7 +toml==0.10.2 +typed-ast==1.4.2 +typing-extensions==3.7.4.3 +virtualenv==20.4.2 \ No newline at end of file diff --git a/resources/tools/generate_frontlines.py b/resources/tools/generate_frontlines.py index c1346bc9..46f429fe 100644 --- a/resources/tools/generate_frontlines.py +++ b/resources/tools/generate_frontlines.py @@ -12,14 +12,17 @@ Terrain = Union[Caucasus, PersianGulf, Syria, Nevada, Normandy, TheChannel] SAVE_PATH = Path("resources/frontlines") + def validate_miz(file_path: Path) -> bool: return bool(file_path.suffix == ".miz" and file_path.exists()) + def validate_airports(airports: Tuple[int], terrain: Terrain): for airport in airports: if terrain.airport_by_id(airport) is None: print(f"Cannot load airport for invalid id {airport}") + def load_files(files) -> List[Mission]: missions = [] for file in files: @@ -31,26 +34,30 @@ def load_files(files) -> List[Mission]: print(f"Error: {file} doesn't look like a valid mission file.") return missions + def create_frontline_dict(mission: Mission) -> Dict[str, Dict]: frontline_dict = {} for group in mission.country("USA").vehicle_group: - groupname = str(group.name).replace(group.name.id, "").replace(":","") + groupname = str(group.name).replace(group.name.id, "").replace(":", "") control_points = groupname.split("|") frontline_dict[groupname] = { "points": [(i.position.x, i.position.y) for i in group.points], - "start_cp": int(control_points[0]) - } + "start_cp": int(control_points[0]), + } return frontline_dict + def process_missions(missions: List[Mission]) -> None: for mission in missions: frontline_dict = create_frontline_dict(mission) write_json(frontline_dict, mission.terrain.name.lower()) + def write_json(frontline_dict: Dict[str, Dict], terrain_name: str) -> None: with open(SAVE_PATH.joinpath(terrain_name + ".json"), "w") as file: json.dump(frontline_dict, file) + if __name__ == "__main__": parser = argparse.ArgumentParser( description="Process a miz file to create json descriptions of multi-segment frontlines" @@ -69,6 +76,3 @@ if __name__ == "__main__": # frontline_dict = create_frontline_dict(missions[0]) print("Done") - - - diff --git a/resources/tools/generate_groundobject_templates.py b/resources/tools/generate_groundobject_templates.py index fce50a0f..6e443aea 100644 --- a/resources/tools/generate_groundobject_templates.py +++ b/resources/tools/generate_groundobject_templates.py @@ -13,7 +13,10 @@ def load_templates(): groups = {} # type: typing.Dict[str, typing.Dict[int, typing.List[Static]]] - for static_group in temp_mis.country("USA").static_group + temp_mis.country("USAF Aggressors").static_group: + for static_group in ( + temp_mis.country("USA").static_group + + temp_mis.country("USAF Aggressors").static_group + ): for static in static_group.units: static_name = str(static.name).split()[0] tpl_name, tpl_idx = static_name[:-1], int(static_name[-1]) @@ -34,13 +37,19 @@ def load_templates(): a = aa b = bb - center = a.position.point_from_heading(a.position.heading_between_point(b.position), dist / 2) + center = a.position.point_from_heading( + a.position.heading_between_point(b.position), dist / 2 + ) for static in static_groups: - tpls[category_name][idx].append({ - "type": static.type, - "offset": Point(center.x - static.position.x, center.y - static.position.y), - "heading": static.heading, - }) + tpls[category_name][idx].append( + { + "type": static.type, + "offset": Point( + center.x - static.position.x, center.y - static.position.y + ), + "heading": static.heading, + } + ) tpls["aa"] = {0: [{"type": "AA", "offset": Point(0, 0), "heading": 0}]} return tpls diff --git a/resources/tools/generate_landmap.py b/resources/tools/generate_landmap.py index a23c7fd1..b61bcc80 100644 --- a/resources/tools/generate_landmap.py +++ b/resources/tools/generate_landmap.py @@ -11,7 +11,8 @@ from game.theater.landmap import Landmap @singledispatch def to_multipoly(obj) -> MultiPolygon: raise NotImplementedError( - f"to_multipoly not implemented for {obj.__class__.__name__}") + f"to_multipoly not implemented for {obj.__class__.__name__}" + ) @to_multipoly.register @@ -28,8 +29,7 @@ def _multipoly_to_multipoly(obj: MultiPolygon) -> MultiPolygon: def _geometry_collection_to_multipoly(obj: GeometryCollection) -> MultiPolygon: if obj.is_empty: return MultiPolygon() - raise RuntimeError( - f"Not sure how to convert collection to multipoly: {obj.wkt}") + raise RuntimeError(f"Not sure how to convert collection to multipoly: {obj.wkt}") for terrain in ["cau", "nev", "syria", "channel", "normandy", "gulf"]: @@ -61,6 +61,11 @@ for terrain in ["cau", "nev", "syria", "channel", "normandy", "gulf"]: with open("../{}landmap.p".format(terrain), "wb") as f: print(len(inclusion_zones), len(exclusion_zones), len(seas_zones)) - pickle.dump(Landmap(to_multipoly(unary_union(inclusion_zones)), - to_multipoly(unary_union(exclusion_zones)), - to_multipoly(unary_union(seas_zones))), f) + pickle.dump( + Landmap( + to_multipoly(unary_union(inclusion_zones)), + to_multipoly(unary_union(exclusion_zones)), + to_multipoly(unary_union(seas_zones)), + ), + f, + ) diff --git a/resources/tools/import_beacons.py b/resources/tools/import_beacons.py index 8e1506ce..9342f9a1 100644 --- a/resources/tools/import_beacons.py +++ b/resources/tools/import_beacons.py @@ -70,13 +70,19 @@ def beacons_from_terrain(dcs_path: Path, path: Path) -> Iterable[Beacon]: with cd(dcs_path): lua = lupa.LuaRuntime() - lua.execute(textwrap.dedent("""\ + lua.execute( + textwrap.dedent( + """\ function module(name) end - """)) + """ + ) + ) - bind_gettext = lua.eval(textwrap.dedent("""\ + bind_gettext = lua.eval( + textwrap.dedent( + """\ function(py_gettext) package.preload["i_18n"] = function() return { @@ -85,16 +91,20 @@ def beacons_from_terrain(dcs_path: Path, path: Path) -> Iterable[Beacon]: end end - """)) + """ + ) + ) try: translator = gettext.translation( - "messages", path / "l10n", languages=["en"]) + "messages", path / "l10n", languages=["en"] + ) def translate(message_name: str) -> str: if not message_name: return message_name return translator.gettext(message_name) + except FileNotFoundError: # TheChannel has no locale data for English. def translate(message_name: str) -> str: @@ -126,7 +136,7 @@ def beacons_from_terrain(dcs_path: Path, path: Path) -> Iterable[Beacon]: beacon["callsign"], beacon_type, convert_lua_frequency(beacon["frequency"]), - getattr(beacon, "channel", None) + getattr(beacon, "channel", None), ) @@ -152,9 +162,10 @@ class Importer: def export_beacons(self, terrain: str, beacons: Iterable[Beacon]) -> None: terrain_py_path = self.export_dir / f"{terrain.lower()}.json" import json - terrain_py_path.write_text(json.dumps([ - dataclasses.asdict(b) for b in beacons - ], indent=True)) + + terrain_py_path.write_text( + json.dumps([dataclasses.asdict(b) for b in beacons], indent=True) + ) def parse_args() -> argparse.Namespace: @@ -169,13 +180,14 @@ def parse_args() -> argparse.Namespace: "--export-to", type=resolved_path, default=EXPORT_DIR, - help="Output directory for generated JSON files.") + help="Output directory for generated JSON files.", + ) parser.add_argument( "dcs_path", metavar="DCS_PATH", type=resolved_path, - help="Path to DCS installation." + help="Path to DCS installation.", ) return parser.parse_args() diff --git a/resources/tools/mkrelease.py b/resources/tools/mkrelease.py index a76029b9..5d70e86d 100644 --- a/resources/tools/mkrelease.py +++ b/resources/tools/mkrelease.py @@ -36,7 +36,13 @@ def _zip_dir(archieve, path): def _mk_archieve(): - path = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, "build", "dcs_liberation_{}.zip".format(VERSION)) + path = os.path.join( + os.path.dirname(__file__), + os.pardir, + os.pardir, + "build", + "dcs_liberation_{}.zip".format(VERSION), + ) if os.path.exists(path): print("version already exists") return @@ -46,9 +52,9 @@ def _mk_archieve(): except FileNotFoundError: pass os.system("pyinstaller.exe --clean pyinstaller.spec") - #archieve = ZipFile(path, "w") - #archieve.writestr("dcs_liberation.bat", "cd dist\\dcs_liberation\r\nliberation_main \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION)) - #_zip_dir(archieve, "./dist/dcs_liberation") + # archieve = ZipFile(path, "w") + # archieve.writestr("dcs_liberation.bat", "cd dist\\dcs_liberation\r\nliberation_main \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION)) + # _zip_dir(archieve, "./dist/dcs_liberation") _mk_archieve() diff --git a/tests/test_factions.py b/tests/test_factions.py index f802dbf0..bc6d7af3 100644 --- a/tests/test_factions.py +++ b/tests/test_factions.py @@ -3,10 +3,31 @@ from pathlib import Path import unittest from dcs.helicopters import UH_1H, AH_64A -from dcs.planes import F_15C, F_15E, F_14B, FA_18C_hornet, F_16C_50, A_10A, AV8BNA, B_52H, B_1B, F_117A, MQ_9_Reaper, \ - E_3A, KC130, KC_135, A_10C, A_10C_2 -from dcs.ships import CVN_74_John_C__Stennis, LHA_1_Tarawa, Oliver_Hazzard_Perry_class, USS_Arleigh_Burke_IIa, \ - Ticonderoga_class +from dcs.planes import ( + F_15C, + F_15E, + F_14B, + FA_18C_hornet, + F_16C_50, + A_10A, + AV8BNA, + B_52H, + B_1B, + F_117A, + MQ_9_Reaper, + E_3A, + KC130, + KC_135, + A_10C, + A_10C_2, +) +from dcs.ships import ( + CVN_74_John_C__Stennis, + LHA_1_Tarawa, + Oliver_Hazzard_Perry_class, + USS_Arleigh_Burke_IIa, + Ticonderoga_class, +) from dcs.vehicles import Armor, Unarmed, Infantry, Artillery from game.factions.faction import Faction @@ -17,12 +38,11 @@ RESOURCES_DIR = THIS_DIR / "resources" class TestFactionLoader(unittest.TestCase): - def setUp(self): pass def test_load_valid_faction(self): - with (RESOURCES_DIR / "valid_faction.json").open('r') as data: + with (RESOURCES_DIR / "valid_faction.json").open("r") as data: faction = Faction.from_json(json.load(data)) self.assertEqual(faction.country, "USA")