mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
200 lines
6.4 KiB
Python
200 lines
6.4 KiB
Python
import logging
|
|
from dataclasses import dataclass, field
|
|
from typing import List, Type, Tuple
|
|
|
|
from dcs.mission import Mission, StartType
|
|
from dcs.planes import IL_78M, KC130, KC135MPRS, KC_135
|
|
from dcs.unittype import UnitType
|
|
from dcs.task import (
|
|
AWACS,
|
|
ActivateBeaconCommand,
|
|
MainTask,
|
|
Refueling,
|
|
SetImmortalCommand,
|
|
SetInvisibleCommand,
|
|
)
|
|
|
|
from game import db
|
|
from .naming import namegen
|
|
from .callsigns import callsign_for_support_unit
|
|
from .conflictgen import Conflict
|
|
from .radios import RadioFrequency, RadioRegistry
|
|
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
|
|
|
TANKER_DISTANCE = 15000
|
|
TANKER_ALT = 4572
|
|
TANKER_HEADING_OFFSET = 45
|
|
|
|
AWACS_DISTANCE = 150000
|
|
AWACS_ALT = 13000
|
|
|
|
|
|
@dataclass
|
|
class AwacsInfo:
|
|
"""AWACS information for the kneeboard."""
|
|
|
|
dcsGroupName: str
|
|
callsign: str
|
|
freq: RadioFrequency
|
|
|
|
|
|
@dataclass
|
|
class TankerInfo:
|
|
"""Tanker information for the kneeboard."""
|
|
|
|
dcsGroupName: str
|
|
callsign: str
|
|
variant: str
|
|
freq: RadioFrequency
|
|
tacan: TacanChannel
|
|
|
|
|
|
@dataclass
|
|
class AirSupport:
|
|
awacs: List[AwacsInfo] = field(default_factory=list)
|
|
tankers: List[TankerInfo] = field(default_factory=list)
|
|
|
|
|
|
class AirSupportConflictGenerator:
|
|
def __init__(
|
|
self,
|
|
mission: Mission,
|
|
conflict: Conflict,
|
|
game,
|
|
radio_registry: RadioRegistry,
|
|
tacan_registry: TacanRegistry,
|
|
) -> None:
|
|
self.mission = mission
|
|
self.conflict = conflict
|
|
self.game = game
|
|
self.air_support = AirSupport()
|
|
self.radio_registry = radio_registry
|
|
self.tacan_registry = tacan_registry
|
|
|
|
@classmethod
|
|
def support_tasks(cls) -> List[Type[MainTask]]:
|
|
return [Refueling, AWACS]
|
|
|
|
@staticmethod
|
|
def _get_tanker_params(unit_type: Type[UnitType]) -> Tuple[int, int]:
|
|
if unit_type is KC130:
|
|
return (TANKER_ALT - 500, 596)
|
|
elif unit_type is KC_135:
|
|
return (TANKER_ALT, 770)
|
|
elif unit_type is KC135MPRS:
|
|
return (TANKER_ALT + 500, 596)
|
|
return (TANKER_ALT, 574)
|
|
|
|
def generate(self):
|
|
player_cp = (
|
|
self.conflict.from_cp
|
|
if self.conflict.from_cp.captured
|
|
else self.conflict.to_cp
|
|
)
|
|
|
|
fallback_tanker_number = 0
|
|
|
|
for i, tanker_unit_type in enumerate(
|
|
db.find_unittype(Refueling, self.conflict.attackers_side)
|
|
):
|
|
alt, airspeed = self._get_tanker_params(tanker_unit_type)
|
|
variant = db.unit_type_name(tanker_unit_type)
|
|
freq = self.radio_registry.alloc_uhf()
|
|
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
|
tanker_heading = (
|
|
self.conflict.to_cp.position.heading_between_point(
|
|
self.conflict.from_cp.position
|
|
)
|
|
+ TANKER_HEADING_OFFSET * i
|
|
)
|
|
tanker_position = player_cp.position.point_from_heading(
|
|
tanker_heading, TANKER_DISTANCE
|
|
)
|
|
tanker_group = self.mission.refuel_flight(
|
|
country=self.mission.country(self.game.player_country),
|
|
name=namegen.next_tanker_name(
|
|
self.mission.country(self.game.player_country), tanker_unit_type
|
|
),
|
|
airport=None,
|
|
plane_type=tanker_unit_type,
|
|
position=tanker_position,
|
|
altitude=alt,
|
|
race_distance=58000,
|
|
frequency=freq.mhz,
|
|
start_type=StartType.Warm,
|
|
speed=airspeed,
|
|
tacanchannel=str(tacan),
|
|
)
|
|
tanker_group.set_frequency(freq.mhz)
|
|
|
|
callsign = callsign_for_support_unit(tanker_group)
|
|
tacan_callsign = {
|
|
"Texaco": "TEX",
|
|
"Arco": "ARC",
|
|
"Shell": "SHL",
|
|
}.get(callsign)
|
|
if tacan_callsign is None:
|
|
# The dict above is all the callsigns currently in the game, but
|
|
# non-Western countries don't use the callsigns and instead just
|
|
# use numbers. It's possible that none of those nations have
|
|
# TACAN compatible refueling aircraft, but fallback just in
|
|
# case.
|
|
tacan_callsign = f"TK{fallback_tanker_number}"
|
|
fallback_tanker_number += 1
|
|
|
|
if tanker_unit_type != IL_78M:
|
|
# Override PyDCS tacan channel.
|
|
tanker_group.points[0].tasks.pop()
|
|
tanker_group.points[0].tasks.append(
|
|
ActivateBeaconCommand(
|
|
tacan.number,
|
|
tacan.band.value,
|
|
tacan_callsign,
|
|
True,
|
|
tanker_group.units[0].id,
|
|
True,
|
|
)
|
|
)
|
|
|
|
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
|
|
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
|
|
|
self.air_support.tankers.append(
|
|
TankerInfo(str(tanker_group.name), callsign, variant, freq, tacan)
|
|
)
|
|
|
|
possible_awacs = db.find_unittype(AWACS, self.conflict.attackers_side)
|
|
|
|
if len(possible_awacs) > 0:
|
|
awacs_unit = possible_awacs[0]
|
|
freq = self.radio_registry.alloc_uhf()
|
|
|
|
awacs_flight = self.mission.awacs_flight(
|
|
country=self.mission.country(self.game.player_country),
|
|
name=namegen.next_awacs_name(
|
|
self.mission.country(self.game.player_country)
|
|
),
|
|
plane_type=awacs_unit,
|
|
altitude=AWACS_ALT,
|
|
airport=None,
|
|
position=self.conflict.position.random_point_within(
|
|
AWACS_DISTANCE, AWACS_DISTANCE
|
|
),
|
|
frequency=freq.mhz,
|
|
start_type=StartType.Warm,
|
|
)
|
|
awacs_flight.set_frequency(freq.mhz)
|
|
|
|
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
|
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
|
|
|
self.air_support.awacs.append(
|
|
AwacsInfo(
|
|
str(awacs_flight.name),
|
|
callsign_for_support_unit(awacs_flight),
|
|
freq,
|
|
)
|
|
)
|
|
else:
|
|
logging.warning("No AWACS for faction")
|