Add SEAD/DEAD target info kneeboard page.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/965
This commit is contained in:
Dan Albert 2021-05-20 00:13:49 -07:00
parent f1adcd1836
commit 8274e68846
2 changed files with 110 additions and 19 deletions

40
game/data/alic.py Normal file
View File

@ -0,0 +1,40 @@
from dcs.unit import Unit
from dcs.vehicles import AirDefence
class AlicCodes:
CODES = {
AirDefence.EWR_1L13.id: 101,
AirDefence.EWR_55G6.id: 102,
AirDefence.SAM_SA_10_S_300_Grumble_Clam_Shell_SR.id: 103,
AirDefence.SAM_SA_10_S_300_Grumble_Big_Bird_SR.id: 104,
AirDefence.SAM_SA_11_Buk_Gadfly_Snow_Drift_SR.id: 107,
AirDefence.SAM_SA_6_Kub_Long_Track_STR.id: 108,
AirDefence.MCC_SR_Sborka_Dog_Ear_SR.id: 109,
AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR.id: 110,
AirDefence.SAM_SA_11_Buk_Gadfly_Fire_Dome_TEL.id: 115,
AirDefence.SAM_SA_8_Osa_Gecko_TEL.id: 117,
AirDefence.SAM_SA_13_Strela_10M3_Gopher_TEL.id: 118,
AirDefence.SAM_SA_15_Tor_Gauntlet.id: 119,
AirDefence.SAM_SA_19_Tunguska_Grison.id: 120,
AirDefence.SPAAA_ZSU_23_4_Shilka_Gun_Dish.id: 121,
AirDefence.SAM_P19_Flat_Face_SR__SA_2_3.id: 122,
AirDefence.SAM_SA_3_S_125_Low_Blow_TR.id: 123,
AirDefence.SAM_Rapier_Blindfire_TR.id: 124,
AirDefence.SAM_Rapier_LN.id: 125,
AirDefence.SAM_SA_2_S_75_Fan_Song_TR.id: 126,
AirDefence.HQ_7_Self_Propelled_LN.id: 127,
AirDefence.HQ_7_Self_Propelled_STR.id: 128,
AirDefence.SAM_Roland_ADS.id: 201,
AirDefence.SAM_Patriot_STR.id: 202,
AirDefence.SAM_Hawk_SR__AN_MPQ_50.id: 203,
AirDefence.SAM_Hawk_TR__AN_MPQ_46.id: 204,
AirDefence.SAM_Roland_EWR.id: 205,
AirDefence.SAM_Hawk_CWAR_AN_MPQ_55.id: 206,
AirDefence.SPAAA_Gepard.id: 207,
AirDefence.SPAAA_Vulcan_M163.id: 208,
}
@classmethod
def code_for(cls, unit: Unit) -> int:
return cls.CODES[unit.type]

View File

@ -30,10 +30,13 @@ from typing import Dict, List, Optional, TYPE_CHECKING, Tuple, Iterator
from PIL import Image, ImageDraw, ImageFont
from dcs.mission import Mission
from dcs.unit import Unit
from dcs.unittype import FlyingType
from tabulate import tabulate
from game.theater import ConflictTheater
from game.data.alic import AlicCodes
from game.db import find_unittype, unit_type_from_name
from game.theater import ConflictTheater, TheaterGroundObject, LatLon
from game.utils import meters
from .aircraft import AIRCRAFT_DATA, FlightData
from .airsupportgen import AwacsInfo, TankerInfo
@ -42,7 +45,6 @@ from .flights.flight import FlightWaypoint, FlightWaypointType, FlightType
from .radios import RadioFrequency
from .runways import RunwayData
if TYPE_CHECKING:
from game import Game
@ -138,6 +140,11 @@ class KneeboardPage:
"""Writes the kneeboard page to the given path."""
raise NotImplementedError
def format_ll(self, ll: LatLon) -> str:
ns = "N" if ll.latitude >= 0 else "S"
ew = "E" if ll.longitude >= 0 else "W"
return f"{ll.latitude:.4}°{ns} {ll.longitude:.4}°{ew}"
@dataclass(frozen=True)
class NumberedWaypoint:
@ -415,21 +422,70 @@ class BriefingPage(KneeboardPage):
return local_time.strftime(f"%H:%M:%S")
class TargetInfoPage(KneeboardPage):
"""A kneeboard page containing target information."""
class SeadTaskPage(KneeboardPage):
"""A kneeboard page containing SEAD/DEAD target information."""
def __init__(
self, flight: FlightData, dark_kneeboard: bool, theater: ConflictTheater
) -> None:
self.flight = flight
self.dark_kneeboard = dark_kneeboard
self.theater = theater
@property
def target_units(self) -> Iterator[Unit]:
if isinstance(self.flight.package.target, TheaterGroundObject):
yield from self.flight.package.target.units
@staticmethod
def alic_for(unit: Unit) -> str:
try:
return str(AlicCodes.code_for(unit))
except KeyError:
return ""
def write(self, path: Path) -> None:
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
if self.flight.custom_name is not None:
custom_name_title = ' ("{}")'.format(self.flight.custom_name)
else:
custom_name_title = ""
task = "DEAD" if self.flight.flight_type == FlightType.DEAD else "SEAD"
writer.title(f"{self.flight.callsign} {task} Target Info{custom_name_title}")
writer.table(
[self.target_info_row(t) for t in self.target_units],
headers=["Description", "ALIC", "Location"],
)
writer.write(path)
def target_info_row(self, unit: Unit) -> List[str]:
ll = self.theater.point_to_ll(unit.position)
unit_type = unit_type_from_name(unit.type)
name = unit.name if unit_type is None else unit_type.name
return [name, self.alic_for(unit), self.format_ll(ll)]
class StrikeTaskPage(KneeboardPage):
"""A kneeboard page containing strike target information."""
def __init__(
self,
flight: FlightData,
targets: List[FlightWaypoint],
dark_kneeboard: bool,
theater: ConflictTheater,
) -> None:
self.flight = flight
self.targets = targets
self.dark_kneeboard = dark_kneeboard
self.theater = theater
@property
def targets(self) -> Iterator[FlightWaypoint]:
for waypoint in self.flight.waypoints:
if waypoint.waypoint_type == FlightWaypointType.TARGET_POINT:
yield waypoint
def write(self, path: Path) -> None:
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
if self.flight.custom_name is not None:
@ -447,7 +503,7 @@ class TargetInfoPage(KneeboardPage):
def target_info_row(self, target: FlightWaypoint) -> List[str]:
ll = self.theater.point_to_ll(target.position)
return [target.pretty_name, f"{ll.latitude} {ll.longitude}"]
return [target.pretty_name, self.format_ll(ll)]
class KneeboardGenerator(MissionInfoGenerator):
@ -491,17 +547,12 @@ class KneeboardGenerator(MissionInfoGenerator):
)
return all_flights
def generate_target_page(self, flight: FlightData) -> Optional[KneeboardPage]:
target_waypoints = [
w
for w in flight.waypoints
if w.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC
]
if not target_waypoints:
return None
return TargetInfoPage(
flight, target_waypoints, self.dark_kneeboard, self.game.theater
)
def generate_task_page(self, flight: FlightData) -> Optional[KneeboardPage]:
if flight.flight_type in (FlightType.DEAD, FlightType.SEAD):
return SeadTaskPage(flight, self.dark_kneeboard, self.game.theater)
elif flight.flight_type is FlightType.STRIKE:
return StrikeTaskPage(flight, self.dark_kneeboard, self.game.theater)
return None
def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
"""Returns a list of kneeboard pages for the given flight."""
@ -517,7 +568,7 @@ class KneeboardGenerator(MissionInfoGenerator):
),
]
if (target_page := self.generate_target_page(flight)) is not None:
if (target_page := self.generate_task_page(flight)) is not None:
pages.append(target_page)
return pages