diff --git a/gen/armor.py b/gen/armor.py index 81931496..9bec232b 100644 --- a/gen/armor.py +++ b/gen/armor.py @@ -228,6 +228,17 @@ class GroundConflictGenerator: heading=forward_heading, move_formation=PointAction.OffRoad) + def _set_reform_waypoint( + self, + dcs_group: VehicleGroup, + forward_heading: int + ) -> None: + """Setting a waypoint close to the spawn position allows the group to reform gracefully + rather than spin + """ + reform_point = dcs_group.position.point_from_heading(forward_heading, 30) + dcs_group.add_waypoint(reform_point) + def _plan_artillery_action( self, stance: CombatStance, @@ -240,6 +251,7 @@ class GroundConflictGenerator: Handles adding the DCS tasks for artillery groups for all combat stances. Returns True if tasking was added, returns False if the stance was not a combat stance. """ + self._set_reform_waypoint(dcs_group, forward_heading) if stance != CombatStance.RETREAT: hold_task = Hold() hold_task.number = 1 @@ -259,10 +271,10 @@ class GroundConflictGenerator: if stance != CombatStance.RETREAT: # Hold position - dcs_group.points[0].tasks.append(Hold()) - retreat = self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE/3)) - dcs_group.add_waypoint(dcs_group.position.point_from_heading(forward_heading, 1), PointAction.OffRoad) dcs_group.points[1].tasks.append(Hold()) + retreat = self.find_retreat_point(dcs_group, heading_sum(forward_heading, 180), (int)(RETREAT_DISTANCE/3)) + dcs_group.add_waypoint(dcs_group.position.point_from_heading(forward_heading, 1), PointAction.OffRoad) + dcs_group.points[2].tasks.append(Hold()) dcs_group.add_waypoint(retreat, PointAction.OffRoad) artillery_fallback = TriggerOnce(Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id)) @@ -300,6 +312,7 @@ class GroundConflictGenerator: Handles adding the DCS tasks for tank and IFV groups for all combat stances. Returns True if tasking was added, returns False if the stance was not a combat stance. """ + self._set_reform_waypoint(dcs_group, forward_heading) if stance == CombatStance.AGGRESSIVE: # Attack nearest enemy if any # Then move forward OR Attack enemy base if it is not too far away @@ -314,7 +327,7 @@ class GroundConflictGenerator: ) ) dcs_group.add_waypoint(target.points[0].position + rand_offset, PointAction.OffRoad) - dcs_group.points[1].tasks.append(AttackGroup(target.id)) + dcs_group.points[2].tasks.append(AttackGroup(target.id)) if ( to_cp.position.distance_to_point(dcs_group.points[0].position) @@ -352,7 +365,7 @@ class GroundConflictGenerator: ) ) dcs_group.add_waypoint(target.points[0].position+rand_offset, PointAction.OffRoad) - dcs_group.points[i].tasks.append(AttackGroup(target.id)) + dcs_group.points[i + 1].tasks.append(AttackGroup(target.id)) if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE: attack_point = to_cp.position.random_point_within(500, 0) dcs_group.add_waypoint(attack_point) @@ -373,6 +386,7 @@ class GroundConflictGenerator: Handles adding the DCS tasks for APC and ATGM groups for all combat stances. Returns True if tasking was added, returns False if the stance was not a combat stance. """ + self._set_reform_waypoint(dcs_group, forward_heading) if stance in [CombatStance.AGGRESSIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]: # APC & ATGM will never move too much forward, but will follow along any offensive if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE: @@ -464,8 +478,8 @@ class GroundConflictGenerator: self.mission.triggerrules.triggers.append(fallback) - @staticmethod def find_retreat_point( + self, dcs_group: VehicleGroup, frontline_heading: int, distance: int = RETREAT_DISTANCE @@ -476,10 +490,17 @@ class GroundConflictGenerator: :param frontline_heading: Heading of the frontline :return: dcs.mapping.Point object with the desired position """ - return dcs_group.points[0].position.point_from_heading(frontline_heading-180, distance) + desired_point = dcs_group.points[0].position.point_from_heading(heading_sum(frontline_heading, +180), distance) + return Conflict.find_ground_position( + desired_point, + 1000000, + heading_sum(frontline_heading, 90), + self.conflict.theater, + ) + - @staticmethod def find_offensive_point( + self, dcs_group: VehicleGroup, frontline_heading: int, distance: int @@ -491,7 +512,13 @@ class GroundConflictGenerator: :param distance: Distance of the offensive (how far unit should move) :return: dcs.mapping.Point object with the desired position """ - return dcs_group.points[0].position.point_from_heading(frontline_heading, distance) + desired_point = dcs_group.points[0].position.point_from_heading(frontline_heading, distance) + return Conflict.find_ground_position( + desired_point, + 1000000, + heading_sum(frontline_heading, 90), + self.conflict.theater, + ) @staticmethod def find_n_nearest_enemy_groups( @@ -562,10 +589,10 @@ class GroundConflictGenerator: For artilery group, decide the distance from frontline with the range of the unit """ rg = group.units[0].threat_range - 7500 - if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY]: + if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]: rg = DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY] - if rg < DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK]: - rg = DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK] + 100 + if rg < DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]: + rg = DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK] return rg def get_valid_position_for_group( @@ -576,16 +603,13 @@ class GroundConflictGenerator: heading: int, spawn_heading: int ): - i = 0 - while i < 1000: - shifted = conflict_position.point_from_heading(heading, random.randint(0, combat_width)) - final_position = shifted.point_from_heading(spawn_heading, distance_from_frontline) + shifted = conflict_position.point_from_heading(heading, random.randint(0, combat_width)) + desired_point = shifted.point_from_heading( + spawn_heading, + random.randint(distance_from_frontline[0], distance_from_frontline[1]) + ) + return Conflict.find_ground_position(desired_point, combat_width, heading, self.conflict.theater) - if self.conflict.theater.is_on_land(final_position): - return final_position - i += 1 - continue - return None def _generate_groups( self, @@ -618,7 +642,7 @@ class GroundConflictGenerator: group.units[0], len(group.units), final_position, - distance_from_frontline, + random.randint(distance_from_frontline[0], distance_from_frontline[1]), heading=opposite_heading(spawn_heading), ) if is_player: diff --git a/gen/conflictgen.py b/gen/conflictgen.py index 65ea4058..c2c4cbbc 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -102,4 +102,4 @@ class Conflict: return pos pos = initial.point_from_heading(opposite_heading(heading), distance) logging.error("Didn't find ground position ({})!".format(initial)) - return initial + return None diff --git a/gen/ground_forces/ai_ground_planner.py b/gen/ground_forces/ai_ground_planner.py index b0f14df4..12df519f 100644 --- a/gen/ground_forces/ai_ground_planner.py +++ b/gen/ground_forces/ai_ground_planner.py @@ -187,14 +187,14 @@ class CombatGroupRole(Enum): DISTANCE_FROM_FRONTLINE = { - CombatGroupRole.TANK:3200, - CombatGroupRole.APC:8000, - CombatGroupRole.IFV:3700, - CombatGroupRole.ARTILLERY:18000, - CombatGroupRole.SHORAD:13000, - CombatGroupRole.LOGI:20000, - CombatGroupRole.INFANTRY:3000, - CombatGroupRole.ATGM:6200 + CombatGroupRole.TANK: (2200, 3200), + CombatGroupRole.APC: (7500, 8500), + CombatGroupRole.IFV: (2700, 3700), + CombatGroupRole.ARTILLERY: (16000, 18000), + CombatGroupRole.SHORAD: (12000, 13000), + CombatGroupRole.LOGI: (18000, 20000), + CombatGroupRole.INFANTRY: (2800, 3300), + CombatGroupRole.ATGM: (5200, 6200), } GROUP_SIZES_BY_COMBAT_STANCE = {