Пример #1
0
        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);
                }
            }
        }
Пример #2
0
        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));
        }