Flesh out typing information, enforce.

(cherry picked from commit fb9a0fe833)
This commit is contained in:
Dan Albert
2021-07-07 17:41:29 -07:00
parent 7cfd6b7151
commit 4e9d661c0c
99 changed files with 426 additions and 453 deletions

View File

@@ -536,7 +536,11 @@ class AircraftConflictGenerator:
)
def _add_radio_waypoint(
self, group: FlyingGroup, position, altitude: Distance, airspeed: int = 600
self,
group: FlyingGroup,
position: Point,
altitude: Distance,
airspeed: int = 600,
) -> MovingPoint:
point = group.add_waypoint(position, altitude.meters, airspeed)
point.alt_type = "RADIO"
@@ -547,7 +551,7 @@ class AircraftConflictGenerator:
group: FlyingGroup,
cp: ControlPoint,
at: Optional[db.StartingPosition] = None,
):
) -> MovingPoint:
if at is None:
at = cp.at
position = at if isinstance(at, Point) else at.position
@@ -563,7 +567,8 @@ class AircraftConflictGenerator:
group.land_at(at)
return destination_waypoint
def _at_position(self, at) -> Point:
@staticmethod
def _at_position(at: Union[Point, ShipGroup, Type[Airport]]) -> Point:
if isinstance(at, Point):
return at
elif isinstance(at, ShipGroup):
@@ -593,7 +598,10 @@ class AircraftConflictGenerator:
parking_slot.unit_id = None
def generate_flights(
self, country, ato: AirTaskingOrder, dynamic_runways: Dict[str, RunwayData]
self,
country: Country,
ato: AirTaskingOrder,
dynamic_runways: Dict[str, RunwayData],
) -> None:
for package in ato.packages:
@@ -719,7 +727,9 @@ class AircraftConflictGenerator:
trigger.add_condition(CoalitionHasAirdrome(coalition, flight.from_cp.id))
def generate_planned_flight(self, cp, country, flight: Flight):
def generate_planned_flight(
self, cp: ControlPoint, country: Country, flight: Flight
) -> FlyingGroup:
name = namegen.next_aircraft_name(country, cp.id, flight)
try:
if flight.start_type == "In Flight":

View File

@@ -1,11 +1,12 @@
from __future__ import annotations
import logging
from dataclasses import dataclass, field
from datetime import timedelta
from typing import List, Type, Tuple, Optional
from typing import List, Type, Tuple, Optional, TYPE_CHECKING
from dcs.mission import Mission, StartType
from dcs.planes import IL_78M, KC130, KC135MPRS, KC_135
from dcs.unittype import UnitType
from dcs.task import (
AWACS,
ActivateBeaconCommand,
@@ -14,15 +15,17 @@ from dcs.task import (
SetImmortalCommand,
SetInvisibleCommand,
)
from dcs.unittype import UnitType
from game import db
from .flights.ai_flight_planner_db import AEWC_CAPABLE
from .naming import namegen
from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict
from .flights.ai_flight_planner_db import AEWC_CAPABLE
from .naming import namegen
from .radios import RadioFrequency, RadioRegistry
from .tacan import TacanBand, TacanChannel, TacanRegistry
if TYPE_CHECKING:
from game import Game
TANKER_DISTANCE = 15000
TANKER_ALT = 4572
@@ -70,7 +73,7 @@ class AirSupportConflictGenerator:
self,
mission: Mission,
conflict: Conflict,
game,
game: Game,
radio_registry: RadioRegistry,
tacan_registry: TacanRegistry,
) -> None:
@@ -95,7 +98,7 @@ class AirSupportConflictGenerator:
return (TANKER_ALT + 500, 596)
return (TANKER_ALT, 574)
def generate(self):
def generate(self) -> None:
player_cp = (
self.conflict.blue_cp
if self.conflict.blue_cp.captured

View File

@@ -97,7 +97,7 @@ class GroundConflictGenerator:
self.unit_map = unit_map
self.jtacs: List[JtacInfo] = []
def _enemy_stance(self):
def _enemy_stance(self) -> CombatStance:
"""Picks the enemy stance according to the number of planned groups on the frontline for each side"""
if len(self.enemy_planned_combat_groups) > len(
self.player_planned_combat_groups
@@ -122,17 +122,7 @@ class GroundConflictGenerator:
]
)
@staticmethod
def _group_point(point: Point, base_distance) -> Point:
distance = random.randint(
int(base_distance * SPREAD_DISTANCE_FACTOR[0]),
int(base_distance * SPREAD_DISTANCE_FACTOR[1]),
)
return point.random_point_within(
distance, base_distance * SPREAD_DISTANCE_SIZE_FACTOR
)
def generate(self):
def generate(self) -> None:
position = Conflict.frontline_position(
self.conflict.front_line, self.game.theater
)
@@ -724,7 +714,7 @@ class GroundConflictGenerator:
distance_from_frontline: int,
heading: int,
spawn_heading: int,
):
) -> Point:
shifted = conflict_position.point_from_heading(
heading, random.randint(0, combat_width)
)
@@ -798,7 +788,7 @@ class GroundConflictGenerator:
count: int,
at: Point,
move_formation: PointAction = PointAction.OffRoad,
heading=0,
heading: int = 0,
) -> VehicleGroup:
if side == self.conflict.attackers_country:

View File

@@ -1,6 +1,11 @@
import logging
import random
from game import db
from typing import Optional
from dcs.unitgroup import VehicleGroup
from game import db, Game
from game.theater.theatergroundobject import CoastalSiteGroundObject
from gen.coastal.silkworm import SilkwormGenerator
COASTAL_MAP = {
@@ -8,10 +13,13 @@ COASTAL_MAP = {
}
def generate_coastal_group(game, ground_object, faction_name: str):
def generate_coastal_group(
game: Game, ground_object: CoastalSiteGroundObject, faction_name: str
) -> Optional[VehicleGroup]:
"""
This generate a coastal defenses group
:return: Nothing, but put the group reference inside the ground object
:return: The generated group, or None if this faction does not support coastal
defenses.
"""
faction = db.FACTIONS[faction_name]
if len(faction.coastal_defenses) > 0:

View File

@@ -1,14 +1,20 @@
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
class SilkwormGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
class SilkwormGenerator(GroupGenerator[VehicleGroup]):
def __init__(
self, game: Game, ground_object: CoastalSiteGroundObject, faction: Faction
) -> None:
super(SilkwormGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
def generate(self) -> None:
positions = self.get_circular_position(5, launcher_distance=120, coverage=180)

View File

@@ -1,3 +1,5 @@
from __future__ import annotations
import logging
from typing import Tuple, Optional
@@ -54,7 +56,7 @@ class Conflict:
def frontline_position(
cls, frontline: FrontLine, theater: ConflictTheater
) -> Tuple[Point, int]:
attack_heading = frontline.attack_heading
attack_heading = int(frontline.attack_heading)
position = cls.find_ground_position(
frontline.position,
FRONTLINE_LENGTH,
@@ -91,7 +93,7 @@ class Conflict:
defender: Country,
front_line: FrontLine,
theater: ConflictTheater,
):
) -> Conflict:
assert cls.has_frontline_between(front_line.blue_cp, front_line.red_cp)
position, heading, distance = cls.frontline_vector(front_line, theater)
conflict = cls(
@@ -138,7 +140,7 @@ class Conflict:
max_distance: int,
heading: int,
theater: ConflictTheater,
coerce=True,
coerce: bool = True,
) -> Optional[Point]:
"""
Finds the nearest valid ground position along a provided heading and it's inverse up to max_distance.

View File

@@ -1,4 +1,5 @@
import random
from typing import Optional
from dcs.unitgroup import VehicleGroup
@@ -12,7 +13,9 @@ from gen.defenses.armored_group_generator import (
)
def generate_armor_group(faction: str, game, ground_object):
def generate_armor_group(
faction: str, game: Game, ground_object: VehicleGroupGroundObject
) -> Optional[VehicleGroup]:
"""
This generate a group of ground units
:return: Generated group

View File

@@ -1,12 +1,14 @@
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
class ArmoredGroupGenerator(GroupGenerator):
class ArmoredGroupGenerator(GroupGenerator[VehicleGroup]):
def __init__(
self,
game: Game,
@@ -35,7 +37,7 @@ class ArmoredGroupGenerator(GroupGenerator):
)
class FixedSizeArmorGroupGenerator(GroupGenerator):
class FixedSizeArmorGroupGenerator(GroupGenerator[VehicleGroup]):
def __init__(
self,
game: Game,
@@ -47,7 +49,7 @@ class FixedSizeArmorGroupGenerator(GroupGenerator):
self.unit_type = unit_type
self.size = size
def generate(self):
def generate(self) -> None:
spacing = random.randint(20, 70)
index = 0

View File

@@ -30,7 +30,7 @@ class EnvironmentGenerator:
self.mission.weather.wind_at_2000 = wind.at_2000m
self.mission.weather.wind_at_8000 = wind.at_8000m
def generate(self):
def generate(self) -> None:
self.mission.start_time = self.conditions.start_time
self.set_clouds(self.conditions.weather.clouds)
self.set_fog(self.conditions.weather.fog)

View File

@@ -6,7 +6,7 @@ from dcs.ships import USS_Arleigh_Burke_IIa, TICONDEROG
class CarrierGroupGenerator(ShipGroupGenerator):
def generate(self):
def generate(self) -> None:
# Carrier Strike Group 8
if self.faction.carrier_names[0] == "Carrier Strike Group 8":

View File

@@ -20,7 +20,7 @@ if TYPE_CHECKING:
class ChineseNavyGroupGenerator(ShipGroupGenerator):
def generate(self):
def generate(self) -> None:
include_frigate = random.choice([True, True, False])
include_dd = random.choice([True, False])

View File

@@ -23,7 +23,7 @@ class DDGroupGenerator(ShipGroupGenerator):
super(DDGroupGenerator, self).__init__(game, ground_object, faction)
self.ddtype = ddtype
def generate(self):
def generate(self) -> None:
self.add_unit(
self.ddtype,
"DD1",

View File

@@ -1,12 +1,15 @@
from dcs.ships import La_Combattante_II
from game import Game
from game.factions.faction import Faction
from game.theater import TheaterGroundObject
from gen.fleet.dd_group import DDGroupGenerator
class LaCombattanteIIGroupGenerator(DDGroupGenerator):
def __init__(self, game, ground_object: TheaterGroundObject, faction: Faction):
def __init__(
self, game: Game, ground_object: TheaterGroundObject, faction: Faction
):
super(LaCombattanteIIGroupGenerator, self).__init__(
game, ground_object, faction, La_Combattante_II
)

View File

@@ -4,7 +4,7 @@ from gen.sam.group_generator import ShipGroupGenerator
class LHAGroupGenerator(ShipGroupGenerator):
def generate(self):
def generate(self) -> None:
# Add carrier
if len(self.faction.helicopter_carrier) > 0:

View File

@@ -23,7 +23,7 @@ if TYPE_CHECKING:
class RussianNavyGroupGenerator(ShipGroupGenerator):
def generate(self):
def generate(self) -> None:
include_frigate = random.choice([True, True, False])
include_dd = random.choice([True, False])

View File

@@ -6,7 +6,7 @@ from gen.sam.group_generator import ShipGroupGenerator
class SchnellbootGroupGenerator(ShipGroupGenerator):
def generate(self):
def generate(self) -> None:
for i in range(random.randint(2, 4)):
self.add_unit(

View File

@@ -1,7 +1,17 @@
from __future__ import annotations
import logging
import random
from typing import TYPE_CHECKING, Optional
from dcs.unitgroup import ShipGroup
from game import db
from game.theater.theatergroundobject import (
LhaGroundObject,
CarrierGroundObject,
ShipGroundObject,
)
from gen.fleet.carrier_group import CarrierGroupGenerator
from gen.fleet.cn_dd_group import ChineseNavyGroupGenerator, Type54GroupGenerator
from gen.fleet.dd_group import (
@@ -21,6 +31,9 @@ from gen.fleet.schnellboot import SchnellbootGroupGenerator
from gen.fleet.uboat import UBoatGroupGenerator
from gen.fleet.ww2lst import WW2LSTGroupGenerator
if TYPE_CHECKING:
from game import Game
SHIP_MAP = {
"SchnellbootGroupGenerator": SchnellbootGroupGenerator,
@@ -39,10 +52,12 @@ SHIP_MAP = {
}
def generate_ship_group(game, ground_object, faction_name: str):
def generate_ship_group(
game: Game, ground_object: ShipGroundObject, faction_name: str
) -> Optional[ShipGroup]:
"""
This generate a ship group
:return: Nothing, but put the group reference inside the ground object
:return: The generated group, or None if this faction does not support ships.
"""
faction = db.FACTIONS[faction_name]
if len(faction.navy_generators) > 0:
@@ -61,26 +76,30 @@ def generate_ship_group(game, ground_object, faction_name: str):
return None
def generate_carrier_group(faction: str, game, ground_object):
"""
This generate a carrier group
:param parentCp: The parent control point
def generate_carrier_group(
faction: str, game: Game, ground_object: CarrierGroundObject
) -> ShipGroup:
"""Generates a carrier group.
:param faction: The faction the TGO belongs to.
:param game: The Game the group is being generated for.
:param ground_object: The ground object which will own the ship group
:param country: Owner country
:return: Nothing, but put the group reference inside the ground object
:return: The generated group.
"""
generator = CarrierGroupGenerator(game, ground_object, db.FACTIONS[faction])
generator.generate()
return generator.get_generated_group()
def generate_lha_group(faction: str, game, ground_object):
"""
This generate a lha carrier group
:param parentCp: The parent control point
def generate_lha_group(
faction: str, game: Game, ground_object: LhaGroundObject
) -> ShipGroup:
"""Generate an LHA group.
:param faction: The faction the TGO belongs to.
:param game: The Game the group is being generated for.
:param ground_object: The ground object which will own the ship group
:param country: Owner country
:return: Nothing, but put the group reference inside the ground object
:return: The generated group.
"""
generator = LHAGroupGenerator(game, ground_object, db.FACTIONS[faction])
generator.generate()

View File

@@ -6,7 +6,7 @@ from gen.sam.group_generator import ShipGroupGenerator
class UBoatGroupGenerator(ShipGroupGenerator):
def generate(self):
def generate(self) -> None:
for i in range(random.randint(1, 4)):
self.add_unit(

View File

@@ -6,7 +6,7 @@ from gen.sam.group_generator import ShipGroupGenerator
class WW2LSTGroupGenerator(ShipGroupGenerator):
def generate(self):
def generate(self) -> None:
# Add LS Samuel Chase
self.add_unit(

View File

@@ -1057,7 +1057,7 @@ class CoalitionMissionPlanner:
# delayed until their takeoff time by AirConflictGenerator.
package.time_over_target = next(start_time) + tot
def message(self, title, text) -> None:
def message(self, title: str, text: str) -> None:
"""Emits a planning message to the player.
If the mission planner belongs to the players coalition, this emits a

View File

@@ -8,7 +8,6 @@ from dcs.mapping import Point
from dcs.point import MovingPoint, PointAction
from dcs.unit import Unit
from game import db
from game.dcs.aircrafttype import AircraftType
from game.squadrons import Pilot, Squadron
from game.theater.controlpoint import ControlPoint, MissionTarget
@@ -323,12 +322,12 @@ class Flight:
def clear_roster(self) -> None:
self.roster.clear()
def __repr__(self):
def __repr__(self) -> str:
if self.custom_name:
return f"{self.custom_name} {self.count} x {self.unit_type}"
return f"[{self.flight_type}] {self.count} x {self.unit_type}"
def __str__(self):
def __str__(self) -> str:
if self.custom_name:
return f"{self.custom_name} {self.count} x {self.unit_type}"
return f"[{self.flight_type}] {self.count} x {self.unit_type}"

View File

@@ -43,7 +43,7 @@ class ForcedOptionsGenerator:
if blue.unrestricted_satnav or red.unrestricted_satnav:
self.mission.forced_options.unrestricted_satnav = True
def generate(self):
def generate(self) -> None:
self._set_options_view()
self._set_external_views()
self._set_labels()

View File

@@ -1,13 +1,18 @@
from __future__ import annotations
import logging
import random
from enum import Enum
from typing import Dict, List
from typing import Dict, List, TYPE_CHECKING
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
if TYPE_CHECKING:
from game import Game
MAX_COMBAT_GROUP_PER_CP = 10
@@ -54,7 +59,7 @@ class CombatGroup:
self.role = role
self.start_position = None
def __str__(self):
def __str__(self) -> str:
s = f"ROLE : {self.role}\n"
if self.size:
s += f"UNITS {self.unit_type} * {self.size}"
@@ -62,7 +67,7 @@ class CombatGroup:
class GroundPlanner:
def __init__(self, cp: ControlPoint, game):
def __init__(self, cp: ControlPoint, game: Game) -> None:
self.cp = cp
self.game = game
self.connected_enemy_cp = [
@@ -82,7 +87,7 @@ class GroundPlanner:
self.units_per_cp[cp.id] = []
self.reserve: List[CombatGroup] = []
def plan_groundwar(self):
def plan_groundwar(self) -> None:
ground_unit_limit = self.cp.frontline_unit_count_limit

View File

@@ -624,7 +624,7 @@ class GroundObjectsGenerator:
self.icls_alloc = iter(range(1, 21))
self.runways: Dict[str, RunwayData] = {}
def generate(self):
def generate(self) -> None:
for cp in self.game.theater.controlpoints:
if cp.captured:
country_name = self.game.player_country

View File

@@ -91,7 +91,10 @@ class KneeboardPageWriter:
return self.x, self.y
def text(
self, text: str, font=None, fill: Tuple[int, int, int] = (0, 0, 0)
self,
text: str,
font: Optional[ImageFont.ImageFont] = None,
fill: Tuple[int, int, int] = (0, 0, 0),
) -> None:
if font is None:
font = self.content_font

View File

@@ -1,22 +0,0 @@
from dataclasses import dataclass, field
from typing import List
from gen.locations.preset_locations import PresetLocation
@dataclass
class PresetControlPointLocations:
"""A repository of preset locations for a given control point"""
# List of possible ashore locations to generate objects (Represented in miz file by an APC_AAV_7_Amphibious)
ashore_locations: List[PresetLocation] = field(default_factory=list)
# List of possible offshore locations to generate ship groups (Represented in miz file by an Oliver Hazard Perry)
offshore_locations: List[PresetLocation] = field(default_factory=list)
# Possible antiship missiles sites locations (Represented in miz file by Iranian Silkworm missiles)
antiship_locations: List[PresetLocation] = field(default_factory=list)
# List of possible powerplants locations (Represented in miz file by static Workshop A object, USA)
powerplant_locations: List[PresetLocation] = field(default_factory=list)

View File

@@ -1,21 +0,0 @@
from dataclasses import dataclass
from dcs import Point
@dataclass
class PresetLocation:
"""A preset location"""
position: Point
heading: int
id: str
def __str__(self):
return (
"-" * 10
+ "X: {}\n Y: {}\nHdg: {}°\nId: {}".format(
self.position.x, self.position.y, self.heading, self.id
)
+ "-" * 10
)

View File

@@ -1,13 +1,20 @@
import logging
import random
from game import db
from typing import Optional
from dcs.unitgroup import VehicleGroup
from game import db, Game
from game.theater.theatergroundobject import MissileSiteGroundObject
from gen.missiles.scud_site import ScudGenerator
from gen.missiles.v1_group import V1GroupGenerator
MISSILES_MAP = {"V1GroupGenerator": V1GroupGenerator, "ScudGenerator": ScudGenerator}
def generate_missile_group(game, ground_object, faction_name: str):
def generate_missile_group(
game: Game, ground_object: MissileSiteGroundObject, faction_name: str
) -> Optional[VehicleGroup]:
"""
This generate a missiles group
:return: Nothing, but put the group reference inside the ground object

View File

@@ -1,16 +1,22 @@
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
class ScudGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
class ScudGenerator(GroupGenerator[VehicleGroup]):
def __init__(
self, game: Game, ground_object: MissileSiteGroundObject, faction: Faction
) -> None:
super(ScudGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
def generate(self) -> None:
# Scuds
self.add_unit(

View File

@@ -1,16 +1,22 @@
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
class V1GroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
class V1GroupGenerator(GroupGenerator[VehicleGroup]):
def __init__(
self, game: Game, ground_object: MissileSiteGroundObject, faction: Faction
) -> None:
super(V1GroupGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
def generate(self) -> None:
# Ramps
self.add_unit(

View File

@@ -257,7 +257,7 @@ class NameGenerator:
existing_alphas: List[str] = []
@classmethod
def reset(cls):
def reset(cls) -> None:
cls.number = 0
cls.infantry_number = 0
cls.convoy_number = 0
@@ -266,7 +266,7 @@ class NameGenerator:
cls.existing_alphas = []
@classmethod
def reset_numbers(cls):
def reset_numbers(cls) -> None:
cls.number = 0
cls.infantry_number = 0
cls.aircraft_number = 0
@@ -274,7 +274,9 @@ class NameGenerator:
cls.cargo_ship_number = 0
@classmethod
def next_aircraft_name(cls, country: Country, parent_base_id: int, flight: Flight):
def next_aircraft_name(
cls, country: Country, parent_base_id: int, flight: Flight
) -> str:
cls.aircraft_number += 1
try:
if flight.custom_name:
@@ -315,17 +317,17 @@ class NameGenerator:
)
@classmethod
def next_awacs_name(cls, country: Country):
def next_awacs_name(cls, country: Country) -> str:
cls.number += 1
return "awacs|{}|{}|0|".format(country.id, cls.number)
@classmethod
def next_tanker_name(cls, country: Country, unit_type: AircraftType):
def next_tanker_name(cls, country: Country, unit_type: AircraftType) -> str:
cls.number += 1
return "tanker|{}|{}|0|{}".format(country.id, cls.number, unit_type.name)
@classmethod
def next_carrier_name(cls, country: Country):
def next_carrier_name(cls, country: Country) -> str:
cls.number += 1
return "carrier|{}|{}|0|".format(country.id, cls.number)
@@ -340,7 +342,7 @@ class NameGenerator:
return f"Cargo Ship {cls.cargo_ship_number:03}"
@classmethod
def random_objective_name(cls):
def random_objective_name(cls) -> str:
if cls.animals:
animal = random.choice(cls.animals)
cls.animals.remove(animal)

View File

@@ -15,7 +15,7 @@ class RadioFrequency:
#: The frequency in kilohertz.
hertz: int
def __str__(self):
def __str__(self) -> str:
if self.hertz >= 1000000:
return self.format("MHz", 1000000)
return self.format("kHz", 1000)

View File

@@ -15,7 +15,7 @@ class BoforsGenerator(AirDefenseGroupGenerator):
name = "Bofors AAA"
def generate(self):
def generate(self) -> None:
index = 0
for i in range(4):

View File

@@ -24,7 +24,7 @@ class FlakGenerator(AirDefenseGroupGenerator):
name = "Flak Site"
def generate(self):
def generate(self) -> None:
index = 0
mixed = random.choice([True, False])
unit_type = random.choice(GFLAK)

View File

@@ -15,7 +15,7 @@ class Flak18Generator(AirDefenseGroupGenerator):
name = "WW2 Flak Site"
def generate(self):
def generate(self) -> None:
spacing = random.randint(30, 60)
index = 0

View File

@@ -14,7 +14,7 @@ class KS19Generator(AirDefenseGroupGenerator):
name = "KS-19 AAA Site"
def generate(self):
def generate(self) -> None:
self.add_unit(
highdigitsams.AAA_SON_9_Fire_Can,
"TR",

View File

@@ -15,7 +15,7 @@ class AllyWW2FlakGenerator(AirDefenseGroupGenerator):
name = "WW2 Ally Flak Site"
def generate(self):
def generate(self) -> None:
positions = self.get_circular_position(4, launcher_distance=30, coverage=360)
for i, position in enumerate(positions):

View File

@@ -13,7 +13,7 @@ class ZSU57Generator(AirDefenseGroupGenerator):
name = "ZSU-57-2 Group"
def generate(self):
def generate(self) -> None:
num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=110, coverage=360

View File

@@ -15,7 +15,7 @@ class ZU23InsurgentGenerator(AirDefenseGroupGenerator):
name = "Zu-23 Site"
def generate(self):
def generate(self) -> None:
index = 0
for i in range(4):
index = index + 1

View File

@@ -38,7 +38,7 @@ class AirDefenseRange(Enum):
self.default_role = default_role
class AirDefenseGroupGenerator(GroupGenerator, ABC):
class AirDefenseGroupGenerator(GroupGenerator[VehicleGroup], ABC):
"""
This is the base for all SAM group generators
"""

View File

@@ -18,7 +18,7 @@ class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator):
name = "Early Cold War Flak Site"
def generate(self):
def generate(self) -> None:
spacing = random.randint(30, 60)
index = 0
@@ -90,7 +90,7 @@ class ColdWarFlakGenerator(AirDefenseGroupGenerator):
name = "Cold War Flak Site"
def generate(self):
def generate(self) -> None:
spacing = random.randint(30, 60)
index = 0

View File

@@ -1,12 +1,13 @@
from typing import Type
from dcs.unitgroup import VehicleGroup
from dcs.vehicles import AirDefence
from dcs.unittype import VehicleType
from gen.sam.group_generator import GroupGenerator
class EwrGenerator(GroupGenerator):
class EwrGenerator(GroupGenerator[VehicleGroup]):
unit_type: Type[VehicleType]
@classmethod

View File

@@ -13,7 +13,7 @@ class FreyaGenerator(AirDefenseGroupGenerator):
name = "Freya EWR Site"
def generate(self):
def generate(self) -> None:
# TODO : would be better with the Concrete structure that is supposed to protect it
self.add_unit(

View File

@@ -3,13 +3,15 @@ from __future__ import annotations
import logging
import math
import random
from typing import TYPE_CHECKING, Type
from collections import Iterable
from typing import TYPE_CHECKING, Type, TypeVar, Generic
from dcs import unitgroup
from dcs.mapping import Point
from dcs.point import PointAction
from dcs.unit import Ship, Vehicle
from dcs.unittype import VehicleType
from dcs.unitgroup import MovingGroup, ShipGroup
from dcs.unittype import VehicleType, UnitType
from game.dcs.groundunittype import GroundUnitType
from game.factions.faction import Faction
@@ -19,12 +21,15 @@ if TYPE_CHECKING:
from game.game import Game
GroupType = TypeVar("GroupType", bound=MovingGroup)
# TODO: Generate a group description rather than a pydcs group.
# It appears that all of this work gets redone at miz generation time (see
# 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:
class GroupGenerator(Generic[GroupType]):
price: int
@@ -38,10 +43,10 @@ class GroupGenerator:
wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0)
wp.ETA_locked = True
def generate(self):
def generate(self) -> None:
raise NotImplementedError
def get_generated_group(self) -> unitgroup.VehicleGroup:
def get_generated_group(self) -> GroupType:
return self.vg
def add_unit(
@@ -58,7 +63,7 @@ class GroupGenerator:
def add_unit_to_group(
self,
group: unitgroup.VehicleGroup,
group: GroupType,
unit_type: Type[VehicleType],
name: str,
position: Point,
@@ -78,7 +83,9 @@ class GroupGenerator:
return unit
def get_circular_position(self, num_units, launcher_distance, coverage=90):
def get_circular_position(
self, num_units: int, launcher_distance: int, coverage: int = 90
) -> Iterable[tuple[float, float, int]]:
"""
Given a position on the map, array a group of units in a circle a uniform distance from the unit
:param num_units:
@@ -104,21 +111,20 @@ class GroupGenerator:
else:
current_offset = self.heading
current_offset -= outer_offset * (math.ceil(num_units / 2) - 1)
for x in range(1, num_units + 1):
positions.append(
(
self.position.x
+ launcher_distance * math.cos(math.radians(current_offset)),
self.position.y
+ launcher_distance * math.sin(math.radians(current_offset)),
current_offset,
)
for _ in range(1, num_units + 1):
x: float = self.position.x + launcher_distance * math.cos(
math.radians(current_offset)
)
y: float = self.position.y + launcher_distance * math.sin(
math.radians(current_offset)
)
heading = current_offset
positions.append((x, y, int(heading)))
current_offset += outer_offset
return positions
class ShipGroupGenerator(GroupGenerator):
class ShipGroupGenerator(GroupGenerator[ShipGroup]):
"""Abstract class for other ship generator classes"""
def __init__(
@@ -133,7 +139,14 @@ class ShipGroupGenerator(GroupGenerator):
wp = self.vg.add_waypoint(self.position, 0)
wp.ETA_locked = True
def add_unit(self, unit_type, name, pos_x, pos_y, heading) -> Ship:
def add_unit(
self,
unit_type: Type[UnitType],
name: str,
pos_x: float,
pos_y: float,
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

View File

@@ -15,7 +15,7 @@ class AvengerGenerator(AirDefenseGroupGenerator):
name = "Avenger Group"
def generate(self):
def generate(self) -> None:
num_launchers = 2
self.add_unit(

View File

@@ -15,7 +15,7 @@ class ChaparralGenerator(AirDefenseGroupGenerator):
name = "Chaparral Group"
def generate(self):
def generate(self) -> None:
num_launchers = 2
self.add_unit(

View File

@@ -15,7 +15,7 @@ class GepardGenerator(AirDefenseGroupGenerator):
name = "Gepard Group"
def generate(self):
def generate(self) -> None:
num_launchers = 2
positions = self.get_circular_position(

View File

@@ -17,7 +17,7 @@ class HawkGenerator(AirDefenseGroupGenerator):
name = "Hawk Site"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.Hawk_sr,
"SR",

View File

@@ -17,7 +17,7 @@ class HQ7Generator(AirDefenseGroupGenerator):
name = "HQ-7 Site"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.HQ_7_STR_SP,
"STR",

View File

@@ -15,7 +15,7 @@ class LinebackerGenerator(AirDefenseGroupGenerator):
name = "Linebacker Group"
def generate(self):
def generate(self) -> None:
num_launchers = 2
self.add_unit(

View File

@@ -15,7 +15,7 @@ class PatriotGenerator(AirDefenseGroupGenerator):
name = "Patriot Battery"
def generate(self):
def generate(self) -> None:
# Command Post
self.add_unit(
AirDefence.Patriot_str,

View File

@@ -16,7 +16,7 @@ class RapierGenerator(AirDefenseGroupGenerator):
name = "Rapier AA Site"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.Rapier_fsa_blindfire_radar,
"BT",

View File

@@ -14,7 +14,7 @@ class RolandGenerator(AirDefenseGroupGenerator):
name = "Roland Site"
def generate(self):
def generate(self) -> None:
num_launchers = 2
self.add_unit(
AirDefence.Roland_Radar,

View File

@@ -28,7 +28,7 @@ class SA10Generator(AirDefenseGroupGenerator):
self.ln1 = AirDefence.S_300PS_5P85C_ln
self.ln2 = AirDefence.S_300PS_5P85D_ln
def generate(self):
def generate(self) -> None:
# Search Radar
self.add_unit(
self.sr1, "SR1", self.position.x, self.position.y + 40, self.heading

View File

@@ -15,7 +15,7 @@ class SA11Generator(AirDefenseGroupGenerator):
name = "SA-11 Buk Battery"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.SA_11_Buk_SR_9S18M1,
"SR",

View File

@@ -15,7 +15,7 @@ class SA13Generator(AirDefenseGroupGenerator):
name = "SA-13 Strela Group"
def generate(self):
def generate(self) -> None:
self.add_unit(
Unarmed.UAZ_469,
"UAZ",

View File

@@ -13,7 +13,7 @@ class SA15Generator(AirDefenseGroupGenerator):
name = "SA-15 Tor Group"
def generate(self):
def generate(self) -> None:
num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=360

View File

@@ -14,7 +14,7 @@ class SA17Generator(AirDefenseGroupGenerator):
name = "SA-17 Grizzly Battery"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.SA_11_Buk_SR_9S18M1,
"SR",

View File

@@ -15,7 +15,7 @@ class SA19Generator(AirDefenseGroupGenerator):
name = "SA-19 Tunguska Group"
def generate(self):
def generate(self) -> None:
num_launchers = 2
if num_launchers == 1:

View File

@@ -15,7 +15,7 @@ class SA2Generator(AirDefenseGroupGenerator):
name = "SA-2/S-75 Site"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.P_19_s_125_sr,
"SR",

View File

@@ -15,7 +15,7 @@ class SA3Generator(AirDefenseGroupGenerator):
name = "SA-3/S-125 Site"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.P_19_s_125_sr,
"SR",

View File

@@ -15,7 +15,7 @@ class SA6Generator(AirDefenseGroupGenerator):
name = "SA-6 Kub Site"
def generate(self):
def generate(self) -> None:
self.add_unit(
AirDefence.Kub_1S91_str,
"STR",

View File

@@ -13,7 +13,7 @@ class SA8Generator(AirDefenseGroupGenerator):
name = "SA-8 OSA Site"
def generate(self):
def generate(self) -> None:
num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=180

View File

@@ -15,7 +15,7 @@ class SA9Generator(AirDefenseGroupGenerator):
name = "SA-9 Group"
def generate(self):
def generate(self) -> None:
self.add_unit(
Unarmed.UAZ_469,
"UAZ",

View File

@@ -15,7 +15,7 @@ class VulcanGenerator(AirDefenseGroupGenerator):
name = "Vulcan Group"
def generate(self):
def generate(self) -> None:
num_launchers = 2
positions = self.get_circular_position(

View File

@@ -15,7 +15,7 @@ class ZSU23Generator(AirDefenseGroupGenerator):
name = "ZSU-23 Group"
def generate(self):
def generate(self) -> None:
num_launchers = 4
positions = self.get_circular_position(

View File

@@ -15,7 +15,7 @@ class ZU23Generator(AirDefenseGroupGenerator):
name = "ZU-23 Group"
def generate(self):
def generate(self) -> None:
index = 0
for i in range(4):
index = index + 1

View File

@@ -15,7 +15,7 @@ class ZU23UralGenerator(AirDefenseGroupGenerator):
name = "ZU-23 Ural Group"
def generate(self):
def generate(self) -> None:
num_launchers = 4
positions = self.get_circular_position(

View File

@@ -15,7 +15,11 @@ class ZU23UralInsurgentGenerator(AirDefenseGroupGenerator):
name = "ZU-23 Ural Insurgent Group"
def generate(self):
@classmethod
def range(cls) -> AirDefenseRange:
return AirDefenseRange.AAA
def generate(self) -> None:
num_launchers = 4
positions = self.get_circular_position(
@@ -29,7 +33,3 @@ class ZU23UralInsurgentGenerator(AirDefenseGroupGenerator):
position[1],
position[2],
)
@classmethod
def range(cls) -> AirDefenseRange:
return AirDefenseRange.AAA

View File

@@ -51,11 +51,11 @@ class TriggersGenerator:
capture_zone_types = (Fob,)
capture_zone_flag = 600
def __init__(self, mission: Mission, game: Game):
def __init__(self, mission: Mission, game: Game) -> None:
self.mission = mission
self.game = game
def _set_allegiances(self, player_coalition: str, enemy_coalition: str):
def _set_allegiances(self, player_coalition: str, enemy_coalition: str) -> None:
"""
Set airbase initial coalition
"""
@@ -87,7 +87,7 @@ class TriggersGenerator:
cp.captured and player_coalition or enemy_coalition
)
def _set_skill(self, player_coalition: str, enemy_coalition: str):
def _set_skill(self, player_coalition: str, enemy_coalition: str) -> None:
"""
Set skill level for all aircraft in the mission
"""
@@ -103,7 +103,7 @@ class TriggersGenerator:
for vehicle_group in country.vehicle_group:
vehicle_group.set_skill(skill_level)
def _gen_markers(self):
def _gen_markers(self) -> None:
"""
Generate markers on F10 map for each existing objective
"""
@@ -188,7 +188,7 @@ class TriggersGenerator:
recapture_trigger.add_action(ClearFlag(flag=flag))
self.mission.triggerrules.triggers.append(recapture_trigger)
def generate(self):
def generate(self) -> None:
player_coalition = "blue"
enemy_coalition = "red"
@@ -198,7 +198,7 @@ class TriggersGenerator:
self._generate_capture_triggers(player_coalition, enemy_coalition)
@classmethod
def get_capture_zone_flag(cls):
def get_capture_zone_flag(cls) -> int:
flag = cls.capture_zone_flag
cls.capture_zone_flag += 1
return flag

View File

@@ -1,9 +1,8 @@
from __future__ import annotations
import random
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
from dcs.mapping import Point
from dcs.mission import Mission
from dcs.unit import Static
from dcs.unittype import StaticType
@@ -11,7 +10,7 @@ from dcs.unittype import StaticType
if TYPE_CHECKING:
from game import Game
from .conflictgen import Conflict, FRONTLINE_LENGTH
from .conflictgen import Conflict
class MarkerSmoke(StaticType):
@@ -46,13 +45,7 @@ class MassiveSmoke(StaticType):
rate = 1
class Outpost(StaticType):
id = "outpost"
name = "outpost"
category = "Fortifications"
def __monkey_static_dict(self: Static):
def __monkey_static_dict(self: Static) -> dict[str, Any]:
global __original_static_dict
d = __original_static_dict(self)
@@ -65,7 +58,6 @@ def __monkey_static_dict(self: Static):
__original_static_dict = Static.dict
Static.dict = __monkey_static_dict
FRONT_SMOKE_SPACING = 800
FRONT_SMOKE_RANDOM_SPREAD = 4000
FRONT_SMOKE_TYPE_CHANCES = {
2: MassiveSmoke,
@@ -74,29 +66,13 @@ FRONT_SMOKE_TYPE_CHANCES = {
100: Smoke,
}
DESTINATION_SMOKE_AMOUNT_FACTOR = 0.03
DESTINATION_SMOKE_DISTANCE_FACTOR = 1
DESTINATION_SMOKE_TYPE_CHANCES = {
5: BigSmoke,
100: Smoke,
}
def turn_heading(heading, fac):
heading += fac
if heading > 359:
heading = heading - 359
if heading < 0:
heading = 359 + heading
return heading
class VisualGenerator:
def __init__(self, mission: Mission, game: Game):
def __init__(self, mission: Mission, game: Game) -> None:
self.mission = mission
self.game = game
def _generate_frontline_smokes(self):
def _generate_frontline_smokes(self) -> None:
for front_line in self.game.theater.conflicts():
from_cp = front_line.blue_cp
to_cp = front_line.red_cp
@@ -128,61 +104,5 @@ class VisualGenerator:
)
break
def _generate_stub_planes(self):
pass
"""
mission_units = set()
for coalition_name, coalition in self.mission.coalition.items():
for country in coalition.countries.values():
for group in country.plane_group + country.helicopter_group + country.vehicle_group:
for unit in group.units:
mission_units.add(db.unit_type_of(unit))
for unit_type in mission_units:
self.mission.static_group(self.mission.country(self.game.player_country), "a", unit_type, Point(0, 300000), hidden=True)"""
def generate_target_smokes(self, target):
spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR
for _ in range(
0,
int(
target.size
* DESTINATION_SMOKE_AMOUNT_FACTOR
* (1.1 - target.base.strength)
),
):
for k, v in DESTINATION_SMOKE_TYPE_CHANCES.items():
if random.randint(0, 100) <= k:
position = target.position.random_point_within(0, spread)
if not self.game.theater.is_on_land(position):
break
self.mission.static_group(
self.mission.country(self.game.enemy_country),
"",
_type=v,
position=position,
hidden=True,
)
break
def generate_transportation_marker(self, at: Point):
self.mission.static_group(
self.mission.country(self.game.player_country),
"",
_type=MarkerSmoke,
position=at,
)
def generate_transportation_destination(self, at: Point):
self.generate_transportation_marker(at.point_from_heading(0, 20))
self.mission.static_group(
self.mission.country(self.game.player_country),
"",
_type=Outpost,
position=at,
)
def generate(self):
def generate(self) -> None:
self._generate_frontline_smokes()
self._generate_stub_planes()