Large aircraft ground spawns (#237)

* Large aircraft ground spawns

Added the ability to add separate ground spawns for C-130 and other large aircraft to campaigns. Implemented on @holyorangejuice 's request.

Large aircraft ground spawns are added to the campaign by placing a C-130 on the ramp, just like an A-10 or AJS37 previously. Note: use the stock DCS C-130, so the campaign miz can be safely opened without the C-130 mod (or any other mod) installed. Not the C-130J player-flyable transport, not the KC-130J tanker included in the UH-60L mod etc.

Large planes (wingspan more than 40 meters, such as the C-130):
- First try spawning on large ground spawns
- Then try the regular airfield ramp spawns

Below 40 meter wingspan aircraft:
- First try spawning on regular or roadbase ground spawns
- Then try the regular airfield ramp spawns
- Then, if both of the above fail, use the large ground spawns

* Specify explicit black version 23.9.1 to fix lint error.

* Update lint.yml

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
This commit is contained in:
MetalStormGhost 2024-07-01 23:26:50 +03:00 committed by GitHub
parent d84a0ebc78
commit 07cc85f6fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 267 additions and 46 deletions

View File

@ -80,6 +80,7 @@
* **[Mission Generator]** Set F-14's IP waypoint according to the flight-plan's ingress point * **[Mission Generator]** Set F-14's IP waypoint according to the flight-plan's ingress point
* **[Mission Generator]** Automatically de-spawn aircraft when arrival/divert is an off-map spawn * **[Mission Generator]** Automatically de-spawn aircraft when arrival/divert is an off-map spawn
* **[Options]** Option to de-spawn AI flights in the air if their start-type was manually set to In-Flight * **[Options]** Option to de-spawn AI flights in the air if their start-type was manually set to In-Flight
* **[Campaign Design]** Ability to add separate ground spawns for C-130 and other large aircraft to campaigns.
* **[Config]** Preference setting to use custom Liberation payloads instead of prioritizing Retribution's default * **[Config]** Preference setting to use custom Liberation payloads instead of prioritizing Retribution's default
* **[Config]** Preference setting to configure the server-port on which Retribution's back-end will run * **[Config]** Preference setting to configure the server-port on which Retribution's back-end will run
* **[Options]** Made AI jettisoning empty fuel tanks optional (disabled by default) * **[Options]** Made AI jettisoning empty fuel tanks optional (disabled by default)

View File

@ -9,7 +9,7 @@ from uuid import UUID
from dcs import Mission from dcs import Mission
from dcs.countries import CombinedJointTaskForcesBlue, CombinedJointTaskForcesRed from dcs.countries import CombinedJointTaskForcesBlue, CombinedJointTaskForcesRed
from dcs.country import Country from dcs.country import Country
from dcs.planes import F_15C, A_10A, AJS37 from dcs.planes import F_15C, A_10A, AJS37, C_130
from dcs.ships import HandyWind, LHA_Tarawa, Stennis, USS_Arleigh_Burke_IIa from dcs.ships import HandyWind, LHA_Tarawa, Stennis, USS_Arleigh_Burke_IIa
from dcs.statics import Fortification, Warehouse from dcs.statics import Fortification, Warehouse
from dcs.terrain import Airport from dcs.terrain import Airport
@ -43,6 +43,7 @@ class MizCampaignLoader:
OFF_MAP_UNIT_TYPE = F_15C.id OFF_MAP_UNIT_TYPE = F_15C.id
GROUND_SPAWN_UNIT_TYPE = A_10A.id GROUND_SPAWN_UNIT_TYPE = A_10A.id
GROUND_SPAWN_ROADBASE_UNIT_TYPE = AJS37.id GROUND_SPAWN_ROADBASE_UNIT_TYPE = AJS37.id
GROUND_SPAWN_LARGE_UNIT_TYPE = C_130.id
CV_UNIT_TYPE = Stennis.id CV_UNIT_TYPE = Stennis.id
LHA_UNIT_TYPE = LHA_Tarawa.id LHA_UNIT_TYPE = LHA_Tarawa.id
@ -237,6 +238,12 @@ class MizCampaignLoader:
if group.units[0].type == self.GROUND_SPAWN_ROADBASE_UNIT_TYPE: if group.units[0].type == self.GROUND_SPAWN_ROADBASE_UNIT_TYPE:
yield group yield group
@property
def ground_spawns_large(self) -> Iterator[PlaneGroup]:
for group in itertools.chain(self.blue.plane_group, self.red.plane_group):
if group.units[0].type == self.GROUND_SPAWN_LARGE_UNIT_TYPE:
yield group
@property @property
def ground_spawns(self) -> Iterator[PlaneGroup]: def ground_spawns(self) -> Iterator[PlaneGroup]:
for group in itertools.chain(self.blue.plane_group, self.red.plane_group): for group in itertools.chain(self.blue.plane_group, self.red.plane_group):
@ -536,6 +543,10 @@ class MizCampaignLoader:
closest, distance = self.objective_info(plane_group) closest, distance = self.objective_info(plane_group)
self._add_ground_spawn(closest.ground_spawns_roadbase, plane_group) self._add_ground_spawn(closest.ground_spawns_roadbase, plane_group)
for plane_group in self.ground_spawns_large:
closest, distance = self.objective_info(plane_group)
self._add_ground_spawn(closest.ground_spawns_large, plane_group)
for plane_group in self.ground_spawns: for plane_group in self.ground_spawns:
closest, distance = self.objective_info(plane_group) closest, distance = self.objective_info(plane_group)
self._add_ground_spawn(closest.ground_spawns, plane_group) self._add_ground_spawn(closest.ground_spawns, plane_group)

View File

@ -56,6 +56,7 @@ class AircraftGenerator:
mission_data: MissionData, mission_data: MissionData,
helipads: dict[ControlPoint, list[StaticGroup]], helipads: dict[ControlPoint, list[StaticGroup]],
ground_spawns_roadbase: dict[ControlPoint, list[Tuple[StaticGroup, Point]]], ground_spawns_roadbase: dict[ControlPoint, list[Tuple[StaticGroup, Point]]],
ground_spawns_large: dict[ControlPoint, list[Tuple[StaticGroup, Point]]],
ground_spawns: dict[ControlPoint, list[Tuple[StaticGroup, Point]]], ground_spawns: dict[ControlPoint, list[Tuple[StaticGroup, Point]]],
) -> None: ) -> None:
self.mission = mission self.mission = mission
@ -69,6 +70,7 @@ class AircraftGenerator:
self.mission_data = mission_data self.mission_data = mission_data
self.helipads = helipads self.helipads = helipads
self.ground_spawns_roadbase = ground_spawns_roadbase self.ground_spawns_roadbase = ground_spawns_roadbase
self.ground_spawns_large = ground_spawns_large
self.ground_spawns = ground_spawns self.ground_spawns = ground_spawns
self.ewrj_package_dict: Dict[int, List[FlyingGroup[Any]]] = {} self.ewrj_package_dict: Dict[int, List[FlyingGroup[Any]]] = {}
@ -208,6 +210,7 @@ class AircraftGenerator:
self.mission, self.mission,
self.helipads, self.helipads,
self.ground_spawns_roadbase, self.ground_spawns_roadbase,
self.ground_spawns_large,
self.ground_spawns, self.ground_spawns,
self.mission_data, self.mission_data,
).create_idle_aircraft() ).create_idle_aircraft()
@ -239,6 +242,7 @@ class AircraftGenerator:
self.mission, self.mission,
self.helipads, self.helipads,
self.ground_spawns_roadbase, self.ground_spawns_roadbase,
self.ground_spawns_large,
self.ground_spawns, self.ground_spawns,
self.mission_data, self.mission_data,
).create_flight_group() ).create_flight_group()

View File

@ -66,6 +66,7 @@ class FlightGroupSpawner:
mission: Mission, mission: Mission,
helipads: dict[ControlPoint, list[StaticGroup]], helipads: dict[ControlPoint, list[StaticGroup]],
ground_spawns_roadbase: dict[ControlPoint, list[Tuple[StaticGroup, Point]]], ground_spawns_roadbase: dict[ControlPoint, list[Tuple[StaticGroup, Point]]],
ground_spawns_large: dict[ControlPoint, list[Tuple[StaticGroup, Point]]],
ground_spawns: dict[ControlPoint, list[Tuple[StaticGroup, Point]]], ground_spawns: dict[ControlPoint, list[Tuple[StaticGroup, Point]]],
mission_data: MissionData, mission_data: MissionData,
) -> None: ) -> None:
@ -74,6 +75,7 @@ class FlightGroupSpawner:
self.mission = mission self.mission = mission
self.helipads = helipads self.helipads = helipads
self.ground_spawns_roadbase = ground_spawns_roadbase self.ground_spawns_roadbase = ground_spawns_roadbase
self.ground_spawns_large = ground_spawns_large
self.ground_spawns = ground_spawns self.ground_spawns = ground_spawns
self.mission_data = mission_data self.mission_data = mission_data
@ -177,6 +179,8 @@ class FlightGroupSpawner:
raise RuntimeError( raise RuntimeError(
f"Cannot spawn fixed-wing aircraft at {cp} because of insufficient ground spawn slots." f"Cannot spawn fixed-wing aircraft at {cp} because of insufficient ground spawn slots."
) )
is_large = self.flight.unit_type.dcs_unit_type.width > 40
pilot_count = len(self.flight.roster.members) pilot_count = len(self.flight.roster.members)
if ( if (
not is_heli not is_heli
@ -192,10 +196,18 @@ class FlightGroupSpawner:
pad_group = self._generate_at_cp_helipad(name, cp) pad_group = self._generate_at_cp_helipad(name, cp)
if pad_group is not None: if pad_group is not None:
return pad_group return pad_group
if cp.has_ground_spawns and self.flight.client_count > 0 and is_large:
pad_group = self._generate_at_cp_ground_spawn(name, cp, is_large)
if pad_group is not None:
return pad_group
if cp.has_ground_spawns and (self.flight.client_count > 0 or is_heli): if cp.has_ground_spawns and (self.flight.client_count > 0 or is_heli):
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
else:
pad_group = self._generate_at_cp_ground_spawn(name, cp, True)
if pad_group is not None:
return pad_group
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
@ -203,6 +215,35 @@ class FlightGroupSpawner:
pad_group = self._generate_at_cp_helipad(name, cp) pad_group = self._generate_at_cp_helipad(name, cp)
if pad_group is not None: if pad_group is not None:
return pad_group return pad_group
# Large planes (wingspan more than 40 meters, looking at you, C-130)
# First try spawning on large ground spawns
# Then try the regular airfield ramp spawns
is_large = self.flight.unit_type.dcs_unit_type.width > 40
if (
cp.has_ground_spawns
and is_large
and len(self.ground_spawns_large[cp]) >= self.flight.count
and (self.flight.client_count > 0)
):
pad_group = self._generate_at_cp_ground_spawn(name, cp, is_large)
if pad_group is not None:
return pad_group
# Below 40 meter wingspan aircraft
# First try spawning on regular or roadbase ground spawns
# Then try the regular airfield ramp spawns
# Then, if both of the above fail, use the large ground spawns
if (
cp.has_ground_spawns
and len(self.ground_spawns[cp])
+ len(self.ground_spawns_roadbase[cp])
+ len(self.ground_spawns_large[cp])
>= self.flight.count
and (self.flight.client_count > 0 or is_heli)
):
pad_group = self._generate_at_cp_ground_spawn(name, cp)
if pad_group is not None:
return pad_group
if ( if (
cp.has_ground_spawns cp.has_ground_spawns
and len(self.ground_spawns[cp]) and len(self.ground_spawns[cp])
@ -213,33 +254,45 @@ class 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
try:
# TODO: get rid of the nevatim hack once fixed in DCS... # TODO: get rid of the nevatim hack once fixed in DCS...
if self._check_nevatim_hack(cp): if self._check_nevatim_hack(cp):
slots = [ slots = [
slot slot
for slot in cp.dcs_airport.free_parking_slots( for slot in cp.dcs_airport.free_parking_slots(
self.flight.squadron.aircraft.dcs_unit_type self.flight.squadron.aircraft.dcs_unit_type
) )
if slot.slot_name in [str(n) for n in range(55, 66)] if slot.slot_name in [str(n) for n in range(55, 66)]
]
return self._generate_at_airfield(name, cp, slots)
elif self._check_ramon_airbase_hack(cp):
# TODO: get rid of the ramon airbase hack once fixed in DCS...
slots = [
slot
for slot in cp.dcs_airport.free_parking_slots(
self.flight.squadron.aircraft.dcs_unit_type
)
if slot.slot_name
not in [
str(n)
for n in [1, 2, 3, 4, 5, 6, 13, 14, 15, 16, 17, 18, 61]
] ]
] return self._generate_at_airfield(name, cp, slots)
return self._generate_at_airfield(name, cp, slots) elif self._check_ramon_airbase_hack(cp):
else: # TODO: get rid of the ramon airbase hack once fixed in DCS...
return self._generate_at_airfield(name, cp) slots = [
slot
for slot in cp.dcs_airport.free_parking_slots(
self.flight.squadron.aircraft.dcs_unit_type
)
if slot.slot_name
not in [
str(n)
for n in [1, 2, 3, 4, 5, 6, 13, 14, 15, 16, 17, 18, 61]
]
]
return self._generate_at_airfield(name, cp, slots)
else:
return self._generate_at_airfield(name, cp)
except NoParkingSlotError:
if (
cp.has_ground_spawns
and len(self.ground_spawns_large[cp]) >= self.flight.count
and (self.flight.client_count > 0 or is_heli)
):
pad_group = self._generate_at_cp_ground_spawn(name, cp, True)
if pad_group is not None:
return pad_group
else:
raise NoParkingSlotError
return self._generate_at_airfield(name, cp)
else: else:
raise NotImplementedError( raise NotImplementedError(
f"Aircraft spawn behavior not implemented for {cp} ({cp.__class__})" f"Aircraft spawn behavior not implemented for {cp} ({cp.__class__})"
@ -439,22 +492,26 @@ class FlightGroupSpawner:
return group return group
def _generate_at_cp_ground_spawn( def _generate_at_cp_ground_spawn(
self, name: str, cp: ControlPoint self, name: str, cp: ControlPoint, is_large: bool = False
) -> Optional[FlyingGroup[Any]]: ) -> Optional[FlyingGroup[Any]]:
is_airbase = False is_airbase = False
is_roadbase = False is_roadbase = False
try: try:
if len(self.ground_spawns_roadbase[cp]) > 0: if is_large:
ground_spawn = self.ground_spawns_roadbase[cp].pop() if len(self.ground_spawns_large[cp]) > 0:
is_roadbase = True ground_spawn = self.ground_spawns_large[cp].pop()
is_airbase = True
else: else:
ground_spawn = self.ground_spawns[cp].pop() if len(self.ground_spawns_roadbase[cp]) > 0:
is_airbase = True ground_spawn = self.ground_spawns_roadbase[cp].pop()
is_roadbase = True
if len(self.ground_spawns[cp]) > 0:
ground_spawn = self.ground_spawns[cp].pop()
is_airbase = True
except IndexError as ex: except IndexError as ex:
logging.warning("Not enough STOL slots available at " + str(ex)) logging.warning("Not enough ground spawn slots available at " + str(ex))
return None return None
# raise RuntimeError(f"Not enough STOL slots available at {cp}") from ex
group = self._generate_at_group(name, ground_spawn[0]) group = self._generate_at_group(name, ground_spawn[0])
@ -490,16 +547,22 @@ class FlightGroupSpawner:
for i in range(self.flight.count - 1): for i in range(self.flight.count - 1):
try: try:
terrain = cp.coalition.game.theater.terrain terrain = cp.coalition.game.theater.terrain
if len(self.ground_spawns_roadbase[cp]) > 0: if is_large:
ground_spawn = self.ground_spawns_roadbase[cp].pop() if len(self.ground_spawns_large[cp]) > 0:
ground_spawn = self.ground_spawns_large[cp].pop()
else: else:
ground_spawn = self.ground_spawns[cp].pop() if len(self.ground_spawns_roadbase[cp]) > 0:
ground_spawn = self.ground_spawns_roadbase[cp].pop()
else:
ground_spawn = self.ground_spawns[cp].pop()
group.units[1 + i].position = Point( group.units[1 + i].position = Point(
ground_spawn[0].x, ground_spawn[0].y, terrain=terrain ground_spawn[0].x, ground_spawn[0].y, terrain=terrain
) )
group.units[1 + i].heading = ground_spawn[0].units[0].heading group.units[1 + i].heading = ground_spawn[0].units[0].heading
except IndexError as ex: except IndexError as ex:
raise RuntimeError(f"Not enough STOL slots available at {cp}") from ex raise RuntimeError(
f"Not enough ground spawn slots available at {cp}"
) from ex
return group return group
def dcs_start_type(self) -> DcsStartType: def dcs_start_type(self) -> DcsStartType:

View File

@ -249,6 +249,7 @@ class MissionGenerator:
mission_data=self.mission_data, mission_data=self.mission_data,
helipads=tgo_generator.helipads, helipads=tgo_generator.helipads,
ground_spawns_roadbase=tgo_generator.ground_spawns_roadbase, ground_spawns_roadbase=tgo_generator.ground_spawns_roadbase,
ground_spawns_large=tgo_generator.ground_spawns_large,
ground_spawns=tgo_generator.ground_spawns, ground_spawns=tgo_generator.ground_spawns,
) )

View File

@ -1007,6 +1007,123 @@ class GroundSpawnRoadbaseGenerator:
self.ground_spawns_roadbase = [] self.ground_spawns_roadbase = []
class GroundSpawnLargeGenerator:
"""
Generates STOL aircraft starting positions for given control point
"""
def __init__(
self,
mission: Mission,
cp: ControlPoint,
game: Game,
radio_registry: RadioRegistry,
tacan_registry: TacanRegistry,
):
self.m = mission
self.cp = cp
self.game = game
self.radio_registry = radio_registry
self.tacan_registry = tacan_registry
self.ground_spawns_large: list[Tuple[StaticGroup, Point]] = []
def create_ground_spawn_large(
self, i: int, vtol_pad: Tuple[PointWithHeading, Point]
) -> None:
# Note: FARPs are generated as neutral object in order not to interfere with
# capture triggers
neutral_country = self.m.country(self.game.neutral_country.name)
country = self.m.country(
self.game.coalition_for(self.cp.captured).faction.country.name
)
terrain = self.cp.coalition.game.theater.terrain
name = f"{self.cp.name} large ground spawn {i}"
logging.info("Generating Large Ground Spawn static : " + name)
pad = InvisibleFARP(unit_id=self.m.next_unit_id(), name=name, terrain=terrain)
pad.position = Point(vtol_pad[0].x, vtol_pad[0].y, terrain=terrain)
pad.heading = vtol_pad[0].heading.degrees
sg = unitgroup.StaticGroup(self.m.next_group_id(), name)
sg.add_unit(pad)
sp = StaticPoint(pad.position)
sg.add_point(sp)
neutral_country.add_static_group(sg)
self.ground_spawns_large.append((sg, vtol_pad[1]))
# tanker_type: Type[VehicleType]
# ammo_truck_type: Type[VehicleType]
tanker_type, ammo_truck_type, power_truck_type = farp_truck_types_for_country(
country.id
)
# Generate a FARP Ammo and Fuel stack for each pad
if self.game.settings.ground_start_trucks:
self.m.vehicle_group(
country=country,
name=(name + "_fuel"),
_type=tanker_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 175, 45
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
self.m.vehicle_group(
country=country,
name=(name + "_ammo"),
_type=ammo_truck_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 185, 45
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
else:
self.m.static_group(
country=country,
name=(name + "_fuel"),
_type=Fortification.FARP_Fuel_Depot,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 180, 55
),
heading=pad.heading,
)
self.m.static_group(
country=country,
name=(name + "_ammo"),
_type=Fortification.FARP_Ammo_Dump_Coating,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 180, 45
),
heading=pad.heading + 270,
)
if self.game.settings.ground_start_ground_power_trucks:
self.m.vehicle_group(
country=country,
name=(name + "_power"),
_type=power_truck_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 185, 45
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
def generate(self) -> None:
try:
for i, vtol_pad in enumerate(self.cp.ground_spawns_large):
self.create_ground_spawn_large(i, vtol_pad)
except AttributeError:
self.ground_spawns_large = []
class GroundSpawnGenerator: class GroundSpawnGenerator:
""" """
Generates STOL aircraft starting positions for given control point Generates STOL aircraft starting positions for given control point
@ -1153,6 +1270,9 @@ class TgoGenerator:
self.ground_spawns_roadbase: dict[ self.ground_spawns_roadbase: dict[
ControlPoint, list[Tuple[StaticGroup, Point]] ControlPoint, list[Tuple[StaticGroup, Point]]
] = defaultdict(list) ] = defaultdict(list)
self.ground_spawns_large: dict[
ControlPoint, list[Tuple[StaticGroup, Point]]
] = defaultdict(list)
self.ground_spawns: dict[ self.ground_spawns: dict[
ControlPoint, list[Tuple[StaticGroup, Point]] ControlPoint, list[Tuple[StaticGroup, Point]]
] = defaultdict(list) ] = defaultdict(list)
@ -1179,7 +1299,15 @@ class TgoGenerator:
] = ground_spawn_roadbase_gen.ground_spawns_roadbase ] = ground_spawn_roadbase_gen.ground_spawns_roadbase
random.shuffle(self.ground_spawns_roadbase[cp]) random.shuffle(self.ground_spawns_roadbase[cp])
# Generate STOL pads # Generate Large Ground Spawn slots
ground_large_spawn_gen = GroundSpawnLargeGenerator(
self.m, cp, self.game, self.radio_registry, self.tacan_registry
)
ground_large_spawn_gen.generate()
self.ground_spawns_large[cp] = ground_large_spawn_gen.ground_spawns_large
random.shuffle(self.ground_spawns_large[cp])
# Generate Ground Spawn slots
ground_spawn_gen = GroundSpawnGenerator( ground_spawn_gen = GroundSpawnGenerator(
self.m, cp, self.game, self.radio_registry, self.tacan_registry self.m, cp, self.game, self.radio_registry, self.tacan_registry
) )

View File

@ -390,6 +390,7 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
self.helipads_quad: List[PointWithHeading] = [] self.helipads_quad: List[PointWithHeading] = []
self.helipads_invisible: List[PointWithHeading] = [] self.helipads_invisible: List[PointWithHeading] = []
self.ground_spawns_roadbase: List[Tuple[PointWithHeading, Point]] = [] self.ground_spawns_roadbase: List[Tuple[PointWithHeading, Point]] = []
self.ground_spawns_large: List[Tuple[PointWithHeading, Point]] = []
self.ground_spawns: List[Tuple[PointWithHeading, Point]] = [] self.ground_spawns: List[Tuple[PointWithHeading, Point]] = []
self._coalition: Optional[Coalition] = None self._coalition: Optional[Coalition] = None
@ -611,7 +612,12 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
""" """
Returns true if cp can operate STOL aircraft Returns true if cp can operate STOL aircraft
""" """
return len(self.ground_spawns_roadbase) + len(self.ground_spawns) > 0 return (
len(self.ground_spawns_roadbase)
+ len(self.ground_spawns_large)
+ len(self.ground_spawns)
> 0
)
def can_recruit_ground_units(self, game: Game) -> bool: def can_recruit_ground_units(self, game: Game) -> bool:
"""Returns True if this control point is capable of recruiting ground units.""" """Returns True if this control point is capable of recruiting ground units."""
@ -1283,6 +1289,7 @@ class Airfield(ControlPoint, CTLD):
if parking_type.include_fixed_wing_stol: if parking_type.include_fixed_wing_stol:
parking_slots += len(self.ground_spawns) parking_slots += len(self.ground_spawns)
parking_slots += len(self.ground_spawns_roadbase) parking_slots += len(self.ground_spawns_roadbase)
parking_slots += len(self.ground_spawns_large)
if parking_type.include_fixed_wing: if parking_type.include_fixed_wing:
parking_slots += len(self.airport.parking_slots) parking_slots += len(self.airport.parking_slots)
return parking_slots return parking_slots
@ -1672,13 +1679,19 @@ class Fob(ControlPoint, RadioFrequencyContainer, CTLD):
+ len(self.helipads_invisible) + len(self.helipads_invisible)
) )
try: if parking_type.include_fixed_wing_stol:
if parking_type.include_fixed_wing_stol: try:
parking_slots += len(self.ground_spawns) parking_slots += len(self.ground_spawns)
except AttributeError:
self.ground_spawns_roadbase = []
try:
parking_slots += len(self.ground_spawns_roadbase) parking_slots += len(self.ground_spawns_roadbase)
except AttributeError: except AttributeError:
self.ground_spawns_roadbase = [] self.ground_spawns_large = []
self.ground_spawns = [] try:
parking_slots += len(self.ground_spawns_large)
except AttributeError:
self.ground_spawns = []
return parking_slots return parking_slots
def can_operate(self, aircraft: AircraftType) -> bool: def can_operate(self, aircraft: AircraftType) -> bool: