def generate_defensive_groups(self) -> None: # AAA for defending against close targets. aa_group = self.add_auxiliary_group("AA") num_launchers = random.randint(6, 8) positions = self.get_circular_position( num_launchers, launcher_distance=210, coverage=360 ) for i, (x, y, heading) in enumerate(positions): self.add_unit_to_group( aa_group, AirDefence.SAM_SA_19_Tunguska_Grison, f"AA#{i}", Point(x, y), heading, ) # SA-15 for both shorter range targets and point defense. pd_group = self.add_auxiliary_group("PD") num_launchers = random.randint(2, 4) positions = self.get_circular_position( num_launchers, launcher_distance=140, coverage=360 ) for i, (x, y, heading) in enumerate(positions): self.add_unit_to_group( pd_group, AirDefence.SAM_SA_15_Tor_Gauntlet, f"PD#{i}", Point(x, y), heading, )
def add_json_cp(self, theater, p: dict) -> ControlPoint: if p["type"] == "airbase": airbase = theater.terrain.airports[p["id"]].__class__ if "radials" in p.keys(): radials = p["radials"] else: radials = LAND if "size" in p.keys(): size = p["size"] else: size = SIZE_REGULAR if "importance" in p.keys(): importance = p["importance"] else: importance = IMPORTANCE_MEDIUM cp = ControlPoint.from_airport(airbase, radials, size, importance) elif p["type"] == "carrier": cp = ControlPoint.carrier("carrier", Point(p["x"], p["y"]), p["id"]) else: cp = ControlPoint.lha("lha", Point(p["x"], p["y"]), p["id"]) if "captured_invert" in p.keys(): cp.captured_invert = p["captured_invert"] else: cp.captured_invert = False return cp
def add_json_cp(self, theater, p: dict) -> ControlPoint: cp: ControlPoint if p["type"] == "airbase": airbase = theater.terrain.airports[p["id"]] if "size" in p.keys(): size = p["size"] else: size = SIZE_REGULAR if "importance" in p.keys(): importance = p["importance"] else: importance = IMPORTANCE_MEDIUM cp = Airfield(airbase, size, importance) elif p["type"] == "carrier": cp = Carrier("carrier", Point(p["x"], p["y"]), p["id"]) else: cp = Lha("lha", Point(p["x"], p["y"]), p["id"]) if "captured_invert" in p.keys(): cp.captured_invert = p["captured_invert"] else: cp.captured_invert = False return cp
def extend_ground_position( cls, initial: Point, max_distance: int, heading: Heading, theater: ConflictTheater, ) -> Point: """Finds the first intersection with an exclusion zone in one heading from an initial point up to max_distance""" extended = initial.point_from_heading(heading.degrees, max_distance) if theater.landmap is None: # TODO: Why is this possible? return extended p0 = ShapelyPoint(initial.x, initial.y) p1 = ShapelyPoint(extended.x, extended.y) line = LineString([p0, p1]) intersection = line.intersection( theater.landmap.inclusion_zone_only.boundary) if intersection.is_empty: # Max extent does not intersect with the boundary of the inclusion # zone, so the full front line is usable. This does assume that the # front line was centered on a valid location. return extended # Otherwise extend the front line only up to the intersection. return initial.point_from_heading(heading.degrees, p0.distance(intersection))
def test_point(self): p1 = Point(2, 2) p2 = p1 + Point(1, 1) self.assertEqual(p2.x, 3) self.assertEqual(p2.y, 3) p2 = 1 + p1 self.assertEqual(p2.x, 3) self.assertEqual(p2.y, 3) self.assertEqual(3 * p2, Point(9, 9)) self.assertEqual(p2.x, 3) self.assertEqual(p2.y, 3) self.assertEqual(p2 * 0.5, Point(1.5, 1.5)) p2 = p1 - 1 self.assertEqual(p2.x, 1) self.assertEqual(p2.y, 1) p2 = p1 - p2 self.assertEqual(p2.x, 1) self.assertEqual(p2.y, 1)
def sa10_site(mission: Mission, position: mapping.Point, heading, prefix="", skill=unit.Skill.Average): russia = mission.country("Russia") vg = mission.vehicle_group(russia, prefix + "SA10 site", Russia.Vehicle.AirDefence.S_300PS_64H6E_sr, position, heading) u = mission.vehicle("Operator 1", Russia.Vehicle.Infantry.Paratrooper_AKS_74) u.position = position.point_from_heading(heading + 180, 10) u.heading = heading vg.add_unit(u) hdg = 90 for i in range(0, 3): # 3 launchers u = mission.vehicle("launcher #" + str(i + 1), Russia.Vehicle.AirDefence.S_300PS_5P85C_ln) u.position = position.point_from_heading(heading + hdg, 50) u.heading = heading vg.add_unit(u) hdg += 90 u = mission.vehicle("radar", Russia.Vehicle.AirDefence.S_300PS_40B6M_tr) u.position = position.point_from_heading(heading, 80) u.heading = heading vg.add_unit(u) u = mission.vehicle("radar", Russia.Vehicle.AirDefence.S_300PS_54K6_cp) u.position = position.point_from_heading(heading + 180, 100) u.heading = heading vg.add_unit(u) for u in vg.units: u.skill = skill return vg
def sa10_site(mission: Mission, position: mapping.Point, heading, prefix="", skill=unit.Skill.Average): russia = mission.country("Russia") vg = mission.vehicle_group(russia, prefix + "SA10 site", Russia.Vehicle.AirDefence.SAM_SA_10_S_300PS_CP_54K6, position, heading) u = mission.vehicle("Operator 1", Russia.Vehicle.Infantry.Infantry_Soldier_Rus) u.position = position.point_from_heading(heading + 180, 10) u.heading = heading vg.add_unit(u) hdg = 90 for i in range(0, 3): # 3 launchers u = mission.vehicle("launcher #" + str(i + 1), Russia.Vehicle.AirDefence.SAM_SA_10_S_300PS_LN_5P85C) u.position = position.point_from_heading(heading + hdg, 50) u.heading = heading vg.add_unit(u) hdg += 90 u = mission.vehicle("radar", Russia.Vehicle.AirDefence.SAM_SA_10_S_300PS_TR_30N6) u.position = position.point_from_heading(heading, 80) u.heading = heading vg.add_unit(u) u = mission.vehicle("radar", Russia.Vehicle.AirDefence.SAM_SA_10_S_300PS_SR_64H6E) u.position = position.point_from_heading(heading + 180, 100) u.heading = heading vg.add_unit(u) for u in vg.units: u.skill = skill return vg
def generate_defensive_groups(self) -> None: # AAA for defending against close targets. aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior) num_launchers = 2 positions = self.get_circular_position( num_launchers, launcher_distance=210, coverage=360 ) for i, (x, y, heading) in enumerate(positions): self.add_unit_to_group( aa_group, AirDefence._2S6_Tunguska, f"AA#{i}", Point(x, y), heading, ) # SA-15 for both shorter range targets and point defense. pd_group = self.add_auxiliary_group(SkynetRole.PointDefense) num_launchers = 2 positions = self.get_circular_position( num_launchers, launcher_distance=140, coverage=360 ) for i, (x, y, heading) in enumerate(positions): self.add_unit_to_group( pd_group, AirDefence.Tor_9A331, f"PD#{i}", Point(x, y), heading, )
def sa10_site(mission: Mission, position: mapping.Point, heading, prefix="", skill=unit.Skill.Average): russia = mission.country("Russia") vg = mission.vehicle_group(russia, prefix + "SA10 site", Russia.Vehicle.AirDefence.SAM_SA_10_S_300_Grumble_Big_Bird_SR, position, heading) u = mission.vehicle("Operator 1", Russia.Vehicle.Infantry.Infantry_AK_74_Rus) u.position = position.point_from_heading(heading + 180, 10) u.heading = heading vg.add_unit(u) hdg = 90 for i in range(0, 3): # 3 launchers u = mission.vehicle("launcher #" + str(i + 1), Russia.Vehicle.AirDefence.SAM_SA_10_S_300_Grumble_TEL_C) u.position = position.point_from_heading(heading + hdg, 50) u.heading = heading vg.add_unit(u) hdg += 90 u = mission.vehicle("radar", Russia.Vehicle.AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR) u.position = position.point_from_heading(heading, 80) u.heading = heading vg.add_unit(u) u = mission.vehicle("radar", Russia.Vehicle.AirDefence.SAM_SA_10_S_300_Grumble_C2) u.position = position.point_from_heading(heading + 180, 100) u.heading = heading vg.add_unit(u) for u in vg.units: u.skill = skill return vg
def find_ground_position( cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater, coerce=True, ) -> Optional[Point]: """ Finds the nearest valid ground position along a provided heading and it's inverse up to max_distance. `coerce=True` will return the closest land position to `initial` regardless of heading or distance `coerce=False` will return None if a point isn't found """ pos = initial if theater.is_on_land(pos): return pos for distance in range(0, int(max_distance), 100): pos = initial.point_from_heading(heading, distance) if theater.is_on_land(pos): return pos pos = initial.point_from_heading(opposite_heading(heading), distance) if coerce: pos = theater.nearest_land_pos(initial) return pos logging.error("Didn't find ground position ({})!".format(initial)) return None
def compute_conflicts_position(self): """ Compute the current conflict center position(s), mainly used for culling calculation :return: List of points of interests """ points = [] # By default, use the existing frontline conflict position for front_line in self.theater.conflicts(): position = Conflict.frontline_position(front_line.control_point_a, front_line.control_point_b, self.theater) points.append(position[0]) points.append(front_line.control_point_a.position) points.append(front_line.control_point_b.position) # If do_not_cull_carrier is enabled, add carriers as culling point if self.settings.perf_do_not_cull_carrier: for cp in self.theater.controlpoints: if cp.is_carrier or cp.is_lha: points.append(cp.position) # If there is no conflict take the center point between the two nearest opposing bases if len(points) == 0: cpoint = None min_distance = sys.maxsize for cp in self.theater.player_points(): for cp2 in self.theater.enemy_points(): d = cp.position.distance_to_point(cp2.position) if d < min_distance: min_distance = d cpoint = Point((cp.position.x + cp2.position.x) / 2, (cp.position.y + cp2.position.y) / 2) points.append(cp.position) points.append(cp2.position) break if cpoint is not None: break if cpoint is not None: points.append(cpoint) packages = itertools.chain(self.blue_ato.packages, self.red_ato.packages) for package in packages: if package.primary_task is FlightType.BARCAP: # BARCAPs will be planned at most locations on smaller theaters, # rendering culling fairly useless. BARCAP packages don't really # need the ground detail since they're defensive. SAMs nearby # are only interesting if there are enemies in the area, and if # there are they won't be culled because of the enemy's mission. continue points.append(package.target.position) # Else 0,0, since we need a default value # (in this case this means the whole map is owned by the same player, so it is not an issue) if len(points) == 0: points.append(Point(0, 0)) self.__culling_points = points
def extend_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: """Finds the first intersection with an exclusion zone in one heading from an initial point up to max_distance""" pos = initial for distance in range(0, int(max_distance), 100): pos = initial.point_from_heading(heading, distance) if not theater.is_on_land(pos): return initial.point_from_heading(heading, distance - 100) return pos
def closest_control_point(self, point: Point) -> ControlPoint: closest = self.controlpoints[0] closest_distance = point.distance_to_point(closest.position) for control_point in self.controlpoints[1:]: distance = point.distance_to_point(control_point.position) if distance < closest_distance: closest = control_point closest_distance = distance return closest
def closest_cp(location: Point) -> (int, float): global theater min_distance, min_cp = None, None for cp in theater.controlpoints: if not min_distance or location.distance_to_point(cp.position) < min_distance: min_distance = location.distance_to_point(cp.position) min_cp = cp.id assert min_cp is not None return min_cp
def closest_control_point( self, point: Point, allow_naval: bool = False ) -> ControlPoint: closest = self.controlpoints[0] closest_distance = point.distance_to_point(closest.position) for control_point in self.controlpoints[1:]: if control_point.is_fleet and not allow_naval: continue distance = point.distance_to_point(control_point.position) if distance < closest_distance: closest = control_point closest_distance = distance return closest
class NormandyTheater(ConflictTheater): terrain = normandy.Normandy() overview_image = "normandy.gif" reference_points = ( ReferencePoint(normandy.Needs_Oar_Point.position, Point(515, 329)), ReferencePoint(normandy.Evreux.position, Point(2029, 1709)), ) landmap = load_landmap("resources\\normandylandmap.p") daytime_map = { "dawn": (6, 8), "day": (10, 17), "dusk": (17, 18), "night": (0, 5), }
class NevadaTheater(ConflictTheater): terrain = nevada.Nevada() overview_image = "nevada.gif" reference_points = ( ReferencePoint(nevada.Mina_Airport_3Q0.position, Point(252, 295)), ReferencePoint(nevada.Laughlin_Airport.position, Point(844, 909)), ) landmap = load_landmap("resources\\nevlandmap.p") daytime_map = { "dawn": (4, 6), "day": (6, 17), "dusk": (17, 18), "night": (0, 5), }
class PersianGulfTheater(ConflictTheater): terrain = persiangulf.PersianGulf() overview_image = "persiangulf.gif" reference_points = ( ReferencePoint(persiangulf.Jiroft_Airport.position, Point(1692, 1343)), ReferencePoint(persiangulf.Liwa_Airbase.position, Point(358, 3238)), ) landmap = load_landmap("resources\\gulflandmap.p") daytime_map = { "dawn": (6, 8), "day": (8, 16), "dusk": (16, 18), "night": (0, 5), }
def test_poly_triangulation(self): points = [Point(1, 2), Point(3, 1), Point(7, 2), Point(9, 4), Point(6, 6), Point(6, 9), Point(4, 8), Point(2, 9), Point(1, 7), Point(0, 5)] poly = Polygon(points) areas = [x.area() for x in poly.triangulate()] self.assertEqual(areas, [2.5, 9.5, 10.0, 7.5, 9.0, 1.0, 5.0, 0.0])
class TheChannelTheater(ConflictTheater): terrain = thechannel.TheChannel() overview_image = "thechannel.gif" reference_points = (ReferencePoint(thechannel.Abbeville_Drucat.position, Point(2005, 2390)), ReferencePoint(thechannel.Detling.position, Point(706, 382))) landmap = load_landmap("resources\\channellandmap.p") daytime_map = { "dawn": (6, 8), "day": (10, 17), "dusk": (17, 18), "night": (0, 5), }
class SyriaTheater(ConflictTheater): terrain = syria.Syria() overview_image = "syria.gif" reference_points = ( ReferencePoint(syria.Eyn_Shemer.position, Point(564, 1289)), ReferencePoint(syria.Tabqa.position, Point(1329, 491)), ) landmap = load_landmap("resources\\syrialandmap.p") daytime_map = { "dawn": (6, 8), "day": (8, 16), "dusk": (16, 18), "night": (0, 5), }
def load_templates(): temp_mis = Mission() temp_mis.load_file("resources/tools/groundobject_templates.miz") groups = { } # type: typing.Dict[str, typing.Dict[int, typing.List[Static]]] for static_group in temp_mis.country( "USA").static_group + temp_mis.country( "USAF Aggressors").static_group: for static in static_group.units: static_name = str(static.name).split()[0] tpl_name, tpl_idx = static_name[:-1], int(static_name[-1]) groups[tpl_name] = groups.get(tpl_name, {}) groups[tpl_name][tpl_idx] = groups[tpl_name].get(tpl_idx, []) groups[tpl_name][tpl_idx].append(static) tpls = { name: {idx: [] for idx in groups[name].keys()} for name in groups.keys() } for category_name, category_groups in groups.items(): for idx, static_groups in category_groups.items(): dist = -1 a, b = None, None for aa in static_groups: for bb in static_groups: if aa.position.distance_to_point(bb.position) > dist: dist = aa.position.distance_to_point(bb.position) a = aa b = bb center = a.position.point_from_heading( a.position.heading_between_point(b.position), dist / 2) for static in static_groups: tpls[category_name][idx].append({ "type": static.type, "offset": Point(center.x - static.position.x, center.y - static.position.y), "heading": static.heading, }) tpls["aa"] = {0: [{"type": "AA", "offset": Point(0, 0), "heading": 0}]} return tpls
class CaucasusTheater(ConflictTheater): terrain = caucasus.Caucasus() overview_image = "caumap.gif" reference_points = ( ReferencePoint(caucasus.Gelendzhik.position, Point(176, 298)), ReferencePoint(caucasus.Batumi.position, Point(1307, 1205)), ) landmap = load_landmap("resources\\caulandmap.p") daytime_map = { "dawn": (6, 9), "day": (9, 18), "dusk": (18, 20), "night": (0, 5), }
def test_add_oblong(self) -> None: m: Mission = dcs.mission.Mission() layer = m.drawings.get_layer(StandardLayer.Common) self.assertEqual(0, len(layer.objects)) oblong = layer.add_oblong( Point(1000, 1000, m.terrain), Point(4000, 1000, m.terrain), 1000, resolution=20, ) self.assertEqual(1, len(layer.objects)) # Resolution 20 should give 43 points # (21 in each end and one extra to close polygon) self.assertEqual(43, len(oblong.points))
def test_add_drawings_to_loaded_mission(self) -> None: m: Mission = dcs.mission.Mission() self.assertEqual(0, len(m.load_file('tests/missions/Draw_tool_test.miz'))) circle = Circle( True, Point(10, 10, m.terrain), "TEST CIRCLE", Rgba(20, 30, 40, 200), ":S", Rgba(50, 60, 70, 150), 10, LineStyle.Solid, 100 ) m.drawings.layers[0].add_drawing(circle) self.assertEqual("TEST CIRCLE", m.drawings.layers[0].objects[1].name) mission_path = 'missions/Draw_tool_test_added_drawings.miz' m.save(mission_path) m2 = dcs.mission.Mission() self.assertEqual(0, len(m2.load_file(mission_path))) self.assert_expected_stuff(m2) self.assertEqual("TEST CIRCLE", m2.drawings.layers[0].objects[1].name)
def generate(self): # Command Post self.add_unit(AirDefence.SAM_Patriot_STR_AN_MPQ_53, "STR", self.position.x + 30, self.position.y + 30, self.heading) 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) num_launchers = random.randint(3, 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 aa_group = self.add_auxiliary_group("AA") num_launchers = random.randint(3, 4) positions = self.get_circular_position(num_launchers, launcher_distance=200, coverage=360) for i, (x, y, heading) in enumerate(positions): self.add_unit_to_group(aa_group, AirDefence.AAA_Vulcan_M163, f"SPAAA#{i}", Point(x, y), heading)
def load_points_from_data(self, points_data) -> List[Point]: points: List[Point] = [] for point_index in sorted(points_data.keys()): x = points_data[point_index]["x"] y = points_data[point_index]["y"] points.append(Point(x, y, self._terrain)) return points
def generate_fob(self) -> None: category = "fob" obj_name = self.control_point.name template = random.choice(list(self.templates[category].values())) point = self.control_point.position # Pick from preset locations object_id = 0 group_id = self.game.next_group_id() # TODO: Create only one TGO per objective, each with multiple units. for unit in template: object_id += 1 template_point = Point(unit["offset"].x, unit["offset"].y) g = BuildingGroundObject( obj_name, category, group_id, object_id, point + template_point, unit["heading"], self.control_point, unit["type"], is_fob_structure=True, ) self.control_point.connected_objectives.append(g)
def generate_strike_target_at(self, category: str, position: Point) -> None: obj_name = namegen.random_objective_name() template = random.choice(list(self.templates[category].values())) object_id = 0 group_id = self.game.next_group_id() # TODO: Create only one TGO per objective, each with multiple units. for unit in template: object_id += 1 template_point = Point(unit["offset"].x, unit["offset"].y) g = BuildingGroundObject( obj_name, category, group_id, object_id, position + template_point, unit["heading"], self.control_point, unit["type"], ) self.control_point.connected_objectives.append(g)
def _generate_destroyed_units(cls) -> None: """Add destroyed units to the Mission""" for d in cls.game.get_destroyed_units(): try: type_name = d["type"] if not isinstance(type_name, str): raise TypeError( "Expected the type of the destroyed static to be a string" ) utype = db.unit_type_from_name(type_name) except KeyError: continue pos = Point(cast(float, d["x"]), cast(float, d["z"])) if (utype is not None and not cls.game.position_culled(pos) and cls.game.settings.perf_destroyed_units): cls.current_mission.static_group( country=cls.current_mission.country( cls.game.player_country), name="", _type=utype, hidden=True, position=pos, heading=d["orientation"], dead=True, )
def nearest_airport(self, position: mapping.Point, coalition: str=None) -> Airport: airports = [x for x in self.airports.values() if x.coalition.lower() == coalition.lower()] if coalition else self.airports dist = sys.float_info.max airport = None for x in airports: d = position.distance_to_point(x.position) if d < dist: airport = x dist = d return airport
def nearest_node(self, position: mapping.Point): """Find nearest node to the given point. Args: position: Point to find the nearest Returns: The nearest node to the that point """ dist = sys.float_info.max node = None for x in self.nodes: d = position.distance_to_point(x.position) if d < dist: node = x dist = d return node