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.
119 lines
4.0 KiB
Python
119 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Optional
|
|
|
|
from PySide2.QtCore import Property, QObject, Signal, Slot
|
|
from dcs import Point
|
|
|
|
from game.server.leaflet import LeafletLatLon
|
|
from game.theater import ConflictTheater, ControlPoint, ControlPointStatus, LatLon
|
|
from game.utils import meters, nautical_miles
|
|
from qt_ui.dialogs import Dialog
|
|
from qt_ui.models import GameModel
|
|
from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2
|
|
|
|
MAX_SHIP_DISTANCE = nautical_miles(80)
|
|
|
|
|
|
class ControlPointJs(QObject):
|
|
nameChanged = Signal()
|
|
blueChanged = Signal()
|
|
positionChanged = Signal()
|
|
mobileChanged = Signal()
|
|
destinationChanged = Signal(list)
|
|
categoryChanged = Signal()
|
|
statusChanged = Signal()
|
|
|
|
def __init__(
|
|
self,
|
|
control_point: ControlPoint,
|
|
game_model: GameModel,
|
|
theater: ConflictTheater,
|
|
) -> None:
|
|
super().__init__()
|
|
self.control_point = control_point
|
|
self.game_model = game_model
|
|
self.theater = theater
|
|
self.dialog: Optional[QBaseMenu2] = None
|
|
|
|
@Property(str, notify=nameChanged)
|
|
def name(self) -> str:
|
|
return self.control_point.name
|
|
|
|
@Property(bool, notify=blueChanged)
|
|
def blue(self) -> bool:
|
|
return self.control_point.captured
|
|
|
|
@Property(str, notify=categoryChanged)
|
|
def category(self) -> str:
|
|
return self.control_point.category
|
|
|
|
@Property(str, notify=statusChanged)
|
|
def status(self) -> str:
|
|
status = self.control_point.status
|
|
if status is ControlPointStatus.Functional:
|
|
return "alive"
|
|
elif status is ControlPointStatus.Damaged:
|
|
return "damaged"
|
|
elif status is ControlPointStatus.Destroyed:
|
|
return "destroyed"
|
|
raise ValueError(f"Unhandled ControlPointStatus: {status.name}")
|
|
|
|
@Property(list, notify=positionChanged)
|
|
def position(self) -> LeafletLatLon:
|
|
ll = self.theater.point_to_ll(self.control_point.position)
|
|
return [ll.latitude, ll.longitude]
|
|
|
|
@Property(bool, notify=mobileChanged)
|
|
def mobile(self) -> bool:
|
|
return self.control_point.moveable and self.control_point.captured
|
|
|
|
@Property(list, notify=destinationChanged)
|
|
def destination(self) -> LeafletLatLon:
|
|
if self.control_point.target_position is None:
|
|
# Qt seems to convert None to [] for list Properties :(
|
|
return []
|
|
return self.theater.point_to_ll(self.control_point.target_position).as_list()
|
|
|
|
def destination_in_range(self, destination: Point) -> bool:
|
|
move_distance = meters(
|
|
destination.distance_to_point(self.control_point.position)
|
|
)
|
|
return move_distance <= MAX_SHIP_DISTANCE
|
|
|
|
@Slot(list, result=bool)
|
|
def destinationInRange(self, destination: LeafletLatLon) -> bool:
|
|
return self.destination_in_range(self.theater.ll_to_point(LatLon(*destination)))
|
|
|
|
@Slot(list, result=str)
|
|
def setDestination(self, destination: LeafletLatLon) -> str:
|
|
if not self.control_point.moveable:
|
|
return f"{self.control_point} is not mobile"
|
|
if not self.control_point.captured:
|
|
return f"{self.control_point} is not owned by player"
|
|
|
|
point = self.theater.ll_to_point(LatLon(*destination))
|
|
if not self.destination_in_range(point):
|
|
return (
|
|
f"Cannot move {self.control_point} more than "
|
|
f"{MAX_SHIP_DISTANCE.nautical_miles}nm."
|
|
)
|
|
self.control_point.target_position = point
|
|
self.destinationChanged.emit(destination)
|
|
return ""
|
|
|
|
@Slot()
|
|
def cancelTravel(self) -> None:
|
|
self.control_point.target_position = None
|
|
self.destinationChanged.emit([])
|
|
|
|
@Slot()
|
|
def showInfoDialog(self) -> None:
|
|
if self.dialog is None:
|
|
self.dialog = QBaseMenu2(None, self.control_point, self.game_model)
|
|
self.dialog.show()
|
|
|
|
@Slot()
|
|
def showPackageDialog(self) -> None:
|
|
Dialog.open_new_package_dialog(self.control_point)
|