Example #1
0
        /// <summary>
        /// Called when user disable the plugin.
        /// </summary>
        public void OnDisabled()
        {
            _isPluginEnabled = false;

            BotManager.ReplaceTreeHooks();

            Navigator.PlayerMover               = new DefaultPlayerMover();
            Navigator.StuckHandler              = new DefaultStuckHandler();
            CombatTargeting.Instance.Provider   = new DefaultCombatTargetingProvider();
            LootTargeting.Instance.Provider     = new DefaultLootTargetingProvider();
            ObstacleTargeting.Instance.Provider = new DefaultObstacleTargetingProvider();
            Navigator.SearchGridProvider        = new MainGridProvider();

            GameEvents.OnPlayerDied -= TrinityOnDeath;
            BotMain.OnStop          -= TrinityBotStop;

            GameEvents.OnPlayerDied   -= TrinityOnDeath;
            GameEvents.OnGameJoined   -= TrinityOnJoinGame;
            GameEvents.OnGameLeft     -= TrinityOnLeaveGame;
            GameEvents.OnItemSold     -= ItemEvents.TrinityOnItemSold;
            GameEvents.OnItemSalvaged -= ItemEvents.TrinityOnItemSalvaged;
            GameEvents.OnItemStashed  -= ItemEvents.TrinityOnItemStashed;
            GameEvents.OnItemIdentificationRequest -= ItemEvents.TrinityOnOnItemIdentificationRequest;
            GameEvents.OnGameChanged  -= GameEvents_OnGameChanged;
            GameEvents.OnWorldChanged -= GameEvents_OnWorldChanged;

            ItemManager.Current = new LootRuleItemManager();

            Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "");
            Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "DISABLED: Trinity is now shut down...");
            Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "");
            GenericCache.Shutdown();
            GenericBlacklist.Shutdown();
        }
Example #2
0
        public static void ResetEverythingNewGame()
        {
            // In-thread stuff
            V.ValidateLoad();

            // Out of thread Async stuff
            BeginInvoke(new Action(() =>
            {
                Logger.Log("New Game - resetting everything");

                Trinity.IsReadyToTownRun   = false;
                Trinity.ForceVendorRunASAP = false;
                TownRun.TownRunCheckTimer.Reset();
                TownRun.SendEmailNotification();
                TownRun.PreTownRunPosition = Vector3.Zero;
                TownRun.PreTownRunWorldId  = -1;
                TownRun.WasVendoring       = false;

                CacheData.AbilityLastUsed.Clear();
                SpellHistory.HistoryQueue.Clear();

                DeathsThisRun                = 0;
                LastDeathTime                = DateTime.UtcNow;
                _hashsetItemStatsLookedAt    = new HashSet <string>();
                _hashsetItemPicksLookedAt    = new HashSet <string>();
                _hashsetItemFollowersIgnored = new HashSet <string>();

                Blacklist60Seconds                     = new HashSet <int>();
                Blacklist90Seconds                     = new HashSet <int>();
                Blacklist15Seconds                     = new HashSet <int>();
                BacktrackList                          = new SortedList <int, Vector3>();
                TotalBacktracks                        = 0;
                HasMappedPlayerAbilities               = false;
                PlayerMover.TotalAntiStuckAttempts     = 1;
                PlayerMover.vSafeMovementLocation      = Vector3.Zero;
                PlayerMover.LastPosition               = Vector3.Zero;
                PlayerMover.TimesReachedStuckPoint     = 0;
                PlayerMover.TimeLastRecordedPosition   = DateTime.MinValue;
                PlayerMover.LastGeneratedStuckPosition = DateTime.MinValue;
                PlayerMover.TimesReachedMaxUnstucks    = 0;
                PlayerMover.CancelUnstuckerForSeconds  = 0;
                PlayerMover.LastCancelledUnstucker     = DateTime.MinValue;
                NavHelper.UsedStuckSpots               = new List <GridPoint>();

                CacheData.FullClear();

                // Reset all the caches
                ProfileHistory = new List <string>();
                CurrentProfile = "";
                FirstProfile   = "";

                Logger.Log("New Game, resetting Gold Inactivity Timer");
                GoldInactivity.Instance.ResetCheckGold();

                CombatBase.IsQuestingMode = false;

                GenericCache.ClearCache();
                GenericBlacklist.ClearBlacklist();
            }));
        }
        private static bool CacheDiaObject(DiaObject freshObject)
        {
            if (!freshObject.IsValid)
            {
                return(false);
            }

            /*
             *  Initialize Variables
             */
            bool AddToCache;

            RefreshStepInit(out AddToCache);

            /*
             *  Get primary reference objects and keys
             */
            c_diaObject = freshObject;

            // Ractor GUID
            CurrentCacheObject.RActorGuid = freshObject.RActorGuid;
            // Check to see if we've already looked at this GUID
            CurrentCacheObject.RActorGuid = freshObject.RActorGuid;
            CurrentCacheObject.ACDGuid    = freshObject.ACDGuid;

            // Get Name
            CurrentCacheObject.InternalName = nameNumberTrimRegex.Replace(freshObject.Name, "");
            CurrentCacheObject.InternalName = nameNumberTrimRegex.Replace(freshObject.Name, "");

            CurrentCacheObject.ActorSNO  = freshObject.ActorSNO;
            CurrentCacheObject.ActorType = freshObject.ActorType;
            CurrentCacheObject.ACDGuid   = freshObject.ACDGuid;
            if (CurrentCacheObject.CommonData == null)
            {
                c_IgnoreReason = "ACDNull";
                return(false);
            }
            if (!CurrentCacheObject.CommonData.IsValid)
            {
                c_IgnoreReason = "ACDInvalid";
            }

            // Position
            CurrentCacheObject.Position = CurrentCacheObject.Object.Position;

            // Distance
            CurrentCacheObject.Distance = Player.Position.Distance2D(CurrentCacheObject.Position);

            float radius;

            if (!DataDictionary.CustomObjectRadius.TryGetValue(CurrentCacheObject.ActorSNO, out radius))
            {
                try
                {
                    radius = CurrentCacheObject.Object.CollisionSphere.Radius;
                }
                catch (Exception ex)
                {
                    Logger.LogError(LogCategory.CacheManagement, "Error refreshing Radius: {0}", ex.Message);
                }
            }

            // Radius Distance
            CurrentCacheObject.Radius = radius;

            // Have ActorSNO Check for SNO based navigation obstacle hashlist
            c_IsObstacle = DataDictionary.NavigationObstacleIds.Contains(CurrentCacheObject.ActorSNO);

            // Add Cell Weight for Obstacle
            if (c_IsObstacle)
            {
                Vector3 pos;
                if (!CacheData.Position.TryGetValue(CurrentCacheObject.RActorGuid, out pos))
                {
                    CurrentCacheObject.Position = c_diaObject.Position;
                    //CacheData.Position.Add(CurrentCacheObject.RActorGuid, CurrentCacheObject.Position);
                }
                if (pos != Vector3.Zero)
                {
                    CurrentCacheObject.Position = pos;
                }

                CacheData.NavigationObstacles.Add(new CacheObstacleObject()
                {
                    ActorSNO   = CurrentCacheObject.ActorSNO,
                    Name       = CurrentCacheObject.InternalName,
                    Position   = CurrentCacheObject.Position,
                    Radius     = CurrentCacheObject.Radius,
                    ObjectType = CurrentCacheObject.Type,
                });

                ((MainGridProvider)MainGridProvider).AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);

                c_IgnoreReason = "NavigationObstacle";
                AddToCache     = false;
                return(AddToCache);
            }

            // Summons by the player
            AddToCache = RefreshStepCachedSummons(AddToCache);
            if (!AddToCache)
            {
                c_IgnoreReason = "CachedPlayerSummons"; return(AddToCache);
            }

            using (new PerformanceLogger("RefreshDiaObject.CachedType"))
            {
                /*
                 * Set Object Type
                 */
                AddToCache = RefreshStepCachedObjectType(AddToCache);
                if (!AddToCache)
                {
                    c_IgnoreReason = "CachedObjectType"; return(AddToCache);
                }
            }

            CurrentCacheObject.Type = CurrentCacheObject.Type;
            if (CurrentCacheObject.Type != GObjectType.Item)
            {
                CurrentCacheObject.ObjectHash = HashGenerator.GenerateWorldObjectHash(CurrentCacheObject.ActorSNO, CurrentCacheObject.Position, CurrentCacheObject.Type.ToString(), Trinity.CurrentWorldDynamicId);
            }

            // Check Blacklists
            AddToCache = RefreshStepCheckBlacklists(AddToCache);
            if (!AddToCache)
            {
                c_IgnoreReason = "CheckBlacklists"; return(AddToCache);
            }

            if (CurrentCacheObject.Type == GObjectType.Item)
            {
                if (GenericBlacklist.ContainsKey(CurrentCacheObject.ObjectHash))
                {
                    AddToCache     = false;
                    c_IgnoreReason = "GenericBlacklist";
                    return(AddToCache);
                }
            }

            // Always Refresh ZDiff for every object
            AddToCache = RefreshStepObjectTypeZDiff(AddToCache);
            if (!AddToCache)
            {
                c_IgnoreReason = "ZDiff"; return(AddToCache);
            }

            using (new PerformanceLogger("RefreshDiaObject.MainObjectType"))
            {
                /*
                 * Main Switch on Object Type - Refresh individual object types (Units, Items, Gizmos)
                 */
                RefreshStepMainObjectType(ref AddToCache);
                if (!AddToCache)
                {
                    c_IgnoreReason = "MainObjectType"; return(AddToCache);
                }
            }

            if (CurrentCacheObject.ObjectHash != String.Empty && GenericBlacklist.ContainsKey(CurrentCacheObject.ObjectHash))
            {
                AddToCache      = false;
                c_IgnoreSubStep = "GenericBlacklist";
                return(AddToCache);
            }

            // Ignore anything unknown
            AddToCache = RefreshStepIgnoreUnknown(AddToCache);
            if (!AddToCache)
            {
                c_IgnoreReason = "IgnoreUnknown"; return(AddToCache);
            }

            using (new PerformanceLogger("RefreshDiaObject.LoS"))
            {
                // Ignore all LoS
                AddToCache = RefreshStepIgnoreLoS(AddToCache);
                if (!AddToCache)
                {
                    c_IgnoreReason = "IgnoreLoS"; return(AddToCache);
                }
            }

            // If it's a unit, add it to the monster cache
            AddUnitToMonsterObstacleCache();

            c_IgnoreReason = "None";

            CurrentCacheObject.ACDGuid            = CurrentCacheObject.ACDGuid;
            CurrentCacheObject.ActorSNO           = CurrentCacheObject.ActorSNO;
            CurrentCacheObject.Animation          = c_CurrentAnimation;
            CurrentCacheObject.DBItemBaseType     = c_DBItemBaseType;
            CurrentCacheObject.DBItemType         = c_DBItemType;
            CurrentCacheObject.DirectionVector    = c_DirectionVector;
            CurrentCacheObject.Distance           = CurrentCacheObject.Distance;
            CurrentCacheObject.DynamicID          = CurrentCacheObject.DynamicID;
            CurrentCacheObject.FollowerType       = c_item_tFollowerType;
            CurrentCacheObject.GameBalanceID      = CurrentCacheObject.GameBalanceID;
            CurrentCacheObject.GoldAmount         = c_GoldStackSize;
            CurrentCacheObject.HasAffixShielded   = c_unit_HasShieldAffix;
            CurrentCacheObject.HasBeenInLoS       = c_HasBeenInLoS;
            CurrentCacheObject.HasBeenNavigable   = c_HasBeenNavigable;
            CurrentCacheObject.HasBeenRaycastable = c_HasBeenRaycastable;
            CurrentCacheObject.HasDotDPS          = c_HasDotDPS;
            CurrentCacheObject.HitPoints          = c_HitPoints;
            CurrentCacheObject.HitPointsPct       = c_HitPointsPct;
            CurrentCacheObject.InternalName       = CurrentCacheObject.InternalName;
            CurrentCacheObject.IsAttackable       = c_unit_IsAttackable;
            CurrentCacheObject.IsElite            = c_unit_IsElite;
            CurrentCacheObject.IsEliteRareUnique  = c_IsEliteRareUnique;
            CurrentCacheObject.IsFacingPlayer     = c_IsFacingPlayer;
            CurrentCacheObject.IsMinion           = c_unit_IsMinion;
            CurrentCacheObject.IsRare             = c_unit_IsRare;
            CurrentCacheObject.IsTreasureGoblin   = c_unit_IsTreasureGoblin;
            CurrentCacheObject.IsUnique           = c_unit_IsUnique;
            CurrentCacheObject.ItemLevel          = c_ItemLevel;
            CurrentCacheObject.ItemLink           = c_ItemLink;
            CurrentCacheObject.ItemQuality        = c_ItemQuality;
            CurrentCacheObject.MonsterAffixes     = c_MonsterAffixes;
            CurrentCacheObject.MonsterSize        = c_unit_MonsterSize;
            CurrentCacheObject.OneHanded          = c_IsOneHandedItem;
            CurrentCacheObject.RActorGuid         = CurrentCacheObject.RActorGuid;
            CurrentCacheObject.Radius             = CurrentCacheObject.Radius;
            CurrentCacheObject.Rotation           = c_Rotation;
            CurrentCacheObject.TrinityItemType    = c_item_GItemType;
            CurrentCacheObject.TwoHanded          = c_IsTwoHandedItem;
            CurrentCacheObject.Type = CurrentCacheObject.Type;
            ObjectCache.Add(CurrentCacheObject);
            return(true);
        }
Example #4
0
 /// <summary>
 /// Called when DemonBuddy shut down.
 /// </summary>
 public void OnShutdown()
 {
     GenericCache.Shutdown();
     GenericBlacklist.Shutdown();
     Helpers.PluginCheck.Shutdown();
 }
Example #5
0
        public bool TryBlacklist(TrinityActor target)
        {
            if (target == null)
            {
                return(false);
            }

            if (target.IsBlacklisted)
            {
                return(false);
            }

            switch (target.Type)
            {
            case TrinityObjectType.Door when target.ActorSnoId == SNOActor.p43_AD_Catacombs_Door_A &&
                target.Targeting.TargetedTimes > 3:
                // Special case 'p43_AD_Catacombs_Door_A' no way to tell it's locked, blacklist quickly to explore
                GenericBlacklist.Blacklist(
                    target, TimeSpan.FromSeconds(15),
                    $"Probably locked door p43_AD_Catacombs_Door_A at {target.Position}");
                return(true);

            case TrinityObjectType.Door when !target.IsUsed:
                return(false);
            }

            if (LastPower != null &&
                LastPower.SNOPower == SNOPower.Axe_Operate_Gizmo &&
                target.IsGizmo &&
                target.IsLastTarget &&
                target.Targeting.TargetedTimes > 25 &&
                target.IsItem &&
                target is TrinityItem item &&
                item.ToAcdItem().IsLowQuality)
            {
                // There's a weird stuck where bot is unable to interact with an item, possibly move/interact range related.
                GenericBlacklist.Blacklist(
                    target,
                    TimeSpan.FromSeconds(120),
                    $"Failed too many times to pickup low quality item. {target.Name} Distance={target.Distance}");
                return(true);
            }

            switch (target.Type)
            {
            case TrinityObjectType.ProgressionGlobe:
            case TrinityObjectType.Shrine:
                return(false);
            }

            if (target.IsElite)
            {
                return(false);
            }

            var times    = target.Targeting.TargetedTimes;
            var duration = target.Targeting.TotalTargetedTime;

            if (duration > TimeSpan.FromSeconds(10) &&
                times > 5)
            {
                GenericBlacklist.Blacklist(
                    target,
                    TimeSpan.FromSeconds(5),
                    $"Micro-Blacklist for reposition / anti-flipflop (Times={times} Duration={duration})");
                return(true);
            }

            if (target.IsCorruptGrowth &&
                duration > TimeSpan.FromSeconds(10) &&
                Core.Player.MovementSpeed <= 2)
            {
                GenericBlacklist.Blacklist(
                    target,
                    TimeSpan.FromSeconds(3),
                    $"Micro-Blacklist for corrupt growth reposition (Times={times} Duration={duration})");
                return(true);
            }

            if (duration > TimeSpan.FromSeconds(30) &&
                !target.IsElite)
            {
                GenericBlacklist.Blacklist(
                    target,
                    TimeSpan.FromSeconds(60),
                    $"Targetted for too long ({duration})");
                return(true);
            }

            if (duration <= TimeSpan.FromSeconds(30) ||
                target.Targeting.TargetedTimes <= 50 ||
                target.IsBoss)
            {
                return(false);
            }

            GenericBlacklist.Blacklist(
                target,
                TimeSpan.FromSeconds(60),
                $"Targetted too many times ({times})");
            return(true);
        }
Example #6
0
        /// <summary>
        /// This method will add and update necessary information about all available actors. Determines ObjectType, sets ranges, updates blacklists, determines avoidance, kiting, target weighting
        /// and the result is we will have a new target for the Target Handler. Returns true if the cache was refreshed.
        /// </summary>
        /// <returns>True if the cache was updated</returns>
        public static bool RefreshDiaObjectCache()
        {
            if (ZetaDia.Service.Hero == null)
            {
                Logger.LogError("Hero is null!");
                return(false);
            }

            //if (!ZetaDia.Service.Hero.IsValid)
            //{
            //    Logger.LogError("Hero is invalid!");
            //    return false;
            //}

            if (!ZetaDia.IsInGame)
            {
                return(false);
            }

            if (ZetaDia.IsLoadingWorld)
            {
                return(false);
            }

            if (!ZetaDia.Me.IsValid)
            {
                return(false);
            }

            if (!ZetaDia.Me.CommonData.IsValid)
            {
                return(false);
            }


            using (new PerformanceLogger("RefreshDiaObjectCache"))
            {
                LastRefreshedCache = DateTime.UtcNow;

                /*
                 *  Refresh the Cache
                 */
                using (new PerformanceLogger("RefreshDiaObjectCache.UpdateBlock"))
                {
                    CacheData.Clear();
                    GenericCache.MaintainCache();
                    GenericBlacklist.MaintainBlacklist();


                    if (Player.CurrentHealthPct <= 0)
                    {
                        return(false);
                    }

                    RefreshCacheInit();

                    // Now pull up all the data and store anything we want to handle in the super special cache list
                    // Also use many cache dictionaries to minimize DB<->D3 memory hits, and speed everything up a lot
                    RefreshCacheMainLoop();
                }

                /*
                 * Add Legendary & Set Minimap Markers to ObjectCache
                 */
                RefreshCacheMarkers();

                // Add Team HotSpots to the cache
                ObjectCache.AddRange(GroupHotSpots.GetCacheObjectHotSpots());

                /* Fire Chains Experimental Avoidance */
                if (Settings.Combat.Misc.UseExperimentalFireChainsAvoidance)
                {
                    const float fireChainSize = 5f;
                    foreach (var unit1 in ObjectCache.Where(u => u.MonsterAffixes.HasFlag(MonsterAffixes.FireChains)))
                    {
                        foreach (var unit2 in ObjectCache.Where(u => u.MonsterAffixes.HasFlag(MonsterAffixes.FireChains)).Where(unit2 => unit1.RActorGuid != unit2.RActorGuid))
                        {
                            for (float i = 0; i <= unit1.Position.Distance2D(unit2.Position); i += (fireChainSize / 4))
                            {
                                Vector3 fireChainSpot = MathEx.CalculatePointFrom(unit1.Position, unit2.Position, i);

                                if (Player.Position.Distance2D(fireChainSpot) <= fireChainSize)
                                {
                                    Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Avoiding Fire Chains!");
                                    _standingInAvoidance = true;
                                }
                                CacheData.TimeBoundAvoidance.Add(new CacheObstacleObject(fireChainSpot, fireChainSize, -2, "FireChains"));
                            }
                        }
                        if (CacheData.TimeBoundAvoidance.Any(aoe => aoe.ActorSNO == -2))
                        {
                            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Generated {0} avoidance points for FireChains, minDistance={1} maxDistance={2}",
                                       CacheData.TimeBoundAvoidance.Count(aoe => aoe.ActorSNO == -2),
                                       CacheData.TimeBoundAvoidance.Where(aoe => aoe.ActorSNO == -2)
                                       .Min(aoe => aoe.Position.Distance2D(Player.Position)),
                                       CacheData.TimeBoundAvoidance.Where(aoe => aoe.ActorSNO == -2)
                                       .Max(aoe => aoe.Position.Distance2D(Player.Position)));
                        }
                    }
                }

                /* Beast Charge Experimental Avoidance */
                if (Settings.Combat.Misc.UseExperimentalSavageBeastAvoidance)
                {
                    const float beastChargePathWidth = 10f;
                    List <int>  chargerSnoList       = new List <int>();
                    foreach (var unit1 in ObjectCache.Where(u => objectIsCharging(u)))
                    {
                        Vector3 endPoint = MathEx.GetPointAt(unit1.Position, 90f, unit1.Unit.Movement.Rotation);

                        for (float i = 0; i <= unit1.Position.Distance2D(endPoint); i += (beastChargePathWidth / 4))
                        {
                            Vector3 pathSpot = MathEx.CalculatePointFrom(unit1.Position, endPoint, i);

                            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement,
                                       "Generating Charge Avoidance: {0} dist: {1}",
                                       pathSpot, pathSpot.Distance2D(unit1.Position));

                            var minWidth = Math.Max(beastChargePathWidth, unit1.Radius) + 5f;

                            if (Player.Position.Distance2D(pathSpot) <= minWidth)
                            {
                                Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Avoiding Charger!");
                                _standingInAvoidance = true;
                            }
                            CacheData.TimeBoundAvoidance.Add(new CacheObstacleObject(pathSpot, minWidth, unit1.ActorSNO,
                                                                                     "Charger"));

                            chargerSnoList.Add(unit1.ActorSNO);
                        }
                        if (CacheData.TimeBoundAvoidance.Any(aoe => chargerSnoList.Contains(aoe.ActorSNO)))
                        {
                            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement,
                                       "Generated {0} avoidance points for BeastCharge, minDistance={1} maxDistance={2}",
                                       CacheData.TimeBoundAvoidance.Count(aoe => chargerSnoList.Contains(aoe.ActorSNO)),
                                       CacheData.TimeBoundAvoidance.Where(aoe => chargerSnoList.Contains(aoe.ActorSNO))
                                       .Min(aoe => aoe.Position.Distance2D(Player.Position)),
                                       CacheData.TimeBoundAvoidance.Where(aoe => chargerSnoList.Contains(aoe.ActorSNO))
                                       .Max(aoe => aoe.Position.Distance2D(Player.Position)));
                        }
                    }
                }


                // Reduce ignore-for-loops counter
                if (_ignoreTargetForLoops > 0)
                {
                    _ignoreTargetForLoops--;
                }
                // If we have an avoidance under our feet, then create a new object which contains a safety point to move to
                // But only if we aren't force-cancelling avoidance for XX time
                bool hasFoundSafePoint = false;

                using (new PerformanceLogger("RefreshDiaObjectCache.AvoidanceCheck"))
                {
                    if (Settings.Combat.Misc.FleeInGhostMode && Player.IsGhosted)
                    {
                        _standingInAvoidance = true;
                    }

                    // Note that if treasure goblin level is set to kamikaze, even avoidance moves are disabled to reach the goblin!
                    if (_standingInAvoidance && !CombatBase.IsDoingGoblinKamakazi && (!AnyTreasureGoblinsPresent || Settings.Combat.Misc.GoblinPriority <= GoblinPriority.Prioritize) &&
                        DateTime.UtcNow.Subtract(timeCancelledEmergencyMove).TotalMilliseconds >= cancelledEmergencyMoveForMilliseconds)
                    {
                        Vector3 safePosition = NavHelper.FindSafeZone(false, 1, Player.Position, true, null, true);

                        // Ignore avoidance stuff if we're incapacitated or didn't find a safe spot we could reach
                        if (safePosition != Vector3.Zero)
                        {
                            if (Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                            {
                                Logger.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Kiting Avoidance: {5} {0} Distance: {1:0} Direction: {2:0}, Health%={3:0.00}, KiteDistance: {4:0}",
                                           safePosition, safePosition.Distance(Me.Position), MathUtil.GetHeading(MathUtil.FindDirectionDegree(Me.Position, safePosition)),
                                           Player.CurrentHealthPct, CombatBase.KiteDistance, _currentAvoidanceName);
                            }

                            hasFoundSafePoint = true;
                            var distance = Vector3.Distance(Player.Position, safePosition);
                            Logger.Log(LogCategory.Avoidance, "Found Safe Spot {0}f away", distance);
                            CurrentTarget = new TrinityCacheObject()
                            {
                                Position     = safePosition,
                                Type         = TrinityObjectType.Avoidance,
                                Weight       = 40000,
                                Distance     = distance,
                                Radius       = 2f,
                                InternalName = "SafePoint",
                                IsSafeSpot   = true
                            };
                        }
                    }
                }

                /*
                 *  Set Weights, assign CurrentTarget
                 */

                if (!hasFoundSafePoint)
                {
                    RefreshDiaGetWeights();

                    if (!CombatBase.IsDoingGoblinKamakazi)
                    {
                        RefreshSetKiting(ref KiteAvoidDestination, NeedToKite);
                    }
                }

                // Not heading straight for a safe-spot?
                // No valid targets but we were told to stay put?
                if (CurrentTarget == null && !CombatBase.IsDoingGoblinKamakazi && _shouldStayPutDuringAvoidance && !_standingInAvoidance)
                {
                    CurrentTarget = new TrinityCacheObject()
                    {
                        Position     = Player.Position,
                        Type         = TrinityObjectType.Avoidance,
                        Weight       = 45000,
                        Distance     = 2f,
                        Radius       = 2f,
                        InternalName = "StayPutPoint",
                        IsSafeSpot   = true,
                    };
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.Avoidance, "Staying Put During Avoidance");
                }

                // Pre-townrun is too far away
                if (!Player.IsInTown && TownRun.PreTownRunPosition != Vector3.Zero && TownRun.PreTownRunWorldId == Player.WorldID && !ForceVendorRunASAP &&
                    TownRun.PreTownRunPosition.Distance2D(Player.Position) <= V.F("Cache.PretownRun.MaxDistance"))
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.UserInformation, "Pre-TownRun position is more than {0} yards away, canceling", V.I("Cache.PretownRun.MaxDistance"));
                    TownRun.PreTownRunPosition = Vector3.Zero;
                    TownRun.PreTownRunWorldId  = -1;
                }

                // Reached pre-townrun position
                if (!Player.IsInTown && TownRun.PreTownRunPosition != Vector3.Zero && TownRun.PreTownRunWorldId == Player.WorldID && !ForceVendorRunASAP &&
                    TownRun.PreTownRunPosition.Distance2D(Player.Position) <= 15f)
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.UserInformation, "Successfully returned to Pre-TownRun Position");
                    TownRun.PreTownRunPosition = Vector3.Zero;
                    TownRun.PreTownRunWorldId  = -1;
                }

                // After a townrun, make sure to return to original TownRun Location
                if (!Player.IsInTown && CurrentTarget == null && TownRun.PreTownRunPosition != Vector3.Zero && TownRun.PreTownRunWorldId == Player.WorldID && !ForceVendorRunASAP)
                {
                    if (TownRun.PreTownRunPosition.Distance2D(Player.Position) > 10f && TownRun.PreTownRunPosition.Distance2D(Player.Position) <= V.F("Cache.PretownRun.MaxDistance"))
                    {
                        CurrentTarget = new TrinityCacheObject()
                        {
                            Position     = TownRun.PreTownRunPosition,
                            Type         = TrinityObjectType.Avoidance,
                            Weight       = 20000,
                            Distance     = 2f,
                            Radius       = 2f,
                            InternalName = "PreTownRunPosition"
                        };
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.UserInformation, "Returning to Pre-TownRun Position");
                    }
                }

                using (new PerformanceLogger("RefreshDiaObjectCache.FinalChecks"))
                {
                    if (Settings.WorldObject.EnableBountyEvents && !CombatBase.IsDoingGoblinKamakazi)
                    {
                        bool eventObjectNear = ObjectCache.Any(o => o.Type == TrinityObjectType.CursedChest || o.Type == TrinityObjectType.CursedShrine);

                        if (!Player.InActiveEvent)
                        {
                            EventStartPosition = Vector3.Zero;
                            EventStartTime     = DateTime.MinValue;
                        }

                        // Reset Event time while we have targts
                        if (CurrentTarget != null && Player.InActiveEvent && eventObjectNear)
                        {
                            EventStartTime = DateTime.UtcNow;
                        }

                        if (eventObjectNear)
                        {
                            EventStartPosition = ObjectCache.FirstOrDefault(o => o.Type == TrinityObjectType.CursedChest || o.Type == TrinityObjectType.CursedShrine).Position;
                        }

                        var activeEvent = ZetaDia.ActInfo.ActiveQuests.FirstOrDefault(q => DataDictionary.EventQuests.Contains(q.QuestSNO));

                        const int waitTimeoutSeconds = 90;
                        if (DateTime.UtcNow.Subtract(EventStartTime).TotalSeconds > waitTimeoutSeconds && activeEvent != null)
                        {
                            CacheData.BlacklistedEvents.Add(activeEvent.QuestSNO);
                        }

                        if (CurrentTarget == null && Player.InActiveEvent && EventStartPosition != Vector3.Zero &&
                            DateTime.UtcNow.Subtract(EventStartTime).TotalSeconds < waitTimeoutSeconds &&
                            activeEvent != null && !CacheData.BlacklistedEvents.Contains(activeEvent.QuestSNO))
                        {
                            CurrentTarget = new TrinityCacheObject()
                            {
                                Position     = EventStartPosition,
                                Type         = TrinityObjectType.Avoidance,
                                Weight       = 20000,
                                Distance     = 2f,
                                Radius       = 2f,
                                InternalName = "WaitForEvent"
                            };
                            Logger.Log("Waiting for Event {0} - Time Remaining: {1:0} seconds",
                                       ZetaDia.ActInfo.ActiveQuests.FirstOrDefault(q => DataDictionary.EventQuests.Contains(q.QuestSNO)).Quest,
                                       waitTimeoutSeconds - DateTime.UtcNow.Subtract(EventStartTime).TotalSeconds);
                        }
                    }

                    if (CombatBase.IsDoingGoblinKamakazi && CurrentTarget != null && CurrentTarget.Type != TrinityObjectType.Door && CurrentTarget.Type != TrinityObjectType.Barricade && !CurrentTarget.InternalName.ToLower().Contains("corrupt") && CurrentTarget.Weight != MaxWeight)
                    {
                        Logger.Log("Forcing Target to Goblin '{0} ({1})' Distance={2}", CombatBase.KamakaziGoblin.InternalName, CombatBase.KamakaziGoblin.ActorSNO, CombatBase.KamakaziGoblin.Distance);
                        CurrentTarget = CombatBase.KamakaziGoblin;
                    }

                    if (CombatBase.IsDoingGoblinKamakazi && CurrentTarget == null)
                    {
                        Logger.Log("No Target, Switching to Goblin '{0} ({1})' Distance={2}", CombatBase.KamakaziGoblin.InternalName, CombatBase.KamakaziGoblin.ActorSNO, CombatBase.KamakaziGoblin.Distance);
                        CurrentTarget = CombatBase.KamakaziGoblin;
                    }

                    // Still no target, let's see if we should backtrack or wait for wrath to come off cooldown...
                    if (CurrentTarget == null)
                    {
                        RefreshWaitTimers();
                    }

                    // Still no target, let's end it all!
                    if (CurrentTarget == null)
                    {
                        Events.OnCacheUpdatedHandler.Invoke();
                        return(true);
                    }

                    if (CurrentTarget.IsUnit)
                    {
                        lastHadUnitInSights = DateTime.UtcNow;
                    }

                    if (CurrentTarget.IsBossOrEliteRareUnique)
                    {
                        lastHadEliteUnitInSights = DateTime.UtcNow;
                    }

                    if (CurrentTarget.IsBoss || CurrentTarget.IsBountyObjective)
                    {
                        lastHadBossUnitInSights = DateTime.UtcNow;
                    }

                    if (CurrentTarget.Type == TrinityObjectType.Container)
                    {
                        lastHadContainerInSights = DateTime.UtcNow;
                    }

                    // Record the last time our target changed
                    if (LastTargetRactorGUID != CurrentTarget.RActorGuid)
                    {
                        RecordTargetHistory();

                        Logger.Log(TrinityLogLevel.Verbose, LogCategory.Weight,
                                   "Found New Target {0} dist={1:0} IsElite={2} Radius={3:0.0} Weight={4:0} ActorSNO={5} " +
                                   "Anim={6} TargetedCount={7} Type={8} ",
                                   CurrentTarget.InternalName,
                                   CurrentTarget.Distance,
                                   CurrentTarget.IsEliteRareUnique,
                                   CurrentTarget.Radius,
                                   CurrentTarget.Weight,
                                   CurrentTarget.ActorSNO,
                                   CurrentTarget.Animation,
                                   CurrentTarget.TimesBeenPrimaryTarget,
                                   CurrentTarget.Type
                                   );

                        _lastPickedTargetTime = DateTime.UtcNow;
                        _targetLastHealth     = 0f;
                    }
                    else
                    {
                        // We're sticking to the same target, so update the target's health cache to check for stucks
                        if (CurrentTarget.IsUnit)
                        {
                            // Check if the health has changed, if so update the target-pick time before we blacklist them again
                            if (CurrentTarget.HitPointsPct != _targetLastHealth)
                            {
                                Logger.Log(TrinityLogLevel.Debug, LogCategory.Weight, "Keeping Target {0} - CurrentTarget.HitPoints: {1:0.00} TargetLastHealth: {2:0.00} ",
                                           CurrentTarget.RActorGuid, CurrentTarget.HitPointsPct, _targetLastHealth);
                                _lastPickedTargetTime = DateTime.UtcNow;
                            }
                            // Now store the target's last-known health
                            _targetLastHealth = CurrentTarget.HitPointsPct;
                        }
                    }
                }

                // Store less important objects.
                if (ObjectCache.Count > 1)
                {
                    var setting   = Settings.Advanced.CacheWeightThresholdPct;
                    var threshold = setting > 0 && CurrentTarget != null ? CurrentTarget.Weight * ((double)setting / 100) : 0;

                    var lowPriorityObjects = ObjectCache.DistinctBy(c => c.RActorGuid).Where(c =>
                                                                                             c.Type != TrinityObjectType.Avoidance && c.Type != TrinityObjectType.Unit ||
                                                                                             c.Weight <threshold && c.Distance> 12f && !c.IsElite
                                                                                             ).ToDictionary(x => x.RActorGuid, x => x);

                    Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Cached {0}/{1} ({2:0}%) WeightThreshold={3}",
                               lowPriorityObjects.Count,
                               ObjectCache.Count,
                               lowPriorityObjects.Count > 0 ? ((double)lowPriorityObjects.Count / ObjectCache.Count) * 100 : 0,
                               threshold);

                    CacheData.LowPriorityObjectCache = lowPriorityObjects;
                }

                // We have a target and the cached was refreshed
                Events.OnCacheUpdatedHandler.Invoke();
                return(true);
            }
        }
Example #7
0
        /// <summary>
        /// Adds Legendary & Set Minimap Markers to ObjectCache
        /// </summary>
        private static void RefreshCacheMarkers()
        {
            const int setItemMarkerTexture       = 404424;
            const int legendaryItemMarkerTexture = 275968;

            if (!BrainBehavior.IsVendoring && !WantToTownRun && !ForceVendorRunASAP && Settings.Loot.Pickup.PickupLegendaries)
            {
                var legendaryItemMarkers = ZetaDia.Minimap.Markers.CurrentWorldMarkers.Where(m => m.IsValid &&
                                                                                             m.Position.Distance2D(Player.Position) >= 45f && m.Position.Distance2D(Player.Position) < 300f &&
                                                                                             (m.MinimapTexture == setItemMarkerTexture || m.MinimapTexture == legendaryItemMarkerTexture) && !Blacklist60Seconds.Contains(m.NameHash)).ToList();

                foreach (var marker in legendaryItemMarkers)
                {
                    var name = (marker.MinimapTexture == setItemMarkerTexture ? "Set Item" : "Legendary Item") + " Minimap Marker";

                    var cacheObject = new TrinityCacheObject
                    {
                        Position     = new Vector3((float)Math.Floor(marker.Position.X), (float)Math.Floor(marker.Position.Y), (float)Math.Floor(marker.Position.Z)),
                        InternalName = name,
                        ActorSNO     = marker.NameHash,
                        RActorGuid   = marker.MinimapTexture,
                        Distance     = marker.Position.Distance(Player.Position),
                        ActorType    = ActorType.Item,
                        Type         = TrinityObjectType.Item,
                        ItemQuality  = ItemQuality.Legendary,
                        Radius       = 2f,
                        Weight       = 50,
                        IsMarker     = true
                    };
                    cacheObject.ObjectHash = HashGenerator.GenerateItemHash(cacheObject);

                    if (GenericBlacklist.ContainsKey(cacheObject.ObjectHash))
                    {
                        Logger.LogDebug(LogCategory.CacheManagement, "Ignoring Marker because it's blacklisted {0} {1} at {2} distance {3}", name, marker.MinimapTexture, marker.Position, marker.Position.Distance(Player.Position));
                        continue;
                    }

                    Logger.LogDebug(LogCategory.CacheManagement, "Adding {0} {1} at {2} distance {3}", name, marker.MinimapTexture, marker.Position, marker.Position.Distance(Player.Position));
                    ObjectCache.Add(cacheObject);
                }

                if (legendaryItemMarkers.Any() && TrinityItemManager.FindValidBackpackLocation(true) != new Vector2(-1, -1))
                {
                    var legendaryItems = ZetaDia.Actors.GetActorsOfType <DiaItem>().Where(i => i.IsValid && i.IsACDBased && i.Position.Distance2D(ZetaDia.Me.Position) < 5f &&
                                                                                          legendaryItemMarkers.Any(im => i.Position.Distance2D(i.Position) < 2f));

                    foreach (var diaItem in legendaryItems)
                    {
                        Logger.LogDebug(LogCategory.CacheManagement, "Adding Legendary Item from Marker {0} dist={1} ActorSNO={2} ACD={3} RActor={4}",
                                        diaItem.Name, diaItem.Distance, diaItem.ActorSNO, diaItem.ACDGuid, diaItem.RActorGuid);

                        ObjectCache.Add(new TrinityCacheObject()
                        {
                            Position         = diaItem.Position,
                            InternalName     = diaItem.Name,
                            RActorGuid       = diaItem.RActorGuid,
                            ActorSNO         = diaItem.ActorSNO,
                            ACDGuid          = diaItem.ACDGuid,
                            HasBeenNavigable = true,
                            HasBeenInLoS     = true,
                            Distance         = diaItem.Distance,
                            ActorType        = ActorType.Item,
                            Type             = TrinityObjectType.Item,
                            Radius           = 2f,
                            Weight           = 50,
                            ItemQuality      = ItemQuality.Legendary,
                        });
                    }
                }
            }

            bool isRiftGuardianQuestStep = ZetaDia.CurrentQuest.QuestSNO == 337492 && ZetaDia.CurrentQuest.StepId == 16;

            if (isRiftGuardianQuestStep)
            {
                // Add Rift Guardian POI's or Markers to ObjectCache
                const int riftGuardianMarkerTexture = 81058;
                Func <MinimapMarker, bool> riftGuardianMarkerFunc = m => m.IsValid && (m.IsPointOfInterest || m.MinimapTexture == riftGuardianMarkerTexture) &&
                                                                    !Blacklist60Seconds.Contains(m.NameHash);

                foreach (var marker in ZetaDia.Minimap.Markers.CurrentWorldMarkers.Where(riftGuardianMarkerFunc))
                {
                    Logger.LogDebug(LogCategory.CacheManagement, "Adding Rift Guardian POI, distance {0}", marker.Position.Distance2D(Player.Position));
                    ObjectCache.Add(new TrinityCacheObject()
                    {
                        Position     = marker.Position,
                        InternalName = "Rift Guardian",
                        Distance     = marker.Position.Distance(Player.Position),
                        RActorGuid   = marker.NameHash,
                        ActorType    = ActorType.Monster,
                        Type         = TrinityObjectType.Unit,
                        Radius       = 10f,
                        Weight       = 5000,
                    });
                }
            }

            if (isRiftGuardianQuestStep || Player.ParticipatingInTieredLootRun) // X1_LR_DungeonFinder
            {
                foreach (var marker in ZetaDia.Minimap.Markers.CurrentWorldMarkers.Where(m => m.IsPointOfInterest && !Blacklist60Seconds.Contains(m.NameHash)))
                {
                    ObjectCache.Add(new TrinityCacheObject()
                    {
                        Position     = marker.Position,
                        InternalName = "Rift Guardian",
                        Distance     = marker.Position.Distance(Player.Position),
                        RActorGuid   = marker.NameHash,
                        ActorType    = ActorType.Monster,
                        Type         = TrinityObjectType.Unit,
                        Radius       = 10f,
                        Weight       = 5000,
                    });
                }
            }
        }
        /// <summary>
        /// This method will add and update necessary information about all available actors. Determines ObjectType, sets ranges, updates blacklists, determines avoidance, kiting, target weighting
        /// and the result is we will have a new target for the Target Handler. Returns true if the cache was refreshed.
        /// </summary>
        /// <returns>True if the cache was updated</returns>
        public static bool RefreshDiaObjectCache(bool forceUpdate = false)
        {
            using (new PerformanceLogger("RefreshDiaObjectCache"))
            {
                if (DateTime.UtcNow.Subtract(LastRefreshedCache).TotalMilliseconds < Settings.Advanced.CacheRefreshRate && !forceUpdate)
                {
                    if (!UpdateCurrentTarget())
                    {
                        return(false);
                    }
                }
                LastRefreshedCache = DateTime.UtcNow;

                using (new PerformanceLogger("RefreshDiaObjectCache.UpdateBlock"))
                {
                    CacheData.Clear();
                    GenericCache.MaintainCache();
                    GenericBlacklist.MaintainBlacklist();

                    // Update player-data cache, including buffs
                    PlayerInfoCache.UpdateCachedPlayerData();

                    if (Player.CurrentHealthPct <= 0)
                    {
                        return(false);
                    }

                    RefreshCacheInit();

                    // Now pull up all the data and store anything we want to handle in the super special cache list
                    // Also use many cache dictionaries to minimize DB<->D3 memory hits, and speed everything up a lot
                    RefreshCacheMainLoop();
                }

                // Add Team HotSpots to the cache
                ObjectCache.AddRange(GroupHotSpots.GetCacheObjectHotSpots());

                /* Fire Chains Experimental Avoidance */
                if (Settings.Combat.Misc.UseExperimentalFireChainsAvoidance)
                {
                    const float fireChainSize = 5f;
                    foreach (var unit1 in ObjectCache.Where(u => u.MonsterAffixes.HasFlag(MonsterAffixes.FireChains)))
                    {
                        foreach (var unit2 in ObjectCache.Where(u => u.MonsterAffixes.HasFlag(MonsterAffixes.FireChains)).Where(unit2 => unit1.RActorGuid != unit2.RActorGuid))
                        {
                            for (float i = 0; i <= unit1.Position.Distance2D(unit2.Position); i += (fireChainSize / 4))
                            {
                                Vector3 fireChainSpot = MathEx.CalculatePointFrom(unit1.Position, unit2.Position, i);

                                if (Trinity.Player.Position.Distance2D(fireChainSpot) <= fireChainSize)
                                {
                                    Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Avoiding Fire Chains!");
                                    _standingInAvoidance = true;
                                }
                                CacheData.TimeBoundAvoidance.Add(new CacheObstacleObject(fireChainSpot, fireChainSize, -2, "FireChains"));
                            }
                        }
                        if (CacheData.TimeBoundAvoidance.Any(aoe => aoe.ActorSNO == -2))
                        {
                            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Generated {0} avoidance points for FireChains, minDistance={1} maxDistance={2}",
                                       CacheData.TimeBoundAvoidance.Count(aoe => aoe.ActorSNO == -2),
                                       CacheData.TimeBoundAvoidance.Where(aoe => aoe.ActorSNO == -2)
                                       .Min(aoe => aoe.Position.Distance2D(Trinity.Player.Position)),
                                       CacheData.TimeBoundAvoidance.Where(aoe => aoe.ActorSNO == -2)
                                       .Max(aoe => aoe.Position.Distance2D(Trinity.Player.Position)));
                        }
                    }
                }

                /* Beast Charge Experimental Avoidance */
                if (Settings.Combat.Misc.UseExperimentalSavageBeastAvoidance)
                {
                    const float beastChargePathWidth = 10f;
                    const int   beastChargerSNO      = 3337;
                    foreach (var unit1 in ObjectCache.Where(u => u.IsFacingPlayer && u.Animation == SNOAnim.Beast_start_charge_02 ||
                                                            u.Animation == SNOAnim.Beast_charge_02 || u.Animation == SNOAnim.Beast_charge_04))
                    {
                        Vector3 endPoint = MathEx.GetPointAt(unit1.Position, 90f, unit1.Unit.Movement.Rotation);

                        for (float i = 0; i <= unit1.Position.Distance2D(endPoint); i += (beastChargePathWidth / 4))
                        {
                            Vector3 pathSpot = MathEx.CalculatePointFrom(unit1.Position, endPoint, i);

                            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement,
                                       "Generating BeastCharge Avoidance: {0} dist: {1}",
                                       pathSpot, pathSpot.Distance2D(unit1.Position));

                            if (Trinity.Player.Position.Distance2D(pathSpot) <= beastChargePathWidth)
                            {
                                Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Avoiding Beast Charger!");
                                _standingInAvoidance = true;
                            }
                            CacheData.TimeBoundAvoidance.Add(new CacheObstacleObject(pathSpot, beastChargePathWidth, beastChargerSNO,
                                                                                     "BeastCharge"));
                        }
                        if (CacheData.TimeBoundAvoidance.Any(aoe => aoe.ActorSNO == beastChargerSNO))
                        {
                            Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement,
                                       "Generated {0} avoidance points for BeastCharge, minDistance={1} maxDistance={2}",
                                       CacheData.TimeBoundAvoidance.Count(aoe => aoe.ActorSNO == beastChargerSNO),
                                       CacheData.TimeBoundAvoidance.Where(aoe => aoe.ActorSNO == beastChargerSNO)
                                       .Min(aoe => aoe.Position.Distance2D(Trinity.Player.Position)),
                                       CacheData.TimeBoundAvoidance.Where(aoe => aoe.ActorSNO == beastChargerSNO)
                                       .Max(aoe => aoe.Position.Distance2D(Trinity.Player.Position)));
                        }
                    }
                }

                /* Poison Experimental Avoidance */



                // Reduce ignore-for-loops counter
                if (_ignoreTargetForLoops > 0)
                {
                    _ignoreTargetForLoops--;
                }
                // If we have an avoidance under our feet, then create a new object which contains a safety point to move to
                // But only if we aren't force-cancelling avoidance for XX time
                bool hasFoundSafePoint = false;

                using (new PerformanceLogger("RefreshDiaObjectCache.AvoidanceCheck"))
                {
                    if (Player.IsGhosted)
                    {
                        _standingInAvoidance = true;
                    }

                    // Note that if treasure goblin level is set to kamikaze, even avoidance moves are disabled to reach the goblin!
                    if (_standingInAvoidance && (!AnyTreasureGoblinsPresent || Settings.Combat.Misc.GoblinPriority <= GoblinPriority.Prioritize) &&
                        DateTime.UtcNow.Subtract(timeCancelledEmergencyMove).TotalMilliseconds >= cancelledEmergencyMoveForMilliseconds)
                    {
                        Vector3 vAnySafePoint = NavHelper.FindSafeZone(false, 1, Player.Position, true, null, true);

                        // Ignore avoidance stuff if we're incapacitated or didn't find a safe spot we could reach
                        if (vAnySafePoint != Vector3.Zero)
                        {
                            if (Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                            {
                                Logger.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Kiting Avoidance: {0} Distance: {1:0} Direction: {2:0}, Health%={3:0.00}, KiteDistance: {4:0}",
                                           vAnySafePoint, vAnySafePoint.Distance(Me.Position), MathUtil.GetHeading(MathUtil.FindDirectionDegree(Me.Position, vAnySafePoint)),
                                           Player.CurrentHealthPct, CombatBase.PlayerKiteDistance);
                            }

                            hasFoundSafePoint = true;
                            CurrentTarget     = new TrinityCacheObject()
                            {
                                Position     = vAnySafePoint,
                                Type         = GObjectType.Avoidance,
                                Weight       = 20000,
                                Distance     = Vector3.Distance(Player.Position, vAnySafePoint),
                                Radius       = 2f,
                                InternalName = "SafePoint"
                            };;
                        }
                    }
                }

                /*
                 *  Set Weights, assign CurrentTarget
                 */

                if (!hasFoundSafePoint)
                {
                    RefreshDiaGetWeights();

                    RefreshSetKiting(ref KiteAvoidDestination, NeedToKite);
                }
                // Not heading straight for a safe-spot?
                // No valid targets but we were told to stay put?
                if (CurrentTarget == null && _shouldStayPutDuringAvoidance && !_standingInAvoidance)
                {
                    CurrentTarget = new TrinityCacheObject()
                    {
                        Position     = Player.Position,
                        Type         = GObjectType.Avoidance,
                        Weight       = 20000,
                        Distance     = 2f,
                        Radius       = 2f,
                        InternalName = "StayPutPoint"
                    };
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Staying Put During Avoidance");
                }

                // Pre-townrun is too far away
                if (!Player.IsInTown && TownRun.PreTownRunPosition != Vector3.Zero && TownRun.PreTownRunWorldId == Player.WorldID && !ForceVendorRunASAP &&
                    TownRun.PreTownRunPosition.Distance2D(Player.Position) <= V.F("Cache.PretownRun.MaxDistance"))
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.UserInformation, "Pre-TownRun position is more than {0} yards away, canceling", V.I("Cache.PretownRun.MaxDistance"));
                    TownRun.PreTownRunPosition = Vector3.Zero;
                    TownRun.PreTownRunWorldId  = -1;
                }

                // Reached pre-townrun position
                if (!Player.IsInTown && TownRun.PreTownRunPosition != Vector3.Zero && TownRun.PreTownRunWorldId == Player.WorldID && !ForceVendorRunASAP &&
                    TownRun.PreTownRunPosition.Distance2D(Player.Position) <= 15f)
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.UserInformation, "Successfully returned to Pre-TownRun Position");
                    TownRun.PreTownRunPosition = Vector3.Zero;
                    TownRun.PreTownRunWorldId  = -1;
                }

                // After a townrun, make sure to return to original TownRun Location
                if (!Player.IsInTown && CurrentTarget == null && TownRun.PreTownRunPosition != Vector3.Zero && TownRun.PreTownRunWorldId == Player.WorldID && !ForceVendorRunASAP)
                {
                    if (TownRun.PreTownRunPosition.Distance2D(Player.Position) > 10f && TownRun.PreTownRunPosition.Distance2D(Player.Position) <= V.F("Cache.PretownRun.MaxDistance"))
                    {
                        CurrentTarget = new TrinityCacheObject()
                        {
                            Position     = TownRun.PreTownRunPosition,
                            Type         = GObjectType.Avoidance,
                            Weight       = 20000,
                            Distance     = 2f,
                            Radius       = 2f,
                            InternalName = "PreTownRunPosition"
                        };
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.UserInformation, "Returning to Pre-TownRun Position");
                    }
                }

                using (new PerformanceLogger("RefreshDiaObjectCache.FinalChecks"))
                {
                    // force to stay put if we want to town run and there's no target
                    if (CurrentTarget == null && ForceVendorRunASAP)
                    {
                        DontMoveMeIAmDoingShit = true;
                    }

                    if (Settings.WorldObject.EnableBountyEvents)
                    {
                        bool eventObjectNear = ObjectCache.Any(o => o.Type == GObjectType.CursedChest || o.Type == GObjectType.CursedShrine);

                        if (!Player.InActiveEvent)
                        {
                            EventStartPosition = Vector3.Zero;
                            EventStartTime     = DateTime.MinValue;
                        }

                        // Reset Event time while we have targts
                        if (CurrentTarget != null && Player.InActiveEvent && eventObjectNear)
                        {
                            EventStartTime = DateTime.UtcNow;
                        }

                        if (eventObjectNear)
                        {
                            EventStartPosition = ObjectCache.FirstOrDefault(o => o.Type == GObjectType.CursedChest || o.Type == GObjectType.CursedShrine).Position;
                        }

                        var activeEvent = ZetaDia.ActInfo.ActiveQuests.FirstOrDefault(q => DataDictionary.EventQuests.Contains(q.QuestSNO));

                        const int waitTimeoutSeconds = 90;
                        if (DateTime.UtcNow.Subtract(EventStartTime).TotalSeconds > waitTimeoutSeconds && activeEvent != null)
                        {
                            CacheData.BlacklistedEvents.Add(activeEvent.QuestSNO);
                        }

                        if (CurrentTarget == null && Player.InActiveEvent && EventStartPosition != Vector3.Zero &&
                            DateTime.UtcNow.Subtract(EventStartTime).TotalSeconds < waitTimeoutSeconds &&
                            activeEvent != null && !CacheData.BlacklistedEvents.Contains(activeEvent.QuestSNO))
                        {
                            CurrentTarget = new TrinityCacheObject()
                            {
                                Position     = EventStartPosition,
                                Type         = GObjectType.Avoidance,
                                Weight       = 20000,
                                Distance     = 2f,
                                Radius       = 2f,
                                InternalName = "WaitForEvent"
                            };
                            Logger.Log("Waiting for Event {0} - Time Remaining: {1:0} seconds",
                                       ZetaDia.ActInfo.ActiveQuests.FirstOrDefault(q => DataDictionary.EventQuests.Contains(q.QuestSNO)).Quest,
                                       waitTimeoutSeconds - DateTime.UtcNow.Subtract(EventStartTime).TotalSeconds);
                        }
                    }

                    // Still no target, let's see if we should backtrack or wait for wrath to come off cooldown...
                    if (CurrentTarget == null)
                    {
                        RefreshDoBackTrack();
                    }

                    // Still no target, let's end it all!
                    if (CurrentTarget == null)
                    {
                        return(true);
                    }


                    if (CurrentTarget.IsUnit)
                    {
                        lastHadUnitInSights = DateTime.UtcNow;
                    }

                    if (CurrentTarget.IsBossOrEliteRareUnique)
                    {
                        lastHadEliteUnitInSights = DateTime.UtcNow;
                    }

                    if (CurrentTarget.IsBoss || CurrentTarget.IsBountyObjective)
                    {
                        lastHadBossUnitInSights = DateTime.UtcNow;
                    }


                    if (CurrentTarget.Type == GObjectType.Container)
                    {
                        lastHadContainerInSights = DateTime.UtcNow;
                    }

                    // Record the last time our target changed
                    if (LastTargetRactorGUID != CurrentTarget.RActorGuid)
                    {
                        RecordTargetHistory();

                        Logger.Log(TrinityLogLevel.Verbose, LogCategory.Weight,
                                   "Found New Target {0} dist={1:0} IsElite={2} Radius={3:0.0} Weight={4:0} ActorSNO={5} " +
                                   "Anim={6} TargetedCount={7} Type={8} ",
                                   CurrentTarget.InternalName,
                                   CurrentTarget.Distance,
                                   CurrentTarget.IsEliteRareUnique,
                                   CurrentTarget.Radius,
                                   CurrentTarget.Weight,
                                   CurrentTarget.ActorSNO,
                                   CurrentTarget.Animation,
                                   CurrentTarget.TimesBeenPrimaryTarget,
                                   CurrentTarget.Type
                                   );

                        _lastPickedTargetTime = DateTime.UtcNow;
                        _targetLastHealth     = 0f;
                    }
                    else
                    {
                        // We're sticking to the same target, so update the target's health cache to check for stucks
                        if (CurrentTarget.IsUnit)
                        {
                            // Check if the health has changed, if so update the target-pick time before we blacklist them again
                            if (CurrentTarget.HitPointsPct != _targetLastHealth)
                            {
                                Logger.Log(TrinityLogLevel.Debug, LogCategory.Weight, "Keeping Target {0} - CurrentTarget.HitPoints: {1:0.00} TargetLastHealth: {2:0.00} ",
                                           CurrentTarget.RActorGuid, CurrentTarget.HitPointsPct, _targetLastHealth);
                                _lastPickedTargetTime = DateTime.UtcNow;
                            }
                            // Now store the target's last-known health
                            _targetLastHealth = CurrentTarget.HitPointsPct;
                        }
                    }
                }
                // We have a target and the cached was refreshed
                return(true);
            }
        }