mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Export airfield data to yaml, switch to ID keys.
This exports all the old AIRFIELD_DATA to yaml files. It's easier for users to send fixes if it's defined this way, and they can also fix it in their install without having to wait for a new release. This also switches the indexes from the unstable DCS airfield names to airfield IDs, so this fixes another case of DCS updates occasionally breaking Liberation. I also ended up finding quite a few typos in airfield names, and incorrect theater names in the process. Those have been fixed.
This commit is contained in:
@@ -71,7 +71,7 @@ class FlightGroupConfigurator:
|
||||
divert = None
|
||||
if self.flight.divert is not None:
|
||||
divert = self.flight.divert.active_runway(
|
||||
self.game.conditions, self.dynamic_runways
|
||||
self.game.theater, self.game.conditions, self.dynamic_runways
|
||||
)
|
||||
|
||||
mission_start_time, waypoints = WaypointGenerator(
|
||||
@@ -93,10 +93,10 @@ class FlightGroupConfigurator:
|
||||
friendly=self.flight.from_cp.captured,
|
||||
departure_delay=mission_start_time,
|
||||
departure=self.flight.departure.active_runway(
|
||||
self.game.conditions, self.dynamic_runways
|
||||
self.game.theater, self.game.conditions, self.dynamic_runways
|
||||
),
|
||||
arrival=self.flight.arrival.active_runway(
|
||||
self.game.conditions, self.dynamic_runways
|
||||
self.game.theater, self.game.conditions, self.dynamic_runways
|
||||
),
|
||||
divert=divert,
|
||||
waypoints=waypoints,
|
||||
|
||||
@@ -11,16 +11,16 @@ from dcs.coalition import Coalition
|
||||
from dcs.countries import country_dict
|
||||
|
||||
from game import db
|
||||
from game.radio.radios import RadioFrequency, RadioRegistry
|
||||
from game.radio.tacan import TacanRegistry
|
||||
from game.theater.bullseye import Bullseye
|
||||
from game.theater import Airfield, FrontLine
|
||||
from game.unitmap import UnitMap
|
||||
from gen.airfields import AIRFIELD_DATA
|
||||
from gen.naming import namegen
|
||||
from game.missiongenerator.aircraft.aircraftgenerator import (
|
||||
AircraftGenerator,
|
||||
)
|
||||
from game.radio.radios import RadioFrequency, RadioRegistry
|
||||
from game.radio.tacan import TacanRegistry
|
||||
from game.theater import Airfield, FrontLine
|
||||
from game.theater.bullseye import Bullseye
|
||||
from game.unitmap import UnitMap
|
||||
from gen.airfields import AirfieldData
|
||||
from gen.naming import namegen
|
||||
from .aircraft.flightdata import FlightData
|
||||
from .airsupport import AirSupport
|
||||
from .airsupportgenerator import AirSupportGenerator
|
||||
@@ -173,8 +173,8 @@ class MissionGenerator:
|
||||
def initialize_radio_registry(
|
||||
self, unique_map_frequencies: set[RadioFrequency]
|
||||
) -> None:
|
||||
for data in AIRFIELD_DATA.values():
|
||||
if data.theater == self.game.theater.terrain.name and data.atc:
|
||||
for data in AirfieldData.for_theater(self.game.theater):
|
||||
if data.atc is not None:
|
||||
unique_map_frequencies.add(data.atc.hf)
|
||||
unique_map_frequencies.add(data.atc.vhf_fm)
|
||||
unique_map_frequencies.add(data.atc.vhf_am)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
"""Radio frequency types and allocators."""
|
||||
from __future__ import annotations
|
||||
|
||||
import itertools
|
||||
import logging
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, FrozenSet, Iterator, List, Set, Tuple
|
||||
|
||||
@@ -35,13 +38,37 @@ class RadioFrequency:
|
||||
"""
|
||||
return self.hertz / 1000000
|
||||
|
||||
@classmethod
|
||||
def parse(cls, text: str) -> RadioFrequency:
|
||||
match = re.match(r"""^(\d+)(?:\.(\d{1,3}))? (MHz|kHz)$""", text)
|
||||
if match is None:
|
||||
raise ValueError(f"Could not parse radio frequency from {text}")
|
||||
|
||||
whole = int(match.group(1))
|
||||
partial_str = match.group(2)
|
||||
units = match.group(3)
|
||||
|
||||
partial = 0
|
||||
if partial_str is not None:
|
||||
partial = int(partial_str)
|
||||
if len(partial_str) == 1:
|
||||
partial *= 100
|
||||
elif len(partial_str) == 2:
|
||||
partial *= 10
|
||||
|
||||
if units == "MHz":
|
||||
return MHz(whole, partial)
|
||||
if units == "kHz":
|
||||
return kHz(whole, partial)
|
||||
raise ValueError(f"Unexpected units in radio frequency: {units}")
|
||||
|
||||
|
||||
def MHz(num: int, khz: int = 0) -> RadioFrequency:
|
||||
return RadioFrequency(num * 1000000 + khz * 1000)
|
||||
|
||||
|
||||
def kHz(num: int) -> RadioFrequency:
|
||||
return RadioFrequency(num * 1000)
|
||||
def kHz(num: int, hz: int = 0) -> RadioFrequency:
|
||||
return RadioFrequency(num * 1000 + hz)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
"""TACAN channel handling."""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from typing import Dict, Iterator, Set
|
||||
@@ -45,6 +48,16 @@ class TacanChannel:
|
||||
def __str__(self) -> str:
|
||||
return f"{self.number}{self.band.value}"
|
||||
|
||||
@classmethod
|
||||
def parse(cls, text: str) -> TacanChannel:
|
||||
match = re.match(r"""^(\d{1,3})([XY])$""", text)
|
||||
if match is None:
|
||||
raise ValueError(f"Could not parse TACAN from {text}")
|
||||
number = int(match.group(1))
|
||||
if not number:
|
||||
raise ValueError("TACAN channel cannot be 0")
|
||||
return TacanChannel(number, TacanBand(match.group(2)))
|
||||
|
||||
|
||||
class OutOfTacanChannelsError(RuntimeError):
|
||||
"""Raised when all channels in this band have been allocated."""
|
||||
|
||||
@@ -7,33 +7,26 @@ import math
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum, unique, auto, IntEnum
|
||||
from functools import total_ordering, cached_property
|
||||
from enum import Enum, IntEnum, auto, unique
|
||||
from functools import cached_property, total_ordering
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Set,
|
||||
TYPE_CHECKING,
|
||||
Union,
|
||||
Sequence,
|
||||
Iterable,
|
||||
Tuple,
|
||||
Union,
|
||||
)
|
||||
|
||||
from dcs.mapping import Point
|
||||
from dcs.ships import (
|
||||
Forrestal,
|
||||
Stennis,
|
||||
KUZNECOW,
|
||||
LHA_Tarawa,
|
||||
Type_071,
|
||||
)
|
||||
from dcs.ships import Forrestal, KUZNECOW, LHA_Tarawa, Stennis, Type_071
|
||||
from dcs.terrain.terrain import Airport, ParkingSlot
|
||||
from dcs.unit import Unit
|
||||
from dcs.unittype import FlyingType
|
||||
|
||||
from game import db
|
||||
from game.point_with_heading import PointWithHeading
|
||||
@@ -45,9 +38,9 @@ from gen.runways import RunwayAssigner, RunwayData
|
||||
from .base import Base
|
||||
from .missiontarget import MissionTarget
|
||||
from .theatergroundobject import (
|
||||
BuildingGroundObject,
|
||||
GenericCarrierGroundObject,
|
||||
TheaterGroundObject,
|
||||
BuildingGroundObject,
|
||||
)
|
||||
from ..ato.starttype import StartType
|
||||
from ..dcs.aircrafttype import AircraftType
|
||||
@@ -61,6 +54,7 @@ if TYPE_CHECKING:
|
||||
from game.squadrons.squadron import Squadron
|
||||
from ..coalition import Coalition
|
||||
from ..transfers import PendingTransfers
|
||||
from . import ConflictTheater
|
||||
|
||||
FREE_FRONTLINE_UNIT_SUPPLY: int = 15
|
||||
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION: int = 12
|
||||
@@ -694,7 +688,10 @@ class ControlPoint(MissionTarget, ABC):
|
||||
|
||||
@abstractmethod
|
||||
def active_runway(
|
||||
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
|
||||
self,
|
||||
theater: ConflictTheater,
|
||||
conditions: Conditions,
|
||||
dynamic_runways: Dict[str, RunwayData],
|
||||
) -> RunwayData:
|
||||
...
|
||||
|
||||
@@ -936,10 +933,13 @@ class Airfield(ControlPoint):
|
||||
self.runway_status.damage()
|
||||
|
||||
def active_runway(
|
||||
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
|
||||
self,
|
||||
theater: ConflictTheater,
|
||||
conditions: Conditions,
|
||||
dynamic_runways: Dict[str, RunwayData],
|
||||
) -> RunwayData:
|
||||
assigner = RunwayAssigner(conditions)
|
||||
return assigner.get_preferred_runway(self.airport)
|
||||
return assigner.get_preferred_runway(theater, self.airport)
|
||||
|
||||
@property
|
||||
def airdrome_id_for_landing(self) -> Optional[int]:
|
||||
@@ -1019,7 +1019,10 @@ class NavalControlPoint(ControlPoint, ABC):
|
||||
return False
|
||||
|
||||
def active_runway(
|
||||
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
|
||||
self,
|
||||
theater: ConflictTheater,
|
||||
conditions: Conditions,
|
||||
dynamic_runways: Dict[str, RunwayData],
|
||||
) -> RunwayData:
|
||||
# TODO: Assign TACAN and ICLS earlier so we don't need this.
|
||||
fallback = RunwayData(
|
||||
@@ -1161,7 +1164,10 @@ class OffMapSpawn(ControlPoint):
|
||||
return Heading.from_degrees(0)
|
||||
|
||||
def active_runway(
|
||||
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
|
||||
self,
|
||||
theater: ConflictTheater,
|
||||
conditions: Conditions,
|
||||
dynamic_runways: Dict[str, RunwayData],
|
||||
) -> RunwayData:
|
||||
logging.warning("TODO: Off map spawns have no runways.")
|
||||
return RunwayData(
|
||||
@@ -1202,7 +1208,10 @@ class Fob(ControlPoint):
|
||||
return self.has_helipads
|
||||
|
||||
def active_runway(
|
||||
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
|
||||
self,
|
||||
theater: ConflictTheater,
|
||||
conditions: Conditions,
|
||||
dynamic_runways: Dict[str, RunwayData],
|
||||
) -> RunwayData:
|
||||
logging.warning("TODO: FOBs have no runways.")
|
||||
return RunwayData(
|
||||
|
||||
Reference in New Issue
Block a user