From 90697194a1a4ca67b53eb7b4056e4f7f5416ac58 Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 4 Dec 2020 00:35:05 -0800 Subject: [PATCH] Migrate buildings and TGOs to unit map. Fixes https://github.com/Khopa/dcs_liberation/issues/485. --- game/debriefing.py | 200 +++++++----------- game/event/event.py | 102 ++++----- game/operation/operation.py | 3 +- game/theater/base.py | 2 +- game/theater/theatergroundobject.py | 4 + game/unitmap.py | 55 ++++- gen/groundobjectsgen.py | 75 ++++--- qt_ui/windows/QDebriefingWindow.py | 27 +-- .../windows/QWaitingForMissionResultWindow.py | 16 +- 9 files changed, 238 insertions(+), 246 deletions(-) diff --git a/game/debriefing.py b/game/debriefing.py index dd6a53b8..8d8fcb89 100644 --- a/game/debriefing.py +++ b/game/debriefing.py @@ -6,14 +6,22 @@ import os import threading import time from collections import defaultdict -from dataclasses import dataclass -from typing import Any, Callable, Dict, List, Type, TYPE_CHECKING +from dataclasses import dataclass, field +from typing import ( + Any, + Callable, + Dict, + Iterator, + List, + Type, + TYPE_CHECKING, +) from dcs.unittype import FlyingType, UnitType from game import db -from game.theater import Airfield, ControlPoint, TheaterGroundObject -from game.unitmap import UnitMap +from game.theater import Airfield, ControlPoint +from game.unitmap import Building, FrontLineUnit, GroundObjectUnit, UnitMap from gen.flights.flight import Flight if TYPE_CHECKING: @@ -22,12 +30,6 @@ if TYPE_CHECKING: DEBRIEFING_LOG_EXTENSION = "log" -@dataclass(frozen=True) -class DebriefingDeadUnitInfo: - player_unit: bool - type: Type[UnitType] - - @dataclass(frozen=True) class DebriefingDeadAircraftInfo: #: The Flight that resulted in the generated unit. @@ -38,27 +40,6 @@ class DebriefingDeadAircraftInfo: return self.flight.departure.captured -@dataclass(frozen=True) -class DebriefingDeadFrontLineUnitInfo: - #: The Flight that resulted in the generated unit. - unit_type: Type[UnitType] - control_point: ControlPoint - - @property - def player_unit(self) -> bool: - return self.control_point.captured - - -@dataclass(frozen=True) -class DebriefingDeadBuildingInfo: - #: The ground object this building was present at. - ground_object: TheaterGroundObject - - @property - def player_unit(self) -> bool: - return self.ground_object.control_point.captured - - @dataclass(frozen=True) class AirLosses: losses: List[DebriefingDeadAircraftInfo] @@ -80,18 +61,12 @@ class AirLosses: return flight.count - losses -@dataclass(frozen=True) -class FrontLineLosses: - losses: List[DebriefingDeadFrontLineUnitInfo] - - def by_type(self, player: bool) -> Dict[Type[UnitType], int]: - losses_by_type: Dict[Type[UnitType], int] = defaultdict(int) - for loss in self.losses: - if loss.control_point.captured != player: - continue - - losses_by_type[loss.unit_type] += 1 - return losses_by_type +@dataclass +class GroundLosses: + front_line: List[FrontLineUnit] = field(default_factory=list) + ground_objects: List[GroundObjectUnit] = field(default_factory=list) + buildings: List[Building] = field(default_factory=list) + airfields: List[Airfield] = field(default_factory=list) @dataclass(frozen=True) @@ -131,80 +106,51 @@ class Debriefing: self.game = game self.unit_map = unit_map - logging.info("--------------------------------") - logging.info("Starting Debriefing preprocessing") - logging.info("--------------------------------") - logging.info(self.state_data) - logging.info("--------------------------------") - self.player_country_id = db.country_id_from_name(game.player_country) self.enemy_country_id = db.country_id_from_name(game.enemy_country) self.air_losses = self.dead_aircraft() - self.front_line_losses = self.dead_front_line_units() - self.dead_units = [] - self.damaged_runways = self.find_damaged_runways() - self.dead_aaa_groups: List[DebriefingDeadUnitInfo] = [] - self.dead_buildings: List[DebriefingDeadBuildingInfo] = [] + self.ground_losses = self.dead_ground_units() - for unit_name in self.state_data.killed_ground_units: - for cp in game.theater.controlpoints: - if cp.captured: - country = self.player_country_id - else: - country = self.enemy_country_id - player_unit = (country == self.player_country_id) + @property + def front_line_losses(self) -> Iterator[FrontLineUnit]: + yield from self.ground_losses.front_line - for ground_object in cp.ground_objects: - # TODO: This seems to destroy an arbitrary building? - if ground_object.is_same_group(unit_name): - self.dead_buildings.append( - DebriefingDeadBuildingInfo(ground_object)) - elif ground_object.dcs_identifier in ["AA", "CARRIER", - "LHA"]: - for g in ground_object.groups: - for u in g.units: - if u.name != unit_name: - continue - unit_type = db.unit_type_from_name(u.type) - if unit_type is None: - logging.error( - f"Could not determine type of %s", - unit_name) - continue - self.dead_units.append(DebriefingDeadUnitInfo( - player_unit, unit_type)) + @property + def ground_object_losses(self) -> Iterator[GroundObjectUnit]: + yield from self.ground_losses.ground_objects - self.player_dead_units = [a for a in self.dead_units if a.player_unit] - self.enemy_dead_units = [a for a in self.dead_units if not a.player_unit] - self.player_dead_buildings = [a for a in self.dead_buildings if a.player_unit] - self.enemy_dead_buildings = [a for a in self.dead_buildings if not a.player_unit] + @property + def building_losses(self) -> Iterator[Building]: + yield from self.ground_losses.buildings - self.player_dead_units_dict: Dict[Type[UnitType], int] = defaultdict(int) - for a in self.player_dead_units: - self.player_dead_units_dict[a.type] += 1 + @property + def damaged_runways(self) -> Iterator[Airfield]: + yield from self.ground_losses.airfields - self.enemy_dead_units_dict: Dict[Type[UnitType], int] = defaultdict(int) - for a in self.enemy_dead_units: - self.enemy_dead_units_dict[a.type] += 1 + def casualty_count(self, control_point: ControlPoint) -> int: + return len( + [x for x in self.front_line_losses if x.origin == control_point] + ) - self.player_dead_buildings_dict: Dict[str, int] = defaultdict(int) - for b in self.player_dead_buildings: - self.player_dead_buildings_dict[b.ground_object.dcs_identifier] += 1 + def front_line_losses_by_type( + self, player: bool) -> Dict[Type[UnitType], int]: + losses_by_type: Dict[Type[UnitType], int] = defaultdict(int) + for loss in self.ground_losses.front_line: + if loss.origin.captured != player: + continue - self.enemy_dead_buildings_dict: Dict[str, int] = defaultdict(int) - for b in self.enemy_dead_buildings: - self.enemy_dead_buildings_dict[b.ground_object.dcs_identifier] += 1 + losses_by_type[loss.unit_type] += 1 + return losses_by_type - logging.info("--------------------------------") - logging.info("Debriefing pre process results :") - logging.info("--------------------------------") - logging.info(self.air_losses) - logging.info(self.front_line_losses) - logging.info(self.player_dead_units_dict) - logging.info(self.enemy_dead_units_dict) - logging.info(self.player_dead_buildings_dict) - logging.info(self.enemy_dead_buildings_dict) + def building_losses_by_type(self, player: bool) -> Dict[str, int]: + losses_by_type: Dict[str, int] = defaultdict(int) + for loss in self.ground_losses.buildings: + if loss.ground_object.control_point.captured != player: + continue + + losses_by_type[loss.ground_object.dcs_identifier] += 1 + return losses_by_type def dead_aircraft(self) -> AirLosses: losses = [] @@ -216,30 +162,38 @@ class Debriefing: losses.append(DebriefingDeadAircraftInfo(flight)) return AirLosses(losses) - def dead_front_line_units(self) -> FrontLineLosses: - losses = [] + def dead_ground_units(self) -> GroundLosses: + losses = GroundLosses() for unit_name in self.state_data.killed_ground_units: - unit = self.unit_map.front_line_unit(unit_name) - if unit is None: - # Killed "ground units" might also be runways or TGO units, so - # no need to log an error. + front_line_unit = self.unit_map.front_line_unit(unit_name) + if front_line_unit is not None: + losses.front_line.append(front_line_unit) continue - losses.append( - DebriefingDeadFrontLineUnitInfo(unit.unit_type, unit.origin)) - return FrontLineLosses(losses) - def find_damaged_runways(self) -> List[Airfield]: - losses = [] - for name in self.state_data.killed_ground_units: - airfield = self.unit_map.airfield(name) - if airfield is None: + ground_object_unit = self.unit_map.ground_object_unit(unit_name) + if ground_object_unit is not None: + losses.ground_objects.append(ground_object_unit) continue - losses.append(airfield) + + building = self.unit_map.building(unit_name) + if building is not None: + losses.buildings.append(building) + continue + + airfield = self.unit_map.airfield(unit_name) + if airfield is not None: + losses.airfields.append(airfield) + continue + + # Only logging as debug because we don't currently track infantry + # deaths, so we expect to see quite a few unclaimed dead ground + # units. We should start tracking those and covert this to a + # warning. + logging.debug(f"Death of untracked ground unit {unit_name} will " + "have no effect. This may be normal behavior.") + return losses - def _is_airfield(self, unit_name: str) -> bool: - return self.unit_map.airfield(unit_name) is not None - @property def base_capture_events(self): """Keeps only the last instance of a base capture event for each base ID.""" diff --git a/game/event/event.py b/game/event/event.py index 706da279..eb2e53b7 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -2,21 +2,20 @@ from __future__ import annotations import logging import math -from collections import defaultdict -from typing import Dict, List, Optional, TYPE_CHECKING, Type +from typing import Dict, List, TYPE_CHECKING, Type from dcs.mapping import Point from dcs.task import Task from dcs.unittype import UnitType -from game import db, persistency +from game import persistency from game.debriefing import AirLosses, Debriefing from game.infos.information import Information +from game.operation.operation import Operation from game.theater import ControlPoint from gen import AirTaskingOrder from gen.ground_forces.combat_stance import CombatStance from ..unitmap import UnitMap -from game.operation.operation import Operation if TYPE_CHECKING: from ..game import Game @@ -130,12 +129,10 @@ class Event: cp.base.aircraft[aircraft] -= 1 @staticmethod - def commit_front_line_losses(debriefing: Debriefing) -> Dict[int, int]: - killed_unit_count_by_cp: Dict[int, int] = defaultdict(int) - for loss in debriefing.front_line_losses.losses: + def commit_front_line_losses(debriefing: Debriefing) -> None: + for loss in debriefing.front_line_losses: unit_type = loss.unit_type - control_point = loss.control_point - killed_unit_count_by_cp[control_point.id] += 1 + control_point = loss.origin available = control_point.base.total_units_of_type(unit_type) if available <= 0: logging.error( @@ -145,64 +142,41 @@ class Event: logging.info(f"{unit_type} destroyed from {control_point}") control_point.base.armor[unit_type] -= 1 - return killed_unit_count_by_cp + + @staticmethod + def commit_ground_object_losses(debriefing: Debriefing) -> None: + for loss in debriefing.ground_object_losses: + # TODO: This should be stored in the TGO, not in the pydcs Group. + if not hasattr(loss.group, "units_losts"): + loss.group.units_losts = [] + + loss.group.units.remove(loss.unit) + loss.group.units_losts.append(loss.unit) + if not loss.ground_object.alive_unit_count: + loss.ground_object.is_dead = True + + def commit_building_losses(self, debriefing: Debriefing) -> None: + for loss in debriefing.building_losses: + loss.ground_object.is_dead = True + self.game.informations.append(Information( + "Building destroyed", + f"{loss.ground_object.dcs_identifier} has been destroyed at " + f"location {loss.ground_object.obj_name}", self.game.turn + )) + + @staticmethod + def commit_damaged_runways(debriefing: Debriefing) -> None: + for damaged_runway in debriefing.damaged_runways: + damaged_runway.damage_runway() def commit(self, debriefing: Debriefing): logging.info("Committing mission results") - for damaged_runway in debriefing.damaged_runways: - damaged_runway.damage_runway() - self.commit_air_losses(debriefing) - killed_unit_count_by_cp = self.commit_front_line_losses(debriefing) - - # ------------------------------ - # Static ground objects - for destroyed_ground_unit_name in debriefing.state_data.killed_ground_units: - for cp in self.game.theater.controlpoints: - if not cp.ground_objects: - continue - - # -- Static ground objects - for i, ground_object in enumerate(cp.ground_objects): - if ground_object.is_dead: - continue - - if ( - (ground_object.group_name == destroyed_ground_unit_name) - or - (ground_object.is_same_group(destroyed_ground_unit_name)) - ): - logging.info("cp {} killing ground object {}".format(cp, ground_object.group_name)) - cp.ground_objects[i].is_dead = True - - info = Information("Building destroyed", - ground_object.dcs_identifier + " has been destroyed at location " + ground_object.obj_name, - self.game.turn) - self.game.informations.append(info) - - - # -- AA Site groups - destroyed_units = 0 - info = Information("Units destroyed at " + ground_object.obj_name, - "", - self.game.turn) - for i, ground_object in enumerate(cp.ground_objects): - if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA", "EWR"]: - for g in ground_object.groups: - if not hasattr(g, "units_losts"): - g.units_losts = [] - for u in g.units: - if u.name == destroyed_ground_unit_name: - g.units.remove(u) - g.units_losts.append(u) - destroyed_units = destroyed_units + 1 - info.text = u.type - ucount = sum([len(g.units) for g in ground_object.groups]) - if ucount == 0: - ground_object.is_dead = True - if destroyed_units > 0: - self.game.informations.append(info) + self.commit_front_line_losses(debriefing) + self.commit_ground_object_losses(debriefing) + self.commit_building_losses(debriefing) + self.commit_damaged_runways(debriefing) # ------------------------------ # Captured bases @@ -257,8 +231,8 @@ class Event: delta = 0.0 player_won = True - ally_casualties = killed_unit_count_by_cp[cp.id] - enemy_casualties = killed_unit_count_by_cp[enemy_cp.id] + ally_casualties = debriefing.casualty_count(cp) + enemy_casualties = debriefing.casualty_count(enemy_cp) ally_units_alive = cp.base.total_armor enemy_units_alive = enemy_cp.base.total_armor diff --git a/game/operation/operation.py b/game/operation/operation.py index a63f1f85..d2c46f58 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -265,7 +265,8 @@ class Operation: cls.current_mission, cls.game, cls.radio_registry, - cls.tacan_registry + cls.tacan_registry, + cls.unit_map ) cls.groundobjectgen.generate() diff --git a/game/theater/base.py b/game/theater/base.py index ba8a72f8..14b96ce2 100644 --- a/game/theater/base.py +++ b/game/theater/base.py @@ -23,7 +23,7 @@ class Base: def __init__(self): self.aircraft: Dict[Type[FlyingType], int] = {} - self.armor: Dict[VehicleType, int] = {} + self.armor: Dict[Type[VehicleType], int] = {} self.aa: Dict[AirDefence, int] = {} self.commision_points: Dict[Type, float] = {} self.strength = 1 diff --git a/game/theater/theatergroundobject.py b/game/theater/theatergroundobject.py index be5c0a18..796c2aa9 100644 --- a/game/theater/theatergroundobject.py +++ b/game/theater/theatergroundobject.py @@ -136,6 +136,10 @@ class TheaterGroundObject(MissionTarget): ] yield from super().mission_types(for_player) + @property + def alive_unit_count(self) -> int: + return sum(len(g.units) for g in self.groups) + class BuildingGroundObject(TheaterGroundObject): def __init__(self, name: str, category: str, group_id: int, object_id: int, diff --git a/game/unitmap.py b/game/unitmap.py index 9c6e20e6..b97fba8e 100644 --- a/game/unitmap.py +++ b/game/unitmap.py @@ -2,25 +2,41 @@ from dataclasses import dataclass from typing import Dict, Optional, Type -from dcs.unitgroup import FlyingGroup, Group -from dcs.unittype import UnitType +from dcs.unit import Unit +from dcs.unitgroup import FlyingGroup, Group, StaticGroup +from dcs.unittype import VehicleType from game import db -from game.theater import Airfield, ControlPoint +from game.theater import Airfield, ControlPoint, TheaterGroundObject +from game.theater.theatergroundobject import BuildingGroundObject from gen.flights.flight import Flight -@dataclass +@dataclass(frozen=True) class FrontLineUnit: - unit_type: Type[UnitType] + unit_type: Type[VehicleType] origin: ControlPoint +@dataclass(frozen=True) +class GroundObjectUnit: + ground_object: TheaterGroundObject + group: Group + unit: Unit + + +@dataclass(frozen=True) +class Building: + ground_object: BuildingGroundObject + + class UnitMap: def __init__(self) -> None: self.aircraft: Dict[str, Flight] = {} self.airfields: Dict[str, Airfield] = {} self.front_line_units: Dict[str, FrontLineUnit] = {} + self.ground_object_units: Dict[str, GroundObjectUnit] = {} + self.buildings: Dict[str, Building] = {} def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None: for unit in group.units: @@ -52,7 +68,36 @@ class UnitMap: unit_type = db.unit_type_from_name(unit.type) if unit_type is None: raise RuntimeError(f"Unknown unit type: {unit.type}") + if not issubclass(unit_type, VehicleType): + raise RuntimeError( + f"{name} is a {unit_type.__name__}, expected a VehicleType") self.front_line_units[name] = FrontLineUnit(unit_type, origin) def front_line_unit(self, name: str) -> Optional[FrontLineUnit]: return self.front_line_units.get(name, None) + + def add_ground_object_units(self, ground_object: TheaterGroundObject, + group: Group) -> None: + for unit in group.units: + # The actual name is a String (the pydcs translatable string), which + # doesn't define __eq__. + name = str(unit.name) + if name in self.ground_object_units: + raise RuntimeError(f"Duplicate TGO unit: {name}") + self.ground_object_units[name] = GroundObjectUnit(ground_object, + group, unit) + + def ground_object_unit(self, name: str) -> Optional[GroundObjectUnit]: + return self.ground_object_units.get(name, None) + + def add_building(self, ground_object: BuildingGroundObject, + building: StaticGroup) -> None: + # The actual name is a String (the pydcs translatable string), which + # doesn't define __eq__. + name = str(building.name) + if name in self.buildings: + raise RuntimeError(f"Duplicate TGO unit: {name}") + self.buildings[name] = Building(ground_object) + + def building(self, name: str) -> Optional[Building]: + return self.buildings.get(name, None) diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index e2c30846..26ae027e 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -9,7 +9,7 @@ from __future__ import annotations import logging import random -from typing import Dict, Iterator, Optional, TYPE_CHECKING +from typing import Dict, Iterator, Optional, TYPE_CHECKING, Type from dcs import Mission from dcs.country import Country @@ -33,7 +33,7 @@ from game.theater.theatergroundobject import ( GenericCarrierGroundObject, LhaGroundObject, ShipGroundObject, ) -from .conflictgen import Conflict +from game.unitmap import UnitMap from .radios import RadioFrequency, RadioRegistry from .runways import RunwayData from .tacan import TacanBand, TacanChannel, TacanRegistry @@ -52,11 +52,12 @@ class GenericGroundObjectGenerator: Currently used only for SAM and missile (V1/V2) sites. """ def __init__(self, ground_object: TheaterGroundObject, country: Country, - game: Game, mission: Mission) -> None: + game: Game, mission: Mission, unit_map: UnitMap) -> None: self.ground_object = ground_object self.country = country self.game = game self.m = mission + self.unit_map = unit_map def generate(self) -> None: if self.game.position_culled(self.ground_object.position): @@ -89,9 +90,10 @@ class GenericGroundObjectGenerator: self.enable_eplrs(vg, unit_type) self.set_alarm_state(vg) + self._register_unit_group(vg) @staticmethod - def enable_eplrs(group: Group, unit_type: UnitType) -> None: + def enable_eplrs(group: Group, unit_type: Type[UnitType]) -> None: if hasattr(unit_type, 'eplrs'): if unit_type.eplrs: group.points[0].tasks.append(EPLRS(group.id)) @@ -102,6 +104,9 @@ class GenericGroundObjectGenerator: else: group.points[0].tasks.append(OptAlarmState(1)) + def _register_unit_group(self, group: Group) -> None: + self.unit_map.add_ground_object_units(self.ground_object, group) + class BuildingSiteGenerator(GenericGroundObjectGenerator): """Generator for building sites. @@ -133,16 +138,17 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator): def generate_vehicle_group(self, unit_type: UnitType) -> None: if not self.ground_object.is_dead: - self.m.vehicle_group( + group = self.m.vehicle_group( country=self.country, name=self.ground_object.group_name, _type=unit_type, position=self.ground_object.position, heading=self.ground_object.heading, ) + self._register_unit_group(group) def generate_static(self, static_type: StaticType) -> None: - self.m.static_group( + group = self.m.static_group( country=self.country, name=self.ground_object.group_name, _type=static_type, @@ -150,6 +156,11 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator): heading=self.ground_object.heading, dead=self.ground_object.is_dead, ) + self._register_building(group) + + def _register_building(self, building: StaticGroup) -> None: + assert isinstance(self.ground_object, BuildingGroundObject) + self.unit_map.add_building(self.ground_object, building) class GenericCarrierGenerator(GenericGroundObjectGenerator): @@ -161,8 +172,8 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): control_point: ControlPoint, country: Country, game: Game, mission: Mission, radio_registry: RadioRegistry, tacan_registry: TacanRegistry, icls_alloc: Iterator[int], - runways: Dict[str, RunwayData]) -> None: - super().__init__(ground_object, country, game, mission) + runways: Dict[str, RunwayData], unit_map: UnitMap) -> None: + super().__init__(ground_object, country, game, mission, unit_map) self.ground_object = ground_object self.control_point = control_point self.radio_registry = radio_registry @@ -190,8 +201,9 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): brc = self.steam_into_wind(ship_group) self.activate_beacons(ship_group, tacan, tacan_callsign, icls) self.add_runway_data(brc or 0, atc, tacan, tacan_callsign, icls) + self._register_unit_group(ship_group) - def get_carrier_type(self, group: Group) -> UnitType: + def get_carrier_type(self, group: Group) -> Type[UnitType]: unit_type = unit_type_from_name(group.units[0].type) if unit_type is None: raise RuntimeError( @@ -328,8 +340,9 @@ class ShipObjectGenerator(GenericGroundObjectGenerator): self.generate_group(group, unit_type) - def generate_group(self, group_def: Group, unit_type: UnitType): - group = self.m.ship_group(self.country, group_def.name, unit_type, + def generate_group(self, group_def: Group, + first_unit_type: Type[UnitType]) -> None: + group = self.m.ship_group(self.country, group_def.name, first_unit_type, position=group_def.position, heading=group_def.units[0].heading) group.units[0].name = self.m.string(group_def.units[0].name) @@ -343,6 +356,7 @@ class ShipObjectGenerator(GenericGroundObjectGenerator): ship.heading = unit.heading group.add_unit(ship) self.set_alarm_state(group) + self._register_unit_group(group) class GroundObjectsGenerator: @@ -355,11 +369,13 @@ class GroundObjectsGenerator: """ def __init__(self, mission: Mission, game: Game, - radio_registry: RadioRegistry, tacan_registry: TacanRegistry): + radio_registry: RadioRegistry, tacan_registry: TacanRegistry, + unit_map: UnitMap) -> None: self.m = mission self.game = game self.radio_registry = radio_registry self.tacan_registry = tacan_registry + self.unit_map = unit_map self.icls_alloc = iter(range(1, 21)) self.runways: Dict[str, RunwayData] = {} @@ -373,25 +389,26 @@ class GroundObjectsGenerator: for ground_object in cp.ground_objects: if isinstance(ground_object, BuildingGroundObject): - generator = BuildingSiteGenerator(ground_object, country, - self.game, self.m) + generator = BuildingSiteGenerator( + ground_object, country, self.game, self.m, + self.unit_map) elif isinstance(ground_object, CarrierGroundObject): - generator = CarrierGenerator(ground_object, cp, country, - self.game, self.m, - self.radio_registry, - self.tacan_registry, - self.icls_alloc, self.runways) + generator = CarrierGenerator( + ground_object, cp, country, self.game, self.m, + self.radio_registry, self.tacan_registry, + self.icls_alloc, self.runways, self.unit_map) elif isinstance(ground_object, LhaGroundObject): - generator = CarrierGenerator(ground_object, cp, country, - self.game, self.m, - self.radio_registry, - self.tacan_registry, - self.icls_alloc, self.runways) + generator = CarrierGenerator( + ground_object, cp, country, self.game, self.m, + self.radio_registry, self.tacan_registry, + self.icls_alloc, self.runways, self.unit_map) elif isinstance(ground_object, ShipGroundObject): - generator = ShipObjectGenerator(ground_object, country, - self.game, self.m) + generator = ShipObjectGenerator( + ground_object, country, self.game, self.m, + self.unit_map) else: - generator = GenericGroundObjectGenerator(ground_object, - country, self.game, - self.m) + + generator = GenericGroundObjectGenerator( + ground_object, country, self.game, self.m, + self.unit_map) generator.generate() diff --git a/qt_ui/windows/QDebriefingWindow.py b/qt_ui/windows/QDebriefingWindow.py index 4b52d585..6ef9ef81 100644 --- a/qt_ui/windows/QDebriefingWindow.py +++ b/qt_ui/windows/QDebriefingWindow.py @@ -41,13 +41,6 @@ class QDebriefingWindow(QDialog): self.layout.addWidget(header) self.layout.addStretch() - # Result - #if self.gameEvent.is_successfull(self.debriefing): - # title = QLabel("Operation end !") - # title.setProperty("style", "title-success") - #else: - # title = QLabel("Operation end !") - # title.setProperty("style", "title-danger") title = QLabel("Casualty report") self.layout.addWidget(title) @@ -68,7 +61,7 @@ class QDebriefingWindow(QDialog): logging.exception( f"Issue adding {unit_type} to debriefing information") - front_line_losses = self.debriefing.front_line_losses.by_type( + front_line_losses = self.debriefing.front_line_losses_by_type( player=True ) for unit_type, count in front_line_losses.items(): @@ -81,9 +74,10 @@ class QDebriefingWindow(QDialog): logging.exception( f"Issue adding {unit_type} to debriefing information") - for building, count in self.debriefing.player_dead_buildings_dict.items(): + 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(building), row, 0) lostUnitsLayout.addWidget(QLabel(str(count)), row, 1) row += 1 except AttributeError: @@ -97,12 +91,6 @@ class QDebriefingWindow(QDialog): enemylostUnitsLayout = QGridLayout() enemylostUnits.setLayout(enemylostUnitsLayout) - #row = 0 - #if self.debriefing.destroyed_objects: - # enemylostUnitsLayout.addWidget(QLabel("Ground assets"), row, 0) - # enemylostUnitsLayout.addWidget(QLabel("{}".format(len(self.debriefing.destroyed_objects))), row, 1) - # row += 1 - enemy_air_losses = self.debriefing.air_losses.by_type(player=False) for unit_type, count in enemy_air_losses.items(): try: @@ -114,7 +102,7 @@ class QDebriefingWindow(QDialog): logging.exception( f"Issue adding {unit_type} to debriefing information") - front_line_losses = self.debriefing.front_line_losses.by_type( + front_line_losses = self.debriefing.front_line_losses_by_type( player=False ) for unit_type, count in front_line_losses.items(): @@ -124,7 +112,8 @@ class QDebriefingWindow(QDialog): enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) row += 1 - for building, count in self.debriefing.enemy_dead_buildings_dict.items(): + 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) @@ -135,6 +124,8 @@ class QDebriefingWindow(QDialog): self.layout.addWidget(enemylostUnits) + # TODO: Display dead ground object units and runways. + # confirm button okay = QPushButton("Okay") okay.clicked.connect(self.close) diff --git a/qt_ui/windows/QWaitingForMissionResultWindow.py b/qt_ui/windows/QWaitingForMissionResultWindow.py index f780091c..5551dda0 100644 --- a/qt_ui/windows/QWaitingForMissionResultWindow.py +++ b/qt_ui/windows/QWaitingForMissionResultWindow.py @@ -138,15 +138,21 @@ class QWaitingForMissionResultWindow(QDialog): updateLayout.addWidget( QLabel("Front line units destroyed"), 1, 0) updateLayout.addWidget( - QLabel(str(len(debriefing.front_line_losses.losses))), 1, 1) + QLabel(str(len(list(debriefing.front_line_losses)))), 1, 1) updateLayout.addWidget( QLabel("Other ground units destroyed"), 2, 0) - updateLayout.addWidget(QLabel(str(len(debriefing.dead_units))), 2, 1) - - updateLayout.addWidget(QLabel("Base Capture Events"), 3, 0) updateLayout.addWidget( - QLabel(str(len(debriefing.base_capture_events))), 3, 1) + QLabel(str(len(list(debriefing.ground_object_losses)))), 2, 1) + + updateLayout.addWidget( + QLabel("Buildings destroyed"), 3, 0) + updateLayout.addWidget( + QLabel(str(len(list(debriefing.building_losses)))), 3, 1) + + updateLayout.addWidget(QLabel("Base Capture Events"), 4, 0) + updateLayout.addWidget( + QLabel(str(len(debriefing.base_capture_events))), 4, 1) # Clear previous content of the window for i in reversed(range(self.gridLayout.count())):