mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Differentiate BARCAP and TARCAP.
Previously the only difference between these was the objective type: TARCAP was for front lines and BARCAP was for everything else. Now BARCAP is for friendly areas and TARCAP is for enemy areas. The practical difference between the two is that a TARCAP package is like the old front line CAP in that it will adjust its patrol time to match the package if it can, and it will also arrive two minutes ahead of the rest of the package to clear the area if needed.
This commit is contained in:
parent
8eef1eaa7c
commit
28e00055ab
@ -2,6 +2,7 @@
|
||||
|
||||
# Features/Improvements
|
||||
* **[Flight Planner]** Added fighter sweep missions.
|
||||
* **[Flight Planner]** Differentiated BARCAP and TARCAP. TARCAP is now for hostile areas and will arrive before the package.
|
||||
|
||||
# 2.2.1
|
||||
|
||||
|
||||
@ -496,7 +496,11 @@ class CoalitionMissionPlanner:
|
||||
error = random.randint(-margin, margin)
|
||||
yield timedelta(minutes=max(0, time + error))
|
||||
|
||||
dca_types = (FlightType.BARCAP, FlightType.INTERCEPTION)
|
||||
dca_types = {
|
||||
FlightType.BARCAP,
|
||||
FlightType.INTERCEPTION,
|
||||
FlightType.TARCAP,
|
||||
}
|
||||
|
||||
non_dca_packages = [p for p in self.ato.packages if
|
||||
p.primary_task not in dca_types]
|
||||
|
||||
@ -340,9 +340,10 @@ class CasFlightPlan(PatrollingFlightPlan):
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FrontLineCapFlightPlan(PatrollingFlightPlan):
|
||||
class TarCapFlightPlan(PatrollingFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
lead_time: timedelta
|
||||
|
||||
@property
|
||||
def waypoints(self) -> List[FlightWaypoint]:
|
||||
@ -353,6 +354,10 @@ class FrontLineCapFlightPlan(PatrollingFlightPlan):
|
||||
self.land,
|
||||
]
|
||||
|
||||
@property
|
||||
def tot_offset(self) -> timedelta:
|
||||
return -self.lead_time
|
||||
|
||||
def depart_time_for_waypoint(
|
||||
self, waypoint: FlightWaypoint) -> Optional[timedelta]:
|
||||
if waypoint == self.patrol_end:
|
||||
@ -363,8 +368,8 @@ class FrontLineCapFlightPlan(PatrollingFlightPlan):
|
||||
def patrol_start_time(self) -> timedelta:
|
||||
start = self.package.escort_start_time
|
||||
if start is not None:
|
||||
return start
|
||||
return super().patrol_start_time
|
||||
return start + self.tot_offset
|
||||
return super().patrol_start_time + self.tot_offset
|
||||
|
||||
@property
|
||||
def patrol_end_time(self) -> timedelta:
|
||||
@ -374,6 +379,10 @@ class FrontLineCapFlightPlan(PatrollingFlightPlan):
|
||||
return super().patrol_end_time
|
||||
|
||||
|
||||
# TODO: Remove when breaking save compat.
|
||||
FrontLineCapFlightPlan = TarCapFlightPlan
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StrikeFlightPlan(FormationFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
@ -635,7 +644,7 @@ class FlightPlanBuilder:
|
||||
elif task == FlightType.SWEEP:
|
||||
return self.generate_sweep(flight)
|
||||
elif task == FlightType.TARCAP:
|
||||
return self.generate_frontline_cap(flight)
|
||||
return self.generate_tarcap(flight)
|
||||
elif task == FlightType.TROOP_TRANSPORT:
|
||||
logging.error(
|
||||
"Troop transport flight plan generation not implemented"
|
||||
@ -704,47 +713,12 @@ class FlightPlanBuilder:
|
||||
if isinstance(location, FrontLine):
|
||||
raise InvalidObjectiveLocation(flight.flight_type, location)
|
||||
|
||||
start, end = self.racetrack_for_objective(location)
|
||||
patrol_alt = random.randint(
|
||||
self.doctrine.min_patrol_altitude,
|
||||
self.doctrine.max_patrol_altitude
|
||||
)
|
||||
|
||||
closest_cache = ObjectiveDistanceCache.get_closest_airfields(location)
|
||||
for airfield in closest_cache.closest_airfields:
|
||||
# If the mission is a BARCAP of an enemy airfield, find the *next*
|
||||
# closest enemy airfield.
|
||||
if airfield == self.package.target:
|
||||
continue
|
||||
if airfield.captured != self.is_player:
|
||||
closest_airfield = airfield
|
||||
break
|
||||
else:
|
||||
raise PlanningError("Could not find any enemy airfields")
|
||||
|
||||
heading = location.position.heading_between_point(
|
||||
closest_airfield.position
|
||||
)
|
||||
|
||||
min_distance_from_enemy = nm_to_meter(20)
|
||||
distance_to_airfield = int(closest_airfield.position.distance_to_point(
|
||||
self.package.target.position
|
||||
))
|
||||
distance_to_no_fly = distance_to_airfield - min_distance_from_enemy
|
||||
min_cap_distance = min(self.doctrine.cap_min_distance_from_cp,
|
||||
distance_to_no_fly)
|
||||
max_cap_distance = min(self.doctrine.cap_max_distance_from_cp,
|
||||
distance_to_no_fly)
|
||||
|
||||
end = location.position.point_from_heading(
|
||||
heading,
|
||||
random.randint(min_cap_distance, max_cap_distance)
|
||||
)
|
||||
diameter = random.randint(
|
||||
self.doctrine.cap_min_track_length,
|
||||
self.doctrine.cap_max_track_length
|
||||
)
|
||||
start = end.point_from_heading(heading - 180, diameter)
|
||||
|
||||
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||
start, end = builder.race_track(start, end, patrol_alt)
|
||||
descent, land = builder.rtb(flight.from_cp)
|
||||
@ -788,20 +762,48 @@ class FlightPlanBuilder:
|
||||
land=land
|
||||
)
|
||||
|
||||
def generate_frontline_cap(self, flight: Flight) -> FrontLineCapFlightPlan:
|
||||
"""Generate a CAP flight plan for the given front line.
|
||||
def racetrack_for_objective(self,
|
||||
location: MissionTarget) -> Tuple[Point, Point]:
|
||||
closest_cache = ObjectiveDistanceCache.get_closest_airfields(location)
|
||||
for airfield in closest_cache.closest_airfields:
|
||||
# If the mission is a BARCAP of an enemy airfield, find the *next*
|
||||
# closest enemy airfield.
|
||||
if airfield == self.package.target:
|
||||
continue
|
||||
if airfield.captured != self.is_player:
|
||||
closest_airfield = airfield
|
||||
break
|
||||
else:
|
||||
raise PlanningError("Could not find any enemy airfields")
|
||||
|
||||
Args:
|
||||
flight: The flight to generate the flight plan for.
|
||||
"""
|
||||
location = self.package.target
|
||||
heading = location.position.heading_between_point(
|
||||
closest_airfield.position
|
||||
)
|
||||
|
||||
if not isinstance(location, FrontLine):
|
||||
raise InvalidObjectiveLocation(flight.flight_type, location)
|
||||
min_distance_from_enemy = nm_to_meter(20)
|
||||
distance_to_airfield = int(closest_airfield.position.distance_to_point(
|
||||
self.package.target.position
|
||||
))
|
||||
distance_to_no_fly = distance_to_airfield - min_distance_from_enemy
|
||||
min_cap_distance = min(self.doctrine.cap_min_distance_from_cp,
|
||||
distance_to_no_fly)
|
||||
max_cap_distance = min(self.doctrine.cap_max_distance_from_cp,
|
||||
distance_to_no_fly)
|
||||
|
||||
ally_cp, enemy_cp = location.control_points
|
||||
patrol_alt = random.randint(self.doctrine.min_patrol_altitude,
|
||||
self.doctrine.max_patrol_altitude)
|
||||
end = location.position.point_from_heading(
|
||||
heading,
|
||||
random.randint(min_cap_distance, max_cap_distance)
|
||||
)
|
||||
diameter = random.randint(
|
||||
self.doctrine.cap_min_track_length,
|
||||
self.doctrine.cap_max_track_length
|
||||
)
|
||||
start = end.point_from_heading(heading - 180, diameter)
|
||||
return start, end
|
||||
|
||||
def racetrack_for_frontline(self,
|
||||
front_line: FrontLine) -> Tuple[Point, Point]:
|
||||
ally_cp, enemy_cp = front_line.control_points
|
||||
|
||||
# Find targets waypoints
|
||||
ingress, heading, distance = Conflict.frontline_vector(
|
||||
@ -822,14 +824,33 @@ class FlightPlanBuilder:
|
||||
orbit0p = orbit_center.point_from_heading(heading, radius)
|
||||
orbit1p = orbit_center.point_from_heading(heading + 180, radius)
|
||||
|
||||
return orbit0p, orbit1p
|
||||
|
||||
def generate_tarcap(self, flight: Flight) -> TarCapFlightPlan:
|
||||
"""Generate a CAP flight plan for the given front line.
|
||||
|
||||
Args:
|
||||
flight: The flight to generate the flight plan for.
|
||||
"""
|
||||
location = self.package.target
|
||||
|
||||
patrol_alt = random.randint(self.doctrine.min_patrol_altitude,
|
||||
self.doctrine.max_patrol_altitude)
|
||||
|
||||
# Create points
|
||||
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine)
|
||||
|
||||
if isinstance(location, FrontLine):
|
||||
orbit0p, orbit1p = self.racetrack_for_frontline(location)
|
||||
else:
|
||||
orbit0p, orbit1p = self.racetrack_for_objective(location)
|
||||
|
||||
start, end = builder.race_track(orbit0p, orbit1p, patrol_alt)
|
||||
descent, land = builder.rtb(flight.from_cp)
|
||||
return FrontLineCapFlightPlan(
|
||||
return TarCapFlightPlan(
|
||||
package=self.package,
|
||||
flight=flight,
|
||||
lead_time=timedelta(minutes=2),
|
||||
# Note that this duration only has an effect if there are no
|
||||
# flights in the package that have requested escort. If the package
|
||||
# requests an escort the CAP flight will remain on station for the
|
||||
|
||||
@ -18,6 +18,7 @@ class QFlightTypeComboBox(QComboBox):
|
||||
"""Combo box for selecting a flight task type."""
|
||||
|
||||
COMMON_ENEMY_MISSIONS = [
|
||||
FlightType.TARCAP,
|
||||
FlightType.ESCORT,
|
||||
FlightType.SEAD,
|
||||
FlightType.DEAD,
|
||||
@ -50,7 +51,6 @@ class QFlightTypeComboBox(QComboBox):
|
||||
]
|
||||
|
||||
ENEMY_AIRBASE_MISSIONS = [
|
||||
FlightType.BARCAP,
|
||||
# TODO: FlightType.STRIKE
|
||||
] + COMMON_ENEMY_MISSIONS
|
||||
|
||||
@ -60,13 +60,11 @@ class QFlightTypeComboBox(QComboBox):
|
||||
] + COMMON_FRIENDLY_MISSIONS
|
||||
|
||||
ENEMY_GROUND_OBJECT_MISSIONS = [
|
||||
FlightType.BARCAP,
|
||||
FlightType.STRIKE,
|
||||
] + COMMON_ENEMY_MISSIONS
|
||||
|
||||
FRONT_LINE_MISSIONS = [
|
||||
FlightType.CAS,
|
||||
FlightType.TARCAP,
|
||||
# TODO: FlightType.TROOP_TRANSPORT
|
||||
# TODO: FlightType.EVAC
|
||||
] + COMMON_ENEMY_MISSIONS
|
||||
|
||||
@ -149,10 +149,10 @@ class QFlightWaypointTab(QFrame):
|
||||
# departs, whereas BARCAP usually isn't part of a strike package and
|
||||
# has a fixed mission time.
|
||||
if task == FlightType.CAP:
|
||||
if isinstance(self.package.target, FrontLine):
|
||||
task = FlightType.TARCAP
|
||||
else:
|
||||
if self.package.target.is_friendly(to_player=True):
|
||||
task = FlightType.BARCAP
|
||||
else:
|
||||
task = FlightType.TARCAP
|
||||
self.flight.flight_type = task
|
||||
self.planner.populate_flight_plan(self.flight)
|
||||
self.flight_waypoint_list.update_list()
|
||||
|
||||
@ -42,3 +42,6 @@ class FrontLine(MissionTarget):
|
||||
def control_points(self) -> Tuple[ControlPoint, ControlPoint]:
|
||||
"""Returns a tuple of the two control points."""
|
||||
return self.control_point_a, self.control_point_b
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
return False
|
||||
|
||||
@ -17,3 +17,7 @@ class MissionTarget:
|
||||
def distance_to(self, other: MissionTarget) -> int:
|
||||
"""Computes the distance to the given mission target."""
|
||||
return self.position.distance_to_point(other.position)
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
"""Returns True if the objective is in friendly territory."""
|
||||
raise NotImplementedError
|
||||
|
||||
@ -113,6 +113,9 @@ class TheaterGroundObject(MissionTarget):
|
||||
def faction_color(self) -> str:
|
||||
return "BLUE" if self.control_point.captured else "RED"
|
||||
|
||||
def is_friendly(self, to_player: bool) -> bool:
|
||||
return not self.control_point.is_friendly(to_player)
|
||||
|
||||
|
||||
class BuildingGroundObject(TheaterGroundObject):
|
||||
def __init__(self, name: str, category: str, group_id: int, object_id: int,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user