mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +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 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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user