mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
A possible explanation for the infrequent CTDs we've been seeing since adding fast forward is that QWebChannel doesn't keep a reference to the python objects that it passes to js, so if the object is GC'd before the front end is done with it, it crashes. We don't really like QWebChannel anyway, so this begins replacing that with FastAPI.
135 lines
4.5 KiB
Python
135 lines
4.5 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import List, Optional
|
|
|
|
from PySide2.QtCore import Property, QObject, Signal, Slot
|
|
from dcs.unit import Unit
|
|
from dcs.vehicles import vehicle_map
|
|
|
|
from game import Game
|
|
from game.dcs.groundunittype import GroundUnitType
|
|
from game.server.leaflet import LeafletLatLon
|
|
from game.theater import TheaterGroundObject
|
|
from qt_ui.dialogs import Dialog
|
|
from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu
|
|
|
|
|
|
class GroundObjectJs(QObject):
|
|
nameChanged = Signal()
|
|
controlPointNameChanged = Signal()
|
|
unitsChanged = Signal()
|
|
blueChanged = Signal()
|
|
positionChanged = Signal()
|
|
samThreatRangesChanged = Signal()
|
|
samDetectionRangesChanged = Signal()
|
|
categoryChanged = Signal()
|
|
deadChanged = Signal()
|
|
|
|
def __init__(self, tgo: TheaterGroundObject, game: Game) -> None:
|
|
super().__init__()
|
|
self.tgo = tgo
|
|
self.game = game
|
|
self.theater = game.theater
|
|
self.buildings = self.theater.find_ground_objects_by_obj_name(self.tgo.obj_name)
|
|
self.dialog: Optional[QGroundObjectMenu] = None
|
|
|
|
@Slot()
|
|
def showInfoDialog(self) -> None:
|
|
if self.dialog is None:
|
|
self.dialog = QGroundObjectMenu(
|
|
None,
|
|
self.tgo,
|
|
self.buildings,
|
|
self.tgo.control_point,
|
|
self.game,
|
|
)
|
|
self.dialog.show()
|
|
|
|
@Slot()
|
|
def showPackageDialog(self) -> None:
|
|
Dialog.open_new_package_dialog(self.tgo)
|
|
|
|
@Property(str, notify=nameChanged)
|
|
def name(self) -> str:
|
|
return self.tgo.name
|
|
|
|
@Property(str, notify=controlPointNameChanged)
|
|
def controlPointName(self) -> str:
|
|
return self.tgo.control_point.name
|
|
|
|
@Property(str, notify=categoryChanged)
|
|
def category(self) -> str:
|
|
return self.tgo.category
|
|
|
|
@staticmethod
|
|
def make_unit_name(unit: Unit, dead: bool) -> str:
|
|
dead_label = " [DEAD]" if dead else ""
|
|
unit_display_name = unit.type
|
|
dcs_unit_type = vehicle_map.get(unit.type)
|
|
if dcs_unit_type is not None:
|
|
# TODO: Make the TGO contain GroundUnitType instead of the pydcs Group.
|
|
# This is a hack because we can't know which variant was used.
|
|
try:
|
|
unit_display_name = next(
|
|
GroundUnitType.for_dcs_type(dcs_unit_type)
|
|
).name
|
|
except StopIteration:
|
|
pass
|
|
return f"Unit #{unit.id} - {unit_display_name}{dead_label}"
|
|
|
|
@Property(list, notify=unitsChanged)
|
|
def units(self) -> List[str]:
|
|
units = []
|
|
# TGOs with a non-empty group set are non-building TGOs. Building TGOs have no
|
|
# groups set, but instead are one TGO per building "group" (DCS doesn't support
|
|
# groups of statics) all with the same name.
|
|
if self.tgo.groups:
|
|
for unit in self.tgo.units:
|
|
units.append(self.make_unit_name(unit, dead=False))
|
|
for unit in self.tgo.dead_units:
|
|
units.append(self.make_unit_name(unit, dead=True))
|
|
else:
|
|
for building in self.buildings:
|
|
dead = " [DEAD]" if building.is_dead else ""
|
|
units.append(f"{building.dcs_identifier}{dead}")
|
|
return units
|
|
|
|
@Property(bool, notify=blueChanged)
|
|
def blue(self) -> bool:
|
|
return self.tgo.control_point.captured
|
|
|
|
@Property(list, notify=positionChanged)
|
|
def position(self) -> LeafletLatLon:
|
|
ll = self.theater.point_to_ll(self.tgo.position)
|
|
return [ll.latitude, ll.longitude]
|
|
|
|
@Property(bool, notify=deadChanged)
|
|
def dead(self) -> bool:
|
|
if not self.tgo.groups:
|
|
return all(b.is_dead for b in self.buildings)
|
|
return not any(g.units for g in self.tgo.groups)
|
|
|
|
@Property(list, notify=samThreatRangesChanged)
|
|
def samThreatRanges(self) -> List[float]:
|
|
if not self.tgo.might_have_aa:
|
|
return []
|
|
|
|
ranges = []
|
|
for group in self.tgo.groups:
|
|
threat_range = self.tgo.threat_range(group)
|
|
if threat_range:
|
|
ranges.append(threat_range.meters)
|
|
return ranges
|
|
|
|
@Property(list, notify=samDetectionRangesChanged)
|
|
def samDetectionRanges(self) -> List[float]:
|
|
if not self.tgo.might_have_aa:
|
|
return []
|
|
|
|
ranges = []
|
|
for group in self.tgo.groups:
|
|
detection_range = self.tgo.detection_range(group)
|
|
if detection_range:
|
|
ranges.append(detection_range.meters)
|
|
return ranges
|