diff --git a/qt_ui/main.py b/qt_ui/main.py index 2acc3b6b..688b7d00 100644 --- a/qt_ui/main.py +++ b/qt_ui/main.py @@ -1,17 +1,31 @@ +import logging +import os import sys from time import sleep +import dcs from PySide2.QtGui import QPixmap from PySide2.QtWidgets import QApplication, QLabel, QSplashScreen from qt_ui import uiconstants from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.QLiberationWindow import QLiberationWindow -from userdata import persistency +from userdata import persistency, logging as logging_module + if __name__ == "__main__": + assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version" + + persistency.setup(sys.argv[1]) + dcs.planes.FlyingType.payload_dirs = [ + os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")] + + VERSION_STRING = sys.argv[2] + logging_module.setup_version_string(VERSION_STRING) + logging.info("Using {} as userdata folder".format(persistency.base_path())) + app = QApplication(sys.argv) # Splash screen setup @@ -38,4 +52,5 @@ if __name__ == "__main__": window.show() splash.finish(window) - sys.exit(app.exec_()) \ No newline at end of file + sys.exit(app.exec_()) + diff --git a/qt_ui/widgets/map/QMapControlPoint.py b/qt_ui/widgets/map/QMapControlPoint.py index a3ac9630..9d930443 100644 --- a/qt_ui/widgets/map/QMapControlPoint.py +++ b/qt_ui/widgets/map/QMapControlPoint.py @@ -52,7 +52,12 @@ class QMapControlPoint(QGraphicsRectItem): self.update() def contextMenuEvent(self, event: QGraphicsSceneContextMenuEvent): - openBaseMenu = QAction("Open base menu") + + if self.model.captured: + openBaseMenu = QAction("Open base menu") + else: + openBaseMenu = QAction("Open intel menu") + openBaseMenu.triggered.connect(self.openBaseMenu) menu = QMenu("Menu", self.parent) diff --git a/qt_ui/widgets/map/QMapEvent.py b/qt_ui/widgets/map/QMapEvent.py index 11e9f83d..745dbf67 100644 --- a/qt_ui/widgets/map/QMapEvent.py +++ b/qt_ui/widgets/map/QMapEvent.py @@ -15,7 +15,6 @@ class QMapEvent(QGraphicsRectItem): self.parent = parent self.setAcceptHoverEvents(True) self.setZValue(2) - print(x,y,w,h) self.setToolTip(str(self.gameEvent)) def paint(self, painter, option, widget=None): @@ -31,7 +30,6 @@ class QMapEvent(QGraphicsRectItem): painter.setBrush(CONST.COLORS["red"]) painter.drawRect(option.rect) - print(option.rect) painter.drawPixmap(option.rect, CONST.EVENT_ICONS[self.gameEvent.__class__]) painter.restore() @@ -39,5 +37,5 @@ class QMapEvent(QGraphicsRectItem): self.openBriefing() def openBriefing(self): - self.briefing = QBriefingWindow(self.window(), self.gameEvent) + self.briefing = QBriefingWindow(self.gameEvent) self.briefing.show() \ No newline at end of file diff --git a/qt_ui/windows/GameUpdateSignal.py b/qt_ui/windows/GameUpdateSignal.py index 59bf1e02..86546757 100644 --- a/qt_ui/windows/GameUpdateSignal.py +++ b/qt_ui/windows/GameUpdateSignal.py @@ -1,11 +1,22 @@ from PySide2.QtCore import QObject, Signal + from game import Game +from game.event import Event, Debriefing + + +class DebriefingSignal: + + def __init__(self, game, gameEvent, debriefing): + self.game = game + self.gameEvent = gameEvent + self.debriefing = debriefing class GameUpdateSignal(QObject): instance = None gameupdated = Signal(Game) + debriefingReceived = Signal(DebriefingSignal) def __init__(self): super(GameUpdateSignal, self).__init__() @@ -14,6 +25,11 @@ class GameUpdateSignal(QObject): def updateGame(self, game: Game): self.gameupdated.emit(game) + def sendDebriefing(self, game: Game, gameEvent: Event, debriefing: Debriefing): + sig = DebriefingSignal(game, gameEvent, debriefing) + self.gameupdated.emit(game) + self.debriefingReceived.emit(sig) + @staticmethod def get_instance(): return GameUpdateSignal.instance diff --git a/qt_ui/windows/QBaseMenu.py b/qt_ui/windows/QBaseMenu.py index 5bf09343..7047c1ce 100644 --- a/qt_ui/windows/QBaseMenu.py +++ b/qt_ui/windows/QBaseMenu.py @@ -1,6 +1,7 @@ from PySide2.QtCore import Qt from PySide2.QtGui import QWindow, QCloseEvent -from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QFrame, QDialog, QVBoxLayout, QGridLayout, QPushButton +from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QFrame, QDialog, QVBoxLayout, QGridLayout, QPushButton, \ + QGroupBox from dcs.unittype import UnitType from game.event import UnitsDeliveryEvent @@ -30,7 +31,7 @@ class QBaseMenu(QDialog): self.deliveryEvent = self.game.units_delivery_event(self.cp) self.setWindowFlags(Qt.WindowStaysOnTopHint) - self.setMinimumSize(200, 200) + self.setMinimumSize(300, 200) self.setModal(True) self.initUi() @@ -52,13 +53,22 @@ class QBaseMenu(QDialog): self.topLayoutWidget.setProperty("style", "baseMenuHeader") self.topLayoutWidget.setLayout(self.topLayout) - units = { - CAP: db.find_unittype(CAP, self.game.player_name), - Embarking: db.find_unittype(Embarking, self.game.player_name), - AirDefence: db.find_unittype(AirDefence, self.game.player_name), - CAS: db.find_unittype(CAS, self.game.player_name), - PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name), - } + if self.cp.captured: + units = { + CAP: db.find_unittype(CAP, self.game.player_name), + Embarking: db.find_unittype(Embarking, self.game.player_name), + CAS: db.find_unittype(CAS, self.game.player_name), + PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name), + AirDefence: db.find_unittype(AirDefence, self.game.player_name), + } + else: + units = { + CAP: db.find_unittype(CAP, self.game.enemy_name), + Embarking: db.find_unittype(Embarking, self.game.enemy_name), + AirDefence: db.find_unittype(AirDefence, self.game.enemy_name), + CAS: db.find_unittype(CAS, self.game.enemy_name), + PinpointStrike: db.find_unittype(PinpointStrike, self.game.enemy_name), + } self.mainLayout = QVBoxLayout() self.mainLayout.addWidget(self.topLayoutWidget) @@ -111,8 +121,33 @@ class QBaseMenu(QDialog): for unit_type in units_column: add_purchase_row(unit_type) + self.mainLayout.addLayout(self.unitLayout) + else: + intel = QGroupBox("Intel") + intelLayout = QVBoxLayout() + + row = 0 + for task_type in units.keys(): + units_column = list(set(units[task_type])) + + if sum([self.cp.base.total_units_of_type(u) for u in units_column]) > 0: + + group = QGroupBox(db.task_name(task_type)) + groupLayout = QGridLayout() + group.setLayout(groupLayout) + + row = 0 + for unit_type in units_column: + existing_units = self.cp.base.total_units_of_type(unit_type) + if existing_units == 0: + continue + groupLayout.addWidget(QLabel("" + db.unit_type_name(unit_type) + ""), row, 0) + groupLayout.addWidget(QLabel(str(existing_units)), row, 1) + row += 1 + + intelLayout.addWidget(group) + self.mainLayout.addLayout(intelLayout) - self.mainLayout.addLayout(self.unitLayout) self.setLayout(self.mainLayout) diff --git a/qt_ui/windows/QBriefingWindow.py b/qt_ui/windows/QBriefingWindow.py index d8c9d9ca..a577a504 100644 --- a/qt_ui/windows/QBriefingWindow.py +++ b/qt_ui/windows/QBriefingWindow.py @@ -7,14 +7,15 @@ from pip._internal.utils import typing from game.game import AWACS_BUDGET_COST, PinpointStrike, db, Event, FrontlineAttackEvent, FrontlinePatrolEvent, Task, \ UnitType +from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow from userdata.persistency import base_path import qt_ui.uiconstants as CONST class QBriefingWindow(QDialog): - def __init__(self, parent, gameEvent: Event): - super(QBriefingWindow, self).__init__(parent) + def __init__(self, gameEvent: Event): + super(QBriefingWindow, self).__init__() self.gameEvent = gameEvent self.setWindowTitle("Briefing : " + str(gameEvent)) self.setMinimumSize(200,200) @@ -40,7 +41,7 @@ class QBriefingWindow(QDialog): self.depart_from = QComboBox() for cp in [b for b in self.game.theater.controlpoints if b.captured]: - self.depart_from.addItem(str(cp.name)) + self.depart_from.addItem(str(cp.name), cp) self.depart_layout.addWidget(self.depart_from_label) self.depart_layout.addWidget(self.depart_from) @@ -91,24 +92,11 @@ class QBriefingWindow(QDialog): scramble_row(flight_task, t, c, True, row) row += 1 - # Options - - - """ - header("Ready?") - self.error_label = label("", columnspan=4) - self.error_label["fg"] = RED - Button(self.frame, text="Commit", command=self.start, **STYLES["btn-primary"]).grid(column=0, row=row, - sticky=E, padx=5, - pady=(10, 10)) - Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-warning"]).grid(column=3, row=row, - sticky=E, padx=5, - pady=(10, 10))""" - self.action_layout = QHBoxLayout() self.commit_button = QPushButton("Commit") - self.back_button = QPushButton("Commit") + self.back_button = QPushButton("Cancel") self.commit_button.clicked.connect(self.start) + self.back_button.clicked.connect(self.close) self.action_layout.addWidget(self.commit_button) self.action_layout.addWidget(self.back_button) @@ -169,7 +157,6 @@ class QBriefingWindow(QDialog): def debriefing_directory_location(self) -> str: return os.path.join(base_path(), "liberation_debriefings") - def start(self): if self.awacs_checkbox.isChecked() == 1: @@ -185,6 +172,11 @@ class QBriefingWindow(QDialog): ca_slots = 0 self.gameEvent.ca_slots = ca_slots + + # Resolve Departure CP + self.gameEvent.departure_cp = self.depart_from.itemData(self.depart_from.currentIndex()) + + flights = {k: {} for k in self.gameEvent.tasks} # type: db.TaskForceDict units_scramble_counts = {} # type: typing.Dict[typing.Type[UnitType], int] tasks_scramble_counts = {} # type: typing.Dict[typing.Type[Task], int] @@ -243,11 +235,12 @@ class QBriefingWindow(QDialog): self.gameEvent.player_defending(flights) - self.gameEvent.departure_cp = self.gameEvent.from_cp self.game.initiate_event(self.gameEvent) - # EventResultsMenu(self.window, self.parent, self.game, self.gameEvent).display() + waiting = QWaitingForMissionResultWindow(self.gameEvent, self.game) + waiting.show() + self.close() def showErrorMessage(self, text): about = QMessageBox() diff --git a/qt_ui/windows/QDebriefingWindow.py b/qt_ui/windows/QDebriefingWindow.py index 7b3919c7..af57ade5 100644 --- a/qt_ui/windows/QDebriefingWindow.py +++ b/qt_ui/windows/QDebriefingWindow.py @@ -1,17 +1,82 @@ -from PySide2.QtGui import QWindow -from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget +import os + +from PySide2 import QtCore +from PySide2.QtGui import QMovie +from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout, QGroupBox, QGridLayout, QPushButton + +from game.game import Event, db, Game +from userdata.debriefing import wait_for_debriefing, Debriefing +from userdata.persistency import base_path -class QDebriefingWindow(QWindow): +class QDebriefingWindow(QDialog): - def __init__(self, parent): - super(QDebriefingWindow, self).__init__(parent) - self.initUi() + def __init__(self, debriefing: Debriefing, gameEvent: Event, game: Game): + super(QDebriefingWindow, self).__init__() - def initUi(self): - layout = QHBoxLayout() - layout.addWidget(QLabel("TODO : This will be the debriefing menu")) + self.setModal(True) + self.setWindowTitle("Debriefing") + self.setMinimumSize(300, 200) - central_widget = QWidget() - central_widget.setLayout(layout) - self.setCentralWidget(central_widget) \ No newline at end of file + self.game = game + self.gameEvent = gameEvent + self.debriefing = debriefing + + self.player_losses = debriefing.destroyed_units.get(self.game.player_country, {}) + self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy_country, {}) + + self.initUI() + + def initUI(self): + + self.layout = QVBoxLayout() + + # Result + + if self.gameEvent.is_successfull(self.debriefing): + title = QLabel("Operation Succesfull !") + title.setProperty("style", "title-success") + else: + title = QLabel("Operation failed !") + title.setProperty("style", "title-danger") + self.layout.addWidget(title) + + # Player lost units + lostUnits = QGroupBox(self.game.player_country + "'s lost units :") + lostUnitsLayout = QGridLayout() + lostUnits.setLayout(lostUnitsLayout) + + row = 0 + for unit_type, count in self.player_losses.items(): + lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) + lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) + row += 1 + + self.layout.addWidget(lostUnits) + + # Enemy lost units + enemylostUnits = QGroupBox(self.game.enemy_country + "'s lost units :") + enemylostUnitsLayout = QGridLayout() + enemylostUnits.setLayout(enemylostUnitsLayout) + + row = 0 + if self.debriefing.destroyed_objects: + enemylostUnitsLayout.addWidget(QLabel("Ground assets"), row, 0) + enemylostUnitsLayout.addWidget(QLabel("{}".format(len(self.debriefing.destroyed_objects))), row, 1) + row += 1 + + for unit_type, count in self.enemy_losses.items(): + if count == 0: + continue + enemylostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) + enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) + row += 1 + + self.layout.addWidget(enemylostUnits) + + # confirm button + okay = QPushButton("Okay") + okay.clicked.connect(self.close) + self.layout.addWidget(okay) + + self.setLayout(self.layout) diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index 6b1f8856..c8f7e68f 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -8,7 +8,8 @@ from game import Game from qt_ui.uiconstants import URLS from qt_ui.widgets.QTopPanel import QTopPanel from qt_ui.widgets.map.QLiberationMap import QLiberationMap -from qt_ui.windows.GameUpdateSignal import GameUpdateSignal +from qt_ui.windows.GameUpdateSignal import GameUpdateSignal, DebriefingSignal +from qt_ui.windows.QDebriefingWindow import QDebriefingWindow from qt_ui.windows.QNewGameWizard import NewGameWizard from userdata import persistency @@ -52,6 +53,7 @@ class QLiberationWindow(QMainWindow): def connectSignals(self): GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) + GameUpdateSignal.get_instance().debriefingReceived.connect(self.onDebriefing) def initActions(self): self.newGameAction = QAction("New Game", self) @@ -154,3 +156,8 @@ class QLiberationWindow(QMainWindow): about.setText(text) print(about.textFormat()) about.exec_() + + def onDebriefing(self, debrief: DebriefingSignal): + print("On Debriefing") + self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game) + self.debriefing.show() diff --git a/qt_ui/windows/QWaitingForMissionResultWindow.py b/qt_ui/windows/QWaitingForMissionResultWindow.py new file mode 100644 index 00000000..b80e8506 --- /dev/null +++ b/qt_ui/windows/QWaitingForMissionResultWindow.py @@ -0,0 +1,61 @@ +import os + +from PySide2 import QtCore +from PySide2.QtGui import QMovie +from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout + +from game.game import Event, Game +from qt_ui.windows.GameUpdateSignal import GameUpdateSignal +from userdata.debriefing import wait_for_debriefing, Debriefing +from userdata.persistency import base_path + + +class QWaitingForMissionResultWindow(QDialog): + + def __init__(self, gameEvent: Event, game: Game): + super(QWaitingForMissionResultWindow, self).__init__() + self.setModal(True) + self.gameEvent = gameEvent + self.game = game + self.setWindowTitle("Waiting for mission completion.") + self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) + + self.initUi() + wait_for_debriefing(lambda debriefing: self.process_debriefing(debriefing)) + + def initUi(self): + self.layout = QVBoxLayout() + self.layout.addWidget(QLabel("You are clear for takeoff !")) + self.layout.addWidget(QLabel("In DCS open and play the mission : ")) + self.layout.addWidget(QLabel("liberation_nextturn")) + self.layout.addWidget(QLabel("or")) + self.layout.addWidget(QLabel("liberation_nextturn_quick")) + self.layout.addWidget(QLabel("Then save the debriefing to the folder :")) + self.layout.addWidget(QLabel(self.debriefing_directory_location())) + + progress = QLabel("") + progress.setAlignment(QtCore.Qt.AlignCenter) + progressBar = QMovie("./resources/ui/loader.gif") + progress.setMovie(progressBar) + self.layout.addWidget(progress) + progressBar.start() + + self.setLayout(self.layout) + + def process_debriefing(self, debriefing: Debriefing): + + print("DEBRIEFING !!") + + debriefing.calculate_units(regular_mission=self.gameEvent.operation.regular_mission, + quick_mission=self.gameEvent.operation.quick_mission, + player_country=self.game.player_country, + enemy_country=self.game.enemy_country) + + self.game.finish_event(event=self.gameEvent, debriefing=debriefing) + self.game.pass_turn(ignored_cps=[self.gameEvent.to_cp, ]) + + GameUpdateSignal.get_instance().sendDebriefing(self.game, self.gameEvent, debriefing) + self.close() + + def debriefing_directory_location(self) -> str: + return os.path.join(base_path(), "liberation_debriefings") diff --git a/resources/ui/loader.gif b/resources/ui/loader.gif new file mode 100644 index 00000000..47adbf03 Binary files /dev/null and b/resources/ui/loader.gif differ diff --git a/userdata/debriefing.py b/userdata/debriefing.py index 4d50f596..5648684b 100644 --- a/userdata/debriefing.py +++ b/userdata/debriefing.py @@ -139,7 +139,7 @@ class Debriefing: for unit in group.units: if unit.id in self._dead_units: unit_type = db.unit_type_of(unit) - logging.info("debriefing: found dead unit {} ({}, {})".format(str(unit.name), unit.id, unit_type)) + logging.info("debriefing: found dead unit {} ({}, {}) for country {}".format(str(unit.name), unit.id, unit_type, country_name)) assert country_name assert unit_type