Default start type for player flights (#303)

* Implemented a new option in settings: Default start type for Player flights.

* Updated changelog.

* Removed unnecessary country parameter.

* Restore missing parameter

* on_pilot_changed should emit pilots_changed in its finally block, otherwise the start-type isn't updated if you have a single client pilot which you switch to a non-client pilot.

Also implemented other changes suggested by @Raffson, such as a more streamlined start_type QComboBox handling and moving the pilots_changed Signal to FlightRosterEditor.

* Decouple Signal from QFlighStartType

---------

Co-authored-by: Raffson <Raffson@users.noreply.github.com>
This commit is contained in:
MetalStormGhost 2024-05-09 13:19:30 +03:00 committed by GitHub
parent 4c455426e9
commit a27663e4b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 108 additions and 8 deletions

View File

@ -11,6 +11,7 @@
* **[Campaign Design]** Ability to configure specific carrier names & types in campaign's yaml file * **[Campaign Design]** Ability to configure specific carrier names & types in campaign's yaml file
* **[Mission Generation]** Ability to inject custom kneeboards * **[Mission Generation]** Ability to inject custom kneeboards
* **[Options]** Extend option (so it can be disabled when fixed in DCS) to force air-starts (except for the slots that work) at Ramon Airbase, similar to the Nevatim fix in Retribution 1.3.0 * **[Options]** Extend option (so it can be disabled when fixed in DCS) to force air-starts (except for the slots that work) at Ramon Airbase, similar to the Nevatim fix in Retribution 1.3.0
* **[Options]** New option in Settings: Default start type for Player flights.
## Fixes ## Fixes
* **[UI/UX]** A-10A flights can be edited again. * **[UI/UX]** A-10A flights can be edited again.

View File

@ -22,6 +22,10 @@ class FlightRoster(IFlightRoster):
def iter_pilots(self) -> Iterator[Pilot | None]: def iter_pilots(self) -> Iterator[Pilot | None]:
yield from self.pilots yield from self.pilots
@property
def player_count(self) -> int:
return len([p for p in self.pilots if p is not None and p.player])
def pilot_at(self, idx: int) -> Pilot | None: def pilot_at(self, idx: int) -> Pilot | None:
return self.pilots[idx] return self.pilots[idx]

View File

@ -26,6 +26,11 @@ class IFlightRoster(ABC):
def max_size(self) -> int: def max_size(self) -> int:
... ...
@property
@abstractmethod
def player_count(self) -> int:
...
@abstractmethod @abstractmethod
def resize(self, new_size: int) -> None: def resize(self, new_size: int) -> None:
... ...

View File

@ -76,6 +76,12 @@ class PackageBuilder:
member.assign_tgp_laser_code( member.assign_tgp_laser_code(
self.laser_code_registry.alloc_laser_code() self.laser_code_registry.alloc_laser_code()
) )
# If this is a client flight, set the start_type again to match the configured default
# https://github.com/dcs-liberation/dcs_liberation/issues/1567
if flight.roster is not None and flight.roster.player_count > 0:
flight.start_type = (
squadron.coalition.game.settings.default_start_type_client
)
self.package.add_flight(flight) self.package.add_flight(flight)
return True return True

View File

@ -739,6 +739,14 @@ class Settings:
"will not be included in automatically planned OCA packages." "will not be included in automatically planned OCA packages."
), ),
) )
default_start_type_client: StartType = choices_option(
"Default start type for Player flights",
page=MISSION_GENERATOR_PAGE,
section=GAMEPLAY_SECTION,
choices={v.value: v for v in StartType},
default=StartType.COLD,
detail=("Default start type for flights containing Player/Client slots."),
)
nevatim_parking_fix: bool = boolean_option( nevatim_parking_fix: bool = boolean_option(
"Force air-starts for aircraft at Nevatim and Ramon Airbase inoperable parking slots", "Force air-starts for aircraft at Nevatim and Ramon Airbase inoperable parking slots",
page=MISSION_GENERATOR_PAGE, page=MISSION_GENERATOR_PAGE,

View File

@ -89,8 +89,7 @@ class QFlightCreator(QDialog):
roster = None roster = None
else: else:
roster = FlightRoster( roster = FlightRoster(
squadron, squadron, initial_size=self.flight_size_spinner.value()
initial_size=self.flight_size_spinner.value(),
) )
self.roster_editor = FlightRosterEditor(squadron, roster) self.roster_editor = FlightRosterEditor(squadron, roster)
self.flight_size_spinner.valueChanged.connect(self.roster_editor.resize) self.flight_size_spinner.valueChanged.connect(self.roster_editor.resize)
@ -100,10 +99,12 @@ class QFlightCreator(QDialog):
roster_layout.addWidget(QLabel("Assigned pilots:")) roster_layout.addWidget(QLabel("Assigned pilots:"))
roster_layout.addLayout(self.roster_editor) roster_layout.addLayout(self.roster_editor)
self.roster_editor.pilots_changed.connect(self.on_pilot_selected)
# When an off-map spawn overrides the start type to in-flight, we save # When an off-map spawn overrides the start type to in-flight, we save
# the selected type into this value. If a non-off-map spawn is selected # the selected type into this value. If a non-off-map spawn is selected
# we restore the previous choice. # we restore the previous choice.
self.restore_start_type = self.game.settings.default_start_type self.restore_start_type: Optional[str] = None
self.start_type = QComboBox() self.start_type = QComboBox()
for start_type in StartType: for start_type in StartType:
self.start_type.addItem(start_type.value, start_type) self.start_type.addItem(start_type.value, start_type)
@ -140,6 +141,8 @@ class QFlightCreator(QDialog):
self.setLayout(layout) self.setLayout(layout)
self.roster_editor.pilots_changed.emit()
def reject(self) -> None: def reject(self) -> None:
super().reject() super().reject()
# Clear the roster to return pilots to the pool. # Clear the roster to return pilots to the pool.
@ -213,6 +216,8 @@ class QFlightCreator(QDialog):
) )
self.divert.change_aircraft(new_aircraft) self.divert.change_aircraft(new_aircraft)
self.roster_editor.pilots_changed.emit()
def on_departure_changed(self, departure: ControlPoint) -> None: def on_departure_changed(self, departure: ControlPoint) -> None:
if isinstance(departure, OffMapSpawn): if isinstance(departure, OffMapSpawn):
previous_type = self.start_type.currentData() previous_type = self.start_type.currentData()
@ -245,6 +250,8 @@ class QFlightCreator(QDialog):
) )
self.on_departure_changed(squadron.location) self.on_departure_changed(squadron.location)
self.roster_editor.pilots_changed.emit()
def update_max_size(self, available: int) -> None: def update_max_size(self, available: int) -> None:
aircraft = self.aircraft_selector.currentData() aircraft = self.aircraft_selector.currentData()
if aircraft is None: if aircraft is None:
@ -255,3 +262,23 @@ class QFlightCreator(QDialog):
default_size = max(2, available, aircraft.max_group_size) default_size = max(2, available, aircraft.max_group_size)
self.flight_size_spinner.setValue(default_size) self.flight_size_spinner.setValue(default_size)
try:
self.roster_editor.pilots_changed.emit()
except AttributeError:
return
def on_pilot_selected(self):
# Pilot selection detected. If this is a player flight, set start_type
# as configured for players in the settings.
# Otherwise, set the start_type as configured for AI.
# https://github.com/dcs-liberation/dcs_liberation/issues/1567
roster = self.roster_editor.roster
if roster is not None and roster.player_count > 0:
start_type = self.game.settings.default_start_type_client
else:
start_type = self.game.settings.default_start_type
self.start_type.setCurrentText(start_type.value)

View File

@ -96,11 +96,16 @@ class PilotControls(QHBoxLayout):
player_toggled = Signal() player_toggled = Signal()
def __init__( def __init__(
self, squadron: Optional[Squadron], roster: Optional[FlightRoster], idx: int self,
squadron: Optional[Squadron],
roster: Optional[FlightRoster],
idx: int,
pilots_changed: Signal,
) -> None: ) -> None:
super().__init__() super().__init__()
self.roster = roster self.roster = roster
self.pilot_index = idx self.pilot_index = idx
self.pilots_changed = pilots_changed
self.selector = PilotSelector(squadron, roster, idx) self.selector = PilotSelector(squadron, roster, idx)
self.selector.currentIndexChanged.connect(self.on_pilot_changed) self.selector.currentIndexChanged.connect(self.on_pilot_changed)
@ -131,6 +136,8 @@ class PilotControls(QHBoxLayout):
pilot.player = checked pilot.player = checked
self.player_toggled.emit() self.player_toggled.emit()
self.pilots_changed.emit()
def on_pilot_changed(self, index: int) -> None: def on_pilot_changed(self, index: int) -> None:
pilot = self.selector.itemData(index) pilot = self.selector.itemData(index)
self.player_checkbox.blockSignals(True) self.player_checkbox.blockSignals(True)
@ -143,6 +150,10 @@ class PilotControls(QHBoxLayout):
if self.roster is not None: if self.roster is not None:
self.player_checkbox.setEnabled(self.roster.squadron.aircraft.flyable) self.player_checkbox.setEnabled(self.roster.squadron.aircraft.flyable)
self.player_checkbox.blockSignals(False) self.player_checkbox.blockSignals(False)
# on_pilot_changed should emit pilots_changed in its finally block,
# otherwise the start-type isn't updated if you have a single client
# pilot which you switch to a non-client pilot
self.pilots_changed.emit()
def update_available_pilots(self) -> None: def update_available_pilots(self) -> None:
self.selector.rebuild() self.selector.rebuild()
@ -174,9 +185,12 @@ class PilotControls(QHBoxLayout):
class FlightRosterEditor(QVBoxLayout): class FlightRosterEditor(QVBoxLayout):
MAX_PILOTS = 4 MAX_PILOTS = 4
pilots_changed = Signal()
def __init__( def __init__(
self, squadron: Optional[Squadron], roster: Optional[IFlightRoster] self,
squadron: Optional[Squadron],
roster: Optional[IFlightRoster],
) -> None: ) -> None:
super().__init__() super().__init__()
self.roster = roster self.roster = roster
@ -190,7 +204,7 @@ class FlightRosterEditor(QVBoxLayout):
return callback return callback
controls = PilotControls(squadron, roster, pilot_idx) controls = PilotControls(squadron, roster, pilot_idx, self.pilots_changed)
controls.selector.available_pilots_changed.connect( controls.selector.available_pilots_changed.connect(
make_reset_callback(pilot_idx) make_reset_callback(pilot_idx)
) )
@ -226,7 +240,12 @@ class FlightRosterEditor(QVBoxLayout):
class QFlightSlotEditor(QGroupBox): class QFlightSlotEditor(QGroupBox):
flight_resized = Signal(int) flight_resized = Signal(int)
def __init__(self, package_model: PackageModel, flight: Flight, game: Game): def __init__(
self,
package_model: PackageModel,
flight: Flight,
game: Game,
):
super().__init__("Slots") super().__init__("Slots")
self.package_model = package_model self.package_model = package_model
self.flight = flight self.flight = flight

View File

@ -43,6 +43,25 @@ class QFlightStartType(QGroupBox):
) )
self.setLayout(self.layout) self.setLayout(self.layout)
def on_pilot_selected(self):
# Pilot selection detected. If this is a player flight, set start_type
# as configured for players in the settings.
# Otherwise, set the start_type as configured for AI.
# https://github.com/dcs-liberation/dcs_liberation/issues/1567
if self.flight.roster.player_count > 0:
self.flight.start_type = (
self.flight.coalition.game.settings.default_start_type_client
)
else:
self.flight.start_type = (
self.flight.coalition.game.settings.default_start_type
)
self.start_type.setCurrentText(self.flight.start_type.value)
self.package_model.update_tot()
def _on_start_type_selected(self): def _on_start_type_selected(self):
selected = self.start_type.currentData() selected = self.start_type.currentData()
self.flight.start_type = selected self.flight.start_type = selected

View File

@ -38,6 +38,17 @@ class QGeneralFlightSettingsTab(QFrame):
self.flight_slot_editor.flight_resized.connect(self.flight_size_changed) self.flight_slot_editor.flight_resized.connect(self.flight_size_changed)
for pc in self.flight_slot_editor.roster_editor.pilot_controls: for pc in self.flight_slot_editor.roster_editor.pilot_controls:
pc.player_toggled.connect(self.on_player_toggle) pc.player_toggled.connect(self.on_player_toggle)
pc.player_toggled.connect(
self.flight_slot_editor.roster_editor.pilots_changed
)
start_type = QFlightStartType(
package_model,
flight,
)
roster = self.flight_slot_editor.roster_editor
roster.pilots_changed.connect(start_type.on_pilot_selected)
widgets = [ widgets = [
QFlightTypeTaskInfo(flight), QFlightTypeTaskInfo(flight),
@ -46,7 +57,7 @@ class QGeneralFlightSettingsTab(QFrame):
game.game, package_model, flight, flight_wpt_list game.game, package_model, flight, flight_wpt_list
), ),
self.flight_slot_editor, self.flight_slot_editor,
QFlightStartType(package_model, flight), start_type,
QFlightCustomName(flight), QFlightCustomName(flight),
] ]
layout = QGridLayout() layout = QGridLayout()