diff --git a/game/commander/missionproposals.py b/game/commander/missionproposals.py index 628bc50c..7fcae8f3 100644 --- a/game/commander/missionproposals.py +++ b/game/commander/missionproposals.py @@ -3,6 +3,7 @@ from enum import Enum, auto from typing import Optional from game.ato.flighttype import FlightType +from game.dcs.aircrafttype import AircraftType from game.theater import MissionTarget @@ -33,6 +34,8 @@ class ProposedFlight: #: field is None. escort_type: Optional[EscortType] = field(default=None) + preferred_type: Optional[AircraftType] = field(default=None) + def __str__(self) -> str: return f"{self.task} {self.num_aircraft} ship" diff --git a/game/commander/packagebuilder.py b/game/commander/packagebuilder.py index 6ea28ac7..9936c8e4 100644 --- a/game/commander/packagebuilder.py +++ b/game/commander/packagebuilder.py @@ -49,7 +49,12 @@ class PackageBuilder: pf = self.package.primary_flight heli = pf.is_helo if pf else False squadron = self.air_wing.best_squadron_for( - self.package.target, plan.task, plan.num_aircraft, heli, this_turn=True + self.package.target, + plan.task, + plan.num_aircraft, + heli, + this_turn=True, + preferred_type=plan.preferred_type, ) if squadron is None: return False diff --git a/game/squadrons/airwing.py b/game/squadrons/airwing.py index 4d50b78e..a6afbc79 100644 --- a/game/squadrons/airwing.py +++ b/game/squadrons/airwing.py @@ -51,6 +51,7 @@ class AirWing: size: int, heli: bool, this_turn: bool, + preferred_type: Optional[AircraftType] = None, ) -> list[Squadron]: airfield_cache = ObjectiveDistanceCache.get_closest_airfields(location) best_aircraft = AircraftType.priority_list_for_task(task) @@ -59,7 +60,13 @@ class AirWing: if control_point.captured != self.player: continue capable_at_base = [] - for squadron in control_point.squadrons: + squadrons = [ + s + for s in control_point.squadrons + if not preferred_type + or s.aircraft.variant_id == preferred_type.variant_id + ] + for squadron in squadrons: if squadron.can_auto_assign_mission( location, task, size, heli, this_turn ): @@ -92,8 +99,11 @@ class AirWing: size: int, heli: bool, this_turn: bool, + preferred_type: Optional[AircraftType] = None, ) -> Optional[Squadron]: - for squadron in self.best_squadrons_for(location, task, size, heli, this_turn): + for squadron in self.best_squadrons_for( + location, task, size, heli, this_turn, preferred_type + ): return squadron return None diff --git a/qt_ui/windows/mission/QAutoCreateDialog.py b/qt_ui/windows/mission/QAutoCreateDialog.py index 350c1d59..28eca71c 100644 --- a/qt_ui/windows/mission/QAutoCreateDialog.py +++ b/qt_ui/windows/mission/QAutoCreateDialog.py @@ -47,76 +47,100 @@ class QAutoCreateDialog(QDialog): hbox = QHBoxLayout() self.primary_combobox = QComboBox() + self.primary_combobox.setFixedWidth(100) self.primary_count = _spinbox_template() + self.primary_type = QComboBox() nr_targets = len(self.package.target.strike_targets) count = max(1, min(4, nr_targets // 2) + nr_targets % 1) if nr_targets else 4 self.primary_count.setValue(count) hbox.addWidget(self.primary_combobox) hbox.addWidget(self.primary_count) + hbox.addWidget(self.primary_type) self.layout.addLayout(hbox) self.checkboxes = {} hbox = QHBoxLayout() - self.tarcap = QCheckBox() - self.tarcap.setText("TARCAP") + self.tarcap = self._create_checkbox("TARCAP") self.tarcap_count = _spinbox_template() hbox.addWidget(self.tarcap) hbox.addWidget(self.tarcap_count) + self.tarcap_type = self._create_type_selector(FlightType.TARCAP) + hbox.addWidget(self.tarcap_type) self.layout.addLayout(hbox) - self.checkboxes[self.tarcap] = (FlightType.TARCAP, self.tarcap_count) + self.checkboxes[self.tarcap] = ( + FlightType.TARCAP, + self.tarcap_count, + self.tarcap_type, + ) hbox = QHBoxLayout() - self.escort = QCheckBox() - self.escort.setText("Escort") + self.escort = self._create_checkbox("Escort") self.escort_count = _spinbox_template() hbox.addWidget(self.escort) hbox.addWidget(self.escort_count) + self.escort_type = self._create_type_selector(FlightType.ESCORT) + hbox.addWidget(self.escort_type) self.layout.addLayout(hbox) - self.checkboxes[self.escort] = (FlightType.ESCORT, self.escort_count) + self.checkboxes[self.escort] = ( + FlightType.ESCORT, + self.escort_count, + self.escort_type, + ) hbox = QHBoxLayout() - self.sead_escort = QCheckBox() - self.sead_escort.setText("SEAD Escort") + self.sead_escort = self._create_checkbox("SEAD Escort") self.sead_escort_count = _spinbox_template() hbox.addWidget(self.sead_escort) hbox.addWidget(self.sead_escort_count) + self.sead_escort_type = self._create_type_selector(FlightType.SEAD_ESCORT) + hbox.addWidget(self.sead_escort_type) + self.layout.addLayout(hbox) self.checkboxes[self.sead_escort] = ( FlightType.SEAD_ESCORT, self.sead_escort_count, + self.sead_escort_type, ) hbox = QHBoxLayout() - self.sead = QCheckBox() - self.sead.setText("SEAD") + self.sead = self._create_checkbox("SEAD") self.sead_count = _spinbox_template() hbox.addWidget(self.sead) hbox.addWidget(self.sead_count) + self.sead_type = self._create_type_selector(FlightType.SEAD) + hbox.addWidget(self.sead_type) self.layout.addLayout(hbox) - self.checkboxes[self.sead] = (FlightType.SEAD, self.sead_count) + self.checkboxes[self.sead] = (FlightType.SEAD, self.sead_count, self.sead_type) hbox = QHBoxLayout() - self.sead_sweep = QCheckBox() - self.sead_sweep.setText("SEAD Sweep") + self.sead_sweep = self._create_checkbox("SEAD Sweep") self.sead_sweep_count = _spinbox_template() hbox.addWidget(self.sead_sweep) hbox.addWidget(self.sead_sweep_count) + self.sead_sweep_type = self._create_type_selector(FlightType.SEAD_SWEEP) + hbox.addWidget(self.sead_sweep_type) self.layout.addLayout(hbox) self.checkboxes[self.sead_sweep] = ( FlightType.SEAD_SWEEP, self.sead_sweep_count, + self.sead_sweep_type, ) hbox = QHBoxLayout() - self.refueling = QCheckBox() - self.refueling.setText("Refueling") + self.refueling = self._create_checkbox("Refueling") self.refueling_count = _spinbox_template() self.refueling_count.setValue(1) hbox.addWidget(self.refueling) hbox.addWidget(self.refueling_count) + self.refueling_type = self._create_type_selector(FlightType.REFUELING) + hbox.addWidget(self.refueling_type, 1) self.layout.addLayout(hbox) - self.checkboxes[self.refueling] = (FlightType.REFUELING, self.refueling_count) + self.checkboxes[self.refueling] = ( + FlightType.REFUELING, + self.refueling_count, + self.refueling_type, + ) self.create_button = QPushButton("Create") self.create_button.setProperty("style", "start-button") @@ -139,6 +163,28 @@ class QAutoCreateDialog(QDialog): if mt in primary_tasks: self.primary_combobox.addItem(mt.value, mt) self.primary_combobox.setCurrentIndex(0) + self._load_aircraft_types() + + @staticmethod + def _create_checkbox(label: str) -> QCheckBox: + cb = QCheckBox(label) + cb.setFixedWidth(100) + return cb + + def _create_type_selector(self, flight_type: FlightType) -> QComboBox: + airwing = self.game.blue.air_wing + cb = QComboBox() + for ac in airwing.best_available_aircrafts_for(flight_type): + cb.addItem(ac.variant_id, ac) + return cb + + def _load_aircraft_types(self): + self.primary_type.clear() + for ac in self.game.blue.air_wing.best_available_aircrafts_for( + self.primary_combobox.currentData() + ): + self.primary_type.addItem(ac.variant_id, ac) + self.primary_type.setCurrentIndex(0) def on_primary_task_changed(self) -> None: disable = self.primary_combobox.currentData() == FlightType.CAS @@ -147,15 +193,26 @@ class QAutoCreateDialog(QDialog): if disable: cb.setChecked(False) cb.setDisabled(disable) + self._load_aircraft_types() def on_create_clicked(self) -> None: pf: List[ProposedFlight] = [] count = self.primary_count.value() - pf.append(ProposedFlight(self.primary_combobox.currentData(), count)) + pf.append( + ProposedFlight( + self.primary_combobox.currentData(), + count, + preferred_type=self.primary_type.currentData(), + ) + ) for cb in self.checkboxes: if cb.isChecked(): - type, spinner = self.checkboxes[cb] - pf.append(ProposedFlight(type, spinner.value())) + type, spinner, ac_box = self.checkboxes[cb] + pf.append( + ProposedFlight( + type, spinner.value(), preferred_type=ac_box.currentData() + ) + ) with MultiEventTracer() as tracer: with tracer.trace(f"Auto-plan package"): pm = ProposedMission(self.package.target, pf, asap=True) diff --git a/qt_ui/windows/mission/QPackageDialog.py b/qt_ui/windows/mission/QPackageDialog.py index 22350929..75412f92 100644 --- a/qt_ui/windows/mission/QPackageDialog.py +++ b/qt_ui/windows/mission/QPackageDialog.py @@ -237,11 +237,12 @@ class QPackageDialog(QDialog): auto_create_dialog = QAutoCreateDialog( self.game, self.package_model, parent=self.window() ) - auto_create_dialog.exec_() - for f in self.package_model.package.flights: - EventStream.put_nowait(GameUpdateEvents().new_flight(f)) - self.package_model.update_tot() - self.package_changed.emit() + if auto_create_dialog.exec_() == QDialog.DialogCode.Accepted: + for f in self.package_model.package.flights: + EventStream.put_nowait(GameUpdateEvents().new_flight(f)) + self.package_model.update_tot() + self.package_changed.emit() + self.auto_create_button.setDisabled(True) def on_change_name(self) -> None: self.package_model.package.custom_name = self.package_name_text.text()