// temporary lol name ... will revise after testing public static Composite CreateRestoPriestHealingOnlyBehavior(bool selfOnly, bool moveInRange) { 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(PriestSettings.HolyHeal.Heal, Math.Max(PriestSettings.HolyHeal.GreaterHeal, PriestSettings.HolyHeal.Renew ))); Logger.WriteDebugInBehaviorCreate("Priest Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; behavs.AddBehavior(dispelPriority, "Dispel", null, Common.CreatePriestDispelBehavior()); } #region Save the Group if (PriestSettings.HolyHeal.DivineHymn != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.DivineHymn) + 300, "Divine Hymn @ " + PriestSettings.HolyHeal.DivineHymn + "% MinCount: " + PriestSettings.HolyHeal.CountDivineHymn, "Divine Hymn", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Divine Hymn", PriestSettings.HolyHeal.DivineHymn, 0, 40, PriestSettings.HolyHeal.CountDivineHymn), new Decorator( ret => ret != null, Spell.Cast("Divine Hymn", mov => false, on => (WoWUnit)on, req => true, cancel => false) ) ) ) ); if (PriestSettings.HolyHeal.VoidShift != 0 ) behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.VoidShift) + 300, "Void Shift @ " + PriestSettings.HolyHeal.VoidShift + "%", "Void Shift", Spell.Cast("Void Shift", mov => false, on => (WoWUnit)on, req => ((WoWUnit)req).IsPlayer && ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.VoidShift ) ); if (PriestSettings.HolyHeal.GuardianSpirit != 0 ) behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.GuardianSpirit) + 300, "Guardian Spirit @ " + PriestSettings.HolyHeal.GuardianSpirit + "%", "Guardian Spirit", Spell.Cast("Guardian Spirit", mov => false, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.GuardianSpirit ) ); #endregion #region Tank Buffing if (PriestSettings.HolyHeal.PowerWordShield != 0 ) behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.PowerWordShield) + 200, "Tank - Power Word: Shield @ " + PriestSettings.HolyHeal.PowerWordShield + "%", "Power Word: Shield", Spell.Cast("Power Word: Shield", on => { WoWUnit unit = Common.GetBestTankTargetForPWS(PriestSettings.HolyHeal.PowerWordShield); if (unit != null && Spell.CanCastHack("Power Word: Shield", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Power Word: Shield ON TANK: {0}", unit.SafeName()); return unit; } return null; }) ); #endregion #region AoE Heals int maxDirectHeal = Math.Max(PriestSettings.HolyHeal.FlashHeal, Math.Max(PriestSettings.HolyHeal.Heal, PriestSettings.HolyHeal.GreaterHeal)); if (maxDirectHeal != 0 ) behavs.AddBehavior(399, "Divine Insight - Prayer of Mending @ " + maxDirectHeal.ToString() + "%", "Prayer of Mending", new Decorator( ret => (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid) && Me.HasAura("Divine Insight"), new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Prayer of Mending", maxDirectHeal, 40, 20, 2), new Decorator( ret => ret != null, Spell.Cast("Prayer of Mending", on => (WoWUnit)on, req => true) ) ) ) ); // instant, so cast this first ALWAYS if (PriestSettings.HolyHeal.HolyWordSanctuary != 0 ) behavs.AddBehavior(398, "Holy Word: Sanctuary @ " + PriestSettings.HolyHeal.HolyWordSanctuary + "% MinCount: " + PriestSettings.HolyHeal.CountHolyWordSanctuary, "Chakra: Sanctuary", new Decorator( ret => Me.HasAura("Chakra: Sanctuary"), new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Holy Word: Sanctuary", PriestSettings.HolyHeal.HolyWordSanctuary, 40, 8, PriestSettings.HolyHeal.CountHolyWordSanctuary), new Decorator( ret => ret != null, Spell.CastOnGround("Holy Word: Sanctuary", on => (WoWUnit)on, req => true, false) ) ) ) ); /* behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.HolyLevel90Talent) + 200, "Divine Star", "Divine Star", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new Decorator( ret => Clusters.GetClusterCount( Me, HealerManager.Instance.TargetList.Where(u => u.HealthPercent < PriestSettings.HolyHeal.CountLevel90Talent).ToList(), ClusterType.Path, 4 ) >= PriestSettings.HolyHeal.CountLevel90Talent, Spell.Cast("Divine Star", on => (WoWUnit)on, req => true) ) ) ); */ if (PriestSettings.HolyHeal.HolyLevel90Talent != 0 ) behavs.AddBehavior(397, "Halo @ " + PriestSettings.HolyHeal.HolyLevel90Talent + "% MinCount: " + PriestSettings.HolyHeal.CountLevel90Talent, "Halo", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new Decorator( ret => ret != null && HealerManager.Instance.TargetList.Count(u => u.IsAlive && u.HealthPercent < PriestSettings.HolyHeal.HolyLevel90Talent && u.Distance < 30) >= PriestSettings.HolyHeal.CountLevel90Talent, Spell.CastOnGround("Halo", on => (WoWUnit)on, req => true) ) ) ); if (PriestSettings.HolyHeal.HolyLevel90Talent != 0 ) behavs.AddBehavior(397, "Cascade @ " + PriestSettings.HolyHeal.HolyLevel90Talent + "% MinCount: " + PriestSettings.HolyHeal.CountLevel90Talent, "Cascade", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Cascade", PriestSettings.HolyHeal.HolyLevel90Talent, 40, 30, PriestSettings.HolyHeal.CountLevel90Talent), new Decorator( ret => ret != null, Spell.Cast("Cascade", on => (WoWUnit)on, req => true) ) ) ) ); if (PriestSettings.HolyHeal.PrayerOfMending != 0 ) behavs.AddBehavior(397, "Prayer of Mending @ " + PriestSettings.HolyHeal.PrayerOfMending + "% MinCount: " + PriestSettings.HolyHeal.CountPrayerOfMending, "Prayer of Mending", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Prayer of Mending", PriestSettings.HolyHeal.PrayerOfMending, 40, 20, PriestSettings.HolyHeal.CountPrayerOfMending), new Decorator( ret => ret != null, Spell.Cast("Prayer of Mending", on => (WoWUnit)on, req => true) ) ) ) ); if ( PriestSettings.HolyHeal.BindingHeal != 0 ) { if (!TalentManager.HasGlyph("Binding Heal")) { behavs.AddBehavior(396, "Binding Heal @ " + PriestSettings.HolyHeal.BindingHeal + "% MinCount: 2", "Binding Heal", Spell.Cast("Binding Heal", mov => true, on => (WoWUnit)on, req => !((WoWUnit)req).IsMe && ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.BindingHeal && Me.HealthPercent < PriestSettings.HolyHeal.BindingHeal, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); } else { behavs.AddBehavior(396, "Binding Heal (glyphed) @ " + PriestSettings.HolyHeal.BindingHeal + "% MinCount: 3", "Binding Heal", Spell.Cast("Binding Heal", mov => true, on => (WoWUnit)on, req => !((WoWUnit)req).IsMe && ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.BindingHeal && Me.HealthPercent < PriestSettings.HolyHeal.BindingHeal && HealerManager.Instance.TargetList.Any( h => h.IsAlive && !h.IsMe && h.Guid != ((WoWUnit)req).Guid && h.HealthPercent < PriestSettings.HolyHeal.BindingHeal && h.SpellDistance() < 20), cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); } } if (PriestSettings.HolyHeal.CircleOfHealing != 0 ) behavs.AddBehavior(395, "Circle of Healing @ " + PriestSettings.HolyHeal.CircleOfHealing + "% MinCount: " + PriestSettings.HolyHeal.CountCircleOfHealing, "Circle of Healing", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Circle of Healing", PriestSettings.HolyHeal.CircleOfHealing, 40, 30, PriestSettings.HolyHeal.CountCircleOfHealing), Spell.Cast("Circle of Healing", on => (WoWUnit)on) ) ) ); if ( PriestSettings.HolyHeal.PrayerOfHealing != 0 ) behavs.AddBehavior(394, "Prayer of Healing @ " + PriestSettings.HolyHeal.PrayerOfHealing + "% MinCount: " + PriestSettings.HolyHeal.CountPrayerOfHealing, "Prayer of Healing", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Prayer of Healing", PriestSettings.HolyHeal.PrayerOfHealing, 40, 30, PriestSettings.HolyHeal.CountPrayerOfHealing), Spell.Cast("Prayer of Healing", on => (WoWUnit)on) ) ) ); #endregion /* HolyWordSanctuary Holy Word: Sanctuary HolyWordSerenity Holy Word: Serenity CircleOfHealing Circle of Healing PrayerOfHealing Prayer of Healing DivineHymn Divine Hymn GuardianSpirit Guardian Spirit VoidShift Void Shift */ #region Direct Heals if (PriestSettings.HolyHeal.HolyWordSerenity != 0) behavs.AddBehavior(HealthToPriority(1) + 4, "Holy Word: Serenity @ " + PriestSettings.HolyHeal.HolyWordSerenity + "%", "Chakra: Serenity", Spell.CastHack("Holy Word: Serenity", on => (WoWUnit)on, req => Me.HasAura("Chakra: Serenity") && ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.HolyWordSerenity ) ); if (maxDirectHeal != 0) behavs.AddBehavior(HealthToPriority(1) + 3, "Surge of Light @ " + maxDirectHeal + "%", "Flash Heal", Spell.Cast("Flash Heal", mov => true, on => (WoWUnit)on, req => Me.HasAura("Surge of Light") && ((WoWUnit)req).HealthPercent < maxDirectHeal ) ); string cmt = ""; int flashHealHealth = PriestSettings.DiscHeal.FlashHeal; if (!SpellManager.HasSpell("Greater Heal")) { flashHealHealth = Math.Max(flashHealHealth, PriestSettings.DiscHeal.GreaterHeal); cmt = "(Adjusted for Greater Heal)"; } if (!SpellManager.HasSpell("Heal")) { flashHealHealth = Math.Max(flashHealHealth, PriestSettings.DiscHeal.Heal); cmt = "(Adjusted for Heal)"; } if (flashHealHealth != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.FlashHeal), "Flash Heal @ " + flashHealHealth + "% " + cmt, "Flash Heal", Spell.Cast("Flash Heal", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < flashHealHealth, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); if (PriestSettings.HolyHeal.GreaterHeal != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.GreaterHeal), "Greater Heal @ " + PriestSettings.HolyHeal.GreaterHeal + "%", "Greater Heal", Spell.Cast( "Greater Heal", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.GreaterHeal, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); if (PriestSettings.HolyHeal.Heal != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.HolyHeal.Heal), "Heal @ " + PriestSettings.HolyHeal.Heal + "%", "Heal", Spell.Cast("Heal", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.Heal, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); #endregion #region Tank - Low Priority Buffs behavs.AddBehavior(HealthToPriority(101), "Tank - Refresh Renew w/ Serenity", "Chakra: Serenity", Spell.CastHack("Holy Word: Serenity", on => { if (Me.HasAura("Chakra: Serenity")) { WoWUnit unit = Group.Tanks .Where(u => u.IsAlive && u.Combat && u.DistanceSqr < 40 * 40 && u.GetAuraTimeLeft("Renew").TotalMilliseconds.Between(350, 2500) && u.InLineOfSpellSight) .OrderBy(u => u.HealthPercent) .FirstOrDefault(); if (unit != null && Spell.CanCastHack("Renew", unit, skipWowCheck: true)) { Logger.WriteDebug("Refreshing RENEW ON TANK: {0}", unit.SafeName()); return unit; } } return null; }, req => true) ); if (PriestSettings.HolyHeal.Renew != 0) behavs.AddBehavior(HealthToPriority(102), "Tank - Buff Renew @ " + PriestSettings.HolyHeal.Renew + "%", "Renew", // roll Renew on Tanks Spell.Cast("Renew", on => { WoWUnit unit = HealerManager.GetBestTankTargetForHOT("Renew", PriestSettings.HolyHeal.Renew); if (unit != null && Spell.CanCastHack("Renew", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing RENEW ON TANK: {0}", unit.SafeName()); return unit; } return null; }) ); #endregion #region Lowest Priority Healer Tasks if (SingularRoutine.CurrentWoWContext == WoWContext.Instances) { behavs.AddBehavior(HealthToPriority(103), "Lightwell", "Lightwell", new Throttle(TimeSpan.FromSeconds(20), new Sequence( new Action(r => { _lightwellTarget = Group.Tanks.FirstOrDefault(t => { if (t.IsAlive && t.Combat && t.GotTarget && t.CurrentTarget.IsBoss()) { if (t.Distance < 40 && t.SpellDistance(t.CurrentTarget) < 10) { return true; } } return false; }); }), Spell.CastOnGround("Lightwell", location => WoWMathHelper.CalculatePointFrom(Me.Location, _lightwellTarget.Location, (float)Math.Min(10.0, _lightwellTarget.Distance / 2)), req => _lightwellTarget != null, false, desc => string.Format("10 yds from {0}", _lightwellTarget.SafeName()) ) ) ) ); } #endregion behavs.OrderBehaviors(); if ( selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) behavs.ListBehaviors(); return new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // 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 ) ) ); }
// temporary lol name ... will revise after testing public static Composite CreateRestoPriestHealingOnlyBehavior(bool selfOnly, bool moveInRange) { 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(PriestSettings.DiscHeal.Heal, Math.Max(PriestSettings.DiscHeal.GreaterHeal, PriestSettings.DiscHeal.Renew))); Logger.WriteDebugInBehaviorCreate("Priest Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; behavs.AddBehavior(dispelPriority, "Dispel", null, Common.CreatePriestDispelBehavior()); } #region Save the Group if (PriestSettings.DiscHeal.VoidShift != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.VoidShift) + PriEmergencyBase, "Void Shift @ " + PriestSettings.DiscHeal.VoidShift + "%", "Void Shift", Spell.Cast("Void Shift", mov => false, on => (WoWUnit)on, req => ((WoWUnit)req).IsPlayer && ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.VoidShift && Me.HealthPercent > ((WoWUnit)req).HealthPercent ) ); behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.PainSuppression) + PriEmergencyBase, "Pain Suppression @ " + PriestSettings.DiscHeal.PainSuppression + "%", "Pain Suppression", Spell.Cast("Pain Suppression", mov => false, on => (WoWUnit)on, req => ((WoWUnit)req).IsPlayer && ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.PainSuppression ) ); behavs.AddBehavior(HealthToPriority(99) + PriEmergencyBase, "Desperate Prayer @ " + PriestSettings.DesperatePrayerHealth + "%", "Desperate Prayer", Spell.Cast("Desperate Prayer", ret => Me, ret => Me.Combat && Me.HealthPercent < PriestSettings.DesperatePrayerHealth) ); behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.PowerWordBarrier) + PriEmergencyBase, "Power Word: Barrier @ " + PriestSettings.DiscHeal.PowerWordBarrier + "%", "Power Word: Barrier", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => GetBestPowerWordBarrierTarget(), new Decorator( ret => ret != null, new PrioritySelector( new Sequence( new Action(r => Logger.WriteDebug("PW:B - attempting cast")), Spell.CastOnGround("Power Word: Barrier", on => (WoWUnit)on, req => true, false) ), new Action( ret => { if (ret != null) { if (!((WoWUnit)ret).IsValid) Logger.WriteDebug("PW:B - FAILED - Healing Target object became invalid"); else if (((WoWUnit)ret).Distance > 40) Logger.WriteDebug("PW:B - FAILED - Healing Target moved out of range"); else if (!Spell.CanCastHack("Power Word: Barrier")) Logger.WriteDebug("PW:B - FAILED - Spell.CanCastHack() said NO to Power Word: Barrier"); else if (Styx.WoWInternals.World.GameWorld.IsInLineOfSpellSight(StyxWoW.Me.GetTraceLinePos(), ((WoWUnit)ret).Location)) Logger.WriteDebug("PW:B - FAILED - Spell.CanCastHack() unit location not in Line of Sight"); else if (Spell.IsSpellOnCooldown("Power Word: Barrier")) Logger.WriteDebug("PW:B - FAILED - Power Word: Barrier is on cooldown"); else Logger.WriteDebug("PW:B - Something FAILED with Power Word: Barrier cast sequence (target={0}, h={1:F1}%, d={2:F1} yds, spellmax={3:F1} yds, cooldown={4})", ((WoWUnit)ret).SafeName(), ((WoWUnit)ret).HealthPercent, ((WoWUnit)ret).Distance, Spell.ActualMaxRange("Power Word: Barrier", (WoWUnit)ret), Spell.IsSpellOnCooldown("Power Word: Barrier") ); } return RunStatus.Failure; }) ) ) ) ) ); #endregion #region Tank Buffing if (PriestSettings.DiscHeal.PrayerOfHealing != 0) behavs.AddBehavior(HealthToPriority(100) + PriHighBase, "Spirit Shell - Group MinCount: " + PriestSettings.DiscHeal.CountPrayerOfHealing, "Prayer of Healing", new Decorator( ret => IsSpiritShellEnabled(), Spell.Cast("Prayer of Healing", on => { WoWUnit unit = HealerManager.GetBestCoverageTarget("Prayer of Healing", 101, 40, 30, PriestSettings.DiscHeal.CountPrayerOfHealing, req => !HasSpiritShellAbsorb((WoWUnit)req)); if (unit != null && Spell.CanCastHack("Prayer of Healing", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Spirit Shell with Prayer of Healing on Group: {0}", unit.SafeName()); return unit; } return null; }) ) ); behavs.AddBehavior(HealthToPriority(99) + PriHighBase, "Spirit Shell - Tank", "Spirit Shell", new Decorator( req => IsSpiritShellEnabled(), Spell.Cast("Greater Heal", on => { WoWUnit unit = Group.Tanks.Where(t => t.IsAlive && t.Combat && !HasSpiritShellAbsorb(t) && t.SpellDistance() < 40).OrderBy( a => a.Distance).FirstOrDefault(); if (unit != null && Spell.CanCastHack("Greater Heal", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Spirit Shell with Greater Heal on TANK: {0}", unit.SafeName()); return unit; } return null; }) ) ); behavs.AddBehavior(HealthToPriority(98) + PriHighBase, "Power Word: Shield - Tank", "Power Word: Shield", Spell.Cast("Power Word: Shield", on => { WoWUnit unit = GetDiscBestTankTargetForPWS(); if (unit != null && Spell.CanCastHack("Power Word: Shield", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Power Word: Shield ON TANK: {0}", unit.SafeName()); return unit; } return null; }) ); if (PriestSettings.DiscHeal.Renew != 0) { behavs.AddBehavior(HealthToPriority(97) + PriHighBase, "Renew @" + PriestSettings.DiscHeal.Renew + "% while moving", "Renew", Spell.Cast("Renew", on => { WoWUnit unit = Group.Tanks.Where(u => u.IsAlive && u.HealthPercent < PriestSettings.DiscHeal.Renew && u.DistanceSqr < 40 * 40 && !u.HasAura("Renew") && u.InLineOfSpellSight).OrderBy(u => u.HealthPercent).FirstOrDefault(); if (unit != null && Spell.CanCastHack("Renew", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Renew ON TANK: {0}", unit.SafeName()); return unit; } return null; }, req => Me.IsMoving && Spell.IsSpellOnCooldown("Power Word: Shield")) ); } #endregion #region Atonement Only // only Atonement healing if above Health % if (AddAtonementBehavior() ) { behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.AtonementAbovePercent) + PriHighAtone, "Atonement Above " + PriestSettings.DiscHeal.AtonementAbovePercent + "%", "Atonement", new Decorator( req => (Me.Combat || SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) && HealerManager.Instance.TargetList.Count(h => h.HealthPercent < PriestSettings.DiscHeal.AtonementAbovePercent) < PriestSettings.DiscHeal.AtonementAboveCount, new PrioritySelector( CreateDiscAtonmementEnsureTarget(), CreateDiscAtonementMovement(), new Decorator( req => Unit.ValidUnit(Me.CurrentTarget), new PrioritySelector( new Action(r => { Logger.WriteDebug("--- atonement only! ---"); return RunStatus.Failure; }), Movement.CreateFaceTargetBehavior(), // Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5)), Common.CreateHolyFireBehavior(), Spell.Cast("Penance", mov => true, on => Me.CurrentTarget, req => true, cancel => false), Spell.Cast("Smite", mov => true, on => Me.CurrentTarget, req => true, cancel => false) ) ) ) ) ); } #endregion #region AoE Heals int maxDirectHeal = Math.Max(PriestSettings.DiscHeal.FlashHeal, Math.Max(PriestSettings.DiscHeal.Heal, PriestSettings.DiscHeal.GreaterHeal)); if (PriestSettings.DiscHeal.SpiritShell != 0) behavs.AddBehavior(201 + PriAoeBase, "Spirit Shell Activate @ " + PriestSettings.DiscHeal.SpiritShell + "% MinCount: " + PriestSettings.DiscHeal.CountSpiritShell, "Spirit Shell", new Decorator( ret => Me.Combat && Spell.CanCastHack("Spirit Shell", Me, true) && HealerManager.Instance.TargetList.Count(h => h.HealthPercent < PriestSettings.DiscHeal.SpiritShell) >= PriestSettings.DiscHeal.CountSpiritShell, new Sequence( Spell.BuffSelf("Spirit Shell", req => true), new PrioritySelector( new Wait( TimeSpan.FromMilliseconds(500), until => IsSpiritShellEnabled(), new Action( r => Logger.WriteDebug("buff for Spirit Shell now visible"))), new Action( r => Logger.WriteDebug("cast successfull but Spirit Shell buff not visible")) ) ) ) ); if (PriestSettings.DiscHeal.PrayerOfMending != 0) behavs.AddBehavior(200 + PriAoeBase, "Prayer of Mending @ " + PriestSettings.DiscHeal.PrayerOfMending + "% MinCount: " + PriestSettings.DiscHeal.CountPrayerOfMending, "Prayer of Mending", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Prayer of Mending", PriestSettings.DiscHeal.PrayerOfMending, 40, 20, PriestSettings.DiscHeal.CountPrayerOfMending), new Decorator( ret => ret != null && Spell.CanCastHack("Prayer of Mending", (WoWUnit) ret), new PrioritySelector( Spell.OffGCD(Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5))), Spell.Cast("Prayer of Mending", on => (WoWUnit)on, req => true) ) ) ) ) ); if (PriestSettings.DiscHeal.DiscLevel90Talent != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.DiscLevel90Talent) + PriAoeBase, "Halo @ " + PriestSettings.DiscHeal.DiscLevel90Talent + "% MinCount: " + PriestSettings.DiscHeal.CountLevel90Talent, "Halo", new Decorator( ret => SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds || SingularRoutine.CurrentWoWContext == WoWContext.Instances, new Decorator( ret => ret != null && HealerManager.Instance.TargetList.Count(u => u.IsAlive && u.HealthPercent < PriestSettings.DiscHeal.DiscLevel90Talent && u.Distance < 30) >= PriestSettings.DiscHeal.CountLevel90Talent && Spell.CanCastHack("Halo", (WoWUnit)ret), new PrioritySelector( Spell.OffGCD(Spell.BuffSelf("Archangel", req => ((WoWUnit)req) != null && Me.HasAura("Evangelism", 5))), Spell.CastOnGround("Halo", on => (WoWUnit)on, req => true) ) ) ) ); if (PriestSettings.DiscHeal.DiscLevel90Talent != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.DiscLevel90Talent) + PriAoeBase, "Cascade @ " + PriestSettings.DiscHeal.DiscLevel90Talent + "% MinCount: " + PriestSettings.DiscHeal.CountLevel90Talent, "Cascade", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Cascade", PriestSettings.DiscHeal.DiscLevel90Talent, 40, 30, PriestSettings.DiscHeal.CountLevel90Talent), new Decorator( ret => ret != null && Spell.CanCastHack("Cascade", (WoWUnit) ret), new PrioritySelector( Spell.OffGCD(Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5))), Spell.Cast("Cascade", on => (WoWUnit)on, req => true) ) ) ) ) ); if (PriestSettings.DiscHeal.BindingHeal != 0) { if (!TalentManager.HasGlyph("Binding Heal")) { behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.BindingHeal) + PriAoeBase, "Binding Heal @ " + PriestSettings.DiscHeal.BindingHeal + "% MinCount: 2", "Binding Heal", Spell.Cast("Binding Heal", mov => true, on => (WoWUnit)on, req => !((WoWUnit)req).IsMe && ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.BindingHeal && Me.HealthPercent < PriestSettings.DiscHeal.BindingHeal, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); } else { behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.BindingHeal) + PriAoeBase, "Binding Heal (glyphed) @ " + PriestSettings.DiscHeal.BindingHeal + "% MinCount: 3", "Binding Heal", Spell.Cast("Binding Heal", mov => true, on => (WoWUnit)on, req => !((WoWUnit)req).IsMe && ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.BindingHeal && Me.HealthPercent < PriestSettings.DiscHeal.BindingHeal && HealerManager.Instance.TargetList.Any(h => h.IsAlive && !h.IsMe && h.Guid != ((WoWUnit)req).Guid && h.HealthPercent < PriestSettings.DiscHeal.BindingHeal && h.SpellDistance() < 20), cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); } } if (PriestSettings.DiscHeal.PrayerOfHealing != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.PrayerOfHealing) + PriAoeBase, "Prayer of Healing @ " + PriestSettings.DiscHeal.PrayerOfHealing + "% MinCount: " + PriestSettings.DiscHeal.CountPrayerOfHealing, "Prayer of Healing", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Prayer of Healing", PriestSettings.DiscHeal.PrayerOfHealing, 40, 30, PriestSettings.DiscHeal.CountPrayerOfHealing), CastBuffsBehavior("Prayer of Healing"), Spell.Cast("Prayer of Healing", on => (WoWUnit)on) ) ) ); #endregion #region Direct Heals if (PriestSettings.DiscHeal.PowerWordShield != 0) { behavs.AddBehavior(99 + PriSingleBase, "Power Word: Shield @ " + PriestSettings.DiscHeal.PowerWordShield + "%", "Power Word: Shield", Spell.Buff("Power Word: Shield", on => (WoWUnit) on, req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.PowerWordShield && CanWePwsUnit((WoWUnit) req)) ); } if (PriestSettings.DiscHeal.Penance != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.Penance) + PriSingleBase, "Penance @ " + PriestSettings.DiscHeal.Penance + "%", "Penance", new Decorator( req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.Penance, new PrioritySelector( CastBuffsBehavior("Penance"), Spell.Cast("Penance", mov => true, on => (WoWUnit)on, req => true, cancel => false ) ) ) ); string cmt = ""; int flashHealHealth = PriestSettings.DiscHeal.FlashHeal; if (!SpellManager.HasSpell("Greater Heal")) { flashHealHealth = Math.Max(flashHealHealth, PriestSettings.DiscHeal.GreaterHeal); cmt = "(Adjusted for Greater Heal)"; } if (!SpellManager.HasSpell("Heal")) { flashHealHealth = Math.Max(flashHealHealth, PriestSettings.DiscHeal.Heal); cmt = "(Adjusted for Heal)"; } if (flashHealHealth != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.FlashHeal) + PriSingleBase, "Flash Heal @ " + flashHealHealth + "% " + cmt, "Flash Heal", new Decorator( req => ((WoWUnit)req).HealthPercent < flashHealHealth && !SkipForSpiritShell((WoWUnit)req), new PrioritySelector( CastBuffsBehavior("Flash Heal"), Spell.Cast("Flash Heal", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ) ); if (PriestSettings.DiscHeal.GreaterHeal != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.GreaterHeal) + PriSingleBase, "Greater Heal @ " + PriestSettings.DiscHeal.GreaterHeal + "%", "Greater Heal", new Decorator( req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.GreaterHeal && !SkipForSpiritShell((WoWUnit)req), new PrioritySelector( CastBuffsBehavior("Greater Heal"), Spell.Cast("Greater Heal", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ) ); if (PriestSettings.DiscHeal.Heal != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.Heal) + PriSingleBase, "Heal @ " + PriestSettings.DiscHeal.Heal + "%", "Heal", Spell.Cast("Heal", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.Heal && !SkipForSpiritShell((WoWUnit)req), cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); #endregion #region Tank - Low Priority Buffs #endregion #region Lowest Priority Healer Tasks // Atonement if (AddAtonementBehavior() && (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds || SingularRoutine.CurrentWoWContext == WoWContext.Instances)) { // check less than # below Atonement Health behavs.AddBehavior(1, "Atonement when Idle = " + PriestSettings.DiscHeal.AtonementWhenIdle.ToString(), "Atonement", new Decorator( req => PriestSettings.DiscHeal.AtonementWhenIdle && (Me.Combat || SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds), new PrioritySelector( CreateDiscAtonmementEnsureTarget(), CreateDiscAtonementMovement(), new Decorator( req => Unit.ValidUnit( Me.CurrentTarget), new PrioritySelector( new Action(r => { Logger.WriteDebug("--- atonement when idle ---"); return RunStatus.Failure; }), Movement.CreateFaceTargetBehavior(), // Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5)), 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) ) ) ) ) ); } #endregion behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) behavs.ListBehaviors(); return new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, // use gcd/cast time to choose dps target and face if needed new Decorator( req => Me.Combat && (Spell.IsGlobalCooldown() || Spell.IsCastingOrChannelling()), new PrioritySelector( CreateDiscAtonmementEnsureTarget(), Movement.CreateFaceTargetBehavior( waitForFacing:false) ) ), 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 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.DistanceSqr(((WoWUnit)req).Location) <= 30 * 30) ) ) ); } if (DruidSettings.Heal.NaturesSwiftness != 0) { behavs.AddBehavior(797, "Nature's Swiftness Heal @ " + DruidSettings.Heal.NaturesSwiftness + "%", "Nature's Swiftness", new Decorator( req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.NaturesSwiftness && !Spell.IsSpellOnCooldown("Nature's Swiftness") && Spell.CanCastHack("Rejuvenation", (WoWUnit)req, skipWowCheck: true), new Sequence( Spell.BuffSelfAndWait("Nature's Swiftness", gcd: HasGcd.No), new PrioritySelector( Spell.Cast("Healing Touch", on => (WoWUnit)on, req => true, cancel => false), new Sequence( Spell.Cast("Regrowth", on => (WoWUnit)on, req => true, cancel => false), new DecoratorContinue( req => !glyphRegrowth, new Action(ret => Spell.UpdateDoubleCast("Regrowth", (WoWUnit) ret)) ) ) ) ) ) ); } 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) ) ) ) ); } if (DruidSettings.Heal.Genesis != 0) behavs.AddBehavior(798, "Genesis @ " + DruidSettings.Heal.Genesis + "% MinCount: " + DruidSettings.Heal.CountGenesis, "Genesis", new Decorator( ret => Me.IsInGroup(), Spell.Buff( "Genesis", on => HealerManager.Instance.TargetList .FirstOrDefault( h => h.IsAlive && h.HasAura("Rejuvenation")), req => HealerManager.Instance.TargetList .Count(h => h.IsAlive && h.HealthPercent < DruidSettings.Heal.Genesis && h.Distance < 60) >= DruidSettings.Heal.CountGenesis ) ) ); #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.HasAura("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.HasAura("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 Atonement Only // only Atonement healing if above Health % if (AddAtonementBehavior() && DruidSettings.Heal.DreamOfCenariusAbovePercent > 0) { behavs.AddBehavior( 100 + PriHighAtone, "DreamOfCenarius Above " + DruidSettings.Heal.DreamOfCenariusAbovePercent + "%", "Wrath", new Decorator( req => (Me.Combat || SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) && HealerManager.Instance.TargetList.Count(h => h.HealthPercent < DruidSettings.Heal.DreamOfCenariusAbovePercent) < DruidSettings.Heal.DreamOfCenariusAboveCount, new PrioritySelector( HealerManager.CreateAttackEnsureTarget(), Helpers.Common.EnsureReadyToAttackFromLongRange(), new Decorator( req => Unit.ValidUnit(Me.CurrentTarget), new PrioritySelector( new Action(r => { Logger.WriteDebug("--- DreamOfCenarius only! ---"); return RunStatus.Failure; }), Movement.CreateFaceTargetBehavior(), Spell.Cast("Wrath", mov => true, on => Me.CurrentTarget, req => true, cancel => false) ) ) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.DreamOfCenariusAbovePercent) + PriHighAtone, "DreamOfCenarius Above " + DruidSettings.Heal.DreamOfCenariusAbovePercent + "%", "DreamOfCenarius", new Decorator( req => (Me.Combat || SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) && !HealerManager.Instance.TargetList.Any(h => h.HealthPercent < DruidSettings.Heal.DreamOfCenariusCancelBelowHealthPercent && h.SpellDistance() < 50) && HealerManager.Instance.TargetList.Count(h => h.HealthPercent < DruidSettings.Heal.DreamOfCenariusAbovePercent) < DruidSettings.Heal.DreamOfCenariusAboveCount, new PrioritySelector( HealerManager.CreateAttackEnsureTarget(), Helpers.Common.EnsureReadyToAttackFromLongRange(), new Decorator( req => Unit.ValidUnit(Me.CurrentTarget), new PrioritySelector( Movement.CreateFaceTargetBehavior(), new Decorator( req => Me.IsSafelyFacing(Me.CurrentTarget, 150), Spell.Cast("Wrath", mov => true, on => Me.CurrentTarget, req => true, cancel => CancelDreamOfCenariusDPS()) ) ) ) ) ) ); } #endregion #region AoE Heals if (DruidSettings.Heal.WildMushroom != 0) behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.WildMushroom) + PriAoeBase, "Wild Mushroom @ " + DruidSettings.Heal.WildMushroom + "% MinCount: " + DruidSettings.Heal.CountWildMushroom, "Wild Mushroom", 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.DistanceSqr(((WoWUnit)req).Location) <= 30 * 30)) ) ) ); /* if (Settings.Heal.SwiftmendAOE != 0) behavs.AddBehavior(HealerManager.HealthToPriority(Settings.Heal.SwiftmendAOE) + PriAoeBase, "Swiftmend @ " + Settings.Heal.SwiftmendAOE + "% MinCount: " + Settings.Heal.CountSwiftmendAOE, "Swiftmend", new Decorator( ret => Me.IsInGroup(), new PrioritySelector( ctx => HealerManager.GetBestCoverageTarget("Swiftmend", Settings.Heal.SwiftmendAOE, 40, 10, Settings.Heal.CountSwiftmendAOE , mainTarget: Unit.NearbyGroupMembersAndPets.Where(p => p.HealthPercent < Settings.Heal.SwiftmendAOE && p.SpellDistance() <= 40 && p.IsAlive && p.HasAnyOfMyAuras("Rejuvenation", "Regrowth"))), Spell.Cast("Force of Nature", on => (WoWUnit)on, req => Spell.GetCharges("Force of Nature") > 1), Spell.Cast(spell => "Swiftmend", mov => false, on => (WoWUnit)on, req => true, skipWowCheck: true) ) ) ); */ #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; if (clearLeft < 3000) { if (target == null || healthPercent > 92 || !Spell.CanCastHack("Regrowth", target)) { WoWUnit lbTarget = HealerManager.Instance.TargetList.FirstOrDefault(u => u.GetAuraTimeLeft("Lifebloom").TotalMilliseconds.Between(1500, 10000)); if (lbTarget != null && Spell.CanCastHack("Regrowth", lbTarget)) { Logger.Write(LogColor.Hilite, "^Clearcasting: Regrowth refresh of Lifebloom @ {0:F1} seconds", lbTarget.GetAuraTimeLeft("Lifebloom").TotalSeconds); return lbTarget; } } } // 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) @ "; } //else if (TalentManager.HasGlyph("Regrowth")) //{ // regrowthInstead = Math.Max(DruidSettings.Heal.Regrowth, DruidSettings.Heal.HealingTouch); // whyRegrowth = "Glyphed Regrowth (instead of Healing Touch) @ "; //} } 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 ) ) ); /* // Atonement if (AddAtonementBehavior() && (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds || SingularRoutine.CurrentWoWContext == WoWContext.Instances) && DruidSettings.Heal.DreamOfCenariusWhenIdle) { // check less than # below DreamOfCenarius Health behavs.AddBehavior(1, "DreamOfCenarius when Idle = " + DruidSettings.Heal.DreamOfCenariusWhenIdle.ToString(), "Wrath", new Decorator( req => (Me.Combat || SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds), new PrioritySelector( HealerManager.CreateAttackEnsureTarget(), Helpers.Common.EnsureReadyToAttackFromLongRange(), new Decorator( req => Unit.ValidUnit(Me.CurrentTarget), new PrioritySelector( new Action(r => { Logger.WriteDebug("--- DreamOfCenarius when idle ---"); return RunStatus.Failure; }), Movement.CreateFaceTargetBehavior(), Spell.Cast("Wrath", mov => true, on => Me.CurrentTarget, req => true, cancel => false) ) ) ) ) ); } */ #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 ) ) ); }
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.SoothingMist); cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.EnvelopingMist); cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.SurgingMist); 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) ) ) ) ) ) ) ) ); }
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.Heal.HealingWave, Math.Max(ShamanSettings.Heal.GreaterHealingWave, ShamanSettings.Heal.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); /* if (SpellManager.HasSpell("Earthliving Weapon")) { behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.AncestralSwiftness), "Unleash Elements", "Unleash Elements", Spell.Buff("Unleash Elements", ret => (WoWUnit)ret, ret => (Me.IsMoving || ((WoWUnit)ret).GetPredictedHealthPercent() < ShamanSettings.Heal.AncestralSwiftness) && Common.IsImbuedForHealing(Me.Inventory.Equipped.MainHand) )); } */ if ( SingularRoutine.CurrentWoWContext == WoWContext.Instances ) behavs.AddBehavior( 9999, "Earth Shield", "Earth Shield", Spell.Buff("Earth Shield", on => GetBestEarthShieldTargetInstance())); 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(HealthToPriority(ShamanSettings.Heal.SpiritLinkTotem) + 600, "Spirit Link Totem", "Spirit Link Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Spirit Link Totem", ret => (WoWUnit)ret, ret => HealerManager.Instance.TargetList.Count( p => p.GetPredictedHealthPercent() < ShamanSettings.Heal.SpiritLinkTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= ShamanSettings.Heal.MinSpiritLinkCount ) ) ); behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.AncestralSwiftness) + 500, String.Format("Oh Shoot Heal @ {0}%", ShamanSettings.Heal.AncestralSwiftness), "Ancestral Swiftness", new Decorator( ret => ((WoWUnit)ret).GetPredictedHealthPercent() < ShamanSettings.Heal.AncestralSwiftness, new Sequence( Spell.BuffSelf("Ancestral Swiftness"), new PrioritySelector( Spell.Cast("Greater Healing Wave", on => (WoWUnit)on), Spell.Cast("Healing Surge", on => (WoWUnit)on, ret => !SpellManager.HasSpell("Greater Healing Wave")) ) ) ) ); #endregion #region AoE Heals behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.HealingTideTotem) + 400, "Healing Tide Totem", "Healing Tide Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Tide Totem", on => Me, req => Me.Combat && HealerManager.Instance.TargetList.Count(p => p.GetPredictedHealthPercent() < ShamanSettings.Heal.HealingTideTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= ShamanSettings.Heal.MinHealingTideCount ) ) ); behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.HealingStreamTotem) + 300, "Healing Stream Totem", "Healing Stream Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Stream Totem", on => (!Me.Combat || Totems.Exist(WoWTotemType.Water)) ? null : HealerManager.Instance.TargetList.FirstOrDefault(p => p.GetPredictedHealthPercent() < ShamanSettings.Heal.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingStream)) ) ) ); behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.HealingRain) + 200, "Healing Rain", "Healing Rain", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => GetBestHealingRainTarget(), new Decorator( ret => ret != null, new PrioritySelector( new Sequence( new Action(r => Logger.WriteDebug("UE+HR - just before UE check")), BuffUnleashLife(on => HealerManager.Instance.TargetList.FirstOrDefault()), new Action(r => Logger.WriteDebug("UE+HR - past UE")), Helpers.Common.CreateWaitForLagDuration(ret => Spell.IsGlobalCooldown()), new Action(r => Logger.WriteDebug("UE+HR - past GCD start")), new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(LagTolerance.No), new ActionAlwaysSucceed()), new Action(r => Logger.WriteDebug("UE+HR - past GCD stop")), Spell.CastOnGround("Healing Rain", on => (WoWUnit)on, req => true, false) ), new Action( ret => { if (ret != null) { if (!((WoWUnit)ret).IsValid) Logger.WriteDebug("UE+HR - FAILED - Healing Target object became invalid"); else if (((WoWUnit)ret).Distance > 40) Logger.WriteDebug("UE+HR - FAILED - Healing Target moved out of range"); else if (!Spell.CanCastHack("Healing Rain")) Logger.WriteDebug("UE+HR - FAILED - Spell.CanCastHack() said NO to Healing Rain"); else if (Styx.WoWInternals.World.GameWorld.IsInLineOfSpellSight(StyxWoW.Me.GetTraceLinePos(), ((WoWUnit)ret).Location)) Logger.WriteDebug("UE+HR - FAILED - Spell.CanCastHack() unit location not in Line of Sight"); else if (Spell.IsSpellOnCooldown("Healing Rain")) Logger.WriteDebug("UE+HR - FAILED - Healing Rain is on cooldown"); else Logger.WriteDebug("UE+HR - Something FAILED with Unleash Life + Healing Rain cast sequence (target={0}, h={1:F1}%, d={2:F1} yds, spellmax={3:F1} yds, cooldown={4})", ((WoWUnit)ret).SafeName(), ((WoWUnit)ret).HealthPercent, ((WoWUnit)ret).Distance, Spell.ActualMaxRange("Healing Rain", (WoWUnit) ret), Spell.IsSpellOnCooldown("Healing Rain") ); } return RunStatus.Failure; }) ) ) ) ) ); behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.ChainHeal) + 100, "Chain Heal", "Chain Heal", new PrioritySelector( ctx => GetBestChainHealTarget(), new Decorator( ret => ret != null, new PrioritySelector( new Sequence( Spell.Buff("Riptide", on => (WoWUnit)on), new Action(r => Logger.WriteDebug("ChainHeal: prepped target with Riptide first")), new Wait(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysFail()) ), new Sequence( Spell.Cast("Chain Heal", on => (WoWUnit)on), new Action(r => TidalWaveRefresh()) ) ) ) ) ); #endregion #region Single Target Heals behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.GreaterHealingWave), "Greater Healing Wave", "Greater Healing Wave", new Decorator( ret => ((WoWUnit)ret).GetPredictedHealthPercent() < ShamanSettings.Heal.GreaterHealingWave, new Sequence( BuffUnleashLife(on => (WoWUnit) on), new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), Spell.Cast("Greater Healing Wave", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).GetPredictedHealthPercent() < ShamanSettings.Heal.GreaterHealingWave, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action( r => TidalWaveConsume() ) ) ) ); behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.HealingWave), "Healing Wave", "Healing Wave", new Sequence( Spell.Cast("Healing Wave", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).GetPredictedHealthPercent() < ShamanSettings.Heal.HealingWave, cancel => { if (((WoWUnit)cancel).HealthPercent > cancelHeal) return true; return false; }), new Action(r => TidalWaveConsume()) ) ); behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.HealingSurge), "Healing Surge", "Healing Surge", new Sequence( Spell.Cast("Healing Surge", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).GetPredictedHealthPercent() < ShamanSettings.Heal.HealingSurge, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action(r => TidalWaveConsume()) ) ); #endregion #region Healing Cooldowns behavs.AddBehavior(HealthToPriority( ShamanSettings.Heal.Ascendance) + 100, "Ascendance", "Ascendance", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.BuffSelf( "Ascendance", ret => HealerManager.Instance.TargetList.Count(p => p.GetPredictedHealthPercent() < ShamanSettings.Heal.Ascendance) >= ShamanSettings.Heal.MinAscendanceCount ) ) ); #endregion behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.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).GetPredictedHealthPercent() <= 99), new PrioritySelector( new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Totems.CreateTotemsBehavior(), /* Spell.Cast("Earth Shield", ret => (WoWUnit)ret, ret => ret is WoWUnit && Group.Tanks.Contains((WoWUnit)ret) && Group.Tanks.All(t => !t.HasMyAura("Earth Shield"))), */ // 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 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.Heal.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()) ) ), #if false , new Sequence( new Action(ret => Logger.WriteDebug(Color.LightGreen, "No Action - stunned:{0} silenced:{1}" , Me.Stunned || Me.IsStunned() , Me.Silenced )), new Action(ret => { return RunStatus.Failure; }) ) , new Decorator( ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget && !Unit.NearbyFriendlyPlayers.Any(u => u.IsInMyPartyOrRaid), new PrioritySelector( Movement.CreateMoveToLosBehavior(), Movement.CreateFaceTargetBehavior(), Helpers.Common.CreateInterruptBehavior(), Spell.Cast("Earth Shock"), Spell.Cast("Lightning Bolt"), Movement.CreateMoveToUnitBehavior( on => StyxWoW.Me.CurrentTarget, 35f, 30f) ) ) #endif 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 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); /* if (SpellManager.HasSpell("Earthliving Weapon")) { behavs.AddBehavior(HealthToPriority(ShamanSettings.Heal.AncestralSwiftness), "Unleash Elements", "Unleash Elements", Spell.Buff("Unleash Elements", ret => (WoWUnit)ret, ret => (Me.IsMoving || ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.Heal.AncestralSwiftness) && Common.IsImbuedForHealing(Me.Inventory.Equipped.MainHand) )); } */ if ( SingularRoutine.CurrentWoWContext == WoWContext.Instances ) behavs.AddBehavior( 9999, "Earth Shield", "Earth Shield", Spell.Buff("Earth Shield", on => GetBestEarthShieldTargetInstance())); 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.Cast( "Spirit Link Totem", ret => (WoWUnit)ret, ret => HealerManager.Instance.TargetList.Count( p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.SpiritLinkTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= ShamanSettings.RestoHealSettings.MinSpiritLinkCount ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.AncestralSwiftness) + 500, String.Format("Oh Shoot Heal @ {0}%", ShamanSettings.RestoHealSettings.AncestralSwiftness), "Ancestral Swiftness", new Decorator( ret => (Me.Combat || ((WoWUnit)ret).Combat) && ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.RestoHealSettings.AncestralSwiftness, new Sequence( Spell.BuffSelf("Ancestral Swiftness"), new PrioritySelector( Spell.Cast("Healing Surge", on => (WoWUnit)on), Spell.Cast("Healing Wave", on => (WoWUnit)on) ) ) ) ); #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) && (Common.talentTotemicPersistance || !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.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) + 100, "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 PrioritySelector( Spell.Cast("Unleash Life", on => (WoWUnit)on), new ActionAlwaysSucceed() ), 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 PrioritySelector( Spell.Cast("Unleash Life", on => (WoWUnit) on), 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 PrioritySelector( Spell.Cast("Unleash Life", on => (WoWUnit) on), 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 && Singular.Dynamics.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(), /* Spell.Cast("Earth Shield", ret => (WoWUnit)ret, ret => ret is WoWUnit && Group.Tanks.Contains((WoWUnit)ret) && Group.Tanks.All(t => !t.HasMyAura("Earth Shield"))), */ // 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 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()) ) ), #if false , new Sequence( new Action(ret => Logger.WriteDebug(Color.LightGreen, "No Action - stunned:{0} silenced:{1}" , Me.Stunned || Me.IsStunned() , Me.Silenced )), new Action(ret => { return RunStatus.Failure; }) ) , new Decorator( ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget() && !Unit.NearbyFriendlyPlayers.Any(u => u.IsInMyPartyOrRaid()), new PrioritySelector( Movement.CreateMoveToLosBehavior(), Movement.CreateFaceTargetBehavior(), Helpers.Common.CreateInterruptBehavior(), Spell.Cast("Frost Shock"), Spell.Cast("Lightning Bolt"), Movement.CreateMoveToUnitBehavior( on => StyxWoW.Me.CurrentTarget, 35f, 30f) ) ) #endif 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 CreateMonkOffHealBehavior() { HealerManager.NeedHealTargeting = SingularSettings.Instance.DpsOffHealAllowed; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, MonkSettings.OffHealSettings.SurgingMist); bool 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, "Cleanse Spirit", null, Dispelling.CreateDispelBehavior()); */ #region Save the Group #endregion #region AoE Heals behavs.AddBehavior( Mistweaver.HealthToPriority(MonkSettings.MistHealSettings.ChiWave) + 400, String.Format("Chi Wave on {0} targets @ {1}%", MonkSettings.OffHealSettings.CountChiWaveTalent, MonkSettings.OffHealSettings.ChiWaveTalent), "Chi Wave", CreateClusterHeal("Chi Wave", ClusterType.Cone, MonkSettings.OffHealSettings.ChiWaveTalent, MonkSettings.OffHealSettings.CountChiWaveTalent, 40) ); behavs.AddBehavior( Mistweaver.HealthToPriority(MonkSettings.MistHealSettings.ChiBurstTalent) + 400, String.Format("Chi Burst on {0} targets @ {1}%", MonkSettings.OffHealSettings.CountChiBurstTalent, MonkSettings.OffHealSettings.ChiBurstTalent), "Chi Burst", CreateClusterHeal("Chi Burst", ClusterType.Cone, MonkSettings.OffHealSettings.ChiBurstTalent, MonkSettings.OffHealSettings.CountChiBurstTalent, 40) ); #endregion #region Single Target Heals behavs.AddBehavior(Mistweaver.HealthToPriority(MonkSettings.OffHealSettings.SurgingMist), string.Format("Surging Mist @ {0}%", MonkSettings.OffHealSettings.SurgingMist), "Surging Mist", Spell.Cast("Surging Mist", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).PredictedHealthPercent(includeMyHeals: true) < MonkSettings.OffHealSettings.SurgingMist, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); #endregion behavs.OrderBehaviors(); if (Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal ) behavs.ListBehaviors(); return new PrioritySelector( ctx => HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, new Decorator( ret => ret != null && (Me.Combat || ((WoWUnit)ret).Combat || ((WoWUnit)ret).PredictedHealthPercent() <= 99), 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, 30f, 25f) ) ) ) ) ) ) ) ); }
//private static WoWUnit _moveToHealTarget = null; //private static WoWUnit _lastMoveToTarget = null; // temporary lol name ... will revise after testing public static Composite CreateDiscHealOnlyBehavior(bool selfOnly, bool moveInRange) { if (SingularRoutine.CurrentWoWContext == WoWContext.Normal) return new ActionAlwaysFail(); HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, PriestSettings.DiscHeal.Heal); cancelHeal = (int)Math.Max(cancelHeal, PriestSettings.DiscHeal.FlashHeal); Logger.WriteDebugInBehaviorCreate("Priest Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); string cmt = ""; int flashHealHealth = PriestSettings.DiscHeal.FlashHeal; if (!SpellManager.HasSpell("Heal")) { flashHealHealth = Math.Max(flashHealHealth, PriestSettings.DiscHeal.Heal); cmt = "(Adjusted for Heal)"; } if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; behavs.AddBehavior(dispelPriority, "Dispel", null, Common.CreatePriestDispelBehavior()); } #region Surge of Light behavs.AddBehavior(99 + PriSurgeOfLight, "Surge of Light: Dont Waste", "Flash of Light", new Decorator( req => Me.GetAuraStacks(SURGE_OF_LIGHT) >= 2 || Me.GetAuraTimeLeft(SURGE_OF_LIGHT) < TimeSpan.FromSeconds(2), Spell.Cast("Flash of Light", on => (WoWUnit)on) ) ); #endregion #region Keep Up Borrowed Time behavs.AddBehavior(99 + PriBorrowedTime, "Power Word: Shield for Borrowed Time", "Power Word: Shield", new Decorator( req => Me.Combat && Me.GetAuraTimeLeft("Borrowed Time") == TimeSpan.Zero, new PrioritySelector( Spell.Buff( "Power Word: Shield", on => Group.Tanks .Where(u => u.IsAlive && u.SpellDistance() < 40 && CanWePwsUnit(u) && Spell.CanCastHack("Power Word: Shield", u)) .FirstOrDefault(), req => { Logger.Write(LogColor.Hilite, "^Borrowed Time: shield Tank for haste buff"); return true; }), Spell.Buff( "Power Word: Shield", on => HealerManager.Instance.TargetList .Where(u => u.IsAlive && u.SpellDistance() < 40 && CanWePwsUnit(u) && Spell.CanCastHack("Power Word: Shield", u)) .OrderBy(u => u.CurrentHealth) .FirstOrDefault(), req => { Logger.Write(LogColor.Hilite, "^Borrowed Time: shield for haste buff"); return true; }) ) ) ); #endregion #region Save the Group behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.PainSuppression) + PriEmergencyBase, "Pain Suppression @ " + PriestSettings.DiscHeal.PainSuppression + "%", "Pain Suppression", new Decorator( req => Me.Combat, Spell.Cast("Pain Suppression", mov => false, on => (WoWUnit) on, req => ((WoWUnit)req).IsPlayer && ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.PainSuppression ) ) ); behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.PowerWordBarrier) + PriEmergencyBase, "Power Word: Barrier @ " + PriestSettings.DiscHeal.PowerWordBarrier + "%", "Power Word: Barrier", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( context => GetBestPowerWordBarrierTarget(), new Decorator( ret => ret != null, new PrioritySelector( new Sequence( new Action(r => Logger.WriteDebug("PW:B - attempting cast")), Spell.CastOnGround("Power Word: Barrier", on => (WoWUnit)on, req => true, false) ), new Action( ret => { if (ret != null) { if (!((WoWUnit)ret).IsValid) Logger.WriteDebug("PW:B - FAILED - Healing Target object became invalid"); else if (((WoWUnit)ret).Distance > 40) Logger.WriteDebug("PW:B - FAILED - Healing Target moved out of range"); else if (!Spell.CanCastHack("Power Word: Barrier")) Logger.WriteDebug("PW:B - FAILED - Spell.CanCastHack() said NO to Power Word: Barrier"); else if (Styx.WoWInternals.World.GameWorld.IsInLineOfSpellSight(StyxWoW.Me.GetTraceLinePos(), ((WoWUnit)ret).Location)) Logger.WriteDebug("PW:B - FAILED - Spell.CanCastHack() unit location not in Line of Sight"); else if (Spell.IsSpellOnCooldown("Power Word: Barrier")) Logger.WriteDebug("PW:B - FAILED - Power Word: Barrier is on cooldown"); else Logger.WriteDebug("PW:B - Something FAILED with Power Word: Barrier cast sequence (target={0}, h={1:F1}%, d={2:F1} yds, spellmax={3:F1} yds, cooldown={4})", ((WoWUnit)ret).SafeName(), ((WoWUnit)ret).HealthPercent, ((WoWUnit)ret).Distance, Spell.ActualMaxRange("Power Word: Barrier", (WoWUnit)ret), Spell.IsSpellOnCooldown("Power Word: Barrier") ); } return RunStatus.Failure; }) ) ) ) ) ); #endregion #region Tank Buffing behavs.AddBehavior(HealthToPriority(100) + PriHighBase, "Clarity of Will - Tank", "Clarity of Will", new Decorator( req => Me.Combat, Spell.Buff("Clarity of Will", on => Group.Tanks.FirstOrDefault( t => t.Combat && !t.HasMyAura("Clarity of Will"))) ) ); if (PriestSettings.DiscHeal.PrayerOfHealing != 0) behavs.AddBehavior(HealthToPriority(99) + PriHighBase, "Spirit Shell - Group MinCount: " + PriestSettings.DiscHeal.CountPrayerOfHealing, "Prayer of Healing", new Decorator( ret => IsSpiritShellEnabled(), Spell.Cast("Prayer of Healing", on => { WoWUnit unit = HealerManager.GetBestCoverageTarget("Prayer of Healing", 101, 40, 30, PriestSettings.DiscHeal.CountPrayerOfHealing, req => !HasSpiritShellAbsorb((WoWUnit)req)); if (unit != null && Spell.CanCastHack("Prayer of Healing", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Spirit Shell with Prayer of Healing on Group: {0}", unit.SafeName()); return unit; } return null; }) ) ); behavs.AddBehavior(HealthToPriority(98) + PriHighBase, "Spirit Shell - Tank", "Spirit Shell", new Decorator( req => IsSpiritShellEnabled(), Spell.Cast("Heal", on => { WoWUnit unit = Group.Tanks.Where(t => t.IsAlive && t.Combat && !HasSpiritShellAbsorb(t) && t.SpellDistance() < 40).OrderBy( a => a.Distance).FirstOrDefault(); if (unit != null && Spell.CanCastHack("Heal", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Spirit Shell with Heal on TANK: {0}", unit.SafeName()); return unit; } return null; }) ) ); behavs.AddBehavior(HealthToPriority(97) + PriHighBase, "Power Word: Shield - Tank", "Power Word: Shield", new Decorator( req => Me.Combat, Spell.Buff( "Power Word: Shield", on => Group.Tanks .Where(u => u.IsAlive && CanWePwsUnit(u) && Spell.CanCastHack("Power Word: Shield", u)) .FirstOrDefault() ) ) ); behavs.AddBehavior(HealthToPriority(96) + PriHighBase, "Prayer of Mending - Tank", "Prayer of Mending", new Decorator( req => Me.Combat, Spell.Buff( "Prayer of Mending", on => Group.Tanks .Where(t => t.IsAlive && !t.HasMyAura("Prayer of Mending") && Spell.CanCastHack("Prayer of Mending", t)) .FirstOrDefault() ) ) ); #endregion #region Save the Essential Party Members const int SaveEssential = 30; behavs.AddBehavior(HealthToPriority(SaveEssential) + PriSaveEssential, "Save Essential Target below " + SaveEssential + "%", "Flash Heal", new Decorator( req => Me.Combat, new PrioritySelector( ctx => HealerManager.FindLowestHealthEssentialTarget(), Spell.HandleOffGCD( Spell.Buff( "Power Infusion", on => Me, req => req != null, gcd: HasGcd.No) ), Spell.Buff( "Power Word: Shield", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.PowerWordShield && CanWePwsUnit((WoWUnit)req) ), Spell.Buff( "Saving Grace", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.SavingGrace && CanWePwsUnit((WoWUnit)req) ), Spell.Cast("Flash Heal", mov => true, on => (WoWUnit)on, req => true, cancel => { if (((WoWUnit)cancel).HealthPercent < SaveEssential) return false; WoWUnit low = HealerManager.FindLowestHealthEssentialTarget(); if (low == null || low == (WoWUnit) cancel) return false; // cancel since we are above emergency and another essential is below return true; } ) ) ) ); #endregion #region Atonement Only // only Atonement healing if above Health % if (AddAtonementBehavior() && PriestSettings.DiscHeal.AtonementAbovePercent > 0) { behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.AtonementAbovePercent) + PriHighAtone, "Atonement Above " + PriestSettings.DiscHeal.AtonementAbovePercent + "%", "Atonement", new Decorator( req => (Me.Combat || SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) && !HealerManager.Instance.TargetList.Any(h => h.HealthPercent < PriestSettings.DiscHeal.AtonementCancelBelowHealthPercent && h.SpellDistance() < 50) && HealerManager.Instance.TargetList.Count(h => h.HealthPercent < PriestSettings.DiscHeal.AtonementAbovePercent) < PriestSettings.DiscHeal.AtonementAboveCount, new PrioritySelector( HealerManager.CreateAttackEnsureTarget(), CreateDiscAtonementMovement(), new Decorator( req => Unit.ValidUnit(Me.CurrentTarget), new PrioritySelector( Movement.CreateFaceTargetBehavior(), // Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5)), new Decorator( req => Me.IsSafelyFacing( Me.CurrentTarget, 150) && Me.ManaPercent >= PriestSettings.DiscHeal.AtonementCancelBelowManaPercent, new PrioritySelector( Spell.HandleOffGCD(Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5) && (Me.CurrentTarget.TimeToDeath() > 15 || Unit.UnitsInCombatWithUsOrOurStuff(40).Count() > 1))), Common.CreateHolyFireBehavior(), Spell.Cast("Penance", mov => true, on => Me.CurrentTarget, req => true, cancel => false), Spell.Cast("Smite", mov => true, on => Me.CurrentTarget, req => PriestSettings.DiscHeal.AtonementUseSmite, cancel => CancelAtonementDPS()) ) ) ) ) ) ) ); } #endregion #region AoE Heals int maxDirectHeal = Math.Max(PriestSettings.DiscHeal.FlashHeal, PriestSettings.DiscHeal.Heal); if (PriestSettings.DiscHeal.SpiritShell != 0) behavs.AddBehavior(201 + PriAoeBase, "Spirit Shell Activate @ " + PriestSettings.DiscHeal.SpiritShell + "% MinCount: " + PriestSettings.DiscHeal.CountSpiritShell, "Spirit Shell", new Decorator( ret => Me.Combat && Spell.CanCastHack("Spirit Shell", Me, true) && HealerManager.Instance.TargetList.Count(h => h.HealthPercent < PriestSettings.DiscHeal.SpiritShell) >= PriestSettings.DiscHeal.CountSpiritShell, new Sequence( Spell.BuffSelf("Spirit Shell", req => true), new PrioritySelector( new Wait( TimeSpan.FromMilliseconds(500), until => IsSpiritShellEnabled(), new Action( r => Logger.WriteDebug("buff for Spirit Shell now visible"))), new Action( r => Logger.WriteDebug("cast successfull but Spirit Shell buff not visible")) ) ) ) ); if (PriestSettings.DiscHeal.PrayerOfMending != 0) { if (!TalentManager.HasGlyph("Focused Mending")) { behavs.AddBehavior(200 + PriAoeBase, "Prayer of Mending @ " + PriestSettings.DiscHeal.PrayerOfMending + "% MinCount: " + PriestSettings.DiscHeal.CountPrayerOfMending, "Prayer of Mending", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Prayer of Mending", PriestSettings.DiscHeal.PrayerOfMending, 40, 20, PriestSettings.DiscHeal.CountPrayerOfMending), new Decorator( ret => ret != null && Spell.CanCastHack("Prayer of Mending", (WoWUnit)ret), new PrioritySelector( Spell.HandleOffGCD(Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5))), Spell.Buff("Prayer of Mending", on => (WoWUnit)on, req => true) ) ) ) ) ); } else { behavs.AddBehavior(200 + PriAoeBase, "Prayer of Mending @ " + PriestSettings.DiscHeal.PrayerOfMending + "% (Glyph of Focused Mending)", "Prayer of Mending", Spell.Buff("Prayer of Mending", on => (WoWUnit)on, req => !((WoWUnit)req).IsMe && ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.PrayerOfMending && Me.HealthPercent < PriestSettings.DiscHeal.PrayerOfMending ) ); } } if (PriestSettings.DiscHeal.DiscLevel90Talent != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.DiscLevel90Talent) + PriAoeBase, "Halo @ " + PriestSettings.DiscHeal.DiscLevel90Talent + "% MinCount: " + PriestSettings.DiscHeal.CountLevel90Talent, "Halo", new Decorator( ret => SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds || SingularRoutine.CurrentWoWContext == WoWContext.Instances, new Decorator( ret => ret != null && HealerManager.Instance.TargetList.Count(u => u.IsAlive && u.HealthPercent < PriestSettings.DiscHeal.DiscLevel90Talent && u.Distance < 30) >= PriestSettings.DiscHeal.CountLevel90Talent && Spell.CanCastHack("Halo", (WoWUnit)ret), new PrioritySelector( Spell.HandleOffGCD(Spell.BuffSelf("Archangel", req => ((WoWUnit)req) != null && Me.HasAura("Evangelism", 5))), Spell.CastOnGround("Halo", on => (WoWUnit)on, req => true) ) ) ) ); if (PriestSettings.DiscHeal.DiscLevel90Talent != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.DiscLevel90Talent) + PriAoeBase, "Cascade @ " + PriestSettings.DiscHeal.DiscLevel90Talent + "% MinCount: " + PriestSettings.DiscHeal.CountLevel90Talent, "Cascade", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Cascade", PriestSettings.DiscHeal.DiscLevel90Talent, 40, 30, PriestSettings.DiscHeal.CountLevel90Talent), new Decorator( ret => ret != null && Spell.CanCastHack("Cascade", (WoWUnit) ret), new PrioritySelector( Spell.HandleOffGCD(Spell.BuffSelf("Archangel", req => Me.HasAura("Evangelism", 5))), Spell.Cast("Cascade", mov => true, on => (WoWUnit)on, req => true) ) ) ) ) ); if (PriestSettings.DiscHeal.HolyNova != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.HolyNova) + PriAoeBase, "Prayer of Healing @ " + PriestSettings.DiscHeal.PrayerOfHealing + "% MinCount: " + PriestSettings.DiscHeal.CountHolyNova, "Holy Nova", new Decorator( req => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid) && Me.IsMoving, Spell.Cast( "Holy Nova", on => (WoWUnit)on, req => PriestSettings.DiscHeal.CountHolyNova <= HealerManager.Instance.TargetList.Count( u => u.HealthPercent.Between(1, PriestSettings.DiscHeal.HolyNova) && u.SpellDistance() < 12) ) ) ); if (PriestSettings.DiscHeal.PrayerOfHealing != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.PrayerOfHealing) + PriAoeBase, "Prayer of Healing @ " + PriestSettings.DiscHeal.PrayerOfHealing + "% MinCount: " + PriestSettings.DiscHeal.CountPrayerOfHealing, "Prayer of Healing", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Prayer of Healing", PriestSettings.DiscHeal.PrayerOfHealing, 40, 30, PriestSettings.DiscHeal.CountPrayerOfHealing), CastBuffsBehavior("Prayer of Healing"), Spell.Cast("Prayer of Healing", on => (WoWUnit)on) ) ) ); #endregion #region Direct Heals if (HasReflectiveShield) { behavs.AddBehavior(100 + PriSingleBase, "Power Word: Shield Self (Glyph of Reflective Shield)", "Power Word: Shield", Spell.BuffSelf("Power Word: Shield", req => CanWePwsUnit(Me) && Unit.NearbyUnitsInCombatWithUsOrOurStuff.Any()) ); } if (PriestSettings.DiscHeal.PowerWordShield != 0) { behavs.AddBehavior(99 + PriSingleBase, "Power Word: Shield @ " + PriestSettings.DiscHeal.PowerWordShield + "%", "Power Word: Shield", Spell.Buff("Power Word: Shield", on => (WoWUnit) on, req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.PowerWordShield && CanWePwsUnit((WoWUnit) req)) ); } if (PriestSettings.DiscHeal.Penance != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.Penance) + PriSingleBase, "Penance @ " + PriestSettings.DiscHeal.Penance + "%", "Penance", new Decorator( req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.Penance, new PrioritySelector( CastBuffsBehavior("Penance"), Spell.Cast("Penance", mov => true, on => (WoWUnit)on, req => true, cancel => false ) ) ) ); if (flashHealHealth != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.FlashHeal) + PriSingleBase, "Flash Heal @ " + flashHealHealth + "% " + cmt, "Flash Heal", new Decorator( req => ((WoWUnit)req).HealthPercent < flashHealHealth && !SkipForSpiritShell((WoWUnit)req), new PrioritySelector( CastBuffsBehavior("Flash Heal"), Spell.Cast("Flash Heal", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ) ); if (PriestSettings.DiscHeal.Heal != 0) behavs.AddBehavior(HealthToPriority(PriestSettings.DiscHeal.Heal) + PriSingleBase, "Heal @ " + PriestSettings.DiscHeal.Heal + "%", "Heal", new Decorator( req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.Heal && !SkipForSpiritShell((WoWUnit)req), new PrioritySelector( CastBuffsBehavior("Heal"), Spell.Cast("Heal", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ) ); #endregion #region Tank - Low Priority Buffs #endregion behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) behavs.ListBehaviors(); return new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindHighestPriorityTarget(), // HealerManager.Instance.FirstUnit, // use gcd/cast time to choose dps target and face if needed new Decorator( req => Me.Combat && (Spell.IsGlobalCooldown() || Spell.IsCastingOrChannelling()), new PrioritySelector( HealerManager.CreateAttackEnsureTarget(), Movement.CreateFaceTargetBehavior(waitForFacing: false) ) ), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown() && ret != null, behavs.GenerateBehaviorTree() ) #if OLD_TANK_FOLLOW_CODE , new Decorator( ret => moveInRange, Movement.CreateMoveToUnitBehavior( ret => Battlegrounds.IsInsideBattleground ? (WoWUnit)ret : Group.Tanks.Where(a => a.IsAlive).OrderBy(a => a.Distance).FirstOrDefault(), 35f ) ) #endif ); }
// temporary lol name ... will revise after testing public static Composite CreateHealingOnlyBehavior(bool selfOnly, bool moveInRange) { 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(Settings.Heal.Rejuvenation, Math.Max(Settings.Heal.HealingTouch, Math.Max(Settings.Heal.Nourish, Settings.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 (SingularSettings.Instance.CombatRezTarget != CombatRezTarget.None ) { behavs.AddBehavior(799, "Rebirth Tank/Healer", "Rebirth", Helpers.Common.CreateCombatRezBehavior( "Rebirth", filter => true, requirements => true) ); } if (Settings.Heal.NaturesSwiftness != 0) { behavs.AddBehavior(797, "Nature's Swiftness Heal @ " + Settings.Heal.NaturesSwiftness + "%", "Nature's Swiftness", new Decorator( req => ((WoWUnit)req).HealthPercent < Settings.Heal.NaturesSwiftness && !Spell.IsSpellOnCooldown("Nature's Swiftness") && Spell.CanCastHack("Rejuvenation", (WoWUnit)req, skipWowCheck: true), new Sequence( Spell.BuffSelf("Nature's Swiftness" ), new PrioritySelector( Spell.Cast("Regrowth", on => (WoWUnit)on, req => true, cancel => false), Spell.Cast("Healing Touch", on => (WoWUnit)on, req => true, cancel => false), Spell.Cast("Nourish", on => (WoWUnit)on, req => true, cancel => false) ) ) ) ); } if (Settings.Heal.Tranquility != 0) behavs.AddBehavior(798, "Tranquility @ " + Settings.Heal.Tranquility + "% MinCount: " + Settings.Heal.CountTranquility, "Tranquility", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Tranquility", mov => true, on => (WoWUnit)on, req => HealerManager.Instance.TargetList.Count( h => h.IsAlive && h.HealthPercent < Settings.Heal.Tranquility && h.SpellDistance() < 40) >= Settings.Heal.CountTranquility, cancel => false ) ) ); if (Settings.Heal.SwiftmendDirectHeal != 0) { behavs.AddBehavior(797, "Swiftmend Direct @ " + Settings.Heal.SwiftmendDirectHeal + "%", "Swiftmend", new Decorator( ret => (!Spell.IsSpellOnCooldown("Swiftmend") || Spell.GetCharges("Force of Nature") > 0) && ((WoWUnit)ret).GetPredictedHealthPercent(true) < Settings.Heal.SwiftmendDirectHeal && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid) && 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 => !TalentManager.HasGlyph("Regrowth"), 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 // Tank: Lifebloom behavs.AddBehavior(99 + PriHighBase, "Lifebloom - Tank", "Lifebloom", Spell.Cast("Lifebloom", on => { WoWUnit unit = GetLifebloomTarget(); if (unit != null && (unit.Combat || Me.Combat) && (unit.GetAuraStacks("Lifebloom") < 3 || unit.GetAuraTimeLeft("Lifebloom").TotalMilliseconds < 2800) && Spell.CanCastHack("Lifebloom", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Lifebloom ON TANK: {0}", unit.SafeName()); return unit; } return null; }) ); // Tank: Rejuv if Lifebloom not trained yet if (Settings.Heal.Rejuvenation != 0 && !SpellManager.HasSpell("Lifebloom")) { behavs.AddBehavior(98 + PriHighBase, "Rejuvenation - Tank", "Rejuvenation", Spell.Cast("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 (Settings.Heal.Ironbark != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) behavs.AddBehavior(HealthToPriority(Settings.Heal.Ironbark) + PriHighBase, "Ironbark @ " + Settings.Heal.Ironbark + "%", "Ironbark", Spell.Buff("Ironbark", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.Ironbark) ); else behavs.AddBehavior(HealthToPriority(Settings.Heal.Ironbark) + PriHighBase, "Ironbark - Tank @ " + Settings.Heal.Ironbark + "%", "Ironbark", Spell.Buff("Ironbark", on => RaFHelper.Leader == (WoWUnit)on && ((WoWUnit)on).IsAlive && ((WoWUnit)on).HealthPercent < Settings.Heal.Ironbark) ); } if (Settings.Heal.CenarionWard != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) behavs.AddBehavior(HealthToPriority(Settings.Heal.CenarionWard) + PriHighBase, "Cenarion Ward @ " + Settings.Heal.CenarionWard + "%", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.CenarionWard) ); else behavs.AddBehavior(HealthToPriority(Settings.Heal.CenarionWard) + PriHighBase, "Cenarion Ward - Tank @ " + Settings.Heal.CenarionWard + "%", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => RaFHelper.Leader == (WoWUnit)on && ((WoWUnit)on).IsAlive && ((WoWUnit)on).HealthPercent < Settings.Heal.CenarionWard) ); } if (Settings.Heal.NaturesVigil != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) behavs.AddBehavior(HealthToPriority(Settings.Heal.NaturesVigil) + PriHighBase, "Nature's Vigil @ " + Settings.Heal.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.NaturesVigil) ); else behavs.AddBehavior(HealthToPriority(Settings.Heal.NaturesVigil) + PriHighBase, "Nature's Vigil - Tank @ " + Settings.Heal.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => RaFHelper.Leader == (WoWUnit)on && ((WoWUnit)on).IsAlive && ((WoWUnit)on).HealthPercent < Settings.Heal.NaturesVigil) ); } if (Settings.Heal.TreeOfLife != 0) { behavs.AddBehavior(HealthToPriority(Settings.Heal.TreeOfLife) + PriHighBase, "Incarnation: Tree of Life @ " + Settings.Heal.TreeOfLife + "% MinCount: " + Settings.Heal.CountTreeOfLife, "Incarnation: Tree of Life", Spell.BuffSelf("Incarnation: Tree of Life", req => ((WoWUnit)req).HealthPercent < Settings.Heal.TreeOfLife && Settings.Heal.CountTreeOfLife <= HealerManager.Instance.TargetList.Count(h => h.IsAlive && h.HealthPercent < Settings.Heal.TreeOfLife)) ); } #endregion #region AoE Heals int maxDirectHeal = Math.Max(Settings.Heal.Nourish, Math.Max(Settings.Heal.HealingTouch, Settings.Heal.Regrowth)); if (Settings.Heal.WildGrowth != 0) behavs.AddBehavior(HealthToPriority(Settings.Heal.WildGrowth) + PriAoeBase, "Wild Growth @ " + Settings.Heal.WildGrowth + "% MinCount: " + Settings.Heal.CountWildGrowth, "Wild Growth", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( // ctx => HealerManager.GetBestCoverageTarget("Wild Growth", Settings.Heal.WildGrowth, 40, 30, Settings.Heal.CountWildGrowth), Spell.Cast( "Wild Growth", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.WildGrowth && Settings.Heal.CountWildGrowth <= HealerManager.Instance.TargetList .Count(p => p.IsAlive && p.HealthPercent <= Settings.Heal.WildGrowth && p.Location.DistanceSqr(((WoWUnit)req).Location) <= 30 * 30)) ) ) ); if (Settings.Heal.WildMushroomBloom != 0) behavs.AddBehavior(HealthToPriority(Settings.Heal.WildMushroomBloom) + PriAoeBase, "Wild Mushroom: Bloom @ " + Settings.Heal.WildMushroomBloom + "% MinCount: " + Settings.Heal.CountMushroomBloom, "Wild Mushroom: Bloom", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, CreateMushroomBloom() ) ); /* if (Settings.Heal.SwiftmendAOE != 0) behavs.AddBehavior(HealthToPriority(Settings.Heal.SwiftmendAOE) + PriAoeBase, "Swiftmend @ " + Settings.Heal.SwiftmendAOE + "% MinCount: " + Settings.Heal.CountSwiftmendAOE, "Swiftmend", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( ctx => HealerManager.GetBestCoverageTarget("Swiftmend", Settings.Heal.SwiftmendAOE, 40, 10, Settings.Heal.CountSwiftmendAOE , mainTarget: Unit.NearbyGroupMembersAndPets.Where(p => p.HealthPercent < Settings.Heal.SwiftmendAOE && p.SpellDistance() <= 40 && p.IsAlive && p.HasAnyOfMyAuras("Rejuvenation", "Regrowth"))), Spell.Cast("Force of Nature", on => (WoWUnit)on, req => Spell.GetCharges("Force of Nature") > 1), Spell.Cast(spell => "Swiftmend", mov => false, on => (WoWUnit)on, req => true, skipWowCheck: true) ) ) ); */ #endregion #region Direct Heals // Regrowth above ToL: Lifebloom so we use Clearcasting procs behavs.AddBehavior(200 + PriSingleBase, "Regrowth on Clearcasting", "Regrowth", new PrioritySelector( Spell.Cast("Regrowth", mov => !Me.HasAnyAura("Nature's Swiftness", "Incarnation: Tree of Life"), on => { WoWUnit target = (WoWUnit)on; if (target.HealthPercent > 95) { WoWUnit lbTarget = GetLifebloomTarget(); if (lbTarget != null && lbTarget.GetAuraStacks("Lifebloom") >= 3 && lbTarget.GetAuraTimeLeft("Lifebloom").TotalMilliseconds.Between(500,10000)) { return lbTarget; } } return target; }, req => Me.GetAuraTimeLeft("Clearcasting").TotalMilliseconds > 1500, cancel => false ) ) ); // ToL: Lifebloom if (Settings.Heal.TreeOfLife != 0 && Common.HasTalent(DruidTalents.Incarnation)) { behavs.AddBehavior(199 + PriSingleBase, "Lifebloom - Tree of Life", "Lifebloom", Spell.Cast("Lifebloom", mov => false, on => HealerManager.Instance.TargetList.FirstOrDefault( h => (h.GetAuraStacks("Lifebloom") < 3 || h.GetAuraTimeLeft("Lifebloom").TotalMilliseconds < 2500) && Spell.CanCastHack("Lifebloom", h, skipWowCheck: true)), req => Me.GetAuraTimeLeft("Incarnation") != TimeSpan.Zero, cancel => false ) ); } behavs.AddBehavior(198 + PriSingleBase, "Rejuvenation @ " + Settings.Heal.Rejuvenation + "%", "Rejuvenation", new PrioritySelector( Spell.Buff("Rejuvenation", true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.Rejuvenation, 1 ) ) ); if (Settings.Heal.Nourish != 0) { // roll 3 Rejuvs if Glyph of Rejuvenation equipped if (TalentManager.HasGlyph("Rejuvenation")) { // make priority 1 higher than Noursh (-1 here due to way HealthToPriority works) behavs.AddBehavior(HealthToPriority(Settings.Heal.Nourish-1) + PriSingleBase, "Roll 3 Rejuvenations for Glyph", "Rejuvenation", new PrioritySelector( Spell.Buff("Rejuvenation", true, 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, 1 ) ) ); } behavs.AddBehavior(HealthToPriority(Settings.Heal.Nourish) + PriSingleBase, "Nourish @ " + Settings.Heal.Nourish + "%", "Nourish", new PrioritySelector( Spell.Cast("Nourish", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.Nourish && ((WoWUnit)req).HasAnyOfMyAuras("Rejuvenation", "Regrowth", "Lifebloom", "Wild Growth"), cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ); } if (Settings.Heal.HealingTouch != 0) { int regrowthInstead = 0; string whyRegrowth = ""; if (SpellManager.HasSpell("Regrowth")) { if (TalentManager.HasGlyph("Regrowth")) { regrowthInstead = Math.Max(Settings.Heal.HealingTouch, Settings.Heal.HealingTouch); whyRegrowth = "Glyphed Regrowth (instead of Healing Touch) @ "; } else if (TalentManager.HasGlyph("Regrowth")) { regrowthInstead = Math.Max(Settings.Heal.HealingTouch, Settings.Heal.HealingTouch); whyRegrowth = "Regrowth (since Healing Touch unknown) @ "; } } if (regrowthInstead != 0) { behavs.AddBehavior(HealthToPriority(Settings.Heal.HealingTouch) + PriSingleBase, whyRegrowth + regrowthInstead + "%", "Regrowth", new PrioritySelector( Spell.Cast("Regrowth", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < regrowthInstead, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ); } else { behavs.AddBehavior(HealthToPriority(Settings.Heal.HealingTouch) + PriSingleBase, "Healing Touch @ " + Settings.Heal.HealingTouch + "%", "Healing Touch", new PrioritySelector( Spell.Cast("Healing Touch", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.HealingTouch, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ); } } if (Settings.Heal.Regrowth != 0) behavs.AddBehavior(HealthToPriority(Settings.Heal.Regrowth) + PriSingleBase, "Regrowth @ " + Settings.Heal.Regrowth + "%", "Regrowth", new PrioritySelector( Spell.Cast("Regrowth", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < Settings.Heal.Regrowth, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ) ); #endregion #region Lowest Priority Healer Tasks behavs.AddBehavior(2, "Rejuvenation while Moving @ " + SingularSettings.Instance.IgnoreHealTargetsAboveHealth + "%", "Rejuvenation", new Decorator( req => Me.IsMoving, Spell.Cast("Rejuvenation", mov => false, on => HealerManager.Instance.TargetList.FirstOrDefault(h => h.IsAlive && h.HealthPercent < SingularSettings.Instance.IgnoreHealTargetsAboveHealth && !h.HasMyAura("Rejuvenation") && Spell.CanCastHack("Rejuvenation", h, true)), req => true ) ) ); if (Settings.Heal.WildMushroomBloom != 0) behavs.AddBehavior(1, "Wild Mushroom: Set", "Wild Mushroom", CreateMushroomSetBehavior() ); #endregion behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal) behavs.ListBehaviors(); return new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // 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 ) ) ); }
public static Composite CreateDpsShamanOffHealBehavior() { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, ShamanSettings.OffHealSettings.HealingSurge); bool 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, "Cleanse Spirit", null, Dispelling.CreateDispelBehavior()); */ #region Save the Group behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.AncestralSwiftness) + 500, String.Format("Oh Shoot Heal @ {0}%", ShamanSettings.OffHealSettings.AncestralSwiftness), null, new Decorator( ret => (Me.Combat || ((WoWUnit)ret).Combat) && ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.OffHealSettings.AncestralSwiftness, new PrioritySelector( Spell.HandleOffGCD(Spell.BuffSelf("Ancestral Swiftness")), Spell.Cast("Healing Surge", on => (WoWUnit)on) ) ) ); #endregion #region AoE Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.HealingStreamTotem) + 300, string.Format("Healing Stream Totem @ {0}%", ShamanSettings.OffHealSettings.HealingStreamTotem), "Healing Stream Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Stream Totem", on => (!Me.Combat || Totems.Exist(WoWTotemType.Water)) ? null : HealerManager.Instance.TargetList.FirstOrDefault(p => p.PredictedHealthPercent() < ShamanSettings.OffHealSettings.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingStream)) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.HealingRain) + 200, string.Format("Healing Rain @ {0}% Count={1}", ShamanSettings.OffHealSettings.HealingRain, ShamanSettings.OffHealSettings.MinHealingRainCount), "Healing Rain", Spell.CastOnGround("Healing Rain", on => Restoration.GetBestHealingRainTarget(), req => HealerManager.Instance.TargetList.Count() > 1, false) ); #endregion #region Single Target Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.HealingSurge), string.Format("Healing Surge @ {0}%", ShamanSettings.OffHealSettings.HealingSurge), "Healing Surge", Spell.Cast("Healing Surge", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).PredictedHealthPercent(includeMyHeals: true) < ShamanSettings.OffHealSettings.HealingSurge, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); #endregion behavs.OrderBehaviors(); if (Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal ) behavs.ListBehaviors(); return new PrioritySelector( ctx => HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, 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(), /* Spell.Cast("Earth Shield", ret => (WoWUnit)ret, ret => ret is WoWUnit && Group.Tanks.Contains((WoWUnit)ret) && Group.Tanks.All(t => !t.HasMyAura("Earth Shield"))), */ behavs.GenerateBehaviorTree(), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 30f, 25f) ) ) ) ) ) ) ) ); }
public static Composite CreateDpsDruidOffHealBehavior() { if (!SingularSettings.Instance.DpsOffHealAllowed) return new ActionAlwaysFail(); HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, DruidSettings.OffHealSettings.HealingTouch); bool moveInRange = (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds); Logger.WriteDebugInBehaviorCreate("Druid Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); #region Save the Group // Tank: Rebirth if (Helpers.Common.CombatRezTargetSetting != CombatRezTarget.None) { behavs.AddBehavior(HealerManager.HealthToPriority(100) + 400, "Rebirth Tank/Healer", "Rebirth", Helpers.Common.CreateCombatRezBehavior("Rebirth", filter => true, requirements => Me.Combat) ); } if (DruidSettings.OffHealSettings.HeartOfTheWild != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(100) + 300, "Heart of the Wild @ " + DruidSettings.OffHealSettings.HeartOfTheWild + "% MinCount: " + DruidSettings.OffHealSettings.CountHeartOfTheWild, "Heart of the Wild", new Decorator( ret => Me.Combat && StyxWoW.Me.IsInGroup(), Spell.BuffSelf( "Heart of the Wild", req => ((WoWUnit)req).HealthPercent < DruidSettings.OffHealSettings.HeartOfTheWild && DruidSettings.OffHealSettings.CountHeartOfTheWild <= HealerManager.Instance.TargetList .Count(p => p.IsAlive && p.HealthPercent <= DruidSettings.OffHealSettings.HeartOfTheWild && p.Location.DistanceSqr(((WoWUnit)req).Location) <= 30 * 30) ) ) ); } #endregion #region Tank Buffing // Tank: Rejuv if Lifebloom not trained yet if (DruidSettings.OffHealSettings.Rejuvenation != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(100) + 200, "Rejuvenation - Tank", "Rejuvenation", Spell.Buff( "Rejuvenation", on => { WoWUnit unit = Resto.GetBestTankTargetFor("Rejuvenation"); if (unit != null && Spell.CanCastHack("Rejuvenation", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Rejuvenation ON TANK: {0}", unit.SafeName()); return unit; } return null; }, req => Me.Combat ) ); } if (DruidSettings.OffHealSettings.CenarionWard != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.OffHealSettings.CenarionWard) + 200, "Cenarion Ward @ " + DruidSettings.OffHealSettings.CenarionWard + "%", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => (WoWUnit)on, req => Me.Combat && ((WoWUnit)req).HealthPercent < DruidSettings.OffHealSettings.CenarionWard) ); else behavs.AddBehavior(HealerManager.HealthToPriority(99) + 200, "Cenarion Ward - Tanks", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => Resto.GetLifebloomTarget(), req => Me.Combat) ); } if (DruidSettings.OffHealSettings.NaturesVigil != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.OffHealSettings.NaturesVigil) + 200, "Nature's Vigil @ " + DruidSettings.OffHealSettings.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => (WoWUnit)on, req => Me.Combat && ((WoWUnit)req).HealthPercent < DruidSettings.OffHealSettings.NaturesVigil) ); else behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.OffHealSettings.NaturesVigil) + 200, "Nature's Vigil - Tank @ " + DruidSettings.OffHealSettings.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => Group.Tanks.FirstOrDefault(u => u.IsAlive && u.HealthPercent < DruidSettings.OffHealSettings.NaturesVigil && !u.HasAura("Nature's Vigil")), req => Me.Combat) ); } #endregion #region Atonement Only // atonement only Balance Druid bool addAtonement = HasTalent(DruidTalents.DreamOfCenarius) && ( Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal || Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.PullBuffs || Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Pull || Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.CombatBuffs || Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat ); // only Atonement healing if above Health % if (addAtonement && DruidSettings.OffHealSettings.DreamOfCenariusAbovePercent > 0 && Me.Specialization == WoWSpec.DruidBalance) { behavs.AddBehavior(HealerManager.HealthToPriority(100)+100, "DreamOfCenarius Above " + DruidSettings.OffHealSettings.DreamOfCenariusAbovePercent + "%", "Moonfire", new Decorator( req => Me.Combat && HealerManager.Instance.TargetList.Count(h => h.HealthPercent < DruidSettings.OffHealSettings.DreamOfCenariusAbovePercent) < DruidSettings.OffHealSettings.DreamOfCenariusAboveCount, new PrioritySelector( HealerManager.CreateAttackEnsureTarget(), Helpers.Common.EnsureReadyToAttackFromLongRange(), new PrioritySelector( ctx => Unit.UnitsInCombatWithUsOrOurStuff().Where(u => u.Combat && !u.IsCrowdControlled() && Me.IsSafelyFacing(u, 160)).ToList(), Spell.Cast("Sunfire", on => ((List<WoWUnit>)on).FirstOrDefault(u => u.HasAuraExpired("Sunfire", 2)), req => Me.CurrentEclipse > 0 && Me.CurrentTarget.HasKnownAuraExpired("Sunfire", 3)), Spell.Cast("Moonfire", on => ((List<WoWUnit>)on).FirstOrDefault(u => u.HasAuraExpired("Moonfire", 2)), req => Me.CurrentEclipse > 0 && Me.CurrentTarget.HasKnownAuraExpired("Moonfire", 3)) ) ) ) ); } #endregion #region Direct Heals behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.OffHealSettings.Rejuvenation), "Rejuvenation @ " + DruidSettings.OffHealSettings.Rejuvenation + "%", "Rejuvenation", new PrioritySelector( Spell.Buff("Rejuvenation", 1, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.OffHealSettings.Rejuvenation ) ) ); bool healingTouchKnown = SpellManager.HasSpell("Healing Touch"); if (DruidSettings.OffHealSettings.HealingTouch != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.OffHealSettings.HealingTouch), "Healing Touch @ " + DruidSettings.OffHealSettings.HealingTouch + "%", "Healing Touch", new PrioritySelector( Spell.Cast("Healing Touch", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.OffHealSettings.HealingTouch, 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 => Me.Combat ) ) ); #endregion behavs.OrderBehaviors(); if (Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal) behavs.ListBehaviors(); return new Decorator( ret => HealerManager.ActingAsOffHealer, new PrioritySelector( ctx => HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, behavs.GenerateBehaviorTree(), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 30f, 25f) ) ) ) ) ); }
public static Composite CreateRestoShamanHealingOnlyBehavior(bool selfOnly, bool moveInRange) { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); behavs.AddBehavior( HealthToPriority( SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), "Unleash Elements", "Unleash Elements", Spell.Buff("Unleash Elements", ret => (WoWUnit)ret, ret => (Me.IsMoving || ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.AncestralSwiftness) && Common.IsImbuedForHealing(Me.Inventory.Equipped.MainHand) )); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), String.Format("Ancestral Swiftness @ {0}%", SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), "Ancestral Swiftness", new Sequence( Spell.BuffSelf("Ancestral Swiftness", ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), Spell.Heal("Greater Healing Wave", ret => (WoWUnit)ret) )); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.GreaterHealingWave), "Greater Healing Wave", "Greater Healing Wave", Spell.Heal("Greater Healing Wave", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.GreaterHealingWave)); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.HealingWave), "Healing Wave", "Healing Wave", Spell.Heal("Healing Wave", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingWave)); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.HealingSurge), "Healing Surge", "Healing Surge", Spell.Heal("Healing Surge", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingSurge)); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.ChainHeal), "Chain Heal", "Chain Heal", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( new PrioritySelector( context => Clusters.GetBestUnitForCluster(ChainHealPlayers, ClusterType.Chained, ChainHealHopRange ), Spell.Heal( "Chain Heal", ret => (WoWPlayer)ret, ret => Clusters.GetClusterCount((WoWPlayer)ret, ChainHealPlayers, ClusterType.Chained, ChainHealHopRange) >= 3) ) ) ) ); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.HealingRain), "Healing Rain", "Healing Rain", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => Clusters.GetBestUnitForCluster(Unit.NearbyFriendlyPlayers.Cast<WoWUnit>(), ClusterType.Radius, 10f), Spell.CastOnGround( "Healing Rain", ret => ((WoWPlayer)ret).Location, ret => (StyxWoW.Me.GroupInfo.IsInRaid ? 3 : 2) < Clusters.GetClusterCount((WoWPlayer)ret, Unit.NearbyFriendlyPlayers.Cast<WoWUnit>(), ClusterType.Radius, 10f)) ) ) ); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.SpiritLinkTotem), "Spirit Link Totem", "Spirit Link Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Spirit Link Totem", ret => (WoWPlayer)ret, ret => Unit.NearbyFriendlyPlayers.Count( p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.SpiritLinkTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= (Me.GroupInfo.IsInRaid ? 3 : 2) ) ) ); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.HealingTideTotemPercent), "Healing Tide Totem", "Healing Tide Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Tide Totem", ret => Unit.NearbyFriendlyPlayers.Count(p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingTideTotemPercent && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= (Me.GroupInfo.IsInRaid ? 3 : 2) ) ) ); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.HealingStreamTotem), "Healing Stream Totem", "Healing Stream Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Stream Totem", ret => Unit.NearbyFriendlyPlayers.Count(p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= (Me.GroupInfo.IsInRaid ? 3 : 2) && !Totems.Exist( WoWTotemType.Water) ) ) ); behavs.AddBehavior(HealthToPriority( SingularSettings.Instance.Shaman.Heal.Ascendance), "Ascendance", "Ascendance", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.BuffSelf( "Ascendance", ret => Unit.NearbyFriendlyPlayers.Count(p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.Ascendance) >= (Me.GroupInfo.IsInRaid ? 3 : 2) ) ) ); behavs.OrderBehaviors(); behavs.ListBehaviors(); return new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.Instance.FirstUnit, // ctx => selfOnly ? StyxWoW.Me : GetHealTarget(), Spell.WaitForCast(), new Decorator( ret => Me.IsCasting, new ActionAlwaysSucceed()), new Decorator( ret => ret != null && ((WoWUnit)ret).GetPredictedHealthPercent() <= SingularSettings.Instance.IgnoreHealTargetsAboveHealth, new PrioritySelector( new Sequence( new Decorator( ret => guidLastHealTarget != ((WoWUnit)ret).Guid, new Action(ret => { guidLastHealTarget = ((WoWUnit)ret).Guid; Logger.WriteDebug(Color.LightGreen, "Heal Target - {0} {1:F1}% @ {2:F1} yds", ((WoWUnit)ret).SafeName(), ((WoWUnit)ret).GetPredictedHealthPercent(), ((WoWUnit)ret).Distance); })), new Action(ret => { return RunStatus.Failure; }) ), /* new Sequence( new Action(ret => Logger.WriteDebug(Color.LightGreen, "-- past spellcast")), new Action(ret => { return RunStatus.Failure; }) ), */ new Decorator( ret => !SpellManager.GlobalCooldown, new PrioritySelector( /* new Sequence( new Action(ret => Logger.WriteDebug(Color.LightGreen, "-- past gcd")), new Action(ret => { return RunStatus.Failure; }) ), */ Totems.CreateTotemsBehavior(), Spell.Heal("Earth Shield", ret => (WoWUnit)ret, ret => ret is WoWPlayer && Group.Tanks.Contains((WoWPlayer)ret) && Group.Tanks.All(t => !t.HasMyAura("Earth Shield"))), // Just pop RT on CD. Plain and simple. Calling GetBestRiptideTarget will see if we can spread RTs (T12 2pc) Spell.Heal("Riptide", ret => GetBestRiptideTarget((WoWPlayer)ret), ret => !Me.HasAnyAura("Tidal Waves", "Ancestral Swiftness") && (((WoWPlayer)ret).GetPredictedHealthPercent() > 15 || Spell.GetSpellCooldown("Ancestral Swiftness").TotalMinutes > 0f) // use Ancestral Swiftness value to check ), behavs.GenerateBehaviorTree(), Spell.Heal("Riptide", ret => GetBestRiptideTarget((WoWPlayer)ret), ret => !Me.HasAura("Ancestral Swiftness")) #if false , new Sequence( new Action(ret => Logger.WriteDebug(Color.LightGreen, "No Action - stunned:{0} silenced:{1}" , Me.Stunned || Me.IsStunned() , Me.Silenced )), new Action(ret => { return RunStatus.Failure; }) ) , new Decorator( ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget && !Unit.NearbyFriendlyPlayers.Any(u => u.IsInMyPartyOrRaid), new PrioritySelector( Movement.CreateMoveToLosBehavior(), Movement.CreateFaceTargetBehavior(), Helpers.Common.CreateInterruptSpellCast(ret => StyxWoW.Me.CurrentTarget), Spell.Cast("Earth Shock"), Spell.Cast("Lightning Bolt"), Movement.CreateMoveToTargetBehavior(true, 35f) )) #endif ) ), new Decorator( ret => moveInRange, Movement.CreateMoveToRangeAndStopBehavior(ret => (WoWUnit)ret, ret => 38f)) ) ) ); }