Esempio n. 1
0
    def _plan_apc_atgm_action(
        self,
        stance: CombatStance,
        dcs_group: VehicleGroup,
        forward_heading: int,
        to_cp: ControlPoint,
    ) -> bool:
        """
        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
            ):
                attack_point = self.conflict.theater.nearest_land_pos(
                    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.OffRoad)

        if stance != CombatStance.RETREAT:
            self.add_morale_trigger(dcs_group, forward_heading)
            return True
        return False
Esempio n. 2
0
 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, 50)
     dcs_group.add_waypoint(reform_point)
Esempio n. 3
0
 def add_unit_to_group(self, group: unitgroup.VehicleGroup,
                       unit_type: Type[VehicleType], name: str,
                       position: Point, heading: int) -> Vehicle:
     unit = Vehicle(self.game.next_unit_id(), f"{group.name}|{name}",
                    unit_type.id)
     unit.position = position
     unit.heading = heading
     group.add_unit(unit)
     return unit
Esempio n. 4
0
    def add_unit_to_group(
        self,
        group: VehicleGroup,
        unit_type: Type[VehicleType],
        name: str,
        position: Point,
        heading: int,
    ) -> Vehicle:
        unit = Vehicle(self.game.next_unit_id(), f"{group.name}|{name}",
                       unit_type.id)
        unit.position = position
        unit.heading = heading
        group.add_unit(unit)

        # get price of unit to calculate the real price of the whole group
        try:
            ground_unit_type = next(GroundUnitType.for_dcs_type(unit_type))
            self.price += ground_unit_type.price
        except StopIteration:
            logging.error(f"Cannot get price for unit {unit_type.name}")

        return unit
Esempio n. 5
0
    def add_morale_trigger(self, dcs_group: VehicleGroup,
                           forward_heading: Heading) -> None:
        """
        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.degrees
        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
        task = ControlledTask(GoToWaypoint(to_index=len(dcs_group.points)))
        task.enabled = False
        dcs_group.add_trigger_action(Hold())
        dcs_group.add_trigger_action(task)

        # 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 add_auxiliary_group(self, role: SkynetRole) -> VehicleGroup:
     gid = self.game.next_group_id()
     group = VehicleGroup(gid, self.group_name_for_role(gid, role))
     self.auxiliary_groups.append(group)
     return group
Esempio n. 7
0
    def _plan_tank_ifv_action(
        self,
        stance: CombatStance,
        enemy_groups: List[Tuple[VehicleGroup, CombatGroup]],
        dcs_group: VehicleGroup,
        forward_heading: int,
        to_cp: ControlPoint,
    ) -> bool:
        """
        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
            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),
                )
                target_point = self.conflict.theater.nearest_land_pos(
                    target.points[0].position + rand_offset
                )
                dcs_group.add_waypoint(target_point)
                dcs_group.points[2].tasks.append(AttackGroup(target.id))

            if (
                to_cp.position.distance_to_point(dcs_group.points[0].position)
                <= AGGRESIVE_MOVE_DISTANCE
            ):
                attack_point = self.conflict.theater.nearest_land_pos(
                    to_cp.position.random_point_within(500, 0)
                )
            else:
                # We use an offset heading here because DCS doesn't always
                # force vehicles to move if there's no heading change.
                offset_heading = forward_heading - 2
                if offset_heading < 0:
                    offset_heading = 358
                attack_point = self.find_offensive_point(
                    dcs_group, offset_heading, AGGRESIVE_MOVE_DISTANCE
                )
            dcs_group.add_waypoint(attack_point, PointAction.OffRoad)
        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 = self.conflict.theater.nearest_land_pos(
                    to_cp.position.random_point_within(500, 0)
                )
            else:
                # We use an offset heading here because DCS doesn't always
                # force vehicles to move if there's no heading change.
                offset_heading = forward_heading - 1
                if offset_heading < 0:
                    offset_heading = 359
                attack_point = self.find_offensive_point(
                    dcs_group, offset_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE
                )
            dcs_group.add_waypoint(attack_point, PointAction.OffRoad)
        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)
            for i, target in enumerate(targets, start=1):
                rand_offset = Point(
                    random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
                    random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
                )
                target_point = self.conflict.theater.nearest_land_pos(
                    target.points[0].position + rand_offset
                )
                dcs_group.add_waypoint(target_point, PointAction.OffRoad)
                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 = self.conflict.theater.nearest_land_pos(
                    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)
            return True
        return False
Esempio n. 8
0
    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
Esempio n. 9
0
 def add_auxiliary_group(self, name_suffix: str) -> VehicleGroup:
     group = VehicleGroup(self.game.next_group_id(),
                          "|".join([self.go.group_name, name_suffix]))
     self.auxiliary_groups.append(group)
     return group