Multiple WIP changes on UI / Submit manually debriefing.
10
changelog.md
@ -2,7 +2,7 @@
|
||||
|
||||
## Features/Improvements :
|
||||
* **[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
|
||||
* **[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
|
||||
@ -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]** 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]** 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 :
|
||||
* **[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 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]** 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
|
||||
* **[Performance]** Tuned the culling setting so that you cannot run into situation where no flights are generated
|
||||
* **[Mission Generator]** Fixed radio frequency for german WW2 warbirds
|
||||
* **[Performance]** Tuned the culling setting so that you cannot run into situation where no AI flights are generated
|
||||
|
||||
* **[Other]** Application doesn't gracefully exit.
|
||||
* **[Other]** Other minor fixes
|
||||
|
||||
# 2.0 RC 9
|
||||
|
||||
@ -1048,6 +1048,8 @@ def find_infantry(country_name: str) -> typing.List[UnitType]:
|
||||
def unit_type_name(unit_type) -> str:
|
||||
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:
|
||||
if name in vehicle_map:
|
||||
|
||||
@ -253,6 +253,12 @@ class Event:
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# Destroyed units carcass
|
||||
# -------------------------
|
||||
|
||||
for destroyed_unit in debriefing.destroyed_units:
|
||||
self.game.add_destroyed_units(destroyed_unit)
|
||||
|
||||
# -----------------------------------
|
||||
# Compute damage to bases
|
||||
for cp in self.game.theater.player_points():
|
||||
|
||||
@ -125,7 +125,11 @@ class Operation:
|
||||
|
||||
# Generate destroyed units
|
||||
for d in self.game.get_destroyed_units():
|
||||
try:
|
||||
utype = db.unit_type_from_name(d["type"])
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
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:
|
||||
self.current_mission.static_group(
|
||||
@ -138,6 +142,7 @@ class Operation:
|
||||
dead=True,
|
||||
)
|
||||
|
||||
|
||||
# Air Support (Tanker & Awacs)
|
||||
self.airsupportgen.generate(self.is_awacs_enabled)
|
||||
|
||||
|
||||
45
gen/armor.py
@ -1,5 +1,5 @@
|
||||
from dcs.action import AITaskPush
|
||||
from dcs.condition import TimeAfter
|
||||
from dcs.action import AITaskPush, AITaskSet
|
||||
from dcs.condition import TimeAfter, UnitDamaged, Or
|
||||
from dcs.task import *
|
||||
from dcs.triggers import TriggerOnce, Event
|
||||
|
||||
@ -155,14 +155,53 @@ class GroundConflictGenerator:
|
||||
target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups)
|
||||
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_trigger = TriggerOnce(Event.NoEvent,
|
||||
"ArtilleryFireTask #" + str(dcs_group.id))
|
||||
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)))
|
||||
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]:
|
||||
if stance == CombatStance.AGGRESIVE:
|
||||
# Attack nearest enemy if any
|
||||
|
||||
@ -56,7 +56,7 @@ class FlightPlanner:
|
||||
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
|
||||
#self.commision_interceptors()
|
||||
self.commision_interceptors()
|
||||
|
||||
# Then some CAP patrol for the next 2 hours
|
||||
self.commision_cap()
|
||||
@ -106,6 +106,7 @@ class FlightPlanner:
|
||||
break
|
||||
inventory[unit] = inventory[unit] - 2
|
||||
flight = Flight(unit, 2, self.from_cp, FlightType.INTERCEPTION)
|
||||
flight.scheduled_in = 1
|
||||
flight.points = []
|
||||
|
||||
self.interceptor_flights.append(flight)
|
||||
|
||||
@ -83,6 +83,5 @@ if __name__ == "__main__":
|
||||
logging.info("Attempt to restore original mission scripting file")
|
||||
liberation_install.restore_original_mission_scripting()
|
||||
logging.info("QT process exited with code : " + str(qt_execution_code))
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
|
||||
@ -5,7 +5,6 @@ from PySide2.QtWidgets import QGraphicsRectItem, QGraphicsSceneHoverEvent, QGrap
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
from game import Game
|
||||
from qt_ui.windows.basemenu.QBaseMenu import QBaseMenu
|
||||
from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2
|
||||
from theater import ControlPoint, db
|
||||
|
||||
|
||||
@ -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 game.game import Event, db, Game
|
||||
@ -25,14 +25,21 @@ class QDebriefingWindow(QDialog):
|
||||
|
||||
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):
|
||||
title = QLabel("<b>Operation Succesfull !</b>")
|
||||
title.setProperty("style", "title-success")
|
||||
else:
|
||||
title = QLabel("<b>Operation failed !</b>")
|
||||
title.setProperty("style", "title-danger")
|
||||
# Result
|
||||
#if self.gameEvent.is_successfull(self.debriefing):
|
||||
# title = QLabel("<b>Operation end !</b>")
|
||||
# title.setProperty("style", "title-success")
|
||||
#else:
|
||||
# title = QLabel("<b>Operation end !</b>")
|
||||
# title.setProperty("style", "title-danger")
|
||||
title = QLabel("<b>Casualty report</b>")
|
||||
self.layout.addWidget(title)
|
||||
|
||||
# Player lost units
|
||||
|
||||
@ -5,7 +5,7 @@ import webbrowser
|
||||
from PySide2.QtCore import Qt
|
||||
from PySide2.QtGui import QIcon
|
||||
from PySide2.QtWidgets import QWidget, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \
|
||||
QSplitter
|
||||
QSplitter, QFileDialog
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
from game import Game
|
||||
@ -73,10 +73,18 @@ class QLiberationWindow(QMainWindow):
|
||||
self.newGameAction.setIcon(QIcon(CONST.ICONS["New"]))
|
||||
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.setIcon(QIcon(CONST.ICONS["Save"]))
|
||||
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.setIcon(QIcon.fromTheme("help-about"))
|
||||
self.showAboutDialogAction.triggered.connect(self.showAboutDialog)
|
||||
@ -88,7 +96,7 @@ class QLiberationWindow(QMainWindow):
|
||||
def initToolbar(self):
|
||||
self.tool_bar = self.addToolBar("File")
|
||||
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)
|
||||
|
||||
def initMenuBar(self):
|
||||
@ -96,7 +104,7 @@ class QLiberationWindow(QMainWindow):
|
||||
|
||||
file_menu = self.menu.addMenu("File")
|
||||
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.addSeparator()
|
||||
file_menu.addAction(self.showLiberationPrefDialogAction)
|
||||
@ -163,11 +171,17 @@ class QLiberationWindow(QMainWindow):
|
||||
wizard.show()
|
||||
wizard.accepted.connect(lambda: self.onGameGenerated(wizard.generatedGame))
|
||||
|
||||
def openFile(self):
|
||||
file = str(QFileDialog.getOpenFileName(self, "Select game file to open"))
|
||||
|
||||
def saveGame(self):
|
||||
logging.info("Saving game")
|
||||
persistency.save_game(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):
|
||||
logging.info("On Game generated")
|
||||
self.game = game
|
||||
|
||||
@ -1,15 +1,18 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
from PySide2 import QtCore
|
||||
from PySide2.QtCore import QObject, Signal
|
||||
from PySide2.QtGui import QMovie, QIcon
|
||||
from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout, QGroupBox, QGridLayout, QPushButton
|
||||
from PySide2.QtCore import QObject, Signal, Qt
|
||||
from PySide2.QtGui import QMovie, QIcon, QPixmap
|
||||
from PySide2.QtWidgets import QLabel, QDialog, QGroupBox, QGridLayout, QPushButton, QFileDialog, QMessageBox, QTextEdit, \
|
||||
QHBoxLayout
|
||||
|
||||
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 DebriefingFileWrittenSignal(QObject):
|
||||
|
||||
instance = None
|
||||
@ -38,40 +41,63 @@ class QWaitingForMissionResultWindow(QDialog):
|
||||
self.setWindowTitle("Waiting for mission completion.")
|
||||
self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False)
|
||||
self.setWindowIcon(QIcon("./resources/icon.png"))
|
||||
self.setMinimumHeight(570)
|
||||
|
||||
self.initUi()
|
||||
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):
|
||||
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.addWidget(QLabel("<b>You are clear for takeoff</b>"), 1, 0)
|
||||
self.gridLayout.addWidget(QLabel(""), 2, 0)
|
||||
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)
|
||||
self.gridLayout.addWidget(QLabel("<i>liberation_nextturn</i>"), 5, 0)
|
||||
self.gridLayout.addWidget(QLabel("Then once the mission is loaded in ME, in menu \"Flight\", click on FLY Mission to launch"), 6, 0)
|
||||
self.gridLayout.addWidget(QLabel(""), 7, 0)
|
||||
self.gridLayout.addWidget(QLabel("<h2>For Multiplayer :</h2>"), 8, 0)
|
||||
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)
|
||||
self.gridLayout.addWidget(QLabel("Click on File/Save. Then exit the mission editor, and go to Multiplayer."), 11, 0)
|
||||
self.gridLayout.addWidget(QLabel("Then host a server with the mission, and tell your friends to join !"), 12, 0)
|
||||
self.gridLayout.addWidget(QLabel("(The step in the mission editor is important, and fix a game breaking bug.)"), 13, 0)
|
||||
self.gridLayout.addWidget(QLabel(""), 14, 0)
|
||||
TEXT = "" + \
|
||||
"<b>You are clear for takeoff</b>" + \
|
||||
"" + \
|
||||
"<h2>For Singleplayer :</h2>\n" + \
|
||||
"In DCS, open the Mission Editor, and load the file : \n" + \
|
||||
"<i>liberation_nextturn</i>\n" + \
|
||||
"<p>Then once the mission is loaded in ME, in menu \"Flight\",\n" + \
|
||||
"click on FLY Mission to launch.</p>\n" + \
|
||||
"" + \
|
||||
"<h2>For Multiplayer :</h2>" + \
|
||||
"In DCS, open the Mission Editor, and load the file : " + \
|
||||
"<i>liberation_nextturn</i>" + \
|
||||
"<p>Click on File/Save. Then exit the mission editor, and go to Multiplayer.</p>" + \
|
||||
"<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.setAlignment(QtCore.Qt.AlignCenter)
|
||||
progressBar = QMovie("./resources/ui/loader.gif")
|
||||
progress.setMovie(progressBar)
|
||||
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)
|
||||
progress_bar = QMovie("./resources/ui/loader.gif")
|
||||
progress.setMovie(progress_bar)
|
||||
|
||||
progressBar.start()
|
||||
self.layout.addLayout(self.gridLayout,0,0)
|
||||
self.actions = QGroupBox("Actions :")
|
||||
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)
|
||||
|
||||
def updateLayout(self, debriefing):
|
||||
@ -100,6 +126,7 @@ class QWaitingForMissionResultWindow(QDialog):
|
||||
|
||||
if not debriefing.mission_ended:
|
||||
self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"), 1, 0)
|
||||
self.gridLayout.addWidget(self.actions, 2, 0)
|
||||
else:
|
||||
#self.gridLayout.addWidget(QLabel("<b>Mission is over !</b>"), 1, 0)
|
||||
proceed = QPushButton("Accept results")
|
||||
@ -111,7 +138,9 @@ class QWaitingForMissionResultWindow(QDialog):
|
||||
print("On Debriefing update")
|
||||
print(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):
|
||||
self.game.finish_event(event=self.gameEvent, debriefing=debriefing)
|
||||
@ -122,3 +151,28 @@ class QWaitingForMissionResultWindow(QDialog):
|
||||
|
||||
def debriefing_directory_location(self) -> str:
|
||||
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
|
||||
|
||||
|
||||
@ -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)
|
||||
@ -1,5 +1,5 @@
|
||||
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 game import Game
|
||||
@ -35,6 +35,8 @@ class QBaseMenu2(QDialog):
|
||||
|
||||
self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
||||
self.setMinimumSize(300, 200)
|
||||
self.setMinimumWidth(680)
|
||||
self.setMaximumWidth(680)
|
||||
self.setModal(True)
|
||||
self.initUi()
|
||||
|
||||
@ -46,6 +48,11 @@ class QBaseMenu2(QDialog):
|
||||
self.topLayoutWidget = QWidget()
|
||||
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.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||
title.setProperty("style", "base-title")
|
||||
@ -59,10 +66,20 @@ class QBaseMenu2(QDialog):
|
||||
self.topLayoutWidget.setLayout(self.topLayout)
|
||||
|
||||
self.mainLayout = QGridLayout()
|
||||
self.mainLayout.addWidget(self.topLayoutWidget, 0, 0)
|
||||
self.mainLayout.addWidget(self.qbase_menu_tab, 1, 0)
|
||||
self.mainLayout.addWidget(header, 0, 0)
|
||||
self.mainLayout.addWidget(self.topLayoutWidget, 1, 0)
|
||||
self.mainLayout.addWidget(self.qbase_menu_tab, 2, 0)
|
||||
|
||||
self.setLayout(self.mainLayout)
|
||||
|
||||
def closeEvent(self, closeEvent:QCloseEvent):
|
||||
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"
|
||||
@ -1,5 +1,5 @@
|
||||
from PySide2.QtWidgets import QLabel, QPushButton, \
|
||||
QSizePolicy, QSpacerItem
|
||||
QSizePolicy, QSpacerItem, QGroupBox, QHBoxLayout
|
||||
from dcs.unittype import UnitType
|
||||
|
||||
from theater import db
|
||||
@ -19,55 +19,75 @@ class QRecruitBehaviour:
|
||||
|
||||
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)
|
||||
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))
|
||||
|
||||
existing_units = QLabel(str(existing_units))
|
||||
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))
|
||||
|
||||
self.existing_units_labels[unit_type] = existing_units
|
||||
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))
|
||||
|
||||
buysell = QGroupBox()
|
||||
buysell.setProperty("style", "buy-box")
|
||||
buysell.setMaximumHeight(36)
|
||||
buysell.setMinimumHeight(36)
|
||||
buysellayout = QHBoxLayout()
|
||||
buysell.setLayout(buysellayout)
|
||||
|
||||
buy = QPushButton("+")
|
||||
buy.setProperty("style", "btn-success")
|
||||
buy.setMinimumSize(24, 24)
|
||||
buy.setProperty("style", "btn-buy")
|
||||
buy.setMinimumSize(16, 16)
|
||||
buy.setMaximumSize(16, 16)
|
||||
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.setProperty("style", "btn-sell")
|
||||
sell.setMinimumSize(16, 16)
|
||||
sell.setMaximumSize(16, 16)
|
||||
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.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), row, 3)
|
||||
layout.addWidget(price, row, 4)
|
||||
|
||||
layout.addWidget(sell, row, 5)
|
||||
layout.addWidget(amount_bought, row, 6)
|
||||
layout.addWidget(buy, row, 7)
|
||||
existLayout.addWidget(unitName)
|
||||
existLayout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum))
|
||||
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
|
||||
|
||||
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"
|
||||
))
|
||||
|
||||
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)
|
||||
))
|
||||
|
||||
|
||||
@ -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 qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
|
||||
@ -6,10 +7,10 @@ from theater import ControlPoint, CAP, CAS, db
|
||||
from game import Game
|
||||
|
||||
|
||||
class QAircraftRecruitmentMenu(QGroupBox, QRecruitBehaviour):
|
||||
class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
|
||||
|
||||
def __init__(self, cp:ControlPoint, game:Game):
|
||||
QGroupBox.__init__(self, "Recruitment")
|
||||
QFrame.__init__(self)
|
||||
self.cp = cp
|
||||
self.game = game
|
||||
|
||||
@ -25,13 +26,14 @@ class QAircraftRecruitmentMenu(QGroupBox, QRecruitBehaviour):
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
main_layout = QVBoxLayout()
|
||||
|
||||
units = {
|
||||
CAP: db.find_unittype(CAP, self.game.player_name),
|
||||
CAS: db.find_unittype(CAS, self.game.player_name),
|
||||
}
|
||||
|
||||
scroll_content = QWidget()
|
||||
task_box_layout = QGridLayout()
|
||||
row = 0
|
||||
|
||||
@ -49,6 +51,11 @@ class QAircraftRecruitmentMenu(QGroupBox, QRecruitBehaviour):
|
||||
stretch.addStretch()
|
||||
task_box_layout.addLayout(stretch, row, 0)
|
||||
|
||||
layout.addLayout(task_box_layout)
|
||||
layout.addStretch()
|
||||
self.setLayout(layout)
|
||||
scroll_content.setLayout(task_box_layout)
|
||||
scroll = QScrollArea()
|
||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setWidget(scroll_content)
|
||||
main_layout.addWidget(scroll)
|
||||
self.setLayout(main_layout)
|
||||
|
||||
@ -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.event import UnitsDeliveryEvent
|
||||
@ -6,10 +7,10 @@ from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
|
||||
from theater import ControlPoint, PinpointStrike, db
|
||||
|
||||
|
||||
class QArmorRecruitmentMenu(QGroupBox, QRecruitBehaviour):
|
||||
class QArmorRecruitmentMenu(QFrame, QRecruitBehaviour):
|
||||
|
||||
def __init__(self, cp:ControlPoint, game:Game):
|
||||
QGroupBox.__init__(self, "Recruitment")
|
||||
QFrame.__init__(self)
|
||||
self.cp = cp
|
||||
self.game = game
|
||||
|
||||
@ -25,13 +26,15 @@ class QArmorRecruitmentMenu(QGroupBox, QRecruitBehaviour):
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
layout = QVBoxLayout()
|
||||
main_layout = QVBoxLayout()
|
||||
|
||||
units = {
|
||||
PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name),
|
||||
}
|
||||
|
||||
scroll_content = QWidget()
|
||||
task_box_layout = QGridLayout()
|
||||
scroll_content.setLayout(task_box_layout)
|
||||
row = 0
|
||||
|
||||
for task_type in units.keys():
|
||||
@ -44,6 +47,11 @@ class QArmorRecruitmentMenu(QGroupBox, QRecruitBehaviour):
|
||||
stretch.addStretch()
|
||||
task_box_layout.addLayout(stretch, row, 0)
|
||||
|
||||
layout.addLayout(task_box_layout)
|
||||
layout.addStretch()
|
||||
self.setLayout(layout)
|
||||
scroll_content.setLayout(task_box_layout)
|
||||
scroll = QScrollArea()
|
||||
scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
|
||||
scroll.setWidgetResizable(True)
|
||||
scroll.setWidget(scroll_content)
|
||||
main_layout.addWidget(scroll)
|
||||
self.setLayout(main_layout)
|
||||
@ -44,6 +44,12 @@ QMenu::item:selected {
|
||||
background: #435466;
|
||||
}
|
||||
|
||||
QScrollBar:horizontal {
|
||||
background: #435466;
|
||||
}
|
||||
QScrollBar:vertical {
|
||||
background: #435466;
|
||||
}
|
||||
|
||||
QLabel{
|
||||
font-weight:normal;
|
||||
@ -73,7 +79,6 @@ QTopPanel * {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
/*QPushButton*/
|
||||
QPushButton {
|
||||
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);
|
||||
border: 1px solid #97A9A9;
|
||||
color:#fff;
|
||||
padding: 6px 20px;
|
||||
padding: 6px;
|
||||
border-radius:2px;
|
||||
cursor: pointer;
|
||||
font-weight:bold;
|
||||
@ -110,7 +115,6 @@ QPushButton[style="btn-success"] , QPushButton[style="start-button"]{
|
||||
background-color:#82A466;
|
||||
color: white;
|
||||
cursor:pointer;
|
||||
padding: 6px 20px;
|
||||
border-radius:2px;
|
||||
font-weight:bold;
|
||||
text-transform:uppercase;
|
||||
@ -124,6 +128,44 @@ QPushButton[style="btn-success"]:hover , QPushButton[style="start-button"]:hover
|
||||
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"]{
|
||||
background-color:#9E3232;
|
||||
color: white;
|
||||
@ -239,10 +281,15 @@ QGroupBox {
|
||||
margin:5px;
|
||||
}
|
||||
|
||||
QGroupBox[style="buy-box"]{
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
QGroupBox::title {
|
||||
subcontrol-origin: margin;
|
||||
subcontrol-position: top left; /* position at the top left */
|
||||
padding: 3px;
|
||||
padding: 5px;
|
||||
color: #B7C0C6;
|
||||
font-weight: 800;
|
||||
}
|
||||
@ -275,18 +322,18 @@ QDialog{
|
||||
|
||||
}
|
||||
|
||||
|
||||
QListView {
|
||||
border: none;
|
||||
border: 1px solid #14202B;
|
||||
background-color: #14202B;
|
||||
}
|
||||
|
||||
/*QTabWidget*/
|
||||
QTabWidget::pane { /* The tab widget frame */
|
||||
border-top: 2px solid #1D2731;
|
||||
border: 1px solid #1D2731;
|
||||
}
|
||||
|
||||
QTabWidget::tab-bar {
|
||||
|
||||
border: 1px solid #14202B;
|
||||
}
|
||||
|
||||
QTabBar::tab {
|
||||
@ -294,6 +341,7 @@ QTabBar::tab {
|
||||
background: #202C3A;
|
||||
border-right: 1px solid #14202B;
|
||||
border-left: 1px solid #14202B;
|
||||
border-top: 1px solid #14202B;
|
||||
min-width: 8ex;
|
||||
padding: 6px 10px;
|
||||
}
|
||||
@ -401,23 +449,26 @@ QHeaderView::section {
|
||||
|
||||
QHeaderView::section:horizontal
|
||||
{
|
||||
border: none;
|
||||
/*border: none;*/
|
||||
text-align:left;
|
||||
background: #4B5B74;
|
||||
|
||||
}
|
||||
|
||||
QHeaderView::section:vertical
|
||||
{
|
||||
border: none;
|
||||
/*border: none;*/
|
||||
text-align:left;
|
||||
background: #4B5B74;
|
||||
}
|
||||
|
||||
QTableWidget {
|
||||
gridline-color: #1D2731;
|
||||
gridline-color: red;
|
||||
background: #4B5B74;
|
||||
}
|
||||
|
||||
QTableView QTableCornerButton::section {
|
||||
background: #4B5B74;
|
||||
}
|
||||
|
||||
/*helper modifiers*/
|
||||
*[style="no-border"] {
|
||||
|
||||
@ -42,7 +42,7 @@ QPushButton[style="start-button"]{
|
||||
background-color:#699245;
|
||||
color: white;
|
||||
cursor:pointer;
|
||||
padding: 15px 15px 15px 15px;
|
||||
padding: 5px 5px 5px 5px;
|
||||
border-radius:5px;
|
||||
}
|
||||
|
||||
@ -53,6 +53,40 @@ QPushButton[style="start-button"]:hover{
|
||||
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"]{
|
||||
background-color:#9E3232;
|
||||
color: white;
|
||||
|
||||
BIN
resources/ui/airbase.png
Normal file
|
After Width: | Height: | Size: 244 KiB |
BIN
resources/ui/carrier.png
Normal file
|
After Width: | Height: | Size: 150 KiB |
BIN
resources/ui/conflict.png
Normal file
|
After Width: | Height: | Size: 117 KiB |
BIN
resources/ui/debriefing.png
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
resources/ui/lha.png
Normal file
|
After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 22 KiB |
@ -1,17 +1,11 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import threading
|
||||
import time
|
||||
import typing
|
||||
|
||||
from dcs.lua import parse
|
||||
from dcs.mission import Mission
|
||||
from dcs.unit import UnitType
|
||||
|
||||
from game import db
|
||||
from .persistency import base_path
|
||||
|
||||
DEBRIEFING_LOG_EXTENSION = "log"
|
||||
|
||||
@ -158,9 +152,6 @@ class Debriefing:
|
||||
else:
|
||||
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("Debriefing pre process results :")
|
||||
logging.info("--------------------------------")
|
||||
@ -172,20 +163,39 @@ class Debriefing:
|
||||
logging.info(self.enemy_dead_buildings_dict)
|
||||
|
||||
|
||||
def _poll_new_debriefing_log(callback: typing.Callable, game):
|
||||
class PollDebriefingFileThread(threading.Thread):
|
||||
"""Thread class with a stop() method. The thread itself has to check
|
||||
regularly for the stopped() condition."""
|
||||
|
||||
def __init__(self, callback: typing.Callable, game):
|
||||
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 True:
|
||||
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.parse(os.path.join(debriefing_directory_location(), file))
|
||||
debriefing = Debriefing(json_data, game)
|
||||
callback(debriefing)
|
||||
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):
|
||||
threading.Thread(target=_poll_new_debriefing_log, args=[callback, game]).start()
|
||||
|
||||
def wait_for_debriefing(callback: typing.Callable, game)->PollDebriefingFileThread:
|
||||
thread = PollDebriefingFileThread(callback, game)
|
||||
thread.start()
|
||||
return thread
|
||||
|
||||
|
||||
@ -4,11 +4,12 @@ import pickle
|
||||
import shutil
|
||||
|
||||
_dcs_saved_game_folder = None # type: str
|
||||
|
||||
_file_abs_path = None
|
||||
|
||||
def setup(user_folder: str):
|
||||
global _dcs_saved_game_folder
|
||||
_dcs_saved_game_folder = user_folder
|
||||
_file_abs_path = os.path.join(base_path(), "liberation_save")
|
||||
|
||||
|
||||
def base_path() -> str:
|
||||
|
||||