diff --git a/game/data/building_data.py b/game/data/building_data.py index bd6ab666..939609a6 100644 --- a/game/data/building_data.py +++ b/game/data/building_data.py @@ -3,6 +3,7 @@ import dcs DEFAULT_AVAILABLE_BUILDINGS = ['fuel', 'ammo', 'comms', 'oil', 'ware', 'farp', 'fob', 'power', 'factory', 'derrick', 'aa'] +WW2_FREE = ['fuel', 'factory', 'ware', 'aa'] WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp', 'aa'] WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'aa'] diff --git a/game/db.py b/game/db.py index c19af009..67eccd5a 100644 --- a/game/db.py +++ b/game/db.py @@ -2,7 +2,9 @@ from datetime import datetime from enum import Enum from typing import Dict, List, Optional, Tuple, Type, Union +from dcs import Mission from dcs.countries import country_dict +from dcs.country import Country from dcs.helicopters import ( AH_1W, AH_64A, @@ -402,18 +404,18 @@ PRICES = { Armor.MT_Pz_Kpfw_V_Panther_Ausf_G:24, Armor.MT_Pz_Kpfw_IV_Ausf_H:16, Armor.HT_Pz_Kpfw_VI_Tiger_I:24, - Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II:26, + Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II:26, Armor.TD_Jagdpanther_G1: 18, Armor.TD_Jagdpanzer_IV: 11, Armor.Sd_Kfz_184_Elefant: 18, Armor.APC_Sd_Kfz_251:4, - Armor.IFV_Sd_Kfz_234_2_Puma:8, + Armor.AC_Sd_Kfz_234_2_Puma:8, Armor.MT_M4_Sherman:12, Armor.MT_M4A4_Sherman_Firefly:16, Armor.CT_Cromwell_IV:12, Armor.M30_Cargo_Carrier:2, Armor.APC_M2A1:4, - Armor.ST_Centaur_IV: 10, + Armor.CT_Centaur_IV: 10, Armor.HIT_Churchill_VII: 16, Armor.LAC_M8_Greyhound: 8, Armor.TD_M10_GMC: 14, @@ -743,13 +745,13 @@ UNIT_BY_TASK = { Armor.MT_Pz_Kpfw_V_Panther_Ausf_G, Armor.MT_Pz_Kpfw_IV_Ausf_H, Armor.HT_Pz_Kpfw_VI_Tiger_I, - Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II, + Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II, Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251, Armor.APC_Sd_Kfz_251, - Armor.IFV_Sd_Kfz_234_2_Puma, - Armor.IFV_Sd_Kfz_234_2_Puma, + Armor.AC_Sd_Kfz_234_2_Puma, + Armor.AC_Sd_Kfz_234_2_Puma, Armor.MT_M4_Sherman, Armor.MT_M4A4_Sherman_Firefly, Armor.CT_Cromwell_IV, @@ -762,12 +764,12 @@ UNIT_BY_TASK = { Armor.MT_Pz_Kpfw_V_Panther_Ausf_G, Armor.MT_Pz_Kpfw_IV_Ausf_H, Armor.HT_Pz_Kpfw_VI_Tiger_I, - Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II, + Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II, Armor.TD_Jagdpanther_G1, Armor.TD_Jagdpanzer_IV, Armor.Sd_Kfz_184_Elefant, Armor.APC_Sd_Kfz_251, - Armor.IFV_Sd_Kfz_234_2_Puma, + Armor.AC_Sd_Kfz_234_2_Puma, Armor.MT_M4_Sherman, Armor.MT_M4A4_Sherman_Firefly, Armor.CT_Cromwell_IV, @@ -776,8 +778,8 @@ UNIT_BY_TASK = { Armor.M30_Cargo_Carrier, Armor.APC_M2A1, Armor.APC_M2A1, - Armor.ST_Centaur_IV, - Armor.ST_Centaur_IV, + Armor.CT_Centaur_IV, + Armor.CT_Centaur_IV, Armor.HIT_Churchill_VII, Armor.LAC_M8_Greyhound, Armor.LAC_M8_Greyhound, @@ -1388,6 +1390,7 @@ class DefaultLiveries: class Default(Enum): af_standard = "" + OH_58D.Liveries = DefaultLiveries F_16C_50.Liveries = DefaultLiveries P_51D_30_NA.Liveries = DefaultLiveries diff --git a/game/factions/faction.py b/game/factions/faction.py index 1ecb20fd..b6c9e058 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -10,7 +10,7 @@ from dcs.planes import plane_map from dcs.unittype import FlyingType, ShipType, VehicleType, UnitType from dcs.vehicles import Armor, Unarmed, Infantry, Artillery, AirDefence -from game.data.building_data import WW2_ALLIES_BUILDINGS, DEFAULT_AVAILABLE_BUILDINGS, WW2_GERMANY_BUILDINGS +from game.data.building_data import WW2_ALLIES_BUILDINGS, DEFAULT_AVAILABLE_BUILDINGS, WW2_GERMANY_BUILDINGS, WW2_FREE from game.data.doctrine import Doctrine, MODERN_DOCTRINE, COLDWAR_DOCTRINE, WWII_DOCTRINE from pydcs_extensions.mod_units import MODDED_VEHICLES, MODDED_AIRPLANES @@ -170,6 +170,8 @@ class Faction: building_set = json.get("building_set", "default") if building_set == "default": faction.building_set = DEFAULT_AVAILABLE_BUILDINGS + elif building_set == "ww2free": + faction.building_set = WW2_FREE elif building_set == "ww2ally": faction.building_set = WW2_ALLIES_BUILDINGS elif building_set == "ww2germany": diff --git a/gen/aircraft.py b/gen/aircraft.py index 25c4f65e..0fac70b0 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -1027,9 +1027,21 @@ class AircraftConflictGenerator: rtb_winchester=OptRTBOnOutOfAmmo.Values.Unguided, restrict_jettison=True) + def configure_dead(self, group: FlyingGroup, package: Package, + flight: Flight, + dynamic_runways: Dict[str, RunwayData]) -> None: + group.task = SEAD.name + self._setup_group(group, SEAD, package, flight, dynamic_runways) + self.configure_behavior( + group, + react_on_threat=OptReactOnThreat.Values.EvadeFire, + roe=OptROE.Values.OpenFire, + rtb_winchester=OptRTBOnOutOfAmmo.Values.ASM, + restrict_jettison=True) + def configure_sead(self, group: FlyingGroup, package: Package, - flight: Flight, - dynamic_runways: Dict[str, RunwayData]) -> None: + flight: Flight, + dynamic_runways: Dict[str, RunwayData]) -> None: group.task = SEAD.name self._setup_group(group, SEAD, package, flight, dynamic_runways) self.configure_behavior( @@ -1087,7 +1099,9 @@ class AircraftConflictGenerator: self.configure_cap(group, package, flight, dynamic_runways) elif flight_type in [FlightType.CAS, FlightType.BAI]: self.configure_cas(group, package, flight, dynamic_runways) - elif flight_type in [FlightType.SEAD, FlightType.DEAD]: + elif flight_type in [FlightType.DEAD, ]: + self.configure_dead(group, package, flight, dynamic_runways) + elif flight_type in [FlightType.SEAD, ]: self.configure_sead(group, package, flight, dynamic_runways) elif flight_type in [FlightType.STRIKE]: self.configure_strike(group, package, flight, dynamic_runways) @@ -1208,6 +1222,7 @@ class PydcsWaypointBuilder: mission: Mission) -> PydcsWaypointBuilder: builders = { FlightWaypointType.INGRESS_CAS: CasIngressBuilder, + FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder, FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder, FlightWaypointType.INGRESS_STRIKE: StrikeIngressBuilder, FlightWaypointType.JOIN: JoinPointBuilder, @@ -1273,14 +1288,14 @@ class CasIngressBuilder(PydcsWaypointBuilder): return waypoint -class SeadIngressBuilder(PydcsWaypointBuilder): +class DeadIngressBuilder(PydcsWaypointBuilder): def build(self) -> MovingPoint: waypoint = super().build() target_group = self.package.target if isinstance(target_group, TheaterGroundObject): - tgroup = self.mission.find_group(target_group.group_identifier) - if tgroup is not None: + tgroup = self.mission.find_group(target_group.group_identifier, search="match") # Match search is used due to TheaterGroundObject.name not matching + if tgroup is not None: # the Mission group name because of SkyNet prefixes. task = AttackGroup(tgroup.id) task.params["expend"] = "All" task.params["attackQtyLimit"] = False @@ -1289,6 +1304,36 @@ class SeadIngressBuilder(PydcsWaypointBuilder): task.params["weaponType"] = 268402702 # Guided Weapons task.params["groupAttack"] = True waypoint.tasks.append(task) + else: + logging.error(f"Could not find group for DEAD mission {target_group.group_identifier}") + + for i, t in enumerate(self.waypoint.targets): + if self.group.units[0].unit_type == JF_17 and i < 4: + self.group.add_nav_target_point(t.position, "PP" + str(i + 1)) + if self.group.units[0].unit_type == F_14B and i == 0: + self.group.add_nav_target_point(t.position, "ST") + if self.group.units[0].unit_type == AJS37 and i < 9: + self.group.add_nav_target_point(t.position, "M" + str(i + 1)) + return waypoint + + +class SeadIngressBuilder(PydcsWaypointBuilder): + def build(self) -> MovingPoint: + waypoint = super().build() + + target_group = self.package.target + if isinstance(target_group, TheaterGroundObject): + tgroup = self.mission.find_group(target_group.group_identifier, search="match") # Match search is used due to TheaterGroundObject.name not matching + if tgroup is not None: # the Mission group name because of SkyNet prefixes. + waypoint.add_task(EngageTargetsInZone( + position=tgroup.position, + radius=nm_to_meter(30), + targets=[ + Targets.All.GroundUnits.AirDefence, + ]) + ) + else: + logging.error(f"Could not find group for DEAD mission {target_group.group_identifier}") for i, t in enumerate(self.waypoint.targets): if self.group.units[0].unit_type == JF_17 and i < 4: diff --git a/gen/fleet/carrier_group.py b/gen/fleet/carrier_group.py index bc7a712c..a06ebaee 100644 --- a/gen/fleet/carrier_group.py +++ b/gen/fleet/carrier_group.py @@ -1,13 +1,9 @@ import random -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import ShipGroupGenerator -class CarrierGroupGenerator(GroupGenerator): - - def __init__(self, game, ground_object, faction): - super(CarrierGroupGenerator, self).__init__(game, ground_object) - self.faction = faction +class CarrierGroupGenerator(ShipGroupGenerator): def generate(self): @@ -27,4 +23,4 @@ class CarrierGroupGenerator(GroupGenerator): self.add_unit(dd_type, "DD3", self.position.x + 4500, self.position.y + 8500, self.heading) self.add_unit(dd_type, "DD4", self.position.x + 4500, self.position.y - 8500, self.heading) - self.get_generated_group().points[0].speed = 20 \ No newline at end of file + self.get_generated_group().points[0].speed = 20 diff --git a/gen/fleet/cn_dd_group.py b/gen/fleet/cn_dd_group.py index efef82d2..020f68c2 100644 --- a/gen/fleet/cn_dd_group.py +++ b/gen/fleet/cn_dd_group.py @@ -1,15 +1,26 @@ +from __future__ import annotations + import random +from typing import TYPE_CHECKING + +from dcs.ships import ( + Type_052C_Destroyer, + Type_052B_Destroyer, + Type_054A_Frigate, + CGN_1144_2_Pyotr_Velikiy, +) + +from game.factions.faction import Faction from gen.fleet.dd_group import DDGroupGenerator -from gen.sam.group_generator import GroupGenerator -from dcs.ships import * +from gen.sam.group_generator import ShipGroupGenerator +from theater.theatergroundobject import TheaterGroundObject + +if TYPE_CHECKING: + from game.game import Game -class ChineseNavyGroupGenerator(GroupGenerator): - - def __init__(self, game, ground_object, faction): - super(ChineseNavyGroupGenerator, self).__init__(game, ground_object) - self.faction = faction +class ChineseNavyGroupGenerator(ShipGroupGenerator): def generate(self): @@ -38,5 +49,5 @@ class ChineseNavyGroupGenerator(GroupGenerator): class Type54GroupGenerator(DDGroupGenerator): - def __init__(self, game, ground_object, faction): + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): super(Type54GroupGenerator, self).__init__(game, ground_object, faction, Type_054A_Frigate) diff --git a/gen/fleet/dd_group.py b/gen/fleet/dd_group.py index 7c02a64b..b11de653 100644 --- a/gen/fleet/dd_group.py +++ b/gen/fleet/dd_group.py @@ -1,14 +1,21 @@ -import random +from __future__ import annotations +from typing import TYPE_CHECKING -from gen.sam.group_generator import GroupGenerator -from dcs.ships import * +from game.factions.faction import Faction +from theater.theatergroundobject import TheaterGroundObject + +from gen.sam.group_generator import ShipGroupGenerator +from dcs.unittype import ShipType +from dcs.ships import Oliver_Hazzard_Perry_class, USS_Arleigh_Burke_IIa + +if TYPE_CHECKING: + from game.game import Game -class DDGroupGenerator(GroupGenerator): +class DDGroupGenerator(ShipGroupGenerator): - def __init__(self, game, ground_object, faction, ddtype): - super(DDGroupGenerator, self).__init__(game, ground_object) - self.faction = faction + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction, ddtype: ShipType): + super(DDGroupGenerator, self).__init__(game, ground_object, faction) self.ddtype = ddtype def generate(self): @@ -18,10 +25,10 @@ class DDGroupGenerator(GroupGenerator): class OliverHazardPerryGroupGenerator(DDGroupGenerator): - def __init__(self, game, ground_object, faction): + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): super(OliverHazardPerryGroupGenerator, self).__init__(game, ground_object, faction, Oliver_Hazzard_Perry_class) class ArleighBurkeGroupGenerator(DDGroupGenerator): - def __init__(self, game, ground_object, faction): + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): super(ArleighBurkeGroupGenerator, self).__init__(game, ground_object, faction, USS_Arleigh_Burke_IIa) diff --git a/gen/fleet/lha_group.py b/gen/fleet/lha_group.py index 6fbf23ac..cfbafcbb 100644 --- a/gen/fleet/lha_group.py +++ b/gen/fleet/lha_group.py @@ -1,13 +1,9 @@ import random -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import ShipGroupGenerator -class LHAGroupGenerator(GroupGenerator): - - def __init__(self, game, ground_object, faction): - super(LHAGroupGenerator, self).__init__(game, ground_object) - self.faction = faction +class LHAGroupGenerator(ShipGroupGenerator): def generate(self): @@ -22,4 +18,4 @@ class LHAGroupGenerator(GroupGenerator): self.add_unit(dd_type, "DD1", self.position.x + 1250, self.position.y + 1450, self.heading) self.add_unit(dd_type, "DD2", self.position.x + 1250, self.position.y - 1450, self.heading) - self.get_generated_group().points[0].speed = 20 \ No newline at end of file + self.get_generated_group().points[0].speed = 20 diff --git a/gen/fleet/ru_dd_group.py b/gen/fleet/ru_dd_group.py index dc048029..0948991a 100644 --- a/gen/fleet/ru_dd_group.py +++ b/gen/fleet/ru_dd_group.py @@ -1,15 +1,29 @@ +from __future__ import annotations import random +from typing import TYPE_CHECKING + +from dcs.ships import ( + FFL_1124_4_Grisha, + FSG_1241_1MP_Molniya, + FFG_11540_Neustrashimy, + FF_1135M_Rezky, + CG_1164_Moskva, + CGN_1144_2_Pyotr_Velikiy, + SSK_877, + SSK_641B +) from gen.fleet.dd_group import DDGroupGenerator -from gen.sam.group_generator import GroupGenerator -from dcs.ships import * +from gen.sam.group_generator import ShipGroupGenerator +from game.factions.faction import Faction +from theater.theatergroundobject import TheaterGroundObject -class RussianNavyGroupGenerator(GroupGenerator): +if TYPE_CHECKING: + from game.game import Game - def __init__(self, game, ground_object, faction): - super(RussianNavyGroupGenerator, self).__init__(game, ground_object) - self.faction = faction + +class RussianNavyGroupGenerator(ShipGroupGenerator): def generate(self): @@ -39,21 +53,20 @@ class RussianNavyGroupGenerator(GroupGenerator): class GrishaGroupGenerator(DDGroupGenerator): - def __init__(self, game, ground_object, faction): + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): super(GrishaGroupGenerator, self).__init__(game, ground_object, faction, FFL_1124_4_Grisha) class MolniyaGroupGenerator(DDGroupGenerator): - def __init__(self, game, ground_object, faction): + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): super(MolniyaGroupGenerator, self).__init__(game, ground_object, faction, FSG_1241_1MP_Molniya) class KiloSubGroupGenerator(DDGroupGenerator): - def __init__(self, game, ground_object, faction): + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): super(KiloSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_877) class TangoSubGroupGenerator(DDGroupGenerator): - def __init__(self, game, ground_object, faction): + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): super(TangoSubGroupGenerator, self).__init__(game, ground_object, faction, SSK_641B) - diff --git a/gen/fleet/schnellboot.py b/gen/fleet/schnellboot.py index 20459ac7..3de8783e 100644 --- a/gen/fleet/schnellboot.py +++ b/gen/fleet/schnellboot.py @@ -2,18 +2,14 @@ import random from dcs.ships import Schnellboot_type_S130 -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import ShipGroupGenerator -class SchnellbootGroupGenerator(GroupGenerator): - - def __init__(self, game, ground_object, faction): - super(SchnellbootGroupGenerator, self).__init__(game, ground_object) - self.faction = faction +class SchnellbootGroupGenerator(ShipGroupGenerator): def generate(self): for i in range(random.randint(2, 4)): self.add_unit(Schnellboot_type_S130, "Schnellboot" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading) - self.get_generated_group().points[0].speed = 20 \ No newline at end of file + self.get_generated_group().points[0].speed = 20 diff --git a/gen/fleet/ship_group_generator.py b/gen/fleet/ship_group_generator.py index b60746b7..db0a78cd 100644 --- a/gen/fleet/ship_group_generator.py +++ b/gen/fleet/ship_group_generator.py @@ -12,6 +12,7 @@ from gen.fleet.schnellboot import SchnellbootGroupGenerator from gen.fleet.uboat import UBoatGroupGenerator from gen.fleet.ww2lst import WW2LSTGroupGenerator + SHIP_MAP = { "SchnellbootGroupGenerator": SchnellbootGroupGenerator, "WW2LSTGroupGenerator": WW2LSTGroupGenerator, @@ -45,7 +46,7 @@ def generate_ship_group(game, ground_object, faction_name: str): return None -def generate_carrier_group(faction:str, game, ground_object): +def generate_carrier_group(faction: str, game, ground_object): """ This generate a carrier group :param parentCp: The parent control point @@ -58,7 +59,7 @@ def generate_carrier_group(faction:str, game, ground_object): return generator.get_generated_group() -def generate_lha_group(faction:str, game, ground_object): +def generate_lha_group(faction: str, game, ground_object): """ This generate a lha carrier group :param parentCp: The parent control point @@ -68,4 +69,4 @@ def generate_lha_group(faction:str, game, ground_object): """ generator = LHAGroupGenerator(game, ground_object, db.FACTIONS[faction]) generator.generate() - return generator.get_generated_group() \ No newline at end of file + return generator.get_generated_group() diff --git a/gen/fleet/uboat.py b/gen/fleet/uboat.py index c93c7814..8d2e9cc0 100644 --- a/gen/fleet/uboat.py +++ b/gen/fleet/uboat.py @@ -2,14 +2,10 @@ import random from dcs.ships import Uboat_VIIC_U_flak -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import ShipGroupGenerator -class UBoatGroupGenerator(GroupGenerator): - - def __init__(self, game, ground_object, faction): - super(UBoatGroupGenerator, self).__init__(game, ground_object) - self.faction = faction +class UBoatGroupGenerator(ShipGroupGenerator): def generate(self): diff --git a/gen/fleet/ww2lst.py b/gen/fleet/ww2lst.py index 07f320d0..e0512009 100644 --- a/gen/fleet/ww2lst.py +++ b/gen/fleet/ww2lst.py @@ -2,14 +2,10 @@ import random from dcs.ships import LS_Samuel_Chase, LST_Mk_II -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import ShipGroupGenerator -class WW2LSTGroupGenerator(GroupGenerator): - - def __init__(self, game, ground_object, faction): - super(WW2LSTGroupGenerator, self).__init__(game, ground_object) - self.faction = faction +class WW2LSTGroupGenerator(ShipGroupGenerator): def generate(self): diff --git a/gen/flights/flight.py b/gen/flights/flight.py index 48296635..d76347f3 100644 --- a/gen/flights/flight.py +++ b/gen/flights/flight.py @@ -60,6 +60,7 @@ class FlightWaypointType(Enum): SPLIT = 17 LOITER = 18 INGRESS_ESCORT = 19 + INGRESS_DEAD = 20 class FlightWaypoint: diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index 60fb32cf..8839b1af 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -36,6 +36,7 @@ INGRESS_TYPES = { FlightWaypointType.INGRESS_ESCORT, FlightWaypointType.INGRESS_SEAD, FlightWaypointType.INGRESS_STRIKE, + FlightWaypointType.INGRESS_DEAD, } @@ -545,7 +546,7 @@ class FlightPlanBuilder: elif task == FlightType.CAS: return self.generate_cas(flight) elif task == FlightType.DEAD: - return self.generate_sead(flight, custom_targets) + return self.generate_dead(flight, custom_targets) elif task == FlightType.ESCORT: return self.generate_escort(flight) elif task == FlightType.SEAD: @@ -734,9 +735,34 @@ class FlightPlanBuilder: land=land ) + def generate_dead(self, flight: Flight, + custom_targets: Optional[List[Unit]]) -> StrikeFlightPlan: + """Generate a DEAD flight at a given location. + + Args: + flight: The flight to generate the flight plan for. + custom_targets: Specific radar equipped units selected by the user. + """ + location = self.package.target + + if not isinstance(location, TheaterGroundObject): + logging.exception(f"Invalid Objective Location for DEAD flight {flight=} at {location=}") + raise InvalidObjectiveLocation(flight.flight_type, location) + + # TODO: Unify these. + # There doesn't seem to be any reason to treat the UI fragged missions + # different from the automatic missions. + targets: Optional[List[StrikeTarget]] = None + if custom_targets is not None: + targets = [] + for target in custom_targets: + targets.append(StrikeTarget(location.name, target)) + + return self.strike_flightplan(flight, location, targets) + def generate_sead(self, flight: Flight, custom_targets: Optional[List[Unit]]) -> StrikeFlightPlan: - """Generate a SEAD/DEAD flight at a given location. + """Generate a SEAD flight at a given location. Args: flight: The flight to generate the flight plan for. @@ -884,10 +910,14 @@ class FlightPlanBuilder: assert self.package.waypoints is not None builder = WaypointBuilder(self.game.conditions, flight, self.doctrine, targets) - sead_types = {FlightType.DEAD, FlightType.SEAD} - if flight.flight_type in sead_types: + # sead_types = {FlightType.DEAD, FlightType.SEAD} + if flight.flight_type is FlightType.SEAD: ingress = builder.ingress_sead(self.package.waypoints.ingress, location) + + elif flight.flight_type is FlightType.DEAD: + ingress = builder.ingress_dead(self.package.waypoints.ingress, + location) else: ingress = builder.ingress_strike(self.package.waypoints.ingress, location) diff --git a/gen/flights/waypointbuilder.py b/gen/flights/waypointbuilder.py index 1e081ee7..ddc76b5f 100644 --- a/gen/flights/waypointbuilder.py +++ b/gen/flights/waypointbuilder.py @@ -168,6 +168,11 @@ class WaypointBuilder: objective: MissionTarget) -> FlightWaypoint: return self._ingress(FlightWaypointType.INGRESS_ESCORT, position, objective) + + def ingress_dead(self, position:Point, + objective: MissionTarget) -> FlightWaypoint: + return self._ingress(FlightWaypointType.INGRESS_DEAD, position, + objective) def ingress_sead(self, position: Point, objective: MissionTarget) -> FlightWaypoint: diff --git a/gen/ground_forces/ai_ground_planner.py b/gen/ground_forces/ai_ground_planner.py index bda87407..02af7c6b 100644 --- a/gen/ground_forces/ai_ground_planner.py +++ b/gen/ground_forces/ai_ground_planner.py @@ -27,11 +27,11 @@ TYPE_TANKS = [ Armor.MT_Pz_Kpfw_V_Panther_Ausf_G, Armor.MT_Pz_Kpfw_IV_Ausf_H, Armor.HT_Pz_Kpfw_VI_Tiger_I, - Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II, + Armor.HT_Pz_Kpfw_VI_Ausf__B_Tiger_II, Armor.MT_M4_Sherman, Armor.MT_M4A4_Sherman_Firefly, Armor.StuG_IV, - Armor.ST_Centaur_IV, + Armor.CT_Centaur_IV, Armor.CT_Cromwell_IV, Armor.HIT_Churchill_VII, @@ -73,13 +73,13 @@ TYPE_IFV = [ Armor.IFV_Marder, Armor.IFV_MCV_80, Armor.IFV_LAV_25, - Armor.IFV_Sd_Kfz_234_2_Puma, + Armor.AC_Sd_Kfz_234_2_Puma, Armor.IFV_M2A2_Bradley, Armor.IFV_BMD_1, Armor.ZBD_04A, # WW2 - Armor.IFV_Sd_Kfz_234_2_Puma, + Armor.AC_Sd_Kfz_234_2_Puma, Armor.LAC_M8_Greyhound, # Mods diff --git a/gen/sam/aaa_flak18.py b/gen/sam/aaa_flak18.py new file mode 100644 index 00000000..fea85f70 --- /dev/null +++ b/gen/sam/aaa_flak18.py @@ -0,0 +1,29 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import GroupGenerator + + +class Flak18Generator(GroupGenerator): + """ + This generate a German flak artillery group using only free units, thus not requiring the WW2 asset pack + """ + + name = "WW2 Flak Site" + price = 40 + + def generate(self): + + spacing = random.randint(30, 60) + index = 0 + + for i in range(3): + for j in range(2): + index = index + 1 + self.add_unit(AirDefence.AAA_8_8cm_Flak_18, "AAA#" + str(index), + self.position.x + spacing * i + random.randint(1, 5), + self.position.y + spacing * j + random.randint(1, 5), self.heading) + + # Add a commander truck + self.add_unit(Unarmed.Blitz_3_6_6700A, "Blitz#", self.position.x - 35, self.position.y - 20, self.heading) diff --git a/gen/sam/cold_war_flak.py b/gen/sam/cold_war_flak.py new file mode 100644 index 00000000..c0b7e81d --- /dev/null +++ b/gen/sam/cold_war_flak.py @@ -0,0 +1,72 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import GroupGenerator + + +class EarlyColdWarFlakGenerator(GroupGenerator): + """ + This generator attempt to mimic an early cold-war era flak AAA site. + The Flak 18 88mm is used as the main long range gun and 2 Bofors 40mm guns provide short range protection. + + This does not include search lights and telemeter computer (Kdo.G 40) because these are paid units only available in WW2 asset pack + """ + + name = "Early Cold War Flak Site" + price = 58 + + def generate(self): + + spacing = random.randint(30, 60) + index = 0 + + # Long range guns + for i in range(3): + for j in range(2): + index = index + 1 + self.add_unit(AirDefence.AAA_8_8cm_Flak_18, "AAA#" + str(index), + self.position.x + spacing * i + random.randint(1, 5), + self.position.y + spacing * j + random.randint(1, 5), self.heading) + + # Short range guns + self.add_unit(AirDefence.AAA_Bofors_40mm, "SHO#1", + self.position.x - 40, self.position.y - 40, self.heading + 180), + self.add_unit(AirDefence.AAA_Bofors_40mm, "SHO#1", + self.position.x + spacing * 2 + 40, self.position.y + spacing + 40, self.heading), + + # Add a truck + self.add_unit(Unarmed.Transport_KAMAZ_43101, "Truck#", self.position.x - 60, self.position.y - 20, self.heading) + + +class ColdWarFlakGenerator(GroupGenerator): + """ + This generator attempt to mimic a cold-war era flak AAA site. + The Flak 18 88mm is used as the main long range gun while 2 Zu-23 guns provide short range protection. + The site is also fitted with a P-19 radar for early detection. + """ + + name = "Cold War Flak Site" + price = 72 + + def generate(self): + + spacing = random.randint(30, 60) + index = 0 + + # Long range guns + for i in range(3): + for j in range(2): + index = index + 1 + self.add_unit(AirDefence.AAA_8_8cm_Flak_18, "AAA#" + str(index), + self.position.x + spacing * i + random.randint(1, 5), + self.position.y + spacing * j + random.randint(1, 5), self.heading) + + # Short range guns + self.add_unit(AirDefence.AAA_ZU_23_Closed, "SHO#1", + self.position.x - 40, self.position.y - 40, self.heading + 180), + self.add_unit(AirDefence.AAA_ZU_23_Closed, "SHO#1", + self.position.x + spacing * 2 + 40, self.position.y + spacing + 40, self.heading), + + # Add a P19 Radar for EWR + self.add_unit(AirDefence.SAM_SR_P_19, "SR#0", self.position.x - 60, self.position.y - 20, self.heading) diff --git a/gen/sam/group_generator.py b/gen/sam/group_generator.py index 9f150ef4..5f6e90d6 100644 --- a/gen/sam/group_generator.py +++ b/gen/sam/group_generator.py @@ -1,14 +1,23 @@ +from __future__ import annotations import math import random +from typing import TYPE_CHECKING, Optional from dcs import unitgroup from dcs.point import PointAction -from dcs.unit import Vehicle +from dcs.unit import Vehicle, Ship +from dcs.unittype import VehicleType + +from game.factions.faction import Faction +from theater.theatergroundobject import TheaterGroundObject + +if TYPE_CHECKING: + from game.game import Game class GroupGenerator(): - def __init__(self, game, ground_object, faction = None): # faction is not mandatory because some subclasses do not use it + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Optional[Faction] = None): # faction is not mandatory because some subclasses do not use it self.game = game self.go = ground_object self.position = ground_object.position @@ -28,8 +37,7 @@ class GroupGenerator(): def get_generated_group(self): return self.vg - def add_unit(self, unit_type, name, pos_x, pos_y, heading): - + def add_unit(self, unit_type: VehicleType, name: str, pos_x: float, pos_y: float, heading: int): nn = "cgroup|" + str(self.go.cp_id) + '|' + str(self.go.group_id) + '|' + str(self.go.group_identifier) + "|" + name unit = Vehicle(self.game.next_unit_id(), @@ -75,3 +83,25 @@ class GroupGenerator(): current_offset += outer_offset return positions +class ShipGroupGenerator(GroupGenerator): + """Abstract class for other ship generator classes""" + def __init__(self, game: Game, ground_object: TheaterGroundObject, faction: Faction): + self.game = game + self.go = ground_object + self.position = ground_object.position + self.heading = random.randint(0, 359) + self.faction = faction + self.vg = unitgroup.ShipGroup(self.game.next_group_id(), self.groupNamePrefix + self.go.group_identifier) + wp = self.vg.add_waypoint(self.position, 0) + wp.ETA_locked = True + + def add_unit(self, unit_type, name, pos_x, pos_y, heading): + nn = "cgroup|" + str(self.go.cp_id) + '|' + str(self.go.group_id) + '|' + str(self.go.group_identifier) + "|" + name + + unit = Ship(self.game.next_unit_id(), + nn, unit_type) + unit.position.x = pos_x + unit.position.y = pos_y + unit.heading = heading + self.vg.add_unit(unit) + return unit diff --git a/gen/sam/sam_group_generator.py b/gen/sam/sam_group_generator.py index 7a127830..ea9cb1f8 100644 --- a/gen/sam/sam_group_generator.py +++ b/gen/sam/sam_group_generator.py @@ -1,13 +1,14 @@ import random from typing import List, Type -from dcs.unittype import UnitType from dcs.vehicles import AirDefence from game import db from gen.sam.aaa_bofors import BoforsGenerator from gen.sam.aaa_flak import FlakGenerator +from gen.sam.aaa_flak18 import Flak18Generator from gen.sam.aaa_zu23_insurgent import ZU23InsurgentGenerator +from gen.sam.cold_war_flak import EarlyColdWarFlakGenerator, ColdWarFlakGenerator from gen.sam.group_generator import GroupGenerator from gen.sam.sam_avenger import AvengerGenerator from gen.sam.sam_chaparral import ChaparralGenerator @@ -61,7 +62,10 @@ SAM_MAP = { "SA13Generator": SA13Generator, "SA15Generator": SA15Generator, "SA19Generator": SA19Generator, - "HQ7Generator": HQ7Generator + "HQ7Generator": HQ7Generator, + "Flak18Generator": Flak18Generator, + "ColdWarFlakGenerator": ColdWarFlakGenerator, + "EarlyColdWarFlakGenerator": EarlyColdWarFlakGenerator } SAM_PRICES = { @@ -106,7 +110,8 @@ def get_faction_possible_sams_generator(faction: str) -> List[Type[GroupGenerato """ return [SAM_MAP[s] for s in db.FACTIONS[faction].sams if s in SAM_MAP.keys()] -def generate_anti_air_group(game, parent_cp, ground_object, faction:str): + +def generate_anti_air_group(game, parent_cp, ground_object, faction: str): """ This generate a SAM group :param parentCp: The parent control point @@ -117,7 +122,7 @@ def generate_anti_air_group(game, parent_cp, ground_object, faction:str): possible_sams_generators = get_faction_possible_sams_generator(faction) if len(possible_sams_generators) > 0: sam_generator_class = random.choice(possible_sams_generators) - generator = sam_generator_class(game, ground_object, faction) + generator = sam_generator_class(game, ground_object, db.FACTIONS[faction]) generator.generate() return generator.get_generated_group() return None @@ -133,8 +138,3 @@ def generate_shorad_group(game, parent_cp, ground_object, faction_name: str): return generator.get_generated_group() else: return generate_anti_air_group(game, parent_cp, ground_object, faction_name) - - - - - diff --git a/pydcs b/pydcs index c12733a4..8e74bfb6 160000 --- a/pydcs +++ b/pydcs @@ -1 +1 @@ -Subproject commit c12733a4712e802b41fd26ad8df7475d06c334b3 +Subproject commit 8e74bfb61b829477d35a80148fc295f2158902dd diff --git a/qt_ui/widgets/map/QMapControlPoint.py b/qt_ui/widgets/map/QMapControlPoint.py index d90a973d..e59cdbfb 100644 --- a/qt_ui/widgets/map/QMapControlPoint.py +++ b/qt_ui/widgets/map/QMapControlPoint.py @@ -90,3 +90,10 @@ class QMapControlPoint(QMapObject): # Reinitialized ground planners and the like. self.game_model.game.initialize_turn() GameUpdateSignal.get_instance().updateGame(self.game_model.game) + + def open_new_package_dialog(self) -> None: + """Extends the default packagedialog to redirect to base menu for red air base.""" + if not self.control_point.captured: + self.on_click() + else: + super(QMapControlPoint, self).open_new_package_dialog() diff --git a/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py b/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py index 48974305..0602c79e 100644 --- a/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py +++ b/qt_ui/windows/basemenu/base_defenses/QBaseDefenseGroupInfo.py @@ -31,11 +31,13 @@ class QBaseDefenseGroupInfo(QGroupBox): attack_button.setMaximumWidth(180) attack_button.clicked.connect(self.onAttack) self.main_layout.addWidget(attack_button, 0, Qt.AlignLeft) - manage_button = QPushButton("Manage") - manage_button.setProperty("style", "btn-success") - manage_button.setMaximumWidth(180) - manage_button.clicked.connect(self.onManage) - self.main_layout.addWidget(manage_button, 0, Qt.AlignLeft) + + if self.cp.captured: + manage_button = QPushButton("Manage") + manage_button.setProperty("style", "btn-success") + manage_button.setMaximumWidth(180) + manage_button.clicked.connect(self.onManage) + self.main_layout.addWidget(manage_button, 0, Qt.AlignLeft) self.setLayout(self.main_layout) diff --git a/qt_ui/windows/newgame/QNewGameWizard.py b/qt_ui/windows/newgame/QNewGameWizard.py index 5fa06451..0a42e7db 100644 --- a/qt_ui/windows/newgame/QNewGameWizard.py +++ b/qt_ui/windows/newgame/QNewGameWizard.py @@ -230,7 +230,7 @@ class FactionSelection(QtWidgets.QWizardPage): if len(blue_faction.requirements.keys()) > 0: has_mod = True for mod in blue_faction.requirements.keys(): - if not "requirements" in red_faction.keys() or mod not in red_faction.requirements.keys(): + if mod not in red_faction.requirements.keys(): self.requiredMods.setText(self.requiredMods.text() + "\n