mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
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:
parent
d84a0ebc78
commit
07cc85f6fa
@ -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)
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user