public static Composite CreatePriestPreCombatBuffs() { return(new PrioritySelector( Spell.WaitForCast(), new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( Spell.BuffSelfAndWait("Levitate", req => PriestSettings.UseLevitate && Me.IsFalling), new Decorator( req => SingularRoutine.IsQuestBotActive && Me.HasMyAura("Levitate") && !Me.IsFalling, new Sequence( new SeqDiag(1, s => string.Format("Levitate: no longer needed since NOT falling and QuestBot active")), new Action(r => Me.CancelAura("Levitate")), new Wait(1, until => !Me.HasMyAura("Levitate"), new ActionAlwaysSucceed()) ) ), //Spell.BuffSelf("Shadow Protection", ret => PriestSettings.UseShadowProtection && Unit.NearbyFriendlyPlayers.Any(u => !u.Dead && !u.IsGhost && (u.IsInMyPartyOrRaid() || u.IsMe) && !Unit.HasAura(u, "Shadow Protection", 0))), // we no longer have Shadow resist Spell.BuffSelf("Fear Ward", ret => PriestSettings.UseFearWard), Spell.BuffSelf("Shadowform", ret => !Shadow.InVoidform), CreatePriestMovementBuffOnTank("PreCombat") ) ) )); }
public static Composite CheckIfWeShouldCancelBladestorm() { if (!HasTalent(WarriorTalents.Bladestorm)) { return(new ActionAlwaysFail()); } return(new ThrottlePasses( 1, TimeSpan.FromSeconds(1), RunStatus.Failure, new Decorator( req => StyxWoW.Me.HasAura("Bladestorm"), new Action(r => { // check if it makes sense to cancel to resume normal speed movement and charge if (!Me.Combat || !Unit.UnfriendlyUnits(20).Any(u => u.Aggro || (u.IsPlayer && u.IsHostile) || u.IsTargetingUs())) { Logger.WriteDebug("Bladestorm: cancel since out of combat or no targets within 20 yds"); Me.CancelAura("Bladestorm"); return RunStatus.Success; } return RunStatus.Failure; }) ) )); }
public static Composite CreateSpiritShellCancel() { if (PriestSettings.DiscHeal.SpiritShell <= 0 || !SpellManager.HasSpell(SPIRIT_SHELL_SPELL)) { return(new ActionAlwaysFail()); } return(new Decorator( req => Me.HasMyAura("Spirit Shell") && !Me.Combat && !HealerManager.Instance.TargetList.Any(m => m.Combat && m.SpellDistance() < 50), new Action(r => { Logger.Write(LogColor.Cancel, "^Spirit Shell: cancel since not in Combat"); Me.CancelAura("Spirit Shell"); }) )); }
/// <summary> /// implements standard Rest behavior. self-heal optional and typically used by DPS that have healing spell, /// as healing specs are better served using a spell appropriate to amount of healing needed. ressurrect /// is optional and done only if spell name passed /// </summary> /// <param name="spellHeal">name of healing spell</param> /// <param name="spellRez">name of ressurrect spell</param> /// <returns></returns> public static Composite CreateDefaultRestBehaviour(string spellHeal = null, string spellRez = null) { return(new PrioritySelector( new Decorator( ret => !Me.IsDead && !Me.IsGhost, new PrioritySelector( // Self-heal if possible new Decorator( ret => { if (IsEatingOrDrinking) { return false; } if (spellHeal == null || Me.HealthPercent > 85) { return false; } if (!SpellManager.HasSpell(spellHeal)) { return false; } if (!Spell.CanCastHack(spellHeal, Me)) { Logger.WriteDebug("DefaultRest: CanCast failed for {0}", spellHeal); return false; } if (Me.PredictedHealthPercent(includeMyHeals: true) > 85) { return false; } return true; }, new Sequence( new PrioritySelector( Movement.CreateEnsureMovementStoppedBehavior(reason: "to heal"), new Wait(TimeSpan.FromMilliseconds(500), until => !Me.IsMoving, new ActionAlwaysSucceed()) ), new PrioritySelector( new Sequence( ctx => Me.HealthPercent, new Action(r => Logger.WriteDebug("Rest Heal - {0} @ {1:F1}% Predict:{2:F1}% and moving:{3}, cancast:{4}", spellHeal, (double)r, Me.PredictedHealthPercent(includeMyHeals: true), Me.IsMoving, Spell.CanCastHack(spellHeal, Me, skipWowCheck: false))), Spell.Cast(spellHeal, mov => true, on => Me, req => true, cancel => { if (Me.HealthPercent < 90) { return false; } Logger.WriteDiagnostic("Rest Heal - cancelling since health reached {0:F1}%", Me.HealthPercent); return true; }, LagTolerance.No ), new Action(r => Logger.WriteDebug("Rest - Before Heal Cast Wait")), new WaitContinue(TimeSpan.FromMilliseconds(1500), until => Spell.CanCastHack(spellHeal, Me), new ActionAlwaysSucceed()), new Action(r => Logger.WriteDebug("Rest - Before Heal Health Increase Wait")), new WaitContinue(TimeSpan.FromMilliseconds(500), until => Me.HealthPercent > (1.1 * ((double)until)), new ActionAlwaysSucceed()), new Action(r => Logger.WriteDebug("Rest - Before Heal Cast Wait")), new Action(r => { Logger.WriteDebug("Rest - After Heal Completed: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)); return RunStatus.Success; }) ), new Action(r => { Logger.WriteDebug("Rest - After Heal Skipped: {0:F1}% Predicted: {1:F1}%", Me.HealthPercent, Me.PredictedHealthPercent(includeMyHeals: true)); return RunStatus.Failure; }) ) ) ), // Make sure we wait out res sickness. Helpers.Common.CreateWaitForRessSickness(), // Cannibalize support goes before drinking/eating. changed to a Sequence with wait because Rest behaviors that had a // .. WaitForCast() before call to DefaultRest would prevent cancelling when health/mana reached new Decorator( ret => SingularSettings.Instance.UseRacials && (Me.PredictedHealthPercent(includeMyHeals: true) <= SingularSettings.Instance.MinHealth || (Me.PowerType == WoWPowerType.Mana && Me.ManaPercent <= SingularSettings.Instance.MinMana)) && Spell.CanCastHack("Cannibalize") && CorpseAround, new Sequence( new DecoratorContinue(ret => Me.IsMoving, Movement.CreateEnsureMovementStoppedBehavior(reason: "to cannibalize")), new Wait(1, ret => !Me.IsMoving, new ActionAlwaysSucceed()), new Action(ret => Logger.Write(LogColor.SpellHeal, "*Cannibalize @ health:{0:F1}%{1}", Me.HealthPercent, (Me.PowerType != WoWPowerType.Mana) ? "" : string.Format(" mana:{0:F1}%", Me.ManaPercent))), new Action(ret => Spell.CastPrimative("Cannibalize")), // wait until Cannibalize in progress new WaitContinue( 1, ret => Me.CastingSpell != null && Me.CastingSpell.Name == "Cannibalize", new ActionAlwaysSucceed() ), // wait until cast or healing complete. use actual health percent here new WaitContinue( 10, ret => Me.CastingSpell == null || Me.CastingSpell.Name != "Cannibalize" || (Me.HealthPercent > 95 && (Me.PowerType != WoWPowerType.Mana || Me.ManaPercent > 95)), new ActionAlwaysSucceed() ), // show completion message and cancel cast if needed new Action(ret => { bool stillCasting = Me.CastingSpell != null && Me.CastingSpell.Name == "Cannibalize"; Logger.WriteFile("{0} @ health:{1:F1}%{2}", stillCasting ? "/cancel Cannibalize" : "Cannibalize ended", Me.HealthPercent, (Me.PowerType != WoWPowerType.Mana) ? "" : string.Format(" mana:{0:F1}%", Me.ManaPercent) ); if (stillCasting) { SpellManager.StopCasting(); } }) ) ), // use a bandage if enabled (it's quicker) new Decorator( ret => Me.IsAlive && Me.PredictedHealthPercent(includeMyHeals: true) <= SingularSettings.Instance.MinHealth, Item.CreateUseBandageBehavior() ), // Make sure we're a class with mana, if not, just ignore drinking all together! Other than that... same for food. new Decorator( ret => !Me.Combat && !Me.Stunned && !Me.IsSwimming && (Me.PowerType == WoWPowerType.Mana || Me.Class == WoWClass.Druid) && Me.ManaPercent <= SingularSettings.Instance.MinMana && !Me.HasAnyAura("Drink", "Refreshment") && Consumable.GetBestDrink(false) != null, new PrioritySelector( Movement.CreateEnsureMovementStoppedBehavior(reason: "to drink"), new Decorator( req => !Me.IsMoving, new Sequence( new Action(ret => { Logger.Write("Drinking @ {0:F1}% mana", Me.ManaPercent); Styx.CommonBot.Rest.DrinkImmediate(); }), Helpers.Common.CreateWaitForLagDuration(), new PrioritySelector( new Wait(TimeSpan.FromMilliseconds(500), until => Me.HasAnyAura("Drink", "Refreshment"), new ActionAlwaysSucceed()), new Action(r => Logger.WriteDiagnostic("Drinking: failed to see 'Drink' aura")) ) ) ) ) ), // Check if we're allowed to eat (and make sure we have some food. Don't bother going further if we have none. new Decorator( ret => !Me.Combat && !Me.Stunned && !Me.IsSwimming && Me.PredictedHealthPercent(includeMyHeals: true) <= SingularSettings.Instance.MinHealth && !Me.HasAnyAura("Food", "Refreshment") && Consumable.GetBestFood(false) != null, new PrioritySelector( Movement.CreateEnsureMovementStoppedBehavior(reason: "to eat"), new Decorator( req => !Me.IsMoving, new Sequence( new Action( ret => { float myHealth = Me.PredictedHealthPercent(includeMyHeals: true); Logger.WriteDebug("NeedToEat: predictedhealth @ {0:F1}", myHealth); Logger.Write("Eating @ {0:F1}% health", Me.HealthPercent); Styx.CommonBot.Rest.FeedImmediate(); }), Helpers.Common.CreateWaitForLagDuration(), new PrioritySelector( new Wait(TimeSpan.FromMilliseconds(500), until => Me.HasAnyAura("Food", "Refreshment"), new ActionAlwaysSucceed()), new Action(r => Logger.WriteDiagnostic("Eating: failed to see 'Food' aura")) ) ) ) ) ), // STAY SEATED (in Rest) while eating/drinking aura active. // true: stay seated // false: allow interruption of Drink new Decorator( ret => Me.HasAnyAura("Drink", "Refreshment") && (Me.PowerType == WoWPowerType.Mana || Me.Specialization == WoWSpec.DruidFeral) && Me.ManaPercent < 95, new ActionAlwaysSucceed() ), new Decorator( req => Me.HasAnyAura("Food", "Refreshment") && Me.HealthPercent < 95, new PrioritySelector( new Decorator( req => spellHeal != null && Spell.CanCastHack(spellHeal, Me) && Me.PowerType == WoWPowerType.Mana && Me.ManaPercent >= 70 && Me.HealthPercent < 85, new Sequence( new Action(r => Logger.Write(LogColor.Hilite, "^Stop eating and '{0}' since mana reached {1:F1}%", spellHeal, Me.ManaPercent)), new Action(r => Me.CancelAura("Food")), new Wait(TimeSpan.FromMilliseconds(500), until => !Me.HasAura("Food"), new ActionAlwaysSucceed()) ) ), new ActionAlwaysSucceed() // keep eating then ) ), // wait here if we are moving -OR- do not have food or drink new Decorator( ret => WaitForRegenIfNoFoodDrink(), new PrioritySelector( new Decorator( ret => Me.IsMoving, new PrioritySelector( new Throttle(5, new Action(ret => Logger.Write("Still moving... waiting until Bot stops"))), new ActionAlwaysSucceed() ) ), new Decorator( req => Me.IsSwimming, new PrioritySelector( new Throttle(15, new Action(ret => Logger.Write("Swimming. Waiting to recover our health/mana back"))), new ActionAlwaysSucceed() ) ), new PrioritySelector( new Throttle(15, new Action(ret => Logger.Write("We have no food/drink. Waiting to recover our health/mana back"))), new ActionAlwaysSucceed() ) ) ), // rez anyone near us if appropriate new Decorator(ret => spellRez != null, Spell.Resurrect(spellRez)), // hack: some bots not calling PreCombatBuffBehavior, so force the call if we are about done resting // SingularRoutine.Instance.PreCombatBuffBehavior, Movement.CreateWorgenDarkFlightBehavior() ) ) )); }
public static Composite CreateMistweaverCombatBehaviorInstances() { return(new PrioritySelector( CancelSoothingMistAsNeeded(), CreateMistWeaverDiagnosticOutputBehavior(on => MyHealTarget), new Decorator( req => true, new PrioritySelector( // HealerManager.CreateMeleeHealerMovementBehavior( Common.CreateMonkCloseDistanceBehavior( min => 30, on => (WoWUnit) on)), HealerManager.CreateStayNearTankBehavior(Common.CreateMonkCloseDistanceBehavior(min => 30, on => (WoWUnit)on)), /* * new Decorator( * unit => MovementManager.IsClassMovementAllowed * && !MonkSettings.DisableRoll * && (unit as WoWUnit).SpellDistance() > 10 * && Me.IsSafelyFacing(unit as WoWUnit, 5f), * Spell.Cast("Roll") * ) * ), */ new Decorator( ret => Me.Combat && HealerManager.AllowHealerDPS(), new PrioritySelector( CreateMistweaverMoveToEnemyTarget(), new Decorator( req => Me.GotTarget() && Me.CurrentTarget.IsAlive, Movement.CreateFaceTargetBehavior() ), Spell.WaitForCastOrChannel(), #region Spinning Crane Kick progress handler new Decorator( req => Me.HasActiveAura("Spinning Crane Kick"), // don't wait for Rushing Jade Wind since we can cast new PrioritySelector( new Action(r => { Logger.WriteFile(SpinningCraneKick + ": in progress with {0} ms left", (long)Me.GetAuraTimeLeft(SpinningCraneKick).TotalMilliseconds); return RunStatus.Failure; }), new Decorator( req => { if (Me.GetAuraTimeLeft(SpinningCraneKick).TotalMilliseconds < 333) { return false; } int countFriendly = Unit.NearbyGroupMembersAndPets.Count(u => u.SpellDistance() <= 8); if (countFriendly >= 3) { return false; } if (HealerManager.CancelHealerDPS()) { Logger.Write(LogColor.Cancel, "/cancel {0} since only {1} friendly targets hit and cannot DPS", SpinningCraneKick, countFriendly); return true; } int countEnemy = Unit.NearbyUnfriendlyUnits.Count(u => u.SpellDistance() <= 8); if ((countFriendly + countEnemy) < 3) { Logger.Write(LogColor.Cancel, "/cancel {0} since only {1} friendly and {2} enemy targets hit", SpinningCraneKick, countFriendly, countEnemy); return true; } return false; }, new Sequence( new Action(r => Me.CancelAura(SpinningCraneKick)), new Wait(1, until => !Me.HasActiveAura(SpinningCraneKick), new ActionAlwaysFail()) ) ), // dont go past here if SCK active new ActionAlwaysSucceed() ) ), #endregion new Decorator( ret => !Spell.IsGlobalCooldown(), new PrioritySelector( new Decorator( req => Me.GotTarget() && Me.CurrentTarget.IsWithinMeleeRange, Helpers.Common.CreateAutoAttack() ), Helpers.Common.CreateInterruptBehavior(), Spell.Cast("Leg Sweep", ret => Spell.UseAOE && SingularRoutine.CurrentWoWContext == WoWContext.Normal && Me.CurrentTarget.IsWithinMeleeRange), Spell.Cast( SpinningCraneKick, ret => Spell.UseAOE && HealerManager.AllowHealerDPS() && Unit.NearbyUnfriendlyUnits.Count(u => u.SpellDistance() <= 8) >= MonkSettings.SpinningCraneKickCnt ), Spell.Cast("Rising Sun Kick"), Spell.Cast("Blackout Kick", req => Me.GetAuraStacks("Teachings of the Monastery") >= 3), Spell.Cast("Tiger Palm") ) ), Spell.Cast("Roll", req => MovementManager.IsClassMovementAllowed && !MonkSettings.DisableRoll && Me.CurrentTarget.Distance > minDistRollAllowed ) ) ) ) ), new Decorator( ret => !Spell.IsGlobalCooldown() && Unit.NearbyGroupMembers.Any(m => m.IsAlive && !m.IsMe), new PrioritySelector( CreateMistweaverMonkHealing(selfOnly: false), new Decorator( req => true, Helpers.Common.CreateInterruptBehavior() ) ) ), Spell.WaitForCastOrChannel() )); }