Replace CP integer ID with a UUID.

This allows unique identification across saves. The front-end needs to
be able to differentiate the first carrier in game A and the first
carrier in game B, but because carriers (and other non-airfield CPs) are
assigned IDs sequentially, collisions were to be expected. The front-end
can't tell the difference between a reloaded game and a new turn, so we
need to ensure different IDs across games.

This is a handy cleanup anyway, since callers constructing CPs no longer
need to manually track the CP ID counter.

Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2078.
This commit is contained in:
Dan Albert 2022-03-20 15:11:58 -07:00
parent 941a7d441c
commit 039ac9ec74
21 changed files with 127 additions and 179 deletions

View File

@ -208,25 +208,25 @@ export type ListControlPointsApiArg = void;
export type GetControlPointByIdApiResponse =
/** status 200 Successful Response */ ControlPoint;
export type GetControlPointByIdApiArg = {
cpId: number;
cpId: string;
};
export type ControlPointDestinationInRangeApiResponse =
/** status 200 Successful Response */ boolean;
export type ControlPointDestinationInRangeApiArg = {
cpId: number;
cpId: string;
lat: number;
lng: number;
};
export type SetControlPointDestinationApiResponse =
/** status 204 Successful Response */ undefined;
export type SetControlPointDestinationApiArg = {
cpId: number;
cpId: string;
body: LatLng;
};
export type ClearControlPointDestinationApiResponse =
/** status 204 Successful Response */ undefined;
export type ClearControlPointDestinationApiArg = {
cpId: number;
cpId: string;
};
export type GetDebugHoldZonesApiResponse =
/** status 200 Successful Response */ HoldZones;
@ -302,12 +302,12 @@ export type OpenTgoInfoDialogApiArg = {
export type OpenNewControlPointPackageDialogApiResponse =
/** status 204 Successful Response */ undefined;
export type OpenNewControlPointPackageDialogApiArg = {
cpId: number;
cpId: string;
};
export type OpenControlPointInfoDialogApiResponse =
/** status 204 Successful Response */ undefined;
export type OpenControlPointInfoDialogApiArg = {
cpId: number;
cpId: string;
};
export type ListSupplyRoutesApiResponse =
/** status 200 Successful Response */ SupplyRoute[];
@ -335,7 +335,7 @@ export type LatLng = {
lng: number;
};
export type ControlPoint = {
id: number;
id: string;
name: string;
blue: boolean;
position: LatLng;

View File

@ -4,7 +4,7 @@ import { ControlPoint } from "./liberationApi";
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
interface ControlPointsState {
controlPoints: { [key: number]: ControlPoint };
controlPoints: { [key: string]: ControlPoint };
}
const initialState: ControlPointsState = {
@ -23,7 +23,7 @@ export const controlPointsSlice = createSlice({
extraReducers: (builder) => {
builder.addCase(gameLoaded, (state, action) => {
state.controlPoints = action.payload.control_points.reduce(
(acc: { [key: number]: ControlPoint }, curr) => {
(acc: { [key: string]: ControlPoint }, curr) => {
acc[curr.id] = curr;
return acc;
},

View File

@ -9,7 +9,7 @@ export default function ControlPointsLayer() {
<LayerGroup>
{Object.values(controlPoints.controlPoints).map((controlPoint) => {
return (
<ControlPoint key={controlPoint.name} controlPoint={controlPoint} />
<ControlPoint key={controlPoint.id} controlPoint={controlPoint} />
);
})}
</LayerGroup>

View File

@ -1,9 +1,8 @@
from __future__ import annotations
import logging
from collections import defaultdict
from dataclasses import dataclass
from typing import Any, TYPE_CHECKING, Union, Optional
from typing import Any, Optional, TYPE_CHECKING, Union
from game.ato.flighttype import FlightType
from game.theater.controlpoint import ControlPoint
@ -67,7 +66,7 @@ class CampaignAirWingConfig:
by_location: dict[ControlPoint, list[SquadronConfig]] = defaultdict(list)
for base_id, squadron_configs in data.items():
if isinstance(base_id, int):
base = theater.find_control_point_by_id(base_id)
base = theater.find_control_point_by_airport_id(base_id)
else:
base = theater.control_point_named(base_id)

View File

@ -3,23 +3,23 @@ from __future__ import annotations
import itertools
from functools import cached_property
from pathlib import Path
from typing import Iterator, List, Dict, Tuple, TYPE_CHECKING
from typing import Iterator, List, TYPE_CHECKING, Tuple
from uuid import UUID
from dcs import Mission
from dcs.countries import CombinedJointTaskForcesBlue, CombinedJointTaskForcesRed
from dcs.country import Country
from dcs.planes import F_15C
from dcs.ships import Stennis, LHA_Tarawa, HandyWind, USS_Arleigh_Burke_IIa
from dcs.ships import HandyWind, LHA_Tarawa, Stennis, USS_Arleigh_Burke_IIa
from dcs.statics import Fortification, Warehouse
from dcs.terrain import Airport
from dcs.unitgroup import PlaneGroup, ShipGroup, VehicleGroup, StaticGroup
from dcs.vehicles import Armor, Unarmed, MissilesSS, AirDefence
from dcs.unitgroup import PlaneGroup, ShipGroup, StaticGroup, VehicleGroup
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
from game.point_with_heading import PointWithHeading
from game.positioned import Positioned
from game.profiling import logged_duration
from game.scenery_group import SceneryGroup
from game.utils import Distance, meters, Heading
from game.theater.controlpoint import (
Airfield,
Carrier,
@ -28,6 +28,7 @@ from game.theater.controlpoint import (
Lha,
OffMapSpawn,
)
from game.utils import Distance, Heading, meters
if TYPE_CHECKING:
from game.theater.conflicttheater import ConflictTheater
@ -94,7 +95,6 @@ class MizCampaignLoader:
self.mission = Mission()
with logged_duration("Loading miz"):
self.mission.load_file(str(miz))
self.control_point_id = itertools.count(1000)
# If there are no red carriers there usually aren't red units. Make sure
# both countries are initialized so we don't have to deal with None.
@ -238,7 +238,7 @@ class MizCampaignLoader:
return SceneryGroup.from_trigger_zones(self.mission.triggers._zones)
@cached_property
def control_points(self) -> Dict[int, ControlPoint]:
def control_points(self) -> dict[UUID, ControlPoint]:
control_points = {}
for airport in self.mission.terrain.airport_list():
if airport.is_blue() or airport.is_red():
@ -248,38 +248,20 @@ class MizCampaignLoader:
for blue in (False, True):
for group in self.off_map_spawns(blue):
control_point = OffMapSpawn(
next(self.control_point_id),
str(group.name),
group.position,
starts_blue=blue,
str(group.name), group.position, starts_blue=blue
)
control_point.captured_invert = group.late_activation
control_points[control_point.id] = control_point
for ship in self.carriers(blue):
control_point = Carrier(
ship.name,
ship.position,
next(self.control_point_id),
starts_blue=blue,
)
control_point = Carrier(ship.name, ship.position, starts_blue=blue)
control_point.captured_invert = ship.late_activation
control_points[control_point.id] = control_point
for ship in self.lhas(blue):
control_point = Lha(
ship.name,
ship.position,
next(self.control_point_id),
starts_blue=blue,
)
control_point = Lha(ship.name, ship.position, starts_blue=blue)
control_point.captured_invert = ship.late_activation
control_points[control_point.id] = control_point
for fob in self.fobs(blue):
control_point = Fob(
str(fob.name),
fob.position,
next(self.control_point_id),
starts_blue=blue,
)
control_point = Fob(str(fob.name), fob.position, starts_blue=blue)
control_point.captured_invert = fob.late_activation
control_points[control_point.id] = control_point

View File

@ -12,6 +12,7 @@ from typing import (
TYPE_CHECKING,
Union,
)
from uuid import UUID
from game.dcs.aircrafttype import AircraftType
from game.dcs.groundunittype import GroundUnitType
@ -337,8 +338,10 @@ class Debriefing:
seen = set()
captures = []
for capture in reversed(self.state_data.base_capture_events):
cp_id_str, new_owner_id_str, _name = capture.split("||")
cp_id = int(cp_id_str)
# The ID string in the JSON file will be an airport ID for airport captures
# but will be a UUID for all other types, since DCS doesn't know the UUIDs
# for the captured FOBs.
cp_id, new_owner_id_str, _name = capture.split("||")
# Only the most recent capture event matters.
if cp_id in seen:
@ -346,7 +349,12 @@ class Debriefing:
seen.add(cp_id)
try:
control_point = self.game.theater.find_control_point_by_id(cp_id)
control_point = self.game.theater.find_control_point_by_airport_id(
int(cp_id)
)
except ValueError:
# The CP ID could not be converted to an int, so it's a UUID.
control_point = self.game.theater.find_control_point_by_id(UUID(cp_id))
except KeyError:
# Captured base is not a part of the campaign. This happens when neutral
# bases are near the conflict. Nothing to do.

View File

@ -7,6 +7,7 @@ from collections.abc import Iterator
from datetime import date, datetime, timedelta
from enum import Enum
from typing import Any, List, TYPE_CHECKING, Type, Union, cast
from uuid import UUID
from dcs.countries import Switzerland, USAFAggressors, UnitedNationsPeacekeepers
from dcs.country import Country
@ -105,7 +106,7 @@ class Game:
self.date = date(start_date.year, start_date.month, start_date.day)
self.game_stats = GameStats()
self.notes = ""
self.ground_planners: dict[int, GroundPlanner] = {}
self.ground_planners: dict[UUID, GroundPlanner] = {}
self.informations: list[Information] = []
self.message("Game Start", "-" * 40)
# Culling Zones are for areas around points of interest that contain things we may not wish to cull.

View File

@ -3,7 +3,8 @@ from __future__ import annotations
import logging
import random
from enum import Enum
from typing import Dict, List, TYPE_CHECKING
from typing import List, TYPE_CHECKING
from uuid import UUID
from game.data.units import UnitClass
from game.dcs.groundunittype import GroundUnitType
@ -82,7 +83,7 @@ class GroundPlanner:
self.shorad_groups: List[CombatGroup] = []
self.recon_groups: List[CombatGroup] = []
self.units_per_cp: Dict[int, List[CombatGroup]] = {}
self.units_per_cp: dict[UUID, List[CombatGroup]] = {}
for cp in self.connected_enemy_cp:
self.units_per_cp[cp.id] = []
self.reserve: List[CombatGroup] = []

View File

@ -1,4 +1,3 @@
import copy
import logging
import random
from typing import Any, Union
@ -8,7 +7,6 @@ from dcs.country import Country
from dcs.mapping import Vector2
from dcs.mission import StartType as DcsStartType
from dcs.planes import F_14A, Su_33
from dcs.point import PointAction
from dcs.ships import KUZNECOW
from dcs.terrain import Airport, NoParkingSlotError
from dcs.unitgroup import FlyingGroup, ShipGroup, StaticGroup
@ -16,10 +14,10 @@ from dcs.unitgroup import FlyingGroup, ShipGroup, StaticGroup
from game.ato import Flight
from game.ato.flightstate import InFlight
from game.ato.starttype import StartType
from game.ato.traveltime import GroundSpeed
from game.naming import namegen
from game.theater import Airfield, ControlPoint, Fob, NavalControlPoint, OffMapSpawn
from game.utils import feet, meters
from game.ato.traveltime import GroundSpeed
WARM_START_HELI_ALT = meters(500)
WARM_START_ALTITUDE = meters(3000)
@ -79,12 +77,11 @@ class FlightGroupSpawner:
return self.generate_mid_mission()
def create_idle_aircraft(self) -> FlyingGroup[Any]:
assert isinstance(self.flight.squadron.location, Airfield)
airport = self.flight.squadron.location.dcs_airport
assert airport is not None
group = self._generate_at_airport(
name=namegen.next_aircraft_name(
self.country, self.flight.departure.id, self.flight
),
airport=self.flight.squadron.location.airport,
name=namegen.next_aircraft_name(self.country, self.flight),
airport=airport,
)
group.uncontrolled = True
@ -95,9 +92,7 @@ class FlightGroupSpawner:
return self.flight.state.spawn_type
def generate_flight_at_departure(self) -> FlyingGroup[Any]:
name = namegen.next_aircraft_name(
self.country, self.flight.departure.id, self.flight
)
name = namegen.next_aircraft_name(self.country, self.flight)
cp = self.flight.departure
try:
if self.start_type is StartType.IN_FLIGHT:
@ -135,9 +130,7 @@ class FlightGroupSpawner:
def generate_mid_mission(self) -> FlyingGroup[Any]:
assert isinstance(self.flight.state, InFlight)
name = namegen.next_aircraft_name(
self.country, self.flight.departure.id, self.flight
)
name = namegen.next_aircraft_name(self.country, self.flight)
speed = self.flight.state.estimate_speed()
pos = self.flight.state.estimate_position()
pos += Vector2(random.randint(100, 1000), random.randint(100, 1000))

View File

@ -18,7 +18,6 @@ from game.ato.flightwaypointtype import FlightWaypointType
from game.ato.starttype import StartType
from game.missiongenerator.airsupport import AirSupport
from game.settings import Settings
from game.theater import ControlPointType
from game.utils import pairwise
from .baiingress import BaiIngressBuilder
from .cargostop import CargoStopBuilder
@ -27,7 +26,6 @@ from .deadingress import DeadIngressBuilder
from .default import DefaultWaypointBuilder
from .holdpoint import HoldPointBuilder
from .joinpoint import JoinPointBuilder
from .splitpoint import SplitPointBuilder
from .landingpoint import LandingPointBuilder
from .ocaaircraftingress import OcaAircraftIngressBuilder
from .ocarunwayingress import OcaRunwayIngressBuilder
@ -36,6 +34,7 @@ from .racetrack import RaceTrackBuilder
from .racetrackend import RaceTrackEndBuilder
from .refuel import RefuelPointBuilder
from .seadingress import SeadIngressBuilder
from .splitpoint import SplitPointBuilder
from .strikeingress import StrikeIngressBuilder
from .sweepingress import SweepIngressBuilder
@ -213,14 +212,12 @@ class WaypointGenerator:
def prevent_spawn_at_hostile_airbase(self, trigger: TriggerRule) -> None:
# Prevent delayed flights from spawning at airbases if they were
# captured before they've spawned.
if self.flight.from_cp.cptype != ControlPointType.AIRBASE:
return
trigger.add_condition(
CoalitionHasAirdrome(
self.flight.squadron.coalition.coalition_id, self.flight.from_cp.id
if (airport := self.flight.departure.dcs_airport) is not None:
trigger.add_condition(
CoalitionHasAirdrome(
self.flight.squadron.coalition.coalition_id, airport.id
)
)
)
def set_startup_time(self, delay: timedelta) -> None:
# Uncontrolled causes the AI unit to spawn, but not begin startup.

View File

@ -227,7 +227,7 @@ class FlotGenerator:
)[0]
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, u),
namegen.next_infantry_name(side, u),
u.dcs_unit_type,
position=infantry_position,
group_size=1,
@ -252,7 +252,7 @@ class FlotGenerator:
)
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, units[0]),
namegen.next_infantry_name(side, units[0]),
units[0].dcs_unit_type,
position=infantry_position,
group_size=1,
@ -264,7 +264,7 @@ class FlotGenerator:
position = infantry_position.random_point_within(55, 5)
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, unit),
namegen.next_infantry_name(side, unit),
unit.dcs_unit_type,
position=position,
group_size=1,
@ -782,7 +782,7 @@ class FlotGenerator:
group = self.mission.vehicle_group(
side,
namegen.next_unit_name(side, cp.id, unit_type),
namegen.next_unit_name(side, unit_type),
unit_type.dcs_unit_type,
position=at,
group_size=count,

View File

@ -2,22 +2,18 @@ from __future__ import annotations
from typing import TYPE_CHECKING
from dcs.action import MarkToAll, SetFlag, DoScript, ClearFlag
from dcs.action import ClearFlag, DoScript, MarkToAll, SetFlag
from dcs.condition import (
TimeAfter,
AllOfCoalitionOutsideZone,
PartOfCoalitionInZone,
FlagIsFalse,
FlagIsTrue,
PartOfCoalitionInZone,
TimeAfter,
)
from dcs.mission import Mission
from dcs.task import Option
from dcs.translation import String
from dcs.triggers import (
Event,
TriggerOnce,
TriggerCondition,
)
from dcs.triggers import Event, TriggerCondition, TriggerOnce
from dcs.unit import Skill
from game.theater import Airfield
@ -61,9 +57,12 @@ class TriggerGenerator:
"""
# Empty neutrals airports
cp_ids = [cp.id for cp in self.game.theater.controlpoints]
airfields = [
cp for cp in self.game.theater.controlpoints if isinstance(cp, Airfield)
]
airport_ids = {cp.airport.id for cp in airfields}
for airport in self.mission.terrain.airport_list():
if airport.id not in cp_ids:
if airport.id not in airport_ids:
airport.unlimited_fuel = False
airport.unlimited_munitions = False
airport.unlimited_aircrafts = False
@ -76,21 +75,20 @@ class TriggerGenerator:
airport.operating_level_fuel = 0
for airport in self.mission.terrain.airport_list():
if airport.id not in cp_ids:
if airport.id not in airport_ids:
airport.unlimited_fuel = True
airport.unlimited_munitions = True
airport.unlimited_aircrafts = True
for cp in self.game.theater.controlpoints:
if isinstance(cp, Airfield):
cp_airport = self.mission.terrain.airport_by_id(cp.airport.id)
if cp_airport is None:
raise RuntimeError(
f"Could not find {cp.airport.name} in the mission"
)
cp_airport.set_coalition(
cp.captured and player_coalition or enemy_coalition
for airfield in airfields:
cp_airport = self.mission.terrain.airport_by_id(airfield.airport.id)
if cp_airport is None:
raise RuntimeError(
f"Could not find {airfield.airport.name} in the mission"
)
cp_airport.set_coalition(
airfield.captured and player_coalition or enemy_coalition
)
def _set_skill(self, player_coalition: str, enemy_coalition: str) -> None:
"""

View File

@ -2,7 +2,7 @@ from __future__ import annotations
import random
import time
from typing import List, Any, TYPE_CHECKING
from typing import Any, List, TYPE_CHECKING
from dcs.country import Country
@ -474,45 +474,27 @@ class NameGenerator:
cls.cargo_ship_number = 0
@classmethod
def next_aircraft_name(
cls, country: Country, parent_base_id: int, flight: Flight
) -> str:
def next_aircraft_name(cls, country: Country, flight: Flight) -> str:
cls.aircraft_number += 1
try:
if flight.custom_name:
name_str = flight.custom_name
else:
name_str = "{} {}".format(
flight.package.target.name, flight.flight_type
)
except AttributeError: # Here to maintain save compatibility with 2.3
if flight.custom_name:
name_str = flight.custom_name
else:
name_str = "{} {}".format(flight.package.target.name, flight.flight_type)
return "{}|{}|{}|{}|{}|".format(
name_str,
country.id,
cls.aircraft_number,
parent_base_id,
flight.unit_type.name,
return "{}|{}|{}|{}|".format(
name_str, country.id, cls.aircraft_number, flight.unit_type.name
)
@classmethod
def next_unit_name(
cls, country: Country, parent_base_id: int, unit_type: UnitType[Any]
) -> str:
def next_unit_name(cls, country: Country, unit_type: UnitType[Any]) -> str:
cls.number += 1
return "unit|{}|{}|{}|{}|".format(
country.id, cls.number, parent_base_id, unit_type.name
)
return "unit|{}|{}|{}|".format(country.id, cls.number, unit_type.name)
@classmethod
def next_infantry_name(
cls, country: Country, parent_base_id: int, unit_type: UnitType[Any]
) -> str:
def next_infantry_name(cls, country: Country, unit_type: UnitType[Any]) -> str:
cls.infantry_number += 1
return "infantry|{}|{}|{}|{}|".format(
return "infantry|{}|{}|{}|".format(
country.id,
cls.infantry_number,
parent_base_id,
unit_type.name,
)

View File

@ -1,6 +1,7 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from uuid import UUID
from pydantic import BaseModel
@ -12,7 +13,7 @@ if TYPE_CHECKING:
class ControlPointJs(BaseModel):
id: int
id: UUID
name: str
blue: bool
position: LeafletPoint

View File

@ -1,3 +1,5 @@
from uuid import UUID
from dcs import Point
from dcs.mapping import LatLng
from fastapi import APIRouter, Body, Depends, HTTPException, status
@ -25,7 +27,7 @@ def list_control_points(
"/{cp_id}", operation_id="get_control_point_by_id", response_model=ControlPointJs
)
def get_control_point(
cp_id: int, game: Game = Depends(GameContext.require)
cp_id: UUID, game: Game = Depends(GameContext.require)
) -> ControlPointJs:
cp = game.theater.find_control_point_by_id(cp_id)
if cp is None:
@ -42,7 +44,7 @@ def get_control_point(
response_model=bool,
)
def destination_in_range(
cp_id: int, lat: float, lng: float, game: Game = Depends(GameContext.require)
cp_id: UUID, lat: float, lng: float, game: Game = Depends(GameContext.require)
) -> bool:
cp = game.theater.find_control_point_by_id(cp_id)
if cp is None:
@ -61,7 +63,7 @@ def destination_in_range(
status_code=status.HTTP_204_NO_CONTENT,
)
def set_destination(
cp_id: int,
cp_id: UUID,
destination: LeafletPoint = Body(..., title="destination"),
game: Game = Depends(GameContext.require),
) -> None:
@ -96,7 +98,7 @@ def set_destination(
operation_id="clear_control_point_destination",
status_code=status.HTTP_204_NO_CONTENT,
)
def cancel_travel(cp_id: int, game: Game = Depends(GameContext.require)) -> None:
def cancel_travel(cp_id: UUID, game: Game = Depends(GameContext.require)) -> None:
cp = game.theater.find_control_point_by_id(cp_id)
if cp is None:
raise HTTPException(

View File

@ -32,7 +32,7 @@ class GameUpdateEventsJs(BaseModel):
updated_front_lines: set[UUID]
deleted_front_lines: set[UUID]
updated_tgos: set[UUID]
updated_control_points: set[int]
updated_control_points: set[UUID]
reset_on_map_center: LeafletPoint | None
game_unloaded: bool
new_turn: bool

View File

@ -53,7 +53,7 @@ def show_tgo_info(
status_code=status.HTTP_204_NO_CONTENT,
)
def new_cp_package(
cp_id: int,
cp_id: UUID,
game: Game = Depends(GameContext.require),
qt: QtCallbacks = Depends(QtContext.get),
) -> None:
@ -72,7 +72,7 @@ def new_cp_package(
status_code=status.HTTP_204_NO_CONTENT,
)
def show_control_point_info(
cp_id: int,
cp_id: UUID,
game: Game = Depends(GameContext.require),
qt: QtCallbacks = Depends(QtContext.get),
) -> None:

View File

@ -33,7 +33,7 @@ class GameUpdateEvents:
updated_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)
updated_control_points: set[int] = field(default_factory=set)
updated_control_points: set[UUID] = field(default_factory=set)
reset_on_map_center: LatLng | None = None
game_unloaded: bool = False
new_turn: bool = False

View File

@ -5,6 +5,7 @@ import math
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Iterator, List, Optional, TYPE_CHECKING, Tuple
from uuid import UUID
from dcs.mapping import Point
from dcs.terrain import (
@ -203,11 +204,17 @@ class ConflictTheater:
assert closest_red is not None
return closest_blue, closest_red
def find_control_point_by_id(self, id: int) -> ControlPoint:
def find_control_point_by_id(self, cp_id: UUID) -> ControlPoint:
for i in self.controlpoints:
if i.id == id:
if i.id == cp_id:
return i
raise KeyError(f"Cannot find ControlPoint with ID {id}")
raise KeyError(f"Cannot find ControlPoint with ID {cp_id}")
def find_control_point_by_airport_id(self, airport_id: int) -> ControlPoint:
for cp in self.controlpoints:
if cp.dcs_airport is not None and cp.dcs_airport.id == airport_id:
return cp
raise KeyError(f"Cannot find ControlPoint with airport ID {airport_id}")
def control_point_named(self, name: str) -> ControlPoint:
for cp in self.controlpoints:

View File

@ -4,6 +4,7 @@ import heapq
import itertools
import logging
import math
import uuid
from abc import ABC, abstractmethod
from collections import defaultdict
from dataclasses import dataclass, field
@ -21,6 +22,7 @@ from typing import (
TYPE_CHECKING,
Tuple,
)
from uuid import UUID
from dcs.mapping import Point
from dcs.ships import Forrestal, KUZNECOW, LHA_Tarawa, Stennis, Type_071
@ -294,7 +296,6 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
# TODO: cptype is obsolete.
def __init__(
self,
cp_id: int,
name: str,
position: Point,
at: StartingPosition,
@ -302,8 +303,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
cptype: ControlPointType = ControlPointType.AIRBASE,
) -> None:
super().__init__(name, position)
# TODO: Should be Airbase specific.
self.id = cp_id
self.id = uuid.uuid4()
self.full_name = name
self.at = at
self.starts_blue = starts_blue
@ -321,7 +321,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
self.base: Base = Base()
self.cptype = cptype
# TODO: Should be Airbase specific.
self.stances: Dict[int, CombatStance] = {}
self.stances: dict[UUID, CombatStance] = {}
from ..groundunitorders import GroundUnitOrders
self.ground_unit_orders = GroundUnitOrders(self)
@ -334,6 +334,10 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
def __repr__(self) -> str:
return f"<{self.__class__}: {self.name}>"
@property
def dcs_airport(self) -> Airport | None:
return None
@property
def coalition(self) -> Coalition:
if self._coalition is None:
@ -956,7 +960,6 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
class Airfield(ControlPoint):
def __init__(self, airport: Airport, starts_blue: bool) -> None:
super().__init__(
airport.id,
airport.name,
airport.position,
airport,
@ -966,6 +969,10 @@ class Airfield(ControlPoint):
self.airport = airport
self._runway_status = RunwayStatus()
@property
def dcs_airport(self) -> Airport:
return self.airport
@property
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
return SymbolSet.LAND_INSTALLATIONS, LandInstallationEntity.AIPORT_AIR_BASE
@ -1142,14 +1149,9 @@ class NavalControlPoint(ControlPoint, ABC):
class Carrier(NavalControlPoint):
def __init__(self, name: str, at: Point, cp_id: int, starts_blue: bool):
def __init__(self, name: str, at: Point, starts_blue: bool):
super().__init__(
cp_id,
name,
at,
at,
starts_blue,
cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP,
name, at, at, starts_blue, cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP
)
@property
@ -1186,15 +1188,8 @@ class Carrier(NavalControlPoint):
class Lha(NavalControlPoint):
def __init__(self, name: str, at: Point, cp_id: int, starts_blue: bool):
super().__init__(
cp_id,
name,
at,
at,
starts_blue,
cptype=ControlPointType.LHA_GROUP,
)
def __init__(self, name: str, at: Point, starts_blue: bool):
super().__init__(name, at, at, starts_blue, cptype=ControlPointType.LHA_GROUP)
@property
def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]:
@ -1223,14 +1218,9 @@ class OffMapSpawn(ControlPoint):
def runway_is_operational(self) -> bool:
return True
def __init__(self, cp_id: int, name: str, position: Point, starts_blue: bool):
def __init__(self, name: str, position: Point, starts_blue: bool):
super().__init__(
cp_id,
name,
position,
position,
starts_blue,
cptype=ControlPointType.OFF_MAP,
name, position, position, starts_blue, cptype=ControlPointType.OFF_MAP
)
@property
@ -1287,15 +1277,8 @@ class OffMapSpawn(ControlPoint):
class Fob(ControlPoint):
def __init__(self, name: str, at: Point, cp_id: int, starts_blue: bool):
super().__init__(
cp_id,
name,
at,
at,
starts_blue,
cptype=ControlPointType.FOB,
)
def __init__(self, name: str, at: Point, starts_blue: bool):
super().__init__(name, at, at, starts_blue, cptype=ControlPointType.FOB)
self.name = name
@property

View File

@ -39,12 +39,6 @@ class QBaseMenu2(QDialog):
self.game_model = game_model
self.objectName = "menuDialogue"
try:
game = self.game_model.game
self.airport = game.theater.terrain.airport_by_id(self.cp.id)
except:
self.airport = None
if self.cp.captured:
self.deliveryEvent = None