mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Move beacons module.
This isn't related to the missiongenerator, and importing this file from some places (such as runway data) will cause a circular reference when importing the rest of the missiongenerator package.
This commit is contained in:
101
game/dcs/beacons.py
Normal file
101
game/dcs/beacons.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from collections.abc import Iterator
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum, auto
|
||||
from pathlib import Path
|
||||
from typing import Optional, TYPE_CHECKING
|
||||
|
||||
from game.radio.radios import RadioFrequency
|
||||
from game.radio.tacan import TacanBand, TacanChannel
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.theater import ConflictTheater
|
||||
|
||||
BEACONS_RESOURCE_PATH = Path("resources/dcs/beacons")
|
||||
|
||||
|
||||
class BeaconType(IntEnum):
|
||||
BEACON_TYPE_NULL = auto()
|
||||
BEACON_TYPE_VOR = auto()
|
||||
BEACON_TYPE_DME = auto()
|
||||
BEACON_TYPE_VOR_DME = auto()
|
||||
BEACON_TYPE_TACAN = auto()
|
||||
BEACON_TYPE_VORTAC = auto()
|
||||
BEACON_TYPE_RSBN = auto()
|
||||
BEACON_TYPE_BROADCAST_STATION = auto()
|
||||
|
||||
BEACON_TYPE_HOMER = auto()
|
||||
BEACON_TYPE_AIRPORT_HOMER = auto()
|
||||
BEACON_TYPE_AIRPORT_HOMER_WITH_MARKER = auto()
|
||||
BEACON_TYPE_ILS_FAR_HOMER = auto()
|
||||
BEACON_TYPE_ILS_NEAR_HOMER = auto()
|
||||
|
||||
BEACON_TYPE_ILS_LOCALIZER = auto()
|
||||
BEACON_TYPE_ILS_GLIDESLOPE = auto()
|
||||
|
||||
BEACON_TYPE_PRMG_LOCALIZER = auto()
|
||||
BEACON_TYPE_PRMG_GLIDESLOPE = auto()
|
||||
|
||||
BEACON_TYPE_ICLS_LOCALIZER = auto()
|
||||
BEACON_TYPE_ICLS_GLIDESLOPE = auto()
|
||||
|
||||
BEACON_TYPE_NAUTICAL_HOMER = auto()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Beacon:
|
||||
name: str
|
||||
callsign: str
|
||||
beacon_type: BeaconType
|
||||
hertz: int
|
||||
channel: Optional[int]
|
||||
|
||||
@property
|
||||
def frequency(self) -> RadioFrequency:
|
||||
return RadioFrequency(self.hertz)
|
||||
|
||||
@property
|
||||
def is_tacan(self) -> bool:
|
||||
return self.beacon_type in (
|
||||
BeaconType.BEACON_TYPE_VORTAC,
|
||||
BeaconType.BEACON_TYPE_TACAN,
|
||||
)
|
||||
|
||||
@property
|
||||
def tacan_channel(self) -> TacanChannel:
|
||||
assert self.is_tacan
|
||||
assert self.channel is not None
|
||||
return TacanChannel(self.channel, TacanBand.X)
|
||||
|
||||
|
||||
class Beacons:
|
||||
_by_terrain: dict[str, dict[str, Beacon]] = {}
|
||||
|
||||
@classmethod
|
||||
def _load_for_theater_if_needed(cls, theater: ConflictTheater) -> None:
|
||||
if theater.terrain.name in cls._by_terrain:
|
||||
return
|
||||
|
||||
beacons_file = BEACONS_RESOURCE_PATH / f"{theater.terrain.name.lower()}.json"
|
||||
if not beacons_file.exists():
|
||||
raise RuntimeError(f"Beacon file {beacons_file.resolve()} is missing")
|
||||
|
||||
beacons = {}
|
||||
for bid, beacon in json.loads(beacons_file.read_text()).items():
|
||||
beacons[bid] = Beacon(**beacon)
|
||||
cls._by_terrain[theater.terrain.name] = beacons
|
||||
|
||||
@classmethod
|
||||
def _dict_for_theater(cls, theater: ConflictTheater) -> dict[str, Beacon]:
|
||||
cls._load_for_theater_if_needed(theater)
|
||||
return cls._by_terrain[theater.terrain.name]
|
||||
|
||||
@classmethod
|
||||
def iter_theater(cls, theater: ConflictTheater) -> Iterator[Beacon]:
|
||||
yield from cls._dict_for_theater(theater).values()
|
||||
|
||||
@classmethod
|
||||
def with_id(cls, beacon_id: str, theater: ConflictTheater) -> Beacon:
|
||||
return cls._dict_for_theater(theater)[beacon_id]
|
||||
Reference in New Issue
Block a user