public static Composite CreateRestoShamanHealingOnlyBehavior(bool selfOnly = false) { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, Math.Max(ShamanSettings.RestoHealSettings.HealingWave, ShamanSettings.RestoHealSettings.HealingSurge)); bool moveInRange = false; if (!selfOnly) { moveInRange = (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds); } Logger.WriteDebugInBehaviorCreate("Shaman Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { behavs.AddBehavior(dispelPriority, "Purify Spirit", null, Dispelling.CreateDispelBehavior()); } #region Save the Group behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.SpiritLinkTotem) + 600, "Spirit Link Totem", "Spirit Link Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.CastOnGround("Spirit Link Totem", on => (WoWUnit)on, ret => HealerManager.Instance.TargetList.Count( p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.SpiritLinkTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= ShamanSettings.RestoHealSettings.MinSpiritLinkCount ) ) ); #endregion #region AoE Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingTideTotem) + 400, "Healing Tide Totem", "Healing Tide Totem", new Decorator( ret => (Me.Combat || ((WoWUnit)ret).Combat) && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid) && (!Totems.Exist(WoWTotem.Cloudburst) || (Totems.GetTotem(WoWTotem.Cloudburst).Expires - DateTime.UtcNow).TotalMilliseconds < 1500), Spell.Cast( "Healing Tide Totem", on => Me, req => Me.Combat && HealerManager.Instance.TargetList.Count(p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingTideTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= ShamanSettings.RestoHealSettings.MinHealingTideCount ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingStreamTotem) + 300, "Healing Stream Totem", "Healing Stream Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.Cast( "Healing Stream Totem", on => { if (Totems.Exist(WoWTotemType.Water)) { return(null); } if (Spell.IsSpellOnCooldown(Totems.ToSpellId(WoWTotem.HealingStream))) { return(null); } // if tank in group, make sure we are near the tank WoWUnit tank = HealerManager.TankToStayNear; if (tank != null) { if (!HealerManager.IsTankSettledIntoFight(tank)) { return(null); } if (tank.Distance > Totems.GetTotemRange(WoWTotem.HealingStream)) { return(null); } } WoWUnit unit = HealerManager.Instance.TargetList .FirstOrDefault( p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingStream) ); return(unit); } ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.GiftoftheQueen) + 300, "Gift of the Queen", "Gift of the Queen", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( context => GetBestGiftoftheQueenTarget(), new Decorator( ret => ret != null, Spell.CastOnGround("Gift of the Queen", on => (WoWUnit)on, req => true, false) ) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.CloudburstTotem) + 300, "Cloudburst Totem", "Cloudburst Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.Cast( "Cloudburst Totem", on => { if (Totems.Exist(WoWTotemType.Water)) { return(null); } if (Spell.IsSpellOnCooldown(Totems.ToSpellId(WoWTotem.Cloudburst))) { return(null); } if (Unit.ValidUnit(Me.CurrentTarget) && (Me.CurrentTarget.TimeToDeath() < 20 || Unit.UnitsInCombatWithUsOrOurStuff().Count() < 3)) { return(null); } // if tank in group, make sure we are near the tank WoWUnit tank = HealerManager.TankToStayNear; if (tank != null) { if (!HealerManager.IsTankSettledIntoFight(tank)) { return(null); } if (tank.Distance > Totems.GetTotemRange(WoWTotem.Cloudburst)) { return(null); } } WoWUnit unit = HealerManager.Instance.TargetList .Where( p => p.HealthPercent < ShamanSettings.RestoHealSettings.CloudburstTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.Cloudburst) ) .OrderBy(p => (int)p.HealthPercent) .FirstOrDefault(); return(unit); } ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingRain) + 200, "Healing Rain", "Healing Rain", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( context => GetBestHealingRainTarget(), new Decorator( ret => ret != null, Spell.CastOnGround("Healing Rain", on => (WoWUnit)on, req => true, false) ) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.ChainHeal) + 150, "Chain Heal", "Chain Heal", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( ctx => GetBestChainHealTarget(), new Decorator( ret => ret != null, new Sequence( new DecoratorContinue( req => ((WoWUnit)req).HasAuraExpired("Riptide", TimeSpan.FromMilliseconds(ChainHealCastTime), true), new Sequence( Spell.Cast("Riptide", on => (WoWUnit)on, req => true, cancel => false), new Wait(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(LagTolerance.No), new ActionAlwaysSucceed()), new Action(r => TidalWaveRefresh()) ) ), new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(LagTolerance.No), new ActionAlwaysSucceed()), Spell.Cast("Chain Heal", on => (WoWUnit)on), new Action(r => TidalWaveRefresh()) ) ) ) ) ); #endregion #region Single Target Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingWave), "Healing Wave", "Healing Wave", new Decorator(ret => ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingWave, new Sequence( new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), new WaitContinue(2, until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), Spell.Cast("Healing Wave", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action(r => TidalWaveConsume()) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingSurge), "Healing Surge", "Healing Surge", new Decorator(ret => ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingSurge, new Sequence( new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), new WaitContinue(2, until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), Spell.Cast("Healing Surge", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action(r => TidalWaveConsume()) ) ) ); #endregion #region Healing Cooldowns behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.Ascendance) + 100, "Ascendance", "Ascendance", new Decorator( ret => ShamanSettings.UseAscendance && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.BuffSelf( "Ascendance", ret => HealerManager.Instance.TargetList.Count(p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.Ascendance) >= ShamanSettings.RestoHealSettings.MinAscendanceCount ) ) ); #endregion behavs.OrderBehaviors(); if (selfOnly == false && CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) { behavs.ListBehaviors(); } return(new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, CreateRestoDiagnosticOutputBehavior(ret => (WoWUnit)ret), new Decorator( ret => ret != null && (Me.Combat || ((WoWUnit)ret).Combat || ((WoWUnit)ret).PredictedHealthPercent() <= 99), new PrioritySelector( new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Totems.CreateTotemsBehavior(), // roll Riptide on Tanks otherwise new Sequence( Spell.Cast("Riptide", on => { WoWUnit unit = GetBestRiptideTankTarget(); if (unit != null && Spell.CanCastHack("Riptide", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing RIPTIDE ON TANK: {0}", unit.SafeName()); return unit; } return null; }), new Action(r => TidalWaveRefresh()) ), // cast Riptide if we are a low level CreateRestoShamanBuffRiptideLowLevel(), // cast Riptide if we need Tidal Waves -- skip if Ancestral Swiftness is CreateRestoShamanBuffTidalWaves(), behavs.GenerateBehaviorTree(), // cast Riptide if we need Tidal Waves -- skip if Ancestral Swiftness is new Decorator( ret => { int rollCount = HealerManager.Instance.TargetList.Count( u => u.IsAlive && u.HasMyAura("Riptide")); // Logger.WriteDebug("GetBestRiptideTarget: currently {0} group members have my Riptide", rollCount); return rollCount < ShamanSettings.RestoHealSettings.RollRiptideCount; }, new Sequence( Spell.Cast("Riptide", on => { // if tank needs Riptide, bail out on Rolling as they have priority if (GetBestRiptideTankTarget() != null) { return null; } // get the best target from all wowunits in our group WoWUnit unit = GetBestRiptideTarget(); if (unit != null) { Logger.WriteDebug(Color.White, "ROLLING RIPTIDE on: {0}", unit.SafeName()); } return unit; }), new Action(r => TidalWaveRefresh()) ) ), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 40f, 34f) ) ) ) ) ) ) ) )); }
//private static WoWUnit _moveToHealTarget = null; //private static WoWUnit _lastMoveToTarget = null; // temporary lol name ... will revise after testing public static Composite CreateHealingOnlyBehavior(bool selfOnly, bool moveInRange) { BehaviorType behaveType = Dynamics.CompositeBuilder.CurrentBehaviorType; if (SingularRoutine.CurrentWoWContext == WoWContext.Normal) { return(new ActionAlwaysFail()); } HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, Math.Max(DruidSettings.Heal.HealingTouch, DruidSettings.Heal.Regrowth)); int maxDirectHeal = Math.Max(DruidSettings.Heal.HealingTouch, DruidSettings.Heal.Regrowth); Logger.WriteDebugInBehaviorCreate("Druid Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); #region Cleanse if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; behavs.AddBehavior(dispelPriority, "Nature's Cure", "Nature's Cure", Dispelling.CreateDispelBehavior()); } #endregion #region Save the Group // Tank: Rebirth if (Helpers.Common.CombatRezTargetSetting != CombatRezTarget.None) { behavs.AddBehavior(799, "Rebirth Tank/Healer", "Rebirth", Helpers.Common.CreateCombatRezBehavior("Rebirth", filter => true, requirements => true) ); } if (DruidSettings.Heal.HeartOfTheWild != 0) { behavs.AddBehavior(795, "Heart of the Wild @ " + DruidSettings.Heal.HeartOfTheWild + "% MinCount: " + DruidSettings.Heal.CountHeartOfTheWild, "Heart of the Wild", new Decorator( ret => Me.IsInGroup(), Spell.BuffSelf( "Heart of the Wild", req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.HeartOfTheWild && DruidSettings.Heal.CountHeartOfTheWild <= HealerManager.Instance.TargetList .Count(p => p.IsAlive && p.HealthPercent <= DruidSettings.Heal.HeartOfTheWild && p.Location.DistanceSquared(((WoWUnit)req).Location) <= 30 * 30) ) ) ); } if (DruidSettings.Heal.Tranquility != 0) { behavs.AddBehavior(798, "Tranquility @ " + DruidSettings.Heal.Tranquility + "% MinCount: " + DruidSettings.Heal.CountTranquility, "Tranquility", new Decorator( ret => Me.IsInGroup(), Spell.Cast( "Tranquility", mov => true, on => (WoWUnit)on, req => HealerManager.Instance.TargetList.Count(h => h.IsAlive && h.HealthPercent < DruidSettings.Heal.Tranquility && h.SpellDistance() < 40) >= DruidSettings.Heal.CountTranquility, cancel => false ) ) ); } if (DruidSettings.Heal.Swiftmend != 0) { behavs.AddBehavior(797, "Swiftmend Direct @ " + DruidSettings.Heal.Swiftmend + "%", "Swiftmend", new Decorator( ret => (!Spell.IsSpellOnCooldown("Swiftmend") || Spell.GetCharges("Force of Nature") > 0) && ((WoWUnit)ret).PredictedHealthPercent(includeMyHeals: true) < DruidSettings.Heal.Swiftmend && (Me.IsInGroup()) && Spell.CanCastHack("Rejuvenation", (WoWUnit)ret, skipWowCheck: true), new Sequence( new DecoratorContinue( req => !((WoWUnit)req).HasAnyAura("Rejuvenation", "Regrowth"), new PrioritySelector( Spell.Buff("Rejuvenation", on => (WoWUnit)on), Spell.Cast("Regrowth", on => (WoWUnit)on, req => !glyphRegrowth, cancel => false) ) ), new Wait(TimeSpan.FromMilliseconds(500), until => ((WoWUnit)until).HasAnyAura("Rejuvenation", "Regrowth"), new ActionAlwaysSucceed()), new Wait(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(), new ActionAlwaysSucceed()), new PrioritySelector( Spell.Cast("Force of Nature", on => (WoWUnit)on, req => Spell.GetCharges("Force of Nature") > 1), Spell.Cast("Swiftmend", on => (WoWUnit)on) ) ) ) ); } #endregion #region Tank Buffing // Priority Buff: buff Mastery: Harmony if (Me.Level >= 80 && DruidSettings.Heal.BuffHarmony) { behavs.AddBehavior(100 + PriHighBase, "Buff Harmony w/ Healing Touch", "Healing Touch", new Sequence( Spell.Cast( "Healing Touch", mov => true, on => (WoWUnit)on, req => { if (Me.GetAuraTimeLeft("Harmony").TotalMilliseconds > 1500) { return(false); } if (((WoWUnit)req).HealthPercent < maxDirectHeal) { return(false); } if (Spell.DoubleCastContains(Me, "Harmony")) { return(false); } if (!Spell.CanCastHack("Healing Touch", (WoWUnit)req)) { return(false); } Logger.Write(LogColor.Hilite, "^Harmony: buffing with Healing Touch"); return(true); }, cancel => Me.GetAuraTimeLeft("Harmony").TotalMilliseconds > 1500 && ((WoWUnit)cancel).HealthPercent > cancelHeal ), new Action(r => Spell.UpdateDoubleCast("Harmony", Me)) ) ); } // Tank: Lifebloom behavs.AddBehavior(99 + PriHighBase, "Lifebloom - Tank", "Lifebloom", Spell.Buff("Lifebloom", on => GetLifebloomTarget(), req => Me.Combat) ); // Tank: Rejuv if Lifebloom not trained yet if (DruidSettings.Heal.Rejuvenation != 0) { behavs.AddBehavior(98 + PriHighBase, "Rejuvenation - Tank", "Rejuvenation", Spell.Buff("Rejuvenation", on => { WoWUnit unit = GetBestTankTargetFor("Rejuvenation"); if (unit != null && Spell.CanCastHack("Rejuvenation", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing Rejuvenation ON TANK: {0}", unit.SafeName()); return(unit); } return(null); }) ); } if (DruidSettings.Heal.Ironbark != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Ironbark) + PriHighBase, "Ironbark @ " + DruidSettings.Heal.Ironbark + "%", "Ironbark", Spell.Buff("Ironbark", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.Ironbark) ); } else { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Ironbark) + PriHighBase, "Ironbark - Tank @ " + DruidSettings.Heal.Ironbark + "%", "Ironbark", Spell.Buff("Ironbark", on => Group.Tanks.FirstOrDefault(u => u.IsAlive && u.HealthPercent < DruidSettings.Heal.Ironbark && !u.HasActiveAura("Ironbark"))) ); } } if (DruidSettings.Heal.CenarionWard != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.CenarionWard) + PriHighBase, "Cenarion Ward @ " + DruidSettings.Heal.CenarionWard + "%", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.CenarionWard) ); } else { behavs.AddBehavior(100 + PriHighBase, "Cenarion Ward - Tanks", "Cenarion Ward", Spell.Buff("Cenarion Ward", on => GetLifebloomTarget(), req => Me.Combat) ); } } if (DruidSettings.Heal.NaturesVigil != 0) { if (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.NaturesVigil) + PriHighBase, "Nature's Vigil @ " + DruidSettings.Heal.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.NaturesVigil) ); } else { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.NaturesVigil) + PriHighBase, "Nature's Vigil - Tank @ " + DruidSettings.Heal.NaturesVigil + "%", "Nature's Vigil", Spell.Buff("Nature's Vigil", on => Group.Tanks.FirstOrDefault(u => u.IsAlive && u.HealthPercent < DruidSettings.Heal.NaturesVigil && !u.HasActiveAura("Nature's Vigil"))) ); } } if (DruidSettings.Heal.TreeOfLife != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.TreeOfLife) + PriHighBase, "Incarnation: Tree of Life @ " + DruidSettings.Heal.TreeOfLife + "% MinCount: " + DruidSettings.Heal.CountTreeOfLife, "Incarnation: Tree of Life", Spell.BuffSelf("Incarnation: Tree of Life", req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.TreeOfLife && DruidSettings.Heal.CountTreeOfLife <= HealerManager.Instance.TargetList.Count(h => h.IsAlive && h.HealthPercent < DruidSettings.Heal.TreeOfLife)) ); } #endregion #region AoE Heals if (DruidSettings.Heal.Efflorescence != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Efflorescence) + PriAoeBase, "Efflorescence @ " + DruidSettings.Heal.Efflorescence, "Efflorescence", new Decorator( ret => Me.IsInGroup(), CreateMushroomSetBehavior() ) ); } if (DruidSettings.Heal.WildGrowth != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.WildGrowth) + PriAoeBase, "Wild Growth @ " + DruidSettings.Heal.WildGrowth + "% MinCount: " + DruidSettings.Heal.CountWildGrowth, "Wild Growth", new Decorator( ret => Me.IsInGroup(), new PrioritySelector( // ctx => HealerManager.GetBestCoverageTarget("Wild Growth", Settings.Heal.WildGrowth, 40, 30, Settings.Heal.CountWildGrowth), Spell.Buff( "Wild Growth", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.WildGrowth && DruidSettings.Heal.CountWildGrowth <= HealerManager.Instance.TargetList .Count(p => p.IsAlive && p.HealthPercent <= DruidSettings.Heal.WildGrowth && p.Location.DistanceSquared(((WoWUnit)req).Location) <= 30 * 30)) ) ) ); } #endregion #region Direct Heals // Regrowth above ToL: Lifebloom so we use Clearcasting procs behavs.AddBehavior(200 + PriSingleBase, "Regrowth on Clearcasting", "Regrowth", new Sequence( CastRegrowth( on => { if (Spell.DoubleCastContains(Me, "Clearcasting")) { return(null); } double clearLeft = Me.GetAuraTimeLeft("Clearcasting").TotalMilliseconds; // ignore if less than regrowth cast time left if (clearLeft < Spell.GetSpellCastTime("Regrowth").TotalMilliseconds) { return(null); } WoWUnit target = (WoWUnit)on; double healthPercent = target == null ? 0.0 : target.HealthPercent; // clearLeft > 3000, so clear target if not needed now and try again next pass if (target != null) { // still have enough time remaining on Clearcasting buff, so hold free Regrowth a bit longer to see if greater need arises if (healthPercent > maxDirectHeal) { target = null; } else if (!Spell.CanCastHack("Regrowth", target)) { target = null; } } if (target != null) { Logger.Write(LogColor.Hilite, "^Clearcasting: Regrowth at Health {0:F1}%", healthPercent); } return(target); }, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal && Me.GetAuraTimeLeft("Clearcasting").TotalMilliseconds > 4000 && !((WoWUnit)cancel).GetAuraTimeLeft("Lifebloom").TotalMilliseconds.Between(Me.CurrentCastTimeLeft.TotalMilliseconds, 8750) ), // add double cast entry to make sure we don't try to reuse immediately new Action(r => Spell.UpdateDoubleCast("Clearcasting", Me, 500)) ) ); behavs.AddBehavior(198 + PriSingleBase, "Rejuvenation @ " + DruidSettings.Heal.Rejuvenation + "%", "Rejuvenation", new PrioritySelector( Spell.Buff("Rejuvenation", 1, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < DruidSettings.Heal.Rejuvenation ) ) ); if (DruidSettings.Heal.HealingTouch != 0) { // roll 3 Rejuvs if Glyph of Rejuvenation equipped if (glyphRejuv) { // make priority 1 higher than Noursh (-1 here due to way HealerManager.HealthToPriority works) behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.HealingTouch - 1) + PriSingleBase, "Roll 3 Rejuvenations for Glyph", "Rejuvenation", new PrioritySelector( Spell.Buff("Rejuvenation", 1, on => { // iterate through so we can stop at either 3 with rejuv or first without int cntHasAura = 0; foreach (WoWUnit h in HealerManager.Instance.TargetList) { if (h.IsAlive) { if (!h.HasKnownAuraExpired("Rejuvenation", 1)) { cntHasAura++; if (cntHasAura >= 3) { return(null); } } else { if (h.InLineOfSpellSight) { return(h); } } } } return(null); }, req => true ) ) ); } } int regrowthInstead = 0; bool healingTouchKnown = SpellManager.HasSpell("Healing Touch"); if (DruidSettings.Heal.HealingTouch != 0) { string whyRegrowth = ""; if (SpellManager.HasSpell("Regrowth")) { if (!healingTouchKnown) { regrowthInstead = Math.Max(DruidSettings.Heal.Regrowth, DruidSettings.Heal.HealingTouch); whyRegrowth = "Regrowth (since Healing Touch unknown) @ "; } } if (regrowthInstead != 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.HealingTouch) + PriSingleBase, whyRegrowth + regrowthInstead + "%", "Regrowth", new PrioritySelector( Spell.Cast( sp => (Me.Combat || !healingTouchKnown) ? "Regrowth" : "Healing Touch", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent <regrowthInstead, cancel => ((WoWUnit)cancel).HealthPercent> cancelHeal ) ) ); } else { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.HealingTouch) + PriSingleBase, "Healing Touch @ " + DruidSettings.Heal.HealingTouch + "%", "Healing Touch", new PrioritySelector( Spell.Cast("Healing Touch", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent <DruidSettings.Heal.HealingTouch, cancel => ((WoWUnit)cancel).HealthPercent> cancelHeal ) ) ); } } if (DruidSettings.Heal.Regrowth != 0 && regrowthInstead == 0) { behavs.AddBehavior(HealerManager.HealthToPriority(DruidSettings.Heal.Regrowth) + PriSingleBase, "Regrowth @ " + DruidSettings.Heal.Regrowth + "%", "Regrowth", new PrioritySelector( Spell.Cast("Regrowth", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent <DruidSettings.Heal.Regrowth, cancel => ((WoWUnit)cancel).HealthPercent> cancelHeal ) ) ); } #endregion #region Lowest Priority Healer Tasks behavs.AddBehavior(3, "Rejuvenation while Moving @ " + SingularSettings.Instance.IgnoreHealTargetsAboveHealth + "%", "Rejuvenation", new Decorator( req => Me.IsMoving, Spell.Buff("Rejuvenation", on => HealerManager.Instance.TargetList.FirstOrDefault(h => h.IsAlive && h.HealthPercent < SingularSettings.Instance.IgnoreHealTargetsAboveHealth && !h.HasMyAura("Rejuvenation") && Spell.CanCastHack("Rejuvenation", h, true)), req => true ) ) ); #endregion behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal) { behavs.ListBehaviors(); } return(new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindHighestPriorityTarget(), // HealerManager.Instance.FirstUnit, Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown() && ret != null, behavs.GenerateBehaviorTree() ), new Decorator( ret => moveInRange, Movement.CreateMoveToUnitBehavior( ret => Battlegrounds.IsInsideBattleground ? (WoWUnit)ret : Group.Tanks.Where(a => a.IsAlive).OrderBy(a => a.Distance).FirstOrDefault(), 35f ) ) )); }
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 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)) ) ) )); }
//private static WoWUnit _moveToHealTarget = null; //private static WoWUnit _lastMoveToTarget = null; public static Composite CreateHolyHealOnlyBehavior(bool selfOnly, bool moveInRange) { if (SingularRoutine.CurrentWoWContext == WoWContext.Normal) { return(new ActionAlwaysFail()); } HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, Math.Max(PriestSettings.HolyHeal.Heal, 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.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 ) ); } if (PriestSettings.LightOfTuure != 0) { behavs.AddBehavior(HealthToPriority(PriestSettings.LightOfTuure) + 300, "Light of T'uure @ " + PriestSettings.LightOfTuure + "%", "Light of T'uure", Spell.Cast("Light of T'uure", mov => false, on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.LightOfTuure && !((WoWUnit)req).HasAura("Light of T'uure") && Group.Tanks.Contains(((WoWUnit)req)) ) ); } #endregion #region AoE Heals int maxDirectHeal = Math.Max(PriestSettings.HolyHeal.FlashHeal, PriestSettings.HolyHeal.Heal); // instant, so cast this first ALWAYS if (PriestSettings.HolyHeal.HolyWordSanctify != 0) { behavs.AddBehavior(398, "Holy Word: Sanctify @ " + PriestSettings.HolyHeal.HolyWordSanctify + "% MinCount: " + PriestSettings.HolyHeal.CountHolyWordSanctify, "Holy Word: Sanctify", new Decorator( ret => true, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Holy Word: Sanctify", PriestSettings.HolyHeal.HolyWordSanctify, 40, 10, PriestSettings.HolyHeal.CountHolyWordSanctify), new Decorator( ret => ret != null, Spell.CastOnGround("Holy Word: Sanctify", on => (WoWUnit)on, req => true, false) ) ) ) ); } if (PriestSettings.HolyHeal.PrayerOfMending != 0) { behavs.AddBehavior(397, "Prayer of Mending @ " + PriestSettings.HolyHeal.PrayerOfMending + "%", "Prayer of Mending", Spell.Cast("Prayer of Mending", mov => true, on => (WoWUnit)on, req => !((WoWUnit)req).IsMe && ((WoWUnit)req).HealthPercent <PriestSettings.HolyHeal.PrayerOfMending, cancel => ((WoWUnit)cancel).HealthPercent> cancelHeal ) ); } /* * 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.PathToUnit, 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 && Unit.UnfriendlyUnits(30).Any(u => u.Combat && !u.IsCrowdControlled()), Spell.Cast("Halo", req => true) ) ) ); } if (PriestSettings.HolyHeal.BindingHeal != 0) { 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 ) ); } 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, 20, PriestSettings.HolyHeal.CountPrayerOfHealing), Spell.Cast("Prayer of Healing", on => (WoWUnit)on) ) ) ); } #endregion /* * HolyWordSanctify Holy Word: Sanctify * 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 (maxDirectHeal != 0) // Nuke a Flash Heal { behavs.AddBehavior(400, "Surge of Light @ " + maxDirectHeal + "%", "Flash Heal", Spell.Cast("Flash Heal", mov => false, on => (WoWUnit)on, req => Me.HasActiveAura("Surge of Light") && ((WoWUnit)req).HealthPercent < 85 ) ); } if (PriestSettings.HolyHeal.HolyWordSerenity != 0) { behavs.AddBehavior(HealthToPriority(1) + 4, "Holy Word: Serenity @ " + PriestSettings.HolyHeal.HolyWordSerenity + "%", "Holy Word: Serenity", Spell.CastHack("Holy Word: Serenity", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.HolyHeal.HolyWordSerenity ) ); } var cmt = ""; var flashHealHealth = PriestSettings.HolyHeal.FlashHeal; if (!SpellManager.HasSpell("Heal")) { flashHealHealth = Math.Max(flashHealHealth, PriestSettings.HolyHeal.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.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 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) < 15) { Logger.WriteDiagnostic("Lightwell: found target {0}", t.SafeName()); return(true); } } return(false); }); return(_lightwellTarget == null ? RunStatus.Failure : RunStatus.Success); }), 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 && 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 ) ) )); }
public static Composite CreateDiscHealOnlyBehavior() { 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); 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 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 (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(99) + PriHighBase, "Power Word: Shield - Tank", "Power Word: Shield", new Decorator( req => Me.Combat, Spell.Buff( "Power Word: Shield", on => Group.Tanks .FirstOrDefault(u => u.IsAlive && CanWePwsUnit(u) && Spell.CanCastHack("Power Word: Shield", u)) ) ) ); behavs.AddBehavior(HealthToPriority(99) + PriHighBase, "Plea - Tank Attonement", "Plea", new Decorator( req => Me.Combat, Spell.Cast( "Plea", on => Group.Tanks .FirstOrDefault(u => u.IsAlive && !u.HasActiveAura("Atonement") && Spell.CanCastHack("Plea", u)) ) ) ); #endregion #region Save the Highest Priority Targets const int SaveEssential = 30; behavs.AddBehavior(HealthToPriority(SaveEssential) + PriSaveEssential, "Save Highest Priority Target below " + SaveEssential + "%", "Shadow Mend", new Decorator( req => Me.Combat, new PrioritySelector( ctx => HealerManager.FindHighestPriorityTarget(), 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.Cast( "Plea", on => (WoWUnit)on, req => !((WoWUnit)req).HasActiveAura("Atonement") ), Spell.Cast( "Shadow Mend", on => (WoWUnit)on, req => ((WoWUnit)req).HealthPercent < PriestSettings.DiscHeal.ShadowMend ) ) ) ); #endregion #region AoE Heals 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.PowerWordRadiance != 0) { behavs.AddBehavior(99 + PriAoeBase, "Power Word: Radiance", "Power Word: Radiance", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => HealerManager.GetBestCoverageTarget("Power Word: Radiance", PriestSettings.DiscHeal.PowerWordRadiance, 40, 30, PriestSettings.DiscHeal.CountPowerWordRadiance, o => o != null && !((WoWUnit)o).HasActiveAura("Atonement")), CastBuffsBehavior("Power Word: Radiance"), Spell.Cast("Power Word: Radiance", on => (WoWUnit)on) ) ) ); } #endregion #region Direct Heals if (PriestSettings.DiscHeal.Plea != 0) { behavs.AddBehavior(100 + PriSingleBase, "Atonement Plea @ " + PriestSettings.DiscHeal.Plea + "%", "Plea", new Decorator( req => !((WoWUnit)req).HasActiveAura("Atonement"), new PrioritySelector( CastBuffsBehavior("Plea"), Spell.Cast("Plea", mov => false, on => (WoWUnit)on, req => true, cancel => false ) ) ) ); } 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)) ); } #endregion behavs.OrderBehaviors(); behavs.ListBehaviors(); return(new PrioritySelector( ctx => HealerManager.FindHighestPriorityTarget(), Spell.WaitForCastOrChannel(), new Decorator( ret => !Spell.IsGlobalCooldown() && ret != null, behavs.GenerateBehaviorTree() ) )); }
private static void CreateMistweaverHealingWowhead(bool selfOnly, PrioritizedBehaviorList behavs) { // save a group if (!selfOnly) { behavs.AddBehavior( HealthToPriority(MonkSettings.MistHealSettings.Revival) + 900, String.Format("Revival on {0} targets @ {1}%", MonkSettings.MistHealSettings.CountRevival, MonkSettings.MistHealSettings.Revival), "Revival", new Decorator( req => (req as WoWUnit).HealthPercent < MonkSettings.MistHealSettings.Revival, Spell.Cast("Revival", on => { if (Spell.IsSpellOnCooldown("Revival")) { return(null); } List <WoWUnit> revlist = HealerManager.Instance.TargetList .Where(u => u.HealthPercent < MonkSettings.MistHealSettings.Revival && u.DistanceSqr < 100 * 100 && u.InLineOfSpellSight) .ToList(); if (revlist.Count() < MonkSettings.MistHealSettings.CountRevival) { return(null); } Logger.Write(LogColor.Hilite, "Revival: found {0} heal targets below {1}%", revlist.Count(), MonkSettings.MistHealSettings.Revival); return(revlist.FirstOrDefault(u => u != null && u.IsValid)); }) ) ); } // save a player behavs.AddBehavior( HealthToPriority(MonkSettings.MistHealSettings.LifeCocoon) + 800, String.Format("Life Cocoon @ {0}%", MonkSettings.MistHealSettings.LifeCocoon), "Life Cocoon", Spell.Buff("Life Cocoon", on => (WoWUnit)on, req => (Me.Combat || (req as WoWUnit).Combat) && (req as WoWUnit).PredictedHealthPercent(includeMyHeals: true) < MonkSettings.MistHealSettings.LifeCocoon) ); if (!selfOnly) { behavs.AddBehavior( HealthToPriority(1) + 700, "Summon Jade Serpent Statue", "Summon Jade Serpent Statue", new Decorator( req => Group.Tanks.Any(t => t.Combat && !t.IsMoving && t.GotTarget() && t.CurrentTarget.IsHostile && t.SpellDistance(t.CurrentTarget) < 10), CreateSummonJadeSerpentStatueBehavior() ) ); } if (!selfOnly) { behavs.AddBehavior( HealthToPriority(99) + 700, "Thunder Focus Tea on me", "Thunder Focus Tea", new Decorator( req => (Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid)), Spell.Cast("Thunder Focus Tea", on => Me) ) ); behavs.AddBehavior( HealthToPriority(100) + 700, String.Format("Roll Renewing Mist on at least {0} targets", MonkSettings.MistHealSettings.RollRenewingMistCount), "Renewing Mist", new Decorator( req => true, CreateMistweaverRollRenewingMist() ) ); } behavs.AddBehavior( HealthToPriority(MonkSettings.MistHealSettings.ChiWave) + 600, String.Format("Chi Wave on {0} targets @ {1}%", MonkSettings.MistHealSettings.CountChiWave, MonkSettings.MistHealSettings.ChiWave), "Chi Wave", new Decorator( req => (req as WoWUnit).PredictedHealthPercent() < MonkSettings.MistHealSettings.ChiWave, Spell.Cast("Chi Wave", on => GetBestChiWaveTarget()) ) ); behavs.AddBehavior( HealthToPriority(1) + 500, "Expel Harm in Combat for Chi", "Expel Harm", Spell.Buff("Expel Harm", on => Common.BestExpelHarmTarget(), req => Me.Combat ) ); behavs.AddBehavior( HealthToPriority(MonkSettings.MistHealSettings.Effuse), String.Format("Effuse @ {0}%", MonkSettings.MistHealSettings.Effuse), "Effuse", new Decorator( req => ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.Effuse && Spell.CanCastHack("Effuse", (WoWUnit)req), new PrioritySelector( Spell.Cast("Effuse", on => (WoWUnit)on), new Action(r => { Logger.WriteDebug("Effuse: failed to ensure Soothing Mist first"); return(RunStatus.Failure); }) ) ) ); behavs.AddBehavior( HealthToPriority(MonkSettings.MistHealSettings.Vivify), String.Format("Vivify @ {0}%", MonkSettings.MistHealSettings.Vivify), "Vivify", new Decorator( req => ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.Vivify && Spell.CanCastHack("Vivify", (WoWUnit)req), new PrioritySelector( Spell.Cast("Vivify", on => (WoWUnit)on), new Action(r => { Logger.WriteDebug("Vivify: failed to ensure Soothing Mist first"); return(RunStatus.Failure); }) ) ) ); behavs.AddBehavior( HealthToPriority(MonkSettings.MistHealSettings.EnvelopingMist), String.Format("Enveloping Mist @ {0}%", MonkSettings.MistHealSettings.EnvelopingMist), "Enveloping Mist", new Decorator( req => ((WoWUnit)req).HasAuraExpired("Enveloping Mist") && ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.EnvelopingMist && Spell.CanCastHack("Enveloping Mist", (WoWUnit)req), new PrioritySelector( Spell.Cast("Enveloping Mist", on => (WoWUnit)on), new Action(r => { Logger.WriteDebug("EnvelopingMist: failed to ensure Soothing Mist first"); return(RunStatus.Failure); }) ) ) ); }
private static void CreateMistweaverHealingRotation(bool selfOnly, PrioritizedBehaviorList behavs) { CreateMistweaverHealingWowhead(selfOnly, behavs); }
public static Composite CreateMistweaverMonkHealing(bool selfOnly = false) { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = SingularSettings.Instance.IgnoreHealTargetsAboveHealth; cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.RenewingMist); cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.EnvelopingMist); cancelHeal = (int)Math.Max(cancelHeal, MonkSettings.MistHealSettings.Effuse); bool moveInRange = false; if (!selfOnly) { moveInRange = (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds); } Logger.WriteDebugInBehaviorCreate("Monk Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { behavs.AddBehavior(dispelPriority, "Detox", "Detox", Dispelling.CreateDispelBehavior()); } CreateMistweaverHealingRotation(selfOnly, behavs); behavs.OrderBehaviors(); if (selfOnly == false && Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) { behavs.ListBehaviors(); } return(new PrioritySelector( // ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // ctx => selfOnly ? StyxWoW.Me : HealerManager.Instance.FirstUnit, ctx => selfOnly ? StyxWoW.Me : MyHealTarget, CreateMistWeaverDiagnosticOutputBehavior(ret => (WoWUnit)ret), new Decorator( ret => ret != null && (Me.Combat || ((WoWUnit)ret).Combat || ((WoWUnit)ret).PredictedHealthPercent() <= 99), // && HealerManager.SavingHealUnit == null // && (selfOnly || !MonkSettings.MistHealSettings.HealFromMelee || !Me.GotTarget() || Me.CurrentTarget.IsWithinMeleeRange), new PrioritySelector( new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( behavs.GenerateBehaviorTree(), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 40f, 34f) ) ) ) ) ) ) ) )); }
public static Composite CreateMonkOffHealBehavior() { HealerManager.NeedHealTargeting = SingularSettings.Instance.DpsOffHealAllowed; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, MonkSettings.OffHealSettings.Effuse); 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.Effuse), string.Format("Effuse @ {0}%", MonkSettings.OffHealSettings.Effuse), "Effuse", Spell.Cast("Effuse", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).PredictedHealthPercent(includeMyHeals: true) < MonkSettings.OffHealSettings.Effuse, 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) ) ) ) ) ) ) ) )); }