Merge pull request #128 from DanAlbert/coalesce-target-points

Improve target waypoint behavior in the kneeboard.
This commit is contained in:
C. Perreau 2020-09-11 20:51:52 +02:00 committed by GitHub
commit 98946d0a63
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 208 additions and 64 deletions

View File

@ -372,17 +372,26 @@ class FlightPlanner:
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"])
ingress_point = FlightWaypoint(
FlightWaypointType.INGRESS_STRIKE,
ingress_pos.x,
ingress_pos.y,
self.doctrine["INGRESS_ALT"]
)
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
ingress_point.name = "INGRESS"
ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE
flight.points.append(ingress_point)
if len(location.groups) > 0 and location.dcs_identifier == "AA":
for g in location.groups:
for j, u in enumerate(g.units):
point = FlightWaypoint(u.position.x, u.position.y, 0)
point = FlightWaypoint(
FlightWaypointType.TARGET_POINT,
u.position.x,
u.position.y,
0
)
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
point.name = location.obj_name + "#" + str(j)
@ -398,7 +407,12 @@ class FlightPlanner:
if building.is_dead:
continue
point = FlightWaypoint(building.position.x, building.position.y, 0)
point = FlightWaypoint(
FlightWaypointType.TARGET_POINT,
building.position.x,
building.position.y,
0
)
point.description = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
point.pretty_name = "STRIKE on " + building.obj_name + " " + building.category + " [" + str(building.dcs_identifier) + " ]"
point.name = building.obj_name
@ -406,7 +420,12 @@ class FlightPlanner:
ingress_point.targets.append(building)
flight.points.append(point)
else:
point = FlightWaypoint(location.position.x, location.position.y, 0)
point = FlightWaypoint(
FlightWaypointType.TARGET_GROUP_LOC,
location.position.x,
location.position.y,
0
)
point.description = "STRIKE on " + location.obj_name
point.pretty_name = "STRIKE on " + location.obj_name
point.name = location.obj_name
@ -415,11 +434,15 @@ class FlightPlanner:
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"])
egress_point = FlightWaypoint(
FlightWaypointType.EGRESS,
egress_pos.x,
egress_pos.y,
self.doctrine["EGRESS_ALT"]
)
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS from " + location.obj_name
egress_point.description = "EGRESS from " + location.obj_name
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(flight.from_cp)
@ -454,18 +477,26 @@ class FlightPlanner:
ascend = self.generate_ascend_point(flight.from_cp)
flight.points.append(ascend)
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
orbit0 = FlightWaypoint(
FlightWaypointType.PATROL_TRACK,
orbit0p.x,
orbit0p.y,
patrol_alt
)
orbit0.name = "ORBIT 0"
orbit0.description = "Standby between this point and the next one"
orbit0.pretty_name = "Race-track start"
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
flight.points.append(orbit0)
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
orbit1 = FlightWaypoint(
FlightWaypointType.PATROL,
orbit1p.x,
orbit1p.y,
patrol_alt
)
orbit1.name = "ORBIT 1"
orbit1.description = "Standby between this point and the previous one"
orbit1.pretty_name = "Race-track end"
orbit1.waypoint_type = FlightWaypointType.PATROL
flight.points.append(orbit1)
orbit0.targets.append(for_cp)
@ -512,18 +543,26 @@ class FlightPlanner:
ascend = self.generate_ascend_point(flight.from_cp)
flight.points.append(ascend)
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
orbit0 = FlightWaypoint(
FlightWaypointType.PATROL_TRACK,
orbit0p.x,
orbit0p.y,
patrol_alt
)
orbit0.name = "ORBIT 0"
orbit0.description = "Standby between this point and the next one"
orbit0.pretty_name = "Race-track start"
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
flight.points.append(orbit0)
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
orbit1 = FlightWaypoint(
FlightWaypointType.PATROL,
orbit1p.x,
orbit1p.y,
patrol_alt
)
orbit1.name = "ORBIT 1"
orbit1.description = "Standby between this point and the previous one"
orbit1.pretty_name = "Race-track end"
orbit1.waypoint_type = FlightWaypointType.PATROL
flight.points.append(orbit1)
# Note : Targets of a PATROL TRACK waypoints are the points to be defended
@ -555,49 +594,67 @@ class FlightPlanner:
egress_heading = heading - 180 - 25
ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"])
ingress_point = FlightWaypoint(
FlightWaypointType.INGRESS_SEAD,
ingress_pos.x,
ingress_pos.y,
self.doctrine["INGRESS_ALT"]
)
ingress_point.name = "INGRESS"
ingress_point.pretty_name = "INGRESS on " + location.obj_name
ingress_point.description = "INGRESS on " + location.obj_name
ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD
flight.points.append(ingress_point)
if len(custom_targets) > 0:
for target in custom_targets:
point = FlightWaypoint(target.position.x, target.position.y, 0)
point = FlightWaypoint(
FlightWaypointType.TARGET_POINT,
target.position.x,
target.position.y,
0
)
point.alt_type = "RADIO"
if flight.flight_type == FlightType.DEAD:
point.description = "SEAD on " + target.type
point.pretty_name = "SEAD on " + location.obj_name
point.only_for_player = True
else:
point.description = "DEAD on " + location.obj_name
point.description = "DEAD on " + target.type
point.pretty_name = "DEAD on " + location.obj_name
point.only_for_player = True
else:
point.description = "SEAD on " + location.obj_name
point.pretty_name = "SEAD on " + location.obj_name
point.only_for_player = True
flight.points.append(point)
ingress_point.targets.append(location)
ingress_point.targetGroup = location
flight.points.append(point)
else:
point = FlightWaypoint(location.position.x, location.position.y, 0)
point = FlightWaypoint(
FlightWaypointType.TARGET_GROUP_LOC,
location.position.x,
location.position.y,
0
)
point.alt_type = "RADIO"
if flight.flight_type == FlightType.DEAD:
point.description = "SEAD on " + location.obj_name
point.pretty_name = "SEAD on " + location.obj_name
point.only_for_player = True
else:
point.description = "DEAD on " + location.obj_name
point.pretty_name = "DEAD on " + location.obj_name
point.only_for_player = True
else:
point.description = "SEAD on " + location.obj_name
point.pretty_name = "SEAD on " + location.obj_name
point.only_for_player = True
ingress_point.targets.append(location)
ingress_point.targetGroup = location
flight.points.append(point)
egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"])
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"])
egress_point = FlightWaypoint(
FlightWaypointType.EGRESS,
egress_pos.x,
egress_pos.y,
self.doctrine["EGRESS_ALT"]
)
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS from " + location.obj_name
egress_point.description = "EGRESS from " + location.obj_name
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(flight.from_cp)
@ -628,28 +685,40 @@ class FlightPlanner:
ascend.alt = 500
flight.points.append(ascend)
ingress_point = FlightWaypoint(ingress.x, ingress.y, cap_alt)
ingress_point = FlightWaypoint(
FlightWaypointType.INGRESS_CAS,
ingress.x,
ingress.y,
cap_alt
)
ingress_point.alt_type = "RADIO"
ingress_point.name = "INGRESS"
ingress_point.pretty_name = "INGRESS"
ingress_point.description = "Ingress into CAS area"
ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS
flight.points.append(ingress_point)
center_point = FlightWaypoint(center.x, center.y, cap_alt)
center_point = FlightWaypoint(
FlightWaypointType.CAS,
center.x,
center.y,
cap_alt
)
center_point.alt_type = "RADIO"
center_point.description = "Provide CAS"
center_point.name = "CAS"
center_point.pretty_name = "CAS"
center_point.waypoint_type = FlightWaypointType.CAS
flight.points.append(center_point)
egress_point = FlightWaypoint(egress.x, egress.y, cap_alt)
egress_point = FlightWaypoint(
FlightWaypointType.EGRESS,
egress.x,
egress.y,
cap_alt
)
egress_point.alt_type = "RADIO"
egress_point.description = "Egress from CAS area"
egress_point.name = "EGRESS"
egress_point.pretty_name = "EGRESS"
egress_point.waypoint_type = FlightWaypointType.EGRESS
flight.points.append(egress_point)
descend = self.generate_descend_point(flight.from_cp)
@ -660,7 +729,6 @@ class FlightPlanner:
rtb = self.generate_rtb_waypoint(flight.from_cp)
flight.points.append(rtb)
def generate_ascend_point(self, from_cp):
"""
Generate ascend point
@ -669,15 +737,18 @@ class FlightPlanner:
"""
ascend_heading = from_cp.heading
pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000)
ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, self.doctrine["PATTERN_ALTITUDE"])
ascend = FlightWaypoint(
FlightWaypointType.ASCEND_POINT,
pos_ascend.x,
pos_ascend.y,
self.doctrine["PATTERN_ALTITUDE"]
)
ascend.name = "ASCEND"
ascend.alt_type = "RADIO"
ascend.description = "Ascend"
ascend.pretty_name = "Ascend"
ascend.waypoint_type = FlightWaypointType.ASCEND_POINT
return ascend
def generate_descend_point(self, from_cp):
"""
Generate approach/descend point
@ -686,15 +757,18 @@ class FlightPlanner:
"""
ascend_heading = from_cp.heading
descend = from_cp.position.point_from_heading(ascend_heading - 180, 10000)
descend = FlightWaypoint(descend.x, descend.y, self.doctrine["PATTERN_ALTITUDE"])
descend = FlightWaypoint(
FlightWaypointType.DESCENT_POINT,
descend.x,
descend.y,
self.doctrine["PATTERN_ALTITUDE"]
)
descend.name = "DESCEND"
descend.alt_type = "RADIO"
descend.description = "Descend to pattern alt"
descend.pretty_name = "Descend to pattern alt"
descend.waypoint_type = FlightWaypointType.DESCENT_POINT
return descend
def generate_rtb_waypoint(self, from_cp):
"""
Generate RTB landing point
@ -702,10 +776,14 @@ class FlightPlanner:
:return:
"""
rtb = from_cp.position
rtb = FlightWaypoint(rtb.x, rtb.y, 0)
rtb = FlightWaypoint(
FlightWaypointType.LANDING_POINT,
rtb.x,
rtb.y,
0
)
rtb.name = "LANDING"
rtb.alt_type = "RADIO"
rtb.description = "RTB"
rtb.pretty_name = "RTB"
rtb.waypoint_type = FlightWaypointType.LANDING_POINT
return rtb
return rtb

View File

@ -62,7 +62,9 @@ class PredefinedWaypointCategory(Enum):
class FlightWaypoint:
def __init__(self, x: float, y: float, alt=0):
def __init__(self, waypoint_type: FlightWaypointType, x: float, y: float,
alt: int = 0) -> None:
self.waypoint_type = waypoint_type
self.x = x
self.y = y
self.alt = alt
@ -73,8 +75,7 @@ class FlightWaypoint:
self.targetGroup = None
self.obj_name = ""
self.pretty_name = ""
self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType
self.category = PredefinedWaypointCategory.NOT_PREDEFINED# type: PredefinedWaypointCategory
self.category: PredefinedWaypointCategory = PredefinedWaypointCategory.NOT_PREDEFINED
self.only_for_player = False
self.data = None

View File

@ -23,19 +23,21 @@ only be added per airframe, so PvP missions where each side have the same
aircraft will be able to see the enemy's kneeboard for the same airframe.
"""
from collections import defaultdict
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, List, Optional, Tuple
from PIL import Image, ImageDraw, ImageFont
from tabulate import tabulate
from dcs.mission import Mission
from dcs.unittype import FlyingType
from tabulate import tabulate
from . import units
from .aircraft import FlightData
from .airfields import RunwayData
from .airsupportgen import AwacsInfo, TankerInfo
from .briefinggen import CommInfo, JtacInfo, MissionInfoGenerator
from .flights.flight import FlightWaypoint, FlightWaypointType
from .radios import RadioFrequency
@ -95,6 +97,54 @@ class KneeboardPage:
raise NotImplementedError
@dataclass(frozen=True)
class NumberedWaypoint:
number: int
waypoint: FlightWaypoint
class FlightPlanBuilder:
def __init__(self) -> None:
self.rows: List[List[str]] = []
self.target_points: List[NumberedWaypoint] = []
def add_waypoint(self, waypoint_num: int, waypoint: FlightWaypoint) -> None:
if waypoint.waypoint_type == FlightWaypointType.TARGET_POINT:
self.target_points.append(NumberedWaypoint(waypoint_num, waypoint))
return
if self.target_points:
self.coalesce_target_points()
self.target_points = []
self.add_waypoint_row(NumberedWaypoint(waypoint_num, waypoint))
def coalesce_target_points(self) -> None:
if len(self.target_points) <= 4:
for steerpoint in self.target_points:
self.add_waypoint_row(steerpoint)
return
first_waypoint_num = self.target_points[0].number
last_waypoint_num = self.target_points[-1].number
self.rows.append([
f"{first_waypoint_num}-{last_waypoint_num}",
"Target points",
"0"
])
def add_waypoint_row(self, waypoint: NumberedWaypoint) -> None:
self.rows.append([
waypoint.number,
waypoint.waypoint.pretty_name,
str(int(units.meters_to_feet(waypoint.waypoint.alt)))
])
def build(self) -> List[List[str]]:
return self.rows
class BriefingPage(KneeboardPage):
"""A kneeboard page containing briefing information."""
def __init__(self, flight: FlightData, comms: List[CommInfo],
@ -122,11 +172,11 @@ class BriefingPage(KneeboardPage):
], headers=["", "Airbase", "ATC", "TCN", "I(C)LS", "RWY"])
writer.heading("Flight Plan")
flight_plan = []
flight_plan_builder = FlightPlanBuilder()
for num, waypoint in enumerate(self.flight.waypoints):
alt = int(units.meters_to_feet(waypoint.alt))
flight_plan.append([num, waypoint.pretty_name, str(alt)])
writer.table(flight_plan, headers=["STPT", "Action", "Alt"])
flight_plan_builder.add_waypoint(num, waypoint)
writer.table(flight_plan_builder.build(),
headers=["STPT", "Action", "Alt"])
writer.heading("Comm Ladder")
comms = []

View File

@ -57,13 +57,16 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured]
for ecp in enemy_cp:
pos = Conflict.frontline_position(self.game.theater, cp, ecp)[0]
wpt = FlightWaypoint(pos.x, pos.y, 800)
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
pos.x,
pos.y,
800)
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
wpt.alt_type = "RADIO"
wpt.pretty_name = wpt.name
wpt.description = "Frontline"
wpt.data = [cp, ecp]
wpt.waypoint_type = FlightWaypointType.CUSTOM
wpt.category = PredefinedWaypointCategory.FRONTLINE
i = add_model_item(i, model, wpt.pretty_name, wpt)
@ -72,14 +75,18 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
for ground_object in cp.ground_objects:
if not ground_object.is_dead and not ground_object.dcs_identifier == "AA":
wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0)
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
ground_object.position.x,
ground_object.position.y,
0
)
wpt.alt_type = "RADIO"
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id)
wpt.pretty_name = wpt.name
wpt.obj_name = ground_object.obj_name
wpt.targets.append(ground_object)
wpt.data = ground_object
wpt.waypoint_type = FlightWaypointType.CUSTOM
if cp.captured:
wpt.description = "Friendly Building"
wpt.category = PredefinedWaypointCategory.ALLY_BUILDING
@ -95,7 +102,12 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
if not ground_object.is_dead and ground_object.dcs_identifier == "AA":
for g in ground_object.groups:
for j, u in enumerate(g.units):
wpt = FlightWaypoint(u.position.x, u.position.y, 0)
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
u.position.x,
u.position.y,
0
)
wpt.alt_type = "RADIO"
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
wpt.pretty_name = wpt.name
@ -114,11 +126,15 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
if self.include_airbases:
for cp in self.game.theater.controlpoints:
if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured):
wpt = FlightWaypoint(cp.position.x, cp.position.y, 0)
wpt = FlightWaypoint(
FlightWaypointType.CUSTOM,
cp.position.x,
cp.position.y,
0
)
wpt.alt_type = "RADIO"
wpt.name = cp.name
wpt.data = cp
wpt.waypoint_type = FlightWaypointType.CUSTOM
if cp.captured:
wpt.description = "Position of " + cp.name + " [Friendly Airbase]"
wpt.category = PredefinedWaypointCategory.ALLY_CP
@ -133,7 +149,6 @@ class QPredefinedWaypointSelectionComboBox(QFilteredComboBox):
else:
wpt.pretty_name = cp.name + " (Airbase)"
i = add_model_item(i, model, wpt.pretty_name, wpt)
self.setModel(model)