public TrinityPower GetBuffPower()
        {
            if (Settings.SpiritWalk.UseMode == UseTime.Always && !Player.IsInTown && Skills.WitchDoctor.SpiritWalk.CanCast())
            {
                return(SpiritWalk());
            }

            if (Skills.WitchDoctor.WallOfDeath.CanCast() && IsInCombatOrBeingAttacked)
            {
                return(WallOfDeath(TargetUtil.GetBestClusterPoint()));
            }

            if (Skills.WitchDoctor.SoulHarvest.CanCast() && HostileMonsters.Any(u => u.Distance < 16f))
            {
                // Refresh the buff time to avoid losing 10 stacks.
                if (Skills.WitchDoctor.SoulHarvest.TimeSinceUse > 4500)
                {
                    return(SoulHarvest());
                }

                // Build some stacks
                if (Skills.WitchDoctor.SoulHarvest.BuffStacks < MaxSoulHarvestStacks)
                {
                    return(SoulHarvest());
                }
            }

            return(DefaultBuffPower());
        }
Example #2
0
        //protected override bool ShouldBombardment(out TrinityActor target)
        //{
        //    target = null;

        //    if (!Skills.Crusader.Bombardment.CanCast())
        //        return false;

        //    target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
        //    return target != null;
        //}

        protected override bool ShouldBombardment(out TrinityActor target)
        {
            target = null;
            if (!Skills.Crusader.Bombardment.CanCast())
            {
                return(false);
            }

            target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;

            //credit: noxiaz, fix for out of combat bombard
            if (target == null && Settings.BombardmentOOC)
            {
                // The check is needed becasue its normally only "ironskin" that decide when ever possible to cast bombardment or not.
                var isCastWindow = !ShouldWaitForConventionofElements(
                    Skills.Crusader.Bombardment,
                    Element.Physical,
                    1500,
                    1000);

                if (isCastWindow &&
                    !ShouldBombardWheneverPossible ||
                    ShouldBombardWheneverPossible)
                {
                    // Get the closest target possible
                    target = HostileMonsters.Where(x => x.Distance < 150f).OrderBy(x => x.Distance).FirstOrDefault();
                }
            }

            return(target != null);
        }
Example #3
0
        public TrinityPower GetBuffPower()
        {
            if (TryLaw(out var power))
            {
                //Core.Logger.Verbose(LogCategory.Routine, $"Buff Law");
                return(power);
            }

            if (!Player.IsInTown &&
                Settings.BombardmentOOC &&
                HostileMonsters.Any(u => u.Distance < 150f))
            {
                //Core.Logger.Verbose(LogCategory.Routine, $"Buff Bombard Func");

                // Break Steed to bombard OOC only if waiting for CoE
                var goodTimetoCast = !ShouldBombardWheneverPossible ||
                                     !IsSteedCharging;

                if (goodTimetoCast &&
                    TryBombard(out power))
                {
                    return(power);
                }
            }

            return(null);
        }
Example #4
0
        // Terror

        protected virtual bool ShouldSoulHarvest(out Vector3 position)
        {
            position = Vector3.Zero;

            if (!Skills.WitchDoctor.SoulHarvest.CanCast())
            {
                return(false);
            }

            var stacks = Skills.WitchDoctor.SoulHarvest.BuffStacks;

            if (stacks > 1 && Skills.WitchDoctor.SoulHarvest.TimeSinceUse > 11000 && HostileMonsters.Any(u => u.Distance <= 18f))
            {
                return(true);
            }

            var isMaxHarvestStacks = stacks == MaxSoulHarvestStacks;

            if (isMaxHarvestStacks && !Sets.RaimentOfTheJadeHarvester.IsEquipped)
            {
                return(false);
            }

            position = TargetUtil.GetBestClusterPoint();
            return(true);
        }
Example #5
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.WaveOfLight.CanCast())
                {
                    return(WaveOfLight(CurrentTarget));
                }
            }

            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, 20f)));
        }
        public TrinityPower GetOffensivePower()
        {
            if (TrySpecialPower(out var 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.WaveOfLight.CanCast())
                {
                    return(WaveOfLight(CurrentTarget));
                }
            }

            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 dem stacks");
                        return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 20f)));
                    }
                }
            }

            // 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 right meow.");
            return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 20f)));
        }
Example #7
0
        private bool TryBombard(out TrinityPower trinityPower)
        {
            trinityPower = null;

            // Make sure we cast Bombardment when IronSkin and CoE is Up.
            // Note iron skin is gated below by Convention of Elements check,
            if (Player.HasBuff(SNOPower.X1_Crusader_IronSkin))
            {
                TrinityActor target;

                if (ShouldBombardment(out target))
                {
                    trinityPower = Bombardment(target);
                    return(true);
                }

                if (ShouldShieldGlare(out target))
                {
                    trinityPower = ShieldGlare(target);
                    return(true);
                }

                if (IsInCombat && ShouldConsecration())
                {
                    trinityPower = Consecration();
                    return(true);
                }
            }

            var eliteExists = HostileMonsters.Any(u => u.IsElite && !TrinityCombat.Weighting.ShouldIgnore(u));

            // Provoke with Votoyias spiker.
            var inProvokeRange = !eliteExists || CurrentTarget != null && CurrentTarget.IsElite && CurrentTarget.Distance < 15f;

            if (!ShouldWaitForConventionofElements(Skills.Crusader.Provoke, Element.Physical, 0, 1000) && Skills.Crusader.Provoke.CanCast() && inProvokeRange)
            {
                trinityPower = Provoke();
                return(true);
            }

            // Wait for CoE to Cast Damage CD's
            var isCastWindow        = !ShouldWaitForConventionofElements(Skills.Crusader.Bombardment, Element.Physical, 1500, 1000);
            var isTargetCloseEnough = !eliteExists || CurrentTarget != null && CurrentTarget.IsElite && CurrentTarget.Distance < 20f;

            if (Skills.Crusader.Bombardment.CanCast() && (ShouldBombardWheneverPossible && isTargetCloseEnough || isCastWindow))
            {
                if (ShouldIronSkin())
                {
                    trinityPower = IronSkin();
                    return(true);
                }
            }

            return(false);
        }
Example #8
0
        protected override bool ShouldIronSkin()
        {
            if (!Skills.Crusader.IronSkin.CanCast())
            {
                return(false);
            }

            if (Player.HasBuff(SNOPower.X1_Crusader_IronSkin))
            {
                return(false);
            }

            return(HostileMonsters.Any(u => u.Distance < 80f));
        }
        public TrinityPower GetBuffPower()
        {
            if (ShouldHorrify())
            {
                return(Horrify());
            }

            if (Settings.SpiritWalk.UseMode == UseTime.Always && !Player.IsInTown && Skills.WitchDoctor.SpiritWalk.CanCast())
            {
                return(SpiritWalk());
            }

            var closeUnit = HostileMonsters.FirstOrDefault(u => u.Distance < 16f);

            if (closeUnit != null && !Player.IsInTown && !IsInCombat && Skills.WitchDoctor.SoulHarvest.CanCast())
            {
                if (Settings.HarvestModeOOC == HarvestMode.NonStop)
                {
                    if (Skills.WitchDoctor.Haunt.CanCast() && Skills.WitchDoctor.Haunt.TimeSinceUse > 1000)
                    {
                        return(Haunt(closeUnit));
                    }

                    if (HostileMonsters.Any(u => u.Distance < 16f && u.HasDebuff(SNOPower.Witchdoctor_Haunt)))
                    {
                        return(SoulHarvest());
                    }
                }

                // Refresh the buff time to avoid losing 10 stacks.
                if (Skills.WitchDoctor.SoulHarvest.TimeSinceUse > 4500)
                {
                    return(SoulHarvest());
                }

                // Build some stacks
                if (Skills.WitchDoctor.SoulHarvest.BuffStacks < 10)
                {
                    return(SoulHarvest());
                }
            }

            return(DefaultBuffPower());
        }
        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)));
        }
 public bool AnyUnitsInRange(float range)
 => HostileMonsters.Any(u => u.Position.Distance(Player.Position) < range);
Example #12
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)));
        }
        protected override bool ShouldArchonTeleport(out Vector3 position)
        {
            position = Vector3.Zero;
            var skill              = Skills.Wizard.ArchonTeleport;
            var affixOnPlayer      = Core.Avoidance.InAvoidance(ZetaDia.Me.Position);
            var isShrine           = TrinityCombat.Targeting.CurrentTarget.Type == TrinityObjectType.Shrine;
            var isProgressionGlobe = TrinityCombat.Targeting.CurrentTarget.Type == TrinityObjectType.ProgressionGlobe;
            var isHealthGlobe      = TrinityCombat.Targeting.CurrentTarget.Type == TrinityObjectType.HealthGlobe;

            //Teleport Activations
            var archonHealthIsLow = IsArchonActive && Player.CurrentHealthPct < Settings.ArchonTeleportHealthEmergency;
            var anyElitesinRange  = TargetUtil.AnyElitesInRange(Settings.TeleportEliteKiteRange);
            var anyMobsInRange    = TargetUtil.AnyMobsInRangeOfPosition(Player.Position, Settings.TeleportTrashKiteRange, Settings.TeleportTrashInRangeCount);

            //Teleport Delays
            var archonHealthIsLowDelay = Skills.Wizard.Teleport.TimeSinceUse > Settings.ArchonTeleportHealthDelay;
            var archonTeleportDelay    = (TalRashaStacks == 4 && Skills.Wizard.Teleport.TimeSinceUse > Settings.Archon4StackDelay) || (TalRashaStacks <4 && Skills.Wizard.Teleport.TimeSinceUse> Settings.Archon1StackDelay);

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

            if (CurrentTarget.Distance < 50 && isShrine || isProgressionGlobe || (isHealthGlobe && archonHealthIsLow))
            {
                //Core.Logger.Log($"Teleporting to Priority Target");
                position = CurrentTarget.Position;
                return(true);
            }

            if ((Player.IsChannelling || !Player.IsChannelling))
            {
                if (affixOnPlayer || (archonHealthIsLow && archonHealthIsLowDelay && anyMobsInRange))
                {
                    //Core.Logger.Log($"Teleport for Survival! Affix: {affixOnPlayer}, Health: {archonHealthIsLow}");
                    Avoider.TryGetSafeSpot(out position, Settings.TeleportKiteMinDistance, Settings.TeleportKiteMaxDistance, ZetaDia.Me.Position, node => !HostileMonsters.Any(m => m.Position.Distance(node.NavigableCenter) < 15f));
                    return(true);
                }

                if (Skills.Wizard.Archon.TimeSinceUse > 19500)
                {
                    //Core.Logger.Log($"Teleport! Archon is about to drop!!!");
                    Avoider.TryGetSafeSpot(out position, Settings.TeleportKiteMinDistance, Settings.TeleportKiteMaxDistance, ZetaDia.Me.Position, node => !HostileMonsters.Any(m => m.Position.Distance(node.NavigableCenter) < 15f));
                    return(true);
                }

                if (CurrentTarget.IsElite && anyElitesinRange && archonTeleportDelay)
                {
                    //Core.Logger.Log($"Teleport! Elite too close: {CurrentTarget.Distance} Setting: {Settings.TeleportEliteKiteRange}");
                    Avoider.TryGetSafeSpot(out position, 40, Settings.TeleportKiteMaxDistance, TrinityCombat.Targeting.CurrentTarget.Position, node => !HostileMonsters.Any(m => m.Position.Distance(node.NavigableCenter) < 15f));
                    return(true);
                }

                var target = TargetUtil.BestRangedAoeUnit(10, 50, ClusterSize);
                if (target != null && target.Distance < 30f)
                {
                    //Core.Logger.Log($"Teleport! Trash Target too close: {CurrentTarget.Distance}");
                    Avoider.TryGetSafeSpot(out position, 40, Settings.TeleportKiteMaxDistance, target.Position, node => !HostileMonsters.Any(m => m.Position.Distance(node.NavigableCenter) < 15f));
                    return(true);
                }
            }

            return(position != Vector3.Zero);
        }
Example #14
0
        public TrinityPower GetBuffPower()
        {
            var closeUnit = HostileMonsters.FirstOrDefault(u => u.Distance < 40f);

            if (ShouldRefreshSpiritGuardsBuff && closeUnit != null && !Player.IsInTown)
            {
                //Core.Logger.Log(LogCategory.Routine, "Need Spirit Gaurds Buff");

                if (Skills.Monk.FistsOfThunder.IsActive)
                {
                    return(new TrinityPower(Skills.Monk.FistsOfThunder, 3f));
                }

                else if (Skills.Monk.DeadlyReach.IsActive)
                {
                    return(new TrinityPower(Skills.Monk.DeadlyReach, 3f));
                }

                else if (Skills.Monk.CripplingWave.IsActive)
                {
                    return(new TrinityPower(Skills.Monk.CripplingWave, 3f));
                }

                else if (Skills.Monk.WayOfTheHundredFists.IsActive)
                {
                    return(new TrinityPower(Skills.Monk.WayOfTheHundredFists, 3f));
                }
            }

            if (ShouldSweepingWind())
            {
                return(SweepingWind());
            }

            if (ShouldMantraOfConviction())
            {
                return(MantraOfConviction());
            }

            if (ShouldMantraOfHealing())
            {
                return(MantraOfConviction());
            }

            if (ShouldMantraOfRetribution())
            {
                return(MantraOfRetribution());
            }

            if (ShouldMantraOfSalvation())
            {
                return(MantraOfSalvation());
            }

            if (ShouldEpiphany())
            {
                return(Epiphany());
            }

            if (ShouldMysticAlly())
            {
                return(MysticAlly());
            }

            if (ShouldBreathOfHeaven())
            {
                return(BreathOfHeaven());
            }

            if (ShouldSerenity())
            {
                return(Serenity());
            }

            if (ShouldBlindingFlash())
            {
                return(BlindingFlash());
            }

            if (ShouldInnerSanctuary())
            {
                return(InnerSanctuary());
            }

            return(null);
        }
        public TrinityPower GetOffensivePower()
        {
            Vector3 position;

            var allUnits = Core.Targets.ByType[TrinityObjectType.Unit].Where(u => u.IsUnit && u.RadiusDistance <= 50f).ToList();

            var clusterUnits =
                (from u in allUnits
                 where u.IsUnit && u.Weight > 0 && !u.IsPlayer
                 orderby
                 u.NearbyUnitsWithinDistance(15f) descending,
                 u.Distance,
                 u.HitPointsPct descending
                 select u).ToList();

            var bestClusterUnit = clusterUnits.FirstOrDefault();

            //10 second 60% damage reduction should always be on to survive
            if (!HasJeramsRevengeBuff && Player.CurrentHealthPct > 0.4 && !Core.Avoidance.InCriticalAvoidance(Player.Position) &&
                (ZetaDia.Me.IsInCombat || Player.CurrentHealthPct < 0.4) && bestClusterUnit != null && Skills.WitchDoctor.WallOfDeath.CanCast())
            {
                return(WallOfDeath(allUnits.FirstOrDefault()));
            }

            if (bestClusterUnit != null)
            {
                if (Player.HasBuff(SNOPower.Witchdoctor_Hex))
                {
                    Vector3 explodePos = PlayerMover.IsBlocked ? Player.Position : bestClusterUnit.Position;
                    return(ExplodeChicken(explodePos));
                }

                if (!HasJeramsRevengeBuff && ZetaDia.Me.IsInCombat && Skills.WitchDoctor.WallOfDeath.CanCast())
                {
                    var target = allUnits.FirstOrDefault();
                    if (target != null)
                    {
                        return(WallOfDeath(target));
                    }
                }

                // Make sure we approach carefully when our defenses are down
                var harvestStacks      = Skills.WitchDoctor.SoulHarvest.BuffStacks;
                var bestHarvestCluster = TargetUtil.GetBestClusterPoint(18f, 50f);
                if (Core.Rift.IsGreaterRift && harvestStacks < 10 && Legendary.LakumbasOrnament.IsEquipped)
                {
                    if (Skills.WitchDoctor.SpiritWalk.CanCast() && TargetUtil.ClusterExists(18f, 50f, 2))
                    {
                        return(SpiritWalk());
                    }

                    if (Skills.WitchDoctor.SpiritWalk.IsBuffActive && bestHarvestCluster != null)
                    {
                        return(Walk(bestHarvestCluster));
                    }
                }

                // SpiritWalk for the invulnerability
                var shouldBecomeInvulnerable = Core.Avoidance.Avoider.ShouldAvoid || Core.Avoidance.Avoider.ShouldKite || (Player.CurrentHealthPct < 0.9f && Core.Rift.IsGreaterRift);
                if (Skills.WitchDoctor.SpiritWalk.CanCast() && Settings.SpiritWalk.UseMode == UseTime.Default && TargetUtil.AnyMobsInRange(20f))
                {
                    return(SpiritWalk());
                }

                // Spam hex for the 50% damage reduction
                var closeUnit = HostileMonsters.FirstOrDefault(u => u.Distance < 40f);
                if (!Player.HasBuff(SNOPower.Witchdoctor_Hex) && Skills.WitchDoctor.Hex.CanCast() && closeUnit != null)
                {
                    return(Hex(TargetUtil.GetBestClusterPoint(15f, 20f)));
                }

                var targetsWithoutLocust     = clusterUnits.Where(u => !u.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm)).OrderBy(u => u.Distance);
                var isAnyTargetWithLocust    = clusterUnits.Any(u => u.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) && u.Distance < 45f);
                var percentTargetsWithHaunt  = TargetUtil.DebuffedPercent(SNOPower.Witchdoctor_Haunt, 8f);
                var percentTargetsWithLocust = TargetUtil.DebuffedPercent(SNOPower.Witchdoctor_Locust_Swarm, 12f);
                var isEliteWithoutHaunt      = clusterUnits.Any(u => u.IsElite && !u.HasDebuff(SNOPower.Witchdoctor_Haunt) && u.Distance <= 20f);
                var isElitewithoutLocust     = clusterUnits.Any(u => u.IsElite && !u.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) && u.Distance <= 20f);
                var harvestBuffCooldown      = Core.Cooldowns.GetBuffCooldown(SNOPower.Witchdoctor_SoulHarvest);
                var harvestPossibleStackGain = 10 - harvestStacks;
                var harvestUnitsInRange      = allUnits.Count(u => u.Distance < 12f);
                var interruptForHarvest      = Skills.WitchDoctor.SoulHarvest.CanCast() && harvestPossibleStackGain >= harvestUnitsInRange && harvestBuffCooldown?.Remaining.TotalMilliseconds < 500;
                var interruptForHaunt        = percentTargetsWithHaunt < 0.2f || isEliteWithoutHaunt;
                var needToSwarmElite         = isElitewithoutLocust && !((Legendary.VileHive.IsEquipped || Runes.WitchDoctor.Pestilence.IsActive) && isAnyTargetWithLocust);
                var interruptForLocust       = (percentTargetsWithLocust < 0.1f || needToSwarmElite) && Player.PrimaryResource > 300 && Skills.WitchDoctor.LocustSwarm.CanCast();
                var interruptForHex          = Skills.WitchDoctor.Hex.CanCast();
                var interruptForSpiritWalk   = Skills.WitchDoctor.SpiritWalk.CanCast() && Settings.SpiritWalk.UseMode == UseTime.Default && shouldBecomeInvulnerable;

                // continue channelling firebats?
                if (Player.IsChannelling)
                {
                    if (!interruptForHaunt && !interruptForLocust && !interruptForHarvest && !interruptForHex && !interruptForSpiritWalk)
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Firebats, 30f, Player.Position, 75, 250));
                    }
                }

                // Emergency health situation
                if (Player.CurrentHealthPct < 0.35)
                {
                    if (Skills.WitchDoctor.SpiritWalk.CanCast())
                    {
                        return(SpiritWalk());
                    }

                    if (TargetUtil.AnyMobsInRange(12f) && Skills.WitchDoctor.SoulHarvest.CanCast())
                    {
                        return(SoulHarvest());
                    }

                    if (!HasJeramsRevengeBuff && Skills.WitchDoctor.WallOfDeath.CanCast() && allUnits.Any())
                    {
                        return(WallOfDeath(allUnits.FirstOrDefault()));
                    }
                }

                // Soul harvest for the damage reduction of Okumbas Ornament
                if (Skills.WitchDoctor.SoulHarvest.CanCast() && (bestClusterUnit.Distance < 12f || harvestStacks < 4 && TargetUtil.AnyMobsInRange(10f)) && harvestStacks < 10)
                {
                    if (harvestPossibleStackGain <= harvestUnitsInRange)
                    {
                        return(SoulHarvest());
                    }
                }

                // Locust
                if (Skills.WitchDoctor.LocustSwarm.CanCast() && Skills.WitchDoctor.LocustSwarm.TimeSinceUse > 1000 && targetsWithoutLocust.Any() && (!Runes.WitchDoctor.Pestilence.IsActive || !isAnyTargetWithLocust))
                {
                    if ((percentTargetsWithLocust < Settings.LocustPct || needToSwarmElite) && Player.PrimaryResource > 300 && targetsWithoutLocust.Any())
                    {
                        return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 10f, targetsWithoutLocust.First().AcdId, 0, 0));
                    }
                }

                if (ShouldBigBadVoodoo(out position))
                {
                    return(BigBadVoodoo(position));
                }

                // Piranhas
                if (Skills.WitchDoctor.Piranhas.CanCast() && Player.PrimaryResource >= 250 &&
                    (TargetUtil.ClusterExists(15f, 40f) || TargetUtil.AnyElitesInRange(40f)) && Player.PrimaryResource >= 250)
                {
                    return(Piranhas(TargetUtil.GetBestClusterUnit()));
                }

                // .80 of mobs give or take. Spelltracker check is to prevent repeat casts ont he same target before the projectile arrives.
                var targetsWithoutHaunt = clusterUnits.Where(u => !u.HasDebuff(SNOPower.Witchdoctor_Haunt) && !SpellTracker.IsUnitTracked(u, SNOPower.Witchdoctor_Haunt)).OrderBy(u => u.Distance);
                if ((percentTargetsWithHaunt < Settings.HauntPct || isEliteWithoutHaunt) && targetsWithoutHaunt.Any() && Player.PrimaryResource > 100)
                {
                    var target = targetsWithoutHaunt.First();
                    return(Haunt(target));
                }

                Vector3 bestBuffedPosition;
                TargetUtil.BestBuffPosition(16f, bestClusterUnit.Position, true, out bestBuffedPosition);
                var bestClusterUnitRadiusPosition = MathEx.GetPointAt(bestClusterUnit.Position, bestClusterUnit.CollisionRadius * 1.1f, bestClusterUnit.Rotation);
                var bestFirebatsPosition          = bestBuffedPosition != Vector3.Zero ? bestBuffedPosition : bestClusterUnitRadiusPosition;
                var distance = bestFirebatsPosition.Distance2D(Player.Position);

                // Walk into cluster or buffed location.
                if (distance > 10f && distance < 35f && !PlayerMover.IsBlocked)
                {
                    if (distance > 20f && Skills.WitchDoctor.SpiritWalk.CanCast())
                    {
                        return(SpiritWalk());
                    }

                    //Core.Logger.Warn($"Walking to cluster position. Dist: {bestFirebatsPosition.Distance(Player.Position)}");
                    return(new TrinityPower(SNOPower.Walk, 3f, bestFirebatsPosition, 0, 0));
                }

                if (Skills.WitchDoctor.Firebats.CanCast())
                {
                    var closestUnit = allUnits.OrderBy(u => u.Distance).FirstOrDefault();
                    if (closestUnit != null)
                    {
                        return(Firebats(closestUnit));
                    }
                }
            }

            return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 15f)));
        }
        public TrinityPower GetBuffPower()
        {
            Vector3 position;

            if (ShouldSummonZombieDogs(out position))
            {
                return(IsInCombat ? SummonZombieDogs(position) : SummonZombieDogs());
            }

            if (ShouldGargantuan(out position))
            {
                return(IsInCombat ? Gargantuan(position) : Gargantuan());
            }

            if (Settings.SpiritWalk.UseMode == UseTime.Always && !Player.IsInTown && Skills.WitchDoctor.SpiritWalk.CanCast())
            {
                return(SpiritWalk());
            }

            if (ShouldSpiritWalk())
            {
                return(SpiritWalk());
            }

            if (ShouldFetishArmy())
            {
                return(FetishArmy());
            }

            if (Skills.WitchDoctor.WallOfDeath.CanCast() && IsInCombatOrBeingAttacked)
            {
                return(WallOfDeath(TargetUtil.GetBestClusterPoint()));
            }

            if (Skills.WitchDoctor.SoulHarvest.CanCast() && (!IsChannellingFirebats || Player.CurrentHealthPct < 0.4f) && HostileMonsters.Any(u => u.Distance < 16f))
            {
                // Refresh the buff time to avoid losing 10 stacks.
                if (Skills.WitchDoctor.SoulHarvest.TimeSinceUse > 4500)
                {
                    return(SoulHarvest());
                }

                // Build some stacks
                if (Skills.WitchDoctor.SoulHarvest.BuffStacks < MaxSoulHarvestStacks)
                {
                    return(SoulHarvest());
                }
            }

            return(null);
        }
        public TrinityPower GetOffensivePower()
        {
            TrinityPower power;

            Core.Logger.Log(LogCategory.Routine, $"FanBuff={IsFanBuffReady} TrashCluster={IsFanTrashCluster} EliteCluster={IsFanEliteCluster} Element={IsLightningElement} Crit%={Player.CriticalChancePct}");

            if (Player.CurrentHealthPct < 0.2 && Skills.DemonHunter.FanOfKnives.CanCast())
            {
                Core.Logger.Log(LogCategory.Routine, "About to die...");
                return(FanOfKnives());
            }

            if (ShouldFan())
            {
                Core.Logger.Log(LogCategory.Routine, "Its time for the pain");

                // +15% dmg
                var isWolfBuffReady = !Skills.DemonHunter.Companion.IsActive || Skills.DemonHunter.Companion.IsBuffActive;
                if (!isWolfBuffReady && Skills.DemonHunter.Companion.CanCast())
                {
                    return(Companion());
                }

                // +40% dmg
                var isVengeanceReady = !Skills.DemonHunter.Vengeance.IsActive || Skills.DemonHunter.Vengeance.IsBuffActive;
                if (!isVengeanceReady && Skills.DemonHunter.Vengeance.CanCast())
                {
                    return(Vengeance());
                }

                //if (!Player.IsMaxCriticalChance)
                //{
                //    return Walk(TargetUtil.BestAoeUnit(100f, true));
                //}

                var clusterUnits = TargetUtil.GetBestClusterUnits().Where(u => u.IsElite || u.NearbyUnitsWithinDistance() > ClusterSize).Select(u => u.Position);
                var fanPosition  = TargetUtil.GetCentroid(clusterUnits);

                // If we cant get there, dont miss the opportunity.
                if (IsReallyBlocked && TimeFromElementStart(Element.Lightning) > 3000)
                {
                    Core.Logger.Log(LogCategory.Routine, "Deloying Blocked Fan");
                    return(FanOfKnives());
                }

                return(FanOfKnives(fanPosition));
            }

            if (!IsReallyBlocked)
            {
                var pullingUnit = HostileMonsters
                                  .Where(u => Math.Abs(u.HitPointsPct - 1) < float.Epsilon && (u.IsElite || u.NearbyUnitsWithinDistance(10f) > 3))
                                  .OrderByDescending(u => u.Distance).FirstOrDefault();

                if (pullingUnit != null)
                {
                    Core.Logger.Log(LogCategory.Routine, "Pulling units");
                    return(Walk(pullingUnit));
                }
            }

            if (IsBossFight && LordGreenStoneDamageStacks < 15)
            {
                Core.Logger.Log(LogCategory.Routine, "Attacking while waiting for fan");
                if (TryPrimaryPower(out power))
                {
                    return(power);
                }
            }

            if (LordGreenStoneDamageStacks < 28 || !IsLightningElement)
            {
                Core.Logger.Log(LogCategory.Routine, "Hanging out near fan position");
                return(Walk(TargetUtil.GetLoiterPosition(TargetUtil.BestAoeUnit(100f, true), 30f)));
            }

            Core.Logger.Log(LogCategory.Routine, "Moving to fan position");
            return(Walk(TargetUtil.BestAoeUnit(100f, true)));
        }
Example #18
0
        public TrinityPower GetMovementPower(Vector3 destination)
        {
            // Leap out of combat for the defensive bonuses.

            if (Skills.Barbarian.Leap.CanCast() && (!Player.HasBuff(SNOPower.Barbarian_Leap) || Player.CurrentHealthPct > 0.35f) && !Player.IsInTown && HostileMonsters.Any(u => u.Distance < 20f))
            {
                return(Leap(destination));
            }

            return(Walk(destination));
        }
Example #19
0
 private bool AvoidCondition(AvoidanceNode n)
 {
     return(HostileMonsters.FirstOrDefault(u => u.Distance < 10f) != null &&
            !Core.Avoidance.InAvoidance(n.NavigableCenter) &&
            Core.Avoidance.Grid.IsIntersectedByFlags(Player.Position, n.NavigableCenter, AvoidanceFlags.CriticalAvoidance));
 }
        protected override bool ShouldTeleport(out Vector3 position)
        {
            position = Vector3.Zero;
            var skill             = Skills.Wizard.Teleport;
            var affixOnPlayer     = Core.Avoidance.InAvoidance(ZetaDia.Me.Position);
            var healthIsLow       = Player.CurrentHealthPct < Settings.TeleportHealthEmergency;
            var archonHealthIsLow = Player.CurrentHealthPct < Settings.ArchonTeleportHealthEmergency;
            var anyElitesinRange  = TargetUtil.AnyElitesInRange(Settings.TeleportEliteKiteRange);
            var anyMobsInRange    = TargetUtil.AnyMobsInRangeOfPosition(Player.Position, Settings.TeleportTrashKiteRange, Settings.TeleportTrashInRangeCount);

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

            if (Player.IsChannelling || !Player.IsChannelling)
            {
                if (anyElitesinRange || anyMobsInRange || healthIsLow || affixOnPlayer)
                {
                    Core.Logger.Log(LogCategory.Routine, $"Close Elites: {anyElitesinRange}, Mobs: {anyMobsInRange}, Health: {healthIsLow}, Affix: {affixOnPlayer}");
                }
                return(true);
            }

            Avoider.TryGetSafeSpot(out position, Settings.TeleportKiteMinDistance, Settings.TeleportKiteMaxDistance, ZetaDia.Me.Position, node => !HostileMonsters.Any(m => m.Position.Distance(node.NavigableCenter) < 15f));

            return(position != Vector3.Zero);
        }