mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Flesh out typing information, enforce.
This commit is contained in:
parent
69c3d41a8a
commit
fb9a0fe833
@ -29,6 +29,7 @@ from dcs.ships import (
|
||||
CV_1143_5,
|
||||
)
|
||||
from dcs.terrain.terrain import Airport
|
||||
from dcs.unit import Ship
|
||||
from dcs.unitgroup import ShipGroup, StaticGroup
|
||||
from dcs.unittype import UnitType
|
||||
from dcs.vehicles import (
|
||||
@ -328,7 +329,7 @@ REWARDS = {
|
||||
StartingPosition = Union[ShipGroup, StaticGroup, Airport, Point]
|
||||
|
||||
|
||||
def upgrade_to_supercarrier(unit, name: str):
|
||||
def upgrade_to_supercarrier(unit: Type[Ship], name: str) -> Type[Ship]:
|
||||
if unit == Stennis:
|
||||
if name == "CVN-71 Theodore Roosevelt":
|
||||
return CVN_71
|
||||
@ -361,7 +362,7 @@ def unit_type_from_name(name: str) -> Optional[Type[UnitType]]:
|
||||
return None
|
||||
|
||||
|
||||
def country_id_from_name(name):
|
||||
def country_id_from_name(name: str) -> int:
|
||||
for k, v in country_dict.items():
|
||||
if v.name == name:
|
||||
return k
|
||||
|
||||
@ -15,6 +15,7 @@ from typing import (
|
||||
Iterator,
|
||||
List,
|
||||
TYPE_CHECKING,
|
||||
Union,
|
||||
)
|
||||
|
||||
from game import db
|
||||
@ -104,8 +105,9 @@ class StateData:
|
||||
#: Names of vehicle (and ship) units that were killed during the mission.
|
||||
killed_ground_units: List[str]
|
||||
|
||||
#: Names of static units that were destroyed during the mission.
|
||||
destroyed_statics: List[str]
|
||||
#: List of descriptions of destroyed statics. Format of each element is a mapping of
|
||||
#: the coordinate type ("x", "y", "z", "type", "orientation") to the value.
|
||||
destroyed_statics: List[dict[str, Union[float, str]]]
|
||||
|
||||
#: Mangled names of bases that were captured during the mission.
|
||||
base_capture_events: List[str]
|
||||
@ -370,13 +372,13 @@ class PollDebriefingFileThread(threading.Thread):
|
||||
self.game = game
|
||||
self.unit_map = unit_map
|
||||
|
||||
def stop(self):
|
||||
def stop(self) -> None:
|
||||
self._stop_event.set()
|
||||
|
||||
def stopped(self):
|
||||
def stopped(self) -> bool:
|
||||
return self._stop_event.is_set()
|
||||
|
||||
def run(self):
|
||||
def run(self) -> None:
|
||||
if os.path.isfile("state.json"):
|
||||
last_modified = os.path.getmtime("state.json")
|
||||
else:
|
||||
@ -401,7 +403,7 @@ class PollDebriefingFileThread(threading.Thread):
|
||||
|
||||
|
||||
def wait_for_debriefing(
|
||||
callback: Callable[[Debriefing], None], game: Game, unit_map
|
||||
callback: Callable[[Debriefing], None], game: Game, unit_map: UnitMap
|
||||
) -> PollDebriefingFileThread:
|
||||
thread = PollDebriefingFileThread(callback, game, unit_map)
|
||||
thread.start()
|
||||
|
||||
@ -1,14 +1,10 @@
|
||||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .event import Event
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.theater import ConflictTheater
|
||||
|
||||
|
||||
class AirWarEvent(Event):
|
||||
"""Event handler for the air battle"""
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return "AirWar"
|
||||
|
||||
@ -38,13 +38,13 @@ class Event:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
game,
|
||||
game: Game,
|
||||
from_cp: ControlPoint,
|
||||
target_cp: ControlPoint,
|
||||
location: Point,
|
||||
attacker_name: str,
|
||||
defender_name: str,
|
||||
):
|
||||
) -> None:
|
||||
self.game = game
|
||||
self.from_cp = from_cp
|
||||
self.to_cp = target_cp
|
||||
@ -265,7 +265,7 @@ class Event:
|
||||
except Exception:
|
||||
logging.exception(f"Could not process base capture {captured}")
|
||||
|
||||
def commit(self, debriefing: Debriefing):
|
||||
def commit(self, debriefing: Debriefing) -> None:
|
||||
logging.info("Committing mission results")
|
||||
|
||||
self.commit_air_losses(debriefing)
|
||||
|
||||
@ -8,5 +8,5 @@ class FrontlineAttackEvent(Event):
|
||||
future unique Event handling
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return "Frontline attack"
|
||||
|
||||
@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import itertools
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Type, List, Any, Iterator
|
||||
from typing import Optional, Dict, Type, List, Any, Iterator, TYPE_CHECKING
|
||||
|
||||
import dcs
|
||||
from dcs.countries import country_dict
|
||||
@ -25,6 +25,9 @@ from game.data.groundunitclass import GroundUnitClass
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.theater.start_generator import ModSettings
|
||||
|
||||
|
||||
@dataclass
|
||||
class Faction:
|
||||
@ -257,7 +260,7 @@ class Faction:
|
||||
if unit.unit_class is unit_class:
|
||||
yield unit
|
||||
|
||||
def apply_mod_settings(self, mod_settings) -> Faction:
|
||||
def apply_mod_settings(self, mod_settings: ModSettings) -> Faction:
|
||||
# aircraft
|
||||
if not mod_settings.a4_skyhawk:
|
||||
self.remove_aircraft("A-4E-C")
|
||||
@ -319,17 +322,17 @@ class Faction:
|
||||
self.remove_air_defenses("KS19Generator")
|
||||
return self
|
||||
|
||||
def remove_aircraft(self, name):
|
||||
def remove_aircraft(self, name: str) -> None:
|
||||
for i in self.aircrafts:
|
||||
if i.dcs_unit_type.id == name:
|
||||
self.aircrafts.remove(i)
|
||||
|
||||
def remove_air_defenses(self, name):
|
||||
def remove_air_defenses(self, name: str) -> None:
|
||||
for i in self.air_defenses:
|
||||
if i == name:
|
||||
self.air_defenses.remove(i)
|
||||
|
||||
def remove_vehicle(self, name):
|
||||
def remove_vehicle(self, name: str) -> None:
|
||||
for i in self.frontline_units:
|
||||
if i.dcs_unit_type.id == name:
|
||||
self.frontline_units.remove(i)
|
||||
@ -342,7 +345,7 @@ def load_ship(name: str) -> Optional[Type[ShipType]]:
|
||||
return None
|
||||
|
||||
|
||||
def load_all_ships(data) -> List[Type[ShipType]]:
|
||||
def load_all_ships(data: list[str]) -> List[Type[ShipType]]:
|
||||
items = []
|
||||
for name in data:
|
||||
item = load_ship(name)
|
||||
|
||||
81
game/game.py
81
game/game.py
@ -1,24 +1,21 @@
|
||||
from game.dcs.aircrafttype import AircraftType
|
||||
import itertools
|
||||
import logging
|
||||
import random
|
||||
import sys
|
||||
from datetime import date, datetime, timedelta
|
||||
from enum import Enum
|
||||
from typing import Any, List
|
||||
from typing import Any, List, Type, Union
|
||||
|
||||
from dcs.action import Coalition
|
||||
from dcs.mapping import Point
|
||||
from dcs.task import CAP, CAS, PinpointStrike
|
||||
from dcs.vehicles import AirDefence
|
||||
from pydcs_extensions.a4ec.a4ec import A_4E_C
|
||||
from faker import Faker
|
||||
|
||||
from game import db
|
||||
from game.inventory import GlobalAircraftInventory
|
||||
from game.models.game_stats import GameStats
|
||||
from game.plugins import LuaPluginManager
|
||||
from gen import aircraft, naming
|
||||
from gen import naming
|
||||
from gen.ato import AirTaskingOrder
|
||||
from gen.conflictgen import Conflict
|
||||
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
|
||||
@ -37,7 +34,7 @@ from .procurement import AircraftProcurementRequest, ProcurementAi
|
||||
from .profiling import logged_duration
|
||||
from .settings import Settings, AutoAtoBehavior
|
||||
from .squadrons import AirWing
|
||||
from .theater import ConflictTheater
|
||||
from .theater import ConflictTheater, ControlPoint
|
||||
from .theater.bullseye import Bullseye
|
||||
from .theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
|
||||
from .threatzones import ThreatZones
|
||||
@ -115,7 +112,7 @@ class Game:
|
||||
self.informations.append(Information("Game Start", "-" * 40, 0))
|
||||
# Culling Zones are for areas around points of interest that contain things we may not wish to cull.
|
||||
self.__culling_zones: List[Point] = []
|
||||
self.__destroyed_units: List[str] = []
|
||||
self.__destroyed_units: list[dict[str, Union[float, str]]] = []
|
||||
self.savepath = ""
|
||||
self.budget = player_budget
|
||||
self.enemy_budget = enemy_budget
|
||||
@ -190,7 +187,7 @@ class Game:
|
||||
self.theater, self.current_day, self.current_turn_time_of_day, self.settings
|
||||
)
|
||||
|
||||
def sanitize_sides(self):
|
||||
def sanitize_sides(self) -> None:
|
||||
"""
|
||||
Make sure the opposing factions are using different countries
|
||||
:return:
|
||||
@ -228,14 +225,9 @@ class Game:
|
||||
return self.blue_bullseye
|
||||
return self.red_bullseye
|
||||
|
||||
def _roll(self, prob, mult):
|
||||
if self.settings.version == "dev":
|
||||
# always generate all events for dev
|
||||
return 100
|
||||
else:
|
||||
return random.randint(1, 100) <= prob * mult
|
||||
|
||||
def _generate_player_event(self, event_class, player_cp, enemy_cp):
|
||||
def _generate_player_event(
|
||||
self, event_class: Type[Event], player_cp: ControlPoint, enemy_cp: ControlPoint
|
||||
) -> None:
|
||||
self.events.append(
|
||||
event_class(
|
||||
self,
|
||||
@ -247,7 +239,7 @@ class Game:
|
||||
)
|
||||
)
|
||||
|
||||
def _generate_events(self):
|
||||
def _generate_events(self) -> None:
|
||||
for front_line in self.theater.conflicts():
|
||||
self._generate_player_event(
|
||||
FrontlineAttackEvent,
|
||||
@ -261,21 +253,22 @@ class Game:
|
||||
else:
|
||||
self.enemy_budget += amount
|
||||
|
||||
def process_player_income(self):
|
||||
def process_player_income(self) -> None:
|
||||
self.budget += Income(self, player=True).total
|
||||
|
||||
def process_enemy_income(self):
|
||||
def process_enemy_income(self) -> None:
|
||||
# TODO: Clean up save compat.
|
||||
if not hasattr(self, "enemy_budget"):
|
||||
self.enemy_budget = 0
|
||||
self.enemy_budget += Income(self, player=False).total
|
||||
|
||||
def initiate_event(self, event: Event) -> UnitMap:
|
||||
@staticmethod
|
||||
def initiate_event(event: Event) -> UnitMap:
|
||||
# assert event in self.events
|
||||
logging.info("Generating {} (regular)".format(event))
|
||||
return event.generate()
|
||||
|
||||
def finish_event(self, event: Event, debriefing: Debriefing):
|
||||
def finish_event(self, event: Event, debriefing: Debriefing) -> None:
|
||||
logging.info("Finishing event {}".format(event))
|
||||
event.commit(debriefing)
|
||||
|
||||
@ -284,16 +277,6 @@ class Game:
|
||||
else:
|
||||
logging.info("finish_event: event not in the events!")
|
||||
|
||||
def is_player_attack(self, event):
|
||||
if isinstance(event, Event):
|
||||
return (
|
||||
event
|
||||
and event.attacker_name
|
||||
and event.attacker_name == self.player_faction.name
|
||||
)
|
||||
else:
|
||||
raise RuntimeError(f"{event} was passed when an Event type was expected")
|
||||
|
||||
def on_load(self, game_still_initializing: bool = False) -> None:
|
||||
if not hasattr(self, "name_generator"):
|
||||
self.name_generator = naming.namegen
|
||||
@ -400,7 +383,7 @@ class Game:
|
||||
# Autosave progress
|
||||
persistency.autosave(self)
|
||||
|
||||
def check_win_loss(self):
|
||||
def check_win_loss(self) -> TurnState:
|
||||
player_airbases = {
|
||||
cp for cp in self.theater.player_points() if cp.runway_is_operational()
|
||||
}
|
||||
@ -567,7 +550,7 @@ class Game:
|
||||
def current_day(self) -> date:
|
||||
return self.date + timedelta(days=self.turn // 4)
|
||||
|
||||
def next_unit_id(self):
|
||||
def next_unit_id(self) -> int:
|
||||
"""
|
||||
Next unit id for pre-generated units
|
||||
"""
|
||||
@ -608,7 +591,7 @@ class Game:
|
||||
return self.blue_navmesh
|
||||
return self.red_navmesh
|
||||
|
||||
def compute_conflicts_position(self):
|
||||
def compute_conflicts_position(self) -> None:
|
||||
"""
|
||||
Compute the current conflict center position(s), mainly used for culling calculation
|
||||
:return: List of points of interests
|
||||
@ -667,15 +650,15 @@ class Game:
|
||||
|
||||
self.__culling_zones = zones
|
||||
|
||||
def add_destroyed_units(self, data):
|
||||
def add_destroyed_units(self, data: dict[str, Union[float, str]]) -> None:
|
||||
pos = Point(data["x"], data["z"])
|
||||
if self.theater.is_on_land(pos):
|
||||
self.__destroyed_units.append(data)
|
||||
|
||||
def get_destroyed_units(self):
|
||||
def get_destroyed_units(self) -> list[dict[str, Union[float, str]]]:
|
||||
return self.__destroyed_units
|
||||
|
||||
def position_culled(self, pos):
|
||||
def position_culled(self, pos: Point) -> bool:
|
||||
"""
|
||||
Check if unit can be generated at given position depending on culling performance settings
|
||||
:param pos: Position you are tryng to spawn stuff at
|
||||
@ -688,7 +671,7 @@ class Game:
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_culling_zones(self):
|
||||
def get_culling_zones(self) -> list[Point]:
|
||||
"""
|
||||
Check culling points
|
||||
:return: List of culling zones
|
||||
@ -696,30 +679,28 @@ class Game:
|
||||
return self.__culling_zones
|
||||
|
||||
# 1 = red, 2 = blue
|
||||
def get_player_coalition_id(self):
|
||||
def get_player_coalition_id(self) -> int:
|
||||
return 2
|
||||
|
||||
def get_enemy_coalition_id(self):
|
||||
def get_enemy_coalition_id(self) -> int:
|
||||
return 1
|
||||
|
||||
def get_player_coalition(self):
|
||||
def get_player_coalition(self) -> Coalition:
|
||||
return Coalition.Blue
|
||||
|
||||
def get_enemy_coalition(self):
|
||||
def get_enemy_coalition(self) -> Coalition:
|
||||
return Coalition.Red
|
||||
|
||||
def get_player_color(self):
|
||||
def get_player_color(self) -> str:
|
||||
return "blue"
|
||||
|
||||
def get_enemy_color(self):
|
||||
def get_enemy_color(self) -> str:
|
||||
return "red"
|
||||
|
||||
def process_win_loss(self, turn_state: TurnState):
|
||||
def process_win_loss(self, turn_state: TurnState) -> None:
|
||||
if turn_state is TurnState.WIN:
|
||||
return self.message(
|
||||
"Congratulations, you are victorious! Start a new campaign to continue."
|
||||
self.message(
|
||||
"Congratulations, you are victorious! Start a new campaign to continue."
|
||||
)
|
||||
elif turn_state is TurnState.LOSS:
|
||||
return self.message(
|
||||
"Game Over, you lose. Start a new campaign to continue."
|
||||
)
|
||||
self.message("Game Over, you lose. Start a new campaign to continue.")
|
||||
|
||||
@ -2,13 +2,13 @@ import datetime
|
||||
|
||||
|
||||
class Information:
|
||||
def __init__(self, title="", text="", turn=0):
|
||||
def __init__(self, title: str = "", text: str = "", turn: int = 0) -> None:
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.turn = turn
|
||||
self.timestamp = datetime.datetime.now()
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return "[{}][{}] {} {}".format(
|
||||
self.timestamp.strftime("%Y-%m-%d %H:%M:%S")
|
||||
if self.timestamp is not None
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
class DestroyedUnit:
|
||||
"""
|
||||
Store info about a destroyed unit
|
||||
"""
|
||||
|
||||
x: int
|
||||
y: int
|
||||
name: str
|
||||
|
||||
def __init__(self, x, y, name):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.name = name
|
||||
@ -1,4 +1,9 @@
|
||||
from typing import List
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
|
||||
|
||||
class FactionTurnMetadata:
|
||||
@ -10,7 +15,7 @@ class FactionTurnMetadata:
|
||||
vehicles_count: int = 0
|
||||
sam_count: int = 0
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.aircraft_count = 0
|
||||
self.vehicles_count = 0
|
||||
self.sam_count = 0
|
||||
@ -24,7 +29,7 @@ class GameTurnMetadata:
|
||||
allied_units: FactionTurnMetadata
|
||||
enemy_units: FactionTurnMetadata
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.allied_units = FactionTurnMetadata()
|
||||
self.enemy_units = FactionTurnMetadata()
|
||||
|
||||
@ -34,10 +39,10 @@ class GameStats:
|
||||
Store statistics for the current game
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.data_per_turn: List[GameTurnMetadata] = []
|
||||
|
||||
def update(self, game):
|
||||
def update(self, game: Game) -> None:
|
||||
"""
|
||||
Save data for current turn
|
||||
:param game: Game we want to save the data about
|
||||
|
||||
@ -62,7 +62,7 @@ class Operation:
|
||||
plugin_scripts: List[str] = []
|
||||
|
||||
@classmethod
|
||||
def prepare(cls, game: Game):
|
||||
def prepare(cls, game: Game) -> None:
|
||||
with open("resources/default_options.lua", "r") as f:
|
||||
options_dict = loads(f.read())["options"]
|
||||
cls._set_mission(Mission(game.theater.terrain))
|
||||
@ -107,7 +107,7 @@ class Operation:
|
||||
cls.current_mission = mission
|
||||
|
||||
@classmethod
|
||||
def _setup_mission_coalitions(cls):
|
||||
def _setup_mission_coalitions(cls) -> None:
|
||||
cls.current_mission.coalition["blue"] = Coalition(
|
||||
"blue", bullseye=cls.game.blue_bullseye.to_pydcs()
|
||||
)
|
||||
@ -163,7 +163,7 @@ class Operation:
|
||||
airsupportgen: AirSupportConflictGenerator,
|
||||
jtacs: List[JtacInfo],
|
||||
airgen: AircraftConflictGenerator,
|
||||
):
|
||||
) -> None:
|
||||
"""Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)"""
|
||||
|
||||
gens: List[MissionInfoGenerator] = [
|
||||
@ -251,7 +251,7 @@ class Operation:
|
||||
# beacon list.
|
||||
|
||||
@classmethod
|
||||
def _generate_ground_units(cls):
|
||||
def _generate_ground_units(cls) -> None:
|
||||
cls.groundobjectgen = GroundObjectsGenerator(
|
||||
cls.current_mission,
|
||||
cls.game,
|
||||
@ -266,7 +266,12 @@ class Operation:
|
||||
"""Add destroyed units to the Mission"""
|
||||
for d in cls.game.get_destroyed_units():
|
||||
try:
|
||||
utype = db.unit_type_from_name(d["type"])
|
||||
type_name = d["type"]
|
||||
if not isinstance(type_name, str):
|
||||
raise TypeError(
|
||||
"Expected the type of the destroyed static to be a string"
|
||||
)
|
||||
utype = db.unit_type_from_name(type_name)
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
@ -418,7 +423,7 @@ class Operation:
|
||||
CargoShipGenerator(cls.current_mission, cls.game, cls.unit_map).generate()
|
||||
|
||||
@classmethod
|
||||
def reset_naming_ids(cls):
|
||||
def reset_naming_ids(cls) -> None:
|
||||
namegen.reset_numbers()
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -1,15 +1,19 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import pickle
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game import Game
|
||||
|
||||
_dcs_saved_game_folder: Optional[str] = None
|
||||
|
||||
|
||||
def setup(user_folder: str):
|
||||
def setup(user_folder: str) -> None:
|
||||
global _dcs_saved_game_folder
|
||||
_dcs_saved_game_folder = user_folder
|
||||
if not save_dir().exists():
|
||||
@ -38,7 +42,7 @@ def mission_path_for(name: str) -> str:
|
||||
return os.path.join(base_path(), "Missions", name)
|
||||
|
||||
|
||||
def load_game(path):
|
||||
def load_game(path: str) -> Optional[Game]:
|
||||
with open(path, "rb") as f:
|
||||
try:
|
||||
save = pickle.load(f)
|
||||
@ -49,7 +53,7 @@ def load_game(path):
|
||||
return None
|
||||
|
||||
|
||||
def save_game(game) -> bool:
|
||||
def save_game(game: Game) -> bool:
|
||||
try:
|
||||
with open(_temporary_save_file(), "wb") as f:
|
||||
pickle.dump(game, f)
|
||||
@ -60,7 +64,7 @@ def save_game(game) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def autosave(game) -> bool:
|
||||
def autosave(game: Game) -> bool:
|
||||
"""
|
||||
Autosave to the autosave location
|
||||
:param game: Game to save
|
||||
|
||||
@ -38,7 +38,7 @@ class PluginSettings:
|
||||
self.settings = Settings()
|
||||
self.initialize_settings()
|
||||
|
||||
def set_settings(self, settings: Settings):
|
||||
def set_settings(self, settings: Settings) -> None:
|
||||
self.settings = settings
|
||||
self.initialize_settings()
|
||||
|
||||
@ -146,7 +146,7 @@ class LuaPlugin(PluginSettings):
|
||||
|
||||
return cls(definition)
|
||||
|
||||
def set_settings(self, settings: Settings):
|
||||
def set_settings(self, settings: Settings) -> None:
|
||||
super().set_settings(settings)
|
||||
for option in self.definition.options:
|
||||
option.set_settings(self.settings)
|
||||
|
||||
@ -2,12 +2,12 @@ from dcs import Point
|
||||
|
||||
|
||||
class PointWithHeading(Point):
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
super(PointWithHeading, self).__init__(0, 0)
|
||||
self.heading = 0
|
||||
|
||||
@staticmethod
|
||||
def from_point(point: Point, heading: int):
|
||||
def from_point(point: Point, heading: int) -> Point:
|
||||
p = PointWithHeading()
|
||||
p.x = point.x
|
||||
p.y = point.y
|
||||
|
||||
@ -5,7 +5,8 @@ import timeit
|
||||
from collections import defaultdict
|
||||
from contextlib import contextmanager
|
||||
from datetime import timedelta
|
||||
from typing import Iterator
|
||||
from types import TracebackType
|
||||
from typing import Iterator, Optional, Type
|
||||
|
||||
|
||||
@contextmanager
|
||||
@ -23,7 +24,12 @@ class MultiEventTracer:
|
||||
def __enter__(self) -> MultiEventTracer:
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: Optional[Type[BaseException]],
|
||||
exc_val: Optional[BaseException],
|
||||
exc_tb: Optional[TracebackType],
|
||||
) -> None:
|
||||
for event, duration in self.events.items():
|
||||
logging.debug("%s took %s", event, duration)
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import timedelta
|
||||
from enum import Enum, unique
|
||||
from typing import Dict, Optional
|
||||
from typing import Dict, Optional, Any
|
||||
|
||||
from dcs.forcedoptions import ForcedOptions
|
||||
|
||||
@ -104,7 +104,7 @@ class Settings:
|
||||
def set_plugin_option(self, identifier: str, enabled: bool) -> None:
|
||||
self.plugins[self.plugin_settings_key(identifier)] = enabled
|
||||
|
||||
def __setstate__(self, state) -> None:
|
||||
def __setstate__(self, state: dict[str, Any]) -> None:
|
||||
# __setstate__ is called with the dict of the object being unpickled. We
|
||||
# can provide save compatibility for new settings options (which
|
||||
# normally would not be present in the unpickled object) by creating a
|
||||
|
||||
@ -13,6 +13,7 @@ from typing import (
|
||||
Optional,
|
||||
Iterator,
|
||||
Sequence,
|
||||
Any,
|
||||
)
|
||||
|
||||
import yaml
|
||||
@ -196,7 +197,7 @@ class Squadron:
|
||||
def send_on_leave(pilot: Pilot) -> None:
|
||||
pilot.send_on_leave()
|
||||
|
||||
def return_from_leave(self, pilot: Pilot):
|
||||
def return_from_leave(self, pilot: Pilot) -> None:
|
||||
if not self.has_unfilled_pilot_slots:
|
||||
raise RuntimeError(
|
||||
f"Cannot return {pilot} from leave because {self} is full"
|
||||
@ -290,7 +291,7 @@ class Squadron:
|
||||
player=player,
|
||||
)
|
||||
|
||||
def __setstate__(self, state) -> None:
|
||||
def __setstate__(self, state: dict[str, Any]) -> None:
|
||||
# TODO: Remove save compat.
|
||||
if "auto_assignable_mission_types" not in state:
|
||||
state["auto_assignable_mission_types"] = set(state["mission_types"])
|
||||
|
||||
@ -8,15 +8,15 @@ from game.dcs.aircrafttype import AircraftType
|
||||
from game.dcs.groundunittype import GroundUnitType
|
||||
from game.dcs.unittype import UnitType
|
||||
|
||||
BASE_MAX_STRENGTH = 1
|
||||
BASE_MIN_STRENGTH = 0
|
||||
BASE_MAX_STRENGTH = 1.0
|
||||
BASE_MIN_STRENGTH = 0.0
|
||||
|
||||
|
||||
class Base:
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.aircraft: dict[AircraftType, int] = {}
|
||||
self.armor: dict[GroundUnitType, int] = {}
|
||||
self.strength = 1
|
||||
self.strength = 1.0
|
||||
|
||||
@property
|
||||
def total_aircraft(self) -> int:
|
||||
@ -42,7 +42,7 @@ class Base:
|
||||
]
|
||||
)
|
||||
|
||||
def commission_units(self, units: dict[Any, int]):
|
||||
def commission_units(self, units: dict[Any, int]) -> None:
|
||||
for unit_type, unit_count in units.items():
|
||||
if unit_count <= 0:
|
||||
continue
|
||||
@ -58,7 +58,7 @@ class Base:
|
||||
|
||||
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
|
||||
|
||||
def commit_losses(self, units_lost: dict[Any, int]):
|
||||
def commit_losses(self, units_lost: dict[Any, int]) -> None:
|
||||
for unit_type, count in units_lost.items():
|
||||
target_dict: dict[Any, int]
|
||||
if unit_type in self.aircraft:
|
||||
@ -77,7 +77,7 @@ class Base:
|
||||
if target_dict[unit_type] == 0:
|
||||
del target_dict[unit_type]
|
||||
|
||||
def affect_strength(self, amount):
|
||||
def affect_strength(self, amount: float) -> None:
|
||||
self.strength += amount
|
||||
if self.strength > BASE_MAX_STRENGTH:
|
||||
self.strength = BASE_MAX_STRENGTH
|
||||
|
||||
@ -5,7 +5,7 @@ import math
|
||||
from dataclasses import dataclass
|
||||
from functools import cached_property
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterator, List, Optional, Tuple
|
||||
from typing import Any, Dict, Iterator, List, Optional, Tuple, TYPE_CHECKING
|
||||
|
||||
from dcs import Mission
|
||||
from dcs.countries import (
|
||||
@ -61,6 +61,9 @@ from ..profiling import logged_duration
|
||||
from ..scenery_group import SceneryGroup
|
||||
from ..utils import Distance, meters
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import TheaterGroundObject
|
||||
|
||||
SIZE_TINY = 150
|
||||
SIZE_SMALL = 600
|
||||
SIZE_REGULAR = 1000
|
||||
@ -505,7 +508,7 @@ class ConflictTheater:
|
||||
"""
|
||||
daytime_map: Dict[str, Tuple[int, int]]
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self) -> None:
|
||||
self.controlpoints: List[ControlPoint] = []
|
||||
self.point_to_ll_transformer = Transformer.from_crs(
|
||||
self.projection_parameters.to_crs(), CRS("WGS84")
|
||||
@ -537,10 +540,12 @@ class ConflictTheater:
|
||||
CRS("WGS84"), self.projection_parameters.to_crs()
|
||||
)
|
||||
|
||||
def add_controlpoint(self, point: ControlPoint):
|
||||
def add_controlpoint(self, point: ControlPoint) -> None:
|
||||
self.controlpoints.append(point)
|
||||
|
||||
def find_ground_objects_by_obj_name(self, obj_name):
|
||||
def find_ground_objects_by_obj_name(
|
||||
self, obj_name: str
|
||||
) -> list[TheaterGroundObject]:
|
||||
found = []
|
||||
for cp in self.controlpoints:
|
||||
for g in cp.ground_objects:
|
||||
|
||||
@ -290,9 +290,9 @@ class ControlPoint(MissionTarget, ABC):
|
||||
at: db.StartingPosition,
|
||||
size: int,
|
||||
importance: float,
|
||||
has_frontline=True,
|
||||
cptype=ControlPointType.AIRBASE,
|
||||
):
|
||||
has_frontline: bool = True,
|
||||
cptype: ControlPointType = ControlPointType.AIRBASE,
|
||||
) -> None:
|
||||
super().__init__(name, position)
|
||||
# TODO: Should be Airbase specific.
|
||||
self.id = cp_id
|
||||
@ -322,7 +322,7 @@ class ControlPoint(MissionTarget, ABC):
|
||||
|
||||
self.target_position: Optional[Point] = None
|
||||
|
||||
def __repr__(self):
|
||||
def __repr__(self) -> str:
|
||||
return f"<{self.__class__}: {self.name}>"
|
||||
|
||||
@property
|
||||
@ -334,11 +334,11 @@ class ControlPoint(MissionTarget, ABC):
|
||||
def heading(self) -> int:
|
||||
...
|
||||
|
||||
def __str__(self):
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def is_global(self):
|
||||
def is_global(self) -> bool:
|
||||
return not self.connected_points
|
||||
|
||||
def transitive_connected_friendly_points(
|
||||
@ -405,21 +405,21 @@ class ControlPoint(MissionTarget, ABC):
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_carrier(self):
|
||||
def is_carrier(self) -> bool:
|
||||
"""
|
||||
:return: Whether this control point is an aircraft carrier
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_fleet(self):
|
||||
def is_fleet(self) -> bool:
|
||||
"""
|
||||
:return: Whether this control point is a boat (mobile)
|
||||
"""
|
||||
return False
|
||||
|
||||
@property
|
||||
def is_lha(self):
|
||||
def is_lha(self) -> bool:
|
||||
"""
|
||||
:return: Whether this control point is an LHA
|
||||
"""
|
||||
@ -439,7 +439,7 @@ class ControlPoint(MissionTarget, ABC):
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def total_aircraft_parking(self):
|
||||
def total_aircraft_parking(self) -> int:
|
||||
"""
|
||||
:return: The maximum number of aircraft that can be stored in this
|
||||
control point
|
||||
@ -471,7 +471,7 @@ class ControlPoint(MissionTarget, ABC):
|
||||
...
|
||||
|
||||
# TODO: Should be naval specific.
|
||||
def get_carrier_group_name(self):
|
||||
def get_carrier_group_name(self) -> Optional[str]:
|
||||
"""
|
||||
Get the carrier group name if the airbase is a carrier
|
||||
:return: Carrier group name
|
||||
@ -497,10 +497,12 @@ class ControlPoint(MissionTarget, ABC):
|
||||
return None
|
||||
|
||||
# TODO: Should be Airbase specific.
|
||||
def is_connected(self, to) -> bool:
|
||||
def is_connected(self, to: ControlPoint) -> bool:
|
||||
return to in self.connected_points
|
||||
|
||||
def find_ground_objects_by_obj_name(self, obj_name):
|
||||
def find_ground_objects_by_obj_name(
|
||||
self, obj_name: str
|
||||
) -> list[TheaterGroundObject]:
|
||||
found = []
|
||||
for g in self.ground_objects:
|
||||
if g.obj_name == obj_name:
|
||||
@ -522,7 +524,7 @@ class ControlPoint(MissionTarget, ABC):
|
||||
f"vehicles have been captured and sold for ${total}M."
|
||||
)
|
||||
|
||||
def retreat_ground_units(self, game: Game):
|
||||
def retreat_ground_units(self, game: Game) -> None:
|
||||
# When there are multiple valid destinations, deliver units to whichever
|
||||
# base is least defended first. The closest approximation of unit
|
||||
# strength we have is price
|
||||
@ -764,8 +766,8 @@ class ControlPoint(MissionTarget, ABC):
|
||||
|
||||
class Airfield(ControlPoint):
|
||||
def __init__(
|
||||
self, airport: Airport, size: int, importance: float, has_frontline=True
|
||||
):
|
||||
self, airport: Airport, size: int, importance: float, has_frontline: bool = True
|
||||
) -> None:
|
||||
super().__init__(
|
||||
airport.id,
|
||||
airport.name,
|
||||
@ -960,7 +962,7 @@ class Carrier(NavalControlPoint):
|
||||
raise RuntimeError("Carriers cannot be captured")
|
||||
|
||||
@property
|
||||
def is_carrier(self):
|
||||
def is_carrier(self) -> bool:
|
||||
return True
|
||||
|
||||
def can_operate(self, aircraft: AircraftType) -> bool:
|
||||
|
||||
@ -88,7 +88,7 @@ class FrontLine(MissionTarget):
|
||||
yield from super().mission_types(for_player)
|
||||
|
||||
@property
|
||||
def position(self):
|
||||
def position(self) -> Point:
|
||||
"""
|
||||
The position where the conflict should occur
|
||||
according to the current strength of each control point.
|
||||
@ -107,12 +107,12 @@ class FrontLine(MissionTarget):
|
||||
return self.blue_cp, self.red_cp
|
||||
|
||||
@property
|
||||
def attack_distance(self):
|
||||
def attack_distance(self) -> float:
|
||||
"""The total distance of all segments"""
|
||||
return sum(i.attack_distance for i in self.segments)
|
||||
|
||||
@property
|
||||
def attack_heading(self):
|
||||
def attack_heading(self) -> float:
|
||||
"""The heading of the active attack segment from player to enemy control point"""
|
||||
return self.active_segment.attack_heading
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ class Landmap:
|
||||
exclusion_zones: MultiPolygon
|
||||
sea_zones: MultiPolygon
|
||||
|
||||
def __post_init__(self):
|
||||
def __post_init__(self) -> None:
|
||||
if not self.inclusion_zones.is_valid:
|
||||
raise RuntimeError("Inclusion zones not valid")
|
||||
if not self.exclusion_zones.is_valid:
|
||||
@ -36,13 +36,5 @@ def load_landmap(filename: str) -> Optional[Landmap]:
|
||||
return None
|
||||
|
||||
|
||||
def poly_contains(x, y, poly: Union[MultiPolygon, Polygon]):
|
||||
def poly_contains(x: float, y: float, poly: Union[MultiPolygon, Polygon]) -> bool:
|
||||
return poly.contains(geometry.Point(x, y))
|
||||
|
||||
|
||||
def poly_centroid(poly) -> Tuple[float, float]:
|
||||
x_list = [vertex[0] for vertex in poly]
|
||||
y_list = [vertex[1] for vertex in poly]
|
||||
x = sum(x_list) / len(poly)
|
||||
y = sum(y_list) / len(poly)
|
||||
return (x, y)
|
||||
|
||||
@ -217,7 +217,7 @@ class BuildingGroundObject(TheaterGroundObject):
|
||||
heading: int,
|
||||
control_point: ControlPoint,
|
||||
dcs_identifier: str,
|
||||
is_fob_structure=False,
|
||||
is_fob_structure: bool = False,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name=name,
|
||||
@ -438,7 +438,7 @@ class CoastalSiteGroundObject(TheaterGroundObject):
|
||||
group_id: int,
|
||||
position: Point,
|
||||
control_point: ControlPoint,
|
||||
heading,
|
||||
heading: int,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
name=name,
|
||||
|
||||
@ -27,7 +27,10 @@ ThreatPoly = Union[MultiPolygon, Polygon]
|
||||
|
||||
class ThreatZones:
|
||||
def __init__(
|
||||
self, airbases: ThreatPoly, air_defenses: ThreatPoly, radar_sam_threats
|
||||
self,
|
||||
airbases: ThreatPoly,
|
||||
air_defenses: ThreatPoly,
|
||||
radar_sam_threats: ThreatPoly,
|
||||
) -> None:
|
||||
self.airbases = airbases
|
||||
self.air_defenses = air_defenses
|
||||
@ -44,8 +47,10 @@ class ThreatZones:
|
||||
boundary = self.closest_boundary(point)
|
||||
return meters(boundary.distance_to_point(point))
|
||||
|
||||
# Type checking ignored because singledispatchmethod doesn't work with required type
|
||||
# definitions. The implementation methods are all typed, so should be fine.
|
||||
@singledispatchmethod
|
||||
def threatened(self, position) -> bool:
|
||||
def threatened(self, position) -> bool: # type: ignore
|
||||
raise NotImplementedError
|
||||
|
||||
@threatened.register
|
||||
@ -61,8 +66,10 @@ class ThreatZones:
|
||||
LineString([self.dcs_to_shapely_point(a), self.dcs_to_shapely_point(b)])
|
||||
)
|
||||
|
||||
# Type checking ignored because singledispatchmethod doesn't work with required type
|
||||
# definitions. The implementation methods are all typed, so should be fine.
|
||||
@singledispatchmethod
|
||||
def threatened_by_aircraft(self, target) -> bool:
|
||||
def threatened_by_aircraft(self, target) -> bool: # type: ignore
|
||||
raise NotImplementedError
|
||||
|
||||
@threatened_by_aircraft.register
|
||||
@ -82,8 +89,10 @@ class ThreatZones:
|
||||
LineString((self.dcs_to_shapely_point(p.position) for p in waypoints))
|
||||
)
|
||||
|
||||
# Type checking ignored because singledispatchmethod doesn't work with required type
|
||||
# definitions. The implementation methods are all typed, so should be fine.
|
||||
@singledispatchmethod
|
||||
def threatened_by_air_defense(self, target) -> bool:
|
||||
def threatened_by_air_defense(self, target) -> bool: # type: ignore
|
||||
raise NotImplementedError
|
||||
|
||||
@threatened_by_air_defense.register
|
||||
@ -102,8 +111,10 @@ class ThreatZones:
|
||||
self.dcs_to_shapely_point(target.position)
|
||||
)
|
||||
|
||||
# Type checking ignored because singledispatchmethod doesn't work with required type
|
||||
# definitions. The implementation methods are all typed, so should be fine.
|
||||
@singledispatchmethod
|
||||
def threatened_by_radar_sam(self, target) -> bool:
|
||||
def threatened_by_radar_sam(self, target) -> bool: # type: ignore
|
||||
raise NotImplementedError
|
||||
|
||||
@threatened_by_radar_sam.register
|
||||
|
||||
@ -592,8 +592,14 @@ class PendingTransfers:
|
||||
self.pending_transfers.append(new_transfer)
|
||||
return new_transfer
|
||||
|
||||
# Type checking ignored because singledispatchmethod doesn't work with required type
|
||||
# definitions. The implementation methods are all typed, so should be fine.
|
||||
@singledispatchmethod
|
||||
def cancel_transport(self, transport, transfer: TransferOrder) -> None:
|
||||
def cancel_transport( # type: ignore
|
||||
self,
|
||||
transport,
|
||||
transfer: TransferOrder,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
@cancel_transport.register
|
||||
|
||||
@ -2,8 +2,9 @@ from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
import math
|
||||
from collections import Iterable
|
||||
from dataclasses import dataclass
|
||||
from typing import Union
|
||||
from typing import Union, Any
|
||||
|
||||
METERS_TO_FEET = 3.28084
|
||||
FEET_TO_METERS = 1 / METERS_TO_FEET
|
||||
@ -16,12 +17,12 @@ MS_TO_KPH = 3.6
|
||||
KPH_TO_MS = 1 / MS_TO_KPH
|
||||
|
||||
|
||||
def heading_sum(h, a) -> int:
|
||||
def heading_sum(h: int, a: int) -> int:
|
||||
h += a
|
||||
return h % 360
|
||||
|
||||
|
||||
def opposite_heading(h):
|
||||
def opposite_heading(h: int) -> int:
|
||||
return heading_sum(h, 180)
|
||||
|
||||
|
||||
@ -180,7 +181,7 @@ def mach(value: float, altitude: Distance) -> Speed:
|
||||
SPEED_OF_SOUND_AT_SEA_LEVEL = knots(661.5)
|
||||
|
||||
|
||||
def pairwise(iterable):
|
||||
def pairwise(iterable: Iterable[Any]) -> Iterable[tuple[Any, Any]]:
|
||||
"""
|
||||
itertools recipe
|
||||
s -> (s0,s1), (s1,s2), (s2, s3), ...
|
||||
|
||||
@ -83,7 +83,7 @@ class Weather:
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def random_wind(minimum: int, maximum) -> WindConditions:
|
||||
def random_wind(minimum: int, maximum: int) -> WindConditions:
|
||||
wind_direction = random.randint(0, 360)
|
||||
at_0m_factor = 1
|
||||
at_2000m_factor = 2
|
||||
|
||||
@ -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":
|
||||
|
||||
@ -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
|
||||
|
||||
18
gen/armor.py
18
gen/armor.py
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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":
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}"
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
@ -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
|
||||
)
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -15,7 +15,7 @@ class BoforsGenerator(AirDefenseGroupGenerator):
|
||||
|
||||
name = "Bofors AAA"
|
||||
|
||||
def generate(self):
|
||||
def generate(self) -> None:
|
||||
|
||||
index = 0
|
||||
for i in range(4):
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
"""
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -15,7 +15,7 @@ class AvengerGenerator(AirDefenseGroupGenerator):
|
||||
|
||||
name = "Avenger Group"
|
||||
|
||||
def generate(self):
|
||||
def generate(self) -> None:
|
||||
num_launchers = 2
|
||||
|
||||
self.add_unit(
|
||||
|
||||
@ -15,7 +15,7 @@ class ChaparralGenerator(AirDefenseGroupGenerator):
|
||||
|
||||
name = "Chaparral Group"
|
||||
|
||||
def generate(self):
|
||||
def generate(self) -> None:
|
||||
num_launchers = 2
|
||||
|
||||
self.add_unit(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -17,7 +17,7 @@ class HawkGenerator(AirDefenseGroupGenerator):
|
||||
|
||||
name = "Hawk Site"
|
||||
|
||||
def generate(self):
|
||||
def generate(self) -> None:
|
||||
self.add_unit(
|
||||
AirDefence.Hawk_sr,
|
||||
"SR",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -15,7 +15,7 @@ class LinebackerGenerator(AirDefenseGroupGenerator):
|
||||
|
||||
name = "Linebacker Group"
|
||||
|
||||
def generate(self):
|
||||
def generate(self) -> None:
|
||||
num_launchers = 2
|
||||
|
||||
self.add_unit(
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user