diff --git a/changelog.md b/changelog.md index fb374c64..11be3812 100644 --- a/changelog.md +++ b/changelog.md @@ -41,6 +41,7 @@ * **[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 * **[Options]** Made AI jettisoning empty fuel tanks optional (disabled by default) +* **[Options]** Add option (so it can be disabled when fixed in DCS) to force air-starts (except for the slots that work) at Nevatim due to https://forum.dcs.world/topic/335545-29-nevatim-ramp-starts-still-bugged/. ## Fixes * **[Mission Generation]** Anti-ship strikes should use "group attack" in their attack-task diff --git a/game/missiongenerator/aircraft/flightgroupspawner.py b/game/missiongenerator/aircraft/flightgroupspawner.py index 6514f253..4540c14b 100644 --- a/game/missiongenerator/aircraft/flightgroupspawner.py +++ b/game/missiongenerator/aircraft/flightgroupspawner.py @@ -1,6 +1,6 @@ import logging import random -from typing import Any, Union, Tuple, Optional +from typing import Any, Union, Tuple, Optional, List from dcs import Mission from dcs.country import Country @@ -18,7 +18,8 @@ from dcs.planes import ( ) from dcs.point import PointAction from dcs.ships import KUZNECOW -from dcs.terrain import NoParkingSlotError +from dcs.terrain import NoParkingSlotError, Sinai, ParkingSlot +from dcs.terrain.sinai.airports import Nevatim from dcs.unitgroup import ( FlyingGroup, ShipGroup, @@ -109,24 +110,31 @@ class FlightGroupSpawner: def create_idle_aircraft(self) -> Optional[FlyingGroup[Any]]: group = None - if ( - self.flight.is_helo - or self.flight.is_lha - and isinstance(self.flight.squadron.location, Fob) - ): + cp = self.flight.squadron.location + if self.flight.is_helo or self.flight.is_lha and isinstance(cp, Fob): group = self._generate_at_cp_helipad( name=namegen.next_aircraft_name(self.country, self.flight), cp=self.flight.squadron.location, ) - elif isinstance(self.flight.squadron.location, Fob): + elif isinstance(cp, Fob): group = self._generate_at_cp_ground_spawn( name=namegen.next_aircraft_name(self.country, self.flight), cp=self.flight.squadron.location, ) - elif isinstance(self.flight.squadron.location, Airfield): + elif isinstance(cp, Airfield): + # TODO: remove hack when fixed in DCS + slots = None + if self._check_nevatim_hack(cp): + ac_type = self.flight.unit_type.dcs_unit_type + slots = [ + slot + for slot in cp.dcs_airport.free_parking_slots(ac_type) + if slot.slot_name in [str(n) for n in range(55, 66)] + ] group = self._generate_at_airfield( name=namegen.next_aircraft_name(self.country, self.flight), - airfield=self.flight.squadron.location, + airfield=cp, + parking_slots=slots, ) if group: group.uncontrolled = True @@ -195,7 +203,19 @@ class FlightGroupSpawner: pad_group = self._generate_at_cp_ground_spawn(name, cp) if pad_group is not None: return pad_group - return self._generate_at_airfield(name, cp) + + # TODO: get rid of the nevatim hack once fixed in DCS... + if self._check_nevatim_hack(cp): + slots = [ + slot + for slot in cp.dcs_airport.free_parking_slots( + self.flight.squadron.aircraft.dcs_unit_type + ) + if slot.slot_name in [str(n) for n in range(55, 66)] + ] + return self._generate_at_airfield(name, cp, slots) + else: + return self._generate_at_airfield(name, cp) else: raise NotImplementedError( f"Aircraft spawn behavior not implemented for {cp} ({cp.__class__})" @@ -209,6 +229,13 @@ class FlightGroupSpawner: group = self._generate_over_departure(name, cp) return group + def _check_nevatim_hack(self, cp: ControlPoint) -> bool: + # TODO: get rid of the nevatim hack once fixed in DCS... + nevatim_hack = self.flight.coalition.game.settings.nevatim_parking_fix + nevatim_hack &= isinstance(self.mission.terrain, Sinai) + nevatim_hack &= isinstance(cp.dcs_airport, Nevatim) + return nevatim_hack + def generate_mid_mission(self) -> FlyingGroup[Any]: assert isinstance(self.flight.state, InFlight) name = namegen.next_aircraft_name(self.country, self.flight) @@ -250,7 +277,12 @@ class FlightGroupSpawner: group.points[0].alt_type = alt_type return group - def _generate_at_airfield(self, name: str, airfield: Airfield) -> FlyingGroup[Any]: + def _generate_at_airfield( + self, + name: str, + airfield: Airfield, + parking_slots: Optional[List[ParkingSlot]] = None, + ) -> FlyingGroup[Any]: # TODO: Delayed runway starts should be converted to air starts for multiplayer. # Runway starts do not work with late activated aircraft in multiplayer. Instead # of spawning on the runway the aircraft will spawn on the taxiway, potentially @@ -267,7 +299,7 @@ class FlightGroupSpawner: maintask=None, start_type=self._start_type_at_airfield(airfield), group_size=self.flight.count, - parking_slots=None, + parking_slots=parking_slots, callsign_name=self.flight.callsign.name if self.flight.callsign else None, callsign_nr=self.flight.callsign.nr if self.flight.callsign else None, ) diff --git a/game/settings/settings.py b/game/settings/settings.py index 4a9ac57e..5089ce2d 100644 --- a/game/settings/settings.py +++ b/game/settings/settings.py @@ -703,6 +703,16 @@ class Settings: "will not be included in automatically planned OCA packages." ), ) + nevatim_parking_fix: bool = boolean_option( + "Force air-starts for all aircraft at Nevatim", + page=MISSION_GENERATOR_PAGE, + section=GAMEPLAY_SECTION, + default=True, # TODO: set to False or remove this when DCS is fixed + detail=( + "Air-starts forced for all aircraft at Nevatim except parking slots " + "55 till 65, since those are the only ones that work." + ), + ) # Mission specific desired_player_mission_duration: timedelta = minutes_option( "Desired mission duration", diff --git a/requirements.txt b/requirements.txt index 8fe27802..c6b976c8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -32,7 +32,7 @@ pluggy==1.4.0 pre-commit==3.6.0 pydantic==2.6.0 pydantic-settings==2.1.0 --e git+https://github.com/dcs-retribution/pydcs@353f5b177dd406122a83e8572fd6ca54adf84389#egg=pydcs +-e git+https://github.com/dcs-retribution/pydcs@9b2f17865859120779385fc546f1b7be3f8ef9c7#egg=pydcs pyinstaller==5.13.2 pyinstaller-hooks-contrib==2024.0 pyparsing==3.1.1