protected override bool ShouldEnergyTwister(out TrinityActor target)
        {
            target = null;

            if (!Skills.Wizard.EnergyTwister.CanCast())
            {
                return(false);
            }

            if (Skills.Wizard.EnergyTwister.TimeSinceUse < Settings.EnergyTwisterDelay)
            {
                return(false);
            }

            target = TargetUtil.BestEliteInRange(50, true);
            if (target != null)
            {
                return(true);
            }

            target = TargetUtil.BestAoeUnit(50, true);
            if (TargetUtil.BestAoeUnit(50, true) != null)
            {
                return(true);
            }

            target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
            if (Skills.Wizard.Archon.CooldownRemaining < 2000 && TalRashaStacks < 3)
            {
                return(true);
            }

            target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
            return(target != null);
        }
Beispiel #2
0
        protected override bool ShouldAncientSpear(out TrinityActor target)
        {
            // Credit: phelon's raekor.

            target = null;

            if (!Skills.Barbarian.AncientSpear.CanCast())
            {
                return(false);
            }

            if (Sets.TheLegacyOfRaekor.IsFullyEquipped && RaekorDamageStacks < 5)
            {
                return(false);
            }

            target = TargetUtil.BestAoeUnit(60, true).IsInLineOfSight
                ? TargetUtil.BestAoeUnit(60, true)
                : TargetUtil.GetBestClusterUnit(10, 60, false, true, false, true);

            if (target == null)
            {
                return(false);
            }

            return(target.Distance <= 60 && Player.PrimaryResourcePct > 0.95);
        }
        /* Non-Archon skills below: Used for DPS and Building Tal Rasha Stacks
         *
         * Stacks are earned by casting an elemental skill: Lightning, Fire, Arcane and Cold.
         * Each stack lasts for 8 seconds, and they do not refresh each other.
         * Each stack adds: 750% damage to attacks, 25% All resistance, Cause a matching elemental meteor to fall [8 second cooldown]
         * Note that these skills do not need to actually deal damage to any enemies to proc a stack.
         */

        protected override bool ShouldArcaneTorrent(out TrinityActor target)
        {
            target = null;

            if (!Skills.Wizard.ArcaneTorrent.CanCast())
            {
                return(false);
            }

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

            target = CurrentTarget;
            if (target.IsTreasureGoblin && target.Distance < Settings.ArcaneTorrentRange && target.IsInLineOfSight)
            {
                return(true);
            }

            target = TargetUtil.GetClosestUnit(Settings.ArcaneTorrentRange);
            if (target != null && (Player.CurrentHealthPct < Settings.EmergencyHealthPct) || IsBlocked || IsStuck)
            {
                return(true);
            }

            target = TargetUtil.BestAoeUnit(Settings.ArcaneTorrentRange, true);
            if (target != null && TalRashaStacks >= 2)
            {
                return(true);
            }

            return(target != null);
        }
Beispiel #4
0
        protected override bool ShouldCripplingWave(out TrinityActor target)
        {
            target = null;

            if (!Skills.Monk.CripplingWave.CanCast())
            {
                return(false);
            }

            target = TargetUtil.BestEliteInRange(45, true);
            if (target != null && target.Distance < 12)
            {
                return(true);
            }

            target = TargetUtil.BestAoeUnit(45, true);
            if (target != null && target.Distance < 12) //removed resource check here
            {
                return(true);
            }

            target = TargetUtil.BestAuraUnit(SNOPower.Monk_CripplingWave, 12f, true);
            if (target != null && target.Distance < 12)
            {
                return(true);
            }

            target = TargetUtil.GetClosestUnit(12f);
            return(target != null);
        }
Beispiel #5
0
        protected virtual bool ShouldAncientSpear(out TrinityActor target)
        {
            target = null;
            var skill = Skills.Barbarian.AncientSpear;

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

            if (skill.IsLastUsed && IsMultiSpender && skill.TimeSinceUse < 250)
            {
                return(false);
            }

            target = TargetUtil.BestAoeUnit(60, true).IsInLineOfSight
                ? TargetUtil.BestAoeUnit(60, true)
                : TargetUtil.GetBestClusterUnit(10, 60, false, true, false, true);

            if (target == null)
            {
                return(false);
            }

            return(target.Distance <= 60 && Player.PrimaryResourcePct > 0.95);
        }
Beispiel #6
0
        protected virtual bool ShouldInnerSanctuary()
        {
            if (!Skills.Monk.InnerSanctuary.CanCast())
            {
                return(false);
            }

            if (HasInstantCooldowns && !Skills.Monk.InnerSanctuary.IsLastUsed)
            {
                return(true);
            }

            if (Core.Buffs.HasBuff(SNOPower.X1_Monk_InnerSanctuary))
            {
                return(false);
            }

            if (Player.CurrentHealthPct > 0.9 && !TargetUtil.AnyElitesInRange(10f))
            {
                return(false);
            }

            if (TargetUtil.BestAoeUnit(25, Player.IsInParty).Distance > 20)
            {
                return(false);
            }

            return(true);
        }
        protected override bool ShouldBlackHole(out TrinityActor target)
        {
            target = null;

            if (!Skills.Wizard.BlackHole.CanCast())
            {
                return(false);
            }

            if (Skills.Wizard.BlackHole.TimeSinceUse < Settings.BlackHoleDelay)
            {
                return(false);
            }

            target = TargetUtil.BestEliteInRange(50, true);
            if (target != null)
            {
                return(true);
            }

            target = TargetUtil.BestAoeUnit(50, true);
            if (TargetUtil.BestAoeUnit(50, true) != null)
            {
                return(true);
            }

            //Make sure that we get all of our Tal Stacks before Archon!
            target = TargetUtil.GetBestClusterUnit() ?? CurrentTarget;
            if (Skills.Wizard.Archon.CooldownRemaining < 2000 && TalRashaStacks < 3)
            {
                return(true);
            }

            return(target != null);
        }
        protected override bool ShouldWallOfDeath(out TrinityActor target)
        {
            target = null;

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

            target = TargetUtil.BestAoeUnit(35, true);

            return(target != null);
        }
        protected override bool ShouldPiranhas(out TrinityActor target)
        {
            target = null;

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

            target = Runes.WitchDoctor.WaveOfMutilation.IsActive
                ? TargetUtil.GetBestPierceTarget(25, true)
                : TargetUtil.BestAoeUnit(35, true);

            return(target != null);
        }
Beispiel #10
0
        protected override bool ShouldInnerSanctuary()
        {
            if (!Skills.Monk.InnerSanctuary.CanCast())
            {
                return(false);
            }

            if (Skills.Monk.InnerSanctuary.TimeSinceUse < Settings.InnerSanctuaryDelay)
            {
                return(false);
            }

            if (TargetUtil.BestAoeUnit(45, true).Distance > Settings.InnerSanctuaryMinRange)
            {
                return(false);
            }

            return(true);
        }
        protected override bool ShouldAncientSpear(out TrinityActor target)
        {
            target = null;

            if (!Skills.Barbarian.AncientSpear.CanCast())
            {
                return(false);
            }

            target = TargetUtil.BestAoeUnit(60, true).IsInLineOfSight
                ? TargetUtil.BestAoeUnit(60, true)
                : TargetUtil.GetBestClusterUnit(10, 60, false, true, false, true);

            // wait for 95% resource would cause max damage on boulder toss
            // but the cooldown reset time for leap is possibly more important?
            //return target.Distance <= 60 && Player.PrimaryResourcePct > 0.95;

            return(target?.Distance <= 60);
        }
Beispiel #12
0
        protected virtual bool ShouldPiranhas(out TrinityActor target)
        {
            target = null;

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

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

            target = Runes.WitchDoctor.WaveOfMutilation.IsActive
                ? TargetUtil.GetBestPierceTarget(25, true)
                : TargetUtil.BestAoeUnit(35, true);

            return(target != null);
        }
Beispiel #13
0
        protected override bool ShouldEpiphany()
        {
            if (!AllowedToUse(Settings.Epiphany, Skills.Monk.Epiphany))
            {
                return(false);
            }

            if (!Skills.Monk.Epiphany.CanCast())
            {
                return(false);
            }

            if (HasInstantCooldowns && !Skills.Monk.Epiphany.IsLastUsed)
            {
                return(true);
            }

            if (TargetUtil.BestAoeUnit(45, true).Distance > 20)
            {
                return(false);
            }

            return(true);
        }
Beispiel #14
0
        protected virtual bool ShouldEpiphany()
        {
            if (!Skills.Monk.Epiphany.CanCast())
            {
                return(false);
            }

            if (HasInstantCooldowns && !Skills.Monk.Epiphany.IsLastUsed)
            {
                return(true);
            }

            if (Player.CurrentHealthPct > 0.9 && !TargetUtil.AnyElitesInRange(30f))
            {
                return(false);
            }

            if (TargetUtil.BestAoeUnit(25, Player.IsInParty)?.Distance > 30 && !TargetUtil.AnyElitesInRange(20))
            {
                return(false);
            }

            return(true);
        }
        public TrinityPower GetOffensivePower()
        {
            // Ported from Phelon's Jade Harvester Routine

            TrinityPower power;
            TrinityActor target;
            Vector3      position;

            if (Settings.HarvestModeIC == HarvestMode.NonStop && Skills.WitchDoctor.SoulHarvest.CanCast() && TargetUtil.AnyMobsInRange(14f))
            {
                return(SoulHarvest());
            }

            if (ShouldPiranhas(out target))
            {
                return(Piranhas(target));
            }

            if (ShouldSoulHarvest(out position))
            {
                if (Skills.WitchDoctor.SpiritWalk.CanCast())
                {
                    return(SpiritWalk());
                }

                return(SoulHarvest(position));
            }

            if (ShouldWallOfDeath(out target))
            {
                return(WallOfDeath(target));
            }

            if (ShouldLocustSwarm(out target))
            {
                return(LocustSwarm(target));
            }

            if (ShouldHaunt(out target))
            {
                return(Haunt(target));
            }

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

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

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

            if (Player.HasBuff(SNOPower.Witchdoctor_SpiritWalk))
            {
                if (Player.CurrentHealthPct < EmergencyHealthPct &&
                    TargetUtil.BestWalkLocation(35f, true).Distance(Player.Position) > 5)
                {
                    return(new TrinityPower(SNOPower.Walk, 7f, TargetUtil.BestWalkLocation(45f, true)));
                }

                if (TargetUtil.BestDpsPosition(TargetUtil.BestAoeUnit(45, true).Position, 45f, true).Distance(Player.Position) > 5)
                {
                    return(new TrinityPower(SNOPower.Walk, 3f,
                                            TargetUtil.BestDpsPosition(TargetUtil.BestAoeUnit(45, true).Position, 45f, true)));
                }
            }

            return(new TrinityPower(SNOPower.Walk, 3f,
                                    TargetUtil.BestDpsPosition(TargetUtil.BestAoeUnit(45, true).Position, 45f, true)));
        }
Beispiel #16
0
        protected override bool ShouldFuriousCharge(out Vector3 position)
        {
            // Credit: phelon's raekor.

            position = Vector3.Zero;
            TrinityActor target = null;

            if (!Skills.Barbarian.FuriousCharge.CanCast())
            {
                return(false);
            }

            var          targetGoal       = Math.Floor(5 * Core.Player.CooldownReductionPct);
            TrinityActor bestPierce       = TargetUtil.GetBestPierceTarget(45);
            var          bestPierceCount  = bestPierce?.NearbyUnitsWithinDistance(7) ?? 0;
            TrinityActor bestTarget       = TargetUtil.BestAoeUnit(45, true);
            var          bestTargetCount  = bestTarget?.NearbyUnitsWithinDistance(7) ?? 0;
            TrinityActor bestCluster      = TargetUtil.GetBestClusterUnit(7, 45);
            var          bestClusterCount = bestCluster?.NearbyUnitsWithinDistance(7) ?? 0;

            if (!Core.Buffs.HasCastingShrine)
            {
                if (bestTarget != null && TargetUtil.PierceHitsMonster(bestTarget.Position))
                {
                    if (bestTargetCount == 1 || bestTargetCount >= targetGoal)
                    {
                        position = GetPositionBehind(bestTarget.Position);
                        return(true);
                    }
                }

                if (bestPierce != null && bestCluster != null && TargetUtil.PierceHitsMonster(bestPierce.Position) && TargetUtil.PierceHitsMonster(bestCluster.Position))
                {
                    if (bestPierceCount == 1 || bestPierceCount >= targetGoal &&
                        bestClusterCount == 1 || bestClusterCount >= targetGoal)
                    {
                        if (bestClusterCount > bestPierceCount)
                        {
                            position = GetPositionBehind(bestCluster.Position);
                            return(true);
                        }
                        position = GetPositionBehind(bestPierce.Position);
                        return(true);
                    }
                    if (bestPierceCount != 1 && bestPierceCount < targetGoal &&
                        (bestClusterCount == 1 || bestClusterCount >= targetGoal))
                    {
                        position = GetPositionBehind(bestCluster.Position);
                        return(true);
                    }
                }

                if (bestPierce != null && TargetUtil.PierceHitsMonster(bestPierce.Position))
                {
                    if (bestClusterCount != 1 && bestClusterCount < targetGoal &&
                        (bestPierceCount == 1 || bestPierceCount >= targetGoal))
                    {
                        position = GetPositionBehind(bestPierce.Position);
                        return(true);
                    }
                }
            }

            position = GetPositionBehind(CurrentTarget.Position);
            return(true);
        }
        public TrinityPower GetOffensivePower()
        {
            // Ported from Phelon's Cold Garg routine

            TrinityPower power;
            TrinityActor target;

            // Recast gargs if they are being lazy.
            var myGargs           = TargetUtil.FindPets(PetType.Gargantuan);
            var distGargsToTarget = TargetUtil.Centroid(myGargs.Select(g => g.Position)).Distance(CurrentTarget.Position);

            if (distGargsToTarget > 30f && Player.PrimaryResourcePct > 0.5f && Skills.WitchDoctor.Gargantuan.CanCast())
            {
                return(Gargantuan(CurrentTarget.Position));
            }

            var     bestDpsTarget = TargetUtil.BestAoeUnit(35f, true);
            Vector3 bestDpsPosition;

            if (Core.Player.HasBuff(SNOPower.Witchdoctor_SpiritWalk))
            {
                if (Player.CurrentHealthPct < EmergencyHealthPct && TargetUtil.ClosestGlobe(35f, true) != null)
                {
                    return(Walk(TargetUtil.ClosestGlobe(35f, true).Position));
                }

                if (TargetUtil.BestBuffPosition(35f, bestDpsTarget.Position, false, out bestDpsPosition) && bestDpsPosition.Distance2D(Player.Position) > 6f)
                {
                    return(Walk(bestDpsPosition));
                }
            }

            if (TargetUtil.BestBuffPosition(12f, bestDpsTarget.Position, false, out bestDpsPosition) &&
                (TargetUtil.UnitsBetweenLocations(Player.Position, bestDpsPosition).Count < 6 || Legendary.IllusoryBoots.IsEquipped) &&
                bestDpsPosition.Distance2D(Player.Position) > 6f)
            {
                return(Walk(bestDpsPosition));
            }

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

            if (ShouldPiranhas(out target))
            {
                return(Piranhas(target));
            }

            if (ShouldWallOfDeath(out target))
            {
                return(WallOfDeath(target));
            }

            if (ShouldHaunt(out target))
            {
                return(Haunt(target));
            }

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

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

            if (Settings.KiteVariation == KiteVariation.DistantEmptySpace)
            {
                // Stand still for damage buff - defualt Avoider.SafeSpot includes logic
                // to suppress minor variations in safespot position if less than 12f
                if (Player.CurrentHealthPct > 0.8f && !TargetUtil.AnyMobsInRange(15f))
                {
                    return(Walk(Avoider.SafeSpot));
                }

                return(Walk(TargetUtil.GetLoiterPosition(bestDpsTarget, 25f)));
            }

            //KiteVariation.NearTargetCluster
            return(Walk(Avoider.SafeSpot));
        }
        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)));
        }