/// <summary> /// Generates an SHA1 hash of a particular GilesObject /// </summary> /// <param name="obj"></param> /// <returns></returns> public static string GenerateObjecthash(GilesObject obj) { using (MD5 md5 = MD5.Create()) { string objHashBase = String.Format("{0}{1}{2}{3}", obj.ActorSNO, obj.Position, obj.Type, GilesTrinity.CurrentWorldDynamicId); string objHash = GetMd5Hash(md5, objHashBase); return(objHash); } }
// For cloning the object (and not simply referencing it) public GilesObject Clone() { GilesObject newGilesObject = new GilesObject(Unit) { Position = Position, Type = Type, Weight = Weight, CentreDistance = CentreDistance, RadiusDistance = RadiusDistance, InternalName = InternalName, ACDGuid = ACDGuid, RActorGuid = RActorGuid, DynamicID = DynamicID, BalanceID = BalanceID, ActorSNO = ActorSNO, ItemLevel = ItemLevel, GoldAmount = GoldAmount, OneHanded = OneHanded, TwoHanded = TwoHanded, ItemQuality = ItemQuality, DBItemBaseType = DBItemBaseType, DBItemType = DBItemType, FollowerType = FollowerType, GilesItemType = GilesItemType, IsElite = IsElite, IsRare = IsRare, IsUnique = IsUnique, IsMinion = IsMinion, IsTreasureGoblin = IsTreasureGoblin, IsEliteRareUnique = IsEliteRareUnique, IsBoss = IsBoss, IsAttackable = IsAttackable, HitPointsPct = HitPointsPct, HitPoints = HitPoints, Radius = Radius, MonsterStyle = MonsterStyle, ForceLeapAgainst = ForceLeapAgainst, ObjectHash = ObjectHash }; return(newGilesObject); }
private static void RecordTargetHistory() { string targetMd5Hash = HashGenerator.GenerateObjecthash(CurrentTarget); // clean up past targets if (!GenericCache.ContainsKey(targetMd5Hash)) { CurrentTarget.HasBeenPrimaryTarget = true; CurrentTarget.TimesBeenPrimaryTarget = 1; CurrentTarget.FirstTargetAssignmentTime = DateTime.Now; GenericCache.AddToCache(new GenericCacheObject(targetMd5Hash, CurrentTarget, new TimeSpan(0, 10, 0))); } else if (GenericCache.ContainsKey(targetMd5Hash)) { GilesObject cTarget = (GilesObject)GenericCache.GetObject(targetMd5Hash).Value; if (!cTarget.IsBoss && cTarget.TimesBeenPrimaryTarget > 15 && !(cTarget.Type == GObjectType.Item && cTarget.ItemQuality >= ItemQuality.Legendary)) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Blacklisting target {0} ActorSNO={1} RActorGUID={2} due to possible stuck/flipflop!", CurrentTarget.InternalName, CurrentTarget.ActorSNO, CurrentTarget.RActorGuid); hashRGUIDBlacklist60.Add(CurrentTarget.RActorGuid); // Add to generic blacklist for safety, as the RActorGUID on items and gold can change as we move away and get closer to the items (while walking around corners) // So we can't use any ID's but rather have to use some data which never changes (actorSNO, position, type, worldID) GenericBlacklist.AddToBlacklist(new GenericCacheObject() { Key = CurrentTarget.ObjectHash, Value = null, Expires = DateTime.Now.AddSeconds(60) }); } else { cTarget.TimesBeenPrimaryTarget++; GenericCache.UpdateObject(new GenericCacheObject(targetMd5Hash, cTarget, new TimeSpan(0, 10, 0))); } } }
private static void RefreshSetKiting(ref Vector3 vKitePointAvoid, bool NeedToKite, ref bool TryToKite) { using (new PerformanceLogger("RefreshDiaObjectCache.Kiting")) { TryToKite = false; var monsterList = from m in GilesObjectCache where m.Type == GObjectType.Unit && m.Weight > 0 && m.RadiusDistance <= PlayerKiteDistance && (m.IsBossOrEliteRareUnique || ((m.HitPointsPct >= .15 || m.MonsterStyle != MonsterSize.Swarm) && !m.IsBossOrEliteRareUnique) ) select m; if (CurrentTarget != null && CurrentTarget.Type == GObjectType.Unit && PlayerKiteDistance > 0 && CurrentTarget.RadiusDistance <= PlayerKiteDistance) { TryToKite = true; vKitePointAvoid = PlayerStatus.CurrentPosition; } if (monsterList.Count() > 0 && (PlayerStatus.ActorClass != ActorClass.Wizard || IsWizardShouldKite())) { TryToKite = true; vKitePointAvoid = PlayerStatus.CurrentPosition; } // Note that if treasure goblin level is set to kamikaze, even avoidance moves are disabled to reach the goblin! bool shouldKamikazeTreasureGoblins = (!AnyTreasureGoblinsPresent || Settings.Combat.Misc.GoblinPriority <= GoblinPriority.Prioritize); double msCancelledEmergency = DateTime.Now.Subtract(timeCancelledEmergencyMove).TotalMilliseconds; bool shouldEmergencyMove = msCancelledEmergency >= cancelledEmergencyMoveForMilliseconds && NeedToKite; double msCancelledKite = DateTime.Now.Subtract(timeCancelledKiteMove).TotalMilliseconds; bool shouldKite = msCancelledKite >= cancelledKiteMoveForMilliseconds && TryToKite; if (shouldKamikazeTreasureGoblins && (shouldEmergencyMove || shouldKite)) { Vector3 vAnySafePoint = NavHelper.FindSafeZone(false, 1, vKitePointAvoid, true, monsterList); if (LastKitePosition == null) { LastKitePosition = new KitePosition() { PositionFoundTime = DateTime.Now, Position = vAnySafePoint, Distance = vAnySafePoint.Distance(PlayerStatus.CurrentPosition) }; } if (vAnySafePoint != Vector3.Zero && vAnySafePoint.Distance(PlayerStatus.CurrentPosition) >= 1) { if ((DateTime.Now.Subtract(LastKitePosition.PositionFoundTime).TotalMilliseconds > 3000 && LastKitePosition.Position == vAnySafePoint) || (CurrentTarget != null && DateTime.Now.Subtract(lastGlobalCooldownUse).TotalMilliseconds > 1500 && TryToKite)) { timeCancelledKiteMove = DateTime.Now; cancelledKiteMoveForMilliseconds = 1500; DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Kite movement failed, cancelling for {0:0}ms", cancelledKiteMoveForMilliseconds); return; } else { LastKitePosition = new KitePosition() { PositionFoundTime = DateTime.Now, Position = vAnySafePoint, Distance = vAnySafePoint.Distance(PlayerStatus.CurrentPosition) }; } if (Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Kiting to: {0} Distance: {1:0} Direction: {2:0}, Health%={3:0.00}, KiteDistance: {4:0}, Nearby Monsters: {5:0} NeedToKite: {6} TryToKite: {7}", vAnySafePoint, vAnySafePoint.Distance(PlayerStatus.CurrentPosition), MathUtil.GetHeading(MathUtil.FindDirectionDegree(Me.Position, vAnySafePoint)), PlayerStatus.CurrentHealthPct, PlayerKiteDistance, monsterList.Count(), NeedToKite, TryToKite); } CurrentTarget = new GilesObject() { Position = vAnySafePoint, Type = GObjectType.Avoidance, Weight = 90000, CentreDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vAnySafePoint), RadiusDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vAnySafePoint), InternalName = "KitePoint" }; //timeCancelledKiteMove = DateTime.Now; //cancelledKiteMoveForMilliseconds = 100; // Try forcing a target update with each kiting //bForceTargetUpdate = true; } //else //{ // // Didn't find any kiting we could reach, so don't look for any more kite spots for at least 1.5 seconds // timeCancelledKiteMove = DateTime.Now; // cancelledKiteMoveForMilliseconds = 500; //} } else if (!shouldEmergencyMove && NeedToKite) { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Emergency movement cancelled for {0:0}ms", DateTime.Now.Subtract(timeCancelledEmergencyMove).TotalMilliseconds - cancelledKiteMoveForMilliseconds); } else if (!shouldKite && TryToKite) { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Kite movement cancelled for {0:0}ms", DateTime.Now.Subtract(timeCancelledKiteMove).TotalMilliseconds - cancelledKiteMoveForMilliseconds); } } }
private static void RefreshDoBackTrack() { // See if we should wait for [playersetting] milliseconds for possible loot drops before continuing run if (DateTime.Now.Subtract(lastHadUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill || DateTime.Now.Subtract(lastHadEliteUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill) { CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForLootDrops" }; DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Behavior, "Waiting for loot to drop, delay: {0}ms", Settings.Combat.Misc.DelayAfterKill); } // Now see if we need to do any backtracking if (CurrentTarget == null && iTotalBacktracks >= 2 && Settings.Combat.Misc.AllowBacktracking && !PlayerStatus.IsInTown) // Never bother with the 1st backtrack position nor if we are in town { // See if we're already within 18 feet of our start position first if (Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[1]) <= 18f) { vBacktrackList = new SortedList <int, Vector3>(); iTotalBacktracks = 0; } // See if we can raytrace to the final location and it's within 25 feet if (iTotalBacktracks >= 2 && Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[1]) <= 25f && NavHelper.CanRayCast(PlayerStatus.CurrentPosition, vBacktrackList[1])) { vBacktrackList = new SortedList <int, Vector3>(); iTotalBacktracks = 0; } if (iTotalBacktracks >= 2) { // See if we can skip to the next backtracker location first if (iTotalBacktracks >= 3) { if (Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks - 1]) <= 10f) { vBacktrackList.Remove(iTotalBacktracks); iTotalBacktracks--; } } CurrentTarget = new GilesObject() { Position = vBacktrackList[iTotalBacktracks], Type = GObjectType.Backtrack, Weight = 20000, CentreDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks]), RadiusDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks]), InternalName = "GilesBacktrack" }; } } else { vBacktrackList = new SortedList <int, Vector3>(); iTotalBacktracks = 0; } // End of backtracking check //TODO : If this code is obselete remove it (Check that) // Finally, a special check for waiting for wrath of the berserker cooldown before engaging Azmodan if (CurrentTarget == null && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && Settings.Combat.Barbarian.WaitWOTB && !GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { bDontSpamOutofCombat = true; Logging.Write("[Trinity] Waiting for Wrath Of The Berserker cooldown before continuing to Azmodan."); CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForWrath" }; } // And a special check for wizard archon if (CurrentTarget == null && Hotbar.Contains(SNOPower.Wizard_Archon) && !GilesUseTimer(SNOPower.Wizard_Archon) && Settings.Combat.Wizard.WaitArchon && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Waiting for Wizard Archon cooldown before continuing to Azmodan."); CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForArchon" }; } // And a very sexy special check for WD BigBadVoodoo if (CurrentTarget == null && Hotbar.Contains(SNOPower.Witchdoctor_BigBadVoodoo) && !PowerManager.CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Waiting for WD BigBadVoodoo cooldown before continuing to Azmodan."); CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForVoodooo" }; } }
/// <summary> /// This method will add and update necessary information about all available actors. Determines GilesObjectType, 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.Now.Subtract(LastRefreshedCache).TotalMilliseconds < Settings.Advanced.CacheRefreshRate && !forceUpdate) { if (!UpdateCurrentTarget()) { return(false); } } LastRefreshedCache = DateTime.Now; using (new PerformanceLogger("RefreshDiaObjectCache.UpdateBlock")) { GenericCache.MaintainCache(); GenericBlacklist.MaintainBlacklist(); using (ZetaDia.Memory.AcquireFrame()) { // Update player-data cache, including buffs GilesPlayerCache.UpdateCachedPlayerData(); if (PlayerStatus.CurrentHealthPct <= 0) { return(false); } if (Settings.Combat.Misc.UseNavMeshTargeting && !gp.CanStandAt(gp.WorldToGrid(PlayerStatus.CurrentPosition.ToVector2()))) { NavHelper.UpdateSearchGridProvider(); } 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(); } } // 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 bFoundSafeSpot = false; using (new PerformanceLogger("RefreshDiaObjectCache.AvoidanceCheck")) { // 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.Now.Subtract(timeCancelledEmergencyMove).TotalMilliseconds >= cancelledEmergencyMoveForMilliseconds) { Vector3 vAnySafePoint = NavHelper.FindSafeZone(false, 1, PlayerStatus.CurrentPosition, true); // Ignore avoidance stuff if we're incapacitated or didn't find a safe spot we could reach if (vAnySafePoint != vNullLocation) { if (Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.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)), PlayerStatus.CurrentHealthPct, PlayerKiteDistance); } bFoundSafeSpot = true; CurrentTarget = new GilesObject() { Position = vAnySafePoint, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vAnySafePoint), RadiusDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vAnySafePoint), InternalName = "GilesSafePoint" };; } //else //{ // // Didn't find any safe spot we could reach, so don't look for any more safe spots for at least 2.8 seconds // cancelledEmergencyMoveForMilliseconds = 2800; // timeCancelledEmergencyMove = DateTime.Now; // DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Unable to find kite location, canceling emergency movement for {0}ms", cancelledEmergencyMoveForMilliseconds); //} } } /* * Give weights to objects */ // Special flag for special whirlwind circumstances bAnyNonWWIgnoreMobsInRange = false; // Now give each object a weight *IF* we aren't skipping direcly to a safe-spot if (!bFoundSafeSpot) { RefreshDiaGetWeights(); RefreshSetKiting(ref vKitePointAvoid, NeedToKite, ref TryToKite); } // Not heading straight for a safe-spot? // No valid targets but we were told to stay put? if (CurrentTarget == null && ShouldStayPutDuringAvoidance && !StandingInAvoidance) { CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesStayPutPoint" }; DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Staying Put During Avoidance"); } using (new PerformanceLogger("RefreshDiaObjectCache.FinalChecks")) { // force to stay put if we want to town run and there's no target if (CurrentTarget == null && ForceVendorRunASAP) { bDontMoveMeIAmDoingShit = true; } // 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); } // Ok record the time we last saw any unit at all if (CurrentTarget.Type == GObjectType.Unit) { lastHadUnitInSights = DateTime.Now; // And record when we last saw any form of elite if (CurrentTarget.IsBoss || CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin) { lastHadEliteUnitInSights = DateTime.Now; } } // Record the last time our target changed if (CurrentTargetRactorGUID != CurrentTarget.RActorGuid) { RecordTargetHistory(); DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Weight, "Found New Target - {0} CurrentTargetRactorGUID: {1} CurrentTarget.RActorGuid: {2}", DateTime.Now, CurrentTargetRactorGUID, CurrentTarget.RActorGuid); dateSincePickedTarget = DateTime.Now; iTargetLastHealth = 0f; } else { // We're sticking to the same target, so update the target's health cache to check for stucks if (CurrentTarget.Type == GObjectType.Unit) { // Check if the health has changed, if so update the target-pick time before we blacklist them again if (CurrentTarget.HitPointsPct != iTargetLastHealth) { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Weight, "Keeping Target {0} - CurrentTarget.iHitPoints: {1:0.00} iTargetLastHealth: {2:0.00} ", CurrentTarget.RActorGuid, CurrentTarget.HitPointsPct, iTargetLastHealth); dateSincePickedTarget = DateTime.Now; } // Now store the target's last-known health iTargetLastHealth = CurrentTarget.HitPointsPct; } } } // We have a target and the cached was refreshed return(true); } }