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))