def init_ui(self): self.conditionsWidget = QConditionsWidget() self.budgetBox = QBudgetBox(self.game) self.passTurnButton = QPushButton("Pass Turn") self.passTurnButton.setIcon(CONST.ICONS["PassTurn"]) self.passTurnButton.setProperty("style", "btn-primary") self.passTurnButton.clicked.connect(self.passTurn) if not self.game: self.passTurnButton.setEnabled(False) self.proceedButton = QPushButton("Take off") self.proceedButton.setIcon(CONST.ICONS["Proceed"]) self.proceedButton.setProperty("style", "start-button") self.proceedButton.clicked.connect(self.launch_mission) if not self.game or self.game.turn == 0: self.proceedButton.setEnabled(False) self.factionsInfos = QFactionsInfos(self.game) self.settings = QPushButton("Settings") self.settings.setDisabled(True) self.settings.setIcon(CONST.ICONS["Settings"]) self.settings.setProperty("style", "btn-primary") self.settings.clicked.connect(self.openSettings) self.statistics = QPushButton("Statistics") self.statistics.setDisabled(True) self.statistics.setIcon(CONST.ICONS["Statistics"]) self.statistics.setProperty("style", "btn-primary") self.statistics.clicked.connect(self.openStatisticsWindow) self.intel_box = QIntelBox(self.game) self.buttonBox = QGroupBox("Misc") self.buttonBoxLayout = QHBoxLayout() self.buttonBoxLayout.addWidget(self.settings) self.buttonBoxLayout.addWidget(self.statistics) self.buttonBox.setLayout(self.buttonBoxLayout) self.proceedBox = QGroupBox("Proceed") self.proceedBoxLayout = QHBoxLayout() self.proceedBoxLayout.addLayout( MaxPlayerCount(self.game_model.ato_model)) self.proceedBoxLayout.addWidget(self.passTurnButton) self.proceedBoxLayout.addWidget(self.proceedButton) self.proceedBox.setLayout(self.proceedBoxLayout) self.layout = QHBoxLayout() self.layout.addWidget(self.factionsInfos) self.layout.addWidget(self.conditionsWidget) self.layout.addWidget(self.budgetBox) self.layout.addWidget(self.intel_box) self.layout.addWidget(self.buttonBox) self.layout.addStretch(1) self.layout.addWidget(self.proceedBox) self.layout.setContentsMargins(0,0,0,0) self.setLayout(self.layout)
class QTopPanel(QFrame): def __init__(self, game_model: GameModel): super(QTopPanel, self).__init__() self.game_model = game_model self.setMaximumHeight(70) self.init_ui() GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) GameUpdateSignal.get_instance().budgetupdated.connect(self.budget_update) @property def game(self) -> Optional[Game]: return self.game_model.game def init_ui(self): self.conditionsWidget = QConditionsWidget() self.budgetBox = QBudgetBox(self.game) self.passTurnButton = QPushButton("Pass Turn") self.passTurnButton.setIcon(CONST.ICONS["PassTurn"]) self.passTurnButton.setProperty("style", "btn-primary") self.passTurnButton.clicked.connect(self.passTurn) if not self.game: self.passTurnButton.setEnabled(False) self.proceedButton = QPushButton("Take off") self.proceedButton.setIcon(CONST.ICONS["Proceed"]) self.proceedButton.setProperty("style", "start-button") self.proceedButton.clicked.connect(self.launch_mission) if not self.game or self.game.turn == 0: self.proceedButton.setEnabled(False) self.factionsInfos = QFactionsInfos(self.game) self.settings = QPushButton("Settings") self.settings.setDisabled(True) self.settings.setIcon(CONST.ICONS["Settings"]) self.settings.setProperty("style", "btn-primary") self.settings.clicked.connect(self.openSettings) self.statistics = QPushButton("Statistics") self.statistics.setDisabled(True) self.statistics.setIcon(CONST.ICONS["Statistics"]) self.statistics.setProperty("style", "btn-primary") self.statistics.clicked.connect(self.openStatisticsWindow) self.intel_box = QIntelBox(self.game) self.buttonBox = QGroupBox("Misc") self.buttonBoxLayout = QHBoxLayout() self.buttonBoxLayout.addWidget(self.settings) self.buttonBoxLayout.addWidget(self.statistics) self.buttonBox.setLayout(self.buttonBoxLayout) self.proceedBox = QGroupBox("Proceed") self.proceedBoxLayout = QHBoxLayout() self.proceedBoxLayout.addLayout( MaxPlayerCount(self.game_model.ato_model)) self.proceedBoxLayout.addWidget(self.passTurnButton) self.proceedBoxLayout.addWidget(self.proceedButton) self.proceedBox.setLayout(self.proceedBoxLayout) self.layout = QHBoxLayout() self.layout.addWidget(self.factionsInfos) self.layout.addWidget(self.conditionsWidget) self.layout.addWidget(self.budgetBox) self.layout.addWidget(self.intel_box) self.layout.addWidget(self.buttonBox) self.layout.addStretch(1) self.layout.addWidget(self.proceedBox) self.layout.setContentsMargins(0,0,0,0) self.setLayout(self.layout) def setGame(self, game: Optional[Game]): if game is None: return self.settings.setEnabled(True) self.statistics.setEnabled(True) self.conditionsWidget.setCurrentTurn(game.turn, game.conditions) self.intel_box.set_game(game) self.budgetBox.setGame(game) self.factionsInfos.setGame(game) self.passTurnButton.setEnabled(True) if game and game.turn == 0: self.proceedButton.setEnabled(False) else: self.proceedButton.setEnabled(True) def openSettings(self): self.subwindow = QSettingsWindow(self.game) self.subwindow.show() def openStatisticsWindow(self): self.subwindow = QStatsWindow(self.game) self.subwindow.show() def passTurn(self): start = timeit.default_timer() self.game.pass_turn(no_action=True) GameUpdateSignal.get_instance().updateGame(self.game) self.proceedButton.setEnabled(True) end = timeit.default_timer() logging.info("Skipping turn took %s", timedelta(seconds=end - start)) def negative_start_packages(self) -> List[Package]: packages = [] for package in self.game_model.ato_model.ato.packages: if not package.flights: continue estimator = TotEstimator(package) for flight in package.flights: if estimator.mission_start_time(flight).total_seconds() < 0: packages.append(package) break return packages @staticmethod def fix_tots(packages: List[Package]) -> None: for package in packages: estimator = TotEstimator(package) package.time_over_target = estimator.earliest_tot() def ato_has_clients(self) -> bool: for package in self.game.blue_ato.packages: for flight in package.flights: if flight.client_count > 0: return True return False def confirm_no_client_launch(self) -> bool: result = QMessageBox.question( self, "Continue without client slots?", ("No client slots have been created for players. Continuing will " "allow the AI to perform the mission, but players will be unable " "to participate.<br />" "<br />" "To add client slots for players, select a package from the " "Packages panel on the left of the main window, and then a flight " "from the Flights panel below the Packages panel. The edit button " "below the Flights panel will allow you to edit the number of " "client slots in the flight. Each client slot allows one player.<br />" "<br />Click 'Yes' to continue with an AI only mission" "<br />Click 'No' if you'd like to make more changes."), QMessageBox.No, QMessageBox.Yes ) return result == QMessageBox.Yes def confirm_negative_start_time(self, negative_starts: List[Package]) -> bool: formatted = '<br />'.join( [f"{p.primary_task} {p.target.name}" for p in negative_starts] ) mbox = QMessageBox( QMessageBox.Question, "Continue with past start times?", ("Some flights in the following packages have start times set " "earlier than mission start time:<br />" "<br />" f"{formatted}<br />" "<br />" "Flight start times are estimated based on the package TOT, so it " "is possible that not all flights will be able to reach the " "target area at their assigned times.<br />" "<br />" "You can either continue with the mission as planned, with the " "misplanned flights potentially flying too fast and/or missing " "their rendezvous; automatically fix negative TOTs; or cancel " "mission start and fix the packages manually."), parent=self ) auto = mbox.addButton("Fix TOTs automatically", QMessageBox.ActionRole) ignore = mbox.addButton("Continue without fixing", QMessageBox.DestructiveRole) cancel = mbox.addButton(QMessageBox.Cancel) mbox.setEscapeButton(cancel) mbox.exec_() clicked = mbox.clickedButton() if clicked == auto: self.fix_tots(negative_starts) return True elif clicked == ignore: return True return False def launch_mission(self): """Finishes planning and waits for mission completion.""" if not self.ato_has_clients() and not self.confirm_no_client_launch(): return negative_starts = self.negative_start_packages() if negative_starts: if not self.confirm_negative_start_time(negative_starts): return closest_cps = self.game.theater.closest_opposing_control_points() game_event = AirWarEvent( self.game, closest_cps[0], closest_cps[1], self.game.theater.controlpoints[0].position, self.game.player_name, self.game.enemy_name) unit_map = self.game.initiate_event(game_event) waiting = QWaitingForMissionResultWindow(game_event, self.game, unit_map) waiting.show() def budget_update(self, game:Game): self.budgetBox.setGame(game)
class QTopPanel(QFrame): def __init__(self, game_model: GameModel): super(QTopPanel, self).__init__() self.game_model = game_model self.dialog: Optional[QDialog] = None self.setMaximumHeight(70) self.init_ui() GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) GameUpdateSignal.get_instance().budgetupdated.connect(self.budget_update) @property def game(self) -> Optional[Game]: return self.game_model.game def init_ui(self): self.conditionsWidget = QConditionsWidget() self.budgetBox = QBudgetBox(self.game) pass_turn_text = "Pass Turn" if not self.game or self.game.turn == 0: pass_turn_text = "Begin Campaign" self.passTurnButton = QPushButton(pass_turn_text) self.passTurnButton.setIcon(CONST.ICONS["PassTurn"]) self.passTurnButton.setProperty("style", "btn-primary") self.passTurnButton.clicked.connect(self.passTurn) if not self.game: self.passTurnButton.setEnabled(False) self.proceedButton = QPushButton("Take off") self.proceedButton.setIcon(CONST.ICONS["Proceed"]) self.proceedButton.setProperty("style", "start-button") self.proceedButton.clicked.connect(self.launch_mission) if not self.game or self.game.turn == 0: self.proceedButton.setEnabled(False) self.factionsInfos = QFactionsInfos(self.game) self.air_wing = QPushButton("Air Wing") self.air_wing.setDisabled(True) self.air_wing.setProperty("style", "btn-primary") self.air_wing.clicked.connect(self.open_air_wing) self.transfers = QPushButton("Transfers") self.transfers.setDisabled(True) self.transfers.setProperty("style", "btn-primary") self.transfers.clicked.connect(self.open_transfers) self.intel_box = QIntelBox(self.game) self.buttonBox = QGroupBox("Misc") self.buttonBoxLayout = QHBoxLayout() self.buttonBoxLayout.addWidget(self.air_wing) self.buttonBoxLayout.addWidget(self.transfers) self.buttonBox.setLayout(self.buttonBoxLayout) self.proceedBox = QGroupBox("Proceed") self.proceedBoxLayout = QHBoxLayout() self.proceedBoxLayout.addLayout(MaxPlayerCount(self.game_model.ato_model)) self.proceedBoxLayout.addWidget(self.passTurnButton) self.proceedBoxLayout.addWidget(self.proceedButton) self.proceedBox.setLayout(self.proceedBoxLayout) self.layout = QHBoxLayout() self.layout.addWidget(self.factionsInfos) self.layout.addWidget(self.conditionsWidget) self.layout.addWidget(self.budgetBox) self.layout.addWidget(self.intel_box) self.layout.addWidget(self.buttonBox) self.layout.addStretch(1) self.layout.addWidget(self.proceedBox) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) def setGame(self, game: Optional[Game]): if game is None: return self.air_wing.setEnabled(True) self.transfers.setEnabled(True) self.conditionsWidget.setCurrentTurn(game.turn, game.conditions) if game.conditions.weather.clouds: base_m = game.conditions.weather.clouds.base base_ft = int(meters(base_m).feet) self.conditionsWidget.setToolTip(f"Cloud Base: {base_m}m / {base_ft}ft") else: self.conditionsWidget.setToolTip("") self.intel_box.set_game(game) self.budgetBox.setGame(game) self.factionsInfos.setGame(game) self.passTurnButton.setEnabled(True) if game and game.turn > 0: self.passTurnButton.setText("Pass Turn") if game and game.turn == 0: self.passTurnButton.setText("Begin Campaign") self.proceedButton.setEnabled(False) else: self.proceedButton.setEnabled(True) def open_air_wing(self): self.dialog = AirWingDialog(self.game_model, self.window()) self.dialog.show() def open_transfers(self): self.dialog = PendingTransfersDialog(self.game_model) self.dialog.show() def passTurn(self): with logged_duration("Skipping turn"): self.game.pass_turn(no_action=True) GameUpdateSignal.get_instance().updateGame(self.game) self.proceedButton.setEnabled(True) def negative_start_packages(self) -> List[Package]: packages = [] for package in self.game_model.ato_model.ato.packages: if not package.flights: continue estimator = TotEstimator(package) for flight in package.flights: if estimator.mission_start_time(flight).total_seconds() < 0: packages.append(package) break return packages @staticmethod def fix_tots(packages: List[Package]) -> None: for package in packages: estimator = TotEstimator(package) package.time_over_target = estimator.earliest_tot() def ato_has_clients(self) -> bool: for package in self.game.blue_ato.packages: for flight in package.flights: if flight.client_count > 0: return True return False def confirm_no_client_launch(self) -> bool: result = QMessageBox.question( self, "Continue without player pilots?", ( "No player pilots have been assigned to flights. Continuing will allow " "the AI to perform the mission, but players will be unable to " "participate.<br />" "<br />" "To assign player pilots to a flight, select a package from the " "Packages panel on the left of the main window, and then a flight from " "the Flights panel below the Packages panel. The edit button below the " "Flights panel will allow you to assign specific pilots to the flight. " "If you have no player pilots available, the checkbox next to the " "name will convert them to a player.<br />" "<br />Click 'Yes' to continue with an AI only mission" "<br />Click 'No' if you'd like to make more changes." ), QMessageBox.No, QMessageBox.Yes, ) return result == QMessageBox.Yes def confirm_negative_start_time(self, negative_starts: List[Package]) -> bool: formatted = "<br />".join( [f"{p.primary_task} {p.target.name}" for p in negative_starts] ) mbox = QMessageBox( QMessageBox.Question, "Continue with past start times?", ( "Some flights in the following packages have start times set " "earlier than mission start time:<br />" "<br />" f"{formatted}<br />" "<br />" "Flight start times are estimated based on the package TOT, so it " "is possible that not all flights will be able to reach the " "target area at their assigned times.<br />" "<br />" "You can either continue with the mission as planned, with the " "misplanned flights potentially flying too fast and/or missing " "their rendezvous; automatically fix negative TOTs; or cancel " "mission start and fix the packages manually." ), parent=self, ) auto = mbox.addButton("Fix TOTs automatically", QMessageBox.ActionRole) ignore = mbox.addButton("Continue without fixing", QMessageBox.DestructiveRole) cancel = mbox.addButton(QMessageBox.Cancel) mbox.setEscapeButton(cancel) mbox.exec_() clicked = mbox.clickedButton() if clicked == auto: self.fix_tots(negative_starts) return True elif clicked == ignore: return True return False def check_no_missing_pilots(self) -> bool: missing_pilots = [] for package in self.game.blue_ato.packages: for flight in package.flights: if flight.missing_pilots > 0: missing_pilots.append((package, flight)) if not missing_pilots: return False formatted = "<br />".join( [f"{p.primary_task} {p.target}: {f}" for p, f in missing_pilots] ) mbox = QMessageBox( QMessageBox.Critical, "Flights are missing pilots", ( "The following flights are missing one or more pilots:<br />" "<br />" f"{formatted}<br />" "<br />" "You must either assign pilots to those flights or cancel those " "missions." ), parent=self, ) mbox.setEscapeButton(mbox.addButton(QMessageBox.Close)) mbox.exec_() return True def launch_mission(self): """Finishes planning and waits for mission completion.""" if not self.ato_has_clients() and not self.confirm_no_client_launch(): return if self.check_no_missing_pilots(): return negative_starts = self.negative_start_packages() if negative_starts: if not self.confirm_negative_start_time(negative_starts): return closest_cps = self.game.theater.closest_opposing_control_points() game_event = AirWarEvent( self.game, closest_cps[0], closest_cps[1], self.game.theater.controlpoints[0].position, self.game.player_faction.name, self.game.enemy_faction.name, ) unit_map = self.game.initiate_event(game_event) waiting = QWaitingForMissionResultWindow(game_event, self.game, unit_map) waiting.show() def budget_update(self, game: Game): self.budgetBox.setGame(game)
def init_ui(self): self.conditionsWidget = QConditionsWidget() self.budgetBox = QBudgetBox(self.game) pass_turn_text = "Pass Turn" if not self.game or self.game.turn == 0: pass_turn_text = "Begin Campaign" self.passTurnButton = QPushButton(pass_turn_text) self.passTurnButton.setIcon(CONST.ICONS["PassTurn"]) self.passTurnButton.setProperty("style", "btn-primary") self.passTurnButton.clicked.connect(self.passTurn) if not self.game: self.passTurnButton.setEnabled(False) self.proceedButton = QPushButton("Take off") self.proceedButton.setIcon(CONST.ICONS["Proceed"]) self.proceedButton.setProperty("style", "start-button") self.proceedButton.clicked.connect(self.launch_mission) if not self.game or self.game.turn == 0: self.proceedButton.setEnabled(False) self.factionsInfos = QFactionsInfos(self.game) self.air_wing = QPushButton("Air Wing") self.air_wing.setDisabled(True) self.air_wing.setProperty("style", "btn-primary") self.air_wing.clicked.connect(self.open_air_wing) self.transfers = QPushButton("Transfers") self.transfers.setDisabled(True) self.transfers.setProperty("style", "btn-primary") self.transfers.clicked.connect(self.open_transfers) self.intel_box = QIntelBox(self.game) self.buttonBox = QGroupBox("Misc") self.buttonBoxLayout = QHBoxLayout() self.buttonBoxLayout.addWidget(self.air_wing) self.buttonBoxLayout.addWidget(self.transfers) self.buttonBox.setLayout(self.buttonBoxLayout) self.proceedBox = QGroupBox("Proceed") self.proceedBoxLayout = QHBoxLayout() self.proceedBoxLayout.addLayout(MaxPlayerCount(self.game_model.ato_model)) self.proceedBoxLayout.addWidget(self.passTurnButton) self.proceedBoxLayout.addWidget(self.proceedButton) self.proceedBox.setLayout(self.proceedBoxLayout) self.layout = QHBoxLayout() self.layout.addWidget(self.factionsInfos) self.layout.addWidget(self.conditionsWidget) self.layout.addWidget(self.budgetBox) self.layout.addWidget(self.intel_box) self.layout.addWidget(self.buttonBox) self.layout.addStretch(1) self.layout.addWidget(self.proceedBox) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout)
def __init__(self, game_model: GameModel, sim_controller: SimController) -> None: super(QTopPanel, self).__init__() self.game_model = game_model self.sim_controller = sim_controller self.dialog: Optional[QDialog] = None self.setMaximumHeight(70) self.conditionsWidget = QConditionsWidget(sim_controller) self.budgetBox = QBudgetBox(self.game) pass_turn_text = "Pass Turn" if not self.game or self.game.turn == 0: pass_turn_text = "Begin Campaign" self.passTurnButton = QPushButton(pass_turn_text) self.passTurnButton.setIcon(CONST.ICONS["PassTurn"]) self.passTurnButton.setProperty("style", "btn-primary") self.passTurnButton.clicked.connect(self.passTurn) if not self.game: self.passTurnButton.setEnabled(False) self.proceedButton = QPushButton("Take off") self.proceedButton.setIcon(CONST.ICONS["Proceed"]) self.proceedButton.setProperty("style", "start-button") self.proceedButton.clicked.connect(self.launch_mission) if not self.game or self.game.turn == 0: self.proceedButton.setEnabled(False) self.factionsInfos = QFactionsInfos(self.game) self.air_wing = QPushButton("Air Wing") self.air_wing.setDisabled(True) self.air_wing.setProperty("style", "btn-primary") self.air_wing.clicked.connect(self.open_air_wing) self.transfers = QPushButton("Transfers") self.transfers.setDisabled(True) self.transfers.setProperty("style", "btn-primary") self.transfers.clicked.connect(self.open_transfers) self.intel_box = QIntelBox(self.game) self.buttonBox = QGroupBox("Misc") self.buttonBoxLayout = QHBoxLayout() self.buttonBoxLayout.addWidget(self.air_wing) self.buttonBoxLayout.addWidget(self.transfers) self.buttonBox.setLayout(self.buttonBoxLayout) self.proceedBox = QGroupBox("Proceed") self.proceedBoxLayout = QHBoxLayout() self.proceedBoxLayout.addLayout(SimSpeedControls(sim_controller)) self.proceedBoxLayout.addLayout( MaxPlayerCount(self.game_model.ato_model)) self.proceedBoxLayout.addWidget(self.passTurnButton) self.proceedBoxLayout.addWidget(self.proceedButton) self.proceedBox.setLayout(self.proceedBoxLayout) self.layout = QHBoxLayout() self.layout.addWidget(self.factionsInfos) self.layout.addWidget(self.conditionsWidget) self.layout.addWidget(self.budgetBox) self.layout.addWidget(self.intel_box) self.layout.addWidget(self.buttonBox) self.layout.addStretch(1) self.layout.addWidget(self.proceedBox) self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) GameUpdateSignal.get_instance().budgetupdated.connect( self.budget_update)