mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
The usual symptom here was the game breaking when a carrier is destroyed. The carrier would no longer be operational but missions would be assigned there that could not generate flight plans.
81 lines
2.8 KiB
Python
81 lines
2.8 KiB
Python
"""Objective adjacency lists."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Dict, Iterator, List, Optional, TYPE_CHECKING
|
|
|
|
from game.utils import Distance
|
|
|
|
if TYPE_CHECKING:
|
|
from game.theater import ConflictTheater, ControlPoint, MissionTarget
|
|
|
|
|
|
class ClosestAirfields:
|
|
"""Precalculates which control points are closes to the given target."""
|
|
|
|
def __init__(
|
|
self, target: MissionTarget, all_control_points: List[ControlPoint]
|
|
) -> None:
|
|
self.target = target
|
|
# This cache is configured once on load, so it's important that it is
|
|
# complete and deterministic to avoid different behaviors across loads.
|
|
# E.g. https://github.com/dcs-liberation/dcs_liberation/issues/819
|
|
self.closest_airfields: List[ControlPoint] = sorted(
|
|
all_control_points, key=lambda c: self.target.distance_to(c)
|
|
)
|
|
|
|
@property
|
|
def operational_airfields(self) -> Iterator[ControlPoint]:
|
|
return (c for c in self.closest_airfields if c.runway_is_operational())
|
|
|
|
def _airfields_within(
|
|
self, distance: Distance, operational: bool
|
|
) -> Iterator[ControlPoint]:
|
|
airfields = (
|
|
self.operational_airfields if operational else self.closest_airfields
|
|
)
|
|
for cp in airfields:
|
|
if cp.distance_to(self.target) < distance.meters:
|
|
yield cp
|
|
else:
|
|
break
|
|
|
|
def operational_airfields_within(
|
|
self, distance: Distance
|
|
) -> Iterator[ControlPoint]:
|
|
"""Iterates over all airfields within the given range of the target.
|
|
|
|
Note that this iterates over *all* airfields, not just friendly
|
|
airfields.
|
|
"""
|
|
return self._airfields_within(distance, operational=True)
|
|
|
|
def all_airfields_within(self, distance: Distance) -> Iterator[ControlPoint]:
|
|
"""Iterates over all airfields within the given range of the target.
|
|
|
|
Note that this iterates over *all* airfields, not just friendly
|
|
airfields.
|
|
"""
|
|
return self._airfields_within(distance, operational=False)
|
|
|
|
|
|
class ObjectiveDistanceCache:
|
|
theater: Optional[ConflictTheater] = None
|
|
closest_airfields: Dict[str, ClosestAirfields] = {}
|
|
|
|
@classmethod
|
|
def set_theater(cls, theater: ConflictTheater) -> None:
|
|
if cls.theater is not None:
|
|
cls.closest_airfields = {}
|
|
cls.theater = theater
|
|
|
|
@classmethod
|
|
def get_closest_airfields(cls, location: MissionTarget) -> ClosestAirfields:
|
|
if cls.theater is None:
|
|
raise RuntimeError("Call ObjectiveDistanceCache.set_theater before using")
|
|
|
|
if location.name not in cls.closest_airfields:
|
|
cls.closest_airfields[location.name] = ClosestAirfields(
|
|
location, cls.theater.controlpoints
|
|
)
|
|
return cls.closest_airfields[location.name]
|