diff --git a/game/db.py b/game/db.py index 0c8a01d1..656550c9 100644 --- a/game/db.py +++ b/game/db.py @@ -1018,8 +1018,7 @@ def unit_task(unit: UnitType) -> Task: return unit_task(SAM_CONVERT[unit]) print(unit.name + " cause issue") - assert False - + return None def find_unittype(for_task: Task, country_name: str) -> typing.List[UnitType]: return [x for x in UNIT_BY_TASK[for_task] if x in FACTIONS[country_name]["units"]] diff --git a/game/event/event.py b/game/event/event.py index dffed6d4..2dfbcee7 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -15,6 +15,8 @@ from game.db import assigned_units_from, unitdict_from from userdata.debriefing import Debriefing from userdata import persistency +import game.db as db + DIFFICULTY_LOG_BASE = 1.1 EVENT_DEPARTURE_MAX_DISTANCE = 340000 @@ -127,6 +129,23 @@ class Event: self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz")) def commit(self, debriefing: Debriefing): + + for destroyed_unit_name in debriefing.dead_units_name: + + for cp in self.game.theater.controlpoints: + + 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_unit_name: + g.units.remove(u) + ucount = sum([len(g.units) for g in ground_object.groups]) + print(ucount) + if ucount == 0: + print("SET DEAD") + ground_object.is_dead = True + for country, losses in debriefing.destroyed_units.items(): if country == self.attacker_name: cp = self.departure_cp diff --git a/game/game.py b/game/game.py index 7d0378cb..df9cae2a 100644 --- a/game/game.py +++ b/game/game.py @@ -96,6 +96,9 @@ class Game: turn = 0 game_stats: GameStats = None + current_unit_id = 0 + current_group_id = 0 + def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater, start_date: datetime): self.settings = Settings() self.events = [] @@ -315,3 +318,16 @@ class Game: def current_day(self): return self.date + timedelta(days=self.turn//4) + def next_unit_id(self): + """ + Next unit id for pre-generated units + """ + self.current_unit_id += 1 + return self.current_unit_id + + def next_group_id(self): + """ + Next unit id for pre-generated units + """ + self.current_group_id += 1 + return self.current_group_id diff --git a/game/operation/operation.py b/game/operation/operation.py index 24da6cdf..290b62bd 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -86,7 +86,9 @@ class Operation: options_dict = loads(f.read())["options"] dcs.Mission.aaa_vehicle_group = aaa.aaa_vehicle_group + self.current_mission = dcs.Mission(terrain) + if is_quick: self.quick_mission = self.current_mission else: @@ -117,6 +119,10 @@ class Operation: self.defenders_starting_position = ship def generate(self): + + # Generate ground object first + self.groundobjectgen.generate() + # air support self.airsupportgen.generate(self.is_awacs_enabled) for i, tanker_type in enumerate(self.airsupportgen.generated_tankers): @@ -132,8 +138,6 @@ class Operation: else: self.current_mission.groundControl.red_tactical_commander = self.ca_slots - # ground infrastructure - self.groundobjectgen.generate() self.extra_aagen.generate() # triggers diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index 8c159487..7fe82734 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -1,6 +1,7 @@ import logging from game import db +from game.db import unit_type_from_name from .conflictgen import * from .naming import * @@ -42,7 +43,6 @@ class GroundObjectsGenerator: ) def generate(self): - side = self.m.country(self.game.enemy_country) cp = None # type: ControlPoint if self.conflict.attackers_country.name == self.game.player_country: @@ -52,54 +52,58 @@ class GroundObjectsGenerator: consumed_farps = set() - for ground_object in cp.ground_objects: - if ground_object.dcs_identifier == "AA": - if ground_object.is_dead: - continue + for cp in self.game.theater.controlpoints: - unit_type = random.choice(self.game.commision_unit_types(cp, AirDefence)) - assert unit_type is not None, "Cannot find unit type for GroundObject defense ({})!".format(cp) - - group = self.m.aaa_vehicle_group( - country=side, - name=ground_object.string_identifier, - _type=unit_type, - position=ground_object.position, - heading=ground_object.heading, - ) - - logging.info("generated defense object identifier {} with mission id {}".format(group.name, group.id)) + if cp.captured: + country = self.game.player_country else: - if ground_object.dcs_identifier in warehouse_map: - static_type = warehouse_map[ground_object.dcs_identifier] + country = self.game.enemy_country + side = self.m.country(country) + + for ground_object in cp.ground_objects: + if ground_object.dcs_identifier == "AA": + for g in ground_object.groups: + if len(g.units) > 0: + vg = self.m.vehicle_group(side, g.name, unit_type_from_name(g.units[0].type), position=g.position) + vg.units[0].name = self.m.string(g.units[0].name) + for i,u in enumerate(g.units): + if i > 0: + vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type) + vehicle.position.x = u.position.x + vehicle.position.y = u.position.y + vehicle.heading = u.heading + vg.add_unit(vehicle) else: - static_type = fortification_map[ground_object.dcs_identifier] + if ground_object.dcs_identifier in warehouse_map: + static_type = warehouse_map[ground_object.dcs_identifier] + else: + static_type = fortification_map[ground_object.dcs_identifier] - if not static_type: - print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier)) - continue + if not static_type: + print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier)) + continue - if ground_object.group_id not in consumed_farps: - consumed_farps.add(ground_object.group_id) - if random.randint(0, 100) > 50: - farp_aa( - self.m, - side, - ground_object.string_identifier, - ground_object.position, - ) + if ground_object.group_id not in consumed_farps: + consumed_farps.add(ground_object.group_id) + if random.randint(0, 100) > 50: + farp_aa( + self.m, + side, + ground_object.string_identifier, + ground_object.position, + ) - group = self.m.static_group( - country=side, - name=ground_object.string_identifier, - _type=static_type, - position=ground_object.position, - heading=ground_object.heading, - dead=ground_object.is_dead, - ) + group = self.m.static_group( + country=side, + name=ground_object.string_identifier, + _type=static_type, + position=ground_object.position, + heading=ground_object.heading, + dead=ground_object.is_dead, + ) - logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id)) + logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id)) def farp_aa(mission_obj, country, name, position: mapping.Point): @@ -116,7 +120,6 @@ def farp_aa(mission_obj, country, name, position: mapping.Point): units = [ AirDefence.SPAAA_ZSU_23_4_Shilka, AirDefence.AAA_ZU_23_Closed, - Armor.MBT_T_55, ] v = mission_obj.vehicle(name + "_AAA", random.choice(units)) diff --git a/gen/sam/group_generator.py b/gen/sam/group_generator.py new file mode 100644 index 00000000..59fb37fa --- /dev/null +++ b/gen/sam/group_generator.py @@ -0,0 +1,73 @@ +import math +import random + +from dcs import unitgroup +from dcs.point import PointAction +from dcs.unit import Vehicle + + +class AntiAirGroupGenerator(): + + def __init__(self, game, ground_object, group_object_group_id): + self.game = game + self.go = ground_object + self.position = ground_object.position + self.heading = random.randint(0, 359) + self.vg = unitgroup.VehicleGroup(self.game.next_group_id(), self.go.group_identifier) + + wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0) + wp.ETA_locked = True + + def generate(self): + raise NotImplementedError + + def get_generated_group(self): + return self.vg + + def add_unit(self, unit_type, name, pos_x, pos_y, heading): + + nn = "cgroup|" + str(self.go.cp_id) + '|' + str(self.go.group_id) + '|' + str(self.go.group_identifier) + "|" + name + + unit = Vehicle(self.game.next_unit_id(), + nn, unit_type.id) + unit.position.x = pos_x + unit.position.y = pos_y + unit.heading = heading + self.vg.add_unit(unit) + return unit + + def get_circular_position(self, 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 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 = self.heading - ((coverage / (num_units - 1)) / 2) + else: + current_offset = self.heading + current_offset -= outer_offset * (math.ceil(num_units / 2) - 1) + for x in range(1, num_units + 1): + positions.append(( + self.position.x + launcher_distance * math.cos(math.radians(current_offset)), + self.position.y + launcher_distance * math.sin(math.radians(current_offset)), + current_offset, + )) + current_offset += outer_offset + return positions + diff --git a/gen/sam/sam_avenger.py b/gen/sam/sam_avenger.py new file mode 100644 index 00000000..41597617 --- /dev/null +++ b/gen/sam/sam_avenger.py @@ -0,0 +1,19 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class AvengerGenerator(AntiAirGroupGenerator): + """ + This generate an Avenger group + """ + + def generate(self): + num_launchers = random.randint(2, 3) + + self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading) + positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_Avenger_M1097, "SPAA#" + str(i), position[0], position[1], position[2]) diff --git a/gen/sam/sam_chaparral.py b/gen/sam/sam_chaparral.py new file mode 100644 index 00000000..fc03add8 --- /dev/null +++ b/gen/sam/sam_chaparral.py @@ -0,0 +1,19 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class ChaparralGenerator(AntiAirGroupGenerator): + """ + This generate a Chaparral group + """ + + def generate(self): + num_launchers = random.randint(2, 4) + + self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading) + positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_Chaparral_M48, "SPAA#" + str(i), position[0], position[1], position[2]) diff --git a/gen/sam/sam_gepard.py b/gen/sam/sam_gepard.py new file mode 100644 index 00000000..84fd3a4f --- /dev/null +++ b/gen/sam/sam_gepard.py @@ -0,0 +1,18 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class GepardGenerator(AntiAirGroupGenerator): + """ + This generate a Gepard group + """ + + def generate(self): + self.add_unit(AirDefence.SPAAA_Gepard, "SPAAA", self.position.x, self.position.y, self.heading) + if random.randint(0, 1) == 1: + self.add_unit(AirDefence.SPAAA_Gepard, "SPAAA2", self.position.x, self.position.y, self.heading) + self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x + 80, self.position.y, self.heading) + diff --git a/gen/sam/sam_group_generator.py b/gen/sam/sam_group_generator.py new file mode 100644 index 00000000..c70b56fc --- /dev/null +++ b/gen/sam/sam_group_generator.py @@ -0,0 +1,82 @@ +import math +import random + +from dcs import unitgroup +from dcs.point import PointAction +from dcs.unit import Vehicle +from dcs.unittype import UnitType +from dcs.vehicles import AirDefence + +from game import db +from gen.sam.sam_avenger import AvengerGenerator +from gen.sam.sam_chaparral import ChaparralGenerator +from gen.sam.sam_gepard import GepardGenerator +from gen.sam.sam_hawk import HawkGenerator +from gen.sam.sam_linebacker import LinebackerGenerator +from gen.sam.sam_patriot import PatriotGenerator +from gen.sam.sam_rapier import RapierGenerator +from gen.sam.sam_roland import RolandGenerator +from gen.sam.sam_sa10 import SA10Generator +from gen.sam.sam_sa11 import SA11Generator +from gen.sam.sam_sa13 import SA13Generator +from gen.sam.sam_sa15 import SA15Generator +from gen.sam.sam_sa19 import SA19Generator +from gen.sam.sam_sa2 import SA2Generator +from gen.sam.sam_sa3 import SA3Generator +from gen.sam.sam_sa6 import SA6Generator +from gen.sam.sam_sa8 import SA8Generator +from gen.sam.sam_sa9 import SA9Generator +from gen.sam.sam_vulcan import VulcanGenerator +from gen.sam.sam_zsu23 import ZSU23Generator +from gen.sam.sam_zu23 import ZU23Generator +from gen.sam.sam_zu23_ural import ZU23UralGenerator + + +def generate_anti_air_group(game, parent_cp, ground_object, faction:str): + """ + This generate a SAM group + :param parentCp: The parent control point + :param ground_object: The ground object which will own the sam group + :param country: Owner country + :return: Nothing, but put the group reference inside the ground object + """ + + SAM_MAP = { + + AirDefence.SAM_Hawk_PCP:HawkGenerator, + AirDefence.AAA_ZU_23_Closed:ZU23Generator, + AirDefence.AAA_ZU_23_Insurgent_on_Ural_375:ZU23UralGenerator, + AirDefence.SPAAA_ZSU_23_4_Shilka:ZSU23Generator, + AirDefence.AAA_Vulcan_M163: VulcanGenerator, + AirDefence.SAM_Linebacker_M6: LinebackerGenerator, + AirDefence.Rapier_FSA_Launcher: RapierGenerator, + AirDefence.SAM_Avenger_M1097: AvengerGenerator, + AirDefence.SPAAA_Gepard: GepardGenerator, + AirDefence.SAM_Roland_ADS: RolandGenerator, + AirDefence.SAM_Patriot_LN_M901: PatriotGenerator, + AirDefence.SAM_Chaparral_M48: ChaparralGenerator, + + AirDefence.SAM_SA_2_LN_SM_90: SA2Generator, + AirDefence.SAM_SA_3_S_125_LN_5P73: SA3Generator, + AirDefence.SAM_SA_6_Kub_LN_2P25: SA6Generator, + AirDefence.SAM_SA_8_Osa_9A33: SA8Generator, + AirDefence.SAM_SA_9_Strela_1_9P31: SA9Generator, + AirDefence.SAM_SA_10_S_300PS_LN_5P85C: SA10Generator, + AirDefence.SAM_SA_10_S_300PS_CP_54K6: SA10Generator, + AirDefence.SAM_SA_11_Buk_LN_9A310M1: SA11Generator, + AirDefence.SAM_SA_13_Strela_10M3_9A35M3: SA13Generator, + AirDefence.SAM_SA_15_Tor_9A331: SA15Generator, + AirDefence.SAM_SA_19_Tunguska_2S6: SA19Generator, + } + + possible_sams = [u for u in db.FACTIONS[faction]["units"] if u in AirDefence.__dict__.values()] + if len(possible_sams) > 0: + sam = random.choice(possible_sams) + generator = SAM_MAP[sam](game, ground_object, 0) + generator.generate() + ground_object.groups = [generator.get_generated_group()] + + + + + diff --git a/gen/sam/sam_hawk.py b/gen/sam/sam_hawk.py new file mode 100644 index 00000000..ab181d5e --- /dev/null +++ b/gen/sam/sam_hawk.py @@ -0,0 +1,25 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class HawkGenerator(AntiAirGroupGenerator): + """ + This generate an HAWK group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_Hawk_PCP, "PCP", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_Hawk_SR_AN_MPQ_50, "SR", self.position.x + 20, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_Hawk_TR_AN_MPQ_46, "TR", self.position.x + 40, self.position.y, self.heading) + + # Triple A for close range defense + self.add_unit(AirDefence.AAA_Vulcan_M163, "AAA", self.position.x + 20, self.position.y+30, self.heading) + + num_launchers = random.randint(3, 6) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_Hawk_LN_M192, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_linebacker.py b/gen/sam/sam_linebacker.py new file mode 100644 index 00000000..e9dd6bcd --- /dev/null +++ b/gen/sam/sam_linebacker.py @@ -0,0 +1,19 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class LinebackerGenerator(AntiAirGroupGenerator): + """ + This generate an m6 linebacker group + """ + + def generate(self): + num_launchers = random.randint(2, 4) + + self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x, self.position.y, self.heading) + positions = self.get_circular_position(num_launchers, launcher_distance=110, coverage=180) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_Linebacker_M6, "M6#" + str(i), position[0], position[1], position[2]) diff --git a/gen/sam/sam_patriot.py b/gen/sam/sam_patriot.py new file mode 100644 index 00000000..24730b40 --- /dev/null +++ b/gen/sam/sam_patriot.py @@ -0,0 +1,30 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class PatriotGenerator(AntiAirGroupGenerator): + """ + This generate a Patriot group + """ + + def generate(self): + # Command Post + self.add_unit(AirDefence.SAM_Patriot_AMG_AN_MRC_137, "MRC", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_Patriot_ECS_AN_MSQ_104, "MSQ", self.position.x + 30, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_Patriot_ICC, "ICC", self.position.x + 60, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_Patriot_EPP_III, "EPP", self.position.x, self.position.y + 30, self.heading) + self.add_unit(AirDefence.SAM_Patriot_STR_AN_MPQ_53, "ICC", self.position.x + 30, self.position.y + 30, self.heading) + + num_launchers = random.randint(2, 4) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_Patriot_LN_M901, "LN#" + str(i), position[0], position[1], position[2]) + + # Short range protection for high value site + num_launchers = random.randint(2, 4) + positions = self.get_circular_position(num_launchers, launcher_distance=300, coverage=360) + for i, position in enumerate(positions): + self.add_unit(AirDefence.AAA_Vulcan_M163, "SPAAA#" + str(i), position[0], position[1], position[2]) diff --git a/gen/sam/sam_rapier.py b/gen/sam/sam_rapier.py new file mode 100644 index 00000000..3e1aeecc --- /dev/null +++ b/gen/sam/sam_rapier.py @@ -0,0 +1,21 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class RapierGenerator(AntiAirGroupGenerator): + """ + This generate a Rapier Group + """ + + def generate(self): + self.add_unit(AirDefence.Rapier_FSA_Blindfire_Tracker, "BT", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.Rapier_FSA_Optical_Tracker, "OT", self.position.x + 20, self.position.y, self.heading) + + num_launchers = random.randint(3, 6) + positions = self.get_circular_position(num_launchers, launcher_distance=80, coverage=240) + + for i, position in enumerate(positions): + self.add_unit(AirDefence.Rapier_FSA_Launcher, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_roland.py b/gen/sam/sam_roland.py new file mode 100644 index 00000000..e61777c8 --- /dev/null +++ b/gen/sam/sam_roland.py @@ -0,0 +1,15 @@ +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class RolandGenerator(AntiAirGroupGenerator): + """ + This generate a Roland group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_Roland_ADS, "ADS", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_Roland_EWR, "EWR", self.position.x + 40, self.position.y, self.heading) + self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x + 80, self.position.y, self.heading) + diff --git a/gen/sam/sam_sa10.py b/gen/sam/sam_sa10.py new file mode 100644 index 00000000..ebf62cbf --- /dev/null +++ b/gen/sam/sam_sa10.py @@ -0,0 +1,43 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA10Generator(AntiAirGroupGenerator): + """ + This generate a SA-10 group + """ + + def generate(self): + # Command Post + self.add_unit(AirDefence.SAM_SA_10_S_300PS_CP_54K6, "CP", self.position.x, self.position.y, self.heading) + + # Search Radar + self.add_unit(AirDefence.SAM_SA_10_S_300PS_SR_5N66M, "SR", self.position.x, self.position.y, self.heading) + + # Search radar for missiles (optionnal) + self.add_unit(AirDefence.SAM_SA_10_S_300PS_SR_64H6E, "SR", self.position.x, self.position.y, self.heading) + + # 2 different launcher type (C & D) + num_launchers = random.randint(6, 8) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + for i, position in enumerate(positions): + if i%2 == 0: + self.add_unit(AirDefence.SAM_SA_10_S_300PS_LN_5P85C, "LN#" + str(i), position[0], position[1], position[2]) + else: + self.add_unit(AirDefence.SAM_SA_10_S_300PS_LN_5P85D, "LN#" + str(i), position[0], position[1], position[2]) + + # Then let's add short range protection to this high value site + # Sa-13 Strela are great for that + num_launchers = random.randint(2, 4) + positions = self.get_circular_position(num_launchers, launcher_distance=300, coverage=360) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_13_Strela_10M3_9A35M3, "IR#" + str(i), position[0], position[1], position[2]) + + # And even some AA + num_launchers = random.randint(6, 8) + positions = self.get_circular_position(num_launchers, launcher_distance=350, coverage=360) + for i, position in enumerate(positions): + self.add_unit(AirDefence.AAA_ZU_23_Emplacement, "AA#" + str(i), position[0], position[1], position[2]) diff --git a/gen/sam/sam_sa11.py b/gen/sam/sam_sa11.py new file mode 100644 index 00000000..16e0fbd5 --- /dev/null +++ b/gen/sam/sam_sa11.py @@ -0,0 +1,21 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA11Generator(AntiAirGroupGenerator): + """ + This generate a SA-11 group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_SA_11_Buk_CC_9S470M1, "CC", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_SA_11_Buk_SR_9S18M1, "SR", self.position.x+20, self.position.y, self.heading) + + num_launchers = random.randint(2, 4) + positions = self.get_circular_position(num_launchers, launcher_distance=140, coverage=180) + + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_11_Buk_LN_9A310M1, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_sa13.py b/gen/sam/sam_sa13.py new file mode 100644 index 00000000..c398974e --- /dev/null +++ b/gen/sam/sam_sa13.py @@ -0,0 +1,20 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA13Generator(AntiAirGroupGenerator): + """ + This generate a SA-13 group + """ + + def generate(self): + self.add_unit(Unarmed.Transport_UAZ_469, "UAZ", self.position.x, self.position.y, self.heading) + self.add_unit(Unarmed.Transport_KAMAZ_43101, "TRUCK", self.position.x+40, self.position.y, self.heading) + + num_launchers = random.randint(2, 3) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_13_Strela_10M3_9A35M3, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_sa15.py b/gen/sam/sam_sa15.py new file mode 100644 index 00000000..3da935cd --- /dev/null +++ b/gen/sam/sam_sa15.py @@ -0,0 +1,14 @@ +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA15Generator(AntiAirGroupGenerator): + """ + This generate a SA-15 group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_SA_15_Tor_9A331, "ADS", self.position.x, self.position.y, self.heading) + self.add_unit(Unarmed.Transport_UAZ_469, "EWR", self.position.x + 40, self.position.y, self.heading) + self.add_unit(Unarmed.Transport_KAMAZ_43101, "TRUCK", self.position.x + 80, self.position.y, self.heading) \ No newline at end of file diff --git a/gen/sam/sam_sa19.py b/gen/sam/sam_sa19.py new file mode 100644 index 00000000..506666e6 --- /dev/null +++ b/gen/sam/sam_sa19.py @@ -0,0 +1,21 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA19Generator(AntiAirGroupGenerator): + """ + This generate a SA-19 group + """ + + def generate(self): + num_launchers = random.randint(1, 3) + + if num_launchers == 1: + self.add_unit(AirDefence.SAM_SA_19_Tunguska_2S6, "LN#0", self.position.x, self.position.y, self.heading) + else: + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_19_Tunguska_2S6, "LN#" + str(i), position[0], position[1], position[2]) diff --git a/gen/sam/sam_sa2.py b/gen/sam/sam_sa2.py new file mode 100644 index 00000000..810013ee --- /dev/null +++ b/gen/sam/sam_sa2.py @@ -0,0 +1,21 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA2Generator(AntiAirGroupGenerator): + """ + This generate a SA-2 group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_SR_P_19, "SR", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_SA_2_TR_SNR_75_Fan_Song, "TR", self.position.x + 20, self.position.y, self.heading) + + num_launchers = random.randint(3, 6) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_2_LN_SM_90, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_sa3.py b/gen/sam/sam_sa3.py new file mode 100644 index 00000000..dd329c61 --- /dev/null +++ b/gen/sam/sam_sa3.py @@ -0,0 +1,21 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA3Generator(AntiAirGroupGenerator): + """ + This generate a SA-3 group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_SR_P_19, "SR", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_SA_3_S_125_TR_SNR, "TR", self.position.x + 20, self.position.y, self.heading) + + num_launchers = random.randint(3, 6) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_3_S_125_LN_5P73, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_sa6.py b/gen/sam/sam_sa6.py new file mode 100644 index 00000000..52700ce6 --- /dev/null +++ b/gen/sam/sam_sa6.py @@ -0,0 +1,20 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA6Generator(AntiAirGroupGenerator): + """ + This generate a SA-6 group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_SA_6_Kub_STR_9S91, "STR", self.position.x, self.position.y, self.heading) + + num_launchers = random.randint(2, 4) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_6_Kub_LN_2P25, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_sa8.py b/gen/sam/sam_sa8.py new file mode 100644 index 00000000..80dfea46 --- /dev/null +++ b/gen/sam/sam_sa8.py @@ -0,0 +1,15 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA8Generator(AntiAirGroupGenerator): + """ + This generate a SA-8 group + """ + + def generate(self): + self.add_unit(AirDefence.SAM_SA_8_Osa_9A33, "OSA", self.position.x, self.position.y, self.heading) + self.add_unit(AirDefence.SAM_SA_8_Osa_LD_9T217, "LD", self.position.x + 20, self.position.y, self.heading) diff --git a/gen/sam/sam_sa9.py b/gen/sam/sam_sa9.py new file mode 100644 index 00000000..53a17e4a --- /dev/null +++ b/gen/sam/sam_sa9.py @@ -0,0 +1,20 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class SA9Generator(AntiAirGroupGenerator): + """ + This generate a SA-9 group + """ + + def generate(self): + self.add_unit(Unarmed.Transport_UAZ_469, "UAZ", self.position.x, self.position.y, self.heading) + self.add_unit(Unarmed.Transport_KAMAZ_43101, "TRUCK", self.position.x+40, self.position.y, self.heading) + + num_launchers = random.randint(2, 3) + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=360) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SAM_SA_9_Strela_1_9P31, "LN#" + str(i), position[0], position[1], position[2]) \ No newline at end of file diff --git a/gen/sam/sam_vulcan.py b/gen/sam/sam_vulcan.py new file mode 100644 index 00000000..a1acbe76 --- /dev/null +++ b/gen/sam/sam_vulcan.py @@ -0,0 +1,18 @@ +import random + +from dcs.vehicles import AirDefence, Unarmed + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class VulcanGenerator(AntiAirGroupGenerator): + """ + This generate a Vulcan group + """ + + def generate(self): + self.add_unit(AirDefence.AAA_Vulcan_M163, "SPAAA", self.position.x, self.position.y, self.heading) + if random.randint(0, 1) == 1: + self.add_unit(AirDefence.AAA_Vulcan_M163, "SPAAA2", self.position.x, self.position.y, self.heading) + self.add_unit(Unarmed.Transport_M818, "TRUCK", self.position.x + 80, self.position.y, self.heading) + diff --git a/gen/sam/sam_zsu23.py b/gen/sam/sam_zsu23.py new file mode 100644 index 00000000..535d8cf3 --- /dev/null +++ b/gen/sam/sam_zsu23.py @@ -0,0 +1,18 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class ZSU23Generator(AntiAirGroupGenerator): + """ + This generate a ZSU 23 group + """ + + def generate(self): + num_launchers = random.randint(2, 5) + + positions = self.get_circular_position(num_launchers, launcher_distance=120, coverage=180) + for i, position in enumerate(positions): + self.add_unit(AirDefence.SPAAA_ZSU_23_4_Shilka, "SPAA#" + str(i), position[0], position[1], position[2]) diff --git a/gen/sam/sam_zu23.py b/gen/sam/sam_zu23.py new file mode 100644 index 00000000..075baa13 --- /dev/null +++ b/gen/sam/sam_zu23.py @@ -0,0 +1,25 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class ZU23Generator(AntiAirGroupGenerator): + """ + This generate a ZU23 flak artillery group + """ + + def generate(self): + grid_x = random.randint(2, 4) + grid_y = random.randint(2, 4) + + spacing = random.randint(10,40) + + index = 0 + for i in range(grid_x): + for j in range(grid_y): + index = index+1 + self.add_unit(AirDefence.AAA_ZU_23_Closed, "AAA#" + str(index), + self.position.x + spacing*i, + self.position.y + spacing*j, self.heading) \ No newline at end of file diff --git a/gen/sam/sam_zu23_ural.py b/gen/sam/sam_zu23_ural.py new file mode 100644 index 00000000..1b11024b --- /dev/null +++ b/gen/sam/sam_zu23_ural.py @@ -0,0 +1,18 @@ +import random + +from dcs.vehicles import AirDefence + +from gen.sam.group_generator import AntiAirGroupGenerator + + +class ZU23UralGenerator(AntiAirGroupGenerator): + """ + This generate a Zu23 Ural group + """ + + def generate(self): + num_launchers = random.randint(2, 8) + + positions = self.get_circular_position(num_launchers, launcher_distance=80, coverage=360) + for i, position in enumerate(positions): + self.add_unit(AirDefence.AAA_ZU_23_on_Ural_375, "SPAA#" + str(i), position[0], position[1], position[2]) diff --git a/qt_ui/main2.py b/qt_ui/main2.py new file mode 100644 index 00000000..d7cd5300 --- /dev/null +++ b/qt_ui/main2.py @@ -0,0 +1,63 @@ +import datetime + +from dcs import Mission +from dcs.terrain import Caucasus +from dcs.vehicles import AirDefence + +from game import Game +from gen.sam.sam_avenger import AvengerGenerator +from gen.sam.sam_chaparral import ChaparralGenerator +from gen.sam.sam_gepard import GepardGenerator +from gen.sam.sam_hawk import HawkGenerator +from gen.sam.sam_linebacker import LinebackerGenerator +from gen.sam.sam_patriot import PatriotGenerator +from gen.sam.sam_rapier import RapierGenerator +from gen.sam.sam_roland import RolandGenerator +from gen.sam.sam_sa10 import SA10Generator +from gen.sam.sam_sa11 import SA11Generator +from gen.sam.sam_sa13 import SA13Generator +from gen.sam.sam_sa15 import SA15Generator +from gen.sam.sam_sa19 import SA19Generator +from gen.sam.sam_sa2 import SA2Generator +from gen.sam.sam_sa3 import SA3Generator +from gen.sam.sam_sa6 import SA6Generator +from gen.sam.sam_sa8 import SA8Generator +from gen.sam.sam_sa9 import SA9Generator +from gen.sam.sam_zsu23 import ZSU23Generator +from gen.sam.sam_zu23 import ZU23Generator +from gen.sam.sam_zu23_ural import ZU23UralGenerator +from theater import TheaterGroundObject +from theater.caucasus import WesternGeorgia + +ter = Caucasus() +m = Mission() + + + +game = Game("USA 1990", "Iran 2015", WesternGeorgia(), datetime.datetime.now()) + +generated_groups = [] + +for i,c in enumerate([SA3Generator, SA2Generator, SA6Generator, RapierGenerator, + HawkGenerator, SA10Generator, SA19Generator, ZU23Generator, + SA8Generator, SA11Generator, SA9Generator, SA13Generator, + ZSU23Generator, SA15Generator, GepardGenerator, RolandGenerator, + PatriotGenerator, ZU23UralGenerator, ChaparralGenerator, + AvengerGenerator, LinebackerGenerator]): + t = TheaterGroundObject() + t.position = ter.kutaisi().position + t.position.x += i*250 + t.dcs_identifier = "AA" + gen = c(game, t) + gen.generate() + vehicle_group = gen.get_generated_group() + generated_groups.append(vehicle_group) + +for g in generated_groups: + g.name = m.string(g.name) + for unit in g.units: + unit.name = m.string(unit.name) + m.country("USA").add_vehicle_group(g) + +m.save("./test.miz") + diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index 60f7bbda..37c6fc84 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -264,15 +264,6 @@ class QLiberationMap(QGraphicsView): overlay.setGraphicsEffect(effect) - - - - - - - - - @staticmethod def set_display_rule(rule: str, value: bool): QLiberationMap.display_rules[rule] = value diff --git a/qt_ui/widgets/map/QMapGroundObject.py b/qt_ui/widgets/map/QMapGroundObject.py index d4fd1078..92c15c0e 100644 --- a/qt_ui/widgets/map/QMapGroundObject.py +++ b/qt_ui/widgets/map/QMapGroundObject.py @@ -13,7 +13,21 @@ class QMapGroundObject(QGraphicsRectItem): self.parent = parent self.setAcceptHoverEvents(True) self.setZValue(2) - self.setToolTip(cp.name + "'s " + self.model.category) + + if len(self.model.groups) > 0: + units = {} + for g in self.model.groups: + for u in g.units: + if u.type in units.keys(): + units[u.type] = units[u.type]+1 + else: + units[u.type] = 1 + tooltip = "" + for unit in units.keys(): + tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n" + self.setToolTip(tooltip + str(model.groups[0].id) + str(model.groups[0].name)) + else: + self.setToolTip(cp.name + "'s " + self.model.category) def paint(self, painter, option, widget=None): diff --git a/qt_ui/windows/QNewGameWizard.py b/qt_ui/windows/QNewGameWizard.py index 49249281..005eb36b 100644 --- a/qt_ui/windows/QNewGameWizard.py +++ b/qt_ui/windows/QNewGameWizard.py @@ -67,11 +67,11 @@ class NewGameWizard(QtWidgets.QWizard): conflicttheater.controlpoints[i].captured = True start_generator.generate_inital_units(conflicttheater, enemy_name, sams, multiplier) - start_generator.generate_groundobjects(conflicttheater) game = Game(player_name=player_name, enemy_name=enemy_name, theater=conflicttheater, start_date=period) + start_generator.generate_groundobjects(conflicttheater, game) game.budget = int(game.budget * multiplier) game.settings.multiplier = multiplier game.settings.sams = sams diff --git a/tests/integration/util.py b/tests/integration/util.py index 46310e43..302ec2cd 100644 --- a/tests/integration/util.py +++ b/tests/integration/util.py @@ -22,8 +22,9 @@ def init(player_country: str, enemy_country: str, theater_klass: typing.Type[Con persistency.setup("./tests/userfolder/") theater = theater_klass() start_generator.generate_inital_units(theater, ENEMY_COUNTRY, True, 1) - start_generator.generate_groundobjects(theater) - return Game(PLAYER_COUNTRY, ENEMY_COUNTRY, theater), theater + game = Game(PLAYER_COUNTRY, ENEMY_COUNTRY, theater) + start_generator.generate_groundobjects(theater, game) + return game, theater def autoflights_for(event: Event, country: str) -> TaskForceDict: @@ -55,7 +56,7 @@ def autodebrief_for(event: Event, type: AutodebriefType) -> Debriefing: for unit in group.units: dead_units.append(str(unit.name)) - return Debriefing(dead_units, []) + return Debriefing(dead_units, [], []) def event_state_save(e: Event) -> typing.Tuple[Base, Base]: diff --git a/theater/persiangulf.py b/theater/persiangulf.py index edbc17f6..abd5abcd 100644 --- a/theater/persiangulf.py +++ b/theater/persiangulf.py @@ -58,7 +58,7 @@ class PersianGulfTheater(ConflictTheater): self.add_controlpoint(self.al_ain, connected_to=[self.al_dhafra, self.al_maktoum]) self.add_controlpoint(self.al_maktoum, connected_to=[self.al_dhafra, self.al_minhad, self.sir_abu_nuayr, self.al_ain]) self.add_controlpoint(self.al_minhad, connected_to=[self.al_maktoum, self.dubai]) - self.add_controlpoint(self.dubai, connected_to=[self.al_minhad, self.sharjah]) + self.add_controlpoint(self.dubai, connected_to=[self.al_minhad, self.sharjah, self.fujairah]) self.add_controlpoint(self.sharjah, connected_to=[self.dubai, self.ras_al_khaimah]) self.add_controlpoint(self.ras_al_khaimah, connected_to=[self.sharjah, self.khasab]) self.add_controlpoint(self.fujairah, connected_to=[self.dubai, self.khasab]) diff --git a/theater/start_generator.py b/theater/start_generator.py index 70056181..38f6152e 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -4,6 +4,7 @@ import random import typing import logging +from gen.sam.sam_group_generator import generate_anti_air_group from theater.base import * from theater.conflicttheater import * @@ -52,7 +53,7 @@ def generate_inital_units(theater: ConflictTheater, enemy_country: str, sams: bo cp.base.commision_units({unit_type: count_per_type}) -def generate_groundobjects(theater: ConflictTheater): +def generate_groundobjects(theater: ConflictTheater, game): with open("resources/groundobject_templates.p", "rb") as f: tpls = pickle.load(f) @@ -131,4 +132,11 @@ def generate_groundobjects(theater: ConflictTheater): g.heading = object["heading"] g.position = Point(point.x + object["offset"].x, point.y + object["offset"].y) + if g.dcs_identifier == "AA": + if cp.captured: + faction = game.player_name + else: + faction = game.enemy_name + generate_anti_air_group(game, cp, g, faction) + cp.ground_objects.append(g) diff --git a/theater/theatergroundobject.py b/theater/theatergroundobject.py index 60a18676..b7ad4b6a 100644 --- a/theater/theatergroundobject.py +++ b/theater/theatergroundobject.py @@ -55,6 +55,8 @@ class TheaterGroundObject: heading = 0 position = None # type: Point + groups = [] + @property def category(self) -> str: for k, v in CATEGORY_MAP.items(): diff --git a/ui/eventresultsmenu.py b/ui/eventresultsmenu.py index 9bd92e2f..38e2c160 100644 --- a/ui/eventresultsmenu.py +++ b/ui/eventresultsmenu.py @@ -122,7 +122,7 @@ class EventResultsMenu(Menu): def simulate_result(self, player_factor: float, enemy_factor: float): def action(): - debriefing = Debriefing({}) + debriefing = Debriefing({}, []) def count(country: Country) -> typing.Dict[UnitType, int]: result = {} diff --git a/ui/window.py b/ui/window.py index bffd5cf1..3e470dfd 100644 --- a/ui/window.py +++ b/ui/window.py @@ -104,11 +104,11 @@ class Window: conflicttheater.controlpoints[i].captured = True start_generator.generate_inital_units(conflicttheater, enemy_name, sams, multiplier) - start_generator.generate_groundobjects(conflicttheater) game = Game(player_name=player_name, enemy_name=enemy_name, theater=conflicttheater, start_date=period) + start_generator.generate_groundobjects(conflicttheater, game) game.budget = int(game.budget * multiplier) game.settings.multiplier = multiplier game.settings.sams = sams diff --git a/userdata/debriefing.py b/userdata/debriefing.py index 5648684b..6df7cde4 100644 --- a/userdata/debriefing.py +++ b/userdata/debriefing.py @@ -57,19 +57,21 @@ def parse_mutliplayer_debriefing(contents: str): class Debriefing: - def __init__(self, dead_units, trigger_state): + def __init__(self, dead_units, dead_units_name, trigger_state): self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]] self.alive_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]] self.destroyed_objects = [] # type: typing.List[str] self._trigger_state = trigger_state self._dead_units = dead_units + self.dead_units_name = dead_units_name @classmethod def parse(cls, path: str): dead_units = [] + dead_units_name = [] - def append_dead_object(object_mission_id_str): + def append_dead_object(object_mission_id_str, object_name): nonlocal dead_units object_mission_id = int(object_mission_id_str) if object_mission_id in dead_units: @@ -77,10 +79,12 @@ class Debriefing: return dead_units.append(object_mission_id) + dead_units_name.append(object_name) def parse_dead_object(event): + print(event) try: - append_dead_object(event["initiatorMissionID"]) + append_dead_object(event["initiatorMissionID"], event["initiator"]) except Exception as e: logging.error(e) @@ -99,7 +103,7 @@ class Debriefing: trigger_state = table.get("debriefing", {}).get("triggers_state", {}) - return Debriefing(dead_units, trigger_state) + return Debriefing(dead_units, dead_units_name, trigger_state) def calculate_units(self, regular_mission: Mission, quick_mission: Mission, player_country: str, enemy_country: str): def count_groups(groups: typing.List[UnitType]) -> typing.Dict[UnitType, int]: