private static void Monk_TempestRushStatus(string trUse) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Behavior, "{0}, xyz={4} spirit={1:0} cd={2} lastUse={3:0}", trUse, GilesTrinity.PlayerStatus.PrimaryResource, PowerManager.CanCast(SNOPower.Monk_TempestRush), TimeSinceUse(SNOPower.Monk_TempestRush), vSideToSideTarget); }
private static void Monk_TempestRushStatus(string trUse) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Behavior, "{0}, xyz={4} spirit={1:0} cd={2} lastUse={3:0}", trUse, Trinity.Player.PrimaryResource, PowerManager.CanCast(SNOPower.Monk_TempestRush), TimeSinceUse(SNOPower.Monk_TempestRush), CombatBase.ZigZagPosition); }
protected virtual bool ShouldExplodeChicken(out Vector3 position) { position = Vector3.Zero; if (!IsChicken) { return(false); } if (!PowerManager.CanCast(SNOPower.Witchdoctor_Hex_Explode)) { return(false); } if (!IsInCombat) { return(false); } var timeAsChicken = Skills.WitchDoctor.Hex.TimeSinceUse; var chickenDuration = (Sets.ManajumasWay.IsEquipped ? 15000 : 2000); var unit = TargetUtil.GetBestClusterUnit(); if (unit == null) { return(false); } position = unit.Position; return(position != Vector3.Zero); }
/// <summary> /// Performs basic checks to see if we have and can cast a power (hotbar, power manager). Checks use timer for Wiz, DH, Monk /// </summary> /// <param name="power"></param> /// <param name="flags"></param> /// <returns></returns> public static bool CanCast(SNOPower power, CanCastFlags flags = CanCastFlags.All) { bool hasPower = Hotbar.Contains(power); if (!hasPower) { return(false); } // Skip this or Barb, Crusader, WD if (Player.ActorClass == ActorClass.Wizard || Player.ActorClass == ActorClass.DemonHunter || Player.ActorClass == ActorClass.Monk) { bool timer = flags.HasFlag(CanCastFlags.NoTimer) || SNOPowerUseTimer(power); if (!timer) { return(false); } } bool powerManager = flags.HasFlag(CanCastFlags.NoPowerManager) || PowerManager.CanCast(power); if (!powerManager) { return(false); } return(true); }
/// <summary> /// This will find a safe place to stand in both Kiting and Avoidance situations /// </summary> /// <param name="isStuck"></param> /// <param name="stuckAttempts"></param> /// <param name="dangerPoint"></param> /// <param name="shouldKite"></param> /// <param name="avoidDeath"></param> /// <returns></returns> internal static Vector3 FindSafeZone(bool isStuck, int stuckAttempts, Vector3 dangerPoint, bool shouldKite = false, IEnumerable <TrinityCacheObject> monsterList = null, bool avoidDeath = false) { if (!isStuck) { if (shouldKite && DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 1500 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } else if (DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 800 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } hasEmergencyTeleportUp = ( // Leap is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Barbarian_Leap)) || // Whirlwind is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Barbarian_Whirlwind) && ((PlayerStatus.PrimaryResource >= 10 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= Trinity.MinEnergyReserve)) || // Tempest rush is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_TempestRush) && ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= Trinity.MinEnergyReserve)) || // Teleport is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Teleport) && PlayerStatus.PrimaryResource >= 15) || // Archon Teleport is available (!PlayerStatus.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport)) ); // Wizards can look for bee stings in range and try a wave of force to dispel them if (!shouldKite && PlayerStatus.ActorClass == ActorClass.Wizard && Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && PlayerStatus.PrimaryResource >= 25 && DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_WaveOfForce]).TotalMilliseconds >= CombatBase.GetSNOPowerUseDelay(SNOPower.Wizard_WaveOfForce) && !PlayerStatus.IsIncapacitated && CacheData.TimeBoundAvoidance.Count(u => u.ActorSNO == 5212 && u.Position.Distance(PlayerStatus.Position) <= 15f) >= 2 && ( //HotbarSkills.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass) || PowerManager.CanCast(SNOPower.Wizard_WaveOfForce))) { ZetaDia.Me.UsePower(SNOPower.Wizard_WaveOfForce, Vector3.Zero, PlayerStatus.WorldDynamicID, -1); } } float highestWeight = 0f; if (monsterList == null) { monsterList = new List <TrinityCacheObject>(); } //Vector3 vBestLocation = FindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath); Vector3 vBestLocation = MainFindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath); highestWeight = 1; // Loop through distance-range steps if (highestWeight <= 0) { return(vBestLocation); } lastFoundSafeSpot = DateTime.UtcNow; lastSafeZonePosition = vBestLocation; return(vBestLocation); }
public bool CanCast(Skill skill) { if (Core.Player.IsIncapacitated) { return(false); } if (!Core.Hotbar.ActivePowers.Contains(skill.SNOPower)) { return(false); } if (!HasEnoughCharges(skill)) { return(false); } if (!HasEnoughResource(skill)) { return(false); } PowerManager.CanCastFlags reason; if (!PowerManager.CanCast(skill.SNOPower, out reason)) { if (reason != PowerManager.CanCastFlags.PowerInvalidTarget || !AllowInvalidTargetPowers.Contains(skill.SNOPower)) { Core.Logger.Debug(LogCategory.Spells, $"PowerManager CanCast failed for {skill.SNOPower} with flags: {reason}"); return(false); } } return(true); }
/// <summary> /// This will find a safe place to stand in both Kiting and Avoidance situations /// </summary> /// <param name="isStuck"></param> /// <param name="stuckAttempts"></param> /// <param name="dangerPoint"></param> /// <param name="shouldKite"></param> /// <param name="avoidDeath"></param> /// <returns></returns> internal static Vector3 FindSafeZone(bool isStuck, int stuckAttempts, Vector3 dangerPoint, bool shouldKite = false, IEnumerable <GilesObject> monsterList = null) { if (!isStuck) { if (shouldKite && DateTime.Now.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 1500 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } else if (DateTime.Now.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 800 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } hasEmergencyTeleportUp = ( // Leap is available (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Leap) && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Barbarian_Leap]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Barbarian_Leap]) || // Whirlwind is available (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && ((PlayerStatus.PrimaryResource >= 10 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= GilesTrinity.MinEnergyReserve)) || // Tempest rush is available (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Monk_TempestRush) && ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= GilesTrinity.MinEnergyReserve)) || // Teleport is available (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Teleport) && PlayerStatus.PrimaryResource >= 15 && PowerManager.CanCast(SNOPower.Wizard_Teleport)) || // Archon Teleport is available (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) && PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport)) ); // Wizards can look for bee stings in range and try a wave of force to dispel them if (!shouldKite && PlayerStatus.ActorClass == ActorClass.Wizard && Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && PlayerStatus.PrimaryResource >= 25 && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Wizard_WaveOfForce]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Wizard_WaveOfForce] && !PlayerStatus.IsIncapacitated && GilesTrinity.hashAvoidanceObstacleCache.Count(u => u.ActorSNO == 5212 && u.Location.Distance(PlayerStatus.CurrentPosition) <= 15f) >= 2 && (ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass) || PowerManager.CanCast(SNOPower.Wizard_WaveOfForce))) { ZetaDia.Me.UsePower(SNOPower.Wizard_WaveOfForce, Vector3.Zero, PlayerStatus.WorldDynamicID, -1); } } float fHighestWeight = 0f; Vector3 vBestLocation = Vector3.Zero; if (monsterList == null) { monsterList = new List <GilesObject>(); } vBestLocation = newFindSafeZone(dangerPoint, shouldKite, isStuck); fHighestWeight = 1; // Loop through distance-range steps if (fHighestWeight > 0) { lastFoundSafeSpot = DateTime.Now; lastSafeZonePosition = vBestLocation; } return(vBestLocation); }
internal static bool Monk_TempestRushReady() { bool isReady = false; if (PlayerStatus.ActorClass != ActorClass.Monk) { return(false); } if (!Hotbar.Contains(SNOPower.Monk_TempestRush)) { return(false); } if (ProfileManager.CurrentProfileBehavior != null) { Type profileBehaviorType = ProfileManager.CurrentProfileBehavior.GetType(); if (profileBehaviorType == typeof(UseObjectTag) || profileBehaviorType == typeof(UsePortalTag) || profileBehaviorType == typeof(UseWaypointTag) || profileBehaviorType == typeof(UseTownPortalTag)) { return(false); } } if (!Hotbar.Contains(SNOPower.Monk_TempestRush)) { return(false); } if (!Monk_HasMantraAbilityAndBuff()) { return(false); } double currentSpirit = ZetaDia.Me.CurrentPrimaryResource; // Minimum 10 spirit to continue channeling tempest rush if (TimeSinceUse(SNOPower.Monk_TempestRush) < 150 && currentSpirit > 10f) { return(true); } // Minimum 25 Spirit to start Tempest Rush if (PowerManager.CanCast(SNOPower.Monk_TempestRush) && currentSpirit > Settings.Combat.Monk.TR_MinSpirit && TimeSinceUse(SNOPower.Monk_TempestRush) > 550) { return(true); } return(isReady); }
internal static RunStatus DeathWaitAction(object ret) { //if (!UIElements.ReviveAtCorpseButton.IsEnabled) //{ // UIElements.ReviveAtLastCheckpointButton.Click(); //} if (FunkyBaseExtension.Settings.Death.WaitForPotionCooldown) { //Check Potion Cast Flags.. PowerManager.CanCastFlags potionCastFlags; if (!PowerManager.CanCast(SNOPower.DrinkHealthPotion, out potionCastFlags)) { if (potionCastFlags.HasFlag(PowerManager.CanCastFlags.PowerUnusableOnCooldown)) { InactivityDetector.Reset(); return(RunStatus.Running); } } } if (FunkyBaseExtension.Settings.Death.WaitForAllSkillsCooldown) { if (FunkyGame.Hero.Class.AC == ActorClass.Wizard && Hotbar.HasBuff(SNOPower.Wizard_Archon)) { Hotbar.RefreshHotbar(); InactivityDetector.Reset(); return(RunStatus.Running); } foreach (var skill in Hotbar.HotbarSkills) { PowerManager.CanCastFlags skillCastFlags; if (!PowerManager.CanCast(skill.Power, out skillCastFlags)) { if (skillCastFlags.HasFlag(PowerManager.CanCastFlags.PowerUnusableOnCooldown)) { InactivityDetector.Reset(); return(RunStatus.Running); } } } } GoldInactivity.LastCoinageUpdate = DateTime.Now; return(RunStatus.Success); }
internal static bool DeathShouldWait(object ret) { //if (!UIElements.ReviveAtCorpseButton.IsEnabled) // return true; if (FunkyBaseExtension.Settings.Death.WaitForPotionCooldown) { //Check Potion Cast Flags.. PowerManager.CanCastFlags potionCastFlags; if (!PowerManager.CanCast(SNOPower.DrinkHealthPotion, out potionCastFlags)) { if (potionCastFlags.HasFlag(PowerManager.CanCastFlags.PowerUnusableOnCooldown)) { BotMain.StatusText = "[Funky] Death: Waiting For Cooldowns!"; return(true); } } } if (FunkyBaseExtension.Settings.Death.WaitForAllSkillsCooldown) { //Check Archon? if (FunkyGame.Hero.Class.AC == ActorClass.Wizard && Hotbar.HasBuff(SNOPower.Wizard_Archon)) { Skill cancelSkill = FunkyGame.Hero.Class.Abilities[SNOPower.Wizard_Archon_Cancel]; Skill.UsePower(ref cancelSkill); Hotbar.RefreshHotbar(); BotMain.StatusText = "[Funky] Death: Waiting For Cooldowns!"; return(true); } foreach (var skill in Hotbar.HotbarSkills) { PowerManager.CanCastFlags skillCastFlags; if (!PowerManager.CanCast(skill.Power, out skillCastFlags)) { if (skillCastFlags.HasFlag(PowerManager.CanCastFlags.PowerUnusableOnCooldown)) { BotMain.StatusText = "[Funky] Death: Waiting For Cooldowns!"; return(true); } } } } return(false); }
public override bool activate() { // Get the current Skill in the hotbar slot we want to use SNOPower currentlyEquippedSkillInHotbarSlot = Zeta.Game.ZetaDia.CPlayer.GetPowerForSlot(this.hotbarSlot); // Check if Skill in slot is off cooldown, out of resource, or slot is empty so we can switch it PowerManager.CanCastFlags castFlag = PowerManager.CanCastFlags.None; bool wasSet = false; bool offCooldown = PowerManager.CanCast(currentlyEquippedSkillInHotbarSlot, out castFlag); if (offCooldown || castFlag == PowerManager.CanCastFlags.PowerUnusableGeneric || currentlyEquippedSkillInHotbarSlot == SNOPower.None) { // Set the skill Zeta.Game.ZetaDia.Me.SetActiveSkill(this.getSnoPower(), this.getRuneId(), this.getHotbarSlot()); Zeta.Game.ZetaDia.Actors.Update(); // Check to see if it actually was set if (Zeta.Game.ZetaDia.CPlayer.GetPowerForSlot(this.getHotbarSlot()) == this.getSnoPower() && Zeta.Game.ZetaDia.CPlayer.GetRuneIndexForSlot(this.getHotbarSlot()) == this.getRuneId()) { wasSet = true; } } return(wasSet); }
public bool CanCast(Skill skill) { if (Core.Player.IsIncapacitated) { return(false); } var snoPower = skill.SNOPower; if (!Core.Hotbar.ActivePowers.Contains(snoPower)) { return(false); } if (!HasEnoughCharges(skill)) { return(false); } if (!HasEnoughResource(skill)) { return(false); } if (!PowerManager.CanCast(snoPower, out var reason)) { if (reason != PowerManager.CanCastFlags.PowerInvalidTarget || !AllowInvalidTargetPowers.Contains(snoPower)) { // TODO: This call is very expensive, 27636ms/31065ms spent in this func. // Core.Logger.Debug(LogCategory.Spells, $"PowerManager CanCast failed for {snoPower} with flags: {reason}"); return(false); } } return(true); }
private static void RefreshDoBackTrack() { // See if we should wait for [playersetting] milliseconds for possible loot drops before continuing run if (CurrentTarget == null && (DateTime.UtcNow.Subtract(lastHadUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill || DateTime.UtcNow.Subtract(lastHadEliteUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill || DateTime.UtcNow.Subtract(lastHadBossUnitInSights).TotalMilliseconds <= 3000 || DateTime.UtcNow.Subtract(Helpers.Composites.LastFoundHoradricCache).TotalMilliseconds <= 5000) || DateTime.UtcNow.Subtract(lastHadContainerInSights).TotalMilliseconds <= Settings.WorldObject.OpenContainerDelay) { CurrentTarget = new TrinityCacheObject() { Position = Player.Position, Type = GObjectType.Avoidance, Weight = 20000, Distance = 2f, Radius = 2f, InternalName = "WaitForLootDrops" }; Logger.Log(TrinityLogLevel.Debug, LogCategory.Behavior, "Waiting for loot to drop, delay: {0}ms", Settings.Combat.Misc.DelayAfterKill); } // End of backtracking check // Finally, a special check for waiting for wrath of the berserker cooldown before engaging Azmodan if (CurrentTarget == null && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && Settings.Combat.Barbarian.WaitWOTB && !SNOPowerUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(Player.Position, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(Player.Position, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { DisableOutofCombatSprint = true; BarbarianCombat.AllowSprintOOC = false; Logger.Log("[Trinity] Waiting for Wrath Of The Berserker cooldown before continuing to Azmodan."); CurrentTarget = new TrinityCacheObject() { Position = Player.Position, Type = GObjectType.Avoidance, Weight = 20000, Distance = 2f, Radius = 2f, InternalName = "WaitForWrath" }; } // And a special check for wizard archon if (CurrentTarget == null && Hotbar.Contains(SNOPower.Wizard_Archon) && !SNOPowerUseTimer(SNOPower.Wizard_Archon) && Settings.Combat.Wizard.WaitArchon && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(Player.Position, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(Player.Position, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Waiting for Wizard Archon cooldown before continuing to Azmodan."); CurrentTarget = new TrinityCacheObject() { Position = Player.Position, Type = GObjectType.Avoidance, Weight = 20000, Distance = 2f, Radius = 2f, InternalName = "WaitForArchon" }; } // And a very sexy special check for WD BigBadVoodoo if (CurrentTarget == null && Hotbar.Contains(SNOPower.Witchdoctor_BigBadVoodoo) && !PowerManager.CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(Player.Position, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(Player.Position, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Waiting for WD BigBadVoodoo cooldown before continuing to Azmodan."); CurrentTarget = new TrinityCacheObject() { Position = Player.Position, Type = GObjectType.Avoidance, Weight = 20000, Distance = 2f, Radius = 2f, InternalName = "WaitForVoodooo" }; } }
private static TrinityPower GetWizardPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower) { // TODO //- AI so Trinity knows that it is already in the densest monster area and not to teleport. //- AI to use teleport when ranged attacks are approaching (succubus slow moving torpedo-orb) //- AI to avoid AoE's (plague, molten, desecrator) //- For Trinity to cast one or two Twisters before moving into melee range or teleporting closer. // Pick the best destructible power available if (UseDestructiblePower) { if (!GetHasBuff(SNOPower.Wizard_Archon)) { return(GetWizardDestructablePower()); } else { if (CurrentTarget.RadiusDistance <= 10f) { return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneStrike, 20f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.Wizard_Archon_DisintegrationWave, 19f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } } // Wizards want to save up to a reserve of 65+ energy MinEnergyReserve = 65; bool hasCriticalMass = ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass); if (!GetHasBuff(SNOPower.Wizard_Archon)) { // Slow Time for in combat if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_SlowTime) && (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 1 || PlayerStatus.CurrentHealthPct <= 0.7 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 35f)) && PowerManager.CanCast(SNOPower.Wizard_SlowTime)) { return(new TrinityPower(SNOPower.Wizard_SlowTime, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Wave of force if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 25 && ( // Check this isn't a critical mass wizard, cos they won't want to use this except for low health unless they don't have nova/blast in which case go for it (hasCriticalMass && ((!Hotbar.Contains(SNOPower.Wizard_FrostNova) && !Hotbar.Contains(SNOPower.Wizard_ExplosiveBlast)) || (PlayerStatus.CurrentHealthPct <= 0.7 && (TargetUtil.AnyMobsInRange(15f) || TargetUtil.IsEliteTargetInRange(23f))))) // Else normal wizard in which case check standard stuff || (!hasCriticalMass && ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 3 || PlayerStatus.CurrentHealthPct <= 0.7 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 23f)) ) && Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && GilesUseTimer(SNOPower.Wizard_WaveOfForce, true) && PowerManager.CanCast(SNOPower.Wizard_WaveOfForce)) { return(new TrinityPower(SNOPower.Wizard_WaveOfForce, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } //SkillDict.Add("Blizzard", SNOPower.Wizard_Blizzard); //RuneDict.Add("GraspingChill", 2); //RuneDict.Add("FrozenSolid", 4); //RuneDict.Add("Snowbound", 3); //RuneDict.Add("StarkWinter", 1); //RuneDict.Add("UnrelentingStorm", 0); bool hasSnowBoundRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Blizzard && s.RuneIndex == 3); // Blizzard if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Blizzard) && (TargetUtil.ClusterExists(45f, 2) || TargetUtil.AnyElitesInRange(40f) || TargetUtil.IsEliteTargetInRange(45f)) && (PlayerStatus.PrimaryResource >= 40 || (hasSnowBoundRune && PlayerStatus.PrimaryResource >= 20)) && GilesUseTimer(SNOPower.Wizard_Blizzard)) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(18f, 45f); return(new TrinityPower(SNOPower.Wizard_Blizzard, 45f, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Meteor if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Meteor) && (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 2 || CurrentTarget.IsEliteRareUnique || CurrentTarget.IsBoss || CurrentTarget.IsTreasureGoblin) && PlayerStatus.PrimaryResource >= 50 && PowerManager.CanCast(SNOPower.Wizard_Meteor)) { return(new TrinityPower(SNOPower.Wizard_Meteor, 21f, new Vector3(CurrentTarget.Position.X, CurrentTarget.Position.Y, CurrentTarget.Position.Z), CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Teleport in combat for critical-mass wizards if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Teleport) && hasCriticalMass && LastPowerUsed != SNOPower.Wizard_Teleport && PlayerStatus.PrimaryResource >= 15 && CurrentTarget.CentreDistance <= 35f && PowerManager.CanCast(SNOPower.Wizard_Teleport)) { vSideToSideTarget = TargetUtil.GetBestClusterPoint(15f, 35f); return(new TrinityPower(SNOPower.Wizard_Teleport, 35f, vSideToSideTarget, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Diamond Skin SPAM if (Hotbar.Contains(SNOPower.Wizard_DiamondSkin) && LastPowerUsed != SNOPower.Wizard_DiamondSkin && (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 0 || PlayerStatus.CurrentHealthPct <= 0.90 || PlayerStatus.IsIncapacitated || PlayerStatus.IsRooted || (!UseOOCBuff && CurrentTarget.RadiusDistance <= 40f)) && ((hasCriticalMass && !UseOOCBuff) || !GetHasBuff(SNOPower.Wizard_DiamondSkin)) && PowerManager.CanCast(SNOPower.Wizard_DiamondSkin)) { return(new TrinityPower(SNOPower.Wizard_DiamondSkin, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM)); } // Familiar if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Familiar) && PlayerStatus.PrimaryResource >= 20 && GilesUseTimer(SNOPower.Wizard_Familiar)) { return(new TrinityPower(SNOPower.Wizard_Familiar, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // The three wizard armors, done in an else-if loop so it doesn't keep replacing one with the other if (!PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 25) { // Energy armor as priority cast if available and not buffed if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor)) { if ((!GetHasBuff(SNOPower.Wizard_EnergyArmor) && PowerManager.CanCast(SNOPower.Wizard_EnergyArmor)) || (Hotbar.Contains(SNOPower.Wizard_Archon) && (!GetHasBuff(SNOPower.Wizard_EnergyArmor) || GilesUseTimer(SNOPower.Wizard_EnergyArmor)))) { return(new TrinityPower(SNOPower.Wizard_EnergyArmor, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } } // Ice Armor else if (Hotbar.Contains(SNOPower.Wizard_IceArmor)) { if (!GetHasBuff(SNOPower.Wizard_IceArmor) && PowerManager.CanCast(SNOPower.Wizard_IceArmor)) { return(new TrinityPower(SNOPower.Wizard_IceArmor, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } } // Storm Armor else if (Hotbar.Contains(SNOPower.Wizard_StormArmor)) { if (!GetHasBuff(SNOPower.Wizard_StormArmor) || ((DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Wizard_StormArmor]).TotalMilliseconds >= 15000) && PowerManager.CanCast(SNOPower.Wizard_Archon))) { return(new TrinityPower(SNOPower.Wizard_StormArmor, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } } } // Magic Weapon if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_MagicWeapon) && PowerManager.CanCast(SNOPower.Wizard_MagicWeapon) && (!GetHasBuff(SNOPower.Wizard_MagicWeapon) || ((DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Wizard_MagicWeapon]).TotalMilliseconds >= 10000) && PowerManager.CanCast(SNOPower.Wizard_Archon)))) { return(new TrinityPower(SNOPower.Wizard_MagicWeapon, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Magic Weapon if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_MagicWeapon) && PlayerStatus.PrimaryResource >= 25 && (GilesUseTimer(SNOPower.Wizard_MagicWeapon) || !GetHasBuff(SNOPower.Wizard_MagicWeapon))) { return(new TrinityPower(SNOPower.Wizard_MagicWeapon, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Hydra if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && LastPowerUsed != SNOPower.Wizard_Hydra && (ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 4 || PlayerStatus.CurrentHealthPct <= 0.7 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsBoss || CurrentTarget.IsTreasureGoblin) && CurrentTarget.RadiusDistance <= 15f)) && Hotbar.Contains(SNOPower.Wizard_Hydra) && PlayerStatus.PrimaryResource >= 15 && GilesUseTimer(SNOPower.Wizard_Hydra)) { // For distant monsters, try to target a little bit in-front of them (as they run towards us), if it's not a treasure goblin float fExtraDistance = 0f; if (CurrentTarget.CentreDistance > 17f && !CurrentTarget.IsTreasureGoblin) { fExtraDistance = CurrentTarget.CentreDistance - 17f; if (fExtraDistance > 5f) { fExtraDistance = 5f; } if (CurrentTarget.CentreDistance - fExtraDistance < 15f) { fExtraDistance -= 2; } } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance - fExtraDistance); return(new TrinityPower(SNOPower.Wizard_Hydra, 30f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Mirror Image @ half health or 5+ monsters or rooted/incapacitated or last elite left @25% health if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_MirrorImage) && (PlayerStatus.CurrentHealthPct <= 0.50 || AnythingWithinRange[RANGE_30] >= 5 || PlayerStatus.IsIncapacitated || PlayerStatus.IsRooted || (ElitesWithinRange[RANGE_30] == 1 && CurrentTarget.IsEliteRareUnique && !CurrentTarget.IsBoss && CurrentTarget.HitPointsPct <= 0.35)) && PowerManager.CanCast(SNOPower.Wizard_MirrorImage)) { return(new TrinityPower(SNOPower.Wizard_MirrorImage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Archon if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_Archon) && Wizard_ShouldStartArchon() && PowerManager.CanCast(SNOPower.Wizard_Archon)) { // Familiar has been removed for now. Uncomment the three comments below relating to familiars to force re-buffing them bool bHasBuffAbilities = (Hotbar.Contains(SNOPower.Wizard_MagicWeapon) || //hashPowerHotbarAbilities.Contains(SNOPower.Wizard_Familiar) || Hotbar.Contains(SNOPower.Wizard_EnergyArmor) || Hotbar.Contains(SNOPower.Wizard_IceArmor) || Hotbar.Contains(SNOPower.Wizard_StormArmor)); int iExtraEnergyNeeded = 25; if (Hotbar.Contains(SNOPower.Wizard_MagicWeapon)) { iExtraEnergyNeeded += 25; } //if (hashPowerHotbarAbilities.Contains(SNOPower.Wizard_Familiar)) iExtraEnergyNeeded += 25; if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor) || Hotbar.Contains(SNOPower.Wizard_IceArmor) || Hotbar.Contains(SNOPower.Wizard_StormArmor)) { iExtraEnergyNeeded += 25; } if (!bHasBuffAbilities || PlayerStatus.PrimaryResource <= iExtraEnergyNeeded) { CanCastArchon = true; } if (!CanCastArchon) { dictAbilityLastUse[SNOPower.Wizard_MagicWeapon] = DateTime.Today; //dictAbilityLastUse[SNOPower.Wizard_Familiar] = DateTime.Today; dictAbilityLastUse[SNOPower.Wizard_EnergyArmor] = DateTime.Today; dictAbilityLastUse[SNOPower.Wizard_IceArmor] = DateTime.Today; dictAbilityLastUse[SNOPower.Wizard_StormArmor] = DateTime.Today; CanCastArchon = true; } else { CanCastArchon = false; return(new TrinityPower(SNOPower.Wizard_Archon, 0f, vNullLocation, CurrentWorldDynamicId, -1, 4, 5, WAIT_FOR_ANIM)); } } //SkillDict.Add("FrostNova", SNOPower.Wizard_FrostNova); //RuneDict.Add("Shatter", 1); //RuneDict.Add("ColdSnap", 3); //RuneDict.Add("FrozenMist", 2); //RuneDict.Add("DeepFreeze", 4); //RuneDict.Add("BoneChill", 0); bool hasDeepFreeze = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_FrostNova && s.RuneIndex == 4); // Frost Nova if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_FrostNova) && !PlayerStatus.IsIncapacitated && ((hasDeepFreeze && TargetUtil.AnyMobsInRange(25, 5)) || (!hasDeepFreeze && (TargetUtil.AnyMobsInRange(25, 1) || PlayerStatus.CurrentHealthPct <= 0.7)) && CurrentTarget.RadiusDistance <= 25f) && PowerManager.CanCast(SNOPower.Wizard_FrostNova)) { return(new TrinityPower(SNOPower.Wizard_FrostNova, 20f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Frost Nova for Critical Mass builds if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_FrostNova) && !PlayerStatus.IsIncapacitated && hasCriticalMass && TargetUtil.AnyMobsInRange(20, 1) && PowerManager.CanCast(SNOPower.Wizard_FrostNova)) { return(new TrinityPower(SNOPower.Wizard_FrostNova, 15f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Explosive Blast SPAM when enough AP, blow erry thing up, nah mean if (!UseOOCBuff && Hotbar.Contains(SNOPower.Wizard_ExplosiveBlast) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 20 && (TargetUtil.AnyMobsInRange(25) && CurrentTarget.RadiusDistance <= 25f) && PowerManager.CanCast(SNOPower.Wizard_ExplosiveBlast)) { float fThisRange = 11f; if (hasCriticalMass) { fThisRange = 9f; } return(new TrinityPower(SNOPower.Wizard_ExplosiveBlast, fThisRange, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Check to see if we have a signature spell on our hotbar, for energy twister check bool bHasSignatureSpell = (Hotbar.Contains(SNOPower.Wizard_MagicMissile) || Hotbar.Contains(SNOPower.Wizard_ShockPulse) || Hotbar.Contains(SNOPower.Wizard_SpectralBlade) || Hotbar.Contains(SNOPower.Wizard_Electrocute)); //SkillDict.Add("EnergyTwister", SNOPower.Wizard_EnergyTwister); //RuneDict.Add("MistralBreeze", 3); //RuneDict.Add("GaleForce", 0); //RuneDict.Add("RagingStorm", 1); //RuneDict.Add("WickedWind", 4); //RuneDict.Add("StromChaser", 2); bool hasWickedWindRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_EnergyTwister && s.RuneIndex == 4); // Energy Twister SPAMS whenever 35 or more ap to generate Arcane Power if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_EnergyTwister) && PlayerStatus.PrimaryResource >= 35 && // If using storm chaser, then force a signature spell every 1 stack of the buff, if we have a signature spell (!bHasSignatureSpell || GetBuffStacks(SNOPower.Wizard_EnergyTwister) < 1) && ((!hasWickedWindRune && CurrentTarget.RadiusDistance <= 25f) || (hasWickedWindRune && CurrentTarget.RadiusDistance <= 60f)) && (!Hotbar.Contains(SNOPower.Wizard_Electrocute) || !hashActorSNOFastMobs.Contains(CurrentTarget.ActorSNO)) && ((hasCriticalMass && !bHasSignatureSpell) || !hasCriticalMass)) { Vector3 targetDirection = MathEx.CalculatePointFrom(PlayerStatus.CurrentPosition, CurrentTarget.Position, 3f); Vector3 bestClusterPoint = TargetUtil.GetBestClusterPoint(10f, 15f); float twisterRange = 28f; if (hasCriticalMass) { twisterRange = 9f; } return(new TrinityPower(SNOPower.Wizard_EnergyTwister, twisterRange, bestClusterPoint, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Disintegrate if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Disintegrate) && ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { float fThisRange = 35f; if (hasCriticalMass) { fThisRange = 20f; } return(new TrinityPower(SNOPower.Wizard_Disintegrate, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Arcane Orb if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_ArcaneOrb) && ((PlayerStatus.PrimaryResource >= 35 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) && GilesUseTimer(SNOPower.Wizard_ArcaneOrb)) { float fThisRange = 40f; if (hasCriticalMass) { fThisRange = 20f; } return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Arcane Torrent if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_ArcaneTorrent) && ((PlayerStatus.PrimaryResource >= 16 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) && GilesUseTimer(SNOPower.Wizard_ArcaneTorrent)) { float fThisRange = 40f; /*if (hasCriticalMass) * fThisRange = 20f;*/ return(new TrinityPower(SNOPower.Wizard_ArcaneTorrent, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } // Ray of Frost if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_RayOfFrost) && PlayerStatus.PrimaryResource >= 12) { float fThisRange = 35f; if (hasCriticalMass) { fThisRange = 20f; } return(new TrinityPower(SNOPower.Wizard_RayOfFrost, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Magic Missile if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_MagicMissile)) { float fThisRange = 35f; if (hasCriticalMass) { fThisRange = 20f; } return(new TrinityPower(SNOPower.Wizard_MagicMissile, fThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } // Shock Pulse if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_ShockPulse)) { return(new TrinityPower(SNOPower.Wizard_ShockPulse, 15f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Spectral Blade if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_SpectralBlade)) { return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 14f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Electrocute if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_Electrocute)) { return(new TrinityPower(SNOPower.Wizard_Electrocute, 40f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } // Default attacks if (!UseOOCBuff && !IsCurrentlyAvoiding) { return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } else { bool cancelArchon = false; if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffArmor && !Wizard_HasWizardArmor()) { cancelArchon = true; } if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffMagicWeaponFamiliar && (!CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) || !CheckAbilityAndBuff(SNOPower.Wizard_Familiar))) { cancelArchon = true; } if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.Timer && DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Wizard_Archon]).TotalSeconds >= Settings.Combat.Wizard.ArchonCancelSeconds) { cancelArchon = true; } if (cancelArchon && Wizard_ShouldStartArchon()) { var archonBuff = ZetaDia.Me.GetBuff(SNOPower.Wizard_Archon); if (archonBuff != null && archonBuff.IsCancelable) { // this actually cancels Archon archonBuff.Cancel(); // this SNOPower is fake - it isn't actually used, we're just putting it here to force a BehaviorTree return/recheck return(new TrinityPower(SNOPower.Wizard_Archon_Cancel, 0f, vNullLocation, -1, -1, -1, -1, false)); } } // Archon form // Archon Slow Time for in combat if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_25] > 1 || PlayerStatus.CurrentHealthPct <= 0.7 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 35f)) && Hotbar.Contains(SNOPower.Wizard_Archon_SlowTime) && GilesUseTimer(SNOPower.Wizard_Archon_SlowTime, true) && PowerManager.CanCast(SNOPower.Wizard_Archon_SlowTime)) { return(new TrinityPower(SNOPower.Wizard_Archon_SlowTime, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Archon Teleport in combat if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) && // Try and teleport-retreat from 1 elite or 3+ greys or a boss at 15 foot range (ElitesWithinRange[RANGE_15] >= 1 || AnythingWithinRange[RANGE_15] >= 3 || (CurrentTarget.IsBoss && CurrentTarget.RadiusDistance <= 15f)) && GilesUseTimer(SNOPower.Wizard_Archon_Teleport) && PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport)) { Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, -20f); return(new TrinityPower(SNOPower.Wizard_Archon_Teleport, 35f, vNewTarget, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Arcane Blast if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] >= 1 || AnythingWithinRange[RANGE_15] >= 1 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f)) && GilesUseTimer(SNOPower.Wizard_Archon_ArcaneBlast) && PowerManager.CanCast(SNOPower.Wizard_Archon_ArcaneBlast)) { return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneBlast, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Arcane Strike (Arcane Strike) Rapid Spam at close-range only if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && CurrentTarget.RadiusDistance <= 5f && TargetUtil.AnyMobsInRange(7f, 2) && CurrentTarget.IsBossOrEliteRareUnique && !Settings.Combat.Wizard.NoArcaneStrike) { return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneStrike, 7f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Disintegrate if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated) { return(new TrinityPower(SNOPower.Wizard_Archon_DisintegrationWave, 49f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } }
public void MoveTowards(Vector3 vMoveToTarget) { if (Trinity.Settings.Advanced.DisableAllMovement) { return; } if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || ZetaDia.Me.IsDead || ZetaDia.IsLoadingWorld) { return; } if (UISafetyCheck()) { return; } TimeLastUsedPlayerMover = DateTime.UtcNow; LastMoveToTarget = vMoveToTarget; // Set the public variable vMoveToTarget = WarnAndLogLongPath(vMoveToTarget); // Store player current position // Store distance to current moveto target float destinationDistance = MyPosition.Distance2D(vMoveToTarget); // Do unstuckery things if (Trinity.Settings.Advanced.UnstuckerEnabled) { // See if we can reset the 10-limit unstuck counter, if >120 seconds since we last generated an unstuck location // this is used if we're NOT stuck... if (TotalAntiStuckAttempts > 1 && DateTime.UtcNow.Subtract(LastGeneratedStuckPosition).TotalSeconds >= 120) { TotalAntiStuckAttempts = 1; TimesReachedStuckPoint = 0; vSafeMovementLocation = Vector3.Zero; NavHelper.UsedStuckSpots = new List <GridPoint>(); Logger.Log(TrinityLogLevel.Info, LogCategory.Movement, "Resetting unstuck timers", true); } // See if we need to, and can, generate unstuck actions // check if we're stuck bool isStuck = UnstuckChecker(); if (isStuck) { // Record the time we last apparently couldn't move for a brief period of time LastRecordedAnyStuck = DateTime.UtcNow; // See if there's any stuck position to try and navigate to generated by random mover vSafeMovementLocation = UnstuckHandler(MyPosition, vMoveToTarget); if (vSafeMovementLocation == Vector3.Zero) { Logger.Log(TrinityLogLevel.Info, LogCategory.Movement, "Unable to find Unstuck point!", vSafeMovementLocation); return; } Logger.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "SafeMovement Location set to {0}", vSafeMovementLocation); } // See if we can clear the total unstuckattempts if we haven't been stuck in over 6 minutes. if (DateTime.UtcNow.Subtract(LastRecordedAnyStuck).TotalSeconds >= 360) { TimesReachedMaxUnstucks = 0; } // Did we have a safe point already generated (eg from last loop through), if so use it as our current location instead if (vSafeMovementLocation != Vector3.Zero) { // Set our current movement target to the safe point we generated last cycle vMoveToTarget = vSafeMovementLocation; destinationDistance = MyPosition.Distance2D(vMoveToTarget); } // Get distance to current destination // Remove the stuck position if it's been reached, this bit of code also creates multiple stuck-patterns in an ever increasing amount if (vSafeMovementLocation != Vector3.Zero && destinationDistance <= 3f) { vSafeMovementLocation = Vector3.Zero; TimesReachedStuckPoint++; // Do we want to immediately generate a 2nd waypoint to "chain" anti-stucks in an ever-increasing path-length? if (TimesReachedStuckPoint <= TotalAntiStuckAttempts) { vSafeMovementLocation = NavHelper.FindSafeZone(true, TotalAntiStuckAttempts, MyPosition); vMoveToTarget = vSafeMovementLocation; } else { if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Info, LogCategory.UserInformation, "Clearing old route and trying new path find to: " + LastMoveToTarget.ToString()); } // Reset the path and allow a whole "New" unstuck generation next cycle TimesReachedStuckPoint = 0; // And cancel unstucking for 9 seconds so DB can try to navigate CancelUnstuckerForSeconds = (9 * TotalAntiStuckAttempts); if (CancelUnstuckerForSeconds < 20) { CancelUnstuckerForSeconds = 20; } LastCancelledUnstucker = DateTime.UtcNow; Navigator.Clear(); Navigator.MoveTo(LastMoveToTarget, "original destination", false); return; } } } // don't use special movement within 10 seconds of being stuck bool cancelSpecialMovementAfterStuck = DateTime.UtcNow.Subtract(LastGeneratedStuckPosition).TotalMilliseconds > 10000; // See if we can use abilities like leap etc. for movement out of combat, but not in town if (Trinity.Settings.Combat.Misc.AllowOOCMovement && !Trinity.Player.IsInTown && !Trinity.DontMoveMeIAmDoingShit && cancelSpecialMovementAfterStuck) { bool bTooMuchZChange = (Math.Abs(MyPosition.Z - vMoveToTarget.Z) >= 4f); // Whirlwind for a barb, special context only if (Trinity.Settings.Combat.Barbarian.SprintMode != BarbarianSprintMode.CombatOnly && Trinity.Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && Trinity.ObjectCache.Any(u => u.IsUnit && MathUtil.IntersectsPath(u.Position, u.Radius + 5f, Trinity.Player.Position, vMoveToTarget)) && Trinity.Player.PrimaryResource >= V.F("Barbarian.Whirlwind.MinFury") && !Trinity.IsWaitingForSpecial && V.B("Barbarian.Whirlwind.UseForMovement")) { ZetaDia.Me.UsePower(SNOPower.Barbarian_Whirlwind, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Barbarian_Whirlwind); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Whirlwind for OOC movement, distance={0}", destinationDistance); } return; } // Leap movement for a barb if (Trinity.Settings.Combat.Barbarian.UseLeapOOC && Trinity.Hotbar.Contains(SNOPower.Barbarian_Leap) && PowerManager.CanCast(SNOPower.Barbarian_Leap) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Barbarian_Leap, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Barbarian_Leap); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Leap for OOC movement, distance={0}", destinationDistance); } return; } // Furious Charge movement for a barb if (Trinity.Settings.Combat.Barbarian.UseChargeOOC && Trinity.Hotbar.Contains(SNOPower.Barbarian_FuriousCharge) && !bTooMuchZChange && destinationDistance >= 20f && PowerManager.CanCast(SNOPower.Barbarian_FuriousCharge) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Barbarian_FuriousCharge, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Barbarian_FuriousCharge); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Furious Charge for OOC movement, distance={0}", destinationDistance); } return; } bool hasTacticalAdvantage = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.DemonHunter_Passive_TacticalAdvantage); int vaultDelay = hasTacticalAdvantage ? 2000 : Trinity.Settings.Combat.DemonHunter.VaultMovementDelay; // DemonHunter Vault if (Trinity.Hotbar.Contains(SNOPower.DemonHunter_Vault) && !bTooMuchZChange && Trinity.Settings.Combat.DemonHunter.VaultMode != DemonHunterVaultMode.CombatOnly && CombatBase.TimeSincePowerUse(SNOPower.DemonHunter_Vault) > vaultDelay && destinationDistance >= 18f && PowerManager.CanCast(SNOPower.DemonHunter_Vault) && !ShrinesInArea(vMoveToTarget) && // Don't Vault into avoidance/monsters if we're kiting (CombatBase.PlayerKiteDistance <= 0 || (CombatBase.PlayerKiteDistance > 0 && (!CacheData.TimeBoundAvoidance.Any(a => a.Position.Distance(vMoveToTarget) <= CombatBase.PlayerKiteDistance) || (!CacheData.TimeBoundAvoidance.Any(a => MathEx.IntersectsPath(a.Position, a.Radius, Trinity.Player.Position, vMoveToTarget))) || !CacheData.MonsterObstacles.Any(a => a.Position.Distance(vMoveToTarget) <= CombatBase.PlayerKiteDistance)))) ) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.DemonHunter_Vault, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.DemonHunter_Vault); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Vault for OOC movement, distance={0}", destinationDistance); } return; } // Tempest rush for a monk if (Trinity.Hotbar.Contains(SNOPower.Monk_TempestRush) && (Trinity.Settings.Combat.Monk.TROption == TempestRushOption.MovementOnly || Trinity.Settings.Combat.Monk.TROption == TempestRushOption.Always || (Trinity.Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && !TargetUtil.AnyElitesInRange(40f)))) { Vector3 vTargetAimPoint = vMoveToTarget; bool canRayCastTarget = true; vTargetAimPoint = TargetUtil.FindTempestRushTarget(); if (!CanChannelTempestRush && ((Trinity.Player.PrimaryResource >= Trinity.Settings.Combat.Monk.TR_MinSpirit && destinationDistance >= Trinity.Settings.Combat.Monk.TR_MinDist) || DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Monk_TempestRush]).TotalMilliseconds <= 150) && canRayCastTarget && PowerManager.CanCast(SNOPower.Monk_TempestRush)) { CanChannelTempestRush = true; } else if ((CanChannelTempestRush && (Trinity.Player.PrimaryResource < 10f)) || !canRayCastTarget) { CanChannelTempestRush = false; } double lastUse = DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Monk_TempestRush]).TotalMilliseconds; if (CanChannelTempestRush) { if (Trinity.SNOPowerUseTimer(SNOPower.Monk_TempestRush)) { LastTempestRushPosition = vTargetAimPoint; ZetaDia.Me.UsePower(SNOPower.Monk_TempestRush, vTargetAimPoint, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Monk_TempestRush); // simulate movement speed of 30 SpeedSensor lastSensor = SpeedSensors.OrderByDescending(s => s.Timestamp).FirstOrDefault(); SpeedSensors.Add(new SpeedSensor() { Location = MyPosition, TimeSinceLastMove = new TimeSpan(0, 0, 0, 0, 1000), Distance = 5f, WorldID = Trinity.CurrentWorldDynamicId }); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Tempest Rush for OOC movement, distance={0:0} spirit={1:0} cd={2} lastUse={3:0} V3={4} vAim={5}", destinationDistance, Trinity.Player.PrimaryResource, PowerManager.CanCast(SNOPower.Monk_TempestRush), lastUse, vMoveToTarget, vTargetAimPoint); } return; } else { return; } } else { if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Tempest rush failed!: {0:00.0} / {1} distance: {2:00.0} / {3} Raycast: {4} MS: {5:0.0} lastUse={6:0}", Trinity.Player.PrimaryResource, Trinity.Settings.Combat.Monk.TR_MinSpirit, destinationDistance, Trinity.Settings.Combat.Monk.TR_MinDist, canRayCastTarget, GetMovementSpeed(), lastUse); } Trinity.MaintainTempestRush = false; } // Always set this from PlayerMover Trinity.LastTempestRushLocation = vTargetAimPoint; } // Dashing Strike OOC if (CombatBase.CanCast(SNOPower.X1_Monk_DashingStrike) && Trinity.Settings.Combat.Monk.UseDashingStrikeOOC && destinationDistance > 15f) { ZetaDia.Me.UsePower(SNOPower.X1_Monk_DashingStrike, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.X1_Monk_DashingStrike); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Dashing Strike for OOC movement, distance={0}", destinationDistance); } } bool hasWormHole = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 4); bool hasCalamity = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 0); // Teleport for a wizard if (!hasCalamity && CombatBase.CanCast(SNOPower.Wizard_Teleport, CombatBase.CanCastFlags.NoTimer) && CombatBase.TimeSincePowerUse(SNOPower.Wizard_Teleport) > 250 && destinationDistance >= 10f && !ShrinesInArea(vMoveToTarget)) { var maxTeleportRange = 75f; Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > maxTeleportRange) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, maxTeleportRange); } ZetaDia.Me.UsePower(SNOPower.Wizard_Teleport, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Wizard_Teleport); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Teleport for OOC movement, distance={0}", destinationDistance); } return; } // Archon Teleport for a wizard if (Trinity.Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) && DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_Archon_Teleport]).TotalMilliseconds >= CombatBase.GetSNOPowerUseDelay(SNOPower.Wizard_Archon_Teleport) && destinationDistance >= 20f && PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (destinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, MyPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Wizard_Archon_Teleport, vThisTarget, Trinity.CurrentWorldDynamicId, -1); SpellHistory.RecordSpell(SNOPower.Wizard_Archon_Teleport); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Archon Teleport for OOC movement, distance={0}", destinationDistance); } return; } } if (MyPosition.Distance2D(vMoveToTarget) > 1f) { // Default movement ZetaDia.Me.UsePower(SNOPower.Walk, vMoveToTarget, Trinity.CurrentWorldDynamicId, -1); if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Moved to:{0} dir:{1} Speed:{2:0.00} Dist:{3:0} ZDiff:{4:0} CanStand:{5} Raycast:{6}", NavHelper.PrettyPrintVector3(vMoveToTarget), MathUtil.GetHeadingToPoint(vMoveToTarget), MovementSpeed, MyPosition.Distance2D(vMoveToTarget), Math.Abs(MyPosition.Z - vMoveToTarget.Z), Trinity.MainGridProvider.CanStandAt(Trinity.MainGridProvider.WorldToGrid(vMoveToTarget.ToVector2())), !Navigator.Raycast(MyPosition, vMoveToTarget) ); } } else { if (Trinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Reached MoveTowards Destination {0} Current Speed: {1:0.0}", vMoveToTarget, MovementSpeed); } } }
private static void RefreshDoBackTrack() { // See if we should wait for [playersetting] milliseconds for possible loot drops before continuing run if (DateTime.Now.Subtract(lastHadUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill || DateTime.Now.Subtract(lastHadEliteUnitInSights).TotalMilliseconds <= Settings.Combat.Misc.DelayAfterKill) { CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForLootDrops" }; DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Behavior, "Waiting for loot to drop, delay: {0}ms", Settings.Combat.Misc.DelayAfterKill); } // Now see if we need to do any backtracking if (CurrentTarget == null && iTotalBacktracks >= 2 && Settings.Combat.Misc.AllowBacktracking && !PlayerStatus.IsInTown) // Never bother with the 1st backtrack position nor if we are in town { // See if we're already within 18 feet of our start position first if (Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[1]) <= 18f) { vBacktrackList = new SortedList <int, Vector3>(); iTotalBacktracks = 0; } // See if we can raytrace to the final location and it's within 25 feet if (iTotalBacktracks >= 2 && Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[1]) <= 25f && NavHelper.CanRayCast(PlayerStatus.CurrentPosition, vBacktrackList[1])) { vBacktrackList = new SortedList <int, Vector3>(); iTotalBacktracks = 0; } if (iTotalBacktracks >= 2) { // See if we can skip to the next backtracker location first if (iTotalBacktracks >= 3) { if (Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks - 1]) <= 10f) { vBacktrackList.Remove(iTotalBacktracks); iTotalBacktracks--; } } CurrentTarget = new GilesObject() { Position = vBacktrackList[iTotalBacktracks], Type = GObjectType.Backtrack, Weight = 20000, CentreDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks]), RadiusDistance = Vector3.Distance(PlayerStatus.CurrentPosition, vBacktrackList[iTotalBacktracks]), InternalName = "GilesBacktrack" }; } } else { vBacktrackList = new SortedList <int, Vector3>(); iTotalBacktracks = 0; } // End of backtracking check //TODO : If this code is obselete remove it (Check that) // Finally, a special check for waiting for wrath of the berserker cooldown before engaging Azmodan if (CurrentTarget == null && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && Settings.Combat.Barbarian.WaitWOTB && !GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { bDontSpamOutofCombat = true; Logging.Write("[Trinity] Waiting for Wrath Of The Berserker cooldown before continuing to Azmodan."); CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForWrath" }; } // And a special check for wizard archon if (CurrentTarget == null && Hotbar.Contains(SNOPower.Wizard_Archon) && !GilesUseTimer(SNOPower.Wizard_Archon) && Settings.Combat.Wizard.WaitArchon && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Waiting for Wizard Archon cooldown before continuing to Azmodan."); CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForArchon" }; } // And a very sexy special check for WD BigBadVoodoo if (CurrentTarget == null && Hotbar.Contains(SNOPower.Witchdoctor_BigBadVoodoo) && !PowerManager.CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(PlayerStatus.CurrentPosition, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { DbHelper.Log(TrinityLogLevel.Normal, LogCategory.UserInformation, "Waiting for WD BigBadVoodoo cooldown before continuing to Azmodan."); CurrentTarget = new GilesObject() { Position = PlayerStatus.CurrentPosition, Type = GObjectType.Avoidance, Weight = 20000, CentreDistance = 2f, RadiusDistance = 2f, InternalName = "GilesWaitForVoodooo" }; } }
public static TrinityPower GetPower() { TrinityPower power; if (UseDestructiblePower) { return(DestroyObjectPower); } if (UseOOCBuff) { // Call of The Ancients if (CanUseCallOfTheAncients && Sets.ImmortalKingsCall.IsFullyEquipped) { return(PowerCallOfTheAncients); } // Sprint OOC if (CanUseSprintOOC) { return(PowerSprint); } } else { if (QueuedPower != null && !Player.IsIncapacitated && PowerManager.CanCast(QueuedPower.SNOPower) && !Player.IsCastingOrLoading) { Logger.LogVerbose(LogCategory.Behavior, "Casting Queued Power {0}", QueuedPower); var next = QueuedPower; QueuedPower.MaxFailedCastReTryAttempts = 5; QueuedPower.WaitBeforeUseDelay = 750; QueuedPower = null; return(next); } } // Ignore Pain when near Frozen if ((ZetaDia.Me.IsFrozen || ZetaDia.Me.IsRooted || Trinity.ObjectCache.Any(o => o.AvoidanceType == AvoidanceType.IceBall)) && CanCastIgnorePain) { Logger.Log("Used Ignore Pain to prevent Frozen"); return(PowerIgnorePain); } if (!UseOOCBuff) { // Refresh Frenzy if (CanCast(SNOPower.Barbarian_Frenzy) && TimeSincePowerUse(SNOPower.Barbarian_Frenzy) > 3000 && TimeSincePowerUse(SNOPower.Barbarian_Frenzy) < 4000) { return(PowerFrenzy); } // Refresh Bash - Punish if (CanCast(SNOPower.Barbarian_Bash) && TimeSincePowerUse(SNOPower.Barbarian_Bash) > 4000 && TimeSincePowerUse(SNOPower.Barbarian_Bash) < 5000) { return(PowerBash); } } // Ignore Pain when low on health if (CanCastIgnorePain) { return(PowerIgnorePain); } // WOTB if (CanUseWrathOfTheBerserker) { return(PowerWrathOfTheBerserker); } // Call of the Ancients if (CanUseCallOfTheAncients) { return(PowerCallOfTheAncients); } // Leap with Earth Set. if (CanUseLeap && Sets.MightOfTheEarth.IsThirdBonusActive) { return(PowerLeap); } // Earthquake if (CanUseEarthquake) { return(PowerEarthquake); } // Avalanche if (CanUseAvalanche) { return(PowerAvalanche); } // War Cry if (CanUseWarCry) { return(PowerWarCry); } // Battle Rage if (CanUseBattleRage) { return(PowerBattleRage); } // Rend if (CanUseRend) { return(PowerRend); } // Overpower if (CanUseOverPower) { return(PowerOverpower); } // Threatening Shout if (CanUseThreatingShout) { return(PowerThreateningShout); } // Ground Stomp if (CanUseGroundStomp) { return(PowerGroundStomp); } // Revenge if (CanUseRevenge) { return(PowerRevenge); } // Ancient Spear if (CanUseAncientSpear) { return(PowerAncientSpear); } // Sprint if (CanUseSprint) { return(PowerSprint); } // Furious Charge if (CanUseFuriousCharge) { return(PowerFuriousCharge); } // Leap if (CanUseLeap) { return(PowerLeap); } // Seismic Slam if (CanUseSeismicSlam) { return(PowerSeismicSlam); } // Bash to 3 stacks (Punish) if (CanUseBashTo3) { return(PowerBash); } // Frenzy to 5 stacks (Maniac) if (CanUseFrenzyTo5) { return(PowerFrenzy); } // HOTA Elites if (CanUseHammerOfTheAncientsElitesOnly) { return(PowerHammerOfTheAncients); } // Whirlwind if (CanUseWhirlwind) { return(PowerWhirlwind); } // Hammer of the Ancients if (CanUseHammerOfTheAncients) { return(PowerHammerOfTheAncients); } // Weapon Throw if (CanUseWeaponThrow) { return(PowerWeaponThrow); } // Frenzy Fury Generator if (CanUseFrenzy) { return(PowerFrenzy); } // Bash Fury Generator if (CanUseBash) { return(PowerBash); } // Cleave Fury Generator if (CanUseCleave) { return(PowerCleave); } // Default Attacks return(DefaultPower); return(power); }
internal void Update(bool combat = false, bool force = false) { double lastUpdate = DateTime.Now.Subtract(lastUpdatedPlayer).TotalMilliseconds; //Update only every 100ms, unless in combat than 25ms.. if (!force && (combat && lastUpdate < 50 || lastUpdate < 150)) { return; } using (ZetaDia.Memory.AcquireFrame()) { // If we aren't in the game of a world is loading, don't do anything yet if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || ZetaDia.IsLoadingWorld) { return; } var me = ZetaDia.Me; if (me == null) { return; } try { if (Bot.Character.Class.AC == ActorClass.DemonHunter) { dDiscipline = me.CurrentSecondaryResource; dDisciplinePct = Bot.Character.Data.dDiscipline / me.MaxSecondaryResource; } double curhealthpct = me.HitpointsCurrentPct; if (!dcurrentHealthPct.Equals(curhealthpct)) { healthvalueChanged(dcurrentHealthPct, curhealthpct); } dCurrentEnergy = me.CurrentPrimaryResource; dCurrentEnergyPct = dCurrentEnergy / me.MaxPrimaryResource; //Critical Avoidance (when no avoidance is set!) if (dCurrentHealthPct < 0.50d && !Bot.Settings.Avoidance.AttemptAvoidanceMovements && !PowerManager.CanCast(SNOPower.DrinkHealthPotion)) { CriticalAvoidance = true; } else if (CriticalAvoidance && !ItemIdentifyBehavior.shouldPreformOOCItemIDing && !TownPortalBehavior.FunkyTPBehaviorFlag && dCurrentHealthPct > 0.5) { //Disable it when not OOC/TP/Low health still.. CriticalAvoidance = false; } bIsInBossEncounter = me.IsInBossEncounter; bIsInTown = ZetaDia.IsInTown; bIsRooted = me.IsRooted; if (me.IsFeared || me.IsStunned || me.IsFrozen || me.IsBlind) { bIsIncapacitated = true; } else { var bIsInKnockBack = (me.CommonData.GetAttribute <int>(ActorAttributeType.InKnockback) != 0); bIsIncapacitated = bIsInKnockBack || Bot.Character.Class.KnockbackLandAnims.Contains(CurrentSNOAnim); } int currentLevelAreaID = ZetaDia.CurrentLevelAreaId; if (iCurrentLevelID != currentLevelAreaID) { levelareaIDchanged(currentLevelAreaID); iCurrentWorldID = ZetaDia.CurrentWorldDynamicId; } if (Bot.Character.Account.CurrentLevel == 60) { CurrentExp = me.ParagonCurrentExperience; } else { CurrentExp = me.CurrentExperience; } //Set Character Radius? if (fCharacterRadius == 0f) { fCharacterRadius = me.ActorInfo.Sphere.Radius; //Wizards are short -- causing issues (At least Male Wizard is!) //if (Bot.Game.ActorClass == ActorClass.Wizard) this.fCharacterRadius += 1f; } //Update vars that are not essential to combat (survival). if (DateTime.Now.Subtract(lastUpdateNonEssentialData).TotalSeconds > 30) { lastUpdateNonEssentialData = DateTime.Now; //update level if not 60 else update paragonlevel if (iMyLevel < 60) { iMyLevel = me.Level; } iMyDynamicID = me.CommonData.DynamicId; FreeBackpackSlots = me.Inventory.NumFreeBackpackSlots; PickupRadius = me.GoldPickupRadius; Coinage = me.Inventory.Coinage; //Clear our BPItems list.. BackPack.BPItems.Clear(); } if (UpdateCoinage) { Coinage = me.Inventory.Coinage; } //Check current scence every 1.5 seconds if (!bIsInTown && DateTime.Now.Subtract(lastCheckedSceneID).TotalSeconds > 1.50) { //Get the current guid, compare/update. int CurrentSceneID = me.CurrentScene.SceneGuid; if (CurrentSceneID != Bot.Character.Data.iSceneID) { Bot.Character.Data.iSceneID = CurrentSceneID; } lastCheckedSceneID = DateTime.Now; } } catch (AccessViolationException) { } } lastUpdatedPlayer = DateTime.Now; }
private void CreatePrecastCriteria() { AbilityPreCastFlags precastconditions_ = Flags; if (precastconditions_.Equals(AbilityPreCastFlags.None)) { Criteria += ((s) => true); return; } if (precastconditions_.HasFlag(AbilityPreCastFlags.CheckPlayerIncapacitated)) { Criteria += ((s) => !Bot.Character.Data.bIsIncapacitated); } if (precastconditions_.HasFlag(AbilityPreCastFlags.CheckPlayerRooted)) { Criteria += ((s) => !Bot.Character.Data.bIsRooted); } if (precastconditions_.HasFlag(AbilityPreCastFlags.CheckExisitingBuff)) { Criteria += ((s) => !Bot.Character.Class.HotBar.HasBuff(s.Power)); } if (precastconditions_.HasFlag(AbilityPreCastFlags.CheckPetCount)) { Criteria += ((s) => Bot.Character.Class.MainPetCount < s.Counter); } if (precastconditions_.HasFlag(AbilityPreCastFlags.CheckRecastTimer)) { Criteria += ((s) => s.LastUsedMilliseconds > s.Cooldown); } if (precastconditions_.HasFlag(AbilityPreCastFlags.CheckCanCast)) { Criteria += ((s) => { bool cancast = PowerManager.CanCast(s.Power, out s.CanCastFlags); if (!cancast && s.CanCastFlags.HasFlag(PowerManager.CanCastFlags.PowerNotEnoughResource)) { if (s.IsSpecialAbility) { Bot.Character.Class.bWaitingForSpecial = true; } if (s.IsRanged || s.Range > 0) { Bot.Character.Class.CanUseDefaultAttack = true; } } //else if (IsSpecialAbility) // Bot.Character_.Class.bWaitingForSpecial=false; return(cancast); }); } if (precastconditions_.HasFlag(AbilityPreCastFlags.CheckEnergy)) { Criteria += ((s) => { bool energyCheck = !s.SecondaryEnergy ? Bot.Character.Data.dCurrentEnergy >= s.Cost : Bot.Character.Data.dDiscipline >= s.Cost; if (s.IsSpecialAbility && !energyCheck) //we trigger waiting for special here. { Bot.Character.Class.bWaitingForSpecial = true; } if (!energyCheck && (s.IsRanged || s.Range > 0)) { Bot.Character.Class.CanUseDefaultAttack = true; } return(energyCheck); }); } }
public void MoveTowards(Vector3 vMoveToTarget) { if (!ZetaDia.IsInGame || !ZetaDia.Me.IsValid || ZetaDia.Me.IsDead || ZetaDia.IsLoadingWorld) { return; } if (UISafetyCheck()) { return; } TimeLastUsedPlayerMover = DateTime.Now; vMyCurrentPosition = GilesTrinity.PlayerStatus.CurrentPosition; LastMoveToTarget = vMoveToTarget; // record speed once per second if (DateTime.Now.Subtract(lastRecordedPosition).TotalMilliseconds >= 1000) { // Record our current location and time if (!SpeedSensors.Any()) { SpeedSensors.Add(new SpeedSensor() { Location = vMyCurrentPosition, TimeSinceLastMove = new TimeSpan(0), Distance = 0f, WorldID = GilesTrinity.CurrentWorldDynamicId }); } else { SpeedSensor lastSensor = SpeedSensors.OrderByDescending(s => s.Timestamp).FirstOrDefault(); SpeedSensors.Add(new SpeedSensor() { Location = vMyCurrentPosition, TimeSinceLastMove = new TimeSpan(DateTime.Now.Subtract(lastSensor.TimeSinceLastMove).Ticks), Distance = Vector3.Distance(vMyCurrentPosition, lastSensor.Location), WorldID = GilesTrinity.CurrentWorldDynamicId }); } lastRecordedPosition = DateTime.Now; } // Set the public variable MovementSpeed = GetMovementSpeed(); vMoveToTarget = WarnAndLogLongPath(vMoveToTarget); // Make sure GilesTrinity doesn't want us to avoid routine-movement //if (GilesTrinity.bDontMoveMeIAmDoingShit) // return; // Store player current position // Store distance to current moveto target float DestinationDistance; DestinationDistance = vMyCurrentPosition.Distance2D(vMoveToTarget); // Do unstuckery things if (GilesTrinity.Settings.Advanced.UnstuckerEnabled) { // See if we can reset the 10-limit unstuck counter, if >120 seconds since we last generated an unstuck location // this is used if we're NOT stuck... if (iTotalAntiStuckAttempts > 1 && DateTime.Now.Subtract(LastGeneratedStuckPosition).TotalSeconds >= 120) { iTotalAntiStuckAttempts = 1; iTimesReachedStuckPoint = 0; vSafeMovementLocation = Vector3.Zero; NavHelper.UsedStuckSpots = new List <GridPoint>(); DbHelper.Log(TrinityLogLevel.Normal, LogCategory.Movement, "Resetting unstuck timers", true); } // See if we need to, and can, generate unstuck actions // check if we're stuck bool isStuck = UnstuckChecker(vMyCurrentPosition); if (DateTime.Now.Subtract(_lastCancelledUnstucker).TotalSeconds > iCancelUnstuckerForSeconds && isStuck) { // Record the time we last apparently couldn't move for a brief period of time _lastRecordedAnyStuck = DateTime.Now; // See if there's any stuck position to try and navigate to generated by random mover vSafeMovementLocation = UnstuckHandler(vMyCurrentPosition, LastMoveToTarget); DbHelper.Log(TrinityLogLevel.Normal, LogCategory.Movement, "SafeMovement Location set to {0}", vSafeMovementLocation); if (vSafeMovementLocation == Vector3.Zero) { return; } } // See if we can clear the total unstuckattempts if we haven't been stuck in over 6 minutes. if (DateTime.Now.Subtract(_lastRecordedAnyStuck).TotalSeconds >= 360) { iTimesReachedMaxUnstucks = 0; } // Did we have a safe point already generated (eg from last loop through), if so use it as our current location instead if (vSafeMovementLocation != Vector3.Zero) { // Set our current movement target to the safe point we generated last cycle vMoveToTarget = vSafeMovementLocation; DestinationDistance = vMyCurrentPosition.Distance2D(vMoveToTarget); } // Get distance to current destination // Remove the stuck position if it's been reached, this bit of code also creates multiple stuck-patterns in an ever increasing amount if (vSafeMovementLocation != Vector3.Zero && DestinationDistance <= 3f) { vSafeMovementLocation = Vector3.Zero; iTimesReachedStuckPoint++; // Do we want to immediately generate a 2nd waypoint to "chain" anti-stucks in an ever-increasing path-length? if (iTimesReachedStuckPoint <= iTotalAntiStuckAttempts) { //GilesTrinity.PlayerStatus.CurrentPosition = vMyCurrentPosition; vSafeMovementLocation = NavHelper.FindSafeZone(true, iTotalAntiStuckAttempts, vMyCurrentPosition); vMoveToTarget = vSafeMovementLocation; } else { if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Movement, "Clearing old route and trying new path find to: " + LastMoveToTarget.ToString()); } // Reset the path and allow a whole "New" unstuck generation next cycle iTimesReachedStuckPoint = 0; // And cancel unstucking for 9 seconds so DB can try to navigate iCancelUnstuckerForSeconds = (9 * iTotalAntiStuckAttempts); if (iCancelUnstuckerForSeconds < 20) { iCancelUnstuckerForSeconds = 20; } _lastCancelledUnstucker = DateTime.Now; Navigator.Clear(); Navigator.MoveTo(LastMoveToTarget, "original destination", false); return; } } } // Is the built-in unstucker enabled or not? // if (GilesTrinity.Settings.Advanced.DebugInStatusBar) // { // Logging.WriteDiagnostic("[Trinity] Moving toward <{0:0},{1:0},{2:0}> distance: {3:0}", vMoveToTarget.X, vMoveToTarget.Y, vMoveToTarget.Z, fDistanceFromTarget); // } // See if there's an obstacle in our way, if so try to navigate around it Vector3 point = vMoveToTarget; foreach (GilesObstacle obstacle in GilesTrinity.hashNavigationObstacleCache.Where(o => vMoveToTarget.Distance2D(o.Location) <= o.Radius)) { if (vShiftedPosition == Vector3.Zero) { // Make sure we only shift max once every 6 seconds if (DateTime.Now.Subtract(lastShiftedPosition).TotalMilliseconds >= 6000) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Shifting position for Navigation Obstacle {0} {1} at {2}", obstacle.ActorSNO, obstacle.Name, obstacle.Location); GetShiftedPosition(ref vMoveToTarget, ref point, obstacle.Radius + 5f); } } else { if (DateTime.Now.Subtract(lastShiftedPosition).TotalMilliseconds <= iShiftPositionFor) { vMoveToTarget = vShiftedPosition; } else { vShiftedPosition = Vector3.Zero; } } } // don't use special movement within 10 seconds of being stuck bool cancelSpecialMovementAfterStuck = DateTime.Now.Subtract(LastGeneratedStuckPosition).TotalMilliseconds > 10000; // See if we can use abilities like leap etc. for movement out of combat, but not in town if (GilesTrinity.Settings.Combat.Misc.AllowOOCMovement && !GilesTrinity.PlayerStatus.IsInTown && !GilesTrinity.bDontMoveMeIAmDoingShit && cancelSpecialMovementAfterStuck) { bool bTooMuchZChange = (Math.Abs(vMyCurrentPosition.Z - vMoveToTarget.Z) >= 4f); // Whirlwind for a barb, special context only if (GilesTrinity.Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && GilesTrinity.GilesObjectCache.Count(u => u.Type == GObjectType.Unit && u.RadiusDistance <= 10f) >= 1 && GilesTrinity.PlayerStatus.PrimaryResource >= 10) { ZetaDia.Me.UsePower(SNOPower.Barbarian_Whirlwind, vMoveToTarget, GilesTrinity.CurrentWorldDynamicId, -1); if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Whirlwind for OOC movement, distance={0}", DestinationDistance); } return; } // Leap movement for a barb if (GilesTrinity.Hotbar.Contains(SNOPower.Barbarian_Leap) && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Barbarian_Leap]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Barbarian_Leap] && DestinationDistance >= 20f && PowerManager.CanCast(SNOPower.Barbarian_Leap) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (DestinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, vMyCurrentPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Barbarian_Leap, vThisTarget, GilesTrinity.CurrentWorldDynamicId, -1); GilesTrinity.dictAbilityLastUse[SNOPower.Barbarian_Leap] = DateTime.Now; if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Leap for OOC movement, distance={0}", DestinationDistance); } return; } // Furious Charge movement for a barb if (GilesTrinity.Hotbar.Contains(SNOPower.Barbarian_FuriousCharge) && !bTooMuchZChange && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Barbarian_FuriousCharge]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Barbarian_FuriousCharge] && DestinationDistance >= 20f && PowerManager.CanCast(SNOPower.Barbarian_FuriousCharge) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (DestinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, vMyCurrentPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Barbarian_FuriousCharge, vThisTarget, GilesTrinity.CurrentWorldDynamicId, -1); GilesTrinity.dictAbilityLastUse[SNOPower.Barbarian_FuriousCharge] = DateTime.Now; if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Furious Charge for OOC movement, distance={0}", DestinationDistance); } return; } // Vault for a DH - maximum set by user-defined setting if (GilesTrinity.Hotbar.Contains(SNOPower.DemonHunter_Vault) && !bTooMuchZChange && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.DemonHunter_Vault]).TotalMilliseconds >= GilesTrinity.Settings.Combat.DemonHunter.VaultMovementDelay && DestinationDistance >= 18f && PowerManager.CanCast(SNOPower.DemonHunter_Vault) && !ShrinesInArea(vMoveToTarget) && // Don't Vault into avoidance/monsters if we're kiting (GilesTrinity.PlayerKiteDistance <= 0 || (GilesTrinity.PlayerKiteDistance > 0 && (!GilesTrinity.hashAvoidanceObstacleCache.Any(a => a.Location.Distance(vMoveToTarget) <= GilesTrinity.PlayerKiteDistance) || (!GilesTrinity.hashAvoidanceObstacleCache.Any(a => MathEx.IntersectsPath(a.Location, a.Radius, GilesTrinity.PlayerStatus.CurrentPosition, vMoveToTarget))) || !GilesTrinity.hashMonsterObstacleCache.Any(a => a.Location.Distance(vMoveToTarget) <= GilesTrinity.PlayerKiteDistance)))) ) { Vector3 vThisTarget = vMoveToTarget; if (DestinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, vMyCurrentPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.DemonHunter_Vault, vThisTarget, GilesTrinity.CurrentWorldDynamicId, -1); GilesTrinity.dictAbilityLastUse[SNOPower.DemonHunter_Vault] = DateTime.Now; if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Vault for OOC movement, distance={0}", DestinationDistance); } return; } // Tempest rush for a monk if (GilesTrinity.Hotbar.Contains(SNOPower.Monk_TempestRush) && (GilesTrinity.Settings.Combat.Monk.TROption == TempestRushOption.MovementOnly || GilesTrinity.Settings.Combat.Monk.TROption == TempestRushOption.Always || (GilesTrinity.Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && !TargetUtil.AnyElitesInRange(40f)))) { Vector3 vTargetAimPoint = vMoveToTarget; //vTargetAimPoint = MathEx.CalculatePointFrom(vMoveToTarget, vMyCurrentPosition, aimPointDistance); //vTargetAimPoint = MathEx.CalculatePointFrom(vMyCurrentPosition, vMoveToTarget, aimPointDistance); //bool canRayCastTarget = GilesTrinity.NavHelper.CanRayCast(vMyCurrentPosition, vTargetAimPoint); bool canRayCastTarget = true; vTargetAimPoint = TargetUtil.FindTempestRushTarget(); if (!CanChannelTempestRush && ((GilesTrinity.PlayerStatus.PrimaryResource >= GilesTrinity.Settings.Combat.Monk.TR_MinSpirit && DestinationDistance >= GilesTrinity.Settings.Combat.Monk.TR_MinDist) || DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Monk_TempestRush]).TotalMilliseconds <= 150) && canRayCastTarget && PowerManager.CanCast(SNOPower.Monk_TempestRush)) { CanChannelTempestRush = true; } else if ((CanChannelTempestRush && (GilesTrinity.PlayerStatus.PrimaryResource < 10f)) || !canRayCastTarget) { CanChannelTempestRush = false; } double lastUse = DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Monk_TempestRush]).TotalMilliseconds; if (CanChannelTempestRush) { if (GilesTrinity.GilesUseTimer(SNOPower.Monk_TempestRush)) { LastTempestRushPosition = vTargetAimPoint; ZetaDia.Me.UsePower(SNOPower.Monk_TempestRush, vTargetAimPoint, GilesTrinity.CurrentWorldDynamicId, -1); GilesTrinity.dictAbilityLastUse[SNOPower.Monk_TempestRush] = DateTime.Now; GilesTrinity.LastPowerUsed = SNOPower.Monk_TempestRush; // simulate movement speed of 30 SpeedSensor lastSensor = SpeedSensors.OrderByDescending(s => s.Timestamp).FirstOrDefault(); SpeedSensors.Add(new SpeedSensor() { Location = vMyCurrentPosition, TimeSinceLastMove = new TimeSpan(0, 0, 0, 0, 1000), Distance = 5f, WorldID = GilesTrinity.CurrentWorldDynamicId }); if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Tempest Rush for OOC movement, distance={0:0} spirit={1:0} cd={2} lastUse={3:0} V3={4} vAim={5}", DestinationDistance, GilesTrinity.PlayerStatus.PrimaryResource, PowerManager.CanCast(SNOPower.Monk_TempestRush), lastUse, vMoveToTarget, vTargetAimPoint); } return; } else { return; } } else { if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Tempest rush failed!: {0:00.0} / {1} distance: {2:00.0} / {3} Raycast: {4} MS: {5:0.0} lastUse={6:0}", GilesTrinity.PlayerStatus.PrimaryResource, GilesTrinity.Settings.Combat.Monk.TR_MinSpirit, DestinationDistance, GilesTrinity.Settings.Combat.Monk.TR_MinDist, canRayCastTarget, GetMovementSpeed(), lastUse); } GilesTrinity.MaintainTempestRush = false; } // Always set this from PlayerMover GilesTrinity.LastTempestRushLocation = vTargetAimPoint; } bool hasWormHole = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 4); // Teleport for a wizard (need to be able to check skill rune in DB for a 3-4 teleport spam in a row) if (GilesTrinity.Hotbar.Contains(SNOPower.Wizard_Teleport) && ((PowerManager.CanCast(SNOPower.Wizard_Teleport) && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Wizard_Teleport]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Wizard_Teleport]) || (hasWormHole && WizardTeleportCount < 3 && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Wizard_Teleport]).TotalMilliseconds >= 250)) && DestinationDistance >= 10f && !ShrinesInArea(vMoveToTarget)) { // Reset teleport count if we've already hit the max if (WizardTeleportCount >= 3) { WizardTeleportCount = 0; } // increment the teleport count for wormhole rune WizardTeleportCount++; var maxTeleportRange = 75f; Vector3 vThisTarget = vMoveToTarget; if (DestinationDistance > maxTeleportRange) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, vMyCurrentPosition, maxTeleportRange); } ZetaDia.Me.UsePower(SNOPower.Wizard_Teleport, vThisTarget, GilesTrinity.CurrentWorldDynamicId, -1); GilesTrinity.dictAbilityLastUse[SNOPower.Wizard_Teleport] = DateTime.Now; if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Teleport for OOC movement, distance={0}", DestinationDistance); } return; } // Archon Teleport for a wizard if (GilesTrinity.Hotbar.Contains(SNOPower.Wizard_Archon_Teleport) && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.Wizard_Archon_Teleport]).TotalMilliseconds >= GilesTrinity.dictAbilityRepeatDelay[SNOPower.Wizard_Archon_Teleport] && DestinationDistance >= 20f && PowerManager.CanCast(SNOPower.Wizard_Archon_Teleport) && !ShrinesInArea(vMoveToTarget)) { Vector3 vThisTarget = vMoveToTarget; if (DestinationDistance > 35f) { vThisTarget = MathEx.CalculatePointFrom(vMoveToTarget, vMyCurrentPosition, 35f); } ZetaDia.Me.UsePower(SNOPower.Wizard_Archon_Teleport, vThisTarget, GilesTrinity.CurrentWorldDynamicId, -1); GilesTrinity.dictAbilityLastUse[SNOPower.Wizard_Archon_Teleport] = DateTime.Now; if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Using Archon Teleport for OOC movement, distance={0}", DestinationDistance); } return; } } if (vMyCurrentPosition.Distance2D(vMoveToTarget) > 1f) { // Default movement ZetaDia.Me.UsePower(SNOPower.Walk, vMoveToTarget, GilesTrinity.CurrentWorldDynamicId, -1); if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Moved to:{0} dir: {1} Speed:{2:0.00} Dist:{3:0} ZDiff:{4:0} Nav:{5} LoS:{6}", vMoveToTarget, MathUtil.GetHeadingToPoint(vMoveToTarget), MovementSpeed, vMyCurrentPosition.Distance2D(vMoveToTarget), Math.Abs(vMyCurrentPosition.Z - vMoveToTarget.Z), GilesTrinity.gp.CanStandAt(GilesTrinity.gp.WorldToGrid(vMoveToTarget.ToVector2())), !Navigator.Raycast(vMyCurrentPosition, vMoveToTarget) ); } } else { if (GilesTrinity.Settings.Advanced.LogCategories.HasFlag(LogCategory.Movement)) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.Movement, "Reached MoveTowards Destination {0} Current Speed: {1:0.0}", vMoveToTarget, MovementSpeed); } } }
private static TrinityPower GetWitchDoctorPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower) { bool hasGraveInjustice = ZetaDia.CPlayer.PassiveSkills.Contains(SNOPower.Witchdoctor_Passive_GraveInjustice); bool hasAngryChicken = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Witchdoctor_Hex && s.RuneIndex == 1); bool isChicken = PlayerStatus.IsHidden; // Hex with angry chicken, is chicken, explode! if (!UseOOCBuff && isChicken && (TargetUtil.AnyMobsInRange(12f, 1) || CurrentTarget.RadiusDistance <= 10f || UseDestructiblePower) && PowerManager.CanCast(SNOPower.Witchdoctor_Hex_Explode)) { ShouldRefreshHotbarAbilities = true; return(new TrinityPower(SNOPower.Witchdoctor_Hex_Explode, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 2, WAIT_FOR_ANIM)); } else if (isChicken) { ShouldRefreshHotbarAbilities = true; } // Pick the best destructible power available if (UseDestructiblePower) { return(GetWitchDoctorDestroyPower()); } // Witch doctors have no reserve requirements? MinEnergyReserve = 0; // Spirit Walk Cast on 65% health or while avoiding anything but molten core or incapacitated or Chasing Goblins if (Hotbar.Contains(SNOPower.Witchdoctor_SpiritWalk) && PlayerStatus.PrimaryResource >= 49 && ( PlayerStatus.CurrentHealthPct <= 0.65 || PlayerStatus.IsIncapacitated || PlayerStatus.IsRooted || (Settings.Combat.Misc.AllowOOCMovement && UseOOCBuff) || (!UseOOCBuff && CurrentTarget.IsTreasureGoblin && CurrentTarget.HitPointsPct < 0.90 && CurrentTarget.RadiusDistance <= 40f) ) && PowerManager.CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // 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); // Soul Harvest Any Elites or 2+ Norms and baby it's harvest season if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_SoulHarvest) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 59 && GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) <= 4 && (TargetUtil.AnyMobsInRange(16f, 2) || TargetUtil.IsEliteTargetInRange(16f)) && PowerManager.CanCast(SNOPower.Witchdoctor_SoulHarvest)) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Soul Harvest with VengefulSpirit if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Witchdoctor_SoulHarvest) && hasVengefulSpirit && PlayerStatus.PrimaryResource >= 59 && TargetUtil.AnyMobsInRange(16, 3) && GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) <= 4 && PowerManager.CanCast(SNOPower.Witchdoctor_SoulHarvest)) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Sacrifice AKA Zombie Dog Jihad, use on Elites Only or to try and Save yourself if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice) && (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 9f)) && PowerManager.CanCast(SNOPower.Witchdoctor_Sacrifice)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 0, WAIT_FOR_ANIM)); } // Gargantuan, Recast on 1+ Elites or Bosses to trigger Restless Giant if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Gargantuan) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 147 && (ElitesWithinRange[RANGE_15] >= 1 || (CurrentTarget != null && (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f)) || iPlayerOwnedGargantuan == 0) && PowerManager.CanCast(SNOPower.Witchdoctor_Gargantuan)) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan, 0f, vNullLocation, CurrentWorldDynamicId, -1, 2, 1, WAIT_FOR_ANIM)); } // Zombie Dogs non-sacrifice build if (!IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_SummonZombieDog) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 49 && (ElitesWithinRange[RANGE_20] >= 2 || AnythingWithinRange[RANGE_20] >= 5 || (CurrentTarget != null && ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin) && CurrentTarget.RadiusDistance <= 30f)) || iPlayerOwnedZombieDog <= 2) && !Settings.Combat.WitchDoctor.ZeroDogs && PowerManager.CanCast(SNOPower.Witchdoctor_SummonZombieDog)) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Zombie Dogs for Sacrifice if (Hotbar.Contains(SNOPower.Witchdoctor_SummonZombieDog) && PlayerStatus.PrimaryResource >= 49 && (LastPowerUsed == SNOPower.Witchdoctor_Sacrifice || iPlayerOwnedZombieDog <= 2) && TimeSinceUse(SNOPower.Witchdoctor_SummonZombieDog) > 1000 && Settings.Combat.WitchDoctor.ZeroDogs && PowerManager.CanCast(SNOPower.Witchdoctor_SummonZombieDog)) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Hex with angry chicken, check if we want to shape shift and explode if (Hotbar.Contains(SNOPower.Witchdoctor_Hex) && hasAngryChicken && PowerManager.CanCast(SNOPower.Witchdoctor_Hex) && PlayerStatus.PrimaryResource >= 49) { ShouldRefreshHotbarAbilities = true; return(new TrinityPower(SNOPower.Witchdoctor_Hex, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 2, WAIT_FOR_ANIM)); } // Hex Spam Cast without angry chicken if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Hex) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 49 && !hasAngryChicken && (TargetUtil.AnyElitesInRange(12) || TargetUtil.AnyMobsInRange(12, 2) || TargetUtil.IsEliteTargetInRange(18f)) && PowerManager.CanCast(SNOPower.Witchdoctor_Hex)) { return(new TrinityPower(SNOPower.Witchdoctor_Hex, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, NO_WAIT_ANIM)); } // Mass Confuse, elites only or big mobs or to escape on low health if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_MassConfusion) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 74 && (ElitesWithinRange[RANGE_12] >= 1 || AnythingWithinRange[RANGE_12] >= 6 || PlayerStatus.CurrentHealthPct <= 0.25 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) && !CurrentTarget.IsTreasureGoblin && PowerManager.CanCast(SNOPower.Witchdoctor_MassConfusion)) { return(new TrinityPower(SNOPower.Witchdoctor_MassConfusion, 0f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Big Bad Voodoo, elites and bosses only if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_BigBadVoodoo) && !PlayerStatus.IsIncapacitated && !CurrentTarget.IsTreasureGoblin && (ElitesWithinRange[RANGE_6] > 0 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) && PowerManager.CanCast(SNOPower.Witchdoctor_BigBadVoodoo)) { return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Grasp of the Dead, look below, droping globes and dogs when using it on elites and 3 norms if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_GraspOfTheDead) && !PlayerStatus.IsIncapacitated && (TargetUtil.AnyMobsInRange(30, 2)) && PlayerStatus.PrimaryResource >= 78 && PowerManager.CanCast(SNOPower.Witchdoctor_GraspOfTheDead)) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(15); return(new TrinityPower(SNOPower.Witchdoctor_GraspOfTheDead, 25f, bestClusterPoint, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Horrify Buff When not in combat for movement speed if (UseOOCBuff && hasGraveInjustice && Hotbar.Contains(SNOPower.Witchdoctor_Horrify) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 37 && PowerManager.CanCast(SNOPower.Witchdoctor_Horrify)) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Horrify Buff at 35% health if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Horrify) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 37 && PlayerStatus.CurrentHealthPct <= 0.35 && PowerManager.CanCast(SNOPower.Witchdoctor_Horrify)) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Fetish Army, elites only if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_FetishArmy) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_25] > 0 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 16f)) && PowerManager.CanCast(SNOPower.Witchdoctor_FetishArmy)) { return(new TrinityPower(SNOPower.Witchdoctor_FetishArmy, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } //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 if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_SpiritBarrage) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 108 && PowerManager.CanCast(SNOPower.Witchdoctor_SpiritBarrage) && !hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, WAIT_FOR_ANIM)); } // Spirit Barrage Manitou if (Hotbar.Contains(SNOPower.Witchdoctor_SpiritBarrage) && PlayerStatus.PrimaryResource >= 108 && TimeSinceUse(SNOPower.Witchdoctor_SpiritBarrage) > 18000 && hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // Haunt the shit out of monster and maybe they will give you treats if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Haunt) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 98 && PowerManager.CanCast(SNOPower.Witchdoctor_Haunt)) { return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 21f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Locust if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Locust_Swarm) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 196 && PowerManager.CanCast(SNOPower.Witchdoctor_Locust_Swarm) && !CurrentTarget.HasDotDPS && LastPowerUsed != SNOPower.Witchdoctor_Locust_Swarm) { return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 12f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Sacrifice for 0 Dogs if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice) && Settings.Combat.WitchDoctor.ZeroDogs && PowerManager.CanCast(SNOPower.Witchdoctor_Sacrifice)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 9f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Wall of Zombies if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_WallOfZombies) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 3 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f)) && PlayerStatus.PrimaryResource >= 103 && PowerManager.CanCast(SNOPower.Witchdoctor_WallOfZombies)) { return(new TrinityPower(SNOPower.Witchdoctor_WallOfZombies, 25f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } var zombieChargerRange = hasGraveInjustice ? Math.Min(PlayerStatus.GoldPickupRadius + 8f, 11f) : 11f; // Zombie Charger aka Zombie bears Spams Bears @ Everything from 11feet away if (!UseOOCBuff && Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 134 && TargetUtil.AnyMobsInRange(zombieChargerRange) && PowerManager.CanCast(SNOPower.Witchdoctor_ZombieCharger)) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } var acidCloudRange = hasGraveInjustice ? Math.Min(PlayerStatus.GoldPickupRadius + 8f, 30f) : 30f; // Acid Cloud if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_AcidCloud) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 172 && PowerManager.CanCast(SNOPower.Witchdoctor_AcidCloud)) { Vector3 bestClusterPoint; if (hasGraveInjustice) { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(PlayerStatus.GoldPickupRadius + 8f, 30f)); } else { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f); } return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, acidCloudRange, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Fire Bats fast-attack if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Firebats) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 98) { return(new TrinityPower(SNOPower.Witchdoctor_Firebats, 20f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Poison Darts fast-attack Spams Darts when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_PoisonDart) && !PlayerStatus.IsIncapacitated) { float fUseThisRange = 35f; if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150) { fUseThisRange = 30f; } return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 2, WAIT_FOR_ANIM)); } // Corpse Spiders fast-attacks Spams Spiders when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_CorpseSpider) && !PlayerStatus.IsIncapacitated) { float fUseThisRange = 35f; if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150) { fUseThisRange = 30f; } return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Toads fast-attacks Spams Toads when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_PlagueOfToads) && !PlayerStatus.IsIncapacitated) { float fUseThisRange = 35f; if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150) { fUseThisRange = 30f; } return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Fire Bomb fast-attacks Spams Bomb when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Witchdoctor_Firebomb) && !PlayerStatus.IsIncapacitated) { float fUseThisRange = 35f; if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 150) { fUseThisRange = 30f; } return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, fUseThisRange, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Default attacks if (!UseOOCBuff && !IsCurrentlyAvoiding) { ShouldRefreshHotbarAbilities = true; return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }
private static TrinityPower GetDemonHunterPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower) { // Pick the best destructible power available if (UseDestructiblePower) { return(GetDemonHunterDestroyPower()); } MinEnergyReserve = 25; // Shadow Power if (!UseOOCBuff && Hotbar.Contains(SNOPower.DemonHunter_ShadowPower) && !PlayerStatus.IsIncapacitated && PlayerStatus.SecondaryResource >= 14 && (PlayerStatus.CurrentHealthPct <= 0.99 || PlayerStatus.IsRooted || ElitesWithinRange[RANGE_25] >= 1 || AnythingWithinRange[RANGE_15] >= 3) && GilesUseTimer(SNOPower.DemonHunter_ShadowPower)) { return(new TrinityPower(SNOPower.DemonHunter_ShadowPower, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Smoke Screen if ((!UseOOCBuff || Settings.Combat.DemonHunter.SpamSmokeScreen) && Hotbar.Contains(SNOPower.DemonHunter_SmokeScreen) && !GetHasBuff(SNOPower.DemonHunter_ShadowPower) && PlayerStatus.SecondaryResource >= 14 && ( (PlayerStatus.CurrentHealthPct <= 0.90 || PlayerStatus.IsRooted || ElitesWithinRange[RANGE_20] >= 1 || AnythingWithinRange[RANGE_15] >= 3 || PlayerStatus.IsIncapacitated) || Settings.Combat.DemonHunter.SpamSmokeScreen ) && GilesUseTimer(SNOPower.DemonHunter_SmokeScreen)) { return(new TrinityPower(SNOPower.DemonHunter_SmokeScreen, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Preparation if ( ( ((!UseOOCBuff && !PlayerStatus.IsIncapacitated && AnythingWithinRange[RANGE_40] >= 1) || Settings.Combat.DemonHunter.SpamPreparation) ) && Hotbar.Contains(SNOPower.DemonHunter_Preparation) && PlayerStatus.SecondaryResource <= 10 && //GilesUseTimer(SNOPower.DemonHunter_Preparation) && //PowerManager.CanCast(SNOPower.DemonHunter_Preparation) TrinityPowerManager.CanUse(SNOPower.DemonHunter_Preparation) ) { return(new TrinityPower(SNOPower.DemonHunter_Preparation, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Evasive Fire if (!UseOOCBuff && Hotbar.Contains(SNOPower.DemonHunter_EvasiveFire) && !PlayerStatus.IsIncapacitated && (((AnythingWithinRange[RANGE_20] >= 1 || CurrentTarget.RadiusDistance <= 20f) && GilesUseTimer(SNOPower.DemonHunter_EvasiveFire)) || DHHasNoPrimary())) { float range = DHHasNoPrimary() ? 70f : 0f; return(new TrinityPower(SNOPower.DemonHunter_EvasiveFire, range, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Companion if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.DemonHunter_Companion) && iPlayerOwnedDHPets == 0 && PlayerStatus.SecondaryResource >= 10 && GilesUseTimer(SNOPower.DemonHunter_Companion)) { return(new TrinityPower(SNOPower.DemonHunter_Companion, 0f, vNullLocation, CurrentWorldDynamicId, -1, 2, 1, WAIT_FOR_ANIM)); } // Sentry Turret if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.DemonHunter_Sentry) && (TargetUtil.AnyElitesInRange(50) || TargetUtil.AnyMobsInRange(50, 2) || TargetUtil.IsEliteTargetInRange(50)) && PlayerStatus.PrimaryResource >= 30 && PowerManager.CanCast(SNOPower.DemonHunter_Sentry)) { return(new TrinityPower(SNOPower.DemonHunter_Sentry, 0f, PlayerStatus.CurrentPosition, CurrentWorldDynamicId, -1, 0, 0, NO_WAIT_ANIM)); } // Marked for Death if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_MarkedForDeath) && PlayerStatus.SecondaryResource >= 3 && (ElitesWithinRange[RANGE_40] >= 1 || AnythingWithinRange[RANGE_40] >= 3 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.Radius <= 40 && CurrentTarget.RadiusDistance <= 40f)) && GilesUseTimer(SNOPower.DemonHunter_MarkedForDeath)) { return(new TrinityPower(SNOPower.DemonHunter_MarkedForDeath, 40f, vNullLocation, CurrentWorldDynamicId, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Vault if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Vault) && !PlayerStatus.IsRooted && !PlayerStatus.IsIncapacitated && // Only use vault to retreat if < level 60, or if in inferno difficulty for level 60's (PlayerStatus.Level < 60 || iCurrentGameDifficulty == GameDifficulty.Inferno) && (CurrentTarget.RadiusDistance <= 10f || AnythingWithinRange[RANGE_6] >= 1) && ((!Hotbar.Contains(SNOPower.DemonHunter_ShadowPower) && PlayerStatus.SecondaryResource >= 16) || (Hotbar.Contains(SNOPower.DemonHunter_ShadowPower) && PlayerStatus.SecondaryResource >= 22)) && //GilesUseTimer(SNOPower.DemonHunter_Vault) && DateTime.Now.Subtract(GilesTrinity.dictAbilityLastUse[SNOPower.DemonHunter_Vault]).TotalMilliseconds >= GilesTrinity.Settings.Combat.DemonHunter.VaultMovementDelay && PowerManager.CanCast(SNOPower.DemonHunter_Vault)) { Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, -15f); return(new TrinityPower(SNOPower.DemonHunter_Vault, 20f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Rain of Vengeance if (!UseOOCBuff && Hotbar.Contains(SNOPower.DemonHunter_RainOfVengeance) && !PlayerStatus.IsIncapacitated && (AnythingWithinRange[RANGE_25] >= 3 || ElitesWithinRange[RANGE_25] >= 1) && GilesUseTimer(SNOPower.DemonHunter_RainOfVengeance) && PowerManager.CanCast(SNOPower.DemonHunter_RainOfVengeance)) { return(new TrinityPower(SNOPower.DemonHunter_RainOfVengeance, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Cluster Arrow if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_ClusterArrow) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 50 && (ElitesWithinRange[RANGE_50] >= 1 || AnythingWithinRange[RANGE_50] >= 5 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 69f)) && GilesUseTimer(SNOPower.DemonHunter_ClusterArrow)) { return(new TrinityPower(SNOPower.DemonHunter_ClusterArrow, 69f, vNullLocation, CurrentWorldDynamicId, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } // Multi Shot if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Multishot) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 30 && (ElitesWithinRange[RANGE_40] >= 1 || AnythingWithinRange[RANGE_40] >= 2 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 30f))) { return(new TrinityPower(SNOPower.DemonHunter_Multishot, 40f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Fan of Knives if (!UseOOCBuff && Hotbar.Contains(SNOPower.DemonHunter_FanOfKnives) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource >= 20 && (AnythingWithinRange[RANGE_15] >= 4 || ElitesWithinRange[RANGE_15] >= 1) && PowerManager.CanCast(SNOPower.DemonHunter_FanOfKnives)) { return(new TrinityPower(SNOPower.DemonHunter_FanOfKnives, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Strafe spam - similar to barbarian whirlwind routine if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Strafe) && !PlayerStatus.IsIncapacitated && !PlayerStatus.IsRooted && // Only if there's 3 guys in 25 yds AnythingWithinRange[RANGE_25] >= 3 && // Check for energy reservation amounts ((PlayerStatus.PrimaryResource >= 15 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { bool bGenerateNewZigZag = (DateTime.Now.Subtract(lastChangedZigZag).TotalMilliseconds >= 1500 || (vPositionLastZigZagCheck != vNullLocation && PlayerStatus.CurrentPosition == vPositionLastZigZagCheck && DateTime.Now.Subtract(lastChangedZigZag).TotalMilliseconds >= 200) || Vector3.Distance(PlayerStatus.CurrentPosition, vSideToSideTarget) <= 4f || CurrentTarget.ACDGuid != iACDGUIDLastWhirlwind); vPositionLastZigZagCheck = PlayerStatus.CurrentPosition; if (bGenerateNewZigZag) { float fExtraDistance = CurrentTarget.CentreDistance <= 10f ? 10f : 5f; //vSideToSideTarget = FindZigZagTargetLocation(CurrentTarget.vPosition, CurrentTarget.fCentreDist + fExtraDistance); vSideToSideTarget = NavHelper.FindSafeZone(false, 1, CurrentTarget.Position, false); // Resetting this to ensure the "no-spam" is reset since we changed our target location LastPowerUsed = SNOPower.None; iACDGUIDLastWhirlwind = CurrentTarget.ACDGuid; lastChangedZigZag = DateTime.Now; } return(new TrinityPower(SNOPower.DemonHunter_Strafe, 25f, vSideToSideTarget, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Spike Trap if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.DemonHunter_SpikeTrap) && LastPowerUsed != SNOPower.DemonHunter_SpikeTrap && (ElitesWithinRange[RANGE_30] >= 1 || AnythingWithinRange[RANGE_25] > 4 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 35f)) && PlayerStatus.PrimaryResource >= 30 && GilesUseTimer(SNOPower.DemonHunter_SpikeTrap)) { // For distant monsters, try to target a little bit in-front of them (as they run towards us), if it's not a treasure goblin float fExtraDistance = 0f; if (CurrentTarget.CentreDistance > 17f && !CurrentTarget.IsTreasureGoblin) { fExtraDistance = CurrentTarget.CentreDistance - 17f; if (fExtraDistance > 5f) { fExtraDistance = 5f; } if (CurrentTarget.CentreDistance - fExtraDistance < 15f) { fExtraDistance -= 2; } } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance - fExtraDistance); return(new TrinityPower(SNOPower.DemonHunter_SpikeTrap, 40f, vNewTarget, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Caltrops if (!UseOOCBuff && Hotbar.Contains(SNOPower.DemonHunter_Caltrops) && !PlayerStatus.IsIncapacitated && PlayerStatus.SecondaryResource >= 6 && (AnythingWithinRange[RANGE_30] >= 2 || ElitesWithinRange[RANGE_40] >= 1) && GilesUseTimer(SNOPower.DemonHunter_Caltrops)) { return(new TrinityPower(SNOPower.DemonHunter_Caltrops, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } //skillDict.Add("ElementalArrow", SNOPower.DemonHunter_ElementalArrow); //runeDict.Add("BallLightning", 1); //runeDict.Add("FrostArrow", 0); //runeDict.Add("ScreamingSkull", 2); //runeDict.Add("LightningBolts", 4); //runeDict.Add("NetherTentacles", 3); var hasBallLightning = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 1); var hasFrostArrow = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 0); var hasScreamingSkull = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 2); var hasLightningBolts = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 4); var hasNetherTentacles = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_ElementalArrow && s.RuneIndex == 3); // Elemental Arrow if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_ElementalArrow) && GilesUseTimer(SNOPower.DemonHunter_ElementalArrow) && !PlayerStatus.IsIncapacitated && ((PlayerStatus.PrimaryResource >= 10 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { // Players with grenades *AND* elemental arrow should spam grenades at close-range instead if (Hotbar.Contains(SNOPower.DemonHunter_Grenades) && CurrentTarget.RadiusDistance <= 18f) { return(new TrinityPower(SNOPower.DemonHunter_Grenades, 18f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Now return elemental arrow, if not sending grenades instead return(new TrinityPower(SNOPower.DemonHunter_ElementalArrow, 65f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Chakram if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Chakram) && !PlayerStatus.IsIncapacitated && // If we have elemental arrow or rapid fire, then use chakram as a 110 second buff, instead ((!Hotbar.Contains(SNOPower.DemonHunter_ClusterArrow)) || DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.DemonHunter_Chakram]).TotalMilliseconds >= 110000) && ((PlayerStatus.PrimaryResource >= 10 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { return(new TrinityPower(SNOPower.DemonHunter_Chakram, 50f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Rapid Fire if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_RapidFire) && !PlayerStatus.IsIncapacitated && ((PlayerStatus.PrimaryResource >= 20 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { // Players with grenades *AND* rapid fire should spam grenades at close-range instead if (Hotbar.Contains(SNOPower.DemonHunter_Grenades) && CurrentTarget.RadiusDistance <= 18f) { return(new TrinityPower(SNOPower.DemonHunter_Grenades, 18f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } // Now return rapid fire, if not sending grenades instead return(new TrinityPower(SNOPower.DemonHunter_RapidFire, 50f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Impale if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Impale) && !PlayerStatus.IsIncapacitated && (AnythingWithinRange[RANGE_12] <= 3) && ((PlayerStatus.PrimaryResource >= 25 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) && CurrentTarget.RadiusDistance <= 50f) { return(new TrinityPower(SNOPower.DemonHunter_Impale, 50f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Hungering Arrow if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow) && !PlayerStatus.IsIncapacitated) { return(new TrinityPower(SNOPower.DemonHunter_HungeringArrow, 50f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } // Entangling shot if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_EntanglingShot) && !PlayerStatus.IsIncapacitated) { return(new TrinityPower(SNOPower.DemonHunter_EntanglingShot, 50f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } // Bola Shot if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_BolaShot) && !PlayerStatus.IsIncapacitated) { return(new TrinityPower(SNOPower.DemonHunter_BolaShot, 50f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Grenades if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Grenades) && !PlayerStatus.IsIncapacitated) { return(new TrinityPower(SNOPower.DemonHunter_Grenades, 40f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Default attacks if (!UseOOCBuff && !IsCurrentlyAvoiding) { return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, CurrentWorldDynamicId, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }
private static TrinityPower GetBarbarianPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower) { // Pick the best destructible power available if (UseDestructiblePower) { return(GetBarbarianDestroyPower()); } // Barbarians need 56 reserve for special spam like WW MinEnergyReserve = 56; // Ignore Pain when low on health if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_IgnorePain) && PlayerStatus.CurrentHealthPct <= 0.45 && GilesUseTimer(SNOPower.Barbarian_IgnorePain, true) && PowerManager.CanCast(SNOPower.Barbarian_IgnorePain)) { return(new TrinityPower(SNOPower.Barbarian_IgnorePain, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } IsWaitingForSpecial = false; if (PlayerStatus.PrimaryResource < MinEnergyReserve) { if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Earthquake) && ElitesWithinRange[RANGE_25] >= 1 && GilesUseTimer(SNOPower.Barbarian_Earthquake) && !GetHasBuff(SNOPower.Barbarian_Earthquake)) { DbHelper.LogNormal("Waiting for Barbarian_Earthquake 1!"); IsWaitingForSpecial = true; } // Earthquake, elites close-range only if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Earthquake) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] > 0 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 13f)) && GilesUseTimer(SNOPower.Barbarian_Earthquake, true) && !GetHasBuff(SNOPower.Barbarian_Earthquake) && PowerManager.CanCast(SNOPower.Barbarian_Earthquake)) { if (PlayerStatus.PrimaryResource >= 50) { return(new TrinityPower(SNOPower.Barbarian_Earthquake, 13f, vNullLocation, CurrentWorldDynamicId, -1, 4, 4, WAIT_FOR_ANIM)); } DbHelper.LogNormal("Waiting for Barbarian_Earthquake 2!"); IsWaitingForSpecial = true; } if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && ElitesWithinRange[RANGE_25] >= 1 && GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) && !GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) { DbHelper.LogNormal("Waiting for Barbarian_WrathOfTheBerserker 1!"); IsWaitingForSpecial = true; } // Berserker special for ignore elites if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && Settings.Combat.Misc.IgnoreElites && (TargetUtil.AnyMobsInRange(25, 3) || TargetUtil.AnyMobsInRange(50, 10) || TargetUtil.AnyMobsInRange(Settings.Combat.Misc.TrashPackClusterRadius, Settings.Combat.Misc.TrashPackSize)) && GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker) && !GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) { DbHelper.LogNormal("Waiting for Barbarian_WrathOfTheBerserker 2!"); IsWaitingForSpecial = true; } if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_CallOfTheAncients) && ElitesWithinRange[RANGE_25] >= 1 && GilesUseTimer(SNOPower.Barbarian_CallOfTheAncients) && !GetHasBuff(SNOPower.Barbarian_CallOfTheAncients)) { DbHelper.LogNormal("Waiting for Barbarian_CallOfTheAncients!"); IsWaitingForSpecial = true; } } // Wrath of the berserker, elites only (wrath of berserker) if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && // If using WOTB on all elites, or if we should only use on "hard" affixes (!Settings.Combat.Barbarian.WOTBHardOnly || (shouldUseBerserkerPower && Settings.Combat.Barbarian.WOTBHardOnly)) && // Not on heart of sin after Cydaea CurrentTarget.ActorSNO != 193077 && // Make sure we are allowed to use wrath on goblins, else make sure this isn't a goblin ( (!Settings.Combat.Barbarian.UseWOTBGoblin || (Settings.Combat.Barbarian.UseWOTBGoblin && CurrentTarget.IsTreasureGoblin)) || // If ignoring elites completely, trigger on 3 trash within 25 yards, or 10 trash in 50 yards (Settings.Combat.Misc.IgnoreElites && (TargetUtil.AnyMobsInRange(25, 3) || TargetUtil.AnyMobsInRange(50, 10)) || !Settings.Combat.Misc.IgnoreElites) || // Otherwise use when Elite target is in 20 yards (TargetUtil.AnyElitesInRange(20, 1) || TargetUtil.IsEliteTargetInRange(20f)) || // Or if our health is low PlayerStatus.CurrentHealthPct <= 60 ) && // Don't still have the buff !GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker) && PowerManager.CanCast(SNOPower.Barbarian_WrathOfTheBerserker)) { if (PlayerStatus.PrimaryResource >= 50) { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Barbarian_WrathOfTheBerserker being used!({0})", CurrentTarget.InternalName); shouldUseBerserkerPower = false; IsWaitingForSpecial = false; return(new TrinityPower(SNOPower.Barbarian_WrathOfTheBerserker, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } else { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Barbarian_WrathOfTheBerserker ready, waiting for fury..."); IsWaitingForSpecial = true; } } // Call of the ancients, elites only if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_CallOfTheAncients) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_25] > 0 || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f)) && GilesUseTimer(SNOPower.Barbarian_CallOfTheAncients, true) && PowerManager.CanCast(SNOPower.Barbarian_CallOfTheAncients)) { if (PlayerStatus.PrimaryResource >= 50) { IsWaitingForSpecial = false; return(new TrinityPower(SNOPower.Barbarian_CallOfTheAncients, 0f, vNullLocation, CurrentWorldDynamicId, -1, 4, 4, WAIT_FOR_ANIM)); } else { DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.UserInformation, "Call of the Ancients ready, waiting for fury..."); IsWaitingForSpecial = true; } } // Battle rage, for if being followed and before we do sprint if (UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_BattleRage) && (GilesUseTimer(SNOPower.Barbarian_BattleRage) || !GetHasBuff(SNOPower.Barbarian_BattleRage)) && PlayerStatus.PrimaryResource >= 20 && PowerManager.CanCast(SNOPower.Barbarian_BattleRage)) { return(new TrinityPower(SNOPower.Barbarian_BattleRage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Special segment for sprint as an out-of-combat only if (UseOOCBuff && !bDontSpamOutofCombat && (Settings.Combat.Misc.AllowOOCMovement || GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Sprint) && !GetHasBuff(SNOPower.Barbarian_Sprint) && PlayerStatus.PrimaryResource >= 20 && GilesUseTimer(SNOPower.Barbarian_Sprint) && PowerManager.CanCast(SNOPower.Barbarian_Sprint)) { return(new TrinityPower(SNOPower.Barbarian_Sprint, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // War cry, constantly maintain if (!PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_WarCry) && (PlayerStatus.PrimaryResource <= 60 || !GetHasBuff(SNOPower.Barbarian_WarCry)) && GilesUseTimer(SNOPower.Barbarian_WarCry, true) && (!GetHasBuff(SNOPower.Barbarian_WarCry) || PowerManager.CanCast(SNOPower.Barbarian_WarCry))) { return(new TrinityPower(SNOPower.Barbarian_WarCry, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Threatening shout if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_ThreateningShout) && !PlayerStatus.IsIncapacitated && ((TargetUtil.AnyMobsInRange(25, Settings.Combat.Barbarian.MinThreatShoutMobCount)) || TargetUtil.IsEliteTargetInRange(25f)) && ( PlayerStatus.CurrentHealthPct <= 0.75 || (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && PlayerStatus.PrimaryResource <= 10) || (IsWaitingForSpecial && PlayerStatus.PrimaryResource <= MinEnergyReserve) ) && GilesUseTimer(SNOPower.Barbarian_ThreateningShout, true) && PowerManager.CanCast(SNOPower.Barbarian_ThreateningShout)) { return(new TrinityPower(SNOPower.Barbarian_ThreateningShout, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Threatening shout out-of-combat if (UseOOCBuff && Settings.Combat.Barbarian.ThreatShoutOOC && Hotbar.Contains(SNOPower.Barbarian_ThreateningShout) && !PlayerStatus.IsIncapacitated && PlayerStatus.PrimaryResource < 25 && GilesUseTimer(SNOPower.Barbarian_ThreateningShout, true) && PowerManager.CanCast(SNOPower.Barbarian_ThreateningShout)) { return(new TrinityPower(SNOPower.Barbarian_ThreateningShout, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Ground Stomp if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_GroundStomp) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 4 || PlayerStatus.CurrentHealthPct <= 0.7) && GilesUseTimer(SNOPower.Barbarian_GroundStomp, true) && PowerManager.CanCast(SNOPower.Barbarian_GroundStomp)) { return(new TrinityPower(SNOPower.Barbarian_GroundStomp, 16f, vNullLocation, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Revenge used off-cooldown if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_Revenge) && !PlayerStatus.IsIncapacitated && // Don't use revenge on goblins, too slow! (!CurrentTarget.IsTreasureGoblin || AnythingWithinRange[RANGE_12] >= 5) && // Doesn't need CURRENT target to be in range, just needs ANYTHING to be within 9 foot, since it's an AOE! (AnythingWithinRange[RANGE_6] > 0 || CurrentTarget.RadiusDistance <= 6f) && GilesUseTimer(SNOPower.Barbarian_Revenge) && PowerManager.CanCast(SNOPower.Barbarian_Revenge)) { // Note - we have LONGER animation times for whirlwind-users // Since whirlwind seems to interrupt rend so easily int iPreDelay = 0; int iPostDelay = 0; if (Hotbar.Contains(SNOPower.Barbarian_Whirlwind)) { if (LastPowerUsed == SNOPower.Barbarian_Whirlwind) { iPreDelay = 3; iPostDelay = 3; } } return(new TrinityPower(SNOPower.Barbarian_Revenge, 0f, PlayerStatus.CurrentPosition, CurrentWorldDynamicId, -1, iPreDelay, iPostDelay, WAIT_FOR_ANIM)); } // Furious charge if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_FuriousCharge) && (ElitesWithinRange[RANGE_12] > 3 && GilesUseTimer(SNOPower.Barbarian_FuriousCharge) && PowerManager.CanCast(SNOPower.Barbarian_FuriousCharge))) { float fExtraDistance; if (CurrentTarget.CentreDistance <= 25) { fExtraDistance = 30; } else { fExtraDistance = (25 - CurrentTarget.CentreDistance); } if (fExtraDistance < 5f) { fExtraDistance = 5f; } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance + fExtraDistance); return(new TrinityPower(SNOPower.Barbarian_FuriousCharge, 32f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2, WAIT_FOR_ANIM)); } // Leap used when off-cooldown, or when out-of-range if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_Leap) && !PlayerStatus.IsIncapacitated && (AnythingWithinRange[RANGE_20] > 1 || ElitesWithinRange[RANGE_20] > 0) && GilesUseTimer(SNOPower.Barbarian_Leap, true) && PowerManager.CanCast(SNOPower.Barbarian_Leap)) { // For close-by monsters, try to leap a little further than their centre-point float fExtraDistance = CurrentTarget.Radius; if (fExtraDistance <= 4f) { fExtraDistance = 4f; } if (CurrentTarget.CentreDistance + fExtraDistance > 35f) { fExtraDistance = 35 - CurrentTarget.CentreDistance; } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance + fExtraDistance); return(new TrinityPower(SNOPower.Barbarian_Leap, 35f, vNewTarget, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // Rend spam for Non-WhirlWind users if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Barbarian_Rend) && TargetUtil.AnyMobsInRange(9) && !CurrentTarget.IsTreasureGoblin && ((!IsWaitingForSpecial && PlayerStatus.PrimaryResource >= 20) || (IsWaitingForSpecial && PlayerStatus.PrimaryResource > MinEnergyReserve)) && (GilesUseTimer(SNOPower.Barbarian_Rend) && (NonRendedTargets_9 > 2 || !CurrentTarget.HasDotDPS)) && (TimeSinceUse(SNOPower.Barbarian_Rend) > 1500 || TargetUtil.AnyMobsInRange(10f, 6)) && LastPowerUsed != SNOPower.Barbarian_Rend ) { iWithinRangeLastRend = GilesObjectCache.Count(u => u.Type == GObjectType.Unit && u.RadiusDistance <= 9f); iACDGUIDLastRend = CurrentTarget.ACDGuid; // Note - we have LONGER animation times for whirlwind-users // Since whirlwind seems to interrupt rend so easily int rendPreDelay = 0; int rendPostDelay = 1; if (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && (LastPowerUsed == SNOPower.Barbarian_Whirlwind || LastPowerUsed == SNOPower.None)) { rendPreDelay = 2; rendPostDelay = 2; } return(new TrinityPower(SNOPower.Barbarian_Rend, 0f, PlayerStatus.CurrentPosition, CurrentWorldDynamicId, -1, rendPreDelay, rendPostDelay, WAIT_FOR_ANIM)); } // Overpower used off-cooldown if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_Overpower) && !PlayerStatus.IsIncapacitated && (CurrentTarget.RadiusDistance <= 6f || ( AnythingWithinRange[RANGE_6] >= 1 && (CurrentTarget.IsEliteRareUnique || CurrentTarget.IsMinion || CurrentTarget.IsBoss || GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker) || (CurrentTarget.IsTreasureGoblin && CurrentTarget.CentreDistance <= 6f) || Hotbar.Contains(SNOPower.Barbarian_SeismicSlam)) ) ) && GilesUseTimer(SNOPower.Barbarian_Overpower) && PowerManager.CanCast(SNOPower.Barbarian_Overpower)) { int iPreDelay = 0; int iPostDelay = 0; // Note - we have LONGER animation times for whirlwind-users // Since whirlwind seems to interrupt rend so easily /*if (hashPowerHotbarAbilities.Contains(SNOPower.Barbarian_Whirlwind)) * { * if (powerLastSnoPowerUsed == SNOPower.Barbarian_Whirlwind || powerLastSnoPowerUsed == SNOPower.None) * { * iPreDelay = 5; * iPostDelay = 5; * } * }*/ return(new TrinityPower(SNOPower.Barbarian_Overpower, 0f, PlayerStatus.CurrentPosition, CurrentWorldDynamicId, -1, iPreDelay, iPostDelay, WAIT_FOR_ANIM)); } // Seismic slam enemies within close range if (!UseOOCBuff && !IsWaitingForSpecial && Hotbar.Contains(SNOPower.Barbarian_SeismicSlam) && !PlayerStatus.IsIncapacitated && (!Hotbar.Contains(SNOPower.Barbarian_BattleRage) || (Hotbar.Contains(SNOPower.Barbarian_BattleRage) && GetHasBuff(SNOPower.Barbarian_BattleRage))) && PlayerStatus.PrimaryResource >= 15 && CurrentTarget.CentreDistance <= 40f && (AnythingWithinRange[RANGE_50] > 1 || (AnythingWithinRange[RANGE_50] > 0 && PlayerStatus.PrimaryResourcePct >= 0.85 && CurrentTarget.HitPointsPct >= 0.30) || (CurrentTarget.IsBoss || CurrentTarget.IsEliteRareUnique || (CurrentTarget.IsTreasureGoblin && CurrentTarget.CentreDistance <= 20f)))) { return(new TrinityPower(SNOPower.Barbarian_SeismicSlam, 40f, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, WAIT_FOR_ANIM)); } // Ancient spear if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_AncientSpear) && GilesUseTimer(SNOPower.Barbarian_AncientSpear) && PowerManager.CanCast(SNOPower.Barbarian_AncientSpear) && CurrentTarget.HitPointsPct >= 0.20) { // For close-by monsters, try to leap a little further than their centre-point float fExtraDistance = CurrentTarget.Radius; if (fExtraDistance <= 4f) { fExtraDistance = 30f; } if (CurrentTarget.CentreDistance + fExtraDistance > 60f) { fExtraDistance = 60 - CurrentTarget.CentreDistance; } if (fExtraDistance < 30) { fExtraDistance = 30f; } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, PlayerStatus.CurrentPosition, CurrentTarget.CentreDistance + fExtraDistance); return(new TrinityPower(SNOPower.Barbarian_AncientSpear, 55f, vNewTarget, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // Sprint buff, if same suitable targets as elites, keep maintained for WW users if (!UseOOCBuff && !bDontSpamOutofCombat && Hotbar.Contains(SNOPower.Barbarian_Sprint) && !PlayerStatus.IsIncapacitated && // Let's check if is not spaming too much DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Barbarian_Sprint]).TotalMilliseconds >= 200 && // Fury Dump Options for sprint: use at max energy constantly, or on a timer ( (Settings.Combat.Barbarian.FuryDumpWOTB && PlayerStatus.PrimaryResourcePct >= 0.95 && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) || (Settings.Combat.Barbarian.FuryDumpAlways && PlayerStatus.PrimaryResourcePct >= 0.95) || ((GilesUseTimer(SNOPower.Barbarian_Sprint) && !GetHasBuff(SNOPower.Barbarian_Sprint)) && // Always keep up if we are whirlwinding, if the target is a goblin, or if we are 16 feet away from the target (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) || CurrentTarget.IsTreasureGoblin || (CurrentTarget.CentreDistance >= 16f && PlayerStatus.PrimaryResource >= 40))) ) && // If they have battle-rage, make sure it's up (!Hotbar.Contains(SNOPower.Barbarian_BattleRage) || (Hotbar.Contains(SNOPower.Barbarian_BattleRage) && GetHasBuff(SNOPower.Barbarian_BattleRage))) && // Check for minimum energy PlayerStatus.PrimaryResource >= 20) { return(new TrinityPower(SNOPower.Barbarian_Sprint, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } //skillDict.Add("Frenzy", SNOPower.Barbarian_Frenzy); //runeDict.Add("Sidearm", 1); //runeDict.Add("Triumph", 4); //runeDict.Add("Vanguard", 2); //runeDict.Add("Smite", 3); //runeDict.Add("Maniac", 0); bool hasManiacRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Barbarian_Frenzy && s.RuneIndex == 0); // Frenzy to 5 stacks if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsRooted && Hotbar.Contains(SNOPower.Barbarian_Frenzy) && !TargetUtil.AnyMobsInRange(15f, 3) && GetBuffStacks(SNOPower.Barbarian_Frenzy) < 5) { return(new TrinityPower(SNOPower.Barbarian_Frenzy, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Whirlwind spam as long as necessary pre-buffs are up if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && !PlayerStatus.IsIncapacitated && !PlayerStatus.IsRooted && (!IsWaitingForSpecial || (IsWaitingForSpecial && !(TargetUtil.AnyMobsInRange(3, 15) || ForceCloseRangeTarget))) && // make sure we're not surrounded if waiting for special // Don't WW against goblins, units in the special SNO list (!Settings.Combat.Barbarian.SelectiveWhirlwind || (Settings.Combat.Barbarian.SelectiveWhirlwind && bAnyNonWWIgnoreMobsInRange && !hashActorSNOWhirlwindIgnore.Contains(CurrentTarget.ActorSNO))) && // Only if within 15 foot of main target ((CurrentTarget.RadiusDistance <= 25f || AnythingWithinRange[RANGE_25] >= 1)) && (AnythingWithinRange[RANGE_50] >= 2 || CurrentTarget.HitPointsPct >= 0.30 || CurrentTarget.IsBossOrEliteRareUnique || PlayerStatus.CurrentHealthPct <= 0.60) && // Check for energy reservation amounts //((playerStatus.dCurrentEnergy >= 20 && !playerStatus.bWaitingForReserveEnergy) || playerStatus.dCurrentEnergy >= iWaitingReservedAmount) && PlayerStatus.PrimaryResource >= 10 && // If they have battle-rage, make sure it's up (!Hotbar.Contains(SNOPower.Barbarian_BattleRage) || (Hotbar.Contains(SNOPower.Barbarian_BattleRage) && GetHasBuff(SNOPower.Barbarian_BattleRage)))) { bool shouldGetNewZigZag = (DateTime.Now.Subtract(lastChangedZigZag).TotalMilliseconds >= 1200 || CurrentTarget.ACDGuid != iACDGUIDLastWhirlwind || vSideToSideTarget.Distance2D(PlayerStatus.CurrentPosition) <= 5f); vPositionLastZigZagCheck = PlayerStatus.CurrentPosition; if (shouldGetNewZigZag) { var wwdist = 25f; vSideToSideTarget = TargetUtil.GetZigZagTarget(CurrentTarget.Position, wwdist); LastPowerUsed = SNOPower.None; iACDGUIDLastWhirlwind = CurrentTarget.ACDGuid; lastChangedZigZag = DateTime.Now; } return(new TrinityPower(SNOPower.Barbarian_Whirlwind, 10f, vSideToSideTarget, CurrentWorldDynamicId, -1, 0, 1, NO_WAIT_ANIM)); } // Battle rage, constantly maintain if (!UseOOCBuff && Hotbar.Contains(SNOPower.Barbarian_BattleRage) && !PlayerStatus.IsIncapacitated && // Fury Dump Options for battle rage IF they don't have sprint ( (Settings.Combat.Barbarian.FuryDumpWOTB && PlayerStatus.PrimaryResourcePct >= 0.99 && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) || (Settings.Combat.Barbarian.FuryDumpAlways && PlayerStatus.PrimaryResourcePct >= 0.99) || !GetHasBuff(SNOPower.Barbarian_BattleRage) ) && PlayerStatus.PrimaryResource >= 20 && PowerManager.CanCast(SNOPower.Barbarian_BattleRage)) { return(new TrinityPower(SNOPower.Barbarian_BattleRage, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } // Hammer of the ancients spam-attacks - never use if waiting for special if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && !IsWaitingForSpecial && Hotbar.Contains(SNOPower.Barbarian_HammerOfTheAncients) && PlayerStatus.PrimaryResource >= 20) { //return new TrinityPower(SNOPower.Barbarian_HammerOfTheAncients, 12f, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, USE_SLOWLY); return(new TrinityPower(SNOPower.Barbarian_HammerOfTheAncients, 18f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // Weapon throw if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_WeaponThrow) && (PlayerStatus.PrimaryResource >= 10 && (CurrentTarget.RadiusDistance >= 5f || BarbHasNoPrimary()))) { return(new TrinityPower(SNOPower.Barbarian_WeaponThrow, 80f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Frenzy rapid-attacks if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Frenzy)) { return(new TrinityPower(SNOPower.Barbarian_Frenzy, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, NO_WAIT_ANIM)); } // Bash fast-attacks if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Bash)) { return(new TrinityPower(SNOPower.Barbarian_Bash, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } // Cleave fast-attacks if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Barbarian_Cleave)) { return(new TrinityPower(SNOPower.Barbarian_Cleave, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 2, WAIT_FOR_ANIM)); } // Default attacks if (!UseOOCBuff && !IsCurrentlyAvoiding) { return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }
private static TrinityPower GetMonkPower(bool IsCurrentlyAvoiding, bool UseOOCBuff, bool UseDestructiblePower) { if (UseDestructiblePower) { return(GetMonkDestroyPower()); } // Monks need 80 for special spam like tempest rushing MinEnergyReserve = 80; bool hasInfusedWithLight = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_BreathOfHeaven && s.RuneIndex == 3); bool hasFistsOfFury = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 0); // apply fists of fury DoT if we have Infused with Light buff + WotHF:FoF if (!UseOOCBuff && hasInfusedWithLight && hasFistsOfFury && GetHasBuff(SNOPower.Monk_BreathOfHeaven) && !CurrentTarget.HasDotDPS) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 14f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, NO_WAIT_ANIM)); } // 4 Mantras for the initial buff (slow-use) if (Hotbar.Contains(SNOPower.Monk_MantraOfEvasion) && !GetHasBuff(SNOPower.Monk_MantraOfEvasion) && PlayerStatus.PrimaryResource >= 50 && GilesUseTimer(SNOPower.Monk_MantraOfEvasion, true)) { return(new TrinityPower(SNOPower.Monk_MantraOfEvasion, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_MantraOfConviction) && !GetHasBuff(SNOPower.Monk_MantraOfConviction) && (PlayerStatus.PrimaryResource >= 50 && PlayerStatus.PrimaryResource >= 85) && GilesUseTimer(SNOPower.Monk_MantraOfConviction, true)) { return(new TrinityPower(SNOPower.Monk_MantraOfConviction, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_MantraOfHealing) && !GetHasBuff(SNOPower.Monk_MantraOfHealing) && PlayerStatus.PrimaryResource >= 50 && GilesUseTimer(SNOPower.Monk_MantraOfHealing, true)) { return(new TrinityPower(SNOPower.Monk_MantraOfHealing, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_MantraOfRetribution) && !GetHasBuff(SNOPower.Monk_MantraOfRetribution) && PlayerStatus.PrimaryResource >= 50 && GilesUseTimer(SNOPower.Monk_MantraOfRetribution, true)) { return(new TrinityPower(SNOPower.Monk_MantraOfRetribution, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM)); } // Mystic ally if (Hotbar.Contains(SNOPower.Monk_MysticAlly) && PlayerStatus.PrimaryResource >= 25 && iPlayerOwnedMysticAlly == 0 && GilesUseTimer(SNOPower.Monk_MysticAlly) && PowerManager.CanCast(SNOPower.Monk_MysticAlly)) { return(new TrinityPower(SNOPower.Monk_MysticAlly, 0f, vNullLocation, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // InnerSanctuary if (!UseOOCBuff && PlayerStatus.CurrentHealthPct <= 0.45 && Hotbar.Contains(SNOPower.Monk_InnerSanctuary) && GilesUseTimer(SNOPower.Monk_InnerSanctuary, true) && PlayerStatus.PrimaryResource >= 30 && PowerManager.CanCast(SNOPower.Monk_InnerSanctuary)) { return(new TrinityPower(SNOPower.Monk_InnerSanctuary, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Serenity if health is low if ((PlayerStatus.CurrentHealthPct <= 0.50 || (PlayerStatus.IsIncapacitated && PlayerStatus.CurrentHealthPct <= 0.90)) && Hotbar.Contains(SNOPower.Monk_Serenity) && GilesUseTimer(SNOPower.Monk_Serenity, true) && PlayerStatus.PrimaryResource >= 10 && PowerManager.CanCast(SNOPower.Monk_Serenity)) { return(new TrinityPower(SNOPower.Monk_Serenity, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Blinding Flash if (!UseOOCBuff && PlayerStatus.PrimaryResource >= 20 && Hotbar.Contains(SNOPower.Monk_BlindingFlash) && ( ElitesWithinRange[RANGE_15] >= 1 || PlayerStatus.CurrentHealthPct <= 0.4 || (AnythingWithinRange[RANGE_15] >= 3) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f) || // as pre-sweeping wind buff (AnythingWithinRange[RANGE_15] >= 1 && Hotbar.Contains(SNOPower.Monk_SweepingWind) && !GetHasBuff(SNOPower.Monk_SweepingWind) && Settings.Combat.Monk.HasInnaSet) ) && // Check if either we don't have sweeping winds, or we do and it's ready to cast in a moment (CheckAbilityAndBuff(SNOPower.Monk_SweepingWind) || (Hotbar.Contains(SNOPower.Monk_SweepingWind) && !GetHasBuff(SNOPower.Monk_SweepingWind) && (Settings.Combat.Monk.HasInnaSet ? PlayerStatus.PrimaryResource >= 15 : PlayerStatus.PrimaryResource >= 85)) || PlayerStatus.CurrentHealthPct <= 0.25) && GilesUseTimer(SNOPower.Monk_BlindingFlash) && PowerManager.CanCast(SNOPower.Monk_BlindingFlash)) { return(new TrinityPower(SNOPower.Monk_BlindingFlash, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM)); } // Blinding Flash as a DEFENSE if (!UseOOCBuff && PlayerStatus.PrimaryResource >= 10 && Hotbar.Contains(SNOPower.Monk_BlindingFlash) && PlayerStatus.CurrentHealthPct <= 0.25 && AnythingWithinRange[RANGE_15] >= 1 && GilesUseTimer(SNOPower.Monk_BlindingFlash) && PowerManager.CanCast(SNOPower.Monk_BlindingFlash)) { return(new TrinityPower(SNOPower.Monk_BlindingFlash, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 1, WAIT_FOR_ANIM)); } // Sweeping winds spam if ((PlayerStatus.PrimaryResource >= 75 || (Settings.Combat.Monk.HasInnaSet && PlayerStatus.PrimaryResource >= 5)) && GilesUseTimer(SNOPower.Monk_SweepingWind) && Hotbar.Contains(SNOPower.Monk_SweepingWind) && GetHasBuff(SNOPower.Monk_SweepingWind) && DateTime.Now.Subtract(SweepWindSpam).TotalMilliseconds >= 4000 && DateTime.Now.Subtract(SweepWindSpam).TotalMilliseconds <= 5400) { SweepWindSpam = DateTime.Now; return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, NO_WAIT_ANIM)); } // Sweeping wind if (!UseOOCBuff && Hotbar.Contains(SNOPower.Monk_SweepingWind) && !GetHasBuff(SNOPower.Monk_SweepingWind) && GilesUseTimer(SNOPower.Monk_SweepingWind) && (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_20] >= 3 || Settings.Combat.Monk.HasInnaSet || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 25f)) && // Check our mantras, if we have them, are up first Monk_HasMantraAbilityAndBuff() && // Check if either we don't have blinding flash, or we do and it's been cast in the last 8000ms (DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Monk_BlindingFlash]).TotalMilliseconds <= 8000 || CheckAbilityAndBuff(SNOPower.Monk_BlindingFlash) || ElitesWithinRange[RANGE_25] > 0 && DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Monk_BlindingFlash]).TotalMilliseconds <= 12500) && // Check the re-use timer and energy costs (PlayerStatus.PrimaryResource >= 75 || (Settings.Combat.Monk.HasInnaSet && PlayerStatus.PrimaryResource >= 5))) { SweepWindSpam = DateTime.Now; return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, vNullLocation, CurrentWorldDynamicId, -1, 0, 0, NO_WAIT_ANIM)); } //skillDict.Add("BreathOfHeaven", SNOPower.Monk_BreathOfHeaven); //runeDict.Add("CircleOfScorn", 0); //runeDict.Add("CircleOfLife", 1); //runeDict.Add("BlazingWrath", 2); //runeDict.Add("InfusedWithLight", 3); //runeDict.Add("PenitentFlame", 4); // Breath of Heaven when needing healing or the buff if (!UseOOCBuff && (PlayerStatus.CurrentHealthPct <= 0.6 || !GetHasBuff(SNOPower.Monk_BreathOfHeaven)) && Hotbar.Contains(SNOPower.Monk_BreathOfHeaven) && (PlayerStatus.PrimaryResource >= 35 || (!Hotbar.Contains(SNOPower.Monk_Serenity) && PlayerStatus.PrimaryResource >= 25)) && GilesUseTimer(SNOPower.Monk_BreathOfHeaven) && PowerManager.CanCast(SNOPower.Monk_BreathOfHeaven)) { return(new TrinityPower(SNOPower.Monk_BreathOfHeaven, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Breath of Heaven for spirit - Infused with Light if (!UseOOCBuff && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Monk_BreathOfHeaven) && !GetHasBuff(SNOPower.Monk_BreathOfHeaven) && hasInfusedWithLight && (TargetUtil.AnyMobsInRange(3, 20) || TargetUtil.IsEliteTargetInRange(20)) && PlayerStatus.PrimaryResourcePct < 0.75 && PowerManager.CanCast(SNOPower.Monk_BreathOfHeaven)) { return(new TrinityPower(SNOPower.Monk_BreathOfHeaven, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } // Seven Sided Strike if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] >= 1 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f) || PlayerStatus.CurrentHealthPct <= 0.55) && Hotbar.Contains(SNOPower.Monk_SevenSidedStrike) && ((PlayerStatus.PrimaryResource >= 50 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) && GilesUseTimer(SNOPower.Monk_SevenSidedStrike, true) && PowerManager.CanCast(SNOPower.Monk_SevenSidedStrike)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_SevenSidedStrike, 16f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 2, 3, WAIT_FOR_ANIM)); } // Exploding Palm if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_25] > 0 || AnythingWithinRange[RANGE_15] >= 3 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 14f)) && Hotbar.Contains(SNOPower.Monk_ExplodingPalm) && ((PlayerStatus.PrimaryResource >= 40 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) && GilesUseTimer(SNOPower.Monk_ExplodingPalm) && PowerManager.CanCast(SNOPower.Monk_ExplodingPalm)) { return(new TrinityPower(SNOPower.Monk_ExplodingPalm, 14f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } //SkillDict.Add("WaveOfLight", SNOPower.Monk_WaveOfLight); //RuneDict.Add("WallOfLight", 0); //RuneDict.Add("ExplosiveLight", 1); //RuneDict.Add("EmpoweredWave", 3); //RuneDict.Add("BlindingLight", 4); //RuneDict.Add("PillarOfTheAncients", 2); //bool hasEmpoweredWaveRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WaveOfLight && s.RuneIndex == 3); bool hasEmpoweredWaveRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WaveOfLight && s.RuneIndex == 3); var minWoLSpirit = hasEmpoweredWaveRune ? 40 : 75; // Wave of light if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Monk_WaveOfLight) && GilesUseTimer(SNOPower.Monk_WaveOfLight) && (TargetUtil.AnyMobsInRange(16f, Settings.Combat.Monk.MinWoLTrashCount) || TargetUtil.IsEliteTargetInRange(20f)) && (PlayerStatus.PrimaryResource >= minWoLSpirit && !IsWaitingForSpecial || PlayerStatus.PrimaryResource > MinEnergyReserve) && // optional check for SW stacks (Settings.Combat.Monk.SWBeforeWoL && (CheckAbilityAndBuff(SNOPower.Monk_SweepingWind) && GetBuffStacks(SNOPower.Monk_SweepingWind) == 3) || !Settings.Combat.Monk.SWBeforeWoL) && Monk_HasMantraAbilityAndBuff()) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 15f); Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_WaveOfLight, 16f, bestClusterPoint, -1, CurrentTarget.ACDGuid, 0, 1, WAIT_FOR_ANIM)); } //SkillDict.Add("CycloneStrike", SNOPower.Monk_CycloneStrike); //RuneDict.Add("EyeOfTheStorm", 3); //RuneDict.Add("Implosion", 1); //RuneDict.Add("Sunburst", 0); //RuneDict.Add("WallOfWind", 4); //RuneDict.Add("SoothingBreeze", 2); bool hasCycloneStikeEyeOfTheStorm = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CycloneStrike && s.RuneIndex == 3); bool hasCycloneStikeImposion = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CycloneStrike && s.RuneIndex == 1); var cycloneStrikeRange = hasCycloneStikeImposion ? 34f : 24f; var cycloneStrikeSpirit = hasCycloneStikeEyeOfTheStorm ? 30 : 50; // Cyclone Strike if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Monk_CycloneStrike) && GilesUseTimer(SNOPower.Monk_CycloneStrike) && ( TargetUtil.AnyElitesInRange(cycloneStrikeRange, 2) || TargetUtil.AnyMobsInRange(cycloneStrikeRange, Settings.Combat.Monk.MinCycloneTrashCount) || (CurrentTarget.RadiusDistance >= 15f && CurrentTarget.RadiusDistance <= cycloneStrikeRange) // pull the current target into attack range ) && ((PlayerStatus.PrimaryResource >= cycloneStrikeSpirit && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) && PowerManager.CanCast(SNOPower.Monk_CycloneStrike)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_CycloneStrike, 0f, vNullLocation, CurrentWorldDynamicId, -1, 2, 2, WAIT_FOR_ANIM)); } // For tempest rush re-use if (!UseOOCBuff && PlayerStatus.PrimaryResource >= 15 && DateTime.Now.Subtract(dictAbilityLastUse[SNOPower.Monk_TempestRush]).TotalMilliseconds <= 150 && ((Settings.Combat.Monk.TROption != TempestRushOption.MovementOnly) && !(Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && TargetUtil.AnyElitesInRange(40f)))) { GenerateMonkZigZag(); MaintainTempestRush = true; string trUse = "Continuing Tempest Rush for Combat"; Monk_TempestRushStatus(trUse); return(new TrinityPower(SNOPower.Monk_TempestRush, 23f, vSideToSideTarget, CurrentWorldDynamicId, -1, 0, 0, NO_WAIT_ANIM)); } // Tempest rush at elites or groups of mobs if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && !PlayerStatus.IsRooted && Hotbar.Contains(SNOPower.Monk_TempestRush) && ((PlayerStatus.PrimaryResource >= Settings.Combat.Monk.TR_MinSpirit && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve) && (Settings.Combat.Monk.TROption == TempestRushOption.Always || Settings.Combat.Monk.TROption == TempestRushOption.CombatOnly || (Settings.Combat.Monk.TROption == TempestRushOption.ElitesGroupsOnly && (TargetUtil.AnyElitesInRange(25) || TargetUtil.AnyMobsInRange(25, 2))) || (Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && !TargetUtil.AnyElitesInRange(90f) && TargetUtil.AnyMobsInRange(40f)))) { GenerateMonkZigZag(); MaintainTempestRush = true; string trUse = "Starting Tempest Rush for Combat"; Monk_TempestRushStatus(trUse); return(new TrinityPower(SNOPower.Monk_TempestRush, 23f, vSideToSideTarget, CurrentWorldDynamicId, -1, 0, 0, NO_WAIT_ANIM)); } // 4 Mantra spam for the 4 second buff if (!UseOOCBuff && !Settings.Combat.Monk.DisableMantraSpam && (!Hotbar.Contains(SNOPower.Monk_TempestRush) || PlayerStatus.PrimaryResource >= 98 || (PlayerStatus.CurrentHealthPct <= 0.55 && PlayerStatus.PrimaryResource >= 75) || CurrentTarget.IsBoss) && (PlayerStatus.PrimaryResource >= 135 || (GetHasBuff(SNOPower.Monk_SweepingWind) && (PlayerStatus.PrimaryResource >= 60 && PlayerStatus.PrimaryResource >= 110 || (PlayerStatus.PrimaryResource >= 100 && PlayerStatus.CurrentHealthPct >= 0.6) || (PlayerStatus.PrimaryResource >= 50 && PlayerStatus.CurrentHealthPct >= 0.6)) && // Checking we have no expensive finishers !Hotbar.Contains(SNOPower.Monk_SevenSidedStrike) && !Hotbar.Contains(SNOPower.Monk_LashingTailKick) && !Hotbar.Contains(SNOPower.Monk_WaveOfLight) && !Hotbar.Contains(SNOPower.Monk_CycloneStrike) && !Hotbar.Contains(SNOPower.Monk_ExplodingPalm))) && (ElitesWithinRange[RANGE_15] >= 1 || AnythingWithinRange[RANGE_15] >= 3 || (AnythingWithinRange[RANGE_15] >= 1 && (Settings.Combat.Monk.HasInnaSet && PlayerStatus.PrimaryResource >= 70)))) { if (Hotbar.Contains(SNOPower.Monk_MantraOfEvasion) && GilesUseTimer(SNOPower.Monk_MantraOfEvasion)) { return(new TrinityPower(SNOPower.Monk_MantraOfEvasion, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_MantraOfConviction) && GilesUseTimer(SNOPower.Monk_MantraOfConviction)) { return(new TrinityPower(SNOPower.Monk_MantraOfConviction, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_MantraOfRetribution) && GilesUseTimer(SNOPower.Monk_MantraOfRetribution)) { return(new TrinityPower(SNOPower.Monk_MantraOfRetribution, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_MantraOfHealing) && GilesUseTimer(SNOPower.Monk_MantraOfHealing)) { return(new TrinityPower(SNOPower.Monk_MantraOfHealing, 0f, vNullLocation, CurrentWorldDynamicId, -1, 1, 1, WAIT_FOR_ANIM)); } } // Lashing Tail Kick if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Monk_LashingTailKick) && !PlayerStatus.IsIncapacitated && (ElitesWithinRange[RANGE_15] > 0 || AnythingWithinRange[RANGE_15] > 4 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 10f)) && // Either doesn't have sweeping wind, or does but the buff is already up (!Hotbar.Contains(SNOPower.Monk_SweepingWind) || (Hotbar.Contains(SNOPower.Monk_SweepingWind) && GetHasBuff(SNOPower.Monk_SweepingWind))) && GilesUseTimer(SNOPower.Monk_LashingTailKick) && ((PlayerStatus.PrimaryResource >= 65 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_LashingTailKick, 10f, vNullLocation, -1, CurrentTarget.ACDGuid, 1, 1, WAIT_FOR_ANIM)); } //skillDict.Add("DashingStrike", SNOPower.Monk_DashingStrike); //runeDict.Add("WayOfTheFallingStar", 1); //runeDict.Add("FlyingSideKick", 4); //runeDict.Add("Quicksilver", 3); //runeDict.Add("SoaringSkull", 0); //runeDict.Add("BlindingSpeed", 2); bool hasWayOfTheFallingStar = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DashingStrike && s.RuneIndex == 1); bool hasQuicksilver = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DashingStrike && s.RuneIndex == 3); var dashingStrikeSpirit = hasQuicksilver ? 10 : 25; // Dashing Strike, quick move to target out of range if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && CurrentTarget.CentreDistance >= 16f && Hotbar.Contains(SNOPower.Monk_DashingStrike) && ((PlayerStatus.PrimaryResource >= dashingStrikeSpirit && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_DashingStrike, Monk_MaxDashingStrikeRange, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, WAIT_FOR_ANIM)); } // Dashing strike + way of the fallen Star if (!UseOOCBuff && !IsCurrentlyAvoiding && !PlayerStatus.IsIncapacitated && Hotbar.Contains(SNOPower.Monk_DashingStrike) && (TimeSinceUse(SNOPower.Monk_DashingStrike) >= 2800) && hasWayOfTheFallingStar && ((PlayerStatus.PrimaryResource >= 25 && !PlayerStatus.WaitingForReserveEnergy) || PlayerStatus.PrimaryResource >= MinEnergyReserve)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_DashingStrike, Monk_MaxDashingStrikeRange, vNullLocation, -1, CurrentTarget.ACDGuid, 2, 2, WAIT_FOR_ANIM)); } // Fists of thunder as the primary, repeatable attack if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Monk_FistsofThunder) && (DateTime.Now.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.Now.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach) || CurrentTarget.RadiusDistance > 12f || AnythingWithinRange[RANGE_50] < 5 && ElitesWithinRange[RANGE_50] <= 0 && !WantToSwap)) { if (DateTime.Now.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700) { OtherThanDeadlyReach = DateTime.Now; } Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_FistsofThunder, 30f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, NO_WAIT_ANIM)); } // Crippling wave if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Monk_CripplingWave) && (DateTime.Now.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.Now.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach) || AnythingWithinRange[RANGE_50] < 5 && ElitesWithinRange[RANGE_50] <= 0 && !WantToSwap)) { OtherThanDeadlyReach = DateTime.Now; Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_CripplingWave, 14f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, NO_WAIT_ANIM)); } // Way of hundred fists if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Monk_WayOfTheHundredFists) && (DateTime.Now.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.Now.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach) || AnythingWithinRange[RANGE_50] < 5 && ElitesWithinRange[RANGE_50] <= 0 && !WantToSwap)) { OtherThanDeadlyReach = DateTime.Now; Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 14f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, NO_WAIT_ANIM)); } // Deadly reach if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.Monk_DeadlyReach)) { if (DateTime.Now.Subtract(ForeSightFirstHit).TotalMilliseconds > 29000) { ForeSightFirstHit = DateTime.Now; } else if (DateTime.Now.Subtract(ForeSight2).TotalMilliseconds > 400 && DateTime.Now.Subtract(ForeSightFirstHit).TotalMilliseconds > 1400) { OtherThanDeadlyReach = DateTime.Now; } if (DateTime.Now.Subtract(ForeSight2).TotalMilliseconds > 2800) { ForeSight2 = DateTime.Now; } Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_DeadlyReach, 16f, vNullLocation, -1, CurrentTarget.ACDGuid, 0, 1, NO_WAIT_ANIM)); } // Default attacks if (!UseOOCBuff && !IsCurrentlyAvoiding) { Monk_TickSweepingWindSpam(); return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, -1, CurrentTarget.ACDGuid, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.None, -1, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }
/// <summary> /// This will find a safe place to stand in both Kiting and Avoidance situations /// </summary> /// <param name="isStuck"></param> /// <param name="stuckAttempts"></param> /// <param name="dangerPoint"></param> /// <param name="shouldKite"></param> /// <param name="avoidDeath"></param> /// <returns></returns> internal static Vector3 FindSafeZone(bool isStuck, int stuckAttempts, Vector3 dangerPoint, bool shouldKite = false, IEnumerable <TrinityCacheObject> monsterList = null, bool avoidDeath = false) { // Handle The Butcher's Lair var butcherFloorPanels = CacheData.TimeBoundAvoidance.Where(aoe => DataDictionary.ButcherFloorPanels.Contains(aoe.ActorSNO)).ToList(); if (butcherFloorPanels.Any()) { foreach (var safePoint in DataDictionary.ButcherPanelPositions.OrderBy(p => p.Value.Distance2DSqr(Trinity.Player.Position))) { // Floor panel with fire animation was added to cache if (butcherFloorPanels.Any(p => p.ActorSNO == safePoint.Key && p.Position.Distance2DSqr(safePoint.Value) <= 15f * 15f)) { continue; } // floor panel position is in Butcher animation avoidance (charging, chain hook) if (CacheData.TimeBoundAvoidance.Any(aoe => aoe.Position.Distance2D(safePoint.Value) < aoe.Radius)) { continue; } // no avoidance object in cache, this point is safe return(safePoint.Value); } // Don't fall back to regular avoidance return(Vector3.Zero); } if (!isStuck) { if (shouldKite && DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 1500 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } if (DateTime.UtcNow.Subtract(lastFoundSafeSpot).TotalMilliseconds <= 800 && lastSafeZonePosition != Vector3.Zero) { return(lastSafeZonePosition); } hasEmergencyTeleportUp = (!Player.IsIncapacitated && ( // Leap is available (CombatBase.CanCast(SNOPower.Barbarian_Leap)) || // Whirlwind is available (CombatBase.CanCast(SNOPower.Barbarian_Whirlwind) && ((Player.PrimaryResource >= 10 && !CombatBase.IsWaitingForSpecial) || Player.PrimaryResource >= Trinity.MinEnergyReserve)) || // Tempest rush is available (CombatBase.CanCast(SNOPower.Monk_TempestRush) && ((Player.PrimaryResource >= 20 && !CombatBase.IsWaitingForSpecial) || Player.PrimaryResource >= Trinity.MinEnergyReserve)) || // Teleport is available (CombatBase.CanCast(SNOPower.Wizard_Teleport) && Player.PrimaryResource >= 15) || // Archon Teleport is available (CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport)) )); // Wizards can look for bee stings in range and try a wave of force to dispel them if (!shouldKite && Player.ActorClass == ActorClass.Wizard && CombatBase.CanCast(SNOPower.Wizard_WaveOfForce) && !Player.IsIncapacitated && CacheData.TimeBoundAvoidance.Count(u => u.ActorSNO == 5212 && u.Position.Distance(Player.Position) <= 15f) >= 2 && ( //HotbarSkills.PassiveSkills.Contains(SNOPower.Wizard_Passive_CriticalMass) || PowerManager.CanCast(SNOPower.Wizard_WaveOfForce))) { ZetaDia.Me.UsePower(SNOPower.Wizard_WaveOfForce, Vector3.Zero, Player.WorldDynamicID, -1); } } float highestWeight = 0f; if (monsterList == null) { monsterList = new List <TrinityCacheObject>(); } //Vector3 vBestLocation = FindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath); Vector3 vBestLocation = MainFindSafeZone(dangerPoint, shouldKite, isStuck, monsterList, avoidDeath); highestWeight = 1; // Loop through distance-range steps if (highestWeight <= 0) { return(vBestLocation); } lastFoundSafeSpot = DateTime.UtcNow; lastSafeZonePosition = vBestLocation; return(vBestLocation); }
private static TrinityPower GetWizardPower(bool isCurrentlyAvoiding, bool useOocBuff, bool useDestructiblePower) { // Pick the best destructible power available if (useDestructiblePower) { if (!GetHasBuff(SNOPower.Wizard_Archon)) { return(GetWizardDestructablePower()); } if (CurrentTarget.RadiusDistance <= 10f) { return(new TrinityPower(SNOPower.Wizard_Archon_ArcaneStrike, 20f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } return(new TrinityPower(SNOPower.Wizard_Archon_DisintegrationWave, 19f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } // Wizards want to save up to a reserve of 65+ energy MinEnergyReserve = 45; if (!GetHasBuff(SNOPower.Wizard_Archon)) { bool hasIllusionist = HotbarSkills.PassiveSkills.Any(p => p == SNOPower.Wizard_Passive_Illusionist); // Illusionist speed boost if (hasIllusionist && useOocBuff) { // Slow Time on self for speed boost if (CombatBase.CanCast(SNOPower.Wizard_SlowTime)) { return(new TrinityPower(SNOPower.Wizard_SlowTime)); } // Mirror Image for speed boost if (CombatBase.CanCast(SNOPower.Wizard_MirrorImage)) { return(new TrinityPower(SNOPower.Wizard_MirrorImage)); } // Teleport already called from PlayerMover, not here (since it's a "movement" spell, not a buff) } bool hasCalamity = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 0); // Offensive Teleport: Calamity if (CombatBase.CanCast(SNOPower.Wizard_Teleport) && hasCalamity) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(); return(new TrinityPower(SNOPower.Wizard_Teleport, 55f, bestClusterPoint)); } bool hasSafePassage = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Teleport && s.RuneIndex == 1); // Defensive Teleport: SafePassage if (CombatBase.CanCast(SNOPower.Wizard_Teleport) && hasSafePassage && TimeSinceUse(SNOPower.Wizard_Teleport) >= 5000 && Player.CurrentHealthPct <= 0.75 && (CurrentTarget.IsBossOrEliteRareUnique || TargetUtil.IsEliteTargetInRange(30f))) { return(new TrinityPower(SNOPower.Wizard_Teleport, 1f, Player.Position)); } // Black Hole experiment //spell steal //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/3/HotbarSlot1 None/-1/HotbarSlot2 //blazar //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/2/HotbarSlot1 None/-1/HotbarSlot2 //event horizon //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/1/HotbarSlot1 None/-1/HotbarSlot2 //absolute zero //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/4/HotbarSlot1 None/-1/HotbarSlot2 //super massive //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/0/HotbarSlot1 None/-1/HotbarSlot2 //no rune //[Trinity] Hotbar Skills (Skill/RuneIndex/Slot): Weapon_Ranged_Wand/-1/HotbarMouseLeft X1_Wizard_Wormhole/-1/HotbarSlot1 None/-1/HotbarSlot2 bool hasSupermassive = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_Wizard_Wormhole && s.RuneIndex == 0); float blackholeRadius = hasSupermassive ? 20f : 15f; if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.X1_Wizard_Wormhole, CombatBase.CanCastFlags.NoTimer) && TargetUtil.ClusterExists(blackholeRadius, 45f, 4)) { return(new TrinityPower(SNOPower.X1_Wizard_Wormhole, 45f, TargetUtil.GetBestClusterUnit(blackholeRadius, 45f, 1, false).Position)); } bool arcaneDynamoPassiveReady = (HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Wizard_Passive_ArcaneDynamo) && GetBuffStacks(SNOPower.Wizard_Passive_ArcaneDynamo) == 5); var bestMeteorClusterUnit = TargetUtil.GetBestClusterUnit(); // Meteor: Arcane Dynamo if (!useOocBuff && !Player.IsIncapacitated && !arcaneDynamoPassiveReady && CombatBase.CanCast(SNOPower.Wizard_Meteor, CombatBase.CanCastFlags.NoTimer) && (TargetUtil.EliteOrTrashInRange(65) || TargetUtil.ClusterExists(15f, 65, 2))) { return(new TrinityPower(SNOPower.Wizard_Meteor, 65f, bestMeteorClusterUnit.Position)); } // Diamond Skin SPAM if (!useOocBuff && CombatBase.CanCast(SNOPower.Wizard_DiamondSkin) && LastPowerUsed != SNOPower.Wizard_DiamondSkin && !GetHasBuff(SNOPower.Wizard_DiamondSkin) && (TargetUtil.AnyElitesInRange(25, 1) || TargetUtil.AnyMobsInRange(25, 1) || Player.CurrentHealthPct <= 0.90 || Player.IsIncapacitated || Player.IsRooted || CurrentTarget.RadiusDistance <= 40f)) { return(new TrinityPower(SNOPower.Wizard_DiamondSkin)); } // Diamond Skin off CD bool hasSleekShell = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_DiamondSkin && s.RuneIndex == 0); if (hasSleekShell && CombatBase.CanCast(SNOPower.Wizard_DiamondSkin)) { return(new TrinityPower(SNOPower.Wizard_DiamondSkin)); } // Slow Time for in combat if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_SlowTime, CombatBase.CanCastFlags.NoTimer) && (TargetUtil.AnyElitesInRange(25, 1) || TargetUtil.AnyMobsInRange(25, 2) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 40f)) && (CombatBase.TimeSpanSincePowerUse(SNOPower.Wizard_SlowTime) > TimeSpan.FromSeconds(15) || SpellHistory.DistanceFromLastTarget(SNOPower.Wizard_SlowTime) > 30f)) { if (TargetUtil.AnyMobsInRange(20f)) { return(new TrinityPower(SNOPower.Wizard_SlowTime)); // cast of Self } return(new TrinityPower(SNOPower.Wizard_SlowTime, 55f, TargetUtil.GetBestClusterUnit(20f).Position)); } // Mirror Image @ half health or 5+ monsters or rooted/incapacitated or last elite left @25% health if (!useOocBuff && CombatBase.CanCast(SNOPower.Wizard_MirrorImage, CombatBase.CanCastFlags.NoTimer) && (Player.CurrentHealthPct <= PlayerEmergencyHealthPotionLimit || TargetUtil.AnyMobsInRange(30, 4) || Player.IsIncapacitated || Player.IsRooted || TargetUtil.AnyElitesInRange(30) || CurrentTarget.IsBossOrEliteRareUnique)) { return(new TrinityPower(SNOPower.Wizard_MirrorImage, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Familiar if (!Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Familiar) && Player.PrimaryResource >= 20 && !Wizard_HasFamiliar()) { return(new TrinityPower(SNOPower.Wizard_Familiar, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2)); } // The three wizard armors, done in an else-if loop so it doesn't keep replacing one with the other if (!Player.IsIncapacitated && Player.PrimaryResource >= 25) { // Energy armor as priority cast if available and not buffed if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor)) { if ((!GetHasBuff(SNOPower.Wizard_EnergyArmor) && PowerManager.CanCast(SNOPower.Wizard_EnergyArmor)) || (Hotbar.Contains(SNOPower.Wizard_Archon) && (!GetHasBuff(SNOPower.Wizard_EnergyArmor) || SNOPowerUseTimer(SNOPower.Wizard_EnergyArmor)))) { return(new TrinityPower(SNOPower.Wizard_EnergyArmor, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2)); } } // Ice Armor else if (Hotbar.Contains(SNOPower.Wizard_IceArmor)) { if (!GetHasBuff(SNOPower.Wizard_IceArmor) && PowerManager.CanCast(SNOPower.Wizard_IceArmor)) { return(new TrinityPower(SNOPower.Wizard_IceArmor, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2)); } } // Storm Armor else if (Hotbar.Contains(SNOPower.Wizard_StormArmor)) { if (!GetHasBuff(SNOPower.Wizard_StormArmor) && PowerManager.CanCast(SNOPower.Wizard_StormArmor)) { return(new TrinityPower(SNOPower.Wizard_StormArmor, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2)); } } } // Magic Weapon (10 minutes) if (!Player.IsIncapacitated && Player.PrimaryResource >= 25 && CombatBase.CanCast(SNOPower.Wizard_MagicWeapon) && !GetHasBuff(SNOPower.Wizard_MagicWeapon)) { return(new TrinityPower(SNOPower.Wizard_MagicWeapon, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 2)); } // Hydra if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Hydra, CombatBase.CanCastFlags.NoTimer) && (CombatBase.TimeSpanSincePowerUse(SNOPower.Wizard_Hydra) > TimeSpan.FromSeconds(15) && SpellHistory.DistanceFromLastTarget(SNOPower.Wizard_Hydra) > 30f) && //LastPowerUsed != SNOPower.Wizard_Hydra && (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 4) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f)) && Player.PrimaryResource >= 15) { // For distant monsters, try to target a little bit in-front of them (as they run towards us), if it's not a treasure goblin float fExtraDistance = 0f; if (CurrentTarget.Distance > 17f && !CurrentTarget.IsTreasureGoblin) { fExtraDistance = CurrentTarget.Distance - 17f; if (fExtraDistance > 5f) { fExtraDistance = 5f; } if (CurrentTarget.Distance - fExtraDistance < 15f) { fExtraDistance -= 2; } } Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, CurrentTarget.Distance - fExtraDistance); return(new TrinityPower(SNOPower.Wizard_Hydra, 30f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2)); } // Archon if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_Archon, CombatBase.CanCastFlags.NoTimer) && Wizard_ShouldStartArchon()) { //CanCastArchon = false; //return new TrinityPower(SNOPower.Wizard_Archon, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 4, 5); // Familiar has been removed for now. Uncomment the three comments below relating to familiars to force re-buffing them int reserveArcanePower = 0; if (Hotbar.Contains(SNOPower.Wizard_MagicWeapon)) { reserveArcanePower += 25; } if (Hotbar.Contains(SNOPower.Wizard_Familiar)) { reserveArcanePower += 25; } if (Hotbar.Contains(SNOPower.Wizard_EnergyArmor) || Hotbar.Contains(SNOPower.Wizard_IceArmor) || Hotbar.Contains(SNOPower.Wizard_StormArmor)) { reserveArcanePower += 25; } bool hasBuffSpells = (Hotbar.Contains(SNOPower.Wizard_MagicWeapon) || Hotbar.Contains(SNOPower.Wizard_Familiar) || Hotbar.Contains(SNOPower.Wizard_EnergyArmor) || Hotbar.Contains(SNOPower.Wizard_IceArmor) || Hotbar.Contains(SNOPower.Wizard_StormArmor)); CanCastArchon = //Player.PrimaryResource >= reserveArcanePower || ( //hasBuffSpells && CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) && (!Hotbar.Contains(SNOPower.Wizard_Familiar) || Wizard_HasFamiliar()) && CheckAbilityAndBuff(SNOPower.Wizard_EnergyArmor) && CheckAbilityAndBuff(SNOPower.Wizard_IceArmor) && CheckAbilityAndBuff(SNOPower.Wizard_StormArmor)); if (CanCastArchon) { Player.WaitingForReserveEnergy = false; CanCastArchon = false; ShouldRefreshHotbarAbilities = true; return(new TrinityPower(SNOPower.Wizard_Archon, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 4, 5)); } Player.WaitingForReserveEnergy = true; } // Explosive Blast if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_ExplosiveBlast, CombatBase.CanCastFlags.NoTimer) && Player.PrimaryResource >= 20) { return(new TrinityPower(SNOPower.Wizard_ExplosiveBlast, 12f, CurrentTarget.Position)); } //SkillDict.Add("Blizzard", SNOPower.Wizard_Blizzard); //RuneDict.Add("GraspingChill", 2); //RuneDict.Add("FrozenSolid", 4); //RuneDict.Add("Snowbound", 3); //RuneDict.Add("StarkWinter", 1); //RuneDict.Add("UnrelentingStorm", 0); bool hasSnowBoundRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Blizzard && s.RuneIndex == 3); // Blizzard if (!useOocBuff && !Player.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_Blizzard) && (TargetUtil.ClusterExists(18f, 90f, 2, false) || TargetUtil.AnyElitesInRange(40f) || TargetUtil.IsEliteTargetInRange(45f)) && (Player.PrimaryResource >= 40 || (hasSnowBoundRune && Player.PrimaryResource >= 20)) && SNOPowerUseTimer(SNOPower.Wizard_Blizzard)) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(18f, 45f, false); return(new TrinityPower(SNOPower.Wizard_Blizzard, 45f, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1)); } bool hasArcaneDynamo = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Wizard_Passive_ArcaneDynamo); // Meteor - no arcane dynamo if (!useOocBuff && !Player.IsIncapacitated && !hasArcaneDynamo && CombatBase.CanCast(SNOPower.Wizard_Meteor, CombatBase.CanCastFlags.NoTimer) && (TargetUtil.EliteOrTrashInRange(65) || TargetUtil.ClusterExists(15f, 65, 2))) { return(new TrinityPower(SNOPower.Wizard_Meteor, 65f, bestMeteorClusterUnit.Position)); } //SkillDict.Add("FrostNova", SNOPower.Wizard_FrostNova); //RuneDict.Add("Shatter", 1); //RuneDict.Add("ColdSnap", 3); //RuneDict.Add("FrozenMist", 2); //RuneDict.Add("DeepFreeze", 4); //RuneDict.Add("BoneChill", 0); bool hasDeepFreeze = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_FrostNova && s.RuneIndex == 4); // Frost Nova if (!useOocBuff && CombatBase.CanCast(SNOPower.Wizard_FrostNova) && !Player.IsIncapacitated && ((hasDeepFreeze && TargetUtil.AnyMobsInRange(25, 5)) || (!hasDeepFreeze && (TargetUtil.AnyMobsInRange(25, 1) || Player.CurrentHealthPct <= 0.7)) && CurrentTarget.RadiusDistance <= 25f)) { return(new TrinityPower(SNOPower.Wizard_FrostNova, 20f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 2)); } // Check to see if we have a signature spell on our hotbar, for energy twister check bool bHasSignatureSpell = (Hotbar.Contains(SNOPower.Wizard_MagicMissile) || Hotbar.Contains(SNOPower.Wizard_ShockPulse) || Hotbar.Contains(SNOPower.Wizard_SpectralBlade) || Hotbar.Contains(SNOPower.Wizard_Electrocute)); //SkillDict.Add("EnergyTwister", SNOPower.Wizard_EnergyTwister); //RuneDict.Add("MistralBreeze", 3); //RuneDict.Add("GaleForce", 0); //RuneDict.Add("RagingStorm", 1); //RuneDict.Add("WickedWind", 4); //RuneDict.Add("StromChaser", 2); bool hasWickedWindRune = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_EnergyTwister && s.RuneIndex == 4); // Energy Twister SPAMS whenever 35 or more ap to generate Arcane Power if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_EnergyTwister) && Player.PrimaryResource >= 35 && // If using storm chaser, then force a signature spell every 1 stack of the buff, if we have a signature spell (!bHasSignatureSpell || GetBuffStacks(SNOPower.Wizard_EnergyTwister) < 1) && ((!hasWickedWindRune && CurrentTarget.RadiusDistance <= 25f) || (hasWickedWindRune && CurrentTarget.RadiusDistance <= 60f)) && (!Hotbar.Contains(SNOPower.Wizard_Electrocute) || !DataDictionary.FastMovingMonsterIds.Contains(CurrentTarget.ActorSNO))) { Vector3 bestClusterPoint = TargetUtil.GetBestClusterPoint(10f, 15f); const float twisterRange = 28f; return(new TrinityPower(SNOPower.Wizard_EnergyTwister, twisterRange, bestClusterPoint, CurrentWorldDynamicId, -1, 0, 0)); } // Wave of force if (!useOocBuff && !Player.IsIncapacitated && !isCurrentlyAvoiding && Player.PrimaryResource >= 25 && CombatBase.CanCast(SNOPower.Wizard_WaveOfForce, CombatBase.CanCastFlags.NoTimer)) { return(new TrinityPower(SNOPower.Wizard_WaveOfForce, 5f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 1, 2)); } bool hasEntropy = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_Disintegrate && s.RuneIndex == 2); float disintegrateRange = hasEntropy ? 10f : 35f; // Disintegrate if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Disintegrate) && ((Player.PrimaryResource >= 20 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve)) { return(new TrinityPower(SNOPower.Wizard_Disintegrate, disintegrateRange, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } // Arcane Orb if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_ArcaneOrb) && ((Player.PrimaryResource >= 30 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve)) { return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, 35f, CurrentTarget.ACDGuid)); } // Arcane Torrent if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_ArcaneTorrent) && ((Player.PrimaryResource >= 16 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve)) { return(new TrinityPower(SNOPower.Wizard_ArcaneTorrent, 40f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } //skillDict.Add("RayOfFrost", SNOPower.Wizard_RayOfFrost); //runeDict.Add("Numb", 2); //runeDict.Add("SnowBlast", 0); //runeDict.Add("ColdBlood", 3); //runeDict.Add("SleetStorm", 1); //runeDict.Add("BlackIce", 4); bool hasSleetStorm = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_RayOfFrost && s.RuneIndex == 1); // Ray of Frost if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && Hotbar.Contains(SNOPower.Wizard_RayOfFrost) && Player.PrimaryResource >= 12 && !Player.WaitingForReserveEnergy) { float range = 50f; if (hasSleetStorm) { range = 5f; } return(new TrinityPower(SNOPower.Wizard_RayOfFrost, range, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1)); } bool hasConflagrate = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Wizard_MagicMissile && s.RuneIndex == 2); // Magic Missile if (!useOocBuff && !isCurrentlyAvoiding && Hotbar.Contains(SNOPower.Wizard_MagicMissile)) { var bestPierceTarget = TargetUtil.GetBestPierceTarget(45f); int targetId; if (bestPierceTarget != null) { targetId = hasConflagrate ? bestPierceTarget.ACDGuid : CurrentTarget.ACDGuid; } else { targetId = CurrentTarget.ACDGuid; } return(new TrinityPower(SNOPower.Wizard_MagicMissile, 45f, targetId)); } // Shock Pulse if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_ShockPulse)) { return(new TrinityPower(SNOPower.Wizard_ShockPulse, 15f, CurrentTarget.ACDGuid)); } // Spectral Blade if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_SpectralBlade)) { return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 14f, CurrentTarget.ACDGuid)); } // Electrocute if (!useOocBuff && !isCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Wizard_Electrocute)) { return(new TrinityPower(SNOPower.Wizard_Electrocute, 40f, CurrentTarget.ACDGuid)); } // Default attacks return(CombatBase.DefaultPower); } else { bool cancelArchon = false; string reason = ""; if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffArmor && !Wizard_HasWizardArmor()) { reason += "Rebuff Armor "; cancelArchon = true; } if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.RebuffMagicWeaponFamiliar && (!CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) || !Wizard_HasFamiliar())) { if (!CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon)) { reason += "Rebuff Magic Weapon "; } if (!Wizard_HasFamiliar()) { reason += "Rebuff Familiar "; } cancelArchon = true; } if (Settings.Combat.Wizard.ArchonCancelOption == WizardArchonCancelOption.Timer && DateTime.UtcNow.Subtract(CacheData.AbilityLastUsed[SNOPower.Wizard_Archon]).TotalSeconds >= Settings.Combat.Wizard.ArchonCancelSeconds) { reason += "Timer"; cancelArchon = true; } if (cancelArchon && Wizard_ShouldStartArchon()) { var archonBuff = ZetaDia.Me.GetBuff(SNOPower.Wizard_Archon); if (archonBuff != null && archonBuff.IsCancelable) { Logger.Log(TrinityLogLevel.Debug, LogCategory.Behavior, "Canceling Archon: {0}", reason); // this actually cancels Archon archonBuff.Cancel(); // this SNOPower is fake - it isn't actually used, we're just putting it here to force a BehaviorTree return/recheck return(new TrinityPower(SNOPower.Wizard_Archon_Cancel, 0f, Vector3.Zero, -1, -1, -1, -1)); } } // Archon form // Archon Slow Time for in combat if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_SlowTime, CombatBase.CanCastFlags.NoTimer) && (CombatBase.TimeSpanSincePowerUse(SNOPower.Wizard_Archon_SlowTime) > TimeSpan.FromSeconds(30))) { return(new TrinityPower(SNOPower.Wizard_Archon_SlowTime, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Archon Teleport in combat for kiting if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport, CombatBase.CanCastFlags.NoTimer) && Settings.Combat.Wizard.KiteLimit > 0 && // Try and teleport-retreat from 1 elite or 3+ greys or a boss at 15 foot range (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 3) || (CurrentTarget.IsBoss && CurrentTarget.RadiusDistance <= 15f))) { Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, -20f); return(new TrinityPower(SNOPower.Wizard_Archon_Teleport, 35f, vNewTarget)); } // Archon teleport in combat for no-kite if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_Teleport, CombatBase.CanCastFlags.NoTimer) && Settings.Combat.Wizard.KiteLimit == 0 && CurrentTarget.RadiusDistance >= 10f) { return(new TrinityPower(SNOPower.Wizard_Archon_Teleport, 35f, CurrentTarget.Position)); } //392694, 392695, 392696 == Arcane Strike, //392697, 392699, 392698 == Disintegration Wave //392692, 392693, 392691 == Arcane Blast, Ice Blast SNOPower beamPower = SNOPower.Wizard_Archon_ArcaneBlast, strikePower = SNOPower.Wizard_Archon_ArcaneStrike, blastPower = SNOPower.Wizard_Archon_DisintegrationWave; HotbarSkills beamSkill = HotbarSkills.AssignedSkills .FirstOrDefault(p => p.Power == SNOPower.Wizard_Archon_DisintegrationWave || p.Power == (SNOPower)392697 || p.Power == (SNOPower)392699 || p.Power == (SNOPower)392698); HotbarSkills strikeSkill = HotbarSkills.AssignedSkills .FirstOrDefault(p => p.Power == SNOPower.Wizard_Archon_ArcaneStrike || p.Power == (SNOPower)392694 || p.Power == (SNOPower)392695 || p.Power == (SNOPower)392696); HotbarSkills blastSkill = HotbarSkills.AssignedSkills .FirstOrDefault(p => p.Power == SNOPower.Wizard_Archon_ArcaneBlast || p.Power == (SNOPower)392692 || p.Power == (SNOPower)392693 || p.Power == (SNOPower)392691); if (beamSkill != null && beamSkill.Power != default(SNOPower)) { beamPower = beamSkill.Power; } if (strikeSkill != null && strikeSkill.Power != default(SNOPower)) { strikePower = strikeSkill.Power; } if (blastSkill != null && blastSkill.Power != default(SNOPower)) { blastPower = blastSkill.Power; } // Arcane Blast - 2 second cooldown, big AoE if (!useOocBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Wizard_Archon_ArcaneBlast, CombatBase.CanCastFlags.NoTimer) && TargetUtil.AnyMobsInRange(15, 1) && CurrentTarget.IsFacingPlayer) { return(new TrinityPower(blastPower, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Disintegrate if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated && (CurrentTarget.CountUnitsBehind(25f) > 2 || Settings.Combat.Wizard.NoArcaneStrike || Settings.Combat.Wizard.KiteLimit > 0)) { return(new TrinityPower(beamPower, 49f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } // Arcane Strike Rapid Spam at close-range only, and no AoE inbetween us and target if (!useOocBuff && !Player.IsIncapacitated && !Settings.Combat.Wizard.NoArcaneStrike && !CacheData.TimeBoundAvoidance.Any(aoe => MathUtil.IntersectsPath(aoe.Position, aoe.Radius, Player.Position, CurrentTarget.Position))) { return(new TrinityPower(strikePower, 7f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1)); } // Disintegrate as final option just in case if (!useOocBuff && !isCurrentlyAvoiding && !Player.IsIncapacitated) { return(new TrinityPower(beamPower, 49f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } return(new TrinityPower(SNOPower.None, -1, Vector3.Zero, -1, -1, 0, 0)); } }
public override void Initialize() { base.Test = (ref CacheObject obj) => { if (obj == null) { // See if we should wait for milliseconds for possible loot drops before continuing run if (DateTime.Now.Subtract(FunkyGame.Targeting.Cache.lastHadUnitInSights).TotalMilliseconds <= FunkyBaseExtension.Settings.General.AfterCombatDelay && DateTime.Now.Subtract(FunkyGame.Targeting.Cache.lastHadEliteUnitInSights).TotalMilliseconds <= 10000 || //Cut the delay time in half for non-elite monsters! DateTime.Now.Subtract(FunkyGame.Targeting.Cache.lastHadUnitInSights).TotalMilliseconds <= FunkyBaseExtension.Settings.General.AfterCombatDelay) { obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "WaitForLootDrops", 2f, -1); return(true); } //Herbfunks wait after loot containers are opened. 3s for rare chests, half the settings delay for everything else. if ((DateTime.Now.Subtract(FunkyGame.Targeting.Cache.lastHadRareChestAsTarget).TotalMilliseconds <= 3750) || (DateTime.Now.Subtract(FunkyGame.Targeting.Cache.lastHadContainerAsTarget).TotalMilliseconds <= (FunkyBaseExtension.Settings.General.AfterCombatDelay * 1.25))) { obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "ContainerLootDropsWait", 2f, -1); return(true); } if (DateTime.Now.Subtract(FunkyGame.Targeting.Cache.lastSeenCursedShrine).TotalMilliseconds <= (1000)) { if (FunkyGame.AdventureMode && SettingAdventureMode.AdventureModeSettingsTag.AllowCombatModifications && FunkyGame.Bounty.CurrentBountyCacheEntry != null && FunkyGame.Bounty.CurrentBountyCacheEntry.Type == BountyTypes.CursedEvent) { Logger.DBLog.Info("[Funky] Cursed Object Found During Cursed Bounty -- Enabling LOS movement for all Units!"); SettingLOSMovement.LOSSettingsTag.MiniumRangeObjects = 10; SettingLOSMovement.LOSSettingsTag.MaximumRange = 125; FunkyGame.Game.AllowAnyUnitForLOSMovement = true; SettingCluster.ClusterSettingsTag = SettingCluster.DisabledClustering; } obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "CursedShrineWait", 2f, -1); return(true); } if (DateTime.Now.Subtract(FunkyGame.Targeting.Cache.lastHadSwitchAsTarget).TotalMilliseconds <= (4000)) { obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "SwitchWait", 2f, -1); return(true); } // Finally, a special check for waiting for wrath of the berserker cooldown before engaging Azmodan if (Hotbar.HasPower(SNOPower.Barbarian_WrathOfTheBerserker) && FunkyBaseExtension.Settings.Barbarian.bWaitForWrath && !FunkyGame.Hero.Class.Abilities[SNOPower.Barbarian_WrathOfTheBerserker].AbilityUseTimer() && FunkyGame.Hero.CurrentWorldDynamicID == 121214 && (Vector3.Distance(FunkyGame.Hero.Position, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(FunkyGame.Hero.Position, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { Logger.DBLog.InfoFormat("[Funky] Waiting for Wrath Of The Berserker cooldown before continuing to Azmodan."); obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "GilesWaitForWrath", 0f, -1); InactivityDetector.Reset(); return(true); } // And a special check for wizard archon if (Hotbar.HasPower(SNOPower.Wizard_Archon) && !FunkyGame.Hero.Class.Abilities[SNOPower.Wizard_Archon].AbilityUseTimer() && FunkyBaseExtension.Settings.Wizard.bWaitForArchon && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(FunkyGame.Hero.Position, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(FunkyGame.Hero.Position, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { Logger.DBLog.InfoFormat("[Funky] Waiting for Wizard Archon cooldown before continuing to Azmodan."); obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "GilesWaitForArchon", 0f, -1); InactivityDetector.Reset(); return(true); } // And a very sexy special check for WD BigBadVoodoo if (Hotbar.HasPower(SNOPower.Witchdoctor_BigBadVoodoo) && !PowerManager.CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && ZetaDia.CurrentWorldId == 121214 && (Vector3.Distance(FunkyGame.Hero.Position, new Vector3(711.25f, 716.25f, 80.13903f)) <= 40f || Vector3.Distance(FunkyGame.Hero.Position, new Vector3(546.8467f, 551.7733f, 1.576313f)) <= 40f)) { Logger.DBLog.InfoFormat("[Funky] Waiting for WD BigBadVoodoo cooldown before continuing to Azmodan."); obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "GilesWaitForVoodooo", 0f, -1); InactivityDetector.Reset(); return(true); } //Currently preforming an interactive profile behavior (check if in town and not vendoring) if (FunkyGame.Game.InteractableCachedObject != null && (!FunkyGame.Hero.bIsInTown || !BrainBehavior.IsVendoring)) { if (FunkyGame.Game.InteractableCachedObject.Position.Distance(FunkyGame.Hero.Position) > 50f) { //if (FunkyGame.Targeting.Cache.LastCachedTarget.Position != Bot.Game.Profile.InteractableCachedObject.Position) // Navigator.Clear(); //Generate the path here so we can start moving.. Navigation.Navigation.NP.MoveTo(FunkyGame.Game.InteractableCachedObject.Position, "ReturnToOOCLoc", true); //Setup a temp target that the handler will use obj = new CacheObject(FunkyGame.Game.InteractableCachedObject.Position, TargetType.LineOfSight, 1d, "ReturnToOOCLoc", 10f, FunkyGame.Game.InteractableCachedObject.RAGUID); return(true); } } //Check if we engaged in combat.. bool EngagedInCombat = false; float distanceFromStart = 0f; if (!FunkyGame.Targeting.Cache.LastCachedTarget.Equals(ObjectCache.FakeCacheObject) && !FunkyGame.Targeting.Cache.Backtracking && FunkyGame.Targeting.Cache.StartingLocation != Vector3.Zero) { EngagedInCombat = true; distanceFromStart = FunkyGame.Hero.Position.Distance(FunkyGame.Targeting.Cache.StartingLocation); //lets see how far we are from our starting location. if (distanceFromStart > 20f && !Navigation.Navigation.CanRayCast(FunkyGame.Hero.Position, PlayerMover.vLastMoveTo, UseSearchGridProvider: true)) { Logger.Write(LogLevel.Movement, "Updating Navigator in Target Refresh"); SkipAheadCache.ClearCache(); Navigator.Clear(); //Navigator.MoveTo(Funky.PlayerMover.vLastMoveTo, "original destination", true); } } //Check if our current path intersects avoidances. (When not in town, and not currently inside avoidance) if (!FunkyGame.Hero.bIsInTown && (FunkyBaseExtension.Settings.Avoidance.AttemptAvoidanceMovements) && //|| FunkyGame.Hero.CriticalAvoidance) Navigation.Navigation.NP.CurrentPath.Count > 0 && FunkyGame.Targeting.Cache.Environment.TriggeringAvoidances.Count == 0) { if (ObjectCache.Obstacles.TestVectorAgainstAvoidanceZones(FunkyGame.Hero.Position, Navigation.Navigation.NP.CurrentPath.Current)) { obj = new CacheObject(FunkyGame.Hero.Position, TargetType.NoMovement, 20000, "AvoidanceIntersection", 2.5f, -1); return(true); } } //Backtracking Check.. if (EngagedInCombat && FunkyBaseExtension.Settings.Backtracking.EnableBacktracking && distanceFromStart >= FunkyBaseExtension.Settings.Backtracking.MinimumDistanceFromStart) { FunkyGame.Targeting.Cache.Backtracking = true; obj = new CacheObject(FunkyGame.Targeting.Cache.StartingLocation, TargetType.Backtrack, 20000, "Backtracking", 2.5f); return(true); } } return(obj != null); }; }
public override RunStatus Tick(object context) { if (!Hotbar.HasPower(Power)) { return(RunStatus.Failure); } if (ContextChangeHandler != null && context == null) { CombatTargeting.Instance.Pulse(); context = ContextChangeHandler(null); } if (context == null) { return(RunStatus.Failure); } bool minReqs = ExtraCondition == null || ExtraCondition(context); if (!minReqs) { return(RunStatus.Failure); } if (!_isSpamming) { using (new PerformanceLogger(BelphegorSettings.Instance.Debug.IsDebugCanCastLogging, "CanCast")) { bool canCast = PowerManager.CanCast(Power); if (!canCast) { return(RunStatus.Failure); } } } Vector3 clickPosition = PositionRetriever != null?PositionRetriever(context) : Vector3.Zero; int worldId = DynamicWorldIdRetriever != null?DynamicWorldIdRetriever(context) : 0; int acdGuid = TargetGuidRetriever != null?TargetGuidRetriever(context) : -1; bool keepSpamming = KeepSpamming != null && KeepSpamming(context); bool castSucceeded = ZetaDia.Me.UsePower(Power, clickPosition, worldId, acdGuid); if (!castSucceeded) { return(RunStatus.Failure); } if (BelphegorSettings.Instance.Debug.IsDebugCastLoggingActive && !keepSpamming) { CastLogSw.Stop(); string castOn = acdGuid == -1 ? "at location " + clickPosition : "on Unit " + acdGuid.ToString(CultureInfo.InvariantCulture); Log.InfoFormat("Using Power: {0} {1}, delay from last cast is {2}ms", Power.ToString(), castOn, CastLogSw.ElapsedMilliseconds); CastLogSw.Restart(); } if (!_isSpamming && SucceedRunner != null) { SucceedRunner(context); } if (keepSpamming) { _isSpamming = true; if (BelphegorSettings.Instance.Debug.IsDebugCastLoggingActive) { CastLogSw.Stop(); string castOn = acdGuid == -1 ? "at location " + clickPosition : "on Unit " + acdGuid.ToString(CultureInfo.InvariantCulture); Log.InfoFormat("Using Power: Is Spamming {0} {1}, delay from last cast is {2}ms", Power.ToString(), castOn, CastLogSw.ElapsedMilliseconds); CastLogSw.Restart(); } return(RunStatus.Running); } _isSpamming = false; return(RunStatus.Success); }
internal void CreatePrecastCriteria() { SkillPrecastFlags precastconditions_ = Flags; if (precastconditions_.Equals(SkillPrecastFlags.None)) { Criteria += ((s) => true); return; } if (precastconditions_.HasFlag(SkillPrecastFlags.CheckPlayerIncapacitated)) { Criteria += ((s) => !FunkyGame.Hero.bIsIncapacitated); } if (precastconditions_.HasFlag(SkillPrecastFlags.CheckPlayerRooted)) { Criteria += ((s) => !FunkyGame.Hero.bIsRooted); } if (precastconditions_.HasFlag(SkillPrecastFlags.CheckExisitingBuff)) { Criteria += ((s) => !Hotbar.HasBuff(s.Power)); } if (precastconditions_.HasFlag(SkillPrecastFlags.CheckPetCount)) { Criteria += ((s) => FunkyGame.Hero.Class.MainPetCount < s.Counter); } if (precastconditions_.HasFlag(SkillPrecastFlags.CheckRecastTimer)) { Criteria += ((s) => s.LastUsedMilliseconds > s.Cooldown); } if (precastconditions_.HasFlag(SkillPrecastFlags.CheckEnergy)) { Criteria += ((s) => { bool energyCheck = !s.SecondaryEnergy ? FunkyGame.Hero.dCurrentEnergy >= s.Cost : FunkyGame.Hero.dDiscipline >= s.Cost; if (s.IsSpecialAbility && !energyCheck) //we trigger waiting for special here. { FunkyGame.Hero.Class.bWaitingForSpecial = true; } if (!energyCheck && (s.IsRanged || s.Range > 0)) { FunkyGame.Hero.Class.CanUseDefaultAttack = true; } return(energyCheck); }); } if (precastconditions_.HasFlag(SkillPrecastFlags.CheckCanCast)) { Criteria += ((s) => { bool cancast = PowerManager.CanCast(s.Power, out s.CanCastFlags); //PowerManager.CanCastFlags.Flag80; (Not enough Resource?) //PowerManager.CanCastFlags.Flag8; (On Cooldown?) if (!cancast && s.CanCastFlags.HasFlag(PowerManager.CanCastFlags.Flag80)) { if (s.IsSpecialAbility) { FunkyGame.Hero.Class.bWaitingForSpecial = true; } if (s.IsRanged || s.Range > 0) { FunkyGame.Hero.Class.CanUseDefaultAttack = true; } } //else if (s.IsSpecialAbility) //FunkyGame.Hero.Class.bWaitingForSpecial = false; return(cancast); }); } }
/// <summary> /// Checks if a skill can and should be cast. /// </summary> /// <param name="skill">the Skill to check</param> /// <param name="condition">function to test against</param> //public static bool CanCast(Skill skill, Func<SkillMeta, bool> condition) //{ // return CanCast(skill, null, condition); //} /// <summary> /// Checks if a skill can and should be cast. /// </summary> /// <param name="skill">the Skill to check</param> /// <param name="changes">action to modify existing skill data</param> //public static bool CanCast(Skill skill, Action<SkillMeta> changes) //{ // return CanCast(skill, null, c => { changes(c); return true; }); //} /// <summary> /// Checks if a skill can and should be cast. /// </summary> /// <param name="skill">the Skill to check</param> /// <param name="cd">Optional combat data to use</param> /// <param name="adhocCondition">Optional function to test against</param> public static bool CanCast(Skill skill, SkillMeta sm = null) { try { var meta = (sm != null) ? skill.Meta.Apply(sm) : skill.Meta; Func <string> check = () => { if (!Hotbar.Contains(skill.SNOPower)) { return("NotOnHotbar"); } if (Player.IsIncapacitated) { return("IsIncapacitated"); } //var adhocConditionResult = (adhocCondition == null) || adhocCondition(meta); var metaConditionResult = (meta.CastCondition == null) || meta.CastCondition(meta); if (!meta.CastFlags.HasFlag(CanCastFlags.NoTimer) && !SNOPowerUseTimer(skill.SNOPower)) { return("PowerUseTimer"); } if (!meta.CastFlags.HasFlag(CanCastFlags.NoPowerManager) && !PowerManager.CanCast(skill.SNOPower)) { return("PowerManager"); } // Note: ZetaDia.Me.IsInCombat is unrealiable and only kicks in after an ability has hit a monster if (meta.IsCombatOnly && CurrentTarget == null) { return("IsInCombat"); } // This is already checked above...? //if (meta.ReUseDelay > 0 && TimeSincePowerUse(skill.SNOPower) < meta.ReUseDelay) // return "ReUseDelay"; //if (meta.IsEliteOnly && Enemies.Nearby.EliteCount == 0) // return false; //if (meta.MaxTargetDistance > CurrentTarget.Distance) // return false; var resourceCost = (meta.RequiredResource > 0) ? meta.RequiredResource : skill.Cost; if (resourceCost > 0 && !skill.IsGeneratorOrPrimary) { var actualResource = (skill.Resource == Resource.Discipline) ? Player.SecondaryResource : Player.PrimaryResource; if (actualResource < resourceCost) { return(string.Format("NotEnoughResource({0}/{1})", Math.Round(actualResource), resourceCost)); } } //if (meta.IsEliteOnly && !CurrentTarget.IsBossOrEliteRareUnique) // return false; //if (!adhocConditionResult) // return "AdHocConditionFailure"; if (!metaConditionResult) { return("ConditionFailure"); } return(string.Empty); }; var failReason = check(); if (!string.IsNullOrEmpty(failReason)) { Logger.Log(TrinityLogLevel.Verbose, LogCategory.SkillSelection, " >> CanCast Failed: {0} ({1}) Reason={2}", skill.Name, (int)skill.SNOPower, failReason); return(false); } return(true); } catch (Exception ex) { Logger.Log("Exception in CanCast for {0}. {1} {2}", skill.Name, ex.Message, ex.InnerException); } return(false); }