private static TrinityPower GetWizardDestructablePower() { if (Hotbar.Contains(SNOPower.Wizard_EnergyTwister) && PlayerStatus.PrimaryResource >= 35) { return(new TrinityPower(SNOPower.Wizard_EnergyTwister, 9f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Wizard_ArcaneOrb)) { return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Wizard_MagicMissile)) { return(new TrinityPower(SNOPower.Wizard_MagicMissile, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Wizard_ShockPulse)) { return(new TrinityPower(SNOPower.Wizard_ShockPulse, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Wizard_SpectralBlade)) { return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 9f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Wizard_Electrocute)) { return(new TrinityPower(SNOPower.Wizard_Electrocute, 9f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(GetDefaultWeaponPower(), GetDefaultWeaponDistance(), vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }
private static bool DemonHunter_HasNoPrimary() { return(!(Hotbar.Contains(SNOPower.DemonHunter_Bolas) || Hotbar.Contains(SNOPower.X1_DemonHunter_EntanglingShot) || Hotbar.Contains(SNOPower.DemonHunter_Grenades) || Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow))); }
private static TrinityPower GetMonkDestroyPower() { if (Monk_TempestRushReady()) { return(new TrinityPower(SNOPower.Monk_TempestRush, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_DashingStrike)) { return(new TrinityPower(SNOPower.Monk_DashingStrike, Monk_MaxDashingStrikeRange, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_FistsofThunder)) { return(new TrinityPower(SNOPower.Monk_FistsofThunder, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_DeadlyReach)) { return(new TrinityPower(SNOPower.Monk_DeadlyReach, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_CripplingWave)) { return(new TrinityPower(SNOPower.Monk_CripplingWave, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Monk_WayOfTheHundredFists)) { return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.Weapon_Melee_Instant, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }
/// <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); }
private static TrinityPower GetBarbarianDestroyPower() { if (Hotbar.Contains(SNOPower.Barbarian_Whirlwind) && PlayerStatus.PrimaryResource > MinEnergyReserve) { return(new TrinityPower(SNOPower.Barbarian_Whirlwind, 10f, vSideToSideTarget, CurrentWorldDynamicId, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Barbarian_Frenzy)) { return(new TrinityPower(SNOPower.Barbarian_Frenzy, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Barbarian_Bash)) { return(new TrinityPower(SNOPower.Barbarian_Bash, 6f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Barbarian_Cleave)) { return(new TrinityPower(SNOPower.Barbarian_Cleave, 6f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Barbarian_Rend) && PlayerStatus.PrimaryResourcePct >= 0.65) { return(new TrinityPower(SNOPower.Barbarian_Rend, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Barbarian_WeaponThrow) && PlayerStatus.PrimaryResource >= 20) { return(new TrinityPower(SNOPower.Barbarian_WeaponThrow, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.Weapon_Melee_Instant, 10f, 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 <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); }
private static bool DoesNotHaveMonkMantraAbility() { return (!Hotbar.Contains(SNOPower.X1_Monk_MantraOfConviction_v2) && !Hotbar.Contains(SNOPower.X1_Monk_MantraOfEvasion_v2) && !Hotbar.Contains(SNOPower.X1_Monk_MantraOfHealing_v2) && !Hotbar.Contains(SNOPower.X1_Monk_MantraOfRetribution_v2)); }
private static bool RefreshUnitAttributes(bool AddToCache = true, DiaUnit unit = null) { if (!DataDictionary.IgnoreUntargettableAttribute.Contains(CurrentCacheObject.ActorSNO) && unit.IsUntargetable) { AddToCache = false; c_IgnoreSubStep = "IsUntargetable"; return(AddToCache); } // don't check for invulnerability on shielded and boss units, they are treated seperately if (!c_unit_HasShieldAffix && unit.IsInvulnerable) { AddToCache = false; c_IgnoreSubStep = "IsInvulnerable"; return(AddToCache); } bool isBurrowed = false; if (!CacheData.UnitIsBurrowed.TryGetValue(CurrentCacheObject.RActorGuid, out isBurrowed)) { isBurrowed = unit.IsBurrowed; // if the unit is NOT burrowed - we can attack them, add to cache (as IsAttackable) if (!isBurrowed) { CacheData.UnitIsBurrowed.Add(CurrentCacheObject.RActorGuid, isBurrowed); } } if (isBurrowed) { AddToCache = false; c_IgnoreSubStep = "IsBurrowed"; return(AddToCache); } // only check for DotDPS/Bleeding in certain conditions to save CPU for everyone else // barbs with rend // All WD's // Monks with Way of the Hundred Fists + Fists of Fury if (AddToCache && ((Player.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_Rend)) || Player.ActorClass == ActorClass.Witchdoctor || (Player.ActorClass == ActorClass.Monk && HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 0))) ) { ////bool hasdotDPS = CurrentCacheObject.CommonData.GetAttribute<int>(ActorAttributeType.DOTDPS) != 0; //bool isBleeding = CurrentCacheObject.CommonData.GetAttribute<int>(ActorAttributeType.Bleeding) != 0; //c_HasDotDPS = hasdotDPS && isBleeding; bool hasdotDPS = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.DOTDPS) != 0; c_HasDotDPS = hasdotDPS; } return(AddToCache); }
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); }
private static TrinityPower GetWizardDestructablePower() { if (Hotbar.Contains(SNOPower.Wizard_WaveOfForce) && Player.PrimaryResource >= 25) { return(new TrinityPower(SNOPower.Wizard_WaveOfForce, 9f)); } if (Hotbar.Contains(SNOPower.Wizard_EnergyTwister) && Player.PrimaryResource >= 35) { return(new TrinityPower(SNOPower.Wizard_EnergyTwister, 9f)); } if (Hotbar.Contains(SNOPower.Wizard_ArcaneOrb)) { return(new TrinityPower(SNOPower.Wizard_ArcaneOrb, 35f)); } if (Hotbar.Contains(SNOPower.Wizard_MagicMissile)) { return(new TrinityPower(SNOPower.Wizard_MagicMissile, 15f)); } if (Hotbar.Contains(SNOPower.Wizard_ShockPulse)) { return(new TrinityPower(SNOPower.Wizard_ShockPulse, 10f)); } if (Hotbar.Contains(SNOPower.Wizard_SpectralBlade)) { return(new TrinityPower(SNOPower.Wizard_SpectralBlade, 5f)); } if (Hotbar.Contains(SNOPower.Wizard_Electrocute)) { return(new TrinityPower(SNOPower.Wizard_Electrocute, 9f)); } if (Hotbar.Contains(SNOPower.Wizard_ArcaneTorrent)) { return(new TrinityPower(SNOPower.Wizard_ArcaneTorrent, 9f)); } if (Hotbar.Contains(SNOPower.Wizard_Blizzard)) { return(new TrinityPower(SNOPower.Wizard_Blizzard, 9f)); } return(CombatBase.DefaultPower); }
private static TrinityPower GetDemonHunterDestroyPower() { if (Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow)) { return(new TrinityPower(SNOPower.DemonHunter_HungeringArrow, 40f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.X1_DemonHunter_EntanglingShot)) { return(new TrinityPower(SNOPower.X1_DemonHunter_EntanglingShot, 40f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.DemonHunter_Bolas)) { return(new TrinityPower(SNOPower.DemonHunter_Bolas, 40f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.DemonHunter_Grenades)) { return(new TrinityPower(SNOPower.DemonHunter_Grenades, 15f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.DemonHunter_ElementalArrow) && Player.PrimaryResource >= 10) { return(new TrinityPower(SNOPower.DemonHunter_ElementalArrow, 40f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.X1_DemonHunter_EvasiveFire) && Player.PrimaryResource >= 10) { return(new TrinityPower(SNOPower.X1_DemonHunter_EvasiveFire, 40f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.DemonHunter_RapidFire) && Player.PrimaryResource >= 10) { return(new TrinityPower(SNOPower.DemonHunter_RapidFire, 40f, CurrentTarget.Position)); } if (Hotbar.Contains(SNOPower.DemonHunter_Chakram) && Player.PrimaryResource >= 20) { return(new TrinityPower(SNOPower.DemonHunter_Chakram, 0f, CurrentTarget.ACDGuid)); } return(CombatBase.DefaultPower); }
/// <summary> /// Checks for all necessary buffs and combat conditions for starting Archon /// </summary> /// <returns></returns> private static bool ShouldStartArchon() { bool canCastArchon = ( CheckAbilityAndBuff(SNOPower.Wizard_MagicWeapon) && (!Hotbar.Contains(SNOPower.Wizard_Familiar) || IsFamiliarActive) && CheckAbilityAndBuff(SNOPower.Wizard_EnergyArmor) && CheckAbilityAndBuff(SNOPower.Wizard_IceArmor) && CheckAbilityAndBuff(SNOPower.Wizard_StormArmor) ); var elitesOnly = Settings.Combat.Wizard.ArchonElitesOnly && TargetUtil.AnyElitesInRange(Settings.Combat.Wizard.ArchonEliteDistance); var trashInRange = !Settings.Combat.Wizard.ArchonElitesOnly && TargetUtil.AnyMobsInRange(Settings.Combat.Wizard.ArchonMobDistance, Settings.Combat.Wizard.ArchonMobCount); // With Chantodos set wait until max stacks before using archon if (Sets.ChantodosResolve.IsFullyEquipped && CacheData.Buffs.HasBuff(SNOPower.P3_ItemPassive_Unique_Ring_021) && CacheData.Buffs.GetBuff(SNOPower.P3_ItemPassive_Unique_Ring_021).StackCount < 20) { return(false); } return(canCastArchon && (elitesOnly || trashInRange || CurrentTarget.IsBoss)); }
/// <summary> /// When Vault should be cast /// </summary> private static bool VaultCondition(SkillMeta meta) { meta.CastRange = 20f; meta.TargetPositionSelector = ret => NavHelper.MainFindSafeZone(Player.Position, true); meta.RequiredResource = Hotbar.Contains(SNOPower.DemonHunter_ShadowPower) ? 22 : 16; meta.ReUseDelay = Settings.Combat.DemonHunter.VaultMovementDelay; if (Settings.Combat.DemonHunter.VaultMode == DemonHunterVaultMode.MovementOnly && IsInCombat) { return(false); } if (Settings.Combat.DemonHunter.VaultMode == DemonHunterVaultMode.CombatOnly && !IsInCombat) { return(false); } if (!Player.IsRooted && (TargetUtil.AnyMobsInRange(7f, 6) || Player.CurrentHealthPct <= 0.7)) { return(true); } return(false); }
private static TrinityPower GetWitchDoctorDestroyPower() { if (Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice) && Settings.Combat.WitchDoctor.ZeroDogs) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 12f, vNullLocation, -1, -1, 1, 2, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Witchdoctor_Firebats)) { return(new TrinityPower(SNOPower.Witchdoctor_Firebats, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Witchdoctor_Firebomb)) { return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Witchdoctor_PoisonDart)) { return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && PlayerStatus.PrimaryResource >= 140) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Witchdoctor_CorpseSpider)) { return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Witchdoctor_PlagueOfToads)) { return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.Witchdoctor_AcidCloud) && PlayerStatus.PrimaryResource >= 172) { return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, 12f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.Weapon_Melee_Instant, 10f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); }
private static TrinityPower GetDemonHunterDestroyPower() { if (Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow)) { return(new TrinityPower(SNOPower.DemonHunter_HungeringArrow, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.DemonHunter_EntanglingShot)) { return(new TrinityPower(SNOPower.DemonHunter_EntanglingShot, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.DemonHunter_BolaShot)) { return(new TrinityPower(SNOPower.DemonHunter_BolaShot, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.DemonHunter_Grenades)) { return(new TrinityPower(SNOPower.DemonHunter_Grenades, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.DemonHunter_ElementalArrow) && PlayerStatus.PrimaryResource >= 10) { return(new TrinityPower(SNOPower.DemonHunter_ElementalArrow, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.DemonHunter_RapidFire) && PlayerStatus.PrimaryResource >= 10) { return(new TrinityPower(SNOPower.DemonHunter_RapidFire, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.DemonHunter_Chakram) && PlayerStatus.PrimaryResource >= 20) { return(new TrinityPower(SNOPower.DemonHunter_Chakram, 15f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } if (Hotbar.Contains(SNOPower.DemonHunter_EvasiveFire) && PlayerStatus.PrimaryResource >= 20) { return(new TrinityPower(SNOPower.DemonHunter_EvasiveFire, 40f, vNullLocation, -1, -1, 0, 0, WAIT_FOR_ANIM)); } return(new TrinityPower(SNOPower.Weapon_Ranged_Instant, 40f, vNullLocation, -1, CurrentTarget.ACDGuid, 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; //Kridershot InternalName=x1_bow_norm_unique_09-137 GameBalanceID=1999595351 ItemLink: {c:ffff8000}Kridershot{/c} bool hasKridershot = ZetaDia.Me.Inventory.Equipped.Any(i => i.GameBalanceId == 1999595351); bool hasPunishment = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 0); // Spam Shadow Power if (Settings.Combat.DemonHunter.SpamShadowPower && CombatBase.CanCast(SNOPower.DemonHunter_ShadowPower) && !Player.IsIncapacitated && (!GetHasBuff(SNOPower.DemonHunter_ShadowPower) || Player.CurrentHealthPct <= PlayerEmergencyHealthPotionLimit) && // if we don't have the buff or our health is low ((!hasPunishment && Player.SecondaryResource >= 14) || (hasPunishment && Player.SecondaryResource >= 39)) && // Save some Discipline for Preparation (Settings.Combat.DemonHunter.SpamShadowPower && Player.SecondaryResource >= 28)) // When spamming Shadow Power, save some Discipline for emergencies { return(new TrinityPower(SNOPower.DemonHunter_ShadowPower)); } // NotSpam Shadow Power if (!UseOOCBuff && !Settings.Combat.DemonHunter.SpamShadowPower && CombatBase.CanCast(SNOPower.DemonHunter_ShadowPower) && !Player.IsIncapacitated && (!GetHasBuff(SNOPower.DemonHunter_ShadowPower) || Player.CurrentHealthPct <= PlayerEmergencyHealthPotionLimit) && // if we don't have the buff or our health is low ((!hasPunishment && Player.SecondaryResource >= 14) || (hasPunishment && Player.SecondaryResource >= 39)) && // Save some Discipline for Preparation (Player.CurrentHealthPct < 1f || Player.IsRooted || TargetUtil.AnyMobsInRange(15))) { return(new TrinityPower(SNOPower.DemonHunter_ShadowPower)); } // Vengeance if (!IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.X1_DemonHunter_Vengeance, CombatBase.CanCastFlags.NoTimer) && ((!Settings.Combat.DemonHunter.VengeanceElitesOnly && TargetUtil.AnyMobsInRange(60, 6)) || TargetUtil.IsEliteTargetInRange(60f))) { return(new TrinityPower(SNOPower.X1_DemonHunter_Vengeance)); } // Smoke Screen if ((!UseOOCBuff || Settings.Combat.DemonHunter.SpamSmokeScreen) && CombatBase.CanCast(SNOPower.DemonHunter_SmokeScreen) && !GetHasBuff(SNOPower.DemonHunter_ShadowPower) && Player.SecondaryResource >= 14 && ( (Player.CurrentHealthPct <= 0.50 || Player.IsRooted || TargetUtil.AnyMobsInRange(15) || Player.IsIncapacitated) || Settings.Combat.DemonHunter.SpamSmokeScreen )) { return(new TrinityPower(SNOPower.DemonHunter_SmokeScreen, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } int sentryCoolDown = SpellHistory.SpellUseCountInTime(SNOPower.DemonHunter_Sentry, TimeSpan.FromSeconds(24)) >= 4 ? 12 : 6; // Sentry Turret if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.DemonHunter_Sentry, CombatBase.CanCastFlags.NoTimer) && (TargetUtil.AnyElitesInRange(50) || TargetUtil.AnyMobsInRange(50, 2) || TargetUtil.IsEliteTargetInRange(50)) && Player.PrimaryResource >= 30 && (SpellHistory.TimeSinceUse(SNOPower.DemonHunter_Sentry) > TimeSpan.FromSeconds(sentryCoolDown) || SpellHistory.DistanceFromLastUsePosition(SNOPower.DemonHunter_Sentry) > 7.5)) { return(new TrinityPower(SNOPower.DemonHunter_Sentry, 65f, TargetUtil.GetBestClusterPoint())); } // Caltrops if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.DemonHunter_Caltrops) && Player.SecondaryResource >= 6 && TargetUtil.AnyMobsInRange(40)) { return(new TrinityPower(SNOPower.DemonHunter_Caltrops, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Preperation: restore 30 disc / 45 sec cd // Invigoration = 1 : passive + 15 disc / 45 sec cd // Punishment = 0 : Cost: 25 disc, restore 75 hatred / NO COOLDOWN // Battle Scars = 3 : instantly restore 40% health + restore 30 disc / 45 sec cd // Focused Mind = 2 : restore 45 disc over 15 sec / 45 sec cd // Backup plan = 4 : 30% chance Prep has no cooldown (remove cast timer) // Restore 75 hatred for 25 disc - NO COOLDOWN! //bool hasPunishment = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 0); bool hasInvigoration = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 1); bool hasBattleScars = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 3); bool hasFocusedMind = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 2); bool hasBackupPlan = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Preparation && s.RuneIndex == 4); float preperationTriggerRange = V.F("DemonHunter.PreperationTriggerRange"); if (((!UseOOCBuff && !Player.IsIncapacitated && (TargetUtil.AnyMobsInRange(preperationTriggerRange))) || Settings.Combat.DemonHunter.SpamPreparation || hasPunishment) && Hotbar.Contains(SNOPower.DemonHunter_Preparation)) { // Preperation w/ Punishment if (hasPunishment && CombatBase.CanCast(SNOPower.DemonHunter_Preparation, CombatBase.CanCastFlags.NoTimer) && Player.SecondaryResource >= 25 && Player.PrimaryResourceMissing >= 75 && TimeSinceUse(SNOPower.DemonHunter_Preparation) >= 1000) { return(new TrinityPower(SNOPower.DemonHunter_Preparation)); } // Preperation w/ Battle Scars - check for health only if (hasBattleScars && CombatBase.CanCast(SNOPower.DemonHunter_Preparation) && Player.CurrentHealthPct < 0.6) { return(new TrinityPower(SNOPower.DemonHunter_Preparation)); } // no rune || invigoration || focused mind || Backup Plan || Battle Scars (need Disc) if ((!hasPunishment) && CombatBase.CanCast(SNOPower.DemonHunter_Preparation) && Player.SecondaryResource <= 15 && TimeSinceUse(SNOPower.DemonHunter_Preparation) >= 1000) { return(new TrinityPower(SNOPower.DemonHunter_Preparation)); } } // SOURCE: Stede - http://www.thebuddyforum.com/demonbuddy-forum/plugins/trinity/155398-trinity-dh-companion-use-abilities.html#post1445496 // Companion On-Use Abilities Added in 2.0 bool hasSpider = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 0); bool hasBat = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 3); bool hasBoar = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 1); bool hasFerret = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 4); bool hasWolf = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_DemonHunter_Companion && s.RuneIndex == 2); if (!UseOOCBuff && !Player.IsIncapacitated && Hotbar.Contains(SNOPower.X1_DemonHunter_Companion)) { // Use Spider Slow on 4 or more trash mobs in an area or on Unique/Elite/Champion if (hasSpider && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && TargetUtil.ClusterExists(25f, 4) && TargetUtil.EliteOrTrashInRange(25f)) { return(new TrinityPower(SNOPower.X1_DemonHunter_Companion)); } //Use Bat when Hatred is Needed if (hasBat && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && Player.PrimaryResourceMissing >= 60) { return(new TrinityPower(SNOPower.X1_DemonHunter_Companion)); } // Use Boar Taunt on 3 or more trash mobs in an area or on Unique/Elite/Champion if (hasBoar && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && ((TargetUtil.ClusterExists(20f, 4) && TargetUtil.EliteOrTrashInRange(20f)) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.Distance <= 20f))) { return(new TrinityPower(SNOPower.X1_DemonHunter_Companion)); } // Ferrets used for picking up Health Globes when low on Health if (hasFerret && ObjectCache.Any(o => o.Type == GObjectType.HealthGlobe && o.Distance < 60f) && Player.CurrentHealthPct < PlayerEmergencyHealthPotionLimit) { return(new TrinityPower(SNOPower.X1_DemonHunter_Companion)); } // Use Wolf Howl on Unique/Elite/Champion - Would help for farming trash, but trash farming should not need this - Used on Elites to reduce Deaths per hour if (hasWolf && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && (TargetUtil.AnyMobsInRange(20) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance < 55f))) { return(new TrinityPower(SNOPower.X1_DemonHunter_Companion)); } } //skillDict.Add("EvasiveFire", SNOPower.DemonHunter_EvasiveFire); //runeDict.Add("Hardened", 0); //runeDict.Add("PartingGift", 2); //runeDict.Add("CoveringFire", 1); //runeDict.Add("Displace", 4); //runeDict.Add("Surge", 3); // Companion if (!Player.IsIncapacitated && CombatBase.CanCast(SNOPower.X1_DemonHunter_Companion) && TargetUtil.EliteOrTrashInRange(30f) && Player.SecondaryResource >= 10) { return(new TrinityPower(SNOPower.X1_DemonHunter_Companion, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 1)); } // Marked for Death if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_MarkedForDeath) && Player.SecondaryResource >= 3 && (TargetUtil.AnyElitesInRange(40) || TargetUtil.AnyMobsInRange(40, 3) || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.Radius <= 40 && CurrentTarget.RadiusDistance <= 40f))) { return(new TrinityPower(SNOPower.DemonHunter_MarkedForDeath, 40f, Vector3.Zero, CurrentWorldDynamicId, CurrentTarget.ACDGuid, 1, 1)); } // Vault if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_Vault) && !Player.IsRooted && !Player.IsIncapacitated && Settings.Combat.DemonHunter.VaultMode != Config.Combat.DemonHunterVaultMode.MovementOnly && // Only use vault to retreat if < level 60, or if in inferno difficulty for level 60's (Player.Level < 60 || Player.GameDifficulty > GameDifficulty.Master) && (CurrentTarget.RadiusDistance <= 10f || TargetUtil.AnyMobsInRange(10)) && // if we have ShadowPower and Disicpline is >= 16 // or if we don't have ShadoWpower and Discipline is >= 22 (Player.SecondaryResource >= (Hotbar.Contains(SNOPower.DemonHunter_ShadowPower) ? 22 : 16)) && TimeSinceUse(SNOPower.DemonHunter_Vault) >= Trinity.Settings.Combat.DemonHunter.VaultMovementDelay) { //Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, -15f); // Lets find a smarter Vault position instead of just "backwards" //Vector3 vNewTarget = NavHelper.FindSafeZone(Trinity.Player.Position, true, false, null, false); Vector3 vNewTarget = NavHelper.MainFindSafeZone(Trinity.Player.Position, true, false, null, false); return(new TrinityPower(SNOPower.DemonHunter_Vault, 20f, vNewTarget, CurrentWorldDynamicId, -1, 1, 2)); } // Rain of Vengeance if (!UseOOCBuff && CombatBase.CanCast(SNOPower.DemonHunter_RainOfVengeance) && !Player.IsIncapacitated && (TargetUtil.ClusterExists(45f, 3) || TargetUtil.EliteOrTrashInRange(45f))) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(45f, 65f, false, true); return(new TrinityPower(SNOPower.DemonHunter_RainOfVengeance, 0f, bestClusterPoint, CurrentWorldDynamicId, -1, 1, 1)); } // Cluster Arrow if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_ClusterArrow) && !Player.IsIncapacitated && Player.PrimaryResource >= 50) { return(new TrinityPower(SNOPower.DemonHunter_ClusterArrow, V.F("DemonHunter.ClusterArrow.UseRange"), CurrentTarget.ACDGuid)); } // Multi Shot if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_Multishot) && !Player.IsIncapacitated && Player.PrimaryResource >= 30 && (TargetUtil.AnyMobsInRange(40, 2) || CurrentTarget.IsBossOrEliteRareUnique || CurrentTarget.IsTreasureGoblin)) { return(new TrinityPower(SNOPower.DemonHunter_Multishot, 30f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 1, 1)); } // Fan of Knives if (!UseOOCBuff && CombatBase.CanCast(SNOPower.DemonHunter_FanOfKnives) && !Player.IsIncapacitated && (TargetUtil.EliteOrTrashInRange(15) || TargetUtil.AnyTrashInRange(15f, 5, false))) { return(new TrinityPower(SNOPower.DemonHunter_FanOfKnives, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Strafe spam - similar to barbarian whirlwind routine if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_Strafe, CombatBase.CanCastFlags.NoTimer) && !Player.IsIncapacitated && !Player.IsRooted && Player.PrimaryResource >= Settings.Combat.DemonHunter.StrafeMinHatred) { bool shouldGetNewZigZag = (DateTime.UtcNow.Subtract(Trinity.LastChangedZigZag).TotalMilliseconds >= V.I("Barbarian.Whirlwind.ZigZagMaxTime") || CurrentTarget.ACDGuid != Trinity.LastZigZagUnitAcdGuid || CombatBase.ZigZagPosition.Distance2D(Player.Position) <= 5f); if (shouldGetNewZigZag) { var wwdist = V.F("Barbarian.Whirlwind.ZigZagDistance"); CombatBase.ZigZagPosition = TargetUtil.GetZigZagTarget(CurrentTarget.Position, wwdist); Trinity.LastZigZagUnitAcdGuid = CurrentTarget.ACDGuid; Trinity.LastChangedZigZag = DateTime.UtcNow; } int postCastTickDelay = TrinityPower.MillisecondsToTickDelay(250); return(new TrinityPower(SNOPower.DemonHunter_Strafe, 15f, CombatBase.ZigZagPosition, CurrentWorldDynamicId, -1, 0, postCastTickDelay)); } // Spike Trap if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.DemonHunter_SpikeTrap) && LastPowerUsed != SNOPower.DemonHunter_SpikeTrap && Player.PrimaryResource >= 30) { // 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.DemonHunter_SpikeTrap, 35f, vNewTarget, CurrentWorldDynamicId, -1, 1, 1)); } //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) && SNOPowerUseTimer(SNOPower.DemonHunter_ElementalArrow) && !Player.IsIncapacitated && ((Player.PrimaryResource >= 10 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve || hasKridershot)) { return(new TrinityPower(SNOPower.DemonHunter_ElementalArrow, 65f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1)); } //skillDict.Add("Chakram", SNOPower.DemonHunter_Chakram); //runeDict.Add("TwinChakrams", 0); //runeDict.Add("Serpentine", 2); //runeDict.Add("RazorDisk", 3); //runeDict.Add("Boomerang", 1); //runeDict.Add("ShurikenCloud", 4); bool hasShurikenCloud = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.DemonHunter_Chakram && s.RuneIndex == 4); // Chakram normal attack if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Chakram) && !Player.IsIncapacitated && !hasShurikenCloud && ((Player.PrimaryResource >= 10 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve)) { return(new TrinityPower(SNOPower.DemonHunter_Chakram, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1)); } // Chakram:Shuriken Cloud if (!Player.IsInTown && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Chakram) && !Player.IsIncapacitated && hasShurikenCloud && TimeSinceUse(SNOPower.DemonHunter_Chakram) >= 110000 && ((Player.PrimaryResource >= 10 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve)) { return(new TrinityPower(SNOPower.DemonHunter_Chakram, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 2)); } // Rapid Fire if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.DemonHunter_RapidFire, CombatBase.CanCastFlags.NoTimer) && !Player.IsIncapacitated && Player.PrimaryResource >= 16 && (Player.PrimaryResource >= Settings.Combat.DemonHunter.RapidFireMinHatred || CombatBase.LastPowerUsed == SNOPower.DemonHunter_RapidFire)) { // 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, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } // Now return rapid fire, if not sending grenades instead return(new TrinityPower(SNOPower.DemonHunter_RapidFire, 40f, CurrentTarget.Position)); } // Impale if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Impale) && !Player.IsIncapacitated && (!TargetUtil.AnyMobsInRange(12, 4)) && ((Player.PrimaryResource >= 25 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve) && CurrentTarget.RadiusDistance <= 50f) { return(new TrinityPower(SNOPower.DemonHunter_Impale, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1)); } // Evasive Fire if (!UseOOCBuff && CombatBase.CanCast(SNOPower.X1_DemonHunter_EvasiveFire) && !Player.IsIncapacitated && (TargetUtil.AnyMobsInRange(10f) || DemonHunter_HasNoPrimary())) { float range = DemonHunter_HasNoPrimary() ? 70f : 0f; return(new TrinityPower(SNOPower.X1_DemonHunter_EvasiveFire, range, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1)); } // Hungering Arrow if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_HungeringArrow) && !Player.IsIncapacitated) { return(new TrinityPower(SNOPower.DemonHunter_HungeringArrow, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } // Entangling shot if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.X1_DemonHunter_EntanglingShot) && !Player.IsIncapacitated) { return(new TrinityPower(SNOPower.X1_DemonHunter_EntanglingShot, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 0)); } // Bola Shot if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Bolas) && !Player.IsIncapacitated) { return(new TrinityPower(SNOPower.DemonHunter_Bolas, 50f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1)); } // Grenades if (!UseOOCBuff && !IsCurrentlyAvoiding && Hotbar.Contains(SNOPower.DemonHunter_Grenades) && !Player.IsIncapacitated) { return(new TrinityPower(SNOPower.DemonHunter_Grenades, 40f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 5, 5)); } // Default attacks return(CombatBase.DefaultPower); }
/// <summary> /// Returns true if we have the ability and the buff is up, or true if we don't have the ability in our hotbar /// </summary> /// <param name="snoPower"></param> /// <returns></returns> internal static bool CheckAbilityAndBuff(SNOPower snoPower) { return (!Hotbar.Contains(snoPower) || (Hotbar.Contains(snoPower) && GetHasBuff(snoPower))); }
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 bool RefreshGilesUnit(bool AddToCache) { AddToCache = true; // See if this is a boss c_unit_IsBoss = hashBossSNO.Contains(c_ActorSNO); // hax for Diablo_shadowClone c_unit_IsAttackable = c_InternalName.StartsWith("Diablo_shadowClone"); // Prepare the fake object for target handler if (FakeObject == null) { FakeObject = c_diaUnit; } if (c_CommonData.ACDGuid == -1) { AddToCache = false; return(AddToCache); } // Dictionary based caching of monster types based on the SNO codes MonsterType monsterType; // See if we need to refresh the monster type or not bool bAddToDictionary = !dictionaryStoredMonsterTypes.TryGetValue(c_ActorSNO, out monsterType); bool bRefreshMonsterType = bAddToDictionary; using (new PerformanceLogger("RefreshUnit.5")) { // If it's a boss and it was an ally, keep refreshing until it's not an ally // Because some bosses START as allied for cutscenes etc. until they become hostile if (c_unit_IsBoss && !bRefreshMonsterType) { switch (monsterType) { case MonsterType.Ally: case MonsterType.Scenery: case MonsterType.Helper: case MonsterType.Team: bRefreshMonsterType = true; break; } } } using (new PerformanceLogger("RefreshUnit.6")) { // Now see if we do need to get new data for this boss or not if (bRefreshMonsterType) { try { monsterType = RefreshMonsterType(c_CommonData, monsterType, bAddToDictionary); } catch (Exception ex) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting monsterinfo and monstertype for unit {0} [{1}]", c_InternalName, c_ActorSNO); DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex); DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.CacheManagement, "ActorTypeAttempt={0}", c_diaUnit.ActorType); AddToCache = false; } } // Make sure it's a valid monster type switch (monsterType) { case MonsterType.Ally: case MonsterType.Scenery: case MonsterType.Helper: case MonsterType.Team: { AddToCache = false; c_IgnoreSubStep = "AllySceneryHelperTeam"; return(AddToCache); } //break; } } // Force return here for un-attackable allies if (!AddToCache) { return(AddToCache); } MonsterAffixes monsterAffixes; using (new PerformanceLogger("RefreshUnit.8")) { // Only set treasure goblins to true *IF* they haven't disabled goblins! Then check the SNO in the goblin hash list! c_unit_IsTreasureGoblin = false; // Flag this as a treasure goblin *OR* ignore this object altogether if treasure goblins are set to ignore if (hashActorSNOGoblins.Contains(c_ActorSNO)) { if (Settings.Combat.Misc.GoblinPriority != 0) { c_unit_IsTreasureGoblin = true; } else { AddToCache = false; c_IgnoreSubStep = "IgnoreTreasureGoblins"; return(AddToCache); } } // Pull up the Monster Affix cached data monsterAffixes = RefreshAffixes(c_CommonData); /* * * This should be moved to HandleTarget * */ if (PlayerStatus.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && GilesUseTimer(SNOPower.Barbarian_WrathOfTheBerserker, true)) { //WotB only used on Arcane, Frozen, Jailer, Molten and Electrified+Reflect Damage elites if (monsterAffixes.HasFlag(MonsterAffixes.ArcaneEnchanted) || monsterAffixes.HasFlag(MonsterAffixes.Frozen) || monsterAffixes.HasFlag(MonsterAffixes.Jailer) || monsterAffixes.HasFlag(MonsterAffixes.Molten) || (monsterAffixes.HasFlag(MonsterAffixes.Electrified) && monsterAffixes.HasFlag(MonsterAffixes.ReflectsDamage)) || //Bosses and uber elites c_unit_IsBoss || c_ActorSNO == 256015 || c_ActorSNO == 256000 || c_ActorSNO == 255996 || //...or more than 4 elite mobs in range (only elites/rares/uniques, not minions!) ElitesWithinRange[RANGE_50] > 4) { shouldUseBerserkerPower = true; } } else { shouldUseBerserkerPower = false; } // Is this something we should try to force leap/other movement abilities against? c_ForceLeapAgainst = false; } double killRange; killRange = RefreshKillRadius(); c_KillRange = killRange; if (monsterAffixes.HasFlag(MonsterAffixes.Shielding)) { c_unit_IsShielded = true; } // Only if at full health, else don't bother checking each loop // See if we already have this monster's size stored, if not get it and cache it if (!dictionaryStoredMonsterSizes.TryGetValue(c_ActorSNO, out c_unit_MonsterSize)) { try { RefreshMonsterSize(); } catch (Exception ex) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting monstersize info for unit {0} [{1}]", c_InternalName, c_ActorSNO); DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex); AddToCache = false; return(AddToCache); } } // Retrieve collision sphere radius, cached if possible if (!dictGilesCollisionSphereCache.TryGetValue(c_ActorSNO, out c_Radius)) { try { RefreshMonsterRadius(); } catch (Exception ex) { DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting collisionsphere radius for unit {0} [{1}]", c_InternalName, c_ActorSNO); DbHelper.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "{0}", ex); AddToCache = false; return(AddToCache); } dictGilesCollisionSphereCache.Add(c_ActorSNO, c_Radius); } double dThisMaxHealth = RefreshMonsterHealth(); // And finally put the two together for a current health percentage c_HitPointsPct = c_HitPoints / dThisMaxHealth; // Unit is already dead if (c_HitPoints <= 0d && !c_unit_IsBoss) { AddToCache = false; c_IgnoreSubStep = "0HitPoints"; // return here immediately return(AddToCache); } AddToCache = RefreshUnitAttributes(AddToCache, c_diaUnit); if (!AddToCache) { return(AddToCache); } c_CurrentAnimation = c_diaUnit.CommonData.CurrentAnimation; // A "fake distance" to account for the large-object size of monsters c_RadiusDistance -= (float)c_Radius; if (c_RadiusDistance <= 1f) { c_RadiusDistance = 1f; } // Special flags to decide whether to target anything at all if (c_IsEliteRareUnique || c_unit_IsBoss) { AnyElitesPresent = true; } // Extended kill radius after last fighting, or when we want to force a town run if ((Settings.Combat.Misc.ExtendedTrashKill && iKeepKillRadiusExtendedFor > 0) || ForceVendorRunASAP || TownRun.IsTryingToTownPortal()) { if (c_RadiusDistance <= killRange && AddToCache) { AnyMobsInRange = true; } } else { if (c_RadiusDistance <= Settings.Combat.Misc.NonEliteRange && AddToCache) { AnyMobsInRange = true; } } if (c_unit_IsTreasureGoblin) { AnyTreasureGoblinsPresent = true; } // Units with very high priority (1900+) allow an extra 50% on the non-elite kill slider range if (!AnyMobsInRange && !AnyElitesPresent && !AnyTreasureGoblinsPresent && c_RadiusDistance <= (Settings.Combat.Misc.NonEliteRange * 1.5)) { int iExtraPriority; // Enable extended kill radius for specific unit-types if (hashActorSNORanged.Contains(c_ActorSNO)) { AnyMobsInRange = true; } if (!AnyMobsInRange && dictActorSNOPriority.TryGetValue(c_ActorSNO, out iExtraPriority)) { if (iExtraPriority >= 1900) { AnyMobsInRange = true; } } } return(AddToCache); }
private static bool RefreshUnitAttributes(bool AddToCache = true, DiaUnit unit = null) { /* * TeamID - check once for all units except bosses (which can potentially change teams - Belial, Cydea) */ string teamIdHash = HashGenerator.GetGenericHash("teamId.RActorGuid=" + c_RActorGuid + ".ActorSNO=" + c_ActorSNO + ".WorldId=" + PlayerStatus.WorldID); int teamId = 0; if (!c_unit_IsBoss && GenericCache.ContainsKey(teamIdHash)) { teamId = (int)GenericCache.GetObject(teamIdHash).Value; } else { teamId = c_CommonData.GetAttribute <int>(ActorAttributeType.TeamID); GenericCache.AddToCache(new GenericCacheObject() { Key = teamIdHash, Value = teamId, Expires = DateTime.Now.AddMinutes(60) }); } if (teamId == 1) { AddToCache = false; c_IgnoreSubStep += "IsTeam1+"; return(AddToCache); } if (unit.IsUntargetable) { AddToCache = false; c_IgnoreSubStep = "IsUntargetable"; return(AddToCache); } // don't check for invulnerability on shielded units, they are treated seperately if (!c_unit_IsShielded && unit.IsInvulnerable) { AddToCache = false; c_IgnoreSubStep = "IsInvulnerable"; return(AddToCache); } bool isBurrowed = false; if (!dictGilesBurrowedCache.TryGetValue(c_RActorGuid, out isBurrowed)) { isBurrowed = unit.IsBurrowed; // if the unit is NOT burrowed - we can attack them, add to cache (as IsAttackable) if (!isBurrowed) { dictGilesBurrowedCache.Add(c_RActorGuid, isBurrowed); } } if (isBurrowed) { AddToCache = false; c_IgnoreSubStep = "IsBurrowed"; return(AddToCache); } // only check for DotDPS/Bleeding in certain conditions to save CPU for everyone else // barbs with rend // All WD's // Monks with Way of the Hundred Fists + Fists of Fury if (AddToCache && ((PlayerStatus.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_Rend)) || PlayerStatus.ActorClass == ActorClass.WitchDoctor || (PlayerStatus.ActorClass == ActorClass.Monk && HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 0))) ) { ////bool hasdotDPS = c_CommonData.GetAttribute<int>(ActorAttributeType.DOTDPS) != 0; //bool isBleeding = c_CommonData.GetAttribute<int>(ActorAttributeType.Bleeding) != 0; //c_HasDotDPS = hasdotDPS && isBleeding; bool hasdotDPS = c_CommonData.GetAttribute <int>(ActorAttributeType.DOTDPS) != 0; c_HasDotDPS = hasdotDPS; } return(AddToCache); }
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; // Epiphany // Desert shroud: reduce incoming damage by 50% bool hasDesertShroud = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_Monk_Epiphany && s.RuneIndex == 0); // Soothing Mist: heal self and allies for 4129+(0.25*HealthGlobeBonus) life bool hasSoothingMist = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.X1_Monk_Epiphany && s.RuneIndex == 1); //Inner Fire X1_Monk_Epiphany/3/HotbarSlot3 //Windwalker X1_Monk_Epiphany/2/HotbarSlot3 //Soothing Mist X1_Monk_Epiphany/1/HotbarSlot3 //Ascendance X1_Monk_Epiphany/4/HotbarSlot3 //Desert Shroud X1_Monk_Epiphany/0/HotbarSlot3 //None X1_Monk_Epiphany/0/HotbarSlot3 if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.X1_Monk_Epiphany, CombatBase.CanCastFlags.NoTimer) && (TargetUtil.EliteOrTrashInRange(15f) || TargetUtil.AnyMobsInRange(15f, 5)) && (Player.PrimaryResourcePct < 0.50 || ((hasDesertShroud || hasSoothingMist) && Player.CurrentHealthPct < 0.50)) ) { return(new TrinityPower(SNOPower.X1_Monk_Epiphany)); } //// Monk - Primary bool hasThunderClap = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 0); //bool hasLightningFlash = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 4); //bool hasStaticCharge = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 3); //bool hasQuickening = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 3); //bool hasBoundingLight = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_FistsofThunder && s.RuneIndex == 1); //bool hasPiercingTrident = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 1); //bool hasKeenEye = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 4); //bool hasScatteredBlows = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 2); //bool hasStrikeFromBeyond = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 3); bool hasForesight = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_DeadlyReach && s.RuneIndex == 0); //bool hasMangle = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 0); //bool hasConcussion = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 2); //bool hasRisingTide = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 3); //bool hasTsunami = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 1); //bool hasBreakingWave = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_CripplingWave && s.RuneIndex == 4); //bool hasHandsOfLightning = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 1); bool hasBlazingFists = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 2); bool hasFistsOfFury = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 0); //bool hasSpiritedSalvo = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 3); //bool hasWindforceFlurry = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_WayOfTheHundredFists && s.RuneIndex == 4); // Breath of Heaven Rune bool hasInfusedWithLight = HotbarSkills.AssignedSkills.Any(s => s.Power == SNOPower.Monk_BreathOfHeaven && s.RuneIndex == 3); // Serenity if health is low if ((Player.CurrentHealthPct <= 0.50 || (Player.IsIncapacitated && Player.CurrentHealthPct <= 0.90)) && CombatBase.CanCast(SNOPower.Monk_Serenity)) { return(new TrinityPower(SNOPower.Monk_Serenity, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Mystic ally if (CombatBase.CanCast(SNOPower.X1_Monk_MysticAlly_v2) && TargetUtil.EliteOrTrashInRange(30f)) { return(new TrinityPower(SNOPower.X1_Monk_MysticAlly_v2, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 2)); } // InnerSanctuary if (!UseOOCBuff && TargetUtil.EliteOrTrashInRange(16f) && CombatBase.CanCast(SNOPower.X1_Monk_InnerSanctuary)) { return(new TrinityPower(SNOPower.X1_Monk_InnerSanctuary, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Blinding Flash if (!UseOOCBuff && Player.PrimaryResource >= 20 && CombatBase.CanCast(SNOPower.Monk_BlindingFlash) && ( TargetUtil.AnyElitesInRange(15, 1) || Player.CurrentHealthPct <= 0.4 || (TargetUtil.AnyMobsInRange(15, 3)) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f) || // as pre-sweeping wind buff (TargetUtil.AnyMobsInRange(15, 1) && CombatBase.CanCast(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) || (!GetHasBuff(SNOPower.Monk_SweepingWind) && (CombatBase.CanCast(SNOPower.Monk_SweepingWind, CombatBase.CanCastFlags.NoTimer))) || Player.CurrentHealthPct <= 0.25)) { return(new TrinityPower(SNOPower.Monk_BlindingFlash, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 1)); } // Blinding Flash as a DEFENSE if (!UseOOCBuff && Player.PrimaryResource >= 10 && CombatBase.CanCast(SNOPower.Monk_BlindingFlash) && Player.CurrentHealthPct <= 0.75 && TargetUtil.AnyMobsInRange(15, 1)) { return(new TrinityPower(SNOPower.Monk_BlindingFlash, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 1)); } // Breath of Heaven when needing healing or the buff if (!UseOOCBuff && (Player.CurrentHealthPct <= 0.6 || !GetHasBuff(SNOPower.Monk_BreathOfHeaven)) && CombatBase.CanCast(SNOPower.Monk_BreathOfHeaven) && (Player.PrimaryResource >= 35 || (!CombatBase.CanCast(SNOPower.Monk_Serenity) && Player.PrimaryResource >= 25))) { return(new TrinityPower(SNOPower.Monk_BreathOfHeaven, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Breath of Heaven for spirit - Infused with Light if (!UseOOCBuff && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_BreathOfHeaven) && !GetHasBuff(SNOPower.Monk_BreathOfHeaven) && hasInfusedWithLight && (TargetUtil.AnyMobsInRange(3, 20) || TargetUtil.IsEliteTargetInRange(20)) && Player.PrimaryResourcePct < 0.75) { return(new TrinityPower(SNOPower.Monk_BreathOfHeaven, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 1, 1)); } // Seven Sided Strike if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 15f) || Player.CurrentHealthPct <= 0.55) && CombatBase.CanCast(SNOPower.Monk_SevenSidedStrike, CombatBase.CanCastFlags.NoTimer) && ((Player.PrimaryResource >= 50 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_SevenSidedStrike, 16f, CurrentTarget.Position, CurrentWorldDynamicId, -1, 2, 3)); } // WayOfTheHundredFists: 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, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1)); } // Sweeping winds spam if ((Player.PrimaryResource >= 75 || (Settings.Combat.Monk.HasInnaSet && Player.PrimaryResource >= 5)) && CombatBase.CanCast(SNOPower.Monk_SweepingWind, CombatBase.CanCastFlags.NoTimer) && GetHasBuff(SNOPower.Monk_SweepingWind) && DateTime.UtcNow.Subtract(SweepWindSpam).TotalMilliseconds >= 4000 && DateTime.UtcNow.Subtract(SweepWindSpam).TotalMilliseconds <= 5400) { SweepWindSpam = DateTime.UtcNow; return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 0)); } bool hasTranscendance = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Monk_Passive_Transcendence); float minSweepingWindSpirit = Settings.Combat.Monk.HasInnaSet ? 5f : 75f; // Sweeping wind if (!UseOOCBuff && CombatBase.CanCast(SNOPower.Monk_SweepingWind) && !GetHasBuff(SNOPower.Monk_SweepingWind) && ((TargetUtil.AnyElitesInRange(25, 1) || TargetUtil.AnyMobsInRange(20, 1) || 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 (TimeSinceUse(SNOPower.Monk_BlindingFlash) <= 8000 || CheckAbilityAndBuff(SNOPower.Monk_BlindingFlash) || TargetUtil.AnyElitesInRange(25, 1) && TimeSinceUse(SNOPower.Monk_BlindingFlash) <= 12500)) && Player.PrimaryResource >= minSweepingWindSpirit) { SweepWindSpam = DateTime.UtcNow; return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 0)); } // Sweeping Wind for Transcendance Health Regen if (CombatBase.CanCast(SNOPower.Monk_SweepingWind, CombatBase.CanCastFlags.NoTimer) && Player.PrimaryResource >= minSweepingWindSpirit && hasTranscendance && Settings.Combat.Monk.SpamSweepingWindOnLowHP && Player.CurrentHealthPct <= V.F("Monk.SweepingWind.SpamOnLowHealthPct") && TimeSinceUse(SNOPower.Monk_SweepingWind) > 500) { SweepWindSpam = DateTime.UtcNow; return(new TrinityPower(SNOPower.Monk_SweepingWind, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 0, 0)); } //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); // Exploding Palm if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_ExplodingPalm, CombatBase.CanCastFlags.NoTimer) && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Monk_ExplodingPalm) && Player.PrimaryResource >= 40) { return(new TrinityPower(SNOPower.Monk_ExplodingPalm, 14f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1)); } //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 && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_WaveOfLight) && (TargetUtil.AnyMobsInRange(16f, Settings.Combat.Monk.MinWoLTrashCount) || TargetUtil.IsEliteTargetInRange(20f)) && (Player.PrimaryResource >= minWoLSpirit && !IsWaitingForSpecial || Player.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)); } //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 && !Player.IsIncapacitated && CombatBase.CanCast(SNOPower.Monk_CycloneStrike) && ( TargetUtil.AnyElitesInRange(cycloneStrikeRange, 1) || TargetUtil.AnyMobsInRange(cycloneStrikeRange, Settings.Combat.Monk.MinCycloneTrashCount) || (CurrentTarget.RadiusDistance >= 15f && CurrentTarget.RadiusDistance <= cycloneStrikeRange) // pull the current target into attack range ) && (Player.PrimaryResource >= (cycloneStrikeSpirit + MinEnergyReserve))) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_CycloneStrike, 0f, Vector3.Zero, CurrentWorldDynamicId, -1, 2, 2)); } // For tempest rush re-use if (!UseOOCBuff && Player.PrimaryResource >= 15 && CombatBase.CanCast(SNOPower.Monk_TempestRush) && TimeSinceUse(SNOPower.Monk_TempestRush) <= 150 && ((Settings.Combat.Monk.TROption != TempestRushOption.MovementOnly) && !(Settings.Combat.Monk.TROption == TempestRushOption.TrashOnly && TargetUtil.AnyElitesInRange(40f)))) { GenerateMonkZigZag(); MaintainTempestRush = true; const string trUse = "Continuing Tempest Rush for Combat"; Monk_TempestRushStatus(trUse); return(new TrinityPower(SNOPower.Monk_TempestRush, 23f, CombatBase.ZigZagPosition, CurrentWorldDynamicId, -1, 0, 0)); } // Tempest rush at elites or groups of mobs if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && !Player.IsRooted && CombatBase.CanCast(SNOPower.Monk_TempestRush) && ((Player.PrimaryResource >= Settings.Combat.Monk.TR_MinSpirit && !Player.WaitingForReserveEnergy) || Player.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; const string trUse = "Starting Tempest Rush for Combat"; Monk_TempestRushStatus(trUse); return(new TrinityPower(SNOPower.Monk_TempestRush, 23f, CombatBase.ZigZagPosition, CurrentWorldDynamicId, -1, 0, 0)); } // Lashing Tail Kick if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_LashingTailKick) && !Player.IsIncapacitated && // 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))) && ((Player.PrimaryResource >= 65 && !Player.WaitingForReserveEnergy) || Player.PrimaryResource >= MinEnergyReserve)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_LashingTailKick, 10f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 1, 1)); } // Dashing Strike if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && CurrentTarget.Distance >= 16f && CombatBase.CanCast(SNOPower.X1_Monk_DashingStrike, CombatBase.CanCastFlags.NoTimer)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.X1_Monk_DashingStrike, Monk_MaxDashingStrikeRange, CurrentTarget.Position, CurrentWorldDynamicId, -1, 2, 2)); } // 4 Mantra spam for the 4 second buff if (!UseOOCBuff && !IsCurrentlyAvoiding && !Player.IsIncapacitated && !Settings.Combat.Monk.DisableMantraSpam) { if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfConviction_v2) && !GetHasBuff(SNOPower.X1_Monk_MantraOfConviction_v2) && (Player.PrimaryResource >= 50) && CurrentTarget != null) { return(new TrinityPower(SNOPower.X1_Monk_MantraOfConviction_v2)); } if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfRetribution_v2) && !GetHasBuff(SNOPower.X1_Monk_MantraOfRetribution_v2) && (Player.PrimaryResource >= 50) && CurrentTarget != null) { return(new TrinityPower(SNOPower.X1_Monk_MantraOfRetribution_v2)); } } //Use Mantra of Healing active if health is low for shield. if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfHealing_v2) && Player.CurrentHealthPct <= V.F("Monk.MantraOfHealing.UseHealthPct") && !Player.IsIncapacitated && !GetHasBuff(SNOPower.X1_Monk_MantraOfHealing_v2)) { return(new TrinityPower(SNOPower.X1_Monk_MantraOfHealing_v2)); } if (CombatBase.CanCast(SNOPower.X1_Monk_MantraOfEvasion_v2) && Player.CurrentHealthPct <= V.F("Monk.MantraOfHealing.UseHealthPct") && !GetHasBuff(SNOPower.X1_Monk_MantraOfEvasion_v2) && CurrentTarget != null) { return(new TrinityPower(SNOPower.X1_Monk_MantraOfEvasion_v2)); } /* * Dual/Trigen Monk section * * Cycle through Deadly Reach, Way of the Hundred Fists, and Fists of Thunder every 3 seconds to keep 8% passive buff up if we have Combination Strike * - or - * Keep Foresight and Blazing Fists buffs up every 30/5 seconds */ bool hasCombinationStrike = HotbarSkills.PassiveSkills.Any(s => s == SNOPower.Monk_Passive_CombinationStrike); bool isDualOrTriGen = HotbarSkills.AssignedSkills.Count(s => s.Power == SNOPower.Monk_DeadlyReach || s.Power == SNOPower.Monk_WayOfTheHundredFists || s.Power == SNOPower.Monk_FistsofThunder || s.Power == SNOPower.Monk_CripplingWave) >= 2 && hasCombinationStrike; // interval in milliseconds for Generators int drInterval = 0; if (hasCombinationStrike) { drInterval = 2500; } else if (hasForesight) { drInterval = 29000; } int wothfInterval = 0; if (hasCombinationStrike) { wothfInterval = 2500; } else if (hasBlazingFists) { wothfInterval = 4500; } int cwInterval = 0; if (hasCombinationStrike) { cwInterval = 2500; } // Fists of Thunder:Thunder Clap - Fly to Target if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_FistsofThunder) && hasThunderClap && CurrentTarget.Distance > 16f) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_FistsofThunder, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } // Deadly Reach: Foresight, every 27 seconds or 2.7 seconds with combo strike if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_DeadlyReach) && (isDualOrTriGen || hasForesight) && (SpellHistory.TimeSinceUse(SNOPower.Monk_DeadlyReach) > TimeSpan.FromMilliseconds(drInterval) || (SpellHistory.SpellUseCountInTime(SNOPower.Monk_DeadlyReach, TimeSpan.FromMilliseconds(27000)) < 3) && hasForesight)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_DeadlyReach, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } // Way of the Hundred Fists: Blazing Fists, every 4-5ish seconds or if we don't have 3 stacks of the buff or or 2.7 seconds with combo strike if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_WayOfTheHundredFists) && (isDualOrTriGen || hasBlazingFists) && (GetBuffStacks(SNOPower.Monk_WayOfTheHundredFists) < 3 || SpellHistory.TimeSinceUse(SNOPower.Monk_WayOfTheHundredFists) > TimeSpan.FromMilliseconds(wothfInterval))) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } // Crippling Wave if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_CripplingWave) && SpellHistory.TimeSinceUse(SNOPower.Monk_CripplingWave) > TimeSpan.FromMilliseconds(cwInterval)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_CripplingWave, 20f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } // Fists of Thunder if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_FistsofThunder)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_FistsofThunder, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } // Deadly Reach normal if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_DeadlyReach)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_DeadlyReach, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } // Way of the Hundred Fists normal if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_WayOfTheHundredFists)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } // Crippling Wave Normal if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_CripplingWave)) { Monk_TickSweepingWindSpam(); return(new TrinityPower(SNOPower.Monk_CripplingWave, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 3)); } //// Fists of thunder as the primary, repeatable attack //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_FistsofThunder) // && (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach) || CurrentTarget.RadiusDistance > 12f || // !TargetUtil.AnyMobsInRange(50, 5) && !TargetUtil.AnyElitesInRange(50) && !WantToSwap)) //{ // if (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700) // OtherThanDeadlyReach = DateTime.UtcNow; // Monk_TickSweepingWindSpam(); // return new TrinityPower(SNOPower.Monk_FistsofThunder, 30f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1); //} //// Crippling wave //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_CripplingWave) // && (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach) // || !TargetUtil.AnyMobsInRange(50, 5) && !TargetUtil.AnyElitesInRange(50) && !WantToSwap)) //{ // OtherThanDeadlyReach = DateTime.UtcNow; // Monk_TickSweepingWindSpam(); // return new TrinityPower(SNOPower.Monk_CripplingWave, 14f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1); //} //// Way of hundred fists //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_WayOfTheHundredFists) // && (DateTime.UtcNow.Subtract(OtherThanDeadlyReach).TotalMilliseconds < 2700 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds < 29000 || !Hotbar.Contains(SNOPower.Monk_DeadlyReach) // || !TargetUtil.AnyMobsInRange(50, 5) && !TargetUtil.AnyElitesInRange(50) && !WantToSwap)) //{ // OtherThanDeadlyReach = DateTime.UtcNow; // Monk_TickSweepingWindSpam(); // return new TrinityPower(SNOPower.Monk_WayOfTheHundredFists, 14f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1); //} //// Deadly reach //if (!UseOOCBuff && !IsCurrentlyAvoiding && CombatBase.CanCast(SNOPower.Monk_DeadlyReach)) //{ // if (DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds > 29000) // { // ForeSightFirstHit = DateTime.UtcNow; // } // else if (DateTime.UtcNow.Subtract(ForeSight2).TotalMilliseconds > 400 && DateTime.UtcNow.Subtract(ForeSightFirstHit).TotalMilliseconds > 1400) // { // OtherThanDeadlyReach = DateTime.UtcNow; // } // if (DateTime.UtcNow.Subtract(ForeSight2).TotalMilliseconds > 2800) // { // ForeSight2 = DateTime.UtcNow; // } // Monk_TickSweepingWindSpam(); // return new TrinityPower(SNOPower.Monk_DeadlyReach, 16f, Vector3.Zero, -1, CurrentTarget.ACDGuid, 0, 1); //} // Default attacks return(CombatBase.DefaultPower); }
public static TrinityPower GetPower() { TrinityPower power = null; // Spirit Walk, always! if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } var hasAngryChicken = (Skills.WitchDoctor.Hex.IsActive && Runes.WitchDoctor.AngryChicken.IsActive) || CacheData.Hotbar.ActivePowers.Contains(SNOPower.Witchdoctor_Hex_ChickenWalk); if (hasAngryChicken && Sets.ManajumasWay.IsEquipped && CanCast(SNOPower.Witchdoctor_Hex)) { return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } // Combat Avoidance Spells if (!UseOOCBuff && IsCurrentlyAvoiding) { // Spirit Walk out of AoE if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Soul harvest at current location while avoiding if (Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && MinimumSoulHarvestCriteria(Enemies.BestCluster)) { Skills.WitchDoctor.SoulHarvest.Cast(); } } // Incapacitated or Rooted if (!UseOOCBuff && (Player.IsIncapacitated || Player.IsRooted)) { // Spirit Walk if (CanCast(SNOPower.Witchdoctor_SpiritWalk)) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } } // Combat Spells with a Target if (!UseOOCBuff && !IsCurrentlyAvoiding && CurrentTarget != null) { if (_bastianGeneratorWaitTimer.IsFinished && ShouldRefreshBastiansGeneratorBuff) { if (Hotbar.Contains(SNOPower.Witchdoctor_CorpseSpider)) { return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, 50f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.Witchdoctor_PoisonDart)) { return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, 50f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.Witchdoctor_PlagueOfToads)) { return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, 50f, CurrentTarget.ACDGuid)); } if (Hotbar.Contains(SNOPower.Witchdoctor_Firebomb)) { return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, 50f, CurrentTarget.ACDGuid)); } _bastianGeneratorWaitTimer.Reset(); } bool hasGraveInjustice = CacheData.Hotbar.PassiveSkills.Contains(SNOPower.Witchdoctor_Passive_GraveInjustice); // Debug.Print(CacheData.Hotbar.GetSkill(SNOPower.Witchdoctor_Hex).RuneIndex.ToString()); var isChicken = CacheData.Hotbar.ActivePowers.Contains(SNOPower.Witchdoctor_Hex_ChickenWalk); bool hasVisionQuest = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_VisionQuest); // Set max ranged attack range, based on Grave Injustice, and current target NOT standing in avoidance, and health > 25% float rangedAttackMaxRange = 30f; if (hasGraveInjustice && !CurrentTarget.IsStandingInAvoidance && Player.CurrentHealthPct > 0.25) { rangedAttackMaxRange = Math.Min(Player.GoldPickupRadius + 8f, 30f); } // Set basic attack range, depending on whether or not we have Bears and whether or not we are a tik tank float basicAttackRange = 35f; if (hasGraveInjustice) { basicAttackRange = rangedAttackMaxRange; } else if (Hotbar.Contains(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { basicAttackRange = 30f; } else if (Legendary.TiklandianVisage.IsEquipped && !TikHorrifyCriteria(Enemies.BestLargeCluster)) { basicAttackRange = 25f; } else if (Legendary.TiklandianVisage.IsEquipped) { basicAttackRange = 1f; } // Summon Pets ----------------------------------------------------------------------- // Hex with angry chicken, is chicken, explode! if (isChicken && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f || UseDestructiblePower)) // && CanCast(SNOPower.Witchdoctor_Hex_Explode) { //Debug.Print("Attempting to cast HEx Explosion {0}", ZetaDia.Me.UsePower(SNOPower.Witchdoctor_Hex_Explode, ZetaDia.Me.Position, // ZetaDia.CurrentWorldDynamicId)); return(new TrinityPower(SNOPower.Witchdoctor_Hex_Explode)); } bool hasJaunt = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 1); bool hasHonoredGuest = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 3); bool hasUmbralShock = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 2); bool hasSeverance = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 0); bool hasHealingJourney = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritWalk && s.RuneIndex == 4); // Spirit Walk for Goblins chasing if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && CurrentTarget.IsTreasureGoblin && CurrentTarget.HitPointsPct < 0.90 && CurrentTarget.RadiusDistance <= 40f) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Spirit Walk < 65% Health: Healing Journey if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHealingJourney && Player.CurrentHealthPct <= V.F("WitchDoctor.SpiritWalk.HealingJourneyHealth")) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } // Spirit Walk < 50% Mana: Honored Guest if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && hasHonoredGuest && Player.PrimaryResourcePct <= V.F("WitchDoctor.SpiritWalk.HonoredGuestMana")) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } //bool shouldRefreshVisionQuest = GetTimeSinceLastVisionQuestRefresh() > 4000; bool shouldRefreshVisionQuest = !GetHasBuff(SNOPower.Witchdoctor_Passive_VisionQuest) || GetTimeSinceLastVisionQuestRefresh() > 3800; // Vision Quest Passive if (hasVisionQuest && shouldRefreshVisionQuest) { // Poison Darts if (CanCast(SNOPower.Witchdoctor_PoisonDart)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid)); } // Corpse Spiders if (CanCast(SNOPower.Witchdoctor_CorpseSpider)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid)); } // Plague Of Toads if (CanCast(SNOPower.Witchdoctor_PlagueOfToads)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid)); } // Fire Bomb if (CanCast(SNOPower.Witchdoctor_Firebomb)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid));; } } bool hasVengefulSpirit = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 4); bool hasSwallowYourSoul = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SoulHarvest && s.RuneIndex == 3); // START Jade Harvester ----------------------------------------------------------------------- if (Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive) { //LogTargetArea("BestLargeCluster", Enemies.BestLargeCluster); //LogTargetArea("BestCluster", Enemies.BestCluster); //LogTargetArea("Nearby", Enemies.Nearby); //LogTargetArea("CloseNearby", Enemies.CloseNearby); // Piranhas if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 && (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) && LastPowerUsed != SNOPower.Witchdoctor_Piranhas && Player.PrimaryResource >= 250) { return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, Enemies.BestCluster.Position)); } // Should we move to cluster for harvest if (IdealSoulHarvestCriteria(Enemies.BestLargeCluster)) { //LogTargetArea("--- Found a good harvest location...", Enemies.BestLargeCluster); MoveToSoulHarvestPoint(Enemies.BestLargeCluster); } // Is there a slightly better position than right here if (MinimumSoulHarvestCriteria(Enemies.BestCluster) && (Enemies.BestCluster.EliteCount >= 2 || Enemies.BestCluster.UnitCount > 4)) { //LogTargetArea("--- Found an average harvest location...", Enemies.BestCluster); MoveToSoulHarvestPoint(Enemies.BestCluster); } // Should we harvest right here? if (MinimumSoulHarvestCriteria(Enemies.CloseNearby)) { //LogTargetArea("--- Harvesting (CurrentPosition)", Enemies.CloseNearby); return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Locust Swarm if (CanCast(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource >= 300 && !CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm)) { return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 20f, CurrentTarget.ACDGuid)); } // Haunt if (Skills.WitchDoctor.Haunt.CanCast() && Player.PrimaryResource >= 50 && !CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Haunt)) { return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 45f, CurrentTarget.ACDGuid)); } // Acid Cloud if (Skills.WitchDoctor.AcidCloud.CanCast() && Player.PrimaryResource >= 325 && LastPowerUsed != SNOPower.Witchdoctor_AcidCloud) { Vector3 bestClusterPoint; if (Passives.WitchDoctor.GraveInjustice.IsActive) { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(Player.GoldPickupRadius + 8f, 30f)); } else { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f); } return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, rangedAttackMaxRange, bestClusterPoint)); } // Spread the love around if (!CurrentTarget.IsTreasureGoblin && CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) && CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Haunt) && Enemies.Nearby.UnitCount > 3 && Enemies.Nearby.DebuffedPercent(HarvesterCoreDebuffs) < 0.5) { //var oldTarget = Trinity.CurrentTarget; Trinity.Blacklist3Seconds.Add(CurrentTarget.RActorGuid); Trinity.CurrentTarget = Enemies.BestCluster.GetTargetWithoutDebuffs(HarvesterCoreDebuffs); //Logger.LogNormal("{0} {1} is fully debuffed, switched to {2} {3}", oldTarget.InternalName, oldTarget.ACDGuid, CurrentTarget.InternalName, CurrentTarget.ACDGuid); } // Save mana for locust swarm || piranhas if (!CurrentTarget.HasDebuff(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource < 300) { //Logger.LogNormal("Saving mana"); return(DefaultPower); } } // END Jade Harvester ----------------------------------------------------------------------- // Tiklandian Visage ---------------------------------------------------------------------- // Constantly casts Horrify and moves the middle of clusters if (Legendary.TiklandianVisage.IsEquipped) { // Piranhas if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 && (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) && LastPowerUsed != SNOPower.Witchdoctor_Piranhas && Player.PrimaryResource >= 250) { return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, Enemies.BestCluster.Position)); } //Cast Horrify before we go into the fray if (CanCast(SNOPower.Witchdoctor_Horrify)) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Should we move to a better position to fear people if (TikHorrifyCriteria(Enemies.BestLargeCluster)) { MoveToHorrifyPoint(Enemies.BestLargeCluster); } } // END Tiklandian Visage ---------------------------------------------------------------------- // Sacrifice if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 && (TargetUtil.AnyElitesInRange(15, 1) || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 9f))) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice)); } // Sacrifice for Circle of Life bool hasCircleofLife = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_CircleOfLife); if (CanCast(SNOPower.Witchdoctor_Sacrifice) && Trinity.PlayerOwnedZombieDogCount > 0 && hasCircleofLife && TargetUtil.AnyMobsInRange(15f)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice)); } // Wall of Zombies if (CanCast(SNOPower.Witchdoctor_WallOfZombies) && (TargetUtil.AnyElitesInRange(15, 1) || TargetUtil.AnyMobsInRange(15, 1) || ((CurrentTarget.IsEliteRareUnique || CurrentTarget.IsTreasureGoblin || CurrentTarget.IsBoss) && CurrentTarget.RadiusDistance <= 25f))) { return(new TrinityPower(SNOPower.Witchdoctor_WallOfZombies, 25f, CurrentTarget.Position)); } bool hasRestlessGiant = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0); bool hasWrathfulProtector = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3); if (CanCast(SNOPower.Witchdoctor_Gargantuan)) { // Gargantuan, Recast on Elites or Bosses to trigger Restless Giant if (hasRestlessGiant && (TargetUtil.IsEliteTargetInRange(30f) || Trinity.PlayerOwnedGargantuanCount == 0)) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } // Gargantuan Wrathful Protector, 15 seconds of smash, use sparingly! if (hasWrathfulProtector && TargetUtil.IsEliteTargetInRange(30f)) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } // Gargantuan regular if (!hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } } bool hasSacrifice = Hotbar.Contains(SNOPower.Witchdoctor_Sacrifice); // Zombie Dogs for Sacrifice if (hasSacrifice && CanCast(SNOPower.Witchdoctor_SummonZombieDog) && (LastPowerUsed == SNOPower.Witchdoctor_Sacrifice || Trinity.PlayerOwnedZombieDogCount <= 2) && LastPowerUsed != SNOPower.Witchdoctor_SummonZombieDog) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog)); } // Hex with angry chicken, check if we want to shape shift and explode if (CanCast(SNOPower.Witchdoctor_Hex) && (TargetUtil.AnyMobsInRange(12f, 1, false) || CurrentTarget.RadiusDistance <= 10f) && hasAngryChicken) { return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } // Hex Spam Cast without angry chicken if (CanCast(SNOPower.Witchdoctor_Hex) && !hasAngryChicken && (TargetUtil.AnyElitesInRange(12) || TargetUtil.AnyMobsInRange(12, 2) || TargetUtil.IsEliteTargetInRange(18f))) { return(new TrinityPower(SNOPower.Witchdoctor_Hex)); } if (CanCast(SNOPower.Witchdoctor_SoulHarvest) && (TargetUtil.AnyElitesInRange(16) || TargetUtil.AnyMobsInRange(16, 2) || TargetUtil.IsEliteTargetInRange(16f))) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Mass Confuse, elites only or big mobs or to escape on low health if (CanCast(SNOPower.Witchdoctor_MassConfusion) && (TargetUtil.AnyElitesInRange(12, 1) || TargetUtil.AnyMobsInRange(12, 6) || Player.CurrentHealthPct <= 0.25 || (CurrentTarget.IsBossOrEliteRareUnique && CurrentTarget.RadiusDistance <= 12f)) && !CurrentTarget.IsTreasureGoblin) { return(new TrinityPower(SNOPower.Witchdoctor_MassConfusion, 0f, CurrentTarget.ACDGuid)); } if (!Settings.Combat.WitchDoctor.UseBigBadVoodooOffCooldown) { // Big Bad Voodoo, elites and bosses only if (CanCast(SNOPower.Witchdoctor_BigBadVoodoo) && (TargetUtil.EliteOrTrashInRange(25f) || (CurrentTarget.IsBoss && CurrentTarget.Distance <= 30f))) { return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo)); } } else { // Big Bad Voodo, cast whenever available if (!UseOOCBuff && !Player.IsIncapacitated && CanCast(SNOPower.Witchdoctor_BigBadVoodoo)) { return(new TrinityPower(SNOPower.Witchdoctor_BigBadVoodoo)); } } // Grasp of the Dead if (CanCast(SNOPower.Witchdoctor_GraspOfTheDead) && (TargetUtil.AnyMobsInRange(30, 2) || TargetUtil.EliteOrTrashInRange(30f)) && Player.PrimaryResource >= 150) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(); return(new TrinityPower(SNOPower.Witchdoctor_GraspOfTheDead, 25f, bestClusterPoint)); } // Piranhas if (CanCast(SNOPower.Witchdoctor_Piranhas) && Player.PrimaryResource >= 250 && (TargetUtil.ClusterExists(15f, 45f) || TargetUtil.AnyElitesInRange(45f)) && Player.PrimaryResource >= 250) { var bestClusterPoint = TargetUtil.GetBestClusterPoint(); return(new TrinityPower(SNOPower.Witchdoctor_Piranhas, 25f, bestClusterPoint)); } bool hasPhobia = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 2); bool hasStalker = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4); bool hasFaceOfDeath = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 1); bool hasFrighteningAspect = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 0); bool hasRuthlessTerror = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 3); float horrifyRadius = hasFaceOfDeath ? 24f : 12f; // Horrify when low on health if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= EmergencyHealthPotionLimit && TargetUtil.AnyMobsInRange(horrifyRadius, 3)) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Horrify Buff at 35% health -- Freightening Aspect if (CanCast(SNOPower.Witchdoctor_Horrify) && Player.CurrentHealthPct <= 0.35 && hasFrighteningAspect) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Spam Horrify if (CanCast(SNOPower.Witchdoctor_Horrify) && Settings.Combat.WitchDoctor.SpamHorrify) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Fetish Army, elites only if (CanCast(SNOPower.Witchdoctor_FetishArmy) && (TargetUtil.EliteOrTrashInRange(30f) || TargetUtil.IsEliteTargetInRange(30f) || Settings.Combat.WitchDoctor.UseFetishArmyOffCooldown)) { return(new TrinityPower(SNOPower.Witchdoctor_FetishArmy)); } bool hasManitou = Runes.WitchDoctor.Manitou.IsActive; // Spirit Barrage Manitou if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && TimeSincePowerUse(SNOPower.Witchdoctor_SpiritBarrage) > 18000 && hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage)); } bool hasResentfulSpirit = Runes.WitchDoctor.ResentfulSpirits.IsActive; // Haunt if (CanCast(SNOPower.Witchdoctor_Haunt) && Player.PrimaryResource >= 50 && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Haunt) && LastPowerUsed != SNOPower.Witchdoctor_Haunt) { return(new TrinityPower(SNOPower.Witchdoctor_Haunt, 21f, CurrentTarget.ACDGuid)); } //skillDict.Add("LocustSwarm", SNOPower.Witchdoctor_Locust_Swarm); // Locust Swarm if (CanCast(SNOPower.Witchdoctor_Locust_Swarm) && Player.PrimaryResource >= 300 && !SpellTracker.IsUnitTracked(CurrentTarget, SNOPower.Witchdoctor_Locust_Swarm) && LastPowerUsed != SNOPower.Witchdoctor_Locust_Swarm) { return(new TrinityPower(SNOPower.Witchdoctor_Locust_Swarm, 12f, CurrentTarget.ACDGuid)); } // Sacrifice for 0 Dogs if (CanCast(SNOPower.Witchdoctor_Sacrifice) && (Settings.Combat.WitchDoctor.ZeroDogs || !WitchDoctorHasPrimaryAttack)) { return(new TrinityPower(SNOPower.Witchdoctor_Sacrifice, 9f)); } var zombieChargerRange = hasGraveInjustice ? Math.Min(Player.GoldPickupRadius + 8f, 11f) : 11f; // Zombie Charger aka Zombie bears Spams Bears @ Everything from 11feet away if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position)); } // Soul Harvest Any Elites or to increase buff stacks if (!Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && CanCast(SNOPower.Witchdoctor_SoulHarvest) && (TargetUtil.AnyMobsInRange(16f, GetBuffStacks(SNOPower.Witchdoctor_SoulHarvest) + 1, false) || (hasSwallowYourSoul && Player.PrimaryResourcePct <= 0.50) || TargetUtil.IsEliteTargetInRange(16f))) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } // Soul Harvest with VengefulSpirit if (!Sets.RaimentOfTheJadeHarvester.IsMaxBonusActive && CanCast(SNOPower.Witchdoctor_SoulHarvest) && hasVengefulSpirit && TargetUtil.AnyMobsInRange(16, 3)) { return(new TrinityPower(SNOPower.Witchdoctor_SoulHarvest)); } bool hasDireBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 0); bool hasVampireBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 3); bool hasPlagueBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 2); bool hasHungryBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 1); bool hasCloudOfBats = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Firebats && s.RuneIndex == 4); int fireBatsChannelCost = hasVampireBats ? 0 : 75; int fireBatsMana = TimeSincePowerUse(SNOPower.Witchdoctor_Firebats) < 125 ? fireBatsChannelCost : 225; bool firebatsMaintain = Trinity.ObjectCache.Any(u => u.IsUnit && u.IsPlayerFacing(70f) && u.Weight > 0 && u.Distance <= V.F("WitchDoctor.Firebats.MaintainRange") && SpellHistory.TimeSinceUse(SNOPower.Witchdoctor_Firebats) <= TimeSpan.FromMilliseconds(250d)); // Fire Bats:Cloud of bats if (hasCloudOfBats && (TargetUtil.AnyMobsInRange(8f) || firebatsMaintain) && CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana) { var range = Settings.Combat.WitchDoctor.FirebatsRange > 12f ? 12f : Settings.Combat.WitchDoctor.FirebatsRange; return(new TrinityPower(SNOPower.Witchdoctor_Firebats, range, CurrentTarget.ACDGuid)); } // Fire Bats fast-attack if (CanCast(SNOPower.Witchdoctor_Firebats) && Player.PrimaryResource >= fireBatsMana && (TargetUtil.AnyMobsInRange(Settings.Combat.WitchDoctor.FirebatsRange) || firebatsMaintain) && !hasCloudOfBats) { return(new TrinityPower(SNOPower.Witchdoctor_Firebats, Settings.Combat.WitchDoctor.FirebatsRange, CurrentTarget.Position)); } // Acid Cloud if (CanCast(SNOPower.Witchdoctor_AcidCloud) && Player.PrimaryResource >= 175) { Vector3 bestClusterPoint; if (hasGraveInjustice) { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, Math.Min(Player.GoldPickupRadius + 8f, 30f)); } else { bestClusterPoint = TargetUtil.GetBestClusterPoint(15f, 30f); } return(new TrinityPower(SNOPower.Witchdoctor_AcidCloud, rangedAttackMaxRange, bestClusterPoint)); } bool hasWellOfSouls = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_SpiritBarrage && s.RuneIndex == 1); bool hasRushOfEssence = CacheData.Hotbar.PassiveSkills.Any(s => s == SNOPower.Witchdoctor_Passive_RushOfEssence); // Spirit Barrage + Rush of Essence if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && hasRushOfEssence && !hasManitou) { if (hasWellOfSouls) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid)); } return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, 21f, CurrentTarget.ACDGuid)); } // Zombie Charger backup if (CanCast(SNOPower.Witchdoctor_ZombieCharger) && Player.PrimaryResource >= 150) { return(new TrinityPower(SNOPower.Witchdoctor_ZombieCharger, zombieChargerRange, CurrentTarget.Position)); } // Regular spirit barage if (CanCast(SNOPower.Witchdoctor_SpiritBarrage) && Player.PrimaryResource >= 100 && !hasManitou) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritBarrage, basicAttackRange, CurrentTarget.ACDGuid)); } // Poison Darts fast-attack Spams Darts when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_PoisonDart)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PoisonDart, basicAttackRange, CurrentTarget.ACDGuid)); } // Corpse Spiders fast-attacks Spams Spiders when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_CorpseSpider)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_CorpseSpider, basicAttackRange, CurrentTarget.ACDGuid)); } // Toads fast-attacks Spams Toads when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_PlagueOfToads)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_PlagueOfToads, basicAttackRange, CurrentTarget.ACDGuid)); } // Fire Bomb fast-attacks Spams Bomb when mana is too low (to cast bears) @12yds or @10yds if Bears avialable if (CanCast(SNOPower.Witchdoctor_Firebomb)) { VisionQuestRefreshTimer.Restart(); return(new TrinityPower(SNOPower.Witchdoctor_Firebomb, basicAttackRange, CurrentTarget.ACDGuid)); } //Hexing Pants Mod if (Legendary.HexingPantsOfMrYan.IsEquipped && CurrentTarget.IsUnit && //!CanCast(SNOPower.Witchdoctor_Piranhas) && CurrentTarget.RadiusDistance > 10f) { return(new TrinityPower(SNOPower.Walk, 10f, CurrentTarget.Position)); } if (Legendary.HexingPantsOfMrYan.IsEquipped && CurrentTarget.IsUnit && //!CanCast(SNOPower.Witchdoctor_Piranhas) && CurrentTarget.RadiusDistance < 10f) { Vector3 vNewTarget = MathEx.CalculatePointFrom(CurrentTarget.Position, Player.Position, -10f); return(new TrinityPower(SNOPower.Walk, 10f, vNewTarget)); } } // Buffs if (UseOOCBuff) { // Spirit Walk OOC if (CanCast(SNOPower.Witchdoctor_SpiritWalk) && Settings.Combat.Misc.AllowOOCMovement) { return(new TrinityPower(SNOPower.Witchdoctor_SpiritWalk)); } //Spam fear at all times if Tiklandian Visage is ewquipped and fear spam is selected to keep fear buff active if (CanCast(SNOPower.Witchdoctor_Horrify) && Settings.Combat.WitchDoctor.SpamHorrify && Legendary.TiklandianVisage.IsEquipped) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } bool hasStalker = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Horrify && s.RuneIndex == 4); // Horrify Buff When not in combat for movement speed -- Stalker if (CanCast(SNOPower.Witchdoctor_Horrify) && hasStalker) { return(new TrinityPower(SNOPower.Witchdoctor_Horrify)); } // Zombie Dogs non-sacrifice build if (CanCast(SNOPower.Witchdoctor_SummonZombieDog) && ((Legendary.TheTallMansFinger.IsEquipped && Trinity.PlayerOwnedZombieDogCount < 1) || (!Legendary.TheTallMansFinger.IsEquipped && Trinity.PlayerOwnedZombieDogCount <= 2))) { return(new TrinityPower(SNOPower.Witchdoctor_SummonZombieDog)); } bool hasRestlessGiant = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 0); bool hasWrathfulProtector = CacheData.Hotbar.ActiveSkills.Any(s => s.Power == SNOPower.Witchdoctor_Gargantuan && s.RuneIndex == 3); if (CanCast(SNOPower.Witchdoctor_Gargantuan) && !hasRestlessGiant && !hasWrathfulProtector && Trinity.PlayerOwnedGargantuanCount == 0) { return(new TrinityPower(SNOPower.Witchdoctor_Gargantuan)); } } // Default Attacks if (IsNull(power)) { power = DefaultPower; } return(power); }
private static bool RefreshGold(bool AddToCache) { AddToCache = true; if (!Settings.Loot.Pickup.PickupGold) { c_IgnoreSubStep = "PickupDisabled"; AddToCache = false; return(AddToCache); } if (Player.ActorClass == ActorClass.Barbarian && Settings.Combat.Barbarian.IgnoreGoldInWOTB && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) { AddToCache = false; c_IgnoreSubStep = "IgnoreGoldInWOTB"; return(AddToCache); } // Get the gold amount of this pile, cached if possible if (!CacheData.GoldStack.TryGetValue(CurrentCacheObject.RActorGuid, out c_GoldStackSize)) { try { c_GoldStackSize = ((ACDItem)CurrentCacheObject.CommonData).Gold; } catch { Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting gold pile amount for item {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO); AddToCache = false; c_IgnoreSubStep = "GetAttributeException"; } CacheData.GoldStack.Add(CurrentCacheObject.RActorGuid, c_GoldStackSize); } if (c_GoldStackSize < Settings.Loot.Pickup.MinimumGoldStack) { AddToCache = false; c_IgnoreSubStep = "NotEnoughGold"; return(AddToCache); } if (CurrentCacheObject.Distance <= Player.GoldPickupRadius) { AddToCache = false; c_IgnoreSubStep = "WithinPickupRadius"; return(AddToCache); } return(AddToCache); }
private static bool RefreshAvoidance(bool AddToCache) { AddToCache = true; try { CurrentCacheObject.Animation = CurrentCacheObject.Object.CommonData.CurrentAnimation; } catch (Exception ex) { Logger.LogDebug(LogCategory.CacheManagement, "Error reading CurrentAnimation for AoE sno:{0} raGuid:{1} name:{2} ex:{3}", CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message); } float customRadius; if (DataDictionary.DefaultAvoidanceCustomRadius.TryGetValue(CurrentCacheObject.ActorSNO, out customRadius)) { CurrentCacheObject.Radius = customRadius; } double minAvoidanceHealth = GetAvoidanceHealth(CurrentCacheObject.ActorSNO); double minAvoidanceRadius = GetAvoidanceRadius(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); // Are we allowed to path around avoidance? if (Settings.Combat.Misc.AvoidanceNavigation) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, (float)minAvoidanceRadius); } AvoidanceType avoidanceType = AvoidanceManager.GetAvoidanceType(CurrentCacheObject.ActorSNO); // Beast Charge should set aoe position as players current position! if (avoidanceType == AvoidanceType.BeastCharge) { CurrentCacheObject.Position = Trinity.Player.Position; } // Monks with Serenity up ignore all AOE's if (Player.ActorClass == ActorClass.Monk && Hotbar.Contains(SNOPower.Monk_Serenity) && GetHasBuff(SNOPower.Monk_Serenity)) { // Monks with serenity are immune minAvoidanceHealth *= V.F("Monk.Avoidance.Serenity"); Logger.Log(TrinityLogLevel.Debug, LogCategory.Avoidance, "Ignoring avoidance as a Monk with Serenity"); } // Witch doctors with spirit walk available and not currently Spirit Walking will subtly ignore ice balls, arcane, desecrator & plague cloud if (Player.ActorClass == ActorClass.Witchdoctor && Hotbar.Contains(SNOPower.Witchdoctor_SpiritWalk) && GetHasBuff(SNOPower.Witchdoctor_SpiritWalk)) { if (avoidanceType == AvoidanceType.IceBall || avoidanceType == AvoidanceType.Arcane || avoidanceType == AvoidanceType.Desecrator || avoidanceType == AvoidanceType.PlagueCloud) { // Ignore ICE/Arcane/Desc/PlagueCloud altogether with spirit walk up or available minAvoidanceHealth *= V.F("WitchDoctor.Avoidance.SpiritWalk"); Logger.Log(TrinityLogLevel.Debug, LogCategory.Avoidance, "Ignoring avoidance as a WitchDoctor with Spirit Walk"); } } // Remove ice balls if the barbarian has wrath of the berserker up, and reduce health from most other SNO avoidances if (Player.ActorClass == ActorClass.Barbarian && Settings.Combat.Barbarian.IgnoreAvoidanceInWOTB && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) { switch (avoidanceType) { case AvoidanceType.IceBall: minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.IceBall"); break; case AvoidanceType.Arcane: minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.Arcane"); break; case AvoidanceType.Desecrator: minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.Desecrator"); break; case AvoidanceType.Belial: minAvoidanceHealth = V.F("Barbarian.Avoidance.WOTB.Belial"); break; case AvoidanceType.PoisonTree: minAvoidanceHealth = V.F("Barbarian.Avoidance.WOTB.PoisonTree"); break; case AvoidanceType.BeastCharge: minAvoidanceHealth = V.F("Barbarian.Avoidance.WOTB.BeastCharge"); break; default: minAvoidanceHealth *= V.F("Barbarian.Avoidance.WOTB.Other"); break; } } if (minAvoidanceHealth == 0) { AddToCache = false; Logger.Log(TrinityLogLevel.Verbose, LogCategory.Avoidance, "Ignoring Avoidance! Name={0} SNO={1} radius={2:0} health={3:0.00} dist={4:0}", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO, minAvoidanceRadius, minAvoidanceHealth, CurrentCacheObject.Distance); return(AddToCache); } // Add it to the list of known avoidance objects, *IF* our health is lower than this avoidance health limit if (minAvoidanceHealth >= Player.CurrentHealthPct) { float avoidanceRadius = (float)GetAvoidanceRadius(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); TimeSpan aoeExpiration; DataDictionary.AvoidanceSpawnerDuration.TryGetValue(CurrentCacheObject.ActorSNO, out aoeExpiration); CacheData.TimeBoundAvoidance.Add(new CacheObstacleObject(CurrentCacheObject.Position, avoidanceRadius, CurrentCacheObject.ActorSNO, CurrentCacheObject.InternalName) { Expires = DateTime.UtcNow.Add(aoeExpiration), ObjectType = GObjectType.Avoidance, Rotation = CurrentCacheObject.Rotation }); // Is this one under our feet? If so flag it up so we can find an avoidance spot if (CurrentCacheObject.Distance <= minAvoidanceRadius) { _standingInAvoidance = true; // Note if this is a travelling projectile or not so we can constantly update our safe points if (DataDictionary.AvoidanceProjectiles.Contains(CurrentCacheObject.ActorSNO)) { _isAvoidingProjectiles = true; Logger.Log(TrinityLogLevel.Verbose, LogCategory.Avoidance, "Is standing in avoidance for projectile Name={0} SNO={1} radius={2:0} health={3:0.00} dist={4:0}", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO, minAvoidanceRadius, minAvoidanceHealth, CurrentCacheObject.Distance); } else { Logger.Log(TrinityLogLevel.Verbose, LogCategory.Avoidance, "Is standing in avoidance Name={0} SNO={1} radius={2:0} health={3:0.00} dist={4:0}", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO, minAvoidanceRadius, minAvoidanceHealth, CurrentCacheObject.Distance); } } } return(AddToCache); }
private static bool RefreshGizmo(bool AddToCache) { if (!Settings.WorldObject.AllowPlayerResurection && CurrentCacheObject.ActorSNO == DataDictionary.PLAYER_HEADSTONE_SNO) { c_IgnoreSubStep = "IgnoreHeadstones"; AddToCache = false; return(AddToCache); } // start as true, then set as false as we go. If nothing matches below, it will return true. AddToCache = true; bool openResplendentChest = CurrentCacheObject.InternalName.ToLower().Contains("chest_rare"); // Ignore it if it's not in range yet, except health wells and resplendent chests if we're opening chests if ((CurrentCacheObject.RadiusDistance > CurrentBotLootRange || CurrentCacheObject.RadiusDistance > 50) && CurrentCacheObject.Type != GObjectType.HealthWell && CurrentCacheObject.Type != GObjectType.Shrine && CurrentCacheObject.RActorGuid != LastTargetRactorGUID) { AddToCache = false; c_IgnoreSubStep = "NotInRange"; } // re-add resplendent chests if (openResplendentChest) { AddToCache = true; c_IgnoreSubStep = ""; } try { CurrentCacheObject.IsBountyObjective = (CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.BountyObjective) > 0); } catch (Exception) { Logger.LogDebug("Error refreshing IsBountyObjective"); } try { CurrentCacheObject.IsQuestMonster = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.QuestMonster) > 1; } catch (Exception ex) { Logger.LogDebug(LogCategory.CacheManagement, "Error reading IsQuestMonster for Unit sno:{0} raGuid:{1} name:{2} ex:{3}", CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message); } try { CurrentCacheObject.IsMinimapActive = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.MinimapActive) > 0; } catch (Exception ex) { Logger.LogDebug(LogCategory.CacheManagement, "Error reading MinimapActive for Gizmo sno:{0} raGuid:{1} name:{2} ex:{3}", CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message); } try { CurrentCacheObject.IsQuestMonster = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.QuestMonster) > 0; } catch (Exception ex) { Logger.LogDebug(LogCategory.CacheManagement, "Error reading IsQuestMonster for Gizmo sno:{0} raGuid:{1} name:{2} ex:{3}", CurrentCacheObject.ActorSNO, CurrentCacheObject.RActorGuid, CurrentCacheObject.InternalName, ex.Message); } if (CurrentCacheObject.Gizmo != null) { CurrentCacheObject.GizmoType = CurrentCacheObject.Gizmo.ActorInfo.GizmoType; } // Anything that's been disabled by a script bool isGizmoDisabledByScript = false; try { if (CurrentCacheObject.Object is DiaGizmo) { isGizmoDisabledByScript = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.GizmoDisabledByScript) > 0; } } catch { CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting Gizmo-Disabled-By-Script attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO); c_IgnoreSubStep = "isGizmoDisabledByScriptException"; AddToCache = false; } if (isGizmoDisabledByScript) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "GizmoDisabledByScript"; return(AddToCache); } // Anything that's Untargetable bool untargetable = false; try { if (CurrentCacheObject.Object is DiaGizmo) { untargetable = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.Untargetable) > 0; } } catch { } // Anything that's Invulnerable bool invulnerable = false; try { if (CurrentCacheObject.Object is DiaGizmo) { invulnerable = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.Invulnerable) > 0; } } catch { } bool noDamage = false; try { if (CurrentCacheObject.Object is DiaGizmo) { noDamage = CurrentCacheObject.Object.CommonData.GetAttribute <int>(ActorAttributeType.NoDamage) > 0; } } catch { CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting NoDamage attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO); c_IgnoreSubStep = "NoDamage"; AddToCache = false; } double minDistance; bool gizmoUsed = false; switch (CurrentCacheObject.Type) { case GObjectType.Door: { AddToCache = true; if (c_diaObject is DiaGizmo && ((DiaGizmo)c_diaObject).HasBeenOperated) { AddToCache = false; c_IgnoreSubStep = "Door has been operated"; return(AddToCache); } try { string currentAnimation = CurrentCacheObject.CommonData.CurrentAnimation.ToString().ToLower(); gizmoUsed = currentAnimation.EndsWith("open") || currentAnimation.EndsWith("opening"); // special hax for A3 Iron Gates if (currentAnimation.Contains("irongate") && currentAnimation.Contains("open")) { gizmoUsed = false; } if (currentAnimation.Contains("irongate") && currentAnimation.Contains("idle")) { gizmoUsed = true; } } catch { } if (gizmoUsed) { Blacklist3Seconds.Add(CurrentCacheObject.RActorGuid); AddToCache = false; c_IgnoreSubStep = "Door is Open or Opening"; return(AddToCache); } try { int gizmoState = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoState); if (gizmoState == 1) { AddToCache = false; c_IgnoreSubStep = "GizmoState=1"; return(AddToCache); } } catch { AddToCache = false; c_IgnoreSubStep = "GizmoStateException"; return(AddToCache); } if (untargetable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Untargetable"; return(AddToCache); } if (AddToCache) { try { DiaGizmo door = null; if (c_diaObject is DiaGizmo) { door = (DiaGizmo)c_diaObject; if (door != null && door.IsGizmoDisabledByScript) { CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); Blacklist3Seconds.Add(CurrentCacheObject.RActorGuid); AddToCache = false; c_IgnoreSubStep = "DoorDisabledbyScript"; return(AddToCache); } } else { AddToCache = false; c_IgnoreSubStep = "InvalidCastToDoor"; } } catch { } } } break; case GObjectType.Interactable: { AddToCache = true; if (untargetable) { ((MainGridProvider)MainGridProvider).AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject() { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Untargetable"; return(AddToCache); } int endAnimation; if (DataDictionary.InteractEndAnimations.TryGetValue(CurrentCacheObject.ActorSNO, out endAnimation)) { if (endAnimation == (int)CurrentCacheObject.Gizmo.CommonData.CurrentAnimation) { AddToCache = false; c_IgnoreSubStep = "EndAnimation"; return(AddToCache); } } if (CurrentCacheObject.Gizmo != null && CurrentCacheObject.Gizmo is GizmoLootContainer && CurrentCacheObject.Gizmo.HasBeenOperated) { AddToCache = false; c_IgnoreSubStep = "GizmoHasBeenOperated"; return(AddToCache); } CurrentCacheObject.Radius = c_diaObject.CollisionSphere.Radius; } break; case GObjectType.HealthWell: { AddToCache = true; try { gizmoUsed = (CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoCharges) <= 0 && CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoCharges) > 0); } catch { Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting shrine-been-operated attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO); AddToCache = true; //return bWantThis; } try { int gizmoState = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoState); if (gizmoState == 1) { AddToCache = false; c_IgnoreSubStep = "GizmoState=1"; return(AddToCache); } } catch { AddToCache = false; c_IgnoreSubStep = "GizmoStateException"; return(AddToCache); } if (gizmoUsed) { c_IgnoreSubStep = "GizmoCharges"; AddToCache = false; return(AddToCache); } if (untargetable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Untargetable"; return(AddToCache); } } break; case GObjectType.CursedShrine: case GObjectType.Shrine: { AddToCache = true; // Shrines // Check if either we want to ignore all shrines if (!Settings.WorldObject.UseShrine) { // We're ignoring all shrines, so blacklist this one c_IgnoreSubStep = "IgnoreAllShrinesSet"; AddToCache = false; return(AddToCache); } try { int gizmoState = CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.GizmoState); if (gizmoState == 1) { AddToCache = false; c_IgnoreSubStep = "GizmoState=1"; return(AddToCache); } } catch { AddToCache = false; c_IgnoreSubStep = "GizmoStateException"; return(AddToCache); } if (untargetable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Untargetable"; return(AddToCache); } // Determine what shrine type it is, and blacklist if the user has disabled it switch (CurrentCacheObject.ActorSNO) { case 176077: //Frenzy Shrine if (!Settings.WorldObject.UseFrenzyShrine) { Blacklist60Seconds.Add(CurrentCacheObject.RActorGuid); c_IgnoreSubStep = "IgnoreShrineOption"; AddToCache = false; } if (Player.ActorClass == ActorClass.Monk && Settings.Combat.Monk.TROption.HasFlag(TempestRushOption.MovementOnly) && Hotbar.Contains(SNOPower.Monk_TempestRush)) { // Frenzy shrines are a huge time sink for monks using Tempest Rush to move, we should ignore them. AddToCache = false; c_IgnoreSubStep = "IgnoreShrineOption"; return(AddToCache); } break; case 176076: //Fortune Shrine if (!Settings.WorldObject.UseFortuneShrine) { AddToCache = false; c_IgnoreSubStep = "IgnoreShrineOption"; return(AddToCache); } break; case 176074: //Protection Shrine if (!Settings.WorldObject.UseProtectionShrine) { AddToCache = false; c_IgnoreSubStep = "IgnoreShrineOption"; return(AddToCache); } break; case 260330: //Empowered Shrine if (!Settings.WorldObject.UseEmpoweredShrine) { AddToCache = false; c_IgnoreSubStep = "IgnoreShrineOption"; return(AddToCache); } break; case 176075: //Enlightened Shrine if (!Settings.WorldObject.UseEnlightenedShrine) { AddToCache = false; c_IgnoreSubStep = "IgnoreShrineOption"; return(AddToCache); } break; case 260331: //Fleeting Shrine if (!Settings.WorldObject.UseFleetingShrine) { AddToCache = false; c_IgnoreSubStep = "IgnoreShrineOption"; return(AddToCache); } break; } //end switch // Bag it! CurrentCacheObject.Radius = 4f; break; } case GObjectType.Barricade: { AddToCache = true; if (noDamage) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "NoDamage"; return(AddToCache); } if (untargetable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Untargetable"; return(AddToCache); } if (invulnerable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Invulnerable"; return(AddToCache); } float maxRadiusDistance; if (DataDictionary.DestructableObjectRadius.TryGetValue(CurrentCacheObject.ActorSNO, out maxRadiusDistance)) { if (CurrentCacheObject.RadiusDistance < maxRadiusDistance) { AddToCache = true; c_IgnoreSubStep = ""; } } if (DateTime.UtcNow.Subtract(PlayerMover.LastGeneratedStuckPosition).TotalSeconds <= 1) { AddToCache = true; c_IgnoreSubStep = ""; break; } // Set min distance to user-defined setting minDistance = Settings.WorldObject.DestructibleRange + CurrentCacheObject.Radius; if (_forceCloseRangeTarget) { minDistance += 6f; } // This object isn't yet in our destructible desire range if (minDistance <= 0 || CurrentCacheObject.RadiusDistance > minDistance) { c_IgnoreSubStep = "NotInBarricadeRange"; AddToCache = false; return(AddToCache); } break; } case GObjectType.Destructible: { AddToCache = true; if (noDamage) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "NoDamage"; return(AddToCache); } if (invulnerable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Invulnerable"; return(AddToCache); } if (untargetable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Untargetable"; return(AddToCache); } if (Player.ActorClass == ActorClass.Monk && Hotbar.Contains(SNOPower.Monk_TempestRush) && TimeSinceUse(SNOPower.Monk_TempestRush) <= 150) { AddToCache = false; c_IgnoreSubStep = "MonkTR"; break; } if (Player.ActorClass == ActorClass.Monk && Hotbar.Contains(SNOPower.Monk_SweepingWind) && GetHasBuff(SNOPower.Monk_SweepingWind)) { AddToCache = false; c_IgnoreSubStep = "MonkSW"; break; } if (CurrentCacheObject.IsQuestMonster || CurrentCacheObject.IsMinimapActive) { AddToCache = true; c_IgnoreSubStep = ""; break; } if (!DataDictionary.ForceDestructibles.Contains(CurrentCacheObject.ActorSNO) && Settings.WorldObject.DestructibleOption == DestructibleIgnoreOption.ForceIgnore) { AddToCache = false; c_IgnoreSubStep = "ForceIgnoreDestructibles"; break; } if (DateTime.UtcNow.Subtract(PlayerMover.LastGeneratedStuckPosition).TotalSeconds <= 1) { AddToCache = true; c_IgnoreSubStep = ""; break; } // Set min distance to user-defined setting minDistance = Settings.WorldObject.DestructibleRange; if (_forceCloseRangeTarget) { minDistance += 6f; } // Only break destructables if we're stuck and using IgnoreNonBlocking if (Settings.WorldObject.DestructibleOption == DestructibleIgnoreOption.DestroyAll) { minDistance += 12f; AddToCache = true; c_IgnoreSubStep = ""; } float maxRadiusDistance; if (DataDictionary.DestructableObjectRadius.TryGetValue(CurrentCacheObject.ActorSNO, out maxRadiusDistance)) { if (CurrentCacheObject.RadiusDistance < maxRadiusDistance) { AddToCache = true; c_IgnoreSubStep = ""; } } // Always add large destructibles within ultra close range if (!AddToCache && CurrentCacheObject.Radius >= 10f && CurrentCacheObject.RadiusDistance <= 5.9f) { AddToCache = true; c_IgnoreSubStep = ""; break; } // This object isn't yet in our destructible desire range if (AddToCache && (minDistance <= 1 || CurrentCacheObject.RadiusDistance > minDistance) && PlayerMover.GetMovementSpeed() >= 1) { AddToCache = false; c_IgnoreSubStep = "NotInDestructableRange"; } if (AddToCache && CurrentCacheObject.RadiusDistance <= 2f && DateTime.UtcNow.Subtract(PlayerMover.LastGeneratedStuckPosition).TotalMilliseconds > 500) { AddToCache = false; c_IgnoreSubStep = "NotStuck2"; } // Add if we're standing still and bumping into it if (CurrentCacheObject.RadiusDistance <= 2f && PlayerMover.GetMovementSpeed() <= 0) { AddToCache = true; c_IgnoreSubStep = ""; } if (CurrentCacheObject.RActorGuid == LastTargetRactorGUID) { AddToCache = true; c_IgnoreSubStep = ""; } break; } case GObjectType.CursedChest: case GObjectType.Container: { AddToCache = false; bool isRareChest = CurrentCacheObject.InternalName.ToLower().Contains("chest_rare") || DataDictionary.ResplendentChestIds.Contains(CurrentCacheObject.ActorSNO); bool isChest = (!isRareChest && CurrentCacheObject.InternalName.ToLower().Contains("chest")) || DataDictionary.ContainerWhiteListIds.Contains(CurrentCacheObject.ActorSNO); // We know it's a container but this is not a known rare chest bool isCorpse = CurrentCacheObject.InternalName.ToLower().Contains("corpse"); bool isWeaponRack = CurrentCacheObject.InternalName.ToLower().Contains("rack"); bool isGroundClicky = CurrentCacheObject.InternalName.ToLower().Contains("ground_clicky"); // We want to do some vendoring, so don't open anything new yet if (ForceVendorRunASAP) { AddToCache = false; c_IgnoreSubStep = "ForceVendorRunASAP"; } // Already open, blacklist it and don't look at it again bool chestOpen; try { chestOpen = (CurrentCacheObject.CommonData.GetAttribute <int>(ActorAttributeType.ChestOpen) > 0); } catch { Logger.Log(TrinityLogLevel.Debug, LogCategory.CacheManagement, "Safely handled exception getting container-been-opened attribute for object {0} [{1}]", CurrentCacheObject.InternalName, CurrentCacheObject.ActorSNO); c_IgnoreSubStep = "ChestOpenException"; AddToCache = false; return(AddToCache); } // Check if chest is open if (chestOpen) { // It's already open! AddToCache = false; c_IgnoreSubStep = "AlreadyOpen"; return(AddToCache); } if (untargetable) { MainGridProvider.AddCellWeightingObstacle(CurrentCacheObject.ActorSNO, CurrentCacheObject.Radius); CacheData.NavigationObstacles.Add(new CacheObstacleObject { ActorSNO = CurrentCacheObject.ActorSNO, Radius = CurrentCacheObject.Radius, Position = CurrentCacheObject.Position, RActorGUID = CurrentCacheObject.RActorGuid, ObjectType = CurrentCacheObject.Type, }); AddToCache = false; c_IgnoreSubStep = "Untargetable"; return(AddToCache); } // Resplendent chests have no range check if (isRareChest && Settings.WorldObject.OpenRareChests) { AddToCache = true; return(AddToCache); } // Regular container, check range if (CurrentCacheObject.RadiusDistance <= Settings.WorldObject.ContainerOpenRange) { if (isChest && Settings.WorldObject.OpenContainers) { return(true); } if (isCorpse && Settings.WorldObject.InspectCorpses) { return(true); } if (isGroundClicky && Settings.WorldObject.InspectGroundClicky) { return(true); } if (isWeaponRack && Settings.WorldObject.InspectWeaponRacks) { return(true); } } if (CurrentCacheObject.IsQuestMonster) { AddToCache = true; return(AddToCache); } if (Settings.WorldObject.OpenAnyContainer) { AddToCache = true; return(AddToCache); } if (!isChest && !isCorpse && !isRareChest) { c_IgnoreSubStep = "InvalidContainer"; } else { c_IgnoreSubStep = "IgnoreContainer"; } break; } } return(AddToCache); }
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)); } }
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)); } }
// Refresh object list from Diablo 3 memory RefreshDiaObjects() private static void RefreshCacheInit() { using (new PerformanceLogger("RefreshDiaObjectCache.CacheInit")) { // Update when we last refreshed with current time LastRefreshedCache = DateTime.Now; // Blank current/last/next targets LastPrimaryTargetPosition = CurrentTarget != null ? CurrentTarget.Position : vNullLocation; vKitePointAvoid = vNullLocation; // store current target GUID CurrentTargetRactorGUID = CurrentTarget != null ? CurrentTarget.RActorGuid : -1; //reset current target CurrentTarget = null; // Reset all variables for target-weight finding AnyTreasureGoblinsPresent = false; CurrentBotKillRange = Math.Min((float)(Settings.Combat.Misc.NonEliteRange), Zeta.CommonBot.Settings.CharacterSettings.Instance.KillRadius); CurrentBotLootRange = Zeta.CommonBot.Settings.CharacterSettings.Instance.LootRadius; ShouldStayPutDuringAvoidance = false; // Set up the fake object for the target handler FakeObject = null; // Always have a minimum kill radius, so we're never getting whacked without retaliating if (CurrentBotKillRange < 10) { CurrentBotKillRange = 10; } // Not allowed to kill monsters due to profile/routine/combat targeting settings - just set the kill range to a third if (!ProfileManager.CurrentProfile.KillMonsters || !CombatTargeting.Instance.AllowedToKillMonsters) { CurrentBotKillRange = 0; } // Not allowed to loots due to profile/routine/loot targeting settings - just set range to a quarter if (!ProfileManager.CurrentProfile.PickupLoot || !LootTargeting.Instance.AllowedToLoot) { CurrentBotLootRange = 0; } if (PlayerStatus.ActorClass == ActorClass.Barbarian && Hotbar.Contains(SNOPower.Barbarian_WrathOfTheBerserker) && GetHasBuff(SNOPower.Barbarian_WrathOfTheBerserker)) { //!sp - keep looking for kills while WOTB is up iKeepKillRadiusExtendedFor = Math.Max(3, iKeepKillRadiusExtendedFor); timeKeepKillRadiusExtendedUntil = DateTime.Now.AddSeconds(iKeepKillRadiusExtendedFor); } // Counter for how many cycles we extend or reduce our attack/kill radius, and our loot radius, after a last kill if (iKeepKillRadiusExtendedFor > 0) { TimeSpan diffResult = DateTime.Now.Subtract(timeKeepKillRadiusExtendedUntil); iKeepKillRadiusExtendedFor = (int)diffResult.Seconds; //DbHelper.Log(TrinityLogLevel.Verbose, LogCategory.Moving, "Kill Radius remaining " + diffResult.Seconds + "s"); if (timeKeepKillRadiusExtendedUntil <= DateTime.Now) { iKeepKillRadiusExtendedFor = 0; } } if (iKeepLootRadiusExtendedFor > 0) { iKeepLootRadiusExtendedFor--; } // Clear forcing close-range priority on mobs after XX period of time if (ForceCloseRangeTarget && DateTime.Now.Subtract(lastForcedKeepCloseRange).TotalMilliseconds > ForceCloseRangeForMilliseconds) { ForceCloseRangeTarget = false; } // Bunch of variables used throughout hashMonsterObstacleCache = new HashSet <GilesObstacle>(); hashAvoidanceObstacleCache = new HashSet <GilesObstacle>(); hashNavigationObstacleCache = new HashSet <GilesObstacle>(); AnyElitesPresent = false; AnyMobsInRange = false; TownRun.lastDistance = 0f; IsAvoidingProjectiles = false; // Every 15 seconds, clear the "blackspots" where avoidance failed, so we can re-check them if (DateTime.Now.Subtract(lastClearedAvoidanceBlackspots).TotalSeconds > 15) { lastClearedAvoidanceBlackspots = DateTime.Now; hashAvoidanceBlackspot = new HashSet <GilesObstacle>(); } // Clear our very short-term destructible blacklist within 3 seconds of last attacking a destructible if (bNeedClearDestructibles && DateTime.Now.Subtract(lastDestroyedDestructible).TotalMilliseconds > 2500) { bNeedClearDestructibles = false; hashRGUIDDestructible3SecBlacklist = new HashSet <int>(); } // Clear our very short-term ignore-monster blacklist (from not being able to raycast on them or already dead units) if (NeedToClearBlacklist3 && DateTime.Now.Subtract(dateSinceBlacklist3Clear).TotalMilliseconds > 3000) { NeedToClearBlacklist3 = false; hashRGUIDBlacklist3 = new HashSet <int>(); } // Reset the counters for player-owned things iPlayerOwnedMysticAlly = 0; iPlayerOwnedGargantuan = 0; iPlayerOwnedZombieDog = 0; iPlayerOwnedDHPets = 0; // Reset the counters for monsters at various ranges ElitesWithinRange = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; AnythingWithinRange = new int[] { 0, 0, 0, 0, 0, 0, 0, 0 }; NonRendedTargets_9 = 0; anyBossesInRange = false; // Flag for if we should search for an avoidance spot or not StandingInAvoidance = false; // Highest weight found as we progress through, so we can pick the best target at the end (the one with the highest weight) w_HighestWeightFound = 0; // Here's the list we'll use to store each object GilesObjectCache = new List <GilesObject>(); hashDoneThisRactor = new HashSet <int>(); } }
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" }; } }