mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
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:
parent
941a7d441c
commit
039ac9ec74
@ -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;
|
||||
|
||||
@ -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;
|
||||
},
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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] = []
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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:
|
||||
"""
|
||||
|
||||
@ -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,
|
||||
)
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user