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