mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Improve detection of functional radar SAMs.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1109
This commit is contained in:
parent
eae0d6be94
commit
ddd6e7d18f
@ -22,7 +22,38 @@ from dcs.ships import (
|
|||||||
)
|
)
|
||||||
from dcs.vehicles import AirDefence
|
from dcs.vehicles import AirDefence
|
||||||
|
|
||||||
UNITS_WITH_RADAR = [
|
TELARS = {
|
||||||
|
AirDefence.SAM_SA_19_Tunguska_Grison,
|
||||||
|
AirDefence.SAM_SA_11_Buk_Gadfly_Fire_Dome_TEL,
|
||||||
|
AirDefence.SAM_SA_8_Osa_Gecko_TEL,
|
||||||
|
AirDefence.SAM_SA_15_Tor_Gauntlet,
|
||||||
|
AirDefence.SAM_Roland_ADS,
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACK_RADARS = {
|
||||||
|
AirDefence.SAM_SA_6_Kub_Straight_Flush_STR,
|
||||||
|
AirDefence.SAM_SA_3_S_125_Low_Blow_TR,
|
||||||
|
AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR,
|
||||||
|
AirDefence.SAM_Hawk_TR__AN_MPQ_46,
|
||||||
|
AirDefence.SAM_Patriot_STR,
|
||||||
|
AirDefence.SAM_SA_2_S_75_Fan_Song_TR,
|
||||||
|
AirDefence.SAM_Rapier_Blindfire_TR,
|
||||||
|
AirDefence.HQ_7_Self_Propelled_STR,
|
||||||
|
}
|
||||||
|
|
||||||
|
LAUNCHER_TRACKER_PAIRS = {
|
||||||
|
AirDefence.SAM_SA_6_Kub_Gainful_TEL: AirDefence.SAM_SA_6_Kub_Straight_Flush_STR,
|
||||||
|
AirDefence.SAM_SA_3_S_125_Goa_LN: AirDefence.SAM_SA_3_S_125_Low_Blow_TR,
|
||||||
|
AirDefence.SAM_SA_10_S_300_Grumble_TEL_D: AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR,
|
||||||
|
AirDefence.SAM_SA_10_S_300_Grumble_TEL_C: AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR,
|
||||||
|
AirDefence.SAM_Hawk_LN_M192: AirDefence.SAM_Hawk_TR__AN_MPQ_46,
|
||||||
|
AirDefence.SAM_Patriot_LN: AirDefence.SAM_Patriot_STR,
|
||||||
|
AirDefence.SAM_SA_2_S_75_Guideline_LN: AirDefence.SAM_SA_2_S_75_Fan_Song_TR,
|
||||||
|
AirDefence.SAM_Rapier_LN: AirDefence.SAM_Rapier_Blindfire_TR,
|
||||||
|
AirDefence.HQ_7_Self_Propelled_LN: AirDefence.HQ_7_Self_Propelled_STR,
|
||||||
|
}
|
||||||
|
|
||||||
|
UNITS_WITH_RADAR = {
|
||||||
# Radars
|
# Radars
|
||||||
AirDefence.SAM_SA_19_Tunguska_Grison,
|
AirDefence.SAM_SA_19_Tunguska_Grison,
|
||||||
AirDefence.SAM_SA_11_Buk_Gadfly_Fire_Dome_TEL,
|
AirDefence.SAM_SA_11_Buk_Gadfly_Fire_Dome_TEL,
|
||||||
@ -74,4 +105,4 @@ UNITS_WITH_RADAR = [
|
|||||||
Type_052B_Destroyer,
|
Type_052B_Destroyer,
|
||||||
Type_054A_Frigate,
|
Type_054A_Frigate,
|
||||||
Type_052C_Destroyer,
|
Type_052C_Destroyer,
|
||||||
]
|
}
|
||||||
|
|||||||
@ -8,9 +8,15 @@ from dcs.mapping import Point
|
|||||||
from dcs.triggers import TriggerZone
|
from dcs.triggers import TriggerZone
|
||||||
from dcs.unit import Unit
|
from dcs.unit import Unit
|
||||||
from dcs.unitgroup import Group
|
from dcs.unitgroup import Group
|
||||||
|
from dcs.unittype import VehicleType
|
||||||
|
|
||||||
from .. import db
|
from .. import db
|
||||||
from ..data.radar_db import UNITS_WITH_RADAR
|
from ..data.radar_db import (
|
||||||
|
UNITS_WITH_RADAR,
|
||||||
|
TRACK_RADARS,
|
||||||
|
TELARS,
|
||||||
|
LAUNCHER_TRACKER_PAIRS,
|
||||||
|
)
|
||||||
from ..utils import Distance, meters
|
from ..utils import Distance, meters
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -166,14 +172,7 @@ class TheaterGroundObject(MissionTarget):
|
|||||||
def detection_range(self, group: Group) -> Distance:
|
def detection_range(self, group: Group) -> Distance:
|
||||||
return self._max_range_of_type(group, "detection_range")
|
return self._max_range_of_type(group, "detection_range")
|
||||||
|
|
||||||
def threat_range(self, group: Group) -> Distance:
|
def threat_range(self, group: Group, radar_only: bool = False) -> Distance:
|
||||||
if not self.detection_range(group):
|
|
||||||
# For simple SAMs like shilkas, the unit has both a threat and
|
|
||||||
# detection range. For complex sites like SA-2s, the launcher has a
|
|
||||||
# threat range and the search/track radars have detection ranges. If
|
|
||||||
# the site has no detection range it has no radars and can't fire,
|
|
||||||
# so it's not actually a threat even if it still has launchers.
|
|
||||||
return meters(0)
|
|
||||||
return self._max_range_of_type(group, "threat_range")
|
return self._max_range_of_type(group, "threat_range")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -459,6 +458,38 @@ class SamGroundObject(BaseDefenseGroundObject):
|
|||||||
def might_have_aa(self) -> bool:
|
def might_have_aa(self) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def threat_range(self, group: Group, radar_only: bool = False) -> Distance:
|
||||||
|
max_non_radar = meters(0)
|
||||||
|
live_trs = set()
|
||||||
|
max_telar_range = meters(0)
|
||||||
|
launchers = set()
|
||||||
|
for unit in group.units:
|
||||||
|
unit_type = db.unit_type_from_name(unit.type)
|
||||||
|
if unit_type is None or not issubclass(unit_type, VehicleType):
|
||||||
|
continue
|
||||||
|
if unit_type in TRACK_RADARS:
|
||||||
|
live_trs.add(unit_type)
|
||||||
|
elif unit_type in TELARS:
|
||||||
|
max_telar_range = max(
|
||||||
|
max_telar_range, meters(getattr(unit_type, "threat_range", 0))
|
||||||
|
)
|
||||||
|
elif unit_type in LAUNCHER_TRACKER_PAIRS:
|
||||||
|
launchers.add(unit_type)
|
||||||
|
else:
|
||||||
|
max_non_radar = max(
|
||||||
|
max_non_radar, meters(getattr(unit_type, "threat_range", 0))
|
||||||
|
)
|
||||||
|
max_tel_range = meters(0)
|
||||||
|
for launcher in launchers:
|
||||||
|
if LAUNCHER_TRACKER_PAIRS[launcher] in live_trs:
|
||||||
|
max_tel_range = max(
|
||||||
|
max_tel_range, meters(getattr(launcher, "threat_range"))
|
||||||
|
)
|
||||||
|
if radar_only:
|
||||||
|
return max(max_tel_range, max_telar_range)
|
||||||
|
else:
|
||||||
|
return max(max_tel_range, max_telar_range, max_non_radar)
|
||||||
|
|
||||||
|
|
||||||
class VehicleGroupGroundObject(BaseDefenseGroundObject):
|
class VehicleGroupGroundObject(BaseDefenseGroundObject):
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|||||||
@ -15,7 +15,6 @@ from shapely.ops import nearest_points, unary_union
|
|||||||
|
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from game.utils import Distance, meters, nautical_miles
|
from game.utils import Distance, meters, nautical_miles
|
||||||
from gen import Conflict
|
|
||||||
from gen.flights.closestairfields import ObjectiveDistanceCache
|
from gen.flights.closestairfields import ObjectiveDistanceCache
|
||||||
from gen.flights.flight import Flight
|
from gen.flights.flight import Flight
|
||||||
|
|
||||||
@ -27,9 +26,12 @@ ThreatPoly = Union[MultiPolygon, Polygon]
|
|||||||
|
|
||||||
|
|
||||||
class ThreatZones:
|
class ThreatZones:
|
||||||
def __init__(self, airbases: ThreatPoly, air_defenses: ThreatPoly) -> None:
|
def __init__(
|
||||||
|
self, airbases: ThreatPoly, air_defenses: ThreatPoly, radar_sam_threats
|
||||||
|
) -> None:
|
||||||
self.airbases = airbases
|
self.airbases = airbases
|
||||||
self.air_defenses = air_defenses
|
self.air_defenses = air_defenses
|
||||||
|
self.radar_sam_threats = radar_sam_threats
|
||||||
self.all = unary_union([airbases, air_defenses])
|
self.all = unary_union([airbases, air_defenses])
|
||||||
|
|
||||||
def closest_boundary(self, point: DcsPoint) -> DcsPoint:
|
def closest_boundary(self, point: DcsPoint) -> DcsPoint:
|
||||||
@ -83,6 +85,20 @@ class ThreatZones:
|
|||||||
LineString((self.dcs_to_shapely_point(p.position) for p in flight.points))
|
LineString((self.dcs_to_shapely_point(p.position) for p in flight.points))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@singledispatchmethod
|
||||||
|
def threatened_by_radar_sam(self, target) -> bool:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@threatened_by_radar_sam.register
|
||||||
|
def _threatened_by_radar_sam_geom(self, position: BaseGeometry) -> bool:
|
||||||
|
return self.radar_sam_threats.intersects(position)
|
||||||
|
|
||||||
|
@threatened_by_radar_sam.register
|
||||||
|
def _threatened_by_radar_sam_flight(self, flight: Flight) -> bool:
|
||||||
|
return self.threatened_by_radar_sam(
|
||||||
|
LineString((self.dcs_to_shapely_point(p.position) for p in flight.points))
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def closest_enemy_airbase(
|
def closest_enemy_airbase(
|
||||||
cls, location: ControlPoint, max_distance: Distance
|
cls, location: ControlPoint, max_distance: Distance
|
||||||
@ -134,6 +150,7 @@ class ThreatZones:
|
|||||||
"""
|
"""
|
||||||
air_threats = []
|
air_threats = []
|
||||||
air_defenses = []
|
air_defenses = []
|
||||||
|
radar_sam_threats = []
|
||||||
for control_point in game.theater.controlpoints:
|
for control_point in game.theater.controlpoints:
|
||||||
if control_point.captured != player:
|
if control_point.captured != player:
|
||||||
continue
|
continue
|
||||||
@ -151,9 +168,16 @@ class ThreatZones:
|
|||||||
point = ShapelyPoint(tgo.position.x, tgo.position.y)
|
point = ShapelyPoint(tgo.position.x, tgo.position.y)
|
||||||
threat_zone = point.buffer(threat_range.meters)
|
threat_zone = point.buffer(threat_range.meters)
|
||||||
air_defenses.append(threat_zone)
|
air_defenses.append(threat_zone)
|
||||||
|
radar_threat_range = tgo.threat_range(group, radar_only=True)
|
||||||
|
if radar_threat_range > nautical_miles(3):
|
||||||
|
point = ShapelyPoint(tgo.position.x, tgo.position.y)
|
||||||
|
threat_zone = point.buffer(threat_range.meters)
|
||||||
|
radar_sam_threats.append(threat_zone)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
airbases=unary_union(air_threats), air_defenses=unary_union(air_defenses)
|
airbases=unary_union(air_threats),
|
||||||
|
air_defenses=unary_union(air_defenses),
|
||||||
|
radar_sam_threats=unary_union(radar_sam_threats),
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@ -833,7 +833,7 @@ class CoalitionMissionPlanner:
|
|||||||
for flight in builder.package.flights:
|
for flight in builder.package.flights:
|
||||||
if self.threat_zones.threatened_by_aircraft(flight):
|
if self.threat_zones.threatened_by_aircraft(flight):
|
||||||
threats[EscortType.AirToAir] = True
|
threats[EscortType.AirToAir] = True
|
||||||
if self.threat_zones.threatened_by_air_defense(flight):
|
if self.threat_zones.threatened_by_radar_sam(flight):
|
||||||
threats[EscortType.Sead] = True
|
threats[EscortType.Sead] = True
|
||||||
return threats
|
return threats
|
||||||
|
|
||||||
|
|||||||
@ -538,17 +538,20 @@ class ThreatZonesJs(QObject):
|
|||||||
fullChanged = Signal()
|
fullChanged = Signal()
|
||||||
aircraftChanged = Signal()
|
aircraftChanged = Signal()
|
||||||
airDefensesChanged = Signal()
|
airDefensesChanged = Signal()
|
||||||
|
radarSamsChanged = Signal()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
full: List[List[LeafletLatLon]],
|
full: List[List[LeafletLatLon]],
|
||||||
aircraft: List[List[LeafletLatLon]],
|
aircraft: List[List[LeafletLatLon]],
|
||||||
air_defenses: List[List[LeafletLatLon]],
|
air_defenses: List[List[LeafletLatLon]],
|
||||||
|
radar_sams: List[List[LeafletLatLon]],
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._full = full
|
self._full = full
|
||||||
self._aircraft = aircraft
|
self._aircraft = aircraft
|
||||||
self._air_defenses = air_defenses
|
self._air_defenses = air_defenses
|
||||||
|
self._radar_sams = radar_sams
|
||||||
|
|
||||||
@Property(list, notify=fullChanged)
|
@Property(list, notify=fullChanged)
|
||||||
def full(self) -> List[List[LeafletLatLon]]:
|
def full(self) -> List[List[LeafletLatLon]]:
|
||||||
@ -562,6 +565,10 @@ class ThreatZonesJs(QObject):
|
|||||||
def airDefenses(self) -> List[List[LeafletLatLon]]:
|
def airDefenses(self) -> List[List[LeafletLatLon]]:
|
||||||
return self._air_defenses
|
return self._air_defenses
|
||||||
|
|
||||||
|
@Property(list, notify=radarSamsChanged)
|
||||||
|
def radarSams(self) -> List[List[LeafletLatLon]]:
|
||||||
|
return self._radar_sams
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def polys_to_leaflet(
|
def polys_to_leaflet(
|
||||||
poly: Union[Polygon, MultiPolygon], theater: ConflictTheater
|
poly: Union[Polygon, MultiPolygon], theater: ConflictTheater
|
||||||
@ -578,11 +585,12 @@ class ThreatZonesJs(QObject):
|
|||||||
cls.polys_to_leaflet(zones.all, theater),
|
cls.polys_to_leaflet(zones.all, theater),
|
||||||
cls.polys_to_leaflet(zones.airbases, theater),
|
cls.polys_to_leaflet(zones.airbases, theater),
|
||||||
cls.polys_to_leaflet(zones.air_defenses, theater),
|
cls.polys_to_leaflet(zones.air_defenses, theater),
|
||||||
|
cls.polys_to_leaflet(zones.radar_sam_threats, theater),
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def empty(cls) -> ThreatZonesJs:
|
def empty(cls) -> ThreatZonesJs:
|
||||||
return ThreatZonesJs([], [], [])
|
return ThreatZonesJs([], [], [], [])
|
||||||
|
|
||||||
|
|
||||||
class ThreatZoneContainerJs(QObject):
|
class ThreatZoneContainerJs(QObject):
|
||||||
|
|||||||
@ -163,10 +163,12 @@ const allFlightPlansLayer = L.layerGroup();
|
|||||||
const blueFullThreatZones = L.layerGroup();
|
const blueFullThreatZones = L.layerGroup();
|
||||||
const blueAircraftThreatZones = L.layerGroup();
|
const blueAircraftThreatZones = L.layerGroup();
|
||||||
const blueAirDefenseThreatZones = L.layerGroup();
|
const blueAirDefenseThreatZones = L.layerGroup();
|
||||||
|
const blueRadarSamThreatZones = L.layerGroup();
|
||||||
|
|
||||||
const redFullThreatZones = L.layerGroup();
|
const redFullThreatZones = L.layerGroup();
|
||||||
const redAircraftThreatZones = L.layerGroup();
|
const redAircraftThreatZones = L.layerGroup();
|
||||||
const redAirDefenseThreatZones = L.layerGroup();
|
const redAirDefenseThreatZones = L.layerGroup();
|
||||||
|
const redRadarSamThreatZones = L.layerGroup();
|
||||||
|
|
||||||
L.control
|
L.control
|
||||||
.groupedLayers(
|
.groupedLayers(
|
||||||
@ -198,12 +200,14 @@ L.control
|
|||||||
Full: blueFullThreatZones,
|
Full: blueFullThreatZones,
|
||||||
Aircraft: blueAircraftThreatZones,
|
Aircraft: blueAircraftThreatZones,
|
||||||
"Air Defenses": blueAirDefenseThreatZones,
|
"Air Defenses": blueAirDefenseThreatZones,
|
||||||
|
"Radar SAMs": blueRadarSamThreatZones,
|
||||||
},
|
},
|
||||||
"Red Threat Zones": {
|
"Red Threat Zones": {
|
||||||
Hide: L.layerGroup().addTo(map),
|
Hide: L.layerGroup().addTo(map),
|
||||||
Full: redFullThreatZones,
|
Full: redFullThreatZones,
|
||||||
Aircraft: redAircraftThreatZones,
|
Aircraft: redAircraftThreatZones,
|
||||||
"Air Defenses": redAirDefenseThreatZones,
|
"Air Defenses": redAirDefenseThreatZones,
|
||||||
|
"Radar SAMs": redRadarSamThreatZones,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -735,9 +739,11 @@ function drawThreatZones() {
|
|||||||
blueFullThreatZones.clearLayers();
|
blueFullThreatZones.clearLayers();
|
||||||
blueAircraftThreatZones.clearLayers();
|
blueAircraftThreatZones.clearLayers();
|
||||||
blueAirDefenseThreatZones.clearLayers();
|
blueAirDefenseThreatZones.clearLayers();
|
||||||
|
blueRadarSamThreatZones.clearLayers();
|
||||||
redFullThreatZones.clearLayers();
|
redFullThreatZones.clearLayers();
|
||||||
redAircraftThreatZones.clearLayers();
|
redAircraftThreatZones.clearLayers();
|
||||||
redAirDefenseThreatZones.clearLayers();
|
redAirDefenseThreatZones.clearLayers();
|
||||||
|
redRadarSamThreatZones.clearLayers();
|
||||||
|
|
||||||
_drawThreatZones(game.threatZones.blue.full, blueFullThreatZones, true);
|
_drawThreatZones(game.threatZones.blue.full, blueFullThreatZones, true);
|
||||||
_drawThreatZones(
|
_drawThreatZones(
|
||||||
@ -750,6 +756,11 @@ function drawThreatZones() {
|
|||||||
blueAirDefenseThreatZones,
|
blueAirDefenseThreatZones,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
_drawThreatZones(
|
||||||
|
game.threatZones.blue.radarSams,
|
||||||
|
blueRadarSamThreatZones,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
_drawThreatZones(game.threatZones.red.full, redFullThreatZones, false);
|
_drawThreatZones(game.threatZones.red.full, redFullThreatZones, false);
|
||||||
_drawThreatZones(
|
_drawThreatZones(
|
||||||
@ -762,6 +773,11 @@ function drawThreatZones() {
|
|||||||
redAirDefenseThreatZones,
|
redAirDefenseThreatZones,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
_drawThreatZones(
|
||||||
|
game.threatZones.red.radarSams,
|
||||||
|
redRadarSamThreatZones,
|
||||||
|
false
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawInitialMap() {
|
function drawInitialMap() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user