mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add proof-of-concept target info kneeboard page.
This is extremely rough and just serves as an example of how to use the map projection API.
This commit is contained in:
parent
8a01209ded
commit
2a06a1ffdf
@ -40,6 +40,7 @@ from dcs.unitgroup import (
|
||||
VehicleGroup,
|
||||
)
|
||||
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
|
||||
from pyproj import CRS, Transformer
|
||||
from shapely import geometry, ops
|
||||
|
||||
from gen.flights.flight import FlightType
|
||||
@ -53,6 +54,7 @@ from .controlpoint import (
|
||||
OffMapSpawn,
|
||||
)
|
||||
from .landmap import Landmap, load_landmap, poly_contains
|
||||
from .projections import TransverseMercator
|
||||
from ..point_with_heading import PointWithHeading
|
||||
from ..utils import Distance, meters, nautical_miles, pairwise
|
||||
|
||||
@ -473,6 +475,12 @@ class ReferencePoint:
|
||||
image_coordinates: Point
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LatLon:
|
||||
latitude: float
|
||||
longitude: float
|
||||
|
||||
|
||||
class ConflictTheater:
|
||||
terrain: Terrain
|
||||
|
||||
@ -725,6 +733,22 @@ class ConflictTheater:
|
||||
MizCampaignLoader(directory / miz, t).populate_theater()
|
||||
return t
|
||||
|
||||
@property
|
||||
def projection_parameters(self) -> TransverseMercator:
|
||||
raise NotImplementedError
|
||||
|
||||
def point_to_ll(self, point: Point) -> LatLon:
|
||||
lat, lon = Transformer.from_crs(
|
||||
self.projection_parameters.to_crs(), CRS("WGS84")
|
||||
).transform(point.x, point.y)
|
||||
return LatLon(lat, lon)
|
||||
|
||||
def ll_to_point(self, ll: LatLon) -> Point:
|
||||
x, y = Transformer.from_crs(
|
||||
CRS("WGS84"), self.projection_parameters.to_crs()
|
||||
).transform(ll.latitude, ll.longitude)
|
||||
return Point(x, y)
|
||||
|
||||
|
||||
class CaucasusTheater(ConflictTheater):
|
||||
terrain = caucasus.Caucasus()
|
||||
@ -742,6 +766,12 @@ class CaucasusTheater(ConflictTheater):
|
||||
"night": (0, 5),
|
||||
}
|
||||
|
||||
@property
|
||||
def projection_parameters(self) -> TransverseMercator:
|
||||
from .caucasus import PARAMETERS
|
||||
|
||||
return PARAMETERS
|
||||
|
||||
|
||||
class PersianGulfTheater(ConflictTheater):
|
||||
terrain = persiangulf.PersianGulf()
|
||||
@ -758,6 +788,12 @@ class PersianGulfTheater(ConflictTheater):
|
||||
"night": (0, 5),
|
||||
}
|
||||
|
||||
@property
|
||||
def projection_parameters(self) -> TransverseMercator:
|
||||
from .persiangulf import PARAMETERS
|
||||
|
||||
return PARAMETERS
|
||||
|
||||
|
||||
class NevadaTheater(ConflictTheater):
|
||||
terrain = nevada.Nevada()
|
||||
@ -774,6 +810,12 @@ class NevadaTheater(ConflictTheater):
|
||||
"night": (0, 5),
|
||||
}
|
||||
|
||||
@property
|
||||
def projection_parameters(self) -> TransverseMercator:
|
||||
from .nevada import PARAMETERS
|
||||
|
||||
return PARAMETERS
|
||||
|
||||
|
||||
class NormandyTheater(ConflictTheater):
|
||||
terrain = normandy.Normandy()
|
||||
@ -790,6 +832,12 @@ class NormandyTheater(ConflictTheater):
|
||||
"night": (0, 5),
|
||||
}
|
||||
|
||||
@property
|
||||
def projection_parameters(self) -> TransverseMercator:
|
||||
from .normandy import PARAMETERS
|
||||
|
||||
return PARAMETERS
|
||||
|
||||
|
||||
class TheChannelTheater(ConflictTheater):
|
||||
terrain = thechannel.TheChannel()
|
||||
@ -806,6 +854,12 @@ class TheChannelTheater(ConflictTheater):
|
||||
"night": (0, 5),
|
||||
}
|
||||
|
||||
@property
|
||||
def projection_parameters(self) -> TransverseMercator:
|
||||
from .thechannel import PARAMETERS
|
||||
|
||||
return PARAMETERS
|
||||
|
||||
|
||||
class SyriaTheater(ConflictTheater):
|
||||
terrain = syria.Syria()
|
||||
@ -822,6 +876,12 @@ class SyriaTheater(ConflictTheater):
|
||||
"night": (0, 5),
|
||||
}
|
||||
|
||||
@property
|
||||
def projection_parameters(self) -> TransverseMercator:
|
||||
from .syria import PARAMETERS
|
||||
|
||||
return PARAMETERS
|
||||
|
||||
|
||||
@dataclass
|
||||
class ComplexFrontLine:
|
||||
|
||||
@ -33,6 +33,7 @@ from dcs.mission import Mission
|
||||
from dcs.unittype import FlyingType
|
||||
from tabulate import tabulate
|
||||
|
||||
from game.theater import ConflictTheater
|
||||
from game.utils import meters
|
||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||
from .airsupportgen import AwacsInfo, TankerInfo
|
||||
@ -293,7 +294,6 @@ class BriefingPage(KneeboardPage):
|
||||
headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"],
|
||||
)
|
||||
|
||||
flight_plan_builder
|
||||
writer.table(
|
||||
[
|
||||
[
|
||||
@ -415,6 +415,41 @@ class BriefingPage(KneeboardPage):
|
||||
return local_time.strftime(f"%H:%M:%S")
|
||||
|
||||
|
||||
class TargetInfoPage(KneeboardPage):
|
||||
"""A kneeboard page containing 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
|
||||
|
||||
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 = ""
|
||||
writer.title(f"{self.flight.callsign} Target Info{custom_name_title}")
|
||||
|
||||
writer.table(
|
||||
[self.target_info_row(t) for t in self.targets],
|
||||
headers=["Description", "Location"],
|
||||
)
|
||||
|
||||
writer.write(path)
|
||||
|
||||
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}"]
|
||||
|
||||
|
||||
class KneeboardGenerator(MissionInfoGenerator):
|
||||
"""Creates kneeboard pages for each client flight in the mission."""
|
||||
|
||||
@ -456,9 +491,21 @@ 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_flight_kneeboard(self, flight: FlightData) -> List[KneeboardPage]:
|
||||
"""Returns a list of kneeboard pages for the given flight."""
|
||||
return [
|
||||
pages: List[KneeboardPage] = [
|
||||
BriefingPage(
|
||||
flight,
|
||||
self.comms,
|
||||
@ -469,3 +516,8 @@ class KneeboardGenerator(MissionInfoGenerator):
|
||||
self.dark_kneeboard,
|
||||
),
|
||||
]
|
||||
|
||||
if (target_page := self.generate_target_page(flight)) is not None:
|
||||
pages.append(target_page)
|
||||
|
||||
return pages
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user