Migrate buildings and TGOs to unit map.

Fixes https://github.com/Khopa/dcs_liberation/issues/485.
This commit is contained in:
Dan Albert 2020-12-04 00:35:05 -08:00
parent 13f4baa34e
commit 90697194a1
9 changed files with 238 additions and 246 deletions

View File

@ -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."""

View File

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

View File

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

View File

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

View File

@ -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,

View File

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

View File

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

View File

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

View File

@ -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())):