internal static void TrinityOnItemSold(object sender, ItemEventArgs e) { ResetTownRun(); try { ACDItem i = e.Item; if (i == null || !i.IsValid || i.IsDisposed) { return; } var cachedItem = CachedACDItem.GetCachedItem(i); switch (i.ItemBaseType) { case ItemBaseType.Gem: case ItemBaseType.Misc: break; default: TownRun.LogJunkItems(cachedItem, cachedItem.TrinityItemBaseType, cachedItem.TrinityItemType, ItemValuation.ValueThisItem(cachedItem, cachedItem.TrinityItemType)); break; } } catch (Exception ex) { if (ex is CoroutineStoppedException) { throw; } } }
/// <summary> /// This will replace the main BehaviorTree hooks for Combat, Vendoring, and Looting. /// </summary> private static void ReplaceTreeHooks() { // This is the do-all-be-all god-head all encompasing piece of trinity TreeHooks.Instance.ReplaceHook("Combat", new Decorator(ctx => CheckHasTarget(ctx), HandleTargetAction())); // We still want the main VendorRun logic, we're just going to take control of *when* this logic kicks in PrioritySelector VendorRunPrioritySelector = (TreeHooks.Instance.Hooks["VendorRun"][0] as Decorator).Children[0] as PrioritySelector; TreeHooks.Instance.ReplaceHook("VendorRun", new Decorator(ret => TownRun.TownRunCanRun(ret), VendorRunPrioritySelector)); // Loot tree is now empty and never runs (Loot is handled through combat) TreeHooks.Instance.ReplaceHook("Loot", new Decorator(ret => false, new Action())); }
public static void CheckShouldTownRunForGambling() { if (!ZetaDia.IsInTown) { IsDumpingShards = false; } if (Trinity.Trinity.Settings.Gambling.ShouldTownRun && ZetaDia.CPlayer.BloodshardCount >= Math.Min(Trinity.Trinity.Settings.Gambling.SaveShardsThreshold, Trinity.Trinity.Player.MaxBloodShards)) { if (CanRun() && !ShouldSaveShards && !TownRun.IsTryingToTownPortal() && !BrainBehavior.IsVendoring) { BrainBehavior.ForceTownrun("Bloodshard Spending Threshold"); } } }
private void TrinityOnItemSalvaged(object sender, ItemEventArgs e) { ACDItem i = e.Item; var cachedItem = GilesCachedACDItem.GetCachedItem(i); ResetTownRun(); switch (i.ItemBaseType) { case ItemBaseType.Gem: case ItemBaseType.Misc: break; default: TownRun.LogJunkItems(cachedItem, cachedItem.TrinityItemBaseType, cachedItem.TrinityItemType, ItemValuation.ValueThisItem(cachedItem, cachedItem.TrinityItemType)); break; } }
public static bool ShouldIgnore(TrinityCacheObject cacheObject) { if (TownRun.IsTryingToTownPortal()) { return(false); } if (cacheObject == null) { return(false); } if (!cacheObject.IsFullyValid()) { return(true); } var isRare = cacheObject.CommonData.MonsterQualityLevel == Zeta.Game.Internals.Actors.MonsterQuality.Rare; var isMinion = cacheObject.CommonData.MonsterQualityLevel == Zeta.Game.Internals.Actors.MonsterQuality.Minion; var isChampion = cacheObject.CommonData.MonsterQualityLevel == Zeta.Game.Internals.Actors.MonsterQuality.Champion; if ((cacheObject.IsEliteRareUnique || cacheObject.IsBoss || isMinion) && cacheObject.HitPointsPct <= Settings.Combat.Misc.ForceKillElitesHealth) { return(false); } if (Trinity.Settings.Combat.Misc.IgnoreHighHitePointTrash && cacheObject.IsTrashMob) { var unitName = cacheObject.InternalName.ToLower(); if (HighHitPointTrashMobNames.Any(name => unitName.Contains(name))) { return(true); } } if ((Trinity.Settings.Combat.Misc.IgnoreRares && (isRare | isMinion) || Trinity.Settings.Combat.Misc.IgnoreChampions && isChampion) && !cacheObject.IsBoss) { return(true); } return(false); }
/// <summary> /// Receive Pulse event from DemonBuddy. /// </summary> public void OnPulse() { try { if (ZetaDia.Me == null) { return; } if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || ZetaDia.IsLoadingWorld) { return; } // hax for sending notifications after a town run if (!Zeta.CommonBot.Logic.BrainBehavior.IsVendoring && !PlayerStatus.IsInTown) { TownRun.SendEmailNotification(); TownRun.SendMobileNotifications(); } // See if we should update the stats file if (DateTime.Now.Subtract(ItemStatsLastPostedReport).TotalSeconds > 10) { ItemStatsLastPostedReport = DateTime.Now; OutputReport(); } // Recording of all the XML's in use this run UsedProfileManager.RecordProfile(); Monk_MaintainTempestRush(); } catch (Exception ex) { DbHelper.Log(LogCategory.UserInformation, "Exception in Pulse: {0}", ex.ToString()); } }
private static void ReplaceVendorRunHook() { if (!TreeHooks.Instance.Hooks.ContainsKey("VendorRun")) { return; } // We still want the main VendorRun logic, we're just going to take control of *when* this logic kicks in var vendorDecorator = TreeHooks.Instance.Hooks["VendorRun"][0] as Decorator; if (vendorDecorator != null) { StoreAndReplaceHook("VendorRun", new Decorator(TownRun.TownRunCanRun, new ActionRunCoroutine(ret => TownRun.TownRunCoroutineWrapper(vendorDecorator)))); } }
private static double RefreshKillRadius() { // Cancel altogether if it's not even in range, unless it's a boss or an injured treasure goblin double killRange = CurrentBotKillRange; // Bosses get extra radius if (c_unit_IsBoss) { if (c_ActorSNO != 80509) { // Kulle Exception killRange *= 1.5; } // And even more if they're already injured if (c_HitPointsPct <= 0.98) { killRange *= 4; } // And make sure we have a MINIMUM range for bosses - incase they are at screen edge etc + Kulle exception if (killRange <= 200 && c_ActorSNO != 80509) { killRange = 200; } } // Special short-range list to ignore weakling mobs if (PlayerKiteDistance <= 0 && !GetHasBuff(SNOPower.Wizard_Archon) && hashActorSNOShortRangeOnly.Contains(c_ActorSNO)) { killRange = 12; } // Prevent long-range mobs beign ignored while they may be pounding on us if (killRange <= 30 && hashActorSNORanged.Contains(c_ActorSNO)) { killRange = 120f; } // Injured treasure goblins get a huge extra radius - since they don't stay on the map long if injured, anyway! if (c_unit_IsTreasureGoblin && (c_CentreDistance <= 60 || c_HitPointsPct <= 0.99)) { c_ForceLeapAgainst = true; if (Settings.Combat.Misc.GoblinPriority <= GoblinPriority.Prioritize) { killRange *= 2.5; } else { killRange *= 4; } // Minimum distance of 60 if (killRange <= 60) { killRange = 60; } } // Elitey type mobs and things else if ((c_unit_IsElite || c_unit_IsRare || c_unit_IsUnique || c_unit_IsMinion)) { c_ForceLeapAgainst = true; // using new GUI slider for elite kill range killRange = Settings.Combat.Misc.EliteRange; // if we've damaged an elite and its still on screen, keep it if (c_HitPointsPct < 1 && killRange < 60) { killRange = 60; } } // Safety for TownRun and UseTownPortalTag if (TownRun.IsTryingToTownPortal()) { if (killRange <= 90) { killRange = 90; } } return(killRange); }
private static bool RefreshGilesUnit(bool AddToCache) { AddToCache = true; // See if this is a boss c_unit_IsBoss = hashBossSNO.Contains(c_ActorSNO); // hax for Diablo_shadowClone c_unit_IsAttackable = c_InternalName.StartsWith("Diablo_shadowClone"); // Prepare the fake object for target handler if (FakeObject == null) { FakeObject = c_diaUnit; } if (c_CommonData.ACDGuid == -1) { AddToCache = false; return(AddToCache); } // Dictionary based caching of monster types based on the SNO codes MonsterType monsterType; // See if we need to refresh the monster type or not bool bAddToDictionary = !dictionaryStoredMonsterTypes.TryGetValue(c_ActorSNO, out monsterType); bool bRefreshMonsterType = bAddToDictionary; using (new PerformanceLogger("RefreshUnit.5")) { // If it's a boss and it was an ally, keep refreshing until it's not an ally // Because some bosses START as allied for cutscenes etc. until they become hostile if (c_unit_IsBoss && !bRefreshMonsterType) { switch (monsterType) { case MonsterType.Ally: case MonsterType.Scenery: case MonsterType.Helper: case MonsterType.Team: bRefreshMonsterType = true; break; } } } using (new PerformanceLogger("RefreshUnit.6")) { // Now see if we do need to get new data for this boss or not if (bRefreshMonsterType) { try { monsterType = RefreshMonsterType(c_CommonData, monsterType, bAddToDictionary); } catch (Exception ex) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting monsterinfo and monstertype for unit {0} [{1}]", c_InternalName, c_ActorSNO); DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex); DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.CacheManagement, "ActorTypeAttempt={0}", c_diaUnit.ActorType); AddToCache = false; } } // Make sure it's a valid monster type switch (monsterType) { case MonsterType.Ally: case MonsterType.Scenery: case MonsterType.Helper: case MonsterType.Team: { AddToCache = false; c_IgnoreSubStep = "AllySceneryHelperTeam"; return(AddToCache); } //break; } } // Force return here for un-attackable allies if (!AddToCache) { return(AddToCache); } MonsterAffixes monsterAffixes; using (new PerformanceLogger("RefreshUnit.8")) { // Only set treasure goblins to true *IF* they haven't disabled goblins! Then check the SNO in the goblin hash list! c_unit_IsTreasureGoblin = false; // Flag this as a treasure goblin *OR* ignore this object altogether if treasure goblins are set to ignore if (hashActorSNOGoblins.Contains(c_ActorSNO)) { if (Settings.Combat.Misc.GoblinPriority != 0) { c_unit_IsTreasureGoblin = true; } else { AddToCache = false; c_IgnoreSubStep = "IgnoreTreasureGoblins"; return(AddToCache); } } // Pull up the Monster Affix cached data monsterAffixes = RefreshAffixes(c_CommonData); /* * * This should be moved to HandleTarget * */ if (PlayerStatus.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker, true)) { //WotB only used on Arcane, Frozen, Jailer, Molten and Electrified+Reflect Damage elites if (monsterAffixes.HasFlag(MonsterAffixes.ArcaneEnchanted) || monsterAffixes.HasFlag(MonsterAffixes.Frozen) || monsterAffixes.HasFlag(MonsterAffixes.Jailer) || monsterAffixes.HasFlag(MonsterAffixes.Molten) || (monsterAffixes.HasFlag(MonsterAffixes.Electrified) && monsterAffixes.HasFlag(MonsterAffixes.ReflectsDamage)) || //Bosses and uber elites c_unit_IsBoss || c_ActorSNO == 256015 || c_ActorSNO == 256000 || c_ActorSNO == 255996 || //...or more than 4 elite mobs in range (only elites/rares/uniques, not minions!) ElitesWithinRange[RANGE_50] > 4) { shouldUseBerserkerPower = true; } } else { shouldUseBerserkerPower = false; } // Is this something we should try to force leap/other movement abilities against? c_ForceLeapAgainst = false; } double killRange; killRange = RefreshKillRadius(); c_KillRange = killRange; if (monsterAffixes.HasFlag(MonsterAffixes.Shielding)) { c_unit_IsShielded = true; } // Only if at full health, else don't bother checking each loop // See if we already have this monster's size stored, if not get it and cache it if (!dictionaryStoredMonsterSizes.TryGetValue(c_ActorSNO, out c_unit_MonsterSize)) { try { RefreshMonsterSize(); } catch (Exception ex) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting monstersize info for unit {0} [{1}]", c_InternalName, c_ActorSNO); DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex); AddToCache = false; return(AddToCache); } } // Retrieve collision sphere radius, cached if possible if (!dictGilesCollisionSphereCache.TryGetValue(c_ActorSNO, out c_Radius)) { try { RefreshMonsterRadius(); } catch (Exception ex) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting collisionsphere radius for unit {0} [{1}]", c_InternalName, c_ActorSNO); DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex); AddToCache = false; return(AddToCache); } dictGilesCollisionSphereCache.Add(c_ActorSNO, c_Radius); } double dThisMaxHealth = RefreshMonsterHealth(); // And finally put the two together for a current health percentage c_HitPointsPct = c_HitPoints / dThisMaxHealth; // Unit is already dead if (c_HitPoints <= 0d && !c_unit_IsBoss) { AddToCache = false; c_IgnoreSubStep = "0HitPoints"; // return here immediately return(AddToCache); } AddToCache = RefreshUnitAttributes(AddToCache, c_diaUnit); if (!AddToCache) { return(AddToCache); } c_CurrentAnimation = c_diaUnit.CommonData.CurrentAnimation; // A "fake distance" to account for the large-object size of monsters c_RadiusDistance -= (float)c_Radius; if (c_RadiusDistance <= 1f) { c_RadiusDistance = 1f; } // Special flags to decide whether to target anything at all if (c_IsEliteRareUnique || c_unit_IsBoss) { AnyElitesPresent = true; } // Extended kill radius after last fighting, or when we want to force a town run if ((Settings.Combat.Misc.ExtendedTrashKill && iKeepKillRadiusExtendedFor > 0) || ForceVendorRunASAP || TownRun.IsTryingToTownPortal()) { if (c_RadiusDistance <= killRange && AddToCache) { AnyMobsInRange = true; } } else { if (c_RadiusDistance <= Settings.Combat.Misc.NonEliteRange && AddToCache) { AnyMobsInRange = true; } } if (c_unit_IsTreasureGoblin) { AnyTreasureGoblinsPresent = true; } // Units with very high priority (1900+) allow an extra 50% on the non-elite kill slider range if (!AnyMobsInRange && !AnyElitesPresent && !AnyTreasureGoblinsPresent && c_RadiusDistance <= (Settings.Combat.Misc.NonEliteRange * 1.5)) { int iExtraPriority; // Enable extended kill radius for specific unit-types if (hashActorSNORanged.Contains(c_ActorSNO)) { AnyMobsInRange = true; } if (!AnyMobsInRange && dictActorSNOPriority.TryGetValue(c_ActorSNO, out iExtraPriority)) { if (iExtraPriority >= 1900) { AnyMobsInRange = true; } } } return(AddToCache); }
/// <summary> /// Find fresh targets, start main BehaviorTree if needed, cast any buffs needed etc. /// </summary> /// <param name="ret"></param> /// <returns></returns> internal static bool CheckHasTarget(object ret) { using (new PerformanceLogger("Trinity.CheckHasTarget")) { // If we aren't in the game or a world is loading, don't do anything yet if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || ZetaDia.IsLoadingWorld) { return(false); } if (ZetaDia.Me.IsDead) { GoldInactivity.ResetCheckGold(); } else if (GoldInactivity.GoldInactive()) { BotMain.PauseWhile(GoldInactivity.GoldInactiveLeaveGame); return(false); } if (lastWorldId != PlayerStatus.WorldID) { ISearchAreaProvider mgp = Navigator.SearchGridProvider; } if (!HotbarRefreshTimer.IsRunning) { HotbarRefreshTimer.Start(); } if (!HasMappedPlayerAbilities || HotbarRefreshTimer.ElapsedMilliseconds > 10000 || ShouldRefreshHotbarAbilities) { // Update the cached player's cache ActorClass tempClass = ActorClass.Invalid; try { tempClass = PlayerStatus.ActorClass; } catch { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.GlobalHandler, "Safely handled exception trying to get character class."); } GilesPlayerCache.RefreshHotbar(); dictAbilityRepeatDelay = new Dictionary <SNOPower, int>(dictAbilityRepeatDefaults); if (ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass) && PlayerStatus.ActorClass == ActorClass.Wizard) { dictAbilityRepeatDelay[SNOPower.Wizard_FrostNova] = 25; dictAbilityRepeatDelay[SNOPower.Wizard_ExplosiveBlast] = 25; dictAbilityRepeatDelay[SNOPower.Wizard_DiamondSkin] = 100; dictAbilityRepeatDelay[SNOPower.Wizard_SlowTime] = 6000; dictAbilityRepeatDelay[SNOPower.Wizard_WaveOfForce] = 1500; dictAbilityRepeatDelay[SNOPower.Wizard_MirrorImage] = 1500; dictAbilityRepeatDelay[SNOPower.Wizard_Archon_ArcaneBlast] = 1500; dictAbilityRepeatDelay[SNOPower.Wizard_Teleport] = 2700; dictAbilityRepeatDelay[SNOPower.Wizard_Archon_SlowTime] = 1500; dictAbilityRepeatDelay[SNOPower.Wizard_Archon_Teleport] = 2700; } if (PlayerStatus.ActorClass == ActorClass.WitchDoctor && ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Witchdoctor_Passive_GraveInjustice)) { dictAbilityRepeatDelay[SNOPower.Witchdoctor_SoulHarvest] = 1000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_SpiritWalk] = 1000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_Horrify] = 1000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_Gargantuan] = 20000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_SummonZombieDog] = 20000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_GraspOfTheDead] = 500; dictAbilityRepeatDelay[SNOPower.Witchdoctor_SpiritBarrage] = 2000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_Locust_Swarm] = 2000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_Haunt] = 2000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_Hex] = 3000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_MassConfusion] = 15000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_FetishArmy] = 20000; dictAbilityRepeatDelay[SNOPower.Witchdoctor_BigBadVoodoo] = 20000; } if (PlayerStatus.ActorClass == ActorClass.Barbarian && ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Barbarian_Passive_BoonOfBulKathos)) { dictAbilityRepeatDelay[SNOPower.Barbarian_Earthquake] = 90500; dictAbilityRepeatDelay[SNOPower.Barbarian_CallOfTheAncients] = 90500; dictAbilityRepeatDelay[SNOPower.Barbarian_WrathOfTheBerserker] = 90500; } // Pick an appropriate health set etc. based on class switch (PlayerStatus.ActorClass) { case ActorClass.Barbarian: // What health % should we use a potion, or look for a globe PlayerEmergencyHealthPotionLimit = Settings.Combat.Barbarian.PotionLevel; PlayerEmergencyHealthGlobeLimit = Settings.Combat.Barbarian.HealthGlobeLevel; PlayerKiteDistance = Settings.Combat.Barbarian.KiteLimit; break; case ActorClass.Monk: // What health % should we use a potion, or look for a globe PlayerEmergencyHealthPotionLimit = Settings.Combat.Monk.PotionLevel; PlayerEmergencyHealthGlobeLimit = Settings.Combat.Monk.HealthGlobeLevel; // Monks never kite :) PlayerKiteDistance = 0; break; case ActorClass.Wizard: // What health % should we use a potion, or look for a globe PlayerEmergencyHealthPotionLimit = Settings.Combat.Wizard.PotionLevel; PlayerEmergencyHealthGlobeLimit = Settings.Combat.Wizard.HealthGlobeLevel; PlayerKiteDistance = Settings.Combat.Wizard.KiteLimit; break; case ActorClass.WitchDoctor: // What health % should we use a potion, or look for a globe PlayerEmergencyHealthPotionLimit = Settings.Combat.WitchDoctor.PotionLevel; PlayerEmergencyHealthGlobeLimit = Settings.Combat.WitchDoctor.HealthGlobeLevel; PlayerKiteDistance = Settings.Combat.WitchDoctor.KiteLimit; break; case ActorClass.DemonHunter: // What health % should we use a potion, or look for a globe PlayerEmergencyHealthPotionLimit = Settings.Combat.DemonHunter.PotionLevel; PlayerEmergencyHealthGlobeLimit = Settings.Combat.DemonHunter.HealthGlobeLevel; PlayerKiteDistance = Settings.Combat.DemonHunter.KiteLimit; break; } } // Clear target current and reset key variables used during the target-handling function //CurrentTarget = null; bDontMoveMeIAmDoingShit = false; TimesBlockedMoving = 0; IsAlreadyMoving = false; lastMovementCommand = DateTime.Today; IsWaitingForPower = false; IsWaitingAfterPower = false; IsWaitingForPotion = false; wasRootedLastTick = false; ClearBlacklists(); using (new PerformanceLogger("CheckHasTarget.RefreshCache")) { // Refresh Cache if needed bool CacheWasRefreshed = RefreshDiaObjectCache(); } // We have a target, start the target handler! if (CurrentTarget != null) { IsWholeNewTarget = true; bDontMoveMeIAmDoingShit = true; ShouldPickNewAbilities = true; return(true); } //Monk_MaintainTempestRush(); // Pop a potion when necessary if (PlayerStatus.CurrentHealthPct <= PlayerEmergencyHealthPotionLimit) { if (!PlayerStatus.IsIncapacitated && GilesUseTimer(SNOPower.DrinkHealthPotion)) { ACDItem thisBestPotion = ZetaDia.Me.Inventory.Backpack.Where(i => i.IsPotion).OrderByDescending(p => p.HitpointsGranted).ThenBy(p => p.ItemStackQuantity).FirstOrDefault(); if (thisBestPotion != null) { WaitWhileAnimating(4, true); ZetaDia.Me.Inventory.UseItem((thisBestPotion.DynamicId)); } dictAbilityLastUse[SNOPower.DrinkHealthPotion] = DateTime.Now; WaitWhileAnimating(3, true); } } sStatusText = "[Trinity] No more targets - DemonBuddy/profile management is now in control"; if (Settings.Advanced.DebugInStatusBar && bResetStatusText) { bResetStatusText = false; BotMain.StatusText = sStatusText; } // Nothing to do... do we have some maintenance we can do instead, like out of combat buffing? if (DateTime.Now.Subtract(lastMaintenanceCheck).TotalMilliseconds > 150) { lastMaintenanceCheck = DateTime.Now; // Out of combat buffing etc. but only if we don't want to return to town etc. ACDAnimationInfo myAnimationState = ZetaDia.Me.CommonData.AnimationInfo; if (!PlayerStatus.IsInTown && !IsReadyToTownRun && !ForceVendorRunASAP && myAnimationState != null && myAnimationState.State != AnimationState.Attacking && myAnimationState.State != AnimationState.Casting && myAnimationState.State != AnimationState.Channeling) { bDontSpamOutofCombat = false; powerBuff = AbilitySelector(false, true, false); if (powerBuff.SNOPower != SNOPower.None) { WaitWhileAnimating(4, true); DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Behavior, "Using OOC Buff: {0}", powerBuff.SNOPower.ToString()); if (powerBuff.WaitTicksBeforeUse > 0) { BotMain.PauseFor(new TimeSpan(0, 0, 0, 0, (int)powerBuff.WaitBeforeUseDelay)); } ZetaDia.Me.UsePower(powerBuff.SNOPower, powerBuff.TargetPosition, powerBuff.TargetDynamicWorldId, powerBuff.TargetRActorGUID); LastPowerUsed = powerBuff.SNOPower; dictAbilityLastUse[powerBuff.SNOPower] = DateTime.Now; if (powerBuff.WaitTicksAfterUse > 0) { BotMain.PauseFor(new TimeSpan(0, 0, 0, 0, (int)powerBuff.WaitAfterUseDelay)); } WaitWhileAnimating(3, true); } } else if (myAnimationState != null) { // Check if we are portalling to town, if so increase our kill radius temporarily switch (myAnimationState.Current) { case SNOAnim.barbarian_male_HTH_Recall_Channel_01: case SNOAnim.Barbarian_Female_HTH_Recall_Channel_01: case SNOAnim.Monk_Male_recall_channel: case SNOAnim.Monk_Female_recall_channel: case SNOAnim.WitchDoctor_Male_recall_channel: case SNOAnim.WitchDoctor_Female_recall_channel: case SNOAnim.Wizard_Male_HTH_recall_channel: case SNOAnim.Wizard_Female_HTH_recall_channel: case SNOAnim.Demonhunter_Male_HTH_recall_channel: case SNOAnim.Demonhunter_Female_HTH_recall_channel: iKeepKillRadiusExtendedFor = 20; timeKeepKillRadiusExtendedUntil = DateTime.Now.AddSeconds(iKeepKillRadiusExtendedFor); break; } } } CurrentTarget = null; if ((GilesTrinity.ForceVendorRunASAP || GilesTrinity.IsReadyToTownRun) && TownRun.TownRunTimerRunning()) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Waiting for town run timer", true); return(true); } // Ok let DemonBuddy do stuff this loop, since we're done for the moment //DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.GlobalHandler, sStatusText); return(false); } }
public static Composite CreateUseHoradricCache() { return (new Decorator(ret => Trinity.Settings.Loot.TownRun.OpenHoradricCaches && !BrainBehavior.IsVendoring && !Trinity.ForceVendorRunASAP && !TownRun.IsTryingToTownPortal() && DateTime.UtcNow.Subtract(LastCheckedForHoradricCache).TotalSeconds > 1, new Sequence( new Action(ret => LastCheckedForHoradricCache = DateTime.UtcNow), new Decorator(ret => HasHoradricCaches(), new Action(ret => OpenHoradricCache()) ) ) )); }
private static void Monk_MaintainTempestRush() { if (!Monk_TempestRushReady()) { return; } if (PlayerStatus.IsInTown || Zeta.CommonBot.Logic.BrainBehavior.IsVendoring) { return; } if (TownRun.IsTryingToTownPortal()) { return; } if (TimeSinceUse(SNOPower.Monk_TempestRush) > 150) { return; } bool shouldMaintain = false; bool nullTarget = CurrentTarget == null; if (!nullTarget) { // maintain for everything except items, doors, interactables... stuff we have to "click" on switch (CurrentTarget.Type) { case GObjectType.Unit: case GObjectType.Gold: case GObjectType.Avoidance: case GObjectType.Barricade: case GObjectType.Destructible: case GObjectType.Globe: { if (Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && (TargetUtil.AnyElitesInRange(40f) || CurrentTarget.IsBossOrEliteRareUnique)) { shouldMaintain = false; } else { shouldMaintain = true; } } break; } } else { shouldMaintain = true; } if (Settings.Combat.Monk.TROption != TempestRushOption.MovementOnly && GilesUseTimer(SNOPower.Monk_TempestRush) && shouldMaintain) { Vector3 target = LastTempestRushLocation; string locationSource = "LastLocation"; //if (CurrentTarget != null && GilesNavHelper.CanRayCast(PlayerStatus.CurrentPosition, CurrentTarget.Position)) //{ // locationSource = "Current Target Position"; // target = CurrentTarget.Position; //} if (target.Distance2D(ZetaDia.Me.Position) <= 1f) { // rrrix edit: we can't maintain here return; //locationSource = "ZigZag"; //target = FindZigZagTargetLocation(target, 23f); } if (target == Vector3.Zero) { return; } float DestinationDistance = target.Distance2D(ZetaDia.Me.Position); //target = MathEx.CalculatePointFrom(target, PlayerStatus.CurrentPosition, aimPointDistance); target = TargetUtil.FindTempestRushTarget(); if (DestinationDistance > 10f && NavHelper.CanRayCast(ZetaDia.Me.Position, target)) { Monk_TempestRushStatus(String.Format("Using Tempest Rush to maintain channeling, source={0}, V3={1} dist={2:0}", locationSource, target, DestinationDistance)); var usePowerResult = ZetaDia.Me.UsePower(SNOPower.Monk_TempestRush, target, CurrentWorldDynamicId, -1); if (usePowerResult) { dictAbilityLastUse[SNOPower.Monk_TempestRush] = DateTime.Now; } } } }
/// <summary> /// Determines whether or not to leave the game based on the gold inactivity timer /// </summary> /// <returns></returns> internal static bool GoldInactive() { if (!GilesTrinity.Settings.Advanced.GoldInactivityEnabled) { // timer isn't enabled so move along! ResetCheckGold(); return(false); } try { if (!ZetaDia.IsInGame) { ResetCheckGold(); //If not in game, reset the timer DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, "Not in game, gold inactivity reset", 0); return(false); } if (ZetaDia.IsLoadingWorld) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, "Loading world, gold inactivity reset", 0); return(false); } if ((DateTime.Now.Subtract(lastCheckBag).TotalSeconds < 5)) { return(false); } // sometimes bosses take a LONG time if (GilesTrinity.CurrentTarget != null && GilesTrinity.CurrentTarget.IsBoss) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, "Current target is boss, gold inactivity reset", 0); ResetCheckGold(); return(false); } if (TownRun.IsTryingToTownPortal()) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, "Trying to town portal, gold inactivity reset", 0); ResetCheckGold(); return(false); } // Don't go inactive on WaitTimer tags ProfileBehavior c = null; try { if (ProfileManager.CurrentProfileBehavior != null) { c = ProfileManager.CurrentProfileBehavior; } } catch { } if (c != null && c.GetType() == typeof(WaitTimerTag)) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, "Wait timer tag, gold inactivity reset", 0); ResetCheckGold(); return(false); } lastCheckBag = DateTime.Now; int currentcoin = GilesTrinity.PlayerStatus.Coinage; if (currentcoin != lastKnowCoin && currentcoin != 0) { lastRefreshCoin = DateTime.Now; lastKnowCoin = currentcoin; } int notpickupgoldsec = Convert.ToInt32(DateTime.Now.Subtract(lastRefreshCoin).TotalSeconds); if (notpickupgoldsec >= GilesTrinity.Settings.Advanced.GoldInactivityTimer) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Gold inactivity after {0}s. Sending abort.", notpickupgoldsec); lastRefreshCoin = DateTime.Now; lastKnowCoin = currentcoin; notpickupgoldsec = 0; return(true); } else if (notpickupgoldsec > 0) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, "Gold unchanged for {0}s", notpickupgoldsec); } } catch (Exception e) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, e.Message); } DbHelper.Log(TrinityLogLevel.Normal, LogCategory.GlobalHandler, "Gold inactivity error - no result", 0); return(false); }
/// <summary> /// This will replace the main BehaviorTree hooks for Combat, Vendoring, and Looting. /// </summary> internal static void ReplaceTreeHooks() { if (Trinity.IsPluginEnabled) { // This is the do-all-be-all god-head all encompasing piece of trinity StoreAndReplaceHook("Combat", new Decorator(Trinity.TargetCheck, Trinity.HandleTargetAction())); // We still want the main VendorRun logic, we're just going to take control of *when* this logic kicks in var vendorDecorator = TreeHooks.Instance.Hooks["VendorRun"][0] as Decorator; if (vendorDecorator != null) { StoreAndReplaceHook("VendorRun", new Decorator(TownRun.TownRunCanRun, TownRun.TownRunWrapper(vendorDecorator.Children[0]))); } // Loot tree is now empty and never runs (Loot is handled through combat) // This is for special out of combat handling like Horadric Cache Composite lootComposite = TreeHooks.Instance.Hooks["Loot"][0]; StoreAndReplaceHook("Loot", Composites.CreateLootBehavior(lootComposite)); if (_goldInactiveComposite == null) { _goldInactiveComposite = GoldInactivity.CreateGoldInactiveLeaveGame(); } Logger.Log("Inserting GoldInactivity into BotBehavior"); TreeHooks.Instance.InsertHook("BotBehavior", 0, _goldInactiveComposite); } else { ReplaceHookWithOriginal("Combat"); ReplaceHookWithOriginal("VendorRun"); ReplaceHookWithOriginal("Loot"); Logger.Log("Removing GoldInactivity from BotBehavior"); TreeHooks.Instance.RemoveHook("BotBehavior", _goldInactiveComposite); } }
private static void RefreshDiaGetWeights() { using (new PerformanceLogger("RefreshDiaObjectCache.Weighting")) { double MovementSpeed = PlayerMover.GetMovementSpeed(); // Store if we are ignoring all units this cycle or not bool bIgnoreAllUnits = !AnyElitesPresent && !AnyMobsInRange && ( ( !AnyTreasureGoblinsPresent && Settings.Combat.Misc.GoblinPriority >= GoblinPriority.Prioritize ) || Settings.Combat.Misc.GoblinPriority < GoblinPriority.Prioritize ) && PlayerStatus.CurrentHealthPct >= 0.85d; bool PrioritizeCloseRangeUnits = (ForceCloseRangeTarget || PlayerStatus.IsRooted || MovementSpeed < 1 || GilesObjectCache.Count(u => u.Type == GObjectType.Unit && u.RadiusDistance < 5f) >= 3); bool hasWrathOfTheBerserker = PlayerStatus.ActorClass == ActorClass.Barbarian && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker); int TrashMobCount = GilesObjectCache.Count(u => u.Type == GObjectType.Unit && u.IsTrashMob); int EliteCount = Settings.Combat.Misc.IgnoreElites ? 0 : GilesObjectCache.Count(u => u.Type == GObjectType.Unit && u.IsBossOrEliteRareUnique); int AvoidanceCount = Settings.Combat.Misc.AvoidAOE ? 0 : GilesObjectCache.Count(o => o.Type == GObjectType.Avoidance && o.CentreDistance <= 50f); bool profileTagCheck = false; if (ProfileManager.CurrentProfileBehavior != null) { Type behaviorType = ProfileManager.CurrentProfileBehavior.GetType(); if (behaviorType == typeof(WaitTimerTag) || behaviorType == typeof(UseTownPortalTag) || behaviorType == typeof(XmlTags.TrinityTownRun)) { profileTagCheck = true; } } bool ShouldIgnoreTrashMobs = (!TownRun.IsTryingToTownPortal() && !profileTagCheck && !PrioritizeCloseRangeUnits && Settings.Combat.Misc.TrashPackSize > 1 && EliteCount == 0 && AvoidanceCount == 0 && PlayerStatus.Level >= 15 && MovementSpeed >= 1 ); string unitWeightInfo = ""; foreach (GilesObject cacheObject in GilesObjectCache.OrderBy(c => c.CentreDistance)) { unitWeightInfo = ""; // Just to make sure each one starts at 0 weight... cacheObject.Weight = 0d; // Now do different calculations based on the object type switch (cacheObject.Type) { // Weight Units case GObjectType.Unit: { int nearbyMonsterCount = GilesObjectCache.Count(u => u.IsTrashMob && cacheObject.Position.Distance2D(u.Position) <= Settings.Combat.Misc.TrashPackClusterRadius); // Ignore Solitary Trash mobs (no elites present) // Except if has been primary target or if already low on health (<= 20%) if (ShouldIgnoreTrashMobs && cacheObject.IsTrashMob && !cacheObject.HasBeenPrimaryTarget && cacheObject.RadiusDistance >= 2f && !(nearbyMonsterCount >= Settings.Combat.Misc.TrashPackSize)) { unitWeightInfo = String.Format("Ignoring trash mob {0} {1} nearbyCount={2} packSize={3} packRadius={4:0} radiusDistance={5:0} ShouldIgnore={6} ms={7:0.00} Elites={8} Avoid={9} profileTagCheck={10} level={11} prioritize={12}", cacheObject.InternalName, cacheObject.RActorGuid, nearbyMonsterCount, Settings.Combat.Misc.TrashPackSize, Settings.Combat.Misc.TrashPackClusterRadius, cacheObject.RadiusDistance, ShouldIgnoreTrashMobs, MovementSpeed, EliteCount, AvoidanceCount, profileTagCheck, PlayerStatus.Level, PrioritizeCloseRangeUnits); break; } else { unitWeightInfo = String.Format("Adding trash mob {0} {1} nearbyCount={2} packSize={3} packRadius={4:0} radiusDistance={5:0} ShouldIgnore={6} ms={7:0.00} Elites={8} Avoid={9} profileTagCheck={10} level={11} prioritize={12}", cacheObject.InternalName, cacheObject.RActorGuid, nearbyMonsterCount, Settings.Combat.Misc.TrashPackSize, Settings.Combat.Misc.TrashPackClusterRadius, cacheObject.RadiusDistance, ShouldIgnoreTrashMobs, MovementSpeed, EliteCount, AvoidanceCount, profileTagCheck, PlayerStatus.Level, PrioritizeCloseRangeUnits); } // Ignore elite option, except if trying to town portal if (Settings.Combat.Misc.IgnoreElites && (cacheObject.IsEliteRareUnique) && !TownRun.IsTryingToTownPortal()) { break; } // No champions, no mobs nearby, no treasure goblins to prioritize, and not injured, so skip mobs if (bIgnoreAllUnits) { break; } // Monster is in cache but not within kill range if (cacheObject.RadiusDistance > cacheObject.KillRange) { break; } if (cacheObject.HitPoints <= 0) { break; } // Total up monsters at various ranges if (cacheObject.RadiusDistance <= 50f) { bool isElite = (cacheObject.IsEliteRareUnique || cacheObject.IsBoss); bool isRended = cacheObject.HasDotDPS; // Flag up any bosses in range if (cacheObject.IsBoss) { anyBossesInRange = true; } if (cacheObject.RadiusDistance <= 6f) { AnythingWithinRange[RANGE_6]++; if (isElite) { ElitesWithinRange[RANGE_6]++; } } if (cacheObject.RadiusDistance <= 9f && !isRended) { NonRendedTargets_9++; } if (cacheObject.RadiusDistance <= 12f) { AnythingWithinRange[RANGE_12]++; if (isElite) { ElitesWithinRange[RANGE_12]++; } } if (cacheObject.RadiusDistance <= 15f) { AnythingWithinRange[RANGE_15]++; if (isElite) { ElitesWithinRange[RANGE_15]++; } } if (cacheObject.RadiusDistance <= 20f) { AnythingWithinRange[RANGE_20]++; if (isElite) { ElitesWithinRange[RANGE_20]++; } } if (cacheObject.RadiusDistance <= 25f) { if (!bAnyNonWWIgnoreMobsInRange && !hashActorSNOWhirlwindIgnore.Contains(cacheObject.ActorSNO)) { bAnyNonWWIgnoreMobsInRange = true; } AnythingWithinRange[RANGE_25]++; if (isElite) { ElitesWithinRange[RANGE_25]++; } } if (cacheObject.RadiusDistance <= 30f) { AnythingWithinRange[RANGE_30]++; if (isElite) { ElitesWithinRange[RANGE_30]++; } } if (cacheObject.RadiusDistance <= 40f) { AnythingWithinRange[RANGE_40]++; if (isElite) { ElitesWithinRange[RANGE_40]++; } } if (cacheObject.RadiusDistance <= 50f) { AnythingWithinRange[RANGE_50]++; if (isElite) { ElitesWithinRange[RANGE_50]++; } } } // Force a close range target because we seem to be stuck *OR* if not ranged and currently rooted if (PrioritizeCloseRangeUnits) { cacheObject.Weight = (50 - cacheObject.RadiusDistance) / 50 * 20000d; // Goblin priority KAMIKAZEEEEEEEE if (cacheObject.IsTreasureGoblin && Settings.Combat.Misc.GoblinPriority == GoblinPriority.Kamikaze) { cacheObject.Weight += 25000; } } else { // Not attackable, could be shielded, make super low priority if (cacheObject.IsShielded) { // Only 500 weight helps prevent it being prioritized over an unshielded cacheObject.Weight = 500; } // Not forcing close-ranged targets from being stuck, so let's calculate a weight! else { // Elites/Bosses that are killed should have weight erased so we don't keep attacking if ((cacheObject.IsEliteRareUnique || cacheObject.IsBoss) && cacheObject.HitPointsPct <= 0) { cacheObject.Weight = 0; break; } // Starting weight of 5000 if (cacheObject.IsTrashMob) { cacheObject.Weight = (CurrentBotKillRange - cacheObject.RadiusDistance) / CurrentBotKillRange * 5000; } // Starting weight of 8000 for elites if (cacheObject.IsBossOrEliteRareUnique) { cacheObject.Weight = (90f - cacheObject.RadiusDistance) / 90f * 8000; } // Give extra weight to ranged enemies if ((PlayerStatus.ActorClass == ActorClass.Barbarian || PlayerStatus.ActorClass == ActorClass.Monk) && (cacheObject.MonsterStyle == MonsterSize.Ranged || hashActorSNORanged.Contains(c_ActorSNO))) { cacheObject.Weight += 1100; cacheObject.ForceLeapAgainst = true; } // Lower health gives higher weight - health is worth up to 1000ish extra weight if (cacheObject.IsTrashMob && cacheObject.HitPointsPct < 0.20) { cacheObject.Weight += (100 - cacheObject.HitPointsPct) / 100 * 1000; } // Elites on low health get extra priority - up to 2500ish if (cacheObject.IsBossOrEliteRareUnique && cacheObject.HitPointsPct < 0.20) { cacheObject.Weight += (100 - cacheObject.HitPointsPct) / 100 * 2500; } // Goblins on low health get extra priority - up to 4000ish if (Settings.Combat.Misc.GoblinPriority >= GoblinPriority.Prioritize && cacheObject.IsTreasureGoblin && cacheObject.HitPointsPct <= 0.98) { cacheObject.Weight += (100 - cacheObject.HitPointsPct) / 100 * 4000; } // Bonuses to priority type monsters from the dictionary/hashlist set at the top of the code int iExtraPriority; if (dictActorSNOPriority.TryGetValue(cacheObject.ActorSNO, out iExtraPriority)) { cacheObject.Weight += iExtraPriority; } // Close range get higher weights the more of them there are, to prevent body-blocking if (cacheObject.RadiusDistance <= 5f) { cacheObject.Weight += (2000 * cacheObject.Radius); } // Special additional weight for corrupt growths in act 4 ONLY if they are at close range (not a standard priority thing) if ((cacheObject.ActorSNO == 210120 || cacheObject.ActorSNO == 210268) && cacheObject.CentreDistance <= 25f) { cacheObject.Weight += 2000; } // Was already a target and is still viable, give it some free extra weight, to help stop flip-flopping between two targets if (cacheObject.RActorGuid == CurrentTargetRactorGUID && cacheObject.CentreDistance <= 25f) { cacheObject.Weight += 1000; } // Prevent going less than 300 yet to prevent annoyances (should only lose this much weight from priority reductions in priority list?) if (cacheObject.Weight < 300) { cacheObject.Weight = 300; } // If any AoE between us and target, do not attack, for non-ranged attacks only if (!Settings.Combat.Misc.KillMonstersInAoE && PlayerKiteDistance <= 0 && hashAvoidanceObstacleCache.Any(o => MathUtil.IntersectsPath(o.Location, o.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight = 1; } // See if there's any AOE avoidance in that spot, if so reduce the weight to 1, for non-ranged attacks only if (!Settings.Combat.Misc.KillMonstersInAoE && PlayerKiteDistance <= 0 && hashAvoidanceObstacleCache.Any(aoe => cacheObject.Position.Distance2D(aoe.Location) <= aoe.Radius)) { cacheObject.Weight = 1; } if (PlayerKiteDistance > 0) { if (GilesObjectCache.Any(m => m.Type == GObjectType.Unit && MathUtil.IntersectsPath(cacheObject.Position, cacheObject.Radius, PlayerStatus.CurrentPosition, m.Position) && m.RActorGuid != cacheObject.RActorGuid)) { cacheObject.Weight = 0; } } // Deal with treasure goblins - note, of priority is set to "0", then the is-a-goblin flag isn't even set for use here - the monster is ignored if (cacheObject.IsTreasureGoblin && !GilesObjectCache.Any(u => (u.Type == GObjectType.Door || u.Type == GObjectType.Barricade) && u.RadiusDistance <= 40f)) { // Logging goblin sightings if (lastGoblinTime == DateTime.Today) { iTotalNumberGoblins++; lastGoblinTime = DateTime.Now; DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Goblin #{0} in sight. Distance={1:0}", iTotalNumberGoblins, cacheObject.CentreDistance); } else { if (DateTime.Now.Subtract(lastGoblinTime).TotalMilliseconds > 30000) { lastGoblinTime = DateTime.Today; } } if (hashAvoidanceObstacleCache.Any(aoe => cacheObject.Position.Distance2D(aoe.Location) <= aoe.Radius) && Settings.Combat.Misc.GoblinPriority != GoblinPriority.Kamikaze) { cacheObject.Weight = 1; break; } // Original Trinity stuff for priority handling now switch (Settings.Combat.Misc.GoblinPriority) { case GoblinPriority.Normal: // Treating goblins as "normal monsters". Ok so I lied a little in the config, they get a little extra weight really! ;) cacheObject.Weight += 751; break; case GoblinPriority.Prioritize: // Super-high priority option below... cacheObject.Weight += 20000; break; case GoblinPriority.Kamikaze: // KAMIKAZE SUICIDAL TREASURE GOBLIN RAPE AHOY! cacheObject.Weight += 40000; break; } } } // Forcing close range target or not? } // This is an attackable unit break; } case GObjectType.Item: case GObjectType.Gold: { // Weight Items // We'll weight them based on distance, giving gold less weight and close objects more //if (cacheObject.GoldAmount > 0) // cacheObject.Weight = 5000d - (Math.Floor(cacheObject.CentreDistance) * 2000d); //else // cacheObject.Weight = 8000d - (Math.Floor(cacheObject.CentreDistance) * 1900d); if (cacheObject.GoldAmount > 0) { cacheObject.Weight = (300 - cacheObject.CentreDistance) / 300 * 9000d; } else { cacheObject.Weight = (300 - cacheObject.CentreDistance) / 300 * 9000d; } // Point-blank items get a weight increase if (cacheObject.GoldAmount <= 0 && cacheObject.CentreDistance <= 12f) { cacheObject.Weight += 1000d; } // Was already a target and is still viable, give it some free extra weight, to help stop flip-flopping between two targets if (cacheObject.RActorGuid == CurrentTargetRactorGUID) { cacheObject.Weight += 800; } // Give yellows more weight if (cacheObject.GoldAmount <= 0 && cacheObject.ItemQuality >= ItemQuality.Rare4) { cacheObject.Weight += 4000d; } // Give legendaries more weight if (cacheObject.GoldAmount <= 0 && cacheObject.ItemQuality >= ItemQuality.Legendary) { cacheObject.Weight += 15000d; } // Are we prioritizing close-range stuff atm? If so limit it at a value 3k lower than monster close-range priority //if (PrioritizeCloseRangeUnits) // cacheObject.Weight = (200f - cacheObject.CentreDistance) / 200f * 18000d; if (PlayerStatus.ActorClass == ActorClass.Monk && TimeSinceUse(SNOPower.Monk_TempestRush) < 1000 && cacheObject.ItemQuality < ItemQuality.Legendary) { cacheObject.Weight = 500; } // If there's a monster in the path-line to the item, reduce the weight to 1, except legendaries if (cacheObject.ItemQuality < ItemQuality.Legendary && hashMonsterObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius * 1.2f, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight = 1; } // ignore any items/gold if there is mobs in kill radius and we aren't combat looting if (CurrentTarget != null && AnyMobsInRange && !Zeta.CommonBot.Settings.CharacterSettings.Instance.CombatLooting && cacheObject.ItemQuality < ItemQuality.Legendary) { cacheObject.Weight = 1; } // See if there's any AOE avoidance in that spot or inbetween us, if so reduce the weight to 1 if (hashAvoidanceObstacleCache.Any(aoe => cacheObject.Position.Distance2D(aoe.Location) <= aoe.Radius)) { cacheObject.Weight = 1; } // ignore non-legendaries and gold near elites if we're ignoring elites // not sure how we should safely determine this distance if (Settings.Combat.Misc.IgnoreElites && cacheObject.ItemQuality < ItemQuality.Legendary && GilesObjectCache.Any(u => u.Type == GObjectType.Unit && u.IsEliteRareUnique && u.Position.Distance2D(cacheObject.Position) <= 40f)) { cacheObject.Weight = 0; } break; } case GObjectType.Globe: { // Weight Health Globes // Give all globes 0 weight (so never gone-to), unless we have low health, then go for them if (PlayerStatus.CurrentHealthPct > PlayerEmergencyHealthGlobeLimit || !Settings.Combat.Misc.CollectHealthGlobe) { cacheObject.Weight = 0; } else { // Ok we have globes enabled, and our health is low...! cacheObject.Weight = (300f - cacheObject.RadiusDistance) / 300f * 17000d; // Point-blank items get a weight increase if (cacheObject.CentreDistance <= 15f) { cacheObject.Weight += 3000d; } // Close items get a weight increase if (cacheObject.CentreDistance <= 60f) { cacheObject.Weight += 1500d; } // Was already a target and is still viable, give it some free extra weight, to help stop flip-flopping between two targets if (cacheObject.RActorGuid == CurrentTargetRactorGUID && cacheObject.CentreDistance <= 25f) { cacheObject.Weight += 800; } // Are we prioritizing close-range stuff atm? If so limit it at a value 3k lower than monster close-range priority //if (bPrioritizeCloseRange) // thisgilesobject.dThisWeight = 22000 - (Math.Floor(thisgilesobject.fCentreDistance) * 200); // If there's a monster in the path-line to the item, reduce the weight by 15% for each Vector3 point = cacheObject.Position; foreach (GilesObstacle tempobstacle in hashMonsterObstacleCache.Where(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, point))) { cacheObject.Weight *= 0.85; } // See if there's any AOE avoidance in that spot, if so reduce the weight by 10% if (hashAvoidanceObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight *= 0.9; } // Calculate a spot reaching a little bit further out from the globe, to help globe-movements if (cacheObject.Weight > 0) { cacheObject.Position = MathEx.CalculatePointFrom(cacheObject.Position, PlayerStatus.CurrentPosition, cacheObject.CentreDistance + 3f); } // do not collect health globes if we are kiting and health globe is too close to monster or avoidance if (PlayerKiteDistance > 0) { if (hashMonsterObstacleCache.Any(m => m.Location.Distance(cacheObject.Position) < PlayerKiteDistance)) { cacheObject.Weight = 0; } if (hashAvoidanceObstacleCache.Any(m => m.Location.Distance(cacheObject.Position) < PlayerKiteDistance)) { cacheObject.Weight = 0; } } } break; } case GObjectType.HealthWell: { // Healths Wells get handled correctly ... if (cacheObject.Type == GObjectType.HealthWell && PlayerStatus.CurrentHealthPct <= .75) { cacheObject.Weight += 7500; } if (cacheObject.Type == GObjectType.HealthWell && PlayerStatus.CurrentHealthPct <= .25) { cacheObject.Weight += 20000d; } break; } case GObjectType.Shrine: { // Weight Shrines cacheObject.Weight = (75f - cacheObject.RadiusDistance) / 75f * 14500f; // Very close shrines get a weight increase if (cacheObject.CentreDistance <= 30f) { cacheObject.Weight += 10000d; } if (cacheObject.Weight > 0) { // Was already a target and is still viable, give it some free extra weight, to help stop flip-flopping between two targets if (cacheObject.RActorGuid == CurrentTargetRactorGUID && cacheObject.CentreDistance <= 25f) { cacheObject.Weight += 400; } // If there's a monster in the path-line to the item if (hashMonsterObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight = 1; } // See if there's any AOE avoidance in that spot, if so reduce the weight to 1 if (hashAvoidanceObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight = 1; } // if there's any monsters nearby if (TargetUtil.AnyMobsInRange(15f)) { cacheObject.Weight = 1; } if (PrioritizeCloseRangeUnits) { cacheObject.Weight = 1; } } break; } case GObjectType.Door: { if (!GilesObjectCache.Any(u => u.Type == GObjectType.Unit && u.HitPointsPct > 0 && MathUtil.IntersectsPath(u.Position, u.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { if (cacheObject.RadiusDistance <= 20f) { cacheObject.Weight += 15000d; } // We're standing on the damn thing... open it!! if (cacheObject.RadiusDistance <= 12f) { cacheObject.Weight += 30000d; } } break; } case GObjectType.Destructible: case GObjectType.Barricade: { // rrrix added this as a single "weight" source based on the DestructableRange. // Calculate the weight based on distance, where a distance = 1 is 5000, 90 = 0 cacheObject.Weight = (90f - cacheObject.RadiusDistance) / 90f * 5000f; // Was already a target and is still viable, give it some free extra weight, to help stop flip-flopping between two targets if (cacheObject.RActorGuid == CurrentTargetRactorGUID && cacheObject.CentreDistance <= 25f) { cacheObject.Weight += 400; } //// Close destructibles get a weight increase //if (cacheObject.CentreDistance <= 16f) // cacheObject.Weight += 1500d; // If there's a monster in the path-line to the item, reduce the weight by 50% if (hashMonsterObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight *= 0.5; } // See if there's any AOE avoidance in that spot, if so reduce the weight to 1 if (hashAvoidanceObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight = 1; } // Are we prioritizing close-range stuff atm? If so limit it at a value 3k lower than monster close-range priority if (PrioritizeCloseRangeUnits) { cacheObject.Weight = (200d - cacheObject.CentreDistance) / 200d * 19200d; } //// We're standing on the damn thing... break it if (cacheObject.RadiusDistance <= 5f) { cacheObject.Weight += 40000d; } //// Fix for WhimsyShire Pinata if (hashSNOContainerResplendant.Contains(cacheObject.ActorSNO)) { cacheObject.Weight = 100 + cacheObject.RadiusDistance; } break; } case GObjectType.Interactable: { // Weight Interactable Specials // Very close interactables get a weight increase cacheObject.Weight = (90d - cacheObject.CentreDistance) / 90d * 15000d; if (cacheObject.CentreDistance <= 12f) { cacheObject.Weight += 1000d; } // Was already a target and is still viable, give it some free extra weight, to help stop flip-flopping between two targets if (cacheObject.RActorGuid == CurrentTargetRactorGUID && cacheObject.CentreDistance <= 25f) { cacheObject.Weight += 400; } // If there's a monster in the path-line to the item, reduce the weight by 50% if (hashMonsterObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight *= 0.5; } // See if there's any AOE avoidance in that spot, if so reduce the weight to 1 if (hashAvoidanceObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight = 1; } //if (bAnyMobsInCloseRange || (CurrentTarget != null && CurrentTarget.IsBossOrEliteRareUnique)) // cacheObject.Weight = 1; break; } case GObjectType.Container: { // Weight Containers // Very close containers get a weight increase cacheObject.Weight = (190d - cacheObject.CentreDistance) / 190d * 11000d; if (cacheObject.CentreDistance <= 12f) { cacheObject.Weight += 600d; } // Was already a target and is still viable, give it some free extra weight, to help stop flip-flopping between two targets if (cacheObject.RActorGuid == CurrentTargetRactorGUID && cacheObject.CentreDistance <= 25f) { cacheObject.Weight += 400; } // If there's a monster in the path-line to the item, reduce the weight by 50% if (hashMonsterObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight *= 0.5; } // See if there's any AOE avoidance in that spot, if so reduce the weight to 1 if (hashAvoidanceObstacleCache.Any(cp => MathUtil.IntersectsPath(cp.Location, cp.Radius, PlayerStatus.CurrentPosition, cacheObject.Position))) { cacheObject.Weight = 1; } break; } } // Switch on object type // Force the character to stay where it is if there is nothing available that is out of avoidance stuff and we aren't already in avoidance stuff if (cacheObject.Weight == 1 && !StandingInAvoidance && GilesObjectCache.Any(o => o.Type == GObjectType.Avoidance)) { cacheObject.Weight = 0; ShouldStayPutDuringAvoidance = true; } DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Weight, "Weight={2:0} target= {0} ({1}) type={3} R-Dist={4:0} IsElite={5} RAGuid={6} {7}", cacheObject.InternalName, cacheObject.ActorSNO, cacheObject.Weight, cacheObject.Type, cacheObject.RadiusDistance, cacheObject.IsElite, cacheObject.RActorGuid, unitWeightInfo); // Prevent current target dynamic ranged weighting flip-flop if (CurrentTargetRactorGUID == cacheObject.RActorGuid && cacheObject.Weight <= 1) { cacheObject.Weight = 100; } // Is the weight of this one higher than the current-highest weight? Then make this the new primary target! if (cacheObject.Weight > w_HighestWeightFound && cacheObject.Weight > 0) { // Clone the current Giles-cache object CurrentTarget = cacheObject.Clone(); w_HighestWeightFound = cacheObject.Weight; // See if we can try attempting kiting later NeedToKite = false; vKitePointAvoid = vNullLocation; // Kiting and Avoidance if (CurrentTarget.Type == GObjectType.Unit) { var AvoidanceList = hashAvoidanceObstacleCache.Where(o => // Distance from avoidance to target is less than avoidance radius o.Location.Distance(CurrentTarget.Position) <= (GetAvoidanceRadius(o.ActorSNO) * 1.2) && // Distance from obstacle to me is <= cacheObject.RadiusDistance o.Location.Distance(PlayerStatus.CurrentPosition) <= (cacheObject.RadiusDistance - 4f) ); // if there's any obstacle within a specified distance of the avoidance radius *1.2 if (AvoidanceList.Any()) { foreach (GilesObstacle o in AvoidanceList) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Targetting, "Avoidance: Id={0} Weight={1} Loc={2} Radius={3} Name={4}", o.ActorSNO, o.Weight, o.Location, o.Radius, o.Name); } vKitePointAvoid = CurrentTarget.Position; NeedToKite = true; } } } } // Loop through all the objects and give them a weight if (CurrentTarget != null && CurrentTarget.InternalName != null && CurrentTarget.ActorSNO > 0 && CurrentTarget.RActorGuid != CurrentTargetRactorGUID) { RecordTargetHistory(); DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Targetting, "Target changed to name={2} sno={0} type={1} raGuid={3}", CurrentTarget.InternalName, CurrentTarget.ActorSNO, CurrentTarget.Type, CurrentTarget.RActorGuid); } } }