Set up JTAC channel assignments.

This commit is contained in:
Dan Albert 2021-07-31 12:59:42 -07:00
parent 119d4b9514
commit 8d68c10905
7 changed files with 101 additions and 88 deletions

View File

@ -16,11 +16,11 @@ from dcs.triggers import TriggerStart
from game.plugins import LuaPluginManager
from game.theater.theatergroundobject import TheaterGroundObject
from gen import Conflict, FlightType, VisualGenerator
from gen import Conflict, FlightType, VisualGenerator, AirSupport
from gen.aircraft import AircraftConflictGenerator, FlightData
from gen.airfields import AIRFIELD_DATA
from gen.airsupportgen import AirSupport, AirSupportConflictGenerator
from gen.armor import GroundConflictGenerator, JtacInfo
from gen.airsupportgen import AirSupportConflictGenerator
from gen.armor import GroundConflictGenerator
from gen.beacons import load_beacons_for_terrain
from gen.briefinggen import BriefingGenerator, MissionInfoGenerator
from gen.cargoshipgen import CargoShipGenerator
@ -58,8 +58,8 @@ class Operation:
enemy_awacs_enabled = True
ca_slots = 1
unit_map: UnitMap
jtacs: List[JtacInfo] = []
plugin_scripts: List[str] = []
air_support = AirSupport()
@classmethod
def prepare(cls, game: Game) -> None:
@ -146,8 +146,7 @@ class Operation:
def notify_info_generators(
cls,
groundobjectgen: GroundObjectsGenerator,
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo],
air_support: AirSupport,
airgen: AircraftConflictGenerator,
) -> None:
"""Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)"""
@ -160,15 +159,15 @@ class Operation:
for dynamic_runway in groundobjectgen.runways.values():
gen.add_dynamic_runway(dynamic_runway)
for tanker in airsupportgen.air_support.tankers:
for tanker in air_support.tankers:
if tanker.blue:
gen.add_tanker(tanker)
for aewc in airsupportgen.air_support.awacs:
for aewc in air_support.awacs:
if aewc.blue:
gen.add_awacs(aewc)
for jtac in jtacs:
for jtac in air_support.jtacs:
if jtac.blue:
gen.add_jtac(jtac)
@ -280,6 +279,7 @@ class Operation:
@classmethod
def generate(cls) -> UnitMap:
"""Build the final Mission to be exported"""
cls.air_support = AirSupport()
cls.create_unit_map()
cls.create_radio_registries()
# Set mission time and weather conditions.
@ -288,10 +288,10 @@ class Operation:
cls._generate_transports()
cls._generate_destroyed_units()
cls._generate_air_units()
cls._generate_ground_conflicts()
cls.assign_channels_to_flights(
cls.airgen.flights, cls.airsupportgen.air_support
)
cls._generate_ground_conflicts()
# Triggers
triggersgen = TriggersGenerator(cls.current_mission, cls.game)
@ -311,7 +311,7 @@ class Operation:
if cls.game.settings.perf_smoke_gen:
visualgen.generate()
cls.generate_lua(cls.airgen, cls.airsupportgen, cls.jtacs)
cls.generate_lua(cls.airgen, cls.air_support)
# Inject Plugins Lua Scripts and data
cls.plugin_scripts.clear()
@ -323,9 +323,7 @@ class Operation:
cls.assign_channels_to_flights(
cls.airgen.flights, cls.airsupportgen.air_support
)
cls.notify_info_generators(
cls.groundobjectgen, cls.airsupportgen, cls.jtacs, cls.airgen
)
cls.notify_info_generators(cls.groundobjectgen, cls.air_support, cls.airgen)
cls.reset_naming_ids()
return cls.unit_map
@ -341,6 +339,7 @@ class Operation:
cls.game,
cls.radio_registry,
cls.tacan_registry,
cls.air_support,
)
cls.airsupportgen.generate()
@ -375,7 +374,6 @@ class Operation:
@classmethod
def _generate_ground_conflicts(cls) -> None:
"""For each frontline in the Operation, generate the ground conflicts and JTACs"""
cls.jtacs = []
for front_line in cls.game.theater.conflicts():
player_cp = front_line.blue_cp
enemy_cp = front_line.red_cp
@ -400,9 +398,9 @@ class Operation:
enemy_cp.stances[player_cp.id],
cls.unit_map,
cls.radio_registry,
cls.air_support,
)
ground_conflict_gen.generate()
cls.jtacs.extend(ground_conflict_gen.jtacs)
@classmethod
def _generate_transports(cls) -> None:
@ -416,10 +414,7 @@ class Operation:
@classmethod
def generate_lua(
cls,
airgen: AircraftConflictGenerator,
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo],
cls, airgen: AircraftConflictGenerator, air_support: AirSupport
) -> None:
# TODO: Refactor this
luaData = {
@ -432,7 +427,7 @@ class Operation:
"BlueAA": {},
} # type: ignore
for i, tanker in enumerate(airsupportgen.air_support.tankers):
for i, tanker in enumerate(air_support.tankers):
luaData["Tankers"][i] = {
"dcsGroupName": tanker.group_name,
"callsign": tanker.callsign,
@ -441,14 +436,14 @@ class Operation:
"tacan": str(tanker.tacan.number) + tanker.tacan.band.name,
}
for i, awacs in enumerate(airsupportgen.air_support.awacs):
for i, awacs in enumerate(air_support.awacs):
luaData["AWACs"][i] = {
"dcsGroupName": awacs.group_name,
"callsign": awacs.callsign,
"radio": awacs.freq.mhz,
}
for i, jtac in enumerate(jtacs):
for i, jtac in enumerate(air_support.jtacs):
luaData["JTACs"][i] = {
"dcsGroupName": jtac.group_name,
"callsign": jtac.callsign,

View File

@ -72,6 +72,9 @@ class CommonRadioChannelAllocator(RadioChannelAllocator):
for awacs in air_support.awacs:
flight.assign_channel(radio_id, next(channel_alloc), awacs.freq)
for jtac in air_support.jtacs:
flight.assign_channel(radio_id, next(channel_alloc), jtac.freq)
if flight.arrival != flight.departure and flight.arrival.atc is not None:
flight.assign_channel(radio_id, next(channel_alloc), flight.arrival.atc)

View File

@ -81,7 +81,7 @@ from game.theater.missiontarget import MissionTarget
from game.theater.theatergroundobject import TheaterGroundObject
from game.transfers import MultiGroupTransport
from game.unitmap import UnitMap
from game.utils import Distance, Heading, meters, nautical_miles, pairwise
from game.utils import Distance, meters, nautical_miles, pairwise
from gen.ato import AirTaskingOrder, Package
from gen.callsigns import create_group_callsign_from_unit
from gen.flights.flight import (
@ -93,7 +93,7 @@ from gen.flights.flight import (
from gen.radios import RadioFrequency, RadioRegistry
from gen.runways import RunwayData
from gen.tacan import TacanBand, TacanRegistry
from .airsupportgen import AirSupport, AwacsInfo, TankerInfo
from .airsupport import AirSupport, AwacsInfo, TankerInfo
from .callsigns import callsign_for_support_unit
from .flights.flightplan import (
AwacsFlightPlan,

55
gen/airsupport.py Normal file
View File

@ -0,0 +1,55 @@
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import timedelta
from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING:
from gen import RadioFrequency, TacanChannel
@dataclass
class AwacsInfo:
"""AWACS information for the kneeboard."""
group_name: str
callsign: str
freq: RadioFrequency
depature_location: Optional[str]
start_time: Optional[timedelta]
end_time: Optional[timedelta]
blue: bool
@dataclass
class TankerInfo:
"""Tanker information for the kneeboard."""
group_name: str
callsign: str
variant: str
freq: RadioFrequency
tacan: TacanChannel
start_time: Optional[timedelta]
end_time: Optional[timedelta]
blue: bool
@dataclass(frozen=True)
class JtacInfo:
"""JTAC information."""
group_name: str
unit_name: str
callsign: str
region: str
code: str
blue: bool
freq: RadioFrequency
@dataclass
class AirSupport:
awacs: list[AwacsInfo] = field(default_factory=list)
tankers: list[TankerInfo] = field(default_factory=list)
jtacs: list[JtacInfo] = field(default_factory=list)

View File

@ -1,9 +1,7 @@
from __future__ import annotations
import logging
from dataclasses import dataclass, field
from datetime import timedelta
from typing import List, Type, Tuple, Optional, TYPE_CHECKING
from typing import List, Type, Tuple, TYPE_CHECKING
from dcs.mission import Mission, StartType
from dcs.planes import IL_78M, KC130, KC135MPRS, KC_135, PlaneType
@ -18,14 +16,14 @@ from dcs.task import (
from dcs.unittype import UnitType
from game.utils import Heading
from .flights.ai_flight_planner_db import AEWC_CAPABLE
from .naming import namegen
from . import AirSupport
from .airsupport import TankerInfo, AwacsInfo
from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict
from .flights.ai_flight_planner_db import AEWC_CAPABLE
from .naming import namegen
from .radios import RadioFrequency, RadioRegistry
from .tacan import TacanBand, TacanChannel, TacanRegistry
from .radios import RadioRegistry
from .tacan import TacanBand, TacanRegistry
if TYPE_CHECKING:
from game import Game
@ -38,39 +36,6 @@ AWACS_DISTANCE = 150000
AWACS_ALT = 13000
@dataclass
class AwacsInfo:
"""AWACS information for the kneeboard."""
group_name: str
callsign: str
freq: RadioFrequency
depature_location: Optional[str]
start_time: Optional[timedelta]
end_time: Optional[timedelta]
blue: bool
@dataclass
class TankerInfo:
"""Tanker information for the kneeboard."""
group_name: str
callsign: str
variant: str
freq: RadioFrequency
tacan: TacanChannel
start_time: Optional[timedelta]
end_time: Optional[timedelta]
blue: bool
@dataclass
class AirSupport:
awacs: List[AwacsInfo] = field(default_factory=list)
tankers: List[TankerInfo] = field(default_factory=list)
class AirSupportConflictGenerator:
def __init__(
self,
@ -79,13 +44,14 @@ class AirSupportConflictGenerator:
game: Game,
radio_registry: RadioRegistry,
tacan_registry: TacanRegistry,
air_support: AirSupport,
) -> None:
self.mission = mission
self.conflict = conflict
self.game = game
self.air_support = AirSupport()
self.radio_registry = radio_registry
self.tacan_registry = tacan_registry
self.air_support = air_support
@classmethod
def support_tasks(cls) -> List[Type[MainTask]]:
@ -94,12 +60,12 @@ class AirSupportConflictGenerator:
@staticmethod
def _get_tanker_params(unit_type: Type[UnitType]) -> Tuple[int, int]:
if unit_type is KC130:
return (TANKER_ALT - 500, 596)
return TANKER_ALT - 500, 596
elif unit_type is KC_135:
return (TANKER_ALT, 770)
return TANKER_ALT, 770
elif unit_type is KC135MPRS:
return (TANKER_ALT + 500, 596)
return (TANKER_ALT, 574)
return TANKER_ALT + 500, 596
return TANKER_ALT, 574
def generate(self) -> None:
player_cp = (

View File

@ -40,6 +40,7 @@ from gen.ground_forces.ai_ground_planner import (
CombatGroup,
CombatGroupRole,
)
from .airsupport import AirSupport, JtacInfo
from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict
from .ground_forces.combat_stance import CombatStance
@ -67,19 +68,6 @@ RANDOM_OFFSET_ATTACK = 250
INFANTRY_GROUP_SIZE = 5
@dataclass(frozen=True)
class JtacInfo:
"""JTAC information."""
group_name: str
unit_name: str
callsign: str
region: str
code: str
blue: bool
freq: RadioFrequency
class GroundConflictGenerator:
def __init__(
self,
@ -92,6 +80,7 @@ class GroundConflictGenerator:
enemy_stance: CombatStance,
unit_map: UnitMap,
radio_registry: RadioRegistry,
air_support: AirSupport,
) -> None:
self.mission = mission
self.conflict = conflict
@ -102,7 +91,7 @@ class GroundConflictGenerator:
self.game = game
self.unit_map = unit_map
self.radio_registry = radio_registry
self.jtacs: List[JtacInfo] = []
self.air_support = air_support
def generate(self) -> None:
position = Conflict.frontline_position(
@ -151,7 +140,7 @@ class GroundConflictGenerator:
# Add JTAC
if self.game.blue.faction.has_jtac:
n = "JTAC" + str(self.conflict.blue_cp.id) + str(self.conflict.red_cp.id)
code = 1688 - len(self.jtacs)
code = 1688 - len(self.air_support.jtacs)
freq = self.radio_registry.alloc_uhf()
utype = self.game.blue.faction.jtac_unit
@ -168,7 +157,7 @@ class GroundConflictGenerator:
maintask=AFAC,
)
jtac.points[0].tasks.append(
FAC(callsign=len(self.jtacs) + 1, frequency=int(freq.mhz))
FAC(callsign=len(self.air_support.jtacs) + 1, frequency=int(freq.mhz))
)
jtac.points[0].tasks.append(SetInvisibleCommand(True))
jtac.points[0].tasks.append(SetImmortalCommand(True))
@ -180,7 +169,7 @@ class GroundConflictGenerator:
)
# Note: Will need to change if we ever add ground based JTAC.
callsign = callsign_for_support_unit(jtac)
self.jtacs.append(
self.air_support.jtacs.append(
JtacInfo(
str(jtac.name),
n,

View File

@ -408,6 +408,8 @@ class BriefingPage(KneeboardPage):
class SupportPage(KneeboardPage):
"""A kneeboard page containing information about support units."""
JTAC_REGION_MAX_LEN = 25
def __init__(
self,
flight: FlightData,
@ -490,7 +492,10 @@ class SupportPage(KneeboardPage):
jtacs.append(
[
jtac.callsign,
KneeboardPageWriter.wrap_line(
jtac.region,
self.JTAC_REGION_MAX_LEN,
),
jtac.code,
self.format_frequency(jtac.freq),
]