mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Merge branch 'develop' into helipads
This commit is contained in:
commit
e00ca5d096
@ -14,7 +14,7 @@
|
||||
DCS Liberation is a [DCS World](https://www.digitalcombatsimulator.com/en/products/world/) turn based single-player or co-op dynamic campaign.
|
||||
It is an external program that generates full and complex DCS missions and manage a persistent combat environment.
|
||||
|
||||

|
||||

|
||||
|
||||
## Downloads
|
||||
|
||||
|
||||
613
game/db.py
613
game/db.py
@ -2,7 +2,7 @@ import json
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional, Tuple, Type, Union
|
||||
from typing import List, Optional, Type, Union
|
||||
|
||||
from dcs.countries import country_dict
|
||||
from dcs.helicopters import (
|
||||
@ -11,7 +11,6 @@ from dcs.helicopters import (
|
||||
AH_64D,
|
||||
CH_47D,
|
||||
CH_53E,
|
||||
HelicopterType,
|
||||
Ka_50,
|
||||
Mi_24V,
|
||||
Mi_26,
|
||||
@ -132,29 +131,21 @@ from dcs.ships import (
|
||||
)
|
||||
from dcs.task import (
|
||||
AWACS,
|
||||
AntishipStrike,
|
||||
CAP,
|
||||
CAS,
|
||||
CargoTransportation,
|
||||
Embarking,
|
||||
Escort,
|
||||
FighterSweep,
|
||||
GroundAttack,
|
||||
Intercept,
|
||||
MainTask,
|
||||
Nothing,
|
||||
PinpointStrike,
|
||||
Reconnaissance,
|
||||
Refueling,
|
||||
SEAD,
|
||||
Task,
|
||||
Transport,
|
||||
RunwayAttack,
|
||||
)
|
||||
from dcs.terrain.terrain import Airport
|
||||
from dcs.unit import Ship, Unit, Vehicle
|
||||
from dcs.unitgroup import ShipGroup, StaticGroup
|
||||
from dcs.unittype import FlyingType, ShipType, UnitType, VehicleType
|
||||
from dcs.unittype import FlyingType, UnitType, VehicleType
|
||||
from dcs.vehicles import (
|
||||
AirDefence,
|
||||
Armor,
|
||||
@ -724,468 +715,12 @@ PRICES = {
|
||||
highdigitsams.SAM_SA_17_Buk_M1_2_LN_9A310M1_2: 40,
|
||||
}
|
||||
|
||||
"""
|
||||
Units separated by tasks. This will include units for both countries. Be advised that unit could only belong to single task!
|
||||
|
||||
Following tasks are present:
|
||||
* CAP - figther aircraft for CAP/Escort/Intercept
|
||||
* CAS - CAS aircraft
|
||||
* Transport - transport aircraft (used as targets in intercept operations)
|
||||
* AWACS - awacs
|
||||
* AntishipStrike - units that will engage shipping
|
||||
* PinpointStrike - armor that will engage in ground war
|
||||
* AirDefense - AA units
|
||||
* Reconnaissance - units that will be used as targets in destroy insurgents operations
|
||||
* Nothing - troops that will be used for helicopter transport operations
|
||||
* Embarking - helicopters that will be used for helicopter transport operations
|
||||
* Carriage - aircraft carriers
|
||||
* CargoTransportation - ships that will be used as targets for ship intercept operations
|
||||
"""
|
||||
UNIT_BY_TASK = {
|
||||
CAP: [
|
||||
A_4E_C,
|
||||
Bf_109K_4,
|
||||
C_101CC,
|
||||
FA_18C_hornet,
|
||||
FW_190A8,
|
||||
FW_190D9,
|
||||
F_14A_135_GR,
|
||||
F_14B,
|
||||
F_15C,
|
||||
F_16A,
|
||||
F_16C_50,
|
||||
F_22A,
|
||||
F_4E,
|
||||
F_5E_3,
|
||||
I_16,
|
||||
JF_17,
|
||||
J_11A,
|
||||
M_2000C,
|
||||
MiG_19P,
|
||||
MiG_21Bis,
|
||||
MiG_23MLD,
|
||||
MiG_25PD,
|
||||
MiG_29A,
|
||||
MiG_29G,
|
||||
MiG_29S,
|
||||
MiG_31,
|
||||
Mirage_2000_5,
|
||||
P_51D,
|
||||
P_51D_30_NA,
|
||||
SA342Mistral,
|
||||
SpitfireLFMkIX,
|
||||
SpitfireLFMkIXCW,
|
||||
Su_27,
|
||||
Su_30,
|
||||
Su_33,
|
||||
Su_57,
|
||||
],
|
||||
CAS: [
|
||||
AH_1W,
|
||||
AH_64A,
|
||||
AH_64D,
|
||||
AJS37,
|
||||
AV8BNA,
|
||||
A_10A,
|
||||
A_10C,
|
||||
A_10C_2,
|
||||
A_20G,
|
||||
B_17G,
|
||||
B_1B,
|
||||
B_52H,
|
||||
F_117A,
|
||||
F_15E,
|
||||
F_86F_Sabre,
|
||||
Hercules,
|
||||
Ju_88A4,
|
||||
Ka_50,
|
||||
L_39ZA,
|
||||
MB_339PAN,
|
||||
MQ_9_Reaper,
|
||||
MiG_15bis,
|
||||
MiG_27K,
|
||||
Mi_24V,
|
||||
Mi_28N,
|
||||
Mi_8MT,
|
||||
OH_58D,
|
||||
P_47D_30,
|
||||
P_47D_30bl1,
|
||||
P_47D_40,
|
||||
RQ_1A_Predator,
|
||||
SA342L,
|
||||
SA342M,
|
||||
SA342Minigun,
|
||||
SH_60B,
|
||||
S_3B,
|
||||
Su_17M4,
|
||||
Su_24M,
|
||||
Su_24MR,
|
||||
Su_25,
|
||||
Su_25T,
|
||||
Su_34,
|
||||
Tornado_GR4,
|
||||
Tornado_IDS,
|
||||
Tu_160,
|
||||
Tu_22M3,
|
||||
Tu_95MS,
|
||||
UH_1H,
|
||||
WingLoong_I,
|
||||
],
|
||||
Transport: [
|
||||
An_26B,
|
||||
An_30M,
|
||||
CH_47D,
|
||||
CH_53E,
|
||||
C_130,
|
||||
C_17A,
|
||||
IL_76MD,
|
||||
Mi_26,
|
||||
UH_60A,
|
||||
Yak_40,
|
||||
],
|
||||
Refueling: [
|
||||
IL_78M,
|
||||
KC130,
|
||||
KC135MPRS,
|
||||
KC_135,
|
||||
S_3B_Tanker,
|
||||
],
|
||||
AWACS: [
|
||||
A_50,
|
||||
E_2C,
|
||||
E_3A,
|
||||
KJ_2000,
|
||||
],
|
||||
PinpointStrike: [
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_MTLB,
|
||||
Armor.APC_MTLB,
|
||||
Artillery.Grad_MRL_FDDM__FC,
|
||||
Artillery.Grad_MRL_FDDM__FC,
|
||||
Artillery.Grad_MRL_FDDM__FC,
|
||||
Artillery.Grad_MRL_FDDM__FC,
|
||||
Artillery.Grad_MRL_FDDM__FC,
|
||||
Armor.Scout_BRDM_2,
|
||||
Armor.Scout_BRDM_2,
|
||||
Armor.Scout_BRDM_2,
|
||||
Armor.APC_BTR_RD,
|
||||
Armor.APC_BTR_RD,
|
||||
Armor.APC_BTR_RD,
|
||||
Armor.APC_BTR_RD,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.APC_BTR_80,
|
||||
Armor.IFV_BTR_82A,
|
||||
Armor.IFV_BTR_82A,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.IFV_BMP_2,
|
||||
Armor.IFV_BMP_2,
|
||||
Armor.IFV_BMP_3,
|
||||
Armor.IFV_BMP_3,
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.LT_PT_76,
|
||||
Armor.ZBD_04A,
|
||||
Armor.ZBD_04A,
|
||||
Armor.ZBD_04A,
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_55,
|
||||
Armor.MBT_T_72B,
|
||||
Armor.MBT_T_72B,
|
||||
Armor.MBT_T_72B3,
|
||||
Armor.MBT_T_72B3,
|
||||
Armor.MBT_T_80U,
|
||||
Armor.MBT_T_80U,
|
||||
Armor.MBT_T_90,
|
||||
Armor.ZTZ_96B,
|
||||
Armor.Scout_Cobra,
|
||||
Armor.Scout_Cobra,
|
||||
Armor.Scout_Cobra,
|
||||
Armor.Scout_Cobra,
|
||||
Armor.APC_M113,
|
||||
Armor.APC_M113,
|
||||
Armor.APC_M113,
|
||||
Armor.APC_M113,
|
||||
Armor.APC_TPz_Fuchs,
|
||||
Armor.APC_TPz_Fuchs,
|
||||
Armor.APC_TPz_Fuchs,
|
||||
Armor.APC_TPz_Fuchs,
|
||||
Armor.ATGM_HMMWV,
|
||||
Armor.ATGM_HMMWV,
|
||||
Armor.ATGM_VAB_Mephisto,
|
||||
Armor.ATGM_VAB_Mephisto,
|
||||
Armor.Scout_HMMWV,
|
||||
Armor.Scout_HMMWV,
|
||||
Armor.IFV_M2A2_Bradley,
|
||||
Armor.IFV_M2A2_Bradley,
|
||||
Armor.ATGM_Stryker,
|
||||
Armor.ATGM_Stryker,
|
||||
Armor.IFV_M1126_Stryker_ICV,
|
||||
Armor.IFV_M1126_Stryker_ICV,
|
||||
Armor.IFV_M1126_Stryker_ICV,
|
||||
Armor.SPG_Stryker_MGS,
|
||||
Armor.IFV_Warrior,
|
||||
Armor.IFV_Warrior,
|
||||
Armor.IFV_Warrior,
|
||||
Armor.IFV_LAV_25,
|
||||
Armor.IFV_LAV_25,
|
||||
Armor.IFV_Marder,
|
||||
Armor.IFV_Marder,
|
||||
Armor.IFV_Marder,
|
||||
Armor.IFV_Marder,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_Leopard_1A3,
|
||||
Armor.MBT_Leopard_1A3,
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.MBT_Leopard_2A6M,
|
||||
Armor.MBT_Challenger_II,
|
||||
Armor.MBT_Chieftain_Mk_3,
|
||||
Armor.MBT_Merkava_IV,
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.Tk_PzIV_H,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I,
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II,
|
||||
Armor.APC_Sd_Kfz_251_Halftrack,
|
||||
Armor.APC_Sd_Kfz_251_Halftrack,
|
||||
Armor.APC_Sd_Kfz_251_Halftrack,
|
||||
Armor.APC_Sd_Kfz_251_Halftrack,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.Tk_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Unarmed.Carrier_M30_Cargo,
|
||||
Unarmed.Carrier_M30_Cargo,
|
||||
Armor.APC_M2A1_Halftrack,
|
||||
Armor.APC_M2A1_Halftrack,
|
||||
Armor.APC_M2A1_Halftrack,
|
||||
Armor.APC_M2A1_Halftrack,
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.Tk_PzIV_H,
|
||||
Armor.HT_Pz_Kpfw_VI_Tiger_I,
|
||||
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II,
|
||||
Armor.SPG_Jagdpanther_G1,
|
||||
Armor.SPG_Jagdpanzer_IV,
|
||||
Armor.SPG_Sd_Kfz_184_Elefant,
|
||||
Armor.APC_Sd_Kfz_251_Halftrack,
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.Tk_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Unarmed.Carrier_M30_Cargo,
|
||||
Unarmed.Carrier_M30_Cargo,
|
||||
Unarmed.Carrier_M30_Cargo,
|
||||
Armor.APC_M2A1_Halftrack,
|
||||
Armor.APC_M2A1_Halftrack,
|
||||
Armor.CT_Centaur_IV,
|
||||
Armor.CT_Centaur_IV,
|
||||
Armor.HIT_Churchill_VII,
|
||||
Armor.Car_M8_Greyhound_Armored,
|
||||
Armor.Car_M8_Greyhound_Armored,
|
||||
Armor.SPG_M10_GMC,
|
||||
Armor.SPG_M10_GMC,
|
||||
Armor.SPG_StuG_III_Ausf__G,
|
||||
Armor.SPG_StuG_IV,
|
||||
Artillery.SPG_M12_GMC_155mm,
|
||||
Armor.SPG_Sturmpanzer_IV_Brummbar,
|
||||
Armor.Car_Daimler_Armored,
|
||||
Armor.LT_Mk_VII_Tetrarch,
|
||||
Artillery.MLRS_M270_227mm,
|
||||
Artillery.SPH_M109_Paladin_155mm,
|
||||
Artillery.SPM_2S9_Nona_120mm_M,
|
||||
Artillery.SPH_2S1_Gvozdika_122mm,
|
||||
Artillery.SPH_2S3_Akatsia_152mm,
|
||||
Artillery.SPH_2S19_Msta_152mm,
|
||||
Artillery.MLRS_BM_21_Grad_122mm,
|
||||
Artillery.MLRS_BM_21_Grad_122mm,
|
||||
Artillery.MLRS_9K57_Uragan_BM_27_220mm,
|
||||
Artillery.MLRS_9A52_Smerch_HE_300mm,
|
||||
Artillery.SPH_Dana_vz77_152mm,
|
||||
Artillery.SPH_T155_Firtina_155mm,
|
||||
Artillery.PLZ_05,
|
||||
Artillery.SPG_M12_GMC_155mm,
|
||||
Armor.SPG_Sturmpanzer_IV_Brummbar,
|
||||
AirDefence.SPAAA_ZU_23_2_Mounted_Ural_375,
|
||||
AirDefence.SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375,
|
||||
AirDefence.SPAAA_ZSU_57_2,
|
||||
AirDefence.SPAAA_ZSU_23_4_Shilka_Gun_Dish,
|
||||
AirDefence.SAM_SA_8_Osa_Gecko_TEL,
|
||||
AirDefence.SAM_SA_9_Strela_1_Gaskin_TEL,
|
||||
AirDefence.SAM_SA_13_Strela_10M3_Gopher_TEL,
|
||||
AirDefence.SAM_SA_15_Tor_Gauntlet,
|
||||
AirDefence.SAM_SA_19_Tunguska_Grison,
|
||||
AirDefence.SPAAA_Gepard,
|
||||
AirDefence.SPAAA_Vulcan_M163,
|
||||
AirDefence.SAM_Linebacker___Bradley_M6,
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
AirDefence.SAM_Avenger__Stinger,
|
||||
AirDefence.SAM_Roland_ADS,
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
AirDefence.AAA_8_8cm_Flak_18,
|
||||
AirDefence.AAA_8_8cm_Flak_36,
|
||||
AirDefence.AAA_8_8cm_Flak_37,
|
||||
AirDefence.AAA_8_8cm_Flak_41,
|
||||
AirDefence.AAA_Bofors_40mm,
|
||||
AirDefence.AAA_S_60_57mm,
|
||||
AirDefence.AAA_M1_37mm,
|
||||
AirDefence.AAA_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,
|
||||
frenchpack.TRM_2000_PAMELA,
|
||||
frenchpack.VAB__50,
|
||||
frenchpack.VAB_MEPHISTO,
|
||||
frenchpack.VAB_T20_13,
|
||||
frenchpack.VBL__50,
|
||||
frenchpack.VBL_AANF1,
|
||||
frenchpack.VBAE_CRAB,
|
||||
frenchpack.VBAE_CRAB_MMP,
|
||||
frenchpack.AMX_30B2,
|
||||
frenchpack.Leclerc_Serie_XXI,
|
||||
frenchpack.DIM__TOYOTA_BLUE,
|
||||
frenchpack.DIM__TOYOTA_GREEN,
|
||||
frenchpack.DIM__TOYOTA_DESERT,
|
||||
frenchpack.DIM__KAMIKAZE,
|
||||
],
|
||||
AirDefence: [],
|
||||
Reconnaissance: [
|
||||
Unarmed.Truck_M818_6x6,
|
||||
Unarmed.Truck_Ural_375,
|
||||
Unarmed.LUV_UAZ_469_Jeep,
|
||||
],
|
||||
Nothing: [
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Infantry_AK_74,
|
||||
],
|
||||
Embarking: [],
|
||||
Carriage: [
|
||||
CVN_74_John_C__Stennis,
|
||||
LHA_1_Tarawa,
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
],
|
||||
CargoTransportation: [
|
||||
Cargo_Ivanov,
|
||||
Bulker_Yakushev,
|
||||
Tanker_Elnya_160,
|
||||
Boat_Armed_Hi_speed,
|
||||
],
|
||||
}
|
||||
|
||||
"""
|
||||
Units from AirDefense category of UNIT_BY_TASK that will be removed from use if "No SAM" option is checked at the start of the game
|
||||
"""
|
||||
SAM_BAN = [
|
||||
AirDefence.SAM_Linebacker___Bradley_M6,
|
||||
AirDefence.SAM_SA_9_Strela_1_Gaskin_TEL,
|
||||
AirDefence.SAM_SA_8_Osa_Gecko_TEL,
|
||||
AirDefence.SAM_SA_19_Tunguska_Grison,
|
||||
AirDefence.SAM_SA_6_Kub_Gainful_TEL,
|
||||
AirDefence.SAM_SA_8_Osa_Gecko_TEL,
|
||||
AirDefence.SAM_SA_3_S_125_Goa_LN,
|
||||
AirDefence.SAM_Hawk_Platoon_Command_Post__PCP,
|
||||
AirDefence.SAM_SA_2_S_75_Guideline_LN,
|
||||
AirDefence.SAM_SA_11_Buk_Gadfly_Fire_Dome_TEL,
|
||||
]
|
||||
|
||||
"""
|
||||
Used to convert SAM site parts to the corresponding site
|
||||
"""
|
||||
SAM_CONVERT = {
|
||||
AirDefence.SAM_P19_Flat_Face_SR__SA_2_3: AirDefence.SAM_SA_3_S_125_Goa_LN,
|
||||
AirDefence.SAM_SA_3_S_125_Low_Blow_TR: AirDefence.SAM_SA_3_S_125_Goa_LN,
|
||||
AirDefence.SAM_SA_3_S_125_Goa_LN: AirDefence.SAM_SA_3_S_125_Goa_LN,
|
||||
AirDefence.SAM_SA_6_Kub_Gainful_TEL: AirDefence.SAM_SA_6_Kub_Gainful_TEL,
|
||||
AirDefence.SAM_SA_6_Kub_Straight_Flush_STR: AirDefence.SAM_SA_6_Kub_Gainful_TEL,
|
||||
AirDefence.SAM_SA_10_S_300_Grumble_TEL_C: AirDefence.SAM_SA_10_S_300_Grumble_TEL_C,
|
||||
AirDefence.SAM_SA_10_S_300_Grumble_Clam_Shell_SR: AirDefence.SAM_SA_10_S_300_Grumble_TEL_C,
|
||||
AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR: AirDefence.SAM_SA_10_S_300_Grumble_TEL_C,
|
||||
AirDefence.SAM_SA_10_S_300_Grumble_C2: AirDefence.SAM_SA_10_S_300_Grumble_TEL_C,
|
||||
AirDefence.SAM_SA_10_S_300_Grumble_Big_Bird_SR: AirDefence.SAM_SA_10_S_300_Grumble_C2,
|
||||
AirDefence.SAM_Hawk_TR__AN_MPQ_46: AirDefence.SAM_Hawk_Platoon_Command_Post__PCP,
|
||||
AirDefence.SAM_Hawk_SR__AN_MPQ_50: AirDefence.SAM_Hawk_Platoon_Command_Post__PCP,
|
||||
AirDefence.SAM_Hawk_LN_M192: AirDefence.SAM_Hawk_Platoon_Command_Post__PCP,
|
||||
"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_300_Grumble_Flap_Lid_TR: AirDefence.SAM_SA_10_S_300_Grumble_C2,
|
||||
AirDefence.SAM_P19_Flat_Face_SR__SA_2_3: AirDefence.SAM_SA_2_S_75_Guideline_LN,
|
||||
},
|
||||
}
|
||||
|
||||
"""
|
||||
Units that will always be spawned in the air
|
||||
"""
|
||||
TAKEOFF_BAN: List[Type[FlyingType]] = []
|
||||
|
||||
"""
|
||||
Units that will be always spawned in the air if launched from the carrier
|
||||
"""
|
||||
CARRIER_TAKEOFF_BAN: List[Type[FlyingType]] = [
|
||||
Su_33, # Kuznecow is bugged in a way that only 2 aircraft could be spawned
|
||||
]
|
||||
|
||||
"""
|
||||
Units separated by country.
|
||||
country : DCS Country name
|
||||
"""
|
||||
FACTIONS = FactionLoader()
|
||||
|
||||
CARRIER_TYPE_BY_PLANE = {
|
||||
FA_18C_hornet: CVN_74_John_C__Stennis,
|
||||
F_14A_135_GR: CVN_74_John_C__Stennis,
|
||||
F_14B: CVN_74_John_C__Stennis,
|
||||
Ka_50: LHA_1_Tarawa,
|
||||
SA342M: LHA_1_Tarawa,
|
||||
UH_1H: LHA_1_Tarawa,
|
||||
Mi_8MT: LHA_1_Tarawa,
|
||||
AV8BNA: LHA_1_Tarawa,
|
||||
}
|
||||
|
||||
"""
|
||||
Aircraft payload overrides. Usually default loadout for the task is loaded during the mission generation.
|
||||
Syntax goes as follows:
|
||||
|
||||
`AircraftIdentifier`: {
|
||||
"Category": "PayloadName",
|
||||
},
|
||||
|
||||
where:
|
||||
* `AircraftIdentifier`: identifier of aircraft (the same that is used troughout the file)
|
||||
* "Category": (in double quotes) is one of the tasks: CAS, CAP, Intercept, Escort or "*"
|
||||
* "PayloadName": payload as found in resources/payloads/UNIT_TYPE.lua file. Sometimes this will match payload names
|
||||
in the mission editor, sometimes it doesn't
|
||||
|
||||
Payload will be used for operation of following type, "*" category will be used always, no matter the operation.
|
||||
"""
|
||||
|
||||
COMMON_OVERRIDE = {
|
||||
CAP: "CAP",
|
||||
Intercept: "CAP",
|
||||
CAS: "CAS",
|
||||
PinpointStrike: "STRIKE",
|
||||
SEAD: "SEAD",
|
||||
AntishipStrike: "ANTISHIP",
|
||||
GroundAttack: "STRIKE",
|
||||
Escort: "CAP",
|
||||
RunwayAttack: "RUNWAY_ATTACK",
|
||||
FighterSweep: "CAP",
|
||||
AWACS: "AEW&C",
|
||||
}
|
||||
|
||||
"""
|
||||
Aircraft livery overrides. Syntax as follows:
|
||||
|
||||
@ -1299,16 +834,6 @@ LHA_CAPABLE = [
|
||||
---------- END OF CONFIGURATION SECTION
|
||||
"""
|
||||
|
||||
UnitsDict = Dict[UnitType, int]
|
||||
PlaneDict = Dict[FlyingType, int]
|
||||
HeliDict = Dict[HelicopterType, int]
|
||||
ArmorDict = Dict[VehicleType, int]
|
||||
ShipDict = Dict[ShipType, int]
|
||||
AirDefenseDict = Dict[AirDefence, int]
|
||||
|
||||
AssignedUnitsDict = Dict[Type[UnitType], Tuple[int, int]]
|
||||
TaskForceDict = Dict[Type[MainTask], AssignedUnitsDict]
|
||||
|
||||
StartingPosition = Union[ShipGroup, StaticGroup, Airport, Point]
|
||||
|
||||
|
||||
@ -1332,22 +857,6 @@ def upgrade_to_supercarrier(unit, name: str):
|
||||
return unit
|
||||
|
||||
|
||||
def unit_task(unit: UnitType) -> Optional[Task]:
|
||||
for task, units in UNIT_BY_TASK.items():
|
||||
if unit in units:
|
||||
return task
|
||||
|
||||
if unit in SAM_CONVERT:
|
||||
return unit_task(SAM_CONVERT[unit])
|
||||
|
||||
print(unit.name + " cause issue")
|
||||
return None
|
||||
|
||||
|
||||
def find_unittype(for_task: Type[MainTask], country_name: str) -> List[Type[UnitType]]:
|
||||
return [x for x in UNIT_BY_TASK[for_task] if x in FACTIONS[country_name].units]
|
||||
|
||||
|
||||
MANPADS: List[Type[VehicleType]] = [
|
||||
AirDefence.MANPADS_SA_18_Igla_Grouse,
|
||||
AirDefence.MANPADS_SA_18_Igla_S_Grouse,
|
||||
@ -1476,106 +985,6 @@ def unit_type_of(unit: Unit) -> UnitType:
|
||||
return unit.type
|
||||
|
||||
|
||||
def task_name(task) -> str:
|
||||
if task == AirDefence:
|
||||
return "AirDefence"
|
||||
elif task == Embarking:
|
||||
return "Transportation"
|
||||
elif task == PinpointStrike:
|
||||
return "Frontline units"
|
||||
else:
|
||||
return task.name
|
||||
|
||||
|
||||
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.sort(key=lambda x: PRICES[x])
|
||||
|
||||
idx = int(len(suitable_unittypes) * factor)
|
||||
variety = int(count + count * factor / 2)
|
||||
|
||||
index_start = min(idx, len(suitable_unittypes) - variety)
|
||||
index_end = min(idx + variety, len(suitable_unittypes))
|
||||
return list(set(suitable_unittypes[index_start:index_end]))
|
||||
|
||||
|
||||
def unitdict_append(unit_dict: UnitsDict, unit_type: UnitType, count: int):
|
||||
unit_dict[unit_type] = unit_dict.get(unit_type, 0) + 1
|
||||
|
||||
|
||||
def unitdict_merge(a: UnitsDict, b: UnitsDict) -> UnitsDict:
|
||||
b = b.copy()
|
||||
for k, v in a.items():
|
||||
b[k] = b.get(k, 0) + v
|
||||
|
||||
return b
|
||||
|
||||
|
||||
def unitdict_split(unit_dict: UnitsDict, count: int):
|
||||
buffer_dict: Dict[UnitType, int] = {}
|
||||
for unit_type, unit_count in unit_dict.items():
|
||||
for _ in range(unit_count):
|
||||
unitdict_append(buffer_dict, unit_type, 1)
|
||||
if sum(buffer_dict.values()) >= count:
|
||||
yield buffer_dict
|
||||
buffer_dict = {}
|
||||
|
||||
if len(buffer_dict):
|
||||
yield buffer_dict
|
||||
|
||||
|
||||
def unitdict_restrict_count(unit_dict: UnitsDict, total_count: int) -> UnitsDict:
|
||||
if total_count == 0:
|
||||
return {}
|
||||
|
||||
groups = list(unitdict_split(unit_dict, total_count))
|
||||
if len(groups) > 0:
|
||||
return groups[0]
|
||||
else:
|
||||
return {}
|
||||
|
||||
|
||||
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()},
|
||||
)
|
||||
|
||||
|
||||
def assigned_units_from(d: PlaneDict) -> AssignedUnitsDict:
|
||||
return {k: (v, 0) for k, v in d.items()}
|
||||
|
||||
|
||||
def assignedunits_split_to_count(dict: AssignedUnitsDict, count: int):
|
||||
buffer_dict: Dict[Type[UnitType], Tuple[int, int]] = {}
|
||||
for unit_type, (unit_count, client_count) in dict.items():
|
||||
for _ in range(unit_count):
|
||||
new_count, new_client_count = buffer_dict.get(unit_type, (0, 0))
|
||||
|
||||
new_count += 1
|
||||
|
||||
if client_count > 0:
|
||||
new_client_count += 1
|
||||
client_count -= 1
|
||||
|
||||
buffer_dict[unit_type] = new_count, new_client_count
|
||||
if new_count >= count:
|
||||
yield buffer_dict
|
||||
buffer_dict = {}
|
||||
|
||||
if len(buffer_dict):
|
||||
yield buffer_dict
|
||||
|
||||
|
||||
def unitdict_from(fd: AssignedUnitsDict) -> Dict:
|
||||
return {k: v1 for k, (v1, v2) in fd.items()}
|
||||
|
||||
|
||||
def country_id_from_name(name):
|
||||
for k, v in country_dict.items():
|
||||
if v.name == name:
|
||||
@ -1583,24 +992,6 @@ def country_id_from_name(name):
|
||||
return -1
|
||||
|
||||
|
||||
def _validate_db():
|
||||
# check unit by task uniquity
|
||||
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
|
||||
)
|
||||
total_set.add(unit_type)
|
||||
|
||||
# check prices
|
||||
for unit_type in total_set:
|
||||
assert unit_type in PRICES, "{} not in prices".format(unit_type)
|
||||
|
||||
|
||||
_validate_db()
|
||||
|
||||
|
||||
class DefaultLiveries:
|
||||
class Default(Enum):
|
||||
af_standard = ""
|
||||
|
||||
@ -434,7 +434,7 @@ class Event:
|
||||
moved_units[frontline_unit] = int(count * move_factor)
|
||||
total_units_redeployed = total_units_redeployed + int(count * move_factor)
|
||||
|
||||
destination.base.commision_units(moved_units)
|
||||
destination.base.commission_units(moved_units)
|
||||
source.base.commit_losses(moved_units)
|
||||
|
||||
# Also transfer pending deliveries.
|
||||
|
||||
@ -3,7 +3,7 @@ from game.data.groundunitclass import GroundUnitClass
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Type, List, Any, cast
|
||||
from typing import Optional, Dict, Type, List, Any, cast, Iterator
|
||||
|
||||
import dcs
|
||||
from dcs.countries import country_dict
|
||||
@ -240,7 +240,7 @@ class Faction:
|
||||
return faction
|
||||
|
||||
@property
|
||||
def units(self) -> List[Type[UnitType]]:
|
||||
def all_units(self) -> List[Type[UnitType]]:
|
||||
return (
|
||||
self.infantry_units
|
||||
+ self.aircrafts
|
||||
@ -251,6 +251,12 @@ class Faction:
|
||||
+ self.logistics_units
|
||||
)
|
||||
|
||||
@property
|
||||
def ground_units(self) -> Iterator[Type[VehicleType]]:
|
||||
yield from self.artillery_units
|
||||
yield from self.frontline_units
|
||||
yield from self.logistics_units
|
||||
|
||||
|
||||
def unit_loader(unit: str, class_repository: List[Any]) -> Optional[Type[UnitType]]:
|
||||
"""
|
||||
|
||||
@ -1,21 +1,12 @@
|
||||
import itertools
|
||||
import logging
|
||||
import math
|
||||
import typing
|
||||
from typing import Dict, Type
|
||||
|
||||
from dcs.task import AWACS, CAP, CAS, Embarking, PinpointStrike, Task, Transport
|
||||
from dcs.unittype import FlyingType, UnitType, VehicleType
|
||||
from dcs.vehicles import AirDefence, Armor
|
||||
from dcs.unittype import FlyingType, VehicleType, UnitType
|
||||
|
||||
from game import db
|
||||
from game.db import PRICES
|
||||
|
||||
STRENGTH_AA_ASSEMBLE_MIN = 0.2
|
||||
PLANES_SCRAMBLE_MIN_BASE = 2
|
||||
PLANES_SCRAMBLE_MAX_BASE = 8
|
||||
PLANES_SCRAMBLE_FACTOR = 0.3
|
||||
|
||||
BASE_MAX_STRENGTH = 1
|
||||
BASE_MIN_STRENGTH = 0
|
||||
|
||||
@ -24,9 +15,6 @@ class Base:
|
||||
def __init__(self):
|
||||
self.aircraft: Dict[Type[FlyingType], int] = {}
|
||||
self.armor: Dict[Type[VehicleType], int] = {}
|
||||
# TODO: Appears unused.
|
||||
self.aa: Dict[AirDefence, int] = {}
|
||||
self.commision_points: Dict[Type, float] = {}
|
||||
self.strength = 1
|
||||
|
||||
@property
|
||||
@ -47,119 +35,32 @@ class Base:
|
||||
logging.exception(f"No price found for {unit_type.id}")
|
||||
return total
|
||||
|
||||
@property
|
||||
def total_aa(self) -> int:
|
||||
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]
|
||||
]
|
||||
)
|
||||
|
||||
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()
|
||||
)
|
||||
for t, c in itertools.chain(self.aircraft.items(), self.armor.items())
|
||||
if t == unit_type
|
||||
]
|
||||
)
|
||||
|
||||
@property
|
||||
def all_units(self):
|
||||
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]:
|
||||
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.sort(key=lambda x: db.PRICES[x], reverse=True)
|
||||
|
||||
result: Dict[UnitType, int] = {}
|
||||
for unit_type in sorted_units:
|
||||
existing_count = available_units[unit_type] # type: int
|
||||
if not existing_count:
|
||||
continue
|
||||
|
||||
if count <= 0:
|
||||
break
|
||||
|
||||
result_unit_count = min(count, existing_count)
|
||||
count -= result_unit_count
|
||||
|
||||
assert result_unit_count > 0
|
||||
result[unit_type] = result.get(unit_type, 0) + result_unit_count
|
||||
|
||||
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]:
|
||||
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
|
||||
)
|
||||
points = self.commision_points[for_type]
|
||||
if points >= 1:
|
||||
self.commision_points[for_type] = points - math.floor(points)
|
||||
return int(math.floor(points))
|
||||
|
||||
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.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]):
|
||||
|
||||
def commission_units(self, units: typing.Dict[typing.Type[UnitType], int]):
|
||||
for unit_type, unit_count in units.items():
|
||||
if unit_count <= 0:
|
||||
continue
|
||||
|
||||
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
|
||||
or for_task == Transport
|
||||
):
|
||||
target_dict = self.aircraft
|
||||
elif for_task == PinpointStrike:
|
||||
if issubclass(unit_type, VehicleType):
|
||||
target_dict = self.armor
|
||||
elif for_task == AirDefence:
|
||||
target_dict = self.aa
|
||||
|
||||
if target_dict is not None:
|
||||
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
|
||||
elif issubclass(unit_type, FlyingType):
|
||||
target_dict = self.aircraft
|
||||
else:
|
||||
logging.error("Unable to determine target dict for " + str(unit_type))
|
||||
logging.error(
|
||||
f"Unexpected unit type of {unit_type}: "
|
||||
f"{unit_type.__module__}.{unit_type.__name__}"
|
||||
)
|
||||
return
|
||||
|
||||
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
|
||||
|
||||
def commit_losses(self, units_lost: typing.Dict[typing.Any, int]):
|
||||
|
||||
@ -190,55 +91,3 @@ class Base:
|
||||
|
||||
def set_strength_to_minimum(self) -> None:
|
||||
self.strength = BASE_MIN_STRENGTH
|
||||
|
||||
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]
|
||||
)
|
||||
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,
|
||||
)
|
||||
|
||||
def assemble_count(self):
|
||||
return int(self.total_armor * 0.5)
|
||||
|
||||
def assemble_aa_count(self) -> int:
|
||||
# previous logic removed because we always want the full air defense capabilities.
|
||||
return self.total_aa
|
||||
|
||||
def scramble_sweep(self, multiplier: float) -> typing.Dict[FlyingType, int]:
|
||||
return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP))
|
||||
|
||||
def scramble_last_defense(self):
|
||||
# return as many CAP-capable aircraft as we can since this is the last defense of the base
|
||||
# (but not more than 20 - that's just nuts)
|
||||
return self._find_best_planes(CAP, min(self.total_aircraft, 20))
|
||||
|
||||
def scramble_cas(self, multiplier: float) -> typing.Dict[FlyingType, int]:
|
||||
return self._find_best_planes(CAS, self.scramble_count(multiplier, CAS))
|
||||
|
||||
def scramble_interceptors(self, multiplier: float) -> typing.Dict[FlyingType, int]:
|
||||
return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP))
|
||||
|
||||
def assemble_attack(self) -> typing.Dict[Armor, int]:
|
||||
return self._find_best_armor(PinpointStrike, self.assemble_count())
|
||||
|
||||
def assemble_defense(self) -> typing.Dict[Armor, int]:
|
||||
count = int(self.total_armor * min(self.strength + 0.5, 1))
|
||||
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(),
|
||||
)
|
||||
|
||||
@ -565,7 +565,7 @@ class ControlPoint(MissionTarget, ABC):
|
||||
while self.base.armor:
|
||||
unit_type, count = self.base.armor.popitem()
|
||||
for _ in range(count):
|
||||
destination.control_point.base.commision_units({unit_type: 1})
|
||||
destination.control_point.base.commission_units({unit_type: 1})
|
||||
destination = heapq.heappushpop(destinations, destination)
|
||||
|
||||
def capture_aircraft(
|
||||
@ -613,7 +613,7 @@ class ControlPoint(MissionTarget, ABC):
|
||||
return
|
||||
parking = destination.unclaimed_parking(game)
|
||||
transfer_amount = min([parking, count])
|
||||
destination.base.commision_units({airframe: transfer_amount})
|
||||
destination.base.commission_units({airframe: transfer_amount})
|
||||
count -= transfer_amount
|
||||
|
||||
def retreat_air_units(self, game: Game) -> None:
|
||||
|
||||
@ -109,7 +109,7 @@ class TransferOrder:
|
||||
|
||||
def disband_at(self, location: ControlPoint) -> None:
|
||||
logging.info(f"Units halting at {location}.")
|
||||
location.base.commision_units(self.units)
|
||||
location.base.commission_units(self.units)
|
||||
self.units.clear()
|
||||
|
||||
@property
|
||||
@ -562,7 +562,7 @@ class PendingTransfers:
|
||||
if transfer.transport is not None:
|
||||
self.cancel_transport(transfer.transport, transfer)
|
||||
self.pending_transfers.remove(transfer)
|
||||
transfer.origin.base.commision_units(transfer.units)
|
||||
transfer.origin.base.commission_units(transfer.units)
|
||||
|
||||
def perform_transfers(self) -> None:
|
||||
incomplete = []
|
||||
|
||||
@ -104,11 +104,11 @@ class PendingUnitDeliveries:
|
||||
game.message(f"{coalition} sold: {name} x {-count} at {source}")
|
||||
|
||||
self.units = defaultdict(int)
|
||||
self.destination.base.commision_units(bought_units)
|
||||
self.destination.base.commission_units(bought_units)
|
||||
self.destination.base.commit_losses(sold_units)
|
||||
|
||||
if units_needing_transfer:
|
||||
ground_unit_source.base.commision_units(units_needing_transfer)
|
||||
ground_unit_source.base.commission_units(units_needing_transfer)
|
||||
self.create_transfer(game, ground_unit_source, units_needing_transfer)
|
||||
|
||||
def create_transfer(
|
||||
|
||||
@ -16,6 +16,7 @@ from dcs.task import (
|
||||
)
|
||||
|
||||
from game import db
|
||||
from .flights.ai_flight_planner_db import AEWC_CAPABLE
|
||||
from .naming import namegen
|
||||
from .callsigns import callsign_for_support_unit
|
||||
from .conflictgen import Conflict
|
||||
@ -102,7 +103,7 @@ class AirSupportConflictGenerator:
|
||||
fallback_tanker_number = 0
|
||||
|
||||
for i, tanker_unit_type in enumerate(
|
||||
db.find_unittype(Refueling, self.conflict.attackers_side)
|
||||
self.game.faction_for(player=True).tankers
|
||||
):
|
||||
alt, airspeed = self._get_tanker_params(tanker_unit_type)
|
||||
variant = db.unit_type_name(tanker_unit_type)
|
||||
@ -178,41 +179,46 @@ class AirSupportConflictGenerator:
|
||||
)
|
||||
|
||||
if not self.game.settings.disable_legacy_aewc:
|
||||
possible_awacs = db.find_unittype(AWACS, self.conflict.attackers_side)
|
||||
possible_awacs = [
|
||||
a
|
||||
for a in self.game.faction_for(player=True).aircrafts
|
||||
if a in AEWC_CAPABLE
|
||||
]
|
||||
|
||||
if len(possible_awacs) > 0:
|
||||
awacs_unit = possible_awacs[0]
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
|
||||
awacs_flight = self.mission.awacs_flight(
|
||||
country=self.mission.country(self.game.player_country),
|
||||
name=namegen.next_awacs_name(
|
||||
self.mission.country(self.game.player_country)
|
||||
),
|
||||
plane_type=awacs_unit,
|
||||
altitude=AWACS_ALT,
|
||||
airport=None,
|
||||
position=self.conflict.position.random_point_within(
|
||||
AWACS_DISTANCE, AWACS_DISTANCE
|
||||
),
|
||||
frequency=freq.mhz,
|
||||
start_type=StartType.Warm,
|
||||
)
|
||||
awacs_flight.set_frequency(freq.mhz)
|
||||
|
||||
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
self.air_support.awacs.append(
|
||||
AwacsInfo(
|
||||
group_name=str(awacs_flight.name),
|
||||
callsign=callsign_for_support_unit(awacs_flight),
|
||||
freq=freq,
|
||||
depature_location=None,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
blue=True,
|
||||
)
|
||||
)
|
||||
else:
|
||||
if not possible_awacs:
|
||||
logging.warning("No AWACS for faction")
|
||||
return
|
||||
|
||||
awacs_unit = possible_awacs[0]
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
|
||||
awacs_flight = self.mission.awacs_flight(
|
||||
country=self.mission.country(self.game.player_country),
|
||||
name=namegen.next_awacs_name(
|
||||
self.mission.country(self.game.player_country)
|
||||
),
|
||||
plane_type=awacs_unit,
|
||||
altitude=AWACS_ALT,
|
||||
airport=None,
|
||||
position=self.conflict.position.random_point_within(
|
||||
AWACS_DISTANCE, AWACS_DISTANCE
|
||||
),
|
||||
frequency=freq.mhz,
|
||||
start_type=StartType.Warm,
|
||||
)
|
||||
awacs_flight.set_frequency(freq.mhz)
|
||||
|
||||
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
self.air_support.awacs.append(
|
||||
AwacsInfo(
|
||||
group_name=str(awacs_flight.name),
|
||||
callsign=callsign_for_support_unit(awacs_flight),
|
||||
freq=freq,
|
||||
depature_location=None,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
blue=True,
|
||||
)
|
||||
)
|
||||
|
||||
@ -166,9 +166,7 @@ class ScrollingUnitTransferGrid(QFrame):
|
||||
scroll_content = QWidget()
|
||||
task_box_layout = QGridLayout()
|
||||
|
||||
unit_types = set(
|
||||
db.find_unittype(PinpointStrike, self.game_model.game.player_name)
|
||||
)
|
||||
unit_types = set(self.game_model.game.faction_for(player=True).ground_units)
|
||||
sorted_units = sorted(
|
||||
{u for u in unit_types if self.cp.base.total_units_of_type(u)},
|
||||
key=lambda u: db.unit_get_expanded_info(
|
||||
|
||||
@ -167,7 +167,7 @@ class QHangarStatus(QHBoxLayout):
|
||||
next_turn = self.control_point.allocated_aircraft(self.game_model.game)
|
||||
max_amount = self.control_point.total_aircraft_parking
|
||||
|
||||
components = [f"{next_turn.present} present"]
|
||||
components = [f"{next_turn.total_present} present"]
|
||||
if next_turn.total_ordered > 0:
|
||||
components.append(f"{next_turn.total_ordered} purchased")
|
||||
elif next_turn.total_ordered < 0:
|
||||
|
||||
@ -7,10 +7,8 @@ from PySide2.QtWidgets import (
|
||||
QScrollArea,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
QMessageBox,
|
||||
)
|
||||
from dcs.task import PinpointStrike
|
||||
from dcs.unittype import FlyingType, UnitType
|
||||
from dcs.unittype import UnitType
|
||||
|
||||
from game import db
|
||||
from game.theater import ControlPoint
|
||||
@ -32,31 +30,24 @@ class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour):
|
||||
def init_ui(self):
|
||||
main_layout = QVBoxLayout()
|
||||
|
||||
units = {
|
||||
PinpointStrike: db.find_unittype(
|
||||
PinpointStrike, self.game_model.game.player_name
|
||||
),
|
||||
}
|
||||
|
||||
scroll_content = QWidget()
|
||||
task_box_layout = QGridLayout()
|
||||
scroll_content.setLayout(task_box_layout)
|
||||
row = 0
|
||||
|
||||
for task_type in units.keys():
|
||||
units_column = list(set(units[task_type]))
|
||||
if len(units_column) == 0:
|
||||
continue
|
||||
units_column.sort(
|
||||
key=lambda u: db.unit_get_expanded_info(
|
||||
self.game_model.game.player_country, u, "name"
|
||||
)
|
||||
unit_types = list(
|
||||
set(self.game_model.game.faction_for(player=True).ground_units)
|
||||
)
|
||||
unit_types.sort(
|
||||
key=lambda u: db.unit_get_expanded_info(
|
||||
self.game_model.game.player_country, u, "name"
|
||||
)
|
||||
for unit_type in units_column:
|
||||
row = self.add_purchase_row(unit_type, task_box_layout, row)
|
||||
stretch = QVBoxLayout()
|
||||
stretch.addStretch()
|
||||
task_box_layout.addLayout(stretch, row, 0)
|
||||
)
|
||||
for unit_type in unit_types:
|
||||
row = self.add_purchase_row(unit_type, task_box_layout, row)
|
||||
stretch = QVBoxLayout()
|
||||
stretch.addStretch()
|
||||
task_box_layout.addLayout(stretch, row, 0)
|
||||
|
||||
scroll_content.setLayout(task_box_layout)
|
||||
scroll = QScrollArea()
|
||||
|
||||
@ -1,3 +1,6 @@
|
||||
from collections import defaultdict
|
||||
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtWidgets import (
|
||||
QFrame,
|
||||
QGridLayout,
|
||||
@ -7,8 +10,6 @@ from PySide2.QtWidgets import (
|
||||
QScrollArea,
|
||||
QWidget,
|
||||
)
|
||||
from PySide2.QtCore import Qt
|
||||
from dcs.task import CAP, CAS, Embarking, PinpointStrike
|
||||
|
||||
from game import Game, db
|
||||
from game.theater import ControlPoint
|
||||
@ -19,51 +20,44 @@ class QIntelInfo(QFrame):
|
||||
super(QIntelInfo, self).__init__()
|
||||
self.cp = cp
|
||||
self.game = game
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
scroll_content = QWidget()
|
||||
intelLayout = QVBoxLayout()
|
||||
intel_layout = QVBoxLayout()
|
||||
|
||||
units = {
|
||||
CAP: db.find_unittype(CAP, self.game.enemy_name),
|
||||
Embarking: db.find_unittype(Embarking, self.game.enemy_name),
|
||||
CAS: db.find_unittype(CAS, self.game.enemy_name),
|
||||
PinpointStrike: db.find_unittype(PinpointStrike, self.game.enemy_name),
|
||||
units_by_task: dict[str, dict[str, int]] = defaultdict(lambda: defaultdict(int))
|
||||
for unit_type, count in self.cp.base.aircraft.items():
|
||||
if count:
|
||||
name = db.unit_get_expanded_info(
|
||||
self.game.enemy_country, unit_type, "name"
|
||||
)
|
||||
units_by_task[unit_type.task_default.name][name] += count
|
||||
|
||||
units_by_task = {
|
||||
task: units_by_task[task] for task in sorted(units_by_task.keys())
|
||||
}
|
||||
|
||||
for task_type in units.keys():
|
||||
units_column = list(set(units[task_type]))
|
||||
front_line_units = defaultdict(int)
|
||||
for unit_type, count in self.cp.base.armor.items():
|
||||
if count:
|
||||
name = db.unit_get_expanded_info(
|
||||
self.game.enemy_country, unit_type, "name"
|
||||
)
|
||||
front_line_units[name] += count
|
||||
|
||||
if sum([self.cp.base.total_units_of_type(u) for u in units_column]) > 0:
|
||||
units_by_task["Front line units"] = front_line_units
|
||||
for task, unit_types in units_by_task.items():
|
||||
task_group = QGroupBox(task)
|
||||
task_layout = QGridLayout()
|
||||
task_group.setLayout(task_layout)
|
||||
|
||||
group = QGroupBox(db.task_name(task_type))
|
||||
groupLayout = QGridLayout()
|
||||
group.setLayout(groupLayout)
|
||||
for row, (name, count) in enumerate(unit_types.items()):
|
||||
task_layout.addWidget(QLabel(f"<b>{name}</b>"), row, 0)
|
||||
task_layout.addWidget(QLabel(str(count)), row, 1)
|
||||
|
||||
row = 0
|
||||
for unit_type in units_column:
|
||||
existing_units = self.cp.base.total_units_of_type(unit_type)
|
||||
if existing_units == 0:
|
||||
continue
|
||||
groupLayout.addWidget(
|
||||
QLabel(
|
||||
"<b>"
|
||||
+ db.unit_get_expanded_info(
|
||||
self.game.enemy_country, unit_type, "name"
|
||||
)
|
||||
+ "</b>"
|
||||
),
|
||||
row,
|
||||
0,
|
||||
)
|
||||
groupLayout.addWidget(QLabel(str(existing_units)), row, 1)
|
||||
row += 1
|
||||
intel_layout.addWidget(task_group)
|
||||
|
||||
intelLayout.addWidget(group)
|
||||
|
||||
scroll_content.setLayout(intelLayout)
|
||||
scroll_content.setLayout(intel_layout)
|
||||
scroll = QScrollArea()
|
||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
||||
|
||||
@ -340,11 +340,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
|
||||
buy_ewr_layout.addLayout(stretch, 2, 0)
|
||||
|
||||
# Armored units
|
||||
|
||||
armored_units = db.find_unittype(
|
||||
PinpointStrike, faction.name
|
||||
) # Todo : refactor this legacy nonsense
|
||||
for unit in set(armored_units):
|
||||
for unit in set(faction.ground_units):
|
||||
self.buyArmorCombo.addItem(
|
||||
db.unit_type_name_2(unit) + " [$" + str(db.PRICES[unit]) + "M]",
|
||||
userData=unit,
|
||||
|
||||
@ -90,7 +90,7 @@ class PilotControls(QHBoxLayout):
|
||||
self.selector.currentIndexChanged.connect(self.on_pilot_changed)
|
||||
self.addWidget(self.selector)
|
||||
|
||||
self.player_checkbox = QCheckBox()
|
||||
self.player_checkbox = QCheckBox(text="Player")
|
||||
self.player_checkbox.setToolTip("Checked if this pilot is a player.")
|
||||
self.on_pilot_changed(self.selector.currentIndex())
|
||||
self.addWidget(self.player_checkbox)
|
||||
|
||||
11
resources/campaigns/caen_to_evreux.json
Normal file
11
resources/campaigns/caen_to_evreux.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Normandy - From Caen to Evreux",
|
||||
"theater": "Normandy",
|
||||
"authors": "Khopa",
|
||||
"recommended_player_faction": "Allies 1944",
|
||||
"recommended_enemy_faction": "Germany 1944",
|
||||
"description": "<p>This is a light scenario on the Normandy map.</p><p>August 1944, allied forces are pushing from Caen/Carpiquet to the cities of Lisieux and Evreux.<p>Lisieux is an important logistic hub for the Werhmacht, and Evreux airbase is hosting most of the Luftwaffe forces in the region.</p>",
|
||||
"miz": "caen_to_evreux.miz",
|
||||
"performance": 1,
|
||||
"version": "6.0"
|
||||
}
|
||||
BIN
resources/campaigns/caen_to_evreux.miz
Normal file
BIN
resources/campaigns/caen_to_evreux.miz
Normal file
Binary file not shown.
@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "Normandy - Normandy Small",
|
||||
"theater": "Normandy",
|
||||
"authors": "Khopa",
|
||||
"recommended_player_faction": "Allies 1944",
|
||||
"recommended_enemy_faction": "Germany 1944",
|
||||
"description": "<p>A lighter version of the Normandy 1944 D-Day scenario.</p>",
|
||||
"miz": "normandy_small.miz",
|
||||
"performance": 2
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user