mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Blacken.
This commit is contained in:
@@ -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
|
||||
]
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
]
|
||||
|
||||
@@ -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)]
|
||||
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)
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
]
|
||||
]
|
||||
|
||||
@@ -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
|
||||
]
|
||||
Type_052C_Destroyer,
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
}
|
||||
|
||||
346
game/db.py
346
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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]]:
|
||||
|
||||
104
game/game.py
104
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."
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
)
|
||||
self.text,
|
||||
)
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 += """
|
||||
|
||||
|
||||
@@ -67,4 +67,3 @@ def autosave(game) -> bool:
|
||||
except Exception:
|
||||
logging.exception("Could not save game")
|
||||
return False
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
return p
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
return ShapelyPoint(point.x, point.y)
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user