mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +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."""
|
||||
from .sim import GameUpdateEvents
|
||||
|
||||
for control_point in self.theater.controlpoints:
|
||||
control_point.initialize_turn_0()
|
||||
|
||||
self.blue.preinit_turn_0()
|
||||
self.red.preinit_turn_0()
|
||||
# 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 game.dcs.groundunittype import GroundUnitType
|
||||
from game.theater import FrontLine
|
||||
from game.transfers import Convoy
|
||||
from game.unitmap import UnitMap
|
||||
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.
|
||||
# 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.
|
||||
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
|
||||
# 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))
|
||||
|
||||
def conflicts(self) -> Iterator[FrontLine]:
|
||||
for player_cp in [x for x in self.controlpoints if x.captured]:
|
||||
for enemy_cp in [
|
||||
x for x in player_cp.connected_points if not x.is_friendly_to(player_cp)
|
||||
]:
|
||||
yield FrontLine(player_cp, enemy_cp)
|
||||
for cp in self.player_points():
|
||||
yield from cp.front_lines.values()
|
||||
|
||||
def enemy_points(self) -> List[ControlPoint]:
|
||||
return list(self.control_points_for(player=False))
|
||||
|
||||
@ -43,6 +43,7 @@ from game.sidc import (
|
||||
)
|
||||
from game.utils import Heading
|
||||
from .base import Base
|
||||
from .frontline import FrontLine
|
||||
from .missiontarget import MissionTarget
|
||||
from .theatergroundobject import (
|
||||
GenericCarrierGroundObject,
|
||||
@ -62,7 +63,7 @@ if TYPE_CHECKING:
|
||||
from game.squadrons.squadron import Squadron
|
||||
from ..coalition import Coalition
|
||||
from ..transfers import PendingTransfers
|
||||
from . import ConflictTheater
|
||||
from .conflicttheater import ConflictTheater
|
||||
|
||||
FREE_FRONTLINE_UNIT_SUPPLY: int = 15
|
||||
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION: int = 12
|
||||
@ -287,15 +288,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
# the distance of the circle on the map.
|
||||
CAPTURE_DISTANCE = nautical_miles(2)
|
||||
|
||||
position = None # type: Point
|
||||
name = None # type: str
|
||||
|
||||
has_frontline = True
|
||||
|
||||
alt = 0
|
||||
|
||||
# TODO: Only airbases have IDs.
|
||||
# TODO: has_frontline is only reasonable for airbases.
|
||||
# TODO: cptype is obsolete.
|
||||
def __init__(
|
||||
self,
|
||||
@ -304,7 +297,6 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
position: Point,
|
||||
at: StartingPosition,
|
||||
starts_blue: bool,
|
||||
has_frontline: bool = True,
|
||||
cptype: ControlPointType = ControlPointType.AIRBASE,
|
||||
) -> None:
|
||||
super().__init__(name, position)
|
||||
@ -319,8 +311,8 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
|
||||
self._coalition: Optional[Coalition] = None
|
||||
self.captured_invert = False
|
||||
self.front_lines: dict[ControlPoint, FrontLine] = {}
|
||||
# TODO: Should be Airbase specific.
|
||||
self.has_frontline = has_frontline
|
||||
self.connected_points: List[ControlPoint] = []
|
||||
self.convoy_routes: 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
|
||||
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
|
||||
def captured(self) -> bool:
|
||||
return self.coalition.player
|
||||
@ -695,6 +722,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
|
||||
|
||||
self._coalition = new_coalition
|
||||
self.base.set_strength_to_minimum()
|
||||
self._recreate_front_lines()
|
||||
|
||||
@property
|
||||
def required_aircraft_start_type(self) -> Optional[StartType]:
|
||||
@ -894,7 +922,6 @@ class Airfield(ControlPoint):
|
||||
airport.position,
|
||||
airport,
|
||||
starts_blue,
|
||||
has_frontline=True,
|
||||
cptype=ControlPointType.AIRBASE,
|
||||
)
|
||||
self.airport = airport
|
||||
@ -1083,7 +1110,6 @@ class Carrier(NavalControlPoint):
|
||||
at,
|
||||
at,
|
||||
starts_blue,
|
||||
has_frontline=False,
|
||||
cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP,
|
||||
)
|
||||
|
||||
@ -1128,7 +1154,6 @@ class Lha(NavalControlPoint):
|
||||
at,
|
||||
at,
|
||||
starts_blue,
|
||||
has_frontline=False,
|
||||
cptype=ControlPointType.LHA_GROUP,
|
||||
)
|
||||
|
||||
@ -1166,7 +1191,6 @@ class OffMapSpawn(ControlPoint):
|
||||
position,
|
||||
position,
|
||||
starts_blue,
|
||||
has_frontline=False,
|
||||
cptype=ControlPointType.OFF_MAP,
|
||||
)
|
||||
|
||||
@ -1231,7 +1255,6 @@ class Fob(ControlPoint):
|
||||
at,
|
||||
at,
|
||||
starts_blue,
|
||||
has_frontline=True,
|
||||
cptype=ControlPointType.FOB,
|
||||
)
|
||||
self.name = name
|
||||
|
||||
@ -2,15 +2,16 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
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 .controlpoint import ControlPoint, MissionTarget
|
||||
from .missiontarget import MissionTarget
|
||||
from ..utils import Heading, pairwise
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato import FlightType
|
||||
from .controlpoint import ControlPoint
|
||||
|
||||
|
||||
FRONTLINE_MIN_CP_DISTANCE = 5000
|
||||
@ -193,3 +194,15 @@ class FrontLine(MissionTarget):
|
||||
):
|
||||
distance = FRONTLINE_MIN_CP_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.ato.flighttype import FlightType
|
||||
from game.config import RUNWAY_REPAIR_COST
|
||||
from game.server import EventStream
|
||||
from game.sim import GameUpdateEvents
|
||||
from game.theater import (
|
||||
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION,
|
||||
ControlPoint,
|
||||
@ -125,7 +127,9 @@ class QBaseMenu2(QDialog):
|
||||
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
|
||||
# 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)
|
||||
|
||||
@property
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user