mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Remove the old map.
The new map has no open bugs.
This commit is contained in:
@@ -2,22 +2,16 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
Optional,
|
||||
)
|
||||
|
||||
from PySide2.QtCore import QUrl
|
||||
from PySide2.QtWebChannel import QWebChannel
|
||||
from PySide2.QtWebEngineWidgets import (
|
||||
QWebEnginePage,
|
||||
QWebEngineSettings,
|
||||
QWebEngineView,
|
||||
)
|
||||
|
||||
from game import Game
|
||||
from game.server.settings import ServerSettings
|
||||
from qt_ui.models import GameModel
|
||||
from .model import MapModel
|
||||
|
||||
|
||||
class LoggingWebPage(QWebEnginePage):
|
||||
@@ -37,14 +31,10 @@ class LoggingWebPage(QWebEnginePage):
|
||||
|
||||
|
||||
class QLiberationMap(QWebEngineView):
|
||||
def __init__(self, game_model: GameModel, new_map: bool, dev: bool, parent) -> None:
|
||||
def __init__(self, game_model: GameModel, dev: bool, parent) -> None:
|
||||
super().__init__(parent)
|
||||
self.game_model = game_model
|
||||
self.setMinimumSize(800, 600)
|
||||
self.map_model = MapModel(game_model)
|
||||
|
||||
self.channel = QWebChannel()
|
||||
self.channel.registerObject("game", self.map_model)
|
||||
|
||||
self.page = LoggingWebPage(self)
|
||||
# Required to allow "cross-origin" access from file:// scoped canvas.html to the
|
||||
@@ -52,16 +42,11 @@ class QLiberationMap(QWebEngineView):
|
||||
self.page.settings().setAttribute(
|
||||
QWebEngineSettings.LocalContentCanAccessRemoteUrls, True
|
||||
)
|
||||
self.page.setWebChannel(self.channel)
|
||||
|
||||
if new_map and dev:
|
||||
if dev:
|
||||
url = QUrl("http://localhost:3000")
|
||||
elif new_map:
|
||||
url = QUrl.fromLocalFile(str(Path("client/build/index.html").resolve()))
|
||||
else:
|
||||
url = QUrl.fromLocalFile(
|
||||
str(Path("resources/ui/map/canvas.html").resolve())
|
||||
)
|
||||
url = QUrl.fromLocalFile(str(Path("client/build/index.html").resolve()))
|
||||
server_settings = ServerSettings.get()
|
||||
host = server_settings.server_bind_address
|
||||
if host.startswith("::"):
|
||||
@@ -70,9 +55,3 @@ class QLiberationMap(QWebEngineView):
|
||||
url.setQuery(f"server={host}:{port}")
|
||||
self.page.load(url)
|
||||
self.setPage(self.page)
|
||||
|
||||
def set_game(self, game: Optional[Game]) -> None:
|
||||
if game is None:
|
||||
self.map_model.clear()
|
||||
else:
|
||||
self.map_model.reset()
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
from .mapmodel import MapModel
|
||||
@@ -1,108 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from PySide2.QtCore import Property, QObject, Signal, Slot
|
||||
from dcs import Point
|
||||
from dcs.mapping import LatLng
|
||||
|
||||
from game.theater import ConflictTheater, ControlPoint
|
||||
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
|
||||
from .leaflet import LeafletLatLon
|
||||
|
||||
MAX_SHIP_DISTANCE = nautical_miles(80)
|
||||
|
||||
|
||||
class ControlPointJs(QObject):
|
||||
nameChanged = Signal()
|
||||
blueChanged = Signal()
|
||||
positionChanged = Signal()
|
||||
mobileChanged = Signal()
|
||||
destinationChanged = Signal(list)
|
||||
sidcChanged = 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=sidcChanged)
|
||||
def sidc(self) -> str:
|
||||
return str(self.control_point.sidc())
|
||||
|
||||
@Property(list, notify=positionChanged)
|
||||
def position(self) -> LeafletLatLon:
|
||||
return self.control_point.position.latlng().as_list()
|
||||
|
||||
@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.control_point.target_position.latlng().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(
|
||||
Point.from_latlng(LatLng(*destination), self.theater.terrain)
|
||||
)
|
||||
|
||||
@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 = Point.from_latlng(LatLng(*destination), self.theater.terrain)
|
||||
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)
|
||||
@@ -1,2 +0,0 @@
|
||||
LeafletLatLon = list[float]
|
||||
LeafletPoly = list[LeafletLatLon]
|
||||
@@ -1,132 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List, Optional
|
||||
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
from dcs.mapping import LatLng
|
||||
|
||||
from game import Game
|
||||
from game.profiling import logged_duration
|
||||
from game.theater import (
|
||||
ConflictTheater,
|
||||
)
|
||||
from qt_ui.models import GameModel
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
from .controlpointjs import ControlPointJs
|
||||
from .leaflet import LeafletLatLon
|
||||
from .supplyroutejs import SupplyRouteJs
|
||||
|
||||
|
||||
# **EVERY PROPERTY NEEDS A NOTIFY SIGNAL**
|
||||
#
|
||||
# https://bugreports.qt.io/browse/PYSIDE-1426
|
||||
#
|
||||
# PySide2 5.15.2 released 6 days before the fix for this was merged, but presumably we
|
||||
# can clean up after 5.15.3 (or a future version) is released.
|
||||
#
|
||||
# Until then, all properties must use a notify signal. For some reason the error doesn't
|
||||
# show up when running from source, and member properties also are not sufficient.
|
||||
# Failing to do this will cause every sync of the property to emit an expensive log
|
||||
# message. This can prevent the UI from being responsive.
|
||||
#
|
||||
# A local signal (i.e. `@Property(t, notify=Signal())`) is not sufficient. The class
|
||||
# needs a named signal for every property, even if it is constant.
|
||||
|
||||
|
||||
class MapModel(QObject):
|
||||
cleared = Signal()
|
||||
|
||||
apiKeyChanged = Signal(str)
|
||||
mapCenterChanged = Signal(list)
|
||||
controlPointsChanged = Signal()
|
||||
supplyRoutesChanged = Signal()
|
||||
mapReset = Signal()
|
||||
|
||||
def __init__(self, game_model: GameModel) -> None:
|
||||
super().__init__()
|
||||
self.game_model = game_model
|
||||
self._map_center = LatLng(0, 0)
|
||||
self._control_points = []
|
||||
self._supply_routes = []
|
||||
|
||||
GameUpdateSignal.get_instance().game_loaded.connect(self.on_game_load)
|
||||
self.reset()
|
||||
|
||||
def clear(self) -> None:
|
||||
self._control_points = []
|
||||
self._supply_routes = []
|
||||
self.cleared.emit()
|
||||
|
||||
def reset(self) -> None:
|
||||
if self.game_model.game is None:
|
||||
self.clear()
|
||||
return
|
||||
with logged_duration("Map reset"):
|
||||
self.reset_control_points()
|
||||
self.reset_routes()
|
||||
self.mapReset.emit()
|
||||
|
||||
def on_game_load(self, game: Optional[Game]) -> None:
|
||||
if game is not None:
|
||||
self.reset_map_center(game.theater)
|
||||
|
||||
def reset_map_center(self, theater: ConflictTheater) -> None:
|
||||
self._map_center = theater.terrain.map_view_default.position.latlng()
|
||||
self.mapCenterChanged.emit(self._map_center.as_list())
|
||||
|
||||
@Property(list, notify=mapCenterChanged)
|
||||
def mapCenter(self) -> LeafletLatLon:
|
||||
return self._map_center.as_list()
|
||||
|
||||
def reset_control_points(self) -> None:
|
||||
self._control_points = [
|
||||
ControlPointJs(c, self.game_model, self.game.theater)
|
||||
for c in self.game.theater.controlpoints
|
||||
]
|
||||
self.controlPointsChanged.emit()
|
||||
|
||||
@Property(list, notify=controlPointsChanged)
|
||||
def controlPoints(self) -> List[ControlPointJs]:
|
||||
return self._control_points
|
||||
|
||||
def reset_routes(self) -> None:
|
||||
seen = set()
|
||||
self._supply_routes = []
|
||||
for control_point in self.game.theater.controlpoints:
|
||||
seen.add(control_point)
|
||||
for destination, convoy_route in control_point.convoy_routes.items():
|
||||
if destination in seen:
|
||||
continue
|
||||
self._supply_routes.append(
|
||||
SupplyRouteJs(
|
||||
control_point,
|
||||
destination,
|
||||
[p.latlng().as_list() for p in convoy_route],
|
||||
sea_route=False,
|
||||
game=self.game,
|
||||
)
|
||||
)
|
||||
for destination, shipping_lane in control_point.shipping_lanes.items():
|
||||
if destination in seen:
|
||||
continue
|
||||
if control_point.is_friendly(destination.captured):
|
||||
self._supply_routes.append(
|
||||
SupplyRouteJs(
|
||||
control_point,
|
||||
destination,
|
||||
[p.latlng().as_list() for p in shipping_lane],
|
||||
sea_route=True,
|
||||
game=self.game,
|
||||
)
|
||||
)
|
||||
self.supplyRoutesChanged.emit()
|
||||
|
||||
@Property(list, notify=supplyRoutesChanged)
|
||||
def supplyRoutes(self) -> List[SupplyRouteJs]:
|
||||
return self._supply_routes
|
||||
|
||||
@property
|
||||
def game(self) -> Game:
|
||||
if self.game_model.game is None:
|
||||
raise RuntimeError("No game loaded")
|
||||
return self.game_model.game
|
||||
@@ -1,91 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import List
|
||||
|
||||
from PySide2.QtCore import Property, QObject, Signal
|
||||
|
||||
from game import Game
|
||||
from game.theater import ControlPoint
|
||||
from game.transfers import MultiGroupTransport, TransportMap
|
||||
from .leaflet import LeafletLatLon
|
||||
|
||||
|
||||
class SupplyRouteJs(QObject):
|
||||
pointsChanged = Signal()
|
||||
frontActiveChanged = Signal()
|
||||
isSeaChanged = Signal()
|
||||
blueChanged = Signal()
|
||||
activeTransportsChanged = Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
a: ControlPoint,
|
||||
b: ControlPoint,
|
||||
points: List[LeafletLatLon],
|
||||
sea_route: bool,
|
||||
game: Game,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.control_point_a = a
|
||||
self.control_point_b = b
|
||||
self._points = points
|
||||
self.sea_route = sea_route
|
||||
self.game = game
|
||||
|
||||
def find_in_transport_map(
|
||||
self, transport_map: TransportMap
|
||||
) -> List[MultiGroupTransport]:
|
||||
transports = []
|
||||
transport = transport_map.find_transport(
|
||||
self.control_point_a, self.control_point_b
|
||||
)
|
||||
if transport is not None:
|
||||
transports.append(transport)
|
||||
transport = transport_map.find_transport(
|
||||
self.control_point_b, self.control_point_a
|
||||
)
|
||||
if transport is not None:
|
||||
transports.append(transport)
|
||||
return transports
|
||||
|
||||
def find_transports(self) -> List[MultiGroupTransport]:
|
||||
if self.sea_route:
|
||||
return self.find_in_transport_map(
|
||||
self.game.blue.transfers.cargo_ships
|
||||
) + self.find_in_transport_map(self.game.red.transfers.cargo_ships)
|
||||
return self.find_in_transport_map(
|
||||
self.game.blue.transfers.convoys
|
||||
) + self.find_in_transport_map(self.game.red.transfers.convoys)
|
||||
|
||||
@Property(list, notify=activeTransportsChanged)
|
||||
def activeTransports(self) -> List[str]:
|
||||
transports = self.find_transports()
|
||||
if not transports:
|
||||
return []
|
||||
|
||||
descriptions = []
|
||||
for transport in transports:
|
||||
units = "units" if transport.size > 1 else "unit"
|
||||
descriptions.append(
|
||||
f"{transport.size} {units} transferring from {transport.origin} to "
|
||||
f"{transport.destination}"
|
||||
)
|
||||
return descriptions
|
||||
|
||||
@Property(list, notify=pointsChanged)
|
||||
def points(self) -> List[LeafletLatLon]:
|
||||
return self._points
|
||||
|
||||
@Property(bool, notify=frontActiveChanged)
|
||||
def frontActive(self) -> bool:
|
||||
if self.sea_route:
|
||||
return False
|
||||
return self.control_point_a.front_is_active(self.control_point_b)
|
||||
|
||||
@Property(bool, notify=isSeaChanged)
|
||||
def isSea(self) -> bool:
|
||||
return self.sea_route
|
||||
|
||||
@Property(bool, notify=blueChanged)
|
||||
def blue(self) -> bool:
|
||||
return self.control_point_a.captured
|
||||
Reference in New Issue
Block a user