mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add SEAD/DEAD target info kneeboard page.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/965
This commit is contained in:
parent
f1adcd1836
commit
8274e68846
40
game/data/alic.py
Normal file
40
game/data/alic.py
Normal 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]
|
||||||
@ -30,10 +30,13 @@ from typing import Dict, List, Optional, TYPE_CHECKING, Tuple, Iterator
|
|||||||
|
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
from dcs.mission import Mission
|
from dcs.mission import Mission
|
||||||
|
from dcs.unit import Unit
|
||||||
from dcs.unittype import FlyingType
|
from dcs.unittype import FlyingType
|
||||||
from tabulate import tabulate
|
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 game.utils import meters
|
||||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||||
from .airsupportgen import AwacsInfo, TankerInfo
|
from .airsupportgen import AwacsInfo, TankerInfo
|
||||||
@ -42,7 +45,6 @@ from .flights.flight import FlightWaypoint, FlightWaypointType, FlightType
|
|||||||
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
|
||||||
|
|
||||||
@ -138,6 +140,11 @@ class KneeboardPage:
|
|||||||
"""Writes the kneeboard page to the given path."""
|
"""Writes the kneeboard page to the given path."""
|
||||||
raise NotImplementedError
|
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)
|
@dataclass(frozen=True)
|
||||||
class NumberedWaypoint:
|
class NumberedWaypoint:
|
||||||
@ -415,21 +422,70 @@ class BriefingPage(KneeboardPage):
|
|||||||
return local_time.strftime(f"%H:%M:%S")
|
return local_time.strftime(f"%H:%M:%S")
|
||||||
|
|
||||||
|
|
||||||
class TargetInfoPage(KneeboardPage):
|
class SeadTaskPage(KneeboardPage):
|
||||||
"""A kneeboard page containing target information."""
|
"""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__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
flight: FlightData,
|
flight: FlightData,
|
||||||
targets: List[FlightWaypoint],
|
|
||||||
dark_kneeboard: bool,
|
dark_kneeboard: bool,
|
||||||
theater: ConflictTheater,
|
theater: ConflictTheater,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.flight = flight
|
self.flight = flight
|
||||||
self.targets = targets
|
|
||||||
self.dark_kneeboard = dark_kneeboard
|
self.dark_kneeboard = dark_kneeboard
|
||||||
self.theater = theater
|
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:
|
def write(self, path: Path) -> None:
|
||||||
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
|
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
|
||||||
if self.flight.custom_name is not None:
|
if self.flight.custom_name is not None:
|
||||||
@ -447,7 +503,7 @@ class TargetInfoPage(KneeboardPage):
|
|||||||
|
|
||||||
def target_info_row(self, target: FlightWaypoint) -> List[str]:
|
def target_info_row(self, target: FlightWaypoint) -> List[str]:
|
||||||
ll = self.theater.point_to_ll(target.position)
|
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):
|
class KneeboardGenerator(MissionInfoGenerator):
|
||||||
@ -491,17 +547,12 @@ class KneeboardGenerator(MissionInfoGenerator):
|
|||||||
)
|
)
|
||||||
return all_flights
|
return all_flights
|
||||||
|
|
||||||
def generate_target_page(self, flight: FlightData) -> Optional[KneeboardPage]:
|
def generate_task_page(self, flight: FlightData) -> Optional[KneeboardPage]:
|
||||||
target_waypoints = [
|
if flight.flight_type in (FlightType.DEAD, FlightType.SEAD):
|
||||||
w
|
return SeadTaskPage(flight, self.dark_kneeboard, self.game.theater)
|
||||||
for w in flight.waypoints
|
elif flight.flight_type is FlightType.STRIKE:
|
||||||
if w.waypoint_type == FlightWaypointType.TARGET_GROUP_LOC
|
return StrikeTaskPage(flight, self.dark_kneeboard, self.game.theater)
|
||||||
]
|
|
||||||
if not target_waypoints:
|
|
||||||
return None
|
return None
|
||||||
return TargetInfoPage(
|
|
||||||
flight, target_waypoints, self.dark_kneeboard, self.game.theater
|
|
||||||
)
|
|
||||||
|
|
||||||
def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
|
def generate_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
|
||||||
"""Returns a list of kneeboard pages for the given flight."""
|
"""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)
|
pages.append(target_page)
|
||||||
|
|
||||||
return pages
|
return pages
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user