mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Migrate buildings and TGOs to unit map.
Fixes https://github.com/Khopa/dcs_liberation/issues/485.
This commit is contained in:
parent
13f4baa34e
commit
90697194a1
@ -6,14 +6,22 @@ import os
|
|||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass, field
|
||||||
from typing import Any, Callable, Dict, List, Type, TYPE_CHECKING
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Iterator,
|
||||||
|
List,
|
||||||
|
Type,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
)
|
||||||
|
|
||||||
from dcs.unittype import FlyingType, UnitType
|
from dcs.unittype import FlyingType, UnitType
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from game.theater import Airfield, ControlPoint, TheaterGroundObject
|
from game.theater import Airfield, ControlPoint
|
||||||
from game.unitmap import UnitMap
|
from game.unitmap import Building, FrontLineUnit, GroundObjectUnit, UnitMap
|
||||||
from gen.flights.flight import Flight
|
from gen.flights.flight import Flight
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -22,12 +30,6 @@ if TYPE_CHECKING:
|
|||||||
DEBRIEFING_LOG_EXTENSION = "log"
|
DEBRIEFING_LOG_EXTENSION = "log"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class DebriefingDeadUnitInfo:
|
|
||||||
player_unit: bool
|
|
||||||
type: Type[UnitType]
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class DebriefingDeadAircraftInfo:
|
class DebriefingDeadAircraftInfo:
|
||||||
#: The Flight that resulted in the generated unit.
|
#: The Flight that resulted in the generated unit.
|
||||||
@ -38,27 +40,6 @@ class DebriefingDeadAircraftInfo:
|
|||||||
return self.flight.departure.captured
|
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)
|
@dataclass(frozen=True)
|
||||||
class AirLosses:
|
class AirLosses:
|
||||||
losses: List[DebriefingDeadAircraftInfo]
|
losses: List[DebriefingDeadAircraftInfo]
|
||||||
@ -80,18 +61,12 @@ class AirLosses:
|
|||||||
return flight.count - losses
|
return flight.count - losses
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class FrontLineLosses:
|
class GroundLosses:
|
||||||
losses: List[DebriefingDeadFrontLineUnitInfo]
|
front_line: List[FrontLineUnit] = field(default_factory=list)
|
||||||
|
ground_objects: List[GroundObjectUnit] = field(default_factory=list)
|
||||||
def by_type(self, player: bool) -> Dict[Type[UnitType], int]:
|
buildings: List[Building] = field(default_factory=list)
|
||||||
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
|
airfields: List[Airfield] = field(default_factory=list)
|
||||||
for loss in self.losses:
|
|
||||||
if loss.control_point.captured != player:
|
|
||||||
continue
|
|
||||||
|
|
||||||
losses_by_type[loss.unit_type] += 1
|
|
||||||
return losses_by_type
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -131,80 +106,51 @@ class Debriefing:
|
|||||||
self.game = game
|
self.game = game
|
||||||
self.unit_map = unit_map
|
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.player_country_id = db.country_id_from_name(game.player_country)
|
||||||
self.enemy_country_id = db.country_id_from_name(game.enemy_country)
|
self.enemy_country_id = db.country_id_from_name(game.enemy_country)
|
||||||
|
|
||||||
self.air_losses = self.dead_aircraft()
|
self.air_losses = self.dead_aircraft()
|
||||||
self.front_line_losses = self.dead_front_line_units()
|
self.ground_losses = self.dead_ground_units()
|
||||||
self.dead_units = []
|
|
||||||
self.damaged_runways = self.find_damaged_runways()
|
|
||||||
self.dead_aaa_groups: List[DebriefingDeadUnitInfo] = []
|
|
||||||
self.dead_buildings: List[DebriefingDeadBuildingInfo] = []
|
|
||||||
|
|
||||||
for unit_name in self.state_data.killed_ground_units:
|
@property
|
||||||
for cp in game.theater.controlpoints:
|
def front_line_losses(self) -> Iterator[FrontLineUnit]:
|
||||||
if cp.captured:
|
yield from self.ground_losses.front_line
|
||||||
country = self.player_country_id
|
|
||||||
else:
|
|
||||||
country = self.enemy_country_id
|
|
||||||
player_unit = (country == self.player_country_id)
|
|
||||||
|
|
||||||
for ground_object in cp.ground_objects:
|
@property
|
||||||
# TODO: This seems to destroy an arbitrary building?
|
def ground_object_losses(self) -> Iterator[GroundObjectUnit]:
|
||||||
if ground_object.is_same_group(unit_name):
|
yield from self.ground_losses.ground_objects
|
||||||
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))
|
|
||||||
|
|
||||||
self.player_dead_units = [a for a in self.dead_units if a.player_unit]
|
@property
|
||||||
self.enemy_dead_units = [a for a in self.dead_units if not a.player_unit]
|
def building_losses(self) -> Iterator[Building]:
|
||||||
self.player_dead_buildings = [a for a in self.dead_buildings if a.player_unit]
|
yield from self.ground_losses.buildings
|
||||||
self.enemy_dead_buildings = [a for a in self.dead_buildings if not a.player_unit]
|
|
||||||
|
|
||||||
self.player_dead_units_dict: Dict[Type[UnitType], int] = defaultdict(int)
|
@property
|
||||||
for a in self.player_dead_units:
|
def damaged_runways(self) -> Iterator[Airfield]:
|
||||||
self.player_dead_units_dict[a.type] += 1
|
yield from self.ground_losses.airfields
|
||||||
|
|
||||||
self.enemy_dead_units_dict: Dict[Type[UnitType], int] = defaultdict(int)
|
def casualty_count(self, control_point: ControlPoint) -> int:
|
||||||
for a in self.enemy_dead_units:
|
return len(
|
||||||
self.enemy_dead_units_dict[a.type] += 1
|
[x for x in self.front_line_losses if x.origin == control_point]
|
||||||
|
)
|
||||||
|
|
||||||
self.player_dead_buildings_dict: Dict[str, int] = defaultdict(int)
|
def front_line_losses_by_type(
|
||||||
for b in self.player_dead_buildings:
|
self, player: bool) -> Dict[Type[UnitType], int]:
|
||||||
self.player_dead_buildings_dict[b.ground_object.dcs_identifier] += 1
|
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)
|
losses_by_type[loss.unit_type] += 1
|
||||||
for b in self.enemy_dead_buildings:
|
return losses_by_type
|
||||||
self.enemy_dead_buildings_dict[b.ground_object.dcs_identifier] += 1
|
|
||||||
|
|
||||||
logging.info("--------------------------------")
|
def building_losses_by_type(self, player: bool) -> Dict[str, int]:
|
||||||
logging.info("Debriefing pre process results :")
|
losses_by_type: Dict[str, int] = defaultdict(int)
|
||||||
logging.info("--------------------------------")
|
for loss in self.ground_losses.buildings:
|
||||||
logging.info(self.air_losses)
|
if loss.ground_object.control_point.captured != player:
|
||||||
logging.info(self.front_line_losses)
|
continue
|
||||||
logging.info(self.player_dead_units_dict)
|
|
||||||
logging.info(self.enemy_dead_units_dict)
|
losses_by_type[loss.ground_object.dcs_identifier] += 1
|
||||||
logging.info(self.player_dead_buildings_dict)
|
return losses_by_type
|
||||||
logging.info(self.enemy_dead_buildings_dict)
|
|
||||||
|
|
||||||
def dead_aircraft(self) -> AirLosses:
|
def dead_aircraft(self) -> AirLosses:
|
||||||
losses = []
|
losses = []
|
||||||
@ -216,30 +162,38 @@ class Debriefing:
|
|||||||
losses.append(DebriefingDeadAircraftInfo(flight))
|
losses.append(DebriefingDeadAircraftInfo(flight))
|
||||||
return AirLosses(losses)
|
return AirLosses(losses)
|
||||||
|
|
||||||
def dead_front_line_units(self) -> FrontLineLosses:
|
def dead_ground_units(self) -> GroundLosses:
|
||||||
losses = []
|
losses = GroundLosses()
|
||||||
for unit_name in self.state_data.killed_ground_units:
|
for unit_name in self.state_data.killed_ground_units:
|
||||||
unit = self.unit_map.front_line_unit(unit_name)
|
front_line_unit = self.unit_map.front_line_unit(unit_name)
|
||||||
if unit is None:
|
if front_line_unit is not None:
|
||||||
# Killed "ground units" might also be runways or TGO units, so
|
losses.front_line.append(front_line_unit)
|
||||||
# no need to log an error.
|
|
||||||
continue
|
continue
|
||||||
losses.append(
|
|
||||||
DebriefingDeadFrontLineUnitInfo(unit.unit_type, unit.origin))
|
|
||||||
return FrontLineLosses(losses)
|
|
||||||
|
|
||||||
def find_damaged_runways(self) -> List[Airfield]:
|
ground_object_unit = self.unit_map.ground_object_unit(unit_name)
|
||||||
losses = []
|
if ground_object_unit is not None:
|
||||||
for name in self.state_data.killed_ground_units:
|
losses.ground_objects.append(ground_object_unit)
|
||||||
airfield = self.unit_map.airfield(name)
|
|
||||||
if airfield is None:
|
|
||||||
continue
|
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
|
return losses
|
||||||
|
|
||||||
def _is_airfield(self, unit_name: str) -> bool:
|
|
||||||
return self.unit_map.airfield(unit_name) is not None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def base_capture_events(self):
|
def base_capture_events(self):
|
||||||
"""Keeps only the last instance of a base capture event for each base ID."""
|
"""Keeps only the last instance of a base capture event for each base ID."""
|
||||||
|
|||||||
@ -2,21 +2,20 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
from collections import defaultdict
|
from typing import Dict, List, TYPE_CHECKING, Type
|
||||||
from typing import Dict, List, Optional, TYPE_CHECKING, Type
|
|
||||||
|
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
from dcs.task import Task
|
from dcs.task import Task
|
||||||
from dcs.unittype import UnitType
|
from dcs.unittype import UnitType
|
||||||
|
|
||||||
from game import db, persistency
|
from game import persistency
|
||||||
from game.debriefing import AirLosses, Debriefing
|
from game.debriefing import AirLosses, Debriefing
|
||||||
from game.infos.information import Information
|
from game.infos.information import Information
|
||||||
|
from game.operation.operation import Operation
|
||||||
from game.theater import ControlPoint
|
from game.theater import ControlPoint
|
||||||
from gen import AirTaskingOrder
|
from gen import AirTaskingOrder
|
||||||
from gen.ground_forces.combat_stance import CombatStance
|
from gen.ground_forces.combat_stance import CombatStance
|
||||||
from ..unitmap import UnitMap
|
from ..unitmap import UnitMap
|
||||||
from game.operation.operation import Operation
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..game import Game
|
from ..game import Game
|
||||||
@ -130,12 +129,10 @@ class Event:
|
|||||||
cp.base.aircraft[aircraft] -= 1
|
cp.base.aircraft[aircraft] -= 1
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def commit_front_line_losses(debriefing: Debriefing) -> Dict[int, int]:
|
def commit_front_line_losses(debriefing: Debriefing) -> None:
|
||||||
killed_unit_count_by_cp: Dict[int, int] = defaultdict(int)
|
for loss in debriefing.front_line_losses:
|
||||||
for loss in debriefing.front_line_losses.losses:
|
|
||||||
unit_type = loss.unit_type
|
unit_type = loss.unit_type
|
||||||
control_point = loss.control_point
|
control_point = loss.origin
|
||||||
killed_unit_count_by_cp[control_point.id] += 1
|
|
||||||
available = control_point.base.total_units_of_type(unit_type)
|
available = control_point.base.total_units_of_type(unit_type)
|
||||||
if available <= 0:
|
if available <= 0:
|
||||||
logging.error(
|
logging.error(
|
||||||
@ -145,64 +142,41 @@ class Event:
|
|||||||
|
|
||||||
logging.info(f"{unit_type} destroyed from {control_point}")
|
logging.info(f"{unit_type} destroyed from {control_point}")
|
||||||
control_point.base.armor[unit_type] -= 1
|
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):
|
def commit(self, debriefing: Debriefing):
|
||||||
logging.info("Committing mission results")
|
logging.info("Committing mission results")
|
||||||
|
|
||||||
for damaged_runway in debriefing.damaged_runways:
|
|
||||||
damaged_runway.damage_runway()
|
|
||||||
|
|
||||||
self.commit_air_losses(debriefing)
|
self.commit_air_losses(debriefing)
|
||||||
killed_unit_count_by_cp = self.commit_front_line_losses(debriefing)
|
self.commit_front_line_losses(debriefing)
|
||||||
|
self.commit_ground_object_losses(debriefing)
|
||||||
# ------------------------------
|
self.commit_building_losses(debriefing)
|
||||||
# Static ground objects
|
self.commit_damaged_runways(debriefing)
|
||||||
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)
|
|
||||||
|
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
# Captured bases
|
# Captured bases
|
||||||
@ -257,8 +231,8 @@ class Event:
|
|||||||
|
|
||||||
delta = 0.0
|
delta = 0.0
|
||||||
player_won = True
|
player_won = True
|
||||||
ally_casualties = killed_unit_count_by_cp[cp.id]
|
ally_casualties = debriefing.casualty_count(cp)
|
||||||
enemy_casualties = killed_unit_count_by_cp[enemy_cp.id]
|
enemy_casualties = debriefing.casualty_count(enemy_cp)
|
||||||
ally_units_alive = cp.base.total_armor
|
ally_units_alive = cp.base.total_armor
|
||||||
enemy_units_alive = enemy_cp.base.total_armor
|
enemy_units_alive = enemy_cp.base.total_armor
|
||||||
|
|
||||||
|
|||||||
@ -265,7 +265,8 @@ class Operation:
|
|||||||
cls.current_mission,
|
cls.current_mission,
|
||||||
cls.game,
|
cls.game,
|
||||||
cls.radio_registry,
|
cls.radio_registry,
|
||||||
cls.tacan_registry
|
cls.tacan_registry,
|
||||||
|
cls.unit_map
|
||||||
)
|
)
|
||||||
cls.groundobjectgen.generate()
|
cls.groundobjectgen.generate()
|
||||||
|
|
||||||
|
|||||||
@ -23,7 +23,7 @@ class Base:
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.aircraft: Dict[Type[FlyingType], int] = {}
|
self.aircraft: Dict[Type[FlyingType], int] = {}
|
||||||
self.armor: Dict[VehicleType, int] = {}
|
self.armor: Dict[Type[VehicleType], int] = {}
|
||||||
self.aa: Dict[AirDefence, int] = {}
|
self.aa: Dict[AirDefence, int] = {}
|
||||||
self.commision_points: Dict[Type, float] = {}
|
self.commision_points: Dict[Type, float] = {}
|
||||||
self.strength = 1
|
self.strength = 1
|
||||||
|
|||||||
@ -136,6 +136,10 @@ class TheaterGroundObject(MissionTarget):
|
|||||||
]
|
]
|
||||||
yield from super().mission_types(for_player)
|
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):
|
class BuildingGroundObject(TheaterGroundObject):
|
||||||
def __init__(self, name: str, category: str, group_id: int, object_id: int,
|
def __init__(self, name: str, category: str, group_id: int, object_id: int,
|
||||||
|
|||||||
@ -2,25 +2,41 @@
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Dict, Optional, Type
|
from typing import Dict, Optional, Type
|
||||||
|
|
||||||
from dcs.unitgroup import FlyingGroup, Group
|
from dcs.unit import Unit
|
||||||
from dcs.unittype import UnitType
|
from dcs.unitgroup import FlyingGroup, Group, StaticGroup
|
||||||
|
from dcs.unittype import VehicleType
|
||||||
|
|
||||||
from game import db
|
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
|
from gen.flights.flight import Flight
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass(frozen=True)
|
||||||
class FrontLineUnit:
|
class FrontLineUnit:
|
||||||
unit_type: Type[UnitType]
|
unit_type: Type[VehicleType]
|
||||||
origin: ControlPoint
|
origin: ControlPoint
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class GroundObjectUnit:
|
||||||
|
ground_object: TheaterGroundObject
|
||||||
|
group: Group
|
||||||
|
unit: Unit
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class Building:
|
||||||
|
ground_object: BuildingGroundObject
|
||||||
|
|
||||||
|
|
||||||
class UnitMap:
|
class UnitMap:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self.aircraft: Dict[str, Flight] = {}
|
self.aircraft: Dict[str, Flight] = {}
|
||||||
self.airfields: Dict[str, Airfield] = {}
|
self.airfields: Dict[str, Airfield] = {}
|
||||||
self.front_line_units: Dict[str, FrontLineUnit] = {}
|
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:
|
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None:
|
||||||
for unit in group.units:
|
for unit in group.units:
|
||||||
@ -52,7 +68,36 @@ class UnitMap:
|
|||||||
unit_type = db.unit_type_from_name(unit.type)
|
unit_type = db.unit_type_from_name(unit.type)
|
||||||
if unit_type is None:
|
if unit_type is None:
|
||||||
raise RuntimeError(f"Unknown unit type: {unit.type}")
|
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)
|
self.front_line_units[name] = FrontLineUnit(unit_type, origin)
|
||||||
|
|
||||||
def front_line_unit(self, name: str) -> Optional[FrontLineUnit]:
|
def front_line_unit(self, name: str) -> Optional[FrontLineUnit]:
|
||||||
return self.front_line_units.get(name, None)
|
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)
|
||||||
|
|||||||
@ -9,7 +9,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
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 import Mission
|
||||||
from dcs.country import Country
|
from dcs.country import Country
|
||||||
@ -33,7 +33,7 @@ from game.theater.theatergroundobject import (
|
|||||||
GenericCarrierGroundObject,
|
GenericCarrierGroundObject,
|
||||||
LhaGroundObject, ShipGroundObject,
|
LhaGroundObject, ShipGroundObject,
|
||||||
)
|
)
|
||||||
from .conflictgen import Conflict
|
from game.unitmap import UnitMap
|
||||||
from .radios import RadioFrequency, RadioRegistry
|
from .radios import RadioFrequency, RadioRegistry
|
||||||
from .runways import RunwayData
|
from .runways import RunwayData
|
||||||
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
||||||
@ -52,11 +52,12 @@ class GenericGroundObjectGenerator:
|
|||||||
Currently used only for SAM and missile (V1/V2) sites.
|
Currently used only for SAM and missile (V1/V2) sites.
|
||||||
"""
|
"""
|
||||||
def __init__(self, ground_object: TheaterGroundObject, country: Country,
|
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.ground_object = ground_object
|
||||||
self.country = country
|
self.country = country
|
||||||
self.game = game
|
self.game = game
|
||||||
self.m = mission
|
self.m = mission
|
||||||
|
self.unit_map = unit_map
|
||||||
|
|
||||||
def generate(self) -> None:
|
def generate(self) -> None:
|
||||||
if self.game.position_culled(self.ground_object.position):
|
if self.game.position_culled(self.ground_object.position):
|
||||||
@ -89,9 +90,10 @@ class GenericGroundObjectGenerator:
|
|||||||
|
|
||||||
self.enable_eplrs(vg, unit_type)
|
self.enable_eplrs(vg, unit_type)
|
||||||
self.set_alarm_state(vg)
|
self.set_alarm_state(vg)
|
||||||
|
self._register_unit_group(vg)
|
||||||
|
|
||||||
@staticmethod
|
@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 hasattr(unit_type, 'eplrs'):
|
||||||
if unit_type.eplrs:
|
if unit_type.eplrs:
|
||||||
group.points[0].tasks.append(EPLRS(group.id))
|
group.points[0].tasks.append(EPLRS(group.id))
|
||||||
@ -102,6 +104,9 @@ class GenericGroundObjectGenerator:
|
|||||||
else:
|
else:
|
||||||
group.points[0].tasks.append(OptAlarmState(1))
|
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):
|
class BuildingSiteGenerator(GenericGroundObjectGenerator):
|
||||||
"""Generator for building sites.
|
"""Generator for building sites.
|
||||||
@ -133,16 +138,17 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator):
|
|||||||
|
|
||||||
def generate_vehicle_group(self, unit_type: UnitType) -> None:
|
def generate_vehicle_group(self, unit_type: UnitType) -> None:
|
||||||
if not self.ground_object.is_dead:
|
if not self.ground_object.is_dead:
|
||||||
self.m.vehicle_group(
|
group = self.m.vehicle_group(
|
||||||
country=self.country,
|
country=self.country,
|
||||||
name=self.ground_object.group_name,
|
name=self.ground_object.group_name,
|
||||||
_type=unit_type,
|
_type=unit_type,
|
||||||
position=self.ground_object.position,
|
position=self.ground_object.position,
|
||||||
heading=self.ground_object.heading,
|
heading=self.ground_object.heading,
|
||||||
)
|
)
|
||||||
|
self._register_unit_group(group)
|
||||||
|
|
||||||
def generate_static(self, static_type: StaticType) -> None:
|
def generate_static(self, static_type: StaticType) -> None:
|
||||||
self.m.static_group(
|
group = self.m.static_group(
|
||||||
country=self.country,
|
country=self.country,
|
||||||
name=self.ground_object.group_name,
|
name=self.ground_object.group_name,
|
||||||
_type=static_type,
|
_type=static_type,
|
||||||
@ -150,6 +156,11 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator):
|
|||||||
heading=self.ground_object.heading,
|
heading=self.ground_object.heading,
|
||||||
dead=self.ground_object.is_dead,
|
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):
|
class GenericCarrierGenerator(GenericGroundObjectGenerator):
|
||||||
@ -161,8 +172,8 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator):
|
|||||||
control_point: ControlPoint, country: Country, game: Game,
|
control_point: ControlPoint, country: Country, game: Game,
|
||||||
mission: Mission, radio_registry: RadioRegistry,
|
mission: Mission, radio_registry: RadioRegistry,
|
||||||
tacan_registry: TacanRegistry, icls_alloc: Iterator[int],
|
tacan_registry: TacanRegistry, icls_alloc: Iterator[int],
|
||||||
runways: Dict[str, RunwayData]) -> None:
|
runways: Dict[str, RunwayData], unit_map: UnitMap) -> None:
|
||||||
super().__init__(ground_object, country, game, mission)
|
super().__init__(ground_object, country, game, mission, unit_map)
|
||||||
self.ground_object = ground_object
|
self.ground_object = ground_object
|
||||||
self.control_point = control_point
|
self.control_point = control_point
|
||||||
self.radio_registry = radio_registry
|
self.radio_registry = radio_registry
|
||||||
@ -190,8 +201,9 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator):
|
|||||||
brc = self.steam_into_wind(ship_group)
|
brc = self.steam_into_wind(ship_group)
|
||||||
self.activate_beacons(ship_group, tacan, tacan_callsign, icls)
|
self.activate_beacons(ship_group, tacan, tacan_callsign, icls)
|
||||||
self.add_runway_data(brc or 0, atc, 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)
|
unit_type = unit_type_from_name(group.units[0].type)
|
||||||
if unit_type is None:
|
if unit_type is None:
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
@ -328,8 +340,9 @@ class ShipObjectGenerator(GenericGroundObjectGenerator):
|
|||||||
|
|
||||||
self.generate_group(group, unit_type)
|
self.generate_group(group, unit_type)
|
||||||
|
|
||||||
def generate_group(self, group_def: Group, unit_type: UnitType):
|
def generate_group(self, group_def: Group,
|
||||||
group = self.m.ship_group(self.country, group_def.name, unit_type,
|
first_unit_type: Type[UnitType]) -> None:
|
||||||
|
group = self.m.ship_group(self.country, group_def.name, first_unit_type,
|
||||||
position=group_def.position,
|
position=group_def.position,
|
||||||
heading=group_def.units[0].heading)
|
heading=group_def.units[0].heading)
|
||||||
group.units[0].name = self.m.string(group_def.units[0].name)
|
group.units[0].name = self.m.string(group_def.units[0].name)
|
||||||
@ -343,6 +356,7 @@ class ShipObjectGenerator(GenericGroundObjectGenerator):
|
|||||||
ship.heading = unit.heading
|
ship.heading = unit.heading
|
||||||
group.add_unit(ship)
|
group.add_unit(ship)
|
||||||
self.set_alarm_state(group)
|
self.set_alarm_state(group)
|
||||||
|
self._register_unit_group(group)
|
||||||
|
|
||||||
|
|
||||||
class GroundObjectsGenerator:
|
class GroundObjectsGenerator:
|
||||||
@ -355,11 +369,13 @@ class GroundObjectsGenerator:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, mission: Mission, game: Game,
|
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.m = mission
|
||||||
self.game = game
|
self.game = game
|
||||||
self.radio_registry = radio_registry
|
self.radio_registry = radio_registry
|
||||||
self.tacan_registry = tacan_registry
|
self.tacan_registry = tacan_registry
|
||||||
|
self.unit_map = unit_map
|
||||||
self.icls_alloc = iter(range(1, 21))
|
self.icls_alloc = iter(range(1, 21))
|
||||||
self.runways: Dict[str, RunwayData] = {}
|
self.runways: Dict[str, RunwayData] = {}
|
||||||
|
|
||||||
@ -373,25 +389,26 @@ class GroundObjectsGenerator:
|
|||||||
|
|
||||||
for ground_object in cp.ground_objects:
|
for ground_object in cp.ground_objects:
|
||||||
if isinstance(ground_object, BuildingGroundObject):
|
if isinstance(ground_object, BuildingGroundObject):
|
||||||
generator = BuildingSiteGenerator(ground_object, country,
|
generator = BuildingSiteGenerator(
|
||||||
self.game, self.m)
|
ground_object, country, self.game, self.m,
|
||||||
|
self.unit_map)
|
||||||
elif isinstance(ground_object, CarrierGroundObject):
|
elif isinstance(ground_object, CarrierGroundObject):
|
||||||
generator = CarrierGenerator(ground_object, cp, country,
|
generator = CarrierGenerator(
|
||||||
self.game, self.m,
|
ground_object, cp, country, self.game, self.m,
|
||||||
self.radio_registry,
|
self.radio_registry, self.tacan_registry,
|
||||||
self.tacan_registry,
|
self.icls_alloc, self.runways, self.unit_map)
|
||||||
self.icls_alloc, self.runways)
|
|
||||||
elif isinstance(ground_object, LhaGroundObject):
|
elif isinstance(ground_object, LhaGroundObject):
|
||||||
generator = CarrierGenerator(ground_object, cp, country,
|
generator = CarrierGenerator(
|
||||||
self.game, self.m,
|
ground_object, cp, country, self.game, self.m,
|
||||||
self.radio_registry,
|
self.radio_registry, self.tacan_registry,
|
||||||
self.tacan_registry,
|
self.icls_alloc, self.runways, self.unit_map)
|
||||||
self.icls_alloc, self.runways)
|
|
||||||
elif isinstance(ground_object, ShipGroundObject):
|
elif isinstance(ground_object, ShipGroundObject):
|
||||||
generator = ShipObjectGenerator(ground_object, country,
|
generator = ShipObjectGenerator(
|
||||||
self.game, self.m)
|
ground_object, country, self.game, self.m,
|
||||||
|
self.unit_map)
|
||||||
else:
|
else:
|
||||||
generator = GenericGroundObjectGenerator(ground_object,
|
|
||||||
country, self.game,
|
generator = GenericGroundObjectGenerator(
|
||||||
self.m)
|
ground_object, country, self.game, self.m,
|
||||||
|
self.unit_map)
|
||||||
generator.generate()
|
generator.generate()
|
||||||
|
|||||||
@ -41,13 +41,6 @@ class QDebriefingWindow(QDialog):
|
|||||||
self.layout.addWidget(header)
|
self.layout.addWidget(header)
|
||||||
self.layout.addStretch()
|
self.layout.addStretch()
|
||||||
|
|
||||||
# Result
|
|
||||||
#if self.gameEvent.is_successfull(self.debriefing):
|
|
||||||
# title = QLabel("<b>Operation end !</b>")
|
|
||||||
# title.setProperty("style", "title-success")
|
|
||||||
#else:
|
|
||||||
# title = QLabel("<b>Operation end !</b>")
|
|
||||||
# title.setProperty("style", "title-danger")
|
|
||||||
title = QLabel("<b>Casualty report</b>")
|
title = QLabel("<b>Casualty report</b>")
|
||||||
self.layout.addWidget(title)
|
self.layout.addWidget(title)
|
||||||
|
|
||||||
@ -68,7 +61,7 @@ class QDebriefingWindow(QDialog):
|
|||||||
logging.exception(
|
logging.exception(
|
||||||
f"Issue adding {unit_type} to debriefing information")
|
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
|
player=True
|
||||||
)
|
)
|
||||||
for unit_type, count in front_line_losses.items():
|
for unit_type, count in front_line_losses.items():
|
||||||
@ -81,9 +74,10 @@ class QDebriefingWindow(QDialog):
|
|||||||
logging.exception(
|
logging.exception(
|
||||||
f"Issue adding {unit_type} to debriefing information")
|
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:
|
try:
|
||||||
lostUnitsLayout.addWidget(QLabel(building, row, 0))
|
lostUnitsLayout.addWidget(QLabel(building), row, 0)
|
||||||
lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
|
lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
|
||||||
row += 1
|
row += 1
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -97,12 +91,6 @@ class QDebriefingWindow(QDialog):
|
|||||||
enemylostUnitsLayout = QGridLayout()
|
enemylostUnitsLayout = QGridLayout()
|
||||||
enemylostUnits.setLayout(enemylostUnitsLayout)
|
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)
|
enemy_air_losses = self.debriefing.air_losses.by_type(player=False)
|
||||||
for unit_type, count in enemy_air_losses.items():
|
for unit_type, count in enemy_air_losses.items():
|
||||||
try:
|
try:
|
||||||
@ -114,7 +102,7 @@ class QDebriefingWindow(QDialog):
|
|||||||
logging.exception(
|
logging.exception(
|
||||||
f"Issue adding {unit_type} to debriefing information")
|
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
|
player=False
|
||||||
)
|
)
|
||||||
for unit_type, count in front_line_losses.items():
|
for unit_type, count in front_line_losses.items():
|
||||||
@ -124,7 +112,8 @@ class QDebriefingWindow(QDialog):
|
|||||||
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||||
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:
|
try:
|
||||||
enemylostUnitsLayout.addWidget(QLabel(building), row, 0)
|
enemylostUnitsLayout.addWidget(QLabel(building), row, 0)
|
||||||
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||||
@ -135,6 +124,8 @@ class QDebriefingWindow(QDialog):
|
|||||||
|
|
||||||
self.layout.addWidget(enemylostUnits)
|
self.layout.addWidget(enemylostUnits)
|
||||||
|
|
||||||
|
# TODO: Display dead ground object units and runways.
|
||||||
|
|
||||||
# confirm button
|
# confirm button
|
||||||
okay = QPushButton("Okay")
|
okay = QPushButton("Okay")
|
||||||
okay.clicked.connect(self.close)
|
okay.clicked.connect(self.close)
|
||||||
|
|||||||
@ -138,15 +138,21 @@ class QWaitingForMissionResultWindow(QDialog):
|
|||||||
updateLayout.addWidget(
|
updateLayout.addWidget(
|
||||||
QLabel("<b>Front line units destroyed</b>"), 1, 0)
|
QLabel("<b>Front line units destroyed</b>"), 1, 0)
|
||||||
updateLayout.addWidget(
|
updateLayout.addWidget(
|
||||||
QLabel(str(len(debriefing.front_line_losses.losses))), 1, 1)
|
QLabel(str(len(list(debriefing.front_line_losses)))), 1, 1)
|
||||||
|
|
||||||
updateLayout.addWidget(
|
updateLayout.addWidget(
|
||||||
QLabel("<b>Other ground units destroyed</b>"), 2, 0)
|
QLabel("<b>Other ground units destroyed</b>"), 2, 0)
|
||||||
updateLayout.addWidget(QLabel(str(len(debriefing.dead_units))), 2, 1)
|
|
||||||
|
|
||||||
updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 3, 0)
|
|
||||||
updateLayout.addWidget(
|
updateLayout.addWidget(
|
||||||
QLabel(str(len(debriefing.base_capture_events))), 3, 1)
|
QLabel(str(len(list(debriefing.ground_object_losses)))), 2, 1)
|
||||||
|
|
||||||
|
updateLayout.addWidget(
|
||||||
|
QLabel("<b>Buildings destroyed</b>"), 3, 0)
|
||||||
|
updateLayout.addWidget(
|
||||||
|
QLabel(str(len(list(debriefing.building_losses)))), 3, 1)
|
||||||
|
|
||||||
|
updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 4, 0)
|
||||||
|
updateLayout.addWidget(
|
||||||
|
QLabel(str(len(debriefing.base_capture_events))), 4, 1)
|
||||||
|
|
||||||
# Clear previous content of the window
|
# Clear previous content of the window
|
||||||
for i in reversed(range(self.gridLayout.count())):
|
for i in reversed(range(self.gridLayout.count())):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user