Multiple WIP changes on UI / Submit manually debriefing.

This commit is contained in:
Khopa 2020-07-26 12:59:16 +02:00
parent cfa4f7da2e
commit f897cf745f
26 changed files with 399 additions and 369 deletions

View File

@ -2,7 +2,7 @@
## Features/Improvements : ## Features/Improvements :
* **[UI/UX]** New dark UI Theme and default theme improvement by Deus * **[UI/UX]** New dark UI Theme and default theme improvement by Deus
* **[UI/UX]** New satellite map backgrounds * **[UI/UX]** New "satellite" map backgrounds
* **[UX]** Base menu is opened with a single mouse click * **[UX]** Base menu is opened with a single mouse click
* **[Units/Factions/Mods]** Added Community A-4E-C support for faction Bluefor Cold War * **[Units/Factions/Mods]** Added Community A-4E-C support for faction Bluefor Cold War
* **[Units/Factions/Mods]** Added MB-339PAN support for faction Bluefor Cold War * **[Units/Factions/Mods]** Added MB-339PAN support for faction Bluefor Cold War
@ -13,19 +13,21 @@
* **[Mission Generator]** Artillery units will start firing mission after a random delay. It should reduces lag spikes induced by artillery strikes by spreading them out. * **[Mission Generator]** Artillery units will start firing mission after a random delay. It should reduces lag spikes induced by artillery strikes by spreading them out.
* **[Mission Generator]** The briefing will now contain the carrier ATC frequency * **[Mission Generator]** The briefing will now contain the carrier ATC frequency
* **[Mission Generator]** The briefing contains a small section about the war on the ground. * **[Mission Generator]** The briefing contains a small section about the war on the ground.
* **[Mission Generator]** DCS Liberation picture added to the in-game briefing * **[Mission Generator]** Previously destroyed units are visible in the mission. (And added a performance settings to disable this behaviour)
## Fixed issues : ## Fixed issues :
* **[Mission Generator]** Carrier will sail into the wind, not in the same direction * **[Mission Generator]** Carrier will sail into the wind, not in the same direction
* **[Mission Generator]** Carrier cold start was not working (flight was starting warm even when cold was selected) * **[Mission Generator]** Carrier cold start was not working (flight was starting warm even when cold was selected)
* **[Mission Generator]** Carrier group ships are more spread out * **[Mission Generator]** Carrier group ships are more spread out
* **[Mission Generator]** Fixed radio frequency for german WW2 warbirds
* **[Mission Generator]** BAse defense units were not controllable with Combined Arms
* **[Units/Factions]** Remove JF-17 from USA 2005 faction * **[Units/Factions]** Remove JF-17 from USA 2005 faction
* **[Units/Factions]** Removed Oliver Hazard Perry from cold war factions (too powerful sam system) * **[Units/Factions]** Removed Oliver Hazard Perry from cold war factions (too powerful sam system)
* **[Bug]** On the persian gulf full map campaign, the two carriers were sharing the same id, this was causing a lot of bugs * **[Bug]** On the persian gulf full map campaign, the two carriers were sharing the same id, this was causing a lot of bugs
* **[Performance]** Tuned the culling setting so that you cannot run into situation where no flights are generated * **[Performance]** Tuned the culling setting so that you cannot run into situation where no AI flights are generated
* **[Mission Generator]** Fixed radio frequency for german WW2 warbirds
* **[Other]** Application doesn't gracefully exit.
* **[Other]** Other minor fixes * **[Other]** Other minor fixes
# 2.0 RC 9 # 2.0 RC 9

View File

@ -1048,6 +1048,8 @@ def find_infantry(country_name: str) -> typing.List[UnitType]:
def unit_type_name(unit_type) -> str: def unit_type_name(unit_type) -> str:
return unit_type.id and unit_type.id or unit_type.name return unit_type.id and unit_type.id or unit_type.name
def unit_type_name_2(unit_type) -> str:
return unit_type.name and unit_type.name or unit_type.id
def unit_type_from_name(name: str) -> UnitType: def unit_type_from_name(name: str) -> UnitType:
if name in vehicle_map: if name in vehicle_map:

View File

@ -253,6 +253,12 @@ class Event:
except Exception as e: except Exception as e:
print(e) print(e)
# Destroyed units carcass
# -------------------------
for destroyed_unit in debriefing.destroyed_units:
self.game.add_destroyed_units(destroyed_unit)
# ----------------------------------- # -----------------------------------
# Compute damage to bases # Compute damage to bases
for cp in self.game.theater.player_points(): for cp in self.game.theater.player_points():

View File

@ -125,7 +125,11 @@ class Operation:
# Generate destroyed units # Generate destroyed units
for d in self.game.get_destroyed_units(): for d in self.game.get_destroyed_units():
utype = db.unit_type_from_name(d["type"]) try:
utype = db.unit_type_from_name(d["type"])
except KeyError:
continue
pos = Point(d["x"], d["z"]) pos = Point(d["x"], d["z"])
if utype is not None and not self.game.position_culled(pos) and self.game.settings.perf_destroyed_units: if utype is not None and not self.game.position_culled(pos) and self.game.settings.perf_destroyed_units:
self.current_mission.static_group( self.current_mission.static_group(
@ -138,6 +142,7 @@ class Operation:
dead=True, dead=True,
) )
# Air Support (Tanker & Awacs) # Air Support (Tanker & Awacs)
self.airsupportgen.generate(self.is_awacs_enabled) self.airsupportgen.generate(self.is_awacs_enabled)

View File

@ -1,5 +1,5 @@
from dcs.action import AITaskPush from dcs.action import AITaskPush, AITaskSet
from dcs.condition import TimeAfter from dcs.condition import TimeAfter, UnitDamaged, Or
from dcs.task import * from dcs.task import *
from dcs.triggers import TriggerOnce, Event from dcs.triggers import TriggerOnce, Event
@ -155,14 +155,53 @@ class GroundConflictGenerator:
target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups) target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups)
if target is not None: if target is not None:
if stance != CombatStance.RETREAT:
hold_task = Hold()
hold_task.number = 1
dcs_group.add_trigger_action(hold_task)
# Artillery strike random start # Artillery strike random start
artillery_trigger = TriggerOnce(Event.NoEvent, artillery_trigger = TriggerOnce(Event.NoEvent,
"ArtilleryFireTask #" + str(dcs_group.id)) "ArtilleryFireTask #" + str(dcs_group.id))
artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45)* 60)) artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45)* 60))
dcs_group.add_trigger_action(FireAtPoint(target, len(group.units) * 10, 100))
fire_task = FireAtPoint(target, len(group.units) * 10, 100)
if stance != CombatStance.RETREAT:
fire_task.number = 2
else:
fire_task.number = 1
dcs_group.add_trigger_action(fire_task)
artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
self.mission.triggerrules.triggers.append(artillery_trigger) self.mission.triggerrules.triggers.append(artillery_trigger)
# Artillery will fall back when under attack
if stance != CombatStance.RETREAT:
# Hold position
dcs_group.points[0].tasks.append(Hold())
retreat = self.find_retreat_point(dcs_group, forward_heading)
dcs_group.add_waypoint(dcs_group.position.point_from_heading(forward_heading, 1), PointAction.OffRoad)
dcs_group.points[1].tasks.append(Hold())
dcs_group.add_waypoint(retreat, PointAction.OffRoad)
artillery_fallback = TriggerOnce(Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id))
for i, u in enumerate(dcs_group.units):
artillery_fallback.add_condition(UnitDamaged(u.id))
if i < len(dcs_group.units) - 1:
artillery_fallback.add_condition(Or())
retreat_task = GoToWaypoint(toIndex=3)
retreat_task.number = 3
dcs_group.add_trigger_action(retreat_task)
artillery_fallback.add_action(AITaskSet(dcs_group.id, len(dcs_group.tasks)))
self.mission.triggerrules.triggers.append(artillery_fallback)
for u in dcs_group.units:
u.initial = True
u.heading = forward_heading + random.randint(-5,5)
elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]: elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]:
if stance == CombatStance.AGGRESIVE: if stance == CombatStance.AGGRESIVE:
# Attack nearest enemy if any # Attack nearest enemy if any

View File

@ -56,7 +56,7 @@ class FlightPlanner:
self.compute_strike_targets() self.compute_strike_targets()
# The priority is to assign air-superiority fighter or interceptor to interception roles, so they can scramble if there is an attacker # The priority is to assign air-superiority fighter or interceptor to interception roles, so they can scramble if there is an attacker
#self.commision_interceptors() self.commision_interceptors()
# Then some CAP patrol for the next 2 hours # Then some CAP patrol for the next 2 hours
self.commision_cap() self.commision_cap()
@ -106,6 +106,7 @@ class FlightPlanner:
break break
inventory[unit] = inventory[unit] - 2 inventory[unit] = inventory[unit] - 2
flight = Flight(unit, 2, self.from_cp, FlightType.INTERCEPTION) flight = Flight(unit, 2, self.from_cp, FlightType.INTERCEPTION)
flight.scheduled_in = 1
flight.points = [] flight.points = []
self.interceptor_flights.append(flight) self.interceptor_flights.append(flight)

View File

@ -83,6 +83,5 @@ if __name__ == "__main__":
logging.info("Attempt to restore original mission scripting file") logging.info("Attempt to restore original mission scripting file")
liberation_install.restore_original_mission_scripting() liberation_install.restore_original_mission_scripting()
logging.info("QT process exited with code : " + str(qt_execution_code)) logging.info("QT process exited with code : " + str(qt_execution_code))
sys.exit(0)

View File

@ -5,7 +5,6 @@ from PySide2.QtWidgets import QGraphicsRectItem, QGraphicsSceneHoverEvent, QGrap
import qt_ui.uiconstants as CONST import qt_ui.uiconstants as CONST
from game import Game from game import Game
from qt_ui.windows.basemenu.QBaseMenu import QBaseMenu
from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2 from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2
from theater import ControlPoint, db from theater import ControlPoint, db

View File

@ -1,4 +1,4 @@
from PySide2.QtGui import QIcon from PySide2.QtGui import QIcon, QPixmap
from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout, QGroupBox, QGridLayout, QPushButton from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout, QGroupBox, QGridLayout, QPushButton
from game.game import Event, db, Game from game.game import Event, db, Game
@ -25,14 +25,21 @@ class QDebriefingWindow(QDialog):
self.layout = QVBoxLayout() self.layout = QVBoxLayout()
# Result header = QLabel(self)
header.setGeometry(0, 0, 655, 106)
pixmap = QPixmap("./resources/ui/debriefing.png")
header.setPixmap(pixmap)
self.layout.addWidget(header)
self.layout.addStretch()
if self.gameEvent.is_successfull(self.debriefing): # Result
title = QLabel("<b>Operation Succesfull !</b>") #if self.gameEvent.is_successfull(self.debriefing):
title.setProperty("style", "title-success") # title = QLabel("<b>Operation end !</b>")
else: # title.setProperty("style", "title-success")
title = QLabel("<b>Operation failed !</b>") #else:
title.setProperty("style", "title-danger") # title = QLabel("<b>Operation end !</b>")
# title.setProperty("style", "title-danger")
title = QLabel("<b>Casualty report</b>")
self.layout.addWidget(title) self.layout.addWidget(title)
# Player lost units # Player lost units

View File

@ -5,7 +5,7 @@ import webbrowser
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtGui import QIcon from PySide2.QtGui import QIcon
from PySide2.QtWidgets import QWidget, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \ from PySide2.QtWidgets import QWidget, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \
QSplitter QSplitter, QFileDialog
import qt_ui.uiconstants as CONST import qt_ui.uiconstants as CONST
from game import Game from game import Game
@ -73,10 +73,18 @@ class QLiberationWindow(QMainWindow):
self.newGameAction.setIcon(QIcon(CONST.ICONS["New"])) self.newGameAction.setIcon(QIcon(CONST.ICONS["New"]))
self.newGameAction.triggered.connect(self.newGame) self.newGameAction.triggered.connect(self.newGame)
self.openAction = QAction("Open", self)
self.openAction.setIcon(QIcon(CONST.ICONS["Open"]))
self.openAction.triggered.connect(self.openFile)
self.saveGameAction = QAction("Save", self) self.saveGameAction = QAction("Save", self)
self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"])) self.saveGameAction.setIcon(QIcon(CONST.ICONS["Save"]))
self.saveGameAction.triggered.connect(self.saveGame) self.saveGameAction.triggered.connect(self.saveGame)
self.saveAsAction = QAction("Save As", self)
self.saveAsAction.setIcon(QIcon(CONST.ICONS["Save"]))
self.saveAsAction.triggered.connect(self.saveGameAs)
self.showAboutDialogAction = QAction("About DCS Liberation", self) self.showAboutDialogAction = QAction("About DCS Liberation", self)
self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about")) self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about"))
self.showAboutDialogAction.triggered.connect(self.showAboutDialog) self.showAboutDialogAction.triggered.connect(self.showAboutDialog)
@ -88,7 +96,7 @@ class QLiberationWindow(QMainWindow):
def initToolbar(self): def initToolbar(self):
self.tool_bar = self.addToolBar("File") self.tool_bar = self.addToolBar("File")
self.tool_bar.addAction(self.newGameAction) self.tool_bar.addAction(self.newGameAction)
#self.tool_bar.addAction(QIcon(CONST.ICONS["Open"]), "Open") self.tool_bar.addAction(self.openAction)
self.tool_bar.addAction(self.saveGameAction) self.tool_bar.addAction(self.saveGameAction)
def initMenuBar(self): def initMenuBar(self):
@ -96,7 +104,7 @@ class QLiberationWindow(QMainWindow):
file_menu = self.menu.addMenu("File") file_menu = self.menu.addMenu("File")
file_menu.addAction(self.newGameAction) file_menu.addAction(self.newGameAction)
#file_menu.addAction(QIcon(CONST.ICONS["Open"]), "Open") # TODO : implement file_menu.addAction(QIcon(CONST.ICONS["Open"]), "Open") # TODO : implement
file_menu.addAction(self.saveGameAction) file_menu.addAction(self.saveGameAction)
file_menu.addSeparator() file_menu.addSeparator()
file_menu.addAction(self.showLiberationPrefDialogAction) file_menu.addAction(self.showLiberationPrefDialogAction)
@ -163,11 +171,17 @@ class QLiberationWindow(QMainWindow):
wizard.show() wizard.show()
wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame)) wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame))
def openFile(self):
file = str(QFileDialog.getOpenFileName(self, "Select game file to open"))
def saveGame(self): def saveGame(self):
logging.info("Saving game") logging.info("Saving game")
persistency.save_game(self.game) persistency.save_game(self.game)
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)
def saveGameAs(self):
file = str(QFileDialog.getSaveFileName(self, "Save As", dir=persistency._dcs_saved_game_folder))
def onGameGenerated(self, game: Game): def onGameGenerated(self, game: Game):
logging.info("On Game generated") logging.info("On Game generated")
self.game = game self.game = game

View File

@ -1,15 +1,18 @@
import json
import os import os
from PySide2 import QtCore from PySide2 import QtCore
from PySide2.QtCore import QObject, Signal from PySide2.QtCore import QObject, Signal, Qt
from PySide2.QtGui import QMovie, QIcon from PySide2.QtGui import QMovie, QIcon, QPixmap
from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout, QGroupBox, QGridLayout, QPushButton from PySide2.QtWidgets import QLabel, QDialog, QGroupBox, QGridLayout, QPushButton, QFileDialog, QMessageBox, QTextEdit, \
QHBoxLayout
from game.game import Event, Game from game.game import Event, Game
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from userdata.debriefing import wait_for_debriefing, Debriefing from userdata.debriefing import wait_for_debriefing, Debriefing
from userdata.persistency import base_path from userdata.persistency import base_path
class DebriefingFileWrittenSignal(QObject): class DebriefingFileWrittenSignal(QObject):
instance = None instance = None
@ -38,40 +41,63 @@ class QWaitingForMissionResultWindow(QDialog):
self.setWindowTitle("Waiting for mission completion.") self.setWindowTitle("Waiting for mission completion.")
self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False)
self.setWindowIcon(QIcon("./resources/icon.png")) self.setWindowIcon(QIcon("./resources/icon.png"))
self.setMinimumHeight(570)
self.initUi() self.initUi()
DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(self.updateLayout) DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(self.updateLayout)
wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game) self.wait_thread = wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game)
def initUi(self): def initUi(self):
self.layout = QGridLayout() self.layout = QGridLayout()
header = QLabel(self)
header.setGeometry(0, 0, 655, 106)
pixmap = QPixmap("./resources/ui/conflict.png")
header.setPixmap(pixmap)
self.layout.addWidget(header, 0, 0)
self.gridLayout = QGridLayout() self.gridLayout = QGridLayout()
self.gridLayout.addWidget(QLabel("<b>You are clear for takeoff</b>"), 1, 0) TEXT = "" + \
self.gridLayout.addWidget(QLabel(""), 2, 0) "<b>You are clear for takeoff</b>" + \
self.gridLayout.addWidget(QLabel("<h2>For Singleplayer :</h2>"), 3, 0) "" + \
self.gridLayout.addWidget(QLabel("In DCS, open the Mission Editor, and load the file : "), 4, 0) "<h2>For Singleplayer :</h2>\n" + \
self.gridLayout.addWidget(QLabel("<i>liberation_nextturn</i>"), 5, 0) "In DCS, open the Mission Editor, and load the file : \n" + \
self.gridLayout.addWidget(QLabel("Then once the mission is loaded in ME, in menu \"Flight\", click on FLY Mission to launch"), 6, 0) "<i>liberation_nextturn</i>\n" + \
self.gridLayout.addWidget(QLabel(""), 7, 0) "<p>Then once the mission is loaded in ME, in menu \"Flight\",\n" + \
self.gridLayout.addWidget(QLabel("<h2>For Multiplayer :</h2>"), 8, 0) "click on FLY Mission to launch.</p>\n" + \
self.gridLayout.addWidget(QLabel("In DCS, open the Mission Editor, and load the file : "), 9, 0) "" + \
self.gridLayout.addWidget(QLabel("<i>liberation_nextturn</i>"), 10, 0) "<h2>For Multiplayer :</h2>" + \
self.gridLayout.addWidget(QLabel("Click on File/Save. Then exit the mission editor, and go to Multiplayer."), 11, 0) "In DCS, open the Mission Editor, and load the file : " + \
self.gridLayout.addWidget(QLabel("Then host a server with the mission, and tell your friends to join !"), 12, 0) "<i>liberation_nextturn</i>" + \
self.gridLayout.addWidget(QLabel("(The step in the mission editor is important, and fix a game breaking bug.)"), 13, 0) "<p>Click on File/Save. Then exit the mission editor, and go to Multiplayer.</p>" + \
self.gridLayout.addWidget(QLabel(""), 14, 0) "<p>Then host a server with the mission, and tell your friends to join !</p>" + \
"<i>(The step in the mission editor is important, and fix a game breaking bug.)</i>" + \
"<h2>Finishing</h2>" + \
"<p>Once you have played the mission, click on the \"Accept Results\" button.</p>"
self.instructions_text = QTextEdit(TEXT)
self.instructions_text.setReadOnly(True)
self.gridLayout.addWidget(self.instructions_text, 1, 0)
progress = QLabel("") progress = QLabel("")
progress.setAlignment(QtCore.Qt.AlignCenter) progress.setAlignment(QtCore.Qt.AlignCenter)
progressBar = QMovie("./resources/ui/loader.gif") progress_bar = QMovie("./resources/ui/loader.gif")
progress.setMovie(progressBar) progress.setMovie(progress_bar)
self.gridLayout.addWidget(progress, 15, 0)
self.gridLayout.addWidget(QLabel(""), 16, 0)
self.gridLayout.addWidget(QLabel("Once you have played the mission, this window will dissapear."), 17, 0)
self.gridLayout.addWidget(QLabel("You will have to click on \"Accept Results\" to proceed"), 18, 0)
progressBar.start() self.actions = QGroupBox("Actions :")
self.layout.addLayout(self.gridLayout,0,0) self.actions_layout = QHBoxLayout()
self.actions.setLayout(self.actions_layout)
manually_submit = QPushButton("Manually Submit [Advanced users]")
manually_submit.clicked.connect(self.submit_manually)
self.actions_layout.addWidget(manually_submit)
cancel = QPushButton("Abort mission")
cancel.clicked.connect(self.close)
self.actions_layout.addWidget(cancel)
self.gridLayout.addWidget(self.actions, 2, 0)
progress_bar.start()
self.layout.addLayout(self.gridLayout, 1, 0)
self.setLayout(self.layout) self.setLayout(self.layout)
def updateLayout(self, debriefing): def updateLayout(self, debriefing):
@ -100,6 +126,7 @@ class QWaitingForMissionResultWindow(QDialog):
if not debriefing.mission_ended: if not debriefing.mission_ended:
self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"), 1, 0) self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"), 1, 0)
self.gridLayout.addWidget(self.actions, 2, 0)
else: else:
#self.gridLayout.addWidget(QLabel("<b>Mission is over !</b>"), 1, 0) #self.gridLayout.addWidget(QLabel("<b>Mission is over !</b>"), 1, 0)
proceed = QPushButton("Accept results") proceed = QPushButton("Accept results")
@ -111,7 +138,9 @@ class QWaitingForMissionResultWindow(QDialog):
print("On Debriefing update") print("On Debriefing update")
print(debriefing) print(debriefing)
DebriefingFileWrittenSignal.get_instance().sendDebriefing(debriefing) DebriefingFileWrittenSignal.get_instance().sendDebriefing(debriefing)
wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game)
if not debriefing.mission_ended:
self.wait_thread = wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game)
def process_debriefing(self, debriefing: Debriefing): def process_debriefing(self, debriefing: Debriefing):
self.game.finish_event(event=self.gameEvent, debriefing=debriefing) self.game.finish_event(event=self.gameEvent, debriefing=debriefing)
@ -122,3 +151,28 @@ class QWaitingForMissionResultWindow(QDialog):
def debriefing_directory_location(self) -> str: def debriefing_directory_location(self) -> str:
return os.path.join(base_path(), "liberation_debriefings") return os.path.join(base_path(), "liberation_debriefings")
def closeEvent(self, evt):
super(QWaitingForMissionResultWindow, self).closeEvent(evt)
if self.wait_thread is not None:
self.wait_thread.stop()
def submit_manually(self):
file = str(QFileDialog.getOpenFileName(self, "Select game file to open", filter="json(*.json)"))
print(file)
try:
with open("state.json", "r") as json_file:
json_data = json.load(json_file)
json_data["mission_ended"] = True
debriefing = Debriefing(json_data, self.game)
self.on_debriefing_udpate(debriefing)
except:
msg = QMessageBox()
msg.setIcon(QMessageBox.Information)
msg.setText("Invalid file : " + file)
msg.setWindowTitle("Invalid file.")
msg.setStandardButtons(QMessageBox.Ok)
msg.setWindowFlags(Qt.WindowStaysOnTopHint)
msg.exec_()
return

View File

@ -1,246 +0,0 @@
import traceback
from PySide2.QtCore import Qt
from PySide2.QtGui import QCloseEvent
from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QVBoxLayout, QGridLayout, QPushButton, \
QGroupBox, QSizePolicy, QSpacerItem
from dcs.unittype import UnitType
from game.event import UnitsDeliveryEvent, ControlPointType
from qt_ui.widgets.QBudgetBox import QBudgetBox
from qt_ui.widgets.base.QAirportInformation import QAirportInformation
from qt_ui.windows.basemenu.base_defenses.QBaseInformation import QBaseInformation
from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from theater import ControlPoint, CAP, Embarking, CAS, PinpointStrike, db
from game import Game
class QBaseMenu(QDialog):
def __init__(self, parent, controlPoint: ControlPoint, game: Game):
super(QBaseMenu, self).__init__(parent)
self.cp = controlPoint
self.game = game
self.is_carrier = self.cp.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]
try:
self.airport = game.theater.terrain.airport_by_id(self.cp.id)
except:
self.airport = None
if self.cp.captured:
self.deliveryEvent = None
for event in self.game.events:
print(event.__class__)
print(UnitsDeliveryEvent.__class__)
if event.__class__ == UnitsDeliveryEvent and event.from_cp == self.cp:
self.deliveryEvent = event
break
if not self.deliveryEvent:
print("Rebuild event")
self.deliveryEvent = self.game.units_delivery_event(self.cp)
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMinimumSize(300, 200)
self.setModal(True)
self.initUi()
def initUi(self):
self.setWindowTitle(self.cp.name)
self.topLayoutWidget = QWidget()
self.topLayout = QHBoxLayout()
title = QLabel("<b>" + self.cp.name + "</b>")
title.setAlignment(Qt.AlignLeft | Qt.AlignTop)
title.setProperty("style", "base-title")
unitsPower = QLabel("{} / {} / Runway : {}".format(self.cp.base.total_planes, self.cp.base.total_armor,
"Available" if self.cp.has_runway() else "Unavailable"))
self.topLayout.addWidget(title)
self.topLayout.addWidget(unitsPower)
self.topLayout.setAlignment(Qt.AlignTop)
self.topLayoutWidget.setProperty("style", "baseMenuHeader")
self.topLayoutWidget.setLayout(self.topLayout)
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),
}
else:
units = {
CAP: db.find_unittype(CAP, self.game.enemy_name),
Embarking: db.find_unittype(Embarking, self.game.enemy_name),
CAS: db.find_unittype(CAS, self.game.enemy_name),
PinpointStrike: db.find_unittype(PinpointStrike, self.game.enemy_name),
}
self.mainLayout = QGridLayout()
self.leftLayout = QVBoxLayout()
self.unitLayout = QVBoxLayout()
self.bought_amount_labels = {}
self.existing_units_labels = {}
row = 0
if self.cp.captured:
self.recruitment = QGroupBox("Recruitment")
self.recruitmentLayout = QVBoxLayout()
self.budget = QBudgetBox()
self.budget.setBudget(self.game.budget, self.game.budget_reward_amount)
self.recruitmentLayout.addWidget(self.budget)
for task_type in units.keys():
if task_type == PinpointStrike and self.is_carrier:
continue
units_column = list(set(units[task_type]))
if len(units_column) == 0: continue
units_column.sort(key=lambda x: db.PRICES[x])
task_box = QGroupBox("{}".format(db.task_name(task_type)))
task_box_layout = QGridLayout()
task_box.setLayout(task_box_layout)
row = 0
for unit_type in units_column:
if self.is_carrier and not unit_type in db.CARRIER_CAPABLE:
continue
row = self.add_purchase_row(unit_type, task_box_layout, row)
stretch = QVBoxLayout()
stretch.addStretch()
task_box_layout.addLayout(stretch, row, 0)
self.recruitmentLayout.addWidget(task_box)
self.recruitmentLayout.addStretch()
self.recruitment.setLayout(self.recruitmentLayout)
self.leftLayout.addWidget(self.recruitment)
self.leftLayout.addStretch()
else:
intel = QGroupBox("Intel")
intelLayout = QVBoxLayout()
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("<b>" + db.unit_type_name(unit_type) + "</b>"), row, 0)
groupLayout.addWidget(QLabel(str(existing_units)), row, 1)
row += 1
intelLayout.addWidget(group)
intelLayout.addStretch()
intel.setLayout(intelLayout)
self.leftLayout.addWidget(intel)
self.mainLayout.addWidget(self.topLayoutWidget, 0, 0)
self.mainLayout.addLayout(self.leftLayout, 1, 0)
self.mainLayout.addWidget(QBaseInformation(self.cp, self.airport), 1, 1)
self.rightLayout = QVBoxLayout()
try:
self.rightLayout.addWidget(QPlannedFlightsView(self.game.planners[self.cp.id]))
except Exception:
traceback.print_exc()
if self.airport:
self.rightLayout.addWidget(QAirportInformation(self.cp, self.airport))
self.mainLayout.addLayout(self.rightLayout, 1, 2)
self.setLayout(self.mainLayout)
def add_purchase_row(self, unit_type, layout, row):
existing_units = self.cp.base.total_units_of_type(unit_type)
scheduled_units = self.deliveryEvent.units.get(unit_type, 0)
unitName = QLabel("<b>" + db.unit_type_name(unit_type) + "</b>")
unitName.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
existing_units = QLabel(str(existing_units))
existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
amount_bought = QLabel("[{}]".format(str(scheduled_units)))
amount_bought.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
self.existing_units_labels[unit_type] = existing_units
self.bought_amount_labels[unit_type] = amount_bought
price = QLabel("{}m".format(db.PRICES[unit_type]))
price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
buy = QPushButton("+")
buy.setProperty("style", "btn-success")
buy.setMinimumSize(24, 24)
buy.clicked.connect(lambda: self.buy(unit_type))
buy.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
sell = QPushButton("-")
sell.setProperty("style", "btn-danger")
sell.setMinimumSize(24, 24)
sell.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
sell.clicked.connect(lambda: self.sell(unit_type))
layout.addWidget(unitName, row, 0)
layout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), row, 1)
layout.addWidget(existing_units, row, 2)
layout.addWidget(amount_bought, row, 3)
layout.addWidget(price, row, 4)
layout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), row, 5)
layout.addWidget(buy, row, 6)
layout.addWidget(sell, row, 7)
return row + 1
def _update_count_label(self, unit_type: UnitType):
self.bought_amount_labels[unit_type].setText("[{}]".format(
unit_type in self.deliveryEvent.units and "{}".format(self.deliveryEvent.units[unit_type]) or "0"
))
self.existing_units_labels[unit_type].setText("{}".format(
self.cp.base.total_units_of_type(unit_type)
))
def buy(self, unit_type):
price = db.PRICES[unit_type]
if self.game.budget >= price:
self.deliveryEvent.deliver({unit_type: 1})
self.game.budget -= price
self.budget.setBudget(self.game.budget, self.game.budget_reward_amount)
self._update_count_label(unit_type)
def sell(self, unit_type):
if self.deliveryEvent.units.get(unit_type, 0) > 0:
price = db.PRICES[unit_type]
self.game.budget += price
self.deliveryEvent.units[unit_type] = self.deliveryEvent.units[unit_type] - 1
if self.deliveryEvent.units[unit_type] == 0:
del self.deliveryEvent.units[unit_type]
elif self.cp.base.total_units_of_type(unit_type) > 0:
price = db.PRICES[unit_type]
self.game.budget += price
self.cp.base.commit_losses({unit_type: 1})
self._update_count_label(unit_type)
def closeEvent(self, closeEvent:QCloseEvent):
GameUpdateSignal.get_instance().updateGame(self.game)

View File

@ -1,5 +1,5 @@
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtGui import QCloseEvent from PySide2.QtGui import QCloseEvent, QPixmap
from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QGridLayout from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QGridLayout
from game import Game from game import Game
@ -35,6 +35,8 @@ class QBaseMenu2(QDialog):
self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMinimumSize(300, 200) self.setMinimumSize(300, 200)
self.setMinimumWidth(680)
self.setMaximumWidth(680)
self.setModal(True) self.setModal(True)
self.initUi() self.initUi()
@ -46,6 +48,11 @@ class QBaseMenu2(QDialog):
self.topLayoutWidget = QWidget() self.topLayoutWidget = QWidget()
self.topLayout = QHBoxLayout() self.topLayout = QHBoxLayout()
header = QLabel(self)
header.setGeometry(0, 0, 655, 106)
pixmap = QPixmap("./resources/ui/airbase.png")
header.setPixmap(pixmap)
title = QLabel("<b>" + self.cp.name + "</b>") title = QLabel("<b>" + self.cp.name + "</b>")
title.setAlignment(Qt.AlignLeft | Qt.AlignTop) title.setAlignment(Qt.AlignLeft | Qt.AlignTop)
title.setProperty("style", "base-title") title.setProperty("style", "base-title")
@ -59,10 +66,20 @@ class QBaseMenu2(QDialog):
self.topLayoutWidget.setLayout(self.topLayout) self.topLayoutWidget.setLayout(self.topLayout)
self.mainLayout = QGridLayout() self.mainLayout = QGridLayout()
self.mainLayout.addWidget(self.topLayoutWidget, 0, 0) self.mainLayout.addWidget(header, 0, 0)
self.mainLayout.addWidget(self.qbase_menu_tab, 1, 0) self.mainLayout.addWidget(self.topLayoutWidget, 1, 0)
self.mainLayout.addWidget(self.qbase_menu_tab, 2, 0)
self.setLayout(self.mainLayout) self.setLayout(self.mainLayout)
def closeEvent(self, closeEvent:QCloseEvent): def closeEvent(self, closeEvent:QCloseEvent):
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)
def get_base_image(self):
if self.cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
return "./resources/ui/carrier.png"
elif self.cp.cptype == ControlPointType.LHA_GROUP:
return "./resources/ui/lha.png"
else:
return "./resources/ui/airbase.png"

View File

@ -1,5 +1,5 @@
from PySide2.QtWidgets import QLabel, QPushButton, \ from PySide2.QtWidgets import QLabel, QPushButton, \
QSizePolicy, QSpacerItem QSizePolicy, QSpacerItem, QGroupBox, QHBoxLayout
from dcs.unittype import UnitType from dcs.unittype import UnitType
from theater import db from theater import db
@ -19,55 +19,75 @@ class QRecruitBehaviour:
def add_purchase_row(self, unit_type, layout, row): def add_purchase_row(self, unit_type, layout, row):
exist = QGroupBox()
exist.setProperty("style", "buy-box")
exist.setMaximumHeight(36)
exist.setMinimumHeight(36)
existLayout = QHBoxLayout()
exist.setLayout(existLayout)
existing_units = self.cp.base.total_units_of_type(unit_type) existing_units = self.cp.base.total_units_of_type(unit_type)
scheduled_units = self.deliveryEvent.units.get(unit_type, 0) scheduled_units = self.deliveryEvent.units.get(unit_type, 0)
unitName = QLabel("<b>" + db.unit_type_name(unit_type) + "</b>") unitName = QLabel("<b>" + db.unit_type_name_2(unit_type) + "</b>")
unitName.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) unitName.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
existing_units = QLabel(str(existing_units)) existing_units = QLabel(str(existing_units))
existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
amount_bought = QLabel("{}".format(str(scheduled_units))) amount_bought = QLabel("<b>{}</b>".format(str(scheduled_units)))
amount_bought.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) amount_bought.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
self.existing_units_labels[unit_type] = existing_units self.existing_units_labels[unit_type] = existing_units
self.bought_amount_labels[unit_type] = amount_bought self.bought_amount_labels[unit_type] = amount_bought
price = QLabel("$ <b>{}</b> m".format(db.PRICES[unit_type])) price = QLabel("<b>$ {:02d}</b> m".format(db.PRICES[unit_type]))
price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
buysell = QGroupBox()
buysell.setProperty("style", "buy-box")
buysell.setMaximumHeight(36)
buysell.setMinimumHeight(36)
buysellayout = QHBoxLayout()
buysell.setLayout(buysellayout)
buy = QPushButton("+") buy = QPushButton("+")
buy.setProperty("style", "btn-success") buy.setProperty("style", "btn-buy")
buy.setMinimumSize(24, 24) buy.setMinimumSize(16, 16)
buy.setMaximumSize(16, 16)
buy.clicked.connect(lambda: self.buy(unit_type)) buy.clicked.connect(lambda: self.buy(unit_type))
buy.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) buy.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
sell = QPushButton("-") sell = QPushButton("-")
sell.setProperty("style", "btn-danger") sell.setProperty("style", "btn-sell")
sell.setMinimumSize(24, 24) sell.setMinimumSize(16, 16)
sell.setMaximumSize(16, 16)
sell.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed)) sell.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
sell.clicked.connect(lambda: self.sell(unit_type)) sell.clicked.connect(lambda: self.sell(unit_type))
layout.addWidget(unitName, row, 0)
layout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), row, 1)
layout.addWidget(existing_units, row, 2)
layout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), row, 3)
layout.addWidget(price, row, 4)
layout.addWidget(sell, row, 5) existLayout.addWidget(unitName)
layout.addWidget(amount_bought, row, 6) existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum))
layout.addWidget(buy, row, 7) existLayout.addWidget(existing_units)
existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum))
existLayout.addWidget(price)
buysellayout.addWidget(sell)
buysellayout.addWidget(amount_bought)
buysellayout.addWidget(buy)
layout.addWidget(exist, row, 1)
layout.addWidget(buysell, row, 2)
return row + 1 return row + 1
def _update_count_label(self, unit_type: UnitType): def _update_count_label(self, unit_type: UnitType):
self.bought_amount_labels[unit_type].setText("{}".format( self.bought_amount_labels[unit_type].setText("<b>{}</b>".format(
unit_type in self.deliveryEvent.units and "{}".format(self.deliveryEvent.units[unit_type]) or "0" unit_type in self.deliveryEvent.units and "{}".format(self.deliveryEvent.units[unit_type]) or "0"
)) ))
self.existing_units_labels[unit_type].setText("{}".format( self.existing_units_labels[unit_type].setText("<b>{}</b>".format(
self.cp.base.total_units_of_type(unit_type) self.cp.base.total_units_of_type(unit_type)
)) ))

View File

@ -1,4 +1,5 @@
from PySide2.QtWidgets import QVBoxLayout, QGridLayout, QGroupBox from PySide2.QtCore import Qt
from PySide2.QtWidgets import QVBoxLayout, QGridLayout, QGroupBox, QScrollArea, QFrame, QWidget
from game.event import UnitsDeliveryEvent from game.event import UnitsDeliveryEvent
from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
@ -6,10 +7,10 @@ from theater import ControlPoint, CAP, CAS, db
from game import Game from game import Game
class QAircraftRecruitmentMenu(QGroupBox, QRecruitBehaviour): class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
def __init__(self, cp:ControlPoint, game:Game): def __init__(self, cp:ControlPoint, game:Game):
QGroupBox.__init__(self, "Recruitment") QFrame.__init__(self)
self.cp = cp self.cp = cp
self.game = game self.game = game
@ -25,13 +26,14 @@ class QAircraftRecruitmentMenu(QGroupBox, QRecruitBehaviour):
self.init_ui() self.init_ui()
def init_ui(self): def init_ui(self):
layout = QVBoxLayout() main_layout = QVBoxLayout()
units = { units = {
CAP: db.find_unittype(CAP, self.game.player_name), CAP: db.find_unittype(CAP, self.game.player_name),
CAS: db.find_unittype(CAS, self.game.player_name), CAS: db.find_unittype(CAS, self.game.player_name),
} }
scroll_content = QWidget()
task_box_layout = QGridLayout() task_box_layout = QGridLayout()
row = 0 row = 0
@ -49,6 +51,11 @@ class QAircraftRecruitmentMenu(QGroupBox, QRecruitBehaviour):
stretch.addStretch() stretch.addStretch()
task_box_layout.addLayout(stretch, row, 0) task_box_layout.addLayout(stretch, row, 0)
layout.addLayout(task_box_layout) scroll_content.setLayout(task_box_layout)
layout.addStretch() scroll = QScrollArea()
self.setLayout(layout) scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setWidgetResizable(True)
scroll.setWidget(scroll_content)
main_layout.addWidget(scroll)
self.setLayout(main_layout)

View File

@ -1,4 +1,5 @@
from PySide2.QtWidgets import QVBoxLayout, QGridLayout, QGroupBox from PySide2.QtCore import Qt
from PySide2.QtWidgets import QVBoxLayout, QGridLayout, QGroupBox, QFrame, QWidget, QScrollArea
from game import Game from game import Game
from game.event import UnitsDeliveryEvent from game.event import UnitsDeliveryEvent
@ -6,10 +7,10 @@ from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
from theater import ControlPoint, PinpointStrike, db from theater import ControlPoint, PinpointStrike, db
class QArmorRecruitmentMenu(QGroupBox, QRecruitBehaviour): class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour):
def __init__(self, cp:ControlPoint, game:Game): def __init__(self, cp:ControlPoint, game:Game):
QGroupBox.__init__(self, "Recruitment") QFrame.__init__(self)
self.cp = cp self.cp = cp
self.game = game self.game = game
@ -25,13 +26,15 @@ class QArmorRecruitmentMenu(QGroupBox, QRecruitBehaviour):
self.init_ui() self.init_ui()
def init_ui(self): def init_ui(self):
layout = QVBoxLayout() main_layout = QVBoxLayout()
units = { units = {
PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name), PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name),
} }
scroll_content = QWidget()
task_box_layout = QGridLayout() task_box_layout = QGridLayout()
scroll_content.setLayout(task_box_layout)
row = 0 row = 0
for task_type in units.keys(): for task_type in units.keys():
@ -44,6 +47,11 @@ class QArmorRecruitmentMenu(QGroupBox, QRecruitBehaviour):
stretch.addStretch() stretch.addStretch()
task_box_layout.addLayout(stretch, row, 0) task_box_layout.addLayout(stretch, row, 0)
layout.addLayout(task_box_layout) scroll_content.setLayout(task_box_layout)
layout.addStretch() scroll = QScrollArea()
self.setLayout(layout) scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
scroll.setWidgetResizable(True)
scroll.setWidget(scroll_content)
main_layout.addWidget(scroll)
self.setLayout(main_layout)

View File

@ -44,6 +44,12 @@ QMenu::item:selected {
background: #435466; background: #435466;
} }
QScrollBar:horizontal {
background: #435466;
}
QScrollBar:vertical {
background: #435466;
}
QLabel{ QLabel{
font-weight:normal; font-weight:normal;
@ -73,7 +79,6 @@ QTopPanel * {
font-weight: bold; font-weight: bold;
} }
/*QPushButton*/ /*QPushButton*/
QPushButton { QPushButton {
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,stop:0 #A4B3B9, stop:1 #85989D); background: qlineargradient(x1:0, y1:0, x2:1, y2:1,stop:0 #A4B3B9, stop:1 #85989D);
@ -94,7 +99,7 @@ QPushButton[style="btn-primary"]{
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,stop:0 #A4B3B9, stop:1 #85989D); background: qlineargradient(x1:0, y1:0, x2:1, y2:1,stop:0 #A4B3B9, stop:1 #85989D);
border: 1px solid #97A9A9; border: 1px solid #97A9A9;
color:#fff; color:#fff;
padding: 6px 20px; padding: 6px;
border-radius:2px; border-radius:2px;
cursor: pointer; cursor: pointer;
font-weight:bold; font-weight:bold;
@ -110,7 +115,6 @@ QPushButton[style="btn-success"] , QPushButton[style="start-button"]{
background-color:#82A466; background-color:#82A466;
color: white; color: white;
cursor:pointer; cursor:pointer;
padding: 6px 20px;
border-radius:2px; border-radius:2px;
font-weight:bold; font-weight:bold;
text-transform:uppercase; text-transform:uppercase;
@ -124,6 +128,44 @@ QPushButton[style="btn-success"]:hover , QPushButton[style="start-button"]:hover
background:#5C863F; background:#5C863F;
} }
/* Buy button */
QPushButton[style="btn-buy"]{
background-color:#82A466;
color: white;
cursor:pointer;
border-radius:2px;
font-weight:bold;
text-transform:uppercase;
margin: 0px;
padding: 2px;
}
QPushButton[style="btn-buy"]:hover{
cursor:pointer;
background:#5C863F;
}
/* Sell button */
QPushButton[style="btn-sell"]{
background-color:#9E3232;
color: white;
cursor:pointer;
border-radius:2px;
font-weight:bold;
text-transform:uppercase;
margin: 0px;
padding: 2px;
}
QPushButton[style="btn-sell"]:hover{
cursor:pointer;
background:#D84545;
}
QPushButton[style="btn-danger"]{ QPushButton[style="btn-danger"]{
background-color:#9E3232; background-color:#9E3232;
color: white; color: white;
@ -239,10 +281,15 @@ QGroupBox {
margin:5px; margin:5px;
} }
QGroupBox[style="buy-box"]{
padding: 0px;
margin: 0px;
}
QGroupBox::title { QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
subcontrol-position: top left; /* position at the top left */ subcontrol-position: top left; /* position at the top left */
padding: 3px; padding: 5px;
color: #B7C0C6; color: #B7C0C6;
font-weight: 800; font-weight: 800;
} }
@ -275,18 +322,18 @@ QDialog{
} }
QListView { QListView {
border: none; border: 1px solid #14202B;
background-color: #14202B;
} }
/*QTabWidget*/ /*QTabWidget*/
QTabWidget::pane { /* The tab widget frame */ QTabWidget::pane { /* The tab widget frame */
border-top: 2px solid #1D2731; border: 1px solid #1D2731;
} }
QTabWidget::tab-bar { QTabWidget::tab-bar {
border: 1px solid #14202B;
} }
QTabBar::tab { QTabBar::tab {
@ -294,6 +341,7 @@ QTabBar::tab {
background: #202C3A; background: #202C3A;
border-right: 1px solid #14202B; border-right: 1px solid #14202B;
border-left: 1px solid #14202B; border-left: 1px solid #14202B;
border-top: 1px solid #14202B;
min-width: 8ex; min-width: 8ex;
padding: 6px 10px; padding: 6px 10px;
} }
@ -401,23 +449,26 @@ QHeaderView::section {
QHeaderView::section:horizontal QHeaderView::section:horizontal
{ {
border: none; /*border: none;*/
text-align:left; text-align:left;
background: #4B5B74; background: #4B5B74;
} }
QHeaderView::section:vertical QHeaderView::section:vertical
{ {
border: none; /*border: none;*/
text-align:left; text-align:left;
background: #4B5B74; background: #4B5B74;
} }
QTableWidget { QTableWidget {
gridline-color: #1D2731; gridline-color: red;
background: #4B5B74; background: #4B5B74;
} }
QTableView QTableCornerButton::section {
background: #4B5B74;
}
/*helper modifiers*/ /*helper modifiers*/
*[style="no-border"] { *[style="no-border"] {

View File

@ -42,7 +42,7 @@ QPushButton[style="start-button"]{
background-color:#699245; background-color:#699245;
color: white; color: white;
cursor:pointer; cursor:pointer;
padding: 15px 15px 15px 15px; padding: 5px 5px 5px 5px;
border-radius:5px; border-radius:5px;
} }
@ -53,6 +53,40 @@ QPushButton[style="start-button"]:hover{
cursor: pointer; cursor: pointer;
} }
/* Buy button */
QPushButton[style="btn-buy"]{
background-color:#82A466;
color: white;
cursor:pointer;
border-radius:2px;
font-weight:bold;
text-transform:uppercase;
margin: 0px;
padding: 2px;
}
QPushButton[style="btn-buy"]:hover{
cursor:pointer;
background:#5C863F;
}
/* Sell button */
QPushButton[style="btn-sell"]{
background-color:#9E3232;
color: white;
cursor:pointer;
border-radius:2px;
font-weight:bold;
text-transform:uppercase;
margin: 0px;
padding: 2px;
}
QPushButton[style="btn-sell"]:hover{
cursor:pointer;
background:#D84545;
}
QPushButton[style="btn-danger"]{ QPushButton[style="btn-danger"]{
background-color:#9E3232; background-color:#9E3232;
color: white; color: white;

BIN
resources/ui/airbase.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

BIN
resources/ui/carrier.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
resources/ui/conflict.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
resources/ui/debriefing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
resources/ui/lha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -1,17 +1,11 @@
import json import json
import logging import logging
import os import os
import re
import threading import threading
import time import time
import typing import typing
from dcs.lua import parse
from dcs.mission import Mission
from dcs.unit import UnitType
from game import db from game import db
from .persistency import base_path
DEBRIEFING_LOG_EXTENSION = "log" DEBRIEFING_LOG_EXTENSION = "log"
@ -158,9 +152,6 @@ class Debriefing:
else: else:
self.enemy_dead_buildings_dict[a.type] = 1 self.enemy_dead_buildings_dict[a.type] = 1
for destroyed_unit in self.destroyed_units:
game.add_destroyed_units(destroyed_unit)
logging.info("--------------------------------") logging.info("--------------------------------")
logging.info("Debriefing pre process results :") logging.info("Debriefing pre process results :")
logging.info("--------------------------------") logging.info("--------------------------------")
@ -172,20 +163,39 @@ class Debriefing:
logging.info(self.enemy_dead_buildings_dict) logging.info(self.enemy_dead_buildings_dict)
def _poll_new_debriefing_log(callback: typing.Callable, game): class PollDebriefingFileThread(threading.Thread):
if os.path.isfile("state.json"): """Thread class with a stop() method. The thread itself has to check
last_modified = os.path.getmtime("state.json") regularly for the stopped() condition."""
else:
last_modified = 0
while True:
if os.path.isfile("state.json") and os.path.getmtime("state.json") > last_modified:
with open("state.json", "r") as json_file:
json_data = json.load(json_file) #Debriefing.parse(os.path.join(debriefing_directory_location(), file))
debriefing = Debriefing(json_data, game)
callback(debriefing)
break
time.sleep(5)
def wait_for_debriefing(callback: typing.Callable, game): def __init__(self, callback: typing.Callable, game):
threading.Thread(target=_poll_new_debriefing_log, args=[callback, game]).start() super(PollDebriefingFileThread, self).__init__()
self._stop_event = threading.Event()
self.callback = callback
self.game = game
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
def run(self):
if os.path.isfile("state.json"):
last_modified = os.path.getmtime("state.json")
else:
last_modified = 0
while not self.stopped():
if os.path.isfile("state.json") and os.path.getmtime("state.json") > last_modified:
with open("state.json", "r") as json_file:
json_data = json.load(json_file)
debriefing = Debriefing(json_data, self.game)
self.callback(debriefing)
break
time.sleep(5)
def wait_for_debriefing(callback: typing.Callable, game)->PollDebriefingFileThread:
thread = PollDebriefingFileThread(callback, game)
thread.start()
return thread

View File

@ -4,11 +4,12 @@ import pickle
import shutil import shutil
_dcs_saved_game_folder = None # type: str _dcs_saved_game_folder = None # type: str
_file_abs_path = None
def setup(user_folder: str): def setup(user_folder: str):
global _dcs_saved_game_folder global _dcs_saved_game_folder
_dcs_saved_game_folder = user_folder _dcs_saved_game_folder = user_folder
_file_abs_path = os.path.join(base_path(), "liberation_save")
def base_path() -> str: def base_path() -> str: