コード例 #1
0
ファイル: WitchDoctorBase.cs プロジェクト: ysj1995/Trinity
        protected virtual bool ShouldSpiritBarrage(out TrinityActor target)
        {
            target = null;

            var skill = Skills.WitchDoctor.SpiritBarrage;

            if (!skill.CanCast())
            {
                return(false);
            }

            if (Player.PrimaryResource < PrimaryEnergyReserve)
            {
                return(false);
            }

            // Allow opportunity for other spenders to be cast.
            if (Skills.WitchDoctor.SpiritBarrage.TimeSinceUse < 750)
            {
                return(false);
            }

            target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;

            if (Runes.WitchDoctor.Phantasm.IsActive)
            {
                var numPhantasmsAtTarget = SpellHistory.FindSpells(skill.SNOPower, target.Position, 12f, 5).Count();
                if (numPhantasmsAtTarget >= 3)
                {
                    return(false);
                }
            }

            return(true);
        }
コード例 #2
0
        protected override bool ShouldBlizzard(out TrinityActor target)
        {
            target = null;
            var blizzardDelay = Runes.Wizard.Snowbound.IsActive ? Settings.BlizzardSnowboundDelay : Settings.BlizzardFrozenSolidDelay;
            var skill         = Skills.Wizard.Blizzard;

            if (!skill.CanCast())
            {
                return(false);
            }

            if (skill.TimeSinceUse < blizzardDelay)
            {
                return(false);
            }

            target = TargetUtil.GetBestClusterUnit(BlizzardRadius, 70f) ?? CurrentTarget;
            if (target == null)
            {
                return(false);
            }

            var isExistingBlizzard = SpellHistory.FindSpells(skill.SNOPower, target.Position, BlizzardRadius, 6).Any();

            return(!isExistingBlizzard);
        }
コード例 #3
0
    public void Initialize(Unit unit)
    {
        Id = Guid.NewGuid();
        isPlayerControlled = unit.IsHumanPlayer;

        if (unit.IsHumanPlayer)
        {
            StatHelper.InitializePlayerStats(stats);
        }
        else
        {
            StatHelper.InitializeCreatureStats(stats);
        }

        StatHelper.InitializeSpeedRates(speedRates);

        SpellHistory = new SpellHistory();

        GlobalCooldown = new Cooldown(1);
        SpellCast      = new SpellCast();

        Buffs  = new Buffs(unit);
        Spells = new SpellStorage();

        PreviousTargets = new List <Guid>();
        PeriodicEffects = new List <PeriodicEffectAura>();
        AbsorbEffects   = new List <AbsorbEffect>();
        CharacterEvents = new Dictionary <int, List <SpellScriptAura> >();
        for (int i = 0; i < CharacterEventTypes.Count; i++)
        {
            CharacterEvents.Add(i, new List <SpellScriptAura>());
        }
    }
コード例 #4
0
        public static bool DrinkPotion()
        {
            var legendaryPotions = Core.Inventory.Backpack.Where(i => i.InternalName.ToLower().Contains("healthpotion_legendary_")).ToList();

            if (legendaryPotions.Any())
            {
                Core.Logger.Verbose(LogCategory.None, "Using Potion", 0);
                var dynamicId = legendaryPotions.First().AnnId;
                InventoryManager.UseItem(dynamicId);
                SpellHistory.RecordSpell(new TrinityPower(SNOPower.DrinkHealthPotion));
                SnapShot.Record();
                return(true);
            }

            var potion = InventoryManager.BaseHealthPotion;

            if (potion != null)
            {
                Core.Logger.Verbose(LogCategory.None, "Using Potion", 0);
                InventoryManager.UseItem(potion.AnnId);
                SpellHistory.RecordSpell(new TrinityPower(SNOPower.DrinkHealthPotion));
                SnapShot.Record();
                return(true);
            }

            Core.Logger.Verbose(LogCategory.None, "No Available potions!", 0);
            return(false);
        }
コード例 #5
0
ファイル: NecromancerBase.cs プロジェクト: ysj1995/Trinity
        protected virtual bool ShouldCommandSkeletons(out TrinityActor target)
        {
            target = null;

            if (!Skills.Necromancer.CommandSkeletons.CanCast())
            {
                return(false);
            }

            target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
            if (target != null)
            {
                // assuming skeletons only get the focus damage bonus on one acd at a time.
                // prevent spamming on the same target over and over, maybe there is an attribute on skeletons
                // for their current target or the skeleton power on the target itself.

                var lastCast = SpellHistory.GetLastUseHistoryItem(SNOPower.P6_Necro_CommandSkeletons);
                if (lastCast == null)
                {
                    return(true);
                }

                LastSkeletonCommandTargetAcdId = lastCast.TargetAcdId;
                return(target.AcdId != lastCast.TargetAcdId);
            }
            return(false);
        }
コード例 #6
0
        public bool CastPower(TrinityPower power)
        {
            if (power.SNOPower != SNOPower.None && Core.GameIsReady)
            {
                if (GameData.InteractPowers.Contains(power.SNOPower))
                {
                    power.TargetPosition = Vector3.Zero;
                }
                else if (power.TargetPosition == Vector3.Zero)
                {
                    power.TargetPosition = Core.Player.Position;
                }

                if (ZetaDia.Me.UsePower(power.SNOPower, power.TargetPosition, Core.Player.WorldDynamicId, power.TargetAcdId))
                {
                    if (GameData.ResetNavigationPowers.Contains(power.SNOPower))
                    {
                        Navigator.Clear();
                    }

                    SpellHistory.RecordSpell(power);
                    return(true);
                }
            }
            return(false);
        }
コード例 #7
0
ファイル: WizardBase.cs プロジェクト: xinhuaer/Trinity
        protected virtual bool ShouldHydra(out Vector3 position)
        {
            TrinityActor target;

            position = Vector3.Zero;

            var skill = Skills.Wizard.Hydra;

            if (!skill.CanCast())
            {
                return(false);
            }

            target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
            if (target == null)
            {
                return(false);
            }

            var isHydraAtLocation = SpellHistory.FindSpells(skill.SNOPower, target.Position, 15f, 6).Any();

            if (isHydraAtLocation)
            {
                return(false);
            }

            if (Player.Summons.HydraCount < MaxHydras && ZetaDia.Me.IsInCombat)
            {
                Core.Logger.Log(LogCategory.Routine, $"Casting Hydra, Less than max Hydras! ({Player.Summons.HydraCount}/{MaxHydras})");
                position = target.Position;
                return(true);
            }

            return(false);
        }
コード例 #8
0
ファイル: PlayerMover.cs プロジェクト: honorbuddy/Trinity
        /// <inheritdoc />
        public void MoveTowards(Vector3 destination)
        {
            if (Core.Settings.Advanced.DisableAllMovement)
                return;

            if (ZetaDia.Globals.IsLoadingWorld)
                return;

            if (Core.IsOutOfGame)
                return;

            if (DateTime.UtcNow < _sleepUntilTime)
                return;

            if (!ZetaDia.IsInGame ||
                ZetaDia.Me == null ||
                !ZetaDia.Me.IsValid ||
                ZetaDia.Me.IsDead ||
                ZetaDia.Globals.IsLoadingWorld)
            {
                return;
            }

            if (IsUIBlockingMovement)
                return;

            var power = TrinityCombat.Routines.Current.GetMovementPower(destination);
            if (power == null ||
                power.SNOPower == SNOPower.None)
            {
                power = new TrinityPower(SNOPower.Walk, 3f, destination);
            }

            // We'll use the target position from the power, but make sure it's something reasonable.
            var isTooFarAway = power.TargetPosition.Distance(Core.Player.Position) > 80f;
            if (isTooFarAway || power.TargetPosition == Vector3.Zero)
                power.TargetPosition = destination;

            var startPosition = ZetaDia.Me.Position;

            if (!ZetaDia.Me.UsePower(power.SNOPower, power.TargetPosition, Core.Player.WorldDynamicId))
                return;

            if (power.SNOPower != SNOPower.Walk)
                SpellHistory.RecordSpell(power);

            if (!GameData.ResetNavigationPowers.Contains(power.SNOPower))
                return;

            Core.Logger.Log(LogCategory.Movement, $"Cast {power.SNOPower} at {power.TargetPosition} from={startPosition}");
            _sleepUntilTime = DateTime.UtcNow + TimeSpan.FromMilliseconds(300);
            TrinityGrid.Instance.AdvanceNavigatorPath(power.MinimumRange, RayType.Walk);
            MoveStop();
        }
コード例 #9
0
ファイル: VacuumItems.cs プロジェクト: ysj1995/Trinity
        /* Should be able to wait for finishing this task While doing something.
         * Otherwise we see the bot locks in dilemma between going to Quest/Rift
         * or Vacuuming nearby items -Seq */
        public async static Task <bool> Execute()
        {
            bool isVacuuming = false;

            if (Core.Player.IsCasting)
            {
                return(isVacuuming = false);
            }

            var count = 0;

            // Items that shouldn't be picked up are currently excluded from cache.
            // a pickup evaluation should be added here if that changes.

            foreach (var item in Core.Targets.OfType <TrinityItem>())
            {
                bool validApproach = Core.Grids.Avoidance.IsIntersectedByFlags(Core.Player.Position, item.Position, AvoidanceFlags.NavigationBlocking, AvoidanceFlags.NavigationImpairing) && !Core.Player.IsFacing(item.Position, 90);

                /* Added checkpoints to avoid approach stuck -Seq */
                if (item.Distance > 8f || VacuumedAcdIds.Contains(item.AcdId) && !validApproach)
                {
                    //Core.Logger.Debug("Vacuuming is valid");
                    continue;
                }

                if (!ZetaDia.Me.UsePower(SNOPower.Axe_Operate_Gizmo, item.Position, Core.Player.WorldDynamicId, item.AcdId))
                {
                    Core.Logger.Verbose($"Failed to vacuum item {item.Name} AcdId={item.AcdId}");
                    continue;
                }

                count++;
                Core.Logger.Debug($"Vacuumed: {item.Name} ({item.ActorSnoId}) InternalName={item.InternalName} GbId={item.GameBalanceId}");
                SpellHistory.RecordSpell(SNOPower.Axe_Operate_Gizmo);
                VacuumedAcdIds.Add(item.AcdId);
                isVacuuming = true;
            }

            if (count > 0)
            {
                Core.Logger.Verbose($"Vacuumed {count} items");
            }

            if (VacuumedAcdIds.Count > 1000)
            {
                VacuumedAcdIds.Clear();
            }
            return(isVacuuming);
        }
コード例 #10
0
ファイル: Skill.cs プロジェクト: shmilyzxt/db-plugins
 /// <summary>
 /// Cast this skill
 /// </summary>
 public void Cast(Vector3 clickPosition, int targetAcdGuid)
 {
     if (SNOPower != SNOPower.None && clickPosition != Vector3.Zero && IsActive && GameIsReady)
     {
         if (ZetaDia.Me.UsePower(SNOPower, clickPosition, Trinity.CurrentWorldDynamicId, targetAcdGuid))
         {
             Trinity.LastPowerUsed = SNOPower;
             CacheData.AbilityLastUsed[SNOPower] = DateTime.UtcNow;
             if (CombatBase.CurrentTarget != null)
             {
                 SpellTracker.TrackSpellOnUnit(CombatBase.CurrentTarget.ACDGuid, SNOPower);
             }
             SpellHistory.RecordSpell(SNOPower);
         }
     }
 }
コード例 #11
0
        /* Should be able to wait for finishing this task While doing something.
         * Otherwise we see the bot locks in dilemma between going to Quest/Rift
         * or Vacuuming nearby items -Seq */
        public static async Task <bool> Execute()
        {
            if (!ZetaDia.IsInGame ||
                ZetaDia.Globals.IsLoadingWorld ||
                ZetaDia.Globals.IsPlayingCutscene ||
                ZetaDia.IsInTown)
            {
                return(true);
            }

            // A check for is casting.
            if (ZetaDia.Me.LoopingAnimationEndTime > 0)
            {
                return(true);
            }

            // Items that shouldn't be picked up are currently excluded from cache.
            // a pickup evaluation should be added here if that changes.
            var currentPickup = Core.Targets.OfType <TrinityItem>().FirstOrDefault(i => i.Distance < 8f && i.ActorSnoId != 0);

            // When no item is inside the vacuum range of 8 just continue.
            if (currentPickup == null)
            {
                return(true);
            }

            // Collect information about the item to pickup.
            var logLine =
                $"[{nameof(VacuumItems)}] {currentPickup.Name} ({currentPickup.ActorSnoId}) InternalName: {currentPickup.InternalName} GbId: {currentPickup.GameBalanceId}";

            var tmp = currentPickup.ToAcdItem().AsRActor;

            // Use the coroutine to pick up the item.
            if (await CommonCoroutines.MoveAndInteract(
                    tmp,
                    () => currentPickup.ActorSnoId == 0) == CoroutineResult.Running)
            {
                return(false);
            }

            // The item was picked up trigger RecordSpell and log the info collected above.
            SpellHistory.RecordSpell(SNOPower.Axe_Operate_Gizmo);
            s_logger.Information(logLine);

            // We are not done yet so return false.
            return(false);
        }
コード例 #12
0
        protected bool IsRestricted(SkillSettings settings, Skill skill)
        {
            if (Player.PrimaryResourcePct < settings.PrimaryResourcePct / 100)
            {
                return(true);
            }

            if (Player.SecondaryResourcePct < settings.SecondaryResourcePct / 100)
            {
                return(true);
            }

            if (Player.CurrentHealthPct > settings.HealthPct)
            {
                return(true);
            }

            if (SpellHistory.TimeSinceUse(skill.SNOPower).TotalMilliseconds < settings.RecastDelayMs)
            {
                return(true);
            }

            if (settings.ClusterSize > 0 &&
                !TargetUtil.ClusterExists(15f, settings.ClusterSize))
            {
                return(true);
            }

            if (settings.WaitForConvention == ConventionMode.GreaterRift &&
                !Core.Rift.IsGreaterRift)
            {
                return(true);
            }

            if (settings.WaitForConvention != ConventionMode.Never &&
                settings.ConventionCondition != null &&
                !settings.ConventionCondition())
            {
                return(true);
            }

            return(false);
        }
コード例 #13
0
ファイル: UsePotion.cs プロジェクト: honorbuddy/Trinity
        public static async Task <CoroutineResult> DrinkPotion()
        {
            if (!ZetaDia.IsInGame ||
                ZetaDia.Globals.IsLoadingWorld ||
                ZetaDia.Globals.IsPlayingCutscene ||
                ZetaDia.IsInTown ||
                SpellHistory.TimeSinceUse(SNOPower.DrinkHealthPotion) <= TimeSpan.FromSeconds(30) ||
                ZetaDia.Me == null ||
                !ZetaDia.Me.IsFullyValid() ||
                ZetaDia.Me.IsDead ||
                Combat.TrinityCombat.Routines.Current == null)
            {
                return(CoroutineResult.NoAction);
            }

            if (ZetaDia.Me.HitpointsCurrentPct > Combat.TrinityCombat.Routines.Current.PotionHealthPct)
            {
                return(CoroutineResult.NoAction);
            }

            if (ZetaDia.Me.IsFeared ||
                ZetaDia.Me.IsStunned ||
                ZetaDia.Me.IsFrozen ||
                ZetaDia.Me.IsBlind ||
                ZetaDia.Me.CommonData
                .GetAttribute <bool>(ActorAttributeType.PowerImmobilize))
            {
                s_logger.Warning($"[{nameof(DrinkPotion)}] Can't use potion while incapacitated!");
                return(CoroutineResult.NoAction);
            }

            if (ActivePotion == null)
            {
                s_logger.Warning($"[{nameof(DrinkPotion)}] No Available potions!");
                return(CoroutineResult.NoAction);
            }

            s_logger.Information($"[{nameof(DrinkPotion)}] Using Potion {ActivePotion.Name}");
            InventoryManager.UseItem(ActivePotion.AnnId);
            SpellHistory.RecordSpell(new TrinityPower(SNOPower.DrinkHealthPotion));
            SnapShot.Record();
            return(CoroutineResult.Done);
        }
コード例 #14
0
ファイル: WizardBase.cs プロジェクト: xinhuaer/Trinity
        protected virtual bool ShouldBlizzard(out TrinityActor target)
        {
            target = null;

            var skill = Skills.Wizard.Blizzard;

            if (!skill.CanCast())
            {
                return(false);
            }

            target = TargetUtil.GetBestClusterUnit(BlizzardRadius, 70f) ?? CurrentTarget;
            if (target == null)
            {
                return(false);
            }

            var isExistingBlizzard = SpellHistory.FindSpells(skill.SNOPower, target.Position, BlizzardRadius, 6).Any();

            return(!isExistingBlizzard);
        }
コード例 #15
0
        public bool CastPower(SNOPower power, Vector3 clickPosition, int targetAcdId)
        {
            if (power != SNOPower.None && Core.GameIsReady)
            {
                if (GameData.InteractPowers.Contains(power))
                {
                    clickPosition = Vector3.Zero;
                }
                else if (clickPosition == Vector3.Zero)
                {
                    clickPosition = Core.Player.Position;
                }

                UpdateNavigationAfterPower(power);

                if (ZetaDia.Me.UsePower(power, clickPosition, Core.Player.WorldDynamicId, targetAcdId))
                {
                    SpellHistory.RecordSpell(power, clickPosition, targetAcdId);
                    return(true);
                }
            }
            return(false);
        }
コード例 #16
0
        protected virtual bool ShouldCommandSkeletons()
        {
            if (!Skills.Necromancer.CommandSkeletons.CanCast() || Skills.Necromancer.Simulacrum.TimeSinceUse < 12500)
            {
                return(false);
            }

            if (Skills.Necromancer.CommandSkeletons.TimeSinceUse < 2500)
            {
                return(false);
            }

            var lastCast = SpellHistory.GetLastUseHistoryItem(SNOPower.P6_Necro_CommandSkeletons);

            if (Target.AcdId == lastCast?.TargetAcdId)
            {
                return(false);
            }

            Core.Logger.Error(LogCategory.Routine,
                              $"[Command Skeletons] - On {Target}.");
            return(true);
        }
コード例 #17
0
        public static bool ShouldUsePotion()
        {
            if (Core.Player == null || Combat.TrinityCombat.Routines.Current == null)
            {
                return(false);
            }

            if (Core.Player.CurrentHealthPct > Combat.TrinityCombat.Routines.Current.PotionHealthPct)
            {
                return(false);
            }

            if (Core.Player.IsIncapacitated || !(Core.Player.CurrentHealthPct > 0) || Core.Player.IsInTown)
            {
                return(false);
            }

            if (SpellHistory.TimeSinceUse(SNOPower.DrinkHealthPotion) <= TimeSpan.FromSeconds(30))
            {
                return(false);
            }

            return(Core.Player.CurrentHealthPct <= Combat.TrinityCombat.Routines.Current.PotionHealthPct);
        }
コード例 #18
0
        public void MoveTowards(Vector3 vMoveToTarget)
        {
            if (Trinity.Settings.Advanced.DisableAllMovement)
            {
                return;
            }

            if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || ZetaDia.Me.IsDead || ZetaDia.IsLoadingWorld)
            {
                return;
            }

            if (UISafetyCheck())
            {
                return;
            }

            TimeLastUsedPlayerMover = DateTime.UtcNow;
            LastMoveToTarget        = vMoveToTarget;

            // Set the public variable

            vMoveToTarget = WarnAndLogLongPath(vMoveToTarget);

            // Store player current position

            // Store distance to current moveto target
            float destinationDistance = MyPosition.Distance2D(vMoveToTarget);

            // Do unstuckery things
            if (Trinity.Settings.Advanced.UnstuckerEnabled)
            {
                // See if we can reset the 10-limit unstuck counter, if >120 seconds since we last generated an unstuck location
                // this is used if we're NOT stuck...
                if (TotalAntiStuckAttempts > 1 && DateTime.UtcNow.Subtract(LastGeneratedStuckPosition).TotalSeconds >= 120)
                {
                    TotalAntiStuckAttempts   = 1;
                    TimesReachedStuckPoint   = 0;
                    vSafeMovementLocation    = Vector3.Zero;
                    NavHelper.UsedStuckSpots = new List <GridPoint>();
                    Logger.Log(TrinityLogLevel.Info, LogCategory.Movement, "Resetting unstuck timers", true);
                }

                // See if we need to, and can, generate unstuck actions
                // check if we're stuck
                bool isStuck = UnstuckChecker();

                if (isStuck)
                {
                    // Record the time we last apparently couldn't move for a brief period of time
                    LastRecordedAnyStuck = DateTime.UtcNow;

                    // See if there's any stuck position to try and navigate to generated by random mover
                    vSafeMovementLocation = UnstuckHandler(MyPosition, vMoveToTarget);

                    if (vSafeMovementLocation == Vector3.Zero)
                    {
                        Logger.Log(TrinityLogLevel.Info, LogCategory.Movement, "Unable to find Unstuck point!", vSafeMovementLocation);
                        return;
                    }
                    Logger.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "SafeMovement Location set to {0}", vSafeMovementLocation);
                }
                // See if we can clear the total unstuckattempts if we haven't been stuck in over 6 minutes.
                if (DateTime.UtcNow.Subtract(LastRecordedAnyStuck).TotalSeconds >= 360)
                {
                    TimesReachedMaxUnstucks = 0;
                }
                // Did we have a safe point already generated (eg from last loop through), if so use it as our current location instead
                if (vSafeMovementLocation != Vector3.Zero)
                {
                    // Set our current movement target to the safe point we generated last cycle
                    vMoveToTarget       = vSafeMovementLocation;
                    destinationDistance = MyPosition.Distance2D(vMoveToTarget);
                }
                // Get distance to current destination
                // Remove the stuck position if it's been reached, this bit of code also creates multiple stuck-patterns in an ever increasing amount
                if (vSafeMovementLocation != Vector3.Zero && destinationDistance <= 3f)
                {
                    vSafeMovementLocation = Vector3.Zero;
                    TimesReachedStuckPoint++;

                    // Do we want to immediately generate a 2nd waypoint to "chain" anti-stucks in an ever-increasing path-length?
                    if (TimesReachedStuckPoint <= TotalAntiStuckAttempts)
                    {
                        vSafeMovementLocation = NavHelper.FindSafeZone(true, TotalAntiStuckAttempts, MyPosition);
                        vMoveToTarget         = vSafeMovementLocation;
                    }
                    else
                    {
                        if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                        {
                            Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Clearing old route and trying new path find to: " + LastMoveToTarget.ToString());
                        }
                        // Reset the path and allow a whole "New" unstuck generation next cycle
                        TimesReachedStuckPoint = 0;
                        // And cancel unstucking for 9 seconds so DB can try to navigate
                        CancelUnstuckerForSeconds = (9 * TotalAntiStuckAttempts);
                        if (CancelUnstuckerForSeconds < 20)
                        {
                            CancelUnstuckerForSeconds = 20;
                        }
                        LastCancelledUnstucker = DateTime.UtcNow;

                        Navigator.Clear();
                        Navigator.MoveTo(LastMoveToTarget, "original destination", false);

                        return;
                    }
                }
            }

            // don't use special movement within 10 seconds of being stuck
            bool cancelSpecialMovementAfterStuck = DateTime.UtcNow.Subtract(LastGeneratedStuckPosition).TotalMilliseconds > 10000;

            // See if we can use abilities like leap etc. for movement out of combat, but not in town
            if (Trinity.Settings.Combat.Misc.AllowOOCMovement && !Trinity.Player.IsInTown && !Trinity.DontMoveMeIAmDoingShit && cancelSpecialMovementAfterStuck)
            {
                bool bTooMuchZChange = (Math.Abs(MyPosition.Z - vMoveToTarget.Z) >= 4f);

                // Whirlwind for a barb, special context only
                if (Trinity.Settings.Combat.Barbarian.SprintMode != BarbarianSprintMode.CombatOnly &&
                    Trinity.Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && Trinity.ObjectCache.Any(u => u.IsUnit &&
                                                                                                     MathUtil.IntersectsPath(u.Position, u.Radius + 5f, Trinity.Player.Position, vMoveToTarget)) &&
                    Trinity.Player.PrimaryResource >= V.F("Barbarian.Whirlwind.MinFury") && !Trinity.IsWaitingForSpecial && V.B("Barbarian.Whirlwind.UseForMovement"))
                {
                    ZetaDia.Me.UsePower(SNOPower.Barbarian_Whirlwind, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1);
                    SpellHistory.RecordSpell(SNOPower.Barbarian_Whirlwind);
                    if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Whirlwind for OOC movement, distance={0}", destinationDistance);
                    }
                    return;
                }

                // Leap movement for a barb
                if (Trinity.Settings.Combat.Barbarian.UseLeapOOC && Trinity.Hotbar.Contains(SNOPower.Barbarian_Leap) &&
                    PowerManager.CanCast(SNOPower.Barbarian_Leap) && !ShrinesInArea(vMoveToTarget))
                {
                    Vector3 vThisTarget = vMoveToTarget;
                    if (destinationDistance > 35f)
                    {
                        vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f);
                    }
                    ZetaDia.Me.UsePower(SNOPower.Barbarian_Leap, vThisTarget, Trinity.CurrentWorldDynamicId, -1);
                    SpellHistory.RecordSpell(SNOPower.Barbarian_Leap);
                    if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Leap for OOC movement, distance={0}", destinationDistance);
                    }
                    return;
                }
                // Furious Charge movement for a barb
                if (Trinity.Settings.Combat.Barbarian.UseChargeOOC && Trinity.Hotbar.Contains(SNOPower.Barbarian_FuriousCharge) && !bTooMuchZChange &&
                    destinationDistance >= 20f &&
                    PowerManager.CanCast(SNOPower.Barbarian_FuriousCharge) && !ShrinesInArea(vMoveToTarget))
                {
                    Vector3 vThisTarget = vMoveToTarget;
                    if (destinationDistance > 35f)
                    {
                        vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f);
                    }
                    ZetaDia.Me.UsePower(SNOPower.Barbarian_FuriousCharge, vThisTarget, Trinity.CurrentWorldDynamicId, -1);
                    SpellHistory.RecordSpell(SNOPower.Barbarian_FuriousCharge);
                    if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Furious Charge for OOC movement, distance={0}", destinationDistance);
                    }
                    return;
                }

                bool hasTacticalAdvantage = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.DemonHunter_Passive_TacticalAdvantage);
                int  vaultDelay           = hasTacticalAdvantage ? 2000 : Trinity.Settings.Combat.DemonHunter.VaultMovementDelay;

                // DemonHunter Vault
                if (Trinity.Hotbar.Contains(SNOPower.DemonHunter_Vault) && !bTooMuchZChange && Trinity.Settings.Combat.DemonHunter.VaultMode != DemonHunterVaultMode.CombatOnly &&
                    CombatBase.TimeSincePowerUse(SNOPower.DemonHunter_Vault) > vaultDelay &&
                    destinationDistance >= 18f &&
                    PowerManager.CanCast(SNOPower.DemonHunter_Vault) && !ShrinesInArea(vMoveToTarget) &&
                    // Don't Vault into avoidance/monsters if we're kiting
                    (CombatBase.PlayerKiteDistance <= 0 || (CombatBase.PlayerKiteDistance > 0 &&
                                                            (!CacheData.TimeBoundAvoidance.Any(a => a.Position.Distance(vMoveToTarget) <= CombatBase.PlayerKiteDistance) ||
                                                             (!CacheData.TimeBoundAvoidance.Any(a => MathEx.IntersectsPath(a.Position, a.Radius, Trinity.Player.Position, vMoveToTarget))) ||
                                                             !CacheData.MonsterObstacles.Any(a => a.Position.Distance(vMoveToTarget) <= CombatBase.PlayerKiteDistance))))
                    )
                {
                    Vector3 vThisTarget = vMoveToTarget;
                    if (destinationDistance > 35f)
                    {
                        vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f);
                    }
                    ZetaDia.Me.UsePower(SNOPower.DemonHunter_Vault, vThisTarget, Trinity.CurrentWorldDynamicId, -1);
                    SpellHistory.RecordSpell(SNOPower.DemonHunter_Vault);
                    if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Vault for OOC movement, distance={0}", destinationDistance);
                    }
                    return;
                }
                // Tempest rush for a monk
                if (Trinity.Hotbar.Contains(SNOPower.Monk_TempestRush) &&
                    (Trinity.Settings.Combat.Monk.TROption == TempestRushOption.MovementOnly || Trinity.Settings.Combat.Monk.TROption == TempestRushOption.Always ||
                     (Trinity.Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && !TargetUtil.AnyElitesInRange(40f))))
                {
                    Vector3 vTargetAimPoint = vMoveToTarget;

                    bool canRayCastTarget = true;

                    vTargetAimPoint = TargetUtil.FindTempestRushTarget();

                    if (!CanChannelTempestRush &&
                        ((Trinity.Player.PrimaryResource >= Trinity.Settings.Combat.Monk.TR_MinSpirit &&
                          destinationDistance >= Trinity.Settings.Combat.Monk.TR_MinDist) ||
                         DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Monk_TempestRush]).TotalMilliseconds <= 150) &&
                        canRayCastTarget && PowerManager.CanCast(SNOPower.Monk_TempestRush))
                    {
                        CanChannelTempestRush = true;
                    }
                    else if ((CanChannelTempestRush && (Trinity.Player.PrimaryResource < 10f)) || !canRayCastTarget)
                    {
                        CanChannelTempestRush = false;
                    }

                    double lastUse = DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Monk_TempestRush]).TotalMilliseconds;

                    if (CanChannelTempestRush)
                    {
                        if (Trinity.SNOPowerUseTimer(SNOPower.Monk_TempestRush))
                        {
                            LastTempestRushPosition = vTargetAimPoint;

                            ZetaDia.Me.UsePower(SNOPower.Monk_TempestRush, vTargetAimPoint, Trinity.CurrentWorldDynamicId, -1);
                            SpellHistory.RecordSpell(SNOPower.Monk_TempestRush);

                            // simulate movement speed of 30
                            SpeedSensor lastSensor = SpeedSensors.OrderByDescending(s => s.Timestamp).FirstOrDefault();
                            SpeedSensors.Add(new SpeedSensor()
                            {
                                Location          = MyPosition,
                                TimeSinceLastMove = new TimeSpan(0, 0, 0, 0, 1000),
                                Distance          = 5f,
                                WorldID           = Trinity.CurrentWorldDynamicId
                            });

                            if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                            {
                                Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Tempest Rush for OOC movement, distance={0:0} spirit={1:0} cd={2} lastUse={3:0} V3={4} vAim={5}",
                                           destinationDistance, Trinity.Player.PrimaryResource, PowerManager.CanCast(SNOPower.Monk_TempestRush), lastUse, vMoveToTarget, vTargetAimPoint);
                            }
                            return;
                        }
                        else
                        {
                            return;
                        }
                    }
                    else
                    {
                        if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                        {
                            Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement,
                                       "Tempest rush failed!: {0:00.0} / {1} distance: {2:00.0} / {3} Raycast: {4} MS: {5:0.0} lastUse={6:0}",
                                       Trinity.Player.PrimaryResource,
                                       Trinity.Settings.Combat.Monk.TR_MinSpirit,
                                       destinationDistance,
                                       Trinity.Settings.Combat.Monk.TR_MinDist,
                                       canRayCastTarget,
                                       GetMovementSpeed(),
                                       lastUse);
                        }

                        Trinity.MaintainTempestRush = false;
                    }

                    // Always set this from PlayerMover
                    Trinity.LastTempestRushLocation = vTargetAimPoint;
                }

                // Dashing Strike OOC
                if (CombatBase.CanCast(SNOPower.X1_Monk_DashingStrike) && Trinity.Settings.Combat.Monk.UseDashingStrikeOOC && destinationDistance > 15f)
                {
                    ZetaDia.Me.UsePower(SNOPower.X1_Monk_DashingStrike, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1);
                    SpellHistory.RecordSpell(SNOPower.X1_Monk_DashingStrike);
                    if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Dashing Strike for OOC movement, distance={0}", destinationDistance);
                    }
                }


                bool hasWormHole = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 4);
                bool hasCalamity = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 0);

                // Teleport for a wizard
                if (!hasCalamity && CombatBase.CanCast(SNOPower.Wizard_Teleport, CombatBase.CanCastFlags.NoTimer) &&
                    CombatBase.TimeSincePowerUse(SNOPower.Wizard_Teleport) > 250 &&
                    destinationDistance >= 10f && !ShrinesInArea(vMoveToTarget))
                {
                    var maxTeleportRange = 75f;

                    Vector3 vThisTarget = vMoveToTarget;
                    if (destinationDistance > maxTeleportRange)
                    {
                        vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, maxTeleportRange);
                    }
                    ZetaDia.Me.UsePower(SNOPower.Wizard_Teleport, vThisTarget, Trinity.CurrentWorldDynamicId, -1);
                    SpellHistory.RecordSpell(SNOPower.Wizard_Teleport);
                    if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Teleport for OOC movement, distance={0}", destinationDistance);
                    }
                    return;
                }
                // Archon Teleport for a wizard
                if (Trinity.Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) &&
                    DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_Archon_Teleport]).TotalMilliseconds >= CombatBase.GetSNOPowerUseDelay(SNOPower.Wizard_Archon_Teleport) &&
                    destinationDistance >= 20f &&
                    PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport) && !ShrinesInArea(vMoveToTarget))
                {
                    Vector3 vThisTarget = vMoveToTarget;
                    if (destinationDistance > 35f)
                    {
                        vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f);
                    }
                    ZetaDia.Me.UsePower(SNOPower.Wizard_Archon_Teleport, vThisTarget, Trinity.CurrentWorldDynamicId, -1);
                    SpellHistory.RecordSpell(SNOPower.Wizard_Archon_Teleport);
                    if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                    {
                        Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Archon Teleport for OOC movement, distance={0}", destinationDistance);
                    }
                    return;
                }
            }

            if (MyPosition.Distance2D(vMoveToTarget) > 1f)
            {
                // Default movement
                ZetaDia.Me.UsePower(SNOPower.Walk, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1);

                if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Moved to:{0} dir:{1} Speed:{2:0.00} Dist:{3:0} ZDiff:{4:0} CanStand:{5} Raycast:{6}",
                               NavHelper.PrettyPrintVector3(vMoveToTarget), MathUtil.GetHeadingToPoint(vMoveToTarget), MovementSpeed, MyPosition.Distance2D(vMoveToTarget),
                               Math.Abs(MyPosition.Z - vMoveToTarget.Z),
                               Trinity.MainGridProvider.CanStandAt(Trinity.MainGridProvider.WorldToGrid(vMoveToTarget.ToVector2())),
                               !Navigator.Raycast(MyPosition, vMoveToTarget)
                               );
                }
            }
            else
            {
                if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement))
                {
                    Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Reached MoveTowards Destination {0} Current Speed: {1:0.0}", vMoveToTarget, MovementSpeed);
                }
            }
        }
コード例 #19
0
        //public static class FireballTracker
        //{
        //    public static Dictionary<int,TrinityActor> Fireballs = new Dictionary<int, TrinityActor>();
        //    private static DateTime _lastUseTime;

        //    public static bool IsFireballPending { get; private set; }

        //    public const int FireballSnoId = (int) SNOActor.zoltunKulle_fieryBoulder_model;
        //    public const int PendingTimeoutMs = 1000;

        //    public static void Update()
        //    {
        //        var lastUseTime = SpellHistory.PowerLastUsedTime(SNOPower.Monk_LashingTailKick);
        //        var msSinceUse = DateTime.UtcNow.Subtract(lastUseTime).TotalMilliseconds;
        //        if (lastUseTime != _lastUseTime)
        //        {
        //            Core.Logger.Log($"New LTK cast, waiting for fireball");
        //            IsFireballPending = msSinceUse < PendingTimeoutMs;
        //            _lastUseTime = lastUseTime;
        //        }

        //        if (msSinceUse > PendingTimeoutMs && IsFireballPending)
        //        {
        //            Core.Logger.Log($"Fireball wait timed out");
        //            IsFireballPending = false;
        //        }

        //        foreach (var actor in Core.Actors.AllRActors.Where(a => a.Type == TrinityObjectType.ClientEffect))
        //        {
        //            if (actor.ActorSnoId == FireballSnoId && !Fireballs.ContainsKey(actor.AnnId))
        //            {
        //                Core.Logger.Log($"New Fireball Found Delay:{msSinceUse}ms");
        //                IsFireballPending = false;
        //                Fireballs.Add(actor.AnnId, actor);
        //            }
        //        }

        //        foreach (var fireball in Fireballs.ToList().Where(fireball => !fireball.Value.IsValid))
        //        {
        //            Fireballs.Remove(fireball.Key);
        //        }
        //    }
        //}

        /* Speed Farm Solo Variant tested @ GR75
         * [Trinity 2.55.668] ------ Equipped Non-Set Legendaries: Items=8, Sets=1 ------
         * [Trinity 2.55.668] Item: SpiritStone: Gyana Na Kashu (222169) is Equipped
         * [Trinity 2.55.668] Item: Belt: Kyoshiro's Soul (298136) is Equipped
         * [Trinity 2.55.668] Item: Boots: Rivera Dancers (197224) is Equipped
         * [Trinity 2.55.668] Item: Ring: Obsidian Ring of the Zodiac (212588) is Equipped
         * [Trinity 2.55.668] Item: Ring: Unity (212581) is Equipped
         * [Trinity 2.55.668] Item: Bracer: Nemesis Bracers (298121) is Equipped
         * [Trinity 2.55.668] Item: FistWeapon: Scarbringer (130557) is Equipped
         * [Trinity 2.55.668] Item: FistWeapon: Vengeful Wind (403775) is Equipped
         * [Trinity 2.55.668] ------ Equipped in Kanai's Cube: Items=3 ------
         * [Trinity 2.55.668] Item: SpiritStone: The Laws of Seph (299454) is Equipped
         * [Trinity 2.55.668] Item: Ring: Ring of Royal Grandeur (298094) is Equipped
         * [Trinity 2.55.668] Item: Sword: In-geom (410946) is Equipped
         * [Trinity 2.55.668] ------ Set: Monkey King's Garb : 5/6 Equipped. ActiveBonuses=3/3 ------
         * [Trinity 2.55.668] Item: Shoulder: Sunwuko's Balance (336175) is Equipped
         * [Trinity 2.55.668] Item: Gloves: Sunwuko's Paws (336172) is Equipped
         * [Trinity 2.55.668] Item: Amulet: Sunwuko's Shines (336174) is Equipped
         * [Trinity 2.55.668] Item: Legs: Sunwuko's Leggings (429075) is Equipped
         * [Trinity 2.55.668] Item: Chest: Sunwuko's Soul (429167) is Equipped
         * [Trinity 2.55.668] ------ Active Skills / Runes ------
         * [Trinity 2.55.668] Skill: Lashing Tail Kick Rune=Sweeping Armada  Type=Spender
         * [Trinity 2.55.668] Skill: Blinding Flash Rune=Faith in the Light Type=Other
         * [Trinity 2.55.668] Skill: Dashing Strike Rune=Way of the Falling Star Type=Other
         * [Trinity 2.55.668] Skill: Mantra of Salvation Rune=Agility Type=Other
         * [Trinity 2.55.668] Skill: Sweeping Wind Rune=Inner Storm Type=Spender
         * [Trinity 2.55.668] Skill: Epiphany Rune=Desert Shroud Type=Other
         * [Trinity 2.55.668] ------ Passives ------
         * [Trinity 2.55.668] Passive: Exalted Soul
         * [Trinity 2.55.668] Passive: Beacon of Ytar
         * [Trinity 2.55.668] Passive: Harmony
         * [Trinity 2.55.668] Passive: Momentum
         *
         * [Trinity 2.55.668] ------ Equipped Non-Set Legendaries: Items=6, Sets=2 ------
         * [Trinity 2.55.668] Item: SpiritStone: Gyana Na Kashu (222169) is Equipped
         * [Trinity 2.55.668] Item: Belt: The Witching Hour (193670) is Equipped
         * [Trinity 2.55.668] Item: Boots: Rivera Dancers (197224) is Equipped
         * [Trinity 2.55.668] Item: Bracer: Spirit Guards (430290) is Equipped
         * [Trinity 2.55.668] Item: FistWeapon: Crystal Fist (175939) is Equipped
         * [Trinity 2.55.668] Item: FistWeapon: Scarbringer (130557) is Equipped
         * [Trinity 2.55.668] ------ Equipped in Kanai's Cube: Items=3 ------
         * [Trinity 2.55.668] Item: SpiritStone: The Laws of Seph (299454) is Equipped
         * [Trinity 2.55.668] Item: Ring: Ring of Royal Grandeur (298094) is Equipped
         * [Trinity 2.55.668] Item: Daibo: Flying Dragon (197065) is Equipped
         * [Trinity 2.55.668] ------ Set: Bastions of Will : 2/2 Equipped. ActiveBonuses=1/1 ------
         * [Trinity 2.55.668] Item: Ring: Focus (332209) is Equipped
         * [Trinity 2.55.668] Item: Ring: Restraint (332210) is Equipped
         * [Trinity 2.55.668] ------ Set: Monkey King's Garb : 5/6 Equipped. ActiveBonuses=3/3 ------
         * [Trinity 2.55.668] Item: Shoulder: Sunwuko's Balance (336175) is Equipped
         * [Trinity 2.55.668] Item: Gloves: Sunwuko's Paws (336172) is Equipped
         * [Trinity 2.55.668] Item: Amulet: Sunwuko's Shines (336174) is Equipped
         * [Trinity 2.55.668] Item: Legs: Sunwuko's Leggings (429075) is Equipped
         * [Trinity 2.55.668] Item: Chest: Sunwuko's Soul (429167) is Equipped
         * [Trinity 2.55.668] ------ Active Skills / Runes ------
         * [Trinity 2.55.668] Skill: Fists of Thunder Rune=Quickening Type=Generator
         * [Trinity 2.55.668] Skill: Lashing Tail Kick Rune=Sweeping Armada  Type=Spender
         * [Trinity 2.55.668] Skill: Blinding Flash Rune=Faith in the Light Type=Other
         * [Trinity 2.55.668] Skill: Dashing Strike Rune=Blinding Speed Type=Other
         * [Trinity 2.55.668] Skill: Mantra of Salvation Rune=Agility Type=Other
         * [Trinity 2.55.668] Skill: Sweeping Wind Rune=Inner Storm Type=Spender
         * [Trinity 2.55.668] ------ Passives ------
         * [Trinity 2.55.668] Passive: Exalted Soul
         * [Trinity 2.55.668] Passive: Seize the Initiative
         * [Trinity 2.55.668] Passive: The Guardian's Path
         * [Trinity 2.55.668] Passive: Beacon of Ytar
         * [Trinity 2.55.668][Routine] Need Spirit Gaurds Buff
         *
         */

        public TrinityPower GetOffensivePower()
        {
            // 853: PowerBuff0VisualEffectNone (-3243) [ PowerSnoId: ItemPassive_Unique_Ring_903_x1: 402411 ] i:1 f:0 Value=1
            // 865: PowerBuff2VisualEffectNone (-3231) [ PowerSnoId: ItemPassive_Unique_Ring_922_x1: 402461 ] i:0 f:0 Value=0
            // 588: BuffIconStartTick2(-3508)[PowerSnoId: ItemPassive_Unique_Gem_018_x1: 428348] i: 79817 f: 0 Value = 79817
            // 863: PowerBuff1VisualEffectD (-3233) [ PowerSnoId: Monk_LashingTailKick: 111676 ] i:1 f:0 Value=1

            TrinityPower power;
            Vector3      position;

            //FireballTracker.Update();
            //if (FireballTracker.IsFireballPending)
            //{
            //    Core.Logger.Log("Waiting for fireball");
            //    return new TrinityPower(SNOPower.None);
            //}

            if (TrySpecialPower(out power))
            {
                return(power);
            }

            if (Core.Buffs.HasCastingShrine)
            {
                if (Skills.Monk.DashingStrike.CanCast() && !Skills.Monk.DashingStrike.IsLastUsed)
                {
                    return(DashingStrike(CurrentTarget.Position));
                }

                if (Skills.Monk.LashingTailKick.CanCast())
                {
                    return(LashingTailKick(CurrentTarget));
                }
            }

            if (ShouldRefreshSpiritGuardsBuff)
            {
                Core.Logger.Log(LogCategory.Routine, "Need Spirit Gaurds Buff");
                if (TryPrimaryClosestTarget(out power))
                {
                    return(power);
                }
            }

            if (ShouldRefreshBastiansGenerator)
            {
                Core.Logger.Log(LogCategory.Routine, "Need Bastians Buff");
                if (TryPrimaryClosestTarget(out power))
                {
                    return(power);
                }
            }

            // With sweeping armada try to keep distance in the sweet spot between 10-15yd
            if (Runes.Monk.SweepingArmada.IsActive)
            {
                var enoughTimePassed = SpellHistory.TimeSinceUse(SNOPower.Walk).TotalMilliseconds > 500;
                var isSoloElite      = TargetUtil.ElitesInRange(25f) == 1 && !AnyUnitsInRange(25f);
                if (enoughTimePassed && isSoloElite && CurrentTarget.RadiusDistance <= 10f && !IsStuck)
                {
                    if (Avoider.TryGetSafeSpot(out position, 12f + CurrentTarget.CollisionRadius, 30f, CurrentTarget.Position))
                    {
                        Core.Logger.Log(LogCategory.Routine, $"Adjusting Distance for Sweeping Armarda RDist={CurrentTarget.RadiusDistance} Dist={ZetaDia.Me.Position.Distance(CurrentTarget.Position)}");
                        return(Walk(position, 2f));
                    }
                }
            }

            if (TrySecondaryPower(out power))
            {
                return(power);
            }

            if (TryPrimaryPower(out power))
            {
                return(power);
            }

            if (IsNoPrimary)
            {
                // Stay away from units for belt to build stacks.
                if (Legendary.KyoshirosSoul.IsEquipped)
                {
                    var needStacks   = Skills.Monk.SweepingWind.BuffStacks < 3 && Player.PrimaryResourcePct <= 0.75f;
                    var needResource = Player.PrimaryResource < PrimaryEnergyReserve;
                    if ((needStacks || needResource) && HostileMonsters.Any(u => u.Distance <= 12f))
                    {
                        Core.Logger.Log(LogCategory.Routine, "Moving away to build stacks");
                        return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 30f)));
                    }
                }
            }

            // SW Stacks but no mana, or skills cooldown maybe, hang out just outside range of target.
            Core.Logger.Log(LogCategory.Routine, "Can't cast anything.");
            return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 25f)));
        }
コード例 #20
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);
        }
コード例 #21
0
        protected override bool ShouldSlowTime(out Vector3 position)
        {
            position = Vector3.Zero;

            var skill = Skills.Wizard.SlowTime;

            if (!skill.CanCast())
            {
                return(false);
            }

            var myPosition      = ZetaDia.Me.Position;
            var clusterPosition = TargetUtil.GetBestClusterPoint(0f, 50f);
            var bubbles         = SpellHistory.FindSpells(skill.SNOPower, 12).ToList();
            var bubblePositions = new List <Vector3>(bubbles.Select(b => b.TargetPosition));
            var isDefensiveRune = Runes.Wizard.PointOfNoReturn.IsActive || Runes.Wizard.StretchTime.IsActive || Runes.Wizard.Exhaustion.IsActive;

            Func <Vector3, bool> isBubbleAtPosition = pos => bubblePositions.Any(b => b.Distance(pos) <= 14f && pos.Distance(myPosition) < SlowTimeRange);

            // On Self
            if (TargetUtil.ClusterExists(15f, 60f, 8) && isDefensiveRune && !isBubbleAtPosition(myPosition))
            {
                position = MathEx.GetPointAt(myPosition, 10f, Player.Rotation);
                return(true);
            }

            // On Elites
            if (CurrentTarget.IsElite && CurrentTarget.Distance < SlowTimeRange && !isBubbleAtPosition(CurrentTarget.Position))
            {
                position = CurrentTarget.Position;
                return(true);
            }

            // On Clusters
            if (TargetUtil.ClusterExists(50f, 5) && clusterPosition.Distance(myPosition) < SlowTimeRange && !isBubbleAtPosition(clusterPosition))
            {
                position = clusterPosition;
                return(true);
            }

            // Delseres Magnum Opus Set
            if (Sets.DelseresMagnumOpus.IsEquipped)
            {
                var isLargeCluster = Core.Clusters.LargeCluster.Exists && Core.Clusters.LargeCluster.Position.Distance(myPosition) < SlowTimeRange;
                if (isLargeCluster && !isBubbleAtPosition(Core.Clusters.LargeCluster.Position))
                {
                    position = Core.Clusters.LargeCluster.Position;
                    return(true);
                }

                var isAnyCluster = Core.Clusters.BestCluster.Exists && Core.Clusters.BestCluster.Position.Distance(myPosition) < SlowTimeRange;
                if (isAnyCluster && !isBubbleAtPosition(Core.Clusters.BestCluster.Position))
                {
                    position = Core.Clusters.BestCluster.Position;
                    return(true);
                }

                if (!isBubbleAtPosition(myPosition))
                {
                    position = MathEx.GetPointAt(myPosition, 10f, Player.Rotation);
                    return(true);
                }
            }

            return(false);
        }
コード例 #22
0
ファイル: Monk.cs プロジェクト: crkzaigit/DemonbuddyProfiles
        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);
        }
コード例 #23
0
        public TrinityPower GetOffensivePower()
        {
            TrinityPower power;
            Vector3      position;

            if (TrySpecialPower(out power))
            {
                return(power);
            }

            if (Core.Buffs.HasCastingShrine)
            {
                if (Skills.Monk.BlindingFlash.CanCast() && Legendary.TheLawsOfSeph.IsEquipped && Player.PrimaryResource < Player.PrimaryResourceMax - 165)
                {
                    return(BlindingFlash());
                }

                if (Skills.Monk.MysticAlly.CanCast() && Runes.Monk.AirAlly.IsActive && Player.PrimaryResource < Player.PrimaryResourceMax - 200)
                {
                    return(MysticAlly());
                }

                if (Skills.Monk.DashingStrike.CanCast() && !Skills.Monk.DashingStrike.IsLastUsed)
                {
                    return(DashingStrike(CurrentTarget.Position));
                }

                if (Skills.Monk.LashingTailKick.CanCast())
                {
                    return(LashingTailKick(CurrentTarget));
                }
            }

            // With sweeping armada try to keep distance in the sweet spot between 10-15yd
            if (Runes.Monk.SweepingArmada.IsActive)
            {
                var enoughTimePassed = SpellHistory.TimeSinceUse(SNOPower.Walk).TotalMilliseconds > 500;
                var isSoloElite      = TargetUtil.ElitesInRange(25f) == 1 && !AnyUnitsInRange(25f);
                if (enoughTimePassed && isSoloElite && CurrentTarget.RadiusDistance <= 10f && !IsStuck)
                {
                    if (Avoider.TryGetSafeSpot(out position, 12f + CurrentTarget.CollisionRadius, 30f, CurrentTarget.Position))
                    {
                        Core.Logger.Log(LogCategory.Routine, $"Adjusting Distance for Sweeping Armarda RDist={CurrentTarget.RadiusDistance} Dist={ZetaDia.Me.Position.Distance(CurrentTarget.Position)}");
                        return(Walk(position, 2f));
                    }
                }
            }

            if (TrySecondaryPower(out power))
            {
                return(power);
            }

            if (TryPrimaryPower(out power))
            {
                return(power);
            }

            if (IsNoPrimary)
            {
                // Stay away from hostile units when Regen skills are on cooldown and under [50] Spirit
                if (Skills.Monk.BlindingFlash.IsActive && Legendary.TheLawsOfSeph.IsEquipped || Skills.Monk.MysticAlly.IsActive && Runes.Monk.AirAlly.IsActive)
                {
                    var regenOnCooldown = !Skills.Monk.BlindingFlash.CanCast() && !Skills.Monk.MysticAlly.CanCast();
                    var needResource    = Player.PrimaryResource < PrimaryEnergyReserve;
                    if ((regenOnCooldown || needResource) && HostileMonsters.Any(u => u.Distance <= 12f))
                    {
                        Core.Logger.Log(LogCategory.Routine, "Moving away - Low Spirit - Regen on Cooldown");
                        return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 30f)));
                    }
                }
            }

            return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 25f)));
        }
コード例 #24
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));
            }
        }