/// <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> /// 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); } }