mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Add carrier support to kneeboards.
This commit is contained in:
parent
a9e65cc83d
commit
d02a3a0d3f
@ -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.
|
||||||
|
|||||||
@ -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
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
)
|
||||||
|
|||||||
@ -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:
|
||||||
|
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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")
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user