AEW&C kneeboard + actually do AEW&C (#922)

* AEW&C will now do AEW&C
* AEW&C gets a frequency
* AEW&C is added to kneeboard (Frequency, Depature, Depature Time, Arrival Time)
This commit is contained in:
Simon Krüger 2021-03-13 23:07:19 +01:00 committed by GitHub
parent 0f07b2c095
commit 260358c5fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 93 additions and 14 deletions

View File

@ -166,6 +166,7 @@ class Operation:
airgen: AircraftConflictGenerator, airgen: AircraftConflictGenerator,
): ):
"""Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)""" """Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)"""
gens: List[MissionInfoGenerator] = [ gens: List[MissionInfoGenerator] = [
KneeboardGenerator(cls.current_mission, cls.game), KneeboardGenerator(cls.current_mission, cls.game),
BriefingGenerator(cls.current_mission, cls.game), BriefingGenerator(cls.current_mission, cls.game),
@ -177,9 +178,8 @@ class Operation:
for tanker in airsupportgen.air_support.tankers: for tanker in airsupportgen.air_support.tankers:
gen.add_tanker(tanker) gen.add_tanker(tanker)
if cls.player_awacs_enabled: for aewc in airsupportgen.air_support.awacs:
for awacs in airsupportgen.air_support.awacs: gen.add_awacs(aewc)
gen.add_awacs(awacs)
for jtac in jtacs: for jtac in jtacs:
gen.add_jtac(jtac) gen.add_jtac(jtac)
@ -378,7 +378,9 @@ class Operation:
cls.game, cls.game,
cls.radio_registry, cls.radio_registry,
cls.unit_map, cls.unit_map,
air_support=cls.airsupportgen.air_support,
) )
cls.airgen.clear_parking_slots() cls.airgen.clear_parking_slots()
cls.airgen.generate_flights( cls.airgen.generate_flights(

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import logging import logging
import random import random
from dataclasses import dataclass from dataclasses import dataclass, field
from datetime import timedelta from datetime import timedelta
from functools import cached_property from functools import cached_property
from typing import Dict, List, Optional, TYPE_CHECKING, Type, Union from typing import Dict, List, Optional, TYPE_CHECKING, Type, Union
@ -67,6 +67,8 @@ from dcs.task import (
Targets, Targets,
Task, Task,
WeaponType, WeaponType,
AWACSTaskAction,
SetFrequencyCommand,
) )
from dcs.terrain.terrain import Airport, NoParkingSlotError from dcs.terrain.terrain import Airport, NoParkingSlotError
from dcs.triggers import Event, TriggerOnce, TriggerRule from dcs.triggers import Event, TriggerOnce, TriggerRule
@ -88,7 +90,6 @@ from game.theater.controlpoint import (
from game.theater.theatergroundobject import TheaterGroundObject from game.theater.theatergroundobject import TheaterGroundObject
from game.unitmap import UnitMap from game.unitmap import UnitMap
from game.utils import Distance, meters, nautical_miles from game.utils import Distance, meters, nautical_miles
from gen.airsupportgen import AirSupport
from gen.ato import AirTaskingOrder, Package from gen.ato import AirTaskingOrder, Package
from gen.callsigns import create_group_callsign_from_unit from gen.callsigns import create_group_callsign_from_unit
from gen.flights.flight import ( from gen.flights.flight import (
@ -104,9 +105,12 @@ from .flights.flightplan import (
LoiterFlightPlan, LoiterFlightPlan,
PatrollingFlightPlan, PatrollingFlightPlan,
SweepFlightPlan, SweepFlightPlan,
AwacsFlightPlan,
) )
from .flights.traveltime import GroundSpeed, TotEstimator from .flights.traveltime import GroundSpeed, TotEstimator
from .naming import namegen from .naming import namegen
from .airsupportgen import AirSupport, AwacsInfo
from .callsigns import callsign_for_support_unit
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
@ -666,6 +670,7 @@ class AircraftConflictGenerator:
game: Game, game: Game,
radio_registry: RadioRegistry, radio_registry: RadioRegistry,
unit_map: UnitMap, unit_map: UnitMap,
air_support: AirSupport,
) -> None: ) -> None:
self.m = mission self.m = mission
self.game = game self.game = game
@ -673,6 +678,7 @@ class AircraftConflictGenerator:
self.radio_registry = radio_registry self.radio_registry = radio_registry
self.unit_map = unit_map self.unit_map = unit_map
self.flights: List[FlightData] = [] self.flights: List[FlightData] = []
self.air_support = air_support
@cached_property @cached_property
def use_client(self) -> bool: def use_client(self) -> bool:
@ -787,7 +793,10 @@ class AircraftConflictGenerator:
OptReactOnThreat(OptReactOnThreat.Values.EvadeFire) OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)
) )
channel = self.get_intra_flight_channel(unit_type) if flight.flight_type == FlightType.AEWC:
channel = self.radio_registry.alloc_uhf()
else:
channel = self.get_intra_flight_channel(unit_type)
group.set_frequency(channel.mhz) group.set_frequency(channel.mhz)
divert = None divert = None
@ -824,6 +833,20 @@ class AircraftConflictGenerator:
if unit_type in [Su_33, C_101EB, C_101CC]: if unit_type in [Su_33, C_101EB, C_101CC]:
self.set_reduced_fuel(flight, group, unit_type) self.set_reduced_fuel(flight, group, unit_type)
if isinstance(flight.flight_plan, AwacsFlightPlan):
callsign = callsign_for_support_unit(group)
self.air_support.awacs.append(
AwacsInfo(
dcsGroupName=str(group.name),
callsign=callsign,
freq=channel,
depature_location=flight.departure.name,
end_time=flight.flight_plan.mission_departure_time,
start_time=flight.flight_plan.mission_start_time,
)
)
def _generate_at_airport( def _generate_at_airport(
self, self,
name: str, name: str,
@ -1356,7 +1379,16 @@ class AircraftConflictGenerator:
dynamic_runways: Dict[str, RunwayData], dynamic_runways: Dict[str, RunwayData],
) -> None: ) -> None:
group.task = AWACS.name group.task = AWACS.name
if not isinstance(flight.flight_plan, AwacsFlightPlan):
logging.error(
f"Cannot configure AEW&C tasks for {flight} because it does not have an AEW&C flight plan."
)
return
self._setup_group(group, AWACS, package, flight, dynamic_runways) self._setup_group(group, AWACS, package, flight, dynamic_runways)
# Awacs task action
self.configure_behavior( self.configure_behavior(
group, group,
react_on_threat=OptReactOnThreat.Values.EvadeFire, react_on_threat=OptReactOnThreat.Values.EvadeFire,
@ -1364,6 +1396,8 @@ class AircraftConflictGenerator:
restrict_jettison=True, restrict_jettison=True,
) )
group.points[0].tasks.append(AWACSTaskAction())
def configure_escort( def configure_escort(
self, self,
group: FlyingGroup, group: FlyingGroup,

View File

@ -1,6 +1,7 @@
import logging import logging
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import List, Type, Tuple from datetime import timedelta
from typing import List, Type, Tuple, Optional
from dcs.mission import Mission, StartType from dcs.mission import Mission, StartType
from dcs.planes import IL_78M, KC130, KC135MPRS, KC_135 from dcs.planes import IL_78M, KC130, KC135MPRS, KC_135
@ -37,6 +38,9 @@ class AwacsInfo:
dcsGroupName: str dcsGroupName: str
callsign: str callsign: str
freq: RadioFrequency freq: RadioFrequency
depature_location: Optional[str]
start_time: Optional[timedelta]
end_time: Optional[timedelta]
@dataclass @dataclass
@ -192,9 +196,12 @@ class AirSupportConflictGenerator:
self.air_support.awacs.append( self.air_support.awacs.append(
AwacsInfo( AwacsInfo(
str(awacs_flight.name), dcsGroupName=str(awacs_flight.name),
callsign_for_support_unit(awacs_flight), callsign=callsign_for_support_unit(awacs_flight),
freq, freq=freq,
depature_location=None,
start_time=None,
end_time=None,
) )
) )
else: else:

View File

@ -20,6 +20,7 @@ from .ground_forces.combat_stance import CombatStance
from .radios import RadioFrequency from .radios import RadioFrequency
from .runways import RunwayData from .runways import RunwayData
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game

View File

@ -713,6 +713,10 @@ class AwacsFlightPlan(LoiterFlightPlan):
if self.divert is not None: if self.divert is not None:
yield self.divert yield self.divert
@property
def mission_start_time(self) -> Optional[timedelta]:
return self.takeoff_time()
def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]: def tot_for_waypoint(self, waypoint: FlightWaypoint) -> Optional[timedelta]:
if waypoint == self.hold: if waypoint == self.hold:
return self.package.time_over_target return self.package.time_over_target

View File

@ -41,6 +41,7 @@ from .flights.flight import FlightWaypoint, FlightWaypointType
from .radios import RadioFrequency from .radios import RadioFrequency
from .runways import RunwayData from .runways import RunwayData
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
@ -285,6 +286,34 @@ class BriefingPage(KneeboardPage):
["Bingo", "Joker"], ["Bingo", "Joker"],
) )
# AEW&C
writer.heading("AEW&C")
aewc_ladder = []
for single_aewc in self.awacs:
if single_aewc.depature_location is None:
dep = "-"
arr = "-"
else:
dep = self._format_time(single_aewc.start_time)
arr = self._format_time(single_aewc.end_time)
aewc_ladder.append(
[
str(single_aewc.callsign),
str(single_aewc.freq),
str(single_aewc.depature_location),
str(dep),
str(arr),
]
)
writer.table(
aewc_ladder,
headers=["Callsign", "FREQ", "Depature", "ETD", "ETA"],
)
# Package Section # Package Section
writer.heading("Comm ladder") writer.heading("Comm ladder")
comm_ladder = [] comm_ladder = []
@ -293,10 +322,6 @@ class BriefingPage(KneeboardPage):
[comm.name, "", "", "", self.format_frequency(comm.freq)] [comm.name, "", "", "", self.format_frequency(comm.freq)]
) )
for a in self.awacs:
comm_ladder.append(
[a.callsign, "AWACS", "", "", self.format_frequency(a.freq)]
)
for tanker in self.tankers: for tanker in self.tankers:
comm_ladder.append( comm_ladder.append(
[ [
@ -365,6 +390,12 @@ class BriefingPage(KneeboardPage):
channel_name = namer.channel_name(channel.radio_id, channel.channel) channel_name = namer.channel_name(channel.radio_id, channel.channel)
return f"{channel_name} {frequency}" return f"{channel_name} {frequency}"
def _format_time(self, time: Optional[datetime.timedelta]) -> str:
if time is None:
return ""
local_time = self.start_time + time
return local_time.strftime(f"%H:%M:%S")
class KneeboardGenerator(MissionInfoGenerator): class KneeboardGenerator(MissionInfoGenerator):
"""Creates kneeboard pages for each client flight in the mission.""" """Creates kneeboard pages for each client flight in the mission."""