mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +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
|
||||
|
||||
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
|
||||
AirDefence.SAM_SA_19_Tunguska_Grison,
|
||||
AirDefence.SAM_SA_11_Buk_Gadfly_Fire_Dome_TEL,
|
||||
@ -74,4 +105,4 @@ UNITS_WITH_RADAR = [
|
||||
Type_052B_Destroyer,
|
||||
Type_054A_Frigate,
|
||||
Type_052C_Destroyer,
|
||||
]
|
||||
}
|
||||
|
||||
@ -8,9 +8,15 @@ from dcs.mapping import Point
|
||||
from dcs.triggers import TriggerZone
|
||||
from dcs.unit import Unit
|
||||
from dcs.unitgroup import Group
|
||||
from dcs.unittype import VehicleType
|
||||
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -166,14 +172,7 @@ class TheaterGroundObject(MissionTarget):
|
||||
def detection_range(self, group: Group) -> Distance:
|
||||
return self._max_range_of_type(group, "detection_range")
|
||||
|
||||
def threat_range(self, group: Group) -> 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)
|
||||
def threat_range(self, group: Group, radar_only: bool = False) -> Distance:
|
||||
return self._max_range_of_type(group, "threat_range")
|
||||
|
||||
@property
|
||||
@ -459,6 +458,38 @@ class SamGroundObject(BaseDefenseGroundObject):
|
||||
def might_have_aa(self) -> bool:
|
||||
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):
|
||||
def __init__(
|
||||
|
||||
@ -15,7 +15,6 @@ from shapely.ops import nearest_points, unary_union
|
||||
|
||||
from game.theater import ControlPoint
|
||||
from game.utils import Distance, meters, nautical_miles
|
||||
from gen import Conflict
|
||||
from gen.flights.closestairfields import ObjectiveDistanceCache
|
||||
from gen.flights.flight import Flight
|
||||
|
||||
@ -27,9 +26,12 @@ ThreatPoly = Union[MultiPolygon, Polygon]
|
||||
|
||||
|
||||
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.air_defenses = air_defenses
|
||||
self.radar_sam_threats = radar_sam_threats
|
||||
self.all = unary_union([airbases, air_defenses])
|
||||
|
||||
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))
|
||||
)
|
||||
|
||||
@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
|
||||
def closest_enemy_airbase(
|
||||
cls, location: ControlPoint, max_distance: Distance
|
||||
@ -134,6 +150,7 @@ class ThreatZones:
|
||||
"""
|
||||
air_threats = []
|
||||
air_defenses = []
|
||||
radar_sam_threats = []
|
||||
for control_point in game.theater.controlpoints:
|
||||
if control_point.captured != player:
|
||||
continue
|
||||
@ -151,9 +168,16 @@ class ThreatZones:
|
||||
point = ShapelyPoint(tgo.position.x, tgo.position.y)
|
||||
threat_zone = point.buffer(threat_range.meters)
|
||||
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(
|
||||
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
|
||||
|
||||
@ -833,7 +833,7 @@ class CoalitionMissionPlanner:
|
||||
for flight in builder.package.flights:
|
||||
if self.threat_zones.threatened_by_aircraft(flight):
|
||||
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
|
||||
return threats
|
||||
|
||||
|
||||
@ -538,17 +538,20 @@ class ThreatZonesJs(QObject):
|
||||
fullChanged = Signal()
|
||||
aircraftChanged = Signal()
|
||||
airDefensesChanged = Signal()
|
||||
radarSamsChanged = Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
full: List[List[LeafletLatLon]],
|
||||
aircraft: List[List[LeafletLatLon]],
|
||||
air_defenses: List[List[LeafletLatLon]],
|
||||
radar_sams: List[List[LeafletLatLon]],
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self._full = full
|
||||
self._aircraft = aircraft
|
||||
self._air_defenses = air_defenses
|
||||
self._radar_sams = radar_sams
|
||||
|
||||
@Property(list, notify=fullChanged)
|
||||
def full(self) -> List[List[LeafletLatLon]]:
|
||||
@ -562,6 +565,10 @@ class ThreatZonesJs(QObject):
|
||||
def airDefenses(self) -> List[List[LeafletLatLon]]:
|
||||
return self._air_defenses
|
||||
|
||||
@Property(list, notify=radarSamsChanged)
|
||||
def radarSams(self) -> List[List[LeafletLatLon]]:
|
||||
return self._radar_sams
|
||||
|
||||
@staticmethod
|
||||
def polys_to_leaflet(
|
||||
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.airbases, theater),
|
||||
cls.polys_to_leaflet(zones.air_defenses, theater),
|
||||
cls.polys_to_leaflet(zones.radar_sam_threats, theater),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def empty(cls) -> ThreatZonesJs:
|
||||
return ThreatZonesJs([], [], [])
|
||||
return ThreatZonesJs([], [], [], [])
|
||||
|
||||
|
||||
class ThreatZoneContainerJs(QObject):
|
||||
|
||||
@ -163,10 +163,12 @@ const allFlightPlansLayer = L.layerGroup();
|
||||
const blueFullThreatZones = L.layerGroup();
|
||||
const blueAircraftThreatZones = L.layerGroup();
|
||||
const blueAirDefenseThreatZones = L.layerGroup();
|
||||
const blueRadarSamThreatZones = L.layerGroup();
|
||||
|
||||
const redFullThreatZones = L.layerGroup();
|
||||
const redAircraftThreatZones = L.layerGroup();
|
||||
const redAirDefenseThreatZones = L.layerGroup();
|
||||
const redRadarSamThreatZones = L.layerGroup();
|
||||
|
||||
L.control
|
||||
.groupedLayers(
|
||||
@ -198,12 +200,14 @@ L.control
|
||||
Full: blueFullThreatZones,
|
||||
Aircraft: blueAircraftThreatZones,
|
||||
"Air Defenses": blueAirDefenseThreatZones,
|
||||
"Radar SAMs": blueRadarSamThreatZones,
|
||||
},
|
||||
"Red Threat Zones": {
|
||||
Hide: L.layerGroup().addTo(map),
|
||||
Full: redFullThreatZones,
|
||||
Aircraft: redAircraftThreatZones,
|
||||
"Air Defenses": redAirDefenseThreatZones,
|
||||
"Radar SAMs": redRadarSamThreatZones,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -735,9 +739,11 @@ function drawThreatZones() {
|
||||
blueFullThreatZones.clearLayers();
|
||||
blueAircraftThreatZones.clearLayers();
|
||||
blueAirDefenseThreatZones.clearLayers();
|
||||
blueRadarSamThreatZones.clearLayers();
|
||||
redFullThreatZones.clearLayers();
|
||||
redAircraftThreatZones.clearLayers();
|
||||
redAirDefenseThreatZones.clearLayers();
|
||||
redRadarSamThreatZones.clearLayers();
|
||||
|
||||
_drawThreatZones(game.threatZones.blue.full, blueFullThreatZones, true);
|
||||
_drawThreatZones(
|
||||
@ -750,6 +756,11 @@ function drawThreatZones() {
|
||||
blueAirDefenseThreatZones,
|
||||
true
|
||||
);
|
||||
_drawThreatZones(
|
||||
game.threatZones.blue.radarSams,
|
||||
blueRadarSamThreatZones,
|
||||
true
|
||||
);
|
||||
|
||||
_drawThreatZones(game.threatZones.red.full, redFullThreatZones, false);
|
||||
_drawThreatZones(
|
||||
@ -762,6 +773,11 @@ function drawThreatZones() {
|
||||
redAirDefenseThreatZones,
|
||||
false
|
||||
);
|
||||
_drawThreatZones(
|
||||
game.threatZones.red.radarSams,
|
||||
redRadarSamThreatZones,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
function drawInitialMap() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user