diff --git a/changelog.md b/changelog.md
index 2e16299e..3db324c2 100644
--- a/changelog.md
+++ b/changelog.md
@@ -13,6 +13,8 @@ Saves from 2.3 are not compatible with 2.4.
* **[Mission Generator]** Multiple groups are created for complex SAM sites (SAMs with additional point defense or SHORADS), improving Skynet behavior.
* **[Skynet]** Point defenses are now configured to remain on to protect the site they accompany.
* **[Balance]** Opfor now gains income using the same rules as the player, significantly increasing their income relative to the player for most campaigns.
+* **[Economy]** FOBs generate only $10M per turn (previously $20M like airbases).
+* **[Economy]** Carriers and off-map spawns generate no income (previously $20M like airbases).
# 2.3.3
diff --git a/game/income.py b/game/income.py
index a9cbecfc..d4e05993 100644
--- a/game/income.py
+++ b/game/income.py
@@ -4,7 +4,6 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING
from game.db import REWARDS
-from game.theater import ControlPoint
if TYPE_CHECKING:
from game import Game
@@ -22,12 +21,6 @@ class BuildingIncome:
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:
@@ -37,12 +30,10 @@ class Income:
self.control_points = []
self.buildings = []
- self.income_per_base = 20
-
names = set()
for cp in game.theater.control_points_for(player):
- self.control_points.append(
- ControlPointIncome(cp, self.income_per_base))
+ if cp.income_per_turn:
+ self.control_points.append(cp)
for tgo in cp.ground_objects:
names.add(tgo.obj_name)
@@ -58,7 +49,7 @@ class Income:
self.buildings.append(BuildingIncome(name, category, count,
REWARDS[category]))
- self.from_bases = sum(cp.income for cp in self.control_points)
+ self.from_bases = sum(cp.income_per_turn 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/controlpoint.py b/game/theater/controlpoint.py
index a22ea097..8904e9b0 100644
--- a/game/theater/controlpoint.py
+++ b/game/theater/controlpoint.py
@@ -478,6 +478,10 @@ class ControlPoint(MissionTarget, ABC):
else:
return 0
+ @property
+ def income_per_turn(self) -> int:
+ return 0
+
class Airfield(ControlPoint):
@@ -542,6 +546,10 @@ class Airfield(ControlPoint):
def can_deploy_ground_units(self) -> bool:
return True
+ @property
+ def income_per_turn(self) -> int:
+ return 20
+
class NavalControlPoint(ControlPoint, ABC):
@@ -741,3 +749,7 @@ class Fob(ControlPoint):
@property
def can_deploy_ground_units(self) -> bool:
return True
+
+ @property
+ def income_per_turn(self) -> int:
+ return 10
diff --git a/qt_ui/windows/finances/QFinancesMenu.py b/qt_ui/windows/finances/QFinancesMenu.py
index 3dfb195b..e1151b64 100644
--- a/qt_ui/windows/finances/QFinancesMenu.py
+++ b/qt_ui/windows/finances/QFinancesMenu.py
@@ -1,3 +1,6 @@
+import itertools
+from typing import Optional
+
from PySide2.QtWidgets import (
QDialog,
QFrame,
@@ -8,7 +11,8 @@ from PySide2.QtWidgets import (
import qt_ui.uiconstants as CONST
from game.game import Game
-from game.income import Income
+from game.income import BuildingIncome, Income
+from game.theater import ControlPoint
class QHorizontalSeparationLine(QFrame):
@@ -25,44 +29,65 @@ class QHorizontalSeparationLine(QFrame):
class FinancesLayout(QGridLayout):
def __init__(self, game: Game, player: bool) -> None:
super().__init__()
+ self.row = itertools.count(0)
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)
+ control_points = reversed(
+ sorted(income.control_points, key=lambda c: c.income_per_turn))
+ for control_point in control_points:
+ self.add_control_point(control_point)
- self.addWidget(QHorizontalSeparationLine(), 1, 0, 1, 3)
+ self.add_line()
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)
+ for building in buildings:
+ self.add_building(building)
- 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)
+ self.add_line()
+
+ self.add_row(middle=f"Income multiplier: {income.multiplier:.1f}",
+ right=f"{income.total}M")
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)
- self.setRowStretch(row + 4, 1)
+
+ self.add_row(middle="Balance", right=f"{budget}M")
+ self.setRowStretch(next(self.row), 1)
+
+ def add_row(self, left: Optional[str] = None, middle: Optional[str] = None,
+ right: Optional[str] = None) -> None:
+ if not any([left, middle, right]):
+ raise ValueError
+
+ row = next(self.row)
+ if left is not None:
+ self.addWidget(QLabel(left), row, 0)
+ if middle is not None:
+ self.addWidget(QLabel(middle), row, 1)
+ if right is not None:
+ self.addWidget(QLabel(right), row, 2)
+
+ def add_control_point(self, control_point: ControlPoint) -> None:
+ self.add_row(left=f"{control_point.name}",
+ right=f"{control_point.income_per_turn}M")
+
+ def add_building(self, building: BuildingIncome) -> None:
+ row = next(self.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)
+
+ def add_line(self) -> None:
+ self.addWidget(QHorizontalSeparationLine(), next(self.row), 0, 1, 3)
class QFinancesMenu(QDialog):