Track airlift cargo kills.

https://github.com/Khopa/dcs_liberation/issues/825
This commit is contained in:
Dan Albert 2021-04-22 19:22:41 -07:00
parent e474748f4d
commit 29b70b3247
6 changed files with 120 additions and 8 deletions

View File

@ -22,7 +22,14 @@ from dcs.unittype import FlyingType, UnitType
from game import db from game import db
from game.theater import Airfield, ControlPoint from game.theater import Airfield, ControlPoint
from game.unitmap import Building, ConvoyUnit, FrontLineUnit, GroundObjectUnit, UnitMap from game.unitmap import (
AirliftUnit,
Building,
ConvoyUnit,
FrontLineUnit,
GroundObjectUnit,
UnitMap,
)
from gen.flights.flight import Flight from gen.flights.flight import Flight
if TYPE_CHECKING: if TYPE_CHECKING:
@ -63,6 +70,9 @@ class GroundLosses:
player_convoy: List[ConvoyUnit] = field(default_factory=list) player_convoy: List[ConvoyUnit] = field(default_factory=list)
enemy_convoy: List[ConvoyUnit] = field(default_factory=list) enemy_convoy: List[ConvoyUnit] = field(default_factory=list)
player_airlifts: List[AirliftUnit] = field(default_factory=list)
enemy_airlifts: List[AirliftUnit] = field(default_factory=list)
player_ground_objects: List[GroundObjectUnit] = field(default_factory=list) player_ground_objects: List[GroundObjectUnit] = field(default_factory=list)
enemy_ground_objects: List[GroundObjectUnit] = field(default_factory=list) enemy_ground_objects: List[GroundObjectUnit] = field(default_factory=list)
@ -128,6 +138,11 @@ class Debriefing:
yield from self.ground_losses.player_convoy yield from self.ground_losses.player_convoy
yield from self.ground_losses.enemy_convoy yield from self.ground_losses.enemy_convoy
@property
def airlift_losses(self) -> Iterator[AirliftUnit]:
yield from self.ground_losses.player_airlifts
yield from self.ground_losses.enemy_airlifts
@property @property
def ground_object_losses(self) -> Iterator[GroundObjectUnit]: def ground_object_losses(self) -> Iterator[GroundObjectUnit]:
yield from self.ground_losses.player_ground_objects yield from self.ground_losses.player_ground_objects
@ -166,6 +181,16 @@ class Debriefing:
losses_by_type[loss.unit_type] += 1 losses_by_type[loss.unit_type] += 1
return losses_by_type return losses_by_type
def airlift_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
if player:
losses = self.ground_losses.player_airlifts
else:
losses = self.ground_losses.enemy_airlifts
for loss in losses:
losses_by_type[loss.unit_type] += 1
return losses_by_type
def building_losses_by_type(self, player: bool) -> Dict[str, int]: def building_losses_by_type(self, player: bool) -> Dict[str, int]:
losses_by_type: Dict[str, int] = defaultdict(int) losses_by_type: Dict[str, int] = defaultdict(int)
if player: if player:
@ -250,6 +275,15 @@ class Debriefing:
"have no effect. This may be normal behavior." "have no effect. This may be normal behavior."
) )
for unit_name in self.state_data.killed_aircraft:
airlift_unit = self.unit_map.airlift_unit(unit_name)
if airlift_unit is not None:
if airlift_unit.transfer.player:
losses.player_airlifts.append(airlift_unit)
else:
losses.enemy_airlifts.append(airlift_unit)
continue
return losses return losses
@property @property

View File

@ -172,6 +172,23 @@ class Event:
logging.info(f"{unit_type} destroyed in {convoy_name}") logging.info(f"{unit_type} destroyed in {convoy_name}")
convoy.kill_unit(unit_type) convoy.kill_unit(unit_type)
@staticmethod
def commit_airlift_losses(debriefing: Debriefing) -> None:
for loss in debriefing.airlift_losses:
unit_type = loss.unit_type
transfer = loss.transfer
available = loss.transfer.units.get(unit_type, 0)
airlift_name = f"airlift from {transfer.origin} to {transfer.destination}"
if available <= 0:
logging.error(
f"Found killed {unit_type} in {airlift_name} but that airlift has "
"none available."
)
continue
logging.info(f"{unit_type} destroyed in {airlift_name}")
transfer.kill_unit(unit_type)
@staticmethod @staticmethod
def commit_ground_object_losses(debriefing: Debriefing) -> None: def commit_ground_object_losses(debriefing: Debriefing) -> None:
for loss in debriefing.ground_object_losses: for loss in debriefing.ground_object_losses:
@ -205,6 +222,7 @@ class Event:
self.commit_air_losses(debriefing) self.commit_air_losses(debriefing)
self.commit_front_line_losses(debriefing) self.commit_front_line_losses(debriefing)
self.commit_convoy_losses(debriefing) self.commit_convoy_losses(debriefing)
self.commit_airlift_losses(debriefing)
self.commit_ground_object_losses(debriefing) self.commit_ground_object_losses(debriefing)
self.commit_building_losses(debriefing) self.commit_building_losses(debriefing)
self.commit_damaged_runways(debriefing) self.commit_damaged_runways(debriefing)

View File

@ -78,6 +78,17 @@ class AirliftOrder(TransferOrder):
def description(self) -> str: def description(self) -> str:
return "Airlift" return "Airlift"
def iter_units(self) -> Iterator[Type[VehicleType]]:
for unit_type, count in self.units.items():
for _ in range(count):
yield unit_type
def kill_unit(self, unit_type: Type[VehicleType]) -> None:
if unit_type in self.units:
self.units[unit_type] -= 1
return
raise KeyError
class Convoy(MissionTarget): class Convoy(MissionTarget):
def __init__(self, origin: ControlPoint, destination: ControlPoint) -> None: def __init__(self, origin: ControlPoint, destination: ControlPoint) -> None:

View File

@ -9,7 +9,7 @@ from dcs.unittype import VehicleType
from game import db from game import db
from game.theater import Airfield, ControlPoint, TheaterGroundObject from game.theater import Airfield, ControlPoint, TheaterGroundObject
from game.theater.theatergroundobject import BuildingGroundObject from game.theater.theatergroundobject import BuildingGroundObject
from game.transfers import Convoy, RoadTransferOrder from game.transfers import AirliftOrder, Convoy
from gen.flights.flight import Flight from gen.flights.flight import Flight
@ -32,6 +32,12 @@ class ConvoyUnit:
convoy: Convoy convoy: Convoy
@dataclass(frozen=True)
class AirliftUnit:
unit_type: Type[VehicleType]
transfer: AirliftOrder
@dataclass(frozen=True) @dataclass(frozen=True)
class Building: class Building:
ground_object: BuildingGroundObject ground_object: BuildingGroundObject
@ -45,6 +51,7 @@ class UnitMap:
self.ground_object_units: Dict[str, GroundObjectUnit] = {} self.ground_object_units: Dict[str, GroundObjectUnit] = {}
self.buildings: Dict[str, Building] = {} self.buildings: Dict[str, Building] = {}
self.convoys: Dict[str, ConvoyUnit] = {} self.convoys: Dict[str, ConvoyUnit] = {}
self.airlifts: Dict[str, AirliftUnit] = {}
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:
@ -54,6 +61,8 @@ class UnitMap:
if name in self.aircraft: if name in self.aircraft:
raise RuntimeError(f"Duplicate unit name: {name}") raise RuntimeError(f"Duplicate unit name: {name}")
self.aircraft[name] = flight self.aircraft[name] = flight
if flight.cargo is not None:
self.add_airlift_units(group, flight.cargo)
def flight(self, unit_name: str) -> Optional[Flight]: def flight(self, unit_name: str) -> Optional[Flight]:
return self.aircraft.get(unit_name, None) return self.aircraft.get(unit_name, None)
@ -140,6 +149,21 @@ class UnitMap:
def convoy_unit(self, name: str) -> Optional[ConvoyUnit]: def convoy_unit(self, name: str) -> Optional[ConvoyUnit]:
return self.convoys.get(name, None) return self.convoys.get(name, None)
def add_airlift_units(self, group: FlyingGroup, airlift: AirliftOrder) -> None:
for transport, cargo_type in zip(group.units, airlift.iter_units()):
# The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__.
name = str(transport.name)
if name in self.airlifts:
raise RuntimeError(f"Duplicate airlift unit: {name}")
unit_type = db.unit_type_from_name(transport.type)
if unit_type is None:
raise RuntimeError(f"Unknown unit type: {transport.type}")
self.airlifts[name] = AirliftUnit(cargo_type, airlift)
def airlift_unit(self, name: str) -> Optional[AirliftUnit]:
return self.airlifts.get(name, None)
def add_building(self, ground_object: BuildingGroundObject, group: Group) -> None: def add_building(self, ground_object: BuildingGroundObject, group: Group) -> None:
# The actual name is a String (the pydcs translatable string), which # The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__. # doesn't define __eq__.

View File

@ -83,6 +83,17 @@ class QDebriefingWindow(QDialog):
except AttributeError: except AttributeError:
logging.exception(f"Issue adding {unit_type} to debriefing information") logging.exception(f"Issue adding {unit_type} to debriefing information")
airlift_losses = self.debriefing.airlift_losses_by_type(player=True)
for unit_type, count in airlift_losses.items():
try:
lostUnitsLayout.addWidget(
QLabel(f"{db.unit_type_name(unit_type)} from airlift"), row, 0
)
lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
row += 1
except AttributeError:
logging.exception(f"Issue adding {unit_type} to debriefing information")
building_losses = self.debriefing.building_losses_by_type(player=True) building_losses = self.debriefing.building_losses_by_type(player=True)
for building, count in building_losses.items(): for building, count in building_losses.items():
try: try:
@ -135,6 +146,17 @@ class QDebriefingWindow(QDialog):
except AttributeError: except AttributeError:
logging.exception(f"Issue adding {unit_type} to debriefing information") logging.exception(f"Issue adding {unit_type} to debriefing information")
airlift_losses = self.debriefing.airlift_losses_by_type(player=False)
for unit_type, count in airlift_losses.items():
try:
lostUnitsLayout.addWidget(
QLabel(f"{db.unit_type_name(unit_type)} from airlift"), row, 0
)
lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
row += 1
except AttributeError:
logging.exception(f"Issue adding {unit_type} to debriefing information")
building_losses = self.debriefing.building_losses_by_type(player=False) building_losses = self.debriefing.building_losses_by_type(player=False)
for building, count in building_losses.items(): for building, count in building_losses.items():
try: try:

View File

@ -151,16 +151,19 @@ class QWaitingForMissionResultWindow(QDialog):
updateLayout.addWidget(QLabel("<b>Convoy units destroyed</b>"), 2, 0) updateLayout.addWidget(QLabel("<b>Convoy units destroyed</b>"), 2, 0)
updateLayout.addWidget(QLabel(str(len(list(debriefing.convoy_losses)))), 2, 1) updateLayout.addWidget(QLabel(str(len(list(debriefing.convoy_losses)))), 2, 1)
updateLayout.addWidget(QLabel("<b>Other ground units destroyed</b>"), 3, 0) updateLayout.addWidget(QLabel("<b>Airlift cargo destroyed</b>"), 3, 0)
updateLayout.addWidget(QLabel(str(len(list(debriefing.airlift_losses)))), 3, 1)
updateLayout.addWidget(QLabel("<b>Other ground units destroyed</b>"), 4, 0)
updateLayout.addWidget( updateLayout.addWidget(
QLabel(str(len(list(debriefing.ground_object_losses)))), 3, 1 QLabel(str(len(list(debriefing.ground_object_losses)))), 4, 1
) )
updateLayout.addWidget(QLabel("<b>Buildings destroyed</b>"), 4, 0) updateLayout.addWidget(QLabel("<b>Buildings destroyed</b>"), 5, 0)
updateLayout.addWidget(QLabel(str(len(list(debriefing.building_losses)))), 4, 1) updateLayout.addWidget(QLabel(str(len(list(debriefing.building_losses)))), 5, 1)
updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 5, 0) updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 6, 0)
updateLayout.addWidget(QLabel(str(len(debriefing.base_capture_events))), 5, 1) updateLayout.addWidget(QLabel(str(len(debriefing.base_capture_events))), 6, 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())):