New automatic de-briefing system implemented

This commit is contained in:
Khopa 2019-10-13 03:40:22 +02:00
parent 4365db0fae
commit 3f5f4f4bb1
18 changed files with 309 additions and 696 deletions

View File

@ -2,6 +2,7 @@ import typing
import enum import enum
from datetime import datetime from datetime import datetime
from dcs.countries import get_by_id, country_dict
from dcs.vehicles import * from dcs.vehicles import *
from dcs.ships import * from dcs.ships import *
from dcs.planes import * from dcs.planes import *
@ -836,6 +837,13 @@ def unitdict_from(fd: AssignedUnitsDict) -> Dict:
return {k: v1 for k, (v1, v2) in fd.items()} return {k: v1 for k, (v1, v2) in fd.items()}
def country_id_from_name(name):
for k,v in country_dict.items():
if v.name == name:
return k
return -1
def _validate_db(): def _validate_db():
# check unit by task uniquity # check unit by task uniquity
total_set = set() total_set = set()

View File

@ -130,44 +130,65 @@ class Event:
def commit(self, debriefing: Debriefing): def commit(self, debriefing: Debriefing):
for destroyed_unit_name in debriefing.dead_units_name: logging.info("Commiting mission results")
for cp in self.game.theater.controlpoints: # ------------------------------
# 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])
type = db.unit_type_from_name(destroyed_aircraft.split("|")[4])
if cpid in cp_map.keys():
cp = cp_map[cpid]
if type in cp.base.aircraft.keys():
logging.info("Aircraft destroyed : " + str(type))
cp.base.aircraft[type] = max(0, cp.base.aircraft[type]-1)
except Exception as e:
print(e)
for i, ground_object in enumerate(cp.ground_objects): # ------------------------------
if ground_object.dcs_identifier == "AA": # Destroyed ground units
for g in ground_object.groups: cp_map = {cp.id: cp for cp in self.game.theater.controlpoints}
for u in g.units: for killed_ground_unit in debriefing.killed_ground_units:
if u.name == destroyed_unit_name: try:
g.units.remove(u) cpid = int(killed_ground_unit.split("|")[3])
ucount = sum([len(g.units) for g in ground_object.groups]) type = db.unit_type_from_name(killed_ground_unit.split("|")[4])
print(ucount) if cpid in cp_map.keys():
if ucount == 0: cp = cp_map[cpid]
print("SET DEAD") if type in cp.base.armor.keys():
ground_object.is_dead = True logging.info("Ground unit destroyed : " + str(type))
cp.base.armor[type] = max(0, cp.base.armor[type] - 1)
except Exception as e:
print(e)
for country, losses in debriefing.destroyed_units.items(): # ------------------------------
if country == self.attacker_name: # Static ground objects
cp = self.departure_cp for destroyed_ground_unit_name in debriefing.killed_ground_units:
else:
cp = self.to_cp
logging.info("base {} commit losses {}".format(cp.base, losses))
cp.base.commit_losses(losses)
for object_identifier in debriefing.destroyed_objects:
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
if not cp.ground_objects: if not cp.ground_objects:
continue continue
# -- Static ground objects
for i, ground_object in enumerate(cp.ground_objects): for i, ground_object in enumerate(cp.ground_objects):
if ground_object.is_dead: if ground_object.is_dead:
continue continue
if ground_object.matches_string_identifier(object_identifier): if ground_object.matches_string_identifier(destroyed_ground_unit_name):
logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier)) logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
cp.ground_objects[i].is_dead = True cp.ground_objects[i].is_dead = True
# -- AA Site groups
for i, ground_object in enumerate(cp.ground_objects):
if ground_object.dcs_identifier == "AA":
for g in ground_object.groups:
for u in g.units:
if u.name == destroyed_ground_unit_name:
g.units.remove(u)
ucount = sum([len(g.units) for g in ground_object.groups])
if ucount == 0:
ground_object.is_dead = True
def skip(self): def skip(self):
pass pass

View File

@ -46,10 +46,12 @@ class FrontlineAttackEvent(Event):
attacker_country = self.game.enemy_country attacker_country = self.game.enemy_country
defender_country = self.game.player_country defender_country = self.game.player_country
alive_attackers = sum([v for k, v in debriefing.alive_units.get(attacker_country, {}).items() if db.unit_task(k) == PinpointStrike]) # TODO : Rework
alive_defenders = sum([v for k, v in debriefing.alive_units.get(defender_country, {}).items() if db.unit_task(k) == PinpointStrike]) #alive_attackers = sum([v for k, v in debriefing.alive_units.get(attacker_country, {}).items() if db.unit_task(k) == PinpointStrike])
#alive_defenders = sum([v for k, v in debriefing.alive_units.get(defender_country, {}).items() if db.unit_task(k) == PinpointStrike])
#attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
attackers_success = True
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
if self.from_cp.captured: if self.from_cp.captured:
return attackers_success return attackers_success
else: else:

View File

@ -50,7 +50,6 @@ class BaseAttackOperation(Operation):
def generate(self): def generate(self):
self.armorgen.generate(self.attack, self.defense) self.armorgen.generate(self.attack, self.defense)
self.aagen.generate(self.aa)
self.airgen.generate_defense(*assigned_units_split(self.intercept), at=self.defenders_starting_position) self.airgen.generate_defense(*assigned_units_split(self.intercept), at=self.defenders_starting_position)

View File

@ -31,7 +31,6 @@ class InfantryTransportOperation(Operation):
self.airgen.generate_passenger_transport(*assigned_units_split(self.transport), at=self.attackers_starting_position) self.airgen.generate_passenger_transport(*assigned_units_split(self.transport), at=self.attackers_starting_position)
self.armorgen.generate_passengers(count=6) self.armorgen.generate_passengers(count=6)
self.aagen.generate_at_defenders_location(self.aa)
self.visualgen.generate_transportation_marker(self.conflict.ground_attackers_location) self.visualgen.generate_transportation_marker(self.conflict.ground_attackers_location)
self.visualgen.generate_transportation_destination(self.conflict.position) self.visualgen.generate_transportation_destination(self.conflict.position)

View File

@ -1,8 +1,7 @@
from dcs.lua.parse import loads from dcs.lua.parse import loads
from userdata.debriefing import *
from gen import * from gen import *
from userdata.debriefing import *
TANKER_CALLSIGNS = ["Texaco", "Arco", "Shell"] TANKER_CALLSIGNS = ["Texaco", "Arco", "Shell"]
@ -17,7 +16,6 @@ class Operation:
conflict = None # type: Conflict conflict = None # type: Conflict
armorgen = None # type: ArmorConflictGenerator armorgen = None # type: ArmorConflictGenerator
airgen = None # type: AircraftConflictGenerator airgen = None # type: AircraftConflictGenerator
aagen = None # type: AAConflictGenerator
extra_aagen = None # type: ExtraAAConflictGenerator extra_aagen = None # type: ExtraAAConflictGenerator
shipgen = None # type: ShipGenerator shipgen = None # type: ShipGenerator
triggersgen = None # type: TriggersGenerator triggersgen = None # type: TriggersGenerator
@ -67,7 +65,6 @@ class Operation:
self.conflict = conflict self.conflict = conflict
self.armorgen = ArmorConflictGenerator(mission, conflict) self.armorgen = ArmorConflictGenerator(mission, conflict)
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings) self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
self.aagen = AAConflictGenerator(mission, conflict)
self.shipgen = ShipGenerator(mission, conflict) self.shipgen = ShipGenerator(mission, conflict)
self.airsupportgen = AirSupportConflictGenerator(mission, conflict, self.game) self.airsupportgen = AirSupportConflictGenerator(mission, conflict, self.game)
self.triggersgen = TriggersGenerator(mission, conflict, self.game) self.triggersgen = TriggersGenerator(mission, conflict, self.game)
@ -85,8 +82,6 @@ class Operation:
with open("resources/default_options.lua", "r") as f: with open("resources/default_options.lua", "r") as f:
options_dict = loads(f.read())["options"] options_dict = loads(f.read())["options"]
dcs.Mission.aaa_vehicle_group = aaa.aaa_vehicle_group
self.current_mission = dcs.Mission(terrain) self.current_mission = dcs.Mission(terrain)
if is_quick: if is_quick:
@ -200,7 +195,10 @@ class Operation:
load_dcs_libe = TriggerStart(comment="Load DCS Liberation Script") load_dcs_libe = TriggerStart(comment="Load DCS Liberation Script")
with open(os.path.abspath("./resources/scripts/dcs_liberation.lua")) as f: with open(os.path.abspath("./resources/scripts/dcs_liberation.lua")) as f:
script = f.read() script = f.read()
script = script.replace("{{json_file_abs_location}}", "'"+os.path.abspath("./resources/scripts/json.lua"+"'")) json_location = "[["+os.path.abspath("resources\\scripts\\json.lua")+"]]"
state_location = "[[" + os.path.abspath("state.json") + "]]"
script = script.replace("{{json_file_abs_location}}", json_location)
script = script.replace("{{debriefing_file_location}}", state_location)
load_dcs_libe.add_action(DoScript(String(script))) load_dcs_libe.add_action(DoScript(String(script)))
self.current_mission.triggerrules.triggers.append(load_dcs_libe) self.current_mission.triggerrules.triggers.append(load_dcs_libe)

View File

@ -1,441 +1,17 @@
import random
import math
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
from dcs.mission import * from dcs.mission import *
from dcs.mission import *
from .conflictgen import *
from .naming import *
DISTANCE_FACTOR = 0.5, 1 DISTANCE_FACTOR = 0.5, 1
EXTRA_AA_MIN_DISTANCE = 50000 EXTRA_AA_MIN_DISTANCE = 50000
EXTRA_AA_MAX_DISTANCE = 150000 EXTRA_AA_MAX_DISTANCE = 150000
EXTRA_AA_POSITION_FROM_CP = 550 EXTRA_AA_POSITION_FROM_CP = 550
def num_sam_dead(sam_type, destroyed_count):
"""
Given a type and count of SAM units, determine if enough units were destroyed to warrant the
loss of a site
:param sam_type:
inidivudal unit name in SAM site which was destroyed
:param destroyed_count:
count of that unit type which was destroyed *in the sortie*
:return:
INT: number of sites lost
"""
sam_threshold = {
AirDefence.SAM_SR_P_19: 1,
AirDefence.SAM_SA_3_S_125_TR_SNR: 1,
AirDefence.SAM_SA_6_Kub_STR_9S91: 1,
AirDefence.SAM_SA_10_S_300PS_SR_5N66M: 1,
AirDefence.SAM_SA_10_S_300PS_TR_30N6: 1,
AirDefence.SAM_SA_10_S_300PS_CP_54K6: 1,
AirDefence.SAM_SA_10_S_300PS_SR_64H6E: 1,
AirDefence.SAM_SA_3_S_125_LN_5P73: 4,
AirDefence.SAM_SA_6_Kub_LN_2P25: 6,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C: 8,
AirDefence.SAM_SA_2_LN_SM_90:4,
AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song: 1,
AirDefence.SAM_Hawk_PCP: 1,
AirDefence.SAM_Hawk_LN_M192: 4,
AirDefence.SAM_Hawk_SR_AN_MPQ_50: 1,
AirDefence.SAM_Hawk_TR_AN_MPQ_46: 1
}
return int(destroyed_count / sam_threshold[sam_type])
def determine_positions(position, heading, num_units, launcher_distance, coverage=90):
"""
Given a position on the map, array a group of units in a circle a uniform distance from the unit
:param position:
position of the center unit
:param heading:
the direction the units should be arranged toward if coverage is not 360
:param num_units:
number of units to play on the circle
:param launcher_distance:
distance the units should be from the center unit
:param coverage:
0-360
:return:
list of tuples representing each unit location
[(pos_x, pos_y, heading), ...]
"""
if coverage == 360:
# one of the positions is shared :'(
outer_offset = coverage / num_units
else:
outer_offset = coverage / (num_units - 1)
positions = []
if num_units % 2 == 0:
current_offset = heading - ((coverage / (num_units - 1)) / 2)
else:
current_offset = heading
current_offset -= outer_offset * (math.ceil(num_units / 2) - 1)
for x in range(1, num_units + 1):
positions.append((
position.x + launcher_distance * math.cos(math.radians(current_offset)),
position.y + launcher_distance * math.sin(math.radians(current_offset)),
current_offset,
))
current_offset += outer_offset
return positions
def aaa_vehicle_group(self, country, name, _type: unittype.VehicleType, position: mapping.Point,
heading=0, group_size=1,
formation=unitgroup.VehicleGroup.Formation.Line,
move_formation: PointAction=PointAction.OffRoad):
"""
Override the default vehicle group so that our group can contain a mix of units (which is required for advanced
SAM sites)
For further docstrings, see the built-in function
"""
vg = unitgroup.VehicleGroup(self.next_group_id(), self.string(name))
for i in range(1, group_size + 1):
heading = randint(0, 359)
if _type == AirDefence.SAM_SA_3_S_125_LN_5P73:
# 4 launchers (180 degrees all facing the same direction), 1 SR, 1 TR
num_launchers = 4
# search radar
v = self.vehicle(
name + " Unit #{nr}-sr".format(nr=i),
AirDefence.SAM_SR_P_19,
)
v.position.x = position.x
v.position.y = position.y + (i - 1) * 20
v.heading = heading
vg.add_unit(v)
# track radar
v = self.vehicle(
name + " Unit #{nr}-tr".format(nr=i),
AirDefence.SAM_SA_3_S_125_TR_SNR,
)
center_x = position.x + randint(20, 40)
center_y = position.y + (i - 1) * 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
plop_positions = determine_positions(
position,
heading,
num_launchers,
launcher_distance=100,
coverage=180,
)
for x in range(0, num_launchers):
v = self.vehicle(
name + " Unit #{nr}-{x}".format(nr=i, x=x),
AirDefence.SAM_SA_3_S_125_LN_5P73,
)
v.position.x = plop_positions[x][0]
v.position.y = plop_positions[x][1]
v.heading = plop_positions[x][2]
vg.add_unit(v)
elif _type == AirDefence.SAM_SA_6_Kub_LN_2P25:
# 6 launchers (360 degree coverage)
# 1 S/TR
# search/track radar
num_launchers = 6
v = self.vehicle(
name + " Unit #{nr}-str".format(nr=i),
AirDefence.SAM_SA_6_Kub_STR_9S91,
)
v.position.x = position.x
v.position.y = position.y + (i - 1) * 20
v.heading = heading
vg.add_unit(v)
plop_positions = determine_positions(
position,
heading,
num_launchers,
launcher_distance=100,
coverage=360,
)
for x in range(0, num_launchers):
v = self.vehicle(
name + " Unit #{nr}-{x}".format(nr=i, x=x),
AirDefence.SAM_SA_6_Kub_LN_2P25,
)
v.position.x = plop_positions[x][0]
v.position.y = plop_positions[x][1]
v.heading = plop_positions[x][2]
vg.add_unit(v)
elif _type == AirDefence.SAM_SA_10_S_300PS_LN_5P85C:
# 8 launchers - 4 directions, two in each direction
# 1 SR (offset)
# 1 TR (center)
# search radar
num_launchers = 8
v = self.vehicle(
name + " Unit #{nr}-sr".format(nr=i),
AirDefence.SAM_SA_10_S_300PS_SR_5N66M,
)
v.position.x = position.x
v.position.y = position.y + (i - 1) * 20
v.heading = heading
vg.add_unit(v)
# track radar
v = self.vehicle(
name + " Unit #{nr}-tr".format(nr=i),
AirDefence.SAM_SA_10_S_300PS_TR_30N6,
)
center_x = position.x + randint(20, 40)
center_y = position.y + (i - 1) * 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
# command center
v = self.vehicle(
name + " Unit #{nr}-c".format(nr=i),
AirDefence.SAM_SA_10_S_300PS_CP_54K6,
)
center_x = position.x + randint(40, 60)
center_y = position.y + (i - 1) * 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
plop_positions = determine_positions(
position,
heading,
num_launchers,
launcher_distance=150,
coverage=360,
)
for x in range(0, num_launchers):
v = self.vehicle(
name + " Unit #{nr}-{x}".format(nr=i, x=x),
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
)
v.position.x = plop_positions[x][0]
v.position.y = plop_positions[x][1]
v.heading = plop_positions[x][2]
vg.add_unit(v)
elif _type == AirDefence.SAM_SA_10_S_300PS_CP_54K6:
# 8 launchers - 4 directions, two in each direction
# 1 SR (offset)
# 1 TR (center)
# search radar
num_launchers = 8
v = self.vehicle(
name + " Unit #{nr}-sr".format(nr=i),
AirDefence.SAM_SA_10_S_300PS_SR_64H6E,
)
v.position.x = position.x
v.position.y = position.y + (i - 1) * 20
v.heading = heading
vg.add_unit(v)
# track radar
v = self.vehicle(
name + " Unit #{nr}-tr".format(nr=i),
AirDefence.SAM_SA_10_S_300PS_TR_30N6,
)
center_x = position.x + randint(20, 40)
center_y = position.y + (i - 1) * 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
# command center
v = self.vehicle(
name + " Unit #{nr}-c".format(nr=i),
AirDefence.SAM_SA_10_S_300PS_CP_54K6,
)
center_x = position.x + randint(40, 60)
center_y = position.y + (i - 1) * 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
plop_positions = determine_positions(
position,
heading,
num_units=num_launchers,
launcher_distance=150,
coverage=360,
)
for x in range(0, num_launchers):
v = self.vehicle(
name + " Unit #{nr}-{x}".format(nr=i, x=x),
AirDefence.SAM_SA_10_S_300PS_LN_5P85D,
)
v.position.x = plop_positions[x][0]
v.position.y = plop_positions[x][1]
v.heading = plop_positions[x][2]
vg.add_unit(v)
elif _type == AirDefence.SAM_Hawk_PCP:
# 4 launchers (180 degrees all facing the same direction), 1 SR, 1 TR, 1 PCP
num_launchers = 4
# search radar
v = self.vehicle(
name + " Unit #{nr}-sr".format(nr=i),
AirDefence.SAM_Hawk_SR_AN_MPQ_50,
)
v.position.x = position.x
v.position.y = position.y + (i - 1) * 20
v.heading = heading
vg.add_unit(v)
# track radar
v = self.vehicle(
name + " Unit #{nr}-tr".format(nr=i),
AirDefence.SAM_Hawk_TR_AN_MPQ_46,
)
center_x = position.x + randint(20, 40)
center_y = position.y + (i - 1) * 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
# PCP
v = self.vehicle(
name + " Unit #{nr}-pcp".format(nr=i),
AirDefence.SAM_Hawk_PCP,
)
center_x = position.x + randint(60, 80)
center_y = position.y + 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
plop_positions = determine_positions(
position,
heading,
num_launchers,
launcher_distance=100,
coverage=180,
)
for x in range(0, num_launchers):
v = self.vehicle(
name + " Unit #{nr}-{x}".format(nr=i, x=x),
AirDefence.SAM_Hawk_LN_M192,
)
v.position.x = plop_positions[x][0]
v.position.y = plop_positions[x][1]
v.heading = plop_positions[x][2]
vg.add_unit(v)
elif _type == AirDefence.SAM_SA_2_LN_SM_90:
# 4 launchers (180 degrees all facing the same direction), 1 SR, 1 TR
num_launchers = 4
# search radar
v = self.vehicle(
name + " Unit #{nr}-sr".format(nr=i),
AirDefence.SAM_SR_P_19,
)
v.position.x = position.x
v.position.y = position.y + (i - 1) * 20
v.heading = heading
vg.add_unit(v)
# track radar
v = self.vehicle(
name + " Unit #{nr}-tr".format(nr=i),
AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song,
)
center_x = position.x + randint(20, 40)
center_y = position.y + (i - 1) * 20
v.position.x = center_x
v.position.y = center_y
v.heading = heading
vg.add_unit(v)
plop_positions = determine_positions(
position,
heading,
num_launchers,
launcher_distance=100,
coverage=180,
)
for x in range(0, num_launchers):
v = self.vehicle(
name + " Unit #{nr}-{x}".format(nr=i, x=x),
AirDefence.SAM_SA_2_LN_SM_90,
)
v.position.x = plop_positions[x][0]
v.position.y = plop_positions[x][1]
v.heading = plop_positions[x][2]
vg.add_unit(v)
else:
v = self.vehicle(name + " Unit #{nr}-sam".format(nr=i), _type)
v.position.x = position.x
v.position.y = position.y + (i - 1) * 20
v.heading = heading
vg.add_unit(v)
wp = vg.add_waypoint(vg.units[0].position, move_formation, 0)
wp.ETA_locked = True
if _type.eplrs:
wp.tasks.append(task.EPLRS(self.next_eplrs("vehicle")))
country.add_vehicle_group(vg)
return vg
class AAConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict):
self.m = mission
self.conflict = conflict
def generate_at_defenders_location(self, units: db.AirDefenseDict):
for unit_type, count in units.items():
for _ in range(count):
self.m.vehicle_group(
country=self.conflict.defenders_country,
name=namegen.next_unit_name(self.conflict.defenders_country, unit_type),
_type=unit_type,
position=self.conflict.ground_defenders_location.random_point_within(100, 100),
group_size=1)
def generate(self, units: db.AirDefenseDict):
for type, count in units.items():
for _, radial in zip(range(count), self.conflict.radials):
distance = randint(self.conflict.size * DISTANCE_FACTOR[0] + 9000, self.conflict.size * DISTANCE_FACTOR[1] + 14000)
p = self.conflict.position.point_from_heading(random.choice(self.conflict.radials), distance)
self.m.aaa_vehicle_group(
country=self.conflict.defenders_country,
name=namegen.next_unit_name(self.conflict.defenders_country, type),
_type=type,
position=p,
group_size=1)
class ExtraAAConflictGenerator: class ExtraAAConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game, player_country: Country, enemy_country: Country): def __init__(self, mission: Mission, conflict: Conflict, game, player_country: Country, enemy_country: Country):
self.mission = mission self.mission = mission
@ -445,7 +21,6 @@ class ExtraAAConflictGenerator:
self.enemy_country = enemy_country self.enemy_country = enemy_country
def generate(self): def generate(self):
from theater.conflicttheater import ControlPoint
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
if cp.is_global: if cp.is_global:

View File

@ -242,11 +242,11 @@ class AircraftConflictGenerator:
else: else:
assert False assert False
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition, is_quick=False, should_orbit=False): def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition, cp, is_quick=False, should_orbit=False):
groups = [] groups = []
for flying_type, count, client_count in self._split_to_groups(units, clients): for flying_type, count, client_count in self._split_to_groups(units, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(side, flying_type), name=namegen.next_unit_name(side, cp.id, flying_type),
side=side, side=side,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -279,7 +279,7 @@ class AircraftConflictGenerator:
for flying_type, count, client_count in self._split_to_groups(attackers, clients): for flying_type, count, client_count in self._split_to_groups(attackers, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.attackers_country, side=self.conflict.attackers_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -313,7 +313,7 @@ class AircraftConflictGenerator:
try: try:
group = self._generate_at_airport( group = self._generate_at_airport(
name=namegen.next_unit_name(country, type), name=namegen.next_unit_name(country, cp.id, type),
side=country, side=country,
unit_type=type, unit_type=type,
count=number, count=number,
@ -322,7 +322,7 @@ class AircraftConflictGenerator:
start_type=StartType.Runway) start_type=StartType.Runway)
except Exception: except Exception:
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(country, type), name=namegen.next_unit_name(country, cp.id, type),
side=country, side=country,
unit_type=type, unit_type=type,
count=number, count=number,
@ -360,7 +360,7 @@ class AircraftConflictGenerator:
try: try:
group = self._generate_at_airport( group = self._generate_at_airport(
name=namegen.next_unit_name(country, type), name=namegen.next_unit_name(country, cp.id, type),
side=country, side=country,
unit_type=type, unit_type=type,
count=number, count=number,
@ -369,7 +369,7 @@ class AircraftConflictGenerator:
start_type=StartType.Runway) start_type=StartType.Runway)
except Exception: except Exception:
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(country, type), name=namegen.next_unit_name(country, cp.id, type),
side=country, side=country,
unit_type=type, unit_type=type,
count=number, count=number,
@ -413,7 +413,7 @@ class AircraftConflictGenerator:
try: try:
group = self._generate_at_airport( group = self._generate_at_airport(
name=namegen.next_unit_name(country, type), name=namegen.next_unit_name(country, cp.id, type),
side=country, side=country,
unit_type=type, unit_type=type,
count=number, count=number,
@ -422,7 +422,7 @@ class AircraftConflictGenerator:
start_type=StartType.Runway) start_type=StartType.Runway)
except Exception: except Exception:
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(country, type), name=namegen.next_unit_name(country, cp.id, type),
side=country, side=country,
unit_type=type, unit_type=type,
count=number, count=number,
@ -450,7 +450,7 @@ class AircraftConflictGenerator:
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients): for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.attackers_country, side=self.conflict.attackers_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -476,7 +476,7 @@ class AircraftConflictGenerator:
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients): for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.attackers_country, side=self.conflict.attackers_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -502,7 +502,7 @@ class AircraftConflictGenerator:
for flying_type, count, client_count in self._split_to_groups(defenders, clients): for flying_type, count, client_count in self._split_to_groups(defenders, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.defenders_country, self.conflict.to_cp.id, flying_type),
side=self.conflict.defenders_country, side=self.conflict.defenders_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -528,7 +528,7 @@ class AircraftConflictGenerator:
for flying_type, count, client_count in self._split_to_groups(attackers, clients): for flying_type, count, client_count in self._split_to_groups(attackers, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.attackers_country, side=self.conflict.attackers_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -552,6 +552,7 @@ class AircraftConflictGenerator:
clients=clients, clients=clients,
at=at and at or self._group_point(self.conflict.air_attackers_location), at=at and at or self._group_point(self.conflict.air_attackers_location),
is_quick=at is None, is_quick=at is None,
cp=self.conflict.from_cp,
should_orbit=True): should_orbit=True):
self._rtb_for(g, self.conflict.from_cp, at) self._rtb_for(g, self.conflict.from_cp, at)
@ -562,13 +563,14 @@ class AircraftConflictGenerator:
clients=clients, clients=clients,
at=at and at or self._group_point(self.conflict.air_defenders_location), at=at and at or self._group_point(self.conflict.air_defenders_location),
is_quick=at is None, is_quick=at is None,
cp=self.conflict.to_cp,
should_orbit=False): should_orbit=False):
self._rtb_for(g, self.conflict.to_cp, at) self._rtb_for(g, self.conflict.to_cp, at)
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None): def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for flying_type, count, client_count in self._split_to_groups(defenders, clients): for flying_type, count, client_count in self._split_to_groups(defenders, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.to_cp.id, flying_type),
side=self.conflict.defenders_country, side=self.conflict.defenders_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -585,7 +587,7 @@ class AircraftConflictGenerator:
def generate_migcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None): def generate_migcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for flying_type, count, client_count in self._split_to_groups(patrol, clients): for flying_type, count, client_count in self._split_to_groups(patrol, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.attackers_country, side=self.conflict.attackers_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -603,7 +605,7 @@ class AircraftConflictGenerator:
def generate_barcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None): def generate_barcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for flying_type, count, client_count in self._split_to_groups(patrol, clients): for flying_type, count, client_count in self._split_to_groups(patrol, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.defenders_country, side=self.conflict.defenders_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -629,7 +631,7 @@ class AircraftConflictGenerator:
for flying_type, count, client_count in self._split_to_groups(transport): for flying_type, count, client_count in self._split_to_groups(transport):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.defenders_country, side=self.conflict.defenders_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -647,7 +649,7 @@ class AircraftConflictGenerator:
def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None): def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for flying_type, count, client_count in self._split_to_groups(interceptors, clients): for flying_type, count, client_count in self._split_to_groups(interceptors, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, flying_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
side=self.conflict.attackers_country, side=self.conflict.attackers_country,
unit_type=flying_type, unit_type=flying_type,
count=count, count=count,
@ -669,7 +671,7 @@ class AircraftConflictGenerator:
def generate_passenger_transport(self, helis: db.HeliDict, clients: db.HeliDict, at: db.StartingPosition): def generate_passenger_transport(self, helis: db.HeliDict, clients: db.HeliDict, at: db.StartingPosition):
for heli_type, count, client_count in self._split_to_groups(helis, clients): for heli_type, count, client_count in self._split_to_groups(helis, clients):
group = self._generate_group( group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_country, heli_type), name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, heli_type),
side=self.conflict.attackers_country, side=self.conflict.attackers_country,
unit_type=heli_type, unit_type=heli_type,
count=count, count=count,

View File

@ -37,11 +37,17 @@ class ArmorConflictGenerator:
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR) return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None, move_formation: PointAction = PointAction.OffRoad): def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None, move_formation: PointAction = PointAction.OffRoad):
if side == self.conflict.attackers_country:
cp = self.conflict.from_cp
else:
cp = self.conflict.to_cp
for c in range(count): for c in range(count):
logging.info("armorgen: {} for {}".format(unit, side.id)) logging.info("armorgen: {} for {}".format(unit, side.id))
group = self.m.vehicle_group( group = self.m.vehicle_group(
side, side,
namegen.next_unit_name(side, unit), namegen.next_unit_name(side, cp.id, unit),
unit, unit,
position=self._group_point(at), position=self._group_point(at),
group_size=1, group_size=1,

View File

@ -4,9 +4,9 @@ from game import db
class NameGenerator: class NameGenerator:
number = 0 number = 0
def next_unit_name(self, country, unit_type): def next_unit_name(self, country, parent_base_id, unit_type):
self.number += 1 self.number += 1
return "unit|{}|{}|{}|".format(country.id, self.number, db.unit_type_name(unit_type)) return "unit|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type))
def next_basedefense_name(self): def next_basedefense_name(self):
return "basedefense_aa|0|0|" return "basedefense_aa|0|0|"

View File

@ -47,12 +47,6 @@ if __name__ == "__main__":
copyfile("./resources/scripts/MissionScripting.lua", installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua") copyfile("./resources/scripts/MissionScripting.lua", installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua")
app.processEvents() app.processEvents()
# Create DCS Liberation script folder
script_dir = installation.get_dcs_saved_games_directory() + os.sep + "Scripts" + os.sep + "DCSLiberation"
if not os.path.exists(script_dir):
os.makedirs(script_dir)
copyfile("./resources/scripts/json.lua", script_dir + os.path.sep + "json.lua")
# Apply CSS (need works) # Apply CSS (need works)
app.setStyleSheet(css) app.setStyleSheet(css)
GameUpdateSignal() GameUpdateSignal()

View File

@ -0,0 +1,11 @@
from PySide2.QtWidgets import QFrame
class QDebriefingInformation(QFrame):
"""
UI component to display debreifing informations
"""
def __init__(self):
super(QDebriefingInformation, self).__init__()
self.init_ui()

View File

@ -19,9 +19,6 @@ class QDebriefingWindow(QDialog):
self.gameEvent = gameEvent self.gameEvent = gameEvent
self.debriefing = debriefing self.debriefing = debriefing
self.player_losses = debriefing.destroyed_units.get(self.game.player_country, {})
self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy_country, {})
self.initUI() self.initUI()
def initUI(self): def initUI(self):
@ -44,7 +41,12 @@ class QDebriefingWindow(QDialog):
lostUnits.setLayout(lostUnitsLayout) lostUnits.setLayout(lostUnitsLayout)
row = 0 row = 0
for unit_type, count in self.player_losses.items(): for unit_type, count in self.debriefing.player_dead_aircraft_dict.items():
lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
row += 1
for unit_type, count in self.debriefing.player_dead_units_dict.items():
lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) lostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1) lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
row += 1 row += 1
@ -56,13 +58,20 @@ class QDebriefingWindow(QDialog):
enemylostUnitsLayout = QGridLayout() enemylostUnitsLayout = QGridLayout()
enemylostUnits.setLayout(enemylostUnitsLayout) enemylostUnits.setLayout(enemylostUnitsLayout)
row = 0 #row = 0
if self.debriefing.destroyed_objects: #if self.debriefing.destroyed_objects:
enemylostUnitsLayout.addWidget(QLabel("Ground assets"), row, 0) # enemylostUnitsLayout.addWidget(QLabel("Ground assets"), row, 0)
enemylostUnitsLayout.addWidget(QLabel("{}".format(len(self.debriefing.destroyed_objects))), row, 1) # 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
enemylostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
row += 1 row += 1
for unit_type, count in self.enemy_losses.items(): for unit_type, count in self.debriefing.enemy_dead_units_dict.items():
if count == 0: if count == 0:
continue continue
enemylostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0) enemylostUnitsLayout.addWidget(QLabel(db.unit_type_name(unit_type)), row, 0)

View File

@ -1,14 +1,32 @@
import os import os
from PySide2 import QtCore from PySide2 import QtCore
from PySide2.QtCore import QObject, Signal
from PySide2.QtGui import QMovie, QIcon from PySide2.QtGui import QMovie, QIcon
from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout from PySide2.QtWidgets import QLabel, QDialog, QVBoxLayout, QGroupBox, QGridLayout, QPushButton
from game.game import Event, Game from game.game import Event, Game
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from userdata.debriefing import wait_for_debriefing, Debriefing from userdata.debriefing import wait_for_debriefing, Debriefing
from userdata.persistency import base_path from userdata.persistency import base_path
class DebriefingFileWrittenSignal(QObject):
instance = None
debriefingReceived = Signal(Debriefing)
def __init__(self):
super(DebriefingFileWrittenSignal, self).__init__()
DebriefingFileWrittenSignal.instance = self
def sendDebriefing(self, debriefing: Debriefing):
self.debriefingReceived.emit(debriefing)
@staticmethod
def get_instance():
return DebriefingFileWrittenSignal.instance
DebriefingFileWrittenSignal()
class QWaitingForMissionResultWindow(QDialog): class QWaitingForMissionResultWindow(QDialog):
@ -22,34 +40,67 @@ class QWaitingForMissionResultWindow(QDialog):
self.setWindowIcon(QIcon("./resources/icon.png")) self.setWindowIcon(QIcon("./resources/icon.png"))
self.initUi() self.initUi()
wait_for_debriefing(lambda debriefing: self.process_debriefing(debriefing)) DebriefingFileWrittenSignal.get_instance().debriefingReceived.connect(self.updateLayout)
wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game)
def initUi(self): def initUi(self):
self.layout = QVBoxLayout() self.layout = QGridLayout()
self.layout.addWidget(QLabel("<b>You are clear for takeoff !</b>")) self.gridLayout = QGridLayout()
self.layout.addWidget(QLabel("In DCS open and play the mission : ")) self.gridLayout.addWidget(QLabel("<b>You are clear for takeoff !</b>"),0,0)
self.layout.addWidget(QLabel("<i>liberation_nextturn</i>")) self.gridLayout.addWidget(QLabel("In DCS open and start playing the mission : "),1,0)
self.layout.addWidget(QLabel("or")) self.gridLayout.addWidget(QLabel("<i>liberation_nextturn</i>"),2,0)
self.layout.addWidget(QLabel("<i>liberation_nextturn_quick</i>")) self.gridLayout.addWidget(QLabel("or"),3,0)
self.layout.addWidget(QLabel("<b>Then save the debriefing to the folder :</b>")) self.gridLayout.addWidget(QLabel("<i>liberation_nextturn_quick</i>"),4,0)
self.layout.addWidget(QLabel(self.debriefing_directory_location()))
progress = QLabel("") progress = QLabel("")
progress.setAlignment(QtCore.Qt.AlignCenter) progress.setAlignment(QtCore.Qt.AlignCenter)
progressBar = QMovie("./resources/ui/loader.gif") progressBar = QMovie("./resources/ui/loader.gif")
progress.setMovie(progressBar) progress.setMovie(progressBar)
self.layout.addWidget(progress) self.gridLayout.addWidget(progress,5,0)
progressBar.start() progressBar.start()
self.layout.addLayout(self.gridLayout,0,0)
self.setLayout(self.layout) self.setLayout(self.layout)
def updateLayout(self, debriefing):
updateBox = QGroupBox("Mission status")
updateLayout = QGridLayout()
updateBox.setLayout(updateLayout)
updateLayout.addWidget(QLabel("<b>Aircrafts destroyed</b>"), 0, 0)
updateLayout.addWidget(QLabel(str(len(debriefing.killed_aircrafts))), 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("<b>Weapons fired</b>"), 2, 0)
updateLayout.addWidget(QLabel(str(len(debriefing.weapons_fired))), 2, 1)
updateLayout.addWidget(QLabel("<b>Base Capture Events</b>"), 3, 0)
updateLayout.addWidget(QLabel(str(len(debriefing.base_capture_events))), 3, 1)
# Clear previous content of the window
for i in reversed(range(self.gridLayout.count())):
self.gridLayout.itemAt(i).widget().setParent(None)
# Set new window content
self.gridLayout.addWidget(updateBox, 0, 0)
if not debriefing.mission_ended:
self.gridLayout.addWidget(QLabel("<b>Mission is being played</b>"), 1, 0)
else:
#self.gridLayout.addWidget(QLabel("<b>Mission is over !</b>"), 1, 0)
proceed = QPushButton("Accept results")
proceed.setProperty("style", "btn-primary")
proceed.clicked.connect(lambda: self.process_debriefing(debriefing))
self.gridLayout.addWidget(proceed, 1, 0)
def on_debriefing_udpate(self, debriefing):
print("On Debriefing update")
print(debriefing)
DebriefingFileWrittenSignal.get_instance().sendDebriefing(debriefing)
wait_for_debriefing(lambda debriefing: self.on_debriefing_udpate(debriefing), self.game)
def process_debriefing(self, debriefing: Debriefing): def process_debriefing(self, debriefing: Debriefing):
debriefing.calculate_units(regular_mission=self.gameEvent.operation.regular_mission,
quick_mission=self.gameEvent.operation.quick_mission,
player_country=self.game.player_country,
enemy_country=self.game.enemy_country)
self.game.finish_event(event=self.gameEvent, debriefing=debriefing) self.game.finish_event(event=self.gameEvent, debriefing=debriefing)
self.game.pass_turn(ignored_cps=[self.gameEvent.to_cp, ]) self.game.pass_turn(ignored_cps=[self.gameEvent.to_cp, ])

View File

@ -1,9 +1,15 @@
local jsonlib = lfs.writedir() .. "Scripts\\DCSLiberation\\json.lua" local jsonlib = {{json_file_abs_location}}
json = loadfile(jsonlib)() json = loadfile(jsonlib)()
killed_aircrafts = {}; logger = mist.Logger:new("DCSLiberation", "info")
killed_ground_units = {};
debriefing_file_location = {{debriefing_file_location}}
killed_aircrafts = {}
killed_ground_units = {}
weapons_fired = {} weapons_fired = {}
base_capture_events = {}
mission_ended = false
local function messageAll(message) local function messageAll(message)
local msg = {} local msg = {}
@ -14,23 +20,24 @@ local function messageAll(message)
end end
write_state = function() write_state = function()
log("Writing DCS Liberation State...") messageAll("Writing DCS Liberation State...")
local stateFile = lfs.writedir()..[[Scripts\DCSLiberation\state.json]] local fp = io.open(debriefing_file_location, 'w')
local fp = io.open(stateFile, 'w')
local game_state = { local game_state = {
["killed_aircrafts"] = killed_aircrafts, ["killed_aircrafts"] = killed_aircrafts,
["killed_ground_units"] = killed_ground_units, ["killed_ground_units"] = killed_ground_units,
["weapons_fired"] = weapons_fired, ["weapons_fired"] = weapons_fired,
["base_capture_events"] = base_capture_events,
["mission_ended"] = mission_ended,
} }
fp:write(json:encode(game_state)) fp:write(json:encode(game_state))
fp:close() fp:close()
log("Done writing DCS Liberation state.") messageAll("Done writing DCS Liberation state.")
end end
mist.scheduleFunction(write_state, {}, timer.getTime() + 10, 60, timer.getTime() + 3600) mist.scheduleFunction(write_state, {}, timer.getTime() + 10, 60, timer.getTime() + 3600)
activeWeapons = {} activeWeapons = {}
local function onCrash(event) local function onEvent(event)
if event.id == world.event.S_EVENT_CRASH and event.initiator then if event.id == world.event.S_EVENT_CRASH and event.initiator then
messageAll("Crash :" .. event.initiator.getName(event.initiator)) messageAll("Crash :" .. event.initiator.getName(event.initiator))
killed_aircrafts[#killed_aircrafts + 1] = event.initiator.getName(event.initiator) killed_aircrafts[#killed_aircrafts + 1] = event.initiator.getName(event.initiator)
@ -43,8 +50,16 @@ local function onCrash(event)
if event.id == world.event.S_EVENT_SHOT and event.weapon then if event.id == world.event.S_EVENT_SHOT and event.weapon then
weapons_fired[#weapons_fired + 1] = event.weapon.getTypeName(event.weapon) weapons_fired[#weapons_fired + 1] = event.weapon.getTypeName(event.weapon)
end end
if event.id == world.event.S_EVENT_BASE_CAPTURED and event.place then
base_capture_events[#base_capture_events + 1] = event.place.getName(event.place) .. "||" .. event.place.getCoalition(event.place)
end
if event.id == world.event.S_EVENT_MISSION_END then
mission_ended = true
write_state()
end
end end
mist.addEventHandler(onEvent)
mist.addEventHandler(onCrash)

View File

@ -1,3 +1,4 @@
import json
import logging import logging
import os import os
import re import re
@ -14,188 +15,97 @@ from .persistency import base_path
DEBRIEFING_LOG_EXTENSION = "log" DEBRIEFING_LOG_EXTENSION = "log"
class DebriefingDeadUnitInfo:
country_id = -1
player_unit = False
type = None
def parse_mutliplayer_debriefing(contents: str): def __init__(self, country_id, player_unit , type):
result = {} self.country_id = country_id
element = None self.player_unit = player_unit
self.type = type
in_events = False
for line in [x.strip() for x in contents.splitlines()]:
if line.startswith("events ="):
in_events = True
elif line.startswith("} -- end of events"):
in_events = False
if not in_events:
continue
key = None
if line.startswith("initiator\t"):
key = "initiator"
if element is None:
element = {}
elif line.startswith("initiatorMissionID\t"):
key = "initiatorMissionID"
if element is None:
element = {}
elif line.startswith("type\t"):
key = "type"
if element is None:
element = {}
elif line.startswith("}, -- end of ["):
result[len(result)] = element
element = None
continue
else:
continue
value = re.findall(r"=\s*\"(.*?)\",", line)[0]
element[key] = value
return {"debriefing": {"events": result}}
class Debriefing: class Debriefing:
def __init__(self, dead_units, dead_units_name, trigger_state): def __init__(self, state_data, game):
self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]] self.base_capture_events = state_data["base_capture_events"]
self.alive_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]] self.killed_aircrafts = state_data["killed_aircrafts"]
self.destroyed_objects = [] # type: typing.List[str] self.killed_ground_units = state_data["killed_ground_units"]
self.weapons_fired = state_data["weapons_fired"]
self.mission_ended = state_data["mission_ended"]
self._trigger_state = trigger_state self.player_country_id = db.country_id_from_name(game.player_country)
self._dead_units = dead_units self.enemy_country_id = db.country_id_from_name(game.enemy_country)
self.dead_units_name = dead_units_name
@classmethod self.dead_aircraft = []
def parse(cls, path: str): self.dead_units = []
dead_units = []
dead_units_name = []
def append_dead_object(object_mission_id_str, object_name): for aircraft in self.killed_aircrafts:
nonlocal dead_units
object_mission_id = int(object_mission_id_str)
if object_mission_id in dead_units:
logging.error("debriefing: failed to append_dead_object {}: already exists!".format(object_mission_id))
return
dead_units.append(object_mission_id)
dead_units_name.append(object_name)
def parse_dead_object(event):
print(event)
try: try:
append_dead_object(event["initiatorMissionID"], event["initiator"]) 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)
self.dead_aircraft.append(aircraft)
except Exception as e: except Exception as e:
logging.error(e) print(e)
with open(path, "r") as f: for unit in self.killed_ground_units:
table_string = f.read()
try: try:
table = parse.loads(table_string) 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)
self.dead_units.append(unit)
except Exception as e: except Exception as e:
table = parse_mutliplayer_debriefing(table_string) print(e)
events = table.get("debriefing", {}).get("events", {}) self.player_dead_aircraft = [a for a in self.dead_aircraft if a.country_id == self.player_country_id]
for event in events.values(): self.enemy_dead_aircraft = [a for a in self.dead_aircraft if a.country_id == self.enemy_country_id]
event_type = event.get("type", None) self.player_dead_units = [a for a in self.dead_units if a.country_id == self.player_country_id]
if event_type in ["crash", "dead"]: self.enemy_dead_units = [a for a in self.dead_units if a.country_id == self.enemy_country_id]
parse_dead_object(event)
trigger_state = table.get("debriefing", {}).get("triggers_state", {}) 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
return Debriefing(dead_units, dead_units_name, trigger_state) 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
def calculate_units(self, regular_mission: Mission, quick_mission: Mission, player_country: str, enemy_country: str): self.player_dead_units_dict = {}
def count_groups(groups: typing.List[UnitType]) -> typing.Dict[UnitType, int]: for a in self.player_dead_units:
result = {} if a.type in self.player_dead_units_dict.keys():
for group in groups: self.player_dead_units_dict[a.type] = self.player_dead_units_dict[a.type] + 1
for unit in group.units: else:
unit_type = db.unit_type_of(unit) self.player_dead_units_dict[a.type] = 1
if unit_type in db.EXTRA_AA.values():
continue
result[unit_type] = result.get(unit_type, 0) + 1 self.enemy_dead_units_dict = {}
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
return result def _poll_new_debriefing_log(callback: typing.Callable, game):
if os.path.isfile("state.json"):
mission = regular_mission if len(self._trigger_state) else quick_mission last_modified = os.path.getmtime("state.json")
else:
player = mission.country(player_country) last_modified = 0
enemy = mission.country(enemy_country) while True:
if os.path.isfile("state.json") and os.path.getmtime("state.json") > last_modified:
player_units = count_groups(player.plane_group + player.vehicle_group + player.ship_group) with open("state.json", "r") as json_file:
enemy_units = count_groups(enemy.plane_group + enemy.vehicle_group + enemy.ship_group) json_data = json.load(json_file) #Debriefing.parse(os.path.join(debriefing_directory_location(), file))
debriefing = Debriefing(json_data, game)
self.destroyed_units = {
player.name: {},
enemy.name: {},
}
all_groups = {
player.name: player.plane_group + player.helicopter_group + player.vehicle_group + player.ship_group,
enemy.name: enemy.plane_group + enemy.helicopter_group + enemy.vehicle_group + enemy.ship_group,
}
static_groups = enemy.static_group
for country_name, country_groups in all_groups.items():
for group in country_groups:
for unit in group.units:
if unit.id in self._dead_units:
unit_type = db.unit_type_of(unit)
logging.info("debriefing: found dead unit {} ({}, {}) for country {}".format(str(unit.name), unit.id, unit_type, country_name))
assert country_name
assert unit_type
self.destroyed_units[country_name][unit_type] = self.destroyed_units[country_name].get(unit_type, 0) + 1
self._dead_units.remove(unit.id)
for group in static_groups:
identifier = group.units[0].id
if identifier in self._dead_units:
logging.info("debriefing: found dead static {} ({})".format(str(group.name), identifier))
assert str(group.name)
self.destroyed_objects.append(str(group.name))
self._dead_units.remove(identifier)
logging.info("debriefing: unsatistied ids: {}".format(self._dead_units))
self.alive_units = {
player.name: {k: v - self.destroyed_units[player.name].get(k, 0) for k, v in player_units.items()},
enemy.name: {k: v - self.destroyed_units[enemy.name].get(k, 0) for k, v in enemy_units.items()},
}
def debriefing_directory_location() -> str:
return os.path.join(base_path(), "liberation_debriefings")
def _logfiles_snapshot() -> typing.Dict[str, float]:
result = {}
for file in os.listdir(debriefing_directory_location()):
fullpath = os.path.join(debriefing_directory_location(), file)
result[file] = os.path.getmtime(fullpath)
return result
def _poll_new_debriefing_log(snapshot: typing.Dict[str, float], callback: typing.Callable):
should_run = True
while should_run:
for file, timestamp in _logfiles_snapshot().items():
if file not in snapshot or timestamp != snapshot[file]:
debriefing = Debriefing.parse(os.path.join(debriefing_directory_location(), file))
callback(debriefing) callback(debriefing)
should_run = False break
break time.sleep(5)
time.sleep(3) def wait_for_debriefing(callback: typing.Callable, game):
threading.Thread(target=_poll_new_debriefing_log, args=[callback, game]).start()
def wait_for_debriefing(callback: typing.Callable):
if not os.path.exists(debriefing_directory_location()):
os.mkdir(debriefing_directory_location())
threading.Thread(target=_poll_new_debriefing_log, args=(_logfiles_snapshot(), callback)).start()

View File

@ -5,6 +5,8 @@ import os
import sys import sys
import shutil import shutil
from dcs import installation
_user_folder = None # type: str _user_folder = None # type: str
@ -17,6 +19,7 @@ def base_path() -> str:
global _user_folder global _user_folder
assert _user_folder assert _user_folder
return installation.get_dcs_saved_games_directory()
openbeta_path = os.path.join(_user_folder, "DCS.openbeta") openbeta_path = os.path.join(_user_folder, "DCS.openbeta")
if "--force-stable-DCS" not in sys.argv and os.path.exists(openbeta_path): if "--force-stable-DCS" not in sys.argv and os.path.exists(openbeta_path):
return openbeta_path return openbeta_path

10
userdata/state.py Normal file
View File

@ -0,0 +1,10 @@
class RunningMissionState:
killed_aircrafts = []
killed_ground_units = []
weapons_fired = []
def __init__(self):
pass