static void Main(string[] args) { var spellTracker = new SpellTracker(); var jungleCamp = new JungleTracker(); jungleCamp.Load(); }
void GetLines() { SpellTracker spelltrk = SpellTracker.FindObjectOfType <SpellTracker>(); foreach (SpellRuneNode node in spelltrk.allRuneNodes) { foreach (SpellRuneConnection connection in node.connectingNodes) { GameObject newLine = new GameObject(); newLine.transform.SetParent(lineContainer.transform); newLine.transform.position = Vector3.zero; RuneLine lines = newLine.AddComponent <RuneLine>(); lines.lineRender = newLine.GetComponent <LineRenderer>(); // Get the PARTICLE Positions lines.pointA = node.particleEffect.GetComponent <RectTransform>(); lines.pointB = connection.subNode.particleEffect.GetComponent <RectTransform>(); Debug.Log(node.spellRuneIndex); Debug.Log(connection.subNode.spellRuneIndex); lines.runePair.int1 = node.spellRuneIndex; lines.runePair.int2 = connection.subNode.spellRuneIndex; lines.UpdateLinePositions(); // Record Line Ints //lines.runePair.int1 = node.spellRuneIndex; //lines.runePair.int2 = connection.subNode.spellRuneIndex; } } }
static Types() { RecallTrackerMenu = Menu.AddSubMenu("::Recall Tracker::"); RecallTracker.Initialize(); SpellTrackerMenu = Menu.AddSubMenu("::Spell Tracker::"); SpellTracker.Initialize(); }
/// <summary> /// When Marked for Death should be cast /// </summary> private static bool MarkedForDeathCondition(SkillMeta meta) { meta.CastRange = 100f; meta.CastFlags = CanCastFlags.NoTimer; if (!CurrentTarget.HasDebuff(SNOPower.DemonHunter_MarkedForDeath) && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.DemonHunter_MarkedForDeath)) { return(true); } return(false); }
/// <summary> /// Cast this skill /// </summary> public void Cast(Vector3 clickPosition, int targetAcdGuid) { if (SNOPower != SNOPower.None && clickPosition != Vector3.Zero && IsActive && GameIsReady) { if (ZetaDia.Me.UsePower(SNOPower, clickPosition, Trinity.CurrentWorldDynamicId, targetAcdGuid)) { Trinity.LastPowerUsed = SNOPower; CacheData.AbilityLastUsed[SNOPower] = DateTime.UtcNow; if (CombatBase.CurrentTarget != null) { SpellTracker.TrackSpellOnUnit(CombatBase.CurrentTarget.ACDGuid, SNOPower); } SpellHistory.RecordSpell(SNOPower); } } }
void Awake() { //Check if there is already an instance of SpellTracker if (instance == null) { //if not, set it to this. instance = this; } //If instance already exists: else if (instance != this) { //Destroy this, this enforces our singleton pattern so there can only be one instance of QuestTracker. Destroy(gameObject); } //Set QuestTracker to DontDestroyOnLoad so that it won't be destroyed when reloading our scene. DontDestroyOnLoad(gameObject); }
protected virtual bool ShouldMarkedForDeath(out TrinityActor target) { target = null; if (!Skills.DemonHunter.MarkedForDeath.CanCast()) { return(false); } if (CurrentTarget.HasDebuff(SNOPower.DemonHunter_MarkedForDeath)) { return(false); } if (SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.DemonHunter_MarkedForDeath)) { return(false); } target = CurrentTarget; return(target != 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 static TrinityPower GetPower() { TrinityPower power = null; // Spirit Walk, always! if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } var hasAngryChicken = (Skills.WitchDoctor.Hex.IsActive && Runes.WitchDoctor.AngryChicken.IsActive) || CacheData.Hotbar.ActivePowers.Contains(SNOPower.Witchdoctor_Hex_ChickenWalk); if (hasAngryChicken && Sets.ManajumasWay.IsEquipped && CanCast(SNOPower.Witchdoctor_Hex)) { return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } // Combat Avoidance Spells if (!UseOOCBuff && IsCurrentlyAvoiding) { // Spirit Walk out of AoE if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Soul harvest at current location while avoiding if (Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && MinimumSoulHarvestCriteria(Enemies.BestCluster)) { Skills.WitchDoctor.SoulHarvest.Cast(); } } // Incapacitated or Rooted if (!UseOOCBuff && (Player.IsIncapacitated || Player.IsRooted)) { // Spirit Walk if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } } // Combat Spells with a Target if (!UseOOCBuff && !IsCurrentlyAvoiding && CurrentTarget != null) { if (_bastianGeneratorWaitTimer.IsFinished && ShouldRefreshBastiansGeneratorBuff) { if (Hotbar.Contains(SNOPower.Witchdoctor_CorpseSpider)) { return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, 50f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.Witchdoctor_PoisonDart)) { return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, 50f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.Witchdoctor_PlagueOfToads)) { return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, 50f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.Witchdoctor_Firebomb)) { return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, 50f, CurrentTarget.ACDGuid)); } _bastianGeneratorWaitTimer.Reset(); } bool hasGraveInjustice = CacheData.Hotbar.PassiveSkills.Contains(SNOPower.Witchdoctor_Passive_GraveInjustice); // Debug.Print(CacheData.Hotbar.GetSkill(SNOPower.Witchdoctor_Hex).RuneIndex.ToString()); var isChicken = CacheData.Hotbar.ActivePowers.Contains(SNOPower.Witchdoctor_Hex_ChickenWalk); bool hasVisionQuest = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_VisionQuest); // Set max ranged attack range, based on Grave Injustice, and current target NOT standing in avoidance, and health > 25% float rangedAttackMaxRange = 30f; if (hasGraveInjustice && !CurrentTarget.IsStandingInAvoidance && Player.CurrentHealthPct > 0.25) { rangedAttackMaxRange = Math.Min(Player.GoldPickupRadius + 8f, 30f); } // Set basic attack range, depending on whether or not we have Bears and whether or not we are a tik tank float basicAttackRange = 35f; if (hasGraveInjustice) { basicAttackRange = rangedAttackMaxRange; } else if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { basicAttackRange = 30f; } else if (Legendary.TiklandianVisage.IsEquipped && !TikHorrifyCriteria(Enemies.BestLargeCluster)) { basicAttackRange = 25f; } else if (Legendary.TiklandianVisage.IsEquipped) { basicAttackRange = 1f; } // Summon Pets ----------------------------------------------------------------------- // Hex with angry chicken, is chicken, explode! if (isChicken && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f || UseDestructiblePower)) // && CanCast(SNOPower.Witchdoctor_Hex_Explode) { //Debug.Print("Attempting to cast HEx Explosion {0}", ZetaDia.Me.UsePower(SNOPower.Witchdoctor_Hex_Explode, ZetaDia.Me.Position, // ZetaDia.CurrentWorldDynamicId)); return(new TrinityPower(SNOPower.Witchdoctor_Hex_Explode)); } bool hasJaunt = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 1); bool hasHonoredGuest = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 3); bool hasUmbralShock = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 2); bool hasSeverance = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 0); bool hasHealingJourney = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 4); // Spirit Walk for Goblins chasing if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && CurrentTarget.IsTreasureGoblin && CurrentTarget.HitPointsPct < 0.90 && CurrentTarget.RadiusDistance <= 40f) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Spirit Walk < 65% Health: Healing Journey if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHealingJourney && Player.CurrentHealthPct <= V.F("WitchDoctor.SpiritWalk.HealingJourneyHealth")) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Spirit Walk < 50% Mana: Honored Guest if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHonoredGuest && Player.PrimaryResourcePct <= V.F("WitchDoctor.SpiritWalk.HonoredGuestMana")) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } //bool shouldRefreshVisionQuest = GetTimeSinceLastVisionQuestRefresh() > 4000; bool shouldRefreshVisionQuest = !GetHasBuff(SNOPower.Witchdoctor_Passive_VisionQuest) || GetTimeSinceLastVisionQuestRefresh() > 3800; // Vision Quest Passive if (hasVisionQuest && shouldRefreshVisionQuest) { // Poison Darts if (CanCast(SNOPower.Witchdoctor_PoisonDart)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid)); } // Corpse Spiders if (CanCast(SNOPower.Witchdoctor_CorpseSpider)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid)); } // Plague Of Toads if (CanCast(SNOPower.Witchdoctor_PlagueOfToads)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid)); } // Fire Bomb if (CanCast(SNOPower.Witchdoctor_Firebomb)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid));; } } bool hasVengefulSpirit = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 4); bool hasSwallowYourSoul = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 3); // START Jade Harvester ----------------------------------------------------------------------- if (Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive) { //LogTargetArea("BestLargeCluster", Enemies.BestLargeCluster); //LogTargetArea("BestCluster", Enemies.BestCluster); //LogTargetArea("Nearby", Enemies.Nearby); //LogTargetArea("CloseNearby", Enemies.CloseNearby); // Piranhas if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 && (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) && LastPowerUsed != SNOPower.Witchdoctor_Piranhas && Player.PrimaryResource >= 250) { return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, Enemies.BestCluster.Position)); } // Should we move to cluster for harvest if (IdealSoulHarvestCriteria(Enemies.BestLargeCluster)) { //LogTargetArea("--- Found a good harvest location...", Enemies.BestLargeCluster); MoveToSoulHarvestPoint(Enemies.BestLargeCluster); } // Is there a slightly better position than right here if (MinimumSoulHarvestCriteria(Enemies.BestCluster) && (Enemies.BestCluster.EliteCount >= 2 || Enemies.BestCluster.UnitCount > 4)) { //LogTargetArea("--- Found an average harvest location...", Enemies.BestCluster); MoveToSoulHarvestPoint(Enemies.BestCluster); } // Should we harvest right here? if (MinimumSoulHarvestCriteria(Enemies.CloseNearby)) { //LogTargetArea("--- Harvesting (CurrentPosition)", Enemies.CloseNearby); return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Locust Swarm if (CanCast(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource >= 300 && !CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm)) { return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 20f, CurrentTarget.ACDGuid)); } // Haunt if (Skills.WitchDoctor.Haunt.CanCast() && Player.PrimaryResource >= 50 && !CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Haunt)) { return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 45f, CurrentTarget.ACDGuid)); } // Acid Cloud if (Skills.WitchDoctor.AcidCloud.CanCast() && Player.PrimaryResource >= 325 && LastPowerUsed != SNOPower.Witchdoctor_AcidCloud) { Vector3 bestClusterPoint; if (Passives.WitchDoctor.GraveInjustice.IsActive) { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(Player.GoldPickupRadius + 8f, 30f)); } else { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f); } return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, rangedAttackMaxRange, bestClusterPoint)); } // Spread the love around if (!CurrentTarget.IsTreasureGoblin && CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) && CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Haunt) && Enemies.Nearby.UnitCount > 3 && Enemies.Nearby.DebuffedPercent(HarvesterCoreDebuffs) < 0.5) { //var oldTarget = Trinity.CurrentTarget; Trinity.Blacklist3Seconds.Add(CurrentTarget.RActorGuid); Trinity.CurrentTarget = Enemies.BestCluster.GetTargetWithoutDebuffs(HarvesterCoreDebuffs); //Logger.LogNormal("{0} {1} is fully debuffed, switched to {2} {3}", oldTarget.InternalName, oldTarget.ACDGuid, CurrentTarget.InternalName, CurrentTarget.ACDGuid); } // Save mana for locust swarm || piranhas if (!CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource < 300) { //Logger.LogNormal("Saving mana"); return(DefaultPower); } } // END Jade Harvester ----------------------------------------------------------------------- // Tiklandian Visage ---------------------------------------------------------------------- // Constantly casts Horrify and moves the middle of clusters if (Legendary.TiklandianVisage.IsEquipped) { // Piranhas if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 && (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) && LastPowerUsed != SNOPower.Witchdoctor_Piranhas && Player.PrimaryResource >= 250) { return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, Enemies.BestCluster.Position)); } //Cast Horrify before we go into the fray if (CanCast(SNOPower.Witchdoctor_Horrify)) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Should we move to a better position to fear people if (TikHorrifyCriteria(Enemies.BestLargeCluster)) { MoveToHorrifyPoint(Enemies.BestLargeCluster); } } // END Tiklandian Visage ---------------------------------------------------------------------- // Sacrifice if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 && (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 9f))) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice)); } // Sacrifice for Circle of Life bool hasCircleofLife = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_CircleOfLife); if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 && hasCircleofLife && TargetUtil.AnyMobsInRange(15f)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice)); } // Wall of Zombies if (CanCast(SNOPower.Witchdoctor_WallOfZombies) && (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 1) || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f))) { return(new TrinityPower(SNOPower.Witchdoctor_WallOfZombies, 25f, CurrentTarget.Position)); } bool hasRestlessGiant = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0); bool hasWrathfulProtector = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3); if (CanCast(SNOPower.Witchdoctor_Gargantuan)) { // Gargantuan, Recast on Elites or Bosses to trigger Restless Giant if (hasRestlessGiant && (TargetUtil.IsEliteTargetInRange(30f) || Trinity.PlayerOwnedGargantuanCount == 0)) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } // Gargantuan Wrathful Protector, 15 seconds of smash, use sparingly! if (hasWrathfulProtector && TargetUtil.IsEliteTargetInRange(30f)) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } // Gargantuan regular if (!hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } } bool hasSacrifice = Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice); // Zombie Dogs for Sacrifice if (hasSacrifice && CanCast(SNOPower.Witchdoctor_SummonZombieDog) && (LastPowerUsed == SNOPower.Witchdoctor_Sacrifice || Trinity.PlayerOwnedZombieDogCount <= 2) && LastPowerUsed != SNOPower.Witchdoctor_SummonZombieDog) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog)); } // Hex with angry chicken, check if we want to shape shift and explode if (CanCast(SNOPower.Witchdoctor_Hex) && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f) && hasAngryChicken) { return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } // Hex Spam Cast without angry chicken if (CanCast(SNOPower.Witchdoctor_Hex) && !hasAngryChicken && (TargetUtil.AnyElitesInRange(12) || TargetUtil.AnyMobsInRange(12, 2) || TargetUtil.IsEliteTargetInRange(18f))) { return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } if (CanCast(SNOPower.Witchdoctor_SoulHarvest) && (TargetUtil.AnyElitesInRange(16) || TargetUtil.AnyMobsInRange(16, 2) || TargetUtil.IsEliteTargetInRange(16f))) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Mass Confuse, elites only or big mobs or to escape on low health if (CanCast(SNOPower.Witchdoctor_MassConfusion) && (TargetUtil.AnyElitesInRange(12, 1) || TargetUtil.AnyMobsInRange(12, 6) || Player.CurrentHealthPct <= 0.25 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) && !CurrentTarget.IsTreasureGoblin) { return(new TrinityPower(SNOPower.Witchdoctor_MassConfusion, 0f, CurrentTarget.ACDGuid)); } if (!Settings.Combat.WitchDoctor.UseBigBadVoodooOffCooldown) { // Big Bad Voodoo, elites and bosses only if (CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && (TargetUtil.EliteOrTrashInRange(25f) || (CurrentTarget.IsBoss && CurrentTarget.Distance <= 30f))) { return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo)); } } else { // Big Bad Voodo, cast whenever available if (!UseOOCBuff && !Player.IsIncapacitated && CanCast(SNOPower.Witchdoctor_BigBadVoodoo)) { return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo)); } } // Grasp of the Dead if (CanCast(SNOPower.Witchdoctor_GraspOfTheDead) && (TargetUtil.AnyMobsInRange(30, 2) || TargetUtil.EliteOrTrashInRange(30f)) && Player.PrimaryResource >= 150) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(); return(new TrinityPower(SNOPower.Witchdoctor_GraspOfTheDead, 25f, bestClusterPoint)); } // Piranhas if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 && (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) && Player.PrimaryResource >= 250) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(); return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, bestClusterPoint)); } bool hasPhobia = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 2); bool hasStalker = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4); bool hasFaceOfDeath = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 1); bool hasFrighteningAspect = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 0); bool hasRuthlessTerror = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 3); float horrifyRadius = hasFaceOfDeath ? 24f : 12f; // Horrify when low on health if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= EmergencyHealthPotionLimit && TargetUtil.AnyMobsInRange(horrifyRadius, 3)) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Horrify Buff at 35% health -- Freightening Aspect if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= 0.35 && hasFrighteningAspect) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Spam Horrify if (CanCast(SNOPower.Witchdoctor_Horrify) && Settings.Combat.WitchDoctor.SpamHorrify) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Fetish Army, elites only if (CanCast(SNOPower.Witchdoctor_FetishArmy) && (TargetUtil.EliteOrTrashInRange(30f) || TargetUtil.IsEliteTargetInRange(30f) || Settings.Combat.WitchDoctor.UseFetishArmyOffCooldown)) { return(new TrinityPower(SNOPower.Witchdoctor_FetishArmy)); } bool hasManitou = Runes.WitchDoctor.Manitou.IsActive; // Spirit Barrage Manitou if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && TimeSincePowerUse(SNOPower.Witchdoctor_SpiritBarrage) > 18000 && hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage)); } bool hasResentfulSpirit = Runes.WitchDoctor.ResentfulSpirits.IsActive; // Haunt if (CanCast(SNOPower.Witchdoctor_Haunt) && Player.PrimaryResource >= 50 && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Haunt) && LastPowerUsed != SNOPower.Witchdoctor_Haunt) { return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 21f, CurrentTarget.ACDGuid)); } //skillDict.Add("LocustSwarm", SNOPower.Witchdoctor_Locust_Swarm); // Locust Swarm if (CanCast(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource >= 300 && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Locust_Swarm) && LastPowerUsed != SNOPower.Witchdoctor_Locust_Swarm) { return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 12f, CurrentTarget.ACDGuid)); } // Sacrifice for 0 Dogs if (CanCast(SNOPower.Witchdoctor_Sacrifice) && (Settings.Combat.WitchDoctor.ZeroDogs || !WitchDoctorHasPrimaryAttack)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 9f)); } var zombieChargerRange = hasGraveInjustice ? Math.Min(Player.GoldPickupRadius + 8f, 11f) : 11f; // Zombie Charger aka Zombie bears Spams Bears @ Everything from 11feet away if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position)); } // Soul Harvest Any Elites or to increase buff stacks if (!Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && CanCast(SNOPower.Witchdoctor_SoulHarvest) && (TargetUtil.AnyMobsInRange(16f, GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) + 1, false) || (hasSwallowYourSoul && Player.PrimaryResourcePct <= 0.50) || TargetUtil.IsEliteTargetInRange(16f))) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Soul Harvest with VengefulSpirit if (!Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && CanCast(SNOPower.Witchdoctor_SoulHarvest) && hasVengefulSpirit && TargetUtil.AnyMobsInRange(16, 3)) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } bool hasDireBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 0); bool hasVampireBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 3); bool hasPlagueBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 2); bool hasHungryBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 1); bool hasCloudOfBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 4); int fireBatsChannelCost = hasVampireBats ? 0 : 75; int fireBatsMana = TimeSincePowerUse(SNOPower.Witchdoctor_Firebats) < 125 ? fireBatsChannelCost : 225; bool firebatsMaintain = Trinity.ObjectCache.Any(u => u.IsUnit && u.IsPlayerFacing(70f) && u.Weight > 0 && u.Distance <= V.F("WitchDoctor.Firebats.MaintainRange") && SpellHistory.TimeSinceUse(SNOPower.Witchdoctor_Firebats) <= TimeSpan.FromMilliseconds(250d)); // Fire Bats:Cloud of bats if (hasCloudOfBats && (TargetUtil.AnyMobsInRange(8f) || firebatsMaintain) && CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana) { var range = Settings.Combat.WitchDoctor.FirebatsRange > 12f ? 12f : Settings.Combat.WitchDoctor.FirebatsRange; return(new TrinityPower(SNOPower.Witchdoctor_Firebats, range, CurrentTarget.ACDGuid)); } // Fire Bats fast-attack if (CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana && (TargetUtil.AnyMobsInRange(Settings.Combat.WitchDoctor.FirebatsRange) || firebatsMaintain) && !hasCloudOfBats) { return(new TrinityPower(SNOPower.Witchdoctor_Firebats, Settings.Combat.WitchDoctor.FirebatsRange, CurrentTarget.Position)); } // Acid Cloud if (CanCast(SNOPower.Witchdoctor_AcidCloud) && Player.PrimaryResource >= 175) { Vector3 bestClusterPoint; if (hasGraveInjustice) { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(Player.GoldPickupRadius + 8f, 30f)); } else { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f); } return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, rangedAttackMaxRange, bestClusterPoint)); } bool hasWellOfSouls = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritBarrage && s.RuneIndex == 1); bool hasRushOfEssence = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_RushOfEssence); // Spirit Barrage + Rush of Essence if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && hasRushOfEssence && !hasManitou) { if (hasWellOfSouls) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid)); } return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid)); } // Zombie Charger backup if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position)); } // Regular spirit barage if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && !hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, basicAttackRange, CurrentTarget.ACDGuid)); } // Poison Darts fast-attack Spams Darts when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_PoisonDart)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid)); } // Corpse Spiders fast-attacks Spams Spiders when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_CorpseSpider)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid)); } // Toads fast-attacks Spams Toads when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_PlagueOfToads)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid)); } // Fire Bomb fast-attacks Spams Bomb when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_Firebomb)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid)); } //Hexing Pants Mod if (Legendary.HexingPantsOfMrYan.IsEquipped && CurrentTarget.IsUnit && //!CanCast(SNOPower.Witchdoctor_Piranhas) && CurrentTarget.RadiusDistance > 10f) { return(new TrinityPower(SNOPower.Walk, 10f, CurrentTarget.Position)); } if (Legendary.HexingPantsOfMrYan.IsEquipped && CurrentTarget.IsUnit && //!CanCast(SNOPower.Witchdoctor_Piranhas) && CurrentTarget.RadiusDistance < 10f) { Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, -10f); return(new TrinityPower(SNOPower.Walk, 10f, vNewTarget)); } } // Buffs if (UseOOCBuff) { // Spirit Walk OOC if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && Settings.Combat.Misc.AllowOOCMovement) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } //Spam fear at all times if Tiklandian Visage is ewquipped and fear spam is selected to keep fear buff active if (CanCast(SNOPower.Witchdoctor_Horrify) && Settings.Combat.WitchDoctor.SpamHorrify && Legendary.TiklandianVisage.IsEquipped) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } bool hasStalker = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4); // Horrify Buff When not in combat for movement speed -- Stalker if (CanCast(SNOPower.Witchdoctor_Horrify) && hasStalker) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Zombie Dogs non-sacrifice build if (CanCast(SNOPower.Witchdoctor_SummonZombieDog) && ((Legendary.TheTallMansFinger.IsEquipped && Trinity.PlayerOwnedZombieDogCount < 1) || (!Legendary.TheTallMansFinger.IsEquipped && Trinity.PlayerOwnedZombieDogCount <= 2))) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog)); } bool hasRestlessGiant = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0); bool hasWrathfulProtector = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3); if (CanCast(SNOPower.Witchdoctor_Gargantuan) && !hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } } // Default Attacks if (IsNull(power)) { power = DefaultPower; } return(power); }
public TrinityPower GetOffensivePower() { Vector3 position; TrinityPower power; 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()) { Core.Logger.Log(LogCategory.Routine, $"Casting Wall of Death on {allUnits.FirstOrDefault()}"); return(WallOfDeath(allUnits.FirstOrDefault())); } if (bestClusterUnit != null) { if (Player.HasBuff(SNOPower.Witchdoctor_Hex)) { Core.Logger.Log(LogCategory.Routine, $"Casting Explode Chicken"); Vector3 explodePos = PlayerMover.IsBlocked ? Player.Position : bestClusterUnit.Position; return(ExplodeChicken(explodePos)); } if (!HasJeramsRevengeBuff && ZetaDia.Me.IsInCombat && Skills.WitchDoctor.WallOfDeath.CanCast()) { Core.Logger.Log(LogCategory.Routine, $"Casting Wall of Death on {allUnits.FirstOrDefault()}"); return(WallOfDeath(allUnits.FirstOrDefault())); } if (!Player.HasBuff(SNOPower.Witchdoctor_Hex) && Skills.WitchDoctor.Hex.CanCast()) { Core.Logger.Log(LogCategory.Routine, $"Casting Hex"); return(Hex(CurrentTarget.Position)); } 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)); var isElitewithoutLocust = clusterUnits.Any(u => u.IsElite && !u.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm)); var harvestStacks = Skills.WitchDoctor.SoulHarvest.BuffStacks; 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(); // continue channelling firebats? if (Player.IsChannelling) { if (!interruptForHaunt && !interruptForLocust && !interruptForHarvest) { Core.Logger.Log(LogCategory.Routine, "Continuation of Firebats."); return(new TrinityPower(SNOPower.Witchdoctor_Firebats, 30f, Player.Position, 75, 250)); } if (interruptForHaunt) { Core.Logger.Log(LogCategory.Routine, "Interrupted Firebats to haunt"); } if (interruptForLocust) { Core.Logger.Log(LogCategory.Routine, "Interrupted Firebats to locust"); } if (interruptForHarvest) { Core.Logger.Log(LogCategory.Routine, "Interrupted Firebats to harvest"); } } // Emergency health situation if (Player.CurrentHealthPct < 0.35) { if (Skills.WitchDoctor.SpiritWalk.CanCast()) { Core.Logger.Log(LogCategory.Routine, $"Defensive Spirit Walking"); return(SpiritWalk()); } if (TargetUtil.AnyMobsInRange(12f) && Skills.WitchDoctor.SoulHarvest.CanCast()) { Core.Logger.Log(LogCategory.Routine, "Emergency Harvest"); return(SoulHarvest()); } if (!HasJeramsRevengeBuff && Skills.WitchDoctor.WallOfDeath.CanCast() && allUnits.Any()) { Core.Logger.Log(LogCategory.Routine, $"Casting Defensive WallOfDeath on {allUnits.FirstOrDefault()}"); return(WallOfDeath(allUnits.FirstOrDefault())); } } // 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()) { Core.Logger.Log(LogCategory.Routine, "Locust"); return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 10f, targetsWithoutLocust.First().Position, 0, 0)); } } // Soul harvest for the damage reduction of Okumbas Ornament if (Skills.WitchDoctor.SoulHarvest.CanCast() && (bestClusterUnit.Distance < 12f || harvestStacks < 4 && TargetUtil.AnyMobsInRange(10f)) && harvestStacks < 10) { Core.Logger.Log(LogCategory.Routine, $"Harvest State: StackGainPossible={harvestPossibleStackGain} Units={harvestUnitsInRange} BuffRemainingSecs:{harvestBuffCooldown?.Remaining.TotalSeconds:N2}"); if (harvestPossibleStackGain <= harvestUnitsInRange) { Core.Logger.Log(LogCategory.Routine, $"Soul Harvest."); return(SoulHarvest()); } } 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(); Core.Logger.Log(LogCategory.Routine, $"Haunt on {target}"); 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.Distance(Player.Position); // Walk into cluster or buffed location. if (distance > 10f && !PlayerMover.IsBlocked) { if (distance > 20f && Skills.WitchDoctor.SpiritWalk.CanCast()) { Core.Logger.Log(LogCategory.Routine, $"Spirit Walking"); 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) { Core.Logger.Log(LogCategory.Routine, $"Casting Firebats"); return(Firebats(closestUnit)); } } } //if (IsChannellingFirebats && Player.CurrentHealthPct > 0.5f && TargetUtil.AnyMobsInRange(FireBatsRange)) // return Firebats(); //if (TrySpecialPower(out power)) // return power; //if (TrySecondaryPower(out power)) // return power; //if (TryPrimaryPower(out power)) // return power; return(Walk(TargetUtil.GetLoiterPosition(CurrentTarget, 15f))); }
public float spellDmg = 1f; //1 = default spell damage // Start is called before the first frame update void Awake() { main = this; }
public static TrinityPower GetPower() { TrinityPower power = null; // Spirit Walk, always! if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Combat Avoidance Spells if (!UseOOCBuff && IsCurrentlyAvoiding) { } // Incapacitated or Rooted if (!UseOOCBuff && (Player.IsIncapacitated || Player.IsRooted)) { // Spirit Walk if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } } // Combat Spells with a Target if (!UseOOCBuff && !IsCurrentlyAvoiding && CurrentTarget != null) { bool hasGraveInjustice = HotbarSkills.PassiveSkills.Contains(SNOPower.Witchdoctor_Passive_GraveInjustice); bool hasAngryChicken = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Hex && s.RuneIndex == 1); bool isChicken = hasAngryChicken && Player.IsHidden; bool hasVisionQuest = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_VisionQuest); // Set max ranged attack range, based on Grave Injustice, and current target NOT standing in avoidance, and health > 25% float rangedAttackMaxRange = 30f; if (hasGraveInjustice && !CurrentTarget.IsStandingInAvoidance && Player.CurrentHealthPct > 0.25) { rangedAttackMaxRange = Math.Min(Player.GoldPickupRadius + 8f, 30f); } // Set basic attack range, depending on whether or not we have Bears float basicAttackRange = 35f; if (hasGraveInjustice) { basicAttackRange = rangedAttackMaxRange; } else if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { basicAttackRange = 30f; } // Hex with angry chicken, is chicken, explode! if (isChicken && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f || UseDestructiblePower) && CanCast(SNOPower.Witchdoctor_Hex_Explode)) { Trinity.ShouldRefreshHotbarAbilities = true; return(new TrinityPower(SNOPower.Witchdoctor_Hex_Explode)); } else if (hasAngryChicken) { Trinity.ShouldRefreshHotbarAbilities = true; } //skillDict.Add("SpiritWalk", SNOPower.Witchdoctor_SpiritWalk); //runeDict.Add("Jaunt", 1); //runeDict.Add("HonoredGuest", 3); //runeDict.Add("UmbralShock", 2); //runeDict.Add("Severance", 0); //runeDict.Add("HealingJourney", 4); bool hasJaunt = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 1); bool hasHonoredGuest = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 3); bool hasUmbralShock = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 2); bool hasSeverance = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 0); bool hasHealingJourney = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 4); // Spirit Walk for Goblins chasing if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && CurrentTarget.IsTreasureGoblin && CurrentTarget.HitPointsPct < 0.90 && CurrentTarget.RadiusDistance <= 40f) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Spirit Walk < 65% Health: Healing Journey if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHealingJourney && Player.CurrentHealthPct <= V.F("WitchDoctor.SpiritWalk.HealingJourneyHealth")) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Spirit Walk < 50% Mana: Honored Guest if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHonoredGuest && Player.PrimaryResourcePct <= V.F("WitchDoctor.SpiritWalk.HonoredGuestMana")) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } //bool shouldRefreshVisionQuest = WitchDoctorCombat.GetTimeSinceLastVisionQuestRefresh() > 4000; bool shouldRefreshVisionQuest = !GetHasBuff(SNOPower.Witchdoctor_Passive_VisionQuest) || GetTimeSinceLastVisionQuestRefresh() > 3800; // Vision Quest Passive if (hasVisionQuest && shouldRefreshVisionQuest) { // Poison Darts if (CanCast(SNOPower.Witchdoctor_PoisonDart)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid)); } // Corpse Spiders if (CanCast(SNOPower.Witchdoctor_CorpseSpider)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid)); } // Plague Of Toads if (CanCast(SNOPower.Witchdoctor_PlagueOfToads)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid)); } // Fire Bomb if (CanCast(SNOPower.Witchdoctor_Firebomb)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid));; } } // Witch Doctor - Terror //skillDict.Add("SoulHarvest", SNOPower.Witchdoctor_SoulHarvest); //runeDict.Add("SwallowYourSoul", 3); //runeDict.Add("Siphon", 0); //runeDict.Add("Languish", 2); //runeDict.Add("SoulToWaste", 1); //runeDict.Add("VengefulSpirit", 4); bool hasVengefulSpirit = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 4); bool hasSwallowYourSoul = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 3); // Soul Harvest Any Elites or to increase buff stacks if (CanCast(SNOPower.Witchdoctor_SoulHarvest) && (TargetUtil.AnyMobsInRange(16f, GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) + 1, false) || (hasSwallowYourSoul && Player.PrimaryResourcePct <= 0.50) || TargetUtil.IsEliteTargetInRange(16f))) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Soul Harvest with VengefulSpirit if (CanCast(SNOPower.Witchdoctor_SoulHarvest) && hasVengefulSpirit && TargetUtil.AnyMobsInRange(16, 3)) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Sacrifice if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 && (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 9f))) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice)); } // Sacrifice for Circle of Life bool hasCircleofLife = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_CircleOfLife); if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 && hasCircleofLife && TargetUtil.AnyMobsInRange(15f)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice)); } bool hasRestlessGiant = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0); bool hasWrathfulProtector = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3); if (CanCast(SNOPower.Witchdoctor_Gargantuan)) { // Gargantuan, Recast on Elites or Bosses to trigger Restless Giant if (hasRestlessGiant && (TargetUtil.IsEliteTargetInRange(30f) || Trinity.PlayerOwnedGargantuanCount == 0)) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } // Gargantuan Wrathful Protector, 15 seconds of smash, use sparingly! if (hasWrathfulProtector && TargetUtil.IsEliteTargetInRange(30f)) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } // Gargantuan regular if (!hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } } bool hasSacrifice = Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice); // Zombie Dogs non-sacrifice build if (!hasSacrifice && CanCast(SNOPower.Witchdoctor_SummonZombieDog) && Trinity.PlayerOwnedZombieDogCount <= 2) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog)); } // Zombie Dogs for Sacrifice if (hasSacrifice && CanCast(SNOPower.Witchdoctor_SummonZombieDog) && (LastPowerUsed == SNOPower.Witchdoctor_Sacrifice || Trinity.PlayerOwnedZombieDogCount <= 2) && CombatBase.LastPowerUsed != SNOPower.Witchdoctor_SummonZombieDog) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog)); } // Hex with angry chicken, check if we want to shape shift and explode if (CanCast(SNOPower.Witchdoctor_Hex) && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f) && hasAngryChicken) { Trinity.ShouldRefreshHotbarAbilities = true; return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } // Hex Spam Cast without angry chicken if (CanCast(SNOPower.Witchdoctor_Hex) && !hasAngryChicken && (TargetUtil.AnyElitesInRange(12) || TargetUtil.AnyMobsInRange(12, 2) || TargetUtil.IsEliteTargetInRange(18f))) { return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } // Mass Confuse, elites only or big mobs or to escape on low health if (CanCast(SNOPower.Witchdoctor_MassConfusion) && (TargetUtil.AnyElitesInRange(12, 1) || TargetUtil.AnyMobsInRange(12, 6) || Player.CurrentHealthPct <= 0.25 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) && !CurrentTarget.IsTreasureGoblin) { return(new TrinityPower(SNOPower.Witchdoctor_MassConfusion, 0f, CurrentTarget.ACDGuid)); } // Big Bad Voodoo, elites and bosses only if (CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && (TargetUtil.EliteOrTrashInRange(25f) || (CurrentTarget.IsBoss && CurrentTarget.Distance <= 30f))) { return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo)); } // Grasp of the Dead if (CanCast(SNOPower.Witchdoctor_GraspOfTheDead) && (TargetUtil.AnyMobsInRange(30, 2) || TargetUtil.EliteOrTrashInRange(30f)) && Player.PrimaryResource >= 150) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(15); return(new TrinityPower(SNOPower.Witchdoctor_GraspOfTheDead, 25f, bestClusterPoint)); } // Piranhas if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 && (TargetUtil.ClusterExists(15f, 45f, 2, true) || TargetUtil.AnyElitesInRange(45f)) && Player.PrimaryResource >= 250) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(15f); return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, bestClusterPoint)); } //skillDict.Add("Horrify", SNOPower.Witchdoctor_Horrify); //runeDict.Add("Phobia", 2); //runeDict.Add("Stalker", 4); //runeDict.Add("FaceOfDeath", 1); //runeDict.Add("FrighteningAspect", 0); //runeDict.Add("RuthlessTerror", 3); bool hasPhobia = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 2); bool hasStalker = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4); bool hasFaceOfDeath = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 1); bool hasFrighteningAspect = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 0); bool hasRuthlessTerror = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 3); float horrifyRadius = hasFaceOfDeath ? 24f : 12f; // Horrify when low on health if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= Trinity.PlayerEmergencyHealthPotionLimit && TargetUtil.AnyMobsInRange(horrifyRadius, 3)) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Horrify Buff at 35% health -- Freightening Aspect if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= 0.35 && hasFrighteningAspect) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Fetish Army, elites only if (CanCast(SNOPower.Witchdoctor_FetishArmy) && (TargetUtil.EliteOrTrashInRange(30f) || TargetUtil.IsEliteTargetInRange(30f) || Settings.Combat.WitchDoctor.UseFetishArmyOffCooldown)) { return(new TrinityPower(SNOPower.Witchdoctor_FetishArmy)); } //skillDict.Add("SpiritBarage", SNOPower.Witchdoctor_SpiritBarrage); //runeDict.Add("TheSpiritIsWilling", 3); //runeDict.Add("WellOfSouls", 1); //runeDict.Add("Phantasm", 2); //runeDict.Add("Phlebotomize", 0); //runeDict.Add("Manitou", 4); bool hasManitou = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritBarrage && s.RuneIndex == 4); // Spirit Barrage Manitou if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && TimeSincePowerUse(SNOPower.Witchdoctor_SpiritBarrage) > 18000 && hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage)); } //skillDict.Add("Haunt", SNOPower.Witchdoctor_Haunt); //runeDict.Add("ConsumingSpirit", 0); //runeDict.Add("ResentfulSpirit", 4); //runeDict.Add("LingeringSpirit", 1); //runeDict.Add("GraspingSpirit", 2); //runeDict.Add("DrainingSpirit", 3); bool hasResentfulSpirit = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Haunt && s.RuneIndex == 4); // Haunt if (CanCast(SNOPower.Witchdoctor_Haunt) && Player.PrimaryResource >= 50 && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Haunt)) { return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 21f, CurrentTarget.ACDGuid)); } //skillDict.Add("LocustSwarm", SNOPower.Witchdoctor_Locust_Swarm); // Locust Swarm if (CanCast(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource >= 300 && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Locust_Swarm) && LastPowerUsed != SNOPower.Witchdoctor_Locust_Swarm) { return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 12f, CurrentTarget.ACDGuid)); } // Sacrifice for 0 Dogs if (CanCast(SNOPower.Witchdoctor_Sacrifice) && (Settings.Combat.WitchDoctor.ZeroDogs || !WitchDoctorHasPrimaryAttack)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 9f)); } // Wall of Zombies if (CanCast(SNOPower.Witchdoctor_WallOfZombies) && (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 4) || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f))) { return(new TrinityPower(SNOPower.Witchdoctor_WallOfZombies, 25f, CurrentTarget.Position)); } var zombieChargerRange = hasGraveInjustice ? Math.Min(Player.GoldPickupRadius + 8f, 11f) : 11f; // Zombie Charger aka Zombie bears Spams Bears @ Everything from 11feet away if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position)); } //skillDict.Add("Firebats", SNOPower.Witchdoctor_Firebats); //runeDict.Add("DireBats", 0); //runeDict.Add("VampireBats", 3); //runeDict.Add("PlagueBats", 2); //runeDict.Add("HungryBats", 1); //runeDict.Add("CloudOfBats", 4); bool hasDireBats = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 0); bool hasVampireBats = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 3); bool hasPlagueBats = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 2); bool hasHungryBats = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 1); bool hasCloudOfBats = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 4); int fireBatsChannelCost = hasVampireBats ? 0 : 75; int fireBatsMana = CombatBase.TimeSincePowerUse(SNOPower.Witchdoctor_Firebats) < 125 ? fireBatsChannelCost : 225; bool firebatsMaintain = Trinity.ObjectCache.Any(u => u.IsUnit && u.IsPlayerFacing(70f) && u.Weight > 0 && u.Distance <= V.F("WitchDoctor.Firebats.MaintainRange") && SpellHistory.TimeSinceUse(SNOPower.Witchdoctor_Firebats) <= TimeSpan.FromMilliseconds(250d)); // Fire Bats:Cloud of bats if (hasCloudOfBats && (TargetUtil.AnyMobsInRange(8f) || firebatsMaintain) && CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana) { var range = Settings.Combat.WitchDoctor.FirebatsRange > 12f ? 12f : Settings.Combat.WitchDoctor.FirebatsRange; return(new TrinityPower(SNOPower.Witchdoctor_Firebats, range, CurrentTarget.ACDGuid)); } // Fire Bats fast-attack if (CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana && (TargetUtil.AnyMobsInRange(Settings.Combat.WitchDoctor.FirebatsRange) || firebatsMaintain) && !hasCloudOfBats) { float range = firebatsMaintain ? Settings.Combat.WitchDoctor.FirebatsRange : V.F("WitchDoctor.Firebats.MaintainRange"); return(new TrinityPower(SNOPower.Witchdoctor_Firebats, Settings.Combat.WitchDoctor.FirebatsRange, CurrentTarget.Position)); } // Acid Cloud if (CanCast(SNOPower.Witchdoctor_AcidCloud) && Player.PrimaryResource >= 175) { Vector3 bestClusterPoint; if (hasGraveInjustice) { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(Player.GoldPickupRadius + 8f, 30f)); } else { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f); } return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, rangedAttackMaxRange, bestClusterPoint)); } bool hasWellOfSouls = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritBarrage && s.RuneIndex == 1); bool hasRushOfEssence = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_RushOfEssence); // Spirit Barrage + Rush of Essence if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && hasRushOfEssence && !hasManitou) { if (hasWellOfSouls) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid)); } return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid)); } // Zombie Charger backup if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position)); } // Regular spirit barage if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && !hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, basicAttackRange, CurrentTarget.ACDGuid)); } // Poison Darts fast-attack Spams Darts when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_PoisonDart)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid)); } // Corpse Spiders fast-attacks Spams Spiders when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_CorpseSpider)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid)); } // Toads fast-attacks Spams Toads when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_PlagueOfToads)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid)); } // Fire Bomb fast-attacks Spams Bomb when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_Firebomb)) { WitchDoctorCombat.VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid)); } } // Buffs if (UseOOCBuff) { // Spirit Walk OOC if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && Settings.Combat.Misc.AllowOOCMovement) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } bool hasStalker = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4); // Horrify Buff When not in combat for movement speed -- Stalker if (CanCast(SNOPower.Witchdoctor_Horrify) && hasStalker) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Zombie Dogs non-sacrifice build if (CanCast(SNOPower.Witchdoctor_SummonZombieDog) && Trinity.PlayerOwnedZombieDogCount <= 2) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog)); } bool hasRestlessGiant = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0); bool hasWrathfulProtector = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3); if (CanCast(SNOPower.Witchdoctor_Gargantuan) && !hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } } // Default Attacks if (IsNull(power)) { power = CombatBase.DefaultPower; } return(power); }