def add_morale_trigger(self, dcs_group, forward_heading): """ This add a trigger to manage units fleeing whenever their group is hit hard, or being engaged by CAS """ if len(dcs_group.units) == 1: return # Units should hold position on last waypoint dcs_group.points[len(dcs_group.points) - 1].tasks.append(Hold()) # Force unit heading for unit in dcs_group.units: unit.heading = forward_heading dcs_group.manualHeading = True # We add a new retreat waypoint dcs_group.add_waypoint(self.find_retreat_point(dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 8)), PointAction.OffRoad) # Fallback task fallback = ControlledTask(GoToWaypoint(toIndex=len(dcs_group.points))) fallback.enabled = False dcs_group.add_trigger_action(Hold()) dcs_group.add_trigger_action(fallback) # Create trigger fallback = TriggerOnce(Event.NoEvent, "Morale manager #" + str(dcs_group.id)) # Usually more than 50% casualties = RETREAT fallback.add_condition(GroupLifeLess(dcs_group.id, random.randint(51, 76))) # Do retreat to the configured retreat waypoint fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) self.mission.triggerrules.triggers.append(fallback)
def _gen_markers(self) -> None: """ Generate markers on F10 map for each existing objective """ if self.game.settings.generate_marks: mark_trigger = TriggerOnce(Event.NoEvent, "Marks generator") mark_trigger.add_condition(TimeAfter(1)) v = 10 for cp in self.game.theater.controlpoints: seen = set() for ground_object in cp.ground_objects: if ground_object.obj_name in seen: continue seen.add(ground_object.obj_name) for location in ground_object.mark_locations: zone = self.mission.triggers.add_triggerzone( location, radius=10, hidden=True, name="MARK") if cp.captured: name = ground_object.obj_name + " [ALLY]" else: name = ground_object.obj_name + " [ENEMY]" mark_trigger.add_action( MarkToAll(v, zone.id, String(name))) v += 1 self.mission.triggerrules.triggers.append(mark_trigger)
def set_startup_time(self, delay: timedelta) -> None: # Uncontrolled causes the AI unit to spawn, but not begin startup. self.group.uncontrolled = True activation_trigger = TriggerOnce(Event.NoEvent, f"FlightStartTrigger{self.group.id}") activation_trigger.add_condition( TimeAfter(seconds=int(delay.total_seconds()))) self.prevent_spawn_at_hostile_airbase(activation_trigger) self.group.add_trigger_action(StartCommand()) activation_trigger.add_action( AITaskPush(self.group.id, len(self.group.tasks))) self.mission.triggerrules.triggers.append(activation_trigger)
def set_activation_time(self, delay: timedelta) -> None: # Note: Late activation causes the waypoint TOTs to look *weird* in the # mission editor. Waypoint times will be relative to the group # activation time rather than in absolute local time. A flight delayed # until 09:10 when the overall mission start time is 09:00, with a join # time of 09:30 will show the join time as 00:30, not 09:30. self.group.late_activation = True activation_trigger = TriggerOnce( Event.NoEvent, f"FlightLateActivationTrigger{self.group.id}") activation_trigger.add_condition( TimeAfter(seconds=int(delay.total_seconds()))) self.prevent_spawn_at_hostile_airbase(activation_trigger) activation_trigger.add_action(ActivateGroup(self.group.id)) self.mission.triggerrules.triggers.append(activation_trigger)
def setup_group_activation_trigger(self, flight, group): if flight.scheduled_in > 0 and flight.client_count == 0: if flight.start_type != "In Flight" and flight.from_cp.cptype not in [ ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP ]: group.late_activation = False group.uncontrolled = True activation_trigger = TriggerOnce( Event.NoEvent, "FlightStartTrigger" + str(group.id)) activation_trigger.add_condition( TimeAfter(seconds=flight.scheduled_in * 60)) if (flight.from_cp.cptype == ControlPointType.AIRBASE): if flight.from_cp.captured: activation_trigger.add_condition( CoalitionHasAirdrome( self.game.get_player_coalition_id(), flight.from_cp.id)) else: activation_trigger.add_condition( CoalitionHasAirdrome( self.game.get_enemy_coalition_id(), flight.from_cp.id)) if flight.flight_type == FlightType.INTERCEPTION: self.setup_interceptor_triggers(group, flight, activation_trigger) group.add_trigger_action(StartCommand()) activation_trigger.add_action( AITaskPush(group.id, len(group.tasks))) self.m.triggerrules.triggers.append(activation_trigger) else: group.late_activation = True activation_trigger = TriggerOnce( Event.NoEvent, "FlightLateActivationTrigger" + str(group.id)) activation_trigger.add_condition( TimeAfter(seconds=flight.scheduled_in * 60)) if (flight.from_cp.cptype == ControlPointType.AIRBASE): if flight.from_cp.captured: activation_trigger.add_condition( CoalitionHasAirdrome( self.game.get_player_coalition_id(), flight.from_cp.id)) else: activation_trigger.add_condition( CoalitionHasAirdrome( self.game.get_enemy_coalition_id(), flight.from_cp.id)) if flight.flight_type == FlightType.INTERCEPTION: self.setup_interceptor_triggers(group, flight, activation_trigger) activation_trigger.add_action(ActivateGroup(group.id)) self.m.triggerrules.triggers.append(activation_trigger)
def plan_action_for_groups(self, stance, ally_groups, enemy_groups, forward_heading, from_cp, to_cp): if not self.game.settings.perf_moving_units: return for dcs_group, group in ally_groups: if group.role == CombatGroupRole.ARTILLERY: # Fire on any ennemy in range if self.game.settings.perf_artillery: target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups) if target is not None: if stance != CombatStance.RETREAT: hold_task = Hold() hold_task.number = 1 dcs_group.add_trigger_action(hold_task) # Artillery strike random start artillery_trigger = TriggerOnce(Event.NoEvent, "ArtilleryFireTask #" + str(dcs_group.id)) artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45)* 60)) fire_task = FireAtPoint(target, len(group.units) * 10, 100) if stance != CombatStance.RETREAT: fire_task.number = 2 else: fire_task.number = 1 dcs_group.add_trigger_action(fire_task) artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) self.mission.triggerrules.triggers.append(artillery_trigger) # Artillery will fall back when under attack 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()) dcs_group.add_waypoint(retreat, PointAction.OffRoad) artillery_fallback = TriggerOnce(Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id)) for i, u in enumerate(dcs_group.units): artillery_fallback.add_condition(UnitDamaged(u.id)) if i < len(dcs_group.units) - 1: artillery_fallback.add_condition(Or()) hold_2 = Hold() hold_2.number = 3 dcs_group.add_trigger_action(hold_2) retreat_task = GoToWaypoint(toIndex=3) retreat_task.number = 4 dcs_group.add_trigger_action(retreat_task) artillery_fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) self.mission.triggerrules.triggers.append(artillery_fallback) for u in dcs_group.units: u.initial = True u.heading = forward_heading + random.randint(-5,5) elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]: if stance == CombatStance.AGGRESIVE: # Attack nearest enemy if any # Then move forward OR Attack enemy base if it is not too far away target = self.find_nearest_enemy_group(dcs_group, enemy_groups) if target is not None: rand_offset = Point(random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK)) dcs_group.add_waypoint(target.points[0].position + rand_offset, PointAction.OffRoad) dcs_group.points[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) else: attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE) dcs_group.add_waypoint(attack_point, PointAction.OnRoad) elif stance == CombatStance.BREAKTHROUGH: # In breakthrough mode, the units will move forward # If the enemy base is close enough, the units will attack the base if to_cp.position.distance_to_point( dcs_group.points[0].position) <= BREAKTHROUGH_OFFENSIVE_DISTANCE: attack_point = to_cp.position.random_point_within(500, 0) else: attack_point = self.find_offensive_point(dcs_group, forward_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE) dcs_group.add_waypoint(attack_point, PointAction.OnRoad) elif stance == CombatStance.ELIMINATION: # In elimination mode, the units focus on destroying as much enemy groups as possible targets = self.find_n_nearest_enemy_groups(dcs_group, enemy_groups, 3) i = 1 for target in targets: rand_offset = Point(random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK), random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK)) dcs_group.add_waypoint(target.points[0].position+rand_offset, PointAction.OffRoad) dcs_group.points[i].tasks.append(AttackGroup(target.id)) i = i + 1 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) if stance != CombatStance.RETREAT: self.add_morale_trigger(dcs_group, forward_heading) elif group.role in [CombatGroupRole.APC, CombatGroupRole.ATGM]: if stance in [CombatStance.AGGRESIVE, 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: attack_point = to_cp.position.random_point_within(500, 0) else: attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE) dcs_group.add_waypoint(attack_point, PointAction.OnRoad) if stance != CombatStance.RETREAT: self.add_morale_trigger(dcs_group, forward_heading) if stance == CombatStance.RETREAT: # In retreat mode, the units will fall back # If the ally base is close enough, the units will even regroup there if from_cp.position.distance_to_point(dcs_group.points[0].position) <= RETREAT_DISTANCE: retreat_point = from_cp.position.random_point_within(500, 250) else: retreat_point = self.find_retreat_point(dcs_group, forward_heading) reposition_point = retreat_point.point_from_heading(forward_heading, 10) # Another point to make the unit face the enemy dcs_group.add_waypoint(retreat_point, PointAction.OnRoad) dcs_group.add_waypoint(reposition_point, PointAction.OffRoad)
def _plan_artillery_action( self, stance: CombatStance, gen_group: CombatGroup, dcs_group: VehicleGroup, forward_heading: int, target: Point, ) -> bool: """ 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 dcs_group.add_trigger_action(hold_task) # Artillery strike random start artillery_trigger = TriggerOnce( Event.NoEvent, "ArtilleryFireTask #" + str(dcs_group.id) ) artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45) * 60)) # TODO: Update to fire at group instead of point fire_task = FireAtPoint(target, gen_group.size * 10, 100) fire_task.number = 2 if stance != CombatStance.RETREAT else 1 dcs_group.add_trigger_action(fire_task) artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks))) self.mission.triggerrules.triggers.append(artillery_trigger) # Artillery will fall back when under attack if stance != CombatStance.RETREAT: # Hold position dcs_group.points[1].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[2].tasks.append(Hold()) dcs_group.add_waypoint(retreat, PointAction.OffRoad) artillery_fallback = TriggerOnce( Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id) ) for i, u in enumerate(dcs_group.units): artillery_fallback.add_condition(UnitDamaged(u.id)) if i < len(dcs_group.units) - 1: artillery_fallback.add_condition(Or()) hold_2 = Hold() hold_2.number = 3 dcs_group.add_trigger_action(hold_2) retreat_task = GoToWaypoint(to_index=3) retreat_task.number = 4 dcs_group.add_trigger_action(retreat_task) artillery_fallback.add_action( AITaskPush(dcs_group.id, len(dcs_group.tasks)) ) self.mission.triggerrules.triggers.append(artillery_fallback) for u in dcs_group.units: u.heading = forward_heading + random.randint(-5, 5) return True return False