private static void Update() { try { using (new PerformanceLogger("CacheUI Update")) { if (_isUpdating) { return; } if (_dataModel.IsDefaultVisible && DateTime.UtcNow.Subtract(_lastUpdatedDefault).TotalMilliseconds > 250) { _isUpdating = true; _dataModel.Cache = new ObservableCollection <CacheUIObject>(GetCacheActorList()); _lastUpdatedDefault = DateTime.UtcNow; } if (_dataModel.IsLazyCacheVisible && DateTime.UtcNow.Subtract(_lastUpdatedLazy).TotalMilliseconds > 50) { _isUpdating = true; _lastUpdatedLazy = DateTime.UtcNow; } _isUpdating = false; } } catch (Exception ex) { _isUpdating = false; Logger.LogError("Error in CacheUI Worker: " + ex); } }
protected static Composite CreateUnstuckBehavior() { return(new Decorator(ret => !IsDone, new Action(ret => { try { if (!_points.Any() || IsWithinRange(_points.First())) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Arrived at Destination {0} Distance={1}", _points.First().ToString(), _points.First()); IsDone = true; return RunStatus.Failure; } if (ZetaDia.Me.Movement.IsMoving) { _lastMoving = DateTime.UtcNow; } // Ooooh bad bad bad - always use Navigator raycast! //var rayResult = ZetaDia.Physics.Raycast(ZetaDia.Me.Position, _points.First(), NavCellFlags.AllowWalk); var raycastHit = Navigator.Raycast(ZetaDia.Me.Position, _points.First()); var stuckResult = DateTime.UtcNow.Subtract(_lastMoving).TotalMilliseconds > 250; if (stuckResult || raycastHit) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Discarded Location {0} Distance={1} RaycastResult={2} StuckResult={3} LocationsRemaining={4}", _points.First().ToString(), _points.First().Distance(ZetaDia.Me.Position), raycastHit, stuckResult, _points.Count - 1); _points.RemoveAt(0); } if (_points.Any() && _points.First() != Vector3.Zero && ZetaDia.Me.Movement.MoveActor(_points.First()) == 1) { return RunStatus.Success; } IsDone = true; return RunStatus.Failure; } catch (Exception ex) { Logger.LogError("Exception in UnstuckBehavior: " + ex); return RunStatus.Failure; } }) )); }
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> /// This is wired up by Plugin.OnEnabled, and called when the bot is started /// </summary> /// <param name="bot"></param> private static void TrinityBotStart(IBot bot) { Logger.Log("Bot Starting"); DateTime timeBotStart = DateTime.UtcNow; V.ValidateLoad(); // Recording of all the XML's in use this run try { string sThisProfile = GlobalSettings.Instance.LastProfile; if (sThisProfile != CurrentProfile) { ProfileHistory.Add(sThisProfile); CurrentProfile = sThisProfile; if (FirstProfile == "") { FirstProfile = sThisProfile; } } DebugUtil.LogSystemInformation(); } catch { } if (!ItemDropStats.MaintainStatTracking) { ItemDropStats.ItemStatsWhenStartedBot = DateTime.UtcNow; ItemDropStats.ItemStatsLastPostedReport = DateTime.UtcNow; ItemDropStats.MaintainStatTracking = true; } else { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Note: Maintaining item stats from previous run. To reset stats fully, please restart DB."); } TrinityItemManager.ResetBackPackCheck(); BeginInvoke(UsedProfileManager.RefreshProfileBlacklists); UsedProfileManager.SetProfileInWindowTitle(); BotManager.ReplaceTreeHooks(); TreeHooks.Instance.OnHooksCleared += BotManager.InstanceOnOnHooksCleared; PlayerMover.TimeLastRecordedPosition = DateTime.UtcNow; PlayerMover.LastRestartedGame = DateTime.UtcNow; Logger.Log("Bot Starting, Resetting Gold Inactivity Timer"); GoldInactivity.Instance.ResetCheckGold(); XpInactivity.Instance.ResetCheckXp(); if (CharacterSettings.Instance.KillRadius < 20) { Logger.Log("WARNING: Low Kill Radius detected, currently set to: {0} (you can change this through Demonbuddy bot settings)", CharacterSettings.Instance.KillRadius); } if (CharacterSettings.Instance.LootRadius < 50) { Logger.Log("WARNING: Low Gold Loot Radius detected, currently set to: {0} (you can change this through Demonbuddy bot settings)", CharacterSettings.Instance.LootRadius); } if (Settings.Loot.ItemFilterMode == ItemFilterMode.TrinityWithItemRules) { try { if (StashRule == null) { StashRule = new Interpreter(); } } catch (Exception ex) { Logger.LogError("Error configuring ItemRules Interpreter: " + ex); } } Logger.LogDebug("Trinity BotStart took {0:0}ms", DateTime.UtcNow.Subtract(timeBotStart).TotalMilliseconds); }
/// <summary> /// TownRunCheckOverlord - determine if we should do a town-run or not /// </summary> /// <param name="ret"></param> /// <returns></returns> internal static bool TownRunCanRun(object ret) { try { using (new PerformanceLogger("TownRunOverlord")) { if (ZetaDia.Me == null || !ZetaDia.Me.IsValid) { return(false); } Trinity.IsReadyToTownRun = false; if (Trinity.Player.IsDead) { return(false); } var a5hubMaxRangePoint = new Vector3(441.0972f, 519.0328f, 7.718145f); var a5waypoint = new Vector3(556.9158f, 524.3738f, 2.796464f); var a5hubRange = a5waypoint.Distance2D(a5hubMaxRangePoint); if (Trinity.Player.LevelAreaId == 270011 && Trinity.Player.Position.Distance2D(a5waypoint) >= a5hubRange) { return(false); } // Check if we should be forcing a town-run if (!Trinity.Player.IsInTown && Trinity.ForceVendorRunASAP || BrainBehavior.IsVendoring) { if (!LastTownRunCheckResult) { if (BrainBehavior.IsVendoring) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Looks like we are being asked to force a town-run by a profile/plugin/new DB feature, now doing so."); } } SetPreTownRunPosition(); Trinity.IsReadyToTownRun = true; } // Time safety switch for more advanced town-run checking to prevent CPU spam if (DateTime.UtcNow.Subtract(LastCheckBackpackDurability).TotalSeconds > 6) { LastCheckBackpackDurability = DateTime.UtcNow; // Check for no space in backpack Vector2 validLocation = TrinityItemManager.FindValidBackpackLocation(true); if (validLocation.X < 0 || validLocation.Y < 0) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "No more space to pickup a 2-slot item, now running town-run routine. (TownRun)"); if (!LastTownRunCheckResult) { LastTownRunCheckResult = true; } Trinity.IsReadyToTownRun = true; Trinity.ForceVendorRunASAP = true; // Record the first position when we run out of bag space, so we can return later SetPreTownRunPosition(); } if (ZetaDia.Me.IsValid) { var equippedItems = ZetaDia.Me.Inventory.Equipped.Where(i => i.DurabilityCurrent != i.DurabilityMax).ToList(); if (equippedItems.Any()) { double min = equippedItems.Min(i => i.DurabilityPercent); float threshold = Trinity.Player.IsInTown ? 0.50f : Zeta.Bot.Settings.CharacterSettings.Instance.RepairWhenDurabilityBelow; bool needsRepair = min <= threshold; if (needsRepair) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Items may need repair, now running town-run routine."); Trinity.IsReadyToTownRun = true; Trinity.ForceVendorRunASAP = true; SetPreTownRunPosition(); } } } } if (ErrorDialog.IsVisible) { Trinity.IsReadyToTownRun = false; } LastTownRunCheckResult = Trinity.IsReadyToTownRun; // Clear blacklists to triple check any potential targets if (Trinity.IsReadyToTownRun) { Trinity.Blacklist3Seconds = new HashSet <int>(); Trinity.Blacklist15Seconds = new HashSet <int>(); Trinity.Blacklist60Seconds = new HashSet <int>(); Trinity.Blacklist90Seconds = new HashSet <int>(); } // Fix for A1 new game with bags full if (Trinity.Player.LevelAreaId == 19947 && ZetaDia.CurrentQuest.QuestSNO == 87700 && (ZetaDia.CurrentQuest.StepId == -1 || ZetaDia.CurrentQuest.StepId == 42)) { Logger.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Can't townrun with the current quest!"); Trinity.IsReadyToTownRun = false; } if (Trinity.IsReadyToTownRun && !(BrainBehavior.IsVendoring || Trinity.Player.IsInTown)) { string cantUseTPreason; if (!ZetaDia.Me.CanUseTownPortal(out cantUseTPreason) && !ZetaDia.IsInTown) { Logger.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "It appears we need to town run but can't: {0}", cantUseTPreason); Trinity.IsReadyToTownRun = false; } } if (Trinity.IsReadyToTownRun && DataDictionary.BossLevelAreaIDs.Contains(Trinity.Player.LevelAreaId)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.GlobalHandler, "Unable to Town Portal - Boss Area!"); return(false); } if (Trinity.IsReadyToTownRun && ZetaDia.IsInTown && DbProvider.DeathHandler.EquipmentNeedsEmergencyRepair()) { Logger.Log(TrinityLogLevel.Debug, LogCategory.GlobalHandler, "EquipmentNeedsEmergencyRepair!"); return(true); } if (Trinity.IsReadyToTownRun && Trinity.CurrentTarget != null) { TownRunCheckTimer.Restart(); Logger.Log(TrinityLogLevel.Debug, LogCategory.GlobalHandler, "Restarting TownRunCheckTimer, we have a target!"); return(false); } if (Trinity.IsReadyToTownRun && DataDictionary.NeverTownPortalLevelAreaIds.Contains(Trinity.Player.LevelAreaId)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.GlobalHandler, "Unable to Town Portal in this area!"); return(false); } if (Trinity.IsReadyToTownRun && (TownRunTimerFinished() || BrainBehavior.IsVendoring)) { //Logger.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Town run timer finished {0} or in town {1} or is vendoring {2} (TownRun)", // TownRunTimerFinished(), Trinity.Player.IsInTown, BrainBehavior.IsVendoring); Trinity.IsReadyToTownRun = false; return(true); } if (Trinity.IsReadyToTownRun && !TownRunCheckTimer.IsRunning) { Logger.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Starting town run timer"); TownRunCheckTimer.Start(); _loggedAnythingThisStash = false; _loggedJunkThisStash = false; } return(false); } } catch (Exception ex) { Logger.LogError("Error Getting TownRun {0}", ex.Message); return(false); } }
/// <summary> /// Called when user Enable the plugin. /// </summary> public void OnEnabled() { try { Logger.Log("OnEnable start"); DateTime dateOnEnabledStart = DateTime.UtcNow; BotMain.OnStart += TrinityBotStart; BotMain.OnStop += TrinityBotStop; SetWindowTitle(); TabUi.InstallTab(); if (!Directory.Exists(FileManager.PluginPath)) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Fatal Error - cannot enable plugin. Invalid path: {0}", FileManager.PluginPath); Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Please check you have installed the plugin to the correct location, and then restart DemonBuddy and re-enable the plugin."); Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, @"Plugin should be installed to \<DemonBuddyFolder>\Plugins\Trinity\"); } else { _isPluginEnabled = true; PluginCheck.Start(); // Settings are available after this... LoadConfiguration(); Navigator.PlayerMover = new PlayerMover(); BotManager.SetUnstuckProvider(); GameEvents.OnPlayerDied += TrinityOnDeath; GameEvents.OnGameJoined += TrinityOnJoinGame; GameEvents.OnGameLeft += TrinityOnLeaveGame; GameEvents.OnItemSold += ItemEvents.TrinityOnItemSold; GameEvents.OnItemSalvaged += ItemEvents.TrinityOnItemSalvaged; GameEvents.OnItemDropped += ItemEvents.TrinityOnItemDropped; GameEvents.OnItemStashed += ItemEvents.TrinityOnItemStashed; GameEvents.OnItemIdentificationRequest += ItemEvents.TrinityOnOnItemIdentificationRequest; GameEvents.OnGameChanged += GameEvents_OnGameChanged; GameEvents.OnWorldChanged += GameEvents_OnWorldChanged; CombatTargeting.Instance.Provider = new BlankCombatProvider(); LootTargeting.Instance.Provider = new BlankLootProvider(); ObstacleTargeting.Instance.Provider = new BlankObstacleProvider(); if (Settings.Loot.ItemFilterMode != ItemFilterMode.DemonBuddy) { ItemManager.Current = new TrinityItemManager(); } // Safety check incase DB "OnStart" event didn't fire properly if (BotMain.IsRunning) { TrinityBotStart(null); if (ZetaDia.IsInGame) { TrinityOnJoinGame(null, null); } } BotManager.SetBotTicksPerSecond(); UILoader.PreLoadWindowContent(); Events.OnCacheUpdated += Enemies.Update; Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "ENABLED: {0} now in action!", Description); } if (StashRule != null) { // reseting stash rules BeginInvoke(() => StashRule.reset()); } Logger.LogDebug("OnEnable took {0}ms", DateTime.UtcNow.Subtract(dateOnEnabledStart).TotalMilliseconds); } catch (Exception ex) { Logger.LogError("Error in OnEnable: " + ex); } }
/// <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); } }