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
|
## Features/Improvements
|
||||||
|
|
||||||
* **[Engine]** Support for DCS 2.8.6.41363, including F-15E support.
|
* **[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
|
## Fixes
|
||||||
|
|
||||||
|
|||||||
@ -384,6 +384,8 @@ export type IpZones = {
|
|||||||
ipBubble: LatLng[][];
|
ipBubble: LatLng[][];
|
||||||
permissibleZone: LatLng[][];
|
permissibleZone: LatLng[][];
|
||||||
safeZones: LatLng[][][];
|
safeZones: LatLng[][][];
|
||||||
|
preferredThreatenedZones: LatLng[][][];
|
||||||
|
tolerableThreatenedLines: LatLng[][];
|
||||||
};
|
};
|
||||||
export type JoinZones = {
|
export type JoinZones = {
|
||||||
homeBubble: LatLng[][];
|
homeBubble: LatLng[][];
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { useGetDebugIpZonesQuery } from "../../api/liberationApi";
|
import { useGetDebugIpZonesQuery } from "../../api/liberationApi";
|
||||||
import { LayerGroup, Polygon } from "react-leaflet";
|
import { LayerGroup, Polygon, Polyline } from "react-leaflet";
|
||||||
|
|
||||||
interface IpZonesProps {
|
interface IpZonesProps {
|
||||||
flightId: string;
|
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
|
import shapely.ops
|
||||||
from dcs import Point
|
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
|
from game.utils import meters, nautical_miles
|
||||||
|
|
||||||
@ -90,6 +90,32 @@ class IpZoneGeometry:
|
|||||||
safe_zones = MultiPolygon([safe_zones])
|
safe_zones = MultiPolygon([safe_zones])
|
||||||
self.safe_zones = 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:
|
def _unsafe_ip(self) -> ShapelyPoint:
|
||||||
unthreatened_home_zone = self.home_bubble.difference(self.threat_zone)
|
unthreatened_home_zone = self.home_bubble.difference(self.threat_zone)
|
||||||
if unthreatened_home_zone.is_empty:
|
if unthreatened_home_zone.is_empty:
|
||||||
@ -100,8 +126,28 @@ class IpZoneGeometry:
|
|||||||
self.permissible_zone, self.threat_zone.boundary
|
self.permissible_zone, self.threat_zone.boundary
|
||||||
)[0]
|
)[0]
|
||||||
|
|
||||||
# No safe point in the IP zone, but the home zone is safe. Pick the max-
|
# No safe point in the IP zone, but the home zone is safe. Pick an IP within
|
||||||
# distance IP that's closest to the untreatened home zone.
|
# 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(
|
return shapely.ops.nearest_points(
|
||||||
self.permissible_zone, unthreatened_home_zone
|
self.permissible_zone, unthreatened_home_zone
|
||||||
)[0]
|
)[0]
|
||||||
|
|||||||
@ -64,14 +64,16 @@ class IpZonesJs(BaseModel):
|
|||||||
ipBubble: LeafletPoly = Field(alias="ipBubble")
|
ipBubble: LeafletPoly = Field(alias="ipBubble")
|
||||||
permissibleZone: LeafletPoly = Field(alias="permissibleZone")
|
permissibleZone: LeafletPoly = Field(alias="permissibleZone")
|
||||||
safeZones: list[LeafletPoly] = Field(alias="safeZones")
|
safeZones: list[LeafletPoly] = Field(alias="safeZones")
|
||||||
|
preferred_threatened_zones: list[LeafletPoly] = Field(
|
||||||
|
alias="preferredThreatenedZones"
|
||||||
|
)
|
||||||
|
tolerable_threatened_lines: list[LeafletLine] = Field(
|
||||||
|
alias="tolerableThreatenedLines"
|
||||||
|
)
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
title = "IpZones"
|
title = "IpZones"
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def empty(cls) -> IpZonesJs:
|
|
||||||
return IpZonesJs(homeBubble=[], ipBubble=[], permissibleZone=[], safeZones=[])
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def for_flight(cls, flight: Flight, game: Game) -> IpZonesJs:
|
def for_flight(cls, flight: Flight, game: Game) -> IpZonesJs:
|
||||||
target = flight.package.target
|
target = flight.package.target
|
||||||
@ -84,6 +86,12 @@ class IpZonesJs(BaseModel):
|
|||||||
geometry.permissible_zone, game.theater
|
geometry.permissible_zone, game.theater
|
||||||
),
|
),
|
||||||
safeZones=ShapelyUtil.polys_to_leaflet(geometry.safe_zones, 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