diff --git a/gen/locations/preset_control_point_locations.py b/gen/locations/preset_control_point_locations.py new file mode 100644 index 00000000..90ae8204 --- /dev/null +++ b/gen/locations/preset_control_point_locations.py @@ -0,0 +1,22 @@ +from dataclasses import dataclass, field + +from typing import List + +from gen.locations.preset_locations import PresetLocation + + +@dataclass +class PresetControlPointLocations: + """A repository of preset locations for a given control point""" + + # List of possible ashore locations to generate objects (Represented in miz file by an APC_AAV_7) + ashore_locations: List[PresetLocation] = field(default_factory=list) + + # List of possible offshore locations to generate ship groups (Represented in miz file by an Oliver Hazard Perry) + offshore_locations: List[PresetLocation] = field(default_factory=list) + + # Possible antiship missiles sites locations (Represented in miz file by Iranian Silkworm missiles) + antiship_locations: List[PresetLocation] = field(default_factory=list) + + # List of possible powerplants locations (Represented in miz file by static Workshop A object, USA) + powerplant_locations: List[PresetLocation] = field(default_factory=list) diff --git a/gen/locations/preset_location_finder.py b/gen/locations/preset_location_finder.py new file mode 100644 index 00000000..d7977efa --- /dev/null +++ b/gen/locations/preset_location_finder.py @@ -0,0 +1,59 @@ +from pathlib import Path +from typing import List + +from dcs import Mission +from dcs.vehicles import MissilesSS + +from gen.locations.preset_control_point_locations import PresetControlPointLocations +from gen.locations.preset_locations import PresetLocation + + +class PresetLocationFinder: + + @staticmethod + def compute_possible_locations(terrain_name: str, cp_name: str) -> PresetControlPointLocations: + """ + Extract the list of preset locations from miz data + :param terrain_name: Terrain/Map name + :param cp_name: Control Point / Airbase name + :return: + """ + + miz_file = Path("./resources/mizdata/", terrain_name.lower(), cp_name + ".miz") + + offshore_locations: List[PresetLocation] = [] + ashore_locations: List[PresetLocation] = [] + powerplants_locations: List[PresetLocation] = [] + antiship_locations: List[PresetLocation] = [] + + if miz_file.exists(): + m = Mission() + m.load_file(miz_file.absolute()) + + for vehicle_group in m.country("USA").vehicle_group: + if len(vehicle_group.units) > 0: + ashore_locations.append(PresetLocation(vehicle_group.position, + vehicle_group.units[0].heading, + vehicle_group.name)) + + for ship_group in m.country("USA").ship_group: + if len(ship_group.units) > 0: + offshore_locations.append(PresetLocation(ship_group.position, + ship_group.units[0].heading, + ship_group.name)) + + for static_group in m.country("USA").static_group: + if len(static_group.units) > 0: + powerplants_locations.append(PresetLocation(static_group.position, + static_group.units[0].heading, + static_group.name)) + + if m.country("Iran") is not None: + for vehicle_group in m.country("Iran").vehicle_group: + if len(vehicle_group.units) > 0 and vehicle_group.units[0].type == MissilesSS.SS_N_2_Silkworm.id: + antiship_locations.append(PresetLocation(vehicle_group.position, + vehicle_group.units[0].heading, + vehicle_group.name)) + + return PresetControlPointLocations(ashore_locations, offshore_locations, + antiship_locations, powerplants_locations) diff --git a/gen/locations/preset_locations.py b/gen/locations/preset_locations.py new file mode 100644 index 00000000..2d8872c3 --- /dev/null +++ b/gen/locations/preset_locations.py @@ -0,0 +1,15 @@ +from dataclasses import dataclass + +from dcs import Point + + +@dataclass +class PresetLocation: + """A preset location""" + position: Point + heading: int + id: str + + def __str__(self): + return "-" * 10 + "X: {}\n Y: {}\nHdg: {}°\nId: {}".format(self.position.x, self.position.y, self.heading, + self.id) + "-" * 10 diff --git a/resources/mizdata/caucasus/Anapa-Vityazevo.miz b/resources/mizdata/caucasus/Anapa-Vityazevo.miz new file mode 100644 index 00000000..3eddb192 Binary files /dev/null and b/resources/mizdata/caucasus/Anapa-Vityazevo.miz differ diff --git a/resources/mizdata/caucasus/Batumi.miz b/resources/mizdata/caucasus/Batumi.miz new file mode 100644 index 00000000..ac262ee8 Binary files /dev/null and b/resources/mizdata/caucasus/Batumi.miz differ diff --git a/resources/mizdata/caucasus/Beslan.miz b/resources/mizdata/caucasus/Beslan.miz new file mode 100644 index 00000000..238fa1c1 Binary files /dev/null and b/resources/mizdata/caucasus/Beslan.miz differ diff --git a/resources/mizdata/caucasus/Gelendzhik.miz b/resources/mizdata/caucasus/Gelendzhik.miz new file mode 100644 index 00000000..d2cd31f3 Binary files /dev/null and b/resources/mizdata/caucasus/Gelendzhik.miz differ diff --git a/resources/mizdata/caucasus/Gudauta.miz b/resources/mizdata/caucasus/Gudauta.miz new file mode 100644 index 00000000..e12de279 Binary files /dev/null and b/resources/mizdata/caucasus/Gudauta.miz differ diff --git a/resources/mizdata/caucasus/Kobuleti.miz b/resources/mizdata/caucasus/Kobuleti.miz new file mode 100644 index 00000000..1f5ccaec Binary files /dev/null and b/resources/mizdata/caucasus/Kobuleti.miz differ diff --git a/resources/mizdata/caucasus/Krasnodar-Center.miz b/resources/mizdata/caucasus/Krasnodar-Center.miz new file mode 100644 index 00000000..f25e54a6 Binary files /dev/null and b/resources/mizdata/caucasus/Krasnodar-Center.miz differ diff --git a/resources/mizdata/caucasus/Krasnodar-Pashkovsky.miz b/resources/mizdata/caucasus/Krasnodar-Pashkovsky.miz new file mode 100644 index 00000000..f2bd0f4f Binary files /dev/null and b/resources/mizdata/caucasus/Krasnodar-Pashkovsky.miz differ diff --git a/resources/mizdata/caucasus/Krymsk.miz b/resources/mizdata/caucasus/Krymsk.miz new file mode 100644 index 00000000..76c56edd Binary files /dev/null and b/resources/mizdata/caucasus/Krymsk.miz differ diff --git a/resources/mizdata/caucasus/Kutaisi.miz b/resources/mizdata/caucasus/Kutaisi.miz new file mode 100644 index 00000000..27d47e03 Binary files /dev/null and b/resources/mizdata/caucasus/Kutaisi.miz differ diff --git a/resources/mizdata/caucasus/Maykop-Khanskaya.miz b/resources/mizdata/caucasus/Maykop-Khanskaya.miz new file mode 100644 index 00000000..92f139ee Binary files /dev/null and b/resources/mizdata/caucasus/Maykop-Khanskaya.miz differ diff --git a/resources/mizdata/caucasus/Mineralnye Vody.miz b/resources/mizdata/caucasus/Mineralnye Vody.miz new file mode 100644 index 00000000..cd84551b Binary files /dev/null and b/resources/mizdata/caucasus/Mineralnye Vody.miz differ diff --git a/resources/mizdata/caucasus/Mozdok.miz b/resources/mizdata/caucasus/Mozdok.miz new file mode 100644 index 00000000..1e19d13a Binary files /dev/null and b/resources/mizdata/caucasus/Mozdok.miz differ diff --git a/resources/mizdata/caucasus/Nalchik.miz b/resources/mizdata/caucasus/Nalchik.miz new file mode 100644 index 00000000..f29c44b0 Binary files /dev/null and b/resources/mizdata/caucasus/Nalchik.miz differ diff --git a/resources/mizdata/caucasus/Novorossiysk.miz b/resources/mizdata/caucasus/Novorossiysk.miz new file mode 100644 index 00000000..08cfd0ec Binary files /dev/null and b/resources/mizdata/caucasus/Novorossiysk.miz differ diff --git a/resources/mizdata/caucasus/Senaki-Kolkhi.miz b/resources/mizdata/caucasus/Senaki-Kolkhi.miz new file mode 100644 index 00000000..db40d5b4 Binary files /dev/null and b/resources/mizdata/caucasus/Senaki-Kolkhi.miz differ diff --git a/resources/mizdata/caucasus/Sochi-Adler.miz b/resources/mizdata/caucasus/Sochi-Adler.miz new file mode 100644 index 00000000..e9bc53ac Binary files /dev/null and b/resources/mizdata/caucasus/Sochi-Adler.miz differ diff --git a/resources/mizdata/caucasus/Sukhumi-Babushara.miz b/resources/mizdata/caucasus/Sukhumi-Babushara.miz new file mode 100644 index 00000000..81a20319 Binary files /dev/null and b/resources/mizdata/caucasus/Sukhumi-Babushara.miz differ diff --git a/resources/mizdata/caucasus/Vaziani.miz b/resources/mizdata/caucasus/Vaziani.miz new file mode 100644 index 00000000..beb84168 Binary files /dev/null and b/resources/mizdata/caucasus/Vaziani.miz differ diff --git a/resources/mizdata/syria/Eyn Shemer.miz b/resources/mizdata/syria/Eyn Shemer.miz new file mode 100644 index 00000000..c29451c1 Binary files /dev/null and b/resources/mizdata/syria/Eyn Shemer.miz differ diff --git a/resources/mizdata/syria/Khalkhalah Defenses.miz b/resources/mizdata/syria/Khalkhalah Defenses.miz new file mode 100644 index 00000000..7584826b Binary files /dev/null and b/resources/mizdata/syria/Khalkhalah Defenses.miz differ diff --git a/resources/mizdata/syria/King Hussein Air College.miz b/resources/mizdata/syria/King Hussein Air College.miz new file mode 100644 index 00000000..6998a4ac Binary files /dev/null and b/resources/mizdata/syria/King Hussein Air College.miz differ diff --git a/theater/start_generator.py b/theater/start_generator.py index 46a04105..eb0252c4 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -21,6 +21,8 @@ from gen.fleet.ship_group_generator import ( generate_lha_group, generate_ship_group, ) +from gen.locations.preset_location_finder import PresetLocationFinder +from gen.locations.preset_locations import PresetLocation from gen.missiles.missiles_group_generator import generate_missile_group from gen.sam.sam_group_generator import ( generate_anti_air_group, @@ -163,6 +165,7 @@ class ControlPointGroundObjectGenerator: def __init__(self, game: Game, control_point: ControlPoint) -> None: self.game = game self.control_point = control_point + self.preset_locations = PresetLocationFinder.compute_possible_locations(game.theater.terrain.name, control_point.full_name) @property def faction_name(self) -> str: @@ -217,6 +220,27 @@ class ControlPointGroundObjectGenerator: g.groups.append(group) self.control_point.connected_objectives.append(g) + def pick_preset_location(self, offshore=False) -> Optional[PresetLocation]: + """ + Return a preset location if any is setup and still available for this point + @:param offshore Whether this should be an offshore location + @:return The preset location if found; None if it couldn't be found + """ + if offshore: + if len(self.preset_locations.offshore_locations) > 0: + location = random.choice(self.preset_locations.offshore_locations) + self.preset_locations.offshore_locations.remove(location) + logging.info("Picked a preset offshore location") + return location + else: + if len(self.preset_locations.ashore_locations) > 0: + location = random.choice(self.preset_locations.ashore_locations) + self.preset_locations.ashore_locations.remove(location) + logging.info("Picked a preset ashore location") + return location + logging.info("No preset location found") + return None + class CarrierGroundObjectGenerator(ControlPointGroundObjectGenerator): def generate(self) -> bool: @@ -383,10 +407,20 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): obj_name = namegen.random_objective_name() template = random.choice(list(self.templates[category].values())) - point = find_location(category != "oil", - self.control_point.position, - self.game.theater, 10000, 40000, - self.control_point.ground_objects) + + offshore = category == "oil" + + # Pick from preset locations + location = self.pick_preset_location(offshore) + + # Else try the old algorithm + if location is None: + point = find_location(not offshore, + self.control_point.position, + self.game.theater, 10000, 40000, + self.control_point.ground_objects) + else: + point = location.position if point is None: logging.error( @@ -409,9 +443,17 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): def generate_aa_site(self) -> None: obj_name = namegen.random_objective_name() - position = find_location(True, self.control_point.position, + + # Pick from preset locations + location = self.pick_preset_location(False) + + # If no preset location, then try the old algorithm + if location is None: + position = find_location(True, self.control_point.position, self.game.theater, 10000, 40000, self.control_point.ground_objects) + else: + position = location.position if position is None: logging.error( @@ -432,9 +474,20 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): self.generate_missile_site() def generate_missile_site(self) -> None: - point = find_location(True, self.control_point.position, - self.game.theater, 2500, 40000, [], False) - if point is None: + + # Pick from preset locations + location = self.pick_preset_location(False) + + # If no preset location, then try the old algorithm + if location is None: + position = find_location(True, self.control_point.position, + self.game.theater, 2500, 40000, + [], False) + else: + position = location.position + + + if position is None: logging.info( f"Could not find point for {self.control_point} missile site") return @@ -442,7 +495,7 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator): group_id = self.game.next_group_id() g = MissileSiteGroundObject(namegen.random_objective_name(), group_id, - point, self.control_point) + position, self.control_point) group = generate_missile_group(self.game, g, self.faction_name) g.groups = [] if group is not None: