From 1f73e02d1507af4e5b364eb1170346cbb4c7c05c Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Thu, 13 Jul 2023 22:01:53 -0700 Subject: [PATCH] Add cheats for destroying and repairing runways. --- changelog.md | 1 + game/settings/settings.py | 1 + game/theater/controlpoint.py | 28 +++++++++++++++++-- qt_ui/windows/basemenu/QBaseMenu2.py | 33 ++++++++++++++++++++++- qt_ui/windows/settings/QSettingsWindow.py | 19 +++++++++++++ 5 files changed, 79 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index c9a05e498..c2a699ac2 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ Saves from 8.x are not compatible with 9.0.0. * **[Flight Planning]** Improved IP selection for targets that are near the center of a threat zone. * **[Modding]** Factions can now specify the ship type to be used for cargo shipping. The Handy Wind will be used by default, but WW2 factions can pick something more appropriate. * **[UI]** An error will be displayed when invalid fast-forward options are selected rather than beginning a never ending simulation. +* **[UI]** Added cheats for instantly repairing and destroying runways. ## Fixes diff --git a/game/settings/settings.py b/game/settings/settings.py index d2f278e16..369ab10ed 100644 --- a/game/settings/settings.py +++ b/game/settings/settings.py @@ -562,6 +562,7 @@ class Settings: show_red_ato: bool = False enable_frontline_cheats: bool = False enable_base_capture_cheat: bool = False + enable_runway_state_cheat: bool = False only_player_takeoff: bool = True # Legacy parameter do not use diff --git a/game/theater/controlpoint.py b/game/theater/controlpoint.py index 4cb33472f..0222e4f80 100644 --- a/game/theater/controlpoint.py +++ b/game/theater/controlpoint.py @@ -249,6 +249,10 @@ def damage(self) -> None: # is reset. self.repair_turns_remaining = None + def repair(self) -> None: + self.repair_turns_remaining = None + self.damaged = False + def begin_repair(self) -> None: if self.repair_turns_remaining is not None: logging.error("Runway already under repair. Restarting.") @@ -257,8 +261,7 @@ def begin_repair(self) -> None: def process_turn(self) -> None: if self.repair_turns_remaining is not None: if self.repair_turns_remaining == 1: - self.repair_turns_remaining = None - self.damaged = False + self.repair() else: self.repair_turns_remaining -= 1 @@ -891,6 +894,11 @@ def airdrome_id_for_landing(self) -> Optional[int]: def parking_slots(self) -> Iterator[ParkingSlot]: yield from [] + @property + @abstractmethod + def runway_is_destroyable(self) -> bool: + ... + @property @abstractmethod def runway_status(self) -> RunwayStatus: @@ -1127,6 +1135,10 @@ def total_aircraft_parking(self) -> int: def heading(self) -> Heading: return Heading.from_degrees(self.airport.runways[0].heading) + @property + def runway_is_destroyable(self) -> bool: + return True + def runway_is_operational(self) -> bool: return not self.runway_status.damaged @@ -1215,6 +1227,10 @@ def find_main_tgo(self) -> GenericCarrierGroundObject: return g raise RuntimeError(f"Found no carrier/LHA group for {self.name}") + @property + def runway_is_destroyable(self) -> bool: + return False + def runway_is_operational(self) -> bool: # Necessary because it's possible for the carrier itself to have sunk # while its escorts are still alive. @@ -1392,6 +1408,10 @@ def active_runway( logging.warning("TODO: Off map spawns have no runways.") return self.stub_runway_data() + @property + def runway_is_destroyable(self) -> bool: + return False + @property def runway_status(self) -> RunwayStatus: return RunwayStatus() @@ -1422,6 +1442,10 @@ def __init__( def symbol_set_and_entity(self) -> tuple[SymbolSet, Entity]: return SymbolSet.LAND_INSTALLATIONS, LandInstallationEntity.MILITARY_BASE + @property + def runway_is_destroyable(self) -> bool: + return False + def runway_is_operational(self) -> bool: return self.has_helipads diff --git a/qt_ui/windows/basemenu/QBaseMenu2.py b/qt_ui/windows/basemenu/QBaseMenu2.py index fbc93431e..ba7a67383 100644 --- a/qt_ui/windows/basemenu/QBaseMenu2.py +++ b/qt_ui/windows/basemenu/QBaseMenu2.py @@ -13,6 +13,7 @@ from game import Game from game.ato.flighttype import FlightType from game.config import RUNWAY_REPAIR_COST +from game.server import EventStream from game.theater import ( AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION, ControlPoint, @@ -69,10 +70,23 @@ def __init__(self, parent, cp: ControlPoint, game_model: GameModel): top_layout.addWidget(self.intel_summary) top_layout.setAlignment(Qt.AlignTop) + runway_buttons_layout = QVBoxLayout() + top_layout.addLayout(runway_buttons_layout) + + if ( + self.cp.runway_is_destroyable + and self.game_model.game.settings.enable_runway_state_cheat + ): + self.cheat_runway_state = QPushButton() + self.update_cheat_runway_state_text() + self.cheat_runway_state.clicked.connect(self.on_cheat_runway_state) + runway_buttons_layout.addWidget(self.cheat_runway_state) + self.repair_button = QPushButton() self.repair_button.clicked.connect(self.begin_runway_repair) self.update_repair_button() - top_layout.addWidget(self.repair_button) + runway_buttons_layout.addWidget(self.repair_button) + runway_buttons_layout.addStretch() base_menu_header.setProperty("style", "baseMenuHeader") base_menu_header.setLayout(top_layout) @@ -129,6 +143,23 @@ def has_transfer_destinations(self) -> bool: self.cp.captured ).has_destinations(self.cp) + def update_cheat_runway_state_text(self) -> None: + if self.cp.runway_can_be_repaired: + self.cheat_runway_state.setText("CHEAT: Repair runway") + else: + self.cheat_runway_state.setText("CHEAT: Destroy runway") + + def on_cheat_runway_state(self) -> None: + if self.cp.runway_can_be_repaired: + self.cp.runway_status.repair() + else: + self.cp.runway_status.damage() + self.update_cheat_runway_state_text() + self.update_repair_button() + self.update_intel_summary() + with EventStream.event_context() as events: + events.update_control_point(self.cp) + @property def can_repair_runway(self) -> bool: return self.cp.captured and self.cp.runway_can_be_repaired diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index 9adb01306..3f6ac90d3 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -69,6 +69,18 @@ def __init__(self, game: Game, apply_settings: Callable[[], None]) -> None: self.base_capture_cheat = QLabeledWidget( "Enable Base Capture Cheat:", self.base_capture_cheat_checkbox ) + + self.base_runway_state_cheat_checkbox = QCheckBox() + self.base_runway_state_cheat_checkbox.setChecked( + game.settings.enable_runway_state_cheat + ) + self.base_runway_state_cheat_checkbox.toggled.connect(apply_settings) + self.main_layout.addLayout( + QLabeledWidget( + "Enable runway state cheat:", self.base_runway_state_cheat_checkbox + ) + ) + self.main_layout.addLayout(self.base_capture_cheat) @property @@ -83,6 +95,10 @@ def show_frontline_cheat(self) -> bool: def show_base_capture_cheat(self) -> bool: return self.base_capture_cheat_checkbox.isChecked() + @property + def enable_runway_state_cheat(self) -> bool: + return self.base_runway_state_cheat_checkbox.isChecked() + class AutoSettingsLayout(QGridLayout): def __init__( @@ -370,6 +386,9 @@ def applySettings(self): self.game.settings.enable_base_capture_cheat = ( self.cheat_options.show_base_capture_cheat ) + self.game.settings.enable_runway_state_cheat = ( + self.cheat_options.enable_runway_state_cheat + ) events = GameUpdateEvents() self.game.compute_unculled_zones(events)