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); } } }
private static TrinityPower GetBarbarianPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower) { // Pick the best destructible power available if (UseDestructiblePower) { return(GetBarbarianDestroyPower()); } // Barbarians need 56 reserve for special spam like WW MinEnergyReserve = 56; // Ignore Pain when low on health if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_IgnorePain) && PlayerStatus.CurrentHealthPct <= 0.45 && GilesUseTimer(SNOPower.Barbarian_IgnorePain, true) && PowerManager.CanCast(SNOPower.Barbarian_IgnorePain)) { return(new TrinityPower(SNOPower.Barbarian_IgnorePain, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } IsWaitingForSpecial = false; if (PlayerStatus.PrimaryResource < MinEnergyReserve) { if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Earthquake) && ElitesWithinRange[RANGE_25] >= 1 && GilesUseTimer(SNOPower.Barbarian_Earthquake) && !GetHasBuff(SNOPower.Barbarian_Earthquake)) { DbHelper.LogNormal("Waiting for Barbarian_Earthquake 1!"); IsWaitingForSpecial = true; } // Earthquake, elites close-range only if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Earthquake) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] > 0 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 13f)) && GilesUseTimer(SNOPower.Barbarian_Earthquake, true) && !GetHasBuff(SNOPower.Barbarian_Earthquake) && PowerManager.CanCast(SNOPower.Barbarian_Earthquake)) { if (PlayerStatus.PrimaryResource >= 50) { return(new TrinityPower(SNOPower.Barbarian_Earthquake, 13f, vNullLocation, CurrentWorldDynamicId, -1, 4, 4, WAIT_FOR_ANIM)); } DbHelper.LogNormal("Waiting for Barbarian_Earthquake 2!"); IsWaitingForSpecial = true; } if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && ElitesWithinRange[RANGE_25] >= 1 && GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) && !GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) { DbHelper.LogNormal("Waiting for Barbarian_WrathOfTheBerserker 1!"); IsWaitingForSpecial = true; } // Berserker special for ignore elites if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && Settings.Combat.Misc.IgnoreElites && (TargetUtil.AnyMobsInRange(25, 3) || TargetUtil.AnyMobsInRange(50, 10) || TargetUtil.AnyMobsInRange(Settings.Combat.Misc.TrashPackClusterRadius, Settings.Combat.Misc.TrashPackSize)) && GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) && !GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) { DbHelper.LogNormal("Waiting for Barbarian_WrathOfTheBerserker 2!"); IsWaitingForSpecial = true; } if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_CallOfTheAncients) && ElitesWithinRange[RANGE_25] >= 1 && GilesUseTimer(SNOPower.Barbarian_CallOfTheAncients) && !GetHasBuff(SNOPower.Barbarian_CallOfTheAncients)) { DbHelper.LogNormal("Waiting for Barbarian_CallOfTheAncients!"); IsWaitingForSpecial = true; } } // Wrath of the berserker, elites only (wrath of berserker) if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && // If using WOTB on all elites, or if we should only use on "hard" affixes (!Settings.Combat.Barbarian.WOTBHardOnly || (shouldUseBerserkerPower && Settings.Combat.Barbarian.WOTBHardOnly)) && // Not on heart of sin after Cydaea CurrentTarget.ActorSNO != 193077 && // Make sure we are allowed to use wrath on goblins, else make sure this isn't a goblin ( (!Settings.Combat.Barbarian.UseWOTBGoblin || (Settings.Combat.Barbarian.UseWOTBGoblin && CurrentTarget.IsTreasureGoblin)) || // If ignoring elites completely, trigger on 3 trash within 25 yards, or 10 trash in 50 yards (Settings.Combat.Misc.IgnoreElites && (TargetUtil.AnyMobsInRange(25, 3) || TargetUtil.AnyMobsInRange(50, 10)) || !Settings.Combat.Misc.IgnoreElites) || // Otherwise use when Elite target is in 20 yards (TargetUtil.AnyElitesInRange(20, 1) || TargetUtil.IsEliteTargetInRange(20f)) || // Or if our health is low PlayerStatus.CurrentHealthPct <= 60 ) && // Don't still have the buff !GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker) && PowerManager.CanCast(SNOPower.Barbarian_WrathOfTheBerserker)) { if (PlayerStatus.PrimaryResource >= 50) { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Barbarian_WrathOfTheBerserker being used!({0})", CurrentTarget.InternalName); shouldUseBerserkerPower = false; IsWaitingForSpecial = false; return(new TrinityPower(SNOPower.Barbarian_WrathOfTheBerserker, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } else { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Barbarian_WrathOfTheBerserker ready, waiting for fury..."); IsWaitingForSpecial = true; } } // Call of the ancients, elites only if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_CallOfTheAncients) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_25] > 0 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f)) && GilesUseTimer(SNOPower.Barbarian_CallOfTheAncients, true) && PowerManager.CanCast(SNOPower.Barbarian_CallOfTheAncients)) { if (PlayerStatus.PrimaryResource >= 50) { IsWaitingForSpecial = false; return(new TrinityPower(SNOPower.Barbarian_CallOfTheAncients, 0f, vNullLocation, CurrentWorldDynamicId, -1, 4, 4, WAIT_FOR_ANIM)); } else { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Call of the Ancients ready, waiting for fury..."); IsWaitingForSpecial = true; } } // Battle rage, for if being followed and before we do sprint if (UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_BattleRage) && (GilesUseTimer(SNOPower.Barbarian_BattleRage) || !GetHasBuff(SNOPower.Barbarian_BattleRage)) && PlayerStatus.PrimaryResource >= 20 && PowerManager.CanCast(SNOPower.Barbarian_BattleRage)) { return(new TrinityPower(SNOPower.Barbarian_BattleRage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Special segment for sprint as an out-of-combat only if (UseOOCBuff && !bDontSpamOutofCombat && (Settings.Combat.Misc.AllowOOCMovement || GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Sprint) && !GetHasBuff(SNOPower.Barbarian_Sprint) && PlayerStatus.PrimaryResource >= 20 && GilesUseTimer(SNOPower.Barbarian_Sprint) && PowerManager.CanCast(SNOPower.Barbarian_Sprint)) { return(new TrinityPower(SNOPower.Barbarian_Sprint, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // War cry, constantly maintain if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_WarCry) && (PlayerStatus.PrimaryResource <= 60 || !GetHasBuff(SNOPower.Barbarian_WarCry)) && GilesUseTimer(SNOPower.Barbarian_WarCry, true) && (!GetHasBuff(SNOPower.Barbarian_WarCry) || PowerManager.CanCast(SNOPower.Barbarian_WarCry))) { return(new TrinityPower(SNOPower.Barbarian_WarCry, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Threatening shout if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_ThreateningShout) && !PlayerStatus.IsIncapacitated && ((TargetUtil.AnyMobsInRange(25, Settings.Combat.Barbarian.MinThreatShoutMobCount)) || TargetUtil.IsEliteTargetInRange(25f)) && ( PlayerStatus.CurrentHealthPct <= 0.75 || (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && PlayerStatus.PrimaryResource <= 10) || (IsWaitingForSpecial && PlayerStatus.PrimaryResource <= MinEnergyReserve) ) && GilesUseTimer(SNOPower.Barbarian_ThreateningShout, true) && PowerManager.CanCast(SNOPower.Barbarian_ThreateningShout)) { return(new TrinityPower(SNOPower.Barbarian_ThreateningShout, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Threatening shout out-of-combat if (UseOOCBuff && Settings.Combat.Barbarian.ThreatShoutOOC && Hotbar.Contains(SNOPower.Barbarian_ThreateningShout) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource < 25 && GilesUseTimer(SNOPower.Barbarian_ThreateningShout, true) && PowerManager.CanCast(SNOPower.Barbarian_ThreateningShout)) { return(new TrinityPower(SNOPower.Barbarian_ThreateningShout, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Ground Stomp if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_GroundStomp) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 4 || PlayerStatus.CurrentHealthPct <= 0.7) && GilesUseTimer(SNOPower.Barbarian_GroundStomp, true) && PowerManager.CanCast(SNOPower.Barbarian_GroundStomp)) { return(new TrinityPower(SNOPower.Barbarian_GroundStomp, 16f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Revenge used off-cooldown if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_Revenge) && !PlayerStatus.IsIncapacitated && // Don't use revenge on goblins, too slow! (!CurrentTarget.IsTreasureGoblin || AnythingWithinRange[RANGE_12] >= 5) && // Doesn't need CURRENT target to be in range, just needs ANYTHING to be within 9 foot, since it's an AOE! (AnythingWithinRange[RANGE_6] > 0 || CurrentTarget.RadiusDistance <= 6f) && GilesUseTimer(SNOPower.Barbarian_Revenge) && PowerManager.CanCast(SNOPower.Barbarian_Revenge)) { // Note - we have LONGER animation times for whirlwind-users // Since whirlwind seems to interrupt rend so easily int iPreDelay = 0; int iPostDelay = 0; if (Hotbar.Contains(SNOPower.Barbarian_Whirlwind)) { if (LastPowerUsed == SNOPower.Barbarian_Whirlwind) { iPreDelay = 3; iPostDelay = 3; } } return(new TrinityPower(SNOPower.Barbarian_Revenge, 0f, PlayerStatus.CurrentPosition, CurrentWorldDynamicId, -1, iPreDelay, iPostDelay, WAIT_FOR_ANIM)); } // Furious charge if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_FuriousCharge) && (ElitesWithinRange[RANGE_12] > 3 && GilesUseTimer(SNOPower.Barbarian_FuriousCharge) && PowerManager.CanCast(SNOPower.Barbarian_FuriousCharge))) { float fExtraDistance; if (CurrentTarget.CentreDistance <= 25) { fExtraDistance = 30; } else { fExtraDistance = (25 - CurrentTarget.CentreDistance); } if (fExtraDistance < 5f) { fExtraDistance = 5f; } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance + fExtraDistance); return(new TrinityPower(SNOPower.Barbarian_FuriousCharge, 32f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Leap used when off-cooldown, or when out-of-range if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_Leap) && !PlayerStatus.IsIncapacitated && (AnythingWithinRange[RANGE_20] > 1 || ElitesWithinRange[RANGE_20] > 0) && GilesUseTimer(SNOPower.Barbarian_Leap, true) && PowerManager.CanCast(SNOPower.Barbarian_Leap)) { // For close-by monsters, try to leap a little further than their centre-point float fExtraDistance = CurrentTarget.Radius; if (fExtraDistance <= 4f) { fExtraDistance = 4f; } if (CurrentTarget.CentreDistance + fExtraDistance > 35f) { fExtraDistance = 35 - CurrentTarget.CentreDistance; } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance + fExtraDistance); return(new TrinityPower(SNOPower.Barbarian_Leap, 35f, vNewTarget, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // Rend spam for Non-WhirlWind users if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Rend) && TargetUtil.AnyMobsInRange(9) && !CurrentTarget.IsTreasureGoblin && ((!IsWaitingForSpecial && PlayerStatus.PrimaryResource >= 20) || (IsWaitingForSpecial && PlayerStatus.PrimaryResource > MinEnergyReserve)) && (GilesUseTimer(SNOPower.Barbarian_Rend) && (NonRendedTargets_9 > 2 || !CurrentTarget.HasDotDPS)) && (TimeSinceUse(SNOPower.Barbarian_Rend) > 1500 || TargetUtil.AnyMobsInRange(10f, 6)) && LastPowerUsed != SNOPower.Barbarian_Rend ) { iWithinRangeLastRend = GilesObjectCache.Count(u => u.Type == GObjectType.Unit && u.RadiusDistance <= 9f); iACDGUIDLastRend = CurrentTarget.ACDGuid; // Note - we have LONGER animation times for whirlwind-users // Since whirlwind seems to interrupt rend so easily int rendPreDelay = 0; int rendPostDelay = 1; if (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && (LastPowerUsed == SNOPower.Barbarian_Whirlwind || LastPowerUsed == SNOPower.None)) { rendPreDelay = 2; rendPostDelay = 2; } return(new TrinityPower(SNOPower.Barbarian_Rend, 0f, PlayerStatus.CurrentPosition, CurrentWorldDynamicId, -1, rendPreDelay, rendPostDelay, WAIT_FOR_ANIM)); } // Overpower used off-cooldown if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_Overpower) && !PlayerStatus.IsIncapacitated && (CurrentTarget.RadiusDistance <= 6f || ( AnythingWithinRange[RANGE_6] >= 1 && (CurrentTarget.IsEliteRareUnique || CurrentTarget.IsMinion || CurrentTarget.IsBoss || GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker) || (CurrentTarget.IsTreasureGoblin && CurrentTarget.CentreDistance <= 6f) || Hotbar.Contains(SNOPower.Barbarian_SeismicSlam)) ) ) && GilesUseTimer(SNOPower.Barbarian_Overpower) && PowerManager.CanCast(SNOPower.Barbarian_Overpower)) { int iPreDelay = 0; int iPostDelay = 0; // Note - we have LONGER animation times for whirlwind-users // Since whirlwind seems to interrupt rend so easily /*if (hashPowerHotbarAbilities.Contains(SNOPower.Barbarian_Whirlwind)) * { * if (powerLastSnoPowerUsed == SNOPower.Barbarian_Whirlwind || powerLastSnoPowerUsed == SNOPower.None) * { * iPreDelay = 5; * iPostDelay = 5; * } * }*/ return(new TrinityPower(SNOPower.Barbarian_Overpower, 0f, PlayerStatus.CurrentPosition, CurrentWorldDynamicId, -1, iPreDelay, iPostDelay, WAIT_FOR_ANIM)); } // Seismic slam enemies within close range if (!UseOOCBuff && !IsWaitingForSpecial && Hotbar.Contains(SNOPower.Barbarian_SeismicSlam) && !PlayerStatus.IsIncapacitated && (!Hotbar.Contains(SNOPower.Barbarian_BattleRage) || (Hotbar.Contains(SNOPower.Barbarian_BattleRage) && GetHasBuff(SNOPower.Barbarian_BattleRage))) && PlayerStatus.PrimaryResource >= 15 && CurrentTarget.CentreDistance <= 40f && (AnythingWithinRange[RANGE_50] > 1 || (AnythingWithinRange[RANGE_50] > 0 && PlayerStatus.PrimaryResourcePct >= 0.85 && CurrentTarget.HitPointsPct >= 0.30) || (CurrentTarget.IsBoss || CurrentTarget.IsEliteRareUnique || (CurrentTarget.IsTreasureGoblin && CurrentTarget.CentreDistance <= 20f)))) { return(new TrinityPower(SNOPower.Barbarian_SeismicSlam, 40f, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, WAIT_FOR_ANIM)); } // Ancient spear if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_AncientSpear) && GilesUseTimer(SNOPower.Barbarian_AncientSpear) && PowerManager.CanCast(SNOPower.Barbarian_AncientSpear) && CurrentTarget.HitPointsPct >= 0.20) { // For close-by monsters, try to leap a little further than their centre-point float fExtraDistance = CurrentTarget.Radius; if (fExtraDistance <= 4f) { fExtraDistance = 30f; } if (CurrentTarget.CentreDistance + fExtraDistance > 60f) { fExtraDistance = 60 - CurrentTarget.CentreDistance; } if (fExtraDistance < 30) { fExtraDistance = 30f; } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance + fExtraDistance); return(new TrinityPower(SNOPower.Barbarian_AncientSpear, 55f, vNewTarget, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // Sprint buff, if same suitable targets as elites, keep maintained for WW users if (!UseOOCBuff && !bDontSpamOutofCombat && Hotbar.Contains(SNOPower.Barbarian_Sprint) && !PlayerStatus.IsIncapacitated && // Let's check if is not spaming too much DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Barbarian_Sprint]).TotalMilliseconds >= 200 && // Fury Dump Options for sprint: use at max energy constantly, or on a timer ( (Settings.Combat.Barbarian.FuryDumpWOTB && PlayerStatus.PrimaryResourcePct >= 0.95 && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) || (Settings.Combat.Barbarian.FuryDumpAlways && PlayerStatus.PrimaryResourcePct >= 0.95) || ((GilesUseTimer(SNOPower.Barbarian_Sprint) && !GetHasBuff(SNOPower.Barbarian_Sprint)) && // Always keep up if we are whirlwinding, if the target is a goblin, or if we are 16 feet away from the target (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) || CurrentTarget.IsTreasureGoblin || (CurrentTarget.CentreDistance >= 16f && PlayerStatus.PrimaryResource >= 40))) ) && // If they have battle-rage, make sure it's up (!Hotbar.Contains(SNOPower.Barbarian_BattleRage) || (Hotbar.Contains(SNOPower.Barbarian_BattleRage) && GetHasBuff(SNOPower.Barbarian_BattleRage))) && // Check for minimum energy PlayerStatus.PrimaryResource >= 20) { return(new TrinityPower(SNOPower.Barbarian_Sprint, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } //skillDict.Add("Frenzy", SNOPower.Barbarian_Frenzy); //runeDict.Add("Sidearm", 1); //runeDict.Add("Triumph", 4); //runeDict.Add("Vanguard", 2); //runeDict.Add("Smite", 3); //runeDict.Add("Maniac", 0); bool hasManiacRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Barbarian_Frenzy && s.RuneIndex == 0); // Frenzy to 5 stacks if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsRooted && Hotbar.Contains(SNOPower.Barbarian_Frenzy) && !TargetUtil.AnyMobsInRange(15f, 3) && GetBuffStacks(SNOPower.Barbarian_Frenzy) < 5) { return(new TrinityPower(SNOPower.Barbarian_Frenzy, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Whirlwind spam as long as necessary pre-buffs are up if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && !PlayerStatus.IsIncapacitated && !PlayerStatus.IsRooted && (!IsWaitingForSpecial || (IsWaitingForSpecial && !(TargetUtil.AnyMobsInRange(3, 15) || ForceCloseRangeTarget))) && // make sure we're not surrounded if waiting for special // Don't WW against goblins, units in the special SNO list (!Settings.Combat.Barbarian.SelectiveWhirlwind || (Settings.Combat.Barbarian.SelectiveWhirlwind && bAnyNonWWIgnoreMobsInRange && !hashActorSNOWhirlwindIgnore.Contains(CurrentTarget.ActorSNO))) && // Only if within 15 foot of main target ((CurrentTarget.RadiusDistance <= 25f || AnythingWithinRange[RANGE_25] >= 1)) && (AnythingWithinRange[RANGE_50] >= 2 || CurrentTarget.HitPointsPct >= 0.30 || CurrentTarget.IsBossOrEliteRareUnique || PlayerStatus.CurrentHealthPct <= 0.60) && // Check for energy reservation amounts //((playerStatus.dCurrentEnergy >= 20 && !playerStatus.bWaitingForReserveEnergy) || playerStatus.dCurrentEnergy >= iWaitingReservedAmount) && PlayerStatus.PrimaryResource >= 10 && // If they have battle-rage, make sure it's up (!Hotbar.Contains(SNOPower.Barbarian_BattleRage) || (Hotbar.Contains(SNOPower.Barbarian_BattleRage) && GetHasBuff(SNOPower.Barbarian_BattleRage)))) { bool shouldGetNewZigZag = (DateTime.Now.Subtract(lastChangedZigZag).TotalMilliseconds >= 1200 || CurrentTarget.ACDGuid != iACDGUIDLastWhirlwind || vSideToSideTarget.Distance2D(PlayerStatus.CurrentPosition) <= 5f); vPositionLastZigZagCheck = PlayerStatus.CurrentPosition; if (shouldGetNewZigZag) { var wwdist = 25f; vSideToSideTarget = TargetUtil.GetZigZagTarget(CurrentTarget.Position, wwdist); LastPowerUsed = SNOPower.None; iACDGUIDLastWhirlwind = CurrentTarget.ACDGuid; lastChangedZigZag = DateTime.Now; } return(new TrinityPower(SNOPower.Barbarian_Whirlwind, 10f, vSideToSideTarget, CurrentWorldDynamicId, -1, 0, 1, NO_WAIT_ANIM)); } // Battle rage, constantly maintain if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_BattleRage) && !PlayerStatus.IsIncapacitated && // Fury Dump Options for battle rage IF they don't have sprint ( (Settings.Combat.Barbarian.FuryDumpWOTB && PlayerStatus.PrimaryResourcePct >= 0.99 && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) || (Settings.Combat.Barbarian.FuryDumpAlways && PlayerStatus.PrimaryResourcePct >= 0.99) || !GetHasBuff(SNOPower.Barbarian_BattleRage) ) && PlayerStatus.PrimaryResource >= 20 && PowerManager.CanCast(SNOPower.Barbarian_BattleRage)) { return(new TrinityPower(SNOPower.Barbarian_BattleRage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Hammer of the ancients spam-attacks - never use if waiting for special if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && !IsWaitingForSpecial && Hotbar.Contains(SNOPower.Barbarian_HammerOfTheAncients) && PlayerStatus.PrimaryResource >= 20) { //return new TrinityPower(SNOPower.Barbarian_HammerOfTheAncients, 12f, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, USE_SLOWLY); return(new TrinityPower(SNOPower.Barbarian_HammerOfTheAncients, 18f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // Weapon throw if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_WeaponThrow) && (PlayerStatus.PrimaryResource >= 10 && (CurrentTarget.RadiusDistance >= 5f || BarbHasNoPrimary()))) { return(new TrinityPower(SNOPower.Barbarian_WeaponThrow, 80f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Frenzy rapid-attacks if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Frenzy)) { return(new TrinityPower(SNOPower.Barbarian_Frenzy, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Bash fast-attacks if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Bash)) { return(new TrinityPower(SNOPower.Barbarian_Bash, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Cleave fast-attacks if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Cleave)) { return(new TrinityPower(SNOPower.Barbarian_Cleave, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 2, WAIT_FOR_ANIM)); } // Default attacks if (!UseOOCBuff && !IsCurrentlyAvoiding) { return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }