mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Creates threat zones around airfields and non-trivial air defenses (it's not worth dodging anything with a threat range under 3nm). These threat zones can be used to aid mission planning and waypoint placement. https://github.com/Khopa/dcs_liberation/issues/292
92 lines
3.2 KiB
Python
92 lines
3.2 KiB
Python
from __future__ import annotations
|
|
|
|
from functools import singledispatchmethod
|
|
from typing import TYPE_CHECKING, Union
|
|
|
|
from dcs.mapping import Point as DcsPoint
|
|
from shapely.geometry import (
|
|
LineString,
|
|
MultiPolygon,
|
|
Point as ShapelyPoint,
|
|
Polygon,
|
|
)
|
|
from shapely.geometry.base import BaseGeometry
|
|
from shapely.ops import unary_union
|
|
|
|
from game.utils import nautical_miles
|
|
from gen.flights.flight import Flight
|
|
|
|
if TYPE_CHECKING:
|
|
from game import Game
|
|
|
|
|
|
ThreatPoly = Union[MultiPolygon, Polygon]
|
|
|
|
|
|
class ThreatZones:
|
|
def __init__(self, airbases: ThreatPoly, air_defenses: ThreatPoly) -> None:
|
|
self.airbases = airbases
|
|
self.air_defenses = air_defenses
|
|
self.all = unary_union([airbases, air_defenses])
|
|
|
|
@singledispatchmethod
|
|
def threatened_by_aircraft(self, target) -> bool:
|
|
raise NotImplementedError
|
|
|
|
@threatened_by_aircraft.register
|
|
def _threatened_by_aircraft_geom(self, position: BaseGeometry) -> bool:
|
|
return self.airbases.intersects(position)
|
|
|
|
@threatened_by_aircraft.register
|
|
def _threatened_by_aircraft_flight(self, flight: Flight) -> bool:
|
|
return self.threatened_by_aircraft(LineString((
|
|
self.dcs_to_shapely_point(p.position) for p in flight.points
|
|
)))
|
|
|
|
@singledispatchmethod
|
|
def threatened_by_air_defense(self, target) -> bool:
|
|
raise NotImplementedError
|
|
|
|
@threatened_by_air_defense.register
|
|
def _threatened_by_air_defense_geom(self, position: BaseGeometry) -> bool:
|
|
return self.air_defenses.intersects(position)
|
|
|
|
@threatened_by_air_defense.register
|
|
def _threatened_by_air_defense_flight(self, flight: Flight) -> bool:
|
|
return self.threatened_by_air_defense(LineString((
|
|
self.dcs_to_shapely_point(p.position) for p in flight.points
|
|
)))
|
|
|
|
@classmethod
|
|
def for_faction(cls, game: Game, player: bool) -> ThreatZones:
|
|
opposing_doctrine = game.faction_for(not player).doctrine
|
|
|
|
airbases = []
|
|
air_defenses = []
|
|
for control_point in game.theater.controlpoints:
|
|
if control_point.captured != player:
|
|
continue
|
|
if control_point.runway_is_operational():
|
|
point = ShapelyPoint(control_point.position.x,
|
|
control_point.position.y)
|
|
cap_threat_range = (opposing_doctrine.cap_max_distance_from_cp +
|
|
opposing_doctrine.cap_engagement_range)
|
|
airbases.append(point.buffer(cap_threat_range.meters))
|
|
|
|
for tgo in control_point.ground_objects:
|
|
threat_range = tgo.threat_range
|
|
# Any system with a shorter range than this is not worth even
|
|
# avoiding.
|
|
if threat_range > nautical_miles(3):
|
|
point = ShapelyPoint(tgo.position.x, tgo.position.y)
|
|
threat_zone = point.buffer(threat_range.meters)
|
|
air_defenses.append(threat_zone)
|
|
|
|
return cls(
|
|
airbases=unary_union(airbases),
|
|
air_defenses=unary_union(air_defenses)
|
|
)
|
|
|
|
@staticmethod
|
|
def dcs_to_shapely_point(point: DcsPoint) -> ShapelyPoint:
|
|
return ShapelyPoint(point.x, point.y) |