Handle runway damage in the debrief.

Apparently we were already getting this info because it's a unit like
any other according to the event system, so if runways were actually
sufficiently damaged we'd emit a ton of exceptions.

This doesn't do anything yet, but tracks the damage state. Will add the
ability to repair them next, and then finally make the damage render the
runway inoperable.
This commit is contained in:
Dan Albert 2020-11-25 13:10:48 -08:00
parent ef0e565337
commit 0c4e920af3
5 changed files with 58 additions and 21 deletions

View File

@ -12,7 +12,7 @@ from typing import Any, Callable, Dict, List, Type, TYPE_CHECKING
from dcs.unittype import FlyingType, UnitType
from game import db
from game.theater import TheaterGroundObject
from game.theater import Airfield, TheaterGroundObject
from game.unitmap import UnitMap
from gen.flights.flight import Flight
@ -91,7 +91,9 @@ class StateData:
return cls(
mission_ended=data["mission_ended"],
killed_aircraft=data["killed_aircrafts"],
killed_ground_units=data["killed_ground_units"],
# Airfields emit a new "dead" event every time a bomb is dropped on
# them when they've already dead. Dedup.
killed_ground_units=list(set(data["killed_ground_units"])),
destroyed_statics=data["destroyed_objects_positions"],
base_capture_events=data["base_capture_events"]
)
@ -114,28 +116,11 @@ class Debriefing:
self.enemy_country_id = db.country_id_from_name(game.enemy_country)
self.air_losses = self.dead_aircraft()
self.dead_units: List[DebriefingDeadUnitInfo] = []
self.dead_units = self.dead_ground_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:
try:
if isinstance(unit_name, int):
# For some reason the state file will include many raw
# integers in the list of destroyed units. These might be
# from the smoke effects?
continue
country = int(unit_name.split("|")[1])
unit_type = db.unit_type_from_name(unit_name.split("|")[4])
if unit_type is None:
logging.error(f"Could not determine type of {unit_name}")
continue
player_unit = country == self.player_country_id
self.dead_units.append(
DebriefingDeadUnitInfo(player_unit, unit_type))
except Exception:
logging.exception(f"Failed to process dead unit {unit_name}")
for unit_name in self.state_data.killed_ground_units:
for cp in game.theater.controlpoints:
if cp.captured:
@ -204,6 +189,40 @@ class Debriefing:
losses.append(DebriefingDeadAircraftInfo(flight))
return AirLosses(losses)
def dead_ground_units(self) -> List[DebriefingDeadUnitInfo]:
losses = []
for unit_name in self.state_data.killed_ground_units:
try:
if isinstance(unit_name, int):
# For some reason the state file will include many raw
# integers in the list of destroyed units. These might be
# from the smoke effects?
continue
if self._is_airfield(unit_name):
continue
country = int(unit_name.split("|")[1])
unit_type = db.unit_type_from_name(unit_name.split("|")[4])
if unit_type is None:
logging.error(f"Could not determine type of {unit_name}")
continue
player_unit = country == self.player_country_id
losses.append(DebriefingDeadUnitInfo(player_unit, unit_type))
except Exception:
logging.exception(f"Failed to process dead unit {unit_name}")
return 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:
continue
losses.append(airfield)
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."""

View File

@ -117,6 +117,9 @@ class Event:
logging.info("Commiting mission results")
for damaged_runway in debriefing.damaged_runways:
damaged_runway.damaged = True
# ------------------------------
# Destroyed aircrafts
for loss in debriefing.air_losses.losses:

View File

@ -32,6 +32,7 @@ from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
from .. import db
from ..debriefing import Debriefing
from ..theater import Airfield
from ..unitmap import UnitMap
if TYPE_CHECKING:
@ -173,6 +174,9 @@ class Operation:
@classmethod
def create_unit_map(cls) -> None:
cls.unit_map = UnitMap()
for control_point in cls.game.theater.controlpoints:
if isinstance(control_point, Airfield):
cls.unit_map.add_airfield(control_point)
@classmethod
def create_radio_registries(cls) -> None:

View File

@ -376,6 +376,7 @@ class Airfield(ControlPoint):
size, importance, has_frontline,
cptype=ControlPointType.AIRBASE)
self.airport = airport
self.damaged = False
def can_land(self, aircraft: FlyingType) -> bool:
return True

View File

@ -3,12 +3,14 @@ from typing import Dict, Optional
from dcs.unitgroup import FlyingGroup
from game.theater import Airfield
from gen.flights.flight import Flight
class UnitMap:
def __init__(self) -> None:
self.aircraft: Dict[str, Flight] = {}
self.airfields: Dict[str, Airfield] = {}
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None:
for unit in group.units:
@ -21,3 +23,11 @@ class UnitMap:
def flight(self, unit_name: str) -> Optional[Flight]:
return self.aircraft.get(unit_name, None)
def add_airfield(self, airfield: Airfield) -> None:
if airfield.name in self.airfields:
raise RuntimeError(f"Duplicate airfield: {airfield.name}")
self.airfields[airfield.name] = airfield
def airfield(self, name: str) -> Optional[Airfield]:
return self.airfields.get(name, None)