Beispiel #1
0
        // 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
                        )
                    )
                );
        }
Beispiel #2
0
        // 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
                        )
                    )
                );
        }
Beispiel #3
0
        //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
                        )
                    )
                );
        }
Beispiel #4
0
 private static void CreateMistweaverHealingRotation(bool selfOnly, PrioritizedBehaviorList behavs)
 {
     CreateMistweaverHealingWowhead(selfOnly, behavs);
 }
Beispiel #5
0
        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)
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                );
        }
Beispiel #6
0
        /*
        private static void CreateMistweaverHealingCustom(bool selfOnly, PrioritizedBehaviorList behavs)
        {
            #region Cast On Cooldown

            if (!selfOnly)
            {
                behavs.AddBehavior(
                    HealthToPriority(MonkSettings.MistHealSettings.RollRenewingMistCount) + 700,
                    String.Format("Roll Renewing Mist on at least {0} targets", MonkSettings.MistHealSettings.RollRenewingMistCount),
                    "Renewing Mist",
                    CreateMistweaverRollRenewingMist()
                    );
            }

            #endregion

            #region Save the Group

            if (!selfOnly)
            {
                behavs.AddBehavior(HealthToPriority(MonkSettings.MistHealSettings.ThunderFocusHealSingle) + 600,
                    String.Format("Thunder Focus Heal Single Target @ {0}%", MonkSettings.MistHealSettings.ThunderFocusHealSingle),
                    "Thunder Focus Tea",
                    new Decorator(
                        ret => (Me.Combat || ((WoWUnit)ret).Combat) && ((WoWUnit)ret).PredictedHealthPercent() < MonkSettings.MistHealSettings.ThunderFocusHealSingle,
                        new Sequence(
                            EnsureSoothingMistOnTarget(on => (WoWUnit)on),
                            Spell.OffGCD(Spell.BuffSelf("Thunder Focus Tea")),
                            new PrioritySelector(
                                Spell.Cast("Surging Mist", on => (WoWUnit)on),
                                Spell.Cast("Enveloping Mist", mov => false, on => (WoWUnit)on, req => true)
                                )
                            )
                        )
                    );
            }

            #endregion

            #region High Priority Buff

            behavs.AddBehavior(
                899,
                "Summon Jade Serpent Statue",
                "Summon Jade Serpent Statue",
                new Decorator(
                    req => Group.Tanks.Any(t => t.Combat && t.GotTarget() && t.IsWithinMeleeRangeOf(t.CurrentTarget)),
                    CreateSummonJadeSerpentStatueBehavior()
                    )
                );

            #endregion

            #region AoE Heals   400

            if (!selfOnly)
            {
                behavs.AddBehavior(
                    HealthToPriority(MonkSettings.MistHealSettings.ThunderFocusHealGroup) + 400,
                    String.Format("Thunder Focus Renewing Mist on {0} targets @ {1}%", MonkSettings.MistHealSettings.CountThunderFocusHealGroup, MonkSettings.MistHealSettings.ThunderFocusHealGroup),
                    "Thunder Focus Tea",
                    new Decorator(
                        ret =>
                        {
                            if (!Me.Combat || !(StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid))
                                return false;

                            if (Me.CurrentChi < 2)
                                return false;

                            if (!Spell.CanCastHack("Thunder Focus Tea"))
                                return false;

                            int count = HealerManager.Instance.TargetList.Count(p => p.HasAuraExpired("Renewing Mist", TimeSpan.FromMilliseconds(150)) && p.PredictedHealthPercent() <= MonkSettings.MistHealSettings.ThunderFocusHealGroup);
                            if (count >= MonkSettings.MistHealSettings.CountThunderFocusHealGroup)
                            {
                                Logger.WriteDebug("ThunderFocus: found {0} below {1}%", MonkSettings.MistHealSettings.CountThunderFocusHealGroup, MonkSettings.MistHealSettings.ThunderFocusHealGroup);
                                return true;
                            }

                            return false;
                        },
                        new Sequence(
                            Spell.Cast("Thunder Focus Tea"),
                            Spell.Cast(
                                "Renewing Mist",
                                on => on as WoWUnit
                                )
                            )
                        )
                    );

                behavs.AddBehavior(
                    HealthToPriority(MonkSettings.MistHealSettings.UpliftGroup) + 400,
                    String.Format("Uplift on {0} targets @ {1}%", MonkSettings.MistHealSettings.CountUpliftGroup, MonkSettings.MistHealSettings.UpliftGroup),
                    "Uplift",
                    new Decorator(
                        ret => (Me.Combat || ((WoWUnit)ret).Combat)
                            && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid)
                            && Me.CurrentChi >= 2,
                        Spell.Cast(
                            "Uplift",
                            on => on as WoWUnit,
                            req =>
                            {
                                int count = HealerManager.Instance.TargetList.Count(p => !p.HasAuraExpired("Renewing Mist", TimeSpan.FromMilliseconds(150)) && p.PredictedHealthPercent() <= MonkSettings.MistHealSettings.UpliftGroup);
                                if (count >= MonkSettings.MistHealSettings.CountUpliftGroup)
                                {
                                    Logger.WriteDebug("Uplift: found {0} with Renewing Mist (needed {1})", count, MonkSettings.MistHealSettings.CountUpliftGroup);
                                    return true;
                                }

                                return false;
                            }
                            )
                        )
                    );

                behavs.AddBehavior(
                    HealthToPriority(MonkSettings.MistHealSettings.ChiWave) + 400,
                    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())
                        )
                    );
            }

            #endregion

            #region Healing Cooldowns

            // save a player
            behavs.AddBehavior(
                HealthToPriority(MonkSettings.MistHealSettings.LifeCocoon) + 100,
                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)
                );

            #endregion

            #region Single Target Heals

            behavs.AddBehavior(
                HealthToPriority(MonkSettings.MistHealSettings.SoothingMist),
                String.Format("Soothing Mist @ {0}%", MonkSettings.MistHealSettings.SoothingMist),
                "Soothing Mist",
                Spell.Cast(
                    "Soothing Mist",
                    mov => true,
                    on => (WoWUnit)on,
                    req => !Me.IsMoving && !IsChannelingSoothingMist((WoWUnit)req) && ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.SoothingMist
                    )
                );

            behavs.AddBehavior(
                HealthToPriority(MonkSettings.MistHealSettings.EnvelopingMist),
                String.Format("Enveloping Mist @ {0}%", MonkSettings.MistHealSettings.EnvelopingMist),
                "Enveloping Mist",
                new Decorator(
                    req => ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.EnvelopingMist,
                    new Sequence(
                        EnsureSoothingMistOnTarget(on => on as WoWUnit),
                        new PrioritySelector(
                            Spell.Cast("Enveloping Mist", on => (WoWUnit)on),
                            Spell.Cast("Surging Mist", on => (WoWUnit)on, req => {
                                if (Me.CurrentChi < 3)
                                    Logger.WriteDebug("CantCastEnvelopingMist: casting Surging Mist since only {0} Chi", Me.CurrentChi);
                                else
                                {
                                    Logger.WriteDebug("CantCastEnvelopingMist: no idea why I cannot cast");
                                    return false;
                                }
                                return true;
                                })
                            )
                        )
                    )
                );

            behavs.AddBehavior(
                HealthToPriority(MonkSettings.MistHealSettings.SurgingMist),
                String.Format("Surging Mist @ {0}%", MonkSettings.MistHealSettings.SurgingMist),
                "Surging Mist",
                new Decorator(
                    req => ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.SurgingMist,
                    new Sequence(
                        EnsureSoothingMistOnTarget(on => on as WoWUnit),
                        Spell.Cast("Surging Mist", on => (WoWUnit)on)
                        )
                    )
                );

            #endregion
        }
        */
        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 => !IsChannelingSoothingMist()
                            && 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,
                    String.Format("Thunder Focus Uplift on {0} targets", MonkSettings.MistHealSettings.CountThunderFocusHealGroup),
                    "Thunder Focus Tea",
                    new Decorator(
                        ret => {
                            if (!Me.Combat || !(StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid))
                                return false;

                            if (Me.CurrentChi < 3 || Spell.IsSpellOnCooldown("Thunder Focus Tea") || Spell.IsSpellOnCooldown("Uplift"))
                                return false;

                            List<WoWUnit> renewlist = Unit.GroupMembersAndPets
                                .Where(p => p.GetAuraTimeLeft("Renewing Mist").TotalMilliseconds > 250)
                                .ToList();
                            int countAlmostExpired = renewlist.Count(p => p.GetAuraTimeLeft("Renewing Mist").TotalMilliseconds < 3000);

                            if (SingularSettings.DebugSpellCasting)
                                Logger.WriteDebug("ThunderFocusTea: found {0} with renew mist, need {1} ({2} about with less than 3 secs)", renewlist.Count(), MonkSettings.MistHealSettings.CountThunderFocusHealGroup, countAlmostExpired);

                            return countAlmostExpired > 0 && renewlist.Count() >= MonkSettings.MistHealSettings.CountThunderFocusHealGroup;
                            },
                        new Sequence(
                            Spell.Cast("Thunder Focus Tea", on => Me),
                            Spell.Cast("Uplift")
                            )
                        )
                    );

                behavs.AddBehavior(
                    HealthToPriority(100) + 700,
                    String.Format("Roll Renewing Mist on at least {0} targets", MonkSettings.MistHealSettings.RollRenewingMistCount),
                    "Renewing Mist",
                    new Decorator(
                        req => !IsChannelingSoothingMist(),
                        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 => !IsChannelingSoothingMist()
                        && (req as WoWUnit).PredictedHealthPercent() < MonkSettings.MistHealSettings.ChiWave,
                    Spell.Cast("Chi Wave", on => GetBestChiWaveTarget())
                    )
                );

            behavs.AddBehavior(
                HealthToPriority(1) + 600,
                String.Format("Instant Surging Mist (Vital Mists=5)"),
                "Surging Mist",
                new Decorator(
                    req => Me.GetAuraStacks("Vital Mists") == 5, // && !IsChannelingSoothingMist(), // not sure we care about channel here
                    Spell.Cast("Surging Mist", on => (WoWUnit)on)
                    )
                );

            behavs.AddBehavior(
                HealthToPriority(1) + 500,
                "Expel Harm in Combat for Chi",
                "Expel Harm",
                Spell.Buff("Expel Harm", on => Common.BestExpelHarmTarget(),
                    req => !IsChannelingSoothingMist()
                        && Me.Combat
                        && Me.CurrentChi < Me.MaxChi
                    )
                );

            if (!selfOnly)
            {
                behavs.AddBehavior(
                    HealthToPriority(MonkSettings.MistHealSettings.UpliftGroup) + 400,
                    String.Format("Uplift on {0} targets @ {1}%", MonkSettings.MistHealSettings.CountUpliftGroup, MonkSettings.MistHealSettings.UpliftGroup),
                    "Uplift",
                    new Decorator(
                        ret => (Me.Combat || ((WoWUnit)ret).Combat)
                            && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid)
                            && Me.CurrentChi >= 2,
                        Spell.Cast(
                            "Uplift",
                            on => on as WoWUnit,
                            req =>
                            {
                                if (IsChannelingSoothingMist())
                                    return false;

                                int count = HealerManager.Instance.TargetList.Count(p => !p.HasAuraExpired("Renewing Mist", TimeSpan.FromMilliseconds(150)) && p.PredictedHealthPercent() < MonkSettings.MistHealSettings.UpliftGroup);
                                if (count >= MonkSettings.MistHealSettings.CountUpliftGroup)
                                {
                                    Logger.WriteDebug("Uplift: found {0} with Renewing Mist (needed {1})", count, MonkSettings.MistHealSettings.CountUpliftGroup);
                                    return true;
                                }

                                return false;
                            }
                            )
                        )
                    );
            }

            if (!selfOnly)
            {
                behavs.AddBehavior(
                    HealthToPriority(MonkSettings.MistHealSettings.SpinningCraneKickGroup),
                    String.Format(SpinningCraneKick + " on {0} targets @ {1}%", MonkSettings.MistHealSettings.CountSpinningCraneKickGroup, MonkSettings.MistHealSettings.SpinningCraneKickGroup),
                    SpinningCraneKick,
                    Spell.Cast(
                        SpinningCraneKick,
                        on => on as WoWUnit,
                        ret =>
                        {
                            if (Spell.IsSpellOnCooldown(SpinningCraneKick))
                                return false;

                            if (!Me.Combat)
                                return false;

                            if (!Spell.UseAOE)
                                return false;

                            if (Me.CurrentChi >= Me.MaxChi)
                                return false;

                            // count heal targets
                            int countHeal = HealerManager.Instance.TargetList
                                .Count(u => u.HealthPercent < MonkSettings.MistHealSettings.SpinningCraneKickGroup && u.SpellDistance() <= 8);
                            if (countHeal >= MonkSettings.MistHealSettings.CountSpinningCraneKickGroup)
                            {
                                Logger.WriteDiagnostic("SpinningCraneKick: found {0} group members to heal", countHeal);
                                return true;
                            }

                            if (HealerManager.AllowHealerDPS())
                            {
                                int countAttack = Unit.NearbyUnfriendlyUnits.Count(u => u.SpellDistance() <= 8);
                                if (countAttack >= MonkSettings.SpinningCraneKickCnt)
                                {
                                    Logger.WriteDiagnostic("SpinningCraneKick: found enemy {0} targets to attack", countAttack);
                                    return true;
                                }

                                float countMerge = countHeal + countAttack;
                                float avgCountNeeded = (0f + MonkSettings.SpinningCraneKickCnt + MonkSettings.MistHealSettings.CountSpinningCraneKickGroup) / 2f;
                                if (countMerge > avgCountNeeded)
                                {
                                    Logger.WriteDiagnostic("SpinningCraneKick: found combination of {0:F1} heal+attack targets, needed {1:F1}", countMerge, avgCountNeeded);
                                    return true;
                                }
                            }

                            return false;
                        }
                        )
                    );
            }

            behavs.AddBehavior(
                HealthToPriority(MonkSettings.MistHealSettings.SoothingMist),
                String.Format("Soothing Mist @ {0}%", MonkSettings.MistHealSettings.SoothingMist),
                "Soothing Mist",
                Spell.Cast(
                    "Soothing Mist",
                    mov => true,
                    on => (WoWUnit)on,
                    req => !IsChannelingSoothingMist()
                        && !Me.IsMoving && !IsChannelingSoothingMist((WoWUnit)req) && ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.SoothingMist
                    )
                );

            behavs.AddBehavior(
                HealthToPriority(MonkSettings.MistHealSettings.SurgingMist),
                String.Format("Surging Mist @ {0}%", MonkSettings.MistHealSettings.SurgingMist),
                "Surging Mist",
                new Decorator(
                    req => ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.SurgingMist
                        && Spell.CanCastHack("Surging Mist", (WoWUnit)req),
                    new Sequence(
                        new PrioritySelector(
                            EnsureSoothingMistOnTarget(on => on as WoWUnit),
                            new Action(r =>
                            {
                                Logger.WriteDebug("SurgingMist: failed to ensure Soothing Mist first");
                                return RunStatus.Failure;
                            })
                            ),
                        new PrioritySelector(
                            Spell.Cast("Surging Mist", on => (WoWUnit)on),
                            new Action(r =>
                            {
                                Logger.WriteDebug("SurgingMist: 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 => Me.CurrentChi >= 3
                        && ((WoWUnit)req).HasAuraExpired("Enveloping Mist")
                        && ((WoWUnit)req).PredictedHealthPercent() < MonkSettings.MistHealSettings.EnvelopingMist
                        && Spell.CanCastHack("Enveloping Mist", (WoWUnit) req),
                    new Sequence(
                        new PrioritySelector(
                            EnsureSoothingMistOnTarget(on => on as WoWUnit),
                            new Action(r =>
                            {
                                Logger.WriteDebug("EnvelopingMist: failed to ensure Soothing Mist first");
                                return RunStatus.Failure;
                            })
                            ),
                        new PrioritySelector(
                            Spell.Cast("Enveloping Mist", on => (WoWUnit)on),
                            new Action(r =>
                            {
                                Logger.WriteDebug("EnvelopingMist: failed to ensure Soothing Mist first");
                                return RunStatus.Failure;
                            })
                            )
                        )
                    )
                );
        }
Beispiel #7
0
        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)
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                );
        }
Beispiel #8
0
        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)
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                );
        }
Beispiel #9
0
        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)
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                );
        }
Beispiel #10
0
        //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
                );
        }
Beispiel #11
0
        // 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
                        )
                    )
                );
        }
Beispiel #12
0
        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)
                                            )
                                        )
                                    )
                                )
                            )
                        )
                    )
                );
        }
Beispiel #13
0
        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)
                                )
                            )
                        )
                    )
                );
        }
Beispiel #14
0
        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))
                        )
                    )
                );
        }