From adb9e38ff43ea3e308d3f315544f9c3eb37ad63d Mon Sep 17 00:00:00 2001 From: Khopa Date: Thu, 11 Jul 2019 21:31:16 +0200 Subject: [PATCH] Waiting and Debriefing Window, QT UI is now functionnal. --- qt_ui/main.py | 19 +++- qt_ui/widgets/map/QMapControlPoint.py | 7 +- qt_ui/widgets/map/QMapEvent.py | 4 +- qt_ui/windows/GameUpdateSignal.py | 16 ++++ qt_ui/windows/QBaseMenu.py | 55 +++++++++-- qt_ui/windows/QBriefingWindow.py | 35 +++---- qt_ui/windows/QDebriefingWindow.py | 89 +++++++++++++++--- qt_ui/windows/QLiberationWindow.py | 9 +- .../windows/QWaitingForMissionResultWindow.py | 61 ++++++++++++ resources/ui/loader.gif | Bin 0 -> 10819 bytes userdata/debriefing.py | 2 +- 11 files changed, 246 insertions(+), 51 deletions(-) create mode 100644 qt_ui/windows/QWaitingForMissionResultWindow.py create mode 100644 resources/ui/loader.gif 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 0000000000000000000000000000000000000000..47adbf03dafbdabb57e1c1dbf8633d0347f66956 GIT binary patch literal 10819 zcmb`NXHZk?{8*d7dlReg7T}tvkwBO{V~yfIR@<`1lwA0JOBU z6ciMsr>8eGG}P48ba!{tXta0l-VF~APfbnH>GZ|L#j2{R*RNl%uC8uwZc?e#-QC^2 zy}kba{{8*^&d$z(fq|{9tj06%%Z$T73F`jf}E)pXPq&dW0ZPaeshd;q_{Bmik*>*F8bO(0>(_Q9T^ME3wM*Qgi-o&XKUMW#dt z#<`)aASN=#798AsaCmM72$3)FlIax_!UE$|Q&$ITYv;QQ=XCc_RU2P16N4-m3WGDVoMK~!Fc$A~@Unhp`|;z;*Ka#(8)H%U zLL38(dL4T~M2!ew-}31m$ZRKYL$u%V4b0yO;Q!)h#8*A187#m?req z;}UQXPM7sw`M87+y2_r*n}?i5134?9Gb-2cs~=uyWE_#CpOo}vKaIQD(KJ-@vB;p% zWn?YE?pcxy6H*VLwjyVMd90s&Z>){rfwAr>zy8h1DOaI7 zodwTGAoZ(^skK}~f$93YX1zGMS@qIeazrLB*&W%t_Qd|vRoBaYO|H9djt3ZbwY&&!0`Fxxt5u!-e~s7il-@0OkEu=)#G4ZnW;T=IBsyn+n^VYx7!p_5is7Xhgt77uzhnq9lw zAduBeGY8zKl`Im}r>_@J<|Xi@2mcYHoEy@tZ$pRC)c|qm;mc2l9`m~;n?{UFC78Qj z|7BworM!+^cydcx!#tKIXv-NlQr(#s$9KPn8YaP_!wfzcIG6>RK#PWxOh=zd_@nM> z6{MOujTGpiO}JN{*&N09!%>$8;0cVucNPhLH}@q;c2!X{VdeA9UL zf#x+@Ubysn(SsNCkas-U_0p5a15QM-+DNz%#E|9cAn z|NHgc)%E!t^OaR#mno)dnh5gFT&|ra`>M1)%;rSeJ9>Gu`Xc;;@kI1#269k{UAU)@ zpKDx1pkI0znv@vqhsQ?v1P9;<4qlOv^jHsKc~nxKeHI4#_eKyx{@w|l0thIC6=ZGm zUVv$EsK=(?zj8|JNS~;ce%f}o z{6?Mv#D^US3=z5_D||IaF3YSV-;}K?yWF^*BYZ6(F?)-|;(54wSt4R$bAikHYm@c6pzDj>+r_){M`VzRU3w|6_+Px7tC%P}IzyF-c`WYLh zYJUq1MzGlg^PEy6zl z+?bxBphdT_8MldKUm(BTszKmr&nq>kh=;Ivrrj>XX&5|K@sseFU@bt$bK#v1Y}fZGIcp4c$foV z3D>Z)5Yn#0rSim&n}+#~i)Wi%xjdzAg*I5~R+t^PN_7wwHzi%E}ciu(WlV6@Emw>xZe=q*i z^yaE)ZI`d+QbfVktHAJ^3R-jV+Y`$3)&iQUFUT-dRawZ_^ZNI#E*)wNXFJpFT4q(&`U zs!ZBpVSx0|dV!Bi!S|Iu!95m)xdNw+TLr14(XH>n9|D(#aZXCDL*DA;#US=_0Q3%L z{KV&rM{QcuwD9sv1-aG>rP~wizs4@qaUcQTnpse?dm5Oyl5fR!oHkRm@%^7db93Wz z2U9+mpWx@rhRP4HUMmK3lVKm0!p1Zm<_=e~gU%lbtmZ%dsLozgSaGyoR=;($QPp++ z`)2K!|1G9};sW&F<06RB7Rz+SW)HW3;hXrRCi38U{V8m{N{s3kPNV~t2xVYJ_}Stx zXd>A!Bp4qN?h|8&L_0;Y2KXjNkjTNAxD+SPKuAcYLt1V|mMy+A%_l$0KHd>2bQ*Q? z*J+Q0@WHzU;3vocS-U}4nTChqOnn2l)MmiOa%q_E=C?UBwlWcn zSp34Y5^8(6w7p8-(2dM$xU>t;W#)~@Rw90z=a1F_wgPy-P~~$dlo|t*u-XYXR0%zC z974ywEK<8_S}+(Y^+^wh7?F`%C-t@J%jE~53g!hyJ-b%g87d)UqaBJxVDiPdQ4;3M zi~daYG|&yHk;2hzj;0z5_!AqwGLh0;u98?a1~6EjM>W$)akgfS}J@i^F-?eG#;zLKQua=$B{6QGMcP_*pl)Lgjin4G%WTq4O< zkiLoD94<@JCnK>{%R?0+);Km#2v7wk|BzAuRC9IZmepi^{gCM10?WOZb<h`|adKi2jxgpRXGO^^E(OEvcZm-M(j@k$x&)w|{hu%@}WgcFzj%)Tav``zBO( zO&c>SG_VpniZMn&HXW3ZLxiz)G6*y^qUKfQSal!UO`^316H=IFFY`G>w}!rHESgae z)>btSlYEqSKk`znbt`IYiZ%v48>^fjae>-k9&$sMCofX=7JuetZQYdAh@jSBu?Sf! z%2JSatdd!v6dMH(X17t!fJnRv*7I5Jkjk-fo3SkProK)*lg@#YT!pf9*QZ+T=m)J3 zYec-4a>-k^vzAj@){mk#$;pIGAkQy1#jNwwy}3IEtE5?cp0be!>8WhGPVu_vq{UuH|gi8CW<*pomT)bOG!Yf zZeC&uSUIoz3MP;EE%IRZ4#mSRrRt8GLcTPYK3CM$$_!Dy^BvuN*{SY+_&FsXc}Kn6 zbo%>UjwuC)Z>jQt%WPscKqw=s$0dIfs zXEXzAj7Jz5>W8=UbV|>}`!itBxfxN>S%v9|$tlrTZ||gXTx~oCX~}9WYwU@Jz`IVs zazYlHkQ1;BF~NuW0-|j{4o~;l_{U65k3kV10u~l&v6F+tqfDD~ThpIm%M*6c?ZpH4 z??>+KjM}kH_~n5Ya~c&!YJad!JyDg%LGX`RDe^c<^#TFJC>D(Lno}c23NarC-xtPP z5@IF)x}~>+%72ioV12V_pr#O5^r~Q^_s-(lT|70vD|V{BIu%MzG+mhC&mbe&6KVu|XG$ zG#IN1=a8tJiy{N`N&l;NBUzDT=eEXE7w6-za_BPO z$7%GT!WZj$YUbs`hx8W2;i^ZmQhh;-@ASgaC*CZtkp~R3?wp$iw~o;(e+tinu|0Ks zP{jQKphsQLC^C@em!%qM)m=gr%6l^rN1`Ghn}+b`Cf!#MH3>$#2l4}6G_ zIdilvzpE5A&dTd!NNsQWdk6UfU!Lx@c?H;1NODS1aY`W=viog0pnFaKR4T^ooOM(U zf59@?`-mo)62h^boAXQjV3dK~P0E;&C7m{YNyRF)=W{rWW zOq{^k?3oe;yOolNk^Bj@yh%YtS|!C~)n0)rM6`dIpK2F%TwT*YW`Fq!FfFT?cxvoZ z37<6Ycy8xWkT@KS$yP1O8*LDYro$SOFKaC`i9{8ijouFUE}a%0frw7t2>P*AsM;z{ z709t5eg10UvCjdOYxh_?XKC@rOm!xZo4y!!FrD8%Jx7=iT#7k}UG<#{VPi~Gal2@m>5mJ$RjD$z&QYU(mL=Rt7pc?vp>=?AH}N_aAv<*Bim@|kV{ zAwA_>s>tbTFe8_c!-7bgG`?v3P(Yccbiu@A;iERsRSka{zGCxoo~VH%{fhJs=kmw3 zPYo8)=D*~i7aqM-TMvNIA6C)V=+k%eX*@Na7Y|1a1=k@A73)q3Av zR-V}bdS^%=SWc_>@6g14;XFJ&y}Vku4GQ^QEF~PnxQi^yTpX6kgg1rA-vuz!Fz@4zrKdW56&&EAHI zyDg*vTrJ+L39pQ7>b0AEZ-y(2fi^tNPZTpa-X1P5(A(LJySwvq#29>>OP$>%9> z9@%%VRFMZxOU`Mc6=BG0d&@O}7rIOywsye=W)&`&^zD6q%h|B~^}NVclQlPDyL(oq zipusx9&eV_;dk()G@U2ic-68BXc-}{i#i6y3+~fg(EO`3(+Zdl9sfcqPuPJ`w^IOg zVkmf*Ijvy2f$D23J9Az?10`oKjh3H*LX>4CL&+bb&thd~%GPKrLN;q%aA z0LT#c;4r3e-V45N5=32*D$a`4kP7}#e#GN^hDRS*Jg4PJ%NxOaT9MjLY{Yyi?&5`hXt6Fk&g+^x+9+<`y?dtMoQ(kh{IFknnj}4uG6CgVwOt5q z=?@0R2yf|~zZX&<eMu*pN) zs0}UEhW3{ea!X06Zy&r=gk;bB^jZy{^edWmh5^DSG#x~Kha9KxpBjFrpV!~EzHk4_ zT29y^`5%rz{(Fu@T2rL1zYC1;ej{>y2GcM6Mw@mOO}u@)tg?x-vw)I((5?trNGOcx z>kSPKiHpESlVVui!xIC28B)^{l6_BSd)S3X=357Zmmo8fV{smlWFOa12Mz2$P~u8t zt@w?S-d-j|Pkac3$}~DQkP`;$8Jig|j+>qt9hfT~UR?SZX4eB>A82+T-dUW=DMGA$ zhNca%4vSDE<GPQUF1KgjBrcX!m0tB~4ej&Cg^Jcm z=ixDC!|Q)cp-qOG<(_=I5$)mzY5R05D}8zs4uXp50BvE}63O17auR^!=R8QwlpA~w zs>asw)w;?!h?^+>uU+w2TLpzU3TQWE zWi+YplA=TePjtcV0=Oza0J~IelE7dDQ7(lCO=Ef&+N6 zN20ui4(ZXCTCDPd;RnGRk%o0?YVLQgG~f+L#>$nDcN1Z)eyfQaY2;&t#6?4II2oOB zMAay33Y_asjZ%$;Iy4WEHRQufFZUu^TO~BB>t$Jl@c_nyp2~O*WOQ~gNn&i(y6K|M zauz2FgX;({zMtVMcTbmwt9ZaxYsYA8$!zPs~_~WW;89ZEH!syy$5YWcuu3&9z zpq$;a_k+eEuI?6!4t*v(nzbN4jv_VuFg0Yo%%g#$z6!)3fdN21{Y%hK1^+TR09fT4X(lLNwI30Ba==#W@%WS~v3rx(mQ(dBf2 zXIO4jQcz)ND5f+&I48E6oK)z9(6B_Bn#eHZrnBO_psWHX&Fgza4`J}PoQ%{%g zjMFA@gASn4S*8Jx?6sBEo>0f7Pm@leBVV9lMc-Bij`o*Z)Y8NvX6rEtwn6s7)v9%!SBB*wbZg71rQf3mxGVYM~M{av&uL_YMe^HPZ|GU7m2xQ!{WY zX`p({igvfo*p}fNXZOr@=Hv>JJ)EQdCo|teGl&2O=jeN-Rs{%?nBwS$vf*N%%c0Pn zaDAXnBSBF^+t<6Ye}s0Dup)A9L10$T*!Zf{^3B}1=M9qOrwDZ(90UfFXh7rcymQL+ zEW`Ax%MS2Z*241{Zp({pD;{q%`BXlC;Y?O)*EMN0S$ivz7BKr`!tT_>l2d)AzPrb_ zdHuRni^gKrlV9cuVEot;#ML=w9HnL@;O>0mz|e{ll-LSolc+Nw3P@DZar}=Y&iZgT zMdQiH^TgaxXZHcMK%^PHmjI_|>PPe|f9VaK`_v&qW?xqxBfk*g8HZjJ>d6ZdKApbCJUXN zS@wRvdUYxsEwnrz!Lj%%Ba1xKWrghsA@$`T7S)kiJd{Jr?8IEjwVV(4L)!4V(+TO} zMbvIpSN6LSct|nF*tAliw)}#>M_DY*_~^ayM<1_9O<%`cQ7CZbIwtQMQc)X5^@^@Exw zG5jpe)8s2#X?ab@b+mO#U4?yC(A5cQY4nTfG5N5~yTMhMcDTgc8}J5V%Un9rz74>^ zgi(cHmkd{1QZ7P`3-Z*P0R?TA-T*R#gn?^5N12DXzxAV)NnFgDkz}v3eVrfXEhc~6@uYMiiy?U!-wc64QKxH&5LN*JCMp%t9v=tmW*Qk~YHRXh9UdEL zi+4QnlJNw`#j&|C{cL6)?NIyW%##h6l|2|*=gkX z_F_AJTtcTRS>BZ5x)E7NHodt1s^sF6BH^43IEc|h%irGdHiVfI$J_;F1$Iz)c*M?~ zpYz;#MNN68DWihjYqlsXh1?YJHl3+yL_h0qu5wtV^pIdC7uqfJx%Fo}gAJf3~K8BD*vu7t5o}hav>@jpUL)m+P6^af7r_a4^?n zV92gG1s7(ougZ>w+X*j`?2P$y2!{QZFD%4k6DgoIeR->pJ_iaQDM^aBU}g=smQ>$O zSd~clP_7?g73o}Pg~8rhV__J9X|P8Iq#hKc5+kn``WC$ivd$Bb4FLh&jmDE5KqQg9pj?q*b% zL3CMVW(OmOdwbjZv*XKsjpvNY-}^m_D3H7)#Ra8*Topl&aacLoiF()3nkEm6^cyel zu9_FGkKW4(eDNt+H_Rc%P*#6O8-M|o$E@nNMz=Cqw%7|A@dWUWGRqt1_1#Mb{}R|d z+m?xEtbj3S3R8gGOtQe#rv|uI;K$v>XSud-ek_<0u1(_mUi5@p<&*C2FGfVZ+S~;q zwCfa->~`;`l*vlojdAF>Wo&8iI7z@J%g&_utNx_gk&C%SN{>szvl=U_+cG%(BWjJ1 zPDbe`LZLCO^3f>^5TnkHPhtQaisO$5wrIy&3@q`MinHy>AO8I%9nM}aj+7q5Mto%bue)Htt)ycoLYJb{%Q?r-kl83rTA`QxKc z5)VUg64sZ5h>V7&1j4NCarOniiS{8cyy5~Qt3#c=Aq*CO;{?RPdO8(-0u&~JzCHvr zrxww35_6bZQ@uuLqZ63g>6zAWG;D#kG}nEClaJnmYhxQjt6SSkyE&l4#gDfFa~Y#% zuM(I-`G4~SXbR`zYsl6hsKep}RB7GLYIp`7PNUa5s-j&H%tFVwcYH3ld*TrMDh-;7 zt@n}@?9Ie%cP*&VvYb04SgYtj^lj4uuoTByU))t&gZprIiOx`tBA0OKr!rK17?Uzr zw(2s3g^w*i;@sbLiQ@g50=fXX4zm#YTK8MpUj zSnmX$=N1TobI<<#F>=`y8W|Bhryv=M6$$D4lj|%>)VnJjgz!)`H#@U?dBK!dAs?t`Z3p&>_f{)ZRTQv{-%fARmkdjXhiXCO5;JkQHwfq6&4PT$5PFeAorz}6f z#VA?~E~h)A+xYoStjQ@>r1TWqDOaZ^YisL(^~Q0W%UREKP5CQCT+*c61YO});vq8o zgFe{$hLtx@TC@P_ge*OJ`4o%wr#p*r=#egg+>-T!=bnpIp2+g;i%GntEDb>sAZ{Hd z-pZC;?#+s8`_e{Mz2!~oU}`E)jCw!E_mX3?;ium%YTe{+8$0(4t;&>5mQx*{=Vqz` zgg8(GyulBI5|cdzIuT>U9awPQ8;zE3S!4A!OJmShB6K^2XSsR8mS3-H9jtY?S?zV1 zu2&Y0voLuv_51141@o<~dW&|}x8!Xf35B(tu7mSw6)?(gNrA{j+mwDdnG<{U(tC3H zD_uerORrt%l`803GXjQ$^Lwm;RhheG^GA&hYOOvW{*WA2e0k+-QhA+#yEnqkyBK25 z$|U82TXz`qQwsXDkNS4`x1@Yjkqf-j1V3X8nU{5HwKw}y%^y4)><9^B;K^t{SLz9X z+&P`OJrnVevc6#Q@O=ZN^Q!^oR(I^<^U5ogRHdu)_J2{0mT|c^v%ztH$eGM9-k`sq zJ{cbWt1kK7p8TJf^N%6Vf7%n+{{kyJ=U9~^Yehcoa9!CsSnjRx5ej+jA9>~Am;nnz zc#)8P0seSmATh|@0Z)p6MLFOI@lgTh(6Dd}%q=N2B`zV@-ZslSvN*ZWJ17K)26JmwC`vg!Wzxl)2kQaXDhCQFyrT~QikcTky3ICoHcZ6{WUHW^%!^p^C z)LU0A+7-p|=W#(TRxM8}l10_*_3ExTH37z9C2Olt(47S{+}&Jt73<59{2b0=Ftpee zddIVcL|0_Jlk?cTzgF4Ch6okFvD=(e1u{eu*-)z4s%is&haKTZ_{;SmdVfZ=n%JZr zAJ^hEVr<)>*@2Ps%2aPT>W$AvMfrX*<~htM%~q4et3vZsFhV9$c5Zu?8g{W>J=}ZC z@>RCFXQYpQ8;u)$S{5hYI^GQDlm+w7__QlSc$N%=E4Gy8W?<>ek|!hMy#a?n$gKUe z+rpxAQrD}QI%g$1sb}(OmA>H7&H`uE+tQ4xYx@T;-)7n0xUzIK*|9SETT_4x?5b7x zWAjh1#3``!k>RIrXT?g&Nb}a;WJ|t>`qwljrY^kV`L}XLBXMCZP-I{FJ>d19}`Bi0lFMk z>^{GWhh9Ww+3G(4d50*eG3r+o^+L^|G>K#ZF$%?P_*G*#6ysF598W8LiNj*oLvRWn zM^Z`ZsgC5;Z0z|Y%ge!?QqNKZR6+=NW~RgxTi6p-F<(LXo!rZ34>j|P`(q63+=Tpt zW{N&fFY#dWD&Y1J^_}W6Zt;GSz?jAss=Xl(yJ3DsX&_HY&AS?^F0t+0hRktkj);sJ zX25JRyg+hMLJtz`II0^};mo(us8GR!XqFTT5qy1|YMZOeLKywk3v`B@XZC5uodfuNl z=JK|VMbC0bE>xeGeoDjm);5)ACW}$i=6q6UOfx g|2k@-t6V^D=-lN;QH?)nSWpZ53kcXZ~y=R literal 0 HcmV?d00001 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