diff --git a/game/db/gamedb.py b/game/db/gamedb.py index 8bebdd04..04a084de 100644 --- a/game/db/gamedb.py +++ b/game/db/gamedb.py @@ -4,10 +4,11 @@ from .database import Database if TYPE_CHECKING: from game.ato import Flight - from game.theater import FrontLine + from game.theater import FrontLine, TheaterGroundObject class GameDb: def __init__(self) -> None: self.flights: Database[Flight] = Database() self.front_lines: Database[FrontLine] = Database() + self.tgos: Database[TheaterGroundObject] = Database() diff --git a/game/game.py b/game/game.py index f5d1714e..b145d4ee 100644 --- a/game/game.py +++ b/game/game.py @@ -278,6 +278,8 @@ class Game: for control_point in self.theater.controlpoints: control_point.initialize_turn_0() + for tgo in control_point.connected_objectives: + self.db.tgos.add(tgo.id, tgo) self.blue.preinit_turn_0() self.red.preinit_turn_0() diff --git a/game/server/app.py b/game/server/app.py index 9c26cc8a..6b43003e 100644 --- a/game/server/app.py +++ b/game/server/app.py @@ -9,7 +9,7 @@ from . import ( frontlines, mapzones, navmesh, - packagedialog, + qt, supplyroutes, tgos, waypoints, @@ -29,7 +29,7 @@ app.include_router(flights.router) app.include_router(frontlines.router) app.include_router(mapzones.router) app.include_router(navmesh.router) -app.include_router(packagedialog.router) +app.include_router(qt.router) app.include_router(supplyroutes.router) app.include_router(tgos.router) app.include_router(waypoints.router) diff --git a/game/server/dependencies.py b/game/server/dependencies.py index f7e5ca55..9f8339d9 100644 --- a/game/server/dependencies.py +++ b/game/server/dependencies.py @@ -2,7 +2,7 @@ from __future__ import annotations from typing import Callable, TYPE_CHECKING -from game.theater import MissionTarget +from game.theater import MissionTarget, TheaterGroundObject if TYPE_CHECKING: from game import Game @@ -28,8 +28,13 @@ class GameContext: class QtCallbacks: - def __init__(self, create_new_package: Callable[[MissionTarget], None]) -> None: + def __init__( + self, + create_new_package: Callable[[MissionTarget], None], + show_tgo_info: Callable[[TheaterGroundObject], None], + ) -> None: self.create_new_package = create_new_package + self.show_tgo_info = show_tgo_info class QtContext: diff --git a/game/server/eventstream/models.py b/game/server/eventstream/models.py index 5a6c4db7..59e1d9a8 100644 --- a/game/server/eventstream/models.py +++ b/game/server/eventstream/models.py @@ -31,6 +31,7 @@ class GameUpdateEventsJs(BaseModel): new_front_lines: list[FrontLineJs] updated_front_lines: set[UUID] deleted_front_lines: set[UUID] + updated_tgos: set[UUID] @classmethod def from_events(cls, events: GameUpdateEvents, game: Game) -> GameUpdateEventsJs: @@ -62,4 +63,5 @@ class GameUpdateEventsJs(BaseModel): ], updated_front_lines=events.updated_front_lines, deleted_front_lines=events.deleted_front_lines, + updated_tgos=events.updated_tgos, ) diff --git a/game/server/packagedialog/routes.py b/game/server/packagedialog/routes.py deleted file mode 100644 index ec718389..00000000 --- a/game/server/packagedialog/routes.py +++ /dev/null @@ -1,18 +0,0 @@ -from uuid import UUID - -from fastapi import APIRouter, Depends - -from game import Game -from ..dependencies import GameContext, QtCallbacks, QtContext - -router: APIRouter = APIRouter(prefix="/package-dialog") - - -@router.post("/front-line/{front_line_id}") -def new_front_line_package( - front_line_id: UUID, - game: Game = Depends(GameContext.get), - qt: QtCallbacks = Depends(QtContext.get), -) -> None: - front_line = game.db.front_lines.get(front_line_id) - qt.create_new_package(front_line) diff --git a/game/server/packagedialog/__init__.py b/game/server/qt/__init__.py similarity index 100% rename from game/server/packagedialog/__init__.py rename to game/server/qt/__init__.py diff --git a/game/server/qt/routes.py b/game/server/qt/routes.py new file mode 100644 index 00000000..69c9ea22 --- /dev/null +++ b/game/server/qt/routes.py @@ -0,0 +1,35 @@ +from uuid import UUID + +from fastapi import APIRouter, Depends + +from game import Game +from ..dependencies import GameContext, QtCallbacks, QtContext + +router: APIRouter = APIRouter(prefix="/qt") + + +@router.post("/create-package/front-line/{front_line_id}") +def new_front_line_package( + front_line_id: UUID, + game: Game = Depends(GameContext.get), + qt: QtCallbacks = Depends(QtContext.get), +) -> None: + qt.create_new_package(game.db.front_lines.get(front_line_id)) + + +@router.post("/create-package/tgo/{tgo_id}") +def new_tgo_package( + tgo_id: UUID, + game: Game = Depends(GameContext.get), + qt: QtCallbacks = Depends(QtContext.get), +) -> None: + qt.create_new_package(game.db.tgos.get(tgo_id)) + + +@router.post("/info/tgo/{tgo_id}") +def show_tgo_info( + tgo_id: UUID, + game: Game = Depends(GameContext.get), + qt: QtCallbacks = Depends(QtContext.get), +) -> None: + qt.show_tgo_info(game.db.tgos.get(tgo_id)) diff --git a/game/server/tgos/models.py b/game/server/tgos/models.py index 2ae03098..3c34135b 100644 --- a/game/server/tgos/models.py +++ b/game/server/tgos/models.py @@ -1,5 +1,7 @@ from __future__ import annotations +from uuid import UUID + from pydantic import BaseModel from game.server.leaflet import LeafletPoint @@ -7,16 +9,17 @@ from game.theater import TheaterGroundObject class TgoJs(BaseModel): + id: UUID name: str control_point_name: str category: str blue: bool position: LeafletPoint - units: list[str] - threat_ranges: list[float] - detection_ranges: list[float] - dead: bool - sidc: str + units: list[str] # TODO: Event stream + threat_ranges: list[float] # TODO: Event stream + detection_ranges: list[float] # TODO: Event stream + dead: bool # TODO: Event stream + sidc: str # TODO: Event stream @staticmethod def for_tgo(tgo: TheaterGroundObject) -> TgoJs: @@ -29,6 +32,7 @@ class TgoJs(BaseModel): tgo.detection_range(group).meters for group in tgo.groups ] return TgoJs( + id=tgo.id, name=tgo.name, control_point_name=tgo.control_point.name, category=tgo.category, diff --git a/game/server/tgos/routes.py b/game/server/tgos/routes.py index d9fc9986..813d1d30 100644 --- a/game/server/tgos/routes.py +++ b/game/server/tgos/routes.py @@ -1,3 +1,5 @@ +from uuid import UUID + from fastapi import APIRouter, Depends from game import Game @@ -15,3 +17,8 @@ def list_tgos(game: Game = Depends(GameContext.get)) -> list[TgoJs]: if not tgo.is_control_point: tgos.append(TgoJs.for_tgo(tgo)) return tgos + + +@router.get("/{tgo_id}") +def get_tgo(tgo_id: UUID, game: Game = Depends(GameContext.get)) -> TgoJs: + return TgoJs.for_tgo(game.db.tgos.get(tgo_id)) diff --git a/game/sim/gameupdateevents.py b/game/sim/gameupdateevents.py index 0f2099bc..abc93c88 100644 --- a/game/sim/gameupdateevents.py +++ b/game/sim/gameupdateevents.py @@ -9,7 +9,7 @@ from dcs import Point if TYPE_CHECKING: from game.ato import Flight, Package from game.sim.combat import FrozenCombat - from game.theater import FrontLine + from game.theater import FrontLine, TheaterGroundObject @dataclass @@ -30,6 +30,7 @@ class GameUpdateEvents: new_front_lines: set[FrontLine] = field(default_factory=set) updated_front_lines: set[UUID] = field(default_factory=set) deleted_front_lines: set[UUID] = field(default_factory=set) + updated_tgos: set[UUID] = field(default_factory=set) shutting_down: bool = False @property @@ -111,6 +112,10 @@ class GameUpdateEvents: self.deleted_front_lines.add(front_line.id) return self + def update_tgo(self, tgo: TheaterGroundObject) -> GameUpdateEvents: + self.updated_tgos.add(tgo.id) + return self + def shut_down(self) -> GameUpdateEvents: self.shutting_down = True return self diff --git a/game/sim/missionresultsprocessor.py b/game/sim/missionresultsprocessor.py index 82a1ecd7..46d903f1 100644 --- a/game/sim/missionresultsprocessor.py +++ b/game/sim/missionresultsprocessor.py @@ -30,7 +30,7 @@ class MissionResultsProcessor: self.commit_convoy_losses(debriefing) self.commit_cargo_ship_losses(debriefing) self.commit_airlift_losses(debriefing) - self.commit_ground_losses(debriefing) + self.commit_ground_losses(debriefing, events) self.commit_damaged_runways(debriefing) self.commit_captures(debriefing, events) self.commit_front_line_battle_impact(debriefing, events) @@ -131,11 +131,11 @@ class MissionResultsProcessor: ) @staticmethod - def commit_ground_losses(debriefing: Debriefing) -> None: + def commit_ground_losses(debriefing: Debriefing, events: GameUpdateEvents) -> None: for ground_object_loss in debriefing.ground_object_losses: - ground_object_loss.theater_unit.kill() + ground_object_loss.theater_unit.kill(events) for scenery_object_loss in debriefing.scenery_object_losses: - scenery_object_loss.ground_unit.kill() + scenery_object_loss.ground_unit.kill(events) @staticmethod def commit_damaged_runways(debriefing: Debriefing) -> None: diff --git a/game/theater/controlpoint.py b/game/theater/controlpoint.py index e58e7815..1fdedcb4 100644 --- a/game/theater/controlpoint.py +++ b/game/theater/controlpoint.py @@ -730,11 +730,11 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC): for squadron in self.squadrons: self._retreat_squadron(game, squadron) - def depopulate_uncapturable_tgos(self) -> None: + def depopulate_uncapturable_tgos(self, events: GameUpdateEvents) -> None: # TODO Rework this. for tgo in self.connected_objectives: if not tgo.capturable: - tgo.clear() + tgo.clear(events) # TODO: Should be Airbase specific. def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None: @@ -742,7 +742,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC): self.ground_unit_orders.refund_all(self.coalition) self.retreat_ground_units(game) self.retreat_air_units(game) - self.depopulate_uncapturable_tgos() + self.depopulate_uncapturable_tgos(events) self._coalition = new_coalition self.base.set_strength_to_minimum() diff --git a/game/theater/theatergroundobject.py b/game/theater/theatergroundobject.py index 8dd4789e..67207dd6 100644 --- a/game/theater/theatergroundobject.py +++ b/game/theater/theatergroundobject.py @@ -1,6 +1,7 @@ from __future__ import annotations import itertools +import uuid from abc import ABC from typing import Iterator, List, Optional, TYPE_CHECKING @@ -22,6 +23,7 @@ from ..data.radar_db import LAUNCHER_TRACKER_PAIRS, TELARS, TRACK_RADARS from ..utils import Distance, Heading, meters if TYPE_CHECKING: + from game.sim import GameUpdateEvents from .theatergroup import TheaterUnit, TheaterGroup from .controlpoint import ControlPoint from ..ato.flighttype import FlightType @@ -62,6 +64,7 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC): sea_object: bool, ) -> None: super().__init__(name, position) + self.id = uuid.uuid4() self.category = category self.heading = heading self.control_point = control_point @@ -212,8 +215,9 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC): def mark_locations(self) -> Iterator[Point]: yield self.position - def clear(self) -> None: + def clear(self, events: GameUpdateEvents) -> None: self.groups = [] + events.update_tgo(self) @property def capturable(self) -> bool: diff --git a/game/theater/theatergroup.py b/game/theater/theatergroup.py index 0c34c74d..f6bfe5ec 100644 --- a/game/theater/theatergroup.py +++ b/game/theater/theatergroup.py @@ -1,22 +1,20 @@ from __future__ import annotations -import logging from dataclasses import dataclass -from typing import Optional, Any, TYPE_CHECKING, Type +from typing import Any, Optional, TYPE_CHECKING, Type from dcs.triggers import TriggerZone -from dcs.unittype import VehicleType, ShipType, StaticType +from dcs.unittype import ShipType, StaticType, UnitType as DcsUnitType, VehicleType from game.dcs.groundunittype import GroundUnitType from game.dcs.shipunittype import ShipUnitType from game.dcs.unittype import UnitType -from dcs.unittype import UnitType as DcsUnitType - from game.point_with_heading import PointWithHeading from game.utils import Heading if TYPE_CHECKING: - from game.layout.layout import LayoutUnit, TgoLayoutGroup + from game.layout.layout import LayoutUnit + from game.sim import GameUpdateEvents from game.theater import TheaterGroundObject @@ -58,8 +56,9 @@ class TheaterUnit: # None for not available StaticTypes return None - def kill(self) -> None: + def kill(self, events: GameUpdateEvents) -> None: self.alive = False + events.update_tgo(self.ground_object) @property def unit_name(self) -> str: diff --git a/qt_ui/widgets/map/model/groundobjectjs.py b/qt_ui/widgets/map/model/groundobjectjs.py deleted file mode 100644 index f389a9e5..00000000 --- a/qt_ui/widgets/map/model/groundobjectjs.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import annotations - -from typing import List, Optional - -from PySide2.QtCore import Property, QObject, Signal, Slot - -from game import Game -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() - sidcChanged = 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.dialog: Optional[QGroundObjectMenu] = None - - @Slot() - def showInfoDialog(self) -> None: - if self.dialog is None: - self.dialog = QGroundObjectMenu( - None, - self.tgo, - 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=sidcChanged) - def sidc(self) -> str: - return str(self.tgo.sidc()) - - @Property(str, notify=categoryChanged) - def category(self) -> str: - return self.tgo.category - - @Property(list, notify=unitsChanged) - def units(self) -> List[str]: - return [unit.display_name for unit in self.tgo.units] - - @Property(bool, notify=blueChanged) - def blue(self) -> bool: - return self.tgo.control_point.captured - - @Property(list, notify=positionChanged) - def position(self) -> LeafletLatLon: - return self.tgo.position.latlng().as_list() - - @Property(bool, notify=deadChanged) - def dead(self) -> bool: - return not any(g.alive_units > 0 for g in self.tgo.groups) - - @Property(list, notify=samThreatRangesChanged) - def samThreatRanges(self) -> List[float]: - if not self.tgo.might_have_aa: - return [] - return [self.tgo.threat_range(group).meters for group in self.tgo.groups] - - @Property(list, notify=samDetectionRangesChanged) - def samDetectionRanges(self) -> List[float]: - if not self.tgo.might_have_aa: - return [] - return [self.tgo.detection_range(group).meters for group in self.tgo.groups] diff --git a/qt_ui/widgets/map/model/mapmodel.py b/qt_ui/widgets/map/model/mapmodel.py index 32f820b1..d12b948d 100644 --- a/qt_ui/widgets/map/model/mapmodel.py +++ b/qt_ui/widgets/map/model/mapmodel.py @@ -15,7 +15,6 @@ from game.theater import ( from qt_ui.models import GameModel from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from .controlpointjs import ControlPointJs -from .groundobjectjs import GroundObjectJs from .supplyroutejs import SupplyRouteJs @@ -41,7 +40,6 @@ class MapModel(QObject): apiKeyChanged = Signal(str) mapCenterChanged = Signal(list) controlPointsChanged = Signal() - groundObjectsChanged = Signal() supplyRoutesChanged = Signal() mapReset = Signal() @@ -50,7 +48,6 @@ class MapModel(QObject): self.game_model = game_model self._map_center = LatLng(0, 0) self._control_points = [] - self._ground_objects = [] self._supply_routes = [] GameUpdateSignal.get_instance().game_loaded.connect(self.on_game_load) @@ -59,7 +56,6 @@ class MapModel(QObject): def clear(self) -> None: self._control_points = [] self._supply_routes = [] - self._ground_objects = [] self.cleared.emit() def reset(self) -> None: @@ -68,7 +64,6 @@ class MapModel(QObject): return with logged_duration("Map reset"): self.reset_control_points() - self.reset_ground_objects() self.reset_routes() self.mapReset.emit() @@ -99,27 +94,6 @@ class MapModel(QObject): def controlPoints(self) -> List[ControlPointJs]: return self._control_points - def reset_ground_objects(self) -> None: - seen = set() - self._ground_objects = [] - for cp in self.game.theater.controlpoints: - for tgo in cp.ground_objects: - if tgo.name in seen: - continue - seen.add(tgo.name) - - if tgo.is_control_point: - # TGOs that are the CP (CV groups) are an implementation quirk that - # we don't need to expose to the UI. - continue - - self._ground_objects.append(GroundObjectJs(tgo, self.game)) - self.groundObjectsChanged.emit() - - @Property(list, notify=groundObjectsChanged) - def groundObjects(self) -> List[GroundObjectJs]: - return self._ground_objects - def reset_routes(self) -> None: seen = set() self._supply_routes = [] diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index dd19a26e..5d7e6104 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -24,7 +24,7 @@ from game.layout import LAYOUTS from game.server import EventStream, GameContext from game.server.dependencies import QtCallbacks, QtContext from game.server.security import ApiKeyManager -from game.theater import MissionTarget +from game.theater import MissionTarget, TheaterGroundObject from qt_ui import liberation_install from qt_ui.dialogs import Dialog from qt_ui.models import GameModel @@ -36,6 +36,7 @@ from qt_ui.widgets.ato import QAirTaskingOrderPanel from qt_ui.widgets.map.QLiberationMap import QLiberationMap from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.QDebriefingWindow import QDebriefingWindow +from qt_ui.windows.groundobject.QGroundObjectMenu import QGroundObjectMenu from qt_ui.windows.infos.QInfoPanel import QInfoPanel from qt_ui.windows.logs.QLogsWindow import QLogsWindow from qt_ui.windows.newgame.QNewGameWizard import NewGameWizard @@ -49,6 +50,7 @@ from qt_ui.windows.stats.QStatsWindow import QStatsWindow class QLiberationWindow(QMainWindow): new_package_signal = Signal(MissionTarget) + tgo_info_signal = Signal(TheaterGroundObject) def __init__(self, game: Optional[Game], new_map: bool) -> None: super().__init__() @@ -63,8 +65,12 @@ class QLiberationWindow(QMainWindow): self.new_package_signal.connect( lambda target: Dialog.open_new_package_dialog(target, self) ) + self.tgo_info_signal.connect(self.open_tgo_info_dialog) QtContext.set_callbacks( - QtCallbacks(lambda target: self.new_package_signal.emit(target)) + QtCallbacks( + lambda target: self.new_package_signal.emit(target), + lambda tgo: self.tgo_info_signal.emit(tgo), + ) ) Dialog.set_game(self.game_model) self.ato_panel = QAirTaskingOrderPanel(self.game_model) @@ -437,6 +443,9 @@ class QLiberationWindow(QMainWindow): self.debriefing = QDebriefingWindow(debrief) self.debriefing.show() + def open_tgo_info_dialog(self, tgo: TheaterGroundObject) -> None: + QGroundObjectMenu(self, tgo, tgo.control_point, self.game).show() + def _qsettings(self) -> QSettings: return QSettings("DCS Liberation", "Qt UI") diff --git a/resources/ui/map/map.js b/resources/ui/map/map.js index 475d9a33..7abc0fec 100644 --- a/resources/ui/map/map.js +++ b/resources/ui/map/map.js @@ -253,7 +253,6 @@ new QWebChannel(qt.webChannelTransport, function (channel) { game.cleared.connect(clearAllLayers); game.mapCenterChanged.connect(recenterMap); game.controlPointsChanged.connect(drawControlPoints); - game.groundObjectsChanged.connect(drawGroundObjects); game.supplyRoutesChanged.connect(drawSupplyRoutes); game.mapReset.connect(drawAircraft); }); @@ -320,6 +319,10 @@ function handleStreamedEvents(events) { for (const id of events.deleted_front_lines) { FrontLine.popId(id).clear(); } + + for (const id of events.updated_tgos) { + TheaterGroundObject.withId(id).update(); + } } function recenterMap(center) { @@ -523,6 +526,39 @@ function drawControlPoints() { class TheaterGroundObject { constructor(tgo) { this.tgo = tgo; + this.marker = null; + this.threatCircles = []; + this.detectionCircles = []; + TheaterGroundObject.register(this); + } + + static registered = []; + + static register(tgo) { + TheaterGroundObject.registered[tgo.tgo.id] = tgo; + } + + static withId(id) { + return TheaterGroundObject.registered[id]; + } + + showInfoDialog() { + postJson(`/qt/info/tgo/${this.tgo.id}`); + } + + showPackageDialog() { + postJson(`/qt/create-package/tgo/${this.tgo.id}`); + } + + update() { + getJson(`/tgos/${this.tgo.id}`).then((tgo) => { + // Clear explicitly before replacing the TGO in case (though this + // shouldn't happen) the replacement data changes the layer this TGO is + // drawn on. + this.clear(); + this.tgo = tgo; + this.draw(); + }); } icon() { @@ -550,27 +586,50 @@ class TheaterGroundObject { const threatColor = this.tgo.blue ? Colors.Blue : Colors.Red; const detectionColor = this.tgo.blue ? "#bb89ff" : "#eee17b"; - this.tgo.samDetectionRanges.forEach((range) => { - L.circle(this.tgo.position, { - radius: range, - color: detectionColor, - fill: false, - weight: 1, - interactive: false, - }).addTo(detectionLayer); + this.tgo.detection_ranges.forEach((range) => { + this.detectionCircles.push( + L.circle(this.tgo.position, { + radius: range, + color: detectionColor, + fill: false, + weight: 1, + interactive: false, + }).addTo(detectionLayer) + ); }); - this.tgo.samThreatRanges.forEach((range) => { - L.circle(this.tgo.position, { - radius: range, - color: threatColor, - fill: false, - weight: 2, - interactive: false, - }).addTo(threatLayer); + this.tgo.threat_ranges.forEach((range) => { + this.threatCircles.push( + L.circle(this.tgo.position, { + radius: range, + color: threatColor, + fill: false, + weight: 2, + interactive: false, + }).addTo(threatLayer) + ); }); } + clear() { + const detectionLayer = this.tgo.blue + ? blueSamDetectionLayer + : redSamDetectionLayer; + const threatLayer = this.tgo.blue ? blueSamThreatLayer : redSamThreatLayer; + + if (this.marker) { + this.marker.removeFrom(this.layer()); + } + + for (const circle of this.threatCircles) { + circle.removeFrom(threatLayer); + } + + for (const circle of this.detectionCircles) { + circle.removeFrom(detectionLayer); + } + } + draw() { if (!this.tgo.blue && this.tgo.dead) { // Don't bother drawing dead opfor TGOs. Blue is worth showing because @@ -579,14 +638,14 @@ class TheaterGroundObject { return; } - L.marker(this.tgo.position, { icon: this.icon() }) + this.marker = L.marker(this.tgo.position, { icon: this.icon() }) .bindTooltip( `${this.tgo.name} (${ - this.tgo.controlPointName + this.tgo.control_point_name })
${this.tgo.units.join("
")}` ) - .on("click", () => this.tgo.showInfoDialog()) - .on("contextmenu", () => this.tgo.showPackageDialog()) + .on("click", () => this.showInfoDialog()) + .on("contextmenu", () => this.showPackageDialog()) .addTo(this.layer()); this.drawSamThreats(); } @@ -601,8 +660,10 @@ function drawGroundObjects() { redSamDetectionLayer.clearLayers(); blueSamThreatLayer.clearLayers(); redSamThreatLayer.clearLayers(); - game.groundObjects.forEach((tgo) => { - new TheaterGroundObject(tgo).draw(); + getJson("/tgos").then((tgos) => { + for (const tgo of tgos) { + new TheaterGroundObject(tgo).draw(); + } }); } @@ -683,7 +744,7 @@ class FrontLine { } openNewPackageDialog() { - postJson(`/package-dialog/front-line/${this.id}`); + postJson(`/qt/create-package/front-line/${this.id}`); } }