mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Stop ad-hoc constructing FrontLines.
The UI needs to be able to identify these to the server and vice versa, so they'll need IDs that don't change. Rather than constructing an ID based on the control points names, make them an owned part of the control point. The constructed ID would be fine, but a UUID will make them more suitable for the database, and this was always fairly gross anyway. Some follow up work if anyone is interested: a bunch of the data that's computed in the various properties can now probably be computed *once* and persisted to the FrontLine type.
This commit is contained in:
parent
4dfc42528d
commit
e5f4974e9a
@ -273,6 +273,9 @@ class Game:
|
|||||||
"""Initialization for the first turn of the game."""
|
"""Initialization for the first turn of the game."""
|
||||||
from .sim import GameUpdateEvents
|
from .sim import GameUpdateEvents
|
||||||
|
|
||||||
|
for control_point in self.theater.controlpoints:
|
||||||
|
control_point.initialize_turn_0()
|
||||||
|
|
||||||
self.blue.preinit_turn_0()
|
self.blue.preinit_turn_0()
|
||||||
self.red.preinit_turn_0()
|
self.red.preinit_turn_0()
|
||||||
# We don't need to actually stream events for turn zero because we haven't given
|
# We don't need to actually stream events for turn zero because we haven't given
|
||||||
|
|||||||
@ -10,7 +10,6 @@ from dcs.unit import Vehicle
|
|||||||
from dcs.unitgroup import VehicleGroup
|
from dcs.unitgroup import VehicleGroup
|
||||||
|
|
||||||
from game.dcs.groundunittype import GroundUnitType
|
from game.dcs.groundunittype import GroundUnitType
|
||||||
from game.theater import FrontLine
|
|
||||||
from game.transfers import Convoy
|
from game.transfers import Convoy
|
||||||
from game.unitmap import UnitMap
|
from game.unitmap import UnitMap
|
||||||
from game.utils import kph
|
from game.utils import kph
|
||||||
@ -46,7 +45,7 @@ class ConvoyGenerator:
|
|||||||
# convoys_travel_full_distance is disabled, so have the convoy only move the first segment on the route.
|
# convoys_travel_full_distance is disabled, so have the convoy only move the first segment on the route.
|
||||||
# This option aims to remove long routes for ground vehicles between control points,
|
# This option aims to remove long routes for ground vehicles between control points,
|
||||||
# since the CPU load for pathfinding long routes on DCS can be pretty heavy.
|
# since the CPU load for pathfinding long routes on DCS can be pretty heavy.
|
||||||
frontline = FrontLine(convoy.origin, convoy.destination)
|
frontline = convoy.origin.front_line_with(convoy.destination)
|
||||||
|
|
||||||
# Select the first route segment from the origin towards the destination
|
# Select the first route segment from the origin towards the destination
|
||||||
# so the convoy spawns at the origin CP. This allows the convoy to be
|
# so the convoy spawns at the origin CP. This allows the convoy to be
|
||||||
|
|||||||
@ -137,11 +137,8 @@ class ConflictTheater:
|
|||||||
return list(self.control_points_for(player=True))
|
return list(self.control_points_for(player=True))
|
||||||
|
|
||||||
def conflicts(self) -> Iterator[FrontLine]:
|
def conflicts(self) -> Iterator[FrontLine]:
|
||||||
for player_cp in [x for x in self.controlpoints if x.captured]:
|
for cp in self.player_points():
|
||||||
for enemy_cp in [
|
yield from cp.front_lines.values()
|
||||||
x for x in player_cp.connected_points if not x.is_friendly_to(player_cp)
|
|
||||||
]:
|
|
||||||
yield FrontLine(player_cp, enemy_cp)
|
|
||||||
|
|
||||||
def enemy_points(self) -> List[ControlPoint]:
|
def enemy_points(self) -> List[ControlPoint]:
|
||||||
return list(self.control_points_for(player=False))
|
return list(self.control_points_for(player=False))
|
||||||
|
|||||||
@ -43,6 +43,7 @@ from game.sidc import (
|
|||||||
)
|
)
|
||||||
from game.utils import Heading
|
from game.utils import Heading
|
||||||
from .base import Base
|
from .base import Base
|
||||||
|
from .frontline import FrontLine
|
||||||
from .missiontarget import MissionTarget
|
from .missiontarget import MissionTarget
|
||||||
from .theatergroundobject import (
|
from .theatergroundobject import (
|
||||||
GenericCarrierGroundObject,
|
GenericCarrierGroundObject,
|
||||||
@ -62,7 +63,7 @@ if TYPE_CHECKING:
|
|||||||
from game.squadrons.squadron import Squadron
|
from game.squadrons.squadron import Squadron
|
||||||
from ..coalition import Coalition
|
from ..coalition import Coalition
|
||||||
from ..transfers import PendingTransfers
|
from ..transfers import PendingTransfers
|
||||||
from . import ConflictTheater
|
from .conflicttheater import ConflictTheater
|
||||||
|
|
||||||
FREE_FRONTLINE_UNIT_SUPPLY: int = 15
|
FREE_FRONTLINE_UNIT_SUPPLY: int = 15
|
||||||
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION: int = 12
|
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION: int = 12
|
||||||
@ -287,15 +288,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
|||||||
# the distance of the circle on the map.
|
# the distance of the circle on the map.
|
||||||
CAPTURE_DISTANCE = nautical_miles(2)
|
CAPTURE_DISTANCE = nautical_miles(2)
|
||||||
|
|
||||||
position = None # type: Point
|
|
||||||
name = None # type: str
|
|
||||||
|
|
||||||
has_frontline = True
|
|
||||||
|
|
||||||
alt = 0
|
|
||||||
|
|
||||||
# TODO: Only airbases have IDs.
|
# TODO: Only airbases have IDs.
|
||||||
# TODO: has_frontline is only reasonable for airbases.
|
|
||||||
# TODO: cptype is obsolete.
|
# TODO: cptype is obsolete.
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -304,7 +297,6 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
|||||||
position: Point,
|
position: Point,
|
||||||
at: StartingPosition,
|
at: StartingPosition,
|
||||||
starts_blue: bool,
|
starts_blue: bool,
|
||||||
has_frontline: bool = True,
|
|
||||||
cptype: ControlPointType = ControlPointType.AIRBASE,
|
cptype: ControlPointType = ControlPointType.AIRBASE,
|
||||||
) -> None:
|
) -> None:
|
||||||
super().__init__(name, position)
|
super().__init__(name, position)
|
||||||
@ -319,8 +311,8 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
|||||||
|
|
||||||
self._coalition: Optional[Coalition] = None
|
self._coalition: Optional[Coalition] = None
|
||||||
self.captured_invert = False
|
self.captured_invert = False
|
||||||
|
self.front_lines: dict[ControlPoint, FrontLine] = {}
|
||||||
# TODO: Should be Airbase specific.
|
# TODO: Should be Airbase specific.
|
||||||
self.has_frontline = has_frontline
|
|
||||||
self.connected_points: List[ControlPoint] = []
|
self.connected_points: List[ControlPoint] = []
|
||||||
self.convoy_routes: Dict[ControlPoint, Tuple[Point, ...]] = {}
|
self.convoy_routes: Dict[ControlPoint, Tuple[Point, ...]] = {}
|
||||||
self.shipping_lanes: Dict[ControlPoint, Tuple[Point, ...]] = {}
|
self.shipping_lanes: Dict[ControlPoint, Tuple[Point, ...]] = {}
|
||||||
@ -347,6 +339,41 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
|||||||
assert self._coalition is None
|
assert self._coalition is None
|
||||||
self._coalition = game.coalition_for(self.starts_blue)
|
self._coalition = game.coalition_for(self.starts_blue)
|
||||||
|
|
||||||
|
def initialize_turn_0(self) -> None:
|
||||||
|
self._recreate_front_lines()
|
||||||
|
|
||||||
|
def _recreate_front_lines(self) -> None:
|
||||||
|
self._clear_front_lines()
|
||||||
|
for connection in self.convoy_routes.keys():
|
||||||
|
if not connection.front_line_active_with(
|
||||||
|
self
|
||||||
|
) and not connection.is_friendly_to(self):
|
||||||
|
self._create_front_line_with(connection)
|
||||||
|
|
||||||
|
def _create_front_line_with(self, connection: ControlPoint) -> None:
|
||||||
|
blue, red = FrontLine.sort_control_points(self, connection)
|
||||||
|
front = FrontLine(blue, red)
|
||||||
|
self.front_lines[connection] = front
|
||||||
|
connection.front_lines[self] = front
|
||||||
|
|
||||||
|
def _remove_front_line_with(self, connection: ControlPoint) -> None:
|
||||||
|
del self.front_lines[connection]
|
||||||
|
del connection.front_lines[self]
|
||||||
|
|
||||||
|
def _clear_front_lines(self) -> None:
|
||||||
|
for opponent in list(self.front_lines.keys()):
|
||||||
|
self._remove_front_line_with(opponent)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def has_frontline(self) -> bool:
|
||||||
|
return bool(self.front_lines)
|
||||||
|
|
||||||
|
def front_line_active_with(self, other: ControlPoint) -> bool:
|
||||||
|
return other in self.front_lines
|
||||||
|
|
||||||
|
def front_line_with(self, other: ControlPoint) -> FrontLine:
|
||||||
|
return self.front_lines[other]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def captured(self) -> bool:
|
def captured(self) -> bool:
|
||||||
return self.coalition.player
|
return self.coalition.player
|
||||||
@ -695,6 +722,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
|||||||
|
|
||||||
self._coalition = new_coalition
|
self._coalition = new_coalition
|
||||||
self.base.set_strength_to_minimum()
|
self.base.set_strength_to_minimum()
|
||||||
|
self._recreate_front_lines()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def required_aircraft_start_type(self) -> Optional[StartType]:
|
def required_aircraft_start_type(self) -> Optional[StartType]:
|
||||||
@ -894,7 +922,6 @@ class Airfield(ControlPoint):
|
|||||||
airport.position,
|
airport.position,
|
||||||
airport,
|
airport,
|
||||||
starts_blue,
|
starts_blue,
|
||||||
has_frontline=True,
|
|
||||||
cptype=ControlPointType.AIRBASE,
|
cptype=ControlPointType.AIRBASE,
|
||||||
)
|
)
|
||||||
self.airport = airport
|
self.airport = airport
|
||||||
@ -1083,7 +1110,6 @@ class Carrier(NavalControlPoint):
|
|||||||
at,
|
at,
|
||||||
at,
|
at,
|
||||||
starts_blue,
|
starts_blue,
|
||||||
has_frontline=False,
|
|
||||||
cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP,
|
cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1128,7 +1154,6 @@ class Lha(NavalControlPoint):
|
|||||||
at,
|
at,
|
||||||
at,
|
at,
|
||||||
starts_blue,
|
starts_blue,
|
||||||
has_frontline=False,
|
|
||||||
cptype=ControlPointType.LHA_GROUP,
|
cptype=ControlPointType.LHA_GROUP,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1166,7 +1191,6 @@ class OffMapSpawn(ControlPoint):
|
|||||||
position,
|
position,
|
||||||
position,
|
position,
|
||||||
starts_blue,
|
starts_blue,
|
||||||
has_frontline=False,
|
|
||||||
cptype=ControlPointType.OFF_MAP,
|
cptype=ControlPointType.OFF_MAP,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1231,7 +1255,6 @@ class Fob(ControlPoint):
|
|||||||
at,
|
at,
|
||||||
at,
|
at,
|
||||||
starts_blue,
|
starts_blue,
|
||||||
has_frontline=True,
|
|
||||||
cptype=ControlPointType.FOB,
|
cptype=ControlPointType.FOB,
|
||||||
)
|
)
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|||||||
@ -2,15 +2,16 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Iterator, List, Tuple, Any, TYPE_CHECKING
|
from typing import Any, Iterator, List, TYPE_CHECKING, Tuple
|
||||||
|
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
|
|
||||||
from .controlpoint import ControlPoint, MissionTarget
|
from .missiontarget import MissionTarget
|
||||||
from ..utils import Heading, pairwise
|
from ..utils import Heading, pairwise
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from game.ato import FlightType
|
from game.ato import FlightType
|
||||||
|
from .controlpoint import ControlPoint
|
||||||
|
|
||||||
|
|
||||||
FRONTLINE_MIN_CP_DISTANCE = 5000
|
FRONTLINE_MIN_CP_DISTANCE = 5000
|
||||||
@ -193,3 +194,15 @@ class FrontLine(MissionTarget):
|
|||||||
):
|
):
|
||||||
distance = FRONTLINE_MIN_CP_DISTANCE
|
distance = FRONTLINE_MIN_CP_DISTANCE
|
||||||
return distance
|
return distance
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def sort_control_points(
|
||||||
|
a: ControlPoint, b: ControlPoint
|
||||||
|
) -> tuple[ControlPoint, ControlPoint]:
|
||||||
|
if a.is_friendly_to(b):
|
||||||
|
raise ValueError(
|
||||||
|
"Cannot sort control points that are friendly to each other"
|
||||||
|
)
|
||||||
|
if a.captured:
|
||||||
|
return a, b
|
||||||
|
return b, a
|
||||||
|
|||||||
@ -13,6 +13,8 @@ from PySide2.QtWidgets import (
|
|||||||
from game import Game
|
from game import Game
|
||||||
from game.ato.flighttype import FlightType
|
from game.ato.flighttype import FlightType
|
||||||
from game.config import RUNWAY_REPAIR_COST
|
from game.config import RUNWAY_REPAIR_COST
|
||||||
|
from game.server import EventStream
|
||||||
|
from game.sim import GameUpdateEvents
|
||||||
from game.theater import (
|
from game.theater import (
|
||||||
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION,
|
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION,
|
||||||
ControlPoint,
|
ControlPoint,
|
||||||
@ -125,7 +127,9 @@ class QBaseMenu2(QDialog):
|
|||||||
self.cp.capture(self.game_model.game, for_player=not self.cp.captured)
|
self.cp.capture(self.game_model.game, for_player=not self.cp.captured)
|
||||||
# Reinitialized ground planners and the like. The ATO needs to be reset because
|
# Reinitialized ground planners and the like. The ATO needs to be reset because
|
||||||
# missions planned against the flipped base are no longer valid.
|
# missions planned against the flipped base are no longer valid.
|
||||||
self.game_model.game.initialize_turn()
|
events = GameUpdateEvents()
|
||||||
|
self.game_model.game.initialize_turn(events)
|
||||||
|
EventStream.put_nowait(events)
|
||||||
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
GameUpdateSignal.get_instance().updateGame(self.game_model.game)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user