mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Updating the game destroys this window so we cannot continue with the calls. It worked in my initial testing, so presumably it's partly dependent on when the finalizers run. Since the windows will be destroyed there's nothing for us to actually update, so just remove that signal and the explicit close calls. Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1344
475 lines
17 KiB
Python
475 lines
17 KiB
Python
import logging
|
|
from typing import List, Optional
|
|
|
|
from PySide2 import QtCore
|
|
from PySide2.QtGui import Qt
|
|
from PySide2.QtWidgets import (
|
|
QComboBox,
|
|
QDialog,
|
|
QGridLayout,
|
|
QGroupBox,
|
|
QHBoxLayout,
|
|
QLabel,
|
|
QMessageBox,
|
|
QPushButton,
|
|
QSpinBox,
|
|
QVBoxLayout,
|
|
)
|
|
from dcs import Point
|
|
from dcs import vehicles
|
|
|
|
from game import Game
|
|
from game.data.building_data import FORTIFICATION_BUILDINGS
|
|
from game.db import REWARDS
|
|
from game.dcs.groundunittype import GroundUnitType
|
|
from game.theater import ControlPoint, TheaterGroundObject
|
|
from game.theater.theatergroundobject import (
|
|
VehicleGroupGroundObject,
|
|
SamGroundObject,
|
|
EwrGroundObject,
|
|
BuildingGroundObject,
|
|
)
|
|
from gen.defenses.armor_group_generator import generate_armor_group_of_type_and_size
|
|
from gen.sam.ewr_group_generator import get_faction_possible_ewrs_generator
|
|
from gen.sam.sam_group_generator import get_faction_possible_sams_generator
|
|
from qt_ui.uiconstants import EVENT_ICONS
|
|
from qt_ui.widgets.QBudgetBox import QBudgetBox
|
|
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
|
from qt_ui.windows.groundobject.QBuildingInfo import QBuildingInfo
|
|
|
|
|
|
class QGroundObjectMenu(QDialog):
|
|
def __init__(
|
|
self,
|
|
parent,
|
|
ground_object: TheaterGroundObject,
|
|
buildings: Optional[List[TheaterGroundObject]],
|
|
cp: ControlPoint,
|
|
game: Game,
|
|
):
|
|
super().__init__(parent)
|
|
self.setMinimumWidth(350)
|
|
self.ground_object = ground_object
|
|
if buildings is None:
|
|
self.buildings = []
|
|
else:
|
|
self.buildings = buildings
|
|
self.cp = cp
|
|
self.game = game
|
|
self.setWindowTitle("Location " + self.ground_object.obj_name)
|
|
self.setWindowIcon(EVENT_ICONS["capture"])
|
|
self.intelBox = QGroupBox("Units :")
|
|
self.buildingBox = QGroupBox("Buildings :")
|
|
self.intelLayout = QGridLayout()
|
|
self.buildingsLayout = QGridLayout()
|
|
self.sell_all_button = None
|
|
self.total_value = 0
|
|
self.init_ui()
|
|
|
|
def init_ui(self):
|
|
|
|
self.mainLayout = QVBoxLayout()
|
|
self.budget = QBudgetBox(self.game)
|
|
self.budget.setGame(self.game)
|
|
|
|
self.doLayout()
|
|
|
|
if isinstance(self.ground_object, BuildingGroundObject):
|
|
self.mainLayout.addWidget(self.buildingBox)
|
|
if self.cp.captured:
|
|
self.mainLayout.addWidget(self.financesBox)
|
|
else:
|
|
self.mainLayout.addWidget(self.intelBox)
|
|
|
|
self.actionLayout = QHBoxLayout()
|
|
|
|
self.sell_all_button = QPushButton("Disband (+" + str(self.total_value) + "M)")
|
|
self.sell_all_button.clicked.connect(self.sell_all)
|
|
self.sell_all_button.setProperty("style", "btn-danger")
|
|
|
|
self.buy_replace = QPushButton("Buy/Replace")
|
|
self.buy_replace.clicked.connect(self.buy_group)
|
|
self.buy_replace.setProperty("style", "btn-success")
|
|
|
|
if self.ground_object.purchasable:
|
|
if self.total_value > 0:
|
|
self.actionLayout.addWidget(self.sell_all_button)
|
|
self.actionLayout.addWidget(self.buy_replace)
|
|
|
|
if self.cp.captured and self.ground_object.purchasable:
|
|
self.mainLayout.addLayout(self.actionLayout)
|
|
self.setLayout(self.mainLayout)
|
|
|
|
def doLayout(self):
|
|
|
|
self.update_total_value()
|
|
self.intelBox = QGroupBox("Units :")
|
|
self.intelLayout = QGridLayout()
|
|
i = 0
|
|
for g in self.ground_object.groups:
|
|
if not hasattr(g, "units_losts"):
|
|
g.units_losts = []
|
|
for unit in g.units:
|
|
unit_display_name = unit.type
|
|
dcs_unit_type = vehicles.vehicle_map.get(unit.type)
|
|
if dcs_unit_type is not None:
|
|
# Hack: Don't know which variant is used.
|
|
try:
|
|
unit_display_name = next(
|
|
GroundUnitType.for_dcs_type(dcs_unit_type)
|
|
).name
|
|
except StopIteration:
|
|
pass
|
|
self.intelLayout.addWidget(
|
|
QLabel(
|
|
"<b>Unit #"
|
|
+ str(unit.id)
|
|
+ " - "
|
|
+ str(unit_display_name)
|
|
+ "</b>"
|
|
),
|
|
i,
|
|
0,
|
|
)
|
|
i = i + 1
|
|
|
|
for unit in g.units_losts:
|
|
dcs_unit_type = vehicles.vehicle_map.get(unit.type)
|
|
if dcs_unit_type is None:
|
|
continue
|
|
|
|
# Hack: Don't know which variant is used.
|
|
|
|
try:
|
|
unit_type = next(GroundUnitType.for_dcs_type(dcs_unit_type))
|
|
name = unit_type.name
|
|
price = unit_type.price
|
|
except StopIteration:
|
|
name = dcs_unit_type.name
|
|
price = 0
|
|
|
|
self.intelLayout.addWidget(
|
|
QLabel(f"<b>Unit #{unit.id} - {name}</b> [DEAD]"), i, 0
|
|
)
|
|
if self.cp.captured:
|
|
repair = QPushButton(f"Repair [{price}M]")
|
|
repair.setProperty("style", "btn-success")
|
|
repair.clicked.connect(
|
|
lambda u=unit, g=g, p=unit_type.price: self.repair_unit(g, u, p)
|
|
)
|
|
self.intelLayout.addWidget(repair, i, 1)
|
|
i = i + 1
|
|
stretch = QVBoxLayout()
|
|
stretch.addStretch()
|
|
self.intelLayout.addLayout(stretch, i, 0)
|
|
|
|
self.buildingBox = QGroupBox("Buildings :")
|
|
self.buildingsLayout = QGridLayout()
|
|
|
|
j = 0
|
|
total_income = 0
|
|
received_income = 0
|
|
for i, building in enumerate(self.buildings):
|
|
if building.dcs_identifier not in FORTIFICATION_BUILDINGS:
|
|
self.buildingsLayout.addWidget(
|
|
QBuildingInfo(building, self.ground_object), j / 3, j % 3
|
|
)
|
|
j = j + 1
|
|
|
|
if building.category in REWARDS.keys():
|
|
total_income = total_income + REWARDS[building.category]
|
|
if not building.is_dead:
|
|
received_income = received_income + REWARDS[building.category]
|
|
else:
|
|
logging.warning(building.category + " not in REWARDS")
|
|
|
|
self.financesBox = QGroupBox("Finances: ")
|
|
self.financesBoxLayout = QGridLayout()
|
|
self.financesBoxLayout.addWidget(
|
|
QLabel("Available: " + str(total_income) + "M"), 2, 1
|
|
)
|
|
self.financesBoxLayout.addWidget(
|
|
QLabel("Receiving: " + str(received_income) + "M"), 2, 2
|
|
)
|
|
|
|
self.financesBox.setLayout(self.financesBoxLayout)
|
|
self.buildingBox.setLayout(self.buildingsLayout)
|
|
self.intelBox.setLayout(self.intelLayout)
|
|
|
|
def do_refresh_layout(self):
|
|
try:
|
|
for i in range(self.mainLayout.count()):
|
|
item = self.mainLayout.itemAt(i)
|
|
if item is not None and item.widget() is not None:
|
|
item.widget().setParent(None)
|
|
self.sell_all_button.setParent(None)
|
|
self.buy_replace.setParent(None)
|
|
self.actionLayout.setParent(None)
|
|
|
|
self.doLayout()
|
|
if isinstance(self.ground_object, BuildingGroundObject):
|
|
self.mainLayout.addWidget(self.buildingBox)
|
|
else:
|
|
self.mainLayout.addWidget(self.intelBox)
|
|
|
|
self.actionLayout = QHBoxLayout()
|
|
if self.total_value > 0:
|
|
self.actionLayout.addWidget(self.sell_all_button)
|
|
self.actionLayout.addWidget(self.buy_replace)
|
|
|
|
if self.cp.captured and self.ground_object.purchasable:
|
|
self.mainLayout.addLayout(self.actionLayout)
|
|
except Exception as e:
|
|
logging.exception(e)
|
|
self.update_total_value()
|
|
|
|
def update_total_value(self):
|
|
total_value = 0
|
|
if not self.ground_object.purchasable:
|
|
return
|
|
for u in self.ground_object.units:
|
|
# Hack: Unknown variant.
|
|
unit_type = next(GroundUnitType.for_dcs_type(vehicles.vehicle_map[u.type]))
|
|
total_value += unit_type.price
|
|
if self.sell_all_button is not None:
|
|
self.sell_all_button.setText("Disband (+$" + str(self.total_value) + "M)")
|
|
self.total_value = total_value
|
|
|
|
def repair_unit(self, group, unit, price):
|
|
if self.game.budget > price:
|
|
self.game.budget -= price
|
|
group.units_losts = [u for u in group.units_losts if u.id != unit.id]
|
|
group.units.append(unit)
|
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
|
|
# Remove destroyed units in the vicinity
|
|
destroyed_units = self.game.get_destroyed_units()
|
|
for d in destroyed_units:
|
|
p = Point(d["x"], d["z"])
|
|
if p.distance_to_point(unit.position) < 15:
|
|
destroyed_units.remove(d)
|
|
logging.info("Removed destroyed units " + str(d))
|
|
logging.info("Repaired unit : " + str(unit.id) + " " + str(unit.type))
|
|
|
|
self.do_refresh_layout()
|
|
|
|
def sell_all(self):
|
|
self.update_total_value()
|
|
self.game.budget = self.game.budget + self.total_value
|
|
self.ground_object.groups = []
|
|
self.do_refresh_layout()
|
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
|
|
def buy_group(self):
|
|
self.subwindow = QBuyGroupForGroundObjectDialog(
|
|
self, self.ground_object, self.cp, self.game, self.total_value
|
|
)
|
|
self.subwindow.show()
|
|
|
|
|
|
class QBuyGroupForGroundObjectDialog(QDialog):
|
|
def __init__(
|
|
self,
|
|
parent,
|
|
ground_object: TheaterGroundObject,
|
|
cp: ControlPoint,
|
|
game: Game,
|
|
current_group_value: int,
|
|
):
|
|
super(QBuyGroupForGroundObjectDialog, self).__init__(parent)
|
|
|
|
self.setMinimumWidth(350)
|
|
self.ground_object = ground_object
|
|
self.cp = cp
|
|
self.game = game
|
|
self.current_group_value = current_group_value
|
|
|
|
self.setWindowTitle("Buy units @ " + self.ground_object.obj_name)
|
|
self.setWindowIcon(EVENT_ICONS["capture"])
|
|
|
|
self.buySamButton = QPushButton("Buy")
|
|
self.buyArmorButton = QPushButton("Buy")
|
|
self.buySamLayout = QGridLayout()
|
|
self.buyArmorLayout = QGridLayout()
|
|
self.amount = QSpinBox()
|
|
self.buyArmorCombo = QComboBox()
|
|
self.samCombo = QComboBox()
|
|
self.buySamBox = QGroupBox("Buy SAM site :")
|
|
self.buyArmorBox = QGroupBox("Buy defensive position :")
|
|
|
|
faction = self.game.player_faction
|
|
|
|
# Sams
|
|
|
|
possible_sams = get_faction_possible_sams_generator(faction)
|
|
for sam in possible_sams:
|
|
self.samCombo.addItem(
|
|
sam.name + " [$" + str(sam.price) + "M]", userData=sam
|
|
)
|
|
self.samCombo.currentIndexChanged.connect(self.samComboChanged)
|
|
|
|
self.buySamLayout.addWidget(QLabel("Site Type :"), 0, 0, Qt.AlignLeft)
|
|
self.buySamLayout.addWidget(self.samCombo, 0, 1, alignment=Qt.AlignRight)
|
|
self.buySamLayout.addWidget(self.buySamButton, 1, 1, alignment=Qt.AlignRight)
|
|
stretch = QVBoxLayout()
|
|
stretch.addStretch()
|
|
self.buySamLayout.addLayout(stretch, 2, 0)
|
|
|
|
self.buySamButton.clicked.connect(self.buySam)
|
|
|
|
# EWRs
|
|
|
|
buy_ewr_box = QGroupBox("Buy EWR:")
|
|
buy_ewr_layout = QGridLayout()
|
|
buy_ewr_box.setLayout(buy_ewr_layout)
|
|
|
|
buy_ewr_layout.addWidget(QLabel("Radar type:"), 0, 0, Qt.AlignLeft)
|
|
|
|
self.ewr_selector = QComboBox()
|
|
buy_ewr_layout.addWidget(self.ewr_selector, 0, 1, alignment=Qt.AlignRight)
|
|
ewr_types = get_faction_possible_ewrs_generator(faction)
|
|
for ewr_type in ewr_types:
|
|
self.ewr_selector.addItem(
|
|
f"{ewr_type.name()} [${ewr_type.price()}M]", ewr_type
|
|
)
|
|
self.ewr_selector.currentIndexChanged.connect(self.on_ewr_selection_changed)
|
|
|
|
self.buy_ewr_button = QPushButton("Buy")
|
|
self.buy_ewr_button.clicked.connect(self.buy_ewr)
|
|
buy_ewr_layout.addWidget(self.buy_ewr_button, 1, 1, alignment=Qt.AlignRight)
|
|
stretch = QVBoxLayout()
|
|
stretch.addStretch()
|
|
buy_ewr_layout.addLayout(stretch, 2, 0)
|
|
|
|
# Armored units
|
|
for unit in set(faction.ground_units):
|
|
self.buyArmorCombo.addItem(f"{unit} [${unit.price}M]", userData=unit)
|
|
self.buyArmorCombo.currentIndexChanged.connect(self.armorComboChanged)
|
|
|
|
self.amount.setMinimum(2)
|
|
self.amount.setMaximum(8)
|
|
self.amount.setValue(2)
|
|
self.amount.valueChanged.connect(self.amountComboChanged)
|
|
|
|
self.buyArmorLayout.addWidget(QLabel("Unit type :"), 0, 0, Qt.AlignLeft)
|
|
self.buyArmorLayout.addWidget(self.buyArmorCombo, 0, 1, alignment=Qt.AlignRight)
|
|
self.buyArmorLayout.addWidget(
|
|
QLabel("Group size :"), 1, 0, alignment=Qt.AlignLeft
|
|
)
|
|
self.buyArmorLayout.addWidget(self.amount, 1, 1, alignment=Qt.AlignRight)
|
|
self.buyArmorLayout.addWidget(
|
|
self.buyArmorButton, 2, 1, alignment=Qt.AlignRight
|
|
)
|
|
stretch2 = QVBoxLayout()
|
|
stretch2.addStretch()
|
|
self.buyArmorLayout.addLayout(stretch2, 3, 0)
|
|
|
|
self.buyArmorButton.clicked.connect(self.buyArmor)
|
|
|
|
# Do layout
|
|
self.buySamBox.setLayout(self.buySamLayout)
|
|
self.buyArmorBox.setLayout(self.buyArmorLayout)
|
|
|
|
self.mainLayout = QHBoxLayout()
|
|
|
|
if isinstance(self.ground_object, SamGroundObject):
|
|
self.mainLayout.addWidget(self.buySamBox)
|
|
elif isinstance(self.ground_object, VehicleGroupGroundObject):
|
|
self.mainLayout.addWidget(self.buyArmorBox)
|
|
elif isinstance(self.ground_object, EwrGroundObject):
|
|
self.mainLayout.addWidget(buy_ewr_box)
|
|
|
|
self.setLayout(self.mainLayout)
|
|
|
|
try:
|
|
self.samComboChanged(0)
|
|
self.armorComboChanged(0)
|
|
self.on_ewr_selection_changed(0)
|
|
except:
|
|
pass
|
|
|
|
def samComboChanged(self, index):
|
|
self.buySamButton.setText(
|
|
"Buy [$"
|
|
+ str(self.samCombo.itemData(index).price)
|
|
+ "M] [-$"
|
|
+ str(self.current_group_value)
|
|
+ "M]"
|
|
)
|
|
|
|
def on_ewr_selection_changed(self, index):
|
|
ewr = self.ewr_selector.itemData(index)
|
|
self.buy_ewr_button.setText(
|
|
f"Buy [${ewr.price()}M][-${self.current_group_value}M]"
|
|
)
|
|
|
|
def armorComboChanged(self, index):
|
|
unit_type = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
|
price = unit_type.price * self.amount.value()
|
|
self.buyArmorButton.setText(f"Buy [${price}M][-${self.current_group_value}M]")
|
|
|
|
def amountComboChanged(self):
|
|
unit_type = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
|
price = unit_type.price * self.amount.value()
|
|
self.buyArmorButton.setText(f"Buy [${price}M][-${self.current_group_value}M]")
|
|
|
|
def buyArmor(self):
|
|
logging.info("Buying Armor ")
|
|
utype = self.buyArmorCombo.itemData(self.buyArmorCombo.currentIndex())
|
|
price = utype.price * self.amount.value() - self.current_group_value
|
|
if price > self.game.budget:
|
|
self.error_money()
|
|
self.close()
|
|
return
|
|
else:
|
|
self.game.budget -= price
|
|
|
|
# Generate Armor
|
|
group = generate_armor_group_of_type_and_size(
|
|
self.game, self.ground_object, utype, int(self.amount.value())
|
|
)
|
|
self.ground_object.groups = [group]
|
|
|
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
|
|
def buySam(self):
|
|
sam_generator = self.samCombo.itemData(self.samCombo.currentIndex())
|
|
price = sam_generator.price - self.current_group_value
|
|
if price > self.game.budget:
|
|
self.error_money()
|
|
return
|
|
else:
|
|
self.game.budget -= price
|
|
|
|
# Generate SAM
|
|
generator = sam_generator(self.game, self.ground_object)
|
|
generator.generate()
|
|
self.ground_object.groups = list(generator.groups)
|
|
|
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
|
|
def buy_ewr(self):
|
|
ewr_generator = self.ewr_selector.itemData(self.ewr_selector.currentIndex())
|
|
price = ewr_generator.price() - self.current_group_value
|
|
if price > self.game.budget:
|
|
self.error_money()
|
|
return
|
|
else:
|
|
self.game.budget -= price
|
|
|
|
generator = ewr_generator(self.game, self.ground_object)
|
|
generator.generate()
|
|
self.ground_object.groups = [generator.vg]
|
|
|
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
|
|
|
def error_money(self):
|
|
msg = QMessageBox()
|
|
msg.setIcon(QMessageBox.Information)
|
|
msg.setText("Not enough money to buy these units !")
|
|
msg.setWindowTitle("Not enough money")
|
|
msg.setStandardButtons(QMessageBox.Ok)
|
|
msg.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
msg.exec_()
|
|
self.close()
|