diff --git a/changelog.md b/changelog.md index c7689a0c..70b047b2 100644 --- a/changelog.md +++ b/changelog.md @@ -16,6 +16,7 @@ Saves from 6.x are not compatible with 7.0. * **[Mission Generation]** Wind speeds no longer follow a uniform distribution. Median wind speeds are now much lower and the standard deviation has been reduced considerably at altitude but increased somewhat at MSL. * **[Mission Generation]** Improved task generation for SEAD flights carrying TALDs. * **[Mission Generation]** Added task timeout for SEAD flights with TALDs to prevent AI from overflying the target. +* **[Mission Generation]** Added (experimental!) option to rewind when aborting take off. This will allow you to modify your ATO after clicking take off without having to reload your game. * **[Modding]** Updated Community A-4E-C mod version support to 2.1.0 release. * **[Modding]** Add support for VSN F-4B and F-4C mod. * **[Modding]** Added support for AI C-47 mod. diff --git a/game/settings/settings.py b/game/settings/settings.py index cd3eec34..80dbf3bd 100644 --- a/game/settings/settings.py +++ b/game/settings/settings.py @@ -324,6 +324,19 @@ class Settings: "modifications." ), ) + reset_simulation_on_abort: bool = boolean_option( + "Reset mission to pre-take off conditions on abort (experimental)", + page=MISSION_GENERATOR_PAGE, + section=GAMEPLAY_SECTION, + default=False, + detail=( + "If enabled, the fast-forward effects will be rewound when aborting take " + "off. DO NOT USE THIS WITH AUTO-RESOLVE ENABLED. Lost " + "aircraft will not be recovered. This option is experimental and may not " + "work. It is always safer to reload your save after abort when using fast-" + "forward." + ), + ) player_mission_interrupts_sim_at: Optional[StartType] = choices_option( "Player missions interrupt fast forward", page=MISSION_GENERATOR_PAGE, diff --git a/game/sim/aircraftsimulation.py b/game/sim/aircraftsimulation.py index 0c300185..846d11ae 100644 --- a/game/sim/aircraftsimulation.py +++ b/game/sim/aircraftsimulation.py @@ -11,12 +11,12 @@ from game.ato.flightstate import ( Uninitialized, ) from .combat import CombatInitiator, FrozenCombat +from .gameupdateevents import GameUpdateEvents from .simulationresults import SimulationResults if TYPE_CHECKING: from game import Game from game.ato import Flight - from .gameupdateevents import GameUpdateEvents class AircraftSimulation: @@ -69,9 +69,13 @@ class AircraftSimulation: for flight in self.iter_flights(): flight.state.reinitialize(now) - def reset(self) -> None: + def reset(self, events: GameUpdateEvents | None = None) -> None: + if events is None: + events = GameUpdateEvents() for flight in self.iter_flights(): flight.set_state(Uninitialized(flight, self.game.settings)) + events.update_flight(flight) + self.combats = [] def iter_flights(self) -> Iterator[Flight]: packages = itertools.chain( diff --git a/game/sim/gameloop.py b/game/sim/gameloop.py index 0299bf88..6b2d498e 100644 --- a/game/sim/gameloop.py +++ b/game/sim/gameloop.py @@ -36,6 +36,14 @@ class GameLoop: def elapsed_time(self) -> timedelta: return self.sim.time - self.game.conditions.start_time + def reset(self) -> None: + self.pause() + self.events = GameUpdateEvents() + self.sim.reset(self.events) + self.send_update(rate_limit=False) + self.started = False + self.completed = False + def start(self) -> None: if self.started: raise RuntimeError("Cannot start game loop because it has already started") diff --git a/game/sim/missionsimulation.py b/game/sim/missionsimulation.py index cef32e19..f3dc39d1 100644 --- a/game/sim/missionsimulation.py +++ b/game/sim/missionsimulation.py @@ -33,6 +33,11 @@ class MissionSimulation: self.completed = False self.time = self.game.conditions.start_time + def reset(self, events: GameUpdateEvents) -> None: + self.completed = False + self.time = self.game.conditions.start_time + self.aircraft_simulation.reset(events) + def begin_simulation(self) -> None: self.time = self.game.conditions.start_time self.aircraft_simulation.begin_simulation() diff --git a/qt_ui/simcontroller.py b/qt_ui/simcontroller.py index 18675139..45f8e0cd 100644 --- a/qt_ui/simcontroller.py +++ b/qt_ui/simcontroller.py @@ -84,6 +84,9 @@ class SimController(QObject): with self.game_loop.paused_sim(): yield + def reset_simulation(self) -> None: + self.game_loop.reset() + def run_to_first_contact(self) -> None: self.game_loop.run_to_first_contact() diff --git a/qt_ui/windows/QWaitingForMissionResultWindow.py b/qt_ui/windows/QWaitingForMissionResultWindow.py index 09a9b730..a4c76906 100644 --- a/qt_ui/windows/QWaitingForMissionResultWindow.py +++ b/qt_ui/windows/QWaitingForMissionResultWindow.py @@ -111,7 +111,7 @@ class QWaitingForMissionResultWindow(QDialog): self.manually_submit.clicked.connect(self.submit_manually) self.actions_layout.addWidget(self.manually_submit) self.cancel = QPushButton("Abort mission") - self.cancel.clicked.connect(self.close) + self.cancel.clicked.connect(self.reject) self.actions_layout.addWidget(self.cancel) self.gridLayout.addWidget(self.actions, 2, 0) @@ -122,7 +122,7 @@ class QWaitingForMissionResultWindow(QDialog): self.manually_submit2.clicked.connect(self.submit_manually) self.actions2_layout.addWidget(self.manually_submit2) self.cancel2 = QPushButton("Abort mission") - self.cancel2.clicked.connect(self.close) + self.cancel2.clicked.connect(self.reject) self.actions2_layout.addWidget(self.cancel2) self.proceed = QPushButton("Accept results") self.proceed.setProperty("style", "btn-success") @@ -133,6 +133,11 @@ class QWaitingForMissionResultWindow(QDialog): self.layout.addLayout(self.gridLayout, 1, 0) self.setLayout(self.layout) + def reject(self) -> None: + if self.game.settings.reset_simulation_on_abort: + self.sim_controller.reset_simulation() + super().reject() + @staticmethod def add_update_row(description: str, count: int, layout: QGridLayout) -> None: row = layout.rowCount() @@ -217,7 +222,7 @@ class QWaitingForMissionResultWindow(QDialog): GameUpdateSignal.get_instance().sendDebriefing(self.debriefing) GameUpdateSignal.get_instance().updateGame(self.game) - self.close() + self.accept() def closeEvent(self, evt): super(QWaitingForMissionResultWindow, self).closeEvent(evt)