Add carrier support to kneeboards.

This commit is contained in:
Dan Albert 2020-09-01 14:10:57 -07:00
parent a9e65cc83d
commit d02a3a0d3f
7 changed files with 191 additions and 128 deletions

View File

@ -80,7 +80,13 @@ class Operation:
self.visualgen = VisualGenerator(mission, conflict, self.game) self.visualgen = VisualGenerator(mission, conflict, self.game)
self.envgen = EnviromentGenerator(mission, conflict, self.game) self.envgen = EnviromentGenerator(mission, conflict, self.game)
self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game) self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game)
self.groundobjectgen = GroundObjectsGenerator(mission, conflict, self.game) self.groundobjectgen = GroundObjectsGenerator(
mission,
conflict,
self.game,
self.radio_registry,
self.tacan_registry
)
self.briefinggen = BriefingGenerator(mission, conflict, self.game) self.briefinggen = BriefingGenerator(mission, conflict, self.game)
def prepare(self, terrain: Terrain, is_quick: bool): def prepare(self, terrain: Terrain, is_quick: bool):
@ -136,15 +142,6 @@ class Operation:
for frequency in unique_beacon_frequencies: for frequency in unique_beacon_frequencies:
self.radio_registry.reserve(frequency) self.radio_registry.reserve(frequency)
# Generate meteo
if self.environment_settings is None:
self.environment_settings = self.envgen.generate()
else:
self.envgen.load(self.environment_settings)
# Generate ground object first
self.groundobjectgen.generate()
for airfield, data in AIRFIELD_DATA.items(): for airfield, data in AIRFIELD_DATA.items():
if data.theater == self.game.theater.terrain.name: if data.theater == self.game.theater.terrain.name:
self.radio_registry.reserve(data.atc.hf) self.radio_registry.reserve(data.atc.hf)
@ -154,6 +151,15 @@ class Operation:
# No need to reserve ILS or TACAN because those are in the # No need to reserve ILS or TACAN because those are in the
# beacon list. # beacon list.
# Generate meteo
if self.environment_settings is None:
self.environment_settings = self.envgen.generate()
else:
self.envgen.load(self.environment_settings)
# Generate ground object first
self.groundobjectgen.generate()
# Generate destroyed units # Generate destroyed units
for d in self.game.get_destroyed_units(): for d in self.game.get_destroyed_units():
try: try:
@ -185,7 +191,12 @@ class Operation:
else: else:
country = self.current_mission.country(self.game.enemy_country) country = self.current_mission.country(self.game.enemy_country)
if cp.id in self.game.planners.keys(): if cp.id in self.game.planners.keys():
self.airgen.generate_flights(cp, country, self.game.planners[cp.id]) self.airgen.generate_flights(
cp,
country,
self.game.planners[cp.id],
self.groundobjectgen.runways
)
# Generate ground units on frontline everywhere # Generate ground units on frontline everywhere
self.game.jtacs = [] self.game.jtacs = []
@ -309,27 +320,16 @@ class Operation:
last_channel = flight.num_radio_channels(radio_id) last_channel = flight.num_radio_channels(radio_id)
channel_alloc = iter(range(first_channel, last_channel + 1)) channel_alloc = iter(range(first_channel, last_channel + 1))
# TODO: Fix departure/arrival to support carriers. flight.assign_channel(radio_id, next(channel_alloc),flight.departure.atc)
if flight.departure is not None:
try:
departure = AIRFIELD_DATA[flight.departure.name]
flight.assign_channel(
radio_id, next(channel_alloc), departure.atc.uhf)
except KeyError:
pass
# TODO: If there ever are multiple AWACS, limit to mission relevant. # TODO: If there ever are multiple AWACS, limit to mission relevant.
for awacs in self.airsupportgen.air_support.awacs: for awacs in self.airsupportgen.air_support.awacs:
flight.assign_channel(radio_id, next(channel_alloc), awacs.freq) flight.assign_channel(radio_id, next(channel_alloc), awacs.freq)
# TODO: Fix departure/arrival to support carriers. # TODO: Fix departure/arrival to support carriers.
if flight.arrival is not None and flight.arrival != flight.departure: if flight.arrival != flight.departure:
try: flight.assign_channel(radio_id, next(channel_alloc),
arrival = AIRFIELD_DATA[flight.arrival.name] flight.arrival.atc)
flight.assign_channel(
radio_id, next(channel_alloc), arrival.atc.uhf)
except KeyError:
pass
try: try:
# TODO: Skip incompatible tankers. # TODO: Skip incompatible tankers.
@ -338,12 +338,8 @@ class Operation:
radio_id, next(channel_alloc), tanker.freq) radio_id, next(channel_alloc), tanker.freq)
if flight.divert is not None: if flight.divert is not None:
try: flight.assign_channel(radio_id, next(channel_alloc),
divert = AIRFIELD_DATA[flight.divert.name] flight.divert.atc)
flight.assign_channel(
radio_id, next(channel_alloc), divert.atc.uhf)
except KeyError:
pass
except StopIteration: except StopIteration:
# Any remaining channels are nice-to-haves, but not necessary for # Any remaining channels are nice-to-haves, but not necessary for
# the few aircraft with a small number of channels available. # the few aircraft with a small number of channels available.

View File

@ -4,6 +4,7 @@ from typing import Dict, List, Optional, Tuple
from game.data.cap_capabilities_db import GUNFIGHTERS from game.data.cap_capabilities_db import GUNFIGHTERS
from game.settings import Settings from game.settings import Settings
from game.utils import nm_to_meter from game.utils import nm_to_meter
from gen.airfields import RunwayData
from gen.flights.ai_flight_planner import FlightPlanner from gen.flights.ai_flight_planner import FlightPlanner
from gen.flights.flight import ( from gen.flights.flight import (
Flight, Flight,
@ -150,15 +151,14 @@ class FlightData:
#: List of playable units in the flight. #: List of playable units in the flight.
client_units: List[FlyingUnit] client_units: List[FlyingUnit]
# TODO: Arrival and departure should not be optional, but carriers don't count.
#: Arrival airport. #: Arrival airport.
arrival: Optional[Airport] arrival: RunwayData
#: Departure airport. #: Departure airport.
departure: Optional[Airport] departure: RunwayData
#: Diver airport. #: Diver airport.
divert: Optional[Airport] divert: Optional[RunwayData]
#: Waypoints of the flight plan. #: Waypoints of the flight plan.
waypoints: List[FlightWaypoint] waypoints: List[FlightWaypoint]
@ -169,8 +169,8 @@ class FlightData:
#: Map of radio frequencies to their assigned radio and channel, if any. #: Map of radio frequencies to their assigned radio and channel, if any.
frequency_to_channel_map: Dict[RadioFrequency, ChannelAssignment] frequency_to_channel_map: Dict[RadioFrequency, ChannelAssignment]
def __init__(self, client_units: List[FlyingUnit], arrival: Airport, def __init__(self, client_units: List[FlyingUnit], arrival: RunwayData,
departure: Airport, divert: Optional[Airport], departure: RunwayData, divert: Optional[RunwayData],
waypoints: List[FlightWaypoint], waypoints: List[FlightWaypoint],
intra_flight_channel: RadioFrequency) -> None: intra_flight_channel: RadioFrequency) -> None:
self.client_units = client_units self.client_units = client_units
@ -261,8 +261,8 @@ class AircraftConflictGenerator:
def _start_type(self) -> StartType: def _start_type(self) -> StartType:
return self.settings.cold_start and StartType.Cold or StartType.Warm return self.settings.cold_start and StartType.Cold or StartType.Warm
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task],
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task], flight: Flight): flight: Flight, dynamic_runways: Dict[str, RunwayData]):
did_load_loadout = False did_load_loadout = False
unit_type = group.units[0].unit_type unit_type = group.units[0].unit_type
@ -319,10 +319,28 @@ class AircraftConflictGenerator:
radio_id, channel = self.get_intra_flight_channel(unit_type) radio_id, channel = self.get_intra_flight_channel(unit_type)
group.set_frequency(channel.mhz, radio_id) group.set_frequency(channel.mhz, radio_id)
# TODO: Support for different departure/arrival airfields.
cp = flight.from_cp
fallback_runway = RunwayData(cp.full_name, runway_name="")
if cp.cptype == ControlPointType.AIRBASE:
# TODO: Implement logic for picking preferred runway.
runway = flight.from_cp.airport.runways[0]
runway_side = ["", "L", "R"][runway.leftright]
runway_name = f"{runway.heading}{runway_side}"
departure_runway = RunwayData.for_airfield(
flight.from_cp.airport, runway_name)
elif cp.is_fleet:
departure_runway = dynamic_runways.get(cp.name, fallback_runway)
else:
logging.warning(f"Unhandled departure control point: {cp.cptype}")
departure_runway = fallback_runway
self.flights.append(FlightData( self.flights.append(FlightData(
client_units=clients, client_units=clients,
departure=flight.from_cp.airport, departure=departure_runway,
arrival=flight.from_cp.airport, arrival=departure_runway,
# TODO: Support for divert airfields.
divert=None, divert=None,
waypoints=flight.points, waypoints=flight.points,
intra_flight_channel=channel intra_flight_channel=channel
@ -477,8 +495,8 @@ class AircraftConflictGenerator:
logging.warning("Pylon not found ! => Pylon" + key + " on " + str(flight.unit_type)) logging.warning("Pylon not found ! => Pylon" + key + " on " + str(flight.unit_type))
def generate_flights(self, cp, country, flight_planner:FlightPlanner): def generate_flights(self, cp, country, flight_planner: FlightPlanner,
dynamic_runways: Dict[str, RunwayData]):
# Clear pydcs parking slots # Clear pydcs parking slots
if cp.airport is not None: if cp.airport is not None:
logging.info("CLEARING SLOTS @ " + cp.airport.name) logging.info("CLEARING SLOTS @ " + cp.airport.name)
@ -497,7 +515,8 @@ class AircraftConflictGenerator:
continue continue
logging.info("Generating flight : " + str(flight.unit_type)) logging.info("Generating flight : " + str(flight.unit_type))
group = self.generate_planned_flight(cp, country, flight) group = self.generate_planned_flight(cp, country, flight)
self.setup_flight_group(group, flight, flight.flight_type) self.setup_flight_group(group, flight, flight.flight_type,
dynamic_runways)
self.setup_group_activation_trigger(flight, group) self.setup_group_activation_trigger(flight, group)
@ -608,19 +627,13 @@ class AircraftConflictGenerator:
flight.group = group flight.group = group
return group return group
def setup_group_as_intercept_flight(self, group, flight):
group.points[0].ETA = 0
group.late_activation = True
self._setup_group(group, Intercept, flight)
for point in flight.points:
group.add_waypoint(Point(point.x,point.y), point.alt)
def setup_flight_group(self, group, flight, flight_type,
def setup_flight_group(self, group, flight, flight_type): dynamic_runways: Dict[str, RunwayData]):
if flight_type in [FlightType.CAP, FlightType.BARCAP, FlightType.TARCAP, FlightType.INTERCEPTION]: if flight_type in [FlightType.CAP, FlightType.BARCAP, FlightType.TARCAP, FlightType.INTERCEPTION]:
group.task = CAP.name group.task = CAP.name
self._setup_group(group, CAP, flight) self._setup_group(group, CAP, flight, dynamic_runways)
# group.points[0].tasks.clear() # group.points[0].tasks.clear()
group.points[0].tasks.clear() group.points[0].tasks.clear()
group.points[0].tasks.append(EngageTargets(max_distance=nm_to_meter(50), targets=[Targets.All.Air])) group.points[0].tasks.append(EngageTargets(max_distance=nm_to_meter(50), targets=[Targets.All.Air]))
@ -632,7 +645,7 @@ class AircraftConflictGenerator:
elif flight_type in [FlightType.CAS, FlightType.BAI]: elif flight_type in [FlightType.CAS, FlightType.BAI]:
group.task = CAS.name group.task = CAS.name
self._setup_group(group, CAS, flight) self._setup_group(group, CAS, flight, dynamic_runways)
group.points[0].tasks.clear() group.points[0].tasks.clear()
group.points[0].tasks.append(EngageTargets(max_distance=nm_to_meter(10), targets=[Targets.All.GroundUnits.GroundVehicles])) group.points[0].tasks.append(EngageTargets(max_distance=nm_to_meter(10), targets=[Targets.All.GroundUnits.GroundVehicles]))
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)) group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
@ -641,7 +654,7 @@ class AircraftConflictGenerator:
group.points[0].tasks.append(OptRestrictJettison(True)) group.points[0].tasks.append(OptRestrictJettison(True))
elif flight_type in [FlightType.SEAD, FlightType.DEAD]: elif flight_type in [FlightType.SEAD, FlightType.DEAD]:
group.task = SEAD.name group.task = SEAD.name
self._setup_group(group, SEAD, flight) self._setup_group(group, SEAD, flight, dynamic_runways)
group.points[0].tasks.clear() group.points[0].tasks.clear()
group.points[0].tasks.append(NoTask()) group.points[0].tasks.append(NoTask())
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)) group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
@ -650,14 +663,14 @@ class AircraftConflictGenerator:
group.points[0].tasks.append(OptRTBOnOutOfAmmo(OptRTBOnOutOfAmmo.Values.ASM)) group.points[0].tasks.append(OptRTBOnOutOfAmmo(OptRTBOnOutOfAmmo.Values.ASM))
elif flight_type in [FlightType.STRIKE]: elif flight_type in [FlightType.STRIKE]:
group.task = PinpointStrike.name group.task = PinpointStrike.name
self._setup_group(group, GroundAttack, flight) self._setup_group(group, GroundAttack, flight, dynamic_runways)
group.points[0].tasks.clear() group.points[0].tasks.clear()
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)) group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire)) group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
group.points[0].tasks.append(OptRestrictJettison(True)) group.points[0].tasks.append(OptRestrictJettison(True))
elif flight_type in [FlightType.ANTISHIP]: elif flight_type in [FlightType.ANTISHIP]:
group.task = AntishipStrike.name group.task = AntishipStrike.name
self._setup_group(group, AntishipStrike, flight) self._setup_group(group, AntishipStrike, flight, dynamic_runways)
group.points[0].tasks.clear() group.points[0].tasks.clear()
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)) group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire)) group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
@ -736,23 +749,3 @@ class AircraftConflictGenerator:
pt.name = String(point.name) pt.name = String(point.name)
self._setup_custom_payload(flight, group) self._setup_custom_payload(flight, group)
def setup_group_as_antiship_flight(self, group, flight):
group.task = AntishipStrike.name
self._setup_group(group, AntishipStrike, flight)
group.points[0].tasks.clear()
group.points[0].tasks.append(AntishipStrikeTaskAction())
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
group.points[0].tasks.append(OptRestrictJettison(True))
for point in flight.points:
group.add_waypoint(Point(point.x, point.y), point.alt)
def setup_radio_preset(self, flight, group):
pass

View File

@ -4,8 +4,10 @@ Remove once https://github.com/pydcs/dcs/issues/69 tracks getting the missing
data added to pydcs. Until then, missing data can be manually filled in here. data added to pydcs. Until then, missing data can be manually filled in here.
""" """
from dataclasses import dataclass, field from dataclasses import dataclass, field
import logging
from typing import Dict, Optional, Tuple from typing import Dict, Optional, Tuple
from pydcs.dcs.terrain.terrain import Airport
from .radios import MHz, RadioFrequency from .radios import MHz, RadioFrequency
from .tacan import TacanBand, TacanChannel from .tacan import TacanBand, TacanChannel
@ -637,3 +639,39 @@ AIRFIELD_DATA = {
atc=AtcData(MHz(3, 775), MHz(118, 50), MHz(38, 450), MHz(250, 50)), atc=AtcData(MHz(3, 775), MHz(118, 50), MHz(38, 450), MHz(250, 50)),
), ),
} }
@dataclass
class RunwayData:
airfield_name: str
runway_name: str
atc: Optional[RadioFrequency] = None
tacan: Optional[TacanChannel] = None
ils: Optional[RadioFrequency] = None
icls: Optional[int] = None
@classmethod
def for_airfield(cls, airport: Airport, runway: str) -> "RunwayData":
"""Creates RunwayData for the given runway of an airfield.
Args:
airport: The airfield the runway belongs to.
runway: Identifier of the runway to use. e.g. "030" or "200L".
"""
atc: Optional[RadioFrequency] = None
tacan: Optional[TacanChannel] = None
ils: Optional[RadioFrequency] = None
try:
airfield = AIRFIELD_DATA[airport.name]
atc = airfield.atc.uhf
tacan = airfield.tacan
ils = airfield.ils_freq(runway)
except KeyError:
logging.warning(f"No airfield data for {airport.name}")
return cls(
airport.name,
runway,
atc,
tacan,
ils
)

View File

@ -1,13 +1,19 @@
import logging
from game import db
from game.data.building_data import FORTIFICATION_UNITS_ID, FORTIFICATION_UNITS from game.data.building_data import FORTIFICATION_UNITS_ID, FORTIFICATION_UNITS
from game.db import unit_type_from_name from game.db import unit_type_from_name
from pydcs.dcs.mission import *
from pydcs.dcs.statics import *
from pydcs.dcs.task import (
ActivateBeaconCommand,
ActivateICLSCommand,
OptAlarmState,
)
from pydcs.dcs.unit import Ship, Vehicle
from pydcs.dcs.unitgroup import StaticGroup
from .airfields import RunwayData
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
from .radios import RadioRegistry
from dcs.mission import * from .tacan import TacanBand, TacanRegistry
from dcs.statics import *
FARP_FRONTLINE_DISTANCE = 10000 FARP_FRONTLINE_DISTANCE = 10000
AA_CP_MIN_DISTANCE = 40000 AA_CP_MIN_DISTANCE = 40000
@ -16,10 +22,15 @@ AA_CP_MIN_DISTANCE = 40000
class GroundObjectsGenerator: class GroundObjectsGenerator:
FARP_CAPACITY = 4 FARP_CAPACITY = 4
def __init__(self, mission: Mission, conflict: Conflict, game): def __init__(self, mission: Mission, conflict: Conflict, game,
radio_registry: RadioRegistry, tacan_registry: TacanRegistry):
self.m = mission self.m = mission
self.conflict = conflict self.conflict = conflict
self.game = game self.game = game
self.radio_registry = radio_registry
self.tacan_registry = tacan_registry
self.icls_alloc = iter(range(1, 21))
self.runways: Dict[str, RunwayData] = {}
def generate_farps(self, number_of_units=1) -> typing.Collection[StaticGroup]: def generate_farps(self, number_of_units=1) -> typing.Collection[StaticGroup]:
if self.conflict.is_vector: if self.conflict.is_vector:
@ -103,6 +114,8 @@ class GroundObjectsGenerator:
utype = db.upgrade_to_supercarrier(utype, cp.name) utype = db.upgrade_to_supercarrier(utype, cp.name)
sg = self.m.ship_group(side, g.name, utype, position=g.position, heading=g.units[0].heading) sg = self.m.ship_group(side, g.name, utype, position=g.position, heading=g.units[0].heading)
atc_channel = self.radio_registry.alloc_uhf()
sg.set_frequency(atc_channel.hertz)
sg.units[0].name = self.m.string(g.units[0].name) sg.units[0].name = self.m.string(g.units[0].name)
for i, u in enumerate(g.units): for i, u in enumerate(g.units):
@ -111,6 +124,8 @@ class GroundObjectsGenerator:
ship.position.x = u.position.x ship.position.x = u.position.x
ship.position.y = u.position.y ship.position.y = u.position.y
ship.heading = u.heading ship.heading = u.heading
# TODO: Verify.
ship.set_frequency(atc_channel.hertz)
sg.add_unit(ship) sg.add_unit(ship)
# Find carrier direction (In the wind) # Find carrier direction (In the wind)
@ -125,10 +140,57 @@ class GroundObjectsGenerator:
attempt = attempt + 1 attempt = attempt + 1
# Set UP TACAN and ICLS # Set UP TACAN and ICLS
modeChannel = "X" if not cp.tacanY else "Y" tacan = self.tacan_registry.alloc_for_band(TacanBand.X)
sg.points[0].tasks.append(ActivateBeaconCommand(channel=cp.tacanN, modechannel=modeChannel, callsign=cp.tacanI, unit_id=sg.units[0].id, aa=False)) icls_channel = next(self.icls_alloc)
if ground_object.dcs_identifier == "CARRIER" and hasattr(cp, "icls"): # TODO: Assign these properly.
sg.points[0].tasks.append(ActivateICLSCommand(cp.icls, unit_id=sg.units[0].id)) if ground_object.dcs_identifier == "CARRIER":
tacan_callsign = random.choice([
"STE",
"CVN",
"CVH",
"CCV",
"ACC",
"ARC",
"GER",
"ABR",
"LIN",
"TRU",
])
else:
tacan_callsign = random.choice([
"LHD",
"LHA",
"LHB",
"LHC",
"LHD",
"LDS",
])
sg.points[0].tasks.append(ActivateBeaconCommand(
channel=tacan.number,
modechannel=tacan.band.value,
callsign=tacan_callsign,
unit_id=sg.units[0].id,
aa=False
))
sg.points[0].tasks.append(ActivateICLSCommand(
icls_channel,
unit_id=sg.units[0].id
))
# TODO: Make unit name usable.
# This relies on one control point mapping exactly
# to one LHA, carrier, or other usable "runway".
# This isn't wholly true, since the DD escorts of
# the carrier group are valid for helicopters, but
# they aren't exposed as such to the game. Should
# clean this up so that's possible. We can't use the
# unit name since it's an arbitrary ID.
self.runways[cp.name] = RunwayData(
cp.name,
"N/A",
atc=atc_channel,
tacan=tacan,
icls=icls_channel,
)
else: else:

View File

@ -31,11 +31,10 @@ from PIL import Image, ImageDraw, ImageFont
from tabulate import tabulate from tabulate import tabulate
from pydcs.dcs.mission import Mission from pydcs.dcs.mission import Mission
from pydcs.dcs.terrain.terrain import Airport
from pydcs.dcs.unittype import FlyingType from pydcs.dcs.unittype import FlyingType
from . import units from . import units
from .aircraft import FlightData from .aircraft import FlightData
from .airfields import AIRFIELD_DATA from .airfields import RunwayData
from .airsupportgen import AwacsInfo, TankerInfo from .airsupportgen import AwacsInfo, TankerInfo
from .radios import RadioFrequency from .radios import RadioFrequency
@ -135,7 +134,7 @@ class BriefingPage(KneeboardPage):
self.airfield_info_row("Departure", self.flight.departure), self.airfield_info_row("Departure", self.flight.departure),
self.airfield_info_row("Arrival", self.flight.arrival), self.airfield_info_row("Arrival", self.flight.arrival),
self.airfield_info_row("Divert", self.flight.divert), self.airfield_info_row("Divert", self.flight.divert),
], headers=["", "Airbase", "ATC", "TCN", "ILS", "RWY"]) ], headers=["", "Airbase", "ATC", "TCN", "I(C)LS", "RWY"])
writer.heading("Flight Plan") writer.heading("Flight Plan")
flight_plan = [] flight_plan = []
@ -176,41 +175,30 @@ class BriefingPage(KneeboardPage):
writer.write(path) writer.write(path)
def airfield_info_row(self, row_title: str, def airfield_info_row(self, row_title: str,
airfield: Optional[Airport]) -> List[str]: runway: Optional[RunwayData]) -> List[str]:
"""Creates a table row for a given airfield. """Creates a table row for a given airfield.
Args: Args:
row_title: Purpose of the airfield. e.g. "Departure", "Arrival" or row_title: Purpose of the airfield. e.g. "Departure", "Arrival" or
"Divert". "Divert".
airfield: The airfield described by this row. runway: The runway described by this row.
Returns: Returns:
A list of strings to be used as a row of the airfield table. A list of strings to be used as a row of the airfield table.
""" """
if airfield is None: if runway is None:
return [row_title, "", "", "", "", ""] return [row_title, "", "", "", "", ""]
# TODO: Implement logic for picking preferred runway. atc = ""
runway = airfield.runways[0] if runway.atc is not None:
runway_side = ["", "L", "R"][runway.leftright] atc = self.format_frequency(runway.atc)
runway_text = f"{runway.heading}{runway_side}"
try:
extra_data = AIRFIELD_DATA[airfield.name]
atc = self.format_frequency(extra_data.atc.uhf)
tacan = extra_data.tacan or ""
ils = extra_data.ils_freq(runway) or ""
except KeyError:
atc = ""
ils = ""
tacan = ""
return [ return [
row_title, row_title,
airfield.name, runway.airfield_name,
atc, atc,
tacan, runway.tacan or "",
ils, runway.ils or runway.icls or "",
runway_text, runway.runway_name,
] ]
def format_frequency(self, frequency: RadioFrequency) -> str: def format_frequency(self, frequency: RadioFrequency) -> str:

View File

@ -30,6 +30,6 @@ for t, uts in db.UNIT_BY_TASK.items():
altitude=10000 altitude=10000
) )
g.task = t.name g.task = t.name
airgen._setup_group(g, t, 0) airgen._setup_group(g, t, 0, {})
mis.save("loadout_test.miz") mis.save("loadout_test.miz")

View File

@ -27,7 +27,6 @@ class ControlPoint:
full_name = None # type: str full_name = None # type: str
base = None # type: theater.base.Base base = None # type: theater.base.Base
at = None # type: db.StartPosition at = None # type: db.StartPosition
icls = 1
allow_sea_units = True allow_sea_units = True
connected_points = None # type: typing.List[ControlPoint] connected_points = None # type: typing.List[ControlPoint]
@ -38,7 +37,6 @@ class ControlPoint:
frontline_offset = 0.0 frontline_offset = 0.0
cptype: ControlPointType = None cptype: ControlPointType = None
ICLS_counter = 1
alt = 0 alt = 0
def __init__(self, id: int, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: float, def __init__(self, id: int, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: float,
@ -63,10 +61,6 @@ class ControlPoint:
self.base = theater.base.Base() self.base = theater.base.Base()
self.cptype = cptype self.cptype = cptype
self.stances = {} self.stances = {}
self.tacanY = False
self.tacanN = None
self.tacanI = "TAC"
self.icls = 0
self.airport = None self.airport = None
@classmethod @classmethod
@ -81,11 +75,6 @@ class ControlPoint:
import theater.conflicttheater import theater.conflicttheater
cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1, cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1,
has_frontline=False, cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP) has_frontline=False, cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP)
cp.tacanY = False
cp.tacanN = random.randint(26, 49)
cp.tacanI = random.choice(["STE", "CVN", "CVH", "CCV", "ACC", "ARC", "GER", "ABR", "LIN", "TRU"])
ControlPoint.ICLS_counter = ControlPoint.ICLS_counter + 1
cp.icls = ControlPoint.ICLS_counter
return cp return cp
@classmethod @classmethod
@ -93,9 +82,6 @@ class ControlPoint:
import theater.conflicttheater import theater.conflicttheater
cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1, cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1,
has_frontline=False, cptype=ControlPointType.LHA_GROUP) has_frontline=False, cptype=ControlPointType.LHA_GROUP)
cp.tacanY = False
cp.tacanN = random.randint(1,25)
cp.tacanI = random.choice(["LHD", "LHA", "LHB", "LHC", "LHD", "LDS"])
return cp return cp
@property @property