public static Composite CreateRestoShamanCombatBehaviorSolo() { return(new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromMediumRange(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( CreateRestoDiagnosticOutputBehavior(on => null), Helpers.Common.CreateInterruptBehavior(), Totems.CreateTotemsBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), Dispelling.CreatePurgeEnemyBehavior("Purge"), Common.CastElementalBlast(), Spell.Buff("Flame Shock", 3, on => Me.CurrentTarget, req => true), Spell.Cast("Lava Burst"), Spell.Cast("Frost Shock"), Spell.Cast("Chain Lightning", ret => Spell.UseAOE && Unit.UnfriendlyUnitsNearTarget(10f).Count() >= 2 && !Unit.UnfriendlyUnitsNearTarget(12f).Any(u => u.IsCrowdControlled())), Spell.Cast("Lightning Bolt") ) ) )); }
public override void Initialize() { RegisterHotkeys(); LuaCore.PopulateSecondryStats(); TalentManager.Init(); TalentManager.Update(); UpdateContext(); // Do this now, so we ensure we update our context when needed. BotEvents.Player.OnMapChanged += e => UpdateContext(); OnWoWContextChanged += (orig, ne) => { Logging.Write("Context changed, re-creating behaviors"); AssignBehaviors(); Spell.GcdInitialize(); Lists.BossList.Init(); }; Spell.GcdInitialize(); Dispelling.Init(); //testing cached units //CachedUnits.Initialize(); EventHandlers.Init(); Lists.BossList.Init(); Instance.AssignBehaviors(); Logging.Write("Initialization Completed"); }
public static Composite CreateDiscCombatNormalCombat() { return(new PrioritySelector( Spell.WaitForCastOrChannel(), new Decorator( ret => Unit.NearbyGroupMembers.Any(m => m.IsAlive), CreateDiscHealOnlyBehavior() ), Helpers.Common.EnsureReadyToAttackFromMediumRange(), Movement.CreateFaceTargetBehavior(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( CreateDiscDiagnosticOutputBehavior(CompositeBuilder.CurrentBehaviorType.ToString()), Helpers.Common.CreateInterruptBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), // Artifact Weapon // Light's Wrath notes: UseDPSArtifactWeaponWhen.AtHighestDPSOpportunity would be good if the player has a larger number of attonement stacks. However, this would only apply for dungeon context. new Decorator( ret => PriestSettings.UseArtifactOnlyInAoE && Unit.NearbyUnitsInCombatWithMeOrMyStuff.Count() > 1, new PrioritySelector( Spell.Cast("Light's Wrath", ret => PriestSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.OnCooldown) ) ), Spell.Cast("Light's Wrath", ret => !PriestSettings.UseArtifactOnlyInAoE && PriestSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.OnCooldown), Dispelling.CreatePurgeEnemyBehavior("Dispel Magic"), Spell.Buff("Shadow Word: Pain", req => Me.CurrentTarget.HasAuraExpired("Shadow Word: Pain", 1) && Me.CurrentTarget.TimeToDeath(99) >= 8), Spell.Buff("Shadow Word: Pain", true, on => { WoWUnit unit = Unit.NearbyUnfriendlyUnits .FirstOrDefault( u => (u.TaggedByMe || u.Aggro) && u.Guid != Me.CurrentTargetGuid && u.IsTargetingMeOrPet && !u.HasMyAura("Shadow Word: Pain") && !u.IsCrowdControlled() ); return unit; }), Spell.Buff("Schism", on => Me.CurrentTarget), Spell.Cast("Penance", mov => true, on => Me.CurrentTarget, req => true, cancel => false), Spell.Cast("Smite", mov => true, on => Me.CurrentTarget, req => true, cancel => false) ) ) )); }
public static Composite CreatePriestDispelBehavior() { return(new PrioritySelector( Spell.Cast("Mass Dispel", on => Me, req => Me.Combat && PriestSettings.CountMassDispel > 0 && Unit.NearbyGroupMembers.Count(u => u.IsAlive && u.SpellDistance() < 15 && Dispelling.CanDispel(u, DispelCapabilities.All)) >= PriestSettings.CountMassDispel), Dispelling.CreateDispelBehavior() )); }
public static Composite CreateRestoShamanCombatBehaviorBattlegrounds() { return(new PrioritySelector( Spell.WaitForCastOrChannel(), CreateRestoDiagnosticOutputBehavior(on => HealerManager.Instance.FirstUnit), new Decorator( ret => !Spell.IsGlobalCooldown() && HealerManager.Instance.TargetList.Any(t => !t.IsMe && t.IsAlive), new PrioritySelector( HealerManager.CreateStayNearTankBehavior(), CreateRestoShamanHealingOnlyBehavior(selfOnly: false) ) ), new Decorator( ret => !Spell.IsGlobalCooldown() && HealerManager.AllowHealerDPS(), new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromMediumRange(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Helpers.Common.CreateInterruptBehavior(), Totems.CreateTotemsBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), Dispelling.CreatePurgeEnemyBehavior("Purge"), Common.CastElementalBlast(), Spell.Buff("Flame Shock", 3, on => Me.CurrentTarget, req => true), Spell.Cast("Lava Burst"), Spell.Cast("Frost Shock"), Spell.Cast("Chain Lightning", ret => Spell.UseAOE && Unit.UnfriendlyUnitsNearTarget(10f).Count() >= 2 && !Unit.UnfriendlyUnitsNearTarget(12f).Any(u => u.IsCrowdControlled())), Spell.Cast("Lightning Bolt") ) ) ) ) )); }
public static Composite RetributionCombat() { return(new Throttle( new PrioritySelector( //new Decorator(ret => AdvancedAI.PvPRot, // RetributionPaladinPvP.CreateRPPvPCombat), new Throttle(1, 1, new Decorator(ret => Me.HasAura("Dire Fixation"), new PrioritySelector( BossMechs.HorridonHeroic()))), Common.CreateInterruptBehavior(), Dispelling.CreateDispelBehavior(), Spell.Cast("Inquisition", ret => (!Me.HasAura("Inquisition") || Me.HasAuraExpired("Inquisition", 2)) && (Me.CurrentHolyPower >= 3 || Me.HasAura("Divine Purpose"))), new Decorator(ret => Me.HasAura("Inquisition") && AdvancedAI.Burst, new PrioritySelector( Spell.Cast("Avenging Wrath", ret => Me.CurrentTarget.IsBoss), Spell.Cast("Holy Avenger", ret => Me.CurrentTarget.IsBoss), Spell.Cast("Guardian of Ancient Kings", ret => Me.CurrentTarget.IsBoss), new Action(ret => { Item.UseHands(); return RunStatus.Failure; }), new Action(ret => { Item.UseTrinkets(); return RunStatus.Failure; }))), Spell.Cast("Seal of Righteousness", ret => !Me.HasAura("Seal of Righteousness") && Clusters.GetClusterCount(Me, Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f) >= 8), Spell.Cast("Seal of Truth", ret => !Me.HasAura("Seal of Truth") && Clusters.GetClusterCount(Me, Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f) < 8), new Decorator(ret => Me.HasAura("Inquisition"), new PrioritySelector( Spell.Cast("Execution Sentence", ret => Me.CurrentTarget.IsBoss), Spell.Cast("Holy Prism"), Spell.CastOnGround("Light's Hammer", on => Me.CurrentTarget.Location, ret => Me.CurrentTarget.IsBoss), Spell.Cast("Divine Storm", ret => Clusters.GetClusterCount(Me, Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f) >= 2 && (Me.CurrentHolyPower == 5 || Me.HasAura("Divine Purpose"))), Spell.Cast("Templar's Verdict", ret => Me.CurrentHolyPower == 5 || Me.HasAura("Divine Purpose")))), Spell.Cast("Hammer of Wrath", ret => Me.CurrentHolyPower <= 4), Spell.Cast("Exorcism", ret => Me.CurrentHolyPower <= 4), Spell.Cast("Hammer of the Righteous", ret => Me.CurrentHolyPower <= 4 && Clusters.GetClusterCount(Me, Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f) >= 2), // Unit.NearbyUnfriendlyUnits.Count(u => u.DistanceSqr <= 8 * 8) >= 2), Spell.Cast("Crusader Strike", ret => Me.CurrentHolyPower <= 4), Spell.Cast("Judgment", on => SecTar, ret => Me.CurrentHolyPower <= 4 && Clusters.GetClusterCount(Me, Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 15f) >= 2 && Me.HasAura("Glyph of Double Jeopardy")), Spell.Cast("Judgment", ret => Me.CurrentHolyPower <= 4), Spell.Cast("Divine Storm", ret => Me.HasAura("Inquisition") && Clusters.GetClusterCount(Me, Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f) >= 2 && Me.GetAuraTimeLeft("Inquisition").TotalSeconds > 4), Spell.Cast("Templar's Verdict", ret => Me.HasAura("Inquisition") && Me.GetAuraTimeLeft("Inquisition").TotalSeconds > 4)))); }
public static Composite CreateDiscCombatNormalPull() { return(new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromMediumRange(), Movement.CreateFaceTargetBehavior(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( CreateDiscDiagnosticOutputBehavior(CompositeBuilder.CurrentBehaviorType.ToString()), Spell.BuffSelf("Power Word: Shield", ret => PriestSettings.UseShieldPrePull && CanWePwsUnit(Me)), Helpers.Common.CreateInterruptBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), Dispelling.CreatePurgeEnemyBehavior("Dispel Magic"), // slow pull if no aggro and not competing for mobs Spell.Cast( "Smite", mov => true, on => Me.CurrentTarget, req => !ObjectManager.GetObjectsOfType <WoWUnit>(true, false).Any(u => u.Aggro || (u.IsPlayer && !u.IsMe && u.DistanceSqr < 60 * 60)) ), Spell.Buff("Shadow Word: Pain", req => Me.CurrentTarget.HasAuraExpired("Shadow Word: Pain", 1) && Me.CurrentTarget.TimeToDeath(99) >= 8), Spell.Buff("Shadow Word: Pain", true, on => { WoWUnit unit = Unit.NearbyUnfriendlyUnits.FirstOrDefault(u => u.Guid != Me.CurrentTargetGuid && u.IsTargetingMeOrPet && !u.HasMyAura("Shadow Word: Pain") && !u.IsCrowdControlled()); return unit; }), Spell.Cast("Penance", mov => true, on => Me.CurrentTarget, req => true, cancel => false), Common.CreateHolyFireBehavior(), Spell.Cast("Smite", mov => true, on => Me.CurrentTarget, req => true, cancel => false) ) ) )); }
public static Composite CreateMageCombatBuffs() { return(new Decorator( req => !Me.CurrentTarget.IsTrivial(), new PrioritySelector( // Defensive CastAlterTime(), // new Wait( 1, until => !HasTalent(MageTalents.Invocation) || Me.HasAura("Invoker's Energy"), new ActionAlwaysSucceed()) Dispelling.CreatePurgeEnemyBehavior("Spellsteal"), // CreateMageSpellstealBehavior(), CreateMageRuneOfPowerBehavior(), Spell.BuffSelf("Time Warp", ret => MageSettings.UseTimeWarp && NeedToTimeWarp) ) )); }
public static Composite CreateHolyCombat() { return(new PrioritySelector( new Decorator( ret => HealerManager.AllowHealerDPS(), new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromMediumRange(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Helpers.Common.CreateInterruptBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), Dispelling.CreatePurgeEnemyBehavior("Dispel Magic"), Spell.Buff("Shadow Word: Pain", true), Spell.Buff("Holy Word: Chastise"), Spell.Buff("Shadow Word: Pain", true, on => { WoWUnit unit = Unit.NearbyUnfriendlyUnits .FirstOrDefault( u => (u.TaggedByMe || u.Aggro) && u.Guid != Me.CurrentTargetGuid && u.IsTargetingMeOrPet && !u.HasMyAura("Shadow Word: Pain") && !u.IsCrowdControlled() ); return unit; }), Common.CreateHolyFireBehavior(), Spell.Cast("Smite", mov => true, on => Me.CurrentTarget, req => true, cancel => HealerManager.CancelHealerDPS()) ) ) ) ) )); }
public static Composite CreateRestoShamanHealingOnlyBehavior(bool selfOnly = false) { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, Math.Max(ShamanSettings.RestoHealSettings.HealingWave, ShamanSettings.RestoHealSettings.HealingSurge)); bool moveInRange = false; if (!selfOnly) { moveInRange = (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds); } Logger.WriteDebugInBehaviorCreate("Shaman Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { behavs.AddBehavior(dispelPriority, "Purify Spirit", null, Dispelling.CreateDispelBehavior()); } #region Save the Group behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.SpiritLinkTotem) + 600, "Spirit Link Totem", "Spirit Link Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.CastOnGround("Spirit Link Totem", on => (WoWUnit)on, ret => HealerManager.Instance.TargetList.Count( p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.SpiritLinkTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= ShamanSettings.RestoHealSettings.MinSpiritLinkCount ) ) ); #endregion #region AoE Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingTideTotem) + 400, "Healing Tide Totem", "Healing Tide Totem", new Decorator( ret => (Me.Combat || ((WoWUnit)ret).Combat) && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid) && (!Totems.Exist(WoWTotem.Cloudburst) || (Totems.GetTotem(WoWTotem.Cloudburst).Expires - DateTime.UtcNow).TotalMilliseconds < 1500), Spell.Cast( "Healing Tide Totem", on => Me, req => Me.Combat && HealerManager.Instance.TargetList.Count(p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingTideTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= ShamanSettings.RestoHealSettings.MinHealingTideCount ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingStreamTotem) + 300, "Healing Stream Totem", "Healing Stream Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.Cast( "Healing Stream Totem", on => { if (Totems.Exist(WoWTotemType.Water)) { return(null); } if (Spell.IsSpellOnCooldown(Totems.ToSpellId(WoWTotem.HealingStream))) { return(null); } // if tank in group, make sure we are near the tank WoWUnit tank = HealerManager.TankToStayNear; if (tank != null) { if (!HealerManager.IsTankSettledIntoFight(tank)) { return(null); } if (tank.Distance > Totems.GetTotemRange(WoWTotem.HealingStream)) { return(null); } } WoWUnit unit = HealerManager.Instance.TargetList .FirstOrDefault( p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingStream) ); return(unit); } ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.GiftoftheQueen) + 300, "Gift of the Queen", "Gift of the Queen", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( context => GetBestGiftoftheQueenTarget(), new Decorator( ret => ret != null, Spell.CastOnGround("Gift of the Queen", on => (WoWUnit)on, req => true, false) ) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.CloudburstTotem) + 300, "Cloudburst Totem", "Cloudburst Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.Cast( "Cloudburst Totem", on => { if (Totems.Exist(WoWTotemType.Water)) { return(null); } if (Spell.IsSpellOnCooldown(Totems.ToSpellId(WoWTotem.Cloudburst))) { return(null); } if (Unit.ValidUnit(Me.CurrentTarget) && (Me.CurrentTarget.TimeToDeath() < 20 || Unit.UnitsInCombatWithUsOrOurStuff().Count() < 3)) { return(null); } // if tank in group, make sure we are near the tank WoWUnit tank = HealerManager.TankToStayNear; if (tank != null) { if (!HealerManager.IsTankSettledIntoFight(tank)) { return(null); } if (tank.Distance > Totems.GetTotemRange(WoWTotem.Cloudburst)) { return(null); } } WoWUnit unit = HealerManager.Instance.TargetList .Where( p => p.HealthPercent < ShamanSettings.RestoHealSettings.CloudburstTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.Cloudburst) ) .OrderBy(p => (int)p.HealthPercent) .FirstOrDefault(); return(unit); } ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingRain) + 200, "Healing Rain", "Healing Rain", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( context => GetBestHealingRainTarget(), new Decorator( ret => ret != null, Spell.CastOnGround("Healing Rain", on => (WoWUnit)on, req => true, false) ) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.ChainHeal) + 150, "Chain Heal", "Chain Heal", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( ctx => GetBestChainHealTarget(), new Decorator( ret => ret != null, new Sequence( new DecoratorContinue( req => ((WoWUnit)req).HasAuraExpired("Riptide", TimeSpan.FromMilliseconds(ChainHealCastTime), true), new Sequence( Spell.Cast("Riptide", on => (WoWUnit)on, req => true, cancel => false), new Wait(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(LagTolerance.No), new ActionAlwaysSucceed()), new Action(r => TidalWaveRefresh()) ) ), new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(LagTolerance.No), new ActionAlwaysSucceed()), Spell.Cast("Chain Heal", on => (WoWUnit)on), new Action(r => TidalWaveRefresh()) ) ) ) ) ); #endregion #region Single Target Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingWave), "Healing Wave", "Healing Wave", new Decorator(ret => ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingWave, new Sequence( new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), new WaitContinue(2, until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), Spell.Cast("Healing Wave", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action(r => TidalWaveConsume()) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingSurge), "Healing Surge", "Healing Surge", new Decorator(ret => ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingSurge, new Sequence( new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), new WaitContinue(2, until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), Spell.Cast("Healing Surge", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action(r => TidalWaveConsume()) ) ) ); #endregion #region Healing Cooldowns behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.Ascendance) + 100, "Ascendance", "Ascendance", new Decorator( ret => ShamanSettings.UseAscendance && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.BuffSelf( "Ascendance", ret => HealerManager.Instance.TargetList.Count(p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.Ascendance) >= ShamanSettings.RestoHealSettings.MinAscendanceCount ) ) ); #endregion behavs.OrderBehaviors(); if (selfOnly == false && CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) { behavs.ListBehaviors(); } return(new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, CreateRestoDiagnosticOutputBehavior(ret => (WoWUnit)ret), new Decorator( ret => ret != null && (Me.Combat || ((WoWUnit)ret).Combat || ((WoWUnit)ret).PredictedHealthPercent() <= 99), new PrioritySelector( new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Totems.CreateTotemsBehavior(), // roll Riptide on Tanks otherwise new Sequence( Spell.Cast("Riptide", on => { WoWUnit unit = GetBestRiptideTankTarget(); if (unit != null && Spell.CanCastHack("Riptide", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing RIPTIDE ON TANK: {0}", unit.SafeName()); return unit; } return null; }), new Action(r => TidalWaveRefresh()) ), // cast Riptide if we are a low level CreateRestoShamanBuffRiptideLowLevel(), // cast Riptide if we need Tidal Waves -- skip if Ancestral Swiftness is CreateRestoShamanBuffTidalWaves(), behavs.GenerateBehaviorTree(), // cast Riptide if we need Tidal Waves -- skip if Ancestral Swiftness is new Decorator( ret => { int rollCount = HealerManager.Instance.TargetList.Count( u => u.IsAlive && u.HasMyAura("Riptide")); // Logger.WriteDebug("GetBestRiptideTarget: currently {0} group members have my Riptide", rollCount); return rollCount < ShamanSettings.RestoHealSettings.RollRiptideCount; }, new Sequence( Spell.Cast("Riptide", on => { // if tank needs Riptide, bail out on Rolling as they have priority if (GetBestRiptideTankTarget() != null) { return null; } // get the best target from all wowunits in our group WoWUnit unit = GetBestRiptideTarget(); if (unit != null) { Logger.WriteDebug(Color.White, "ROLLING RIPTIDE on: {0}", unit.SafeName()); } return unit; }), new Action(r => TidalWaveRefresh()) ) ), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 40f, 34f) ) ) ) ) ) ) ) )); }
public static Composite CreateRestoShamanCombatBehaviorInstances() { #if OLD_APPROACH return(new PrioritySelector( ctx => HealerManager.Instance.TargetList.Any(t => !t.IsMe && t.IsAlive), Safers.EnsureTarget(), Movement.CreateFaceTargetBehavior(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( CreateRestoDiagnosticOutputBehavior(on => Me.CurrentTarget), new Decorator( ret => (bool)ret, new PrioritySelector( HealerManager.CreateStayNearTankBehavior(), CreateRestoShamanHealingOnlyBehavior(selfOnly: false), Helpers.Common.CreateInterruptBehavior(), Dispelling.CreatePurgeEnemyBehavior("Purge"), Totems.CreateTotemsBehavior(), Spell.Cast("Lightning Bolt", ret => TalentManager.HasGlyph("Telluric Currents")) ) ), new Decorator( ret => !((bool)ret), new PrioritySelector( CreateRestoDiagnosticOutputBehavior(on => HealerManager.Instance.FirstUnit), Spell.Cast("Elemental Blast"), Spell.Buff("Flame Shock", true, on => Me.CurrentTarget, req => true, 3), Spell.Cast("Lava Burst"), Spell.Cast("Frost Shock"), Spell.Cast("Chain Lightning", ret => Spell.UseAOE && Unit.UnfriendlyUnitsNearTarget(10f).Count() >= 2 && !Unit.UnfriendlyUnitsNearTarget(12f).Any(u => u.IsCrowdControlled())), Spell.Cast("Lightning Bolt") ) ) ) ) )); #else return(new PrioritySelector( Spell.WaitForCastOrChannel(), CreateRestoDiagnosticOutputBehavior(on => HealerManager.FindLowestHealthTarget()), HealerManager.CreateStayNearTankBehavior(), new Decorator( ret => Me.Combat && HealerManager.AllowHealerDPS(), new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromMediumRange(), Movement.CreateFaceTargetBehavior(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Helpers.Common.CreateInterruptBehavior(), Totems.CreateTotemsBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), Common.CastElementalBlast(cancel: c => HealerManager.CancelHealerDPS()), Spell.Buff("Flame Shock", 3, on => Me.CurrentTarget, req => true), Spell.Cast("Lava Burst", on => Me.CurrentTarget, req => true, cancel => HealerManager.CancelHealerDPS()), Spell.Cast("Frost Shock"), Spell.Cast("Chain Lightning", on => Me.CurrentTarget, req => Spell.UseAOE && Unit.UnfriendlyUnitsNearTarget(10f).Count() >= 2 && !Unit.UnfriendlyUnitsNearTarget(12f).Any(u => u.IsCrowdControlled()), cancel => HealerManager.CancelHealerDPS()), Spell.Cast("Lightning Bolt", on => Me.CurrentTarget, req => true, cancel => HealerManager.CancelHealerDPS()) ) ) ) ), new Decorator( ret => Unit.NearbyGroupMembers.Any(m => m.IsAlive && !m.IsMe), new PrioritySelector( CreateRestoShamanHealingOnlyBehavior(selfOnly: false), Helpers.Common.CreateInterruptBehavior(), Dispelling.CreatePurgeEnemyBehavior("Purge"), Totems.CreateTotemsBehavior(), new Decorator( req => TalentManager.HasGlyph("Telluric Currents"), new PrioritySelector( Safers.EnsureTarget(), Movement.CreateFaceTargetBehavior(), Spell.Cast("Lightning Bolt", mov => true, on => Unit.NearbyUnitsInCombatWithUsOrOurStuff .Where(u => u.IsAlive && u.SpellDistance() < 40 && Me.IsSafelyFacing(u)) .OrderByDescending(u => u.HealthPercent) .FirstOrDefault(), req => !HealerManager.Instance.TargetList.Any(h => h.IsAlive && h.SpellDistance() < 40 && h.HealthPercent < ShamanSettings.RestoHealSettings.TelluricHealthCast), cancel => HealerManager.Instance.TargetList.Any(h => h.IsAlive && h.SpellDistance() < 40 && h.HealthPercent < ShamanSettings.RestoHealSettings.TelluricHealthCancel) ) ) ) ) ) )); #endif }
public static Composite RestorationCombat() { HealerManager.NeedHealTargeting = true; //var cancelHeal = Math.Max(95, Math.Max(AvehealingWave(), Math.Max(AvegreaterhealingWave(), AvehealingSurge())));//95,93,55,25 var cancelHeal = Math.Max(95, Math.Max(93, Math.Max(55, 25)));//95,93,55,25 return(new PrioritySelector( Spell.WaitForCastOrChannel(), //new Decorator(ret => AdvancedAI.PvPRot, // RestorationShamanPvP.CreateRSPvPCombat), new Decorator(ret => (Me.Combat || healtarget.Combat /*|| healtarget.GetPredictedHealthPercent() <= 99*/) && !Me.Mounted, new PrioritySelector( //Totems.CreateTotemsBehavior(), RollRiptide(), TidalWaves(), new Decorator(ret => AdvancedAI.Dispell, Dispelling.CreateDispelBehavior()), Item.UsePotionAndHealthstone(40), new Throttle(1, 1, new PrioritySelector( Spell.Cast("Earth Shield", on => GetBestEarthShieldTargetInstance(), ret => !GetBestEarthShieldTargetInstance().CachedHasAura("Earth Shield")))), Spell.Cast("Spirit Link Totem", on => healtarget, ret => HealerManager.Instance.TargetList.Count(p => p.GetPredictedHealthPercent() < 40 && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= 3 && AdvancedAI.Burst), new Decorator(ret => healtarget.HealthPercent < 25, new Sequence( Spell.Cast("Ancestral Swiftness"), Spell.Cast("Greater Healing Wave", on => healtarget))), Spell.Cast("Healing Tide Totem", ret => Me.Combat && HealerManager.Instance.TargetList.Count(p => p.GetPredictedHealthPercent() < 60 && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= (Me.GroupInfo.IsInRaid ? 3 : 2) && AdvancedAI.Burst), Spell.Cast("Healing Stream Totem", ret => Me.Combat && !Totems.Exist(WoWTotemType.Water) && HealerManager.Instance.TargetList.Any(p => p.GetPredictedHealthPercent() < 95 && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide))), Spell.Cast("Mana Tide Totem", ret => !Totems.Exist(WoWTotemType.Water) && Me.ManaPercent < 80), HealingRain(), ChainHeal(), Spell.Cast("Elemental Blast", on => BoltTar()), //ret => SpellManager.HasSpell("Elemental Blast"), //cancel => healtarget.HealthPercent < 40), Spell.Cast("Greater Healing Wave", on => healtarget, //ret => AvegreaterhealingWave() < Deficit(),//55 ret => healtarget.HealthPercent <55, cancel => healtarget.HealthPercent> cancelHeal), Spell.Cast("Healing Wave", on => healtarget, //ret => AvehealingWave() < Deficit(),//93 ret => healtarget.HealthPercent <93, cancel => healtarget.HealthPercent> cancelHeal), Spell.Cast("Healing Surge", on => healtarget, //ret => AvehealingSurge() < Deficit(),//25 ret => healtarget.HealthPercent <25, cancel => healtarget.HealthPercent> cancelHeal), Spell.Cast("Ascendance", ret => HealerManager.Instance.TargetList.Count(p => p.GetPredictedHealthPercent() < 50) >= 4 && !Me.CachedHasAura("Ascendance") && AdvancedAI.Burst), Riptide(), new Decorator(ret => AdvancedAI.InterruptsEnabled, Common.CreateInterruptBehavior()), //Totems.CreateTotemsBehavior(), Spell.Cast("Lightning Bolt", on => BoltTar(), ret => TalentManager.HasGlyph("Telluric Currents"), cancel => healtarget.HealthPercent < 70))))); }
public override void Initialize() { DateTime timeStart = DateTime.UtcNow; Logger.WriteFile("Initialize: started"); // cannot call method which references SingularSettings TalentManager.Init(); // initializes CurrentSpec which is referenced everywhere SingularSettings.Initialize(); // loads Singular global and spec-specific settings (must determine spec first) DetermineCurrentWoWContext(); Task.Run(() => WriteSupportInfo()); _lastLogLevel = GlobalSettings.Instance.LogLevel; // When we actually need to use it, we will. Spell.Init(); Spell.GcdInitialize(); EventHandlers.Init(); MountManager.Init(); HotkeyDirector.Init(); MovementManager.Init(); // SoulstoneManager.Init(); // switch to using Death behavior Dispelling.Init(); PartyBuff.Init(); Singular.Lists.BossList.Init(); Targeting.Instance.WeighTargetsFilter += PullMoreWeighTargetsFilter; //Logger.Write("Combat log event handler started."); // Do this now, so we ensure we update our context when needed. BotEvents.Player.OnMapChanged += e => { // Don't run this handler if we're not the current routine! if (!WeAreTheCurrentCombatRoutine) { return; } // Only ever update the context. All our internal handlers will use the context changed event // so we're not reliant on anything outside of ourselves for updates. UpdateContext(); }; TreeHooks.Instance.HooksCleared += () => { // Don't run this handler if we're not the current routine! if (!WeAreTheCurrentCombatRoutine) { return; } Logger.Write(LogColor.Hilite, "Hooks cleared, re-creating behaviors"); RebuildBehaviors(silent: true); Spell.GcdInitialize(); // probably not needed, but quick }; GlobalSettings.Instance.PropertyChanged += (sender, e) => { // Don't run this handler if we're not the current routine! if (!WeAreTheCurrentCombatRoutine) { return; } // only LogLevel change will impact our behav trees // .. as we conditionally include/omit some diagnostic nodes if debugging // also need to keep a cached copy of prior value as the event // .. fires on the settor, not when the value is different if (e.PropertyName == "LogLevel" && _lastLogLevel != GlobalSettings.Instance.LogLevel) { _lastLogLevel = GlobalSettings.Instance.LogLevel; Logger.Write(LogColor.Hilite, "HonorBuddy {0} setting changed to {1}, re-creating behaviors", e.PropertyName, _lastLogLevel.ToString()); RebuildBehaviors(); Spell.GcdInitialize(); // probably not needed, but quick } }; // install botevent handler so we can consolidate validation on whether // .. local botevent handlers should be called or not SingularBotEventInitialize(); Logger.Write("Determining talent spec."); try { TalentManager.Update(); } catch (Exception e) { StopBot(e.ToString()); } Logger.Write("Current spec is " + SpecName()); // write current settings to log file... only written at startup and when Save press in Settings UI Task.Run(() => SingularSettings.Instance.LogSettings()); // Update the current WoWContext, and fire an event for the change. UpdateContext(); // NOTE: Hook these events AFTER the context update. OnWoWContextChanged += (orig, ne) => { Logger.Write(LogColor.Hilite, "Context changed, re-creating behaviors"); SingularRoutine.DescribeContext(); RebuildBehaviors(); Spell.GcdInitialize(); Singular.Lists.BossList.Init(); }; RoutineManager.Reloaded += (s, e) => { Logger.Write(LogColor.Hilite, "Routines were reloaded, re-creating behaviors"); RebuildBehaviors(silent: true); Spell.GcdInitialize(); }; // create silently since Start button will create a context change (at least first Start) // .. which will build behaviors again if (!Instance.RebuildBehaviors()) { return; } // if (IsPluginEnabled("DrinkPotions")) { Logger.Write(LogColor.Hilite, "info: disabling DrinkPotions plugin, conflicts with Singular potion support"); SetPluginEnabled("DrinkPotions", false); } SpellImmunityManager.Add(16292, WoWSpellSchool.Frost); // http://www.wowhead.com/npc=16292/aquantion Logger.WriteDebug(Color.White, "Verified behaviors can be created!"); Logger.Write("Initialization complete!"); Logger.WriteDiagnostic(Color.White, "Initialize: completed taking {0:F2} seconds", (DateTime.UtcNow - timeStart).TotalSeconds); }
public static Composite CreatePriestShadowNormalCombat() { return(new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromLongRange(), Spell.WaitForCastOrChannel(), CreateFightAssessment(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( SingularRoutine.MoveBehaviorInlineToCombat(BehaviorType.Heal), SingularRoutine.MoveBehaviorInlineToCombat(BehaviorType.CombatBuffs), Helpers.Common.CreateInterruptBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), new Decorator( req => !Me.CurrentTarget.IsTrivial(), new PrioritySelector( Dispelling.CreatePurgeEnemyBehavior("Dispel Magic"), // Mana Management stuff - send in the fiends Common.CreateShadowfiendBehavior(), // Defensive stuff Spell.BuffSelf("Dispersion", ret => Me.ManaPercent < PriestSettings.DispersionMana || Me.HealthPercent < 40 || (Me.ManaPercent < SingularSettings.Instance.MinMana && Me.IsSwimming) || Unit.NearbyUnfriendlyUnits.Count(t => t.GotTarget() && t.CurrentTarget.IsTargetingMyStuff()) >= 3), Common.CreatePriestShackleUndeadAddBehavior(), Common.CreatePsychicScreamBehavior(), Spell.HandleOffGCD( new PrioritySelector( ctx => Me.CurrentTarget.TimeToDeath() > 15 || cntAoeTargets > 1, new Sequence( Spell.BuffSelfAndWait(sp => "Vampiric Embrace", req => ((bool)req) && Me.HealthPercent < PriestSettings.VampiricEmbracePct), Spell.BuffSelf("Power Infusion") ), Spell.Cast("Power Infusion", req => ((bool)req) || Me.HasAura("Vampiric Embrace")) ) ) ) ), // Shadow immune npcs. // Spell.Cast("Holy Fire", req => Me.CurrentTarget.IsImmune(WoWSpellSchool.Shadow)), // AoE Rotation new Decorator( ret => Spell.UseAOE && cntAoeTargets > 1, new PrioritySelector( ctx => AoeTargets.FirstOrDefault(u => Me.IsSafelyFacing(u) && u.InLineOfSpellSight), Spell.Cast("Void Eruption", when => !InVoidform), Spell.Cast( "Mind Sear", mov => true, on => { IEnumerable <WoWUnit> aoeTargetsFacing = AoeTargets.Where(u => Me.IsSafelyFacing(u) && u.InLineOfSpellSight && u.IsTrivial()); WoWUnit unit = Clusters.GetBestUnitForCluster(aoeTargetsFacing, ClusterType.Radius, 10); if (unit == null) { return null; } if (3 > Clusters.GetClusterCount(unit, AoeTargets, ClusterType.Radius, 10)) { return null; } if (Unit.UnfriendlyUnits(50).Any(u => !u.IsTrivial() && unit.SpellDistance(u) < 12)) { return null; } Logger.Write(LogColor.Hilite, "^Trivial Farming: all trivial mobs within 12 yds of {0}", unit.SafeName()); return unit; }, cancel => Me.HealthPercent < PriestSettings.ShadowHeal ), Spell.Buff("Shadow Word: Pain", true), // no multi message for current target Spell.Buff("Vampiric Touch", true), // no multi message for current target Spell.Buff( // multi-dot others w/ message "Vampiric Touch", true, on => { WoWUnit dotTarget = AoeTargets.FirstOrDefault(u => u != Me.CurrentTarget && !u.HasMyAura("Vampiric Touch") && u.InLineOfSpellSight && !Spell.DoubleCastContains(u, "Vampiric Touch")); if (dotTarget != null && Spell.CanCastHack("Vampiric Touch", dotTarget)) { Logger.Write(LogColor.Hilite, "^Multi-DoT: cast Vampiric Touch on {0}", dotTarget.SafeName()); return dotTarget; } return null; }, req => true ), Spell.Buff( // multi-dot others w/ message "Shadow Word: Pain", true, on => { WoWUnit dotTarget = AoeTargets.FirstOrDefault(u => u != Me.CurrentTarget && !u.HasMyAura("Shadow Word: Pain") && u.InLineOfSpellSight && !Spell.DoubleCastContains(u, "Shadow Word: Pain")); if (dotTarget != null && Spell.CanCastHack("Shadow Word: Pain", dotTarget)) { Logger.Write(LogColor.Hilite, "^Multi-DoT: cast Shadow Word: Pain on {0}", dotTarget.SafeName()); return dotTarget; } return null; }, req => true ), // When we enter void form, even if AOE, we use our single target rotation after maintaining debuffs. new Decorator(ret => InVoidform, CreateMaintainVoidformBehaviour()), // filler spell Spell.Cast( "Mind Sear", mov => true, on => { IEnumerable <WoWUnit> AoeTargetsFacing = AoeTargets.Where(u => Me.IsSafelyFacing(u) && u.InLineOfSpellSight); WoWUnit unit = Clusters.GetBestUnitForCluster(AoeTargetsFacing, ClusterType.Radius, 10); if (unit == null) { return null; } if (4 > Clusters.GetClusterCount(unit, AoeTargets, ClusterType.Radius, 10)) { return null; } return unit; }, cancel => Me.HealthPercent < PriestSettings.ShadowHeal ), CastMindFlay(on => (WoWUnit)on, req => cntAoeTargets < 4) ) ), new Decorator(ret => InVoidform, CreateMaintainVoidformBehaviour()), CreateBuildVoidformBehaviour() ) ) )); }
public static Composite CreateElementalNormalCombat() { return(new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromLongRange(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Helpers.Common.CreateInterruptBehavior(), Totems.CreateTotemsBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), Dispelling.CreatePurgeEnemyBehavior("Purge"), Common.CreateShamanDpsShieldBehavior(), Spell.BuffSelf("Thunderstorm", ret => Unit.NearbyUnfriendlyUnits.Count(u => u.Distance < 10f) >= 3), Common.CastElementalBlast(), Spell.CastOnGround("Lightning Surge Totem", on => Clusters.GetBestUnitForCluster(Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f).Location, ret => ShamanSettings.UseLightningSurgeTotem && SingularRoutine.CurrentWoWContext == WoWContext.Normal && Unit.UnfriendlyUnits(8).Count() >= ShamanSettings.LightningSurgeTotemCount), Spell.BuffSelf("Earth Elemental", ret => ShamanSettings.EarthElementalHealthPercent <= Me.HealthPercent), Spell.BuffSelf("Totem Mastery", req => !Me.HasAura("Ember Totem")), // Only one buff should need to be checked. Spell.Buff("Flame Shock", true, req => !Me.CurrentTarget.HasMyAura("Flame Shock") && Me.CurrentTarget.TimeToDeath(int.MaxValue) > 6), Spell.Cast("Fire Elemental"), Spell.Cast("Earth Shock", req => MaelstormDeficit <= 0), Spell.Cast("Ascendance"), Spell.BuffSelf("Elemental Mastery"), Spell.Cast("Lava Burst"), Spell.CastOnGround("Earthquake Totem", on => Clusters.GetBestUnitForCluster(Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f).Location, req => Spell.UseAOE && Me.CurrentTarget.Distance < 34 && (Me.ManaPercent > ShamanSettings.EarthquakeMaelPercent || Me.GetAuraTimeLeft("Lucidity") > TimeSpan.Zero) && Unit.UnfriendlyUnitsNearTarget(10f).Count() >= ShamanSettings.EarthquakeCount - 1), Spell.Cast("Earth Shock", req => Me.CurrentMaelstrom >= 90), // Artifact Weapon new Decorator( ret => ShamanSettings.UseArtifactOnlyInAoE && Unit.UnfriendlyUnitsNearTarget(10f).Count() > 1, // Focused toward Chain Lightning new PrioritySelector( Spell.Cast("Stormkeeper", ret => ShamanSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.OnCooldown || !Common.HasTalent(ShamanTalents.Ascendance) || (ShamanSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.AtHighestDPSOpportunity && Common.HasTalent(ShamanTalents.Ascendance) && Spell.GetSpellCooldown("Ascendance") > TimeSpan.FromSeconds(15)) ) ) ), Spell.Cast("Stormkeeper", ret => ShamanSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.OnCooldown || !Common.HasTalent(ShamanTalents.Ascendance) || (ShamanSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.AtHighestDPSOpportunity && Common.HasTalent(ShamanTalents.Ascendance) && Spell.GetSpellCooldown("Ascendance") > TimeSpan.FromSeconds(15)) ), Spell.Cast("Chain Lightning", ret => Spell.UseAOE && Unit.UnfriendlyUnitsNearTarget(10f).Count() > 1 && !Unit.UnfriendlyUnitsNearTarget(10f).Any(u => u.IsCrowdControlled())), Spell.Cast("Lightning Bolt") ) ) // Movement.CreateMoveToUnitBehavior( on => StyxWoW.Me.CurrentTarget, 38f, 33f) // Movement.CreateMoveToRangeAndStopBehavior(ret => Me.CurrentTarget, ret => NormalPullDistance) )); }
//private static WoWUnit _moveToHealTarget = null; //private static WoWUnit _lastMoveToTarget = null; // temporary lol name ... will revise after testing public static Composite CreateHealingOnlyBehavior(bool selfOnly, bool moveInRange) { BehaviorType behaveType = Dynamics.CompositeBuilder.CurrentBehaviorType; if (SingularRoutine.CurrentWoWContext == WoWContext.Normal) { return(new ActionAlwaysFail()); } HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, Math.Max(DruidSettings.Heal.HealingTouch, DruidSettings.Heal.Regrowth)); int maxDirectHeal = Math.Max(DruidSettings.Heal.HealingTouch, DruidSettings.Heal.Regrowth); Logger.WriteDebugInBehaviorCreate("Druid Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); #region Cleanse if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; behavs.AddBehavior(dispelPriority, "Nature's Cure", "Nature's Cure", Dispelling.CreateDispelBehavior()); } #endregion #region Save the Group // Tank: Rebirth if (Helpers.Common.CombatRezTargetSetting != CombatRezTarget.None) { behavs.AddBehavior(799, "Rebirth Tank/Healer", "Rebirth", Helpers.Common.CreateCombatRezBehavior("Rebirth", filter => true, requirements => true) ); } if (DruidSettings.Heal.HeartOfTheWild != 0) { behavs.AddBehavior(795, "Heart of the Wild @ " + DruidSettings.Heal.HeartOfTheWild + "% MinCount: " + DruidSettings.Heal.CountHeartOfTheWild, "Heart of the Wild", new Decorator( ret => Me.IsInGroup(), Spell.BuffSelf( "Heart of the Wild", req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.HeartOfTheWild && DruidSettings.Heal.CountHeartOfTheWild <= HealerManager.Instance.TargetList .Count(p => p.IsAlive && p.HealthPercent <= DruidSettings.Heal.HeartOfTheWild && p.Location.DistanceSquared(((WoWUnit)req).Location) <= 30 * 30) ) ) ); } if (DruidSettings.Heal.Tranquility != 0) { behavs.AddBehavior(798, "Tranquility @ " + DruidSettings.Heal.Tranquility + "% MinCount: " + DruidSettings.Heal.CountTranquility, "Tranquility", new Decorator( ret => Me.IsInGroup(), Spell.Cast( "Tranquility", mov => true, on => (WoWUnit)on, req => HealerManager.Instance.TargetList.Count(h => h.IsAlive && h.HealthPercent < DruidSettings.Heal.Tranquility && h.SpellDistance() < 40) >= DruidSettings.Heal.CountTranquility, cancel => false ) ) ); } if (DruidSettings.Heal.Swiftmend != 0) { behavs.AddBehavior(797, "Swiftmend Direct @ " + DruidSettings.Heal.Swiftmend + "%", "Swiftmend", new Decorator( ret => (!Spell.IsSpellOnCooldown("Swiftmend") || Spell.GetCharges("Force of Nature") > 0) && ((WoWUnit)ret).PredictedHealthPercent(includeMyHeals: true) < DruidSettings.Heal.Swiftmend && (Me.IsInGroup()) && Spell.CanCastHack("Rejuvenation", (WoWUnit)ret, skipWowCheck: true), new Sequence( new DecoratorContinue( req => !((WoWUnit)req).HasAnyAura("Rejuvenation", "Regrowth"), new PrioritySelector( Spell.Buff("Rejuvenation", on => (WoWUnit)on), Spell.Cast("Regrowth", on => (WoWUnit)on, req => !glyphRegrowth, cancel => false) ) ), new Wait(TimeSpan.FromMilliseconds(500), until => ((WoWUnit)until).HasAnyAura("Rejuvenation", "Regrowth"), new ActionAlwaysSucceed()), new Wait(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(), new ActionAlwaysSucceed()), new PrioritySelector( Spell.Cast("Force of Nature", on => (WoWUnit)on, req => Spell.GetCharges("Force of Nature") > 1), Spell.Cast("Swiftmend", on => (WoWUnit)on) ) ) ) ); } #endregion #region Tank Buffing // Priority Buff: buff Mastery: Harmony if (Me.Level >= 80 && DruidSettings.Heal.BuffHarmony) { behavs.AddBehavior(100 + PriHighBase, "Buff Harmony w/ Healing Touch", "Healing Touch", new Sequence( Spell.Cast( "Healing Touch", mov => true, on => (WoWUnit)on, req => { if (Me.GetAuraTimeLeft("Harmony").TotalMilliseconds > 1500) { return(false); } if (((WoWUnit)req).HealthPercent < maxDirectHeal) { return(false); } if (Spell.DoubleCastContains(Me, "Harmony")) { return(false); } if (!Spell.CanCastHack("Healing Touch", (WoWUnit)req)) { return(false); } Logger.Write(LogColor.Hilite, "^Harmony: buffing with Healing Touch"); return(true); }, cancel => Me.GetAuraTimeLeft("Harmony").TotalMilliseconds > 1500 && ((WoWUnit)cancel).HealthPercent > cancelHeal ), new Action(r => Spell.UpdateDoubleCast("Harmony", Me)) ) ); } // Tank: Lifebloom behavs.AddBehavior(99 + PriHighBase, "Lifebloom - Tank", "Lifebloom", Spell.Buff("Lifebloom", on => GetLifebloomTarget(), req => Me.Combat) ); // Tank: Rejuv if Lifebloom not trained yet if (DruidSettings.Heal.Rejuvenation != 0) { behavs.AddBehavior(98 + PriHighBase, "Rejuvenation - Tank", "Rejuvenation", Spell.Buff("Rejuvenation", on => { WoWUnit unit = GetBestTankTargetFor("Rejuvenation"); if (unit != null && Spell.CanCastHack("Rejuvenation", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Rejuvenation ON TANK: {0}", unit.SafeName()); return(unit); } return(null); }) ); } if (DruidSettings.Heal.Ironbark != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Ironbark) + PriHighBase, "Ironbark @ " + DruidSettings.Heal.Ironbark + "%", "Ironbark", Spell.Buff("Ironbark", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.Ironbark) ); } else { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Ironbark) + PriHighBase, "Ironbark - Tank @ " + DruidSettings.Heal.Ironbark + "%", "Ironbark", Spell.Buff("Ironbark", on => Group.Tanks.FirstOrDefault(u => u.IsAlive && u.HealthPercent < DruidSettings.Heal.Ironbark && !u.HasActiveAura("Ironbark"))) ); } } if (DruidSettings.Heal.CenarionWard != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.CenarionWard) + PriHighBase, "Cenarion Ward @ " + DruidSettings.Heal.CenarionWard + "%", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.CenarionWard) ); } else { behavs.AddBehavior(100 + PriHighBase, "Cenarion Ward - Tanks", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => GetLifebloomTarget(), req => Me.Combat) ); } } if (DruidSettings.Heal.NaturesVigil != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.NaturesVigil) + PriHighBase, "Nature's Vigil @ " + DruidSettings.Heal.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.NaturesVigil) ); } else { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.NaturesVigil) + PriHighBase, "Nature's Vigil - Tank @ " + DruidSettings.Heal.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => Group.Tanks.FirstOrDefault(u => u.IsAlive && u.HealthPercent < DruidSettings.Heal.NaturesVigil && !u.HasActiveAura("Nature's Vigil"))) ); } } if (DruidSettings.Heal.TreeOfLife != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.TreeOfLife) + PriHighBase, "Incarnation: Tree of Life @ " + DruidSettings.Heal.TreeOfLife + "% MinCount: " + DruidSettings.Heal.CountTreeOfLife, "Incarnation: Tree of Life", Spell.BuffSelf("Incarnation: Tree of Life", req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.TreeOfLife && DruidSettings.Heal.CountTreeOfLife <= HealerManager.Instance.TargetList.Count(h => h.IsAlive && h.HealthPercent < DruidSettings.Heal.TreeOfLife)) ); } #endregion #region AoE Heals if (DruidSettings.Heal.Efflorescence != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Efflorescence) + PriAoeBase, "Efflorescence @ " + DruidSettings.Heal.Efflorescence, "Efflorescence", new Decorator( ret => Me.IsInGroup(), CreateMushroomSetBehavior() ) ); } if (DruidSettings.Heal.WildGrowth != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.WildGrowth) + PriAoeBase, "Wild Growth @ " + DruidSettings.Heal.WildGrowth + "% MinCount: " + DruidSettings.Heal.CountWildGrowth, "Wild Growth", new Decorator( ret => Me.IsInGroup(), new PrioritySelector( // ctx => HealerManager.GetBestCoverageTarget("Wild Growth", Settings.Heal.WildGrowth, 40, 30, Settings.Heal.CountWildGrowth), Spell.Buff( "Wild Growth", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.WildGrowth && DruidSettings.Heal.CountWildGrowth <= HealerManager.Instance.TargetList .Count(p => p.IsAlive && p.HealthPercent <= DruidSettings.Heal.WildGrowth && p.Location.DistanceSquared(((WoWUnit)req).Location) <= 30 * 30)) ) ) ); } #endregion #region Direct Heals // Regrowth above ToL: Lifebloom so we use Clearcasting procs behavs.AddBehavior(200 + PriSingleBase, "Regrowth on Clearcasting", "Regrowth", new Sequence( CastRegrowth( on => { if (Spell.DoubleCastContains(Me, "Clearcasting")) { return(null); } double clearLeft = Me.GetAuraTimeLeft("Clearcasting").TotalMilliseconds; // ignore if less than regrowth cast time left if (clearLeft < Spell.GetSpellCastTime("Regrowth").TotalMilliseconds) { return(null); } WoWUnit target = (WoWUnit)on; double healthPercent = target == null ? 0.0 : target.HealthPercent; // clearLeft > 3000, so clear target if not needed now and try again next pass if (target != null) { // still have enough time remaining on Clearcasting buff, so hold free Regrowth a bit longer to see if greater need arises if (healthPercent > maxDirectHeal) { target = null; } else if (!Spell.CanCastHack("Regrowth", target)) { target = null; } } if (target != null) { Logger.Write(LogColor.Hilite, "^Clearcasting: Regrowth at Health {0:F1}%", healthPercent); } return(target); }, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal && Me.GetAuraTimeLeft("Clearcasting").TotalMilliseconds > 4000 && !((WoWUnit)cancel).GetAuraTimeLeft("Lifebloom").TotalMilliseconds.Between(Me.CurrentCastTimeLeft.TotalMilliseconds, 8750) ), // add double cast entry to make sure we don't try to reuse immediately new Action(r => Spell.UpdateDoubleCast("Clearcasting", Me, 500)) ) ); behavs.AddBehavior(198 + PriSingleBase, "Rejuvenation @ " + DruidSettings.Heal.Rejuvenation + "%", "Rejuvenation", new PrioritySelector( Spell.Buff("Rejuvenation", 1, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.Rejuvenation ) ) ); if (DruidSettings.Heal.HealingTouch != 0) { // roll 3 Rejuvs if Glyph of Rejuvenation equipped if (glyphRejuv) { // make priority 1 higher than Noursh (-1 here due to way HealerManager.HealthToPriority works) behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.HealingTouch - 1) + PriSingleBase, "Roll 3 Rejuvenations for Glyph", "Rejuvenation", new PrioritySelector( Spell.Buff("Rejuvenation", 1, on => { // iterate through so we can stop at either 3 with rejuv or first without int cntHasAura = 0; foreach (WoWUnit h in HealerManager.Instance.TargetList) { if (h.IsAlive) { if (!h.HasKnownAuraExpired("Rejuvenation", 1)) { cntHasAura++; if (cntHasAura >= 3) { return(null); } } else { if (h.InLineOfSpellSight) { return(h); } } } } return(null); }, req => true ) ) ); } } int regrowthInstead = 0; bool healingTouchKnown = SpellManager.HasSpell("Healing Touch"); if (DruidSettings.Heal.HealingTouch != 0) { string whyRegrowth = ""; if (SpellManager.HasSpell("Regrowth")) { if (!healingTouchKnown) { regrowthInstead = Math.Max(DruidSettings.Heal.Regrowth, DruidSettings.Heal.HealingTouch); whyRegrowth = "Regrowth (since Healing Touch unknown) @ "; } } if (regrowthInstead != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.HealingTouch) + PriSingleBase, whyRegrowth + regrowthInstead + "%", "Regrowth", new PrioritySelector( Spell.Cast( sp => (Me.Combat || !healingTouchKnown) ? "Regrowth" : "Healing Touch", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent <regrowthInstead, cancel => ((WoWUnit)cancel).HealthPercent> cancelHeal ) ) ); } else { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.HealingTouch) + PriSingleBase, "Healing Touch @ " + DruidSettings.Heal.HealingTouch + "%", "Healing Touch", new PrioritySelector( Spell.Cast("Healing Touch", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent <DruidSettings.Heal.HealingTouch, cancel => ((WoWUnit)cancel).HealthPercent> cancelHeal ) ) ); } } if (DruidSettings.Heal.Regrowth != 0 && regrowthInstead == 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Regrowth) + PriSingleBase, "Regrowth @ " + DruidSettings.Heal.Regrowth + "%", "Regrowth", new PrioritySelector( Spell.Cast("Regrowth", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent <DruidSettings.Heal.Regrowth, cancel => ((WoWUnit)cancel).HealthPercent> cancelHeal ) ) ); } #endregion #region Lowest Priority Healer Tasks behavs.AddBehavior(3, "Rejuvenation while Moving @ " + SingularSettings.Instance.IgnoreHealTargetsAboveHealth + "%", "Rejuvenation", new Decorator( req => Me.IsMoving, Spell.Buff("Rejuvenation", on => HealerManager.Instance.TargetList.FirstOrDefault(h => h.IsAlive && h.HealthPercent < SingularSettings.Instance.IgnoreHealTargetsAboveHealth && !h.HasMyAura("Rejuvenation") && Spell.CanCastHack("Rejuvenation", h, true)), req => true ) ) ); #endregion behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal) { behavs.ListBehaviors(); } return(new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindHighestPriorityTarget(), // HealerManager.Instance.FirstUnit, Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown() && ret != null, behavs.GenerateBehaviorTree() ), new Decorator( ret => moveInRange, Movement.CreateMoveToUnitBehavior( ret => Battlegrounds.IsInsideBattleground ? (WoWUnit)ret : Group.Tanks.Where(a => a.IsAlive).OrderBy(a => a.Distance).FirstOrDefault(), 35f ) ) )); }
private static Composite CreateDispelBehavior() { return(new PrioritySelector( Spell.Cast("Cleanse", on => Me, ret => Dispelling.CanDispel(Me)), Spell.Cast("Cleanse", on => dispeltar, ret => Dispelling.CanDispel(dispeltar)))); }
public static Composite CreateShamanEnhancementNormalCombat() { return(new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromMelee(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( SingularRoutine.MoveBehaviorInlineToCombat(BehaviorType.Heal), SingularRoutine.MoveBehaviorInlineToCombat(BehaviorType.CombatBuffs), CreateEnhanceDiagnosticOutputBehavior(), Helpers.Common.CreateInterruptBehavior(), Totems.CreateTotemsBehavior(), Movement.WaitForFacing(), Movement.WaitForLineOfSpellSight(), Spell.BuffSelf("Feral Spirit", ret => !Me.CurrentTarget.IsTrivial() && NeedFeralSpirit), // Artifact Weapon new Decorator( ret => ShamanSettings.UseArtifactOnlyInAoE && Unit.NearbyUnitsInCombatWithMeOrMyStuff.Count() > 1, new PrioritySelector( Spell.Cast("Doom Winds", ret => ShamanSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.OnCooldown) ) ), Spell.Cast("Doom Winds", ret => !ShamanSettings.UseArtifactOnlyInAoE && ShamanSettings.UseDPSArtifactWeaponWhen == UseDPSArtifactWeaponWhen.OnCooldown), new Decorator( req => AttackEvenIfGhostWolf, new PrioritySelector( Dispelling.CreatePurgeEnemyBehavior("Purge"), Common.CreateShamanDpsShieldBehavior(), // Spell.BuffSelf("Spiritwalker's Grace", ret => StyxWoW.Me.IsMoving && StyxWoW.Me.Combat), // pull more logic (use instants first, then ranged pulls if possible) Spell.CastOnGround("Lightning Surge Totem", on => Clusters.GetBestUnitForCluster(Unit.NearbyUnfriendlyUnits, ClusterType.Radius, 8f).Location, ret => ShamanSettings.UseLightningSurgeTotem && SingularRoutine.CurrentWoWContext == WoWContext.Normal && Unit.UnfriendlyUnits(8).Count() >= ShamanSettings.LightningSurgeTotemCount), Spell.Cast("Ascendance", req => Me.HealthPercent <= ShamanSettings.AscendanceHealthPercent), Spell.Cast("Lava Lash", req => Me.HasActiveAura("Hot Hand")), Spell.Cast("Windsong"), Spell.Cast("Rockbiter", req => (Common.HasTalent(ShamanTalents.Boulderfist) && !Me.HasActiveAura("Boulderfist")) || (Common.HasTalent(ShamanTalents.Landslide) && !Me.HasActiveAura("Landslide"))), Spell.Cast("Frostbrand", req => Common.HasTalent(ShamanTalents.Hailstorm) && !Me.HasActiveAura("Frostbrand")), Spell.Cast("Boulderfist", req => Me.CurrentMaelstrom < 130 && Spell.GetCharges("Boulderfist") >= 2), Spell.Cast("Flametongue", req => !Me.HasActiveAura("Flametongue")), Spell.Cast("Feral Spirit", ret => NeedFeralSpirit), Spell.Cast("Earthen Spike"), Spell.Cast("Crash Lightning", when => Unit.UnfriendlyUnitsNearTarget(10).Count(u => u.TaggedByMe || !u.TaggedByOther) >= 2), Spell.Cast("Stormstrike"), Spell.Cast("Crash Ligthning", req => Common.HasTalent(ShamanTalents.CrashingStorm)), Spell.Cast("Lava Lash", req => Me.CurrentMaelstrom > 110), Spell.Cast("Sundering", req => SingularRoutine.CurrentWoWContext != WoWContext.Instances), Spell.Cast("Rockbiter"), Spell.Cast("Lightning Bolt", req => !Me.CurrentTarget.IsWithinMeleeRange), // won't happen often, but if at range and no abilities enter ghost wolf CreateInCombatGapCloser() ) ) ) ), Movement.CreateMoveToMeleeBehavior(true) )); }
internal static Composite CreatePaladinHealBehavior(bool selfOnly, bool moveInRange) { HealerManager.NeedHealTargeting = true; return (new PrioritySelector( ctx => _healTarget = (selfOnly ? StyxWoW.Me : HealerManager.Instance.FirstUnit), new Decorator( ret => ret != null && (selfOnly || moveInRange || ((WoWUnit)ret).InLineOfSpellSight && ((WoWUnit)ret).DistanceSqr < 40 * 40), new PrioritySelector( Spell.WaitForCast(), new Decorator( ret => moveInRange, Movement.CreateMoveToLosBehavior(ret => (WoWUnit)ret)), Dispelling.CreateDispelBehavior(), Spell.BuffSelf("Avenging Wrath", req => Unit.NearbyFriendlyPlayers.Count(p => p.HealthPercent <= SingularSettings.Instance.Paladin().AvengingHealth&& p.DistanceSqr < 40 * 40) >= SingularSettings.Instance.Paladin().AvengingCount), Spell.BuffSelf("Holy Avenger", req => Me.HasAura("Avenging Wrath")), Spell.Cast( "Light of Dawn", ret => StyxWoW.Me, ret => Unit.NearbyFriendlyPlayers.Count(p => p.HealthPercent <= SingularSettings.Instance.Paladin().AuraMasteryHealth&& p.DistanceSqr < 40 * 40 && StyxWoW.Me.IsSafelyFacing(p.Location)) >= SingularSettings.Instance.Paladin().AuraMasteryCount), Spell.Cast( "Beacon of Light", ret => (WoWUnit)ret, ret => ret is WoWPlayer && !Common.HasTalent(PaladinTalents.BeaconOfVirtue) && Group.Tanks.Contains((WoWPlayer)ret) && Group.Tanks.All(t => !t.HasMyAura("Beacon of Light"))), Spell.Cast( "Beacon of Light", ret => (WoWUnit)ret, ret => ret is WoWPlayer && Common.HasTalent(PaladinTalents.BeaconOfVirtue) && Unit.NearbyFriendlyPlayers.Count(p => p.HealthPercent <= SingularSettings.Instance.Paladin().BeaconVirtueHealth&& p.DistanceSqr < 30 * 30) >= SingularSettings.Instance.Paladin().BeaconVirtueCount), Spell.Cast( "Holy Shock", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).HealthPercent <= SingularSettings.Instance.Paladin().HolyShockHealth), Spell.Cast("Holy Prism", req => Me.GotTarget), Spell.Cast( "Bestow Faith", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).HealthPercent <= SingularSettings.Instance.Paladin().BestowFaithHealth), Spell.Cast( "Lay on Hands", ret => (WoWUnit)ret, ret => StyxWoW.Me.Combat && !((WoWUnit)ret).HasAura("Forbearance") && ((WoWUnit)ret).HealthPercent <= SingularSettings.Instance.Paladin().LayOnHandsHealth), Spell.Cast( "Light of Dawn", ret => StyxWoW.Me, ret => Unit.NearbyFriendlyPlayers.Count(p => p.HealthPercent <= SingularSettings.Instance.Paladin().LightOfDawnHealth&& p != StyxWoW.Me && p.DistanceSqr < 30 * 30 && StyxWoW.Me.IsSafelyFacing(p.Location)) >= SingularSettings.Instance.Paladin().LightOfDawnCount), Spell.Cast( "Tyr's Deliverance", ret => StyxWoW.Me, ret => PaladinSettings.UseDPSArtifactWeaponWhen != UseDPSArtifactWeaponWhen.None && Unit.NearbyFriendlyPlayers.Count(p => p.HealthPercent <= SingularSettings.Instance.Paladin().TyrsDeliveranceHealth&& p.DistanceSqr < 15 * 15) >= SingularSettings.Instance.Paladin().TyrsDeliveranceCount), Spell.Cast( "Flash of Light", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).HealthPercent <= SingularSettings.Instance.Paladin().FlashOfLightHealth), Spell.Cast( "Holy Light", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).HealthPercent <= SingularSettings.Instance.Paladin().HolyLightHealth), new Decorator( ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget() && Unit.NearbyFriendlyPlayers.Count(u => u.IsInMyPartyOrRaid()) == 0, new PrioritySelector( Helpers.Common.EnsureReadyToAttackFromMelee(), Helpers.Common.CreateInterruptBehavior(), Spell.Buff("Judgment"), Spell.Cast("Holy Shock"), Spell.Cast("Crusader Strike"), Movement.CreateMoveToMeleeBehavior(true) ) ), new Decorator( ret => moveInRange, // Get in range Movement.CreateMoveToUnitBehavior(on => _healTarget, 35f, 30f) ) ) ) )); }
public static Composite CreateMistweaverMonkHealing(bool selfOnly = false) { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = SingularSettings.Instance.IgnoreHealTargetsAboveHealth; cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.RenewingMist); cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.EnvelopingMist); cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.Effuse); bool moveInRange = false; if (!selfOnly) { moveInRange = (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds); } Logger.WriteDebugInBehaviorCreate("Monk Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { behavs.AddBehavior(dispelPriority, "Detox", "Detox", Dispelling.CreateDispelBehavior()); } CreateMistweaverHealingRotation(selfOnly, behavs); behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) { behavs.ListBehaviors(); } return(new PrioritySelector( // ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // ctx => selfOnly ? StyxWoW.Me : HealerManager.Instance.FirstUnit, ctx => selfOnly ? StyxWoW.Me : MyHealTarget, CreateMistWeaverDiagnosticOutputBehavior(ret => (WoWUnit)ret), new Decorator( ret => ret != null && (Me.Combat || ((WoWUnit)ret).Combat || ((WoWUnit)ret).PredictedHealthPercent() <= 99), // && HealerManager.SavingHealUnit == null // && (selfOnly || !MonkSettings.MistHealSettings.HealFromMelee || !Me.GotTarget() || Me.CurrentTarget.IsWithinMeleeRange), new PrioritySelector( new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( behavs.GenerateBehaviorTree(), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 40f, 34f) ) ) ) ) ) ) ) )); }