diff --git a/changelog.md b/changelog.md
index a28e5bd2..9a48c7d7 100644
--- a/changelog.md
+++ b/changelog.md
@@ -4,7 +4,7 @@ Saves from 2.5 are not compatible with 3.0.
## Features/Improvements
-* **[Campaign]** Ground units can now be transferred by road and airlift. See https://github.com/Khopa/dcs_liberation/wiki/Unit-Transfers for more information.
+* **[Campaign]** Ground units can now be transferred by road, airlift, and cargo ship. See https://github.com/Khopa/dcs_liberation/wiki/Unit-Transfers for more information.
* **[Campaign]** Ground units can no longer be sold. To move units to a new location, transfer them.
* **[Campaign]** Ground units must now be recruited at a base with a factory and transferred to their destination. When buying units in the UI, the purchase will automatically be fulfilled at the closest factory and a transfer will be created on the next turn. This feature is off by default.
* **[UI]** Campaigns generated for an older or newer version of the game will now be marked as incompatible. They can still be played, but bugs may be present.
diff --git a/game/debriefing.py b/game/debriefing.py
index 1ccafaca..1d534be2 100644
--- a/game/debriefing.py
+++ b/game/debriefing.py
@@ -22,6 +22,7 @@ from dcs.unittype import FlyingType, UnitType
from game import db
from game.theater import Airfield, ControlPoint
+from game.transfers import CargoShip
from game.unitmap import (
AirliftUnit,
Building,
@@ -70,6 +71,9 @@ class GroundLosses:
player_convoy: List[ConvoyUnit] = field(default_factory=list)
enemy_convoy: List[ConvoyUnit] = field(default_factory=list)
+ player_cargo_ships: List[CargoShip] = field(default_factory=list)
+ enemy_cargo_ships: List[CargoShip] = field(default_factory=list)
+
player_airlifts: List[AirliftUnit] = field(default_factory=list)
enemy_airlifts: List[AirliftUnit] = field(default_factory=list)
@@ -138,6 +142,11 @@ class Debriefing:
yield from self.ground_losses.player_convoy
yield from self.ground_losses.enemy_convoy
+ @property
+ def cargo_ship_losses(self) -> Iterator[CargoShip]:
+ yield from self.ground_losses.player_cargo_ships
+ yield from self.ground_losses.enemy_cargo_ships
+
@property
def airlift_losses(self) -> Iterator[AirliftUnit]:
yield from self.ground_losses.player_airlifts
@@ -181,6 +190,17 @@ class Debriefing:
losses_by_type[loss.unit_type] += 1
return losses_by_type
+ def cargo_ship_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
+ losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
+ if player:
+ ships = self.ground_losses.player_cargo_ships
+ else:
+ ships = self.ground_losses.enemy_cargo_ships
+ for ship in ships:
+ for unit_type, count in ship.units.items():
+ losses_by_type[unit_type] += count
+ return losses_by_type
+
def airlift_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
if player:
@@ -237,6 +257,14 @@ class Debriefing:
losses.enemy_convoy.append(convoy_unit)
continue
+ cargo_ship = self.unit_map.cargo_ship(unit_name)
+ if cargo_ship is not None:
+ if cargo_ship.player_owned:
+ losses.player_cargo_ships.append(cargo_ship)
+ else:
+ losses.enemy_cargo_ships.append(cargo_ship)
+ continue
+
ground_object_unit = self.unit_map.ground_object_unit(unit_name)
if ground_object_unit is not None:
if ground_object_unit.ground_object.control_point.captured:
diff --git a/game/event/event.py b/game/event/event.py
index d37664ee..4ca70790 100644
--- a/game/event/event.py
+++ b/game/event/event.py
@@ -169,6 +169,15 @@ class Event:
logging.info(f"{unit_type} destroyed in {convoy_name}")
convoy.kill_unit(unit_type)
+ @staticmethod
+ def commit_cargo_ship_losses(debriefing: Debriefing) -> None:
+ for ship in debriefing.cargo_ship_losses:
+ logging.info(
+ f"All units destroyed in cargo ship from {ship.origin} to "
+ f"{ship.destination}."
+ )
+ ship.kill_all()
+
@staticmethod
def commit_airlift_losses(debriefing: Debriefing) -> None:
for loss in debriefing.airlift_losses:
diff --git a/game/operation/operation.py b/game/operation/operation.py
index 1376be88..1304c064 100644
--- a/game/operation/operation.py
+++ b/game/operation/operation.py
@@ -23,7 +23,8 @@ from gen.airsupportgen import AirSupport, AirSupportConflictGenerator
from gen.armor import GroundConflictGenerator, JtacInfo
from gen.beacons import load_beacons_for_terrain
from gen.briefinggen import BriefingGenerator, MissionInfoGenerator
-from gen.convoys import ConvoyGenerator
+from gen.cargoshipgen import CargoShipGenerator
+from gen.convoygen import ConvoyGenerator
from gen.environmentgen import EnvironmentGenerator
from gen.forcedoptionsgen import ForcedOptionsGenerator
from gen.groundobjectsgen import GroundObjectsGenerator
@@ -304,7 +305,7 @@ class Operation:
# Set mission time and weather conditions.
EnvironmentGenerator(cls.current_mission, cls.game.conditions).generate()
cls._generate_ground_units()
- cls._generate_convoys()
+ cls._generate_transports()
cls._generate_destroyed_units()
cls._generate_air_units()
cls.assign_channels_to_flights(
@@ -426,9 +427,10 @@ class Operation:
cls.jtacs.extend(ground_conflict_gen.jtacs)
@classmethod
- def _generate_convoys(cls) -> None:
+ def _generate_transports(cls) -> None:
"""Generates convoys for unit transfers by road."""
ConvoyGenerator(cls.current_mission, cls.game, cls.unit_map).generate()
+ CargoShipGenerator(cls.current_mission, cls.game, cls.unit_map).generate()
@classmethod
def reset_naming_ids(cls):
diff --git a/game/transfers.py b/game/transfers.py
index 3685ed79..a22cabb6 100644
--- a/game/transfers.py
+++ b/game/transfers.py
@@ -4,7 +4,7 @@ import logging
from collections import defaultdict
from dataclasses import dataclass, field
from functools import singledispatchmethod
-from typing import Dict, Iterator, List, Optional, TYPE_CHECKING, Type
+from typing import Dict, Generic, Iterator, List, Optional, TYPE_CHECKING, Type, TypeVar
from dcs.mapping import Point
from dcs.unittype import FlyingType, VehicleType
@@ -271,6 +271,10 @@ class MultiGroupTransport(MissionTarget, Transport):
pass
raise KeyError
+ def kill_all(self) -> None:
+ for transfer in self.transfers:
+ transfer.kill_all()
+
def disband(self) -> None:
for transfer in list(self.transfers):
self.remove_units(transfer)
@@ -298,14 +302,6 @@ class MultiGroupTransport(MissionTarget, Transport):
def description(self) -> str:
raise NotImplementedError
- @property
- def route_start(self) -> Point:
- raise NotImplementedError
-
- @property
- def route_end(self) -> Point:
- raise NotImplementedError
-
class Convoy(MultiGroupTransport):
def __init__(self, origin: ControlPoint, destination: ControlPoint) -> None:
@@ -345,12 +341,8 @@ class CargoShip(MultiGroupTransport):
yield from super().mission_types(for_player)
@property
- def route_start(self) -> Point:
- return self.origin.shipping_lanes[self.destination][0]
-
- @property
- def route_end(self) -> Point:
- return self.destination.shipping_lanes[self.origin][-1]
+ def route(self) -> List[Point]:
+ return self.origin.shipping_lanes[self.destination]
def description(self) -> str:
return f"On a ship to {self.destination}"
@@ -359,16 +351,19 @@ class CargoShip(MultiGroupTransport):
return None
-class TransportMap:
+TransportType = TypeVar("TransportType", bound=MultiGroupTransport)
+
+
+class TransportMap(Generic[TransportType]):
def __init__(self) -> None:
# Dict of origin -> destination -> transport.
self.transports: Dict[
- ControlPoint, Dict[ControlPoint, MultiGroupTransport]
+ ControlPoint, Dict[ControlPoint, TransportType]
] = defaultdict(dict)
def create_transport(
self, origin: ControlPoint, destination: ControlPoint
- ) -> MultiGroupTransport:
+ ) -> TransportType:
raise NotImplementedError
def transport_exists(self, origin: ControlPoint, destination: ControlPoint) -> bool:
@@ -376,27 +371,27 @@ class TransportMap:
def find_transport(
self, origin: ControlPoint, destination: ControlPoint
- ) -> Optional[MultiGroupTransport]:
+ ) -> Optional[TransportType]:
return self.transports[origin].get(destination)
def find_or_create_transport(
self, origin: ControlPoint, destination: ControlPoint
- ) -> MultiGroupTransport:
+ ) -> TransportType:
transport = self.find_transport(origin, destination)
if transport is None:
transport = self.create_transport(origin, destination)
self.transports[origin][destination] = transport
return transport
- def departing_from(self, origin: ControlPoint) -> Iterator[MultiGroupTransport]:
+ def departing_from(self, origin: ControlPoint) -> Iterator[TransportType]:
yield from self.transports[origin].values()
- def travelling_to(self, destination: ControlPoint) -> Iterator[MultiGroupTransport]:
+ def travelling_to(self, destination: ControlPoint) -> Iterator[TransportType]:
for destination_dict in self.transports.values():
if destination in destination_dict:
yield destination_dict[destination]
- def disband_transport(self, transport: MultiGroupTransport) -> None:
+ def disband_transport(self, transport: TransportType) -> None:
transport.disband()
del self.transports[transport.origin][transport.destination]
@@ -416,7 +411,7 @@ class TransportMap:
next_stop = self.next_stop_for(transfer)
self.find_or_create_transport(transfer.position, next_stop).add_units(transfer)
- def remove(self, transport: MultiGroupTransport, transfer: TransferOrder) -> None:
+ def remove(self, transport: TransportType, transfer: TransferOrder) -> None:
transport.remove_units(transfer)
if not transport.transfers:
self.disband_transport(transport)
@@ -425,7 +420,7 @@ class TransportMap:
for transport in list(self):
self.disband_transport(transport)
- def __iter__(self) -> Iterator[MultiGroupTransport]:
+ def __iter__(self) -> Iterator[TransportType]:
for destination_dict in self.transports.values():
yield from destination_dict.values()
diff --git a/game/unitmap.py b/game/unitmap.py
index 4571f2b3..dd1f527b 100644
--- a/game/unitmap.py
+++ b/game/unitmap.py
@@ -9,7 +9,7 @@ from dcs.unittype import VehicleType
from game import db
from game.theater import Airfield, ControlPoint, TheaterGroundObject
from game.theater.theatergroundobject import BuildingGroundObject
-from game.transfers import MultiGroupTransport, TransferOrder
+from game.transfers import CargoShip, Convoy, TransferOrder
from gen.flights.flight import Flight
@@ -29,7 +29,7 @@ class GroundObjectUnit:
@dataclass(frozen=True)
class ConvoyUnit:
unit_type: Type[VehicleType]
- convoy: MultiGroupTransport
+ convoy: Convoy
@dataclass(frozen=True)
@@ -51,6 +51,7 @@ class UnitMap:
self.ground_object_units: Dict[str, GroundObjectUnit] = {}
self.buildings: Dict[str, Building] = {}
self.convoys: Dict[str, ConvoyUnit] = {}
+ self.cargo_ships: Dict[str, CargoShip] = {}
self.airlifts: Dict[str, AirliftUnit] = {}
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None:
@@ -130,7 +131,7 @@ class UnitMap:
def ground_object_unit(self, name: str) -> Optional[GroundObjectUnit]:
return self.ground_object_units.get(name, None)
- def add_convoy_units(self, group: Group, convoy: MultiGroupTransport) -> None:
+ def add_convoy_units(self, group: Group, convoy: Convoy) -> None:
for unit in group.units:
# The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__.
@@ -149,6 +150,23 @@ class UnitMap:
def convoy_unit(self, name: str) -> Optional[ConvoyUnit]:
return self.convoys.get(name, None)
+ def add_cargo_ship(self, group: Group, ship: CargoShip) -> None:
+ if len(group.units) > 1:
+ # Cargo ship "groups" are single units. Killing the one ship kills the whole
+ # transfer. If we ever want to add escorts or create multiple cargo ships in
+ # a convoy of ships that logic needs to change.
+ raise ValueError("Expected cargo ship to be a single unit group.")
+ unit = group.units[0]
+ # The actual name is a String (the pydcs translatable string), which
+ # doesn't define __eq__.
+ name = str(unit.name)
+ if name in self.cargo_ships:
+ raise RuntimeError(f"Duplicate cargo ship: {name}")
+ self.cargo_ships[name] = ship
+
+ def cargo_ship(self, name: str) -> Optional[CargoShip]:
+ return self.cargo_ships.get(name, None)
+
def add_airlift_units(self, group: FlyingGroup, transfer: TransferOrder) -> None:
for transport, cargo_type in zip(group.units, transfer.iter_units()):
# The actual name is a String (the pydcs translatable string), which
diff --git a/gen/cargoshipgen.py b/gen/cargoshipgen.py
new file mode 100644
index 00000000..e3161a42
--- /dev/null
+++ b/gen/cargoshipgen.py
@@ -0,0 +1,47 @@
+from __future__ import annotations
+
+import itertools
+from typing import TYPE_CHECKING
+
+from dcs import Mission
+from dcs.ships import Bulker_Handy_Wind
+from dcs.unitgroup import ShipGroup
+
+from game.transfers import CargoShip
+from game.unitmap import UnitMap
+from game.utils import knots
+
+if TYPE_CHECKING:
+ from game import Game
+
+
+class CargoShipGenerator:
+ def __init__(self, mission: Mission, game: Game, unit_map: UnitMap) -> None:
+ self.mission = mission
+ self.game = game
+ self.unit_map = unit_map
+ self.count = itertools.count()
+
+ def generate(self) -> None:
+ # Reset the count to make generation deterministic.
+ for ship in self.game.transfers.cargo_ships:
+ self.generate_cargo_ship(ship)
+
+ def generate_cargo_ship(self, ship: CargoShip) -> ShipGroup:
+ country = self.mission.country(
+ self.game.player_country if ship.player_owned else self.game.enemy_country
+ )
+ waypoints = ship.route
+ group = self.mission.ship_group(
+ country,
+ ship.name,
+ Bulker_Handy_Wind,
+ position=waypoints[0],
+ group_size=1,
+ )
+ for waypoint in waypoints[1:]:
+ # 12 knots is very slow but it's also nearly the max allowed by DCS for this
+ # type of ship.
+ group.add_waypoint(waypoint, speed=knots(12).kph)
+ self.unit_map.add_cargo_ship(group, ship)
+ return group
diff --git a/gen/convoys.py b/gen/convoygen.py
similarity index 95%
rename from gen/convoys.py
rename to gen/convoygen.py
index f3033e9e..9c904009 100644
--- a/gen/convoys.py
+++ b/gen/convoygen.py
@@ -10,7 +10,7 @@ from dcs.unit import Vehicle
from dcs.unitgroup import VehicleGroup
from dcs.unittype import VehicleType
-from game.transfers import MultiGroupTransport
+from game.transfers import Convoy
from game.unitmap import UnitMap
from game.utils import kph
@@ -30,7 +30,7 @@ class ConvoyGenerator:
for convoy in self.game.transfers.convoys:
self.generate_convoy(convoy)
- def generate_convoy(self, convoy: MultiGroupTransport) -> VehicleGroup:
+ def generate_convoy(self, convoy: Convoy) -> VehicleGroup:
group = self._create_mixed_unit_group(
convoy.name,
convoy.route_start,
diff --git a/qt_ui/windows/QDebriefingWindow.py b/qt_ui/windows/QDebriefingWindow.py
index 7308b0e6..86e59e0d 100644
--- a/qt_ui/windows/QDebriefingWindow.py
+++ b/qt_ui/windows/QDebriefingWindow.py
@@ -1,4 +1,5 @@
import logging
+from typing import Callable, Dict, TypeVar
from PySide2.QtGui import QIcon, QPixmap
from PySide2.QtWidgets import (
@@ -14,6 +15,57 @@ from game import db
from game.debriefing import Debriefing
+T = TypeVar("T")
+
+
+class LossGrid(QGridLayout):
+ def __init__(self, debriefing: Debriefing, player: bool) -> None:
+ super().__init__()
+
+ if player:
+ country = debriefing.player_country
+ else:
+ country = debriefing.enemy_country
+
+ self.add_loss_rows(
+ debriefing.air_losses.by_type(player),
+ lambda u: db.unit_get_expanded_info(country, u, "name"),
+ )
+ self.add_loss_rows(
+ debriefing.front_line_losses_by_type(player),
+ lambda u: db.unit_type_name(u),
+ )
+ self.add_loss_rows(
+ debriefing.convoy_losses_by_type(player),
+ lambda u: f"{db.unit_type_name(u)} from convoy",
+ )
+ self.add_loss_rows(
+ debriefing.cargo_ship_losses_by_type(player),
+ lambda u: f"{db.unit_type_name(u)} from cargo ship",
+ )
+ self.add_loss_rows(
+ debriefing.airlift_losses_by_type(player),
+ lambda u: f"{db.unit_type_name(u)} from airlift",
+ )
+ self.add_loss_rows(
+ debriefing.building_losses_by_type(player),
+ lambda u: u,
+ )
+
+ # TODO: Display dead ground object units and runways.
+
+ def add_loss_rows(self, losses: Dict[T, int], make_name: Callable[[T], str]):
+ for unit_type, count in losses.items():
+ row = self.rowCount()
+ try:
+ name = make_name(unit_type)
+ except AttributeError:
+ logging.exception(f"Could not make unit name for {unit_type}")
+ name = unit_type.id
+ self.addWidget(QLabel(name), row, 0)
+ self.addWidget(QLabel(str(count)), row, 1)
+
+
class QDebriefingWindow(QDialog):
def __init__(self, debriefing: Debriefing):
super(QDebriefingWindow, self).__init__()
@@ -24,155 +76,27 @@ class QDebriefingWindow(QDialog):
self.setMinimumSize(300, 200)
self.setWindowIcon(QIcon("./resources/icon.png"))
- self.initUI()
-
- def initUI(self):
-
- self.layout = QVBoxLayout()
+ layout = QVBoxLayout()
+ self.setLayout(layout)
header = QLabel(self)
header.setGeometry(0, 0, 655, 106)
pixmap = QPixmap("./resources/ui/debriefing.png")
header.setPixmap(pixmap)
- self.layout.addWidget(header)
- self.layout.addStretch()
+ layout.addWidget(header)
+ layout.addStretch()
title = QLabel("Casualty report")
- self.layout.addWidget(title)
+ layout.addWidget(title)
- # Player lost units
- lostUnits = QGroupBox(f"{self.debriefing.player_country}'s lost units:")
- lostUnitsLayout = QGridLayout()
- lostUnits.setLayout(lostUnitsLayout)
+ player_lost_units = QGroupBox(f"{self.debriefing.player_country}'s lost units:")
+ player_lost_units.setLayout(LossGrid(debriefing, player=True))
+ layout.addWidget(player_lost_units)
- row = 0
- player_air_losses = self.debriefing.air_losses.by_type(player=True)
- for unit_type, count in player_air_losses.items():
- try:
- lostUnitsLayout.addWidget(
- QLabel(
- db.unit_get_expanded_info(
- self.debriefing.player_country, unit_type, "name"
- )
- ),
- row,
- 0,
- )
- lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {unit_type} to debriefing information")
+ enemy_lost_units = QGroupBox(f"{self.debriefing.enemy_country}'s lost units:")
+ enemy_lost_units.setLayout(LossGrid(debriefing, player=False))
+ layout.addWidget(enemy_lost_units)
- front_line_losses = self.debriefing.front_line_losses_by_type(player=True)
- for unit_type, count in front_line_losses.items():
- try:
- lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
- lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {unit_type} to debriefing information")
-
- convoy_losses = self.debriefing.convoy_losses_by_type(player=True)
- for unit_type, count in convoy_losses.items():
- try:
- lostUnitsLayout.addWidget(
- QLabel(f"{db.unit_type_name(unit_type)} from convoy"), row, 0
- )
- lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {unit_type} to debriefing information")
-
- airlift_losses = self.debriefing.airlift_losses_by_type(player=True)
- for unit_type, count in airlift_losses.items():
- try:
- lostUnitsLayout.addWidget(
- QLabel(f"{db.unit_type_name(unit_type)} from airlift"), row, 0
- )
- lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {unit_type} to debriefing information")
-
- building_losses = self.debriefing.building_losses_by_type(player=True)
- for building, count in building_losses.items():
- try:
- lostUnitsLayout.addWidget(QLabel(building), row, 0)
- lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {building} to debriefing information")
-
- self.layout.addWidget(lostUnits)
-
- # Enemy lost units
- enemylostUnits = QGroupBox(f"{self.debriefing.enemy_country}'s lost units:")
- enemylostUnitsLayout = QGridLayout()
- enemylostUnits.setLayout(enemylostUnitsLayout)
-
- enemy_air_losses = self.debriefing.air_losses.by_type(player=False)
- for unit_type, count in enemy_air_losses.items():
- try:
- enemylostUnitsLayout.addWidget(
- QLabel(
- db.unit_get_expanded_info(
- self.debriefing.enemy_country, unit_type, "name"
- )
- ),
- row,
- 0,
- )
- enemylostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {unit_type} to debriefing information")
-
- front_line_losses = self.debriefing.front_line_losses_by_type(player=False)
- for unit_type, count in front_line_losses.items():
- if count == 0:
- continue
- enemylostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
- enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
- row += 1
-
- convoy_losses = self.debriefing.convoy_losses_by_type(player=False)
- for unit_type, count in convoy_losses.items():
- try:
- lostUnitsLayout.addWidget(
- QLabel(f"{db.unit_type_name(unit_type)} from convoy"), row, 0
- )
- lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {unit_type} to debriefing information")
-
- airlift_losses = self.debriefing.airlift_losses_by_type(player=False)
- for unit_type, count in airlift_losses.items():
- try:
- lostUnitsLayout.addWidget(
- QLabel(f"{db.unit_type_name(unit_type)} from airlift"), row, 0
- )
- lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {unit_type} to debriefing information")
-
- building_losses = self.debriefing.building_losses_by_type(player=False)
- for building, count in building_losses.items():
- try:
- enemylostUnitsLayout.addWidget(QLabel(building), row, 0)
- enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
- row += 1
- except AttributeError:
- logging.exception(f"Issue adding {building} to debriefing information")
-
- self.layout.addWidget(enemylostUnits)
-
- # TODO: Display dead ground object units and runways.
-
- # confirm button
okay = QPushButton("Okay")
okay.clicked.connect(self.close)
- self.layout.addWidget(okay)
-
- self.setLayout(self.layout)
+ layout.addWidget(okay)
diff --git a/qt_ui/windows/QWaitingForMissionResultWindow.py b/qt_ui/windows/QWaitingForMissionResultWindow.py
index bd7f6163..10219007 100644
--- a/qt_ui/windows/QWaitingForMissionResultWindow.py
+++ b/qt_ui/windows/QWaitingForMissionResultWindow.py
@@ -4,6 +4,7 @@ import json
import os
import timeit
from datetime import timedelta
+from typing import Sized
from PySide2 import QtCore
from PySide2.QtCore import QObject, Qt, Signal
@@ -132,38 +133,48 @@ class QWaitingForMissionResultWindow(QDialog):
self.layout.addLayout(self.gridLayout, 1, 0)
self.setLayout(self.layout)
+ @staticmethod
+ def add_update_row(description: str, count: Sized, layout: QGridLayout) -> None:
+ row = layout.rowCount()
+ layout.addWidget(QLabel(f"{description}"), row, 0)
+ layout.addWidget(QLabel(f"{len(count)}"), row, 1)
+
def updateLayout(self, debriefing: Debriefing) -> None:
updateBox = QGroupBox("Mission status")
- updateLayout = QGridLayout()
- updateBox.setLayout(updateLayout)
+ update_layout = QGridLayout()
+ updateBox.setLayout(update_layout)
self.debriefing = debriefing
- updateLayout.addWidget(QLabel("Aircraft destroyed"), 0, 0)
- updateLayout.addWidget(
- QLabel(str(len(list(debriefing.air_losses.losses)))), 0, 1
+ self.add_update_row(
+ "Aircraft destroyed", list(debriefing.air_losses.losses), update_layout
)
-
- updateLayout.addWidget(QLabel("Front line units destroyed"), 1, 0)
- updateLayout.addWidget(
- QLabel(str(len(list(debriefing.front_line_losses)))), 1, 1
+ self.add_update_row(
+ "Front line units destroyed",
+ list(debriefing.front_line_losses),
+ update_layout,
)
-
- updateLayout.addWidget(QLabel("Convoy units destroyed"), 2, 0)
- updateLayout.addWidget(QLabel(str(len(list(debriefing.convoy_losses)))), 2, 1)
-
- updateLayout.addWidget(QLabel("Airlift cargo destroyed"), 3, 0)
- updateLayout.addWidget(QLabel(str(len(list(debriefing.airlift_losses)))), 3, 1)
-
- updateLayout.addWidget(QLabel("Other ground units destroyed"), 4, 0)
- updateLayout.addWidget(
- QLabel(str(len(list(debriefing.ground_object_losses)))), 4, 1
+ self.add_update_row(
+ "Convoy units destroyed", list(debriefing.convoy_losses), update_layout
+ )
+ self.add_update_row(
+ "Shipping cargo destroyed",
+ list(debriefing.cargo_ship_losses),
+ update_layout,
+ )
+ self.add_update_row(
+ "Airlift cargo destroyed", list(debriefing.airlift_losses), update_layout
+ )
+ self.add_update_row(
+ "Ground units lost at objective areas",
+ list(debriefing.ground_object_losses),
+ update_layout,
+ )
+ self.add_update_row(
+ "Buildings destroyed", list(debriefing.building_losses), update_layout
+ )
+ self.add_update_row(
+ "Base capture events", list(debriefing.base_capture_events), update_layout
)
-
- updateLayout.addWidget(QLabel("Buildings destroyed"), 5, 0)
- updateLayout.addWidget(QLabel(str(len(list(debriefing.building_losses)))), 5, 1)
-
- updateLayout.addWidget(QLabel("Base Capture Events"), 6, 0)
- updateLayout.addWidget(QLabel(str(len(debriefing.base_capture_events))), 6, 1)
# Clear previous content of the window
for i in reversed(range(self.gridLayout.count())):