Add play/pause features to the sim.

There's no UI feedback for this yet other than the log messages.

https://github.com/dcs-liberation/dcs_liberation/issues/1704
This commit is contained in:
Dan Albert 2021-10-31 18:07:42 -07:00
parent 748d80ff3b
commit 87bf3110c8
13 changed files with 369 additions and 112 deletions

View File

@ -1,17 +1,11 @@
from __future__ import annotations from __future__ import annotations
import itertools import itertools
import json
import logging import logging
import os
import threading
import time
from collections import defaultdict from collections import defaultdict
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path
from typing import ( from typing import (
Any, Any,
Callable,
Dict, Dict,
Iterator, Iterator,
List, List,
@ -19,6 +13,7 @@ from typing import (
Union, Union,
) )
from game.ato.flight import Flight
from game.dcs.aircrafttype import AircraftType from game.dcs.aircrafttype import AircraftType
from game.dcs.groundunittype import GroundUnitType from game.dcs.groundunittype import GroundUnitType
from game.theater import Airfield, ControlPoint from game.theater import Airfield, ControlPoint
@ -27,16 +22,14 @@ from game.unitmap import (
AirliftUnits, AirliftUnits,
Building, Building,
ConvoyUnit, ConvoyUnit,
FlyingUnit,
FrontLineUnit, FrontLineUnit,
GroundObjectUnit, GroundObjectUnit,
UnitMap, UnitMap,
FlyingUnit,
) )
from game.ato.flight import Flight
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
from game.sim import MissionSimulation
DEBRIEFING_LOG_EXTENSION = "log" DEBRIEFING_LOG_EXTENSION = "log"
@ -356,54 +349,3 @@ class Debriefing:
captures.append(BaseCaptureEvent(control_point, captured_by_player)) captures.append(BaseCaptureEvent(control_point, captured_by_player))
return captures return captures
class PollDebriefingFileThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(
self,
callback: Callable[[Debriefing], None],
mission_simulation: MissionSimulation,
) -> None:
super().__init__()
self._stop_event = threading.Event()
self.callback = callback
self.mission_sim = mission_simulation
def stop(self) -> None:
self._stop_event.set()
def stopped(self) -> bool:
return self._stop_event.is_set()
def run(self) -> None:
if os.path.isfile("state.json"):
last_modified = os.path.getmtime("state.json")
else:
last_modified = 0
while not self.stopped():
try:
if (
os.path.isfile("state.json")
and os.path.getmtime("state.json") > last_modified
):
self.callback(
self.mission_sim.debrief_current_state(Path("state.json"))
)
break
except json.JSONDecodeError:
logging.exception(
"Failed to decode state.json. Probably attempted read while DCS "
"was still writing the file. Will retry in 5 seconds."
)
time.sleep(5)
def wait_for_debriefing(
callback: Callable[[Debriefing], None], mission_simulation: MissionSimulation
) -> PollDebriefingFileThread:
thread = PollDebriefingFileThread(callback, mission_simulation)
thread.start()
return thread

View File

@ -0,0 +1,56 @@
from __future__ import annotations
import json
import logging
import os
import time
from pathlib import Path
from threading import Event, Thread
from typing import Callable, TYPE_CHECKING
if TYPE_CHECKING:
from game.debriefing import Debriefing
from game.sim import MissionSimulation
class PollDebriefingFileThread(Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(
self,
callback: Callable[[Debriefing], None],
mission_sim: MissionSimulation,
) -> None:
super().__init__()
self._stop_event = Event()
self.callback = callback
self.mission_sim = mission_sim
def stop(self) -> None:
self._stop_event.set()
def stopped(self) -> bool:
return self._stop_event.is_set()
def run(self) -> None:
if os.path.isfile("state.json"):
last_modified = os.path.getmtime("state.json")
else:
last_modified = 0
while not self.stopped():
try:
if (
os.path.isfile("state.json")
and os.path.getmtime("state.json") > last_modified
):
self.callback(
self.mission_sim.debrief_current_state(Path("state.json"))
)
break
except json.JSONDecodeError:
logging.exception(
"Failed to decode state.json. Probably attempted read while DCS "
"was still writing the file. Will retry in 5 seconds."
)
time.sleep(5)

View File

@ -1,7 +1,6 @@
from __future__ import annotations from __future__ import annotations
import itertools import itertools
import logging
from collections import Iterator from collections import Iterator
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -24,30 +23,17 @@ if TYPE_CHECKING:
from game import Game from game import Game
TICK = timedelta(seconds=1)
class AircraftSimulation: class AircraftSimulation:
def __init__(self, game: Game) -> None: def __init__(self, game: Game) -> None:
self.game = game self.game = game
self.time = self.game.conditions.start_time
def run(self) -> None: def begin_simulation(self) -> None:
self.reset() self.reset()
self.set_initial_flight_states() self.set_initial_flight_states()
if self.game.settings.fast_forward_to_first_contact:
self.simulate_until_first_contact()
logging.info(f"Mission simulation completed at {self.time}")
def simulate_until_first_contact(self) -> None: def on_game_tick(self, time: datetime, duration: timedelta) -> bool:
while True:
self.time += TICK
if self.tick():
return
def tick(self) -> bool:
for flight in self.iter_flights(): for flight in self.iter_flights():
flight.on_game_tick(self.time, TICK) flight.on_game_tick(time, duration)
# Finish updating all flights before computing engagement zones so that the new # Finish updating all flights before computing engagement zones so that the new
# positions are used. # positions are used.
@ -83,7 +69,6 @@ class AircraftSimulation:
raise ValueError(f"Unknown start type {flight.start_type} for {flight}") raise ValueError(f"Unknown start type {flight.start_type} for {flight}")
def reset(self) -> None: def reset(self) -> None:
self.time = self.game.conditions.start_time
for flight in self.iter_flights(): for flight in self.iter_flights():
flight.set_state(Uninitialized(flight, self.game.settings)) flight.set_state(Uninitialized(flight, self.game.settings))

73
game/sim/gameloop.py Normal file
View File

@ -0,0 +1,73 @@
from __future__ import annotations
import logging
from pathlib import Path
from typing import Callable, TYPE_CHECKING
from .gamelooptimer import GameLoopTimer
from .missionsimulation import MissionSimulation, SimulationAlreadyCompletedError
from .simspeedsetting import SimSpeedSetting
if TYPE_CHECKING:
from game import Game
from game.debriefing import Debriefing
class GameLoop:
def __init__(self, game: Game, on_complete: Callable[[], None]) -> None:
self.game = game
self.on_complete = on_complete
self.timer = GameLoopTimer(self.tick)
self.sim = MissionSimulation(self.game)
self.started = False
self.completed = False
def start(self) -> None:
if self.started:
raise RuntimeError("Cannot start game loop because it has already started")
self.started = True
self.sim.begin_simulation()
def pause(self) -> None:
self.set_simulation_speed(SimSpeedSetting.PAUSED)
def set_simulation_speed(self, simulation_speed: SimSpeedSetting) -> None:
self.timer.stop()
if simulation_speed != self.timer.simulation_speed:
logging.info(f"Speed changed to {simulation_speed}")
if not self.started:
self.start()
self.timer.set_speed(simulation_speed)
def run_to_first_contact(self) -> None:
self.pause()
logging.info("Running sim to first contact")
while not self.completed:
self.tick()
def pause_and_generate_miz(self, output: Path) -> None:
self.pause()
self.sim.generate_miz(output)
def pause_and_debrief(self, state_path: Path, force_end: bool) -> Debriefing:
self.pause()
return self.sim.debrief_current_state(state_path, force_end)
def complete_with_results(self, debriefing: Debriefing) -> None:
self.pause()
self.sim.process_results(debriefing)
self.completed = True
def tick(self) -> None:
if not self.started:
raise RuntimeError("Attempted to tick game loop before initialization")
try:
self.completed = self.sim.tick()
if self.completed:
self.pause()
logging.info(f"Simulation completed at {self.sim.time}")
self.on_complete()
else:
logging.info(f"Simulation continued at {self.sim.time}")
except SimulationAlreadyCompletedError:
logging.exception("Attempted to tick already completed sim")

40
game/sim/gamelooptimer.py Normal file
View File

@ -0,0 +1,40 @@
from threading import Lock, Timer
from typing import Callable, Optional
from .simspeedsetting import SimSpeedSetting
class GameLoopTimer:
def __init__(self, callback: Callable[[], None]) -> None:
self.callback = callback
self.simulation_speed = SimSpeedSetting.PAUSED
self._timer: Optional[Timer] = None
self._timer_lock = Lock()
def set_speed(self, simulation_speed: SimSpeedSetting) -> None:
with self._timer_lock:
self._stop()
self.simulation_speed = simulation_speed
self._recreate_timer()
def stop(self) -> None:
with self._timer_lock:
self._stop()
def _stop(self) -> None:
if self._timer is not None:
self._timer.cancel()
def _recreate_timer(self) -> None:
self._stop()
factor = self.simulation_speed.speed_factor
if not factor:
self._timer = None
return None
self._timer = Timer(1 / factor, self._tick)
self._timer.start()
def _tick(self) -> None:
self.callback()
with self._timer_lock:
self._recreate_timer()

View File

@ -1,29 +1,45 @@
from __future__ import annotations from __future__ import annotations
import json import json
from datetime import timedelta
from pathlib import Path from pathlib import Path
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from game.debriefing import Debriefing from game.debriefing import Debriefing
from game.missiongenerator import MissionGenerator from game.missiongenerator import MissionGenerator
from game.sim.aircraftsimulation import AircraftSimulation
from game.sim.missionresultsprocessor import MissionResultsProcessor
from game.unitmap import UnitMap from game.unitmap import UnitMap
from .aircraftsimulation import AircraftSimulation
from .missionresultsprocessor import MissionResultsProcessor
if TYPE_CHECKING: if TYPE_CHECKING:
from game import Game from game import Game
TICK = timedelta(seconds=1)
class SimulationAlreadyCompletedError(RuntimeError):
def __init__(self) -> None:
super().__init__("Simulation already completed")
class MissionSimulation: class MissionSimulation:
def __init__(self, game: Game) -> None: def __init__(self, game: Game) -> None:
self.game = game self.game = game
self.unit_map: Optional[UnitMap] = None self.unit_map: Optional[UnitMap] = None
self.time = game.conditions.start_time self.aircraft_simulation = AircraftSimulation(self.game)
self.completed = False
self.time = self.game.conditions.start_time
def run(self) -> None: def begin_simulation(self) -> None:
sim = AircraftSimulation(self.game) self.aircraft_simulation.begin_simulation()
sim.run()
self.time = sim.time def tick(self) -> bool:
self.time += TICK
if self.completed:
raise RuntimeError("Simulation already completed")
self.completed = self.aircraft_simulation.on_game_tick(self.time, TICK)
return self.completed
def generate_miz(self, output: Path) -> None: def generate_miz(self, output: Path) -> None:
self.unit_map = MissionGenerator(self.game, self.time).generate_miz(output) self.unit_map = MissionGenerator(self.game, self.time).generate_miz(output)

View File

@ -0,0 +1,28 @@
from __future__ import annotations
from enum import Enum, unique
@unique
class SimSpeedSetting(Enum):
PAUSED = (0, "Paused")
X1 = (1, "1x")
X2 = (2, "2x")
X5 = (5, "5x")
X10 = (10, "10x")
X100 = (100, "100x")
X1000 = (1000, "1000x")
def __init__(self, speed_factor: int, text: str) -> None:
self.speed_factor = speed_factor
self.text = text
def __str__(self) -> str:
return self.text
@classmethod
def from_factor(cls, speed_factor: int) -> SimSpeedSetting:
for setting in SimSpeedSetting:
if setting.speed_factor == speed_factor:
return setting
raise ValueError

0
qt_ui/__init__.py Normal file
View File

75
qt_ui/simcontroller.py Normal file
View File

@ -0,0 +1,75 @@
from __future__ import annotations
import logging
from pathlib import Path
from typing import Callable, Optional, TYPE_CHECKING
from PySide2.QtCore import QObject, Signal
from game.sim.gameloop import GameLoop
from game.sim.simspeedsetting import SimSpeedSetting
from game.polldebriefingfilethread import PollDebriefingFileThread
if TYPE_CHECKING:
from game import Game
from game.debriefing import Debriefing
class SimController(QObject):
sim_speed_reset = Signal(SimSpeedSetting)
simulation_complete = Signal()
def __init__(self, game: Optional[Game]) -> None:
super().__init__()
self.game_loop: Optional[GameLoop] = None
self.recreate_game_loop(game)
self.started = False
@property
def completed(self) -> bool:
return self.game_loop.completed
def set_game(self, game: Optional[Game]) -> None:
self.recreate_game_loop(game)
self.sim_speed_reset.emit(SimSpeedSetting.PAUSED)
def recreate_game_loop(self, game: Optional[Game]) -> None:
if self.game_loop is not None:
self.game_loop.pause()
if game is not None:
self.game_loop = GameLoop(game, self.on_simulation_complete)
self.started = False
def set_simulation_speed(self, simulation_speed: SimSpeedSetting) -> None:
if self.game_loop.completed and simulation_speed is not SimSpeedSetting.PAUSED:
logging.debug("Cannot unpause sim: already complete")
return
if not self.started and simulation_speed is not SimSpeedSetting.PAUSED:
self.game_loop.start()
self.started = True
self.game_loop.set_simulation_speed(simulation_speed)
def run_to_first_contact(self) -> None:
self.game_loop.run_to_first_contact()
def generate_miz(self, output: Path) -> None:
self.game_loop.pause_and_generate_miz(output)
def wait_for_debriefing(
self, callback: Callable[[Debriefing], None]
) -> PollDebriefingFileThread:
thread = PollDebriefingFileThread(callback, self.game_loop.sim)
thread.start()
return thread
def debrief_current_state(
self, state_path: Path, force_end: bool = False
) -> Debriefing:
return self.game_loop.pause_and_debrief(state_path, force_end)
def process_results(self, debriefing: Debriefing) -> None:
return self.game_loop.complete_with_results(debriefing)
def on_simulation_complete(self) -> None:
logging.debug("Simulation complete")
self.simulation_complete.emit()

View File

@ -13,15 +13,16 @@ import qt_ui.uiconstants as CONST
from game import Game, persistency from game import Game, persistency
from game.ato.package import Package from game.ato.package import Package
from game.profiling import logged_duration from game.profiling import logged_duration
from game.sim import MissionSimulation
from game.utils import meters from game.utils import meters
from gen.flights.traveltime import TotEstimator from gen.flights.traveltime import TotEstimator
from qt_ui.models import GameModel from qt_ui.models import GameModel
from qt_ui.simcontroller import SimController
from qt_ui.widgets.QBudgetBox import QBudgetBox from qt_ui.widgets.QBudgetBox import QBudgetBox
from qt_ui.widgets.QConditionsWidget import QConditionsWidget from qt_ui.widgets.QConditionsWidget import QConditionsWidget
from qt_ui.widgets.QFactionsInfos import QFactionsInfos from qt_ui.widgets.QFactionsInfos import QFactionsInfos
from qt_ui.widgets.QIntelBox import QIntelBox from qt_ui.widgets.QIntelBox import QIntelBox
from qt_ui.widgets.clientslots import MaxPlayerCount from qt_ui.widgets.clientslots import MaxPlayerCount
from qt_ui.widgets.simspeedcontrols import SimSpeedControls
from qt_ui.windows.AirWingDialog import AirWingDialog from qt_ui.windows.AirWingDialog import AirWingDialog
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.PendingTransfersDialog import PendingTransfersDialog from qt_ui.windows.PendingTransfersDialog import PendingTransfersDialog
@ -29,21 +30,14 @@ from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResul
class QTopPanel(QFrame): class QTopPanel(QFrame):
def __init__(self, game_model: GameModel): def __init__(self, game_model: GameModel, sim_controller: SimController) -> None:
super(QTopPanel, self).__init__() super(QTopPanel, self).__init__()
self.game_model = game_model self.game_model = game_model
self.sim_controller = sim_controller
self.dialog: Optional[QDialog] = None self.dialog: Optional[QDialog] = None
self.setMaximumHeight(70) 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.conditionsWidget = QConditionsWidget()
self.budgetBox = QBudgetBox(self.game) self.budgetBox = QBudgetBox(self.game)
@ -86,6 +80,7 @@ class QTopPanel(QFrame):
self.proceedBox = QGroupBox("Proceed") self.proceedBox = QGroupBox("Proceed")
self.proceedBoxLayout = QHBoxLayout() self.proceedBoxLayout = QHBoxLayout()
self.proceedBoxLayout.addLayout(SimSpeedControls(sim_controller))
self.proceedBoxLayout.addLayout(MaxPlayerCount(self.game_model.ato_model)) self.proceedBoxLayout.addLayout(MaxPlayerCount(self.game_model.ato_model))
self.proceedBoxLayout.addWidget(self.passTurnButton) self.proceedBoxLayout.addWidget(self.passTurnButton)
self.proceedBoxLayout.addWidget(self.proceedButton) self.proceedBoxLayout.addWidget(self.proceedButton)
@ -105,6 +100,13 @@ class QTopPanel(QFrame):
self.setLayout(self.layout) self.setLayout(self.layout)
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 setGame(self, game: Optional[Game]): def setGame(self, game: Optional[Game]):
if game is None: if game is None:
return return
@ -277,11 +279,13 @@ class QTopPanel(QFrame):
if not self.confirm_negative_start_time(negative_starts): if not self.confirm_negative_start_time(negative_starts):
return return
sim = MissionSimulation(self.game) if self.game.settings.fast_forward_to_first_contact:
sim.run() self.sim_controller.run_to_first_contact()
sim.generate_miz(persistency.mission_path_for("liberation_nextturn.miz")) self.sim_controller.generate_miz(
persistency.mission_path_for("liberation_nextturn.miz")
)
waiting = QWaitingForMissionResultWindow(self.game, sim, self) waiting = QWaitingForMissionResultWindow(self.game, self.sim_controller, self)
waiting.exec_() waiting.exec_()
def budget_update(self, game: Game): def budget_update(self, game: Game):

View File

@ -0,0 +1,35 @@
from __future__ import annotations
from typing import Optional
from PySide2.QtWidgets import QButtonGroup, QHBoxLayout, QPushButton, QWidget
from game.sim.simspeedsetting import SimSpeedSetting
from qt_ui.simcontroller import SimController
class SimSpeedControls(QHBoxLayout):
def __init__(
self, sim_controller: SimController, parent: Optional[QWidget] = None
) -> None:
super().__init__(parent)
self.sim_controller = sim_controller
self.button_group = QButtonGroup(self)
self.buttons: dict[SimSpeedSetting, QPushButton] = {}
for speed_setting in SimSpeedSetting:
button = QPushButton(speed_setting.text)
button.setCheckable(True) # TODO: CSS
self.button_group.addButton(button, id=speed_setting.speed_factor)
self.addWidget(button)
self.buttons[speed_setting] = button
self.button_group.idPressed.connect(self.speed_changed)
self.sim_controller.sim_speed_reset.connect(self.on_sim_speed_reset)
def speed_changed(self, speed_factor: int) -> None:
setting = SimSpeedSetting.from_factor(speed_factor)
self.sim_controller.set_simulation_speed(setting)
def on_sim_speed_reset(self, speed_setting: SimSpeedSetting) -> None:
self.buttons[speed_setting].setChecked(True)

View File

@ -23,6 +23,7 @@ from game.debriefing import Debriefing
from qt_ui import liberation_install from qt_ui import liberation_install
from qt_ui.dialogs import Dialog from qt_ui.dialogs import Dialog
from qt_ui.models import GameModel from qt_ui.models import GameModel
from qt_ui.simcontroller import SimController
from qt_ui.uiconstants import URLS from qt_ui.uiconstants import URLS
from qt_ui.uncaughtexceptionhandler import UncaughtExceptionHandler from qt_ui.uncaughtexceptionhandler import UncaughtExceptionHandler
from qt_ui.widgets.QTopPanel import QTopPanel from qt_ui.widgets.QTopPanel import QTopPanel
@ -31,14 +32,14 @@ from qt_ui.widgets.map.QLiberationMap import QLiberationMap
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from qt_ui.windows.QDebriefingWindow import QDebriefingWindow from qt_ui.windows.QDebriefingWindow import QDebriefingWindow
from qt_ui.windows.infos.QInfoPanel import QInfoPanel from qt_ui.windows.infos.QInfoPanel import QInfoPanel
from qt_ui.windows.logs.QLogsWindow import QLogsWindow
from qt_ui.windows.newgame.QNewGameWizard import NewGameWizard from qt_ui.windows.newgame.QNewGameWizard import NewGameWizard
from qt_ui.windows.notes.QNotesWindow import QNotesWindow
from qt_ui.windows.preferences.QLiberationPreferencesWindow import ( from qt_ui.windows.preferences.QLiberationPreferencesWindow import (
QLiberationPreferencesWindow, QLiberationPreferencesWindow,
) )
from qt_ui.windows.settings.QSettingsWindow import QSettingsWindow from qt_ui.windows.settings.QSettingsWindow import QSettingsWindow
from qt_ui.windows.stats.QStatsWindow import QStatsWindow from qt_ui.windows.stats.QStatsWindow import QStatsWindow
from qt_ui.windows.notes.QNotesWindow import QNotesWindow
from qt_ui.windows.logs.QLogsWindow import QLogsWindow
class QLiberationWindow(QMainWindow): class QLiberationWindow(QMainWindow):
@ -50,6 +51,7 @@ class QLiberationWindow(QMainWindow):
self.game = game self.game = game
self.game_model = GameModel(game) self.game_model = GameModel(game)
Dialog.set_game(self.game_model) Dialog.set_game(self.game_model)
self.sim_controller = SimController(self.game)
self.ato_panel = QAirTaskingOrderPanel(self.game_model) self.ato_panel = QAirTaskingOrderPanel(self.game_model)
self.info_panel = QInfoPanel(self.game) self.info_panel = QInfoPanel(self.game)
self.liberation_map = QLiberationMap(self.game_model, self) self.liberation_map = QLiberationMap(self.game_model, self)
@ -99,7 +101,7 @@ class QLiberationWindow(QMainWindow):
vbox = QVBoxLayout() vbox = QVBoxLayout()
vbox.setMargin(0) vbox.setMargin(0)
vbox.addWidget(QTopPanel(self.game_model)) vbox.addWidget(QTopPanel(self.game_model, self.sim_controller))
vbox.addWidget(hbox) vbox.addWidget(hbox)
central_widget = QWidget() central_widget = QWidget()
@ -314,6 +316,7 @@ class QLiberationWindow(QMainWindow):
self.game = game self.game = game
if self.info_panel is not None: if self.info_panel is not None:
self.info_panel.setGame(game) self.info_panel.setGame(game)
self.sim_controller.set_game(game)
self.game_model.set(self.game) self.game_model.set(self.game)
self.liberation_map.set_game(game) self.liberation_map.set_game(game)
except AttributeError: except AttributeError:

View File

@ -22,10 +22,10 @@ from PySide2.QtWidgets import (
from jinja2 import Environment, FileSystemLoader, select_autoescape from jinja2 import Environment, FileSystemLoader, select_autoescape
from game import Game from game import Game
from game.debriefing import Debriefing, wait_for_debriefing from game.debriefing import Debriefing
from game.persistency import base_path from game.persistency import base_path
from game.profiling import logged_duration from game.profiling import logged_duration
from game.sim import MissionSimulation from qt_ui.simcontroller import SimController
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
@ -53,13 +53,13 @@ class QWaitingForMissionResultWindow(QDialog):
def __init__( def __init__(
self, self,
game: Game, game: Game,
mission_simulation: MissionSimulation, sim_controller: SimController,
parent: Optional[QWidget] = None, parent: Optional[QWidget] = None,
) -> None: ) -> None:
super(QWaitingForMissionResultWindow, self).__init__(parent=parent) super(QWaitingForMissionResultWindow, self).__init__(parent=parent)
self.setWindowModality(QtCore.Qt.WindowModal) self.setWindowModality(QtCore.Qt.WindowModal)
self.game = game self.game = game
self.mission_sim = mission_simulation self.sim_controller = sim_controller
self.setWindowTitle("Waiting for mission completion.") self.setWindowTitle("Waiting for mission completion.")
self.setWindowIcon(QIcon("./resources/icon.png")) self.setWindowIcon(QIcon("./resources/icon.png"))
self.setMinimumHeight(570) self.setMinimumHeight(570)
@ -68,8 +68,8 @@ class QWaitingForMissionResultWindow(QDialog):
DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect( DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(
self.updateLayout self.updateLayout
) )
self.wait_thread = wait_for_debriefing( self.wait_thread = sim_controller.wait_for_debriefing(
lambda debriefing: self.on_debriefing_update(debriefing), self.mission_sim lambda debriefing: self.on_debriefing_update(debriefing)
) )
def initUi(self): def initUi(self):
@ -204,13 +204,13 @@ class QWaitingForMissionResultWindow(QDialog):
DebriefingFileWrittenSignal.get_instance().sendDebriefing(debriefing) DebriefingFileWrittenSignal.get_instance().sendDebriefing(debriefing)
except Exception: except Exception:
logging.exception("Got an error while sending debriefing") logging.exception("Got an error while sending debriefing")
self.wait_thread = wait_for_debriefing( self.wait_thread = self.sim_controller.wait_for_debriefing(
lambda d: self.on_debriefing_update(d), self.mission_sim lambda d: self.on_debriefing_update(d)
) )
def process_debriefing(self): def process_debriefing(self):
with logged_duration("Turn processing"): with logged_duration("Turn processing"):
self.mission_sim.process_results(self.debriefing) self.sim_controller.process_results(self.debriefing)
self.game.pass_turn() self.game.pass_turn()
GameUpdateSignal.get_instance().sendDebriefing(self.debriefing) GameUpdateSignal.get_instance().sendDebriefing(self.debriefing)
@ -231,5 +231,5 @@ class QWaitingForMissionResultWindow(QDialog):
) )
logging.debug("Processing manually submitted %s", file[0]) logging.debug("Processing manually submitted %s", file[0])
self.on_debriefing_update( self.on_debriefing_update(
self.mission_sim.debrief_current_state(Path(file[0], force_end=True)) self.sim_controller.debrief_current_state(Path(file[0], force_end=True))
) )