mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
This reverts commit bd2ec12e0f039c9500ea0dd94e7b2e4f7d168fef. Country is both the data (name, ID, etc) and the container for groups added to the miz, so it can't be used across multiple mission generations. See https://github.com/pydcs/dcs/issues/314 for potential follow up work that would let us do this. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2864.
211 lines
7.5 KiB
Python
211 lines
7.5 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import List, TYPE_CHECKING, Tuple, Type
|
|
|
|
from dcs.mission import Mission, StartType
|
|
from dcs.planes import IL_78M, KC130, KC135MPRS, KC_135, PlaneType
|
|
from dcs.task import (
|
|
AWACS,
|
|
ActivateBeaconCommand,
|
|
MainTask,
|
|
Refueling,
|
|
SetImmortalCommand,
|
|
SetInvisibleCommand,
|
|
)
|
|
from dcs.unittype import UnitType
|
|
|
|
from game.ato import FlightType
|
|
from game.callsigns import callsign_for_support_unit
|
|
from game.naming import namegen
|
|
from game.radio.radios import RadioRegistry
|
|
from game.radio.tacan import TacanBand, TacanRegistry, TacanUsage
|
|
from game.utils import Heading
|
|
from .airconflictdescription import AirConflictDescription
|
|
from .missiondata import AwacsInfo, MissionData, TankerInfo
|
|
|
|
if TYPE_CHECKING:
|
|
from game import Game
|
|
|
|
TANKER_DISTANCE = 15000
|
|
TANKER_ALT = 4572
|
|
TANKER_HEADING_OFFSET = 45
|
|
|
|
AWACS_DISTANCE = 150000
|
|
AWACS_ALT = 13000
|
|
|
|
|
|
class AirSupportGenerator:
|
|
def __init__(
|
|
self,
|
|
mission: Mission,
|
|
conflict: AirConflictDescription,
|
|
game: Game,
|
|
radio_registry: RadioRegistry,
|
|
tacan_registry: TacanRegistry,
|
|
mission_data: MissionData,
|
|
) -> None:
|
|
self.mission = mission
|
|
self.conflict = conflict
|
|
self.game = game
|
|
self.radio_registry = radio_registry
|
|
self.tacan_registry = tacan_registry
|
|
self.mission_data = mission_data
|
|
|
|
@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) -> None:
|
|
player_cp = (
|
|
self.conflict.blue_cp
|
|
if self.conflict.blue_cp.captured
|
|
else self.conflict.red_cp
|
|
)
|
|
|
|
country = self.mission.country(self.game.blue.country_name)
|
|
|
|
if not self.game.settings.disable_legacy_tanker:
|
|
fallback_tanker_number = 0
|
|
|
|
for i, tanker_unit_type in enumerate(
|
|
self.game.faction_for(player=True).tankers
|
|
):
|
|
unit_type = tanker_unit_type.dcs_unit_type
|
|
if not issubclass(unit_type, PlaneType):
|
|
logging.warning(f"Refueling aircraft {unit_type} must be a plane")
|
|
continue
|
|
|
|
# TODO: Make loiter altitude a property of the unit type.
|
|
alt, airspeed = self._get_tanker_params(tanker_unit_type.dcs_unit_type)
|
|
freq = self.radio_registry.alloc_uhf()
|
|
tacan = self.tacan_registry.alloc_for_band(
|
|
TacanBand.Y, TacanUsage.AirToAir
|
|
)
|
|
tanker_heading = Heading.from_degrees(
|
|
self.conflict.red_cp.position.heading_between_point(
|
|
self.conflict.blue_cp.position
|
|
)
|
|
+ TANKER_HEADING_OFFSET * i
|
|
)
|
|
tanker_position = player_cp.position.point_from_heading(
|
|
tanker_heading.degrees, TANKER_DISTANCE
|
|
)
|
|
tanker_group = self.mission.refuel_flight(
|
|
country=country,
|
|
name=namegen.next_tanker_name(country, tanker_unit_type),
|
|
airport=None,
|
|
plane_type=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.mission_data.tankers.append(
|
|
TankerInfo(
|
|
group_name=str(tanker_group.name),
|
|
callsign=callsign,
|
|
variant=tanker_unit_type.name,
|
|
freq=freq,
|
|
tacan=tacan,
|
|
start_time=None,
|
|
end_time=None,
|
|
blue=True,
|
|
)
|
|
)
|
|
|
|
if not self.game.settings.disable_legacy_aewc:
|
|
possible_awacs = [
|
|
a
|
|
for a in self.game.faction_for(player=True).aircrafts
|
|
if a.capable_of(FlightType.AEWC)
|
|
]
|
|
|
|
if not possible_awacs:
|
|
logging.warning("No AWACS for faction")
|
|
return
|
|
|
|
awacs_unit = possible_awacs[0]
|
|
freq = self.radio_registry.alloc_uhf()
|
|
|
|
unit_type = awacs_unit.dcs_unit_type
|
|
if not issubclass(unit_type, PlaneType):
|
|
logging.warning(f"AWACS aircraft {unit_type} must be a plane")
|
|
return
|
|
|
|
awacs_flight = self.mission.awacs_flight(
|
|
country=country,
|
|
name=namegen.next_awacs_name(country),
|
|
plane_type=unit_type,
|
|
altitude=AWACS_ALT,
|
|
airport=None,
|
|
position=self.conflict.center.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.mission_data.awacs.append(
|
|
AwacsInfo(
|
|
group_name=str(awacs_flight.name),
|
|
callsign=callsign_for_support_unit(awacs_flight),
|
|
freq=freq,
|
|
depature_location=None,
|
|
start_time=None,
|
|
end_time=None,
|
|
blue=True,
|
|
)
|
|
)
|