mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Disallow air wing generation with overfull bases.
This also changes the window close button of the air wing configuration dialog to cancel rather than revert and continue, because otherwise there's no way for the user to back out of the dialog without fixing all the overfull bases first. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/2910.
This commit is contained in:
parent
d271ff17c2
commit
42a7102948
@ -17,6 +17,8 @@ Saves from 7.0.0 are compatible with 7.1.0
|
||||
* **[Mission Generation]** Added option to prevent scud and V2 sites from firing at the start of the mission.
|
||||
* **[Mission Generation]** Added settings for controlling number of tactical commander, observer, JTAC, and game master slots.
|
||||
* **[Mission Planning]** Per-flight TOT offsets can now be set in the flight details UI. This allows individual flights to be scheduled ahead of or behind the rest of the package.
|
||||
* **[New Game Wizard]** The air wing configuration dialog will check for and reject overfull airbases before continuing when the new squadron rules are used.
|
||||
* **[New Game Wizard]** Closing the air wing configuration dialog will now cancel and return to the new game wizard rather than reverting changes and continuing.
|
||||
* **[UI]** Parking capacity of each squadron's base is now shown during air wing configuration to avoid overcrowding bases when beginning the game with full squadrons.
|
||||
|
||||
## Fixes
|
||||
|
||||
@ -13,7 +13,7 @@ import yaml
|
||||
from PySide6 import QtWidgets
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtWidgets import QApplication, QCheckBox, QSplashScreen
|
||||
from PySide6.QtWidgets import QApplication, QCheckBox, QSplashScreen, QDialog
|
||||
from dcs.payloads import PayloadDirectories
|
||||
|
||||
from game import Game, VERSION, logging_config, persistence
|
||||
@ -357,7 +357,8 @@ def create_game(params: CreateGameParams) -> Game:
|
||||
)
|
||||
game = generator.generate()
|
||||
if params.show_air_wing_config:
|
||||
AirWingConfigurationDialog(game, None).exec_()
|
||||
if AirWingConfigurationDialog(game, None).exec() == QDialog.DialogCode.Rejected:
|
||||
sys.exit("Aborted air wing configuration")
|
||||
game.begin_turn_0(squadrons_start_full=params.use_new_squadron_rules)
|
||||
return game
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import textwrap
|
||||
from collections import defaultdict
|
||||
from typing import Iterable, Iterator, Optional
|
||||
|
||||
@ -630,6 +631,32 @@ class AircraftTypeList(QListView):
|
||||
self.update(self.selectionModel().currentIndex())
|
||||
|
||||
|
||||
def describe_overfull_airbases(
|
||||
overfull: Iterable[tuple[ControlPoint, int, list[Squadron]]]
|
||||
) -> str:
|
||||
string_builder = []
|
||||
for (
|
||||
control_point,
|
||||
used_parking,
|
||||
squadrons,
|
||||
) in overfull:
|
||||
capacity = control_point.total_aircraft_parking
|
||||
base_description = f"{control_point.name} {used_parking}/{capacity}"
|
||||
string_builder.append(f"<p><strong>{base_description}</strong></p>")
|
||||
squadron_descriptions = []
|
||||
for squadron in squadrons:
|
||||
squadron_details = (
|
||||
f"{squadron.aircraft} {squadron.name} {squadron.max_size} aircraft"
|
||||
)
|
||||
squadron_descriptions.append(f"<li>{squadron_details}</li>")
|
||||
string_builder.append(f"<ul>{''.join(squadron_descriptions)}</ul>")
|
||||
|
||||
if not string_builder:
|
||||
string_builder.append("All airbases are within parking limits.")
|
||||
|
||||
return "".join(string_builder)
|
||||
|
||||
|
||||
class OverfullAirbasesDisplay(QGroupBox):
|
||||
def __init__(
|
||||
self,
|
||||
@ -649,27 +676,9 @@ class OverfullAirbasesDisplay(QGroupBox):
|
||||
self.on_allocation_changed()
|
||||
|
||||
def on_allocation_changed(self) -> None:
|
||||
string_builder = []
|
||||
for (
|
||||
control_point,
|
||||
used_parking,
|
||||
squadrons,
|
||||
) in self.parking_tracker.iter_overfull():
|
||||
capacity = control_point.total_aircraft_parking
|
||||
base_description = f"{control_point.name} {used_parking}/{capacity}"
|
||||
string_builder.append(f"<p><strong>{base_description}</strong></p>")
|
||||
squadron_descriptions = []
|
||||
for squadron in squadrons:
|
||||
squadron_details = (
|
||||
f"{squadron.aircraft} {squadron.name} {squadron.max_size} aircraft"
|
||||
)
|
||||
squadron_descriptions.append(f"<li>{squadron_details}</li>")
|
||||
string_builder.append(f"<ul>{''.join(squadron_descriptions)}</ul>")
|
||||
|
||||
if not string_builder:
|
||||
string_builder.append("All airbases are within parking limits.")
|
||||
|
||||
self.label.setText("".join(string_builder))
|
||||
self.label.setText(
|
||||
describe_overfull_airbases(self.parking_tracker.iter_overfull())
|
||||
)
|
||||
|
||||
|
||||
class AirWingConfigurationTab(QWidget):
|
||||
@ -771,6 +780,7 @@ class AirWingConfigurationDialog(QDialog):
|
||||
|
||||
def __init__(self, game: Game, parent) -> None:
|
||||
super().__init__(parent)
|
||||
self.game = game
|
||||
self.parking_tracker = AirWingConfigParkingTracker(game)
|
||||
|
||||
self.setMinimumSize(1024, 768)
|
||||
@ -821,7 +831,29 @@ class AirWingConfigurationDialog(QDialog):
|
||||
for tab in self.tabs:
|
||||
tab.revert()
|
||||
|
||||
def can_continue(self) -> bool:
|
||||
if not self.game.settings.enable_squadron_aircraft_limits:
|
||||
return True
|
||||
|
||||
overfull = list(self.parking_tracker.iter_overfull())
|
||||
if not overfull:
|
||||
return True
|
||||
|
||||
description = (
|
||||
"<p>The following airbases are over capacity:</p>"
|
||||
f"{describe_overfull_airbases(overfull)}"
|
||||
)
|
||||
QMessageBox().critical(
|
||||
self,
|
||||
"Cannot continue with overfull bases",
|
||||
description,
|
||||
QMessageBox.Ok,
|
||||
)
|
||||
return False
|
||||
|
||||
def accept(self) -> None:
|
||||
if not self.can_continue():
|
||||
return
|
||||
for tab in self.tabs:
|
||||
tab.apply()
|
||||
super().accept()
|
||||
@ -829,8 +861,16 @@ class AirWingConfigurationDialog(QDialog):
|
||||
def reject(self) -> None:
|
||||
result = QMessageBox.information(
|
||||
None,
|
||||
"Discard changes?",
|
||||
"Are you sure you want to discard your changes and start the campaign?",
|
||||
"Abort new game?",
|
||||
"<br />".join(
|
||||
textwrap.wrap(
|
||||
"Are you sure you want to cancel air wing configuration and "
|
||||
"return to the new game wizard? If you instead want to revert your "
|
||||
"air wing changes and continue, use the revert and accept buttons "
|
||||
"below.",
|
||||
width=55,
|
||||
)
|
||||
),
|
||||
QMessageBox.Yes,
|
||||
QMessageBox.No,
|
||||
)
|
||||
|
||||
@ -13,6 +13,7 @@ from PySide6.QtWidgets import (
|
||||
QTextEdit,
|
||||
QVBoxLayout,
|
||||
QWidget,
|
||||
QDialog,
|
||||
)
|
||||
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
||||
|
||||
@ -223,7 +224,12 @@ class NewGameWizard(QtWidgets.QWizard):
|
||||
)
|
||||
self.generatedGame = generator.generate()
|
||||
|
||||
AirWingConfigurationDialog(self.generatedGame, self).exec_()
|
||||
if (
|
||||
AirWingConfigurationDialog(self.generatedGame, self).exec()
|
||||
== QDialog.DialogCode.Rejected
|
||||
):
|
||||
logging.info("Aborted air wing configuration")
|
||||
return
|
||||
|
||||
self.generatedGame.begin_turn_0(squadrons_start_full=use_new_squadron_rules)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user