mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Don't plan BARCAPs so aggressively.
Limit the commit range of a BARCAP to halfway to the closest enemy airbase so that they don't become offensive missions. This has the side effect of largely reducing long retreats to hold points from front line airfields, since the package can get much closer without being at risk of engagement by an enemy BARCAP. Fixes https://github.com/Khopa/dcs_liberation/issues/742.
This commit is contained in:
parent
1a2475dc25
commit
5da4cace94
@ -1,7 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from functools import singledispatchmethod
|
||||
from typing import TYPE_CHECKING, Union
|
||||
from typing import Optional, TYPE_CHECKING, Union
|
||||
|
||||
from dcs.mapping import Point as DcsPoint
|
||||
from shapely.geometry import (
|
||||
@ -13,7 +13,9 @@ from shapely.geometry import (
|
||||
from shapely.geometry.base import BaseGeometry
|
||||
from shapely.ops import nearest_points, unary_union
|
||||
|
||||
from game.utils import nautical_miles
|
||||
from game.theater import ControlPoint
|
||||
from game.utils import Distance, meters, nautical_miles
|
||||
from gen.flights.closestairfields import ObjectiveDistanceCache
|
||||
from gen.flights.flight import Flight
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -78,6 +80,39 @@ class ThreatZones:
|
||||
self.dcs_to_shapely_point(p.position) for p in flight.points
|
||||
)))
|
||||
|
||||
@classmethod
|
||||
def closest_enemy_airbase(cls, location: ControlPoint,
|
||||
max_distance: Distance) -> Optional[ControlPoint]:
|
||||
airfields = ObjectiveDistanceCache.get_closest_airfields(location)
|
||||
for airfield in airfields.airfields_within(max_distance):
|
||||
if airfield.captured != location.captured:
|
||||
return airfield
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def barcap_threat_range(cls, game: Game,
|
||||
control_point: ControlPoint) -> Distance:
|
||||
doctrine = game.faction_for(control_point.captured).doctrine
|
||||
cap_threat_range = (doctrine.cap_max_distance_from_cp +
|
||||
doctrine.cap_engagement_range)
|
||||
opposing_airfield = cls.closest_enemy_airbase(control_point,
|
||||
cap_threat_range * 2)
|
||||
if opposing_airfield is None:
|
||||
return cap_threat_range
|
||||
|
||||
airfield_distance = meters(
|
||||
opposing_airfield.position.distance_to_point(control_point.position)
|
||||
)
|
||||
|
||||
# BARCAPs should not commit further than halfway to the closest enemy
|
||||
# airfield (with some breathing room) to avoid those missions becoming
|
||||
# offensive. For dissimilar doctrines we could weight this so that, as
|
||||
# an example, modern US goes no closer than 70% of the way to the WW2
|
||||
# German base, and the Germans go no closer than 30% of the way to the
|
||||
# US base, but for now equal weighting is fine.
|
||||
max_distance = airfield_distance * 0.45
|
||||
return min(cap_threat_range, max_distance)
|
||||
|
||||
@classmethod
|
||||
def for_faction(cls, game: Game, player: bool) -> ThreatZones:
|
||||
"""Generates the threat zones projected by the given coalition.
|
||||
@ -92,8 +127,6 @@ class ThreatZones:
|
||||
zone belongs to the player, it is the zone that will be avoided by
|
||||
the enemy and vice versa.
|
||||
"""
|
||||
doctrine = game.faction_for(player).doctrine
|
||||
|
||||
airbases = []
|
||||
air_defenses = []
|
||||
for control_point in game.theater.controlpoints:
|
||||
@ -102,8 +135,7 @@ class ThreatZones:
|
||||
if control_point.runway_is_operational():
|
||||
point = ShapelyPoint(control_point.position.x,
|
||||
control_point.position.y)
|
||||
cap_threat_range = (doctrine.cap_max_distance_from_cp +
|
||||
doctrine.cap_engagement_range)
|
||||
cap_threat_range = cls.barcap_threat_range(game, control_point)
|
||||
airbases.append(point.buffer(cap_threat_range.meters))
|
||||
|
||||
for tgo in control_point.ground_objects:
|
||||
|
||||
@ -17,6 +17,7 @@ from typing import Iterator, List, Optional, Set, TYPE_CHECKING, Tuple
|
||||
|
||||
from dcs.mapping import Point
|
||||
from dcs.unit import Unit
|
||||
from shapely.geometry import Point as ShapelyPoint
|
||||
|
||||
from game.data.doctrine import Doctrine
|
||||
from game.theater import (
|
||||
@ -976,7 +977,7 @@ class FlightPlanBuilder:
|
||||
if isinstance(location, FrontLine):
|
||||
raise InvalidObjectiveLocation(flight.flight_type, location)
|
||||
|
||||
start, end = self.racetrack_for_objective(location)
|
||||
start, end = self.racetrack_for_objective(location, barcap=True)
|
||||
patrol_alt = meters(random.randint(
|
||||
int(self.doctrine.min_patrol_altitude.meters),
|
||||
int(self.doctrine.max_patrol_altitude.meters)
|
||||
@ -1037,8 +1038,8 @@ class FlightPlanBuilder:
|
||||
divert=builder.divert(flight.divert)
|
||||
)
|
||||
|
||||
def racetrack_for_objective(self,
|
||||
location: MissionTarget) -> Tuple[Point, Point]:
|
||||
def racetrack_for_objective(self, location: MissionTarget,
|
||||
barcap: bool) -> 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*
|
||||
@ -1055,12 +1056,28 @@ class FlightPlanBuilder:
|
||||
closest_airfield.position
|
||||
)
|
||||
|
||||
min_distance_from_enemy = nautical_miles(20)
|
||||
distance_to_airfield = meters(
|
||||
closest_airfield.position.distance_to_point(
|
||||
self.package.target.position
|
||||
))
|
||||
distance_to_no_fly = distance_to_airfield - min_distance_from_enemy
|
||||
position = ShapelyPoint(self.package.target.position.x,
|
||||
self.package.target.position.y)
|
||||
|
||||
if barcap:
|
||||
# BARCAPs should remain far enough back from the enemy that their
|
||||
# commit range does not enter the enemy's threat zone. Include a 5nm
|
||||
# buffer.
|
||||
distance_to_no_fly = meters(
|
||||
position.distance(self.threat_zones.all)
|
||||
) - self.doctrine.cap_engagement_range - nautical_miles(5)
|
||||
else:
|
||||
# Other race tracks (TARCAPs, currently) just try to keep some
|
||||
# distance from the nearest enemy airbase, but since they are by
|
||||
# definition in enemy territory they can't avoid the threat zone
|
||||
# without being useless.
|
||||
min_distance_from_enemy = nautical_miles(20)
|
||||
distance_to_airfield = meters(
|
||||
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,
|
||||
@ -1125,7 +1142,8 @@ class FlightPlanBuilder:
|
||||
orbit0p, orbit1p = self.racetrack_for_frontline(
|
||||
flight.departure.position, location)
|
||||
else:
|
||||
orbit0p, orbit1p = self.racetrack_for_objective(location)
|
||||
orbit0p, orbit1p = self.racetrack_for_objective(location,
|
||||
barcap=False)
|
||||
|
||||
start, end = builder.race_track(orbit0p, orbit1p, patrol_alt)
|
||||
return TarCapFlightPlan(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user