diff --git a/game/data/weapons.py b/game/data/weapons.py index 6b9164a7..b3e3b059 100644 --- a/game/data/weapons.py +++ b/game/data/weapons.py @@ -5,14 +5,14 @@ import inspect import logging from collections import defaultdict from dataclasses import dataclass, field -from typing import Dict, Iterator, Optional, Set, Tuple, Union, cast +from typing import Dict, Iterator, Optional, Set, Tuple, Union, cast, Any from dcs.unitgroup import FlyingGroup from dcs.weapons_data import Weapons, weapon_ids from game.dcs.aircrafttype import AircraftType -PydcsWeapon = Dict[str, Union[int, str]] +PydcsWeapon = Dict[str, Any] PydcsWeaponAssignment = Tuple[int, PydcsWeapon] diff --git a/game/dcs/aircrafttype.py b/game/dcs/aircrafttype.py index 9b5fedae..a851abfd 100644 --- a/game/dcs/aircrafttype.py +++ b/game/dcs/aircrafttype.py @@ -106,7 +106,7 @@ class PatrolConfig: @dataclass(frozen=True) -class AircraftType(UnitType[FlyingType]): +class AircraftType(UnitType[Type[FlyingType]]): carrier_capable: bool lha_capable: bool always_keeps_gun: bool diff --git a/game/dcs/groundunittype.py b/game/dcs/groundunittype.py index 908e0e18..c22d8a21 100644 --- a/game/dcs/groundunittype.py +++ b/game/dcs/groundunittype.py @@ -15,7 +15,7 @@ from game.dcs.unittype import UnitType @dataclass(frozen=True) -class GroundUnitType(UnitType[VehicleType]): +class GroundUnitType(UnitType[Type[VehicleType]]): unit_class: Optional[GroundUnitClass] spawn_weight: int diff --git a/game/dcs/unittype.py b/game/dcs/unittype.py index 25181a66..2fc6ec9f 100644 --- a/game/dcs/unittype.py +++ b/game/dcs/unittype.py @@ -4,12 +4,12 @@ from typing import TypeVar, Generic, Type from dcs.unittype import UnitType as DcsUnitType -DcsUnitTypeT = TypeVar("DcsUnitTypeT", bound=DcsUnitType) +DcsUnitTypeT = TypeVar("DcsUnitTypeT", bound=Type[DcsUnitType]) @dataclass(frozen=True) class UnitType(Generic[DcsUnitTypeT]): - dcs_unit_type: Type[DcsUnitTypeT] + dcs_unit_type: DcsUnitTypeT name: str description: str year_introduced: str diff --git a/game/event/event.py b/game/event/event.py index daac7f27..1076da9c 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -5,7 +5,6 @@ from typing import List, TYPE_CHECKING, Type from dcs.mapping import Point from dcs.task import Task -from dcs.unittype import VehicleType from game import persistency from game.debriefing import AirLosses, Debriefing diff --git a/game/factions/faction.py b/game/factions/faction.py index 2a0156c2..382525de 100644 --- a/game/factions/faction.py +++ b/game/factions/faction.py @@ -84,10 +84,10 @@ class Faction: requirements: Dict[str, str] = field(default_factory=dict) # possible aircraft carrier units - aircraft_carrier: List[Type[UnitType]] = field(default_factory=list) + aircraft_carrier: List[Type[ShipType]] = field(default_factory=list) # possible helicopter carrier units - helicopter_carrier: List[Type[UnitType]] = field(default_factory=list) + helicopter_carrier: List[Type[ShipType]] = field(default_factory=list) # Possible carrier names carrier_names: List[str] = field(default_factory=list) diff --git a/game/operation/operation.py b/game/operation/operation.py index b976c5e7..36f2f54d 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -70,20 +70,6 @@ class Operation: cls._setup_mission_coalitions() cls.current_mission.options.load_from_dict(options_dict) - @classmethod - def conflicts(cls) -> Iterable[Conflict]: - assert cls.game - for frontline in cls.game.theater.conflicts(): - yield Conflict( - cls.game.theater, - frontline, - cls.game.player_faction.name, - cls.game.enemy_faction.name, - cls.game.player_country, - cls.game.enemy_country, - frontline.position, - ) - @classmethod def air_conflict(cls) -> Conflict: assert cls.game @@ -97,8 +83,8 @@ class Operation: FrontLine(player_cp, enemy_cp), cls.game.player_faction.name, cls.game.enemy_faction.name, - cls.game.player_country, - cls.game.enemy_country, + cls.current_mission.country(cls.game.player_country), + cls.current_mission.country(cls.game.enemy_country), mid_point, ) diff --git a/game/theater/base.py b/game/theater/base.py index 4e727825..02839481 100644 --- a/game/theater/base.py +++ b/game/theater/base.py @@ -2,8 +2,6 @@ import itertools import logging from typing import Any -from dcs.unit import UnitType as DcsUnitType - from game.dcs.aircrafttype import AircraftType from game.dcs.groundunittype import GroundUnitType from game.dcs.unittype import UnitType @@ -33,7 +31,7 @@ class Base: total += unit_type.price * count return total - def total_units_of_type(self, unit_type: UnitType[DcsUnitType]) -> int: + def total_units_of_type(self, unit_type: UnitType[Any]) -> int: return sum( [ c diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py index cd8574f9..29400a16 100644 --- a/game/theater/conflicttheater.py +++ b/game/theater/conflicttheater.py @@ -545,7 +545,7 @@ class ConflictTheater: def find_ground_objects_by_obj_name( self, obj_name: str - ) -> list[TheaterGroundObject]: + ) -> list[TheaterGroundObject[Any]]: found = [] for cp in self.controlpoints: for g in cp.ground_objects: diff --git a/game/theater/controlpoint.py b/game/theater/controlpoint.py index e7daf471..ea5d981a 100644 --- a/game/theater/controlpoint.py +++ b/game/theater/controlpoint.py @@ -43,6 +43,7 @@ from .missiontarget import MissionTarget from .theatergroundobject import ( GenericCarrierGroundObject, TheaterGroundObject, + NavalGroundObject, ) from ..dcs.aircrafttype import AircraftType from ..dcs.groundunittype import GroundUnitType @@ -298,7 +299,7 @@ class ControlPoint(MissionTarget, ABC): self.id = cp_id self.full_name = name self.at = at - self.connected_objectives: List[TheaterGroundObject] = [] + self.connected_objectives: List[TheaterGroundObject[Any]] = [] self.preset_locations = PresetLocations() self.helipads: List[PointWithHeading] = [] @@ -326,7 +327,7 @@ class ControlPoint(MissionTarget, ABC): return f"<{self.__class__}: {self.name}>" @property - def ground_objects(self) -> List[TheaterGroundObject]: + def ground_objects(self) -> List[TheaterGroundObject[Any]]: return list(self.connected_objectives) @property @@ -502,7 +503,7 @@ class ControlPoint(MissionTarget, ABC): def find_ground_objects_by_obj_name( self, obj_name: str - ) -> list[TheaterGroundObject]: + ) -> list[TheaterGroundObject[Any]]: found = [] for g in self.ground_objects: if g.obj_name == obj_name: @@ -881,9 +882,12 @@ class NavalControlPoint(ControlPoint, ABC): def heading(self) -> int: return 0 # TODO compute heading - def find_main_tgo(self) -> TheaterGroundObject: + def find_main_tgo(self) -> GenericCarrierGroundObject: for g in self.ground_objects: - if g.dcs_identifier in ["CARRIER", "LHA"]: + if isinstance(g, GenericCarrierGroundObject) and g.dcs_identifier in [ + "CARRIER", + "LHA", + ]: return g raise RuntimeError(f"Found no carrier/LHA group for {self.name}") diff --git a/game/theater/theatergroundobject.py b/game/theater/theatergroundobject.py index 4a7c8990..a49b63ab 100644 --- a/game/theater/theatergroundobject.py +++ b/game/theater/theatergroundobject.py @@ -2,12 +2,12 @@ from __future__ import annotations import itertools import logging -from typing import Iterator, List, TYPE_CHECKING, Union +from typing import Iterator, List, TYPE_CHECKING, Union, Generic, TypeVar, Any from dcs.mapping import Point from dcs.triggers import TriggerZone from dcs.unit import Unit -from dcs.unitgroup import Group +from dcs.unitgroup import Group, ShipGroup, VehicleGroup from dcs.unittype import VehicleType from .. import db @@ -47,7 +47,10 @@ NAME_BY_CATEGORY = { } -class TheaterGroundObject(MissionTarget): +GroupT = TypeVar("GroupT", bound=Group) + + +class TheaterGroundObject(MissionTarget, Generic[GroupT]): def __init__( self, name: str, @@ -66,7 +69,7 @@ class TheaterGroundObject(MissionTarget): self.control_point = control_point self.dcs_identifier = dcs_identifier self.sea_object = sea_object - self.groups: List[Group] = [] + self.groups: List[GroupT] = [] @property def is_dead(self) -> bool: @@ -206,7 +209,7 @@ class TheaterGroundObject(MissionTarget): raise NotImplementedError -class BuildingGroundObject(TheaterGroundObject): +class BuildingGroundObject(TheaterGroundObject[VehicleGroup]): def __init__( self, name: str, @@ -253,7 +256,7 @@ class BuildingGroundObject(TheaterGroundObject): def kill(self) -> None: self._dead = True - def iter_building_group(self) -> Iterator[TheaterGroundObject]: + def iter_building_group(self) -> Iterator[TheaterGroundObject[Any]]: for tgo in self.control_point.ground_objects: if tgo.obj_name == self.obj_name and not tgo.is_dead: yield tgo @@ -338,7 +341,7 @@ class FactoryGroundObject(BuildingGroundObject): ) -class NavalGroundObject(TheaterGroundObject): +class NavalGroundObject(TheaterGroundObject[ShipGroup]): def mission_types(self, for_player: bool) -> Iterator[FlightType]: from gen.flights.flight import FlightType @@ -407,7 +410,7 @@ class LhaGroundObject(GenericCarrierGroundObject): return f"{self.faction_color}|EWR|{super().group_name}" -class MissileSiteGroundObject(TheaterGroundObject): +class MissileSiteGroundObject(TheaterGroundObject[VehicleGroup]): def __init__( self, name: str, group_id: int, position: Point, control_point: ControlPoint ) -> None: @@ -431,7 +434,7 @@ class MissileSiteGroundObject(TheaterGroundObject): return False -class CoastalSiteGroundObject(TheaterGroundObject): +class CoastalSiteGroundObject(TheaterGroundObject[VehicleGroup]): def __init__( self, name: str, @@ -463,7 +466,7 @@ class CoastalSiteGroundObject(TheaterGroundObject): # The SamGroundObject represents all type of AA # The TGO can have multiple types of units (AAA,SAM,Support...) # Differentiation can be made during generation with the airdefensegroupgenerator -class SamGroundObject(TheaterGroundObject): +class SamGroundObject(TheaterGroundObject[VehicleGroup]): def __init__( self, name: str, @@ -535,7 +538,7 @@ class SamGroundObject(TheaterGroundObject): return True -class VehicleGroupGroundObject(TheaterGroundObject): +class VehicleGroupGroundObject(TheaterGroundObject[VehicleGroup]): def __init__( self, name: str, @@ -563,7 +566,7 @@ class VehicleGroupGroundObject(TheaterGroundObject): return True -class EwrGroundObject(TheaterGroundObject): +class EwrGroundObject(TheaterGroundObject[VehicleGroup]): def __init__( self, name: str, diff --git a/game/unitdelivery.py b/game/unitdelivery.py index 0a2a3db3..dfbc2409 100644 --- a/game/unitdelivery.py +++ b/game/unitdelivery.py @@ -30,16 +30,16 @@ class PendingUnitDeliveries: self.destination = destination # Maps unit type to order quantity. - self.units: dict[UnitType[DcsUnitType], int] = defaultdict(int) + self.units: dict[UnitType[Any], int] = defaultdict(int) def __str__(self) -> str: return f"Pending delivery to {self.destination}" - def order(self, units: dict[UnitType[DcsUnitType], int]) -> None: + def order(self, units: dict[UnitType[Any], int]) -> None: for k, v in units.items(): self.units[k] += v - def sell(self, units: dict[UnitType[DcsUnitType], int]) -> None: + def sell(self, units: dict[UnitType[Any], int]) -> None: for k, v in units.items(): self.units[k] -= v diff --git a/game/unitmap.py b/game/unitmap.py index 98793991..a0485a9c 100644 --- a/game/unitmap.py +++ b/game/unitmap.py @@ -2,7 +2,7 @@ import itertools import math from dataclasses import dataclass -from typing import Dict, Optional +from typing import Dict, Optional, Any from dcs.unit import Unit from dcs.unitgroup import FlyingGroup, Group, VehicleGroup @@ -29,7 +29,7 @@ class FrontLineUnit: @dataclass(frozen=True) class GroundObjectUnit: - ground_object: TheaterGroundObject + ground_object: TheaterGroundObject[Any] group: Group unit: Unit @@ -100,7 +100,7 @@ class UnitMap: def add_ground_object_units( self, - ground_object: TheaterGroundObject, + ground_object: TheaterGroundObject[Any], persistence_group: Group, miz_group: Group, ) -> None: diff --git a/gen/aircraft.py b/gen/aircraft.py index 668b8d95..3fa1b94d 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -5,7 +5,7 @@ import random from dataclasses import dataclass from datetime import timedelta from functools import cached_property -from typing import Dict, List, Optional, TYPE_CHECKING, Type, Union, Iterable +from typing import Dict, List, Optional, TYPE_CHECKING, Type, Union, Iterable, Any from dcs import helicopters from dcs.action import AITaskPush, ActivateGroup @@ -351,7 +351,7 @@ class AircraftConflictGenerator: def _setup_group( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -537,7 +537,7 @@ class AircraftConflictGenerator: def _add_radio_waypoint( self, - group: FlyingGroup, + group: FlyingGroup[Any], position: Point, altitude: Distance, airspeed: int = 600, @@ -548,7 +548,7 @@ class AircraftConflictGenerator: def _rtb_for( self, - group: FlyingGroup, + group: FlyingGroup[Any], cp: ControlPoint, at: Optional[db.StartingPosition] = None, ) -> MovingPoint: @@ -680,7 +680,7 @@ class AircraftConflictGenerator: self.unit_map.add_aircraft(group, flight) def set_activation_time( - self, flight: Flight, group: FlyingGroup, delay: timedelta + self, flight: Flight, group: FlyingGroup[Any], delay: timedelta ) -> None: # Note: Late activation causes the waypoint TOTs to look *weird* in the # mission editor. Waypoint times will be relative to the group @@ -699,7 +699,7 @@ class AircraftConflictGenerator: self.m.triggerrules.triggers.append(activation_trigger) def set_startup_time( - self, flight: Flight, group: FlyingGroup, delay: timedelta + self, flight: Flight, group: FlyingGroup[Any], delay: timedelta ) -> None: # Uncontrolled causes the AI unit to spawn, but not begin startup. group.uncontrolled = True @@ -775,7 +775,7 @@ class AircraftConflictGenerator: @staticmethod def set_reduced_fuel( - flight: Flight, group: FlyingGroup, unit_type: Type[PlaneType] + flight: Flight, group: FlyingGroup[Any], unit_type: Type[PlaneType] ) -> None: if unit_type is Su_33: for unit in group.units: @@ -801,7 +801,7 @@ class AircraftConflictGenerator: def configure_behavior( self, flight: Flight, - group: FlyingGroup, + group: FlyingGroup[Any], react_on_threat: Optional[OptReactOnThreat.Values] = None, roe: Optional[OptROE.Values] = None, rtb_winchester: Optional[OptRTBOnOutOfAmmo.Values] = None, @@ -834,13 +834,13 @@ class AircraftConflictGenerator: # https://forums.eagle.ru/forum/english/digital-combat-simulator/dcs-world-2-5/bugs-and-problems-ai/ai-ad/7121294-ai-stuck-at-high-aoa-after-making-sharp-turn-if-afterburner-is-restricted @staticmethod - def configure_eplrs(group: FlyingGroup, flight: Flight) -> None: + def configure_eplrs(group: FlyingGroup[Any], flight: Flight) -> None: if flight.unit_type.eplrs_capable: group.points[0].tasks.append(EPLRS(group.id)) def configure_cap( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -857,7 +857,7 @@ class AircraftConflictGenerator: def configure_sweep( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -874,7 +874,7 @@ class AircraftConflictGenerator: def configure_cas( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -892,7 +892,7 @@ class AircraftConflictGenerator: def configure_dead( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -917,7 +917,7 @@ class AircraftConflictGenerator: def configure_sead( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -941,7 +941,7 @@ class AircraftConflictGenerator: def configure_strike( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -959,7 +959,7 @@ class AircraftConflictGenerator: def configure_anti_ship( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -977,7 +977,7 @@ class AircraftConflictGenerator: def configure_runway_attack( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -995,7 +995,7 @@ class AircraftConflictGenerator: def configure_oca_strike( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -1012,7 +1012,7 @@ class AircraftConflictGenerator: def configure_awacs( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -1040,7 +1040,7 @@ class AircraftConflictGenerator: def configure_refueling( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -1066,7 +1066,7 @@ class AircraftConflictGenerator: def configure_escort( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -1082,7 +1082,7 @@ class AircraftConflictGenerator: def configure_sead_escort( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -1105,7 +1105,7 @@ class AircraftConflictGenerator: def configure_transport( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -1120,13 +1120,13 @@ class AircraftConflictGenerator: restrict_jettison=True, ) - def configure_unknown_task(self, group: FlyingGroup, flight: Flight) -> None: + def configure_unknown_task(self, group: FlyingGroup[Any], flight: Flight) -> None: logging.error(f"Unhandled flight type: {flight.flight_type}") self.configure_behavior(flight, group) def setup_flight_group( self, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, dynamic_runways: Dict[str, RunwayData], @@ -1170,7 +1170,7 @@ class AircraftConflictGenerator: self.configure_eplrs(group, flight) def create_waypoints( - self, group: FlyingGroup, package: Package, flight: Flight + self, group: FlyingGroup[Any], package: Package, flight: Flight ) -> None: for waypoint in flight.points: @@ -1238,7 +1238,7 @@ class AircraftConflictGenerator: waypoint: FlightWaypoint, package: Package, flight: Flight, - group: FlyingGroup, + group: FlyingGroup[Any], ) -> None: estimator = TotEstimator(package) start_time = estimator.mission_start_time(flight) @@ -1281,7 +1281,7 @@ class PydcsWaypointBuilder: def __init__( self, waypoint: FlightWaypoint, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, mission: Mission, @@ -1324,7 +1324,7 @@ class PydcsWaypointBuilder: def for_waypoint( cls, waypoint: FlightWaypoint, - group: FlyingGroup, + group: FlyingGroup[Any], package: Package, flight: Flight, mission: Mission, diff --git a/gen/coastal/silkworm.py b/gen/coastal/silkworm.py index ccb1374d..6712762a 100644 --- a/gen/coastal/silkworm.py +++ b/gen/coastal/silkworm.py @@ -1,13 +1,12 @@ -from dcs.unitgroup import VehicleGroup from dcs.vehicles import MissilesSS, Unarmed, AirDefence from game import Game from game.factions.faction import Faction from game.theater.theatergroundobject import CoastalSiteGroundObject -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import VehicleGroupGenerator -class SilkwormGenerator(GroupGenerator[VehicleGroup]): +class SilkwormGenerator(VehicleGroupGenerator[CoastalSiteGroundObject]): def __init__( self, game: Game, ground_object: CoastalSiteGroundObject, faction: Faction ) -> None: diff --git a/gen/defenses/armored_group_generator.py b/gen/defenses/armored_group_generator.py index 51058b88..c7404d0d 100644 --- a/gen/defenses/armored_group_generator.py +++ b/gen/defenses/armored_group_generator.py @@ -1,14 +1,12 @@ import random -from dcs.unitgroup import VehicleGroup - 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 VehicleGroupGenerator -class ArmoredGroupGenerator(GroupGenerator[VehicleGroup]): +class ArmoredGroupGenerator(VehicleGroupGenerator[VehicleGroupGroundObject]): def __init__( self, game: Game, @@ -37,7 +35,7 @@ class ArmoredGroupGenerator(GroupGenerator[VehicleGroup]): ) -class FixedSizeArmorGroupGenerator(GroupGenerator[VehicleGroup]): +class FixedSizeArmorGroupGenerator(VehicleGroupGenerator[VehicleGroupGroundObject]): def __init__( self, game: Game, diff --git a/gen/fleet/cn_dd_group.py b/gen/fleet/cn_dd_group.py index c47cc6ac..144df4b4 100644 --- a/gen/fleet/cn_dd_group.py +++ b/gen/fleet/cn_dd_group.py @@ -3,7 +3,6 @@ from __future__ import annotations import random from typing import TYPE_CHECKING - from dcs.ships import ( Type_052C, Type_052B, @@ -11,9 +10,9 @@ from dcs.ships import ( ) from game.factions.faction import Faction +from game.theater.theatergroundobject import ShipGroundObject from gen.fleet.dd_group import DDGroupGenerator from gen.sam.group_generator import ShipGroupGenerator -from game.theater.theatergroundobject import TheaterGroundObject if TYPE_CHECKING: from game.game import Game @@ -65,9 +64,7 @@ class ChineseNavyGroupGenerator(ShipGroupGenerator): class Type54GroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(Type54GroupGenerator, self).__init__( game, ground_object, faction, Type_054A ) diff --git a/gen/fleet/dd_group.py b/gen/fleet/dd_group.py index d3875088..db766a0d 100644 --- a/gen/fleet/dd_group.py +++ b/gen/fleet/dd_group.py @@ -1,12 +1,13 @@ from __future__ import annotations + from typing import TYPE_CHECKING, Type -from game.factions.faction import Faction -from game.theater.theatergroundobject import TheaterGroundObject - -from gen.sam.group_generator import ShipGroupGenerator -from dcs.unittype import ShipType from dcs.ships import PERRY, USS_Arleigh_Burke_IIa +from dcs.unittype import ShipType + +from game.factions.faction import Faction +from game.theater.theatergroundobject import ShipGroundObject +from gen.sam.group_generator import ShipGroupGenerator if TYPE_CHECKING: from game.game import Game @@ -16,7 +17,7 @@ class DDGroupGenerator(ShipGroupGenerator): def __init__( self, game: Game, - ground_object: TheaterGroundObject, + ground_object: ShipGroundObject, faction: Faction, ddtype: Type[ShipType], ): @@ -42,18 +43,14 @@ class DDGroupGenerator(ShipGroupGenerator): class OliverHazardPerryGroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(OliverHazardPerryGroupGenerator, self).__init__( game, ground_object, faction, PERRY ) class ArleighBurkeGroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(ArleighBurkeGroupGenerator, self).__init__( game, ground_object, faction, USS_Arleigh_Burke_IIa ) diff --git a/gen/fleet/lacombattanteII.py b/gen/fleet/lacombattanteII.py index 6638dd4a..bd476f45 100644 --- a/gen/fleet/lacombattanteII.py +++ b/gen/fleet/lacombattanteII.py @@ -2,14 +2,12 @@ from dcs.ships import La_Combattante_II from game import Game from game.factions.faction import Faction -from game.theater import TheaterGroundObject +from game.theater.theatergroundobject import ShipGroundObject from gen.fleet.dd_group import DDGroupGenerator class LaCombattanteIIGroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(LaCombattanteIIGroupGenerator, self).__init__( game, ground_object, faction, La_Combattante_II ) diff --git a/gen/fleet/ru_dd_group.py b/gen/fleet/ru_dd_group.py index 4354b5fb..67f9c923 100644 --- a/gen/fleet/ru_dd_group.py +++ b/gen/fleet/ru_dd_group.py @@ -1,4 +1,5 @@ from __future__ import annotations + import random from typing import TYPE_CHECKING @@ -12,11 +13,10 @@ from dcs.ships import ( SOM, ) +from game.factions.faction import Faction +from game.theater.theatergroundobject import ShipGroundObject from gen.fleet.dd_group import DDGroupGenerator from gen.sam.group_generator import ShipGroupGenerator -from game.factions.faction import Faction -from game.theater.theatergroundobject import TheaterGroundObject - if TYPE_CHECKING: from game.game import Game @@ -85,32 +85,24 @@ class RussianNavyGroupGenerator(ShipGroupGenerator): class GrishaGroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(GrishaGroupGenerator, self).__init__( game, ground_object, faction, ALBATROS ) class MolniyaGroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(MolniyaGroupGenerator, self).__init__( game, ground_object, faction, MOLNIYA ) class KiloSubGroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(KiloSubGroupGenerator, self).__init__(game, ground_object, faction, KILO) class TangoSubGroupGenerator(DDGroupGenerator): - def __init__( - self, game: Game, ground_object: TheaterGroundObject, faction: Faction - ): + def __init__(self, game: Game, ground_object: ShipGroundObject, faction: Faction): super(TangoSubGroupGenerator, self).__init__(game, ground_object, faction, SOM) diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 29c9ae96..6d259cd2 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -18,6 +18,7 @@ from typing import ( TYPE_CHECKING, Tuple, TypeVar, + Any, ) from game.dcs.aircrafttype import AircraftType @@ -284,7 +285,7 @@ class ObjectiveFinder: self.game = game self.is_player = is_player - def enemy_air_defenses(self) -> Iterator[tuple[TheaterGroundObject, Distance]]: + def enemy_air_defenses(self) -> Iterator[tuple[TheaterGroundObject[Any], Distance]]: """Iterates over all enemy SAM sites.""" doctrine = self.game.faction_for(self.is_player).doctrine threat_zones = self.game.threat_zone_for(not self.is_player) @@ -314,14 +315,14 @@ class ObjectiveFinder: yield ground_object, target_range - def threatening_air_defenses(self) -> Iterator[TheaterGroundObject]: + def threatening_air_defenses(self) -> Iterator[TheaterGroundObject[Any]]: """Iterates over enemy SAMs in threat range of friendly control points. SAM sites are sorted by their closest proximity to any friendly control point (airfield or fleet). """ - target_ranges: list[tuple[TheaterGroundObject, Distance]] = [] + target_ranges: list[tuple[TheaterGroundObject[Any], Distance]] = [] for target, threat_range in self.enemy_air_defenses(): ranges: list[Distance] = [] for cp in self.friendly_control_points(): @@ -385,13 +386,13 @@ class ObjectiveFinder: for target, _range in target_ranges: yield target - def strike_targets(self) -> Iterator[TheaterGroundObject]: + def strike_targets(self) -> Iterator[TheaterGroundObject[Any]]: """Iterates over enemy strike targets. Targets are sorted by their closest proximity to any friendly control point (airfield or fleet). """ - targets: List[Tuple[TheaterGroundObject, int]] = [] + targets: List[Tuple[TheaterGroundObject[Any], int]] = [] # Building objectives are made of several individual TGOs (one per # building). found_targets: Set[str] = set() diff --git a/gen/flights/flightplan.py b/gen/flights/flightplan.py index 07498342..d7ad8b9e 100644 --- a/gen/flights/flightplan.py +++ b/gen/flights/flightplan.py @@ -1130,7 +1130,7 @@ class FlightPlanBuilder: ) @staticmethod - def anti_ship_targets_for_tgo(tgo: TheaterGroundObject) -> List[StrikeTarget]: + def anti_ship_targets_for_tgo(tgo: NavalGroundObject) -> List[StrikeTarget]: return [StrikeTarget(f"{g.name} at {tgo.name}", g) for g in tgo.groups] def generate_anti_ship(self, flight: Flight) -> StrikeFlightPlan: diff --git a/gen/flights/waypointbuilder.py b/gen/flights/waypointbuilder.py index f8380897..f861b4b8 100644 --- a/gen/flights/waypointbuilder.py +++ b/gen/flights/waypointbuilder.py @@ -10,6 +10,7 @@ from typing import ( TYPE_CHECKING, Tuple, Union, + Any, ) from dcs.mapping import Point @@ -33,7 +34,9 @@ from .flight import Flight, FlightWaypoint, FlightWaypointType @dataclass(frozen=True) class StrikeTarget: name: str - target: Union[VehicleGroup, TheaterGroundObject, Unit, Group, MultiGroupTransport] + target: Union[ + VehicleGroup, TheaterGroundObject[Any], Unit, Group, MultiGroupTransport + ] class WaypointBuilder: diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index 55e7aee5..e14544e2 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -9,7 +9,17 @@ from __future__ import annotations import logging import random -from typing import Dict, Iterator, Optional, TYPE_CHECKING, Type, List +from typing import ( + Dict, + Iterator, + Optional, + TYPE_CHECKING, + Type, + List, + TypeVar, + Any, + Generic, +) from dcs import Mission, Point, unitgroup from dcs.action import SceneryDestructionZone @@ -56,7 +66,10 @@ FARP_FRONTLINE_DISTANCE = 10000 AA_CP_MIN_DISTANCE = 40000 -class GenericGroundObjectGenerator: +TgoT = TypeVar("TgoT", bound=TheaterGroundObject[Any]) + + +class GenericGroundObjectGenerator(Generic[TgoT]): """An unspecialized ground object generator. Currently used only for SAM @@ -64,7 +77,7 @@ class GenericGroundObjectGenerator: def __init__( self, - ground_object: TheaterGroundObject, + ground_object: TgoT, country: Country, game: Game, mission: Mission, @@ -133,7 +146,7 @@ class GenericGroundObjectGenerator: ) -class MissileSiteGenerator(GenericGroundObjectGenerator): +class MissileSiteGenerator(GenericGroundObjectGenerator[MissileSiteGroundObject]): @property def culled(self) -> bool: # Don't cull missile sites - their range is long enough to make them easily @@ -196,7 +209,7 @@ class MissileSiteGenerator(GenericGroundObjectGenerator): return site_range -class BuildingSiteGenerator(GenericGroundObjectGenerator): +class BuildingSiteGenerator(GenericGroundObjectGenerator[BuildingGroundObject]): """Generator for building sites. Building sites are the primary type of non-airbase objective locations that @@ -324,7 +337,7 @@ class SceneryGenerator(BuildingSiteGenerator): self.unit_map.add_scenery(scenery) -class GenericCarrierGenerator(GenericGroundObjectGenerator): +class GenericCarrierGenerator(GenericGroundObjectGenerator[GenericCarrierGroundObject]): """Base type for carrier group generation. Used by both CV(N) groups and LHA groups. @@ -518,7 +531,7 @@ class LhaGenerator(GenericCarrierGenerator): ) -class ShipObjectGenerator(GenericGroundObjectGenerator): +class ShipObjectGenerator(GenericGroundObjectGenerator[ShipGroundObject]): """Generator for non-carrier naval groups.""" def generate(self) -> None: @@ -637,7 +650,7 @@ class GroundObjectsGenerator: ).generate() for ground_object in cp.ground_objects: - generator: GenericGroundObjectGenerator + generator: GenericGroundObjectGenerator[Any] if isinstance(ground_object, FactoryGroundObject): generator = FactoryGenerator( ground_object, country, self.game, self.m, self.unit_map diff --git a/gen/missiles/scud_site.py b/gen/missiles/scud_site.py index 0c5fd953..ca7f9b94 100644 --- a/gen/missiles/scud_site.py +++ b/gen/missiles/scud_site.py @@ -1,15 +1,14 @@ import random -from dcs.unitgroup import VehicleGroup from dcs.vehicles import Unarmed, MissilesSS, AirDefence from game import Game from game.factions.faction import Faction from game.theater.theatergroundobject import MissileSiteGroundObject -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import VehicleGroupGenerator -class ScudGenerator(GroupGenerator[VehicleGroup]): +class ScudGenerator(VehicleGroupGenerator[MissileSiteGroundObject]): def __init__( self, game: Game, ground_object: MissileSiteGroundObject, faction: Faction ) -> None: diff --git a/gen/missiles/v1_group.py b/gen/missiles/v1_group.py index 8cfb1dda..9d377754 100644 --- a/gen/missiles/v1_group.py +++ b/gen/missiles/v1_group.py @@ -1,15 +1,14 @@ import random -from dcs.unitgroup import VehicleGroup from dcs.vehicles import Unarmed, MissilesSS, AirDefence from game import Game from game.factions.faction import Faction from game.theater.theatergroundobject import MissileSiteGroundObject -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import VehicleGroupGenerator -class V1GroupGenerator(GroupGenerator[VehicleGroup]): +class V1GroupGenerator(VehicleGroupGenerator[MissileSiteGroundObject]): def __init__( self, game: Game, ground_object: MissileSiteGroundObject, faction: Faction ) -> None: diff --git a/gen/sam/airdefensegroupgenerator.py b/gen/sam/airdefensegroupgenerator.py index d74027c1..36755036 100644 --- a/gen/sam/airdefensegroupgenerator.py +++ b/gen/sam/airdefensegroupgenerator.py @@ -8,7 +8,7 @@ from dcs.unitgroup import VehicleGroup from game import Game from game.theater.theatergroundobject import SamGroundObject -from gen.sam.group_generator import GroupGenerator +from gen.sam.group_generator import VehicleGroupGenerator class SkynetRole(Enum): @@ -38,7 +38,7 @@ class AirDefenseRange(Enum): self.default_role = default_role -class AirDefenseGroupGenerator(GroupGenerator[VehicleGroup], ABC): +class AirDefenseGroupGenerator(VehicleGroupGenerator[SamGroundObject], ABC): """ This is the base for all SAM group generators """ diff --git a/gen/sam/ewrs.py b/gen/sam/ewrs.py index e20adc72..fdcdf061 100644 --- a/gen/sam/ewrs.py +++ b/gen/sam/ewrs.py @@ -1,13 +1,13 @@ from typing import Type -from dcs.unitgroup import VehicleGroup -from dcs.vehicles import AirDefence from dcs.unittype import VehicleType +from dcs.vehicles import AirDefence -from gen.sam.group_generator import GroupGenerator +from game.theater.theatergroundobject import EwrGroundObject +from gen.sam.group_generator import VehicleGroupGenerator -class EwrGenerator(GroupGenerator[VehicleGroup]): +class EwrGenerator(VehicleGroupGenerator[EwrGroundObject]): unit_type: Type[VehicleType] @classmethod diff --git a/gen/sam/group_generator.py b/gen/sam/group_generator.py index 2c7bc54d..7795036e 100644 --- a/gen/sam/group_generator.py +++ b/gen/sam/group_generator.py @@ -4,24 +4,27 @@ import logging import math import random from collections import Iterable -from typing import TYPE_CHECKING, Type, TypeVar, Generic +from typing import TYPE_CHECKING, Type, TypeVar, Generic, Any from dcs import unitgroup from dcs.mapping import Point from dcs.point import PointAction -from dcs.unit import Ship, Vehicle -from dcs.unitgroup import MovingGroup, ShipGroup -from dcs.unittype import VehicleType, UnitType +from dcs.unit import Ship, Vehicle, Unit +from dcs.unitgroup import ShipGroup, VehicleGroup +from dcs.unittype import VehicleType, UnitType, ShipType from game.dcs.groundunittype import GroundUnitType from game.factions.faction import Faction -from game.theater.theatergroundobject import TheaterGroundObject +from game.theater.theatergroundobject import TheaterGroundObject, NavalGroundObject if TYPE_CHECKING: from game.game import Game -GroupType = TypeVar("GroupType", bound=MovingGroup) +GroupT = TypeVar("GroupT", VehicleGroup, ShipGroup) +UnitT = TypeVar("UnitT", bound=Unit) +UnitTypeT = TypeVar("UnitTypeT", bound=Type[UnitType]) +TgoT = TypeVar("TgoT", bound=TheaterGroundObject[Any]) # TODO: Generate a group description rather than a pydcs group. @@ -29,41 +32,62 @@ GroupType = TypeVar("GroupType", bound=MovingGroup) # groundobjectsgen for an example). We can do less work and include the data we # care about in the format we want if we just generate our own group description # types rather than pydcs groups. -class GroupGenerator(Generic[GroupType]): - - price: int - - def __init__(self, game: Game, ground_object: TheaterGroundObject) -> None: +class GroupGenerator(Generic[GroupT, UnitT, UnitTypeT, TgoT]): + def __init__(self, game: Game, ground_object: TgoT, group: GroupT) -> None: self.game = game self.go = ground_object self.position = ground_object.position self.heading = random.randint(0, 359) self.price = 0 - self.vg = unitgroup.VehicleGroup(self.game.next_group_id(), self.go.group_name) - wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0) - wp.ETA_locked = True + self.vg: GroupT = group def generate(self) -> None: raise NotImplementedError - def get_generated_group(self) -> GroupType: + def get_generated_group(self) -> GroupT: return self.vg def add_unit( self, - unit_type: Type[VehicleType], + unit_type: UnitTypeT, name: str, pos_x: float, pos_y: float, heading: int, - ) -> Vehicle: + ) -> UnitT: return self.add_unit_to_group( self.vg, unit_type, name, Point(pos_x, pos_y), heading ) def add_unit_to_group( self, - group: GroupType, + group: GroupT, + unit_type: UnitTypeT, + name: str, + position: Point, + heading: int, + ) -> UnitT: + raise NotImplementedError + + +class VehicleGroupGenerator( + Generic[TgoT], GroupGenerator[VehicleGroup, Vehicle, Type[VehicleType], TgoT] +): + def __init__(self, game: Game, ground_object: TgoT) -> None: + super().__init__( + game, + ground_object, + unitgroup.VehicleGroup(self.game.next_group_id(), self.go.group_name), + ) + wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0) + wp.ETA_locked = True + + def generate(self) -> None: + raise NotImplementedError + + def add_unit_to_group( + self, + group: VehicleGroup, unit_type: Type[VehicleType], name: str, position: Point, @@ -124,32 +148,34 @@ class GroupGenerator(Generic[GroupType]): return positions -class ShipGroupGenerator(GroupGenerator[ShipGroup]): +class ShipGroupGenerator( + GroupGenerator[ShipGroup, Ship, Type[ShipType], NavalGroundObject] +): """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) + def __init__(self, game: Game, ground_object: NavalGroundObject, faction: Faction): + super().__init__( + game, + ground_object, + unitgroup.ShipGroup(self.game.next_group_id(), self.go.group_name), + ) self.faction = faction - self.vg = unitgroup.ShipGroup(self.game.next_group_id(), self.go.group_name) wp = self.vg.add_waypoint(self.position, 0) wp.ETA_locked = True - def add_unit( + def generate(self) -> None: + raise NotImplementedError + + def add_unit_to_group( self, - unit_type: Type[UnitType], + group: ShipGroup, + unit_type: Type[ShipType], name: str, - pos_x: float, - pos_y: float, + position: Point, heading: int, ) -> Ship: unit = Ship(self.game.next_unit_id(), f"{self.go.group_name}|{name}", unit_type) - unit.position.x = pos_x - unit.position.y = pos_y + unit.position = position unit.heading = heading - self.vg.add_unit(unit) + group.add_unit(unit) return unit diff --git a/gen/sam/sam_sa10.py b/gen/sam/sam_sa10.py index ef633226..6b277bfa 100644 --- a/gen/sam/sam_sa10.py +++ b/gen/sam/sam_sa10.py @@ -1,4 +1,7 @@ +from typing import Type + from dcs.mapping import Point +from dcs.unittype import VehicleType from dcs.vehicles import AirDefence from game import Game @@ -20,13 +23,13 @@ class SA10Generator(AirDefenseGroupGenerator): def __init__(self, game: Game, ground_object: SamGroundObject): super().__init__(game, ground_object) - self.sr1 = AirDefence.S_300PS_40B6MD_sr - self.sr2 = AirDefence.S_300PS_64H6E_sr - self.cp = AirDefence.S_300PS_54K6_cp - self.tr1 = AirDefence.S_300PS_40B6M_tr - self.tr2 = AirDefence.S_300PS_40B6M_tr - self.ln1 = AirDefence.S_300PS_5P85C_ln - self.ln2 = AirDefence.S_300PS_5P85D_ln + self.sr1: Type[VehicleType] = AirDefence.S_300PS_40B6MD_sr + self.sr2: Type[VehicleType] = AirDefence.S_300PS_64H6E_sr + self.cp: Type[VehicleType] = AirDefence.S_300PS_54K6_cp + self.tr1: Type[VehicleType] = AirDefence.S_300PS_40B6M_tr + self.tr2: Type[VehicleType] = AirDefence.S_300PS_40B6M_tr + self.ln1: Type[VehicleType] = AirDefence.S_300PS_5P85C_ln + self.ln2: Type[VehicleType] = AirDefence.S_300PS_5P85D_ln def generate(self) -> None: # Search Radar