Ejemplo n.º 1
0
        // thanks to Main for the super fast can-stand-at code
        internal static Vector3 MainFindSafeZone(Vector3 origin, bool shouldKite = false, bool isStuck = false, IEnumerable <TrinityCacheObject> monsterList = null, bool avoidDeath = false, float minDistance = 0f)
        {
            const float gridSquareSize = 5f;
            var         dhMaxDistance  = Math.Max(Trinity.Settings.Combat.DemonHunter.KiteMaxDistance, Trinity.Settings.Combat.DemonHunter.KiteLimit + 5);
            float       maxDistance    = Trinity.Player.ActorClass == ActorClass.DemonHunter ? dhMaxDistance : 100f;
            const int   maxWeight      = 100;

            double gridSquareRadius = Math.Sqrt((Math.Pow(gridSquareSize / 2, 2) + Math.Pow(gridSquareSize / 2, 2)));

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

            if (MainGridProvider.Width == 0)
            {
                // Do not remove nav server logging, we need to differentiate between legitmate trinity issues and nav server issues.
                // In this case, if MainGridProvider is empty, then the bot cannot kite.
                Logger.Log("NavServer Issue: MainGridProvider is empty, unable to avoidance/kite position");
                return(Vector3.Zero);
            }

            int totalNodes       = 0;
            int nodesCantStand   = 0;
            int nodesZDiff       = 0;
            int nodesGT45Raycast = 0;
            int nodesAvoidance   = 0;
            int nodesMonsters    = 0;
            int nodesObjects     = 0;
            int pathFailures     = 0;
            int navRaycast       = 0;
            int pointsFound      = 0;

            int worldId = Trinity.Player.WorldID;

            Stopwatch[] timers = Enumerable.Range(0, 12).Select(i => new Stopwatch()).ToArray();

            Vector2 minWorld;

            minWorld.X = origin.X - maxDistance;
            minWorld.Y = origin.Y - maxDistance;

            //Point minPoint = WorldToGrid(minWorld);
            Point minPoint = MainGridProvider.WorldToGrid(minWorld);

            minPoint.X = Math.Max(minPoint.X, 0);
            minPoint.Y = Math.Max(minPoint.Y, 0);

            Vector2 maxWorld;

            maxWorld.X = origin.X + maxDistance;
            maxWorld.Y = origin.Y + maxDistance;

            // MainGridProvider will be empty/clear we start receiving navServer data
            //Point maxPoint = WorldToGrid(maxWorld);
            Point maxPoint = MainGridProvider.WorldToGrid(maxWorld);

            maxPoint.X = Math.Min(maxPoint.X, MainGridProvider.Width - 1);
            maxPoint.Y = Math.Min(maxPoint.Y, MainGridProvider.Height - 1);

            Point originPos = MainGridProvider.WorldToGrid(origin.ToVector2());

            //var monsters = monsterList.ToList();

            using (new PerformanceLogger("MainFindSafeZoneLoop"))
            {
                for (int y = minPoint.Y; y <= maxPoint.Y; y++)
                {
                    int searchAreaBasis = y * MainGridProvider.Width;
                    for (int x = minPoint.X; x <= maxPoint.X; x++)
                    {
                        totalNodes++;

                        timers[0].Start();

                        int dx = originPos.X - x;
                        int dy = originPos.Y - y;

                        // Ignore out of range
                        if (dx * dx + dy * dy > (maxDistance / 2.5f) * (maxDistance / 2.5f))
                        {
                            continue;
                        }

                        // extremely efficient CanStandAt
                        if (!MainGridProvider.SearchArea[searchAreaBasis + x])
                        {
                            nodesCantStand++;
                            continue;
                        }

                        Vector2 xy  = MainGridProvider.GridToWorld(new Point(x, y));
                        Vector3 xyz = Vector3.Zero;

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

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

                        timers[0].Stop();

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

                        /*
                         * Check if a square is occupied already
                         */
                        // Avoidance
                        timers[2].Start();
                        if (CacheData.TimeBoundAvoidance.Any(aoe => xyz.Distance2D(aoe.Position) <= gridSquareRadius))
                        {
                            nodesAvoidance++;
                            continue;
                        }
                        timers[2].Stop();

                        if (monsterList != null && monsterList.Any(m => xyz.Distance(m.Position) - m.Radius - 2 <= minDistance))
                        {
                            nodesMonsters++;
                            continue;
                        }

                        // Monsters
                        if (shouldKite)
                        {
                            timers[3].Start();
                            double checkRadius = gridSquareRadius;

                            if (CombatBase.KiteDistance > 0)
                            {
                                checkRadius = gridSquareSize + 10f;
                            }

                            // Any monster standing in this GridPoint
                            if (CacheData.MonsterObstacles.Any(monster => monster.Position.Distance2D(xyz) - monster.Radius <= checkRadius))
                            {
                                nodesMonsters++;
                                continue;
                            }



                            timers[3].Stop();
                        }

                        timers[4].Start();
                        if (isStuck && UsedStuckSpots.Any(p => Vector3.Distance(p.Position, gridPoint.Position) <= gridSquareRadius))
                        {
                            continue;
                        }
                        timers[4].Stop();

                        // set base weight
                        if (!isStuck && !avoidDeath)
                        {
                            // e.g. ((100 - 15) / 100) * 100) = 85
                            // e.g. ((100 - 35) / 100) * 100) = 65
                            // e.g. ((100 - 75) / 100) * 100) = 25
                            gridPoint.Weight = ((maxDistance - gridPoint.Distance) / maxDistance) * maxWeight;

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

                        // Boss Areas
                        timers[5].Start();
                        if (UnSafeZone.UnsafeKiteAreas.Any(a => a.WorldId == Trinity.Player.WorldID && a.Position.Distance2DSqr(gridPoint.Position) <= (a.Radius * a.Radius)))
                        {
                            continue;
                        }
                        timers[5].Stop();

                        if (shouldKite)
                        {
                            /*
                             * We want to down-weight any grid points where monsters are closer to it than we are
                             */
                            timers[7].Start();
                            foreach (CacheObstacleObject monster in CacheData.MonsterObstacles)
                            {
                                float distFromPointToMonster = gridPoint.Position.Distance2D(monster.Position);
                                float distFromPointToOrigin  = gridPoint.Position.Distance2D(origin);

                                // No Kite Distance Setting
                                if (CombatBase.KiteDistance <= 0)
                                {
                                    // higher weight closer to monster
                                    if (distFromPointToMonster < distFromPointToOrigin)
                                    {
                                        gridPoint.Weight += distFromPointToOrigin;
                                    }
                                    else if (distFromPointToMonster > distFromPointToOrigin)
                                    {
                                        gridPoint.Weight -= distFromPointToMonster;
                                    }
                                }
                                else // Kite Distance is Set
                                {
                                    // higher weight further from monster
                                    if (distFromPointToMonster < distFromPointToOrigin)
                                    {
                                        gridPoint.Weight -= distFromPointToOrigin;
                                    }
                                    else if (distFromPointToMonster > distFromPointToOrigin)
                                    {
                                        gridPoint.Weight += distFromPointToMonster;
                                    }
                                    if (PositionCache.Cache.Any(cachePoint => gridPoint.Position.Distance2D(cachePoint.Position) <= gridSquareRadius))
                                    {
                                        gridPoint.Weight += distFromPointToOrigin; // always <= max distance, 0-150ish
                                    }
                                }
                            }
                            timers[7].Stop();

                            timers[8].Start();
                            foreach (CacheObstacleObject avoidance in CacheData.TimeBoundAvoidance)
                            {
                                float distSqrFromPointToAvoidance = gridPoint.Position.Distance2DSqr(avoidance.Position);

                                // position is inside avoidance
                                if (distSqrFromPointToAvoidance < (avoidance.Radius * avoidance.Radius))
                                {
                                    continue;
                                }

                                float distSqrFromPointToOrigin = gridPoint.Position.Distance2DSqr(origin);
                                if (distSqrFromPointToAvoidance < distSqrFromPointToOrigin)
                                {
                                    gridPoint.Weight -= distSqrFromPointToOrigin;
                                }
                                else if (distSqrFromPointToAvoidance > distSqrFromPointToOrigin)
                                {
                                    gridPoint.Weight += distSqrFromPointToAvoidance;
                                }
                            }
                            timers[8].Stop();
                        }
                        else if (isStuck)
                        {
                            // give weight to points nearer to our destination
                            gridPoint.Weight *= (maxDistance - PlayerMover.LastMoveToTarget.Distance2D(gridPoint.Position)) / maxDistance * maxWeight;
                        }
                        else if (!avoidDeath) // melee avoidance use only
                        {
                            timers[9].Start();
                            var monsterCount = Trinity.ObjectCache.Count(u => u.IsUnit && u.Position.Distance2D(gridPoint.Position) <= 2.5f);
                            if (monsterCount > 0)
                            {
                                gridPoint.Weight *= monsterCount;
                            }
                            timers[9].Stop();
                        }

                        pointsFound++;

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


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

            string times = "";

            for (int t = 0; t < timers.Length; t++)
            {
                if (timers[t].IsRunning)
                {
                    timers[t].Stop();
                }
                times += string.Format("{0}/{1:0.0} ", t, timers[t].ElapsedMilliseconds);
            }

            Logger.Log(TrinityLogLevel.Debug, LogCategory.Avoidance, "Kiting grid found {0}, distance: {1:0}, weight: {2:0}", bestPoint.Position, bestPoint.Distance, bestPoint.Weight);
            Logger.Log(TrinityLogLevel.Debug, LogCategory.Avoidance,
                       "Kiting grid stats Total={0} CantStand={1} ZDiff {2} GT45Raycast {3} Avoidance {4} Monsters {5} Obstacles {14} pathFailures {6} navRaycast {7} "
                       + "pointsFound {8} shouldKite={9} isStuck={10} avoidDeath={11} monsters={12} timers={13}",
                       totalNodes,
                       nodesCantStand,
                       nodesZDiff,
                       nodesGT45Raycast,
                       nodesAvoidance,
                       nodesMonsters,
                       pathFailures,
                       navRaycast,
                       pointsFound,
                       shouldKite,
                       isStuck,
                       avoidDeath,
                       monsterList == null ? 0 : monsterList.Count(),
                       times,
                       nodesObjects
                       );

            if (double.IsNaN(bestPoint.Position.X))
            {
                Logger.LogVerbose("Somethign went wrong, NaN value for MainFindSafeZone result");
                return(Vector3.Zero);
            }

            return(bestPoint.Position);
        }
Ejemplo n.º 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);
        }
Ejemplo n.º 3
0
        // thanks to Main for the super fast can-stand-at code
        internal static Vector3 MainFindSafeZone(Vector3 origin, bool shouldKite = false, bool isStuck = false, IEnumerable <TrinityCacheObject> monsterList = null, bool avoidDeath = false)
        {
            MainGridProvider.Update();
            Navigator.Clear();

            const float gridSquareSize = 10f;
            const float maxDistance    = 55f;
            const int   maxWeight      = 100;

            double gridSquareRadius = Math.Sqrt((Math.Pow(gridSquareSize / 2, 2) + Math.Pow(gridSquareSize / 2, 2)));

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

            int totalNodes       = 0;
            int nodesCantStand   = 0;
            int nodesZDiff       = 0;
            int nodesGT45Raycast = 0;
            int nodesAvoidance   = 0;
            int nodesMonsters    = 0;
            int pathFailures     = 0;
            int navRaycast       = 0;
            int pointsFound      = 0;

            int worldId = Trinity.Player.WorldID;

            Stopwatch[] timers = Enumerable.Range(0, 12).Select(i => new Stopwatch()).ToArray();

            Vector2 minWorld;

            minWorld.X = origin.X - maxDistance;
            minWorld.Y = origin.Y - maxDistance;

            Point minPoint = MainGridProvider.WorldToGrid(minWorld);

            minPoint.X = Math.Max(minPoint.X, 0);
            minPoint.Y = Math.Max(minPoint.Y, 0);

            Vector2 maxWorld;

            maxWorld.X = origin.X + maxDistance;
            maxWorld.Y = origin.Y + maxDistance;

            Point maxPoint = MainGridProvider.WorldToGrid(maxWorld);

            maxPoint.X = Math.Min(maxPoint.X, MainGridProvider.Width - 1);
            maxPoint.Y = Math.Min(maxPoint.Y, MainGridProvider.Height - 1);

            Point originPos = MainGridProvider.WorldToGrid(origin.ToVector2());

            using (new PerformanceLogger("MainFindSafeZoneLoop"))
            {
                for (int y = minPoint.Y; y <= maxPoint.Y; y++)
                {
                    int searchAreaBasis = y * MainGridProvider.Width;
                    for (int x = minPoint.X; x <= maxPoint.X; x++)
                    {
                        totalNodes++;

                        timers[0].Start();

                        int dx = originPos.X - x;
                        int dy = originPos.Y - y;
                        //if (dx * dx + dy * dy > radius * 2.5 * radius * 2.5)
                        //    continue;

                        // Ignore out of range
                        if (dx * dx + dy * dy > (maxDistance / 2.5f) * (maxDistance / 2.5f))
                        {
                            continue;
                        }
                        // extremely efficient CanStandAt
                        if (!MainGridProvider.SearchArea[searchAreaBasis + x])
                        {
                            nodesCantStand++;
                            continue;
                        }

                        Vector2 xy  = MainGridProvider.GridToWorld(new Point(x, y));
                        Vector3 xyz = Vector3.Zero;

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

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

                        timers[0].Stop();


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

                        /*
                         * Check if a square is occupied already
                         */
                        // Avoidance
                        timers[2].Start();
                        if (CacheData.TimeBoundAvoidance.Any(a => xyz.Distance2DSqr(a.Position) - (a.Radius * a.Radius) <= gridSquareRadius * gridSquareRadius))
                        {
                            nodesAvoidance++;
                            continue;
                        }
                        timers[2].Stop();

                        timers[9].Start();
                        // Obstacles
                        if (CacheData.NavigationObstacles.Any(a => xyz.Distance2DSqr(a.Position) - (a.Radius * a.Radius) <= gridSquareRadius * gridSquareRadius))
                        {
                            nodesMonsters++;
                            continue;
                        }
                        timers[9].Stop();

                        timers[10].Start();
                        if (CacheData.NavigationObstacles.Any(a => a.Position.Distance2DSqr(Trinity.Player.Position) < maxDistance * maxDistance &&
                                                              MathUtil.IntersectsPath(a.Position, a.Radius, Trinity.Player.Position, gridPoint.Position)))
                        {
                            pathFailures++;
                        }
                        timers[10].Stop();

                        // Monsters
                        if (shouldKite)
                        {
                            timers[3].Start();
                            double checkRadius = gridSquareRadius;

                            if (CombatBase.PlayerKiteDistance > 0)
                            {
                                checkRadius = gridSquareSize + CombatBase.PlayerKiteDistance;
                            }

                            // Any monster standing in this GridPoint
                            if (CacheData.MonsterObstacles.Any(a => Vector3.Distance(xyz, a.Position) + a.Radius <= checkRadius))
                            {
                                nodesMonsters++;
                                continue;
                            }

                            if (!hasEmergencyTeleportUp)
                            {
                                // Any monsters blocking in a straight line between origin and this GridPoint
                                foreach (CacheObstacleObject monster in CacheData.MonsterObstacles.Where(m =>
                                                                                                         MathEx.IntersectsPath(new Vector3(m.Position.X, m.Position.Y, 0), m.Radius, new Vector3(origin.X, origin.Y, 0), new Vector3(gridPoint.Position.X, gridPoint.Position.Y, 0))
                                                                                                         ))
                                {
                                    nodesMonsters++;
                                    continue;
                                }
                            }
                            timers[3].Stop();
                        }

                        timers[4].Start();
                        if (isStuck && UsedStuckSpots.Any(p => Vector3.Distance(p.Position, gridPoint.Position) <= gridSquareRadius))
                        {
                            continue;
                        }
                        timers[4].Stop();

                        // set base weight
                        if (!isStuck && !avoidDeath)
                        {
                            // e.g. ((100 - 15) / 100) * 100) = 85
                            // e.g. ((100 - 35) / 100) * 100) = 65
                            // e.g. ((100 - 75) / 100) * 100) = 25
                            gridPoint.Weight = ((maxDistance - gridPoint.Distance) / maxDistance) * maxWeight;

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

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

                        if (shouldKite)
                        {
                            // make sure we can raycast to our target
                            //if (!DataDictionary.StraightLinePathingLevelAreaIds.Contains(Trinity.Player.LevelAreaId) &&
                            //    !NavHelper.CanRayCast(gridPoint.Position, Trinity.LastPrimaryTargetPosition))
                            //{
                            //    navRaycast++;
                            //    continue;
                            //}

                            /*
                             * We want to down-weight any grid points where monsters are closer to it than we are
                             */
                            timers[7].Start();
                            foreach (CacheObstacleObject monster in CacheData.MonsterObstacles)
                            {
                                float distFromMonster           = gridPoint.Position.Distance2D(monster.Position);
                                float distFromOrigin            = gridPoint.Position.Distance2D(origin);
                                float distFromOriginToAvoidance = origin.Distance2D(monster.Position);
                                if (distFromOriginToAvoidance < distFromOrigin)
                                {
                                    continue;
                                }

                                if (distFromMonster < distFromOrigin)
                                {
                                    gridPoint.Weight -= distFromOrigin;
                                }
                                else if (distFromMonster > distFromOrigin)
                                {
                                    gridPoint.Weight += distFromMonster;
                                }
                            }
                            timers[7].Stop();

                            timers[8].Start();
                            foreach (CacheObstacleObject avoidance in CacheData.TimeBoundAvoidance)
                            {
                                float distFromAvoidance         = gridPoint.Position.Distance2D(avoidance.Position);
                                float distFromOrigin            = gridPoint.Position.Distance2D(origin);
                                float distFromOriginToAvoidance = origin.Distance2D(avoidance.Position);

                                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;
                                }
                            }
                            timers[8].Stop();
                        }
                        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 && !avoidDeath) // melee avoidance use only
                        {
                            timers[9].Start();
                            var monsterCount = Trinity.ObjectCache.Count(u => u.IsUnit && u.Position.Distance2D(gridPoint.Position) <= 2.5f);
                            if (monsterCount > 0)
                            {
                                gridPoint.Weight *= monsterCount;
                            }
                            timers[9].Stop();
                        }

                        pointsFound++;

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


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

            string times = "";

            for (int t = 0; t < timers.Length; t++)
            {
                if (timers[t].IsRunning)
                {
                    timers[t].Stop();
                }
                times += string.Format("{0}/{1:0.0} ", t, timers[t].ElapsedMilliseconds);
            }

            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Kiting grid found {0}, distance: {1:0}, weight: {2:0}", bestPoint.Position, bestPoint.Distance, bestPoint.Weight);
            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement,
                       "Kiting grid stats Total={0} CantStand={1} ZDiff {2} GT45Raycast {3} Avoidance {4} Monsters {5} pathFailures {6} navRaycast {7} "
                       + "pointsFound {8} shouldKite={9} isStuck={10} avoidDeath={11} monsters={12} timers={13}",
                       totalNodes,
                       nodesCantStand,
                       nodesZDiff,
                       nodesGT45Raycast,
                       nodesAvoidance,
                       nodesMonsters,
                       pathFailures,
                       navRaycast,
                       pointsFound,
                       shouldKite,
                       isStuck,
                       avoidDeath,
                       monsterList == null ? 0 : monsterList.Count(),
                       times
                       );
            return(bestPoint.Position);
        }