Eclipse/Druss99 31c80dfd02 refactor of previous commits
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
2025-10-19 19:34:38 +02:00

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