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()); }
//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); }
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); }
// 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); }
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))); }
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); }
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);
//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); }
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))); }
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)); }
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); }