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}`);
}
}