mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Improve hold/split/join point positioning.
This also removes ascend/descend waypoints. They don't seem to be helping at all. The AI already gets an implicit ascend waypoint (they won't go to waypoint one until they've climbed sufficiently), and forcing unnecessary sharp turns toward the possibly mispredicted ascent direction can mess with the AI. It's also yet another variable to contend with when planning hold points, and hold points do essentially the same thing. Fixes https://github.com/Khopa/dcs_liberation/issues/352. (cherry picked from commit 21cd764f6625db0784059a0b59a1e8a72a7a699d)
This commit is contained in:
parent
8ffbf32677
commit
d9056acc6d
@ -4,6 +4,9 @@
|
||||
* **[Factions]** Added factions : Georgia 2008, USN 1985, France 2005 Frenchpack by HerrTom
|
||||
* **[Factions]** Added map Persian Gulf full by Plob
|
||||
|
||||
## Fixes :
|
||||
* **[Flight Planner]** Hold, join, and split points are planned cautiously near enemy airfields. Ascend/descend points are no longer planned.
|
||||
|
||||
# 2.2.0
|
||||
|
||||
## Features/Improvements :
|
||||
|
||||
@ -16,6 +16,8 @@ class Doctrine:
|
||||
sead_max_range: int
|
||||
|
||||
rendezvous_altitude: int
|
||||
hold_distance: int
|
||||
push_distance: int
|
||||
join_distance: int
|
||||
split_distance: int
|
||||
ingress_egress_distance: int
|
||||
@ -44,6 +46,8 @@ MODERN_DOCTRINE = Doctrine(
|
||||
strike_max_range=1500000,
|
||||
sead_max_range=1500000,
|
||||
rendezvous_altitude=feet_to_meter(25000),
|
||||
hold_distance=nm_to_meter(15),
|
||||
push_distance=nm_to_meter(20),
|
||||
join_distance=nm_to_meter(20),
|
||||
split_distance=nm_to_meter(20),
|
||||
ingress_egress_distance=nm_to_meter(45),
|
||||
@ -69,6 +73,8 @@ COLDWAR_DOCTRINE = Doctrine(
|
||||
strike_max_range=1500000,
|
||||
sead_max_range=1500000,
|
||||
rendezvous_altitude=feet_to_meter(22000),
|
||||
hold_distance=nm_to_meter(10),
|
||||
push_distance=nm_to_meter(10),
|
||||
join_distance=nm_to_meter(10),
|
||||
split_distance=nm_to_meter(10),
|
||||
ingress_egress_distance=nm_to_meter(30),
|
||||
@ -93,6 +99,8 @@ WWII_DOCTRINE = Doctrine(
|
||||
antiship=True,
|
||||
strike_max_range=1500000,
|
||||
sead_max_range=1500000,
|
||||
hold_distance=nm_to_meter(5),
|
||||
push_distance=nm_to_meter(5),
|
||||
join_distance=nm_to_meter(5),
|
||||
split_distance=nm_to_meter(5),
|
||||
rendezvous_altitude=feet_to_meter(10000),
|
||||
|
||||
@ -7,6 +7,7 @@ generating the waypoints for the mission.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from datetime import timedelta
|
||||
from functools import cached_property
|
||||
import logging
|
||||
@ -275,18 +276,14 @@ class PatrollingFlightPlan(FlightPlan):
|
||||
@dataclass(frozen=True)
|
||||
class BarCapFlightPlan(PatrollingFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
ascent: FlightWaypoint
|
||||
descent: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
|
||||
@property
|
||||
def waypoints(self) -> List[FlightWaypoint]:
|
||||
return [
|
||||
self.takeoff,
|
||||
self.ascent,
|
||||
self.patrol_start,
|
||||
self.patrol_end,
|
||||
self.descent,
|
||||
self.land,
|
||||
]
|
||||
|
||||
@ -294,20 +291,16 @@ class BarCapFlightPlan(PatrollingFlightPlan):
|
||||
@dataclass(frozen=True)
|
||||
class CasFlightPlan(PatrollingFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
ascent: FlightWaypoint
|
||||
target: FlightWaypoint
|
||||
descent: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
|
||||
@property
|
||||
def waypoints(self) -> List[FlightWaypoint]:
|
||||
return [
|
||||
self.takeoff,
|
||||
self.ascent,
|
||||
self.patrol_start,
|
||||
self.target,
|
||||
self.patrol_end,
|
||||
self.descent,
|
||||
self.land,
|
||||
]
|
||||
|
||||
@ -321,18 +314,14 @@ class CasFlightPlan(PatrollingFlightPlan):
|
||||
@dataclass(frozen=True)
|
||||
class FrontLineCapFlightPlan(PatrollingFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
ascent: FlightWaypoint
|
||||
descent: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
|
||||
@property
|
||||
def waypoints(self) -> List[FlightWaypoint]:
|
||||
return [
|
||||
self.takeoff,
|
||||
self.ascent,
|
||||
self.patrol_start,
|
||||
self.patrol_end,
|
||||
self.descent,
|
||||
self.land,
|
||||
]
|
||||
|
||||
@ -360,28 +349,24 @@ class FrontLineCapFlightPlan(PatrollingFlightPlan):
|
||||
@dataclass(frozen=True)
|
||||
class StrikeFlightPlan(FormationFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
ascent: FlightWaypoint
|
||||
hold: FlightWaypoint
|
||||
join: FlightWaypoint
|
||||
ingress: FlightWaypoint
|
||||
targets: List[FlightWaypoint]
|
||||
egress: FlightWaypoint
|
||||
split: FlightWaypoint
|
||||
descent: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
|
||||
@property
|
||||
def waypoints(self) -> List[FlightWaypoint]:
|
||||
return [
|
||||
self.takeoff,
|
||||
self.ascent,
|
||||
self.hold,
|
||||
self.join,
|
||||
self.ingress
|
||||
] + self.targets + [
|
||||
self.egress,
|
||||
self.split,
|
||||
self.descent,
|
||||
self.land,
|
||||
]
|
||||
|
||||
@ -681,10 +666,8 @@ class FlightPlanBuilder:
|
||||
flight=flight,
|
||||
patrol_duration=self.doctrine.cap_duration,
|
||||
takeoff=builder.takeoff(flight.from_cp),
|
||||
ascent=builder.ascent(flight.from_cp),
|
||||
patrol_start=start,
|
||||
patrol_end=end,
|
||||
descent=descent,
|
||||
land=land
|
||||
)
|
||||
|
||||
@ -736,10 +719,8 @@ class FlightPlanBuilder:
|
||||
# duration of the escorted mission, or until it is winchester/bingo.
|
||||
patrol_duration=self.doctrine.cap_duration,
|
||||
takeoff=builder.takeoff(flight.from_cp),
|
||||
ascent=builder.ascent(flight.from_cp),
|
||||
patrol_start=start,
|
||||
patrol_end=end,
|
||||
descent=descent,
|
||||
land=land
|
||||
)
|
||||
|
||||
@ -805,14 +786,12 @@ class FlightPlanBuilder:
|
||||
package=self.package,
|
||||
flight=flight,
|
||||
takeoff=builder.takeoff(flight.from_cp),
|
||||
ascent=builder.ascent(flight.from_cp),
|
||||
hold=builder.hold(self._hold_point(flight)),
|
||||
join=builder.join(self.package.waypoints.join),
|
||||
ingress=ingress,
|
||||
targets=[target],
|
||||
egress=egress,
|
||||
split=builder.split(self.package.waypoints.split),
|
||||
descent=descent,
|
||||
land=land
|
||||
)
|
||||
|
||||
@ -842,11 +821,9 @@ class FlightPlanBuilder:
|
||||
flight=flight,
|
||||
patrol_duration=self.doctrine.cas_duration,
|
||||
takeoff=builder.takeoff(flight.from_cp),
|
||||
ascent=builder.ascent(flight.from_cp),
|
||||
patrol_start=builder.ingress_cas(ingress, location),
|
||||
target=builder.cas(center),
|
||||
patrol_end=builder.egress(egress, location),
|
||||
descent=descent,
|
||||
land=land
|
||||
)
|
||||
|
||||
@ -871,12 +848,50 @@ class FlightPlanBuilder:
|
||||
return builder.strike_area(location)
|
||||
|
||||
def _hold_point(self, flight: Flight) -> Point:
|
||||
heading = flight.from_cp.position.heading_between_point(
|
||||
self.package.target.position
|
||||
)
|
||||
return flight.from_cp.position.point_from_heading(
|
||||
heading, nm_to_meter(15)
|
||||
assert self.package.waypoints is not None
|
||||
origin = flight.from_cp.position
|
||||
target = self.package.target.position
|
||||
join = self.package.waypoints.join
|
||||
origin_to_target = origin.distance_to_point(target)
|
||||
join_to_target = join.distance_to_point(target)
|
||||
if origin_to_target < join_to_target:
|
||||
# If the origin airfield is closer to the target than the join
|
||||
# point, plan the hold point such that it retreats from the origin
|
||||
# airfield.
|
||||
return join.point_from_heading(target.heading_between_point(origin),
|
||||
self.doctrine.push_distance)
|
||||
|
||||
heading_to_join = origin.heading_between_point(join)
|
||||
hold_point = origin.point_from_heading(heading_to_join,
|
||||
self.doctrine.push_distance)
|
||||
if hold_point.distance_to_point(join) >= self.doctrine.push_distance:
|
||||
# Hold point is between the origin airfield and the join point and
|
||||
# spaced sufficiently.
|
||||
return hold_point
|
||||
|
||||
# The hold point is between the origin airfield and the join point, but
|
||||
# the distance between the hold point and the join point is too short.
|
||||
# Bend the hold point out to extend the distance while maintaining the
|
||||
# minimum distance from the origin airfield to keep the AI flying
|
||||
# properly.
|
||||
origin_to_join = origin.distance_to_point(join)
|
||||
cos_theta = (
|
||||
(self.doctrine.hold_distance ** 2 +
|
||||
origin_to_join ** 2 -
|
||||
self.doctrine.join_distance ** 2) /
|
||||
(2 * self.doctrine.hold_distance * origin_to_join)
|
||||
)
|
||||
try:
|
||||
theta = math.acos(cos_theta)
|
||||
except ValueError:
|
||||
# No solution that maintains hold and join distances. Extend the
|
||||
# hold point away from the target.
|
||||
return origin.point_from_heading(
|
||||
target.heading_between_point(origin),
|
||||
self.doctrine.hold_distance)
|
||||
|
||||
return origin.point_from_heading(heading_to_join - theta,
|
||||
self.doctrine.hold_distance)
|
||||
|
||||
# TODO: Make a model for the waypoint builder and use that in the UI.
|
||||
def generate_ascend_point(self, flight: Flight,
|
||||
@ -944,23 +959,37 @@ class FlightPlanBuilder:
|
||||
package=self.package,
|
||||
flight=flight,
|
||||
takeoff=builder.takeoff(flight.from_cp),
|
||||
ascent=builder.ascent(flight.from_cp),
|
||||
hold=builder.hold(self._hold_point(flight)),
|
||||
join=builder.join(self.package.waypoints.join),
|
||||
ingress=ingress,
|
||||
targets=target_waypoints,
|
||||
egress=builder.egress(self.package.waypoints.egress, location),
|
||||
split=builder.split(self.package.waypoints.split),
|
||||
descent=descent,
|
||||
land=land
|
||||
)
|
||||
|
||||
def _join_point(self, ingress_point: Point) -> Point:
|
||||
ingress_distance = self._distance_to_package_airfield(ingress_point)
|
||||
if ingress_distance < self.doctrine.join_distance:
|
||||
# If the ingress point is close to the origin, plan the join point
|
||||
# farther back.
|
||||
return ingress_point.point_from_heading(
|
||||
self.package.target.position.heading_between_point(
|
||||
self.package_airfield().position),
|
||||
self.doctrine.join_distance)
|
||||
heading = self._heading_to_package_airfield(ingress_point)
|
||||
return ingress_point.point_from_heading(heading,
|
||||
-self.doctrine.join_distance)
|
||||
|
||||
def _split_point(self, egress_point: Point) -> Point:
|
||||
egress_distance = self._distance_to_package_airfield(egress_point)
|
||||
if egress_distance < self.doctrine.split_distance:
|
||||
# If the ingress point is close to the origin, plan the split point
|
||||
# farther back.
|
||||
return egress_point.point_from_heading(
|
||||
self.package.target.position.heading_between_point(
|
||||
self.package_airfield().position),
|
||||
self.doctrine.split_distance)
|
||||
heading = self._heading_to_package_airfield(egress_point)
|
||||
return egress_point.point_from_heading(heading,
|
||||
-self.doctrine.split_distance)
|
||||
@ -983,6 +1012,9 @@ class FlightPlanBuilder:
|
||||
def _heading_to_package_airfield(self, point: Point) -> int:
|
||||
return self.package_airfield().position.heading_between_point(point)
|
||||
|
||||
def _distance_to_package_airfield(self, point: Point) -> int:
|
||||
return self.package_airfield().position.distance_to_point(point)
|
||||
|
||||
def package_airfield(self) -> ControlPoint:
|
||||
# We'll always have a package, but if this is being planned via the UI
|
||||
# it could be the first flight in the package.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user