mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add a wrapper type for ground unit info.
This commit is contained in:
parent
8a0824880e
commit
09704b6f37
@ -1,239 +1,17 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
from enum import unique, Enum
|
from enum import unique, Enum
|
||||||
from typing import Type
|
|
||||||
|
|
||||||
from dcs.vehicles import AirDefence, Infantry, Unarmed, Artillery, Armor
|
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
from pydcs_extensions.frenchpack import frenchpack
|
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
class GroundUnitClass(Enum):
|
class GroundUnitClass(Enum):
|
||||||
Tank = (
|
Tank = "Tank"
|
||||||
"Tank",
|
Atgm = "ATGM"
|
||||||
(
|
Ifv = "IFV"
|
||||||
Armor.MBT_T_55,
|
Apc = "APC"
|
||||||
Armor.MBT_T_72B,
|
Artillery = "Artillery"
|
||||||
Armor.MBT_T_72B3,
|
Logistics = "Logistics"
|
||||||
Armor.MBT_T_80U,
|
Recon = "Recon"
|
||||||
Armor.MBT_T_90,
|
Infantry = "Infantry"
|
||||||
Armor.MBT_Leopard_2A4,
|
Shorads = "SHORADS"
|
||||||
Armor.MBT_Leopard_2A4_Trs,
|
Manpads = "MANPADS"
|
||||||
Armor.MBT_Leopard_2A5,
|
|
||||||
Armor.MBT_Leopard_2A6M,
|
|
||||||
Armor.MBT_Leopard_1A3,
|
|
||||||
Armor.MBT_Leclerc,
|
|
||||||
Armor.MBT_Challenger_II,
|
|
||||||
Armor.MBT_Chieftain_Mk_3,
|
|
||||||
Armor.MBT_M1A2_Abrams,
|
|
||||||
Armor.MBT_M60A3_Patton,
|
|
||||||
Armor.MBT_Merkava_IV,
|
|
||||||
Armor.ZTZ_96B,
|
|
||||||
# WW2
|
|
||||||
# Axis
|
|
||||||
Armor.Tk_PzIV_H,
|
|
||||||
Armor.SPG_Sturmpanzer_IV_Brummbar,
|
|
||||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
|
||||||
Armor.HT_Pz_Kpfw_VI_Tiger_I,
|
|
||||||
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II,
|
|
||||||
# Allies
|
|
||||||
Armor.Tk_M4_Sherman,
|
|
||||||
Armor.CT_Centaur_IV,
|
|
||||||
Armor.CT_Cromwell_IV,
|
|
||||||
Armor.HIT_Churchill_VII,
|
|
||||||
# Mods
|
|
||||||
frenchpack.DIM__TOYOTA_BLUE,
|
|
||||||
frenchpack.DIM__TOYOTA_GREEN,
|
|
||||||
frenchpack.DIM__TOYOTA_DESERT,
|
|
||||||
frenchpack.DIM__KAMIKAZE,
|
|
||||||
frenchpack.AMX_30B2,
|
|
||||||
frenchpack.Leclerc_Serie_XXI,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Atgm = (
|
|
||||||
"ATGM",
|
|
||||||
(
|
|
||||||
Armor.ATGM_HMMWV,
|
|
||||||
Armor.ATGM_VAB_Mephisto,
|
|
||||||
Armor.ATGM_Stryker,
|
|
||||||
Armor.IFV_BMP_2,
|
|
||||||
# WW2 (Tank Destroyers)
|
|
||||||
# Axxis
|
|
||||||
Armor.SPG_StuG_III_Ausf__G,
|
|
||||||
Armor.SPG_StuG_IV,
|
|
||||||
Armor.SPG_Jagdpanzer_IV,
|
|
||||||
Armor.SPG_Jagdpanther_G1,
|
|
||||||
Armor.SPG_Sd_Kfz_184_Elefant,
|
|
||||||
# Allies
|
|
||||||
Armor.SPG_M10_GMC,
|
|
||||||
Armor.MT_M4A4_Sherman_Firefly,
|
|
||||||
# Mods
|
|
||||||
frenchpack.VBAE_CRAB_MMP,
|
|
||||||
frenchpack.VAB_MEPHISTO,
|
|
||||||
frenchpack.TRM_2000_PAMELA,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Ifv = (
|
|
||||||
"IFV",
|
|
||||||
(
|
|
||||||
Armor.IFV_BMP_3,
|
|
||||||
Armor.IFV_BMP_2,
|
|
||||||
Armor.IFV_BMP_1,
|
|
||||||
Armor.IFV_Marder,
|
|
||||||
Armor.IFV_Warrior,
|
|
||||||
Armor.SPG_Stryker_MGS,
|
|
||||||
Armor.IFV_M2A2_Bradley,
|
|
||||||
Armor.IFV_BMD_1,
|
|
||||||
Armor.ZBD_04A,
|
|
||||||
# Mods
|
|
||||||
frenchpack.VBAE_CRAB,
|
|
||||||
frenchpack.VAB_T20_13,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Apc = (
|
|
||||||
"APC",
|
|
||||||
(
|
|
||||||
Armor.IFV_M1126_Stryker_ICV,
|
|
||||||
Armor.APC_M113,
|
|
||||||
Armor.APC_BTR_80,
|
|
||||||
Armor.IFV_BTR_82A,
|
|
||||||
Armor.APC_MTLB,
|
|
||||||
Armor.APC_AAV_7_Amphibious,
|
|
||||||
Armor.APC_TPz_Fuchs,
|
|
||||||
Armor.APC_BTR_RD,
|
|
||||||
# WW2
|
|
||||||
Armor.APC_M2A1_Halftrack,
|
|
||||||
Armor.APC_Sd_Kfz_251_Halftrack,
|
|
||||||
# Mods
|
|
||||||
frenchpack.VAB__50,
|
|
||||||
frenchpack.VBL__50,
|
|
||||||
frenchpack.VBL_AANF1,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Artillery = (
|
|
||||||
"Artillery",
|
|
||||||
(
|
|
||||||
Artillery.Grad_MRL_FDDM__FC,
|
|
||||||
Artillery.MLRS_9A52_Smerch_HE_300mm,
|
|
||||||
Artillery.SPH_2S1_Gvozdika_122mm,
|
|
||||||
Artillery.SPH_2S3_Akatsia_152mm,
|
|
||||||
Artillery.MLRS_BM_21_Grad_122mm,
|
|
||||||
Artillery.MLRS_9K57_Uragan_BM_27_220mm,
|
|
||||||
Artillery.SPH_M109_Paladin_155mm,
|
|
||||||
Artillery.MLRS_M270_227mm,
|
|
||||||
Artillery.SPM_2S9_Nona_120mm_M,
|
|
||||||
Artillery.SPH_Dana_vz77_152mm,
|
|
||||||
Artillery.SPH_T155_Firtina_155mm,
|
|
||||||
Artillery.PLZ_05,
|
|
||||||
Artillery.SPH_2S19_Msta_152mm,
|
|
||||||
Artillery.MLRS_9A52_Smerch_CM_300mm,
|
|
||||||
# WW2
|
|
||||||
Artillery.SPG_M12_GMC_155mm,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Logistics = (
|
|
||||||
"Logistics",
|
|
||||||
(
|
|
||||||
Unarmed.Carrier_M30_Cargo,
|
|
||||||
Unarmed.Truck_M818_6x6,
|
|
||||||
Unarmed.Truck_KAMAZ_43101,
|
|
||||||
Unarmed.Truck_Ural_375,
|
|
||||||
Unarmed.Truck_GAZ_66,
|
|
||||||
Unarmed.Truck_GAZ_3307,
|
|
||||||
Unarmed.Truck_GAZ_3308,
|
|
||||||
Unarmed.Truck_Ural_4320_31_Arm_d,
|
|
||||||
Unarmed.Truck_Ural_4320T,
|
|
||||||
Unarmed.Truck_Opel_Blitz,
|
|
||||||
Unarmed.LUV_Kubelwagen_82,
|
|
||||||
Unarmed.Carrier_Sd_Kfz_7_Tractor,
|
|
||||||
Unarmed.LUV_Kettenrad,
|
|
||||||
Unarmed.Car_Willys_Jeep,
|
|
||||||
Unarmed.LUV_Land_Rover_109,
|
|
||||||
Unarmed.Truck_Land_Rover_101_FC,
|
|
||||||
# Mods
|
|
||||||
frenchpack.VBL,
|
|
||||||
frenchpack.VAB,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Recon = (
|
|
||||||
"Recon",
|
|
||||||
(
|
|
||||||
Armor.Scout_HMMWV,
|
|
||||||
Armor.Scout_Cobra,
|
|
||||||
Armor.LT_PT_76,
|
|
||||||
Armor.IFV_LAV_25,
|
|
||||||
Armor.Scout_BRDM_2,
|
|
||||||
# WW2
|
|
||||||
Armor.LT_Mk_VII_Tetrarch,
|
|
||||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
|
||||||
Armor.Car_M8_Greyhound_Armored,
|
|
||||||
Armor.Car_Daimler_Armored,
|
|
||||||
# Mods
|
|
||||||
frenchpack.ERC_90,
|
|
||||||
frenchpack.AMX_10RCR,
|
|
||||||
frenchpack.AMX_10RCR_SEPAR,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Infantry = (
|
|
||||||
"Infantry",
|
|
||||||
(
|
|
||||||
Infantry.Insurgent_AK_74,
|
|
||||||
Infantry.Infantry_AK_74,
|
|
||||||
Infantry.Infantry_M1_Garand,
|
|
||||||
Infantry.Infantry_Mauser_98,
|
|
||||||
Infantry.Infantry_SMLE_No_4_Mk_1,
|
|
||||||
Infantry.Infantry_M4_Georgia,
|
|
||||||
Infantry.Infantry_AK_74_Rus,
|
|
||||||
Infantry.Paratrooper_AKS,
|
|
||||||
Infantry.Paratrooper_RPG_16,
|
|
||||||
Infantry.Infantry_M249,
|
|
||||||
Infantry.Infantry_M4,
|
|
||||||
Infantry.Infantry_RPG,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
Shorads = (
|
|
||||||
"SHORADS",
|
|
||||||
(
|
|
||||||
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,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self, class_name: str, unit_list: tuple[Type[VehicleType], ...]
|
|
||||||
) -> None:
|
|
||||||
self.class_name = class_name
|
|
||||||
self.unit_list = unit_list
|
|
||||||
|
|
||||||
def __contains__(self, unit_type: Type[VehicleType]) -> bool:
|
|
||||||
return unit_type in self.unit_list
|
|
||||||
|
|||||||
391
game/db.py
391
game/db.py
@ -1,8 +1,7 @@
|
|||||||
import json
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Optional, Type, Union
|
from typing import Optional, Type, Union
|
||||||
|
|
||||||
from dcs.countries import country_dict
|
from dcs.countries import country_dict
|
||||||
from dcs.helicopters import (
|
from dcs.helicopters import (
|
||||||
@ -21,8 +20,6 @@ from dcs.planes import (
|
|||||||
plane_map,
|
plane_map,
|
||||||
)
|
)
|
||||||
from dcs.ships import (
|
from dcs.ships import (
|
||||||
Boat_Armed_Hi_speed,
|
|
||||||
Bulker_Yakushev,
|
|
||||||
CVN_71_Theodore_Roosevelt,
|
CVN_71_Theodore_Roosevelt,
|
||||||
CVN_72_Abraham_Lincoln,
|
CVN_72_Abraham_Lincoln,
|
||||||
CVN_73_George_Washington,
|
CVN_73_George_Washington,
|
||||||
@ -30,21 +27,12 @@ from dcs.ships import (
|
|||||||
CVN_75_Harry_S__Truman,
|
CVN_75_Harry_S__Truman,
|
||||||
CV_1143_5_Admiral_Kuznetsov,
|
CV_1143_5_Admiral_Kuznetsov,
|
||||||
CV_1143_5_Admiral_Kuznetsov_2017,
|
CV_1143_5_Admiral_Kuznetsov_2017,
|
||||||
Cargo_Ivanov,
|
|
||||||
LHA_1_Tarawa,
|
|
||||||
Tanker_Elnya_160,
|
|
||||||
ship_map,
|
ship_map,
|
||||||
)
|
)
|
||||||
from dcs.terrain.terrain import Airport
|
from dcs.terrain.terrain import Airport
|
||||||
from dcs.unit import Ship, Unit, Vehicle
|
|
||||||
from dcs.unitgroup import ShipGroup, StaticGroup
|
from dcs.unitgroup import ShipGroup, StaticGroup
|
||||||
from dcs.unittype import UnitType, VehicleType
|
from dcs.unittype import UnitType
|
||||||
from dcs.vehicles import (
|
from dcs.vehicles import (
|
||||||
AirDefence,
|
|
||||||
Armor,
|
|
||||||
Artillery,
|
|
||||||
Infantry,
|
|
||||||
Unarmed,
|
|
||||||
vehicle_map,
|
vehicle_map,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -250,271 +238,6 @@ For example, player accessible Hornet is called `FA_18C_hornet`, and MANPAD Igla
|
|||||||
# to be cheap enough to repair with a single turn's income.
|
# to be cheap enough to repair with a single turn's income.
|
||||||
RUNWAY_REPAIR_COST = 100
|
RUNWAY_REPAIR_COST = 100
|
||||||
|
|
||||||
"""
|
|
||||||
Prices for the aircraft.
|
|
||||||
This defines both price for the player (although only aircraft listed in CAP/CAS/Transport/Armor/AirDefense roles will be purchasable)
|
|
||||||
and prioritization for the enemy (i.e. less important bases will receive units with lower price)
|
|
||||||
"""
|
|
||||||
PRICES = {
|
|
||||||
# armor
|
|
||||||
Armor.APC_MTLB: 4,
|
|
||||||
Artillery.Grad_MRL_FDDM__FC: 4,
|
|
||||||
Armor.Scout_BRDM_2: 6,
|
|
||||||
Armor.APC_BTR_RD: 6,
|
|
||||||
Armor.APC_BTR_80: 8,
|
|
||||||
Armor.IFV_BTR_82A: 10,
|
|
||||||
Armor.MBT_T_55: 18,
|
|
||||||
Armor.MBT_T_72B: 20,
|
|
||||||
Armor.MBT_T_72B3: 25,
|
|
||||||
Armor.MBT_T_80U: 25,
|
|
||||||
Armor.MBT_T_90: 30,
|
|
||||||
Armor.IFV_BMD_1: 8,
|
|
||||||
Armor.IFV_BMP_1: 14,
|
|
||||||
Armor.IFV_BMP_2: 16,
|
|
||||||
Armor.IFV_BMP_3: 18,
|
|
||||||
Armor.LT_PT_76: 9,
|
|
||||||
Armor.ZBD_04A: 12,
|
|
||||||
Armor.ZTZ_96B: 30,
|
|
||||||
Armor.Scout_Cobra: 4,
|
|
||||||
Armor.APC_M113: 6,
|
|
||||||
Armor.Scout_HMMWV: 2,
|
|
||||||
Armor.ATGM_HMMWV: 8,
|
|
||||||
Armor.ATGM_VAB_Mephisto: 12,
|
|
||||||
Armor.IFV_M2A2_Bradley: 12,
|
|
||||||
Armor.IFV_M1126_Stryker_ICV: 10,
|
|
||||||
Armor.SPG_Stryker_MGS: 14,
|
|
||||||
Armor.ATGM_Stryker: 12,
|
|
||||||
Armor.MBT_M60A3_Patton: 16,
|
|
||||||
Armor.MBT_M1A2_Abrams: 25,
|
|
||||||
Armor.MBT_Leclerc: 25,
|
|
||||||
Armor.MBT_Leopard_1A3: 18,
|
|
||||||
Armor.MBT_Leopard_2A4: 20,
|
|
||||||
Armor.MBT_Leopard_2A4_Trs: 20,
|
|
||||||
Armor.MBT_Leopard_2A5: 22,
|
|
||||||
Armor.MBT_Leopard_2A6M: 25,
|
|
||||||
Armor.MBT_Merkava_IV: 25,
|
|
||||||
Armor.APC_TPz_Fuchs: 5,
|
|
||||||
Armor.MBT_Challenger_II: 25,
|
|
||||||
Armor.MBT_Chieftain_Mk_3: 20,
|
|
||||||
Armor.IFV_Marder: 10,
|
|
||||||
Armor.IFV_Warrior: 10,
|
|
||||||
Armor.IFV_LAV_25: 7,
|
|
||||||
Armor.APC_AAV_7_Amphibious: 10,
|
|
||||||
Artillery.MLRS_M270_227mm: 55,
|
|
||||||
Artillery.SPH_M109_Paladin_155mm: 25,
|
|
||||||
Artillery.SPM_2S9_Nona_120mm_M: 12,
|
|
||||||
Artillery.SPH_2S1_Gvozdika_122mm: 18,
|
|
||||||
Artillery.SPH_2S3_Akatsia_152mm: 24,
|
|
||||||
Artillery.SPH_2S19_Msta_152mm: 30,
|
|
||||||
Artillery.MLRS_BM_21_Grad_122mm: 15,
|
|
||||||
Artillery.MLRS_9K57_Uragan_BM_27_220mm: 50,
|
|
||||||
Artillery.MLRS_9A52_Smerch_HE_300mm: 40,
|
|
||||||
Artillery.Mortar_2B11_120mm: 4,
|
|
||||||
Artillery.SPH_Dana_vz77_152mm: 26,
|
|
||||||
Artillery.PLZ_05: 25,
|
|
||||||
Artillery.SPH_T155_Firtina_155mm: 28,
|
|
||||||
Artillery.MLRS_9A52_Smerch_CM_300mm: 60,
|
|
||||||
Unarmed.LUV_UAZ_469_Jeep: 3,
|
|
||||||
Unarmed.Truck_Ural_375: 3,
|
|
||||||
Unarmed.Truck_GAZ_3307: 2,
|
|
||||||
Infantry.Infantry_M4: 1,
|
|
||||||
Infantry.Infantry_AK_74: 1,
|
|
||||||
Unarmed.Truck_M818_6x6: 3,
|
|
||||||
Unarmed.LUV_Land_Rover_109: 1,
|
|
||||||
Unarmed.Truck_GAZ_3308: 1,
|
|
||||||
Unarmed.Truck_GAZ_66: 1,
|
|
||||||
Unarmed.Truck_KAMAZ_43101: 1,
|
|
||||||
Unarmed.Truck_Land_Rover_101_FC: 1,
|
|
||||||
Unarmed.Truck_Ural_4320_31_Arm_d: 1,
|
|
||||||
Unarmed.Truck_Ural_4320T: 1,
|
|
||||||
# WW2
|
|
||||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G: 24,
|
|
||||||
Armor.Tk_PzIV_H: 16,
|
|
||||||
Armor.HT_Pz_Kpfw_VI_Tiger_I: 24,
|
|
||||||
Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II: 26,
|
|
||||||
Armor.SPG_Jagdpanther_G1: 18,
|
|
||||||
Armor.SPG_Jagdpanzer_IV: 11,
|
|
||||||
Armor.SPG_Sd_Kfz_184_Elefant: 18,
|
|
||||||
Armor.APC_Sd_Kfz_251_Halftrack: 4,
|
|
||||||
Armor.IFV_Sd_Kfz_234_2_Puma: 8,
|
|
||||||
Armor.Tk_M4_Sherman: 12,
|
|
||||||
Armor.MT_M4A4_Sherman_Firefly: 16,
|
|
||||||
Armor.CT_Cromwell_IV: 12,
|
|
||||||
Unarmed.Carrier_M30_Cargo: 2,
|
|
||||||
Armor.APC_M2A1_Halftrack: 4,
|
|
||||||
Armor.CT_Centaur_IV: 10,
|
|
||||||
Armor.HIT_Churchill_VII: 16,
|
|
||||||
Armor.Car_M8_Greyhound_Armored: 8,
|
|
||||||
Armor.SPG_M10_GMC: 14,
|
|
||||||
Armor.SPG_StuG_III_Ausf__G: 12,
|
|
||||||
Armor.SPG_StuG_IV: 14,
|
|
||||||
Artillery.SPG_M12_GMC_155mm: 10,
|
|
||||||
Armor.SPG_Sturmpanzer_IV_Brummbar: 10,
|
|
||||||
Armor.Car_Daimler_Armored: 8,
|
|
||||||
Armor.LT_Mk_VII_Tetrarch: 8,
|
|
||||||
Unarmed.Tractor_M4_Hi_Speed: 2,
|
|
||||||
Unarmed.Carrier_Sd_Kfz_7_Tractor: 1,
|
|
||||||
Unarmed.LUV_Kettenrad: 1,
|
|
||||||
Unarmed.LUV_Kubelwagen_82: 1,
|
|
||||||
Unarmed.Truck_Opel_Blitz: 1,
|
|
||||||
Unarmed.Truck_Bedford: 1,
|
|
||||||
Unarmed.Truck_GMC_Jimmy_6x6_Truck: 1,
|
|
||||||
Unarmed.Car_Willys_Jeep: 1,
|
|
||||||
# ship
|
|
||||||
CV_1143_5_Admiral_Kuznetsov: 100,
|
|
||||||
CVN_74_John_C__Stennis: 100,
|
|
||||||
LHA_1_Tarawa: 50,
|
|
||||||
Bulker_Yakushev: 10,
|
|
||||||
Boat_Armed_Hi_speed: 10,
|
|
||||||
Cargo_Ivanov: 10,
|
|
||||||
Tanker_Elnya_160: 10,
|
|
||||||
# Air Defence units
|
|
||||||
AirDefence.SAM_SA_19_Tunguska_Grison: 30,
|
|
||||||
AirDefence.SAM_SA_6_Kub_Gainful_TEL: 20,
|
|
||||||
AirDefence.SAM_SA_3_S_125_Goa_LN: 6,
|
|
||||||
AirDefence.SAM_SA_11_Buk_Gadfly_Fire_Dome_TEL: 30,
|
|
||||||
AirDefence.SAM_SA_11_Buk_Gadfly_C2: 25,
|
|
||||||
AirDefence.SAM_SA_11_Buk_Gadfly_Snow_Drift_SR: 28,
|
|
||||||
AirDefence.SAM_SA_8_Osa_Gecko_TEL: 28,
|
|
||||||
AirDefence.SAM_SA_15_Tor_Gauntlet: 40,
|
|
||||||
AirDefence.SAM_SA_13_Strela_10M3_Gopher_TEL: 16,
|
|
||||||
AirDefence.SAM_SA_9_Strela_1_Gaskin_TEL: 12,
|
|
||||||
AirDefence.SAM_SA_8_Osa_LD_9T217: 22,
|
|
||||||
AirDefence.SAM_Patriot_CR__AMG_AN_MRC_137: 35,
|
|
||||||
AirDefence.SAM_Patriot_ECS: 30,
|
|
||||||
AirDefence.SPAAA_Gepard: 24,
|
|
||||||
AirDefence.SAM_Hawk_Platoon_Command_Post__PCP: 14,
|
|
||||||
AirDefence.SPAAA_Vulcan_M163: 10,
|
|
||||||
AirDefence.SAM_Hawk_LN_M192: 8,
|
|
||||||
AirDefence.SAM_Chaparral_M48: 16,
|
|
||||||
AirDefence.SAM_Linebacker___Bradley_M6: 18,
|
|
||||||
AirDefence.SAM_Patriot_LN: 15,
|
|
||||||
AirDefence.SAM_Avenger__Stinger: 20,
|
|
||||||
AirDefence.SAM_Patriot_EPP_III: 15,
|
|
||||||
AirDefence.SAM_Patriot_C2_ICC: 18,
|
|
||||||
AirDefence.SAM_Roland_ADS: 12,
|
|
||||||
AirDefence.MANPADS_Stinger: 6,
|
|
||||||
AirDefence.MANPADS_Stinger_C2_Desert: 4,
|
|
||||||
AirDefence.MANPADS_Stinger_C2: 4,
|
|
||||||
AirDefence.SPAAA_ZSU_23_4_Shilka_Gun_Dish: 10,
|
|
||||||
AirDefence.SPAAA_ZSU_57_2: 12,
|
|
||||||
AirDefence.AAA_ZU_23_Closed_Emplacement: 6,
|
|
||||||
AirDefence.AAA_ZU_23_Emplacement: 6,
|
|
||||||
AirDefence.SPAAA_ZU_23_2_Mounted_Ural_375: 7,
|
|
||||||
AirDefence.AAA_ZU_23_Insurgent_Closed_Emplacement: 6,
|
|
||||||
AirDefence.SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375: 7,
|
|
||||||
AirDefence.AAA_ZU_23_Insurgent_Emplacement: 6,
|
|
||||||
AirDefence.MANPADS_SA_18_Igla_Grouse: 10,
|
|
||||||
AirDefence.MANPADS_SA_18_Igla_Grouse_C2: 8,
|
|
||||||
AirDefence.MANPADS_SA_18_Igla_S_Grouse: 12,
|
|
||||||
AirDefence.MANPADS_SA_18_Igla_S_Grouse_C2: 8,
|
|
||||||
AirDefence.EWR_1L13: 30,
|
|
||||||
AirDefence.SAM_SA_6_Kub_Straight_Flush_STR: 22,
|
|
||||||
AirDefence.EWR_55G6: 30,
|
|
||||||
AirDefence.MCC_SR_Sborka_Dog_Ear_SR: 10,
|
|
||||||
AirDefence.SAM_Hawk_TR__AN_MPQ_46: 14,
|
|
||||||
AirDefence.SAM_Hawk_SR__AN_MPQ_50: 18,
|
|
||||||
AirDefence.SAM_Patriot_STR: 22,
|
|
||||||
AirDefence.SAM_Hawk_CWAR_AN_MPQ_55: 20,
|
|
||||||
AirDefence.SAM_P19_Flat_Face_SR__SA_2_3: 14,
|
|
||||||
AirDefence.SAM_Roland_EWR: 16,
|
|
||||||
AirDefence.SAM_SA_3_S_125_Low_Blow_TR: 14,
|
|
||||||
AirDefence.SAM_SA_2_S_75_Guideline_LN: 8,
|
|
||||||
AirDefence.SAM_SA_2_S_75_Fan_Song_TR: 12,
|
|
||||||
AirDefence.SAM_Rapier_LN: 6,
|
|
||||||
AirDefence.SAM_Rapier_Tracker: 6,
|
|
||||||
AirDefence.SAM_Rapier_Blindfire_TR: 8,
|
|
||||||
AirDefence.HQ_7_Self_Propelled_LN: 20,
|
|
||||||
AirDefence.HQ_7_Self_Propelled_STR: 24,
|
|
||||||
AirDefence.AAA_8_8cm_Flak_18: 6,
|
|
||||||
AirDefence.AAA_Flak_38_20mm: 6,
|
|
||||||
AirDefence.AAA_8_8cm_Flak_36: 8,
|
|
||||||
AirDefence.AAA_8_8cm_Flak_37: 9,
|
|
||||||
AirDefence.AAA_Flak_Vierling_38_Quad_20mm: 5,
|
|
||||||
AirDefence.AAA_SP_Kdo_G_40: 8,
|
|
||||||
AirDefence.SL_Flakscheinwerfer_37: 4,
|
|
||||||
AirDefence.PU_Maschinensatz_33: 10,
|
|
||||||
AirDefence.AAA_8_8cm_Flak_41: 10,
|
|
||||||
AirDefence.EWR_FuMG_401_Freya_LZ: 25,
|
|
||||||
AirDefence.AAA_Bofors_40mm: 8,
|
|
||||||
AirDefence.AAA_S_60_57mm: 8,
|
|
||||||
AirDefence.AAA_M1_37mm: 7,
|
|
||||||
AirDefence.AAA_M45_Quadmount_HB_12_7mm: 4,
|
|
||||||
AirDefence.AAA_QF_3_7: 10,
|
|
||||||
# FRENCH PACK MOD
|
|
||||||
frenchpack.AMX_10RCR: 10,
|
|
||||||
frenchpack.AMX_10RCR_SEPAR: 12,
|
|
||||||
frenchpack.ERC_90: 12,
|
|
||||||
frenchpack.MO_120_RT: 10,
|
|
||||||
frenchpack._53T2: 4,
|
|
||||||
frenchpack.TRM_2000: 4,
|
|
||||||
frenchpack.TRM_2000_Fuel: 4,
|
|
||||||
frenchpack.TRM_2000_53T2: 8,
|
|
||||||
frenchpack.TRM_2000_PAMELA: 14,
|
|
||||||
frenchpack.VAB_MEDICAL: 8,
|
|
||||||
frenchpack.VAB: 6,
|
|
||||||
frenchpack.VAB__50: 4,
|
|
||||||
frenchpack.VAB_T20_13: 6,
|
|
||||||
frenchpack.VAB_MEPHISTO: 8,
|
|
||||||
frenchpack.VAB_MORTIER: 10,
|
|
||||||
frenchpack.VBL__50: 4,
|
|
||||||
frenchpack.VBL_AANF1: 2,
|
|
||||||
frenchpack.VBL: 1,
|
|
||||||
frenchpack.VBAE_CRAB: 8,
|
|
||||||
frenchpack.VBAE_CRAB_MMP: 12,
|
|
||||||
frenchpack.AMX_30B2: 18,
|
|
||||||
frenchpack.Tracma_TD_1500: 2,
|
|
||||||
frenchpack.Infantry_Soldier_JTAC: 1,
|
|
||||||
frenchpack.Leclerc_Serie_XXI: 35,
|
|
||||||
frenchpack.DIM__TOYOTA_BLUE: 2,
|
|
||||||
frenchpack.DIM__TOYOTA_GREEN: 2,
|
|
||||||
frenchpack.DIM__TOYOTA_DESERT: 2,
|
|
||||||
frenchpack.DIM__KAMIKAZE: 6,
|
|
||||||
# SA-10
|
|
||||||
AirDefence.SAM_SA_10_S_300_Grumble_C2: 18,
|
|
||||||
AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR: 24,
|
|
||||||
AirDefence.SAM_SA_10_S_300_Grumble_Clam_Shell_SR: 30,
|
|
||||||
AirDefence.SAM_SA_10_S_300_Grumble_Big_Bird_SR: 30,
|
|
||||||
AirDefence.SAM_SA_10_S_300_Grumble_TEL_C: 22,
|
|
||||||
AirDefence.SAM_SA_10_S_300_Grumble_TEL_D: 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,
|
|
||||||
highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85CE: 24,
|
|
||||||
highdigitsams.SAM_SA_10__5V55RUD__S_300PS_LN_5P85DE: 24,
|
|
||||||
highdigitsams.SAM_SA_10B_S_300PS_30N6_TR: 26,
|
|
||||||
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,
|
|
||||||
highdigitsams.SAM_SA_20_S_300PMU1_SR_5N66E: 38,
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Units separated by country.
|
Units separated by country.
|
||||||
country : DCS Country name
|
country : DCS Country name
|
||||||
@ -620,107 +343,6 @@ def upgrade_to_supercarrier(unit, name: str):
|
|||||||
return unit
|
return unit
|
||||||
|
|
||||||
|
|
||||||
MANPADS: List[Type[VehicleType]] = [
|
|
||||||
AirDefence.MANPADS_SA_18_Igla_Grouse,
|
|
||||||
AirDefence.MANPADS_SA_18_Igla_S_Grouse,
|
|
||||||
AirDefence.MANPADS_Stinger,
|
|
||||||
]
|
|
||||||
|
|
||||||
INFANTRY: List[VehicleType] = [
|
|
||||||
Infantry.Paratrooper_AKS,
|
|
||||||
Infantry.Paratrooper_AKS,
|
|
||||||
Infantry.Paratrooper_AKS,
|
|
||||||
Infantry.Paratrooper_AKS,
|
|
||||||
Infantry.Paratrooper_AKS,
|
|
||||||
Infantry.Infantry_RPG,
|
|
||||||
Infantry.Infantry_M4,
|
|
||||||
Infantry.Infantry_M4,
|
|
||||||
Infantry.Infantry_M4,
|
|
||||||
Infantry.Infantry_M4,
|
|
||||||
Infantry.Infantry_M4,
|
|
||||||
Infantry.Infantry_M249,
|
|
||||||
Artillery.Mortar_2B11_120mm,
|
|
||||||
Infantry.Infantry_AK_74,
|
|
||||||
Infantry.Infantry_AK_74,
|
|
||||||
Infantry.Infantry_AK_74,
|
|
||||||
Infantry.Infantry_AK_74,
|
|
||||||
Infantry.Infantry_AK_74,
|
|
||||||
Infantry.Paratrooper_RPG_16,
|
|
||||||
Infantry.Infantry_M4_Georgia,
|
|
||||||
Infantry.Infantry_M4_Georgia,
|
|
||||||
Infantry.Infantry_M4_Georgia,
|
|
||||||
Infantry.Infantry_M4_Georgia,
|
|
||||||
Infantry.Infantry_AK_74_Rus,
|
|
||||||
Infantry.Infantry_AK_74_Rus,
|
|
||||||
Infantry.Infantry_AK_74_Rus,
|
|
||||||
Infantry.Infantry_AK_74_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_Mauser_98,
|
|
||||||
Infantry.Infantry_M1_Garand,
|
|
||||||
Infantry.Infantry_M1_Garand,
|
|
||||||
Infantry.Infantry_M1_Garand,
|
|
||||||
Infantry.Insurgent_AK_74,
|
|
||||||
Infantry.Insurgent_AK_74,
|
|
||||||
Infantry.Insurgent_AK_74,
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def find_manpad(country_name: str) -> List[VehicleType]:
|
|
||||||
return [x for x in MANPADS if x in FACTIONS[country_name].infantry_units]
|
|
||||||
|
|
||||||
|
|
||||||
def find_infantry(country_name: str, allow_manpad: bool = False) -> List[VehicleType]:
|
|
||||||
if allow_manpad:
|
|
||||||
inf = INFANTRY + MANPADS
|
|
||||||
else:
|
|
||||||
inf = INFANTRY
|
|
||||||
return [x for x in inf if x in FACTIONS[country_name].infantry_units]
|
|
||||||
|
|
||||||
|
|
||||||
def unit_type_name(unit_type) -> str:
|
|
||||||
return unit_type.id and unit_type.id or unit_type.name
|
|
||||||
|
|
||||||
|
|
||||||
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: Type[UnitType], request_type: str
|
|
||||||
) -> str:
|
|
||||||
original_name = unit_type.name and unit_type.name or unit_type.id
|
|
||||||
default_value = None
|
|
||||||
faction_value = None
|
|
||||||
with UNITINFOTEXT_PATH.open("r", encoding="utf-8") as fdata:
|
|
||||||
data = json.load(fdata)
|
|
||||||
type_exists = data.get(unit_type.id)
|
|
||||||
if type_exists is not None:
|
|
||||||
for faction in type_exists:
|
|
||||||
if default_value is None:
|
|
||||||
default_exists = faction.get("default")
|
|
||||||
if default_exists is not None:
|
|
||||||
default_value = default_exists.get(request_type)
|
|
||||||
if faction_value is None:
|
|
||||||
faction_exists = faction.get(country_name)
|
|
||||||
if faction_exists is not None:
|
|
||||||
faction_value = faction_exists.get(request_type)
|
|
||||||
if default_value is None:
|
|
||||||
if request_type == "text":
|
|
||||||
return "WIP - This unit doesn't have any description text yet."
|
|
||||||
if request_type == "name":
|
|
||||||
return original_name
|
|
||||||
else:
|
|
||||||
return "Unknown"
|
|
||||||
if faction_value is None:
|
|
||||||
return default_value
|
|
||||||
return faction_value
|
|
||||||
|
|
||||||
|
|
||||||
def unit_type_from_name(name: str) -> Optional[Type[UnitType]]:
|
def unit_type_from_name(name: str) -> Optional[Type[UnitType]]:
|
||||||
if name in vehicle_map:
|
if name in vehicle_map:
|
||||||
return vehicle_map[name]
|
return vehicle_map[name]
|
||||||
@ -734,15 +356,6 @@ def unit_type_from_name(name: str) -> Optional[Type[UnitType]]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def unit_type_of(unit: Unit) -> UnitType:
|
|
||||||
if isinstance(unit, Vehicle):
|
|
||||||
return vehicle_map[unit.type]
|
|
||||||
elif isinstance(unit, Ship):
|
|
||||||
return ship_map[unit.type]
|
|
||||||
else:
|
|
||||||
return unit.type
|
|
||||||
|
|
||||||
|
|
||||||
def country_id_from_name(name):
|
def country_id_from_name(name):
|
||||||
for k, v in country_dict.items():
|
for k, v in country_dict.items():
|
||||||
if v.name == name:
|
if v.name == name:
|
||||||
|
|||||||
@ -12,6 +12,7 @@ from dcs.helicopters import helicopter_map
|
|||||||
from dcs.planes import plane_map
|
from dcs.planes import plane_map
|
||||||
from dcs.unittype import FlyingType
|
from dcs.unittype import FlyingType
|
||||||
|
|
||||||
|
from game.dcs.unittype import UnitType
|
||||||
from game.radio.channels import (
|
from game.radio.channels import (
|
||||||
ChannelNamer,
|
ChannelNamer,
|
||||||
RadioChannelAllocator,
|
RadioChannelAllocator,
|
||||||
@ -90,15 +91,7 @@ class RadioConfig:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AircraftType:
|
class AircraftType(UnitType[FlyingType]):
|
||||||
dcs_unit_type: Type[FlyingType]
|
|
||||||
name: str
|
|
||||||
description: str
|
|
||||||
year_introduced: str
|
|
||||||
country_of_origin: str
|
|
||||||
manufacturer: str
|
|
||||||
role: str
|
|
||||||
price: int
|
|
||||||
carrier_capable: bool
|
carrier_capable: bool
|
||||||
lha_capable: bool
|
lha_capable: bool
|
||||||
always_keeps_gun: bool
|
always_keeps_gun: bool
|
||||||
|
|||||||
95
game/dcs/groundunittype.py
Normal file
95
game/dcs/groundunittype.py
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Type, Optional, ClassVar, Iterator
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
from dcs.unittype import VehicleType
|
||||||
|
from dcs.vehicles import vehicle_map
|
||||||
|
|
||||||
|
from game.data.groundunitclass import GroundUnitClass
|
||||||
|
from game.dcs.unittype import UnitType
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class GroundUnitType(UnitType[VehicleType]):
|
||||||
|
unit_class: Optional[GroundUnitClass]
|
||||||
|
spawn_weight: int
|
||||||
|
|
||||||
|
_by_name: ClassVar[dict[str, GroundUnitType]] = {}
|
||||||
|
_by_unit_type: ClassVar[
|
||||||
|
dict[Type[VehicleType], list[GroundUnitType]]
|
||||||
|
] = defaultdict(list)
|
||||||
|
_loaded: ClassVar[bool] = False
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def dcs_id(self) -> str:
|
||||||
|
return self.dcs_unit_type.id
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def register(cls, aircraft_type: GroundUnitType) -> None:
|
||||||
|
cls._by_name[aircraft_type.name] = aircraft_type
|
||||||
|
cls._by_unit_type[aircraft_type.dcs_unit_type].append(aircraft_type)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def named(cls, name: str) -> GroundUnitType:
|
||||||
|
if not cls._loaded:
|
||||||
|
cls._load_all()
|
||||||
|
return cls._by_name[name]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def for_dcs_type(cls, dcs_unit_type: Type[VehicleType]) -> Iterator[GroundUnitType]:
|
||||||
|
yield from cls._by_unit_type[dcs_unit_type]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _each_unit_type() -> Iterator[Type[VehicleType]]:
|
||||||
|
yield from vehicle_map.values()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _load_all(cls) -> None:
|
||||||
|
for unit_type in cls._each_unit_type():
|
||||||
|
for data in cls._each_variant_of(unit_type):
|
||||||
|
cls.register(data)
|
||||||
|
cls._loaded = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _each_variant_of(cls, vehicle: Type[VehicleType]) -> Iterator[GroundUnitType]:
|
||||||
|
data_path = Path("resources/units/ground_units") / f"{vehicle.id}.yaml"
|
||||||
|
if not data_path.exists():
|
||||||
|
logging.warning(f"No data for {vehicle.id}; it will not be available")
|
||||||
|
return
|
||||||
|
|
||||||
|
with data_path.open() as data_file:
|
||||||
|
data = yaml.safe_load(data_file)
|
||||||
|
|
||||||
|
try:
|
||||||
|
introduction = data["introduced"]
|
||||||
|
if introduction is None:
|
||||||
|
introduction = "N/A"
|
||||||
|
except KeyError:
|
||||||
|
introduction = "No data."
|
||||||
|
|
||||||
|
class_name = data.get("class")
|
||||||
|
unit_class: Optional[GroundUnitClass] = None
|
||||||
|
if class_name is not None:
|
||||||
|
unit_class = GroundUnitClass(class_name)
|
||||||
|
|
||||||
|
for variant in data.get("variants", [vehicle.id]):
|
||||||
|
yield GroundUnitType(
|
||||||
|
dcs_unit_type=vehicle,
|
||||||
|
unit_class=unit_class,
|
||||||
|
spawn_weight=data.get("spawn_weight", 0),
|
||||||
|
name=variant,
|
||||||
|
description=data.get("description", "No data."),
|
||||||
|
year_introduced=introduction,
|
||||||
|
country_of_origin=data.get("origin", "No data."),
|
||||||
|
manufacturer=data.get("manufacturer", "No data."),
|
||||||
|
role=data.get("role", "No data."),
|
||||||
|
price=data.get("price", 1),
|
||||||
|
)
|
||||||
21
game/dcs/unittype.py
Normal file
21
game/dcs/unittype.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import TypeVar, Generic, Type
|
||||||
|
|
||||||
|
from dcs.unittype import UnitType as DcsUnitType
|
||||||
|
|
||||||
|
DcsUnitTypeT = TypeVar("DcsUnitTypeT", bound=DcsUnitType)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class UnitType(Generic[DcsUnitTypeT]):
|
||||||
|
dcs_unit_type: Type[DcsUnitTypeT]
|
||||||
|
name: str
|
||||||
|
description: str
|
||||||
|
year_introduced: str
|
||||||
|
country_of_origin: str
|
||||||
|
manufacturer: str
|
||||||
|
role: str
|
||||||
|
price: int
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.name
|
||||||
@ -14,14 +14,12 @@ from typing import (
|
|||||||
Dict,
|
Dict,
|
||||||
Iterator,
|
Iterator,
|
||||||
List,
|
List,
|
||||||
Type,
|
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
|
|
||||||
from dcs.unittype import UnitType
|
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.theater import Airfield, ControlPoint
|
from game.theater import Airfield, ControlPoint
|
||||||
from game.transfers import CargoShip
|
from game.transfers import CargoShip
|
||||||
from game.unitmap import (
|
from game.unitmap import (
|
||||||
@ -183,8 +181,8 @@ class Debriefing:
|
|||||||
def casualty_count(self, control_point: ControlPoint) -> int:
|
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[GroundUnitType, int]:
|
||||||
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
|
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||||
if player:
|
if player:
|
||||||
losses = self.ground_losses.player_front_line
|
losses = self.ground_losses.player_front_line
|
||||||
else:
|
else:
|
||||||
@ -193,8 +191,8 @@ class Debriefing:
|
|||||||
losses_by_type[loss.unit_type] += 1
|
losses_by_type[loss.unit_type] += 1
|
||||||
return losses_by_type
|
return losses_by_type
|
||||||
|
|
||||||
def convoy_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
|
def convoy_losses_by_type(self, player: bool) -> dict[GroundUnitType, int]:
|
||||||
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
|
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||||
if player:
|
if player:
|
||||||
losses = self.ground_losses.player_convoy
|
losses = self.ground_losses.player_convoy
|
||||||
else:
|
else:
|
||||||
@ -203,8 +201,8 @@ class Debriefing:
|
|||||||
losses_by_type[loss.unit_type] += 1
|
losses_by_type[loss.unit_type] += 1
|
||||||
return losses_by_type
|
return losses_by_type
|
||||||
|
|
||||||
def cargo_ship_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
|
def cargo_ship_losses_by_type(self, player: bool) -> dict[GroundUnitType, int]:
|
||||||
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
|
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||||
if player:
|
if player:
|
||||||
ships = self.ground_losses.player_cargo_ships
|
ships = self.ground_losses.player_cargo_ships
|
||||||
else:
|
else:
|
||||||
@ -214,8 +212,8 @@ class Debriefing:
|
|||||||
losses_by_type[unit_type] += count
|
losses_by_type[unit_type] += count
|
||||||
return losses_by_type
|
return losses_by_type
|
||||||
|
|
||||||
def airlift_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
|
def airlift_losses_by_type(self, player: bool) -> dict[GroundUnitType, int]:
|
||||||
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
|
losses_by_type: dict[GroundUnitType, int] = defaultdict(int)
|
||||||
if player:
|
if player:
|
||||||
losses = self.ground_losses.player_airlifts
|
losses = self.ground_losses.player_airlifts
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -14,6 +14,7 @@ from game.operation.operation import Operation
|
|||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from gen import AirTaskingOrder
|
from gen import AirTaskingOrder
|
||||||
from gen.ground_forces.combat_stance import CombatStance
|
from gen.ground_forces.combat_stance import CombatStance
|
||||||
|
from ..dcs.groundunittype import GroundUnitType
|
||||||
from ..unitmap import UnitMap
|
from ..unitmap import UnitMap
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -439,7 +440,7 @@ class Event:
|
|||||||
|
|
||||||
# Also transfer pending deliveries.
|
# Also transfer pending deliveries.
|
||||||
for unit_type, count in source.pending_unit_deliveries.units.items():
|
for unit_type, count in source.pending_unit_deliveries.units.items():
|
||||||
if not issubclass(unit_type, VehicleType):
|
if not isinstance(unit_type, GroundUnitType):
|
||||||
continue
|
continue
|
||||||
if count <= 0:
|
if count <= 0:
|
||||||
# Don't transfer *sales*...
|
# Don't transfer *sales*...
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional, Dict, Type, List, Any, cast, Iterator
|
from typing import Optional, Dict, Type, List, Any, Iterator
|
||||||
|
|
||||||
import dcs
|
import dcs
|
||||||
from dcs.countries import country_dict
|
from dcs.countries import country_dict
|
||||||
from dcs.planes import plane_map
|
from dcs.unittype import ShipType, UnitType
|
||||||
from dcs.unittype import FlyingType, ShipType, VehicleType, UnitType
|
|
||||||
from dcs.vehicles import Armor, Unarmed, Infantry, Artillery, AirDefence
|
|
||||||
|
|
||||||
from game.data.building_data import (
|
from game.data.building_data import (
|
||||||
WW2_ALLIES_BUILDINGS,
|
WW2_ALLIES_BUILDINGS,
|
||||||
@ -24,7 +23,7 @@ from game.data.doctrine import (
|
|||||||
)
|
)
|
||||||
from game.data.groundunitclass import GroundUnitClass
|
from game.data.groundunitclass import GroundUnitClass
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
from pydcs_extensions.mod_units import MODDED_VEHICLES
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -55,16 +54,16 @@ class Faction:
|
|||||||
tankers: List[AircraftType] = field(default_factory=list)
|
tankers: List[AircraftType] = field(default_factory=list)
|
||||||
|
|
||||||
# Available frontline units
|
# Available frontline units
|
||||||
frontline_units: List[Type[VehicleType]] = field(default_factory=list)
|
frontline_units: List[GroundUnitType] = field(default_factory=list)
|
||||||
|
|
||||||
# Available artillery units
|
# Available artillery units
|
||||||
artillery_units: List[Type[VehicleType]] = field(default_factory=list)
|
artillery_units: List[GroundUnitType] = field(default_factory=list)
|
||||||
|
|
||||||
# Infantry units used
|
# Infantry units used
|
||||||
infantry_units: List[Type[VehicleType]] = field(default_factory=list)
|
infantry_units: List[GroundUnitType] = field(default_factory=list)
|
||||||
|
|
||||||
# Logistics units used
|
# Logistics units used
|
||||||
logistics_units: List[Type[VehicleType]] = field(default_factory=list)
|
logistics_units: List[GroundUnitType] = field(default_factory=list)
|
||||||
|
|
||||||
# Possible SAMS site generators for this faction
|
# Possible SAMS site generators for this faction
|
||||||
air_defenses: List[str] = field(default_factory=list)
|
air_defenses: List[str] = field(default_factory=list)
|
||||||
@ -135,15 +134,11 @@ class Faction:
|
|||||||
#: both will use it.
|
#: both will use it.
|
||||||
unrestricted_satnav: bool = False
|
unrestricted_satnav: bool = False
|
||||||
|
|
||||||
def has_access_to_unittype(self, unitclass: GroundUnitClass) -> bool:
|
def has_access_to_unittype(self, unit_class: GroundUnitClass) -> bool:
|
||||||
has_access = False
|
for vehicle in itertools.chain(self.frontline_units, self.artillery_units):
|
||||||
for vehicle in unitclass.unit_list:
|
if vehicle.unit_class is unit_class:
|
||||||
if vehicle in self.frontline_units:
|
|
||||||
return True
|
return True
|
||||||
if vehicle in self.artillery_units:
|
return False
|
||||||
return True
|
|
||||||
|
|
||||||
return has_access
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction:
|
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction:
|
||||||
@ -172,10 +167,18 @@ class Faction:
|
|||||||
set(faction.aircrafts + faction.awacs + faction.tankers)
|
set(faction.aircrafts + faction.awacs + faction.tankers)
|
||||||
)
|
)
|
||||||
|
|
||||||
faction.frontline_units = load_all_vehicles(json.get("frontline_units", []))
|
faction.frontline_units = [
|
||||||
faction.artillery_units = load_all_vehicles(json.get("artillery_units", []))
|
GroundUnitType.named(n) for n in json.get("frontline_units", [])
|
||||||
faction.infantry_units = load_all_vehicles(json.get("infantry_units", []))
|
]
|
||||||
faction.logistics_units = load_all_vehicles(json.get("logistics_units", []))
|
faction.artillery_units = [
|
||||||
|
GroundUnitType.named(n) for n in json.get("artillery_units", [])
|
||||||
|
]
|
||||||
|
faction.infantry_units = [
|
||||||
|
GroundUnitType.named(n) for n in json.get("infantry_units", [])
|
||||||
|
]
|
||||||
|
faction.logistics_units = [
|
||||||
|
GroundUnitType.named(n) for n in json.get("logistics_units", [])
|
||||||
|
]
|
||||||
|
|
||||||
faction.ewrs = json.get("ewrs", [])
|
faction.ewrs = json.get("ewrs", [])
|
||||||
|
|
||||||
@ -242,55 +245,24 @@ class Faction:
|
|||||||
return faction
|
return faction
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def ground_units(self) -> Iterator[Type[VehicleType]]:
|
def ground_units(self) -> Iterator[GroundUnitType]:
|
||||||
yield from self.artillery_units
|
yield from self.artillery_units
|
||||||
yield from self.frontline_units
|
yield from self.frontline_units
|
||||||
yield from self.logistics_units
|
yield from self.logistics_units
|
||||||
|
|
||||||
|
def infantry_with_class(
|
||||||
def unit_loader(unit: str, class_repository: List[Any]) -> Optional[Type[UnitType]]:
|
self, unit_class: GroundUnitClass
|
||||||
"""
|
) -> Iterator[GroundUnitType]:
|
||||||
Find unit by name
|
for unit in self.infantry_units:
|
||||||
:param unit: Unit name as string
|
if unit.unit_class is unit_class:
|
||||||
:param class_repository: Repository of classes (Either a module, a class, or a list of classes)
|
yield unit
|
||||||
:return: The unit as a PyDCS type
|
|
||||||
"""
|
|
||||||
if unit is None:
|
|
||||||
return None
|
|
||||||
elif unit in plane_map.keys():
|
|
||||||
return plane_map[unit]
|
|
||||||
else:
|
|
||||||
for mother_class in class_repository:
|
|
||||||
if getattr(mother_class, unit, None) is not None:
|
|
||||||
return getattr(mother_class, unit)
|
|
||||||
if type(mother_class) is list:
|
|
||||||
for m in mother_class:
|
|
||||||
if m.__name__ == unit:
|
|
||||||
return m
|
|
||||||
logging.error(f"FACTION ERROR : Unable to find {unit} in pydcs")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def load_vehicle(name: str) -> Optional[Type[VehicleType]]:
|
|
||||||
return cast(
|
|
||||||
Optional[FlyingType],
|
|
||||||
unit_loader(
|
|
||||||
name, [Infantry, Unarmed, Armor, AirDefence, Artillery, MODDED_VEHICLES]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def load_all_vehicles(data) -> List[Type[VehicleType]]:
|
|
||||||
items = []
|
|
||||||
for name in data:
|
|
||||||
item = load_vehicle(name)
|
|
||||||
if item is not None:
|
|
||||||
items.append(item)
|
|
||||||
return items
|
|
||||||
|
|
||||||
|
|
||||||
def load_ship(name: str) -> Optional[Type[ShipType]]:
|
def load_ship(name: str) -> Optional[Type[ShipType]]:
|
||||||
return cast(Optional[FlyingType], unit_loader(name, [dcs.ships]))
|
if (ship := getattr(dcs.ships, name, None)) is not None:
|
||||||
|
return ship
|
||||||
|
logging.error(f"FACTION ERROR : Unable to find {name} in dcs.ships")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def load_all_ships(data) -> List[Type[ShipType]]:
|
def load_all_ships(data) -> List[Type[ShipType]]:
|
||||||
|
|||||||
@ -3,13 +3,12 @@ from __future__ import annotations
|
|||||||
import math
|
import math
|
||||||
import random
|
import random
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Iterator, List, Optional, TYPE_CHECKING, Tuple, Type
|
from typing import Iterator, List, Optional, TYPE_CHECKING, Tuple
|
||||||
|
|
||||||
from dcs.unittype import FlyingType, VehicleType
|
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from game.data.groundunitclass import GroundUnitClass
|
from game.data.groundunitclass import GroundUnitClass
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.factions.faction import Faction
|
from game.factions.faction import Faction
|
||||||
from game.theater import ControlPoint, MissionTarget
|
from game.theater import ControlPoint, MissionTarget
|
||||||
from game.utils import Distance
|
from game.utils import Distance
|
||||||
@ -148,17 +147,17 @@ class ProcurementAi:
|
|||||||
|
|
||||||
def affordable_ground_unit_of_class(
|
def affordable_ground_unit_of_class(
|
||||||
self, budget: float, unit_class: GroundUnitClass
|
self, budget: float, unit_class: GroundUnitClass
|
||||||
) -> Optional[Type[VehicleType]]:
|
) -> Optional[GroundUnitType]:
|
||||||
faction_units = set(self.faction.frontline_units) | set(
|
faction_units = set(self.faction.frontline_units) | set(
|
||||||
self.faction.artillery_units
|
self.faction.artillery_units
|
||||||
)
|
)
|
||||||
of_class = set(unit_class.unit_list) & faction_units
|
of_class = {u for u in faction_units if u.unit_class is unit_class}
|
||||||
|
|
||||||
# faction has no access to needed unit type, take a random unit
|
# faction has no access to needed unit type, take a random unit
|
||||||
if not of_class:
|
if not of_class:
|
||||||
of_class = faction_units
|
of_class = faction_units
|
||||||
|
|
||||||
affordable_units = [u for u in of_class if db.PRICES[u] <= budget]
|
affordable_units = [u for u in of_class if u.price <= budget]
|
||||||
if not affordable_units:
|
if not affordable_units:
|
||||||
return None
|
return None
|
||||||
return random.choice(affordable_units)
|
return random.choice(affordable_units)
|
||||||
@ -180,7 +179,7 @@ class ProcurementAi:
|
|||||||
# Can't afford any more units.
|
# Can't afford any more units.
|
||||||
break
|
break
|
||||||
|
|
||||||
budget -= db.PRICES[unit]
|
budget -= unit.price
|
||||||
cp.pending_unit_deliveries.order({unit: 1})
|
cp.pending_unit_deliveries.order({unit: 1})
|
||||||
|
|
||||||
return budget
|
return budget
|
||||||
@ -361,9 +360,9 @@ class ProcurementAi:
|
|||||||
class_cost = 0
|
class_cost = 0
|
||||||
total_cost = 0
|
total_cost = 0
|
||||||
for unit_type, count in allocations.all.items():
|
for unit_type, count in allocations.all.items():
|
||||||
cost = db.PRICES[unit_type] * count
|
cost = unit_type.price * count
|
||||||
total_cost += cost
|
total_cost += cost
|
||||||
if unit_type in unit_class:
|
if unit_type.unit_class is unit_class:
|
||||||
class_cost += cost
|
class_cost += cost
|
||||||
if not total_cost:
|
if not total_cost:
|
||||||
return 0
|
return 0
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import logging
|
import logging
|
||||||
import typing
|
from typing import Any
|
||||||
from typing import Dict, Type
|
|
||||||
|
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
from game.db import PRICES
|
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
|
from game.dcs.unittype import UnitType
|
||||||
|
|
||||||
BASE_MAX_STRENGTH = 1
|
BASE_MAX_STRENGTH = 1
|
||||||
BASE_MIN_STRENGTH = 0
|
BASE_MIN_STRENGTH = 0
|
||||||
@ -14,8 +12,8 @@ BASE_MIN_STRENGTH = 0
|
|||||||
|
|
||||||
class Base:
|
class Base:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.aircraft: Dict[AircraftType, int] = {}
|
self.aircraft: dict[AircraftType, int] = {}
|
||||||
self.armor: Dict[Type[VehicleType], int] = {}
|
self.armor: dict[GroundUnitType, int] = {}
|
||||||
self.strength = 1
|
self.strength = 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -30,13 +28,10 @@ class Base:
|
|||||||
def total_armor_value(self) -> int:
|
def total_armor_value(self) -> int:
|
||||||
total = 0
|
total = 0
|
||||||
for unit_type, count in self.armor.items():
|
for unit_type, count in self.armor.items():
|
||||||
try:
|
total += unit_type.price * count
|
||||||
total += PRICES[unit_type] * count
|
|
||||||
except KeyError:
|
|
||||||
logging.exception(f"No price found for {unit_type.id}")
|
|
||||||
return total
|
return total
|
||||||
|
|
||||||
def total_units_of_type(self, unit_type: typing.Any) -> int:
|
def total_units_of_type(self, unit_type: UnitType) -> int:
|
||||||
return sum(
|
return sum(
|
||||||
[
|
[
|
||||||
c
|
c
|
||||||
@ -45,30 +40,25 @@ class Base:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def commission_units(self, units: typing.Dict[typing.Any, int]):
|
def commission_units(self, units: dict[Any, int]):
|
||||||
for unit_type, unit_count in units.items():
|
for unit_type, unit_count in units.items():
|
||||||
if unit_count <= 0:
|
if unit_count <= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
target_dict: dict[typing.Any, int]
|
target_dict: dict[Any, int]
|
||||||
if isinstance(unit_type, AircraftType):
|
if isinstance(unit_type, AircraftType):
|
||||||
target_dict = self.aircraft
|
target_dict = self.aircraft
|
||||||
elif issubclass(unit_type, VehicleType):
|
elif isinstance(unit_type, GroundUnitType):
|
||||||
target_dict = self.armor
|
target_dict = self.armor
|
||||||
else:
|
else:
|
||||||
logging.error(
|
logging.error(f"Unexpected unit type of {unit_type}")
|
||||||
f"Unexpected unit type of {unit_type}: "
|
|
||||||
f"{unit_type.__module__}.{unit_type.__name__}"
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
|
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
|
||||||
|
|
||||||
def commit_losses(self, units_lost: typing.Dict[typing.Any, int]):
|
def commit_losses(self, units_lost: dict[Any, int]):
|
||||||
|
|
||||||
for unit_type, count in units_lost.items():
|
for unit_type, count in units_lost.items():
|
||||||
|
target_dict: dict[Any, int]
|
||||||
target_dict: dict[typing.Any, int]
|
|
||||||
if unit_type in self.aircraft:
|
if unit_type in self.aircraft:
|
||||||
target_dict = self.aircraft
|
target_dict = self.aircraft
|
||||||
elif unit_type in self.armor:
|
elif unit_type in self.armor:
|
||||||
|
|||||||
@ -16,7 +16,6 @@ from typing import (
|
|||||||
Optional,
|
Optional,
|
||||||
Set,
|
Set,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Type,
|
|
||||||
Union,
|
Union,
|
||||||
Sequence,
|
Sequence,
|
||||||
Iterable,
|
Iterable,
|
||||||
@ -32,7 +31,6 @@ from dcs.ships import (
|
|||||||
)
|
)
|
||||||
from dcs.terrain.terrain import Airport, ParkingSlot
|
from dcs.terrain.terrain import Airport, ParkingSlot
|
||||||
from dcs.unit import Unit
|
from dcs.unit import Unit
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from game.point_with_heading import PointWithHeading
|
from game.point_with_heading import PointWithHeading
|
||||||
@ -46,8 +44,8 @@ from .theatergroundobject import (
|
|||||||
GenericCarrierGroundObject,
|
GenericCarrierGroundObject,
|
||||||
TheaterGroundObject,
|
TheaterGroundObject,
|
||||||
)
|
)
|
||||||
from ..db import PRICES
|
|
||||||
from ..dcs.aircrafttype import AircraftType
|
from ..dcs.aircrafttype import AircraftType
|
||||||
|
from ..dcs.groundunittype import GroundUnitType
|
||||||
from ..utils import nautical_miles
|
from ..utils import nautical_miles
|
||||||
from ..weather import Conditions
|
from ..weather import Conditions
|
||||||
|
|
||||||
@ -161,13 +159,13 @@ class AircraftAllocations:
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class GroundUnitAllocations:
|
class GroundUnitAllocations:
|
||||||
present: dict[Type[VehicleType], int]
|
present: dict[GroundUnitType, int]
|
||||||
ordered: dict[Type[VehicleType], int]
|
ordered: dict[GroundUnitType, int]
|
||||||
transferring: dict[Type[VehicleType], int]
|
transferring: dict[GroundUnitType, int]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def all(self) -> dict[Type[VehicleType], int]:
|
def all(self) -> dict[GroundUnitType, int]:
|
||||||
combined: dict[Type[VehicleType], int] = defaultdict(int)
|
combined: dict[GroundUnitType, int] = defaultdict(int)
|
||||||
for unit_type, count in itertools.chain(
|
for unit_type, count in itertools.chain(
|
||||||
self.present.items(), self.ordered.items(), self.transferring.items()
|
self.present.items(), self.ordered.items(), self.transferring.items()
|
||||||
):
|
):
|
||||||
@ -178,11 +176,11 @@ class GroundUnitAllocations:
|
|||||||
def total_value(self) -> int:
|
def total_value(self) -> int:
|
||||||
total: int = 0
|
total: int = 0
|
||||||
for unit_type, count in self.present.items():
|
for unit_type, count in self.present.items():
|
||||||
total += PRICES[unit_type] * count
|
total += unit_type.price * count
|
||||||
for unit_type, count in self.ordered.items():
|
for unit_type, count in self.ordered.items():
|
||||||
total += PRICES[unit_type] * count
|
total += unit_type.price * count
|
||||||
for unit_type, count in self.transferring.items():
|
for unit_type, count in self.transferring.items():
|
||||||
total += PRICES[unit_type] * count
|
total += unit_type.price * count
|
||||||
|
|
||||||
return total
|
return total
|
||||||
|
|
||||||
@ -697,10 +695,10 @@ class ControlPoint(MissionTarget, ABC):
|
|||||||
) -> GroundUnitAllocations:
|
) -> GroundUnitAllocations:
|
||||||
on_order = {}
|
on_order = {}
|
||||||
for unit_bought, count in self.pending_unit_deliveries.units.items():
|
for unit_bought, count in self.pending_unit_deliveries.units.items():
|
||||||
if type(unit_bought) == type and issubclass(unit_bought, VehicleType):
|
if isinstance(unit_bought, GroundUnitType):
|
||||||
on_order[unit_bought] = count
|
on_order[unit_bought] = count
|
||||||
|
|
||||||
transferring: dict[Type[VehicleType], int] = defaultdict(int)
|
transferring: dict[GroundUnitType, int] = defaultdict(int)
|
||||||
for transfer in transfers:
|
for transfer in transfers:
|
||||||
if transfer.destination == self:
|
if transfer.destination == self:
|
||||||
for unit_type, count in transfer.units.items():
|
for unit_type, count in transfer.units.items():
|
||||||
|
|||||||
@ -12,15 +12,14 @@ from typing import (
|
|||||||
List,
|
List,
|
||||||
Optional,
|
Optional,
|
||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
Type,
|
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Sequence,
|
Sequence,
|
||||||
)
|
)
|
||||||
|
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.procurement import AircraftProcurementRequest
|
from game.procurement import AircraftProcurementRequest
|
||||||
from game.squadrons import Squadron
|
from game.squadrons import Squadron
|
||||||
from game.theater import ControlPoint, MissionTarget
|
from game.theater import ControlPoint, MissionTarget
|
||||||
@ -73,7 +72,7 @@ class TransferOrder:
|
|||||||
player: bool = field(init=False)
|
player: bool = field(init=False)
|
||||||
|
|
||||||
#: The units being transferred.
|
#: The units being transferred.
|
||||||
units: Dict[Type[VehicleType], int]
|
units: Dict[GroundUnitType, int]
|
||||||
|
|
||||||
transport: Optional[Transport] = field(default=None)
|
transport: Optional[Transport] = field(default=None)
|
||||||
|
|
||||||
@ -90,7 +89,7 @@ class TransferOrder:
|
|||||||
def kill_all(self) -> None:
|
def kill_all(self) -> None:
|
||||||
self.units.clear()
|
self.units.clear()
|
||||||
|
|
||||||
def kill_unit(self, unit_type: Type[VehicleType]) -> None:
|
def kill_unit(self, unit_type: GroundUnitType) -> None:
|
||||||
if unit_type not in self.units or not self.units[unit_type]:
|
if unit_type not in self.units or not self.units[unit_type]:
|
||||||
raise KeyError(f"{self.destination} has no {unit_type} remaining")
|
raise KeyError(f"{self.destination} has no {unit_type} remaining")
|
||||||
self.units[unit_type] -= 1
|
self.units[unit_type] -= 1
|
||||||
@ -99,7 +98,7 @@ class TransferOrder:
|
|||||||
def size(self) -> int:
|
def size(self) -> int:
|
||||||
return sum(c for c in self.units.values())
|
return sum(c for c in self.units.values())
|
||||||
|
|
||||||
def iter_units(self) -> Iterator[Type[VehicleType]]:
|
def iter_units(self) -> Iterator[GroundUnitType]:
|
||||||
for unit_type, count in self.units.items():
|
for unit_type, count in self.units.items():
|
||||||
for _ in range(count):
|
for _ in range(count):
|
||||||
yield unit_type
|
yield unit_type
|
||||||
@ -157,7 +156,7 @@ class Airlift(Transport):
|
|||||||
self.flight = flight
|
self.flight = flight
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def units(self) -> Dict[Type[VehicleType], int]:
|
def units(self) -> Dict[GroundUnitType, int]:
|
||||||
return self.transfer.units
|
return self.transfer.units
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -315,7 +314,7 @@ class MultiGroupTransport(MissionTarget, Transport):
|
|||||||
transfer.transport = None
|
transfer.transport = None
|
||||||
self.transfers.remove(transfer)
|
self.transfers.remove(transfer)
|
||||||
|
|
||||||
def kill_unit(self, unit_type: Type[VehicleType]) -> None:
|
def kill_unit(self, unit_type: GroundUnitType) -> None:
|
||||||
for transfer in self.transfers:
|
for transfer in self.transfers:
|
||||||
try:
|
try:
|
||||||
transfer.kill_unit(unit_type)
|
transfer.kill_unit(unit_type)
|
||||||
@ -338,13 +337,18 @@ class MultiGroupTransport(MissionTarget, Transport):
|
|||||||
return sum(sum(t.units.values()) for t in self.transfers)
|
return sum(sum(t.units.values()) for t in self.transfers)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def units(self) -> Dict[Type[VehicleType], int]:
|
def units(self) -> dict[GroundUnitType, int]:
|
||||||
units: Dict[Type[VehicleType], int] = defaultdict(int)
|
units: Dict[GroundUnitType, int] = defaultdict(int)
|
||||||
for transfer in self.transfers:
|
for transfer in self.transfers:
|
||||||
for unit_type, count in transfer.units.items():
|
for unit_type, count in transfer.units.items():
|
||||||
units[unit_type] += count
|
units[unit_type] += count
|
||||||
return units
|
return units
|
||||||
|
|
||||||
|
def iter_units(self) -> Iterator[GroundUnitType]:
|
||||||
|
for unit_type, count in self.units.items():
|
||||||
|
for _ in range(count):
|
||||||
|
yield unit_type
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def player_owned(self) -> bool:
|
def player_owned(self) -> bool:
|
||||||
return self.origin.captured
|
return self.origin.captured
|
||||||
|
|||||||
@ -3,13 +3,11 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, Optional, TYPE_CHECKING, Type, Any
|
from typing import Dict, Optional, TYPE_CHECKING, Any
|
||||||
|
|
||||||
from dcs.unittype import UnitType, VehicleType
|
|
||||||
|
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from .db import PRICES
|
from .dcs.groundunittype import GroundUnitType
|
||||||
from .dcs.aircrafttype import AircraftType
|
from .dcs.unittype import UnitType
|
||||||
from .theater.transitnetwork import (
|
from .theater.transitnetwork import (
|
||||||
NoPathError,
|
NoPathError,
|
||||||
TransitNetwork,
|
TransitNetwork,
|
||||||
@ -25,24 +23,21 @@ class GroundUnitSource:
|
|||||||
control_point: ControlPoint
|
control_point: ControlPoint
|
||||||
|
|
||||||
|
|
||||||
AircraftOrVehicleType = Any
|
|
||||||
|
|
||||||
|
|
||||||
class PendingUnitDeliveries:
|
class PendingUnitDeliveries:
|
||||||
def __init__(self, destination: ControlPoint) -> None:
|
def __init__(self, destination: ControlPoint) -> None:
|
||||||
self.destination = destination
|
self.destination = destination
|
||||||
|
|
||||||
# Maps unit type to order quantity.
|
# Maps unit type to order quantity.
|
||||||
self.units: Dict[AircraftOrVehicleType, int] = defaultdict(int)
|
self.units: Dict[UnitType, int] = defaultdict(int)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"Pending delivery to {self.destination}"
|
return f"Pending delivery to {self.destination}"
|
||||||
|
|
||||||
def order(self, units: Dict[AircraftOrVehicleType, int]) -> None:
|
def order(self, units: Dict[UnitType, int]) -> None:
|
||||||
for k, v in units.items():
|
for k, v in units.items():
|
||||||
self.units[k] += v
|
self.units[k] += v
|
||||||
|
|
||||||
def sell(self, units: Dict[AircraftOrVehicleType, int]) -> None:
|
def sell(self, units: Dict[UnitType, int]) -> None:
|
||||||
for k, v in units.items():
|
for k, v in units.items():
|
||||||
self.units[k] -= v
|
self.units[k] -= v
|
||||||
|
|
||||||
@ -50,24 +45,20 @@ class PendingUnitDeliveries:
|
|||||||
self.refund(game, self.units)
|
self.refund(game, self.units)
|
||||||
self.units = defaultdict(int)
|
self.units = defaultdict(int)
|
||||||
|
|
||||||
def refund(self, game: Game, units: Dict[Type[UnitType], int]) -> None:
|
def refund(self, game: Game, units: Dict[UnitType, int]) -> None:
|
||||||
for unit_type, count in units.items():
|
for unit_type, count in units.items():
|
||||||
try:
|
logging.info(f"Refunding {count} {unit_type} at {self.destination.name}")
|
||||||
price = PRICES[unit_type]
|
game.adjust_budget(
|
||||||
except KeyError:
|
unit_type.price * count, player=self.destination.captured
|
||||||
logging.error(f"Could not refund {unit_type.id}, price unknown")
|
)
|
||||||
continue
|
|
||||||
|
|
||||||
logging.info(f"Refunding {count} {unit_type.id} at {self.destination.name}")
|
def pending_orders(self, unit_type: UnitType) -> int:
|
||||||
game.adjust_budget(price * count, player=self.destination.captured)
|
|
||||||
|
|
||||||
def pending_orders(self, unit_type: AircraftOrVehicleType) -> int:
|
|
||||||
pending_units = self.units.get(unit_type)
|
pending_units = self.units.get(unit_type)
|
||||||
if pending_units is None:
|
if pending_units is None:
|
||||||
pending_units = 0
|
pending_units = 0
|
||||||
return pending_units
|
return pending_units
|
||||||
|
|
||||||
def available_next_turn(self, unit_type: AircraftOrVehicleType) -> int:
|
def available_next_turn(self, unit_type: UnitType) -> int:
|
||||||
current_units = self.destination.base.total_units_of_type(unit_type)
|
current_units = self.destination.base.total_units_of_type(unit_type)
|
||||||
return self.pending_orders(unit_type) + current_units
|
return self.pending_orders(unit_type) + current_units
|
||||||
|
|
||||||
@ -81,20 +72,14 @@ class PendingUnitDeliveries:
|
|||||||
self.refund_all(game)
|
self.refund_all(game)
|
||||||
return
|
return
|
||||||
|
|
||||||
bought_units: Dict[AircraftOrVehicleType, int] = {}
|
bought_units: Dict[UnitType, int] = {}
|
||||||
units_needing_transfer: Dict[Type[VehicleType], int] = {}
|
units_needing_transfer: Dict[GroundUnitType, int] = {}
|
||||||
sold_units: Dict[AircraftOrVehicleType, int] = {}
|
sold_units: Dict[UnitType, int] = {}
|
||||||
for unit_type, count in self.units.items():
|
for unit_type, count in self.units.items():
|
||||||
coalition = "Ally" if self.destination.captured else "Enemy"
|
coalition = "Ally" if self.destination.captured else "Enemy"
|
||||||
|
d: dict[Any, int]
|
||||||
if isinstance(unit_type, AircraftType):
|
|
||||||
name = unit_type.name
|
|
||||||
else:
|
|
||||||
name = unit_type.id
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
type(unit_type) == type
|
isinstance(unit_type, GroundUnitType)
|
||||||
and issubclass(unit_type, VehicleType)
|
|
||||||
and self.destination != ground_unit_source
|
and self.destination != ground_unit_source
|
||||||
):
|
):
|
||||||
source = ground_unit_source
|
source = ground_unit_source
|
||||||
@ -106,11 +91,11 @@ class PendingUnitDeliveries:
|
|||||||
if count >= 0:
|
if count >= 0:
|
||||||
d[unit_type] = count
|
d[unit_type] = count
|
||||||
game.message(
|
game.message(
|
||||||
f"{coalition} reinforcements: {name} x {count} at {source}"
|
f"{coalition} reinforcements: {unit_type} x {count} at {source}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
sold_units[unit_type] = -count
|
sold_units[unit_type] = -count
|
||||||
game.message(f"{coalition} sold: {name} x {-count} at {source}")
|
game.message(f"{coalition} sold: {unit_type} x {-count} at {source}")
|
||||||
|
|
||||||
self.units = defaultdict(int)
|
self.units = defaultdict(int)
|
||||||
self.destination.base.commission_units(bought_units)
|
self.destination.base.commission_units(bought_units)
|
||||||
@ -121,7 +106,7 @@ class PendingUnitDeliveries:
|
|||||||
self.create_transfer(game, ground_unit_source, units_needing_transfer)
|
self.create_transfer(game, ground_unit_source, units_needing_transfer)
|
||||||
|
|
||||||
def create_transfer(
|
def create_transfer(
|
||||||
self, game: Game, source: ControlPoint, units: Dict[Type[VehicleType], int]
|
self, game: Game, source: ControlPoint, units: Dict[GroundUnitType, int]
|
||||||
) -> None:
|
) -> None:
|
||||||
game.transfers.new_transfer(TransferOrder(source, self.destination, units))
|
game.transfers.new_transfer(TransferOrder(source, self.destination, units))
|
||||||
|
|
||||||
|
|||||||
@ -2,13 +2,12 @@
|
|||||||
import itertools
|
import itertools
|
||||||
import math
|
import math
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, Optional, Type
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from dcs.unit import Unit
|
from dcs.unit import Unit
|
||||||
from dcs.unitgroup import FlyingGroup, Group, VehicleGroup
|
from dcs.unitgroup import FlyingGroup, Group, VehicleGroup
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
from game import db
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.squadrons import Pilot
|
from game.squadrons import Pilot
|
||||||
from game.theater import Airfield, ControlPoint, TheaterGroundObject
|
from game.theater import Airfield, ControlPoint, TheaterGroundObject
|
||||||
from game.theater.theatergroundobject import BuildingGroundObject, SceneryGroundObject
|
from game.theater.theatergroundobject import BuildingGroundObject, SceneryGroundObject
|
||||||
@ -24,7 +23,7 @@ class FlyingUnit:
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class FrontLineUnit:
|
class FrontLineUnit:
|
||||||
unit_type: Type[VehicleType]
|
unit_type: GroundUnitType
|
||||||
origin: ControlPoint
|
origin: ControlPoint
|
||||||
|
|
||||||
|
|
||||||
@ -37,13 +36,13 @@ class GroundObjectUnit:
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class ConvoyUnit:
|
class ConvoyUnit:
|
||||||
unit_type: Type[VehicleType]
|
unit_type: GroundUnitType
|
||||||
convoy: Convoy
|
convoy: Convoy
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class AirliftUnits:
|
class AirliftUnits:
|
||||||
cargo: tuple[Type[VehicleType], ...]
|
cargo: tuple[GroundUnitType, ...]
|
||||||
transfer: TransferOrder
|
transfer: TransferOrder
|
||||||
|
|
||||||
|
|
||||||
@ -85,20 +84,15 @@ class UnitMap:
|
|||||||
def airfield(self, name: str) -> Optional[Airfield]:
|
def airfield(self, name: str) -> Optional[Airfield]:
|
||||||
return self.airfields.get(name, None)
|
return self.airfields.get(name, None)
|
||||||
|
|
||||||
def add_front_line_units(self, group: Group, origin: ControlPoint) -> None:
|
def add_front_line_units(
|
||||||
|
self, group: Group, origin: ControlPoint, unit_type: GroundUnitType
|
||||||
|
) -> None:
|
||||||
for unit in group.units:
|
for unit in group.units:
|
||||||
# The actual name is a String (the pydcs translatable string), which
|
# The actual name is a String (the pydcs translatable string), which
|
||||||
# doesn't define __eq__.
|
# doesn't define __eq__.
|
||||||
name = str(unit.name)
|
name = str(unit.name)
|
||||||
if name in self.front_line_units:
|
if name in self.front_line_units:
|
||||||
raise RuntimeError(f"Duplicate front line unit: {name}")
|
raise RuntimeError(f"Duplicate front line unit: {name}")
|
||||||
unit_type = db.unit_type_from_name(unit.type)
|
|
||||||
if unit_type is None:
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
self.front_line_units[name] = FrontLineUnit(unit_type, origin)
|
self.front_line_units[name] = FrontLineUnit(unit_type, origin)
|
||||||
|
|
||||||
def front_line_unit(self, name: str) -> Optional[FrontLineUnit]:
|
def front_line_unit(self, name: str) -> Optional[FrontLineUnit]:
|
||||||
@ -141,19 +135,12 @@ class UnitMap:
|
|||||||
return self.ground_object_units.get(name, None)
|
return self.ground_object_units.get(name, None)
|
||||||
|
|
||||||
def add_convoy_units(self, group: Group, convoy: Convoy) -> None:
|
def add_convoy_units(self, group: Group, convoy: Convoy) -> None:
|
||||||
for unit in group.units:
|
for unit, unit_type in zip(group.units, convoy.iter_units()):
|
||||||
# The actual name is a String (the pydcs translatable string), which
|
# The actual name is a String (the pydcs translatable string), which
|
||||||
# doesn't define __eq__.
|
# doesn't define __eq__.
|
||||||
name = str(unit.name)
|
name = str(unit.name)
|
||||||
if name in self.convoys:
|
if name in self.convoys:
|
||||||
raise RuntimeError(f"Duplicate convoy unit: {name}")
|
raise RuntimeError(f"Duplicate convoy unit: {name}")
|
||||||
unit_type = db.unit_type_from_name(unit.type)
|
|
||||||
if unit_type is None:
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
self.convoys[name] = ConvoyUnit(unit_type, convoy)
|
self.convoys[name] = ConvoyUnit(unit_type, convoy)
|
||||||
|
|
||||||
def convoy_unit(self, name: str) -> Optional[ConvoyUnit]:
|
def convoy_unit(self, name: str) -> Optional[ConvoyUnit]:
|
||||||
|
|||||||
@ -110,7 +110,6 @@ class AirSupportConflictGenerator:
|
|||||||
):
|
):
|
||||||
# TODO: Make loiter altitude a property of the unit type.
|
# TODO: Make loiter altitude a property of the unit type.
|
||||||
alt, airspeed = self._get_tanker_params(tanker_unit_type.dcs_unit_type)
|
alt, airspeed = self._get_tanker_params(tanker_unit_type.dcs_unit_type)
|
||||||
variant = db.unit_type_name(tanker_unit_type)
|
|
||||||
freq = self.radio_registry.alloc_uhf()
|
freq = self.radio_registry.alloc_uhf()
|
||||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
||||||
tanker_heading = (
|
tanker_heading = (
|
||||||
@ -175,7 +174,7 @@ class AirSupportConflictGenerator:
|
|||||||
TankerInfo(
|
TankerInfo(
|
||||||
str(tanker_group.name),
|
str(tanker_group.name),
|
||||||
callsign,
|
callsign,
|
||||||
variant,
|
tanker_unit_type.name,
|
||||||
freq,
|
freq,
|
||||||
tacan,
|
tacan,
|
||||||
blue=True,
|
blue=True,
|
||||||
|
|||||||
76
gen/armor.py
76
gen/armor.py
@ -10,7 +10,6 @@ from dcs.action import AITaskPush
|
|||||||
from dcs.condition import GroupLifeLess, Or, TimeAfter, UnitDamaged
|
from dcs.condition import GroupLifeLess, Or, TimeAfter, UnitDamaged
|
||||||
from dcs.country import Country
|
from dcs.country import Country
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
from dcs.planes import MQ_9_Reaper
|
|
||||||
from dcs.point import PointAction
|
from dcs.point import PointAction
|
||||||
from dcs.task import (
|
from dcs.task import (
|
||||||
EPLRS,
|
EPLRS,
|
||||||
@ -26,19 +25,18 @@ from dcs.task import (
|
|||||||
from dcs.triggers import Event, TriggerOnce
|
from dcs.triggers import Event, TriggerOnce
|
||||||
from dcs.unit import Vehicle
|
from dcs.unit import Vehicle
|
||||||
from dcs.unitgroup import VehicleGroup
|
from dcs.unitgroup import VehicleGroup
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
from game import db
|
from game.data.groundunitclass import GroundUnitClass
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
|
from game.theater.controlpoint import ControlPoint
|
||||||
from game.unitmap import UnitMap
|
from game.unitmap import UnitMap
|
||||||
from game.utils import heading_sum, opposite_heading
|
from game.utils import heading_sum, opposite_heading
|
||||||
from game.theater.controlpoint import ControlPoint
|
|
||||||
|
|
||||||
from gen.ground_forces.ai_ground_planner import (
|
from gen.ground_forces.ai_ground_planner import (
|
||||||
DISTANCE_FROM_FRONTLINE,
|
DISTANCE_FROM_FRONTLINE,
|
||||||
CombatGroup,
|
CombatGroup,
|
||||||
CombatGroupRole,
|
CombatGroupRole,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .callsigns import callsign_for_support_unit
|
from .callsigns import callsign_for_support_unit
|
||||||
from .conflictgen import Conflict
|
from .conflictgen import Conflict
|
||||||
from .ground_forces.combat_stance import CombatStance
|
from .ground_forces.combat_stance import CombatStance
|
||||||
@ -226,19 +224,18 @@ class GroundConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
cp = self.conflict.red_cp
|
cp = self.conflict.red_cp
|
||||||
|
|
||||||
if is_player:
|
faction = self.game.faction_for(is_player)
|
||||||
faction = self.game.player_name
|
|
||||||
else:
|
|
||||||
faction = self.game.enemy_name
|
|
||||||
|
|
||||||
# Disable infantry unit gen if disabled
|
# Disable infantry unit gen if disabled
|
||||||
if not self.game.settings.perf_infantry:
|
if not self.game.settings.perf_infantry:
|
||||||
if self.game.settings.manpads:
|
if self.game.settings.manpads:
|
||||||
# 50% of armored units protected by manpad
|
# 50% of armored units protected by manpad
|
||||||
if random.choice([True, False]):
|
if random.choice([True, False]):
|
||||||
manpads = db.find_manpad(faction)
|
manpads = list(faction.infantry_with_class(GroundUnitClass.Manpads))
|
||||||
if len(manpads) > 0:
|
if manpads:
|
||||||
u = random.choice(manpads)
|
u = random.choices(
|
||||||
|
manpads, weights=[m.spawn_weight for m in manpads]
|
||||||
|
)[0]
|
||||||
self.mission.vehicle_group(
|
self.mission.vehicle_group(
|
||||||
side,
|
side,
|
||||||
namegen.next_infantry_name(side, cp.id, u),
|
namegen.next_infantry_name(side, cp.id, u),
|
||||||
@ -250,30 +247,38 @@ class GroundConflictGenerator:
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
possible_infantry_units = db.find_infantry(
|
possible_infantry_units = set(
|
||||||
faction, allow_manpad=self.game.settings.manpads
|
faction.infantry_with_class(GroundUnitClass.Infantry)
|
||||||
)
|
)
|
||||||
if len(possible_infantry_units) == 0:
|
if self.game.settings.manpads:
|
||||||
|
possible_infantry_units |= set(
|
||||||
|
faction.infantry_with_class(GroundUnitClass.Manpads)
|
||||||
|
)
|
||||||
|
if not possible_infantry_units:
|
||||||
return
|
return
|
||||||
|
|
||||||
u = random.choice(possible_infantry_units)
|
infantry_choices = list(possible_infantry_units)
|
||||||
|
units = random.choices(
|
||||||
|
infantry_choices,
|
||||||
|
weights=[u.spawn_weight for u in infantry_choices],
|
||||||
|
k=INFANTRY_GROUP_SIZE,
|
||||||
|
)
|
||||||
self.mission.vehicle_group(
|
self.mission.vehicle_group(
|
||||||
side,
|
side,
|
||||||
namegen.next_infantry_name(side, cp.id, u),
|
namegen.next_infantry_name(side, cp.id, units[0]),
|
||||||
u,
|
units[0].dcs_unit_type,
|
||||||
position=infantry_position,
|
position=infantry_position,
|
||||||
group_size=1,
|
group_size=1,
|
||||||
heading=forward_heading,
|
heading=forward_heading,
|
||||||
move_formation=PointAction.OffRoad,
|
move_formation=PointAction.OffRoad,
|
||||||
)
|
)
|
||||||
|
|
||||||
for i in range(INFANTRY_GROUP_SIZE):
|
for unit in units[1:]:
|
||||||
u = random.choice(possible_infantry_units)
|
|
||||||
position = infantry_position.random_point_within(55, 5)
|
position = infantry_position.random_point_within(55, 5)
|
||||||
self.mission.vehicle_group(
|
self.mission.vehicle_group(
|
||||||
side,
|
side,
|
||||||
namegen.next_infantry_name(side, cp.id, u),
|
namegen.next_infantry_name(side, cp.id, unit),
|
||||||
u,
|
unit.dcs_unit_type,
|
||||||
position=position,
|
position=position,
|
||||||
group_size=1,
|
group_size=1,
|
||||||
heading=forward_heading,
|
heading=forward_heading,
|
||||||
@ -313,7 +318,7 @@ class GroundConflictGenerator:
|
|||||||
)
|
)
|
||||||
artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45) * 60))
|
artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45) * 60))
|
||||||
# TODO: Update to fire at group instead of point
|
# TODO: Update to fire at group instead of point
|
||||||
fire_task = FireAtPoint(target, len(gen_group.units) * 10, 100)
|
fire_task = FireAtPoint(target, gen_group.size * 10, 100)
|
||||||
fire_task.number = 2 if stance != CombatStance.RETREAT else 1
|
fire_task.number = 2 if stance != CombatStance.RETREAT else 1
|
||||||
dcs_group.add_trigger_action(fire_task)
|
dcs_group.add_trigger_action(fire_task)
|
||||||
artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
|
artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
|
||||||
@ -503,7 +508,7 @@ class GroundConflictGenerator:
|
|||||||
return
|
return
|
||||||
|
|
||||||
for dcs_group, group in ally_groups:
|
for dcs_group, group in ally_groups:
|
||||||
if hasattr(group.units[0], "eplrs") and group.units[0].eplrs:
|
if getattr(group.unit_type.dcs_unit_type, "eplrs", False):
|
||||||
dcs_group.points[0].tasks.append(EPLRS(dcs_group.id))
|
dcs_group.points[0].tasks.append(EPLRS(dcs_group.id))
|
||||||
|
|
||||||
if group.role == CombatGroupRole.ARTILLERY:
|
if group.role == CombatGroupRole.ARTILLERY:
|
||||||
@ -674,7 +679,7 @@ class GroundConflictGenerator:
|
|||||||
Search the enemy groups for a potential target suitable to an artillery unit
|
Search the enemy groups for a potential target suitable to an artillery unit
|
||||||
"""
|
"""
|
||||||
# TODO: Update to return a list of groups instead of a single point
|
# TODO: Update to return a list of groups instead of a single point
|
||||||
rng = group.units[0].threat_range
|
rng = getattr(group.unit_type.dcs_unit_type, "threat_range", 0)
|
||||||
if not enemy_groups:
|
if not enemy_groups:
|
||||||
return None
|
return None
|
||||||
for _ in range(10):
|
for _ in range(10):
|
||||||
@ -691,7 +696,7 @@ class GroundConflictGenerator:
|
|||||||
"""
|
"""
|
||||||
For artilery group, decide the distance from frontline with the range of the unit
|
For artilery group, decide the distance from frontline with the range of the unit
|
||||||
"""
|
"""
|
||||||
rg = group.units[0].threat_range - 7500
|
rg = getattr(group.unit_type.dcs_unit_type, "threat_range", 0) - 7500
|
||||||
if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]:
|
if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]:
|
||||||
rg = random.randint(
|
rg = random.randint(
|
||||||
DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][0],
|
DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][0],
|
||||||
@ -724,7 +729,7 @@ class GroundConflictGenerator:
|
|||||||
|
|
||||||
def _generate_groups(
|
def _generate_groups(
|
||||||
self,
|
self,
|
||||||
groups: List[CombatGroup],
|
groups: list[CombatGroup],
|
||||||
frontline_vector: Tuple[Point, int, int],
|
frontline_vector: Tuple[Point, int, int],
|
||||||
is_player: bool,
|
is_player: bool,
|
||||||
) -> List[Tuple[VehicleGroup, CombatGroup]]:
|
) -> List[Tuple[VehicleGroup, CombatGroup]]:
|
||||||
@ -755,10 +760,9 @@ class GroundConflictGenerator:
|
|||||||
if final_position is not None:
|
if final_position is not None:
|
||||||
g = self._generate_group(
|
g = self._generate_group(
|
||||||
self.mission.country(country),
|
self.mission.country(country),
|
||||||
group.units[0],
|
group.unit_type,
|
||||||
len(group.units),
|
group.size,
|
||||||
final_position,
|
final_position,
|
||||||
distance_from_frontline,
|
|
||||||
heading=opposite_heading(spawn_heading),
|
heading=opposite_heading(spawn_heading),
|
||||||
)
|
)
|
||||||
if is_player:
|
if is_player:
|
||||||
@ -782,10 +786,9 @@ class GroundConflictGenerator:
|
|||||||
def _generate_group(
|
def _generate_group(
|
||||||
self,
|
self,
|
||||||
side: Country,
|
side: Country,
|
||||||
unit: VehicleType,
|
unit_type: GroundUnitType,
|
||||||
count: int,
|
count: int,
|
||||||
at: Point,
|
at: Point,
|
||||||
distance_from_frontline,
|
|
||||||
move_formation: PointAction = PointAction.OffRoad,
|
move_formation: PointAction = PointAction.OffRoad,
|
||||||
heading=0,
|
heading=0,
|
||||||
) -> VehicleGroup:
|
) -> VehicleGroup:
|
||||||
@ -795,18 +798,17 @@ class GroundConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
cp = self.conflict.red_cp
|
cp = self.conflict.red_cp
|
||||||
|
|
||||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
|
||||||
group = self.mission.vehicle_group(
|
group = self.mission.vehicle_group(
|
||||||
side,
|
side,
|
||||||
namegen.next_unit_name(side, cp.id, unit),
|
namegen.next_unit_name(side, cp.id, unit_type),
|
||||||
unit,
|
unit_type.dcs_unit_type,
|
||||||
position=at,
|
position=at,
|
||||||
group_size=count,
|
group_size=count,
|
||||||
heading=heading,
|
heading=heading,
|
||||||
move_formation=move_formation,
|
move_formation=move_formation,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.unit_map.add_front_line_units(group, cp)
|
self.unit_map.add_front_line_units(group, cp, unit_type)
|
||||||
|
|
||||||
for c in range(count):
|
for c in range(count):
|
||||||
vehicle: Vehicle = group.units[c]
|
vehicle: Vehicle = group.units[c]
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
from typing import Dict, TYPE_CHECKING, Type
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from dcs import Mission
|
from dcs import Mission
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
from dcs.point import PointAction
|
from dcs.point import PointAction
|
||||||
from dcs.unit import Vehicle
|
from dcs.unit import Vehicle
|
||||||
from dcs.unitgroup import VehicleGroup
|
from dcs.unitgroup import VehicleGroup
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.transfers import Convoy
|
from game.transfers import Convoy
|
||||||
from game.unitmap import UnitMap
|
from game.unitmap import UnitMap
|
||||||
from game.utils import kph
|
from game.utils import kph
|
||||||
@ -50,7 +50,7 @@ class ConvoyGenerator:
|
|||||||
self,
|
self,
|
||||||
name: str,
|
name: str,
|
||||||
position: Point,
|
position: Point,
|
||||||
units: Dict[Type[VehicleType], int],
|
units: dict[GroundUnitType, int],
|
||||||
for_player: bool,
|
for_player: bool,
|
||||||
) -> VehicleGroup:
|
) -> VehicleGroup:
|
||||||
country = self.mission.country(
|
country = self.mission.country(
|
||||||
@ -63,7 +63,7 @@ class ConvoyGenerator:
|
|||||||
group = self.mission.vehicle_group(
|
group = self.mission.vehicle_group(
|
||||||
country,
|
country,
|
||||||
name,
|
name,
|
||||||
main_unit_type,
|
main_unit_type.dcs_unit_type,
|
||||||
position=position,
|
position=position,
|
||||||
group_size=main_unit_count,
|
group_size=main_unit_count,
|
||||||
move_formation=PointAction.OnRoad,
|
move_formation=PointAction.OnRoad,
|
||||||
@ -76,7 +76,7 @@ class ConvoyGenerator:
|
|||||||
for unit_type, count in unit_types[1:]:
|
for unit_type, count in unit_types[1:]:
|
||||||
for i in range(count):
|
for i in range(count):
|
||||||
v = self.mission.vehicle(
|
v = self.mission.vehicle(
|
||||||
f"{name} Unit #{next(unit_name_counter)}", unit_type
|
f"{name} Unit #{next(unit_name_counter)}", unit_type.dcs_unit_type
|
||||||
)
|
)
|
||||||
v.position.x = position.x
|
v.position.x = position.x
|
||||||
v.position.y = next(y)
|
v.position.y = next(y)
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from dcs.vehicles import Armor
|
from dcs.unitgroup import VehicleGroup
|
||||||
|
|
||||||
from game import db
|
from game import db, Game
|
||||||
|
from game.data.groundunitclass import GroundUnitClass
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
|
from game.theater.theatergroundobject import VehicleGroupGroundObject
|
||||||
from gen.defenses.armored_group_generator import (
|
from gen.defenses.armored_group_generator import (
|
||||||
ArmoredGroupGenerator,
|
ArmoredGroupGenerator,
|
||||||
FixedSizeArmorGroupGenerator,
|
FixedSizeArmorGroupGenerator,
|
||||||
@ -14,8 +17,14 @@ def generate_armor_group(faction: str, game, ground_object):
|
|||||||
This generate a group of ground units
|
This generate a group of ground units
|
||||||
:return: Generated group
|
:return: Generated group
|
||||||
"""
|
"""
|
||||||
|
armor_types = (
|
||||||
|
GroundUnitClass.Apc,
|
||||||
|
GroundUnitClass.Atgm,
|
||||||
|
GroundUnitClass.Ifv,
|
||||||
|
GroundUnitClass.Tank,
|
||||||
|
)
|
||||||
possible_unit = [
|
possible_unit = [
|
||||||
u for u in db.FACTIONS[faction].frontline_units if u in Armor.__dict__.values()
|
u for u in db.FACTIONS[faction].frontline_units if u.unit_class in armor_types
|
||||||
]
|
]
|
||||||
if len(possible_unit) > 0:
|
if len(possible_unit) > 0:
|
||||||
unit_type = random.choice(possible_unit)
|
unit_type = random.choice(possible_unit)
|
||||||
@ -23,7 +32,9 @@ def generate_armor_group(faction: str, game, ground_object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def generate_armor_group_of_type(game, ground_object, unit_type):
|
def generate_armor_group_of_type(
|
||||||
|
game: Game, ground_object: VehicleGroupGroundObject, unit_type: GroundUnitType
|
||||||
|
) -> VehicleGroup:
|
||||||
"""
|
"""
|
||||||
This generate a group of ground units of given type
|
This generate a group of ground units of given type
|
||||||
:return: Generated group
|
:return: Generated group
|
||||||
@ -33,7 +44,12 @@ def generate_armor_group_of_type(game, ground_object, unit_type):
|
|||||||
return generator.get_generated_group()
|
return generator.get_generated_group()
|
||||||
|
|
||||||
|
|
||||||
def generate_armor_group_of_type_and_size(game, ground_object, unit_type, size: int):
|
def generate_armor_group_of_type_and_size(
|
||||||
|
game: Game,
|
||||||
|
ground_object: VehicleGroupGroundObject,
|
||||||
|
unit_type: GroundUnitType,
|
||||||
|
size: int,
|
||||||
|
) -> VehicleGroup:
|
||||||
"""
|
"""
|
||||||
This generate a group of ground units of given type and size
|
This generate a group of ground units of given type and size
|
||||||
:return: Generated group
|
:return: Generated group
|
||||||
|
|||||||
@ -1,15 +1,22 @@
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
|
from game import Game
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
|
from game.theater.theatergroundobject import VehicleGroupGroundObject
|
||||||
from gen.sam.group_generator import GroupGenerator
|
from gen.sam.group_generator import GroupGenerator
|
||||||
|
|
||||||
|
|
||||||
class ArmoredGroupGenerator(GroupGenerator):
|
class ArmoredGroupGenerator(GroupGenerator):
|
||||||
def __init__(self, game, ground_object, unit_type):
|
def __init__(
|
||||||
super(ArmoredGroupGenerator, self).__init__(game, ground_object)
|
self,
|
||||||
|
game: Game,
|
||||||
|
ground_object: VehicleGroupGroundObject,
|
||||||
|
unit_type: GroundUnitType,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(game, ground_object)
|
||||||
self.unit_type = unit_type
|
self.unit_type = unit_type
|
||||||
|
|
||||||
def generate(self):
|
def generate(self) -> None:
|
||||||
|
|
||||||
grid_x = random.randint(2, 3)
|
grid_x = random.randint(2, 3)
|
||||||
grid_y = random.randint(1, 2)
|
grid_y = random.randint(1, 2)
|
||||||
|
|
||||||
@ -20,7 +27,7 @@ class ArmoredGroupGenerator(GroupGenerator):
|
|||||||
for j in range(grid_y):
|
for j in range(grid_y):
|
||||||
index = index + 1
|
index = index + 1
|
||||||
self.add_unit(
|
self.add_unit(
|
||||||
self.unit_type,
|
self.unit_type.dcs_unit_type,
|
||||||
"Armor#" + str(index),
|
"Armor#" + str(index),
|
||||||
self.position.x + spacing * i,
|
self.position.x + spacing * i,
|
||||||
self.position.y + spacing * j,
|
self.position.y + spacing * j,
|
||||||
@ -29,8 +36,14 @@ class ArmoredGroupGenerator(GroupGenerator):
|
|||||||
|
|
||||||
|
|
||||||
class FixedSizeArmorGroupGenerator(GroupGenerator):
|
class FixedSizeArmorGroupGenerator(GroupGenerator):
|
||||||
def __init__(self, game, ground_object, unit_type, size):
|
def __init__(
|
||||||
super(FixedSizeArmorGroupGenerator, self).__init__(game, ground_object)
|
self,
|
||||||
|
game: Game,
|
||||||
|
ground_object: VehicleGroupGroundObject,
|
||||||
|
unit_type: GroundUnitType,
|
||||||
|
size: int,
|
||||||
|
) -> None:
|
||||||
|
super().__init__(game, ground_object)
|
||||||
self.unit_type = unit_type
|
self.unit_type = unit_type
|
||||||
self.size = size
|
self.size = size
|
||||||
|
|
||||||
@ -41,7 +54,7 @@ class FixedSizeArmorGroupGenerator(GroupGenerator):
|
|||||||
for i in range(self.size):
|
for i in range(self.size):
|
||||||
index = index + 1
|
index = index + 1
|
||||||
self.add_unit(
|
self.add_unit(
|
||||||
self.unit_type,
|
self.unit_type.dcs_unit_type,
|
||||||
"Armor#" + str(index),
|
"Armor#" + str(index),
|
||||||
self.position.x + spacing * i,
|
self.position.x + spacing * i,
|
||||||
self.position.y,
|
self.position.y,
|
||||||
|
|||||||
@ -3,11 +3,9 @@ import random
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Dict, List
|
from typing import Dict, List
|
||||||
|
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
from game.theater import ControlPoint
|
|
||||||
|
|
||||||
from game.data.groundunitclass import GroundUnitClass
|
from game.data.groundunitclass import GroundUnitClass
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
|
from game.theater import ControlPoint
|
||||||
from gen.ground_forces.combat_stance import CombatStance
|
from gen.ground_forces.combat_stance import CombatStance
|
||||||
|
|
||||||
MAX_COMBAT_GROUP_PER_CP = 10
|
MAX_COMBAT_GROUP_PER_CP = 10
|
||||||
@ -48,17 +46,19 @@ GROUP_SIZES_BY_COMBAT_STANCE = {
|
|||||||
|
|
||||||
|
|
||||||
class CombatGroup:
|
class CombatGroup:
|
||||||
def __init__(self, role: CombatGroupRole):
|
def __init__(
|
||||||
self.units: List[VehicleType] = []
|
self, role: CombatGroupRole, unit_type: GroundUnitType, size: int
|
||||||
|
) -> None:
|
||||||
|
self.unit_type = unit_type
|
||||||
|
self.size = size
|
||||||
self.role = role
|
self.role = role
|
||||||
self.assigned_enemy_cp = None
|
self.assigned_enemy_cp = None
|
||||||
self.start_position = None
|
self.start_position = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
s = ""
|
s = f"ROLE : {self.role}\n"
|
||||||
s += "ROLE : " + str(self.role) + "\n"
|
if self.size:
|
||||||
if len(self.units) > 0:
|
s += f"UNITS {self.unit_type} * {self.size}"
|
||||||
s += "UNITS " + self.units[0].name + " * " + str(len(self.units))
|
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
|
||||||
@ -97,28 +97,29 @@ class GroundPlanner:
|
|||||||
|
|
||||||
# Create combat groups and assign them randomly to each enemy CP
|
# Create combat groups and assign them randomly to each enemy CP
|
||||||
for unit_type in self.cp.base.armor:
|
for unit_type in self.cp.base.armor:
|
||||||
if unit_type in GroundUnitClass.Tank:
|
unit_class = unit_type.unit_class
|
||||||
|
if unit_class is GroundUnitClass.Tank:
|
||||||
collection = self.tank_groups
|
collection = self.tank_groups
|
||||||
role = CombatGroupRole.TANK
|
role = CombatGroupRole.TANK
|
||||||
elif unit_type in GroundUnitClass.Apc:
|
elif unit_class is GroundUnitClass.Apc:
|
||||||
collection = self.apc_group
|
collection = self.apc_group
|
||||||
role = CombatGroupRole.APC
|
role = CombatGroupRole.APC
|
||||||
elif unit_type in GroundUnitClass.Artillery:
|
elif unit_class is GroundUnitClass.Artillery:
|
||||||
collection = self.art_group
|
collection = self.art_group
|
||||||
role = CombatGroupRole.ARTILLERY
|
role = CombatGroupRole.ARTILLERY
|
||||||
elif unit_type in GroundUnitClass.Ifv:
|
elif unit_class is GroundUnitClass.Ifv:
|
||||||
collection = self.ifv_group
|
collection = self.ifv_group
|
||||||
role = CombatGroupRole.IFV
|
role = CombatGroupRole.IFV
|
||||||
elif unit_type in GroundUnitClass.Logistics:
|
elif unit_class is GroundUnitClass.Logistics:
|
||||||
collection = self.logi_groups
|
collection = self.logi_groups
|
||||||
role = CombatGroupRole.LOGI
|
role = CombatGroupRole.LOGI
|
||||||
elif unit_type in GroundUnitClass.Atgm:
|
elif unit_class is GroundUnitClass.Atgm:
|
||||||
collection = self.atgm_group
|
collection = self.atgm_group
|
||||||
role = CombatGroupRole.ATGM
|
role = CombatGroupRole.ATGM
|
||||||
elif unit_type in GroundUnitClass.Shorads:
|
elif unit_class is GroundUnitClass.Shorads:
|
||||||
collection = self.shorad_groups
|
collection = self.shorad_groups
|
||||||
role = CombatGroupRole.SHORAD
|
role = CombatGroupRole.SHORAD
|
||||||
elif unit_type in GroundUnitClass.Recon:
|
elif unit_class is GroundUnitClass.Recon:
|
||||||
collection = self.recon_groups
|
collection = self.recon_groups
|
||||||
role = CombatGroupRole.RECON
|
role = CombatGroupRole.RECON
|
||||||
else:
|
else:
|
||||||
@ -137,17 +138,17 @@ class GroundPlanner:
|
|||||||
while available > 0:
|
while available > 0:
|
||||||
|
|
||||||
if role == CombatGroupRole.SHORAD:
|
if role == CombatGroupRole.SHORAD:
|
||||||
n = 1
|
count = 1
|
||||||
else:
|
else:
|
||||||
n = random.choice(group_size_choice)
|
count = random.choice(group_size_choice)
|
||||||
if n > available:
|
if count > available:
|
||||||
if available >= 2:
|
if available >= 2:
|
||||||
n = 2
|
count = 2
|
||||||
else:
|
else:
|
||||||
n = 1
|
count = 1
|
||||||
available -= n
|
available -= count
|
||||||
|
|
||||||
group = CombatGroup(role)
|
group = CombatGroup(role, unit_type, count)
|
||||||
if len(self.connected_enemy_cp) > 0:
|
if len(self.connected_enemy_cp) > 0:
|
||||||
enemy_cp = random.choice(self.connected_enemy_cp).id
|
enemy_cp = random.choice(self.connected_enemy_cp).id
|
||||||
self.units_per_cp[enemy_cp].append(group)
|
self.units_per_cp[enemy_cp].append(group)
|
||||||
@ -155,9 +156,6 @@ class GroundPlanner:
|
|||||||
else:
|
else:
|
||||||
self.reserve.append(group)
|
self.reserve.append(group)
|
||||||
group.assigned_enemy_cp = "__reserve__"
|
group.assigned_enemy_cp = "__reserve__"
|
||||||
|
|
||||||
for i in range(n):
|
|
||||||
group.units.append(unit_type)
|
|
||||||
collection.append(group)
|
collection.append(group)
|
||||||
|
|
||||||
if remaining_available_frontline_units == 0:
|
if remaining_available_frontline_units == 0:
|
||||||
|
|||||||
@ -3,11 +3,9 @@ import time
|
|||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from dcs.country import Country
|
from dcs.country import Country
|
||||||
from dcs.unittype import UnitType
|
|
||||||
|
|
||||||
from game import db
|
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from game.dcs.unittype import UnitType
|
||||||
from gen.flights.flight import Flight
|
from gen.flights.flight import Flight
|
||||||
|
|
||||||
ALPHA_MILITARY = [
|
ALPHA_MILITARY = [
|
||||||
@ -298,7 +296,7 @@ class NameGenerator:
|
|||||||
def next_unit_name(cls, country: Country, parent_base_id: int, unit_type: UnitType):
|
def next_unit_name(cls, country: Country, parent_base_id: int, unit_type: UnitType):
|
||||||
cls.number += 1
|
cls.number += 1
|
||||||
return "unit|{}|{}|{}|{}|".format(
|
return "unit|{}|{}|{}|{}|".format(
|
||||||
country.id, cls.number, parent_base_id, db.unit_type_name(unit_type)
|
country.id, cls.number, parent_base_id, unit_type.name
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -310,7 +308,7 @@ class NameGenerator:
|
|||||||
country.id,
|
country.id,
|
||||||
cls.infantry_number,
|
cls.infantry_number,
|
||||||
parent_base_id,
|
parent_base_id,
|
||||||
db.unit_type_name(unit_type),
|
unit_type.name,
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -10,7 +10,8 @@ from dcs.unit import Unit
|
|||||||
from dcs.vehicles import vehicle_map
|
from dcs.vehicles import vehicle_map
|
||||||
from shapely.geometry import LineString, Point as ShapelyPoint, Polygon, MultiPolygon
|
from shapely.geometry import LineString, Point as ShapelyPoint, Polygon, MultiPolygon
|
||||||
|
|
||||||
from game import Game, db
|
from game import Game
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.navmesh import NavMesh
|
from game.navmesh import NavMesh
|
||||||
from game.profiling import logged_duration
|
from game.profiling import logged_duration
|
||||||
from game.theater import (
|
from game.theater import (
|
||||||
@ -191,12 +192,6 @@ class GroundObjectJs(QObject):
|
|||||||
self.game = game
|
self.game = game
|
||||||
self.theater = game.theater
|
self.theater = game.theater
|
||||||
self.buildings = self.theater.find_ground_objects_by_obj_name(self.tgo.obj_name)
|
self.buildings = self.theater.find_ground_objects_by_obj_name(self.tgo.obj_name)
|
||||||
|
|
||||||
if self.tgo.is_friendly(to_player=True):
|
|
||||||
self.country = game.player_country
|
|
||||||
else:
|
|
||||||
self.country = game.enemy_country
|
|
||||||
|
|
||||||
self.dialog: Optional[QGroundObjectMenu] = None
|
self.dialog: Optional[QGroundObjectMenu] = None
|
||||||
|
|
||||||
@Slot()
|
@Slot()
|
||||||
@ -223,14 +218,15 @@ class GroundObjectJs(QObject):
|
|||||||
def category(self) -> str:
|
def category(self) -> str:
|
||||||
return self.tgo.category
|
return self.tgo.category
|
||||||
|
|
||||||
def make_unit_name(self, unit: Unit, dead: bool) -> str:
|
@staticmethod
|
||||||
|
def make_unit_name(unit: Unit, dead: bool) -> str:
|
||||||
dead_label = " [DEAD]" if dead else ""
|
dead_label = " [DEAD]" if dead else ""
|
||||||
unit_display_name = unit.type
|
unit_display_name = unit.type
|
||||||
unit_type = vehicle_map.get(unit.type)
|
dcs_unit_type = vehicle_map.get(unit.type)
|
||||||
if unit_type is not None:
|
if dcs_unit_type is not None:
|
||||||
unit_display_name = db.unit_get_expanded_info(
|
# TODO: Make the TGO contain GroundUnitType instead of the pydcs Group.
|
||||||
self.country, unit_type, "name"
|
# This is a hack because we can't know which variant was used.
|
||||||
)
|
unit_display_name = next(GroundUnitType.for_dcs_type(dcs_unit_type)).name
|
||||||
return f"Unit #{unit.id} - {unit_display_name}{dead_label}"
|
return f"Unit #{unit.id} - {unit_display_name}{dead_label}"
|
||||||
|
|
||||||
@Property(list, notify=unitsChanged)
|
@Property(list, notify=unitsChanged)
|
||||||
|
|||||||
@ -11,7 +11,6 @@ from PySide2.QtWidgets import (
|
|||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
)
|
)
|
||||||
|
|
||||||
from game import db
|
|
||||||
from game.debriefing import Debriefing
|
from game.debriefing import Debriefing
|
||||||
|
|
||||||
|
|
||||||
@ -24,25 +23,19 @@ class LossGrid(QGridLayout):
|
|||||||
|
|
||||||
self.add_loss_rows(debriefing.air_losses.by_type(player), lambda u: u.name)
|
self.add_loss_rows(debriefing.air_losses.by_type(player), lambda u: u.name)
|
||||||
self.add_loss_rows(
|
self.add_loss_rows(
|
||||||
debriefing.front_line_losses_by_type(player),
|
debriefing.front_line_losses_by_type(player), lambda u: str(u)
|
||||||
lambda u: db.unit_type_name(u),
|
|
||||||
)
|
)
|
||||||
self.add_loss_rows(
|
self.add_loss_rows(
|
||||||
debriefing.convoy_losses_by_type(player),
|
debriefing.convoy_losses_by_type(player), lambda u: f"{u} from convoy"
|
||||||
lambda u: f"{db.unit_type_name(u)} from convoy",
|
|
||||||
)
|
)
|
||||||
self.add_loss_rows(
|
self.add_loss_rows(
|
||||||
debriefing.cargo_ship_losses_by_type(player),
|
debriefing.cargo_ship_losses_by_type(player),
|
||||||
lambda u: f"{db.unit_type_name(u)} from cargo ship",
|
lambda u: f"{u} from cargo ship",
|
||||||
)
|
)
|
||||||
self.add_loss_rows(
|
self.add_loss_rows(
|
||||||
debriefing.airlift_losses_by_type(player),
|
debriefing.airlift_losses_by_type(player), lambda u: f"{u} from airlift"
|
||||||
lambda u: f"{db.unit_type_name(u)} from airlift",
|
|
||||||
)
|
|
||||||
self.add_loss_rows(
|
|
||||||
debriefing.building_losses_by_type(player),
|
|
||||||
lambda u: u,
|
|
||||||
)
|
)
|
||||||
|
self.add_loss_rows(debriefing.building_losses_by_type(player), lambda u: u)
|
||||||
|
|
||||||
# TODO: Display dead ground object units and runways.
|
# TODO: Display dead ground object units and runways.
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,5 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import Type, Union
|
|
||||||
|
|
||||||
import dcs
|
|
||||||
from PySide2.QtCore import Qt
|
from PySide2.QtCore import Qt
|
||||||
from PySide2.QtGui import QIcon
|
from PySide2.QtGui import QIcon
|
||||||
from PySide2.QtWidgets import (
|
from PySide2.QtWidgets import (
|
||||||
@ -13,79 +9,23 @@ from PySide2.QtWidgets import (
|
|||||||
QTextBrowser,
|
QTextBrowser,
|
||||||
QFrame,
|
QFrame,
|
||||||
)
|
)
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
import gen.flights.ai_flight_planner_db
|
import gen.flights.ai_flight_planner_db
|
||||||
from game import db
|
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.aircrafttype import AircraftType
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
|
from game.dcs.unittype import UnitType
|
||||||
from game.game import Game
|
from game.game import Game
|
||||||
from gen.flights.flight import FlightType
|
from gen.flights.flight import FlightType
|
||||||
from qt_ui.uiconstants import AIRCRAFT_BANNERS, VEHICLE_BANNERS
|
from qt_ui.uiconstants import AIRCRAFT_BANNERS, VEHICLE_BANNERS
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class UnitInfo:
|
|
||||||
name: str
|
|
||||||
description: str
|
|
||||||
introduction_year: str
|
|
||||||
origin: str
|
|
||||||
manufacturer: str
|
|
||||||
role: str
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_unit_type(
|
|
||||||
cls, country: str, unit_type: Union[AircraftType, Type[VehicleType]]
|
|
||||||
) -> UnitInfo:
|
|
||||||
if isinstance(unit_type, AircraftType):
|
|
||||||
return cls.from_aircraft(unit_type)
|
|
||||||
else:
|
|
||||||
return cls.from_vehicle_type(country, unit_type)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_aircraft(cls, aircraft: AircraftType) -> UnitInfo:
|
|
||||||
return UnitInfo(
|
|
||||||
aircraft.name,
|
|
||||||
aircraft.description,
|
|
||||||
aircraft.year_introduced,
|
|
||||||
aircraft.country_of_origin,
|
|
||||||
aircraft.manufacturer,
|
|
||||||
aircraft.role,
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_vehicle_type(cls, country: str, unit_type: Type[VehicleType]) -> UnitInfo:
|
|
||||||
name = db.unit_get_expanded_info(country, unit_type, "name")
|
|
||||||
manufacturer = db.unit_get_expanded_info(country, unit_type, "manufacturer")
|
|
||||||
origin = db.unit_get_expanded_info(country, unit_type, "country-of-origin")
|
|
||||||
role = db.unit_get_expanded_info(country, unit_type, "role")
|
|
||||||
introduction = db.unit_get_expanded_info(
|
|
||||||
country, unit_type, "year-of-variant-introduction"
|
|
||||||
)
|
|
||||||
description = db.unit_get_expanded_info(country, unit_type, "text")
|
|
||||||
return UnitInfo(
|
|
||||||
name,
|
|
||||||
description,
|
|
||||||
introduction,
|
|
||||||
origin,
|
|
||||||
manufacturer,
|
|
||||||
role,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class QUnitInfoWindow(QDialog):
|
class QUnitInfoWindow(QDialog):
|
||||||
def __init__(
|
def __init__(self, game: Game, unit_type: UnitType) -> None:
|
||||||
self, game: Game, unit_type: Union[AircraftType, Type[VehicleType]]
|
|
||||||
) -> None:
|
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.setModal(True)
|
self.setModal(True)
|
||||||
self.game = game
|
self.game = game
|
||||||
self.unit_type = unit_type
|
self.unit_type = unit_type
|
||||||
if isinstance(unit_type, AircraftType):
|
|
||||||
self.name = unit_type.name
|
self.name = unit_type.name
|
||||||
else:
|
|
||||||
self.name = db.unit_get_expanded_info(
|
|
||||||
self.game.player_country, self.unit_type, "name"
|
|
||||||
)
|
|
||||||
self.setWindowTitle(f"Unit Info: {self.name}")
|
self.setWindowTitle(f"Unit Info: {self.name}")
|
||||||
self.setWindowIcon(QIcon("./resources/icon.png"))
|
self.setWindowIcon(QIcon("./resources/icon.png"))
|
||||||
self.setMinimumHeight(570)
|
self.setMinimumHeight(570)
|
||||||
@ -101,8 +41,8 @@ class QUnitInfoWindow(QDialog):
|
|||||||
|
|
||||||
if isinstance(self.unit_type, AircraftType):
|
if isinstance(self.unit_type, AircraftType):
|
||||||
pixmap = AIRCRAFT_BANNERS.get(self.unit_type.dcs_id)
|
pixmap = AIRCRAFT_BANNERS.get(self.unit_type.dcs_id)
|
||||||
elif dcs.vehicles.vehicle_map.get(self.unit_type.id) is not None:
|
elif isinstance(self.unit_type, GroundUnitType):
|
||||||
pixmap = VEHICLE_BANNERS.get(self.unit_type.id)
|
pixmap = VEHICLE_BANNERS.get(self.unit_type.dcs_id)
|
||||||
if pixmap is None:
|
if pixmap is None:
|
||||||
pixmap = AIRCRAFT_BANNERS.get("Missing")
|
pixmap = AIRCRAFT_BANNERS.get("Missing")
|
||||||
header.setPixmap(pixmap.scaled(header.width(), header.height()))
|
header.setPixmap(pixmap.scaled(header.width(), header.height()))
|
||||||
@ -115,20 +55,21 @@ class QUnitInfoWindow(QDialog):
|
|||||||
self.details_grid_layout = QGridLayout()
|
self.details_grid_layout = QGridLayout()
|
||||||
self.details_grid_layout.setMargin(0)
|
self.details_grid_layout.setMargin(0)
|
||||||
|
|
||||||
unit_info = UnitInfo.from_unit_type(self.game.player_country, self.unit_type)
|
|
||||||
self.name_box = QLabel(
|
self.name_box = QLabel(
|
||||||
f"<b>Name:</b> {unit_info.manufacturer} {unit_info.name}"
|
f"<b>Name:</b> {unit_type.manufacturer} {unit_type.name}"
|
||||||
)
|
)
|
||||||
self.name_box.setProperty("style", "info-element")
|
self.name_box.setProperty("style", "info-element")
|
||||||
|
|
||||||
self.country_box = QLabel(f"<b>Country of Origin:</b> {unit_info.origin}")
|
self.country_box = QLabel(
|
||||||
|
f"<b>Country of Origin:</b> {unit_type.country_of_origin}"
|
||||||
|
)
|
||||||
self.country_box.setProperty("style", "info-element")
|
self.country_box.setProperty("style", "info-element")
|
||||||
|
|
||||||
self.role_box = QLabel(f"<b>Role:</b> {unit_info.role}")
|
self.role_box = QLabel(f"<b>Role:</b> {unit_type.role}")
|
||||||
self.role_box.setProperty("style", "info-element")
|
self.role_box.setProperty("style", "info-element")
|
||||||
|
|
||||||
self.year_box = QLabel(
|
self.year_box = QLabel(
|
||||||
f"<b>Variant Introduction:</b> {unit_info.introduction_year}"
|
f"<b>Variant Introduction:</b> {unit_type.year_introduced}"
|
||||||
)
|
)
|
||||||
self.year_box.setProperty("style", "info-element")
|
self.year_box.setProperty("style", "info-element")
|
||||||
|
|
||||||
@ -152,7 +93,7 @@ class QUnitInfoWindow(QDialog):
|
|||||||
# Finally, add the description box.
|
# Finally, add the description box.
|
||||||
self.details_text = QTextBrowser()
|
self.details_text = QTextBrowser()
|
||||||
self.details_text.setProperty("style", "info-desc")
|
self.details_text.setProperty("style", "info-desc")
|
||||||
self.details_text.setText(unit_info.description)
|
self.details_text.setText(unit_type.description)
|
||||||
self.gridLayout.addWidget(self.details_text, 3, 0)
|
self.gridLayout.addWidget(self.details_text, 3, 0)
|
||||||
|
|
||||||
self.layout.addLayout(self.gridLayout, 1, 0)
|
self.layout.addLayout(self.gridLayout, 1, 0)
|
||||||
|
|||||||
@ -10,7 +10,6 @@ from PySide2.QtWidgets import (
|
|||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
|
|
||||||
from game import db
|
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from game.transfers import MultiGroupTransport
|
from game.transfers import MultiGroupTransport
|
||||||
from qt_ui.dialogs import Dialog
|
from qt_ui.dialogs import Dialog
|
||||||
@ -19,7 +18,7 @@ from qt_ui.uiconstants import VEHICLES_ICONS
|
|||||||
|
|
||||||
|
|
||||||
class DepartingConvoyInfo(QGroupBox):
|
class DepartingConvoyInfo(QGroupBox):
|
||||||
def __init__(self, convoy: MultiGroupTransport, game_model: GameModel) -> None:
|
def __init__(self, convoy: MultiGroupTransport) -> None:
|
||||||
super().__init__(f"{convoy.name} to {convoy.destination}")
|
super().__init__(f"{convoy.name} to {convoy.destination}")
|
||||||
self.convoy = convoy
|
self.convoy = convoy
|
||||||
|
|
||||||
@ -31,17 +30,14 @@ class DepartingConvoyInfo(QGroupBox):
|
|||||||
|
|
||||||
for idx, (unit_type, count) in enumerate(convoy.units.items()):
|
for idx, (unit_type, count) in enumerate(convoy.units.items()):
|
||||||
icon = QLabel()
|
icon = QLabel()
|
||||||
if unit_type.id in VEHICLES_ICONS.keys():
|
if unit_type.dcs_id in VEHICLES_ICONS.keys():
|
||||||
icon.setPixmap(VEHICLES_ICONS[unit_type.id])
|
icon.setPixmap(VEHICLES_ICONS[unit_type.dcs_id])
|
||||||
else:
|
else:
|
||||||
icon.setText("<b>" + unit_type.id[:8] + "</b>")
|
icon.setText("<b>" + unit_type.id[:8] + "</b>")
|
||||||
icon.setProperty("style", "icon-armor")
|
icon.setProperty("style", "icon-armor")
|
||||||
unit_layout.addWidget(icon, idx, 0)
|
unit_layout.addWidget(icon, idx, 0)
|
||||||
unit_display_name = db.unit_get_expanded_info(
|
|
||||||
game_model.game.enemy_country, unit_type, "name"
|
|
||||||
)
|
|
||||||
unit_layout.addWidget(
|
unit_layout.addWidget(
|
||||||
QLabel(f"{count} x <strong>{unit_display_name}</strong>"),
|
QLabel(f"{count} x <strong>{unit_type.name}</strong>"),
|
||||||
idx,
|
idx,
|
||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
@ -68,7 +64,6 @@ class DepartingConvoysList(QFrame):
|
|||||||
def __init__(self, cp: ControlPoint, game_model: GameModel):
|
def __init__(self, cp: ControlPoint, game_model: GameModel):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.cp = cp
|
self.cp = cp
|
||||||
self.game_model = game_model
|
|
||||||
self.setMinimumWidth(500)
|
self.setMinimumWidth(500)
|
||||||
|
|
||||||
layout = QVBoxLayout()
|
layout = QVBoxLayout()
|
||||||
@ -79,11 +74,11 @@ class DepartingConvoysList(QFrame):
|
|||||||
scroll_content.setLayout(task_box_layout)
|
scroll_content.setLayout(task_box_layout)
|
||||||
|
|
||||||
for convoy in game_model.game.transfers.convoys.departing_from(cp):
|
for convoy in game_model.game.transfers.convoys.departing_from(cp):
|
||||||
group_info = DepartingConvoyInfo(convoy, game_model)
|
group_info = DepartingConvoyInfo(convoy)
|
||||||
task_box_layout.addWidget(group_info)
|
task_box_layout.addWidget(group_info)
|
||||||
|
|
||||||
for cargo_ship in game_model.game.transfers.cargo_ships.departing_from(cp):
|
for cargo_ship in game_model.game.transfers.cargo_ships.departing_from(cp):
|
||||||
group_info = DepartingConvoyInfo(cargo_ship, game_model)
|
group_info = DepartingConvoyInfo(cargo_ship)
|
||||||
task_box_layout.addWidget(group_info)
|
task_box_layout.addWidget(group_info)
|
||||||
|
|
||||||
scroll_content.setLayout(task_box_layout)
|
scroll_content.setLayout(task_box_layout)
|
||||||
|
|||||||
@ -20,10 +20,10 @@ from PySide2.QtWidgets import (
|
|||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
from dcs.task import PinpointStrike
|
|
||||||
from dcs.unittype import UnitType
|
from dcs.unittype import UnitType
|
||||||
|
|
||||||
from game import Game, db
|
from game import Game
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from game.transfers import TransferOrder
|
from game.transfers import TransferOrder
|
||||||
from qt_ui.models import GameModel
|
from qt_ui.models import GameModel
|
||||||
@ -63,12 +63,7 @@ class UnitTransferList(QFrame):
|
|||||||
task_box_layout = QGridLayout()
|
task_box_layout = QGridLayout()
|
||||||
scroll_content.setLayout(task_box_layout)
|
scroll_content.setLayout(task_box_layout)
|
||||||
|
|
||||||
units_column = sorted(
|
units_column = sorted(cp.base.armor, key=lambda u: u.name)
|
||||||
cp.base.armor,
|
|
||||||
key=lambda u: db.unit_get_expanded_info(
|
|
||||||
self.game_model.game.player_country, u, "name"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
for count, unit_type in enumerate(units_column):
|
for count, unit_type in enumerate(units_column):
|
||||||
@ -169,9 +164,7 @@ class ScrollingUnitTransferGrid(QFrame):
|
|||||||
unit_types = set(self.game_model.game.faction_for(player=True).ground_units)
|
unit_types = set(self.game_model.game.faction_for(player=True).ground_units)
|
||||||
sorted_units = sorted(
|
sorted_units = sorted(
|
||||||
{u for u in unit_types if self.cp.base.total_units_of_type(u)},
|
{u for u in unit_types if self.cp.base.total_units_of_type(u)},
|
||||||
key=lambda u: db.unit_get_expanded_info(
|
key=lambda u: u.name,
|
||||||
self.game_model.game.player_country, u, "name"
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
for row, unit_type in enumerate(sorted_units):
|
for row, unit_type in enumerate(sorted_units):
|
||||||
self.add_unit_row(unit_type, task_box_layout, row)
|
self.add_unit_row(unit_type, task_box_layout, row)
|
||||||
@ -190,7 +183,7 @@ class ScrollingUnitTransferGrid(QFrame):
|
|||||||
|
|
||||||
def add_unit_row(
|
def add_unit_row(
|
||||||
self,
|
self,
|
||||||
unit_type: Type[UnitType],
|
unit_type: GroundUnitType,
|
||||||
layout: QGridLayout,
|
layout: QGridLayout,
|
||||||
row: int,
|
row: int,
|
||||||
) -> None:
|
) -> None:
|
||||||
@ -203,13 +196,7 @@ class ScrollingUnitTransferGrid(QFrame):
|
|||||||
|
|
||||||
origin_inventory = self.cp.base.total_units_of_type(unit_type)
|
origin_inventory = self.cp.base.total_units_of_type(unit_type)
|
||||||
|
|
||||||
unit_name = QLabel(
|
unit_name = QLabel(f"<b>{unit_type.name}</b>")
|
||||||
"<b>"
|
|
||||||
+ db.unit_get_expanded_info(
|
|
||||||
self.game_model.game.player_country, unit_type, "name"
|
|
||||||
)
|
|
||||||
+ "</b>"
|
|
||||||
)
|
|
||||||
unit_name.setSizePolicy(
|
unit_name.setSizePolicy(
|
||||||
QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
import logging
|
import logging
|
||||||
from typing import Type, Union
|
|
||||||
|
|
||||||
from PySide2.QtWidgets import (
|
from PySide2.QtWidgets import (
|
||||||
QGroupBox,
|
QGroupBox,
|
||||||
@ -10,9 +9,8 @@ from PySide2.QtWidgets import (
|
|||||||
QSizePolicy,
|
QSizePolicy,
|
||||||
QSpacerItem,
|
QSpacerItem,
|
||||||
)
|
)
|
||||||
from dcs.unittype import VehicleType
|
|
||||||
|
|
||||||
from game.dcs.aircrafttype import AircraftType
|
from game.dcs.unittype import UnitType
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from game.unitdelivery import PendingUnitDeliveries
|
from game.unitdelivery import PendingUnitDeliveries
|
||||||
from qt_ui.models import GameModel
|
from qt_ui.models import GameModel
|
||||||
@ -38,7 +36,7 @@ class QRecruitBehaviour:
|
|||||||
return self.cp.pending_unit_deliveries
|
return self.cp.pending_unit_deliveries
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def budget(self) -> int:
|
def budget(self) -> float:
|
||||||
return self.game_model.game.budget
|
return self.game_model.game.budget
|
||||||
|
|
||||||
@budget.setter
|
@budget.setter
|
||||||
@ -47,7 +45,7 @@ class QRecruitBehaviour:
|
|||||||
|
|
||||||
def add_purchase_row(
|
def add_purchase_row(
|
||||||
self,
|
self,
|
||||||
unit_type: Union[AircraftType, Type[VehicleType]],
|
unit_type: UnitType,
|
||||||
layout: QLayout,
|
layout: QLayout,
|
||||||
row: int,
|
row: int,
|
||||||
) -> int:
|
) -> int:
|
||||||
@ -61,7 +59,7 @@ class QRecruitBehaviour:
|
|||||||
existing_units = self.cp.base.total_units_of_type(unit_type)
|
existing_units = self.cp.base.total_units_of_type(unit_type)
|
||||||
scheduled_units = self.pending_deliveries.units.get(unit_type, 0)
|
scheduled_units = self.pending_deliveries.units.get(unit_type, 0)
|
||||||
|
|
||||||
unitName = QLabel(f"<b>{self.name_of(unit_type)}</b>")
|
unitName = QLabel(f"<b>{unit_type.name}</b>")
|
||||||
unitName.setSizePolicy(
|
unitName.setSizePolicy(
|
||||||
QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||||||
)
|
)
|
||||||
@ -75,7 +73,7 @@ class QRecruitBehaviour:
|
|||||||
self.existing_units_labels[unit_type] = existing_units
|
self.existing_units_labels[unit_type] = existing_units
|
||||||
self.bought_amount_labels[unit_type] = amount_bought
|
self.bought_amount_labels[unit_type] = amount_bought
|
||||||
|
|
||||||
price = QLabel(f"<b>$ {self.price_of(unit_type)}</b> M")
|
price = QLabel(f"<b>$ {unit_type.price}</b> M")
|
||||||
price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
|
price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
|
||||||
|
|
||||||
buysell = QGroupBox()
|
buysell = QGroupBox()
|
||||||
@ -149,8 +147,7 @@ class QRecruitBehaviour:
|
|||||||
|
|
||||||
return row + 1
|
return row + 1
|
||||||
|
|
||||||
def _update_count_label(self, unit_type: Union[AircraftType, Type[VehicleType]]):
|
def _update_count_label(self, unit_type: UnitType) -> None:
|
||||||
|
|
||||||
self.bought_amount_labels[unit_type].setText(
|
self.bought_amount_labels[unit_type].setText(
|
||||||
"<b>{}</b>".format(
|
"<b>{}</b>".format(
|
||||||
unit_type in self.pending_deliveries.units
|
unit_type in self.pending_deliveries.units
|
||||||
@ -166,34 +163,32 @@ class QRecruitBehaviour:
|
|||||||
def update_available_budget(self) -> None:
|
def update_available_budget(self) -> None:
|
||||||
GameUpdateSignal.get_instance().updateBudget(self.game_model.game)
|
GameUpdateSignal.get_instance().updateBudget(self.game_model.game)
|
||||||
|
|
||||||
def buy(self, unit_type: Union[AircraftType, Type[VehicleType]]):
|
def buy(self, unit_type: UnitType) -> None:
|
||||||
if not self.enable_purchase(unit_type):
|
if not self.enable_purchase(unit_type):
|
||||||
logging.error(f"Purchase of {unit_type.id} not allowed at {self.cp.name}")
|
logging.error(f"Purchase of {unit_type} not allowed at {self.cp.name}")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.pending_deliveries.order({unit_type: 1})
|
self.pending_deliveries.order({unit_type: 1})
|
||||||
self.budget -= self.price_of(unit_type)
|
self.budget -= unit_type.price
|
||||||
self._update_count_label(unit_type)
|
self._update_count_label(unit_type)
|
||||||
self.update_available_budget()
|
self.update_available_budget()
|
||||||
|
|
||||||
def sell(self, unit_type):
|
def sell(self, unit_type: UnitType) -> None:
|
||||||
if self.pending_deliveries.available_next_turn(unit_type) > 0:
|
if self.pending_deliveries.available_next_turn(unit_type) > 0:
|
||||||
self.budget += self.price_of(unit_type)
|
self.budget += unit_type.price
|
||||||
self.pending_deliveries.sell({unit_type: 1})
|
self.pending_deliveries.sell({unit_type: 1})
|
||||||
if self.pending_deliveries.units[unit_type] == 0:
|
if self.pending_deliveries.units[unit_type] == 0:
|
||||||
del self.pending_deliveries.units[unit_type]
|
del self.pending_deliveries.units[unit_type]
|
||||||
self._update_count_label(unit_type)
|
self._update_count_label(unit_type)
|
||||||
self.update_available_budget()
|
self.update_available_budget()
|
||||||
|
|
||||||
def enable_purchase(
|
def enable_purchase(self, unit_type: UnitType) -> bool:
|
||||||
self, unit_type: Union[AircraftType, Type[VehicleType]]
|
return self.budget >= unit_type.price
|
||||||
) -> bool:
|
|
||||||
return self.budget >= self.price_of(unit_type)
|
|
||||||
|
|
||||||
def enable_sale(self, unit_type: Union[AircraftType, Type[VehicleType]]) -> bool:
|
def enable_sale(self, unit_type: UnitType) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def info(self, unit_type):
|
def info(self, unit_type: UnitType) -> None:
|
||||||
self.info_window = QUnitInfoWindow(self.game_model.game, unit_type)
|
self.info_window = QUnitInfoWindow(self.game_model.game, unit_type)
|
||||||
self.info_window.show()
|
self.info_window.show()
|
||||||
|
|
||||||
@ -202,9 +197,3 @@ class QRecruitBehaviour:
|
|||||||
Set the maximum number of units that can be bought
|
Set the maximum number of units that can be bought
|
||||||
"""
|
"""
|
||||||
self.maximum_units = maximum_units
|
self.maximum_units = maximum_units
|
||||||
|
|
||||||
def name_of(self, unit_type: Union[AircraftType, Type[VehicleType]]) -> str:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|
||||||
def price_of(self, unit_type: Union[AircraftType, Type[VehicleType]]) -> int:
|
|
||||||
raise NotImplementedError
|
|
||||||
|
|||||||
@ -92,12 +92,6 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def name_of(self, unit_type: AircraftType) -> str:
|
|
||||||
return unit_type.name
|
|
||||||
|
|
||||||
def price_of(self, unit_type: AircraftType) -> int:
|
|
||||||
return unit_type.price
|
|
||||||
|
|
||||||
def buy(self, unit_type: AircraftType) -> None:
|
def buy(self, unit_type: AircraftType) -> None:
|
||||||
if self.maximum_units > 0:
|
if self.maximum_units > 0:
|
||||||
if self.cp.unclaimed_parking(self.game_model.game) <= 0:
|
if self.cp.unclaimed_parking(self.game_model.game) <= 0:
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
from typing import Type
|
|
||||||
|
|
||||||
from PySide2.QtCore import Qt
|
from PySide2.QtCore import Qt
|
||||||
from PySide2.QtWidgets import (
|
from PySide2.QtWidgets import (
|
||||||
QFrame,
|
QFrame,
|
||||||
@ -8,10 +6,8 @@ from PySide2.QtWidgets import (
|
|||||||
QVBoxLayout,
|
QVBoxLayout,
|
||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
from dcs.unittype import UnitType, VehicleType
|
|
||||||
|
|
||||||
from game import db
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.db import PRICES
|
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from qt_ui.models import GameModel
|
from qt_ui.models import GameModel
|
||||||
from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
|
from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
|
||||||
@ -39,11 +35,7 @@ class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour):
|
|||||||
unit_types = list(
|
unit_types = list(
|
||||||
set(self.game_model.game.faction_for(player=True).ground_units)
|
set(self.game_model.game.faction_for(player=True).ground_units)
|
||||||
)
|
)
|
||||||
unit_types.sort(
|
unit_types.sort(key=lambda u: u.name)
|
||||||
key=lambda u: db.unit_get_expanded_info(
|
|
||||||
self.game_model.game.player_country, u, "name"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
for unit_type in unit_types:
|
for unit_type in unit_types:
|
||||||
row = self.add_purchase_row(unit_type, task_box_layout, row)
|
row = self.add_purchase_row(unit_type, task_box_layout, row)
|
||||||
stretch = QVBoxLayout()
|
stretch = QVBoxLayout()
|
||||||
@ -59,18 +51,10 @@ class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour):
|
|||||||
main_layout.addWidget(scroll)
|
main_layout.addWidget(scroll)
|
||||||
self.setLayout(main_layout)
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
def enable_purchase(self, unit_type: Type[UnitType]) -> bool:
|
def enable_purchase(self, unit_type: GroundUnitType) -> bool:
|
||||||
if not super().enable_purchase(unit_type):
|
if not super().enable_purchase(unit_type):
|
||||||
return False
|
return False
|
||||||
return self.cp.has_ground_unit_source(self.game_model.game)
|
return self.cp.has_ground_unit_source(self.game_model.game)
|
||||||
|
|
||||||
def enable_sale(self, unit_type: Type[UnitType]) -> bool:
|
def enable_sale(self, unit_type: GroundUnitType) -> bool:
|
||||||
return self.pending_deliveries.pending_orders(unit_type) > 0
|
return self.pending_deliveries.pending_orders(unit_type) > 0
|
||||||
|
|
||||||
def name_of(self, unit_type: Type[VehicleType]) -> str:
|
|
||||||
return db.unit_get_expanded_info(
|
|
||||||
self.game_model.game.player_country, unit_type, "name"
|
|
||||||
)
|
|
||||||
|
|
||||||
def price_of(self, unit_type: Type[VehicleType]) -> int:
|
|
||||||
return PRICES[unit_type]
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ from PySide2.QtWidgets import (
|
|||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
|
|
||||||
from game import Game, db
|
from game import Game
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
|
|
||||||
|
|
||||||
@ -38,10 +38,7 @@ class QIntelInfo(QFrame):
|
|||||||
front_line_units = defaultdict(int)
|
front_line_units = defaultdict(int)
|
||||||
for unit_type, count in self.cp.base.armor.items():
|
for unit_type, count in self.cp.base.armor.items():
|
||||||
if count:
|
if count:
|
||||||
name = db.unit_get_expanded_info(
|
front_line_units[unit_type.name] += count
|
||||||
self.game.enemy_country, unit_type, "name"
|
|
||||||
)
|
|
||||||
front_line_units[name] += count
|
|
||||||
|
|
||||||
units_by_task["Front line units"] = front_line_units
|
units_by_task["Front line units"] = front_line_units
|
||||||
for task, unit_types in units_by_task.items():
|
for task, unit_types in units_by_task.items():
|
||||||
|
|||||||
@ -20,7 +20,8 @@ from dcs import vehicles
|
|||||||
|
|
||||||
from game import Game, db
|
from game import Game, db
|
||||||
from game.data.building_data import FORTIFICATION_BUILDINGS
|
from game.data.building_data import FORTIFICATION_BUILDINGS
|
||||||
from game.db import PRICES, REWARDS, unit_type_of
|
from game.db import REWARDS
|
||||||
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.theater import ControlPoint, TheaterGroundObject
|
from game.theater import ControlPoint, TheaterGroundObject
|
||||||
from game.theater.theatergroundobject import (
|
from game.theater.theatergroundobject import (
|
||||||
VehicleGroupGroundObject,
|
VehicleGroupGroundObject,
|
||||||
@ -108,17 +109,18 @@ class QGroundObjectMenu(QDialog):
|
|||||||
for g in self.ground_object.groups:
|
for g in self.ground_object.groups:
|
||||||
if not hasattr(g, "units_losts"):
|
if not hasattr(g, "units_losts"):
|
||||||
g.units_losts = []
|
g.units_losts = []
|
||||||
for u in g.units:
|
for unit in g.units:
|
||||||
unit_display_name = u.type
|
unit_display_name = unit.type
|
||||||
unit_type = vehicles.vehicle_map.get(u.type)
|
dcs_unit_type = vehicles.vehicle_map.get(unit.type)
|
||||||
if unit_type is not None:
|
if dcs_unit_type is not None:
|
||||||
unit_display_name = db.unit_get_expanded_info(
|
# Hack: Don't know which variant is used.
|
||||||
self.game.enemy_country, unit_type, "name"
|
unit_display_name = next(
|
||||||
)
|
GroundUnitType.for_dcs_type(dcs_unit_type)
|
||||||
|
).name
|
||||||
self.intelLayout.addWidget(
|
self.intelLayout.addWidget(
|
||||||
QLabel(
|
QLabel(
|
||||||
"<b>Unit #"
|
"<b>Unit #"
|
||||||
+ str(u.id)
|
+ str(unit.id)
|
||||||
+ " - "
|
+ " - "
|
||||||
+ str(unit_display_name)
|
+ str(unit_display_name)
|
||||||
+ "</b>"
|
+ "</b>"
|
||||||
@ -128,26 +130,30 @@ class QGroundObjectMenu(QDialog):
|
|||||||
)
|
)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
|
|
||||||
for u in g.units_losts:
|
for unit in g.units_losts:
|
||||||
|
dcs_unit_type = vehicles.vehicle_map.get(unit.type)
|
||||||
|
if dcs_unit_type is None:
|
||||||
|
continue
|
||||||
|
|
||||||
utype = unit_type_of(u)
|
# Hack: Don't know which variant is used.
|
||||||
if utype in PRICES:
|
unit_type = next(GroundUnitType.for_dcs_type(dcs_unit_type))
|
||||||
price = PRICES[utype]
|
|
||||||
else:
|
|
||||||
price = 6
|
|
||||||
|
|
||||||
self.intelLayout.addWidget(
|
self.intelLayout.addWidget(
|
||||||
QLabel(
|
QLabel(
|
||||||
"<b>Unit #" + str(u.id) + " - " + str(u.type) + "</b> [DEAD]"
|
"<b>Unit #"
|
||||||
|
+ str(unit.id)
|
||||||
|
+ " - "
|
||||||
|
+ str(unit_type)
|
||||||
|
+ "</b> [DEAD]"
|
||||||
),
|
),
|
||||||
i,
|
i,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
if self.cp.captured:
|
if self.cp.captured:
|
||||||
repair = QPushButton("Repair [" + str(price) + "M]")
|
repair = QPushButton(f"Repair [{unit_type.price}M]")
|
||||||
repair.setProperty("style", "btn-success")
|
repair.setProperty("style", "btn-success")
|
||||||
repair.clicked.connect(
|
repair.clicked.connect(
|
||||||
lambda u=u, g=g, p=price: self.repair_unit(g, u, p)
|
lambda u=unit, g=g, p=unit_type.price: self.repair_unit(g, u, p)
|
||||||
)
|
)
|
||||||
self.intelLayout.addWidget(repair, i, 1)
|
self.intelLayout.addWidget(repair, i, 1)
|
||||||
i = i + 1
|
i = i + 1
|
||||||
@ -217,13 +223,12 @@ class QGroundObjectMenu(QDialog):
|
|||||||
|
|
||||||
def update_total_value(self):
|
def update_total_value(self):
|
||||||
total_value = 0
|
total_value = 0
|
||||||
for group in self.ground_object.groups:
|
if not self.ground_object.purchasable:
|
||||||
for u in group.units:
|
return
|
||||||
utype = unit_type_of(u)
|
for u in self.ground_object.units:
|
||||||
if utype in PRICES:
|
# Hack: Unknown variant.
|
||||||
total_value = total_value + PRICES[utype]
|
unit_type = next(GroundUnitType.for_dcs_type(vehicles.vehicle_map[u.type]))
|
||||||
else:
|
total_value += unit_type.price
|
||||||
total_value = total_value + 1
|
|
||||||
if self.sell_all_button is not None:
|
if self.sell_all_button is not None:
|
||||||
self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)")
|
self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)")
|
||||||
self.total_value = total_value
|
self.total_value = total_value
|
||||||
@ -340,10 +345,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
|
|||||||
|
|
||||||
# Armored units
|
# Armored units
|
||||||
for unit in set(faction.ground_units):
|
for unit in set(faction.ground_units):
|
||||||
self.buyArmorCombo.addItem(
|
self.buyArmorCombo.addItem(f"{unit} [${unit.price}M]", userData=unit)
|
||||||
db.unit_type_name_2(unit) + " [$" + str(db.PRICES[unit]) + "M]",
|
|
||||||
userData=unit,
|
|
||||||
)
|
|
||||||
self.buyArmorCombo.currentIndexChanged.connect(self.armorComboChanged)
|
self.buyArmorCombo.currentIndexChanged.connect(self.armorComboChanged)
|
||||||
|
|
||||||
self.amount.setMinimum(2)
|
self.amount.setMinimum(2)
|
||||||
@ -404,33 +406,19 @@ class QBuyGroupForGroundObjectDialog(QDialog):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def armorComboChanged(self, index):
|
def armorComboChanged(self, index):
|
||||||
self.buyArmorButton.setText(
|
unit_type = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
||||||
"Buy [$"
|
price = unit_type.price * self.amount.value()
|
||||||
+ str(db.PRICES[self.buyArmorCombo.itemData(index)] * self.amount.value())
|
self.buyArmorButton.setText(f"Buy [${price}M][-${self.current_group_value}M]")
|
||||||
+ "M][-$"
|
|
||||||
+ str(self.current_group_value)
|
|
||||||
+ "M]"
|
|
||||||
)
|
|
||||||
|
|
||||||
def amountComboChanged(self):
|
def amountComboChanged(self):
|
||||||
self.buyArmorButton.setText(
|
unit_type = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
||||||
"Buy [$"
|
price = unit_type.price * self.amount.value()
|
||||||
+ str(
|
self.buyArmorButton.setText(f"Buy [${price}M][-${self.current_group_value}M]")
|
||||||
db.PRICES[
|
|
||||||
self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
|
||||||
]
|
|
||||||
* self.amount.value()
|
|
||||||
)
|
|
||||||
+ "M][-$"
|
|
||||||
+ str(self.current_group_value)
|
|
||||||
+ "M]"
|
|
||||||
)
|
|
||||||
|
|
||||||
def buyArmor(self):
|
def buyArmor(self):
|
||||||
logging.info("Buying Armor ")
|
logging.info("Buying Armor ")
|
||||||
utype = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
utype = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
||||||
logging.info(utype)
|
price = utype.price * self.amount.value() - self.current_group_value
|
||||||
price = db.PRICES[utype] * self.amount.value() - self.current_group_value
|
|
||||||
if price > self.game.budget:
|
if price > self.game.budget:
|
||||||
self.error_money()
|
self.error_money()
|
||||||
self.close()
|
self.close()
|
||||||
|
|||||||
@ -15,7 +15,7 @@ from PySide2.QtWidgets import (
|
|||||||
QWidget,
|
QWidget,
|
||||||
)
|
)
|
||||||
|
|
||||||
from game.game import Game, db
|
from game.game import Game
|
||||||
from qt_ui.uiconstants import ICONS
|
from qt_ui.uiconstants import ICONS
|
||||||
from qt_ui.windows.finances.QFinancesMenu import FinancesLayout
|
from qt_ui.windows.finances.QFinancesMenu import FinancesLayout
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ class ArmyIntelLayout(IntelTableLayout):
|
|||||||
for vehicle, count in base.armor.items():
|
for vehicle, count in base.armor.items():
|
||||||
if not count:
|
if not count:
|
||||||
continue
|
continue
|
||||||
self.add_row(vehicle.id, count)
|
self.add_row(vehicle.name, count)
|
||||||
|
|
||||||
self.add_spacer()
|
self.add_spacer()
|
||||||
self.add_row("<b>Total</b>", total)
|
self.add_row("<b>Total</b>", total)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user