mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Improve IP selection near threat zone centers.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2754.
This commit is contained in:
parent
f89ac52bf3
commit
12cdb8646c
@ -209,6 +209,8 @@ BAI/ANTISHIP/DEAD/STRIKE/BARCAP/CAS/OCA/AIR-ASSAULT (main) missions
|
||||
## Features/Improvements
|
||||
|
||||
* **[Engine]** Support for DCS 2.8.6.41363, including F-15E support.
|
||||
* **[Flight Planning]** Improved IP selection for targets that are near the center of a threat zone.
|
||||
* **[Modding]** Factions can now specify the ship type to be used for cargo shipping. The Handy Wind will be used by default, but WW2 factions can pick something more appropriate.
|
||||
|
||||
## Fixes
|
||||
|
||||
|
||||
@ -384,6 +384,8 @@ export type IpZones = {
|
||||
ipBubble: LatLng[][];
|
||||
permissibleZone: LatLng[][];
|
||||
safeZones: LatLng[][][];
|
||||
preferredThreatenedZones: LatLng[][][];
|
||||
tolerableThreatenedLines: LatLng[][];
|
||||
};
|
||||
export type JoinZones = {
|
||||
homeBubble: LatLng[][];
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { useGetDebugIpZonesQuery } from "../../api/liberationApi";
|
||||
import { LayerGroup, Polygon } from "react-leaflet";
|
||||
import { LayerGroup, Polygon, Polyline } from "react-leaflet";
|
||||
|
||||
interface IpZonesProps {
|
||||
flightId: string;
|
||||
@ -56,6 +56,29 @@ function IpZones(props: IpZonesProps) {
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{data.preferredThreatenedZones.map((zone, idx) => {
|
||||
return (
|
||||
<Polygon
|
||||
key={idx}
|
||||
positions={zone}
|
||||
color="#fe7d0a"
|
||||
fillOpacity={0.1}
|
||||
interactive={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{data.tolerableThreatenedLines.map((line, idx) => {
|
||||
return (
|
||||
<Polyline
|
||||
key={idx}
|
||||
positions={line}
|
||||
color="#80BA80"
|
||||
interactive={false}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
import shapely.ops
|
||||
from dcs import Point
|
||||
from shapely.geometry import MultiPolygon, Point as ShapelyPoint
|
||||
from shapely.geometry import MultiPolygon, Point as ShapelyPoint, MultiLineString
|
||||
|
||||
from game.utils import meters, nautical_miles
|
||||
|
||||
@ -90,6 +90,32 @@ class IpZoneGeometry:
|
||||
safe_zones = MultiPolygon([safe_zones])
|
||||
self.safe_zones = safe_zones
|
||||
|
||||
# See explanation where this is used in _unsafe_ip.
|
||||
# https://github.com/dcs-liberation/dcs_liberation/issues/2754
|
||||
preferred_threatened_zone_wiggle_room = nautical_miles(5)
|
||||
threat_buffer_distance = self.permissible_zone.distance(
|
||||
self.threat_zone.boundary
|
||||
)
|
||||
preferred_threatened_zone_mask = self.threat_zone.buffer(
|
||||
-threat_buffer_distance - preferred_threatened_zone_wiggle_room.meters
|
||||
)
|
||||
preferred_threatened_zones = self.threat_zone.difference(
|
||||
preferred_threatened_zone_mask
|
||||
)
|
||||
|
||||
if not isinstance(preferred_threatened_zones, MultiPolygon):
|
||||
preferred_threatened_zones = MultiPolygon([preferred_threatened_zones])
|
||||
self.preferred_threatened_zones = preferred_threatened_zones
|
||||
|
||||
tolerable_threatened_lines = self.preferred_threatened_zones.intersection(
|
||||
self.permissible_zone.boundary
|
||||
)
|
||||
if tolerable_threatened_lines.is_empty:
|
||||
tolerable_threatened_lines = MultiLineString([])
|
||||
elif not isinstance(tolerable_threatened_lines, MultiLineString):
|
||||
tolerable_threatened_lines = MultiLineString([tolerable_threatened_lines])
|
||||
self.tolerable_threatened_lines = tolerable_threatened_lines
|
||||
|
||||
def _unsafe_ip(self) -> ShapelyPoint:
|
||||
unthreatened_home_zone = self.home_bubble.difference(self.threat_zone)
|
||||
if unthreatened_home_zone.is_empty:
|
||||
@ -100,8 +126,28 @@ class IpZoneGeometry:
|
||||
self.permissible_zone, self.threat_zone.boundary
|
||||
)[0]
|
||||
|
||||
# No safe point in the IP zone, but the home zone is safe. Pick the max-
|
||||
# distance IP that's closest to the untreatened home zone.
|
||||
# No safe point in the IP zone, but the home zone is safe. Pick an IP within
|
||||
# both the permissible zone and preferred threatened zone that's as close to the
|
||||
# unthreatened home zone as possible. This should get us a max-range IP that
|
||||
# is roughly as safe as possible without unjustifiably long routes.
|
||||
#
|
||||
# If we do the obvious thing and pick the IP that minimizes threatened travel
|
||||
# time (the IP closest to the threat boundary) and the objective is near the
|
||||
# center of the threat zone (common when there is an airbase covered only by air
|
||||
# defenses with shorter range than the BARCAP zone, and the target is a TGO near
|
||||
# the CP), the IP could be placed such that the flight would fly all the way
|
||||
# around the threat zone just to avoid a few more threatened miles of travel. To
|
||||
# avoid that, we generate a set of preferred threatened areas that offer a
|
||||
# trade-off between travel time and safety.
|
||||
#
|
||||
# https://github.com/dcs-liberation/dcs_liberation/issues/2754
|
||||
if not self.tolerable_threatened_lines.is_empty:
|
||||
return shapely.ops.nearest_points(
|
||||
self.tolerable_threatened_lines, self.home
|
||||
)[0]
|
||||
|
||||
# But if no part of the permissible zone is tolerably threatened, fall back to
|
||||
# the old safety maximizing approach.
|
||||
return shapely.ops.nearest_points(
|
||||
self.permissible_zone, unthreatened_home_zone
|
||||
)[0]
|
||||
|
||||
@ -64,14 +64,16 @@ class IpZonesJs(BaseModel):
|
||||
ipBubble: LeafletPoly = Field(alias="ipBubble")
|
||||
permissibleZone: LeafletPoly = Field(alias="permissibleZone")
|
||||
safeZones: list[LeafletPoly] = Field(alias="safeZones")
|
||||
preferred_threatened_zones: list[LeafletPoly] = Field(
|
||||
alias="preferredThreatenedZones"
|
||||
)
|
||||
tolerable_threatened_lines: list[LeafletLine] = Field(
|
||||
alias="tolerableThreatenedLines"
|
||||
)
|
||||
|
||||
class Config:
|
||||
title = "IpZones"
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> IpZonesJs:
|
||||
return IpZonesJs(homeBubble=[], ipBubble=[], permissibleZone=[], safeZones=[])
|
||||
|
||||
@classmethod
|
||||
def for_flight(cls, flight: Flight, game: Game) -> IpZonesJs:
|
||||
target = flight.package.target
|
||||
@ -84,6 +86,12 @@ class IpZonesJs(BaseModel):
|
||||
geometry.permissible_zone, game.theater
|
||||
),
|
||||
safeZones=ShapelyUtil.polys_to_leaflet(geometry.safe_zones, game.theater),
|
||||
preferredThreatenedZones=ShapelyUtil.polys_to_leaflet(
|
||||
geometry.preferred_threatened_zones, game.theater
|
||||
),
|
||||
tolerableThreatenedLines=ShapelyUtil.lines_to_leaflet(
|
||||
geometry.tolerable_threatened_lines, game.theater
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user