/// <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(); }
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); }
/// <summary> /// Called when DemonBuddy shut down. /// </summary> public void OnShutdown() { GenericCache.Shutdown(); GenericBlacklist.Shutdown(); Helpers.PluginCheck.Shutdown(); }
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); }
/// <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); } }
/// <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); } }