Implemented generating player slots in the Pretense campaign.

This commit is contained in:
MetalStormGhost 2023-09-19 01:05:52 +03:00
parent 95bac8ec17
commit 946d578ffb
2 changed files with 213 additions and 22 deletions

View File

@ -9,6 +9,7 @@ from typing import Any, Dict, List, TYPE_CHECKING, Tuple, Optional
from dcs import Point from dcs import Point
from dcs.country import Country from dcs.country import Country
from dcs.mission import Mission from dcs.mission import Mission
from dcs.terrain import NoParkingSlotError
from dcs.unitgroup import FlyingGroup, StaticGroup from dcs.unitgroup import FlyingGroup, StaticGroup
from game.ato.airtaaskingorder import AirTaskingOrder from game.ato.airtaaskingorder import AirTaskingOrder
@ -19,6 +20,7 @@ from game.ato.package import Package
from game.ato.starttype import StartType from game.ato.starttype import StartType
from game.coalition import Coalition from game.coalition import Coalition
from game.data.weapons import WeaponType from game.data.weapons import WeaponType
from game.dcs.aircrafttype import AircraftType
from game.missiongenerator.aircraft.flightdata import FlightData from game.missiongenerator.aircraft.flightdata import FlightData
from game.missiongenerator.aircraft.flightgroupconfigurator import ( from game.missiongenerator.aircraft.flightgroupconfigurator import (
FlightGroupConfigurator, FlightGroupConfigurator,
@ -36,6 +38,7 @@ from game.theater.controlpoint import (
ParkingType, ParkingType,
Airfield, Airfield,
) )
from game.theater.theatergroundobject import EwrGroundObject, SamGroundObject
from game.unitmap import UnitMap from game.unitmap import UnitMap
from game.squadrons import Squadron from game.squadrons import Squadron
@ -51,6 +54,7 @@ PRETENSE_BARCAP_FLIGHTS_PER_CP = 1
PRETENSE_AI_AIRCRAFT_PER_FLIGHT = 2 PRETENSE_AI_AIRCRAFT_PER_FLIGHT = 2
PRETENSE_AI_AWACS_PER_FLIGHT = 1 PRETENSE_AI_AWACS_PER_FLIGHT = 1
PRETENSE_AI_TANKERS_PER_FLIGHT = 1 PRETENSE_AI_TANKERS_PER_FLIGHT = 1
PRETENSE_PLAYER_AIRCRAFT_PER_FLIGHT = 2
class PretenseAircraftGenerator: class PretenseAircraftGenerator:
@ -214,6 +218,49 @@ class PretenseAircraftGenerator:
coalition.air_wing.add_squadron(squadron) coalition.air_wing.add_squadron(squadron)
return squadron return squadron
def generate_pretense_squadron_for(
self,
aircraft_type: AircraftType,
cp: ControlPoint,
coalition: Coalition,
) -> Optional[Squadron]:
"""
Generates a Pretense squadron from the faction squadron definitions for the designated
AircraftType. Use FlightType AIR_ASSAULT
for Pretense supply helicopters and TRANSPORT for off-map cargo plane squadrons.
"""
squadron_def = coalition.air_wing.squadron_def_generator.generate_for_aircraft(
aircraft_type
)
flight_type = random.choice(list(squadron_def.auto_assignable_mission_types))
if flight_type == FlightType.ESCORT and aircraft_type.helicopter:
flight_type = FlightType.CAS
if flight_type in (
FlightType.INTERCEPTION,
FlightType.ESCORT,
FlightType.SWEEP,
):
flight_type = FlightType.BARCAP
if flight_type in (FlightType.SEAD_ESCORT, FlightType.SEAD_SWEEP):
flight_type = FlightType.SEAD
if flight_type == FlightType.ANTISHIP:
flight_type = FlightType.STRIKE
if flight_type == FlightType.TRANSPORT:
flight_type = FlightType.AIR_ASSAULT
squadron = Squadron.create_from(
squadron_def,
flight_type,
2,
cp,
coalition,
self.game,
)
if squadron.aircraft not in coalition.air_wing.squadrons:
coalition.air_wing.squadrons[squadron.aircraft] = list()
coalition.air_wing.add_squadron(squadron)
return squadron
def generate_pretense_aircraft( def generate_pretense_aircraft(
self, cp: ControlPoint, ato: AirTaskingOrder self, cp: ControlPoint, ato: AirTaskingOrder
) -> None: ) -> None:
@ -462,6 +509,68 @@ class PretenseAircraftGenerator:
ato.add_package(package) ato.add_package(package)
return return
def generate_pretense_aircraft_for_players(
self, cp: ControlPoint, coalition: Coalition, ato: AirTaskingOrder
) -> None:
"""
Plans and generates player piloted aircraft groups/packages for Pretense.
Aircraft generation is done by walking the control points which will be made into
Pretense "zones" and spawning flights for different missions.
After the flight is generated the package is added to the ATO so the flights
can be configured.
Args:
cp: Control point to generate aircraft for.
coalition: Coalition to generate aircraft for.
ato: The ATO to generate aircraft for.
"""
aircraft_per_flight = PRETENSE_PLAYER_AIRCRAFT_PER_FLIGHT
random_aircraft_list = list(coalition.faction.aircraft)
random.shuffle(random_aircraft_list)
for aircraft_type in random_aircraft_list:
# Don't generate any player flights for non-flyable types (obviously)
if not aircraft_type.flyable:
continue
if not cp.can_operate(aircraft_type):
continue
squadron = self.generate_pretense_squadron_for(
aircraft_type,
cp,
coalition,
)
if squadron is not None:
squadron.owned_aircraft += PRETENSE_PLAYER_AIRCRAFT_PER_FLIGHT
squadron.untasked_aircraft += PRETENSE_PLAYER_AIRCRAFT_PER_FLIGHT
squadron.populate_for_turn_0(False)
for pilot in squadron.pilot_pool:
pilot.player = True
package = Package(cp, squadron.flight_db, auto_asap=False)
flight = Flight(
package,
squadron,
aircraft_per_flight,
squadron.primary_task,
StartType.COLD,
divert=cp,
)
for pilot in flight.roster.pilots:
if pilot is not None:
pilot.player = True
print(
f"Generated flight for {squadron.primary_task} flying {squadron.aircraft.name} at {squadron.location.name}. Pilot client count: {flight.client_count}"
)
package.add_flight(flight)
flight.state = WaitingForStart(
flight, self.game.settings, self.game.conditions.start_time
)
ato.add_package(package)
return
def initialize_pretense_data_structures(self, cp: ControlPoint) -> None: def initialize_pretense_data_structures(self, cp: ControlPoint) -> None:
""" """
Ensures that the data structures used to pass flight group information Ensures that the data structures used to pass flight group information
@ -531,6 +640,7 @@ class PretenseAircraftGenerator:
""" """
self.initialize_pretense_data_structures(cp) self.initialize_pretense_data_structures(cp)
is_player = True
if country == cp.coalition.faction.country: if country == cp.coalition.faction.country:
offmap_transport_cp = self.find_pretense_cargo_plane_cp(cp) offmap_transport_cp = self.find_pretense_cargo_plane_cp(cp)
@ -558,7 +668,6 @@ class PretenseAircraftGenerator:
self.generate_pretense_aircraft(cp, ato) self.generate_pretense_aircraft(cp, ato)
else: else:
is_player = True
coalition = ( coalition = (
self.game.coalition_for(is_player) self.game.coalition_for(is_player)
if country == self.game.coalition_for(is_player).faction.country if country == self.game.coalition_for(is_player).faction.country
@ -566,6 +675,11 @@ class PretenseAircraftGenerator:
) )
self.generate_pretense_aircraft_for_other_side(cp, coalition, ato) self.generate_pretense_aircraft_for_other_side(cp, coalition, ato)
if country == self.game.coalition_for(is_player).faction.country:
if not isinstance(cp, OffMapSpawn):
coalition = self.game.coalition_for(is_player)
self.generate_pretense_aircraft_for_players(cp, coalition, ato)
self._reserve_frequencies_and_tacan(ato) self._reserve_frequencies_and_tacan(ato)
def generate_packages( def generate_packages(
@ -590,10 +704,15 @@ class PretenseAircraftGenerator:
) )
flight.return_pilots_and_aircraft() flight.return_pilots_and_aircraft()
continue continue
logging.info(f"Generating flight: {flight.unit_type}") logging.info(
group = self.create_and_configure_flight( f"Generating flight: {flight.unit_type} for {flight.flight_type.name}"
flight, country, dynamic_runways
) )
try:
group = self.create_and_configure_flight(
flight, country, dynamic_runways
)
except NoParkingSlotError:
return
self.unit_map.add_aircraft(group, flight) self.unit_map.add_aircraft(group, flight)
def create_and_configure_flight( def create_and_configure_flight(
@ -632,6 +751,67 @@ class PretenseAircraftGenerator:
self.use_client, self.use_client,
).configure() ).configure()
) )
else:
if flight.client_count > 0:
if flight.flight_type == FlightType.CAS:
for conflict in self.game.theater.conflicts():
flight.package.target = conflict
break
elif (
flight.flight_type == FlightType.STRIKE
or flight.flight_type == FlightType.BAI
):
for cp in self.game.theater.closest_opposing_control_points():
if cp.coalition == flight.coalition:
continue
for mission_target in cp.ground_objects:
flight.package.target = mission_target
elif (
flight.flight_type == FlightType.OCA_RUNWAY
or flight.flight_type == FlightType.OCA_AIRCRAFT
):
for cp in self.game.theater.controlpoints:
if cp.coalition == flight.coalition or not isinstance(
cp, Airfield
):
continue
flight.package.target = cp
elif flight.flight_type == FlightType.DEAD:
for cp in self.game.theater.controlpoints:
if cp.coalition == flight.coalition:
continue
for ground_object in cp.ground_objects:
is_ewr = isinstance(ground_object, EwrGroundObject)
is_sam = isinstance(ground_object, SamGroundObject)
if is_ewr or is_sam:
flight.package.target = ground_object
elif flight.flight_type == FlightType.AIR_ASSAULT:
for cp in self.game.theater.closest_opposing_control_points():
if cp.coalition == flight.coalition:
continue
if flight.is_hercules:
if cp.coalition == flight.coalition or not isinstance(
cp, Airfield
):
continue
flight.package.target = cp
FlightGroupConfigurator(
flight,
group,
self.game,
self.mission,
self.time,
self.radio_registry,
self.tacan_registy,
self.laser_code_registry,
self.mission_data,
dynamic_runways,
self.use_client,
).configure()
# for unit in group.units:
# unit.set_client()
if self.ewrj: if self.ewrj:
self._track_ewrj_flight(flight, group) self._track_ewrj_flight(flight, group)

View File

@ -82,9 +82,10 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
try: try:
if self.start_type is StartType.IN_FLIGHT: if self.start_type is StartType.IN_FLIGHT:
self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][ if self.flight.client_count == 0:
self.flight.flight_type.name self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][
].append(name) self.flight.flight_type.name
].append(name)
group = self._generate_over_departure(name, cp) group = self._generate_over_departure(name, cp)
return group return group
elif isinstance(cp, NavalControlPoint): elif isinstance(cp, NavalControlPoint):
@ -95,9 +96,10 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
f"Carrier group {carrier_group} is a " f"Carrier group {carrier_group} is a "
f"{carrier_group.__class__.__name__}, expected a ShipGroup" f"{carrier_group.__class__.__name__}, expected a ShipGroup"
) )
self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][ if self.flight.client_count == 0:
self.flight.flight_type.name self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][
].append(name) self.flight.flight_type.name
].append(name)
return self._generate_at_group(name, carrier_group) return self._generate_at_group(name, carrier_group)
elif isinstance(cp, Fob): elif isinstance(cp, Fob):
is_heli = self.flight.squadron.aircraft.helicopter is_heli = self.flight.squadron.aircraft.helicopter
@ -125,9 +127,10 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
pad_group = self._generate_at_cp_ground_spawn(name, cp) pad_group = self._generate_at_cp_ground_spawn(name, cp)
if pad_group is not None: if pad_group is not None:
return pad_group return pad_group
self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][ if self.flight.client_count == 0:
self.flight.flight_type.name self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][
].append(name) self.flight.flight_type.name
].append(name)
return self._generate_over_departure(name, cp) return self._generate_over_departure(name, cp)
elif isinstance(cp, Airfield): elif isinstance(cp, Airfield):
is_heli = self.flight.squadron.aircraft.helicopter is_heli = self.flight.squadron.aircraft.helicopter
@ -145,9 +148,10 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
pad_group = self._generate_at_cp_ground_spawn(name, cp) pad_group = self._generate_at_cp_ground_spawn(name, cp)
if pad_group is not None: if pad_group is not None:
return pad_group return pad_group
self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][ if self.flight.client_count == 0:
self.flight.flight_type.name self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][
].append(name) self.flight.flight_type.name
].append(name)
return self._generate_at_airfield(name, cp) return self._generate_at_airfield(name, cp)
else: else:
raise NotImplementedError( raise NotImplementedError(
@ -155,12 +159,19 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
) )
except NoParkingSlotError: except NoParkingSlotError:
# Generated when there is no place on Runway or on Parking Slots # Generated when there is no place on Runway or on Parking Slots
logging.warning( if self.flight.client_count > 0:
"No room on runway or parking slots. Starting from the air." # Don't generate player airstarts
) logging.warning(
self.flight.start_type = StartType.IN_FLIGHT "No room on runway or parking slots. Not generating a player air-start."
group = self._generate_over_departure(name, cp) )
return group raise NoParkingSlotError
else:
logging.warning(
"No room on runway or parking slots. Starting from the air."
)
self.flight.start_type = StartType.IN_FLIGHT
group = self._generate_over_departure(name, cp)
return group
def generate_mid_mission(self) -> FlyingGroup[Any]: def generate_mid_mission(self) -> FlyingGroup[Any]:
assert isinstance(self.flight.state, InFlight) assert isinstance(self.flight.state, InFlight)