Draw frozen combat on the map.

Very basic display. Draws the engagement footprint for air-to-air
combat, a line from the flight to the target for IP, and lines from SAMs
to their target for air defense.

https://github.com/dcs-liberation/dcs_liberation/issues/1680
This commit is contained in:
Dan Albert
2021-12-21 14:06:04 -08:00
parent fb10a8d28e
commit 515efd0598
11 changed files with 268 additions and 11 deletions

View File

@@ -9,22 +9,29 @@ from dcs import Point
from game import Game
from game.ato.airtaaskingorder import AirTaskingOrder
from game.profiling import logged_duration
from game.sim.combat import FrozenCombat
from game.sim.combat.aircombat import AirCombat
from game.sim.combat.atip import AtIp
from game.sim.combat.defendingsam import DefendingSam
from game.theater import (
ConflictTheater,
)
from qt_ui.models import GameModel
from qt_ui.simcontroller import SimController
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from .aircombatjs import AirCombatJs
from .controlpointjs import ControlPointJs
from .flightjs import FlightJs
from .frontlinejs import FrontLineJs
from .groundobjectjs import GroundObjectJs
from .holdzonesjs import HoldZonesJs
from .ipcombatjs import IpCombatJs
from .ipzonesjs import IpZonesJs
from .joinzonesjs import JoinZonesJs
from .leaflet import LeafletLatLon
from .mapzonesjs import MapZonesJs
from .navmeshjs import NavMeshJs
from .samcombatjs import SamCombatJs
from .supplyroutejs import SupplyRouteJs
from .threatzonecontainerjs import ThreatZoneContainerJs
from .threatzonesjs import ThreatZonesJs
@@ -63,6 +70,9 @@ class MapModel(QObject):
ipZonesChanged = Signal()
joinZonesChanged = Signal()
holdZonesChanged = Signal()
airCombatsChanged = Signal()
samCombatsChanged = Signal()
ipCombatsChanged = Signal()
def __init__(self, game_model: GameModel, sim_controller: SimController) -> None:
super().__init__()
@@ -83,6 +93,10 @@ class MapModel(QObject):
self._join_zones = JoinZonesJs.empty()
self._hold_zones = HoldZonesJs.empty()
self._selected_flight_index: Optional[Tuple[int, int]] = None
self._air_combats = []
self._sam_combats = []
self._ip_combats = []
GameUpdateSignal.get_instance().game_loaded.connect(self.on_game_load)
GameUpdateSignal.get_instance().flight_paths_changed.connect(self.reset_atos)
GameUpdateSignal.get_instance().package_selection_changed.connect(
@@ -92,6 +106,8 @@ class MapModel(QObject):
self.set_flight_selection
)
sim_controller.sim_update.connect(self.on_sim_update)
sim_controller.on_add_combat.connect(self.on_add_combat)
sim_controller.on_combat_changed.connect(self.on_combat_changed)
self.reset()
def clear(self) -> None:
@@ -107,6 +123,9 @@ class MapModel(QObject):
self._map_zones = MapZonesJs([], [], [])
self._unculled_zones = []
self._ip_zones = IpZonesJs.empty()
self._air_combats = []
self._sam_combats = []
self._ip_combats = []
self.cleared.emit()
def on_sim_update(self) -> None:
@@ -180,6 +199,7 @@ class MapModel(QObject):
self.reset_navmeshes()
self.reset_map_zones()
self.reset_unculled_zones()
self.reset_combats()
def on_game_load(self, game: Optional[Game]) -> None:
if game is not None:
@@ -367,6 +387,79 @@ class MapModel(QObject):
def holdZones(self) -> HoldZonesJs:
return self._hold_zones
def reset_combats(self) -> None:
self._air_combats = []
self._sam_combats = []
self._ip_combats = []
self.airCombatsChanged.emit()
self.samCombatsChanged.emit()
self.ipCombatsChanged.emit()
def on_add_combat(self, combat: FrozenCombat) -> None:
if isinstance(combat, AirCombat):
self.add_air_combat(combat)
elif isinstance(combat, DefendingSam):
self.add_sam_combat(combat)
elif isinstance(combat, AtIp):
self.add_ip_combat(combat)
else:
logging.error(f"Unhandled FrozenCombat type: {combat.__class__}")
def add_air_combat(self, combat: AirCombat) -> None:
self._air_combats.append(AirCombatJs(combat, self.game.theater))
self.airCombatsChanged.emit()
def add_sam_combat(self, combat: DefendingSam) -> None:
self._sam_combats.append(SamCombatJs(combat, self.game_model))
self.samCombatsChanged.emit()
def add_ip_combat(self, combat: AtIp) -> None:
self._ip_combats.append(IpCombatJs(combat, self.game_model))
self.ipCombatsChanged.emit()
def on_combat_changed(self, combat: FrozenCombat) -> None:
if isinstance(combat, AirCombat):
self.refresh_air_combat(combat)
elif isinstance(combat, DefendingSam):
self.refresh_sam_combat(combat)
elif isinstance(combat, AtIp):
self.refresh_ip_combat(combat)
else:
logging.error(f"Unhandled FrozenCombat type: {combat.__class__}")
def refresh_air_combat(self, combat: AirCombat) -> None:
for js in self._air_combats:
if js.combat == combat:
js.refresh()
return
logging.error(f"Could not find existing combat model to update for {combat}")
def refresh_sam_combat(self, combat: DefendingSam) -> None:
for js in self._sam_combats:
if js.combat == combat:
js.refresh()
return
logging.error(f"Could not find existing combat model to update for {combat}")
def refresh_ip_combat(self, combat: AtIp) -> None:
for js in self._ip_combats:
if js.combat == combat:
js.refresh()
return
logging.error(f"Could not find existing combat model to update for {combat}")
@Property(list, notify=airCombatsChanged)
def airCombats(self) -> list[AirCombatJs]:
return self._air_combats
@Property(list, notify=samCombatsChanged)
def samCombats(self) -> list[SamCombatJs]:
return self._sam_combats
@Property(list, notify=ipCombatsChanged)
def ipCombats(self) -> list[IpCombatJs]:
return self._ip_combats
@property
def game(self) -> Game:
if self.game_model.game is None: