diff --git a/changelog.md b/changelog.md index 7c501e91..57427840 100644 --- a/changelog.md +++ b/changelog.md @@ -15,6 +15,7 @@ * **[Factions]** Germany 1944, added Stug III and Stug IV * **[Factions]** Added factions Insurgents (Hard) with better and more weapons * **[Plugins]** [The EWRS plugin](https://github.com/Bob7heBuilder/EWRS) is now included. +* **[UI]** Added enemy intelligence summary and details window. ## Fixes: * **[Factions]** AI would never buy artillery units for the frontline - fixed diff --git a/qt_ui/widgets/QIntelBox.py b/qt_ui/widgets/QIntelBox.py index 639e0afa..3925baf3 100644 --- a/qt_ui/widgets/QIntelBox.py +++ b/qt_ui/widgets/QIntelBox.py @@ -1,32 +1,42 @@ from typing import Optional from PySide2.QtWidgets import ( + QGridLayout, QGroupBox, QHBoxLayout, QLabel, QPushButton, - QVBoxLayout, ) from game import Game +from game.income import Income from qt_ui.windows.intel import IntelWindow class QIntelBox(QGroupBox): def __init__(self, game: Game) -> None: super().__init__("Intel") + self.setProperty("style", "IntelSummary") + self.game = game columns = QHBoxLayout() self.setLayout(columns) - summary = QVBoxLayout() + summary = QGridLayout() columns.addLayout(summary) - self.total_aircraft = QLabel() - summary.addWidget(self.total_aircraft) - self.total_ground_forces = QLabel() - summary.addWidget(self.total_ground_forces) + summary.addWidget(QLabel("Air superiority:"), 0, 0) + self.air_strength = QLabel() + summary.addWidget(self.air_strength, 0, 1) + + summary.addWidget(QLabel("Front line:"), 1, 0) + self.ground_strength = QLabel() + summary.addWidget(self.ground_strength, 1, 1) + + summary.addWidget(QLabel("Economic strength:"), 2, 0) + self.economic_strength = QLabel() + summary.addWidget(self.economic_strength, 2, 1) details = QPushButton("Details") columns.addWidget(details) @@ -40,17 +50,57 @@ class QIntelBox(QGroupBox): self.game = game self.update_summary() + @staticmethod + def forces_strength_text(own: int, enemy: int) -> str: + if not enemy: + return "enemy eliminated" + + ratio = own / enemy + if ratio < 0.6: + return "outnumbered" + if ratio < 0.8: + return "slightly outnumbered" + if ratio < 1.2: + return "evenly matched" + if ratio < 1.4: + return "slight advantage" + return "strong advantage" + + def economic_strength_text(self) -> str: + assert self.game is not None + own = Income(self.game, player=True).total + enemy = Income(self.game, player=False).total + + if not enemy: + return "enemy economy ruined" + + ratio = own / enemy + if ratio < 0.6: + return "strong disadvantage" + if ratio < 0.8: + return "slight disadvantage" + if ratio < 1.2: + return "evenly matched" + if ratio < 1.4: + return "slight advantage" + return "strong advantage" + def update_summary(self) -> None: if self.game is None: - aircraft = 0 - ground_units = 0 - else: - data = self.game.game_stats.data_per_turn[-1] - aircraft = data.enemy_units.aircraft_count - ground_units = data.enemy_units.vehicles_count - self.total_aircraft.setText(f"Total enemy aircraft: {aircraft}") - self.total_ground_forces.setText( - f"Total enemy ground units: {ground_units}") + self.air_strength.setText("no data") + self.ground_strength.setText("no data") + self.economic_strength.setText("no data") + return + + data = self.game.game_stats.data_per_turn[-1] + + self.air_strength.setText(self.forces_strength_text( + data.allied_units.aircraft_count, + data.enemy_units.aircraft_count)) + self.ground_strength.setText(self.forces_strength_text( + data.allied_units.vehicles_count, + data.enemy_units.vehicles_count)) + self.economic_strength.setText(self.economic_strength_text()) def open_details_window(self) -> None: self.details_window = IntelWindow(self.game) diff --git a/qt_ui/windows/intel.py b/qt_ui/windows/intel.py index e104af44..497d65fb 100644 --- a/qt_ui/windows/intel.py +++ b/qt_ui/windows/intel.py @@ -1,7 +1,15 @@ +import itertools + from PySide2.QtWidgets import ( QDialog, - QGroupBox, + QFrame, + QGridLayout, + QLabel, + QLayout, QScrollArea, + QSizePolicy, + QSpacerItem, + QTabWidget, QVBoxLayout, QWidget, ) @@ -11,22 +19,115 @@ 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") +class ScrollingFrame(QFrame): + def __init__(self) -> None: + super().__init__() widget = QWidget() scroll_area = QScrollArea() scroll_area.setWidgetResizable(True) scroll_area.setWidget(widget) - scrolling_layout = QVBoxLayout() - widget.setLayout(scrolling_layout) + self.scrolling_layout = QVBoxLayout() + widget.setLayout(self.scrolling_layout) self.setLayout(QVBoxLayout()) self.layout().addWidget(scroll_area) - scrolling_layout.addLayout(FinancesLayout(game, player=False)) + def addWidget(self, widget: QWidget, *args, **kwargs) -> None: + self.scrolling_layout.addWidget(widget, *args, **kwargs) + + def addLayout(self, layout: QLayout, *args, **kwargs) -> None: + self.scrolling_layout.addLayout(layout, *args, **kwargs) + + +class EconomyIntelTab(ScrollingFrame): + def __init__(self, game: Game) -> None: + super().__init__() + self.addLayout(FinancesLayout(game, player=False)) + + +class IntelTableLayout(QGridLayout): + def __init__(self) -> None: + super().__init__() + self.row = itertools.count(0) + + def add_header(self, text: str) -> None: + self.addWidget(QLabel(f"{text}"), next(self.row), 0) + + def add_spacer(self) -> None: + self.addItem( + QSpacerItem(0, 0, QSizePolicy.Preferred, QSizePolicy.Expanding), + next(self.row), 0) + + def add_row(self, text: str, count: int) -> None: + row = next(self.row) + self.addWidget(QLabel(text), row, 0) + self.addWidget(QLabel(str(count)), row, 1) + + +class AircraftIntelLayout(IntelTableLayout): + def __init__(self, game: Game, player: bool) -> None: + super().__init__() + + total = 0 + for control_point in game.theater.control_points_for(player): + base = control_point.base + total += base.total_aircraft + if not base.total_aircraft: + continue + + self.add_header(control_point.name) + for airframe, count in base.aircraft.items(): + if not count: + continue + self.add_row(airframe.id, count) + + self.add_spacer() + self.add_row("Total", total) + + +class AircraftIntelTab(ScrollingFrame): + def __init__(self, game: Game) -> None: + super().__init__() + self.addLayout(AircraftIntelLayout(game, player=False)) + + +class ArmyIntelLayout(IntelTableLayout): + def __init__(self, game: Game, player: bool) -> None: + super().__init__() + + total = 0 + for control_point in game.theater.control_points_for(player): + base = control_point.base + total += base.total_armor + if not base.total_armor: + continue + + self.add_header(control_point.name) + for vehicle, count in base.armor.items(): + if not count: + continue + self.add_row(vehicle.id, count) + + self.add_spacer() + self.add_row("Total", total) + + +class ArmyIntelTab(ScrollingFrame): + def __init__(self, game: Game) -> None: + super().__init__() + self.addLayout(ArmyIntelLayout(game, player=False)) + + +class IntelTabs(QTabWidget): + + def __init__(self, game: Game): + super().__init__() + + self.addTab(EconomyIntelTab(game), "Economy") + self.addTab(AircraftIntelTab(game), "Air forces") + self.addTab(ArmyIntelTab(game), "Ground forces") class IntelWindow(QDialog): @@ -38,9 +139,9 @@ class IntelWindow(QDialog): self.setModal(True) self.setWindowTitle("Intelligence") self.setWindowIcon(ICONS["Statistics"]) - self.setMinimumSize(800, 600) + self.setMinimumSize(600, 500) layout = QVBoxLayout() self.setLayout(layout) - layout.addWidget(EconomyIntelBox(game), stretch=1) + layout.addWidget(IntelTabs(game), stretch=1) diff --git a/resources/stylesheets/style-dcs.css b/resources/stylesheets/style-dcs.css index 5cf07d77..f8539e83 100644 --- a/resources/stylesheets/style-dcs.css +++ b/resources/stylesheets/style-dcs.css @@ -546,7 +546,7 @@ QGroupBox[style="QWeatherWidget"] { padding: 0px; margin-left: 0px; margin-right: 5px; - margin-top: 1ex; + margin-top: 1px; margin-bottom: 5px; } @@ -555,4 +555,22 @@ QGroupBox[style="QWeatherWidget"] QLabel[style="text-sm"] { margin: 0px; font-size: 9px; line-height: 9px; +} + +QGroupBox[style="IntelSummary"] { + padding-left: 40px; + padding-right: 0px; + padding-top: 0px; + padding-bottom: 0px; + margin-left: 0px; + margin-right: 5px; + margin-top: 1px; + margin-bottom: 5px; +} + +QGroupBox[style="IntelSummary"] QLabel { + padding: 0px; + margin: 0px; + font-size: 9px; + line-height: 9px; } \ No newline at end of file