mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Move TGOs out of MapModel.
This commit is contained in:
parent
d0ad554e14
commit
c5c596dc2f
@ -4,10 +4,11 @@ from .database import Database
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.ato import Flight
|
from game.ato import Flight
|
||||||
from game.theater import FrontLine
|
from game.theater import FrontLine, TheaterGroundObject
|
||||||
|
|
||||||
|
|
||||||
class GameDb:
|
class GameDb:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.flights: Database[Flight] = Database()
|
self.flights: Database[Flight] = Database()
|
||||||
self.front_lines: Database[FrontLine] = Database()
|
self.front_lines: Database[FrontLine] = Database()
|
||||||
|
self.tgos: Database[TheaterGroundObject] = Database()
|
||||||
|
|||||||
@ -278,6 +278,8 @@ class Game:
|
|||||||
|
|
||||||
for control_point in self.theater.controlpoints:
|
for control_point in self.theater.controlpoints:
|
||||||
control_point.initialize_turn_0()
|
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.blue.preinit_turn_0()
|
||||||
self.red.preinit_turn_0()
|
self.red.preinit_turn_0()
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from . import (
|
|||||||
frontlines,
|
frontlines,
|
||||||
mapzones,
|
mapzones,
|
||||||
navmesh,
|
navmesh,
|
||||||
packagedialog,
|
qt,
|
||||||
supplyroutes,
|
supplyroutes,
|
||||||
tgos,
|
tgos,
|
||||||
waypoints,
|
waypoints,
|
||||||
@ -29,7 +29,7 @@ app.include_router(flights.router)
|
|||||||
app.include_router(frontlines.router)
|
app.include_router(frontlines.router)
|
||||||
app.include_router(mapzones.router)
|
app.include_router(mapzones.router)
|
||||||
app.include_router(navmesh.router)
|
app.include_router(navmesh.router)
|
||||||
app.include_router(packagedialog.router)
|
app.include_router(qt.router)
|
||||||
app.include_router(supplyroutes.router)
|
app.include_router(supplyroutes.router)
|
||||||
app.include_router(tgos.router)
|
app.include_router(tgos.router)
|
||||||
app.include_router(waypoints.router)
|
app.include_router(waypoints.router)
|
||||||
|
|||||||
@ -2,7 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from typing import Callable, TYPE_CHECKING
|
from typing import Callable, TYPE_CHECKING
|
||||||
|
|
||||||
from game.theater import MissionTarget
|
from game.theater import MissionTarget, TheaterGroundObject
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game import Game
|
from game import Game
|
||||||
@ -28,8 +28,13 @@ class GameContext:
|
|||||||
|
|
||||||
|
|
||||||
class QtCallbacks:
|
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.create_new_package = create_new_package
|
||||||
|
self.show_tgo_info = show_tgo_info
|
||||||
|
|
||||||
|
|
||||||
class QtContext:
|
class QtContext:
|
||||||
|
|||||||
@ -31,6 +31,7 @@ class GameUpdateEventsJs(BaseModel):
|
|||||||
new_front_lines: list[FrontLineJs]
|
new_front_lines: list[FrontLineJs]
|
||||||
updated_front_lines: set[UUID]
|
updated_front_lines: set[UUID]
|
||||||
deleted_front_lines: set[UUID]
|
deleted_front_lines: set[UUID]
|
||||||
|
updated_tgos: set[UUID]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_events(cls, events: GameUpdateEvents, game: Game) -> GameUpdateEventsJs:
|
def from_events(cls, events: GameUpdateEvents, game: Game) -> GameUpdateEventsJs:
|
||||||
@ -62,4 +63,5 @@ class GameUpdateEventsJs(BaseModel):
|
|||||||
],
|
],
|
||||||
updated_front_lines=events.updated_front_lines,
|
updated_front_lines=events.updated_front_lines,
|
||||||
deleted_front_lines=events.deleted_front_lines,
|
deleted_front_lines=events.deleted_front_lines,
|
||||||
|
updated_tgos=events.updated_tgos,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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)
|
|
||||||
35
game/server/qt/routes.py
Normal file
35
game/server/qt/routes.py
Normal file
@ -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))
|
||||||
@ -1,5 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from game.server.leaflet import LeafletPoint
|
from game.server.leaflet import LeafletPoint
|
||||||
@ -7,16 +9,17 @@ from game.theater import TheaterGroundObject
|
|||||||
|
|
||||||
|
|
||||||
class TgoJs(BaseModel):
|
class TgoJs(BaseModel):
|
||||||
|
id: UUID
|
||||||
name: str
|
name: str
|
||||||
control_point_name: str
|
control_point_name: str
|
||||||
category: str
|
category: str
|
||||||
blue: bool
|
blue: bool
|
||||||
position: LeafletPoint
|
position: LeafletPoint
|
||||||
units: list[str]
|
units: list[str] # TODO: Event stream
|
||||||
threat_ranges: list[float]
|
threat_ranges: list[float] # TODO: Event stream
|
||||||
detection_ranges: list[float]
|
detection_ranges: list[float] # TODO: Event stream
|
||||||
dead: bool
|
dead: bool # TODO: Event stream
|
||||||
sidc: str
|
sidc: str # TODO: Event stream
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def for_tgo(tgo: TheaterGroundObject) -> TgoJs:
|
def for_tgo(tgo: TheaterGroundObject) -> TgoJs:
|
||||||
@ -29,6 +32,7 @@ class TgoJs(BaseModel):
|
|||||||
tgo.detection_range(group).meters for group in tgo.groups
|
tgo.detection_range(group).meters for group in tgo.groups
|
||||||
]
|
]
|
||||||
return TgoJs(
|
return TgoJs(
|
||||||
|
id=tgo.id,
|
||||||
name=tgo.name,
|
name=tgo.name,
|
||||||
control_point_name=tgo.control_point.name,
|
control_point_name=tgo.control_point.name,
|
||||||
category=tgo.category,
|
category=tgo.category,
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
from uuid import UUID
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends
|
||||||
|
|
||||||
from game import Game
|
from game import Game
|
||||||
@ -15,3 +17,8 @@ def list_tgos(game: Game = Depends(GameContext.get)) -> list[TgoJs]:
|
|||||||
if not tgo.is_control_point:
|
if not tgo.is_control_point:
|
||||||
tgos.append(TgoJs.for_tgo(tgo))
|
tgos.append(TgoJs.for_tgo(tgo))
|
||||||
return tgos
|
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))
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from dcs import Point
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.ato import Flight, Package
|
from game.ato import Flight, Package
|
||||||
from game.sim.combat import FrozenCombat
|
from game.sim.combat import FrozenCombat
|
||||||
from game.theater import FrontLine
|
from game.theater import FrontLine, TheaterGroundObject
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -30,6 +30,7 @@ class GameUpdateEvents:
|
|||||||
new_front_lines: set[FrontLine] = field(default_factory=set)
|
new_front_lines: set[FrontLine] = field(default_factory=set)
|
||||||
updated_front_lines: set[UUID] = field(default_factory=set)
|
updated_front_lines: set[UUID] = field(default_factory=set)
|
||||||
deleted_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
|
shutting_down: bool = False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -111,6 +112,10 @@ class GameUpdateEvents:
|
|||||||
self.deleted_front_lines.add(front_line.id)
|
self.deleted_front_lines.add(front_line.id)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
def update_tgo(self, tgo: TheaterGroundObject) -> GameUpdateEvents:
|
||||||
|
self.updated_tgos.add(tgo.id)
|
||||||
|
return self
|
||||||
|
|
||||||
def shut_down(self) -> GameUpdateEvents:
|
def shut_down(self) -> GameUpdateEvents:
|
||||||
self.shutting_down = True
|
self.shutting_down = True
|
||||||
return self
|
return self
|
||||||
|
|||||||
@ -30,7 +30,7 @@ class MissionResultsProcessor:
|
|||||||
self.commit_convoy_losses(debriefing)
|
self.commit_convoy_losses(debriefing)
|
||||||
self.commit_cargo_ship_losses(debriefing)
|
self.commit_cargo_ship_losses(debriefing)
|
||||||
self.commit_airlift_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_damaged_runways(debriefing)
|
||||||
self.commit_captures(debriefing, events)
|
self.commit_captures(debriefing, events)
|
||||||
self.commit_front_line_battle_impact(debriefing, events)
|
self.commit_front_line_battle_impact(debriefing, events)
|
||||||
@ -131,11 +131,11 @@ class MissionResultsProcessor:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@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:
|
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:
|
for scenery_object_loss in debriefing.scenery_object_losses:
|
||||||
scenery_object_loss.ground_unit.kill()
|
scenery_object_loss.ground_unit.kill(events)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def commit_damaged_runways(debriefing: Debriefing) -> None:
|
def commit_damaged_runways(debriefing: Debriefing) -> None:
|
||||||
|
|||||||
@ -730,11 +730,11 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
|||||||
for squadron in self.squadrons:
|
for squadron in self.squadrons:
|
||||||
self._retreat_squadron(game, squadron)
|
self._retreat_squadron(game, squadron)
|
||||||
|
|
||||||
def depopulate_uncapturable_tgos(self) -> None:
|
def depopulate_uncapturable_tgos(self, events: GameUpdateEvents) -> None:
|
||||||
# TODO Rework this.
|
# TODO Rework this.
|
||||||
for tgo in self.connected_objectives:
|
for tgo in self.connected_objectives:
|
||||||
if not tgo.capturable:
|
if not tgo.capturable:
|
||||||
tgo.clear()
|
tgo.clear(events)
|
||||||
|
|
||||||
# TODO: Should be Airbase specific.
|
# TODO: Should be Airbase specific.
|
||||||
def capture(self, game: Game, events: GameUpdateEvents, for_player: bool) -> None:
|
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.ground_unit_orders.refund_all(self.coalition)
|
||||||
self.retreat_ground_units(game)
|
self.retreat_ground_units(game)
|
||||||
self.retreat_air_units(game)
|
self.retreat_air_units(game)
|
||||||
self.depopulate_uncapturable_tgos()
|
self.depopulate_uncapturable_tgos(events)
|
||||||
|
|
||||||
self._coalition = new_coalition
|
self._coalition = new_coalition
|
||||||
self.base.set_strength_to_minimum()
|
self.base.set_strength_to_minimum()
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import itertools
|
import itertools
|
||||||
|
import uuid
|
||||||
from abc import ABC
|
from abc import ABC
|
||||||
from typing import Iterator, List, Optional, TYPE_CHECKING
|
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
|
from ..utils import Distance, Heading, meters
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
|
from game.sim import GameUpdateEvents
|
||||||
from .theatergroup import TheaterUnit, TheaterGroup
|
from .theatergroup import TheaterUnit, TheaterGroup
|
||||||
from .controlpoint import ControlPoint
|
from .controlpoint import ControlPoint
|
||||||
from ..ato.flighttype import FlightType
|
from ..ato.flighttype import FlightType
|
||||||
@ -62,6 +64,7 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC):
|
|||||||
sea_object: bool,
|
sea_object: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(name, position)
|
super().__init__(name, position)
|
||||||
|
self.id = uuid.uuid4()
|
||||||
self.category = category
|
self.category = category
|
||||||
self.heading = heading
|
self.heading = heading
|
||||||
self.control_point = control_point
|
self.control_point = control_point
|
||||||
@ -212,8 +215,9 @@ class TheaterGroundObject(MissionTarget, SidcDescribable, ABC):
|
|||||||
def mark_locations(self) -> Iterator[Point]:
|
def mark_locations(self) -> Iterator[Point]:
|
||||||
yield self.position
|
yield self.position
|
||||||
|
|
||||||
def clear(self) -> None:
|
def clear(self, events: GameUpdateEvents) -> None:
|
||||||
self.groups = []
|
self.groups = []
|
||||||
|
events.update_tgo(self)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def capturable(self) -> bool:
|
def capturable(self) -> bool:
|
||||||
|
|||||||
@ -1,22 +1,20 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
|
||||||
from dataclasses import dataclass
|
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.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.groundunittype import GroundUnitType
|
||||||
from game.dcs.shipunittype import ShipUnitType
|
from game.dcs.shipunittype import ShipUnitType
|
||||||
from game.dcs.unittype import UnitType
|
from game.dcs.unittype import UnitType
|
||||||
from dcs.unittype import UnitType as DcsUnitType
|
|
||||||
|
|
||||||
from game.point_with_heading import PointWithHeading
|
from game.point_with_heading import PointWithHeading
|
||||||
from game.utils import Heading
|
from game.utils import Heading
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
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
|
from game.theater import TheaterGroundObject
|
||||||
|
|
||||||
|
|
||||||
@ -58,8 +56,9 @@ class TheaterUnit:
|
|||||||
# None for not available StaticTypes
|
# None for not available StaticTypes
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def kill(self) -> None:
|
def kill(self, events: GameUpdateEvents) -> None:
|
||||||
self.alive = False
|
self.alive = False
|
||||||
|
events.update_tgo(self.ground_object)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def unit_name(self) -> str:
|
def unit_name(self) -> str:
|
||||||
|
|||||||
@ -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]
|
|
||||||
@ -15,7 +15,6 @@ from game.theater import (
|
|||||||
from qt_ui.models import GameModel
|
from qt_ui.models import GameModel
|
||||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||||
from .controlpointjs import ControlPointJs
|
from .controlpointjs import ControlPointJs
|
||||||
from .groundobjectjs import GroundObjectJs
|
|
||||||
from .supplyroutejs import SupplyRouteJs
|
from .supplyroutejs import SupplyRouteJs
|
||||||
|
|
||||||
|
|
||||||
@ -41,7 +40,6 @@ class MapModel(QObject):
|
|||||||
apiKeyChanged = Signal(str)
|
apiKeyChanged = Signal(str)
|
||||||
mapCenterChanged = Signal(list)
|
mapCenterChanged = Signal(list)
|
||||||
controlPointsChanged = Signal()
|
controlPointsChanged = Signal()
|
||||||
groundObjectsChanged = Signal()
|
|
||||||
supplyRoutesChanged = Signal()
|
supplyRoutesChanged = Signal()
|
||||||
mapReset = Signal()
|
mapReset = Signal()
|
||||||
|
|
||||||
@ -50,7 +48,6 @@ class MapModel(QObject):
|
|||||||
self.game_model = game_model
|
self.game_model = game_model
|
||||||
self._map_center = LatLng(0, 0)
|
self._map_center = LatLng(0, 0)
|
||||||
self._control_points = []
|
self._control_points = []
|
||||||
self._ground_objects = []
|
|
||||||
self._supply_routes = []
|
self._supply_routes = []
|
||||||
|
|
||||||
GameUpdateSignal.get_instance().game_loaded.connect(self.on_game_load)
|
GameUpdateSignal.get_instance().game_loaded.connect(self.on_game_load)
|
||||||
@ -59,7 +56,6 @@ class MapModel(QObject):
|
|||||||
def clear(self) -> None:
|
def clear(self) -> None:
|
||||||
self._control_points = []
|
self._control_points = []
|
||||||
self._supply_routes = []
|
self._supply_routes = []
|
||||||
self._ground_objects = []
|
|
||||||
self.cleared.emit()
|
self.cleared.emit()
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
@ -68,7 +64,6 @@ class MapModel(QObject):
|
|||||||
return
|
return
|
||||||
with logged_duration("Map reset"):
|
with logged_duration("Map reset"):
|
||||||
self.reset_control_points()
|
self.reset_control_points()
|
||||||
self.reset_ground_objects()
|
|
||||||
self.reset_routes()
|
self.reset_routes()
|
||||||
self.mapReset.emit()
|
self.mapReset.emit()
|
||||||
|
|
||||||
@ -99,27 +94,6 @@ class MapModel(QObject):
|
|||||||
def controlPoints(self) -> List[ControlPointJs]:
|
def controlPoints(self) -> List[ControlPointJs]:
|
||||||
return self._control_points
|
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:
|
def reset_routes(self) -> None:
|
||||||
seen = set()
|
seen = set()
|
||||||
self._supply_routes = []
|
self._supply_routes = []
|
||||||
|
|||||||
@ -24,7 +24,7 @@ from game.layout import LAYOUTS
|
|||||||
from game.server import EventStream, GameContext
|
from game.server import EventStream, GameContext
|
||||||
from game.server.dependencies import QtCallbacks, QtContext
|
from game.server.dependencies import QtCallbacks, QtContext
|
||||||
from game.server.security import ApiKeyManager
|
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 import liberation_install
|
||||||
from qt_ui.dialogs import Dialog
|
from qt_ui.dialogs import Dialog
|
||||||
from qt_ui.models import GameModel
|
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.widgets.map.QLiberationMap import QLiberationMap
|
||||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||||
from qt_ui.windows.QDebriefingWindow import QDebriefingWindow
|
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.infos.QInfoPanel import QInfoPanel
|
||||||
from qt_ui.windows.logs.QLogsWindow import QLogsWindow
|
from qt_ui.windows.logs.QLogsWindow import QLogsWindow
|
||||||
from qt_ui.windows.newgame.QNewGameWizard import NewGameWizard
|
from qt_ui.windows.newgame.QNewGameWizard import NewGameWizard
|
||||||
@ -49,6 +50,7 @@ from qt_ui.windows.stats.QStatsWindow import QStatsWindow
|
|||||||
|
|
||||||
class QLiberationWindow(QMainWindow):
|
class QLiberationWindow(QMainWindow):
|
||||||
new_package_signal = Signal(MissionTarget)
|
new_package_signal = Signal(MissionTarget)
|
||||||
|
tgo_info_signal = Signal(TheaterGroundObject)
|
||||||
|
|
||||||
def __init__(self, game: Optional[Game], new_map: bool) -> None:
|
def __init__(self, game: Optional[Game], new_map: bool) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@ -63,8 +65,12 @@ class QLiberationWindow(QMainWindow):
|
|||||||
self.new_package_signal.connect(
|
self.new_package_signal.connect(
|
||||||
lambda target: Dialog.open_new_package_dialog(target, self)
|
lambda target: Dialog.open_new_package_dialog(target, self)
|
||||||
)
|
)
|
||||||
|
self.tgo_info_signal.connect(self.open_tgo_info_dialog)
|
||||||
QtContext.set_callbacks(
|
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)
|
Dialog.set_game(self.game_model)
|
||||||
self.ato_panel = QAirTaskingOrderPanel(self.game_model)
|
self.ato_panel = QAirTaskingOrderPanel(self.game_model)
|
||||||
@ -437,6 +443,9 @@ class QLiberationWindow(QMainWindow):
|
|||||||
self.debriefing = QDebriefingWindow(debrief)
|
self.debriefing = QDebriefingWindow(debrief)
|
||||||
self.debriefing.show()
|
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:
|
def _qsettings(self) -> QSettings:
|
||||||
return QSettings("DCS Liberation", "Qt UI")
|
return QSettings("DCS Liberation", "Qt UI")
|
||||||
|
|
||||||
|
|||||||
@ -253,7 +253,6 @@ new QWebChannel(qt.webChannelTransport, function (channel) {
|
|||||||
game.cleared.connect(clearAllLayers);
|
game.cleared.connect(clearAllLayers);
|
||||||
game.mapCenterChanged.connect(recenterMap);
|
game.mapCenterChanged.connect(recenterMap);
|
||||||
game.controlPointsChanged.connect(drawControlPoints);
|
game.controlPointsChanged.connect(drawControlPoints);
|
||||||
game.groundObjectsChanged.connect(drawGroundObjects);
|
|
||||||
game.supplyRoutesChanged.connect(drawSupplyRoutes);
|
game.supplyRoutesChanged.connect(drawSupplyRoutes);
|
||||||
game.mapReset.connect(drawAircraft);
|
game.mapReset.connect(drawAircraft);
|
||||||
});
|
});
|
||||||
@ -320,6 +319,10 @@ function handleStreamedEvents(events) {
|
|||||||
for (const id of events.deleted_front_lines) {
|
for (const id of events.deleted_front_lines) {
|
||||||
FrontLine.popId(id).clear();
|
FrontLine.popId(id).clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const id of events.updated_tgos) {
|
||||||
|
TheaterGroundObject.withId(id).update();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function recenterMap(center) {
|
function recenterMap(center) {
|
||||||
@ -523,6 +526,39 @@ function drawControlPoints() {
|
|||||||
class TheaterGroundObject {
|
class TheaterGroundObject {
|
||||||
constructor(tgo) {
|
constructor(tgo) {
|
||||||
this.tgo = 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() {
|
icon() {
|
||||||
@ -550,27 +586,50 @@ class TheaterGroundObject {
|
|||||||
const threatColor = this.tgo.blue ? Colors.Blue : Colors.Red;
|
const threatColor = this.tgo.blue ? Colors.Blue : Colors.Red;
|
||||||
const detectionColor = this.tgo.blue ? "#bb89ff" : "#eee17b";
|
const detectionColor = this.tgo.blue ? "#bb89ff" : "#eee17b";
|
||||||
|
|
||||||
this.tgo.samDetectionRanges.forEach((range) => {
|
this.tgo.detection_ranges.forEach((range) => {
|
||||||
L.circle(this.tgo.position, {
|
this.detectionCircles.push(
|
||||||
radius: range,
|
L.circle(this.tgo.position, {
|
||||||
color: detectionColor,
|
radius: range,
|
||||||
fill: false,
|
color: detectionColor,
|
||||||
weight: 1,
|
fill: false,
|
||||||
interactive: false,
|
weight: 1,
|
||||||
}).addTo(detectionLayer);
|
interactive: false,
|
||||||
|
}).addTo(detectionLayer)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.tgo.samThreatRanges.forEach((range) => {
|
this.tgo.threat_ranges.forEach((range) => {
|
||||||
L.circle(this.tgo.position, {
|
this.threatCircles.push(
|
||||||
radius: range,
|
L.circle(this.tgo.position, {
|
||||||
color: threatColor,
|
radius: range,
|
||||||
fill: false,
|
color: threatColor,
|
||||||
weight: 2,
|
fill: false,
|
||||||
interactive: false,
|
weight: 2,
|
||||||
}).addTo(threatLayer);
|
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() {
|
draw() {
|
||||||
if (!this.tgo.blue && this.tgo.dead) {
|
if (!this.tgo.blue && this.tgo.dead) {
|
||||||
// Don't bother drawing dead opfor TGOs. Blue is worth showing because
|
// Don't bother drawing dead opfor TGOs. Blue is worth showing because
|
||||||
@ -579,14 +638,14 @@ class TheaterGroundObject {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
L.marker(this.tgo.position, { icon: this.icon() })
|
this.marker = L.marker(this.tgo.position, { icon: this.icon() })
|
||||||
.bindTooltip(
|
.bindTooltip(
|
||||||
`${this.tgo.name} (${
|
`${this.tgo.name} (${
|
||||||
this.tgo.controlPointName
|
this.tgo.control_point_name
|
||||||
})<br />${this.tgo.units.join("<br />")}`
|
})<br />${this.tgo.units.join("<br />")}`
|
||||||
)
|
)
|
||||||
.on("click", () => this.tgo.showInfoDialog())
|
.on("click", () => this.showInfoDialog())
|
||||||
.on("contextmenu", () => this.tgo.showPackageDialog())
|
.on("contextmenu", () => this.showPackageDialog())
|
||||||
.addTo(this.layer());
|
.addTo(this.layer());
|
||||||
this.drawSamThreats();
|
this.drawSamThreats();
|
||||||
}
|
}
|
||||||
@ -601,8 +660,10 @@ function drawGroundObjects() {
|
|||||||
redSamDetectionLayer.clearLayers();
|
redSamDetectionLayer.clearLayers();
|
||||||
blueSamThreatLayer.clearLayers();
|
blueSamThreatLayer.clearLayers();
|
||||||
redSamThreatLayer.clearLayers();
|
redSamThreatLayer.clearLayers();
|
||||||
game.groundObjects.forEach((tgo) => {
|
getJson("/tgos").then((tgos) => {
|
||||||
new TheaterGroundObject(tgo).draw();
|
for (const tgo of tgos) {
|
||||||
|
new TheaterGroundObject(tgo).draw();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -683,7 +744,7 @@ class FrontLine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
openNewPackageDialog() {
|
openNewPackageDialog() {
|
||||||
postJson(`/package-dialog/front-line/${this.id}`);
|
postJson(`/qt/create-package/front-line/${this.id}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user