From 17c40234e974a7182fb14e342f6674efe4ca3deb Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 25 Dec 2020 03:19:20 -0800 Subject: [PATCH] Add basic intel window. Currently only shows the enemy's economic information. https://github.com/Khopa/dcs_liberation/issues/658 (cherry picked from commit 1d76ee4871fef779ea96bd39a4b568f40607d4cf) --- game/game.py | 22 +---- game/income.py | 64 +++++++++++++ game/theater/conflicttheater.py | 9 +- qt_ui/widgets/QBudgetBox.py | 3 +- qt_ui/widgets/QIntelBox.py | 19 +++- qt_ui/windows/finances/QFinancesMenu.py | 114 ++++++++++++------------ qt_ui/windows/intel.py | 44 +++++++++ 7 files changed, 197 insertions(+), 78 deletions(-) create mode 100644 game/income.py create mode 100644 qt_ui/windows/intel.py diff --git a/game/game.py b/game/game.py index 4ec1f980..516095f9 100644 --- a/game/game.py +++ b/game/game.py @@ -12,7 +12,6 @@ from dcs.task import CAP, CAS, PinpointStrike from dcs.vehicles import AirDefence from game import db -from game.db import PLAYER_BUDGET_BASE, REWARDS from game.inventory import GlobalAircraftInventory from game.models.game_stats import GameStats from game.plugins import LuaPluginManager @@ -27,6 +26,7 @@ from .debriefing import Debriefing from .event.event import Event, UnitsDeliveryEvent from .event.frontlineattack import FrontlineAttackEvent from .factions.faction import Faction +from .income import Income from .infos.information import Information from .procurement import ProcurementAi from .settings import Settings @@ -167,30 +167,14 @@ class Game: front_line.control_point_a, front_line.control_point_b) - @property - def budget_reward_amount(self) -> int: - reward = PLAYER_BUDGET_BASE * len(self.theater.player_points()) - for cp in self.theater.player_points(): - for g in cp.ground_objects: - if g.category in REWARDS.keys() and not g.is_dead: - reward += REWARDS[g.category] - return int(reward * self.settings.player_income_multiplier) - def process_player_income(self): - self.budget += self.budget_reward_amount + self.budget += Income(self, player=True).total def process_enemy_income(self): # TODO: Clean up save compat. if not hasattr(self, "enemy_budget"): self.enemy_budget = 0 - - production = 0.0 - for enemy_point in self.theater.enemy_points(): - for g in enemy_point.ground_objects: - if g.category in REWARDS.keys() and not g.is_dead: - production = production + REWARDS[g.category] - - self.enemy_budget += production * self.settings.enemy_income_multiplier + self.enemy_budget += Income(self, player=False).total def units_delivery_event(self, to_cp: ControlPoint) -> UnitsDeliveryEvent: event = UnitsDeliveryEvent(attacker_name=self.player_name, diff --git a/game/income.py b/game/income.py new file mode 100644 index 00000000..b3fe5ab5 --- /dev/null +++ b/game/income.py @@ -0,0 +1,64 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING + +from game.db import PLAYER_BUDGET_BASE, REWARDS +from game.theater import ControlPoint + +if TYPE_CHECKING: + from game import Game + + +@dataclass(frozen=True) +class BuildingIncome: + name: str + category: str + number: int + income_per_building: int + + @property + def income(self) -> int: + return self.number * self.income_per_building + + +@dataclass(frozen=True) +class ControlPointIncome: + control_point: ControlPoint + income: int + + +class Income: + def __init__(self, game: Game, player: bool) -> None: + if player: + self.multiplier = game.settings.player_income_multiplier + else: + self.multiplier = game.settings.enemy_income_multiplier + self.control_points = [] + self.buildings = [] + + self.income_per_base = PLAYER_BUDGET_BASE if player else 0 + + names = set() + for cp in game.theater.control_points_for(player): + self.control_points.append( + ControlPointIncome(cp, self.income_per_base)) + for tgo in cp.ground_objects: + names.add(tgo.obj_name) + + for name in names: + count = 0 + tgos = game.theater.find_ground_objects_by_obj_name(name) + category = tgos[0].category + if category not in REWARDS: + continue + for tgo in tgos: + if not tgo.is_dead: + count += 1 + self.buildings.append(BuildingIncome(name, category, count, + REWARDS[category])) + + self.from_bases = sum(cp.income for cp in self.control_points) + self.total_buildings = sum(b.income for b in self.buildings) + self.total = ((self.total_buildings + self.from_bases) * + self.multiplier) diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py index 4e180e7a..aef2dd40 100644 --- a/game/theater/conflicttheater.py +++ b/game/theater/conflicttheater.py @@ -503,8 +503,13 @@ class ConflictTheater: ) return new_point + def control_points_for(self, player: bool) -> Iterator[ControlPoint]: + for point in self.controlpoints: + if point.captured == player: + yield point + def player_points(self) -> List[ControlPoint]: - return [point for point in self.controlpoints if point.captured] + return list(self.control_points_for(player=True)) def conflicts(self, from_player=True) -> Iterator[FrontLine]: for cp in [x for x in self.controlpoints if x.captured == from_player]: @@ -512,7 +517,7 @@ class ConflictTheater: yield FrontLine(cp, connected_point, self) def enemy_points(self) -> List[ControlPoint]: - return [point for point in self.controlpoints if not point.captured] + return list(self.control_points_for(player=False)) def closest_control_point(self, point: Point) -> ControlPoint: closest = self.controlpoints[0] diff --git a/qt_ui/widgets/QBudgetBox.py b/qt_ui/widgets/QBudgetBox.py index ad1d66a4..f732b685 100644 --- a/qt_ui/widgets/QBudgetBox.py +++ b/qt_ui/widgets/QBudgetBox.py @@ -1,6 +1,7 @@ from PySide2.QtWidgets import QLabel, QHBoxLayout, QGroupBox, QPushButton import qt_ui.uiconstants as CONST +from game.income import Income from qt_ui.windows.finances.QFinancesMenu import QFinancesMenu @@ -41,7 +42,7 @@ class QBudgetBox(QGroupBox): return self.game = game - self.setBudget(self.game.budget, self.game.budget_reward_amount) + self.setBudget(self.game.budget, Income(self.game, player=True).total) self.finances.setEnabled(True) def openFinances(self): diff --git a/qt_ui/widgets/QIntelBox.py b/qt_ui/widgets/QIntelBox.py index 5953e4bf..639e0afa 100644 --- a/qt_ui/widgets/QIntelBox.py +++ b/qt_ui/widgets/QIntelBox.py @@ -1,8 +1,15 @@ from typing import Optional -from PySide2.QtWidgets import QGroupBox, QHBoxLayout, QLabel, QVBoxLayout +from PySide2.QtWidgets import ( + QGroupBox, + QHBoxLayout, + QLabel, + QPushButton, + QVBoxLayout, +) from game import Game +from qt_ui.windows.intel import IntelWindow class QIntelBox(QGroupBox): @@ -21,8 +28,14 @@ class QIntelBox(QGroupBox): self.total_ground_forces = QLabel() summary.addWidget(self.total_ground_forces) + details = QPushButton("Details") + columns.addWidget(details) + details.clicked.connect(self.open_details_window) + self.update_summary() + self.details_window: Optional[IntelWindow] = None + def set_game(self, game: Optional[Game]) -> None: self.game = game self.update_summary() @@ -38,3 +51,7 @@ class QIntelBox(QGroupBox): self.total_aircraft.setText(f"Total enemy aircraft: {aircraft}") self.total_ground_forces.setText( f"Total enemy ground units: {ground_units}") + + def open_details_window(self) -> None: + self.details_window = IntelWindow(self.game) + self.details_window.show() diff --git a/qt_ui/windows/finances/QFinancesMenu.py b/qt_ui/windows/finances/QFinancesMenu.py index 67cd27c9..045ee09c 100644 --- a/qt_ui/windows/finances/QFinancesMenu.py +++ b/qt_ui/windows/finances/QFinancesMenu.py @@ -1,19 +1,68 @@ -from PySide2.QtWidgets import QDialog, QGridLayout, QLabel, QFrame, QSizePolicy +from PySide2.QtWidgets import ( + QDialog, + QFrame, + QGridLayout, + QLabel, + QSizePolicy, +) import qt_ui.uiconstants as CONST -from game.db import REWARDS, PLAYER_BUDGET_BASE from game.game import Game +from game.income import Income class QHorizontalSeparationLine(QFrame): - def __init__(self): - super().__init__() - self.setMinimumWidth(1) - self.setFixedHeight(20) - self.setFrameShape(QFrame.HLine) - self.setFrameShadow(QFrame.Sunken) - self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) + def __init__(self): + super().__init__() + self.setMinimumWidth(1) + self.setFixedHeight(20) + self.setFrameShape(QFrame.HLine) + self.setFrameShadow(QFrame.Sunken) + self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) + + +class FinancesLayout(QGridLayout): + def __init__(self, game: Game, player: bool) -> None: + super().__init__() + + income = Income(game, player) + + self.addWidget(QLabel("Control Points"), 0, 0) + self.addWidget(QLabel( + f"{len(income.control_points)} bases x {income.income_per_base}M"), + 0, 1) + self.addWidget(QLabel(f"{income.from_bases}M"), 0, 2) + + self.addWidget(QHorizontalSeparationLine(), 1, 0, 1, 3) + + buildings = reversed(sorted(income.buildings, key=lambda b: b.income)) + row = 2 + for row, building in enumerate(buildings, row): + self.addWidget( + QLabel(f"{building.category.upper()} [{building.name}]"), + row, 0) + self.addWidget(QLabel( + f"{building.number} buildings x {building.income_per_building}M"), + row, 1) + rlabel = QLabel(f"{building.income}M") + rlabel.setProperty("style", "green") + self.addWidget(rlabel, row, 2) + + self.addWidget(QHorizontalSeparationLine(), row + 1, 0, 1, 3) + self.addWidget(QLabel( + f"Income multiplier: {income.multiplier:.1f}"), + row + 2, 1 + ) + self.addWidget(QLabel(f"{income.total}M"), row + 2, 2) + + if player: + budget = game.budget + else: + budget = game.enemy_budget + self.addWidget(QLabel(f"Balance"), row + 3, 1) + self.addWidget(QLabel(f"{budget}M"), row + 3, 2) + class QFinancesMenu(QDialog): @@ -26,49 +75,4 @@ class QFinancesMenu(QDialog): self.setWindowIcon(CONST.ICONS["Money"]) self.setMinimumSize(450, 200) - reward = PLAYER_BUDGET_BASE * len(self.game.theater.player_points()) - layout = QGridLayout() - layout.addWidget(QLabel("Control Points"), 0, 0) - layout.addWidget(QLabel(str(len(self.game.theater.player_points())) + " bases x " + str(PLAYER_BUDGET_BASE) + "M"), 0, 1) - layout.addWidget(QLabel(str(reward) + "M"), 0, 2) - - layout.addWidget(QHorizontalSeparationLine(), 1, 0, 1, 3) - - i = 2 - for cp in self.game.theater.player_points(): - obj_names = [] - [obj_names.append(ground_object.obj_name) for ground_object in cp.ground_objects if ground_object.obj_name not in obj_names] - for obj_name in obj_names: - reward = 0 - g = None - cat = None - number = 0 - for ground_object in cp.ground_objects: - if ground_object.obj_name != obj_name or ground_object.is_dead: - continue - else: - if g is None: - g = ground_object - cat = g.category - if cat in REWARDS.keys(): - number = number + 1 - reward += REWARDS[cat] - - if g is not None and cat in REWARDS.keys(): - layout.addWidget(QLabel("" + g.category.upper() + " [" + obj_name + "]"), i, 0) - layout.addWidget(QLabel(str(number) + " buildings x " + str(REWARDS[cat]) + "M"), i, 1) - rlabel = QLabel(str(reward) + "M") - rlabel.setProperty("style", "green") - layout.addWidget(rlabel, i, 2) - i = i + 1 - - self.setLayout(layout) - - layout.addWidget(QHorizontalSeparationLine(), i + 1, 0, 1, 3) - layout.addWidget(QLabel( - f"Income multiplier: {game.settings.player_income_multiplier:.1f}"), - i + 2, 1 - ) - layout.addWidget( - QLabel("" + str(self.game.budget_reward_amount) + "M "), - i + 2, 2) + self.setLayout(FinancesLayout(game, player=True)) diff --git a/qt_ui/windows/intel.py b/qt_ui/windows/intel.py new file mode 100644 index 00000000..95c712fd --- /dev/null +++ b/qt_ui/windows/intel.py @@ -0,0 +1,44 @@ +from PySide2.QtWidgets import ( + QDialog, + QGroupBox, + QScrollArea, QVBoxLayout, QWidget, +) + +from game.game import Game +from qt_ui.uiconstants import ICONS +from qt_ui.windows.finances.QFinancesMenu import FinancesLayout + + +class EconomyIntelBox(QGroupBox): + def __init__(self, game: Game) -> None: + super().__init__("Economy") + + widget = QWidget() + scroll_area = QScrollArea() + scroll_area.setWidgetResizable(True) + scroll_area.setWidget(widget) + + scrolling_layout = QVBoxLayout() + widget.setLayout(scrolling_layout) + + self.setLayout(QVBoxLayout()) + self.layout().addWidget(scroll_area) + + scrolling_layout.addLayout(FinancesLayout(game, player=False)) + + +class IntelWindow(QDialog): + + def __init__(self, game: Game): + super().__init__() + + self.game = game + self.setModal(True) + self.setWindowTitle("Intelligence") + self.setWindowIcon(ICONS["Statistics"]) + self.setMinimumSize(600, 250) + + layout = QVBoxLayout() + self.setLayout(layout) + + layout.addWidget(EconomyIntelBox(game))