public static Composite CreateShamanNonHealBehavior() { return (new PrioritySelector( new Decorator( ret => !StyxWoW.Me.Combat, Spell.Heal( "Healing Surge", ret => StyxWoW.Me, ret => StyxWoW.Me.HealthPercent <= 70) ), new Decorator( ret => StyxWoW.Me.Combat, new PrioritySelector( Spell.BuffSelf( "Healing Tide Totem", ret => Unit.NearbyFriendlyPlayers.Any( p => p.HealthPercent < SingularSettings.Instance.Shaman.HealingTideTotemPercent && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide))), Spell.BuffSelf( "Healing Stream Totem", ret => !Totems.Exist(WoWTotemType.Water) && Unit.NearbyFriendlyPlayers.Any( p => p.HealthPercent < SingularSettings.Instance.Shaman.HealHealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide))), Spell.Heal( "Healing Surge", ret => StyxWoW.Me, ret => StyxWoW.Me.HealthPercent <= 30) ) ) )); }
public static Composite CreateRestoShamanHealingOnlyBehavior(bool selfOnly = false) { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, Math.Max(ShamanSettings.RestoHealSettings.HealingWave, ShamanSettings.RestoHealSettings.HealingSurge)); bool moveInRange = false; if (!selfOnly) { moveInRange = (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds); } Logger.WriteDebugInBehaviorCreate("Shaman Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) { behavs.AddBehavior(dispelPriority, "Purify Spirit", null, Dispelling.CreateDispelBehavior()); } #region Save the Group behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.SpiritLinkTotem) + 600, "Spirit Link Totem", "Spirit Link Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.CastOnGround("Spirit Link Totem", on => (WoWUnit)on, ret => HealerManager.Instance.TargetList.Count( p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.SpiritLinkTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= ShamanSettings.RestoHealSettings.MinSpiritLinkCount ) ) ); #endregion #region AoE Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingTideTotem) + 400, "Healing Tide Totem", "Healing Tide Totem", new Decorator( ret => (Me.Combat || ((WoWUnit)ret).Combat) && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid) && (!Totems.Exist(WoWTotem.Cloudburst) || (Totems.GetTotem(WoWTotem.Cloudburst).Expires - DateTime.UtcNow).TotalMilliseconds < 1500), Spell.Cast( "Healing Tide Totem", on => Me, req => Me.Combat && HealerManager.Instance.TargetList.Count(p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingTideTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= ShamanSettings.RestoHealSettings.MinHealingTideCount ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingStreamTotem) + 300, "Healing Stream Totem", "Healing Stream Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.Cast( "Healing Stream Totem", on => { if (Totems.Exist(WoWTotemType.Water)) { return(null); } if (Spell.IsSpellOnCooldown(Totems.ToSpellId(WoWTotem.HealingStream))) { return(null); } // if tank in group, make sure we are near the tank WoWUnit tank = HealerManager.TankToStayNear; if (tank != null) { if (!HealerManager.IsTankSettledIntoFight(tank)) { return(null); } if (tank.Distance > Totems.GetTotemRange(WoWTotem.HealingStream)) { return(null); } } WoWUnit unit = HealerManager.Instance.TargetList .FirstOrDefault( p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingStream) ); return(unit); } ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.GiftoftheQueen) + 300, "Gift of the Queen", "Gift of the Queen", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( context => GetBestGiftoftheQueenTarget(), new Decorator( ret => ret != null, Spell.CastOnGround("Gift of the Queen", on => (WoWUnit)on, req => true, false) ) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.CloudburstTotem) + 300, "Cloudburst Totem", "Cloudburst Totem", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.Cast( "Cloudburst Totem", on => { if (Totems.Exist(WoWTotemType.Water)) { return(null); } if (Spell.IsSpellOnCooldown(Totems.ToSpellId(WoWTotem.Cloudburst))) { return(null); } if (Unit.ValidUnit(Me.CurrentTarget) && (Me.CurrentTarget.TimeToDeath() < 20 || Unit.UnitsInCombatWithUsOrOurStuff().Count() < 3)) { return(null); } // if tank in group, make sure we are near the tank WoWUnit tank = HealerManager.TankToStayNear; if (tank != null) { if (!HealerManager.IsTankSettledIntoFight(tank)) { return(null); } if (tank.Distance > Totems.GetTotemRange(WoWTotem.Cloudburst)) { return(null); } } WoWUnit unit = HealerManager.Instance.TargetList .Where( p => p.HealthPercent < ShamanSettings.RestoHealSettings.CloudburstTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.Cloudburst) ) .OrderBy(p => (int)p.HealthPercent) .FirstOrDefault(); return(unit); } ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingRain) + 200, "Healing Rain", "Healing Rain", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( context => GetBestHealingRainTarget(), new Decorator( ret => ret != null, Spell.CastOnGround("Healing Rain", on => (WoWUnit)on, req => true, false) ) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.ChainHeal) + 150, "Chain Heal", "Chain Heal", new Decorator( ret => Me.Combat && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), new PrioritySelector( ctx => GetBestChainHealTarget(), new Decorator( ret => ret != null, new Sequence( new DecoratorContinue( req => ((WoWUnit)req).HasAuraExpired("Riptide", TimeSpan.FromMilliseconds(ChainHealCastTime), true), new Sequence( Spell.Cast("Riptide", on => (WoWUnit)on, req => true, cancel => false), new Wait(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(LagTolerance.No), new ActionAlwaysSucceed()), new Action(r => TidalWaveRefresh()) ) ), new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(LagTolerance.No), new ActionAlwaysSucceed()), Spell.Cast("Chain Heal", on => (WoWUnit)on), new Action(r => TidalWaveRefresh()) ) ) ) ) ); #endregion #region Single Target Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingWave), "Healing Wave", "Healing Wave", new Decorator(ret => ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingWave, new Sequence( new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), new WaitContinue(2, until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), Spell.Cast("Healing Wave", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action(r => TidalWaveConsume()) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.HealingSurge), "Healing Surge", "Healing Surge", new Decorator(ret => ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.RestoHealSettings.HealingSurge, new Sequence( new WaitContinue(TimeSpan.FromMilliseconds(1500), until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), new WaitContinue(2, until => !Spell.IsGlobalCooldown(), new ActionAlwaysSucceed()), Spell.Cast("Healing Surge", mov => true, on => (WoWUnit)on, req => true, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal), new Action(r => TidalWaveConsume()) ) ) ); #endregion #region Healing Cooldowns behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.RestoHealSettings.Ascendance) + 100, "Ascendance", "Ascendance", new Decorator( ret => ShamanSettings.UseAscendance && (StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid), Spell.BuffSelf( "Ascendance", ret => HealerManager.Instance.TargetList.Count(p => p.PredictedHealthPercent() < ShamanSettings.RestoHealSettings.Ascendance) >= ShamanSettings.RestoHealSettings.MinAscendanceCount ) ) ); #endregion behavs.OrderBehaviors(); if (selfOnly == false && CompositeBuilder.CurrentBehaviorType == BehaviorType.Combat) { behavs.ListBehaviors(); } return(new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, CreateRestoDiagnosticOutputBehavior(ret => (WoWUnit)ret), new Decorator( ret => ret != null && (Me.Combat || ((WoWUnit)ret).Combat || ((WoWUnit)ret).PredictedHealthPercent() <= 99), new PrioritySelector( new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Totems.CreateTotemsBehavior(), // roll Riptide on Tanks otherwise new Sequence( Spell.Cast("Riptide", on => { WoWUnit unit = GetBestRiptideTankTarget(); if (unit != null && Spell.CanCastHack("Riptide", unit, skipWowCheck: true)) { Logger.WriteDebug("Buffing RIPTIDE ON TANK: {0}", unit.SafeName()); return unit; } return null; }), new Action(r => TidalWaveRefresh()) ), // cast Riptide if we are a low level CreateRestoShamanBuffRiptideLowLevel(), // cast Riptide if we need Tidal Waves -- skip if Ancestral Swiftness is CreateRestoShamanBuffTidalWaves(), behavs.GenerateBehaviorTree(), // cast Riptide if we need Tidal Waves -- skip if Ancestral Swiftness is new Decorator( ret => { int rollCount = HealerManager.Instance.TargetList.Count( u => u.IsAlive && u.HasMyAura("Riptide")); // Logger.WriteDebug("GetBestRiptideTarget: currently {0} group members have my Riptide", rollCount); return rollCount < ShamanSettings.RestoHealSettings.RollRiptideCount; }, new Sequence( Spell.Cast("Riptide", on => { // if tank needs Riptide, bail out on Rolling as they have priority if (GetBestRiptideTankTarget() != null) { return null; } // get the best target from all wowunits in our group WoWUnit unit = GetBestRiptideTarget(); if (unit != null) { Logger.WriteDebug(Color.White, "ROLLING RIPTIDE on: {0}", unit.SafeName()); } return unit; }), new Action(r => TidalWaveRefresh()) ) ), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 40f, 34f) ) ) ) ) ) ) ) )); }
public static Composite CreateDpsShamanOffHealBehavior() { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); int cancelHeal = (int)Math.Max(SingularSettings.Instance.IgnoreHealTargetsAboveHealth, ShamanSettings.OffHealSettings.HealingSurge); bool moveInRange = (SingularRoutine.CurrentWoWContext == WoWContext.Battlegrounds); Logger.WriteDebugInBehaviorCreate("Shaman Healing: will cancel cast of direct heal if health reaches {0:F1}%", cancelHeal); /* * int dispelPriority = (SingularSettings.Instance.DispelDebuffs == RelativePriority.HighPriority) ? 999 : -999; * if (SingularSettings.Instance.DispelDebuffs != RelativePriority.None) * behavs.AddBehavior(dispelPriority, "Cleanse Spirit", null, Dispelling.CreateDispelBehavior()); */ #region Save the Group behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.AncestralSwiftness) + 500, String.Format("Oh Shoot Heal @ {0}%", ShamanSettings.OffHealSettings.AncestralSwiftness), null, new Decorator( ret => (Me.Combat || ((WoWUnit)ret).Combat) && ((WoWUnit)ret).PredictedHealthPercent() < ShamanSettings.OffHealSettings.AncestralSwiftness, new PrioritySelector( Spell.HandleOffGCD(Spell.BuffSelf("Ancestral Swiftness")), Spell.Cast("Healing Surge", on => (WoWUnit)on) ) ) ); #endregion #region AoE Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.HealingStreamTotem) + 300, string.Format("Healing Stream Totem @ {0}%", ShamanSettings.OffHealSettings.HealingStreamTotem), "Healing Stream Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Stream Totem", on => (!Me.Combat || Totems.Exist(WoWTotemType.Water)) ? null : HealerManager.Instance.TargetList.FirstOrDefault(p => p.PredictedHealthPercent() < ShamanSettings.OffHealSettings.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingStream)) ) ) ); behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.HealingRain) + 200, string.Format("Healing Rain @ {0}% Count={1}", ShamanSettings.OffHealSettings.HealingRain, ShamanSettings.OffHealSettings.MinHealingRainCount), "Healing Rain", Spell.CastOnGround("Healing Rain", on => Restoration.GetBestHealingRainTarget(), req => HealerManager.Instance.TargetList.Count() > 1, false) ); #endregion #region Single Target Heals behavs.AddBehavior(HealerManager.HealthToPriority(ShamanSettings.OffHealSettings.HealingSurge), string.Format("Healing Surge @ {0}%", ShamanSettings.OffHealSettings.HealingSurge), "Healing Surge", Spell.Cast("Healing Surge", mov => true, on => (WoWUnit)on, req => ((WoWUnit)req).PredictedHealthPercent(includeMyHeals: true) < ShamanSettings.OffHealSettings.HealingSurge, cancel => ((WoWUnit)cancel).HealthPercent > cancelHeal ) ); #endregion behavs.OrderBehaviors(); if (Singular.Dynamics.CompositeBuilder.CurrentBehaviorType == BehaviorType.Heal) { behavs.ListBehaviors(); } return(new PrioritySelector( ctx => HealerManager.FindLowestHealthTarget(), // HealerManager.Instance.FirstUnit, new Decorator( ret => ret != null && (Me.Combat || ((WoWUnit)ret).Combat || ((WoWUnit)ret).PredictedHealthPercent() <= 99), new PrioritySelector( new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Totems.CreateTotemsBehavior(), /* * Spell.Cast("Earth Shield", * ret => (WoWUnit)ret, * ret => ret is WoWUnit && Group.Tanks.Contains((WoWUnit)ret) && Group.Tanks.All(t => !t.HasMyAura("Earth Shield"))), */ behavs.GenerateBehaviorTree(), new Decorator( ret => moveInRange, new Sequence( new Action(r => _moveToHealUnit = (WoWUnit)r), new PrioritySelector( Movement.CreateMoveToLosBehavior(on => _moveToHealUnit), Movement.CreateMoveToUnitBehavior(on => _moveToHealUnit, 30f, 25f) ) ) ) ) ) ) ) )); }
public static Composite CreateShamanDpsHealBehavior() { Composite offheal; if (!SingularSettings.Instance.DpsOffHealAllowed) { offheal = new ActionAlwaysFail(); } else { offheal = new Decorator( ret => HealerManager.ActingAsOffHealer, CreateDpsShamanOffHealBehavior() ); } return(new Decorator( ret => !Spell.IsGlobalCooldown() && !Spell.IsCastingOrChannelling(), new PrioritySelector( new Decorator( ret => !Me.Combat && (!Me.IsMoving || Me.HasAura("Maelstrom Weapon", 5)) && Me.HealthPercent <= 85 && // not redundant... this eliminates unnecessary GetPredicted... checks SpellManager.HasSpell("Healing Surge") && Me.PredictedHealthPercent(includeMyHeals: true) < 85, new PrioritySelector( new Sequence( ctx => (float)Me.HealthPercent, new Action(r => Logger.WriteDebug("Healing Surge: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Healing Surge", Me, skipWowCheck: false))), Spell.Cast( "Healing Surge", mov => true, on => Me, req => true, cancel => Me.HealthPercent > 85 ), new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()), new Action(r => Logger.WriteDebug("Healing Surge: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true))) ), new Action(r => Logger.WriteDebug("Healing Surge: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true))) ) ), new Decorator( ret => Me.Combat, new PrioritySelector( Spell.BuffSelf("Astral Shift", ret => Me.HealthPercent < ShamanSettings.AstralShiftPercent || Common.StressfulSituation), Spell.BuffSelf(WoWTotem.StoneBulwark.ToSpellId(), ret => !Me.IsMoving && (Common.StressfulSituation || Me.HealthPercent < ShamanSettings.StoneBulwarkTotemPercent && !Totems.Exist(WoWTotem.EarthElemental))), Spell.BuffSelf("Shamanistic Rage", ret => Me.HealthPercent < ShamanSettings.ShamanisticRagePercent || Common.StressfulSituation), Spell.HandleOffGCD( Spell.BuffSelf("Ancestral Guidance", ret => Me.HealthPercent < ShamanSettings.SelfAncestralGuidance && Me.GotTarget() && Me.CurrentTarget.TimeToDeath() > 8 ) ), new Decorator( req => !Me.IsMoving && ShamanSettings.SelfHealingStreamTotem > 0 && !Totems.Exist(WoWTotemType.Water), Spell.BuffSelf( WoWTotem.HealingStream.ToSpellId(), req => { int count = Unit.NearbyUnitsInCombatWithMeOrMyStuff.Count(u => !u.IsTrivial()); if (count > 1) { return true; } if (count > 0 && Me.HealthPercent < ShamanSettings.SelfHealingStreamTotem) { if (!Me.GotTarget() || Me.CurrentTarget.IsPlayer || Me.CurrentTarget.TimeToDeath() > 5) { return true; } if (Me.HealthPercent < (ShamanSettings.SelfHealingStreamTotem / 2)) { return true; } } return false; } ) ), // save myself if possible new Decorator( ret => (!Me.IsInGroup() || Battlegrounds.IsInsideBattleground) && (!Me.IsMoving || Me.HasAura("Maelstrom Weapon", 5) || !Spell.IsSpellOnCooldown("Ancestral Swiftness")) && Me.HealthPercent < ShamanSettings.SelfAncestralSwiftnessHeal && Me.PredictedHealthPercent(includeMyHeals: true) < ShamanSettings.SelfAncestralSwiftnessHeal, new PrioritySelector( Spell.HandleOffGCD(Spell.BuffSelf("Ancestral Swiftness")), new PrioritySelector( new Sequence( ctx => (float)Me.HealthPercent, new Action(r => Logger.WriteDebug("Healing Surge: {0:F1}% Predict:{1:F1}% and moving:{2}, cancast:{3}", (float)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack("Healing Surge", Me, skipWowCheck: false))), Spell.Cast( "Healing Surge", mov => true, on => Me, req => true, cancel => Me.HealthPercent > 85 ), new WaitContinue(TimeSpan.FromMilliseconds(500), until => !Me.IsCasting && Me.HealthPercent > (1.1 * ((float)until)), new ActionAlwaysSucceed()), new Action(r => Logger.WriteDebug("Healing Surge: After Heal Attempted: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true))) ), new Action(r => Logger.WriteDebug("Healing Surge: After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true))) ) ) ) ) ), offheal ) )); }
public static Composite CreateShamanCombatBuffsPVP() { return(new PrioritySelector( Totems.CreateTotemsBehavior(), Spell.BuffSelf("Astral Shift", ret => Me.HealthPercent < ShamanSettings.AstralShiftPercent || Common.StressfulSituation), Spell.BuffSelf(WoWTotem.StoneBulwark.ToSpellId(), ret => !Me.IsMoving && (Common.StressfulSituation || Me.HealthPercent < ShamanSettings.StoneBulwarkTotemPercent && !Totems.Exist(WoWTotem.EarthElemental))), Spell.BuffSelf("Shamanistic Rage", ret => Me.HealthPercent < 70 || Me.ManaPercent < 70 || Common.StressfulSituation), // hex someone if they are not current target, attacking us, and 12 yds or more away new PrioritySelector( ctx => Unit.NearbyUnfriendlyUnits .Where(u => (u.CreatureType == WoWCreatureType.Beast || u.CreatureType == WoWCreatureType.Humanoid) && (u.Aggro || u.PetAggro || (u.Combat && u.IsTargetingMeOrPet)) && u.Distance.Between(10, 30) && Me.IsSafelyFacing(u) && u.InLineOfSpellSight && Me.GotTarget() && u.Location.Distance(Me.CurrentTarget.Location) > 10) .OrderByDescending(u => u.Distance) .FirstOrDefault(), Spell.Cast("Hex", onUnit => (WoWUnit)onUnit) ), new Decorator( req => { if (!Unit.ValidUnit(Me.CurrentTarget)) { return false; } if (Me.Specialization == WoWSpec.ShamanEnhancement && !Me.CurrentTarget.IsWithinMeleeRange) { return false; } if (Me.CurrentTarget.TimeToDeath() > 5 || Me.CurrentTarget.HealthPercent > 20 || Me.HealthPercent < Me.CurrentTarget.HealthPercent) { return true; } if (Unit.UnfriendlyUnits(40).Count(u => u.IsTargetingMyStuff()) >= 2) { return true; } return false; }, new PrioritySelector( Spell.BuffSelfAndWait(PartyBuff.BloodlustSpellName, req => ShamanSettings.UseBloodlust && MovementManager.IsClassMovementAllowed && IsPvpFightWorthLusting, gcd: HasGcd.No ), Spell.BuffSelf( "Ascendance", req => ShamanSettings.UseAscendance && ((Me.GotTarget() && Me.CurrentTarget.HealthPercent > 70) || Unit.NearbyUnfriendlyUnits.Count() > 1), gcd: HasGcd.No ), Spell.BuffSelf( "Elemental Mastery", req => !PartyBuff.WeHaveBloodlust, gcd: HasGcd.No ) ) ) // , Spell.BuffSelf("Spiritwalker's Grace", ret => Me.IsMoving && Me.Combat) )); }
public static Composite CreateRestoShamanHealingOnlyBehavior(bool selfOnly, bool moveInRange) { HealerManager.NeedHealTargeting = true; PrioritizedBehaviorList behavs = new PrioritizedBehaviorList(); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), "Unleash Elements", "Unleash Elements", Spell.Buff("Unleash Elements", ret => (WoWUnit)ret, ret => (Me.IsMoving || ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.AncestralSwiftness) && Common.IsImbuedForHealing(Me.Inventory.Equipped.MainHand) )); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), String.Format("Ancestral Swiftness @ {0}%", SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), "Ancestral Swiftness", new Sequence( Spell.BuffSelf("Ancestral Swiftness", ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.AncestralSwiftness), Spell.Heal("Greater Healing Wave", ret => (WoWUnit)ret) )); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.GreaterHealingWave), "Greater Healing Wave", "Greater Healing Wave", Spell.Heal("Greater Healing Wave", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.GreaterHealingWave)); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.HealingWave), "Healing Wave", "Healing Wave", Spell.Heal("Healing Wave", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingWave)); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.HealingSurge), "Healing Surge", "Healing Surge", Spell.Heal("Healing Surge", ret => (WoWUnit)ret, ret => ((WoWUnit)ret).GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingSurge)); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.ChainHeal), "Chain Heal", "Chain Heal", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( new PrioritySelector( context => Clusters.GetBestUnitForCluster(ChainHealPlayers, ClusterType.Chained, ChainHealHopRange), Spell.Heal( "Chain Heal", ret => (WoWPlayer)ret, ret => Clusters.GetClusterCount((WoWPlayer)ret, ChainHealPlayers, ClusterType.Chained, ChainHealHopRange) >= 3) ) ) ) ); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.HealingRain), "Healing Rain", "Healing Rain", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, new PrioritySelector( context => Clusters.GetBestUnitForCluster(Unit.NearbyFriendlyPlayers.Cast <WoWUnit>(), ClusterType.Radius, 10f), Spell.CastOnGround( "Healing Rain", ret => ((WoWPlayer)ret).Location, ret => (StyxWoW.Me.GroupInfo.IsInRaid ? 3 : 2) < Clusters.GetClusterCount((WoWPlayer)ret, Unit.NearbyFriendlyPlayers.Cast <WoWUnit>(), ClusterType.Radius, 10f)) ) ) ); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.SpiritLinkTotem), "Spirit Link Totem", "Spirit Link Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Spirit Link Totem", ret => (WoWPlayer)ret, ret => Unit.NearbyFriendlyPlayers.Count( p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.SpiritLinkTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.SpiritLink)) >= (Me.GroupInfo.IsInRaid ? 3 : 2) ) ) ); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.HealingTideTotemPercent), "Healing Tide Totem", "Healing Tide Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Tide Totem", ret => Unit.NearbyFriendlyPlayers.Count(p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingTideTotemPercent && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= (Me.GroupInfo.IsInRaid ? 3 : 2) ) ) ); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.HealingStreamTotem), "Healing Stream Totem", "Healing Stream Totem", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.Cast( "Healing Stream Totem", ret => Unit.NearbyFriendlyPlayers.Count(p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.HealingStreamTotem && p.Distance <= Totems.GetTotemRange(WoWTotem.HealingTide)) >= (Me.GroupInfo.IsInRaid ? 3 : 2) && !Totems.Exist(WoWTotemType.Water) ) ) ); behavs.AddBehavior(HealthToPriority(SingularSettings.Instance.Shaman.Heal.Ascendance), "Ascendance", "Ascendance", new Decorator( ret => StyxWoW.Me.GroupInfo.IsInParty || StyxWoW.Me.GroupInfo.IsInRaid, Spell.BuffSelf( "Ascendance", ret => Unit.NearbyFriendlyPlayers.Count(p => p.GetPredictedHealthPercent() < SingularSettings.Instance.Shaman.Heal.Ascendance) >= (Me.GroupInfo.IsInRaid ? 3 : 2) ) ) ); behavs.OrderBehaviors(); behavs.ListBehaviors(); return(new PrioritySelector( ctx => selfOnly ? StyxWoW.Me : HealerManager.Instance.FirstUnit, // ctx => selfOnly ? StyxWoW.Me : GetHealTarget(), Spell.WaitForCast(), new Decorator( ret => Me.IsCasting, new ActionAlwaysSucceed()), new Decorator( ret => ret != null && ((WoWUnit)ret).GetPredictedHealthPercent() <= SingularSettings.Instance.IgnoreHealTargetsAboveHealth, new PrioritySelector( new Sequence( new Decorator( ret => guidLastHealTarget != ((WoWUnit)ret).Guid, new Action(ret => { guidLastHealTarget = ((WoWUnit)ret).Guid; Logger.WriteDebug(Color.LightGreen, "Heal Target - {0} {1:F1}% @ {2:F1} yds", ((WoWUnit)ret).SafeName(), ((WoWUnit)ret).GetPredictedHealthPercent(), ((WoWUnit)ret).Distance); })), new Action(ret => { return RunStatus.Failure; }) ), /* * new Sequence( * new Action(ret => Logger.WriteDebug(Color.LightGreen, "-- past spellcast")), * new Action(ret => { return RunStatus.Failure; }) * ), */ new Decorator( ret => !SpellManager.GlobalCooldown, new PrioritySelector( /* * new Sequence( * new Action(ret => Logger.WriteDebug(Color.LightGreen, "-- past gcd")), * new Action(ret => { return RunStatus.Failure; }) * ), */ Totems.CreateTotemsBehavior(), Spell.Heal("Earth Shield", ret => (WoWUnit)ret, ret => ret is WoWPlayer && Group.Tanks.Contains((WoWPlayer)ret) && Group.Tanks.All(t => !t.HasMyAura("Earth Shield"))), // Just pop RT on CD. Plain and simple. Calling GetBestRiptideTarget will see if we can spread RTs (T12 2pc) Spell.Heal("Riptide", ret => GetBestRiptideTarget((WoWPlayer)ret), ret => !Me.HasAnyAura("Tidal Waves", "Ancestral Swiftness") && (((WoWPlayer)ret).GetPredictedHealthPercent() > 15 || Spell.GetSpellCooldown("Ancestral Swiftness").TotalMinutes > 0f) // use Ancestral Swiftness value to check ), behavs.GenerateBehaviorTree(), Spell.Heal("Riptide", ret => GetBestRiptideTarget((WoWPlayer)ret), ret => !Me.HasAura("Ancestral Swiftness")) #if false , new Sequence( new Action(ret => Logger.WriteDebug(Color.LightGreen, "No Action - stunned:{0} silenced:{1}" , Me.Stunned || Me.IsStunned() , Me.Silenced )), new Action(ret => { return RunStatus.Failure; }) ) , new Decorator( ret => StyxWoW.Me.Combat && StyxWoW.Me.GotTarget && !Unit.NearbyFriendlyPlayers.Any(u => u.IsInMyPartyOrRaid), new PrioritySelector( Movement.CreateMoveToLosBehavior(), Movement.CreateFaceTargetBehavior(), Helpers.Common.CreateInterruptSpellCast(ret => StyxWoW.Me.CurrentTarget), Spell.Cast("Earth Shock"), Spell.Cast("Lightning Bolt"), Movement.CreateMoveToTargetBehavior(true, 35f) )) #endif ) ), new Decorator( ret => moveInRange, Movement.CreateMoveToRangeAndStopBehavior(ret => (WoWUnit)ret, ret => 38f)) ) ) )); }
private static Composite CreateElementalDiagnosticOutputBehavior() { if (!SingularSettings.Debug) { return(new ActionAlwaysFail()); } return(new ThrottlePasses(1, 1, new Action(ret => { uint stks = 0; string shield; WoWAura aura = Me.GetAuraByName("Lightning Shield"); if (aura != null) { stks = aura.StackCount; if (!Me.HasAura("Lightning Shield", (int)stks)) { Logger.WriteDebug(Color.MediumVioletRed, "Inconsistancy Error: have {0} stacks but Me.HasAura('Lightning Shield', {0}) was False!!!!", stks, stks); } } else { aura = Me.GetAuraByName("Water Shield"); if (aura == null) { aura = Me.GetAuraByName("Earth Shield"); if (aura != null) { stks = aura.StackCount; } } } shield = aura == null ? "(null)" : aura.Name; string line = string.Format(".... [{0}] h={1:F1}%/m={2:F1}%, shield={3}, stks={4}, moving={5}", CompositeBuilder.CurrentBehaviorType.ToString(), Me.HealthPercent, Me.ManaPercent, shield, stks, Me.IsMoving.ToYN() ); WoWUnit target = Me.CurrentTarget; if (target == null) { line += ", target=(null)"; } else { line += string.Format(", target={0} @ {1:F1} yds, th={2:F1}%, face={3} loss={4}, fs={5}", target.SafeName(), target.Distance, target.HealthPercent, Me.IsSafelyFacing(target).ToYN(), target.InLineOfSpellSight.ToYN(), (long)target.GetAuraTimeLeft("Flame Shock").TotalMilliseconds ); } if (Totems.Exist(WoWTotemType.Fire)) { line += ", fire=" + Totems.GetTotem(WoWTotemType.Fire).Name; } if (Totems.Exist(WoWTotemType.Earth)) { line += ", earth=" + Totems.GetTotem(WoWTotemType.Earth).Name; } if (Totems.Exist(WoWTotemType.Water)) { line += ", water=" + Totems.GetTotem(WoWTotemType.Water).Name; } if (Totems.Exist(WoWTotemType.Air)) { line += ", air=" + Totems.GetTotem(WoWTotemType.Air).Name; } Logger.WriteDebug(Color.Yellow, line); return RunStatus.Failure; }) )); }