Beispiel #1
0
        private static TrinityPower GetWizardDestructablePower()
        {
            if (Hotbar.Contains(SNOPower.Wizard_EnergyTwister) && PlayerStatus.PrimaryResource >= 35)
            {
                return(new TrinityPower(SNOPower.Wizard_EnergyTwister, 9f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
            }

            if (Hotbar.Contains(SNOPower.Wizard_ArcaneOrb))
            {
                return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
            }

            if (Hotbar.Contains(SNOPower.Wizard_MagicMissile))
            {
                return(new TrinityPower(SNOPower.Wizard_MagicMissile, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
            }

            if (Hotbar.Contains(SNOPower.Wizard_ShockPulse))
            {
                return(new TrinityPower(SNOPower.Wizard_ShockPulse, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
            }

            if (Hotbar.Contains(SNOPower.Wizard_SpectralBlade))
            {
                return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 9f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
            }

            if (Hotbar.Contains(SNOPower.Wizard_Electrocute))
            {
                return(new TrinityPower(SNOPower.Wizard_Electrocute, 9f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
            }

            return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
        }
Beispiel #2
0
 private static bool DemonHunter_HasNoPrimary()
 {
     return(!(Hotbar.Contains(SNOPower.DemonHunter_Bolas) ||
              Hotbar.Contains(SNOPower.X1_DemonHunter_EntanglingShot) ||
              Hotbar.Contains(SNOPower.DemonHunter_Grenades) ||
              Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow)));
 }
Beispiel #3
0
 private static TrinityPower GetMonkDestroyPower()
 {
     if (Monk_TempestRushReady())
     {
         return(new TrinityPower(SNOPower.Monk_TempestRush, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Monk_DashingStrike))
     {
         return(new TrinityPower(SNOPower.Monk_DashingStrike, Monk_MaxDashingStrikeRange, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Monk_FistsofThunder))
     {
         return(new TrinityPower(SNOPower.Monk_FistsofThunder, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Monk_DeadlyReach))
     {
         return(new TrinityPower(SNOPower.Monk_DeadlyReach, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Monk_CripplingWave))
     {
         return(new TrinityPower(SNOPower.Monk_CripplingWave, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Monk_WayOfTheHundredFists))
     {
         return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     return(new TrinityPower(SNOPower.Weapon_Melee_Instant, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
 }
        /// <summary>
        /// Performs basic checks to see if we have and can cast a power (hotbar, power manager). Checks use timer for Wiz, DH, Monk
        /// </summary>
        /// <param name="power"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public static bool CanCast(SNOPower power, CanCastFlags flags = CanCastFlags.All)
        {
            bool hasPower = Hotbar.Contains(power);

            if (!hasPower)
            {
                return(false);
            }

            // Skip this or Barb, Crusader, WD
            if (Player.ActorClass == ActorClass.Wizard || Player.ActorClass == ActorClass.DemonHunter || Player.ActorClass == ActorClass.Monk)
            {
                bool timer = flags.HasFlag(CanCastFlags.NoTimer) || SNOPowerUseTimer(power);

                if (!timer)
                {
                    return(false);
                }
            }

            bool powerManager = flags.HasFlag(CanCastFlags.NoPowerManager) || PowerManager.CanCast(power);

            if (!powerManager)
            {
                return(false);
            }

            return(true);
        }
        /// <summary>
        /// This will find a safe place to stand in both Kiting and Avoidance situations
        /// </summary>
        /// <param name="isStuck"></param>
        /// <param name="stuckAttempts"></param>
        /// <param name="dangerPoint"></param>
        /// <param name="shouldKite"></param>
        /// <param name="avoidDeath"></param>
        /// <returns></returns>
        internal static Vector3 FindSafeZone(bool isStuck, int stuckAttempts, Vector3 dangerPoint, bool shouldKite = false, IEnumerable <TrinityCacheObject> monsterList = null, bool avoidDeath = false)
        {
            if (!isStuck)
            {
                if (shouldKite && DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 1500 && lastSafeZonePosition != Vector3.Zero)
                {
                    return(lastSafeZonePosition);
                }
                else if (DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 800 && lastSafeZonePosition != Vector3.Zero)
                {
                    return(lastSafeZonePosition);
                }
                hasEmergencyTeleportUp = (
                    // Leap is available
                    (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Barbarian_Leap)) ||
                    // Whirlwind is available
                    (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Barbarian_Whirlwind) &&
                     ((PlayerStatus.PrimaryResource >= 10 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= Trinity.MinEnergyReserve)) ||
                    // Tempest rush is available
                    (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_TempestRush) &&
                     ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= Trinity.MinEnergyReserve)) ||
                    // Teleport is available
                    (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Teleport) && PlayerStatus.PrimaryResource >= 15) ||
                    // Archon Teleport is available
                    (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport))
                    );
                // Wizards can look for bee stings in range and try a wave of force to dispel them
                if (!shouldKite && PlayerStatus.ActorClass == ActorClass.Wizard && Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && PlayerStatus.PrimaryResource >= 25 &&
                    DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_WaveOfForce]).TotalMilliseconds >= CombatBase.GetSNOPowerUseDelay(SNOPower.Wizard_WaveOfForce) &&
                    !PlayerStatus.IsIncapacitated && CacheData.TimeBoundAvoidance.Count(u => u.ActorSNO == 5212 && u.Position.Distance(PlayerStatus.Position) <= 15f) >= 2 &&
                    (
                        //HotbarSkills.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass) ||
                        PowerManager.CanCast(SNOPower.Wizard_WaveOfForce)))
                {
                    ZetaDia.Me.UsePower(SNOPower.Wizard_WaveOfForce, Vector3.Zero, PlayerStatus.WorldDynamicID, -1);
                }
            }

            float highestWeight = 0f;

            if (monsterList == null)
            {
                monsterList = new List <TrinityCacheObject>();
            }

            //Vector3 vBestLocation = FindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath);
            Vector3 vBestLocation = MainFindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath);

            highestWeight = 1;

            // Loop through distance-range steps
            if (highestWeight <= 0)
            {
                return(vBestLocation);
            }

            lastFoundSafeSpot    = DateTime.UtcNow;
            lastSafeZonePosition = vBestLocation;
            return(vBestLocation);
        }
Beispiel #6
0
 private static TrinityPower GetBarbarianDestroyPower()
 {
     if (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && PlayerStatus.PrimaryResource > MinEnergyReserve)
     {
         return(new TrinityPower(SNOPower.Barbarian_Whirlwind, 10f, vSideToSideTarget, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Barbarian_Frenzy))
     {
         return(new TrinityPower(SNOPower.Barbarian_Frenzy, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Barbarian_Bash))
     {
         return(new TrinityPower(SNOPower.Barbarian_Bash, 6f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Barbarian_Cleave))
     {
         return(new TrinityPower(SNOPower.Barbarian_Cleave, 6f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Barbarian_Rend) && PlayerStatus.PrimaryResourcePct >= 0.65)
     {
         return(new TrinityPower(SNOPower.Barbarian_Rend, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Barbarian_WeaponThrow) && PlayerStatus.PrimaryResource >= 20)
     {
         return(new TrinityPower(SNOPower.Barbarian_WeaponThrow, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     return(new TrinityPower(SNOPower.Weapon_Melee_Instant, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
 }
Beispiel #7
0
        /// <summary>
        /// This will find a safe place to stand in both Kiting and Avoidance situations
        /// </summary>
        /// <param name="isStuck"></param>
        /// <param name="stuckAttempts"></param>
        /// <param name="dangerPoint"></param>
        /// <param name="shouldKite"></param>
        /// <param name="avoidDeath"></param>
        /// <returns></returns>
        internal static Vector3 FindSafeZone(bool isStuck, int stuckAttempts, Vector3 dangerPoint, bool shouldKite = false, IEnumerable <GilesObject> monsterList = null)
        {
            if (!isStuck)
            {
                if (shouldKite && DateTime.Now.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 1500 && lastSafeZonePosition != Vector3.Zero)
                {
                    return(lastSafeZonePosition);
                }
                else if (DateTime.Now.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 800 && lastSafeZonePosition != Vector3.Zero)
                {
                    return(lastSafeZonePosition);
                }
                hasEmergencyTeleportUp = (
                    // Leap is available
                    (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Leap) &&
                     DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Barbarian_Leap]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Barbarian_Leap]) ||
                    // Whirlwind is available
                    (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Whirlwind) &&
                     ((PlayerStatus.PrimaryResource >= 10 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= GilesTrinity.MinEnergyReserve)) ||
                    // Tempest rush is available
                    (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Monk_TempestRush) &&
                     ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= GilesTrinity.MinEnergyReserve)) ||
                    // Teleport is available
                    (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Teleport) &&
                     PlayerStatus.PrimaryResource >= 15 &&
                     PowerManager.CanCast(SNOPower.Wizard_Teleport)) ||
                    // Archon Teleport is available
                    (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) &&
                     PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport))
                    );
                // Wizards can look for bee stings in range and try a wave of force to dispel them
                if (!shouldKite && PlayerStatus.ActorClass == ActorClass.Wizard && Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && PlayerStatus.PrimaryResource >= 25 &&
                    DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Wizard_WaveOfForce]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Wizard_WaveOfForce] &&
                    !PlayerStatus.IsIncapacitated && GilesTrinity.hashAvoidanceObstacleCache.Count(u => u.ActorSNO == 5212 && u.Location.Distance(PlayerStatus.CurrentPosition) <= 15f) >= 2 &&
                    (ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass) || PowerManager.CanCast(SNOPower.Wizard_WaveOfForce)))
                {
                    ZetaDia.Me.UsePower(SNOPower.Wizard_WaveOfForce, Vector3.Zero, PlayerStatus.WorldDynamicID, -1);
                }
            }

            float   fHighestWeight = 0f;
            Vector3 vBestLocation  = Vector3.Zero;

            if (monsterList == null)
            {
                monsterList = new List <GilesObject>();
            }

            vBestLocation  = newFindSafeZone(dangerPoint, shouldKite, isStuck);
            fHighestWeight = 1;

            // Loop through distance-range steps
            if (fHighestWeight > 0)
            {
                lastFoundSafeSpot    = DateTime.Now;
                lastSafeZonePosition = vBestLocation;
            }
            return(vBestLocation);
        }
Beispiel #8
0
 private static bool DoesNotHaveMonkMantraAbility()
 {
     return
         (!Hotbar.Contains(SNOPower.X1_Monk_MantraOfConviction_v2) &&
          !Hotbar.Contains(SNOPower.X1_Monk_MantraOfEvasion_v2) &&
          !Hotbar.Contains(SNOPower.X1_Monk_MantraOfHealing_v2) &&
          !Hotbar.Contains(SNOPower.X1_Monk_MantraOfRetribution_v2));
 }
Beispiel #9
0
        private static bool RefreshUnitAttributes(bool AddToCache = true, DiaUnit unit = null)
        {
            if (!DataDictionary.IgnoreUntargettableAttribute.Contains(CurrentCacheObject.ActorSNO) && unit.IsUntargetable)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "IsUntargetable";
                return(AddToCache);
            }

            // don't check for invulnerability on shielded and boss units, they are treated seperately
            if (!c_unit_HasShieldAffix && unit.IsInvulnerable)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "IsInvulnerable";
                return(AddToCache);
            }

            bool isBurrowed = false;

            if (!CacheData.UnitIsBurrowed.TryGetValue(CurrentCacheObject.RActorGuid, out isBurrowed))
            {
                isBurrowed = unit.IsBurrowed;
                // if the unit is NOT burrowed - we can attack them, add to cache (as IsAttackable)
                if (!isBurrowed)
                {
                    CacheData.UnitIsBurrowed.Add(CurrentCacheObject.RActorGuid, isBurrowed);
                }
            }

            if (isBurrowed)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "IsBurrowed";
                return(AddToCache);
            }

            // only check for DotDPS/Bleeding in certain conditions to save CPU for everyone else
            // barbs with rend
            // All WD's
            // Monks with Way of the Hundred Fists + Fists of Fury
            if (AddToCache &&
                ((Player.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_Rend)) ||
                 Player.ActorClass == ActorClass.Witchdoctor ||
                 (Player.ActorClass == ActorClass.Monk && HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 0)))
                )
            {
                ////bool hasdotDPS = CurrentCacheObject.CommonData.GetAttribute<int>(ActorAttributeType.DOTDPS) != 0;
                //bool isBleeding = CurrentCacheObject.CommonData.GetAttribute<int>(ActorAttributeType.Bleeding) != 0;
                //c_HasDotDPS = hasdotDPS && isBleeding;
                bool hasdotDPS = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.DOTDPS) != 0;
                c_HasDotDPS = hasdotDPS;
            }
            return(AddToCache);
        }
Beispiel #10
0
        internal static bool Monk_TempestRushReady()
        {
            bool isReady = false;

            if (PlayerStatus.ActorClass != ActorClass.Monk)
            {
                return(false);
            }

            if (!Hotbar.Contains(SNOPower.Monk_TempestRush))
            {
                return(false);
            }

            if (ProfileManager.CurrentProfileBehavior != null)
            {
                Type profileBehaviorType = ProfileManager.CurrentProfileBehavior.GetType();
                if (profileBehaviorType == typeof(UseObjectTag) ||
                    profileBehaviorType == typeof(UsePortalTag) ||
                    profileBehaviorType == typeof(UseWaypointTag) ||
                    profileBehaviorType == typeof(UseTownPortalTag))
                {
                    return(false);
                }
            }

            if (!Hotbar.Contains(SNOPower.Monk_TempestRush))
            {
                return(false);
            }

            if (!Monk_HasMantraAbilityAndBuff())
            {
                return(false);
            }

            double currentSpirit = ZetaDia.Me.CurrentPrimaryResource;

            // Minimum 10 spirit to continue channeling tempest rush
            if (TimeSinceUse(SNOPower.Monk_TempestRush) < 150 && currentSpirit > 10f)
            {
                return(true);
            }

            // Minimum 25 Spirit to start Tempest Rush
            if (PowerManager.CanCast(SNOPower.Monk_TempestRush) && currentSpirit > Settings.Combat.Monk.TR_MinSpirit && TimeSinceUse(SNOPower.Monk_TempestRush) > 550)
            {
                return(true);
            }

            return(isReady);
        }
Beispiel #11
0
        private static TrinityPower GetWizardDestructablePower()
        {
            if (Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && Player.PrimaryResource >= 25)
            {
                return(new TrinityPower(SNOPower.Wizard_WaveOfForce, 9f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_EnergyTwister) && Player.PrimaryResource >= 35)
            {
                return(new TrinityPower(SNOPower.Wizard_EnergyTwister, 9f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_ArcaneOrb))
            {
                return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, 35f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_MagicMissile))
            {
                return(new TrinityPower(SNOPower.Wizard_MagicMissile, 15f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_ShockPulse))
            {
                return(new TrinityPower(SNOPower.Wizard_ShockPulse, 10f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_SpectralBlade))
            {
                return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 5f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_Electrocute))
            {
                return(new TrinityPower(SNOPower.Wizard_Electrocute, 9f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_ArcaneTorrent))
            {
                return(new TrinityPower(SNOPower.Wizard_ArcaneTorrent, 9f));
            }

            if (Hotbar.Contains(SNOPower.Wizard_Blizzard))
            {
                return(new TrinityPower(SNOPower.Wizard_Blizzard, 9f));
            }

            return(CombatBase.DefaultPower);
        }
Beispiel #12
0
        private static TrinityPower GetDemonHunterDestroyPower()
        {
            if (Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow))
            {
                return(new TrinityPower(SNOPower.DemonHunter_HungeringArrow, 40f, CurrentTarget.ACDGuid));
            }

            if (Hotbar.Contains(SNOPower.X1_DemonHunter_EntanglingShot))
            {
                return(new TrinityPower(SNOPower.X1_DemonHunter_EntanglingShot, 40f, CurrentTarget.ACDGuid));
            }

            if (Hotbar.Contains(SNOPower.DemonHunter_Bolas))
            {
                return(new TrinityPower(SNOPower.DemonHunter_Bolas, 40f, CurrentTarget.ACDGuid));
            }

            if (Hotbar.Contains(SNOPower.DemonHunter_Grenades))
            {
                return(new TrinityPower(SNOPower.DemonHunter_Grenades, 15f, CurrentTarget.ACDGuid));
            }

            if (Hotbar.Contains(SNOPower.DemonHunter_ElementalArrow) && Player.PrimaryResource >= 10)
            {
                return(new TrinityPower(SNOPower.DemonHunter_ElementalArrow, 40f, CurrentTarget.ACDGuid));
            }

            if (Hotbar.Contains(SNOPower.X1_DemonHunter_EvasiveFire) && Player.PrimaryResource >= 10)
            {
                return(new TrinityPower(SNOPower.X1_DemonHunter_EvasiveFire, 40f, CurrentTarget.ACDGuid));
            }

            if (Hotbar.Contains(SNOPower.DemonHunter_RapidFire) && Player.PrimaryResource >= 10)
            {
                return(new TrinityPower(SNOPower.DemonHunter_RapidFire, 40f, CurrentTarget.Position));
            }

            if (Hotbar.Contains(SNOPower.DemonHunter_Chakram) && Player.PrimaryResource >= 20)
            {
                return(new TrinityPower(SNOPower.DemonHunter_Chakram, 0f, CurrentTarget.ACDGuid));
            }

            return(CombatBase.DefaultPower);
        }
Beispiel #13
0
        /// <summary>
        /// Checks for all necessary buffs and combat conditions for starting Archon
        /// </summary>
        /// <returns></returns>
        private static bool ShouldStartArchon()
        {
            bool canCastArchon = (
                CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) &&
                (!Hotbar.Contains(SNOPower.Wizard_Familiar) || IsFamiliarActive) &&
                CheckAbilityAndBuff(SNOPower.Wizard_EnergyArmor) &&
                CheckAbilityAndBuff(SNOPower.Wizard_IceArmor) &&
                CheckAbilityAndBuff(SNOPower.Wizard_StormArmor)
                );

            var elitesOnly   = Settings.Combat.Wizard.ArchonElitesOnly && TargetUtil.AnyElitesInRange(Settings.Combat.Wizard.ArchonEliteDistance);
            var trashInRange = !Settings.Combat.Wizard.ArchonElitesOnly && TargetUtil.AnyMobsInRange(Settings.Combat.Wizard.ArchonMobDistance, Settings.Combat.Wizard.ArchonMobCount);

            // With Chantodos set wait until max stacks before using archon
            if (Sets.ChantodosResolve.IsFullyEquipped && CacheData.Buffs.HasBuff(SNOPower.P3_ItemPassive_Unique_Ring_021) && CacheData.Buffs.GetBuff(SNOPower.P3_ItemPassive_Unique_Ring_021).StackCount < 20)
            {
                return(false);
            }

            return(canCastArchon && (elitesOnly || trashInRange || CurrentTarget.IsBoss));
        }
        /// <summary>
        /// When Vault should be cast
        /// </summary>
        private static bool VaultCondition(SkillMeta meta)
        {
            meta.CastRange = 20f;
            meta.TargetPositionSelector = ret => NavHelper.MainFindSafeZone(Player.Position, true);
            meta.RequiredResource       = Hotbar.Contains(SNOPower.DemonHunter_ShadowPower) ? 22 : 16;
            meta.ReUseDelay             = Settings.Combat.DemonHunter.VaultMovementDelay;

            if (Settings.Combat.DemonHunter.VaultMode == DemonHunterVaultMode.MovementOnly && IsInCombat)
            {
                return(false);
            }

            if (Settings.Combat.DemonHunter.VaultMode == DemonHunterVaultMode.CombatOnly && !IsInCombat)
            {
                return(false);
            }

            if (!Player.IsRooted && (TargetUtil.AnyMobsInRange(7f, 6) || Player.CurrentHealthPct <= 0.7))
            {
                return(true);
            }

            return(false);
        }
Beispiel #15
0
 private static TrinityPower GetWitchDoctorDestroyPower()
 {
     if (Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice) && Settings.Combat.WitchDoctor.ZeroDogs)
     {
         return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 12f, vNullLocation, -1, -1, 1, 2, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Witchdoctor_Firebats))
     {
         return(new TrinityPower(SNOPower.Witchdoctor_Firebats, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Witchdoctor_Firebomb))
     {
         return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Witchdoctor_PoisonDart))
     {
         return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 140)
     {
         return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Witchdoctor_CorpseSpider))
     {
         return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Witchdoctor_PlagueOfToads))
     {
         return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.Witchdoctor_AcidCloud) && PlayerStatus.PrimaryResource >= 172)
     {
         return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     return(new TrinityPower(SNOPower.Weapon_Melee_Instant, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
 }
Beispiel #16
0
 private static TrinityPower GetDemonHunterDestroyPower()
 {
     if (Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow))
     {
         return(new TrinityPower(SNOPower.DemonHunter_HungeringArrow, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.DemonHunter_EntanglingShot))
     {
         return(new TrinityPower(SNOPower.DemonHunter_EntanglingShot, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.DemonHunter_BolaShot))
     {
         return(new TrinityPower(SNOPower.DemonHunter_BolaShot, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.DemonHunter_Grenades))
     {
         return(new TrinityPower(SNOPower.DemonHunter_Grenades, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.DemonHunter_ElementalArrow) && PlayerStatus.PrimaryResource >= 10)
     {
         return(new TrinityPower(SNOPower.DemonHunter_ElementalArrow, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.DemonHunter_RapidFire) && PlayerStatus.PrimaryResource >= 10)
     {
         return(new TrinityPower(SNOPower.DemonHunter_RapidFire, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.DemonHunter_Chakram) && PlayerStatus.PrimaryResource >= 20)
     {
         return(new TrinityPower(SNOPower.DemonHunter_Chakram, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     if (Hotbar.Contains(SNOPower.DemonHunter_EvasiveFire) && PlayerStatus.PrimaryResource >= 20)
     {
         return(new TrinityPower(SNOPower.DemonHunter_EvasiveFire, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
     }
     return(new TrinityPower(SNOPower.Weapon_Ranged_Instant, 40f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM));
 }
Beispiel #17
0
        private static TrinityPower GetDemonHunterPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower)
        {
            // Pick the best destructible power available
            if (UseDestructiblePower)
            {
                return(GetDemonHunterDestroyPower());
            }

            MinEnergyReserve = 25;

            //Kridershot InternalName=x1_bow_norm_unique_09-137 GameBalanceID=1999595351 ItemLink: {c:ffff8000}Kridershot{/c}
            bool hasKridershot = ZetaDia.Me.Inventory.Equipped.Any(i => i.GameBalanceId == 1999595351);

            bool hasPunishment = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 0);

            // Spam Shadow Power
            if (Settings.Combat.DemonHunter.SpamShadowPower && CombatBase.CanCast(SNOPower.DemonHunter_ShadowPower) && !Player.IsIncapacitated &&
                (!GetHasBuff(SNOPower.DemonHunter_ShadowPower) || Player.CurrentHealthPct <= PlayerEmergencyHealthPotionLimit) && // if we don't have the buff or our health is low
                ((!hasPunishment && Player.SecondaryResource >= 14) || (hasPunishment && Player.SecondaryResource >= 39)) &&      // Save some Discipline for Preparation
                (Settings.Combat.DemonHunter.SpamShadowPower && Player.SecondaryResource >= 28))                                  // When spamming Shadow Power, save some Discipline for emergencies

            {
                return(new TrinityPower(SNOPower.DemonHunter_ShadowPower));
            }

            // NotSpam Shadow Power
            if (!UseOOCBuff && !Settings.Combat.DemonHunter.SpamShadowPower && CombatBase.CanCast(SNOPower.DemonHunter_ShadowPower) && !Player.IsIncapacitated &&
                (!GetHasBuff(SNOPower.DemonHunter_ShadowPower) || Player.CurrentHealthPct <= PlayerEmergencyHealthPotionLimit) && // if we don't have the buff or our health is low
                ((!hasPunishment && Player.SecondaryResource >= 14) || (hasPunishment && Player.SecondaryResource >= 39)) &&      // Save some Discipline for Preparation
                (Player.CurrentHealthPct < 1f || Player.IsRooted || TargetUtil.AnyMobsInRange(15)))
            {
                return(new TrinityPower(SNOPower.DemonHunter_ShadowPower));
            }

            // Vengeance
            if (!IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.X1_DemonHunter_Vengeance, CombatBase.CanCastFlags.NoTimer) &&
                ((!Settings.Combat.DemonHunter.VengeanceElitesOnly && TargetUtil.AnyMobsInRange(60, 6)) || TargetUtil.IsEliteTargetInRange(60f)))
            {
                return(new TrinityPower(SNOPower.X1_DemonHunter_Vengeance));
            }

            // Smoke Screen
            if ((!UseOOCBuff || Settings.Combat.DemonHunter.SpamSmokeScreen) && CombatBase.CanCast(SNOPower.DemonHunter_SmokeScreen) &&
                !GetHasBuff(SNOPower.DemonHunter_ShadowPower) && Player.SecondaryResource >= 14 &&
                (
                    (Player.CurrentHealthPct <= 0.50 || Player.IsRooted || TargetUtil.AnyMobsInRange(15) || Player.IsIncapacitated) ||
                    Settings.Combat.DemonHunter.SpamSmokeScreen
                ))
            {
                return(new TrinityPower(SNOPower.DemonHunter_SmokeScreen, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
            }


            int sentryCoolDown = SpellHistory.SpellUseCountInTime(SNOPower.DemonHunter_Sentry, TimeSpan.FromSeconds(24)) >= 4 ? 12 : 6;

            // Sentry Turret
            if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.DemonHunter_Sentry, CombatBase.CanCastFlags.NoTimer) &&
                (TargetUtil.AnyElitesInRange(50) || TargetUtil.AnyMobsInRange(50, 2) || TargetUtil.IsEliteTargetInRange(50)) &&
                Player.PrimaryResource >= 30 &&
                (SpellHistory.TimeSinceUse(SNOPower.DemonHunter_Sentry) > TimeSpan.FromSeconds(sentryCoolDown) || SpellHistory.DistanceFromLastUsePosition(SNOPower.DemonHunter_Sentry) > 7.5))
            {
                return(new TrinityPower(SNOPower.DemonHunter_Sentry, 65f, TargetUtil.GetBestClusterPoint()));
            }

            // Caltrops
            if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.DemonHunter_Caltrops) &&
                Player.SecondaryResource >= 6 && TargetUtil.AnyMobsInRange(40))
            {
                return(new TrinityPower(SNOPower.DemonHunter_Caltrops, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
            }


            // Preperation: restore 30 disc / 45 sec cd
            // Invigoration = 1 : passive + 15 disc / 45 sec cd
            // Punishment   = 0 : Cost: 25 disc, restore 75 hatred / NO COOLDOWN
            // Battle Scars = 3 : instantly restore 40% health + restore 30 disc / 45 sec cd
            // Focused Mind = 2 : restore 45 disc over 15 sec / 45 sec cd
            // Backup plan  = 4 : 30% chance Prep has no cooldown (remove cast timer)

            // Restore 75 hatred for 25 disc - NO COOLDOWN!
            //bool hasPunishment = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 0);
            bool hasInvigoration = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 1);
            bool hasBattleScars  = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 3);
            bool hasFocusedMind  = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 2);
            bool hasBackupPlan   = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 4);

            float preperationTriggerRange = V.F("DemonHunter.PreperationTriggerRange");

            if (((!UseOOCBuff && !Player.IsIncapacitated &&
                  (TargetUtil.AnyMobsInRange(preperationTriggerRange))) || Settings.Combat.DemonHunter.SpamPreparation || hasPunishment) &&
                Hotbar.Contains(SNOPower.DemonHunter_Preparation))
            {
                // Preperation w/ Punishment
                if (hasPunishment && CombatBase.CanCast(SNOPower.DemonHunter_Preparation, CombatBase.CanCastFlags.NoTimer) &&
                    Player.SecondaryResource >= 25 && Player.PrimaryResourceMissing >= 75 && TimeSinceUse(SNOPower.DemonHunter_Preparation) >= 1000)
                {
                    return(new TrinityPower(SNOPower.DemonHunter_Preparation));
                }

                // Preperation w/ Battle Scars - check for health only
                if (hasBattleScars && CombatBase.CanCast(SNOPower.DemonHunter_Preparation) && Player.CurrentHealthPct < 0.6)
                {
                    return(new TrinityPower(SNOPower.DemonHunter_Preparation));
                }

                // no rune || invigoration || focused mind || Backup Plan || Battle Scars (need Disc)
                if ((!hasPunishment) && CombatBase.CanCast(SNOPower.DemonHunter_Preparation) && Player.SecondaryResource <= 15 && TimeSinceUse(SNOPower.DemonHunter_Preparation) >= 1000)
                {
                    return(new TrinityPower(SNOPower.DemonHunter_Preparation));
                }
            }


            // SOURCE: Stede - http://www.thebuddyforum.com/demonbuddy-forum/plugins/trinity/155398-trinity-dh-companion-use-abilities.html#post1445496

            // Companion On-Use Abilities Added in 2.0
            bool hasSpider = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 0);
            bool hasBat    = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 3);
            bool hasBoar   = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 1);
            bool hasFerret = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 4);
            bool hasWolf   = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 2);

            if (!UseOOCBuff && !Player.IsIncapacitated && Hotbar.Contains(SNOPower.X1_DemonHunter_Companion))
            {
                // Use Spider Slow on 4 or more trash mobs in an area or on Unique/Elite/Champion
                if (hasSpider && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && TargetUtil.ClusterExists(25f, 4) && TargetUtil.EliteOrTrashInRange(25f))
                {
                    return(new TrinityPower(SNOPower.X1_DemonHunter_Companion));
                }

                //Use Bat when Hatred is Needed
                if (hasBat && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && Player.PrimaryResourceMissing >= 60)
                {
                    return(new TrinityPower(SNOPower.X1_DemonHunter_Companion));
                }

                // Use Boar Taunt on 3 or more trash mobs in an area or on Unique/Elite/Champion
                if (hasBoar && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && ((TargetUtil.ClusterExists(20f, 4) && TargetUtil.EliteOrTrashInRange(20f)) ||
                                                                                         (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.Distance <= 20f)))
                {
                    return(new TrinityPower(SNOPower.X1_DemonHunter_Companion));
                }

                // Ferrets used for picking up Health Globes when low on Health
                if (hasFerret && ObjectCache.Any(o => o.Type == GObjectType.HealthGlobe && o.Distance < 60f) && Player.CurrentHealthPct < PlayerEmergencyHealthPotionLimit)
                {
                    return(new TrinityPower(SNOPower.X1_DemonHunter_Companion));
                }

                // Use Wolf Howl on Unique/Elite/Champion - Would help for farming trash, but trash farming should not need this - Used on Elites to reduce Deaths per hour
                if (hasWolf && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) &&
                    (TargetUtil.AnyMobsInRange(20) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance < 55f)))
                {
                    return(new TrinityPower(SNOPower.X1_DemonHunter_Companion));
                }
            }

            //skillDict.Add("EvasiveFire", SNOPower.DemonHunter_EvasiveFire);
            //runeDict.Add("Hardened", 0);
            //runeDict.Add("PartingGift", 2);
            //runeDict.Add("CoveringFire", 1);
            //runeDict.Add("Displace", 4);
            //runeDict.Add("Surge", 3);


            // Companion
            if (!Player.IsIncapacitated && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && TargetUtil.EliteOrTrashInRange(30f) &&
                Player.SecondaryResource >= 10)
            {
                return(new TrinityPower(SNOPower.X1_DemonHunter_Companion, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 1));
            }

            // Marked for Death
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_MarkedForDeath) &&
                Player.SecondaryResource >= 3 &&
                (TargetUtil.AnyElitesInRange(40) || TargetUtil.AnyMobsInRange(40, 3) ||

                 ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) &&
                  CurrentTarget.Radius <= 40 && CurrentTarget.RadiusDistance <= 40f)))
            {
                return(new TrinityPower(SNOPower.DemonHunter_MarkedForDeath, 40f, Vector3.Zero, CurrentWorldDynamicId, CurrentTarget.ACDGuid, 1, 1));
            }

            // Vault
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_Vault) && !Player.IsRooted && !Player.IsIncapacitated &&
                Settings.Combat.DemonHunter.VaultMode != Config.Combat.DemonHunterVaultMode.MovementOnly &&
                // Only use vault to retreat if < level 60, or if in inferno difficulty for level 60's
                (Player.Level < 60 || Player.GameDifficulty > GameDifficulty.Master) &&
                (CurrentTarget.RadiusDistance <= 10f || TargetUtil.AnyMobsInRange(10)) &&
                // if we have ShadowPower and Disicpline is >= 16
                // or if we don't have ShadoWpower and Discipline is >= 22
                (Player.SecondaryResource >= (Hotbar.Contains(SNOPower.DemonHunter_ShadowPower) ? 22 : 16)) &&
                TimeSinceUse(SNOPower.DemonHunter_Vault) >= Trinity.Settings.Combat.DemonHunter.VaultMovementDelay)
            {
                //Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, -15f);
                // Lets find a smarter Vault position instead of just "backwards"
                //Vector3 vNewTarget = NavHelper.FindSafeZone(Trinity.Player.Position, true, false, null, false);
                Vector3 vNewTarget = NavHelper.MainFindSafeZone(Trinity.Player.Position, true, false, null, false);

                return(new TrinityPower(SNOPower.DemonHunter_Vault, 20f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2));
            }

            // Rain of Vengeance
            if (!UseOOCBuff && CombatBase.CanCast(SNOPower.DemonHunter_RainOfVengeance) && !Player.IsIncapacitated &&
                (TargetUtil.ClusterExists(45f, 3) || TargetUtil.EliteOrTrashInRange(45f)))
            {
                var bestClusterPoint = TargetUtil.GetBestClusterPoint(45f, 65f, false, true);

                return(new TrinityPower(SNOPower.DemonHunter_RainOfVengeance, 0f, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1));
            }

            // Cluster Arrow
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_ClusterArrow) && !Player.IsIncapacitated &&
                Player.PrimaryResource >= 50)
            {
                return(new TrinityPower(SNOPower.DemonHunter_ClusterArrow, V.F("DemonHunter.ClusterArrow.UseRange"), CurrentTarget.ACDGuid));
            }

            // Multi Shot
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_Multishot) && !Player.IsIncapacitated &&
                Player.PrimaryResource >= 30 &&
                (TargetUtil.AnyMobsInRange(40, 2) || CurrentTarget.IsBossOrEliteRareUnique || CurrentTarget.IsTreasureGoblin))
            {
                return(new TrinityPower(SNOPower.DemonHunter_Multishot, 30f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 1, 1));
            }

            // Fan of Knives
            if (!UseOOCBuff && CombatBase.CanCast(SNOPower.DemonHunter_FanOfKnives) && !Player.IsIncapacitated &&
                (TargetUtil.EliteOrTrashInRange(15) || TargetUtil.AnyTrashInRange(15f, 5, false)))
            {
                return(new TrinityPower(SNOPower.DemonHunter_FanOfKnives, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
            }

            // Strafe spam - similar to barbarian whirlwind routine
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_Strafe, CombatBase.CanCastFlags.NoTimer) &&
                !Player.IsIncapacitated && !Player.IsRooted && Player.PrimaryResource >= Settings.Combat.DemonHunter.StrafeMinHatred)
            {
                bool shouldGetNewZigZag =
                    (DateTime.UtcNow.Subtract(Trinity.LastChangedZigZag).TotalMilliseconds >= V.I("Barbarian.Whirlwind.ZigZagMaxTime") ||
                     CurrentTarget.ACDGuid != Trinity.LastZigZagUnitAcdGuid ||
                     CombatBase.ZigZagPosition.Distance2D(Player.Position) <= 5f);

                if (shouldGetNewZigZag)
                {
                    var wwdist = V.F("Barbarian.Whirlwind.ZigZagDistance");

                    CombatBase.ZigZagPosition = TargetUtil.GetZigZagTarget(CurrentTarget.Position, wwdist);

                    Trinity.LastZigZagUnitAcdGuid = CurrentTarget.ACDGuid;
                    Trinity.LastChangedZigZag     = DateTime.UtcNow;
                }

                int postCastTickDelay = TrinityPower.MillisecondsToTickDelay(250);

                return(new TrinityPower(SNOPower.DemonHunter_Strafe, 15f, CombatBase.ZigZagPosition, CurrentWorldDynamicId, -1, 0, postCastTickDelay));
            }

            // Spike Trap
            if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.DemonHunter_SpikeTrap) &&
                LastPowerUsed != SNOPower.DemonHunter_SpikeTrap && Player.PrimaryResource >= 30)
            {
                // For distant monsters, try to target a little bit in-front of them (as they run towards us), if it's not a treasure goblin
                float fExtraDistance = 0f;
                if (CurrentTarget.Distance > 17f && !CurrentTarget.IsTreasureGoblin)
                {
                    fExtraDistance = CurrentTarget.Distance - 17f;
                    if (fExtraDistance > 5f)
                    {
                        fExtraDistance = 5f;
                    }
                    if (CurrentTarget.Distance - fExtraDistance < 15f)
                    {
                        fExtraDistance -= 2;
                    }
                }
                Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, CurrentTarget.Distance - fExtraDistance);
                return(new TrinityPower(SNOPower.DemonHunter_SpikeTrap, 35f, vNewTarget, CurrentWorldDynamicId, -1, 1, 1));
            }

            //skillDict.Add("ElementalArrow", SNOPower.DemonHunter_ElementalArrow);
            //runeDict.Add("BallLightning", 1);
            //runeDict.Add("FrostArrow", 0);
            //runeDict.Add("ScreamingSkull", 2);
            //runeDict.Add("LightningBolts", 4);
            //runeDict.Add("NetherTentacles", 3);

            var hasBallLightning   = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 1);
            var hasFrostArrow      = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 0);
            var hasScreamingSkull  = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 2);
            var hasLightningBolts  = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 4);
            var hasNetherTentacles = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 3);

            // Elemental Arrow
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_ElementalArrow) &&
                SNOPowerUseTimer(SNOPower.DemonHunter_ElementalArrow) && !Player.IsIncapacitated &&
                ((Player.PrimaryResource >= 10 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve || hasKridershot))
            {
                return(new TrinityPower(SNOPower.DemonHunter_ElementalArrow, 65f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1));
            }

            //skillDict.Add("Chakram", SNOPower.DemonHunter_Chakram);
            //runeDict.Add("TwinChakrams", 0);
            //runeDict.Add("Serpentine", 2);
            //runeDict.Add("RazorDisk", 3);
            //runeDict.Add("Boomerang", 1);
            //runeDict.Add("ShurikenCloud", 4);

            bool hasShurikenCloud = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Chakram && s.RuneIndex == 4);

            // Chakram normal attack
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Chakram) && !Player.IsIncapacitated &&
                !hasShurikenCloud &&
                ((Player.PrimaryResource >= 10 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve))
            {
                return(new TrinityPower(SNOPower.DemonHunter_Chakram, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1));
            }

            // Chakram:Shuriken Cloud
            if (!Player.IsInTown && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Chakram) && !Player.IsIncapacitated &&
                hasShurikenCloud && TimeSinceUse(SNOPower.DemonHunter_Chakram) >= 110000 &&
                ((Player.PrimaryResource >= 10 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve))
            {
                return(new TrinityPower(SNOPower.DemonHunter_Chakram, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 2));
            }

            // Rapid Fire
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_RapidFire, CombatBase.CanCastFlags.NoTimer) &&
                !Player.IsIncapacitated && Player.PrimaryResource >= 16 &&
                (Player.PrimaryResource >= Settings.Combat.DemonHunter.RapidFireMinHatred || CombatBase.LastPowerUsed == SNOPower.DemonHunter_RapidFire))
            {
                // Players with grenades *AND* rapid fire should spam grenades at close-range instead
                if (Hotbar.Contains(SNOPower.DemonHunter_Grenades) && CurrentTarget.RadiusDistance <= 18f)
                {
                    return(new TrinityPower(SNOPower.DemonHunter_Grenades, 18f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
                }
                // Now return rapid fire, if not sending grenades instead
                return(new TrinityPower(SNOPower.DemonHunter_RapidFire, 40f, CurrentTarget.Position));
            }

            // Impale
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Impale) && !Player.IsIncapacitated &&
                (!TargetUtil.AnyMobsInRange(12, 4)) &&
                ((Player.PrimaryResource >= 25 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve) &&
                CurrentTarget.RadiusDistance <= 50f)
            {
                return(new TrinityPower(SNOPower.DemonHunter_Impale, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1));
            }

            // Evasive Fire
            if (!UseOOCBuff && CombatBase.CanCast(SNOPower.X1_DemonHunter_EvasiveFire) && !Player.IsIncapacitated &&
                (TargetUtil.AnyMobsInRange(10f) || DemonHunter_HasNoPrimary()))
            {
                float range = DemonHunter_HasNoPrimary() ? 70f : 0f;

                return(new TrinityPower(SNOPower.X1_DemonHunter_EvasiveFire, range, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1));
            }

            // Hungering Arrow
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow) && !Player.IsIncapacitated)
            {
                return(new TrinityPower(SNOPower.DemonHunter_HungeringArrow, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
            }

            // Entangling shot
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.X1_DemonHunter_EntanglingShot) && !Player.IsIncapacitated)
            {
                return(new TrinityPower(SNOPower.X1_DemonHunter_EntanglingShot, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
            }

            // Bola Shot
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Bolas) && !Player.IsIncapacitated)
            {
                return(new TrinityPower(SNOPower.DemonHunter_Bolas, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1));
            }

            // Grenades
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Grenades) && !Player.IsIncapacitated)
            {
                return(new TrinityPower(SNOPower.DemonHunter_Grenades, 40f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 5, 5));
            }

            // Default attacks
            return(CombatBase.DefaultPower);
        }
 /// <summary>
 /// Returns true if we have the ability and the buff is up, or true if we don't have the ability in our hotbar
 /// </summary>
 /// <param name="snoPower"></param>
 /// <returns></returns>
 internal static bool CheckAbilityAndBuff(SNOPower snoPower)
 {
     return
         (!Hotbar.Contains(snoPower) || (Hotbar.Contains(snoPower) && GetHasBuff(snoPower)));
 }
Beispiel #19
0
        private static TrinityPower GetWitchDoctorPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower)
        {
            bool hasGraveInjustice = ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Witchdoctor_Passive_GraveInjustice);

            bool hasAngryChicken = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Hex && s.RuneIndex == 1);
            bool isChicken       = PlayerStatus.IsHidden;

            // Hex with angry chicken, is chicken, explode!
            if (!UseOOCBuff && isChicken && (TargetUtil.AnyMobsInRange(12f, 1) || CurrentTarget.RadiusDistance <= 10f || UseDestructiblePower) && PowerManager.CanCast(SNOPower.Witchdoctor_Hex_Explode))
            {
                ShouldRefreshHotbarAbilities = true;
                return(new TrinityPower(SNOPower.Witchdoctor_Hex_Explode, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 2, WAIT_FOR_ANIM));
            }
            else if (isChicken)
            {
                ShouldRefreshHotbarAbilities = true;
            }

            // Pick the best destructible power available
            if (UseDestructiblePower)
            {
                return(GetWitchDoctorDestroyPower());
            }

            // Witch doctors have no reserve requirements?
            MinEnergyReserve = 0;
            // Spirit Walk Cast on 65% health or while avoiding anything but molten core or incapacitated or Chasing Goblins
            if (Hotbar.Contains(SNOPower.Witchdoctor_SpiritWalk) && PlayerStatus.PrimaryResource >= 49 &&
                (
                    PlayerStatus.CurrentHealthPct <= 0.65 || PlayerStatus.IsIncapacitated || PlayerStatus.IsRooted || (Settings.Combat.Misc.AllowOOCMovement && UseOOCBuff) ||
                    (!UseOOCBuff && CurrentTarget.IsTreasureGoblin && CurrentTarget.HitPointsPct < 0.90 && CurrentTarget.RadiusDistance <= 40f)
                ) &&
                PowerManager.CanCast(SNOPower.Witchdoctor_SpiritWalk))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }

            // Witch Doctor - Terror
            //skillDict.Add("SoulHarvest", SNOPower.Witchdoctor_SoulHarvest);
            //runeDict.Add("SwallowYourSoul", 3);
            //runeDict.Add("Siphon", 0);
            //runeDict.Add("Languish", 2);
            //runeDict.Add("SoulToWaste", 1);
            //runeDict.Add("VengefulSpirit", 4);

            bool hasVengefulSpirit = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 4);

            // Soul Harvest Any Elites or 2+ Norms and baby it's harvest season
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_SoulHarvest) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 59 && GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) <= 4 &&
                (TargetUtil.AnyMobsInRange(16f, 2) || TargetUtil.IsEliteTargetInRange(16f)) && PowerManager.CanCast(SNOPower.Witchdoctor_SoulHarvest))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }

            // Soul Harvest with VengefulSpirit
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Witchdoctor_SoulHarvest) && hasVengefulSpirit && PlayerStatus.PrimaryResource >= 59 &&
                TargetUtil.AnyMobsInRange(16, 3) && GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) <= 4 && PowerManager.CanCast(SNOPower.Witchdoctor_SoulHarvest))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }

            // Sacrifice AKA Zombie Dog Jihad, use on Elites Only or to try and Save yourself
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice) &&
                (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 9f)) &&
                PowerManager.CanCast(SNOPower.Witchdoctor_Sacrifice))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 0, WAIT_FOR_ANIM));
            }

            // Gargantuan, Recast on 1+ Elites or Bosses to trigger Restless Giant
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Gargantuan) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 147 &&
                (ElitesWithinRange[RANGE_15] >= 1 ||
                 (CurrentTarget != null && (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f)) || iPlayerOwnedGargantuan == 0) &&
                PowerManager.CanCast(SNOPower.Witchdoctor_Gargantuan))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan, 0f, vNullLocation, CurrentWorldDynamicId, -1, 2, 1, WAIT_FOR_ANIM));
            }

            // Zombie Dogs non-sacrifice build
            if (!IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_SummonZombieDog) && !PlayerStatus.IsIncapacitated &&
                PlayerStatus.PrimaryResource >= 49 && (ElitesWithinRange[RANGE_20] >= 2 || AnythingWithinRange[RANGE_20] >= 5 ||
                                                       (CurrentTarget != null && ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin) && CurrentTarget.RadiusDistance <= 30f)) || iPlayerOwnedZombieDog <= 2) &&
                !Settings.Combat.WitchDoctor.ZeroDogs &&
                PowerManager.CanCast(SNOPower.Witchdoctor_SummonZombieDog))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }

            // Zombie Dogs for Sacrifice
            if (Hotbar.Contains(SNOPower.Witchdoctor_SummonZombieDog) &&
                PlayerStatus.PrimaryResource >= 49 &&
                (LastPowerUsed == SNOPower.Witchdoctor_Sacrifice || iPlayerOwnedZombieDog <= 2) &&
                TimeSinceUse(SNOPower.Witchdoctor_SummonZombieDog) > 1000 &&
                Settings.Combat.WitchDoctor.ZeroDogs &&
                PowerManager.CanCast(SNOPower.Witchdoctor_SummonZombieDog))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }

            // Hex with angry chicken, check if we want to shape shift and explode
            if (Hotbar.Contains(SNOPower.Witchdoctor_Hex) && hasAngryChicken && PowerManager.CanCast(SNOPower.Witchdoctor_Hex) && PlayerStatus.PrimaryResource >= 49)
            {
                ShouldRefreshHotbarAbilities = true;
                return(new TrinityPower(SNOPower.Witchdoctor_Hex, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 2, WAIT_FOR_ANIM));
            }

            // Hex Spam Cast without angry chicken
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Hex) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 49 && !hasAngryChicken &&
                (TargetUtil.AnyElitesInRange(12) || TargetUtil.AnyMobsInRange(12, 2) || TargetUtil.IsEliteTargetInRange(18f)) && PowerManager.CanCast(SNOPower.Witchdoctor_Hex))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Hex, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, NO_WAIT_ANIM));
            }
            // Mass Confuse, elites only or big mobs or to escape on low health
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_MassConfusion) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 74 &&
                (ElitesWithinRange[RANGE_12] >= 1 || AnythingWithinRange[RANGE_12] >= 6 || PlayerStatus.CurrentHealthPct <= 0.25 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) &&
                !CurrentTarget.IsTreasureGoblin && PowerManager.CanCast(SNOPower.Witchdoctor_MassConfusion))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_MassConfusion, 0f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM));
            }
            // Big Bad Voodoo, elites and bosses only
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_BigBadVoodoo) && !PlayerStatus.IsIncapacitated &&
                !CurrentTarget.IsTreasureGoblin &&
                (ElitesWithinRange[RANGE_6] > 0 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) &&
                PowerManager.CanCast(SNOPower.Witchdoctor_BigBadVoodoo))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }

            // Grasp of the Dead, look below, droping globes and dogs when using it on elites and 3 norms
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_GraspOfTheDead) && !PlayerStatus.IsIncapacitated &&
                (TargetUtil.AnyMobsInRange(30, 2)) &&
                PlayerStatus.PrimaryResource >= 78 && PowerManager.CanCast(SNOPower.Witchdoctor_GraspOfTheDead))
            {
                var bestClusterPoint = TargetUtil.GetBestClusterPoint(15);

                return(new TrinityPower(SNOPower.Witchdoctor_GraspOfTheDead, 25f, bestClusterPoint, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }
            // Horrify Buff When not in combat for movement speed
            if (UseOOCBuff && hasGraveInjustice && Hotbar.Contains(SNOPower.Witchdoctor_Horrify) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 37 &&
                PowerManager.CanCast(SNOPower.Witchdoctor_Horrify))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Horrify, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }
            // Horrify Buff at 35% health
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Horrify) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 37 &&
                PlayerStatus.CurrentHealthPct <= 0.35 &&
                PowerManager.CanCast(SNOPower.Witchdoctor_Horrify))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Horrify, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }
            // Fetish Army, elites only
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_FetishArmy) && !PlayerStatus.IsIncapacitated &&
                (ElitesWithinRange[RANGE_25] > 0 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 16f)) &&
                PowerManager.CanCast(SNOPower.Witchdoctor_FetishArmy))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_FetishArmy, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
            }

            //skillDict.Add("SpiritBarage", SNOPower.Witchdoctor_SpiritBarrage);
            //runeDict.Add("TheSpiritIsWilling", 3);
            //runeDict.Add("WellOfSouls", 1);
            //runeDict.Add("Phantasm", 2);
            //runeDict.Add("Phlebotomize", 0);
            //runeDict.Add("Manitou", 4);

            bool hasManitou = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritBarrage && s.RuneIndex == 4);

            // Spirit Barrage
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_SpiritBarrage) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 108 &&
                PowerManager.CanCast(SNOPower.Witchdoctor_SpiritBarrage) && !hasManitou)
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, WAIT_FOR_ANIM));
            }

            // Spirit Barrage Manitou
            if (Hotbar.Contains(SNOPower.Witchdoctor_SpiritBarrage) && PlayerStatus.PrimaryResource >= 108 && TimeSinceUse(SNOPower.Witchdoctor_SpiritBarrage) > 18000 && hasManitou)
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM));
            }

            // Haunt the shit out of monster and maybe they will give you treats
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Haunt) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 98 &&
                PowerManager.CanCast(SNOPower.Witchdoctor_Haunt))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 21f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM));
            }

            // Locust
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Locust_Swarm) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 196 &&
                PowerManager.CanCast(SNOPower.Witchdoctor_Locust_Swarm) && !CurrentTarget.HasDotDPS && LastPowerUsed != SNOPower.Witchdoctor_Locust_Swarm)
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 12f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM));
            }

            // Sacrifice for 0 Dogs
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice) &&
                Settings.Combat.WitchDoctor.ZeroDogs &&
                PowerManager.CanCast(SNOPower.Witchdoctor_Sacrifice))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 9f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
            }

            // Wall of Zombies
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_WallOfZombies) && !PlayerStatus.IsIncapacitated &&
                (ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 3 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f)) &&
                PlayerStatus.PrimaryResource >= 103 && PowerManager.CanCast(SNOPower.Witchdoctor_WallOfZombies))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_WallOfZombies, 25f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
            }

            var zombieChargerRange = hasGraveInjustice ? Math.Min(PlayerStatus.GoldPickupRadius + 8f, 11f) : 11f;

            // Zombie Charger aka Zombie bears Spams Bears @ Everything from 11feet away
            if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 134 &&
                TargetUtil.AnyMobsInRange(zombieChargerRange) &&
                PowerManager.CanCast(SNOPower.Witchdoctor_ZombieCharger))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
            }

            var acidCloudRange = hasGraveInjustice ? Math.Min(PlayerStatus.GoldPickupRadius + 8f, 30f) : 30f;

            // Acid Cloud
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_AcidCloud) && !PlayerStatus.IsIncapacitated &&
                PlayerStatus.PrimaryResource >= 172 && PowerManager.CanCast(SNOPower.Witchdoctor_AcidCloud))
            {
                Vector3 bestClusterPoint;
                if (hasGraveInjustice)
                {
                    bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(PlayerStatus.GoldPickupRadius + 8f, 30f));
                }
                else
                {
                    bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f);
                }

                return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, acidCloudRange, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
            }

            // Fire Bats fast-attack
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Firebats) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 98)
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Firebats, 20f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM));
            }
            // Poison Darts fast-attack Spams Darts when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_PoisonDart) && !PlayerStatus.IsIncapacitated)
            {
                float fUseThisRange = 35f;
                if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150)
                {
                    fUseThisRange = 30f;
                }
                return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 2, WAIT_FOR_ANIM));
            }
            // Corpse Spiders fast-attacks Spams Spiders when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_CorpseSpider) && !PlayerStatus.IsIncapacitated)
            {
                float fUseThisRange = 35f;
                if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150)
                {
                    fUseThisRange = 30f;
                }
                return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM));
            }
            // Toads fast-attacks Spams Toads when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_PlagueOfToads) && !PlayerStatus.IsIncapacitated)
            {
                float fUseThisRange = 35f;
                if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150)
                {
                    fUseThisRange = 30f;
                }
                return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM));
            }
            // Fire Bomb fast-attacks Spams Bomb when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
            if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Firebomb) && !PlayerStatus.IsIncapacitated)
            {
                float fUseThisRange = 35f;
                if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150)
                {
                    fUseThisRange = 30f;
                }
                return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM));
            }
            // Default attacks
            if (!UseOOCBuff && !IsCurrentlyAvoiding)
            {
                ShouldRefreshHotbarAbilities = true;
                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));
        }
Beispiel #20
0
        private static bool RefreshGilesUnit(bool AddToCache)
        {
            AddToCache = true;

            // See if this is a boss
            c_unit_IsBoss = hashBossSNO.Contains(c_ActorSNO);

            // hax for Diablo_shadowClone
            c_unit_IsAttackable = c_InternalName.StartsWith("Diablo_shadowClone");

            // Prepare the fake object for target handler
            if (FakeObject == null)
            {
                FakeObject = c_diaUnit;
            }

            if (c_CommonData.ACDGuid == -1)
            {
                AddToCache = false;
                return(AddToCache);
            }

            // Dictionary based caching of monster types based on the SNO codes
            MonsterType monsterType;
            // See if we need to refresh the monster type or not
            bool bAddToDictionary    = !dictionaryStoredMonsterTypes.TryGetValue(c_ActorSNO, out monsterType);
            bool bRefreshMonsterType = bAddToDictionary;

            using (new PerformanceLogger("RefreshUnit.5"))
            {
                // If it's a boss and it was an ally, keep refreshing until it's not an ally
                // Because some bosses START as allied for cutscenes etc. until they become hostile
                if (c_unit_IsBoss && !bRefreshMonsterType)
                {
                    switch (monsterType)
                    {
                    case MonsterType.Ally:
                    case MonsterType.Scenery:
                    case MonsterType.Helper:
                    case MonsterType.Team:
                        bRefreshMonsterType = true;
                        break;
                    }
                }
            }
            using (new PerformanceLogger("RefreshUnit.6"))
            {
                // Now see if we do need to get new data for this boss or not
                if (bRefreshMonsterType)
                {
                    try
                    {
                        monsterType = RefreshMonsterType(c_CommonData, monsterType, bAddToDictionary);
                    }
                    catch (Exception ex)
                    {
                        DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting monsterinfo and monstertype for unit {0} [{1}]", c_InternalName, c_ActorSNO);
                        DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex);
                        DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.CacheManagement, "ActorTypeAttempt={0}", c_diaUnit.ActorType);
                        AddToCache = false;
                    }
                }

                // Make sure it's a valid monster type
                switch (monsterType)
                {
                case MonsterType.Ally:
                case MonsterType.Scenery:
                case MonsterType.Helper:
                case MonsterType.Team:
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "AllySceneryHelperTeam";
                    return(AddToCache);
                }
                    //break;
                }
            }
            // Force return here for un-attackable allies
            if (!AddToCache)
            {
                return(AddToCache);
            }

            MonsterAffixes monsterAffixes;

            using (new PerformanceLogger("RefreshUnit.8"))
            {
                // Only set treasure goblins to true *IF* they haven't disabled goblins! Then check the SNO in the goblin hash list!
                c_unit_IsTreasureGoblin = false;
                // Flag this as a treasure goblin *OR* ignore this object altogether if treasure goblins are set to ignore
                if (hashActorSNOGoblins.Contains(c_ActorSNO))
                {
                    if (Settings.Combat.Misc.GoblinPriority != 0)
                    {
                        c_unit_IsTreasureGoblin = true;
                    }
                    else
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "IgnoreTreasureGoblins";
                        return(AddToCache);
                    }
                }
                // Pull up the Monster Affix cached data
                monsterAffixes = RefreshAffixes(c_CommonData);

                /*
                 *
                 * This should be moved to HandleTarget
                 *
                 */
                if (PlayerStatus.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker, true))
                {
                    //WotB only used on Arcane, Frozen, Jailer, Molten and Electrified+Reflect Damage elites
                    if (monsterAffixes.HasFlag(MonsterAffixes.ArcaneEnchanted) || monsterAffixes.HasFlag(MonsterAffixes.Frozen) ||
                        monsterAffixes.HasFlag(MonsterAffixes.Jailer) || monsterAffixes.HasFlag(MonsterAffixes.Molten) ||
                        (monsterAffixes.HasFlag(MonsterAffixes.Electrified) && monsterAffixes.HasFlag(MonsterAffixes.ReflectsDamage)) ||
                        //Bosses and uber elites
                        c_unit_IsBoss || c_ActorSNO == 256015 || c_ActorSNO == 256000 || c_ActorSNO == 255996 ||
                        //...or more than 4 elite mobs in range (only elites/rares/uniques, not minions!)
                        ElitesWithinRange[RANGE_50] > 4)
                    {
                        shouldUseBerserkerPower = true;
                    }
                }
                else
                {
                    shouldUseBerserkerPower = false;
                }

                // Is this something we should try to force leap/other movement abilities against?
                c_ForceLeapAgainst = false;
            }
            double killRange;

            killRange = RefreshKillRadius();

            c_KillRange = killRange;

            if (monsterAffixes.HasFlag(MonsterAffixes.Shielding))
            {
                c_unit_IsShielded = true;
            }

            // Only if at full health, else don't bother checking each loop
            // See if we already have this monster's size stored, if not get it and cache it
            if (!dictionaryStoredMonsterSizes.TryGetValue(c_ActorSNO, out c_unit_MonsterSize))
            {
                try
                {
                    RefreshMonsterSize();
                }
                catch (Exception ex)
                {
                    DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting monstersize info for unit {0} [{1}]", c_InternalName, c_ActorSNO);
                    DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex);
                    AddToCache = false;
                    return(AddToCache);
                }
            }
            // Retrieve collision sphere radius, cached if possible
            if (!dictGilesCollisionSphereCache.TryGetValue(c_ActorSNO, out c_Radius))
            {
                try
                {
                    RefreshMonsterRadius();
                }
                catch (Exception ex)
                {
                    DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting collisionsphere radius for unit {0} [{1}]", c_InternalName, c_ActorSNO);
                    DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex);
                    AddToCache = false;
                    return(AddToCache);
                }
                dictGilesCollisionSphereCache.Add(c_ActorSNO, c_Radius);
            }

            double dThisMaxHealth = RefreshMonsterHealth();

            // And finally put the two together for a current health percentage
            c_HitPointsPct = c_HitPoints / dThisMaxHealth;

            // Unit is already dead
            if (c_HitPoints <= 0d && !c_unit_IsBoss)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "0HitPoints";

                // return here immediately
                return(AddToCache);
            }

            AddToCache = RefreshUnitAttributes(AddToCache, c_diaUnit);

            if (!AddToCache)
            {
                return(AddToCache);
            }

            c_CurrentAnimation = c_diaUnit.CommonData.CurrentAnimation;


            // A "fake distance" to account for the large-object size of monsters
            c_RadiusDistance -= (float)c_Radius;
            if (c_RadiusDistance <= 1f)
            {
                c_RadiusDistance = 1f;
            }

            // Special flags to decide whether to target anything at all
            if (c_IsEliteRareUnique || c_unit_IsBoss)
            {
                AnyElitesPresent = true;
            }

            // Extended kill radius after last fighting, or when we want to force a town run
            if ((Settings.Combat.Misc.ExtendedTrashKill && iKeepKillRadiusExtendedFor > 0) || ForceVendorRunASAP || TownRun.IsTryingToTownPortal())
            {
                if (c_RadiusDistance <= killRange && AddToCache)
                {
                    AnyMobsInRange = true;
                }
            }
            else
            {
                if (c_RadiusDistance <= Settings.Combat.Misc.NonEliteRange && AddToCache)
                {
                    AnyMobsInRange = true;
                }
            }
            if (c_unit_IsTreasureGoblin)
            {
                AnyTreasureGoblinsPresent = true;
            }

            // Units with very high priority (1900+) allow an extra 50% on the non-elite kill slider range
            if (!AnyMobsInRange && !AnyElitesPresent && !AnyTreasureGoblinsPresent && c_RadiusDistance <= (Settings.Combat.Misc.NonEliteRange * 1.5))
            {
                int iExtraPriority;
                // Enable extended kill radius for specific unit-types
                if (hashActorSNORanged.Contains(c_ActorSNO))
                {
                    AnyMobsInRange = true;
                }
                if (!AnyMobsInRange && dictActorSNOPriority.TryGetValue(c_ActorSNO, out iExtraPriority))
                {
                    if (iExtraPriority >= 1900)
                    {
                        AnyMobsInRange = true;
                    }
                }
            }
            return(AddToCache);
        }
Beispiel #21
0
        private static bool RefreshUnitAttributes(bool AddToCache = true, DiaUnit unit = null)
        {
            /*
             *  TeamID  - check once for all units except bosses (which can potentially change teams - Belial, Cydea)
             */
            string teamIdHash = HashGenerator.GetGenericHash("teamId.RActorGuid=" + c_RActorGuid + ".ActorSNO=" + c_ActorSNO + ".WorldId=" + PlayerStatus.WorldID);

            int teamId = 0;

            if (!c_unit_IsBoss && GenericCache.ContainsKey(teamIdHash))
            {
                teamId = (int)GenericCache.GetObject(teamIdHash).Value;
            }
            else
            {
                teamId = c_CommonData.GetAttribute <int>(ActorAttributeType.TeamID);

                GenericCache.AddToCache(new GenericCacheObject()
                {
                    Key     = teamIdHash,
                    Value   = teamId,
                    Expires = DateTime.Now.AddMinutes(60)
                });
            }
            if (teamId == 1)
            {
                AddToCache       = false;
                c_IgnoreSubStep += "IsTeam1+";
                return(AddToCache);
            }



            if (unit.IsUntargetable)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "IsUntargetable";
                return(AddToCache);
            }

            // don't check for invulnerability on shielded units, they are treated seperately
            if (!c_unit_IsShielded && unit.IsInvulnerable)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "IsInvulnerable";
                return(AddToCache);
            }

            bool isBurrowed = false;

            if (!dictGilesBurrowedCache.TryGetValue(c_RActorGuid, out isBurrowed))
            {
                isBurrowed = unit.IsBurrowed;
                // if the unit is NOT burrowed - we can attack them, add to cache (as IsAttackable)
                if (!isBurrowed)
                {
                    dictGilesBurrowedCache.Add(c_RActorGuid, isBurrowed);
                }
            }

            if (isBurrowed)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "IsBurrowed";
                return(AddToCache);
            }

            // only check for DotDPS/Bleeding in certain conditions to save CPU for everyone else
            // barbs with rend
            // All WD's
            // Monks with Way of the Hundred Fists + Fists of Fury
            if (AddToCache &&
                ((PlayerStatus.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_Rend)) ||
                 PlayerStatus.ActorClass == ActorClass.WitchDoctor ||
                 (PlayerStatus.ActorClass == ActorClass.Monk && HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 0)))
                )
            {
                ////bool hasdotDPS = c_CommonData.GetAttribute<int>(ActorAttributeType.DOTDPS) != 0;
                //bool isBleeding = c_CommonData.GetAttribute<int>(ActorAttributeType.Bleeding) != 0;
                //c_HasDotDPS = hasdotDPS && isBleeding;
                bool hasdotDPS = c_CommonData.GetAttribute <int>(ActorAttributeType.DOTDPS) != 0;
                c_HasDotDPS = hasdotDPS;
            }
            return(AddToCache);
        }
Beispiel #22
0
        private static TrinityPower GetMonkPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower)
        {
            if (UseDestructiblePower)
            {
                return(GetMonkDestroyPower());
            }

            // Monks need 80 for special spam like tempest rushing
            MinEnergyReserve = 80;


            // Epiphany
            // Desert shroud: reduce incoming damage by 50%
            bool hasDesertShroud = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_Monk_Epiphany && s.RuneIndex == 0);
            // Soothing Mist: heal self and allies for 4129+(0.25*HealthGlobeBonus) life
            bool hasSoothingMist = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_Monk_Epiphany && s.RuneIndex == 1);

            //Inner Fire X1_Monk_Epiphany/3/HotbarSlot3
            //Windwalker  X1_Monk_Epiphany/2/HotbarSlot3
            //Soothing Mist X1_Monk_Epiphany/1/HotbarSlot3
            //Ascendance X1_Monk_Epiphany/4/HotbarSlot3
            //Desert Shroud  X1_Monk_Epiphany/0/HotbarSlot3
            //None   X1_Monk_Epiphany/0/HotbarSlot3

            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.X1_Monk_Epiphany, CombatBase.CanCastFlags.NoTimer) &&
                (TargetUtil.EliteOrTrashInRange(15f) || TargetUtil.AnyMobsInRange(15f, 5)) &&
                (Player.PrimaryResourcePct < 0.50 || ((hasDesertShroud || hasSoothingMist) && Player.CurrentHealthPct < 0.50))
                )
            {
                return(new TrinityPower(SNOPower.X1_Monk_Epiphany));
            }

            //// Monk - Primary

            bool hasThunderClap = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 0);
            //bool hasLightningFlash = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 4);
            //bool hasStaticCharge = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 3);
            //bool hasQuickening = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 3);
            //bool hasBoundingLight = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 1);

            //bool hasPiercingTrident = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 1);
            //bool hasKeenEye = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 4);
            //bool hasScatteredBlows = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 2);
            //bool hasStrikeFromBeyond = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 3);
            bool hasForesight = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 0);

            //bool hasMangle = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 0);
            //bool hasConcussion = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 2);
            //bool hasRisingTide = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 3);
            //bool hasTsunami = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 1);
            //bool hasBreakingWave = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 4);

            //bool hasHandsOfLightning = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 1);
            bool hasBlazingFists = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 2);
            bool hasFistsOfFury  = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 0);
            //bool hasSpiritedSalvo = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 3);
            //bool hasWindforceFlurry = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 4);

            // Breath of Heaven Rune
            bool hasInfusedWithLight = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_BreathOfHeaven && s.RuneIndex == 3);

            // Serenity if health is low
            if ((Player.CurrentHealthPct <= 0.50 || (Player.IsIncapacitated && Player.CurrentHealthPct <= 0.90)) && CombatBase.CanCast(SNOPower.Monk_Serenity))
            {
                return(new TrinityPower(SNOPower.Monk_Serenity, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
            }

            // Mystic ally
            if (CombatBase.CanCast(SNOPower.X1_Monk_MysticAlly_v2) && TargetUtil.EliteOrTrashInRange(30f))
            {
                return(new TrinityPower(SNOPower.X1_Monk_MysticAlly_v2, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 2));
            }

            // InnerSanctuary
            if (!UseOOCBuff && TargetUtil.EliteOrTrashInRange(16f) && CombatBase.CanCast(SNOPower.X1_Monk_InnerSanctuary))
            {
                return(new TrinityPower(SNOPower.X1_Monk_InnerSanctuary, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
            }

            // Blinding Flash
            if (!UseOOCBuff && Player.PrimaryResource >= 20 && CombatBase.CanCast(SNOPower.Monk_BlindingFlash) &&
                (
                    TargetUtil.AnyElitesInRange(15, 1) ||
                    Player.CurrentHealthPct <= 0.4 ||
                    (TargetUtil.AnyMobsInRange(15, 3)) ||
                    (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f) ||
                    // as pre-sweeping wind buff
                    (TargetUtil.AnyMobsInRange(15, 1) && CombatBase.CanCast(SNOPower.Monk_SweepingWind) && !GetHasBuff(SNOPower.Monk_SweepingWind) && Settings.Combat.Monk.HasInnaSet)
                ) &&
                // Check if either we don't have sweeping winds, or we do and it's ready to cast in a moment
                (CheckAbilityAndBuff(SNOPower.Monk_SweepingWind) ||
                 (!GetHasBuff(SNOPower.Monk_SweepingWind) &&
                  (CombatBase.CanCast(SNOPower.Monk_SweepingWind, CombatBase.CanCastFlags.NoTimer))) ||
                 Player.CurrentHealthPct <= 0.25))
            {
                return(new TrinityPower(SNOPower.Monk_BlindingFlash, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 1));
            }

            // Blinding Flash as a DEFENSE
            if (!UseOOCBuff && Player.PrimaryResource >= 10 && CombatBase.CanCast(SNOPower.Monk_BlindingFlash) &&
                Player.CurrentHealthPct <= 0.75 && TargetUtil.AnyMobsInRange(15, 1))
            {
                return(new TrinityPower(SNOPower.Monk_BlindingFlash, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 1));
            }

            // Breath of Heaven when needing healing or the buff
            if (!UseOOCBuff && (Player.CurrentHealthPct <= 0.6 || !GetHasBuff(SNOPower.Monk_BreathOfHeaven)) && CombatBase.CanCast(SNOPower.Monk_BreathOfHeaven) &&
                (Player.PrimaryResource >= 35 || (!CombatBase.CanCast(SNOPower.Monk_Serenity) && Player.PrimaryResource >= 25)))
            {
                return(new TrinityPower(SNOPower.Monk_BreathOfHeaven, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
            }

            // Breath of Heaven for spirit - Infused with Light
            if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_BreathOfHeaven) && !GetHasBuff(SNOPower.Monk_BreathOfHeaven) && hasInfusedWithLight &&
                (TargetUtil.AnyMobsInRange(3, 20) || TargetUtil.IsEliteTargetInRange(20)) && Player.PrimaryResourcePct < 0.75)
            {
                return(new TrinityPower(SNOPower.Monk_BreathOfHeaven, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
            }


            // Seven Sided Strike
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated &&
                (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f) || Player.CurrentHealthPct <= 0.55) &&
                CombatBase.CanCast(SNOPower.Monk_SevenSidedStrike, CombatBase.CanCastFlags.NoTimer) &&
                ((Player.PrimaryResource >= 50 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_SevenSidedStrike, 16f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 2, 3));
            }

            // WayOfTheHundredFists: apply fists of fury DoT if we have Infused with Light buff + WotHF:FoF
            if (!UseOOCBuff && hasInfusedWithLight && hasFistsOfFury && GetHasBuff(SNOPower.Monk_BreathOfHeaven) && !CurrentTarget.HasDotDPS)
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 14f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1));
            }


            // Sweeping winds spam
            if ((Player.PrimaryResource >= 75 || (Settings.Combat.Monk.HasInnaSet && Player.PrimaryResource >= 5)) &&
                CombatBase.CanCast(SNOPower.Monk_SweepingWind, CombatBase.CanCastFlags.NoTimer) && GetHasBuff(SNOPower.Monk_SweepingWind) &&
                DateTime.UtcNow.Subtract(SweepWindSpam).TotalMilliseconds >= 4000 && DateTime.UtcNow.Subtract(SweepWindSpam).TotalMilliseconds <= 5400)
            {
                SweepWindSpam = DateTime.UtcNow;
                return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 0));
            }

            bool  hasTranscendance      = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Monk_Passive_Transcendence);
            float minSweepingWindSpirit = Settings.Combat.Monk.HasInnaSet ? 5f : 75f;

            // Sweeping wind
            if (!UseOOCBuff && CombatBase.CanCast(SNOPower.Monk_SweepingWind) && !GetHasBuff(SNOPower.Monk_SweepingWind) &&
                ((TargetUtil.AnyElitesInRange(25, 1) || TargetUtil.AnyMobsInRange(20, 1) || Settings.Combat.Monk.HasInnaSet ||
                  (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 25f)) &&
                 // Check our mantras, if we have them, are up first
                 Monk_HasMantraAbilityAndBuff() &&
                 // Check if either we don't have blinding flash, or we do and it's been cast in the last 8000ms
                 (TimeSinceUse(SNOPower.Monk_BlindingFlash) <= 8000 || CheckAbilityAndBuff(SNOPower.Monk_BlindingFlash) ||
                  TargetUtil.AnyElitesInRange(25, 1) && TimeSinceUse(SNOPower.Monk_BlindingFlash) <= 12500)) &&
                Player.PrimaryResource >= minSweepingWindSpirit)
            {
                SweepWindSpam = DateTime.UtcNow;
                return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 0));
            }

            // Sweeping Wind for Transcendance Health Regen
            if (CombatBase.CanCast(SNOPower.Monk_SweepingWind, CombatBase.CanCastFlags.NoTimer) &&
                Player.PrimaryResource >= minSweepingWindSpirit &&
                hasTranscendance && Settings.Combat.Monk.SpamSweepingWindOnLowHP &&
                Player.CurrentHealthPct <= V.F("Monk.SweepingWind.SpamOnLowHealthPct") &&
                TimeSinceUse(SNOPower.Monk_SweepingWind) > 500)
            {
                SweepWindSpam = DateTime.UtcNow;
                return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 0));
            }

            //skillDict.Add("BreathOfHeaven", SNOPower.Monk_BreathOfHeaven);
            //runeDict.Add("CircleOfScorn", 0);
            //runeDict.Add("CircleOfLife", 1);
            //runeDict.Add("BlazingWrath", 2);
            //runeDict.Add("InfusedWithLight", 3);
            //runeDict.Add("PenitentFlame", 4);

            // Exploding Palm
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated &&
                CombatBase.CanCast(SNOPower.Monk_ExplodingPalm, CombatBase.CanCastFlags.NoTimer) &&
                !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Monk_ExplodingPalm) &&
                Player.PrimaryResource >= 40)
            {
                return(new TrinityPower(SNOPower.Monk_ExplodingPalm, 14f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1));
            }

            //SkillDict.Add("WaveOfLight", SNOPower.Monk_WaveOfLight);
            //RuneDict.Add("WallOfLight", 0);
            //RuneDict.Add("ExplosiveLight", 1);
            //RuneDict.Add("EmpoweredWave", 3);
            //RuneDict.Add("BlindingLight", 4);
            //RuneDict.Add("PillarOfTheAncients", 2);
            //bool hasEmpoweredWaveRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WaveOfLight && s.RuneIndex == 3);

            bool hasEmpoweredWaveRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WaveOfLight && s.RuneIndex == 3);
            var  minWoLSpirit         = hasEmpoweredWaveRune ? 40 : 75;

            // Wave of light
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_WaveOfLight) &&
                (TargetUtil.AnyMobsInRange(16f, Settings.Combat.Monk.MinWoLTrashCount) || TargetUtil.IsEliteTargetInRange(20f)) &&
                (Player.PrimaryResource >= minWoLSpirit && !IsWaitingForSpecial || Player.PrimaryResource > MinEnergyReserve) &&
                // optional check for SW stacks
                (Settings.Combat.Monk.SWBeforeWoL && (CheckAbilityAndBuff(SNOPower.Monk_SweepingWind) && GetBuffStacks(SNOPower.Monk_SweepingWind) == 3) || !Settings.Combat.Monk.SWBeforeWoL) &&
                Monk_HasMantraAbilityAndBuff())
            {
                var bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 15f);
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_WaveOfLight, 16f, bestClusterPoint, -1, CurrentTarget.ACDGuid, 0, 1));
            }

            //SkillDict.Add("CycloneStrike", SNOPower.Monk_CycloneStrike);
            //RuneDict.Add("EyeOfTheStorm", 3);
            //RuneDict.Add("Implosion", 1);
            //RuneDict.Add("Sunburst", 0);
            //RuneDict.Add("WallOfWind", 4);
            //RuneDict.Add("SoothingBreeze", 2);

            bool hasCycloneStikeEyeOfTheStorm = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CycloneStrike && s.RuneIndex == 3);
            bool hasCycloneStikeImposion      = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CycloneStrike && s.RuneIndex == 1);

            var cycloneStrikeRange  = hasCycloneStikeImposion ? 34f : 24f;
            var cycloneStrikeSpirit = hasCycloneStikeEyeOfTheStorm ? 30 : 50;

            // Cyclone Strike
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_CycloneStrike) &&
                (
                    TargetUtil.AnyElitesInRange(cycloneStrikeRange, 1) ||
                    TargetUtil.AnyMobsInRange(cycloneStrikeRange, Settings.Combat.Monk.MinCycloneTrashCount) ||
                    (CurrentTarget.RadiusDistance >= 15f && CurrentTarget.RadiusDistance <= cycloneStrikeRange) // pull the current target into attack range
                ) &&
                (Player.PrimaryResource >= (cycloneStrikeSpirit + MinEnergyReserve)))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_CycloneStrike, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 2));
            }

            // For tempest rush re-use
            if (!UseOOCBuff && Player.PrimaryResource >= 15 && CombatBase.CanCast(SNOPower.Monk_TempestRush) &&
                TimeSinceUse(SNOPower.Monk_TempestRush) <= 150 &&
                ((Settings.Combat.Monk.TROption != TempestRushOption.MovementOnly) &&
                 !(Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && TargetUtil.AnyElitesInRange(40f))))
            {
                GenerateMonkZigZag();
                MaintainTempestRush = true;
                const string trUse = "Continuing Tempest Rush for Combat";
                Monk_TempestRushStatus(trUse);
                return(new TrinityPower(SNOPower.Monk_TempestRush, 23f, CombatBase.ZigZagPosition, CurrentWorldDynamicId, -1, 0, 0));
            }

            // Tempest rush at elites or groups of mobs
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && !Player.IsRooted && CombatBase.CanCast(SNOPower.Monk_TempestRush) &&
                ((Player.PrimaryResource >= Settings.Combat.Monk.TR_MinSpirit && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve) &&
                (Settings.Combat.Monk.TROption == TempestRushOption.Always ||
                 Settings.Combat.Monk.TROption == TempestRushOption.CombatOnly ||
                 (Settings.Combat.Monk.TROption == TempestRushOption.ElitesGroupsOnly && (TargetUtil.AnyElitesInRange(25) || TargetUtil.AnyMobsInRange(25, 2))) ||
                 (Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && !TargetUtil.AnyElitesInRange(90f) && TargetUtil.AnyMobsInRange(40f))))
            {
                GenerateMonkZigZag();
                MaintainTempestRush = true;
                const string trUse = "Starting Tempest Rush for Combat";
                Monk_TempestRushStatus(trUse);
                return(new TrinityPower(SNOPower.Monk_TempestRush, 23f, CombatBase.ZigZagPosition, CurrentWorldDynamicId, -1, 0, 0));
            }

            // Lashing Tail Kick
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_LashingTailKick) && !Player.IsIncapacitated &&
                // Either doesn't have sweeping wind, or does but the buff is already up
                (!Hotbar.Contains(SNOPower.Monk_SweepingWind) || (Hotbar.Contains(SNOPower.Monk_SweepingWind) && GetHasBuff(SNOPower.Monk_SweepingWind))) &&
                ((Player.PrimaryResource >= 65 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_LashingTailKick, 10f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1));
            }

            // Dashing Strike
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && CurrentTarget.Distance >= 16f &&
                CombatBase.CanCast(SNOPower.X1_Monk_DashingStrike, CombatBase.CanCastFlags.NoTimer))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.X1_Monk_DashingStrike, Monk_MaxDashingStrikeRange, CurrentTarget.Position, CurrentWorldDynamicId, -1, 2, 2));
            }

            // 4 Mantra spam for the 4 second buff
            if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && !Settings.Combat.Monk.DisableMantraSpam)
            {
                if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfConviction_v2) && !GetHasBuff(SNOPower.X1_Monk_MantraOfConviction_v2) &&
                    (Player.PrimaryResource >= 50) && CurrentTarget != null)
                {
                    return(new TrinityPower(SNOPower.X1_Monk_MantraOfConviction_v2));
                }

                if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfRetribution_v2) && !GetHasBuff(SNOPower.X1_Monk_MantraOfRetribution_v2) &&
                    (Player.PrimaryResource >= 50) && CurrentTarget != null)
                {
                    return(new TrinityPower(SNOPower.X1_Monk_MantraOfRetribution_v2));
                }
            }

            //Use Mantra of Healing active if health is low for shield.
            if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfHealing_v2) && Player.CurrentHealthPct <= V.F("Monk.MantraOfHealing.UseHealthPct") &&
                !Player.IsIncapacitated && !GetHasBuff(SNOPower.X1_Monk_MantraOfHealing_v2))
            {
                return(new TrinityPower(SNOPower.X1_Monk_MantraOfHealing_v2));
            }

            if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfEvasion_v2) && Player.CurrentHealthPct <= V.F("Monk.MantraOfHealing.UseHealthPct") &&
                !GetHasBuff(SNOPower.X1_Monk_MantraOfEvasion_v2) && CurrentTarget != null)
            {
                return(new TrinityPower(SNOPower.X1_Monk_MantraOfEvasion_v2));
            }


            /*
             * Dual/Trigen Monk section
             *
             * Cycle through Deadly Reach, Way of the Hundred Fists, and Fists of Thunder every 3 seconds to keep 8% passive buff up if we have Combination Strike
             *  - or -
             * Keep Foresight and Blazing Fists buffs up every 30/5 seconds
             */
            bool hasCombinationStrike = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Monk_Passive_CombinationStrike);
            bool isDualOrTriGen       = HotbarSkills.AssignedSkills.Count(s =>
                                                                          s.Power == SNOPower.Monk_DeadlyReach ||
                                                                          s.Power == SNOPower.Monk_WayOfTheHundredFists ||
                                                                          s.Power == SNOPower.Monk_FistsofThunder ||
                                                                          s.Power == SNOPower.Monk_CripplingWave) >= 2 && hasCombinationStrike;

            // interval in milliseconds for Generators
            int drInterval = 0;

            if (hasCombinationStrike)
            {
                drInterval = 2500;
            }
            else if (hasForesight)
            {
                drInterval = 29000;
            }

            int wothfInterval = 0;

            if (hasCombinationStrike)
            {
                wothfInterval = 2500;
            }
            else if (hasBlazingFists)
            {
                wothfInterval = 4500;
            }

            int cwInterval = 0;

            if (hasCombinationStrike)
            {
                cwInterval = 2500;
            }

            // Fists of Thunder:Thunder Clap - Fly to Target
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_FistsofThunder) && hasThunderClap && CurrentTarget.Distance > 16f)
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_FistsofThunder, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }

            // Deadly Reach: Foresight, every 27 seconds or 2.7 seconds with combo strike
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_DeadlyReach) && (isDualOrTriGen || hasForesight) &&
                (SpellHistory.TimeSinceUse(SNOPower.Monk_DeadlyReach) > TimeSpan.FromMilliseconds(drInterval) ||
                 (SpellHistory.SpellUseCountInTime(SNOPower.Monk_DeadlyReach, TimeSpan.FromMilliseconds(27000)) < 3) && hasForesight))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_DeadlyReach, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }

            // Way of the Hundred Fists: Blazing Fists, every 4-5ish seconds or if we don't have 3 stacks of the buff or or 2.7 seconds with combo strike
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_WayOfTheHundredFists) && (isDualOrTriGen || hasBlazingFists) &&
                (GetBuffStacks(SNOPower.Monk_WayOfTheHundredFists) < 3 ||
                 SpellHistory.TimeSinceUse(SNOPower.Monk_WayOfTheHundredFists) > TimeSpan.FromMilliseconds(wothfInterval)))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }

            // Crippling Wave
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_CripplingWave) &&
                SpellHistory.TimeSinceUse(SNOPower.Monk_CripplingWave) > TimeSpan.FromMilliseconds(cwInterval))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_CripplingWave, 20f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }

            // Fists of Thunder
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_FistsofThunder))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_FistsofThunder, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }

            // Deadly Reach normal
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_DeadlyReach))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_DeadlyReach, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }

            // Way of the Hundred Fists normal
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_WayOfTheHundredFists))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }

            // Crippling Wave Normal
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_CripplingWave))
            {
                Monk_TickSweepingWindSpam();
                return(new TrinityPower(SNOPower.Monk_CripplingWave, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3));
            }


            //// Fists of thunder as the primary, repeatable attack
            //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_FistsofThunder)
            //    && (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach) || CurrentTarget.RadiusDistance > 12f ||
            //    !TargetUtil.AnyMobsInRange(50, 5) && !TargetUtil.AnyElitesInRange(50) && !WantToSwap))
            //{
            //    if (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700)
            //        OtherThanDeadlyReach = DateTime.UtcNow;
            //    Monk_TickSweepingWindSpam();
            //    return new TrinityPower(SNOPower.Monk_FistsofThunder, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1);
            //}
            //// Crippling wave
            //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_CripplingWave)
            //    && (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach)
            //    || !TargetUtil.AnyMobsInRange(50, 5) && !TargetUtil.AnyElitesInRange(50) && !WantToSwap))
            //{
            //    OtherThanDeadlyReach = DateTime.UtcNow;
            //    Monk_TickSweepingWindSpam();
            //    return new TrinityPower(SNOPower.Monk_CripplingWave, 14f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1);
            //}
            //// Way of hundred fists
            //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_WayOfTheHundredFists)
            //    && (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach)
            //    || !TargetUtil.AnyMobsInRange(50, 5) && !TargetUtil.AnyElitesInRange(50) && !WantToSwap))
            //{
            //    OtherThanDeadlyReach = DateTime.UtcNow;
            //    Monk_TickSweepingWindSpam();
            //    return new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 14f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1);
            //}
            //// Deadly reach
            //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_DeadlyReach))
            //{
            //    if (DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds > 29000)
            //    {
            //        ForeSightFirstHit = DateTime.UtcNow;
            //    }
            //    else if (DateTime.UtcNow.Subtract(ForeSight2).TotalMilliseconds > 400 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds > 1400)
            //    {
            //        OtherThanDeadlyReach = DateTime.UtcNow;
            //    }
            //    if (DateTime.UtcNow.Subtract(ForeSight2).TotalMilliseconds > 2800)
            //    {
            //        ForeSight2 = DateTime.UtcNow;
            //    }
            //    Monk_TickSweepingWindSpam();
            //    return new TrinityPower(SNOPower.Monk_DeadlyReach, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1);
            //}

            // Default attacks
            return(CombatBase.DefaultPower);
        }
Beispiel #23
0
        public static TrinityPower GetPower()
        {
            TrinityPower power = null;

            // Spirit Walk, always!
            if (CanCast(SNOPower.Witchdoctor_SpiritWalk))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk));
            }

            var hasAngryChicken = (Skills.WitchDoctor.Hex.IsActive && Runes.WitchDoctor.AngryChicken.IsActive) || CacheData.Hotbar.ActivePowers.Contains(SNOPower.Witchdoctor_Hex_ChickenWalk);

            if (hasAngryChicken && Sets.ManajumasWay.IsEquipped && CanCast(SNOPower.Witchdoctor_Hex))
            {
                return(new TrinityPower(SNOPower.Witchdoctor_Hex));
            }

            // Combat Avoidance Spells
            if (!UseOOCBuff && IsCurrentlyAvoiding)
            {
                // Spirit Walk out of AoE
                if (CanCast(SNOPower.Witchdoctor_SpiritWalk))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk));
                }

                // Soul harvest at current location while avoiding
                if (Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && MinimumSoulHarvestCriteria(Enemies.BestCluster))
                {
                    Skills.WitchDoctor.SoulHarvest.Cast();
                }
            }

            // Incapacitated or Rooted
            if (!UseOOCBuff && (Player.IsIncapacitated || Player.IsRooted))
            {
                // Spirit Walk
                if (CanCast(SNOPower.Witchdoctor_SpiritWalk))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk));
                }
            }

            // Combat Spells with a Target
            if (!UseOOCBuff && !IsCurrentlyAvoiding && CurrentTarget != null)
            {
                if (_bastianGeneratorWaitTimer.IsFinished && ShouldRefreshBastiansGeneratorBuff)
                {
                    if (Hotbar.Contains(SNOPower.Witchdoctor_CorpseSpider))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, 50f, CurrentTarget.ACDGuid));
                    }
                    if (Hotbar.Contains(SNOPower.Witchdoctor_PoisonDart))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, 50f, CurrentTarget.ACDGuid));
                    }
                    if (Hotbar.Contains(SNOPower.Witchdoctor_PlagueOfToads))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, 50f, CurrentTarget.ACDGuid));
                    }
                    if (Hotbar.Contains(SNOPower.Witchdoctor_Firebomb))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, 50f, CurrentTarget.ACDGuid));
                    }
                    _bastianGeneratorWaitTimer.Reset();
                }


                bool hasGraveInjustice = CacheData.Hotbar.PassiveSkills.Contains(SNOPower.Witchdoctor_Passive_GraveInjustice);



                //                Debug.Print(CacheData.Hotbar.GetSkill(SNOPower.Witchdoctor_Hex).RuneIndex.ToString());
                var isChicken = CacheData.Hotbar.ActivePowers.Contains(SNOPower.Witchdoctor_Hex_ChickenWalk);

                bool hasVisionQuest = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_VisionQuest);

                // Set max ranged attack range, based on Grave Injustice, and current target NOT standing in avoidance, and health > 25%
                float rangedAttackMaxRange = 30f;
                if (hasGraveInjustice && !CurrentTarget.IsStandingInAvoidance && Player.CurrentHealthPct > 0.25)
                {
                    rangedAttackMaxRange = Math.Min(Player.GoldPickupRadius + 8f, 30f);
                }

                // Set basic attack range, depending on whether or not we have Bears and whether or not we are a tik tank
                float basicAttackRange = 35f;
                if (hasGraveInjustice)
                {
                    basicAttackRange = rangedAttackMaxRange;
                }
                else if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150)
                {
                    basicAttackRange = 30f;
                }
                else if (Legendary.TiklandianVisage.IsEquipped && !TikHorrifyCriteria(Enemies.BestLargeCluster))
                {
                    basicAttackRange = 25f;
                }
                else if (Legendary.TiklandianVisage.IsEquipped)
                {
                    basicAttackRange = 1f;
                }



                // Summon Pets  -----------------------------------------------------------------------

                // Hex with angry chicken, is chicken, explode!
                if (isChicken && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f || UseDestructiblePower)) // && CanCast(SNOPower.Witchdoctor_Hex_Explode)
                {
                    //Debug.Print("Attempting to cast HEx Explosion {0}", ZetaDia.Me.UsePower(SNOPower.Witchdoctor_Hex_Explode, ZetaDia.Me.Position,
                    //    ZetaDia.CurrentWorldDynamicId));

                    return(new TrinityPower(SNOPower.Witchdoctor_Hex_Explode));
                }

                bool hasJaunt          = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 1);
                bool hasHonoredGuest   = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 3);
                bool hasUmbralShock    = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 2);
                bool hasSeverance      = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 0);
                bool hasHealingJourney = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 4);

                // Spirit Walk for Goblins chasing
                if (CanCast(SNOPower.Witchdoctor_SpiritWalk) &&
                    CurrentTarget.IsTreasureGoblin && CurrentTarget.HitPointsPct < 0.90 && CurrentTarget.RadiusDistance <= 40f)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk));
                }

                // Spirit Walk < 65% Health: Healing Journey
                if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHealingJourney &&
                    Player.CurrentHealthPct <= V.F("WitchDoctor.SpiritWalk.HealingJourneyHealth"))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk));
                }

                // Spirit Walk < 50% Mana: Honored Guest
                if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHonoredGuest &&
                    Player.PrimaryResourcePct <= V.F("WitchDoctor.SpiritWalk.HonoredGuestMana"))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk));
                }

                //bool shouldRefreshVisionQuest = GetTimeSinceLastVisionQuestRefresh() > 4000;
                bool shouldRefreshVisionQuest = !GetHasBuff(SNOPower.Witchdoctor_Passive_VisionQuest) || GetTimeSinceLastVisionQuestRefresh() > 3800;

                // Vision Quest Passive
                if (hasVisionQuest && shouldRefreshVisionQuest)
                {
                    // Poison Darts
                    if (CanCast(SNOPower.Witchdoctor_PoisonDart))
                    {
                        VisionQuestRefreshTimer.Restart();
                        return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid));
                    }
                    // Corpse Spiders
                    if (CanCast(SNOPower.Witchdoctor_CorpseSpider))
                    {
                        VisionQuestRefreshTimer.Restart();
                        return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid));
                    }
                    // Plague Of Toads
                    if (CanCast(SNOPower.Witchdoctor_PlagueOfToads))
                    {
                        VisionQuestRefreshTimer.Restart();
                        return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid));
                    }
                    // Fire Bomb
                    if (CanCast(SNOPower.Witchdoctor_Firebomb))
                    {
                        VisionQuestRefreshTimer.Restart();
                        return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid));;
                    }
                }

                bool hasVengefulSpirit  = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 4);
                bool hasSwallowYourSoul = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 3);

                // START Jade Harvester -----------------------------------------------------------------------

                if (Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive)
                {
                    //LogTargetArea("BestLargeCluster", Enemies.BestLargeCluster);
                    //LogTargetArea("BestCluster", Enemies.BestCluster);
                    //LogTargetArea("Nearby", Enemies.Nearby);
                    //LogTargetArea("CloseNearby", Enemies.CloseNearby);

                    // Piranhas
                    if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 &&
                        (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) &&
                        LastPowerUsed != SNOPower.Witchdoctor_Piranhas &&
                        Player.PrimaryResource >= 250)
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, Enemies.BestCluster.Position));
                    }

                    // Should we move to cluster for harvest
                    if (IdealSoulHarvestCriteria(Enemies.BestLargeCluster))
                    {
                        //LogTargetArea("--- Found a good harvest location...", Enemies.BestLargeCluster);
                        MoveToSoulHarvestPoint(Enemies.BestLargeCluster);
                    }

                    // Is there a slightly better position than right here
                    if (MinimumSoulHarvestCriteria(Enemies.BestCluster) && (Enemies.BestCluster.EliteCount >= 2 || Enemies.BestCluster.UnitCount > 4))
                    {
                        //LogTargetArea("--- Found an average harvest location...", Enemies.BestCluster);
                        MoveToSoulHarvestPoint(Enemies.BestCluster);
                    }

                    // Should we harvest right here?
                    if (MinimumSoulHarvestCriteria(Enemies.CloseNearby))
                    {
                        //LogTargetArea("--- Harvesting (CurrentPosition)", Enemies.CloseNearby);
                        return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest));
                    }

                    // Locust Swarm
                    if (CanCast(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource >= 300 &&
                        !CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 20f, CurrentTarget.ACDGuid));
                    }

                    // Haunt
                    if (Skills.WitchDoctor.Haunt.CanCast() && Player.PrimaryResource >= 50 &&
                        !CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Haunt))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 45f, CurrentTarget.ACDGuid));
                    }

                    // Acid Cloud
                    if (Skills.WitchDoctor.AcidCloud.CanCast() && Player.PrimaryResource >= 325 &&
                        LastPowerUsed != SNOPower.Witchdoctor_AcidCloud)
                    {
                        Vector3 bestClusterPoint;
                        if (Passives.WitchDoctor.GraveInjustice.IsActive)
                        {
                            bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(Player.GoldPickupRadius + 8f, 30f));
                        }
                        else
                        {
                            bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f);
                        }

                        return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, rangedAttackMaxRange, bestClusterPoint));
                    }

                    // Spread the love around
                    if (!CurrentTarget.IsTreasureGoblin && CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) &&
                        CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Haunt) && Enemies.Nearby.UnitCount > 3 &&
                        Enemies.Nearby.DebuffedPercent(HarvesterCoreDebuffs) < 0.5)
                    {
                        //var oldTarget = Trinity.CurrentTarget;
                        Trinity.Blacklist3Seconds.Add(CurrentTarget.RActorGuid);
                        Trinity.CurrentTarget = Enemies.BestCluster.GetTargetWithoutDebuffs(HarvesterCoreDebuffs);
                        //Logger.LogNormal("{0} {1} is fully debuffed, switched to {2} {3}", oldTarget.InternalName, oldTarget.ACDGuid, CurrentTarget.InternalName, CurrentTarget.ACDGuid);
                    }

                    // Save mana for locust swarm || piranhas
                    if (!CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource < 300)
                    {
                        //Logger.LogNormal("Saving mana");
                        return(DefaultPower);
                    }
                }

                // END Jade Harvester -----------------------------------------------------------------------

                // Tiklandian Visage ----------------------------------------------------------------------
                // Constantly casts Horrify and moves the middle of clusters

                if (Legendary.TiklandianVisage.IsEquipped)
                {
                    // Piranhas
                    if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 &&
                        (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) &&
                        LastPowerUsed != SNOPower.Witchdoctor_Piranhas &&
                        Player.PrimaryResource >= 250)
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, Enemies.BestCluster.Position));
                    }

                    //Cast Horrify before we go into the fray
                    if (CanCast(SNOPower.Witchdoctor_Horrify))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Horrify));
                    }

                    // Should we move to a better position to fear people
                    if (TikHorrifyCriteria(Enemies.BestLargeCluster))
                    {
                        MoveToHorrifyPoint(Enemies.BestLargeCluster);
                    }
                }

                // END Tiklandian Visage ----------------------------------------------------------------------

                // Sacrifice
                if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 &&
                    (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 9f)))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice));
                }

                // Sacrifice for Circle of Life
                bool hasCircleofLife = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_CircleOfLife);
                if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 && hasCircleofLife && TargetUtil.AnyMobsInRange(15f))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice));
                }

                // Wall of Zombies
                if (CanCast(SNOPower.Witchdoctor_WallOfZombies) &&
                    (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 1) ||
                     ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f)))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_WallOfZombies, 25f, CurrentTarget.Position));
                }

                bool hasRestlessGiant     = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0);
                bool hasWrathfulProtector = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3);

                if (CanCast(SNOPower.Witchdoctor_Gargantuan))
                {
                    // Gargantuan, Recast on Elites or Bosses to trigger Restless Giant
                    if (hasRestlessGiant && (TargetUtil.IsEliteTargetInRange(30f) || Trinity.PlayerOwnedGargantuanCount == 0))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan));
                    }

                    // Gargantuan Wrathful Protector, 15 seconds of smash, use sparingly!
                    if (hasWrathfulProtector && TargetUtil.IsEliteTargetInRange(30f))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan));
                    }

                    // Gargantuan regular
                    if (!hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0)
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan));
                    }
                }

                bool hasSacrifice = Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice);

                // Zombie Dogs for Sacrifice
                if (hasSacrifice && CanCast(SNOPower.Witchdoctor_SummonZombieDog) &&
                    (LastPowerUsed == SNOPower.Witchdoctor_Sacrifice || Trinity.PlayerOwnedZombieDogCount <= 2) &&
                    LastPowerUsed != SNOPower.Witchdoctor_SummonZombieDog)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog));
                }

                // Hex with angry chicken, check if we want to shape shift and explode
                if (CanCast(SNOPower.Witchdoctor_Hex) && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f) &&
                    hasAngryChicken)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Hex));
                }

                // Hex Spam Cast without angry chicken
                if (CanCast(SNOPower.Witchdoctor_Hex) && !hasAngryChicken &&
                    (TargetUtil.AnyElitesInRange(12) || TargetUtil.AnyMobsInRange(12, 2) || TargetUtil.IsEliteTargetInRange(18f)))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Hex));
                }

                if (CanCast(SNOPower.Witchdoctor_SoulHarvest) && (TargetUtil.AnyElitesInRange(16) || TargetUtil.AnyMobsInRange(16, 2) || TargetUtil.IsEliteTargetInRange(16f)))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest));
                }

                // Mass Confuse, elites only or big mobs or to escape on low health
                if (CanCast(SNOPower.Witchdoctor_MassConfusion) &&
                    (TargetUtil.AnyElitesInRange(12, 1) || TargetUtil.AnyMobsInRange(12, 6) || Player.CurrentHealthPct <= 0.25 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) &&
                    !CurrentTarget.IsTreasureGoblin)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_MassConfusion, 0f, CurrentTarget.ACDGuid));
                }

                if (!Settings.Combat.WitchDoctor.UseBigBadVoodooOffCooldown)
                {
                    // Big Bad Voodoo, elites and bosses only
                    if (CanCast(SNOPower.Witchdoctor_BigBadVoodoo) &&
                        (TargetUtil.EliteOrTrashInRange(25f) || (CurrentTarget.IsBoss && CurrentTarget.Distance <= 30f)))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo));
                    }
                }
                else
                {
                    // Big Bad Voodo, cast whenever available
                    if (!UseOOCBuff && !Player.IsIncapacitated && CanCast(SNOPower.Witchdoctor_BigBadVoodoo))
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo));
                    }
                }
                // Grasp of the Dead
                if (CanCast(SNOPower.Witchdoctor_GraspOfTheDead) &&
                    (TargetUtil.AnyMobsInRange(30, 2) || TargetUtil.EliteOrTrashInRange(30f)) &&
                    Player.PrimaryResource >= 150)
                {
                    var bestClusterPoint = TargetUtil.GetBestClusterPoint();

                    return(new TrinityPower(SNOPower.Witchdoctor_GraspOfTheDead, 25f, bestClusterPoint));
                }

                // Piranhas
                if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 &&
                    (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) &&
                    Player.PrimaryResource >= 250)
                {
                    var bestClusterPoint = TargetUtil.GetBestClusterPoint();

                    return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, bestClusterPoint));
                }

                bool hasPhobia            = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 2);
                bool hasStalker           = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4);
                bool hasFaceOfDeath       = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 1);
                bool hasFrighteningAspect = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 0);
                bool hasRuthlessTerror    = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 3);

                float horrifyRadius = hasFaceOfDeath ? 24f : 12f;

                // Horrify when low on health
                if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= EmergencyHealthPotionLimit && TargetUtil.AnyMobsInRange(horrifyRadius, 3))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Horrify));
                }

                // Horrify Buff at 35% health -- Freightening Aspect
                if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= 0.35 && hasFrighteningAspect)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Horrify));
                }

                // Spam Horrify
                if (CanCast(SNOPower.Witchdoctor_Horrify) && Settings.Combat.WitchDoctor.SpamHorrify)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Horrify));
                }

                // Fetish Army, elites only
                if (CanCast(SNOPower.Witchdoctor_FetishArmy) &&
                    (TargetUtil.EliteOrTrashInRange(30f) || TargetUtil.IsEliteTargetInRange(30f) || Settings.Combat.WitchDoctor.UseFetishArmyOffCooldown))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_FetishArmy));
                }

                bool hasManitou = Runes.WitchDoctor.Manitou.IsActive;

                // Spirit Barrage Manitou
                if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 &&
                    TimeSincePowerUse(SNOPower.Witchdoctor_SpiritBarrage) > 18000 && hasManitou)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage));
                }

                bool hasResentfulSpirit = Runes.WitchDoctor.ResentfulSpirits.IsActive;
                // Haunt
                if (CanCast(SNOPower.Witchdoctor_Haunt) &&
                    Player.PrimaryResource >= 50 &&
                    !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Haunt) &&
                    LastPowerUsed != SNOPower.Witchdoctor_Haunt)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 21f, CurrentTarget.ACDGuid));
                }

                //skillDict.Add("LocustSwarm", SNOPower.Witchdoctor_Locust_Swarm);

                // Locust Swarm
                if (CanCast(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource >= 300 &&
                    !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Locust_Swarm) && LastPowerUsed != SNOPower.Witchdoctor_Locust_Swarm)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 12f, CurrentTarget.ACDGuid));
                }

                // Sacrifice for 0 Dogs
                if (CanCast(SNOPower.Witchdoctor_Sacrifice) &&
                    (Settings.Combat.WitchDoctor.ZeroDogs || !WitchDoctorHasPrimaryAttack))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 9f));
                }

                var zombieChargerRange = hasGraveInjustice ? Math.Min(Player.GoldPickupRadius + 8f, 11f) : 11f;

                // Zombie Charger aka Zombie bears Spams Bears @ Everything from 11feet away
                if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position));
                }

                // Soul Harvest Any Elites or to increase buff stacks
                if (!Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && CanCast(SNOPower.Witchdoctor_SoulHarvest) &&
                    (TargetUtil.AnyMobsInRange(16f, GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) + 1, false) || (hasSwallowYourSoul && Player.PrimaryResourcePct <= 0.50) || TargetUtil.IsEliteTargetInRange(16f)))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest));
                }

                // Soul Harvest with VengefulSpirit
                if (!Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && CanCast(SNOPower.Witchdoctor_SoulHarvest) && hasVengefulSpirit &&
                    TargetUtil.AnyMobsInRange(16, 3))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest));
                }

                bool hasDireBats    = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 0);
                bool hasVampireBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 3);
                bool hasPlagueBats  = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 2);
                bool hasHungryBats  = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 1);
                bool hasCloudOfBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 4);

                int fireBatsChannelCost = hasVampireBats ? 0 : 75;
                int fireBatsMana        = TimeSincePowerUse(SNOPower.Witchdoctor_Firebats) < 125 ? fireBatsChannelCost : 225;

                bool firebatsMaintain =
                    Trinity.ObjectCache.Any(u => u.IsUnit &&
                                            u.IsPlayerFacing(70f) && u.Weight > 0 &&
                                            u.Distance <= V.F("WitchDoctor.Firebats.MaintainRange") &&
                                            SpellHistory.TimeSinceUse(SNOPower.Witchdoctor_Firebats) <= TimeSpan.FromMilliseconds(250d));

                // Fire Bats:Cloud of bats
                if (hasCloudOfBats && (TargetUtil.AnyMobsInRange(8f) || firebatsMaintain) &&
                    CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana)
                {
                    var range = Settings.Combat.WitchDoctor.FirebatsRange > 12f ? 12f : Settings.Combat.WitchDoctor.FirebatsRange;

                    return(new TrinityPower(SNOPower.Witchdoctor_Firebats, range, CurrentTarget.ACDGuid));
                }

                // Fire Bats fast-attack
                if (CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana &&
                    (TargetUtil.AnyMobsInRange(Settings.Combat.WitchDoctor.FirebatsRange) || firebatsMaintain) && !hasCloudOfBats)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Firebats, Settings.Combat.WitchDoctor.FirebatsRange, CurrentTarget.Position));
                }

                // Acid Cloud
                if (CanCast(SNOPower.Witchdoctor_AcidCloud) && Player.PrimaryResource >= 175)
                {
                    Vector3 bestClusterPoint;
                    if (hasGraveInjustice)
                    {
                        bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(Player.GoldPickupRadius + 8f, 30f));
                    }
                    else
                    {
                        bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f);
                    }

                    return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, rangedAttackMaxRange, bestClusterPoint));
                }

                bool hasWellOfSouls   = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritBarrage && s.RuneIndex == 1);
                bool hasRushOfEssence = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_RushOfEssence);

                // Spirit Barrage + Rush of Essence
                if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 &&
                    hasRushOfEssence && !hasManitou)
                {
                    if (hasWellOfSouls)
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid));
                    }

                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid));
                }

                // Zombie Charger backup
                if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position));
                }

                // Regular spirit barage
                if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && !hasManitou)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, basicAttackRange, CurrentTarget.ACDGuid));
                }

                // Poison Darts fast-attack Spams Darts when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
                if (CanCast(SNOPower.Witchdoctor_PoisonDart))
                {
                    VisionQuestRefreshTimer.Restart();
                    return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid));
                }
                // Corpse Spiders fast-attacks Spams Spiders when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
                if (CanCast(SNOPower.Witchdoctor_CorpseSpider))
                {
                    VisionQuestRefreshTimer.Restart();
                    return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid));
                }
                // Toads fast-attacks Spams Toads when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
                if (CanCast(SNOPower.Witchdoctor_PlagueOfToads))
                {
                    VisionQuestRefreshTimer.Restart();
                    return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid));
                }
                // Fire Bomb fast-attacks Spams Bomb when mana is too low (to cast bears) @12yds or @10yds if Bears avialable
                if (CanCast(SNOPower.Witchdoctor_Firebomb))
                {
                    VisionQuestRefreshTimer.Restart();
                    return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid));
                }

                //Hexing Pants Mod
                if (Legendary.HexingPantsOfMrYan.IsEquipped && CurrentTarget.IsUnit &&
                    //!CanCast(SNOPower.Witchdoctor_Piranhas) &&
                    CurrentTarget.RadiusDistance > 10f)
                {
                    return(new TrinityPower(SNOPower.Walk, 10f, CurrentTarget.Position));
                }

                if (Legendary.HexingPantsOfMrYan.IsEquipped && CurrentTarget.IsUnit &&
                    //!CanCast(SNOPower.Witchdoctor_Piranhas) &&
                    CurrentTarget.RadiusDistance < 10f)
                {
                    Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, -10f);
                    return(new TrinityPower(SNOPower.Walk, 10f, vNewTarget));
                }
            }

            // Buffs
            if (UseOOCBuff)
            {
                // Spirit Walk OOC
                if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && Settings.Combat.Misc.AllowOOCMovement)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk));
                }


                //Spam fear at all times if Tiklandian Visage is ewquipped and fear spam is selected to keep fear buff active
                if (CanCast(SNOPower.Witchdoctor_Horrify) && Settings.Combat.WitchDoctor.SpamHorrify && Legendary.TiklandianVisage.IsEquipped)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Horrify));
                }

                bool hasStalker = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4);
                // Horrify Buff When not in combat for movement speed -- Stalker
                if (CanCast(SNOPower.Witchdoctor_Horrify) && hasStalker)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Horrify));
                }

                // Zombie Dogs non-sacrifice build
                if (CanCast(SNOPower.Witchdoctor_SummonZombieDog) &&
                    ((Legendary.TheTallMansFinger.IsEquipped && Trinity.PlayerOwnedZombieDogCount < 1) ||
                     (!Legendary.TheTallMansFinger.IsEquipped && Trinity.PlayerOwnedZombieDogCount <= 2)))
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog));
                }

                bool hasRestlessGiant     = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0);
                bool hasWrathfulProtector = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3);

                if (CanCast(SNOPower.Witchdoctor_Gargantuan) && !hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0)
                {
                    return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan));
                }
            }

            // Default Attacks
            if (IsNull(power))
            {
                power = DefaultPower;
            }

            return(power);
        }
Beispiel #24
0
        private static bool RefreshGold(bool AddToCache)
        {
            AddToCache = true;

            if (!Settings.Loot.Pickup.PickupGold)
            {
                c_IgnoreSubStep = "PickupDisabled";
                AddToCache      = false;
                return(AddToCache);
            }

            if (Player.ActorClass == ActorClass.Barbarian && Settings.Combat.Barbarian.IgnoreGoldInWOTB && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) &&
                GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker))
            {
                AddToCache      = false;
                c_IgnoreSubStep = "IgnoreGoldInWOTB";
                return(AddToCache);
            }

            // Get the gold amount of this pile, cached if possible
            if (!CacheData.GoldStack.TryGetValue(CurrentCacheObject.RActorGuid, out c_GoldStackSize))
            {
                try
                {
                    c_GoldStackSize = ((ACDItem)CurrentCacheObject.CommonData).Gold;
                }
                catch
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting gold pile amount for item {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO);
                    AddToCache      = false;
                    c_IgnoreSubStep = "GetAttributeException";
                }
                CacheData.GoldStack.Add(CurrentCacheObject.RActorGuid, c_GoldStackSize);
            }

            if (c_GoldStackSize < Settings.Loot.Pickup.MinimumGoldStack)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "NotEnoughGold";
                return(AddToCache);
            }

            if (CurrentCacheObject.Distance <= Player.GoldPickupRadius)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "WithinPickupRadius";
                return(AddToCache);
            }
            return(AddToCache);
        }
        private static bool RefreshAvoidance(bool AddToCache)
        {
            AddToCache = true;

            try
            {
                CurrentCacheObject.Animation = CurrentCacheObject.Object.CommonData.CurrentAnimation;
            }
            catch (Exception ex)
            {
                Logger.LogDebug(LogCategory.CacheManagement, "Error reading CurrentAnimation for AoE sno:{0} raGuid:{1} name:{2} ex:{3}",
                                CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message);
            }

            float customRadius;

            if (DataDictionary.DefaultAvoidanceCustomRadius.TryGetValue(CurrentCacheObject.ActorSNO, out customRadius))
            {
                CurrentCacheObject.Radius = customRadius;
            }

            double minAvoidanceHealth = GetAvoidanceHealth(CurrentCacheObject.ActorSNO);
            double minAvoidanceRadius = GetAvoidanceRadius(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);

            // Are we allowed to path around avoidance?
            if (Settings.Combat.Misc.AvoidanceNavigation)
            {
                MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, (float)minAvoidanceRadius);
            }

            AvoidanceType avoidanceType = AvoidanceManager.GetAvoidanceType(CurrentCacheObject.ActorSNO);

            // Beast Charge should set aoe position as players current position!
            if (avoidanceType == AvoidanceType.BeastCharge)
            {
                CurrentCacheObject.Position = Trinity.Player.Position;
            }

            // Monks with Serenity up ignore all AOE's
            if (Player.ActorClass == ActorClass.Monk && Hotbar.Contains(SNOPower.Monk_Serenity) && GetHasBuff(SNOPower.Monk_Serenity))
            {
                // Monks with serenity are immune
                minAvoidanceHealth *= V.F("Monk.Avoidance.Serenity");
                Logger.Log(TrinityLogLevel.Debug, LogCategory.Avoidance, "Ignoring avoidance as a Monk with Serenity");
            }
            // Witch doctors with spirit walk available and not currently Spirit Walking will subtly ignore ice balls, arcane, desecrator & plague cloud
            if (Player.ActorClass == ActorClass.Witchdoctor && Hotbar.Contains(SNOPower.Witchdoctor_SpiritWalk) && GetHasBuff(SNOPower.Witchdoctor_SpiritWalk))
            {
                if (avoidanceType == AvoidanceType.IceBall || avoidanceType == AvoidanceType.Arcane || avoidanceType == AvoidanceType.Desecrator || avoidanceType == AvoidanceType.PlagueCloud)
                {
                    // Ignore ICE/Arcane/Desc/PlagueCloud altogether with spirit walk up or available
                    minAvoidanceHealth *= V.F("WitchDoctor.Avoidance.SpiritWalk");
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.Avoidance, "Ignoring avoidance as a WitchDoctor with Spirit Walk");
                }
            }
            // Remove ice balls if the barbarian has wrath of the berserker up, and reduce health from most other SNO avoidances
            if (Player.ActorClass == ActorClass.Barbarian &&
                Settings.Combat.Barbarian.IgnoreAvoidanceInWOTB &&
                Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) &&
                GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker))
            {
                switch (avoidanceType)
                {
                case AvoidanceType.IceBall:
                    minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.IceBall");
                    break;

                case AvoidanceType.Arcane:
                    minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.Arcane");
                    break;

                case AvoidanceType.Desecrator:
                    minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.Desecrator");
                    break;

                case AvoidanceType.Belial:
                    minAvoidanceHealth = V.F("Barbarian.Avoidance.WOTB.Belial");
                    break;

                case AvoidanceType.PoisonTree:
                    minAvoidanceHealth = V.F("Barbarian.Avoidance.WOTB.PoisonTree");
                    break;

                case AvoidanceType.BeastCharge:
                    minAvoidanceHealth = V.F("Barbarian.Avoidance.WOTB.BeastCharge");
                    break;

                default:
                    minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.Other");
                    break;
                }
            }

            if (minAvoidanceHealth == 0)
            {
                AddToCache = false;
                Logger.Log(TrinityLogLevel.Verbose, LogCategory.Avoidance, "Ignoring Avoidance! Name={0} SNO={1} radius={2:0} health={3:0.00} dist={4:0}",
                           CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO, minAvoidanceRadius, minAvoidanceHealth, CurrentCacheObject.Distance);
                return(AddToCache);
            }

            // Add it to the list of known avoidance objects, *IF* our health is lower than this avoidance health limit
            if (minAvoidanceHealth >= Player.CurrentHealthPct)
            {
                float avoidanceRadius = (float)GetAvoidanceRadius(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);

                TimeSpan aoeExpiration;
                DataDictionary.AvoidanceSpawnerDuration.TryGetValue(CurrentCacheObject.ActorSNO, out aoeExpiration);

                CacheData.TimeBoundAvoidance.Add(new CacheObstacleObject(CurrentCacheObject.Position, avoidanceRadius, CurrentCacheObject.ActorSNO, CurrentCacheObject.InternalName)
                {
                    Expires    = DateTime.UtcNow.Add(aoeExpiration),
                    ObjectType = GObjectType.Avoidance,
                    Rotation   = CurrentCacheObject.Rotation
                });

                // Is this one under our feet? If so flag it up so we can find an avoidance spot
                if (CurrentCacheObject.Distance <= minAvoidanceRadius)
                {
                    _standingInAvoidance = true;

                    // Note if this is a travelling projectile or not so we can constantly update our safe points
                    if (DataDictionary.AvoidanceProjectiles.Contains(CurrentCacheObject.ActorSNO))
                    {
                        _isAvoidingProjectiles = true;
                        Logger.Log(TrinityLogLevel.Verbose, LogCategory.Avoidance, "Is standing in avoidance for projectile Name={0} SNO={1} radius={2:0} health={3:0.00} dist={4:0}",
                                   CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO, minAvoidanceRadius, minAvoidanceHealth, CurrentCacheObject.Distance);
                    }
                    else
                    {
                        Logger.Log(TrinityLogLevel.Verbose, LogCategory.Avoidance, "Is standing in avoidance Name={0} SNO={1} radius={2:0} health={3:0.00} dist={4:0}",
                                   CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO, minAvoidanceRadius, minAvoidanceHealth, CurrentCacheObject.Distance);
                    }
                }
            }

            return(AddToCache);
        }
Beispiel #26
0
        private static bool RefreshGizmo(bool AddToCache)
        {
            if (!Settings.WorldObject.AllowPlayerResurection && CurrentCacheObject.ActorSNO == DataDictionary.PLAYER_HEADSTONE_SNO)
            {
                c_IgnoreSubStep = "IgnoreHeadstones";
                AddToCache      = false;
                return(AddToCache);
            }
            // start as true, then set as false as we go. If nothing matches below, it will return true.
            AddToCache = true;

            bool openResplendentChest = CurrentCacheObject.InternalName.ToLower().Contains("chest_rare");

            // Ignore it if it's not in range yet, except health wells and resplendent chests if we're opening chests
            if ((CurrentCacheObject.RadiusDistance > CurrentBotLootRange || CurrentCacheObject.RadiusDistance > 50) && CurrentCacheObject.Type != GObjectType.HealthWell && CurrentCacheObject.Type != GObjectType.Shrine && CurrentCacheObject.RActorGuid != LastTargetRactorGUID)
            {
                AddToCache      = false;
                c_IgnoreSubStep = "NotInRange";
            }

            // re-add resplendent chests
            if (openResplendentChest)
            {
                AddToCache      = true;
                c_IgnoreSubStep = "";
            }

            try
            {
                CurrentCacheObject.IsBountyObjective = (CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.BountyObjective) > 0);
            }
            catch (Exception)
            {
                Logger.LogDebug("Error refreshing IsBountyObjective");
            }
            try
            {
                CurrentCacheObject.IsQuestMonster = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.QuestMonster) > 1;
            }
            catch (Exception ex)
            {
                Logger.LogDebug(LogCategory.CacheManagement, "Error reading IsQuestMonster for Unit sno:{0} raGuid:{1} name:{2} ex:{3}",
                                CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message);
            }

            try
            {
                CurrentCacheObject.IsMinimapActive = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.MinimapActive) > 0;
            }
            catch (Exception ex)
            {
                Logger.LogDebug(LogCategory.CacheManagement, "Error reading MinimapActive for Gizmo sno:{0} raGuid:{1} name:{2} ex:{3}",
                                CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message);
            }

            try
            {
                CurrentCacheObject.IsQuestMonster = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.QuestMonster) > 0;
            }
            catch (Exception ex)
            {
                Logger.LogDebug(LogCategory.CacheManagement, "Error reading IsQuestMonster for Gizmo sno:{0} raGuid:{1} name:{2} ex:{3}",
                                CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message);
            }

            if (CurrentCacheObject.Gizmo != null)
            {
                CurrentCacheObject.GizmoType = CurrentCacheObject.Gizmo.ActorInfo.GizmoType;
            }

            // Anything that's been disabled by a script
            bool isGizmoDisabledByScript = false;

            try
            {
                if (CurrentCacheObject.Object is DiaGizmo)
                {
                    isGizmoDisabledByScript = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.GizmoDisabledByScript) > 0;
                }
            }
            catch
            {
                CacheData.NavigationObstacles.Add(new CacheObstacleObject
                {
                    ActorSNO   = CurrentCacheObject.ActorSNO,
                    Radius     = CurrentCacheObject.Radius,
                    Position   = CurrentCacheObject.Position,
                    RActorGUID = CurrentCacheObject.RActorGuid,
                    ObjectType = CurrentCacheObject.Type,
                });

                Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement,
                           "Safely handled exception getting Gizmo-Disabled-By-Script attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO);
                c_IgnoreSubStep = "isGizmoDisabledByScriptException";
                AddToCache      = false;
            }
            if (isGizmoDisabledByScript)
            {
                MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);

                CacheData.NavigationObstacles.Add(new CacheObstacleObject
                {
                    ActorSNO   = CurrentCacheObject.ActorSNO,
                    Radius     = CurrentCacheObject.Radius,
                    Position   = CurrentCacheObject.Position,
                    RActorGUID = CurrentCacheObject.RActorGuid,
                    ObjectType = CurrentCacheObject.Type,
                });

                AddToCache      = false;
                c_IgnoreSubStep = "GizmoDisabledByScript";
                return(AddToCache);
            }


            // Anything that's Untargetable
            bool untargetable = false;

            try
            {
                if (CurrentCacheObject.Object is DiaGizmo)
                {
                    untargetable = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.Untargetable) > 0;
                }
            }
            catch
            {
            }


            // Anything that's Invulnerable
            bool invulnerable = false;

            try
            {
                if (CurrentCacheObject.Object is DiaGizmo)
                {
                    invulnerable = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.Invulnerable) > 0;
                }
            }
            catch
            {
            }

            bool noDamage = false;

            try
            {
                if (CurrentCacheObject.Object is DiaGizmo)
                {
                    noDamage = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.NoDamage) > 0;
                }
            }
            catch
            {
                CacheData.NavigationObstacles.Add(new CacheObstacleObject
                {
                    ActorSNO   = CurrentCacheObject.ActorSNO,
                    Radius     = CurrentCacheObject.Radius,
                    Position   = CurrentCacheObject.Position,
                    RActorGUID = CurrentCacheObject.RActorGuid,
                    ObjectType = CurrentCacheObject.Type,
                });

                Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement,
                           "Safely handled exception getting NoDamage attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO);
                c_IgnoreSubStep = "NoDamage";
                AddToCache      = false;
            }

            double minDistance;
            bool   gizmoUsed = false;

            switch (CurrentCacheObject.Type)
            {
            case GObjectType.Door:
            {
                AddToCache = true;

                if (c_diaObject is DiaGizmo && ((DiaGizmo)c_diaObject).HasBeenOperated)
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "Door has been operated";
                    return(AddToCache);
                }

                try
                {
                    string currentAnimation = CurrentCacheObject.CommonData.CurrentAnimation.ToString().ToLower();
                    gizmoUsed = currentAnimation.EndsWith("open") || currentAnimation.EndsWith("opening");

                    // special hax for A3 Iron Gates
                    if (currentAnimation.Contains("irongate") && currentAnimation.Contains("open"))
                    {
                        gizmoUsed = false;
                    }
                    if (currentAnimation.Contains("irongate") && currentAnimation.Contains("idle"))
                    {
                        gizmoUsed = true;
                    }
                }
                catch { }
                if (gizmoUsed)
                {
                    Blacklist3Seconds.Add(CurrentCacheObject.RActorGuid);
                    AddToCache      = false;
                    c_IgnoreSubStep = "Door is Open or Opening";
                    return(AddToCache);
                }

                try
                {
                    int gizmoState = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoState);
                    if (gizmoState == 1)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "GizmoState=1";
                        return(AddToCache);
                    }
                }
                catch
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "GizmoStateException";
                    return(AddToCache);
                }

                if (untargetable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Untargetable";
                    return(AddToCache);
                }


                if (AddToCache)
                {
                    try
                    {
                        DiaGizmo door = null;
                        if (c_diaObject is DiaGizmo)
                        {
                            door = (DiaGizmo)c_diaObject;

                            if (door != null && door.IsGizmoDisabledByScript)
                            {
                                CacheData.NavigationObstacles.Add(new CacheObstacleObject
                                    {
                                        ActorSNO   = CurrentCacheObject.ActorSNO,
                                        Radius     = CurrentCacheObject.Radius,
                                        Position   = CurrentCacheObject.Position,
                                        RActorGUID = CurrentCacheObject.RActorGuid,
                                        ObjectType = CurrentCacheObject.Type,
                                    });

                                Blacklist3Seconds.Add(CurrentCacheObject.RActorGuid);
                                AddToCache      = false;
                                c_IgnoreSubStep = "DoorDisabledbyScript";
                                return(AddToCache);
                            }
                        }
                        else
                        {
                            AddToCache      = false;
                            c_IgnoreSubStep = "InvalidCastToDoor";
                        }
                    }

                    catch { }
                }
            }
            break;

            case GObjectType.Interactable:
            {
                AddToCache = true;

                if (untargetable)
                {
                    ((MainGridProvider)MainGridProvider).AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject()
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Untargetable";
                    return(AddToCache);
                }


                int endAnimation;
                if (DataDictionary.InteractEndAnimations.TryGetValue(CurrentCacheObject.ActorSNO, out endAnimation))
                {
                    if (endAnimation == (int)CurrentCacheObject.Gizmo.CommonData.CurrentAnimation)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "EndAnimation";
                        return(AddToCache);
                    }
                }

                if (CurrentCacheObject.Gizmo != null && CurrentCacheObject.Gizmo is GizmoLootContainer && CurrentCacheObject.Gizmo.HasBeenOperated)
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "GizmoHasBeenOperated";
                    return(AddToCache);
                }

                CurrentCacheObject.Radius = c_diaObject.CollisionSphere.Radius;
            }
            break;

            case GObjectType.HealthWell:
            {
                AddToCache = true;
                try
                {
                    gizmoUsed = (CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoCharges) <= 0 && CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoCharges) > 0);
                }
                catch
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting shrine-been-operated attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO);
                    AddToCache = true;
                    //return bWantThis;
                }
                try
                {
                    int gizmoState = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoState);
                    if (gizmoState == 1)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "GizmoState=1";
                        return(AddToCache);
                    }
                }
                catch
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "GizmoStateException";
                    return(AddToCache);
                }
                if (gizmoUsed)
                {
                    c_IgnoreSubStep = "GizmoCharges";
                    AddToCache      = false;
                    return(AddToCache);
                }
                if (untargetable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Untargetable";
                    return(AddToCache);
                }
            }
            break;

            case GObjectType.CursedShrine:
            case GObjectType.Shrine:
            {
                AddToCache = true;
                // Shrines
                // Check if either we want to ignore all shrines
                if (!Settings.WorldObject.UseShrine)
                {
                    // We're ignoring all shrines, so blacklist this one
                    c_IgnoreSubStep = "IgnoreAllShrinesSet";
                    AddToCache      = false;
                    return(AddToCache);
                }

                try
                {
                    int gizmoState = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoState);
                    if (gizmoState == 1)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "GizmoState=1";
                        return(AddToCache);
                    }
                }
                catch
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "GizmoStateException";
                    return(AddToCache);
                }
                if (untargetable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Untargetable";
                    return(AddToCache);
                }


                // Determine what shrine type it is, and blacklist if the user has disabled it
                switch (CurrentCacheObject.ActorSNO)
                {
                case 176077:              //Frenzy Shrine
                    if (!Settings.WorldObject.UseFrenzyShrine)
                    {
                        Blacklist60Seconds.Add(CurrentCacheObject.RActorGuid);
                        c_IgnoreSubStep = "IgnoreShrineOption";
                        AddToCache      = false;
                    }
                    if (Player.ActorClass == ActorClass.Monk && Settings.Combat.Monk.TROption.HasFlag(TempestRushOption.MovementOnly) && Hotbar.Contains(SNOPower.Monk_TempestRush))
                    {
                        // Frenzy shrines are a huge time sink for monks using Tempest Rush to move, we should ignore them.
                        AddToCache      = false;
                        c_IgnoreSubStep = "IgnoreShrineOption";
                        return(AddToCache);
                    }
                    break;

                case 176076:              //Fortune Shrine
                    if (!Settings.WorldObject.UseFortuneShrine)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "IgnoreShrineOption";
                        return(AddToCache);
                    }
                    break;

                case 176074:              //Protection Shrine
                    if (!Settings.WorldObject.UseProtectionShrine)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "IgnoreShrineOption";
                        return(AddToCache);
                    }
                    break;

                case 260330:              //Empowered Shrine
                    if (!Settings.WorldObject.UseEmpoweredShrine)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "IgnoreShrineOption";
                        return(AddToCache);
                    }
                    break;

                case 176075:              //Enlightened Shrine
                    if (!Settings.WorldObject.UseEnlightenedShrine)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "IgnoreShrineOption";
                        return(AddToCache);
                    }
                    break;

                case 260331:              //Fleeting Shrine
                    if (!Settings.WorldObject.UseFleetingShrine)
                    {
                        AddToCache      = false;
                        c_IgnoreSubStep = "IgnoreShrineOption";
                        return(AddToCache);
                    }
                    break;
                }          //end switch

                // Bag it!
                CurrentCacheObject.Radius = 4f;
                break;
            }

            case GObjectType.Barricade:
            {
                AddToCache = true;

                if (noDamage)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "NoDamage";
                    return(AddToCache);
                }
                if (untargetable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Untargetable";
                    return(AddToCache);
                }


                if (invulnerable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Invulnerable";
                    return(AddToCache);
                }

                float maxRadiusDistance;

                if (DataDictionary.DestructableObjectRadius.TryGetValue(CurrentCacheObject.ActorSNO, out maxRadiusDistance))
                {
                    if (CurrentCacheObject.RadiusDistance < maxRadiusDistance)
                    {
                        AddToCache      = true;
                        c_IgnoreSubStep = "";
                    }
                }

                if (DateTime.UtcNow.Subtract(PlayerMover.LastGeneratedStuckPosition).TotalSeconds <= 1)
                {
                    AddToCache      = true;
                    c_IgnoreSubStep = "";
                    break;
                }

                // Set min distance to user-defined setting
                minDistance = Settings.WorldObject.DestructibleRange + CurrentCacheObject.Radius;
                if (_forceCloseRangeTarget)
                {
                    minDistance += 6f;
                }

                // This object isn't yet in our destructible desire range
                if (minDistance <= 0 || CurrentCacheObject.RadiusDistance > minDistance)
                {
                    c_IgnoreSubStep = "NotInBarricadeRange";
                    AddToCache      = false;
                    return(AddToCache);
                }

                break;
            }

            case GObjectType.Destructible:
            {
                AddToCache = true;

                if (noDamage)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "NoDamage";
                    return(AddToCache);
                }

                if (invulnerable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Invulnerable";
                    return(AddToCache);
                }
                if (untargetable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Untargetable";
                    return(AddToCache);
                }


                if (Player.ActorClass == ActorClass.Monk && Hotbar.Contains(SNOPower.Monk_TempestRush) && TimeSinceUse(SNOPower.Monk_TempestRush) <= 150)
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "MonkTR";
                    break;
                }

                if (Player.ActorClass == ActorClass.Monk && Hotbar.Contains(SNOPower.Monk_SweepingWind) && GetHasBuff(SNOPower.Monk_SweepingWind))
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "MonkSW";
                    break;
                }

                if (CurrentCacheObject.IsQuestMonster || CurrentCacheObject.IsMinimapActive)
                {
                    AddToCache      = true;
                    c_IgnoreSubStep = "";
                    break;
                }

                if (!DataDictionary.ForceDestructibles.Contains(CurrentCacheObject.ActorSNO) && Settings.WorldObject.DestructibleOption == DestructibleIgnoreOption.ForceIgnore)
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "ForceIgnoreDestructibles";
                    break;
                }

                if (DateTime.UtcNow.Subtract(PlayerMover.LastGeneratedStuckPosition).TotalSeconds <= 1)
                {
                    AddToCache      = true;
                    c_IgnoreSubStep = "";
                    break;
                }

                // Set min distance to user-defined setting
                minDistance = Settings.WorldObject.DestructibleRange;
                if (_forceCloseRangeTarget)
                {
                    minDistance += 6f;
                }

                // Only break destructables if we're stuck and using IgnoreNonBlocking
                if (Settings.WorldObject.DestructibleOption == DestructibleIgnoreOption.DestroyAll)
                {
                    minDistance    += 12f;
                    AddToCache      = true;
                    c_IgnoreSubStep = "";
                }

                float maxRadiusDistance;

                if (DataDictionary.DestructableObjectRadius.TryGetValue(CurrentCacheObject.ActorSNO, out maxRadiusDistance))
                {
                    if (CurrentCacheObject.RadiusDistance < maxRadiusDistance)
                    {
                        AddToCache      = true;
                        c_IgnoreSubStep = "";
                    }
                }
                // Always add large destructibles within ultra close range
                if (!AddToCache && CurrentCacheObject.Radius >= 10f && CurrentCacheObject.RadiusDistance <= 5.9f)
                {
                    AddToCache      = true;
                    c_IgnoreSubStep = "";
                    break;
                }

                // This object isn't yet in our destructible desire range
                if (AddToCache && (minDistance <= 1 || CurrentCacheObject.RadiusDistance > minDistance) && PlayerMover.GetMovementSpeed() >= 1)
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "NotInDestructableRange";
                }
                if (AddToCache && CurrentCacheObject.RadiusDistance <= 2f && DateTime.UtcNow.Subtract(PlayerMover.LastGeneratedStuckPosition).TotalMilliseconds > 500)
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "NotStuck2";
                }

                // Add if we're standing still and bumping into it
                if (CurrentCacheObject.RadiusDistance <= 2f && PlayerMover.GetMovementSpeed() <= 0)
                {
                    AddToCache      = true;
                    c_IgnoreSubStep = "";
                }

                if (CurrentCacheObject.RActorGuid == LastTargetRactorGUID)
                {
                    AddToCache      = true;
                    c_IgnoreSubStep = "";
                }

                break;
            }

            case GObjectType.CursedChest:
            case GObjectType.Container:
            {
                AddToCache = false;

                bool isRareChest = CurrentCacheObject.InternalName.ToLower().Contains("chest_rare") || DataDictionary.ResplendentChestIds.Contains(CurrentCacheObject.ActorSNO);
                bool isChest     = (!isRareChest && CurrentCacheObject.InternalName.ToLower().Contains("chest")) ||
                                   DataDictionary.ContainerWhiteListIds.Contains(CurrentCacheObject.ActorSNO); // We know it's a container but this is not a known rare chest
                bool isCorpse       = CurrentCacheObject.InternalName.ToLower().Contains("corpse");
                bool isWeaponRack   = CurrentCacheObject.InternalName.ToLower().Contains("rack");
                bool isGroundClicky = CurrentCacheObject.InternalName.ToLower().Contains("ground_clicky");

                // We want to do some vendoring, so don't open anything new yet
                if (ForceVendorRunASAP)
                {
                    AddToCache      = false;
                    c_IgnoreSubStep = "ForceVendorRunASAP";
                }

                // Already open, blacklist it and don't look at it again
                bool chestOpen;
                try
                {
                    chestOpen = (CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.ChestOpen) > 0);
                }
                catch
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting container-been-opened attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO);
                    c_IgnoreSubStep = "ChestOpenException";
                    AddToCache      = false;
                    return(AddToCache);
                }

                // Check if chest is open
                if (chestOpen)
                {
                    // It's already open!
                    AddToCache      = false;
                    c_IgnoreSubStep = "AlreadyOpen";
                    return(AddToCache);
                }

                if (untargetable)
                {
                    MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius);
                    CacheData.NavigationObstacles.Add(new CacheObstacleObject
                        {
                            ActorSNO   = CurrentCacheObject.ActorSNO,
                            Radius     = CurrentCacheObject.Radius,
                            Position   = CurrentCacheObject.Position,
                            RActorGUID = CurrentCacheObject.RActorGuid,
                            ObjectType = CurrentCacheObject.Type,
                        });

                    AddToCache      = false;
                    c_IgnoreSubStep = "Untargetable";
                    return(AddToCache);
                }

                // Resplendent chests have no range check
                if (isRareChest && Settings.WorldObject.OpenRareChests)
                {
                    AddToCache = true;
                    return(AddToCache);
                }

                // Regular container, check range
                if (CurrentCacheObject.RadiusDistance <= Settings.WorldObject.ContainerOpenRange)
                {
                    if (isChest && Settings.WorldObject.OpenContainers)
                    {
                        return(true);
                    }

                    if (isCorpse && Settings.WorldObject.InspectCorpses)
                    {
                        return(true);
                    }

                    if (isGroundClicky && Settings.WorldObject.InspectGroundClicky)
                    {
                        return(true);
                    }

                    if (isWeaponRack && Settings.WorldObject.InspectWeaponRacks)
                    {
                        return(true);
                    }
                }

                if (CurrentCacheObject.IsQuestMonster)
                {
                    AddToCache = true;
                    return(AddToCache);
                }

                if (Settings.WorldObject.OpenAnyContainer)
                {
                    AddToCache = true;
                    return(AddToCache);
                }

                if (!isChest && !isCorpse && !isRareChest)
                {
                    c_IgnoreSubStep = "InvalidContainer";
                }
                else
                {
                    c_IgnoreSubStep = "IgnoreContainer";
                }
                break;
            }
            }
            return(AddToCache);
        }
Beispiel #27
0
        private static TrinityPower GetWizardPower(bool isCurrentlyAvoiding, bool useOocBuff, bool useDestructiblePower)
        {
            // Pick the best destructible power available
            if (useDestructiblePower)
            {
                if (!GetHasBuff(SNOPower.Wizard_Archon))
                {
                    return(GetWizardDestructablePower());
                }
                if (CurrentTarget.RadiusDistance <= 10f)
                {
                    return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneStrike, 20f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
                }
                return(new TrinityPower(SNOPower.Wizard_Archon_DisintegrationWave, 19f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
            }
            // Wizards want to save up to a reserve of 65+ energy
            MinEnergyReserve = 45;

            if (!GetHasBuff(SNOPower.Wizard_Archon))
            {
                bool hasIllusionist = HotbarSkills.PassiveSkills.Any(p => p == SNOPower.Wizard_Passive_Illusionist);

                // Illusionist speed boost
                if (hasIllusionist && useOocBuff)
                {
                    // Slow Time on self for speed boost
                    if (CombatBase.CanCast(SNOPower.Wizard_SlowTime))
                    {
                        return(new TrinityPower(SNOPower.Wizard_SlowTime));
                    }

                    // Mirror Image for speed boost
                    if (CombatBase.CanCast(SNOPower.Wizard_MirrorImage))
                    {
                        return(new TrinityPower(SNOPower.Wizard_MirrorImage));
                    }

                    // Teleport already called from PlayerMover, not here (since it's a "movement" spell, not a buff)
                }


                bool hasCalamity = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 0);
                // Offensive Teleport: Calamity
                if (CombatBase.CanCast(SNOPower.Wizard_Teleport) && hasCalamity)
                {
                    var bestClusterPoint = TargetUtil.GetBestClusterPoint();
                    return(new TrinityPower(SNOPower.Wizard_Teleport, 55f, bestClusterPoint));
                }

                bool hasSafePassage = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 1);
                // Defensive Teleport: SafePassage
                if (CombatBase.CanCast(SNOPower.Wizard_Teleport) && hasSafePassage &&
                    TimeSinceUse(SNOPower.Wizard_Teleport) >= 5000 && Player.CurrentHealthPct <= 0.75 &&
                    (CurrentTarget.IsBossOrEliteRareUnique || TargetUtil.IsEliteTargetInRange(30f)))
                {
                    return(new TrinityPower(SNOPower.Wizard_Teleport, 1f, Player.Position));
                }

                // Black Hole experiment
                //spell steal
                //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/3/HotbarSlot1 None/-1/HotbarSlot2
                //blazar
                //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/2/HotbarSlot1 None/-1/HotbarSlot2
                //event horizon
                //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/1/HotbarSlot1 None/-1/HotbarSlot2
                //absolute zero
                //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/4/HotbarSlot1 None/-1/HotbarSlot2
                //super massive
                //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/0/HotbarSlot1 None/-1/HotbarSlot2
                //no rune
                //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/-1/HotbarSlot1 None/-1/HotbarSlot2

                bool  hasSupermassive = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_Wizard_Wormhole && s.RuneIndex == 0);
                float blackholeRadius = hasSupermassive ? 20f : 15f;
                if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.X1_Wizard_Wormhole, CombatBase.CanCastFlags.NoTimer) &&
                    TargetUtil.ClusterExists(blackholeRadius, 45f, 4))
                {
                    return(new TrinityPower(SNOPower.X1_Wizard_Wormhole, 45f, TargetUtil.GetBestClusterUnit(blackholeRadius, 45f, 1, false).Position));
                }

                bool arcaneDynamoPassiveReady =
                    (HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Wizard_Passive_ArcaneDynamo) && GetBuffStacks(SNOPower.Wizard_Passive_ArcaneDynamo) == 5);

                var bestMeteorClusterUnit = TargetUtil.GetBestClusterUnit();
                // Meteor: Arcane Dynamo
                if (!useOocBuff && !Player.IsIncapacitated && !arcaneDynamoPassiveReady && CombatBase.CanCast(SNOPower.Wizard_Meteor, CombatBase.CanCastFlags.NoTimer) &&
                    (TargetUtil.EliteOrTrashInRange(65) || TargetUtil.ClusterExists(15f, 65, 2)))
                {
                    return(new TrinityPower(SNOPower.Wizard_Meteor, 65f, bestMeteorClusterUnit.Position));
                }

                // Diamond Skin SPAM
                if (!useOocBuff && CombatBase.CanCast(SNOPower.Wizard_DiamondSkin) && LastPowerUsed != SNOPower.Wizard_DiamondSkin && !GetHasBuff(SNOPower.Wizard_DiamondSkin) &&
                    (TargetUtil.AnyElitesInRange(25, 1) || TargetUtil.AnyMobsInRange(25, 1) || Player.CurrentHealthPct <= 0.90 || Player.IsIncapacitated || Player.IsRooted || CurrentTarget.RadiusDistance <= 40f))
                {
                    return(new TrinityPower(SNOPower.Wizard_DiamondSkin));
                }

                // Diamond Skin off CD
                bool hasSleekShell = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_DiamondSkin && s.RuneIndex == 0);
                if (hasSleekShell && CombatBase.CanCast(SNOPower.Wizard_DiamondSkin))
                {
                    return(new TrinityPower(SNOPower.Wizard_DiamondSkin));
                }

                // Slow Time for in combat
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_SlowTime, CombatBase.CanCastFlags.NoTimer) &&
                    (TargetUtil.AnyElitesInRange(25, 1) || TargetUtil.AnyMobsInRange(25, 2) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 40f)) &&
                    (CombatBase.TimeSpanSincePowerUse(SNOPower.Wizard_SlowTime) > TimeSpan.FromSeconds(15) || SpellHistory.DistanceFromLastTarget(SNOPower.Wizard_SlowTime) > 30f))
                {
                    if (TargetUtil.AnyMobsInRange(20f))
                    {
                        return(new TrinityPower(SNOPower.Wizard_SlowTime)); // cast of Self
                    }
                    return(new TrinityPower(SNOPower.Wizard_SlowTime, 55f, TargetUtil.GetBestClusterUnit(20f).Position));
                }

                // Mirror Image  @ half health or 5+ monsters or rooted/incapacitated or last elite left @25% health
                if (!useOocBuff && CombatBase.CanCast(SNOPower.Wizard_MirrorImage, CombatBase.CanCastFlags.NoTimer) &&
                    (Player.CurrentHealthPct <= PlayerEmergencyHealthPotionLimit || TargetUtil.AnyMobsInRange(30, 4) || Player.IsIncapacitated || Player.IsRooted ||
                     TargetUtil.AnyElitesInRange(30) || CurrentTarget.IsBossOrEliteRareUnique))
                {
                    return(new TrinityPower(SNOPower.Wizard_MirrorImage, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
                }

                // Familiar
                if (!Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Familiar) && Player.PrimaryResource >= 20 && !Wizard_HasFamiliar())
                {
                    return(new TrinityPower(SNOPower.Wizard_Familiar, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2));
                }

                // The three wizard armors, done in an else-if loop so it doesn't keep replacing one with the other
                if (!Player.IsIncapacitated && Player.PrimaryResource >= 25)
                {
                    // Energy armor as priority cast if available and not buffed
                    if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor))
                    {
                        if ((!GetHasBuff(SNOPower.Wizard_EnergyArmor) && PowerManager.CanCast(SNOPower.Wizard_EnergyArmor)) || (Hotbar.Contains(SNOPower.Wizard_Archon) && (!GetHasBuff(SNOPower.Wizard_EnergyArmor) || SNOPowerUseTimer(SNOPower.Wizard_EnergyArmor))))
                        {
                            return(new TrinityPower(SNOPower.Wizard_EnergyArmor, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2));
                        }
                    }
                    // Ice Armor
                    else if (Hotbar.Contains(SNOPower.Wizard_IceArmor))
                    {
                        if (!GetHasBuff(SNOPower.Wizard_IceArmor) && PowerManager.CanCast(SNOPower.Wizard_IceArmor))
                        {
                            return(new TrinityPower(SNOPower.Wizard_IceArmor, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2));
                        }
                    }
                    // Storm Armor
                    else if (Hotbar.Contains(SNOPower.Wizard_StormArmor))
                    {
                        if (!GetHasBuff(SNOPower.Wizard_StormArmor) && PowerManager.CanCast(SNOPower.Wizard_StormArmor))
                        {
                            return(new TrinityPower(SNOPower.Wizard_StormArmor, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2));
                        }
                    }
                }
                // Magic Weapon (10 minutes)
                if (!Player.IsIncapacitated && Player.PrimaryResource >= 25 && CombatBase.CanCast(SNOPower.Wizard_MagicWeapon) && !GetHasBuff(SNOPower.Wizard_MagicWeapon))
                {
                    return(new TrinityPower(SNOPower.Wizard_MagicWeapon, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2));
                }

                // Hydra
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Hydra, CombatBase.CanCastFlags.NoTimer) &&
                    (CombatBase.TimeSpanSincePowerUse(SNOPower.Wizard_Hydra) > TimeSpan.FromSeconds(15) && SpellHistory.DistanceFromLastTarget(SNOPower.Wizard_Hydra) > 30f) && //LastPowerUsed != SNOPower.Wizard_Hydra &&
                    (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 4) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f)) &&
                    Player.PrimaryResource >= 15)
                {
                    // For distant monsters, try to target a little bit in-front of them (as they run towards us), if it's not a treasure goblin
                    float fExtraDistance = 0f;
                    if (CurrentTarget.Distance > 17f && !CurrentTarget.IsTreasureGoblin)
                    {
                        fExtraDistance = CurrentTarget.Distance - 17f;
                        if (fExtraDistance > 5f)
                        {
                            fExtraDistance = 5f;
                        }
                        if (CurrentTarget.Distance - fExtraDistance < 15f)
                        {
                            fExtraDistance -= 2;
                        }
                    }
                    Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, CurrentTarget.Distance - fExtraDistance);
                    return(new TrinityPower(SNOPower.Wizard_Hydra, 30f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2));
                }

                // Archon
                if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_Archon, CombatBase.CanCastFlags.NoTimer) && Wizard_ShouldStartArchon())
                {
                    //CanCastArchon = false;
                    //return new TrinityPower(SNOPower.Wizard_Archon, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 4, 5);

                    // Familiar has been removed for now. Uncomment the three comments below relating to familiars to force re-buffing them
                    int reserveArcanePower = 0;
                    if (Hotbar.Contains(SNOPower.Wizard_MagicWeapon))
                    {
                        reserveArcanePower += 25;
                    }
                    if (Hotbar.Contains(SNOPower.Wizard_Familiar))
                    {
                        reserveArcanePower += 25;
                    }
                    if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor) || Hotbar.Contains(SNOPower.Wizard_IceArmor) ||
                        Hotbar.Contains(SNOPower.Wizard_StormArmor))
                    {
                        reserveArcanePower += 25;
                    }

                    bool hasBuffSpells =
                        (Hotbar.Contains(SNOPower.Wizard_MagicWeapon) ||
                         Hotbar.Contains(SNOPower.Wizard_Familiar) ||
                         Hotbar.Contains(SNOPower.Wizard_EnergyArmor) ||
                         Hotbar.Contains(SNOPower.Wizard_IceArmor) ||
                         Hotbar.Contains(SNOPower.Wizard_StormArmor));

                    CanCastArchon = //Player.PrimaryResource >= reserveArcanePower ||
                                    (
                        //hasBuffSpells &&
                        CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) &&
                        (!Hotbar.Contains(SNOPower.Wizard_Familiar) || Wizard_HasFamiliar()) &&
                        CheckAbilityAndBuff(SNOPower.Wizard_EnergyArmor) &&
                        CheckAbilityAndBuff(SNOPower.Wizard_IceArmor) &&
                        CheckAbilityAndBuff(SNOPower.Wizard_StormArmor));

                    if (CanCastArchon)
                    {
                        Player.WaitingForReserveEnergy = false;
                        CanCastArchon = false;
                        ShouldRefreshHotbarAbilities = true;
                        return(new TrinityPower(SNOPower.Wizard_Archon, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 4, 5));
                    }
                    Player.WaitingForReserveEnergy = true;
                }
                // Explosive Blast
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_ExplosiveBlast, CombatBase.CanCastFlags.NoTimer) && Player.PrimaryResource >= 20)
                {
                    return(new TrinityPower(SNOPower.Wizard_ExplosiveBlast, 12f, CurrentTarget.Position));
                }

                //SkillDict.Add("Blizzard", SNOPower.Wizard_Blizzard);
                //RuneDict.Add("GraspingChill", 2);
                //RuneDict.Add("FrozenSolid", 4);
                //RuneDict.Add("Snowbound", 3);
                //RuneDict.Add("StarkWinter", 1);
                //RuneDict.Add("UnrelentingStorm", 0);

                bool hasSnowBoundRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Blizzard && s.RuneIndex == 3);

                // Blizzard
                if (!useOocBuff && !Player.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Blizzard) &&
                    (TargetUtil.ClusterExists(18f, 90f, 2, false) || TargetUtil.AnyElitesInRange(40f) || TargetUtil.IsEliteTargetInRange(45f)) &&
                    (Player.PrimaryResource >= 40 || (hasSnowBoundRune && Player.PrimaryResource >= 20)) && SNOPowerUseTimer(SNOPower.Wizard_Blizzard))
                {
                    var bestClusterPoint = TargetUtil.GetBestClusterPoint(18f, 45f, false);
                    return(new TrinityPower(SNOPower.Wizard_Blizzard, 45f, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1));
                }

                bool hasArcaneDynamo = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Wizard_Passive_ArcaneDynamo);

                // Meteor - no arcane dynamo
                if (!useOocBuff && !Player.IsIncapacitated && !hasArcaneDynamo && CombatBase.CanCast(SNOPower.Wizard_Meteor, CombatBase.CanCastFlags.NoTimer) &&
                    (TargetUtil.EliteOrTrashInRange(65) || TargetUtil.ClusterExists(15f, 65, 2)))
                {
                    return(new TrinityPower(SNOPower.Wizard_Meteor, 65f, bestMeteorClusterUnit.Position));
                }

                //SkillDict.Add("FrostNova", SNOPower.Wizard_FrostNova);
                //RuneDict.Add("Shatter", 1);
                //RuneDict.Add("ColdSnap", 3);
                //RuneDict.Add("FrozenMist", 2);
                //RuneDict.Add("DeepFreeze", 4);
                //RuneDict.Add("BoneChill", 0);

                bool hasDeepFreeze = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_FrostNova && s.RuneIndex == 4);

                // Frost Nova
                if (!useOocBuff && CombatBase.CanCast(SNOPower.Wizard_FrostNova) && !Player.IsIncapacitated &&
                    ((hasDeepFreeze && TargetUtil.AnyMobsInRange(25, 5)) || (!hasDeepFreeze && (TargetUtil.AnyMobsInRange(25, 1) || Player.CurrentHealthPct <= 0.7)) &&
                     CurrentTarget.RadiusDistance <= 25f))
                {
                    return(new TrinityPower(SNOPower.Wizard_FrostNova, 20f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 2));
                }

                // Check to see if we have a signature spell on our hotbar, for energy twister check
                bool bHasSignatureSpell = (Hotbar.Contains(SNOPower.Wizard_MagicMissile) || Hotbar.Contains(SNOPower.Wizard_ShockPulse) ||
                                           Hotbar.Contains(SNOPower.Wizard_SpectralBlade) || Hotbar.Contains(SNOPower.Wizard_Electrocute));

                //SkillDict.Add("EnergyTwister", SNOPower.Wizard_EnergyTwister);
                //RuneDict.Add("MistralBreeze", 3);
                //RuneDict.Add("GaleForce", 0);
                //RuneDict.Add("RagingStorm", 1);
                //RuneDict.Add("WickedWind", 4);
                //RuneDict.Add("StromChaser", 2);

                bool hasWickedWindRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_EnergyTwister && s.RuneIndex == 4);

                // Energy Twister SPAMS whenever 35 or more ap to generate Arcane Power
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_EnergyTwister) &&
                    Player.PrimaryResource >= 35 &&
                    // If using storm chaser, then force a signature spell every 1 stack of the buff, if we have a signature spell
                    (!bHasSignatureSpell || GetBuffStacks(SNOPower.Wizard_EnergyTwister) < 1) &&
                    ((!hasWickedWindRune && CurrentTarget.RadiusDistance <= 25f) ||
                     (hasWickedWindRune && CurrentTarget.RadiusDistance <= 60f)) &&
                    (!Hotbar.Contains(SNOPower.Wizard_Electrocute) || !DataDictionary.FastMovingMonsterIds.Contains(CurrentTarget.ActorSNO)))
                {
                    Vector3 bestClusterPoint = TargetUtil.GetBestClusterPoint(10f, 15f);

                    const float twisterRange = 28f;
                    return(new TrinityPower(SNOPower.Wizard_EnergyTwister, twisterRange, bestClusterPoint, CurrentWorldDynamicId, -1, 0, 0));
                }

                // Wave of force
                if (!useOocBuff && !Player.IsIncapacitated && !isCurrentlyAvoiding && Player.PrimaryResource >= 25 && CombatBase.CanCast(SNOPower.Wizard_WaveOfForce, CombatBase.CanCastFlags.NoTimer))
                {
                    return(new TrinityPower(SNOPower.Wizard_WaveOfForce, 5f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 1, 2));
                }

                bool hasEntropy = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Disintegrate && s.RuneIndex == 2);

                float disintegrateRange = hasEntropy ? 10f : 35f;
                // Disintegrate
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Disintegrate) &&
                    ((Player.PrimaryResource >= 20 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve))
                {
                    return(new TrinityPower(SNOPower.Wizard_Disintegrate, disintegrateRange, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
                }
                // Arcane Orb
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_ArcaneOrb) &&
                    ((Player.PrimaryResource >= 30 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve))
                {
                    return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, 35f, CurrentTarget.ACDGuid));
                }
                // Arcane Torrent
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_ArcaneTorrent) &&
                    ((Player.PrimaryResource >= 16 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve))
                {
                    return(new TrinityPower(SNOPower.Wizard_ArcaneTorrent, 40f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
                }

                //skillDict.Add("RayOfFrost", SNOPower.Wizard_RayOfFrost);
                //runeDict.Add("Numb", 2);
                //runeDict.Add("SnowBlast", 0);
                //runeDict.Add("ColdBlood", 3);
                //runeDict.Add("SleetStorm", 1);
                //runeDict.Add("BlackIce", 4);

                bool hasSleetStorm = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_RayOfFrost && s.RuneIndex == 1);

                // Ray of Frost
                if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_RayOfFrost) &&
                    Player.PrimaryResource >= 12 && !Player.WaitingForReserveEnergy)
                {
                    float range = 50f;
                    if (hasSleetStorm)
                    {
                        range = 5f;
                    }

                    return(new TrinityPower(SNOPower.Wizard_RayOfFrost, range, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1));
                }

                bool hasConflagrate = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_MagicMissile && s.RuneIndex == 2);

                // Magic Missile
                if (!useOocBuff && !isCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_MagicMissile))
                {
                    var bestPierceTarget = TargetUtil.GetBestPierceTarget(45f);
                    int targetId;

                    if (bestPierceTarget != null)
                    {
                        targetId = hasConflagrate ?
                                   bestPierceTarget.ACDGuid :
                                   CurrentTarget.ACDGuid;
                    }
                    else
                    {
                        targetId = CurrentTarget.ACDGuid;
                    }

                    return(new TrinityPower(SNOPower.Wizard_MagicMissile, 45f, targetId));
                }
                // Shock Pulse
                if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_ShockPulse))
                {
                    return(new TrinityPower(SNOPower.Wizard_ShockPulse, 15f, CurrentTarget.ACDGuid));
                }
                // Spectral Blade
                if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_SpectralBlade))
                {
                    return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 14f, CurrentTarget.ACDGuid));
                }
                // Electrocute
                if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_Electrocute))
                {
                    return(new TrinityPower(SNOPower.Wizard_Electrocute, 40f, CurrentTarget.ACDGuid));
                }


                // Default attacks
                return(CombatBase.DefaultPower);
            }
            else
            {
                bool   cancelArchon = false;
                string reason       = "";

                if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffArmor && !Wizard_HasWizardArmor())
                {
                    reason      += "Rebuff Armor ";
                    cancelArchon = true;
                }
                if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffMagicWeaponFamiliar &&
                    (!CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) || !Wizard_HasFamiliar()))
                {
                    if (!CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon))
                    {
                        reason += "Rebuff Magic Weapon ";
                    }
                    if (!Wizard_HasFamiliar())
                    {
                        reason += "Rebuff Familiar ";
                    }
                    cancelArchon = true;
                }

                if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.Timer &&
                    DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_Archon]).TotalSeconds >= Settings.Combat.Wizard.ArchonCancelSeconds)
                {
                    reason      += "Timer";
                    cancelArchon = true;
                }

                if (cancelArchon && Wizard_ShouldStartArchon())
                {
                    var archonBuff = ZetaDia.Me.GetBuff(SNOPower.Wizard_Archon);
                    if (archonBuff != null && archonBuff.IsCancelable)
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Behavior, "Canceling Archon: {0}", reason);
                        // this actually cancels Archon
                        archonBuff.Cancel();

                        // this SNOPower is fake - it isn't actually used, we're just putting it here to force a BehaviorTree return/recheck
                        return(new TrinityPower(SNOPower.Wizard_Archon_Cancel, 0f, Vector3.Zero, -1, -1, -1, -1));
                    }
                }

                // Archon form
                // Archon Slow Time for in combat
                if (!useOocBuff && !Player.IsIncapacitated &&
                    CombatBase.CanCast(SNOPower.Wizard_Archon_SlowTime, CombatBase.CanCastFlags.NoTimer) &&
                    (CombatBase.TimeSpanSincePowerUse(SNOPower.Wizard_Archon_SlowTime) > TimeSpan.FromSeconds(30)))
                {
                    return(new TrinityPower(SNOPower.Wizard_Archon_SlowTime, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
                }

                // Archon Teleport in combat for kiting
                if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport, CombatBase.CanCastFlags.NoTimer) &&
                    Settings.Combat.Wizard.KiteLimit > 0 &&
                    // Try and teleport-retreat from 1 elite or 3+ greys or a boss at 15 foot range
                    (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 3) || (CurrentTarget.IsBoss && CurrentTarget.RadiusDistance <= 15f)))
                {
                    Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, -20f);
                    return(new TrinityPower(SNOPower.Wizard_Archon_Teleport, 35f, vNewTarget));
                }

                // Archon teleport in combat for no-kite
                if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport, CombatBase.CanCastFlags.NoTimer) &&
                    Settings.Combat.Wizard.KiteLimit == 0 && CurrentTarget.RadiusDistance >= 10f)
                {
                    return(new TrinityPower(SNOPower.Wizard_Archon_Teleport, 35f, CurrentTarget.Position));
                }
                //392694, 392695, 392696 == Arcane Strike,
                //392697, 392699, 392698 == Disintegration Wave
                //392692, 392693, 392691 == Arcane Blast, Ice Blast

                SNOPower
                    beamPower   = SNOPower.Wizard_Archon_ArcaneBlast,
                    strikePower = SNOPower.Wizard_Archon_ArcaneStrike,
                    blastPower  = SNOPower.Wizard_Archon_DisintegrationWave;

                HotbarSkills beamSkill = HotbarSkills.AssignedSkills
                                         .FirstOrDefault(p => p.Power == SNOPower.Wizard_Archon_DisintegrationWave || p.Power == (SNOPower)392697 || p.Power == (SNOPower)392699 || p.Power == (SNOPower)392698);

                HotbarSkills strikeSkill = HotbarSkills.AssignedSkills
                                           .FirstOrDefault(p => p.Power == SNOPower.Wizard_Archon_ArcaneStrike || p.Power == (SNOPower)392694 || p.Power == (SNOPower)392695 || p.Power == (SNOPower)392696);

                HotbarSkills blastSkill = HotbarSkills.AssignedSkills
                                          .FirstOrDefault(p => p.Power == SNOPower.Wizard_Archon_ArcaneBlast || p.Power == (SNOPower)392692 || p.Power == (SNOPower)392693 || p.Power == (SNOPower)392691);

                if (beamSkill != null && beamSkill.Power != default(SNOPower))
                {
                    beamPower = beamSkill.Power;
                }

                if (strikeSkill != null && strikeSkill.Power != default(SNOPower))
                {
                    strikePower = strikeSkill.Power;
                }

                if (blastSkill != null && blastSkill.Power != default(SNOPower))
                {
                    blastPower = blastSkill.Power;
                }

                // Arcane Blast - 2 second cooldown, big AoE
                if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_ArcaneBlast, CombatBase.CanCastFlags.NoTimer) && TargetUtil.AnyMobsInRange(15, 1) && CurrentTarget.IsFacingPlayer)
                {
                    return(new TrinityPower(blastPower, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1));
                }

                // Disintegrate
                if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && (CurrentTarget.CountUnitsBehind(25f) > 2 || Settings.Combat.Wizard.NoArcaneStrike || Settings.Combat.Wizard.KiteLimit > 0))
                {
                    return(new TrinityPower(beamPower, 49f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
                }

                // Arcane Strike Rapid Spam at close-range only, and no AoE inbetween us and target
                if (!useOocBuff && !Player.IsIncapacitated && !Settings.Combat.Wizard.NoArcaneStrike &&
                    !CacheData.TimeBoundAvoidance.Any(aoe => MathUtil.IntersectsPath(aoe.Position, aoe.Radius, Player.Position, CurrentTarget.Position)))
                {
                    return(new TrinityPower(strikePower, 7f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1));
                }

                // Disintegrate as final option just in case
                if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated)
                {
                    return(new TrinityPower(beamPower, 49f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0));
                }

                return(new TrinityPower(SNOPower.None, -1, Vector3.Zero, -1, -1, 0, 0));
            }
        }
Beispiel #28
0
        private static TrinityPower GetWizardPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower)
        {
            // TODO
            //- AI so Trinity knows that it is already in the densest monster area and not to teleport.
            //- AI to use teleport when ranged attacks are approaching (succubus slow moving torpedo-orb)
            //- AI to avoid AoE's (plague, molten, desecrator)
            //- For Trinity to cast one or two Twisters before moving into melee range or teleporting closer.

            // Pick the best destructible power available
            if (UseDestructiblePower)
            {
                if (!GetHasBuff(SNOPower.Wizard_Archon))
                {
                    return(GetWizardDestructablePower());
                }
                else
                {
                    if (CurrentTarget.RadiusDistance <= 10f)
                    {
                        return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneStrike, 20f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM));
                    }
                    return(new TrinityPower(SNOPower.Wizard_Archon_DisintegrationWave, 19f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM));
                }
            }
            // Wizards want to save up to a reserve of 65+ energy
            MinEnergyReserve = 65;

            bool hasCriticalMass = ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass);

            if (!GetHasBuff(SNOPower.Wizard_Archon))
            {
                // Slow Time for in combat
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_SlowTime) &&
                    (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 1 || PlayerStatus.CurrentHealthPct <= 0.7 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 35f)) &&
                    PowerManager.CanCast(SNOPower.Wizard_SlowTime))
                {
                    return(new TrinityPower(SNOPower.Wizard_SlowTime, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
                }
                // Wave of force
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 25 &&
                    (
                        // Check this isn't a critical mass wizard, cos they won't want to use this except for low health unless they don't have nova/blast in which case go for it
                        (hasCriticalMass && ((!Hotbar.Contains(SNOPower.Wizard_FrostNova) && !Hotbar.Contains(SNOPower.Wizard_ExplosiveBlast)) ||
                                             (PlayerStatus.CurrentHealthPct <= 0.7 && (TargetUtil.AnyMobsInRange(15f) || TargetUtil.IsEliteTargetInRange(23f)))))
                        // Else normal wizard in which case check standard stuff
                        || (!hasCriticalMass && ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 3 || PlayerStatus.CurrentHealthPct <= 0.7 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 23f))
                    ) &&
                    Hotbar.Contains(SNOPower.Wizard_WaveOfForce) &&
                    GilesUseTimer(SNOPower.Wizard_WaveOfForce, true) && PowerManager.CanCast(SNOPower.Wizard_WaveOfForce))
                {
                    return(new TrinityPower(SNOPower.Wizard_WaveOfForce, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                }

                //SkillDict.Add("Blizzard", SNOPower.Wizard_Blizzard);
                //RuneDict.Add("GraspingChill", 2);
                //RuneDict.Add("FrozenSolid", 4);
                //RuneDict.Add("Snowbound", 3);
                //RuneDict.Add("StarkWinter", 1);
                //RuneDict.Add("UnrelentingStorm", 0);

                bool hasSnowBoundRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Blizzard && s.RuneIndex == 3);

                // Blizzard
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Blizzard) &&
                    (TargetUtil.ClusterExists(45f, 2) || TargetUtil.AnyElitesInRange(40f) || TargetUtil.IsEliteTargetInRange(45f)) &&
                    (PlayerStatus.PrimaryResource >= 40 || (hasSnowBoundRune && PlayerStatus.PrimaryResource >= 20)) && GilesUseTimer(SNOPower.Wizard_Blizzard))
                {
                    var bestClusterPoint = TargetUtil.GetBestClusterPoint(18f, 45f);
                    return(new TrinityPower(SNOPower.Wizard_Blizzard, 45f, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
                }
                // Meteor
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Meteor) &&
                    (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 2 || CurrentTarget.IsEliteRareUnique || CurrentTarget.IsBoss || CurrentTarget.IsTreasureGoblin) &&
                    PlayerStatus.PrimaryResource >= 50 && PowerManager.CanCast(SNOPower.Wizard_Meteor))
                {
                    return(new TrinityPower(SNOPower.Wizard_Meteor, 21f, new Vector3(CurrentTarget.Position.X, CurrentTarget.Position.Y, CurrentTarget.Position.Z), CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                }
                // Teleport in combat for critical-mass wizards
                if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Teleport) && hasCriticalMass &&
                    LastPowerUsed != SNOPower.Wizard_Teleport &&
                    PlayerStatus.PrimaryResource >= 15 && CurrentTarget.CentreDistance <= 35f &&
                    PowerManager.CanCast(SNOPower.Wizard_Teleport))
                {
                    vSideToSideTarget = TargetUtil.GetBestClusterPoint(15f, 35f);
                    return(new TrinityPower(SNOPower.Wizard_Teleport, 35f, vSideToSideTarget, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                }

                // Diamond Skin SPAM
                if (Hotbar.Contains(SNOPower.Wizard_DiamondSkin) && LastPowerUsed != SNOPower.Wizard_DiamondSkin &&
                    (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 0 || PlayerStatus.CurrentHealthPct <= 0.90 || PlayerStatus.IsIncapacitated || PlayerStatus.IsRooted || (!UseOOCBuff && CurrentTarget.RadiusDistance <= 40f)) &&
                    ((hasCriticalMass && !UseOOCBuff) || !GetHasBuff(SNOPower.Wizard_DiamondSkin)) &&
                    PowerManager.CanCast(SNOPower.Wizard_DiamondSkin))
                {
                    return(new TrinityPower(SNOPower.Wizard_DiamondSkin, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM));
                }
                // Familiar
                if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Familiar) &&
                    PlayerStatus.PrimaryResource >= 20 && GilesUseTimer(SNOPower.Wizard_Familiar))
                {
                    return(new TrinityPower(SNOPower.Wizard_Familiar, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                }

                // The three wizard armors, done in an else-if loop so it doesn't keep replacing one with the other
                if (!PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 25)
                {
                    // Energy armor as priority cast if available and not buffed
                    if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor))
                    {
                        if ((!GetHasBuff(SNOPower.Wizard_EnergyArmor) && PowerManager.CanCast(SNOPower.Wizard_EnergyArmor)) || (Hotbar.Contains(SNOPower.Wizard_Archon) && (!GetHasBuff(SNOPower.Wizard_EnergyArmor) || GilesUseTimer(SNOPower.Wizard_EnergyArmor))))
                        {
                            return(new TrinityPower(SNOPower.Wizard_EnergyArmor, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                        }
                    }
                    // Ice Armor
                    else if (Hotbar.Contains(SNOPower.Wizard_IceArmor))
                    {
                        if (!GetHasBuff(SNOPower.Wizard_IceArmor) && PowerManager.CanCast(SNOPower.Wizard_IceArmor))
                        {
                            return(new TrinityPower(SNOPower.Wizard_IceArmor, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                        }
                    }
                    // Storm Armor
                    else if (Hotbar.Contains(SNOPower.Wizard_StormArmor))
                    {
                        if (!GetHasBuff(SNOPower.Wizard_StormArmor) || ((DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Wizard_StormArmor]).TotalMilliseconds >= 15000) && PowerManager.CanCast(SNOPower.Wizard_Archon)))
                        {
                            return(new TrinityPower(SNOPower.Wizard_StormArmor, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                        }
                    }
                }
                // Magic Weapon
                if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_MagicWeapon) && PowerManager.CanCast(SNOPower.Wizard_MagicWeapon) &&
                    (!GetHasBuff(SNOPower.Wizard_MagicWeapon) || ((DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Wizard_MagicWeapon]).TotalMilliseconds >= 10000) && PowerManager.CanCast(SNOPower.Wizard_Archon))))
                {
                    return(new TrinityPower(SNOPower.Wizard_MagicWeapon, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                }
                // Magic Weapon
                if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_MagicWeapon) &&
                    PlayerStatus.PrimaryResource >= 25 && (GilesUseTimer(SNOPower.Wizard_MagicWeapon) || !GetHasBuff(SNOPower.Wizard_MagicWeapon)))
                {
                    return(new TrinityPower(SNOPower.Wizard_MagicWeapon, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                }
                // Hydra
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated &&
                    LastPowerUsed != SNOPower.Wizard_Hydra &&
                    (ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 4 || PlayerStatus.CurrentHealthPct <= 0.7 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsBoss || CurrentTarget.IsTreasureGoblin) && CurrentTarget.RadiusDistance <= 15f)) &&
                    Hotbar.Contains(SNOPower.Wizard_Hydra) &&
                    PlayerStatus.PrimaryResource >= 15 && GilesUseTimer(SNOPower.Wizard_Hydra))
                {
                    // For distant monsters, try to target a little bit in-front of them (as they run towards us), if it's not a treasure goblin
                    float fExtraDistance = 0f;
                    if (CurrentTarget.CentreDistance > 17f && !CurrentTarget.IsTreasureGoblin)
                    {
                        fExtraDistance = CurrentTarget.CentreDistance - 17f;
                        if (fExtraDistance > 5f)
                        {
                            fExtraDistance = 5f;
                        }
                        if (CurrentTarget.CentreDistance - fExtraDistance < 15f)
                        {
                            fExtraDistance -= 2;
                        }
                    }
                    Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance - fExtraDistance);
                    return(new TrinityPower(SNOPower.Wizard_Hydra, 30f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM));
                }
                // Mirror Image  @ half health or 5+ monsters or rooted/incapacitated or last elite left @25% health
                if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_MirrorImage) &&
                    (PlayerStatus.CurrentHealthPct <= 0.50 || AnythingWithinRange[RANGE_30] >= 5 || PlayerStatus.IsIncapacitated || PlayerStatus.IsRooted || (ElitesWithinRange[RANGE_30] == 1 && CurrentTarget.IsEliteRareUnique && !CurrentTarget.IsBoss && CurrentTarget.HitPointsPct <= 0.35)) &&
                    PowerManager.CanCast(SNOPower.Wizard_MirrorImage))
                {
                    return(new TrinityPower(SNOPower.Wizard_MirrorImage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
                }
                // Archon
                if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_Archon) && Wizard_ShouldStartArchon() &&
                    PowerManager.CanCast(SNOPower.Wizard_Archon))
                {
                    // Familiar has been removed for now. Uncomment the three comments below relating to familiars to force re-buffing them
                    bool bHasBuffAbilities = (Hotbar.Contains(SNOPower.Wizard_MagicWeapon) ||
                                              //hashPowerHotbarAbilities.Contains(SNOPower.Wizard_Familiar) ||
                                              Hotbar.Contains(SNOPower.Wizard_EnergyArmor) || Hotbar.Contains(SNOPower.Wizard_IceArmor) ||
                                              Hotbar.Contains(SNOPower.Wizard_StormArmor));
                    int iExtraEnergyNeeded = 25;
                    if (Hotbar.Contains(SNOPower.Wizard_MagicWeapon))
                    {
                        iExtraEnergyNeeded += 25;
                    }
                    //if (hashPowerHotbarAbilities.Contains(SNOPower.Wizard_Familiar)) iExtraEnergyNeeded += 25;
                    if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor) || Hotbar.Contains(SNOPower.Wizard_IceArmor) ||
                        Hotbar.Contains(SNOPower.Wizard_StormArmor))
                    {
                        iExtraEnergyNeeded += 25;
                    }
                    if (!bHasBuffAbilities || PlayerStatus.PrimaryResource <= iExtraEnergyNeeded)
                    {
                        CanCastArchon = true;
                    }
                    if (!CanCastArchon)
                    {
                        dictAbilityLastUse[SNOPower.Wizard_MagicWeapon] = DateTime.Today;
                        //dictAbilityLastUse[SNOPower.Wizard_Familiar] = DateTime.Today;
                        dictAbilityLastUse[SNOPower.Wizard_EnergyArmor] = DateTime.Today;
                        dictAbilityLastUse[SNOPower.Wizard_IceArmor]    = DateTime.Today;
                        dictAbilityLastUse[SNOPower.Wizard_StormArmor]  = DateTime.Today;
                        CanCastArchon = true;
                    }
                    else
                    {
                        CanCastArchon = false;
                        return(new TrinityPower(SNOPower.Wizard_Archon, 0f, vNullLocation, CurrentWorldDynamicId, -1, 4, 5, WAIT_FOR_ANIM));
                    }
                }

                //SkillDict.Add("FrostNova", SNOPower.Wizard_FrostNova);
                //RuneDict.Add("Shatter", 1);
                //RuneDict.Add("ColdSnap", 3);
                //RuneDict.Add("FrozenMist", 2);
                //RuneDict.Add("DeepFreeze", 4);
                //RuneDict.Add("BoneChill", 0);

                bool hasDeepFreeze = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_FrostNova && s.RuneIndex == 4);

                // Frost Nova
                if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_FrostNova) && !PlayerStatus.IsIncapacitated &&
                    ((hasDeepFreeze && TargetUtil.AnyMobsInRange(25, 5)) || (!hasDeepFreeze && (TargetUtil.AnyMobsInRange(25, 1) || PlayerStatus.CurrentHealthPct <= 0.7)) &&
                     CurrentTarget.RadiusDistance <= 25f) &&
                    PowerManager.CanCast(SNOPower.Wizard_FrostNova))
                {
                    return(new TrinityPower(SNOPower.Wizard_FrostNova, 20f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
                }

                // Frost Nova for Critical Mass builds
                if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_FrostNova) && !PlayerStatus.IsIncapacitated &&
                    hasCriticalMass && TargetUtil.AnyMobsInRange(20, 1) && PowerManager.CanCast(SNOPower.Wizard_FrostNova))
                {
                    return(new TrinityPower(SNOPower.Wizard_FrostNova, 15f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
                }

                // Explosive Blast SPAM when enough AP, blow erry thing up, nah mean
                if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_ExplosiveBlast) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 20 &&
                    (TargetUtil.AnyMobsInRange(25) && CurrentTarget.RadiusDistance <= 25f) &&
                    PowerManager.CanCast(SNOPower.Wizard_ExplosiveBlast))
                {
                    float fThisRange = 11f;
                    if (hasCriticalMass)
                    {
                        fThisRange = 9f;
                    }
                    return(new TrinityPower(SNOPower.Wizard_ExplosiveBlast, fThisRange, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
                }

                // Check to see if we have a signature spell on our hotbar, for energy twister check
                bool bHasSignatureSpell = (Hotbar.Contains(SNOPower.Wizard_MagicMissile) || Hotbar.Contains(SNOPower.Wizard_ShockPulse) ||
                                           Hotbar.Contains(SNOPower.Wizard_SpectralBlade) || Hotbar.Contains(SNOPower.Wizard_Electrocute));

                //SkillDict.Add("EnergyTwister", SNOPower.Wizard_EnergyTwister);
                //RuneDict.Add("MistralBreeze", 3);
                //RuneDict.Add("GaleForce", 0);
                //RuneDict.Add("RagingStorm", 1);
                //RuneDict.Add("WickedWind", 4);
                //RuneDict.Add("StromChaser", 2);

                bool hasWickedWindRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_EnergyTwister && s.RuneIndex == 4);

                // Energy Twister SPAMS whenever 35 or more ap to generate Arcane Power
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_EnergyTwister) &&
                    PlayerStatus.PrimaryResource >= 35 &&
                    // If using storm chaser, then force a signature spell every 1 stack of the buff, if we have a signature spell
                    (!bHasSignatureSpell || GetBuffStacks(SNOPower.Wizard_EnergyTwister) < 1) &&
                    ((!hasWickedWindRune && CurrentTarget.RadiusDistance <= 25f) ||
                     (hasWickedWindRune && CurrentTarget.RadiusDistance <= 60f)) &&
                    (!Hotbar.Contains(SNOPower.Wizard_Electrocute) || !hashActorSNOFastMobs.Contains(CurrentTarget.ActorSNO)) &&
                    ((hasCriticalMass && !bHasSignatureSpell) || !hasCriticalMass))
                {
                    Vector3 targetDirection = MathEx.CalculatePointFrom(PlayerStatus.CurrentPosition, CurrentTarget.Position, 3f);

                    Vector3 bestClusterPoint = TargetUtil.GetBestClusterPoint(10f, 15f);

                    float twisterRange = 28f;
                    if (hasCriticalMass)
                    {
                        twisterRange = 9f;
                    }
                    return(new TrinityPower(SNOPower.Wizard_EnergyTwister, twisterRange, bestClusterPoint, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM));
                }

                // Disintegrate
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Disintegrate) &&
                    ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve))
                {
                    float fThisRange = 35f;
                    if (hasCriticalMass)
                    {
                        fThisRange = 20f;
                    }
                    return(new TrinityPower(SNOPower.Wizard_Disintegrate, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM));
                }
                // Arcane Orb
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_ArcaneOrb) &&
                    ((PlayerStatus.PrimaryResource >= 35 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) &&
                    GilesUseTimer(SNOPower.Wizard_ArcaneOrb))
                {
                    float fThisRange = 40f;
                    if (hasCriticalMass)
                    {
                        fThisRange = 20f;
                    }
                    return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM));
                }
                // Arcane Torrent
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_ArcaneTorrent) &&
                    ((PlayerStatus.PrimaryResource >= 16 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) &&
                    GilesUseTimer(SNOPower.Wizard_ArcaneTorrent))
                {
                    float fThisRange = 40f;

                    /*if (hasCriticalMass)
                     *  fThisRange = 20f;*/
                    return(new TrinityPower(SNOPower.Wizard_ArcaneTorrent, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM));
                }
                // Ray of Frost
                if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_RayOfFrost) &&
                    PlayerStatus.PrimaryResource >= 12)
                {
                    float fThisRange = 35f;
                    if (hasCriticalMass)
                    {
                        fThisRange = 20f;
                    }
                    return(new TrinityPower(SNOPower.Wizard_RayOfFrost, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM));
                }
                // Magic Missile
                if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_MagicMissile))
                {
                    float fThisRange = 35f;
                    if (hasCriticalMass)
                    {
                        fThisRange = 20f;
                    }
                    return(new TrinityPower(SNOPower.Wizard_MagicMissile, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM));
                }
                // Shock Pulse
                if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_ShockPulse))
                {
                    return(new TrinityPower(SNOPower.Wizard_ShockPulse, 15f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM));
                }
                // Spectral Blade
                if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_SpectralBlade))
                {
                    return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 14f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM));
                }
                // Electrocute
                if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_Electrocute))
                {
                    return(new TrinityPower(SNOPower.Wizard_Electrocute, 40f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, 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));
            }
            else
            {
                bool cancelArchon = false;

                if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffArmor && !Wizard_HasWizardArmor())
                {
                    cancelArchon = true;
                }

                if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffMagicWeaponFamiliar &&
                    (!CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) || !CheckAbilityAndBuff(SNOPower.Wizard_Familiar)))
                {
                    cancelArchon = true;
                }

                if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.Timer &&
                    DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Wizard_Archon]).TotalSeconds >= Settings.Combat.Wizard.ArchonCancelSeconds)
                {
                    cancelArchon = true;
                }

                if (cancelArchon && Wizard_ShouldStartArchon())
                {
                    var archonBuff = ZetaDia.Me.GetBuff(SNOPower.Wizard_Archon);
                    if (archonBuff != null && archonBuff.IsCancelable)
                    {
                        // this actually cancels Archon
                        archonBuff.Cancel();

                        // this SNOPower is fake - it isn't actually used, we're just putting it here to force a BehaviorTree return/recheck
                        return(new TrinityPower(SNOPower.Wizard_Archon_Cancel, 0f, vNullLocation, -1, -1, -1, -1, false));
                    }
                }

                // Archon form
                // Archon Slow Time for in combat
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated &&
                    (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 1 || PlayerStatus.CurrentHealthPct <= 0.7 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 35f)) &&
                    Hotbar.Contains(SNOPower.Wizard_Archon_SlowTime) &&
                    GilesUseTimer(SNOPower.Wizard_Archon_SlowTime, true) && PowerManager.CanCast(SNOPower.Wizard_Archon_SlowTime))
                {
                    return(new TrinityPower(SNOPower.Wizard_Archon_SlowTime, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
                }
                // Archon Teleport in combat
                if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) &&
                    // Try and teleport-retreat from 1 elite or 3+ greys or a boss at 15 foot range
                    (ElitesWithinRange[RANGE_15] >= 1 || AnythingWithinRange[RANGE_15] >= 3 || (CurrentTarget.IsBoss && CurrentTarget.RadiusDistance <= 15f)) &&
                    GilesUseTimer(SNOPower.Wizard_Archon_Teleport) && PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport))
                {
                    Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, -20f);
                    return(new TrinityPower(SNOPower.Wizard_Archon_Teleport, 35f, vNewTarget, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
                }
                // Arcane Blast
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated &&
                    (ElitesWithinRange[RANGE_15] >= 1 || AnythingWithinRange[RANGE_15] >= 1 ||
                     (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f)) &&
                    GilesUseTimer(SNOPower.Wizard_Archon_ArcaneBlast) && PowerManager.CanCast(SNOPower.Wizard_Archon_ArcaneBlast))
                {
                    return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneBlast, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM));
                }
                // Arcane Strike (Arcane Strike) Rapid Spam at close-range only
                if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && CurrentTarget.RadiusDistance <= 5f && TargetUtil.AnyMobsInRange(7f, 2) &&
                    CurrentTarget.IsBossOrEliteRareUnique && !Settings.Combat.Wizard.NoArcaneStrike)
                {
                    return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneStrike, 7f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM));
                }
                // Disintegrate
                if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated)
                {
                    return(new TrinityPower(SNOPower.Wizard_Archon_DisintegrationWave, 49f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM));
                }
                return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM));
            }
        }
Beispiel #29
0
        // Refresh object list from Diablo 3 memory RefreshDiaObjects()
        private static void RefreshCacheInit()
        {
            using (new PerformanceLogger("RefreshDiaObjectCache.CacheInit"))
            {
                // Update when we last refreshed with current time
                LastRefreshedCache = DateTime.Now;

                // Blank current/last/next targets
                LastPrimaryTargetPosition = CurrentTarget != null ? CurrentTarget.Position : vNullLocation;
                vKitePointAvoid           = vNullLocation;
                // store current target GUID
                CurrentTargetRactorGUID = CurrentTarget != null ? CurrentTarget.RActorGuid : -1;
                //reset current target
                CurrentTarget = null;
                // Reset all variables for target-weight finding
                AnyTreasureGoblinsPresent = false;
                CurrentBotKillRange       = Math.Min((float)(Settings.Combat.Misc.NonEliteRange), Zeta.CommonBot.Settings.CharacterSettings.Instance.KillRadius);

                CurrentBotLootRange          = Zeta.CommonBot.Settings.CharacterSettings.Instance.LootRadius;
                ShouldStayPutDuringAvoidance = false;
                // Set up the fake object for the target handler
                FakeObject = null;

                // Always have a minimum kill radius, so we're never getting whacked without retaliating
                if (CurrentBotKillRange < 10)
                {
                    CurrentBotKillRange = 10;
                }

                // Not allowed to kill monsters due to profile/routine/combat targeting settings - just set the kill range to a third
                if (!ProfileManager.CurrentProfile.KillMonsters || !CombatTargeting.Instance.AllowedToKillMonsters)
                {
                    CurrentBotKillRange = 0;
                }

                // Not allowed to loots due to profile/routine/loot targeting settings - just set range to a quarter
                if (!ProfileManager.CurrentProfile.PickupLoot || !LootTargeting.Instance.AllowedToLoot)
                {
                    CurrentBotLootRange = 0;
                }

                if (PlayerStatus.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker))
                { //!sp - keep looking for kills while WOTB is up
                    iKeepKillRadiusExtendedFor      = Math.Max(3, iKeepKillRadiusExtendedFor);
                    timeKeepKillRadiusExtendedUntil = DateTime.Now.AddSeconds(iKeepKillRadiusExtendedFor);
                }
                // Counter for how many cycles we extend or reduce our attack/kill radius, and our loot radius, after a last kill
                if (iKeepKillRadiusExtendedFor > 0)
                {
                    TimeSpan diffResult = DateTime.Now.Subtract(timeKeepKillRadiusExtendedUntil);
                    iKeepKillRadiusExtendedFor = (int)diffResult.Seconds;
                    //DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Moving, "Kill Radius remaining " + diffResult.Seconds + "s");
                    if (timeKeepKillRadiusExtendedUntil <= DateTime.Now)
                    {
                        iKeepKillRadiusExtendedFor = 0;
                    }
                }
                if (iKeepLootRadiusExtendedFor > 0)
                {
                    iKeepLootRadiusExtendedFor--;
                }

                // Clear forcing close-range priority on mobs after XX period of time
                if (ForceCloseRangeTarget && DateTime.Now.Subtract(lastForcedKeepCloseRange).TotalMilliseconds > ForceCloseRangeForMilliseconds)
                {
                    ForceCloseRangeTarget = false;
                }
                // Bunch of variables used throughout
                hashMonsterObstacleCache    = new HashSet <GilesObstacle>();
                hashAvoidanceObstacleCache  = new HashSet <GilesObstacle>();
                hashNavigationObstacleCache = new HashSet <GilesObstacle>();
                AnyElitesPresent            = false;
                AnyMobsInRange        = false;
                TownRun.lastDistance  = 0f;
                IsAvoidingProjectiles = false;
                // Every 15 seconds, clear the "blackspots" where avoidance failed, so we can re-check them
                if (DateTime.Now.Subtract(lastClearedAvoidanceBlackspots).TotalSeconds > 15)
                {
                    lastClearedAvoidanceBlackspots = DateTime.Now;
                    hashAvoidanceBlackspot         = new HashSet <GilesObstacle>();
                }
                // Clear our very short-term destructible blacklist within 3 seconds of last attacking a destructible
                if (bNeedClearDestructibles && DateTime.Now.Subtract(lastDestroyedDestructible).TotalMilliseconds > 2500)
                {
                    bNeedClearDestructibles            = false;
                    hashRGUIDDestructible3SecBlacklist = new HashSet <int>();
                }
                // Clear our very short-term ignore-monster blacklist (from not being able to raycast on them or already dead units)
                if (NeedToClearBlacklist3 && DateTime.Now.Subtract(dateSinceBlacklist3Clear).TotalMilliseconds > 3000)
                {
                    NeedToClearBlacklist3 = false;
                    hashRGUIDBlacklist3   = new HashSet <int>();
                }

                // Reset the counters for player-owned things
                iPlayerOwnedMysticAlly = 0;
                iPlayerOwnedGargantuan = 0;
                iPlayerOwnedZombieDog  = 0;
                iPlayerOwnedDHPets     = 0;
                // Reset the counters for monsters at various ranges
                ElitesWithinRange   = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 };
                AnythingWithinRange = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 };
                NonRendedTargets_9  = 0;
                anyBossesInRange    = false;
                // Flag for if we should search for an avoidance spot or not
                StandingInAvoidance = false;
                // Highest weight found as we progress through, so we can pick the best target at the end (the one with the highest weight)
                w_HighestWeightFound = 0;
                // Here's the list we'll use to store each object
                GilesObjectCache   = new List <GilesObject>();
                hashDoneThisRactor = new HashSet <int>();
            }
        }
Beispiel #30
0
 private static void RefreshDoBackTrack()
 {
     // See if we should wait for [playersetting] milliseconds for possible loot drops before continuing run
     if (DateTime.Now.Subtract(lastHadUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill || DateTime.Now.Subtract(lastHadEliteUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill)
     {
         CurrentTarget = new GilesObject()
         {
             Position       = PlayerStatus.CurrentPosition,
             Type           = GObjectType.Avoidance,
             Weight         = 20000,
             CentreDistance = 2f,
             RadiusDistance = 2f,
             InternalName   = "GilesWaitForLootDrops"
         };
         DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Behavior, "Waiting for loot to drop, delay: {0}ms", Settings.Combat.Misc.DelayAfterKill);
     }
     // Now see if we need to do any backtracking
     if (CurrentTarget == null && iTotalBacktracks >= 2 && Settings.Combat.Misc.AllowBacktracking && !PlayerStatus.IsInTown)
     // Never bother with the 1st backtrack position nor if we are in town
     {
         // See if we're already within 18 feet of our start position first
         if (Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[1]) <= 18f)
         {
             vBacktrackList   = new SortedList <int, Vector3>();
             iTotalBacktracks = 0;
         }
         // See if we can raytrace to the final location and it's within 25 feet
         if (iTotalBacktracks >= 2 && Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[1]) <= 25f &&
             NavHelper.CanRayCast(PlayerStatus.CurrentPosition, vBacktrackList[1]))
         {
             vBacktrackList   = new SortedList <int, Vector3>();
             iTotalBacktracks = 0;
         }
         if (iTotalBacktracks >= 2)
         {
             // See if we can skip to the next backtracker location first
             if (iTotalBacktracks >= 3)
             {
                 if (Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks - 1]) <= 10f)
                 {
                     vBacktrackList.Remove(iTotalBacktracks);
                     iTotalBacktracks--;
                 }
             }
             CurrentTarget = new GilesObject()
             {
                 Position       = vBacktrackList[iTotalBacktracks],
                 Type           = GObjectType.Backtrack,
                 Weight         = 20000,
                 CentreDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks]),
                 RadiusDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks]),
                 InternalName   = "GilesBacktrack"
             };
         }
     }
     else
     {
         vBacktrackList   = new SortedList <int, Vector3>();
         iTotalBacktracks = 0;
     }
     // End of backtracking check
     //TODO : If this code is obselete remove it (Check that)
     // Finally, a special check for waiting for wrath of the berserker cooldown before engaging Azmodan
     if (CurrentTarget == null && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && Settings.Combat.Barbarian.WaitWOTB && !GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) &&
         ZetaDia.CurrentWorldId == 121214 &&
         (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f))
     {
         bDontSpamOutofCombat = true;
         Logging.Write("[Trinity] Waiting for Wrath Of The Berserker cooldown before continuing to Azmodan.");
         CurrentTarget = new GilesObject()
         {
             Position       = PlayerStatus.CurrentPosition,
             Type           = GObjectType.Avoidance,
             Weight         = 20000,
             CentreDistance = 2f,
             RadiusDistance = 2f,
             InternalName   = "GilesWaitForWrath"
         };
     }
     // And a special check for wizard archon
     if (CurrentTarget == null && Hotbar.Contains(SNOPower.Wizard_Archon) && !GilesUseTimer(SNOPower.Wizard_Archon) && Settings.Combat.Wizard.WaitArchon && ZetaDia.CurrentWorldId == 121214 &&
         (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f))
     {
         DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Waiting for Wizard Archon cooldown before continuing to Azmodan.");
         CurrentTarget = new GilesObject()
         {
             Position       = PlayerStatus.CurrentPosition,
             Type           = GObjectType.Avoidance,
             Weight         = 20000,
             CentreDistance = 2f,
             RadiusDistance = 2f,
             InternalName   = "GilesWaitForArchon"
         };
     }
     // And a very sexy special check for WD BigBadVoodoo
     if (CurrentTarget == null && Hotbar.Contains(SNOPower.Witchdoctor_BigBadVoodoo) && !PowerManager.CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && ZetaDia.CurrentWorldId == 121214 &&
         (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f))
     {
         DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Waiting for WD BigBadVoodoo cooldown before continuing to Azmodan.");
         CurrentTarget = new GilesObject()
         {
             Position       = PlayerStatus.CurrentPosition,
             Type           = GObjectType.Avoidance,
             Weight         = 20000,
             CentreDistance = 2f,
             RadiusDistance = 2f,
             InternalName   = "GilesWaitForVoodooo"
         };
     }
 }