Example #1
0
        // Special Zig-Zag movement for whirlwind/tempest
        /// <summary>
        /// Finds an optimal position for Barbarian Whirlwind, Monk Tempest Rush, or Demon Hunter Strafe
        /// </summary>
        /// <param name="origin"></param>
        /// <param name="ringDistance"></param>
        /// <param name="randomizeDistance"></param>
        /// <returns></returns>
        internal static Vector3 GetZigZagTarget(Vector3 origin, float ringDistance, bool randomizeDistance = false)
        {
            var     minDistance      = 9f;
            Vector3 myPos            = PlayerStatus.CurrentPosition;
            float   distanceToTarget = origin.Distance2D(myPos);

            Vector3 zigZagPoint = origin;

            using (new PerformanceLogger("FindZigZagTargetLocation"))
            {
                using (new PerformanceLogger("FindZigZagTargetLocation.CheckObjectCache"))
                {
                    bool  useTargetBasedZigZag = false;
                    float maxDistance          = 25f;
                    int   minTargets           = 2;

                    if (GilesTrinity.PlayerStatus.ActorClass == ActorClass.Monk)
                    {
                        maxDistance          = 20f;
                        minTargets           = 3;
                        useTargetBasedZigZag = (GilesTrinity.Settings.Combat.Monk.TargetBasedZigZag);
                    }
                    if (GilesTrinity.PlayerStatus.ActorClass == ActorClass.Barbarian)
                    {
                        useTargetBasedZigZag = (GilesTrinity.Settings.Combat.Barbarian.TargetBasedZigZag);
                    }

                    int  eliteCount         = ObjectCache.Count(u => u.Type == GObjectType.Unit && u.IsBossOrEliteRareUnique);
                    bool shouldZigZagElites = ((GilesTrinity.CurrentTarget.IsBossOrEliteRareUnique && eliteCount > 1) || eliteCount == 0);

                    if (useTargetBasedZigZag && shouldZigZagElites && !AnyTreasureGoblinsPresent && ObjectCache.Where(o => o.Type == GObjectType.Unit).Count() >= minTargets)
                    {
                        var clusterPoint = TargetUtil.GetBestClusterPoint(ringDistance, ringDistance, false);
                        if (clusterPoint.Distance2D(PlayerStatus.CurrentPosition) >= minDistance)
                        {
                            DbHelper.Log(LogCategory.Movement, "Returning ZigZag: BestClusterPoint {0} r-dist={1} t-dist={2}", clusterPoint, ringDistance, clusterPoint.Distance2D(PlayerStatus.CurrentPosition));
                            return(clusterPoint);
                        }
                        IEnumerable <GilesObject> zigZagTargets =
                            from u in ObjectCache
                            where u.Type == GObjectType.Unit && u.RadiusDistance < maxDistance &&
                            !GilesTrinity.hashAvoidanceObstacleCache.Any(a => Vector3.Distance(u.Position, a.Location) < AvoidanceManager.GetAvoidanceRadiusBySNO(a.ActorSNO, a.Radius) && PlayerStatus.CurrentHealthPct <= AvoidanceManager.GetAvoidanceHealthBySNO(a.ActorSNO, 1))
                            select u;
                        if (zigZagTargets.Count() >= minTargets)
                        {
                            zigZagPoint = zigZagTargets.OrderByDescending(u => u.CentreDistance).FirstOrDefault().Position;
                            if (NavHelper.CanRayCast(zigZagPoint) && zigZagPoint.Distance2D(PlayerStatus.CurrentPosition) >= minDistance)
                            {
                                DbHelper.Log(LogCategory.Movement, "Returning ZigZag: TargetBased {0} r-dist={1} t-dist={2}", zigZagPoint, ringDistance, zigZagPoint.Distance2D(PlayerStatus.CurrentPosition));
                                return(zigZagPoint);
                            }
                        }
                    }
                }

                Random rndNum = new Random(int.Parse(Guid.NewGuid().ToString().Substring(0, 8), NumberStyles.HexNumber));

                using (new PerformanceLogger("FindZigZagTargetLocation.RandomZigZagPoint"))
                {
                    float   highestWeightFound = float.NegativeInfinity;
                    Vector3 bestLocation       = Vector3.Zero;

                    // the unit circle always starts at 0 :)
                    double min = 0;
                    // the maximum size of a unit circle
                    double max = 2 * Math.PI;
                    // the number of times we will iterate around the circle to find points
                    double piSlices = 16;

                    // We will do several "passes" to make sure we can get a point that we can least zig-zag to
                    // The total number of points tested will be piSlices * distancePasses.Count
                    List <float> distancePasses = new List <float>();
                    distancePasses.Add(ringDistance * 1 / 2); // Do one loop at 1/2 distance
                    distancePasses.Add(ringDistance * 3 / 4); // Do one loop at 3/4 distance
                    distancePasses.Add(ringDistance);         // Do one loop at exact distance

                    foreach (float distance in distancePasses)
                    {
                        for (double direction = min; direction < max; direction += (Math.PI / piSlices))
                        {
                            // Starting weight is 1
                            float pointWeight = 1f;

                            // Find a new XY
                            zigZagPoint = MathEx.GetPointAt(origin, distance, (float)direction);
                            // Get the Z
                            zigZagPoint.Z = GilesTrinity.gp.GetHeight(zigZagPoint.ToVector2());

                            // Make sure we're actually zig-zagging our target, except if we're kiting
                            bool intersectsPath = MathUtil.IntersectsPath(CurrentTarget.Position, CurrentTarget.Radius, myPos, zigZagPoint);
                            if (GilesTrinity.PlayerKiteDistance <= 0 && !intersectsPath)
                            {
                                continue;
                            }

                            // if we're kiting, lets not actualy run through monsters
                            if (GilesTrinity.PlayerKiteDistance > 0 && GilesTrinity.hashMonsterObstacleCache.Any(m => m.Location.Distance(zigZagPoint) <= GilesTrinity.PlayerKiteDistance))
                            {
                                continue;
                            }

                            // Ignore point if any AoE in this point position
                            if (GilesTrinity.hashAvoidanceObstacleCache.Any(m => m.Location.Distance(zigZagPoint) <= m.Radius && PlayerStatus.CurrentHealthPct <= AvoidanceManager.GetAvoidanceHealthBySNO(m.ActorSNO, 1)))
                            {
                                continue;
                            }

                            // Make sure this point is in LoS/walkable (not around corners or into a wall)
                            bool canRayCast = !Navigator.Raycast(PlayerStatus.CurrentPosition, zigZagPoint);
                            if (!canRayCast)
                            {
                                continue;
                            }

                            float distanceToPoint           = zigZagPoint.Distance2D(myPos);
                            float distanceFromTargetToPoint = zigZagPoint.Distance2D(origin);

                            // Lots of weight for points further away from us (e.g. behind our CurrentTarget)
                            pointWeight *= distanceToPoint;

                            // Add weight for any units in this point
                            int monsterCount = ObjectCache.Count(u => u.Type == GObjectType.Unit && u.Position.Distance2D(zigZagPoint) <= Math.Max(u.Radius, 10f));
                            if (monsterCount > 0)
                            {
                                pointWeight *= monsterCount;
                            }

                            DbHelper.Log(LogCategory.Movement, "ZigZag Point: {0} distance={1:0} distaceFromTarget={2:0} intersectsPath={3} weight={4:0} monsterCount={5}",
                                         zigZagPoint, distanceToPoint, distanceFromTargetToPoint, intersectsPath, pointWeight, monsterCount);

                            // Use this one if it's more weight, or we haven't even found one yet, or if same weight as another with a random chance
                            if (pointWeight > highestWeightFound)
                            {
                                highestWeightFound = pointWeight;

                                if (GilesTrinity.Settings.Combat.Misc.UseNavMeshTargeting)
                                {
                                    bestLocation = new Vector3(zigZagPoint.X, zigZagPoint.Y, GilesTrinity.gp.GetHeight(zigZagPoint.ToVector2()));
                                }
                                else
                                {
                                    bestLocation = new Vector3(zigZagPoint.X, zigZagPoint.Y, zigZagPoint.Z + 4);
                                }
                            }
                        }
                    }
                    DbHelper.Log(LogCategory.Movement, "Returning ZigZag: RandomXY {0} r-dist={1} t-dist={2}", bestLocation, ringDistance, bestLocation.Distance2D(PlayerStatus.CurrentPosition));
                    return(bestLocation);
                }
            }
        }
Example #2
0
        internal static Vector3 newFindSafeZone(Vector3 origin, bool shouldKite = false, bool isStuck = false, IEnumerable <GilesObject> monsterList = null)
        {
            /*
             * generate 50x50 grid of 5x5 squares within max 100 distance from origin to edge of grid
             *
             * all squares start with 0 weight
             *
             * check if Center IsNavigable
             * check Z
             * check if avoidance is present
             * check if monsters are present
             *
             * final distance tile weight = (Max Dist - Dist)/Max Dist*Max Weight
             *
             * end result should be that only navigable squares where no avoidance, monsters, or obstacles are present
             */

            float gridSquareSize = 5f;
            int   maxDistance    = 200;
            int   maxWeight      = 100;
            int   maxZDiff       = 14;

            int gridTotalSize = (int)(maxDistance / gridSquareSize) * 2;

            /* If maxDistance is the radius of a circle from the origin, then we want to get the hypotenuse of the radius (x) and tangent (y) as our search grid corner
             * anything outside of the circle will not be considered
             */
            Vector2 topleft = new Vector2(origin.X - maxDistance, origin.Y - maxDistance);


            //Make a circle on the corners of the square
            double gridSquareRadius = Math.Sqrt((Math.Pow(gridSquareSize / 2, 2) + Math.Pow(gridSquareSize / 2, 2)));

            GridPoint bestPoint = new GridPoint(Vector3.Zero, 0, 0);

            int nodesNotNavigable = 0;
            int nodesZDiff        = 0;
            int nodesGT45Raycast  = 0;
            int nodesAvoidance    = 0;
            int nodesMonsters     = 0;
            int pathFailures      = 0;

            for (int x = 0; x < gridTotalSize; x++)
            {
                for (int y = 0; y < gridTotalSize; y++)
                {
                    Vector2 xy   = new Vector2(topleft.X + (x * gridSquareSize), topleft.Y + (y * gridSquareSize));
                    Vector3 xyz  = Vector3.Zero;
                    Point   p_xy = Point.Empty;

                    if (GilesTrinity.Settings.Combat.Misc.UseNavMeshTargeting)
                    {
                        xyz = new Vector3(xy.X, xy.Y, gp.GetHeight(xy));
                    }
                    else
                    {
                        xyz = new Vector3(xy.X, xy.Y, origin.Z + 4);
                    }

                    GridPoint gridPoint = new GridPoint(xyz, 0, origin.Distance(xyz));

                    //if (gridPoint.Distance > maxDistance + gridSquareRadius)
                    //    continue;
                    if (GilesTrinity.Settings.Combat.Misc.UseNavMeshTargeting)
                    {
                        p_xy = gp.WorldToGrid(xy);
                        if (!gp.CanStandAt(p_xy))
                        {
                            nodesNotNavigable++;
                            continue;
                        }
                    }
                    else
                    {
                        // If ZDiff is way too different (up a cliff or wall)
                        if (Math.Abs(gridPoint.Position.Z - origin.Z) > maxZDiff)
                        {
                            nodesZDiff++;
                            continue;
                        }
                    }
                    if (gridPoint.Distance > 45 && Navigator.Raycast(origin, xyz))
                    {
                        nodesGT45Raycast++;
                        continue;
                    }

                    if (isStuck && gridPoint.Distance > (PlayerMover.iTotalAntiStuckAttempts + 2) * 5)
                    {
                        continue;
                    }

                    /*
                     * Check if a square is occupied already
                     */
                    // Avoidance
                    if (GilesTrinity.hashAvoidanceObstacleCache.Any(a => Vector3.Distance(xyz, a.Location) - a.Radius <= gridSquareRadius))
                    {
                        nodesAvoidance++;
                        continue;
                    }
                    // Obstacles
                    if (GilesTrinity.hashNavigationObstacleCache.Any(a => Vector3.Distance(xyz, a.Location) - a.Radius <= gridSquareRadius))
                    {
                        nodesMonsters++;
                        continue;
                    }

                    // Monsters
                    if (shouldKite)
                    {
                        // Any monster standing in this GridPoint
                        if (GilesTrinity.hashMonsterObstacleCache.Any(a => Vector3.Distance(xyz, a.Location) - a.Radius <= (shouldKite ? gridSquareRadius : gridSquareSize + GilesTrinity.PlayerKiteDistance)))
                        {
                            nodesMonsters++;
                            continue;
                        }

                        if (!hasEmergencyTeleportUp)
                        {
                            // Any monsters blocking in a straight line between origin and this GridPoint
                            foreach (GilesObstacle monster in GilesTrinity.hashMonsterObstacleCache.Where(m =>
                                                                                                          MathEx.IntersectsPath(new Vector3(m.Location.X, m.Location.Y, 0), m.Radius, new Vector3(origin.X, origin.Y, 0), new Vector3(gridPoint.Position.X, gridPoint.Position.Y, 0))
                                                                                                          ))
                            {
                                nodesMonsters++;
                                continue;
                            }
                        }

                        int nearbyMonsters = (monsterList != null ? monsterList.Count() : 0);
                    }

                    if (isStuck && UsedStuckSpots.Any(p => Vector3.Distance(p.Position, gridPoint.Position) <= gridSquareRadius))
                    {
                        continue;
                    }

                    if (!isStuck)
                    {
                        gridPoint.Weight = ((maxDistance - gridPoint.Distance) / maxDistance) * maxWeight;

                        // Low weight for close range grid points
                        if (shouldKite && gridPoint.Distance < GilesTrinity.PlayerKiteDistance)
                        {
                            gridPoint.Weight = (int)gridPoint.Distance;
                        }
                    }
                    else
                    {
                        gridPoint.Weight = gridPoint.Distance;
                    }

                    // Boss Areas
                    if (UnSafeZone.UnsafeKiteAreas.Any(a => a.WorldId == ZetaDia.CurrentWorldId && Vector3.Distance(a.Position, gridPoint.Position) <= a.Radius))
                    {
                        continue;
                    }

                    if (shouldKite)
                    {
                        // make sure we can raycast to our target
                        if (!NavHelper.CanRayCast(gridPoint.Position, GilesTrinity.LastPrimaryTargetPosition))
                        {
                            continue;
                        }

                        /*
                         * We want to down-weight any grid points where monsters are closer to it than we are
                         */
                        foreach (GilesObstacle monster in GilesTrinity.hashMonsterObstacleCache)
                        {
                            float distFromMonster           = gridPoint.Position.Distance2D(monster.Location);
                            float distFromOrigin            = gridPoint.Position.Distance2D(origin);
                            float distFromOriginToAvoidance = origin.Distance2D(monster.Location);
                            if (distFromOriginToAvoidance < distFromOrigin)
                            {
                                continue;
                            }

                            if (distFromMonster < distFromOrigin)
                            {
                                gridPoint.Weight -= distFromOrigin;
                            }
                            else if (distFromMonster > distFromOrigin)
                            {
                                gridPoint.Weight += distFromMonster;
                            }
                        }
                        foreach (GilesObstacle avoidance in GilesTrinity.hashAvoidanceObstacleCache)
                        {
                            float distFromAvoidance         = gridPoint.Position.Distance2D(avoidance.Location);
                            float distFromOrigin            = gridPoint.Position.Distance2D(origin);
                            float distFromOriginToAvoidance = origin.Distance2D(avoidance.Location);

                            float health = AvoidanceManager.GetAvoidanceHealthBySNO(avoidance.ActorSNO, 1f);
                            float radius = AvoidanceManager.GetAvoidanceRadiusBySNO(avoidance.ActorSNO, 1f);

                            // position is inside avoidance
                            if (PlayerStatus.CurrentHealthPct < health && distFromAvoidance < radius)
                            {
                                continue;
                            }

                            // closer to avoidance than it is to player
                            if (distFromOriginToAvoidance < distFromOrigin)
                            {
                                continue;
                            }

                            if (distFromAvoidance < distFromOrigin)
                            {
                                gridPoint.Weight -= distFromOrigin;
                            }
                            else if (distFromAvoidance > distFromOrigin)
                            {
                                gridPoint.Weight += distFromAvoidance;
                            }
                        }
                    }
                    else if (isStuck)
                    {
                        // give weight to points nearer to our destination
                        gridPoint.Weight *= (maxDistance - PlayerMover.LastMoveToTarget.Distance2D(gridPoint.Position)) / maxDistance * maxWeight;
                    }
                    else if (!shouldKite && !isStuck) // melee avoidance use only
                    {
                        var monsterCount = GilesTrinity.GilesObjectCache.Count(u => u.Type == GObjectType.Unit && u.Position.Distance2D(gridPoint.Position) <= gridSquareRadius);
                        if (monsterCount > 0)
                        {
                            gridPoint.Weight *= monsterCount;
                        }
                    }

                    if (gridPoint.Weight > bestPoint.Weight && gridPoint.Distance > 1)
                    {
                        bestPoint = gridPoint;
                    }

                    //if (gridPoint.Weight > 0)
                    //    DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Moving, "Kiting grid point {0}, distance: {1:0}, weight: {2:0}", gridPoint.Position, gridPoint.Distance, gridPoint.Weight);
                }
            }

            if (isStuck)
            {
                UsedStuckSpots.Add(bestPoint);
            }

            DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Kiting grid found {0}, distance: {1:0}, weight: {2:0}", bestPoint.Position, bestPoint.Distance, bestPoint.Weight);
            DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Kiting grid stats NotNavigable {0} ZDiff {1} GT45Raycast {2} Avoidance {3} Monsters {4} pathFailures {5}",
                         nodesNotNavigable,
                         nodesZDiff,
                         nodesGT45Raycast,
                         nodesAvoidance,
                         nodesMonsters,
                         pathFailures);
            return(bestPoint.Position);
        }