mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
refactor to enum typing and many other fixes fix tests attempt to fix some typescript more typescript fixes more typescript test fixes revert all API changes update to pydcs mypy fixes Use properties to check if player is blue/red/neutral update requirements.txt black -_- bump pydcs and fix mypy add opponent property bump pydcs
131 lines
4.6 KiB
Python
131 lines
4.6 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any, TYPE_CHECKING
|
|
|
|
from dcs import Point
|
|
from pydantic import BaseModel
|
|
|
|
from game.server.leaflet import LeafletPoint
|
|
|
|
if TYPE_CHECKING:
|
|
from game import Game
|
|
from game.theater import ControlPoint
|
|
from game.transfers import MultiGroupTransport, TransportMap
|
|
|
|
|
|
class TransportFinder:
|
|
def __init__(
|
|
self, game: Game, control_point_a: ControlPoint, control_point_b: ControlPoint
|
|
) -> None:
|
|
self.game = game
|
|
self.control_point_a = control_point_a
|
|
self.control_point_b = control_point_b
|
|
|
|
def find_in_transport_map(
|
|
self, transport_map: TransportMap[Any]
|
|
) -> 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, sea_route: bool) -> list[MultiGroupTransport]:
|
|
if 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)
|
|
|
|
def describe_active_transports(self, sea_route: bool) -> list[str]:
|
|
transports = self.find_transports(sea_route)
|
|
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
|
|
|
|
|
|
class SupplyRouteJs(BaseModel):
|
|
id: str
|
|
points: list[LeafletPoint]
|
|
front_active: bool
|
|
is_sea: bool
|
|
blue: bool
|
|
active_transports: list[str]
|
|
|
|
class Config:
|
|
title = "SupplyRoute"
|
|
|
|
@staticmethod
|
|
def for_link(
|
|
game: Game, a: ControlPoint, b: ControlPoint, points: list[Point], sea: bool
|
|
) -> SupplyRouteJs:
|
|
if a.captured.is_blue:
|
|
blue = True
|
|
else:
|
|
blue = False
|
|
return SupplyRouteJs(
|
|
# Although these are not persistent objects in the backend, the frontend
|
|
# needs unique IDs for anything that it will use in a list. That means that
|
|
# any data that we expose as a list most likely needs a unique ID. List
|
|
# indexes are **not** sufficient as IDs across game loads, as any indexes
|
|
# that persist between games will not be updated in the UI.
|
|
#
|
|
# Generating a UUID for these ephemeral objects is awkward, but does not
|
|
# cause any issues since the only thing the ID is used for is to
|
|
# disambiguate objects across save games.
|
|
#
|
|
# https://reactjs.org/docs/lists-and-keys.html#keys
|
|
# https://github.com/dcs-liberation/dcs_liberation/issues/2167
|
|
id=f"{a}:{b}",
|
|
points=[p.latlng() for p in points],
|
|
front_active=not sea and a.front_is_active(b),
|
|
is_sea=sea,
|
|
blue=blue,
|
|
active_transports=TransportFinder(game, a, b).describe_active_transports(
|
|
sea
|
|
),
|
|
)
|
|
|
|
@staticmethod
|
|
def all_in_game(game: Game) -> list[SupplyRouteJs]:
|
|
seen = set()
|
|
routes = []
|
|
for control_point in game.theater.controlpoints:
|
|
seen.add(control_point)
|
|
for destination, route in control_point.convoy_routes.items():
|
|
if destination in seen:
|
|
continue
|
|
routes.append(
|
|
SupplyRouteJs.for_link(
|
|
game, control_point, destination, list(route), sea=False
|
|
)
|
|
)
|
|
for destination, route in control_point.shipping_lanes.items():
|
|
if destination in seen:
|
|
continue
|
|
if not destination.is_friendly_to(control_point):
|
|
continue
|
|
routes.append(
|
|
SupplyRouteJs.for_link(
|
|
game, control_point, destination, list(route), sea=True
|
|
)
|
|
)
|
|
return routes
|