Add unit name -> Liberation object map.

Generated units are added to this during mission generation so we can
map destroyed units back to the data that generated them. Currently only
implemented for aircraft as a proof of concept.
This commit is contained in:
Dan Albert 2020-11-21 13:26:47 -08:00
parent d5a081a15f
commit f6fad30852
11 changed files with 285 additions and 200 deletions

View File

@ -1,176 +1,209 @@
from __future__ import annotations
import json
import logging
import os
import threading
import time
import typing
from collections import defaultdict
from dataclasses import dataclass
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.unitmap import UnitMap
from gen.flights.flight import Flight
if TYPE_CHECKING:
from game import Game
DEBRIEFING_LOG_EXTENSION = "log"
@dataclass(frozen=True)
class DebriefingDeadUnitInfo:
country_id = -1
player_unit = False
type = None
player_unit: bool
type: Type[UnitType]
def __init__(self, country_id, player_unit , type):
self.country_id = country_id
self.player_unit = player_unit
self.type = type
def __repr__(self):
return str(self.country_id) + " " + str(self.player_unit) + " " + str(self.type)
@dataclass(frozen=True)
class DebriefingDeadAircraftInfo:
#: The Flight that resulted in the generated unit.
flight: Flight
@property
def player_unit(self) -> bool:
return self.flight.departure.captured
@dataclass(frozen=True)
class DebriefingDeadBuildingInfo:
#: The ground object this building was present at.
ground_object: TheaterGroundObject
@property
def player_unit(self) -> bool:
return self.ground_object.control_point.captured
@dataclass(frozen=True)
class AirLosses:
losses: List[DebriefingDeadAircraftInfo]
def by_type(self, player: bool) -> Dict[Type[FlyingType], int]:
losses_by_type: Dict[Type[FlyingType], int] = defaultdict(int)
for loss in self.losses:
if loss.flight.departure.captured != player:
continue
losses_by_type[loss.flight.unit_type] += loss.flight.count
return losses_by_type
@dataclass(frozen=True)
class StateData:
#: True if the mission ended. If False, the mission exited abnormally.
mission_ended: bool
#: Names of aircraft units that were killed during the mission.
killed_aircraft: List[str]
#: Names of vehicle (and ship) units that were killed during the mission.
killed_ground_units: List[str]
#: Names of static units that were destroyed during the mission.
destroyed_statics: List[str]
#: Mangled names of bases that were captured during the mission.
base_capture_events: List[str]
@classmethod
def from_json(cls, data: Dict[str, Any]) -> StateData:
return cls(
mission_ended=data["mission_ended"],
killed_aircraft=data["killed_aircrafts"],
killed_ground_units=data["killed_ground_units"],
destroyed_statics=data["destroyed_objects_positions"],
base_capture_events=data["base_capture_events"]
)
class Debriefing:
def __init__(self, state_data, game):
self.state_data = state_data
self.killed_aircrafts = state_data["killed_aircrafts"]
self.killed_ground_units = state_data["killed_ground_units"]
self.weapons_fired = state_data["weapons_fired"]
self.mission_ended = state_data["mission_ended"]
self.destroyed_units = state_data["destroyed_objects_positions"]
def __init__(self, state_data: Dict[str, Any], game: Game,
unit_map: UnitMap) -> None:
self.state_data = StateData.from_json(state_data)
self.game = game
self.unit_map = unit_map
self.__destroyed_units = []
logging.info("--------------------------------")
logging.info("Starting Debriefing preprocessing")
logging.info("--------------------------------")
logging.info(self.base_capture_events)
logging.info(self.killed_aircrafts)
logging.info(self.killed_ground_units)
logging.info(self.weapons_fired)
logging.info(self.mission_ended)
logging.info(self.destroyed_units)
logging.info(self.state_data)
logging.info("--------------------------------")
self.player_country_id = db.country_id_from_name(game.player_country)
self.enemy_country_id = db.country_id_from_name(game.enemy_country)
self.dead_aircraft = []
self.dead_units = []
self.dead_aaa_groups = []
self.dead_buildings = []
self.air_losses = self.dead_aircraft()
self.dead_units: List[DebriefingDeadUnitInfo] = []
self.dead_aaa_groups: List[DebriefingDeadUnitInfo] = []
self.dead_buildings: List[DebriefingDeadBuildingInfo] = []
for aircraft in self.killed_aircrafts:
for unit_name in self.state_data.killed_ground_units:
try:
country = int(aircraft.split("|")[1])
type = db.unit_type_from_name(aircraft.split("|")[4])
player_unit = (country == self.player_country_id)
aircraft = DebriefingDeadUnitInfo(country, player_unit, type)
if type is not None:
self.dead_aircraft.append(aircraft)
except Exception as e:
logging.error(e)
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 in self.killed_ground_units:
try:
country = int(unit.split("|")[1])
type = db.unit_type_from_name(unit.split("|")[4])
player_unit = (country == self.player_country_id)
unit = DebriefingDeadUnitInfo(country, player_unit, type)
if type is not None:
self.dead_units.append(unit)
except Exception as e:
logging.error(e)
for unit in self.killed_ground_units:
for unit_name in self.state_data.killed_ground_units:
for cp in game.theater.controlpoints:
logging.info(cp.name)
logging.info(cp.captured)
if cp.captured:
country = self.player_country_id
else:
country = self.enemy_country_id
player_unit = (country == self.player_country_id)
for i, ground_object in enumerate(cp.ground_objects):
logging.info(unit)
logging.info(ground_object.group_name)
if ground_object.is_same_group(unit):
unit = DebriefingDeadUnitInfo(country, player_unit, ground_object.dcs_identifier)
self.dead_buildings.append(unit)
elif ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
for ground_object in cp.ground_objects:
# TODO: This seems to destroy an arbitrary building?
if ground_object.is_same_group(unit_name):
self.dead_buildings.append(
DebriefingDeadBuildingInfo(ground_object))
elif ground_object.dcs_identifier in ["AA", "CARRIER",
"LHA"]:
for g in ground_object.groups:
for u in g.units:
if u.name == unit:
unit = DebriefingDeadUnitInfo(country, player_unit, db.unit_type_from_name(u.type))
self.dead_units.append(unit)
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_aircraft = [a for a in self.dead_aircraft if a.country_id == self.player_country_id]
self.enemy_dead_aircraft = [a for a in self.dead_aircraft if a.country_id == self.enemy_country_id]
self.player_dead_units = [a for a in self.dead_units if a.country_id == self.player_country_id]
self.enemy_dead_units = [a for a in self.dead_units if a.country_id == self.enemy_country_id]
self.player_dead_buildings = [a for a in self.dead_buildings if a.country_id == self.player_country_id]
self.enemy_dead_buildings = [a for a in self.dead_buildings if a.country_id == self.enemy_country_id]
self.player_dead_units = [a for a in self.dead_units if a.player_unit]
self.enemy_dead_units = [a for a in self.dead_units if not a.player_unit]
self.player_dead_buildings = [a for a in self.dead_buildings if a.player_unit]
self.enemy_dead_buildings = [a for a in self.dead_buildings if not a.player_unit]
logging.info(self.player_dead_aircraft)
logging.info(self.enemy_dead_aircraft)
logging.info(self.player_dead_units)
logging.info(self.enemy_dead_units)
self.player_dead_aircraft_dict = {}
for a in self.player_dead_aircraft:
if a.type in self.player_dead_aircraft_dict.keys():
self.player_dead_aircraft_dict[a.type] = self.player_dead_aircraft_dict[a.type] + 1
else:
self.player_dead_aircraft_dict[a.type] = 1
self.enemy_dead_aircraft_dict = {}
for a in self.enemy_dead_aircraft:
if a.type in self.enemy_dead_aircraft_dict.keys():
self.enemy_dead_aircraft_dict[a.type] = self.enemy_dead_aircraft_dict[a.type] + 1
else:
self.enemy_dead_aircraft_dict[a.type] = 1
self.player_dead_units_dict = {}
self.player_dead_units_dict: Dict[Type[UnitType], int] = defaultdict(int)
for a in self.player_dead_units:
if a.type in self.player_dead_units_dict.keys():
self.player_dead_units_dict[a.type] = self.player_dead_units_dict[a.type] + 1
else:
self.player_dead_units_dict[a.type] = 1
self.player_dead_units_dict[a.type] += 1
self.enemy_dead_units_dict = {}
self.enemy_dead_units_dict: Dict[Type[UnitType], int] = defaultdict(int)
for a in self.enemy_dead_units:
if a.type in self.enemy_dead_units_dict.keys():
self.enemy_dead_units_dict[a.type] = self.enemy_dead_units_dict[a.type] + 1
else:
self.enemy_dead_units_dict[a.type] = 1
self.enemy_dead_units_dict[a.type] += 1
self.player_dead_buildings_dict = {}
for a in self.player_dead_buildings:
if a.type in self.player_dead_buildings_dict.keys():
self.player_dead_buildings_dict[a.type] = self.player_dead_buildings_dict[a.type] + 1
else:
self.player_dead_buildings_dict[a.type] = 1
self.player_dead_buildings_dict: Dict[str, int] = defaultdict(int)
for b in self.player_dead_buildings:
self.player_dead_buildings_dict[b.ground_object.dcs_identifier] += 1
self.enemy_dead_buildings_dict = {}
for a in self.enemy_dead_buildings:
if a.type in self.enemy_dead_buildings_dict.keys():
self.enemy_dead_buildings_dict[a.type] = self.enemy_dead_buildings_dict[a.type] + 1
else:
self.enemy_dead_buildings_dict[a.type] = 1
self.enemy_dead_buildings_dict: Dict[str, int] = defaultdict(int)
for b in self.enemy_dead_buildings:
self.enemy_dead_buildings_dict[b.ground_object.dcs_identifier] += 1
logging.info("--------------------------------")
logging.info("Debriefing pre process results :")
logging.info("--------------------------------")
logging.info(self.player_dead_aircraft_dict)
logging.info(self.enemy_dead_aircraft_dict)
logging.info(self.air_losses)
logging.info(self.player_dead_units_dict)
logging.info(self.enemy_dead_units_dict)
logging.info(self.player_dead_buildings_dict)
logging.info(self.enemy_dead_buildings_dict)
def dead_aircraft(self) -> AirLosses:
losses = []
for unit_name in self.state_data.killed_aircraft:
flight = self.unit_map.flight(unit_name)
if flight is None:
logging.error(f"Could not find Flight matching {unit_name}")
continue
losses.append(DebriefingDeadAircraftInfo(flight))
return AirLosses(losses)
@property
def base_capture_events(self):
"""Keeps only the last instance of a base capture event for each base ID"""
reversed_captures = [i for i in self.state_data["base_capture_events"][::-1]]
"""Keeps only the last instance of a base capture event for each base ID."""
reversed_captures = list(reversed(self.state_data.base_capture_events))
last_base_cap_indexes = []
for idx, base in enumerate(i.split("||")[0] for i in reversed_captures):
if base in [x[1] for x in last_base_cap_indexes]:
continue
else:
if base not in [x[1] for x in last_base_cap_indexes]:
last_base_cap_indexes.append((idx, base))
return [reversed_captures[idx[0]] for idx in last_base_cap_indexes]
@ -179,11 +212,13 @@ class PollDebriefingFileThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(self, callback: typing.Callable, game):
super(PollDebriefingFileThread, self).__init__()
def __init__(self, callback: Callable[[Debriefing], None],
game: Game, unit_map: UnitMap) -> None:
super().__init__()
self._stop_event = threading.Event()
self.callback = callback
self.game = game
self.unit_map = unit_map
def stop(self):
self._stop_event.set()
@ -200,14 +235,14 @@ class PollDebriefingFileThread(threading.Thread):
if os.path.isfile("state.json") and os.path.getmtime("state.json") > last_modified:
with open("state.json", "r") as json_file:
json_data = json.load(json_file)
debriefing = Debriefing(json_data, self.game)
debriefing = Debriefing(json_data, self.game, self.unit_map)
self.callback(debriefing)
break
time.sleep(5)
def wait_for_debriefing(callback: typing.Callable, game)->PollDebriefingFileThread:
thread = PollDebriefingFileThread(callback, game)
def wait_for_debriefing(callback: Callable[[Debriefing], None],
game: Game, unit_map) -> PollDebriefingFileThread:
thread = PollDebriefingFileThread(callback, game, unit_map)
thread.start()
return thread

View File

@ -13,6 +13,7 @@ from game.debriefing import Debriefing
from game.infos.information import Information
from game.theater import ControlPoint
from gen.ground_forces.combat_stance import CombatStance
from ..unitmap import UnitMap
if TYPE_CHECKING:
from ..game import Game
@ -85,17 +86,18 @@ class Event:
def bonus(self) -> int:
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
def is_successfull(self, debriefing: Debriefing) -> bool:
return self.operation.is_successfull(debriefing)
def is_successful(self, debriefing: Debriefing) -> bool:
return self.operation.is_successful(debriefing)
def generate(self):
def generate(self) -> UnitMap:
self.operation.is_awacs_enabled = self.is_awacs_enabled
self.operation.ca_slots = self.ca_slots
self.operation.prepare(self.game)
self.operation.generate()
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
self.environment_settings = self.operation.environment_settings
unit_map = self.operation.generate()
self.operation.current_mission.save(
persistency.mission_path_for("liberation_nextturn.miz"))
return unit_map
def commit(self, debriefing: Debriefing):
@ -103,41 +105,39 @@ class Event:
# ------------------------------
# Destroyed aircrafts
cp_map = {cp.id: cp for cp in self.game.theater.controlpoints}
for destroyed_aircraft in debriefing.killed_aircrafts:
try:
cpid = int(destroyed_aircraft.split("|")[3])
aircraft = db.unit_type_from_name(
destroyed_aircraft.split("|")[4])
if cpid in cp_map:
cp = cp_map[cpid]
if aircraft in cp.base.aircraft:
logging.info(f"Aircraft destroyed: {aircraft}")
cp.base.aircraft[aircraft] = max(
0, cp.base.aircraft[aircraft] - 1)
except Exception:
logging.exception("Failed to commit destroyed aircraft")
for loss in debriefing.air_losses.losses:
aircraft = loss.flight.unit_type
cp = loss.flight.departure
available = cp.base.total_units_of_type(aircraft)
if available <= 0:
logging.error(
f"Found killed {aircraft} from {cp} but that airbase has "
f"none available.")
continue
logging.info(f"{aircraft} destroyed from {cp}")
cp.base.aircraft[aircraft] -= 1
# ------------------------------
# Destroyed ground units
killed_unit_count_by_cp = {cp.id: 0 for cp in self.game.theater.controlpoints}
cp_map = {cp.id: cp for cp in self.game.theater.controlpoints}
for killed_ground_unit in debriefing.killed_ground_units:
for killed_ground_unit in debriefing.state_data.killed_ground_units:
try:
cpid = int(killed_ground_unit.split("|")[3])
aircraft = db.unit_type_from_name(killed_ground_unit.split("|")[4])
unit_type = db.unit_type_from_name(killed_ground_unit.split("|")[4])
if cpid in cp_map.keys():
killed_unit_count_by_cp[cpid] = killed_unit_count_by_cp[cpid] + 1
cp = cp_map[cpid]
if aircraft in cp.base.armor.keys():
logging.info("Ground unit destroyed : " + str(aircraft))
cp.base.armor[aircraft] = max(0, cp.base.armor[aircraft] - 1)
if unit_type in cp.base.armor.keys():
logging.info(f"Ground unit destroyed: {unit_type}")
cp.base.armor[unit_type] = max(0, cp.base.armor[unit_type] - 1)
except Exception as e:
print(e)
# ------------------------------
# Static ground objects
for destroyed_ground_unit_name in debriefing.killed_ground_units:
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
@ -224,7 +224,7 @@ class Event:
# Destroyed units carcass
# -------------------------
for destroyed_unit in debriefing.destroyed_units:
for destroyed_unit in debriefing.state_data.destroyed_statics:
self.game.add_destroyed_units(destroyed_unit)
# -----------------------------------

View File

@ -23,7 +23,7 @@ class FrontlineAttackEvent(Event):
def __str__(self):
return "Frontline attack"
def is_successfull(self, debriefing: Debriefing):
def is_successful(self, debriefing: Debriefing):
attackers_success = True
if self.from_cp.captured:
return attackers_success

View File

@ -27,6 +27,7 @@ from .factions.faction import Faction
from .infos.information import Information
from .settings import Settings
from .theater import ConflictTheater, ControlPoint, OffMapSpawn
from .unitmap import UnitMap
from .weather import Conditions, TimeOfDay
COMMISION_UNIT_VARIETY = 4
@ -173,15 +174,15 @@ class Game:
self.events.append(event)
return event
def initiate_event(self, event: Event):
def initiate_event(self, event: Event) -> UnitMap:
#assert event in self.events
logging.info("Generating {} (regular)".format(event))
event.generate()
return event.generate()
def finish_event(self, event: Event, debriefing: Debriefing):
logging.info("Finishing event {}".format(event))
event.commit(debriefing)
if event.is_successfull(debriefing):
if event.is_successful(debriefing):
self.budget += event.bonus()
if event in self.events:

View File

@ -34,6 +34,7 @@ from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
from .. import db
from ..debriefing import Debriefing
from ..unitmap import UnitMap
if TYPE_CHECKING:
from game import Game
@ -59,6 +60,7 @@ class Operation:
is_quick = None
is_awacs_enabled = False
ca_slots = 0
unit_map: UnitMap
def __init__(self,
departure_cp: ControlPoint,
@ -94,7 +96,7 @@ class Operation:
def units_of(self, country_name: str) -> List[UnitType]:
return []
def is_successfull(self, debriefing: Debriefing) -> bool:
def is_successful(self, debriefing: Debriefing) -> bool:
return True
@classmethod
@ -177,6 +179,10 @@ class Operation:
gen.add_flight(flight)
gen.generate()
@classmethod
def create_unit_map(cls) -> None:
cls.unit_map = UnitMap()
@classmethod
def create_radio_registries(cls) -> None:
unique_map_frequencies = set() # type: Set[RadioFrequency]
@ -269,8 +275,9 @@ class Operation:
dead=True,
)
def generate(self):
def generate(self) -> UnitMap:
"""Build the final Mission to be exported"""
self.create_unit_map()
self.create_radio_registries()
# Set mission time and weather conditions.
EnvironmentGenerator(self.current_mission,
@ -323,6 +330,8 @@ class Operation:
self.airgen
)
return self.unit_map
@classmethod
def _generate_air_units(cls) -> None:
"""Generate the air units for the Operation"""
@ -339,7 +348,7 @@ class Operation:
# Generate Aircraft Activity on the map
cls.airgen = AircraftConflictGenerator(
cls.current_mission, cls.game.settings, cls.game,
cls.radio_registry)
cls.radio_registry, cls.unit_map)
cls.airgen.generate_flights(
cls.current_mission.country(cls.game.player_country),

23
game/unitmap.py Normal file
View File

@ -0,0 +1,23 @@
"""Maps generated units back to their Liberation types."""
from typing import Dict, Optional
from dcs.unitgroup import FlyingGroup
from gen.flights.flight import Flight
class UnitMap:
def __init__(self) -> None:
self.aircraft: Dict[str, Flight] = {}
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> 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.aircraft:
raise RuntimeError(f"Duplicate unit name: {name}")
self.aircraft[name] = flight
def flight(self, group_name: str) -> Optional[Flight]:
return self.aircraft.get(group_name, None)

View File

@ -76,6 +76,7 @@ from game.theater.controlpoint import (
OffMapSpawn,
)
from game.theater.theatergroundobject import TheaterGroundObject
from game.unitmap import UnitMap
from game.utils import knots_to_kph, nm_to_meter
from gen.airsupportgen import AirSupport
from gen.ato import AirTaskingOrder, Package
@ -647,12 +648,13 @@ AIRCRAFT_DATA["P-47D-30"] = AIRCRAFT_DATA["P-51D"]
class AircraftConflictGenerator:
def __init__(self, mission: Mission, settings: Settings,
game: Game, radio_registry: RadioRegistry):
def __init__(self, mission: Mission, settings: Settings, game: Game,
radio_registry: RadioRegistry, unit_map: UnitMap) -> None:
self.m = mission
self.game = game
self.settings = settings
self.radio_registry = radio_registry
self.unit_map = unit_map
self.flights: List[FlightData] = []
@cached_property
@ -927,6 +929,7 @@ class AircraftConflictGenerator:
logging.info(f"Generating flight: {flight.unit_type}")
group = self.generate_planned_flight(flight.from_cp, country,
flight)
self.unit_map.add_aircraft(group, flight)
self.setup_flight_group(group, package, flight, dynamic_runways)
self.create_waypoints(group, package, flight)

View File

@ -235,8 +235,9 @@ class QTopPanel(QFrame):
game_event.departure_cp = self.game.theater.controlpoints[0]
game_event.player_attacking()
self.game.initiate_event(game_event)
waiting = QWaitingForMissionResultWindow(game_event, self.game)
unit_map = self.game.initiate_event(game_event)
waiting = QWaitingForMissionResultWindow(game_event, self.game,
unit_map)
waiting.show()
def budget_update(self, game:Game):

View File

@ -1,3 +1,5 @@
import logging
from PySide2.QtGui import QIcon, QPixmap
from PySide2.QtWidgets import (
QDialog,
@ -55,29 +57,35 @@ class QDebriefingWindow(QDialog):
lostUnits.setLayout(lostUnitsLayout)
row = 0
for unit_type, count in self.debriefing.player_dead_aircraft_dict.items():
player_air_losses = self.debriefing.air_losses.by_type(player=True)
for unit_type, count in player_air_losses.items():
try:
lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
lostUnitsLayout.addWidget(
QLabel(db.unit_type_name(unit_type)), row, 0)
lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
row += 1
except:
print("Issue adding " + str(unit_type) + " to debriefing information")
except AttributeError:
logging.exception(
f"Issue adding {unit_type} to debriefing information")
for unit_type, count in self.debriefing.player_dead_units_dict.items():
try:
lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
lostUnitsLayout.addWidget(
QLabel(db.unit_type_name(unit_type)), row, 0)
lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
row += 1
except:
print("Issue adding " + str(unit_type) + " to debriefing information")
except AttributeError:
logging.exception(
f"Issue adding {unit_type} to debriefing information")
for building, count in self.debriefing.player_dead_buildings_dict.items():
try:
lostUnitsLayout.addWidget(QLabel(building, row, 0))
lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
lostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
row += 1
except:
print("Issue adding " + str(building) + " to debriefing information")
except AttributeError:
logging.exception(
f"Issue adding {building} to debriefing information")
self.layout.addWidget(lostUnits)
@ -92,15 +100,16 @@ class QDebriefingWindow(QDialog):
# enemylostUnitsLayout.addWidget(QLabel("{}".format(len(self.debriefing.destroyed_objects))), row, 1)
# row += 1
for unit_type, count in self.debriefing.enemy_dead_aircraft_dict.items():
if count == 0:
continue
enemy_air_losses = self.debriefing.air_losses.by_type(player=False)
for unit_type, count in enemy_air_losses.items():
try:
enemylostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
enemylostUnitsLayout.addWidget(
QLabel(db.unit_type_name(unit_type)), row, 0)
enemylostUnitsLayout.addWidget(QLabel(str(count)), row, 1)
row += 1
except:
print("Issue adding " + str(unit_type) + " to debriefing information")
except AttributeError:
logging.exception(
f"Issue adding {unit_type} to debriefing information")
for unit_type, count in self.debriefing.enemy_dead_units_dict.items():
if count == 0:
@ -114,8 +123,9 @@ class QDebriefingWindow(QDialog):
enemylostUnitsLayout.addWidget(QLabel(building), row, 0)
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
row += 1
except:
print("Issue adding " + str(building) + " to debriefing information")
except AttributeError:
logging.exception(
f"Issue adding {building} to debriefing information")
self.layout.addWidget(enemylostUnits)

View File

@ -20,6 +20,7 @@ from jinja2 import Environment, FileSystemLoader, select_autoescape
from game.debriefing import Debriefing, wait_for_debriefing
from game.game import Event, Game, logging
from game.persistency import base_path
from game.unitmap import UnitMap
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
@ -39,22 +40,27 @@ class DebriefingFileWrittenSignal(QObject):
def get_instance():
return DebriefingFileWrittenSignal.instance
DebriefingFileWrittenSignal()
class QWaitingForMissionResultWindow(QDialog):
def __init__(self, gameEvent: Event, game: Game):
def __init__(self, gameEvent: Event, game: Game, unit_map: UnitMap) -> None:
super(QWaitingForMissionResultWindow, self).__init__()
self.setModal(True)
self.gameEvent = gameEvent
self.game = game
self.unit_map = unit_map
self.setWindowTitle("Waiting for mission completion.")
self.setWindowIcon(QIcon("./resources/icon.png"))
self.setMinimumHeight(570)
self.initUi()
DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(self.updateLayout)
self.wait_thread = wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game)
self.wait_thread = wait_for_debriefing(
lambda debriefing: self.on_debriefing_update(debriefing), self.game,
self.unit_map)
def initUi(self):
self.layout = QGridLayout()
@ -119,17 +125,17 @@ class QWaitingForMissionResultWindow(QDialog):
self.layout.addLayout(self.gridLayout, 1, 0)
self.setLayout(self.layout)
def updateLayout(self, debriefing):
def updateLayout(self, debriefing: Debriefing) -> None:
updateBox = QGroupBox("Mission status")
updateLayout = QGridLayout()
updateBox.setLayout(updateLayout)
self.debriefing = debriefing
updateLayout.addWidget(QLabel("<b>Aircraft destroyed</b>"), 0, 0)
updateLayout.addWidget(QLabel(str(len(debriefing.killed_aircrafts))), 0, 1)
updateLayout.addWidget(QLabel(str(len(debriefing.air_losses.losses))), 0, 1)
updateLayout.addWidget(QLabel("<b>Ground units destroyed</b>"), 1, 0)
updateLayout.addWidget(QLabel(str(len(debriefing.killed_ground_units))), 1, 1)
updateLayout.addWidget(QLabel(str(len(debriefing.dead_units))), 1, 1)
#updateLayout.addWidget(QLabel("<b>Weapons fired</b>"), 2, 0)
#updateLayout.addWidget(QLabel(str(len(debriefing.weapons_fired))), 2, 1)
@ -142,28 +148,27 @@ class QWaitingForMissionResultWindow(QDialog):
try:
self.gridLayout.itemAt(i).widget().setParent(None)
except:
pass
logging.exception("Failed to clear window")
# Set new window content
self.gridLayout.addWidget(updateBox, 0, 0)
if not debriefing.mission_ended:
if not debriefing.state_data.mission_ended:
self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"), 1, 0)
self.gridLayout.addWidget(self.actions, 2, 0)
else:
self.gridLayout.addWidget(QLabel("<b>Mission is over</b>"), 1, 0)
self.gridLayout.addWidget(self.actions2, 2, 0)
def on_debriefing_udpate(self, debriefing):
def on_debriefing_update(self, debriefing: Debriefing) -> None:
try:
logging.info("On Debriefing update")
print(debriefing)
logging.debug(debriefing)
DebriefingFileWrittenSignal.get_instance().sendDebriefing(debriefing)
except Exception as e:
logging.error("Got an error while sending debriefing")
logging.error(e)
self.wait_thread = wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game)
except Exception:
logging.exception("Got an error while sending debriefing")
self.wait_thread = wait_for_debriefing(
lambda d: self.on_debriefing_update(d), self.game, self.unit_map)
def process_debriefing(self):
self.game.finish_event(event=self.gameEvent, debriefing=self.debriefing)
@ -187,8 +192,8 @@ class QWaitingForMissionResultWindow(QDialog):
with open(file[0], "r") as json_file:
json_data = json.load(json_file)
json_data["mission_ended"] = True
debriefing = Debriefing(json_data, self.game)
self.on_debriefing_udpate(debriefing)
debriefing = Debriefing(json_data, self.game, self.unit_map)
self.on_debriefing_update(debriefing)
except Exception as e:
logging.error(e)
msg = QMessageBox()

View File

@ -6,7 +6,6 @@ logger:info("Check that json.lua is loaded : json = "..tostring(json))
killed_aircrafts = {}
killed_ground_units = {}
weapons_fired = {}
base_capture_events = {}
destroyed_objects_positions = {}
mission_ended = false
@ -33,7 +32,6 @@ function write_state()
local game_state = {
["killed_aircrafts"] = killed_aircrafts,
["killed_ground_units"] = killed_ground_units,
["weapons_fired"] = weapons_fired,
["base_capture_events"] = base_capture_events,
["mission_ended"] = mission_ended,
["destroyed_objects_positions"] = destroyed_objects_positions,